Back to Course |
How to Create Laravel Package: Step-by-Step Example

Styling: CSS and Assets

Currently, our pages don't have any styling. What if you want to use Tailwind CSS classes, how do you install Tailwind into a package?

Don't worry, you don't need to configure Vite or similar inside the package. Your goal is to compile the CSS file before structuring the package. In the first steps in our tutorial, we have that code as a Laravel project, not a package. So that's exactly where you should add Tailwind classes and run npm run build to compile the final CSS into the public folder.

Then, with the package, all you need to do here is copy that pre-built CSS file into the package /resources folder.

This is exactly what I will do, and we have this file:

packages/laraveldaily/laravel-permission-editor/resources/assets/css/app.css:

*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after // ...

Then, we need to register that CSS to be published in the Service Provider:

class PermissionEditorServiceProvider extends ServiceProvider
{
public function boot()
{
$this->loadViewsFrom(__DIR__ . '/../resources/views', 'permission-editor');
 
if ($this->app->runningInConsole()) {
$this->publishes([
__DIR__.'/../resources/assets' => public_path('permission-editor'),
], 'permission-editor-assets');
}
}
}

The statement $this->publishes() means that whenever the user runs php artisan vendor:publish and chooses our package, the contents of our resources/assets folder will be published. We need to also wrap it in a console-only behavior check.

php artisan vendor:publish

Laravel package vendor publish

You can also run this command specifying the package service provider as a parameter:

php artisan vendor:publish --provider="Laraveldaily\LaravelPermissionEditor\PermissionEditorServiceProvider"

And now we have this file in the /public folder of the project:

Laravel package public folder

So we can reference it in the main layout Blade file as our main asset:

packages/laraveldaily/laravel-permission-editor/resources/views/layouts/app.blade.php:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
 
<link href="{{ asset('permission-editor/css/app.css') }}" rel="stylesheet" />
<title>Laravel Permission Editor</title>
</head>

Behind the scenes, I've added the Tailwind CSS layout with the classes that you may check in the final repository of the package, at the end of the tutorial.

Just one full file, as an example:

packages/laraveldaily/laravel-permission-editor/resources/views/roles/index.blade.php:

@extends('permission-editor::layouts.app')
 
@section('content')
<div class="sm:flex sm:items-center">
<div class="sm:flex-auto">
<h1 class="text-xl font-semibold text-gray-900">Roles</h1>
</div>
<div class="mt-4 sm:mt-0 sm:ml-16 sm:flex-none">
<a href="{{ route('permission-editor.roles.create') }}"
class="inline-flex items-center justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:w-auto">
Add Role
</a>
</div>
</div>
<div class="mt-8 flex flex-col">
<div class="-my-2 -mx-4 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div class="inline-block min-w-full py-2 align-middle md:px-6 lg:px-8">
<div class="overflow-hidden shadow ring-1 ring-black ring-opacity-5 md:rounded-lg">
<table class="min-w-full divide-y divide-gray-300">
<thead class="bg-gray-50">
<tr>
<th scope="col"
class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6">
Name
</th>
<th scope="col"
class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900">
Permissions
</th>
<th scope="col"></th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 bg-white">
@forelse ($roles as $role)
<tr>
<td class="whitespace-nowrap px-3 py-4 text-sm font-medium text-gray-900 sm:pl-6">
{{ $role->name }}
</td>
<td class="whitespace-nowrap px-3 py-4 text-sm font-medium text-gray-900">
{{ $role->permissions_count }}
</td>
<td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6">
<a href="{{ route('permission-editor.roles.edit', $role) }}"
class="inline-flex items-center justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-1 text-sm font-medium text-white shadow-sm hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 sm:w-auto">
Edit
</a>
 
<form action="{{ route('permission-editor.roles.destroy', $role) }}"
method="POST"
onsubmit="return confirm('Are you sure?')"
class="inline-block">
@csrf
@method('DELETE')
<button type="submit"
class="inline-flex items-center justify-center rounded-md border border-transparent bg-red-600 px-4 py-1 text-sm font-medium text-white shadow-sm hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 sm:w-auto">
Delete
</button>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="3"
class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6">
No roles found.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</div>
@endsection

In this case of just a few views, I didn't extract the CSS classes into Blade components, but you can do that if you wish, then the Blade files code would be shorter, with <x-button> and other components.

Now, we refresh the browser...

Laravel package design

And we have our design based on those CSS classes!