asignua/filament-themes
asignua/filament-themes adds theme management to Filament admin panels, letting you register, organize, and switch between custom themes. It provides a straightforward way to integrate theme selection into your Filament resources and pages.
composer require hasnayeen/themes, then publish and run the migration (theme_settings table). If you prefer user-column storage, publish the config first and skip the migration — you'll add the columns to users yourself.PanelProvider: ->plugin(ThemesPlugin::make()). Nothing renders until the plugin is attached to a panel, so a missing Themes page is almost always a registration problem, not a config problem.theme_settings table if you have multiple panels, multiple guards, or plan to add panels later; user model columns if you have one panel and want to avoid the extra query. Migrating between the two is manual work, so treat this as a one-way decision.config/themes.php (theme lists, storage, defaults), the plugin's Themes page class (to see how cards/previews are composed), and one built-in theme class (Nord or Ocean) — it is the shortest reference for the hasnayeen/themes contract you'll implement for custom themes.Default + override precedence. Configure a panel-wide theme as the fallback and let per-user choice win. This gives you a safe branded default for new users and lets power users personalise. Document the precedence in your onboarding, because "why does my theme reset?" is the first support question.
Per-panel theme lists for branding boundaries. Use the per-panel list to expose only themes that make sense for that audience — e.g. Ocean + your brand theme for a customer-facing panel, the full six for an internal admin panel. This is also your cheapest way to hide a theme you haven't contrast-tested yet, without deleting it.
Custom themes as classes, registered in config. Implement the hasnayeen/themes contract, define light and dark palettes plus the primary colour, then register the class in config/themes.php. Keep the class dumb — palettes only, no logic — so it stays trivially testable and diffable in review.
Primary colour via swatch or free hex. Use the swatches as the guided path for non-designers and the #rrggbb field for brand matching. Wrap the free-hex case with your own validation (valid hex, and a contrast check against your intended surfaces) before it ever reaches production.
Gate for the "locked" case. canChangeTheme is the right hook for kiosk displays, tenant-managed branding, or "only admins may change this". Implement it via a Gate definition or policy method rather than branching inside the page — it keeps the decision in one place and makes it testable.
Multi-panel / multi-tenant. Combine per-panel lists with theme_settings storage and key your rows by panel (and tenant, if you have them). One tenant's branding refresh then can't leak into another's panel.
Testing. Write a Livewire test that selects a theme, asserts persistence, and asserts the gate blocks unauthorised users. For every custom theme, add a contrast test mirroring the package's own AA assertions — the built-in guarantees don't extend to your classes.
Translations. Ten languages ship in-box; publish the lang files only for the handful of strings you want to reword (e.g. "Theme" → "Appearance"), rather than overriding the whole set.
Gate::before will bypass canChangeTheme. If you have a super-admin bypass returning true, theme locking won't apply to those users. That's usually fine, but verify it's the intent.theme_settings keyed by panel or tenant name go stale when you rename one. Prefer stable identifiers, and add a small cleanup command or migration when you rename panels.php artisan config:cache/config:clear after editing theme lists — a newly registered theme that "doesn't appear" is nearly always config cache. Also clear views if you publish the Themes page.composer.json and keep your theme registrations in one config file plus one small service provider, so a future upgrade is a single-file change. Check compatibility against your Filament major version explicitly — theming plugins are tightly coupled to Filament's colour system.How can I help you explore Laravel packages today?