PHPackages                             silasrm/filament-themes - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. silasrm/filament-themes

ActiveLibrary

silasrm/filament-themes
=======================

Complete, ready-made panel themes for Filament with a live-preview switcher and flexible persistence (per-user, global, per-tenant).

v1.0.1(1mo ago)022↓66.7%MITPHPPHP ^8.2CI failing

Since Jul 17Pushed 1mo agoCompare

[ Source](https://github.com/silasrm/filament-themes)[ Packagist](https://packagist.org/packages/silasrm/filament-themes)[ Docs](https://github.com/silasrm/filament-themes)[ RSS](/packages/silasrm-filament-themes/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (8)Versions (3)Used By (0)

Filament Themes
===============

[](#filament-themes)

Ready-made **complete** panel themes for [Filament](https://filamentphp.com) — not just accent colours, but a full restyle of background, sidebar, topbar, cards, tables, inputs, text and borders. Ships a live-preview switcher and flexible persistence (per-user, global, per-tenant).

Supports **Filament 4 &amp; 5**, **Laravel 11 / 12 / 13**, PHP 8.2+.

Installation
------------

[](#installation)

```
composer require silasrm/filament-themes
```

The package's service provider is auto-discovered, which also registers the `filament-themes` **view namespace** and translations. If you have disabled package auto-discovery, register it manually in `bootstrap/providers.php`:

```
use Silasrm\FilamentThemes\FilamentThemesServiceProvider;

return [
    // ...
    FilamentThemesServiceProvider::class,
];
```

Optionally publish the config to add or override themes:

```
php artisan vendor:publish --tag=filament-themes-config
```

Usage
-----

[](#usage)

Register the plugin on your panel. With no configuration it works out of the box using a cache-backed global store:

```
use Silasrm\FilamentThemes\ThemesPlugin;

public function panel(Panel $panel): Panel
{
    return $panel->plugin(ThemesPlugin::make());
}
```

A **Theme** page appears in the panel with a grid of theme swatches — hover to preview instantly, click to apply. The applied theme is re-applied on every request via a render hook, so a full page reload keeps the chosen look.

### Persistence modes

[](#persistence-modes)

Combine any of the three. Resolution cascades **user → tenant → global → default**. The first store that returns a known theme key wins.

```
use Filament\Facades\Filament;
use Silasrm\FilamentThemes\ThemesPlugin;

ThemesPlugin::make()
    ->perUser()                    // each user picks their own (stored on the user model)
    ->global()                     // app-wide default (cache-backed, no migration)
    ->perTenant(
        get: fn (): ?string => Filament::getTenant()?->branding_settings['theme'] ?? null,
        set: fn (string $key): void => Filament::getTenant()?->update(['branding_settings' => [...]]),
    )
    ->defaultTheme('azul');
```

- **`perUser(?string $attribute = 'theme')`** — reads/writes an attribute on the authenticated user. Falls back to session for guests.
- **`global(?ThemeStore $store = null)`** — app-wide theme. Defaults to a cache store (set `global_store` to `session` or `database` in config).
- **`perTenant(Closure $get, Closure $set)`** — delegate to your tenant model. `$get` receives the resolved tenant (or `null`) and must return a `?string`theme key; `$set` receives the chosen `string` key.
- **`tenantResolver(Closure $resolver)`** — resolve the tenant model when Filament has not bound it yet (e.g. the **login screen** of a domain-based tenant). `$resolver` receives the `Request` and returns the tenant model (or `null`). See [Domain-based tenancy &amp; login](#domain-based-tenancy--login).
- **`defaultTheme(string $key)`** — fallback key used when nothing is selected.

#### Multi-panel / multi-tenant

[](#multi-panel--multi-tenant)

Each panel builds its **own** resolver from the stores you configured on *that*panel. This avoids a shared singleton being overwritten by the last-booted panel, so e.g. an `admin` and a `member` panel can each resolve their own tenant's theme independently. If you need a resolver outside the plugin (e.g. in a custom controller), build one bound to the current panel:

```
$plugin = Filament::getCurrentPanel()->getPlugin('filament-themes');
$resolver = $plugin->makeResolver(app(\Silasrm\FilamentThemes\ThemeManager::class));
$resolver->resolveKey();   // current theme key for this panel
```

### Forcing light/dark mode

[](#forcing-lightdark-mode)

By default the plugin **forces** the panel's colour mode to follow the resolved theme's `mode` (`light`/`dark`). This prevents Filament's built-in dark mode from flipping its `--gray-*` scale (including form labels) while the theme's surface colours stay fixed — which would otherwise yield invisible white-on-white labels.

If you would rather let the visitor's OS/browser preference win, disable it:

```
ThemesPlugin::make()->forceMode(false);
```

### Custom themes

[](#custom-themes)

```
use Silasrm\FilamentThemes\Theme;

ThemesPlugin::make()->themes([
    Theme::make('minha-marca', 'Minha Marca')
        ->primary('#ff0055')
        ->secondary('#7c3aed')
        ->gray('#64748b')
        ->surfaces(
            bg: '#faf5ff',
            surface: '#ffffff',
            sidebar: '#f3e8ff',
            topbar: '#ffffff',
            text: '#2e1065',
            muted: '#6d6591',
            border: '#e9d5ff',
        ),

    Theme::make('minha-dark', 'Minha Dark')
        ->dark()
        ->primary('#22d3ee')
        ->surfaces(
            bg: '#0b1220',
            surface: '#111827',
            sidebar: '#0b1220',
            topbar: '#0b1220',
            text: '#e5e7eb',
            muted: '#9ca3af',
            border: '#1f2937',
        ),
]);
```

`Theme` fluent API:

MethodPurpose`Theme::make(string $key, string $label)`Create a theme (key is used for storage &amp; CSS scoping)`->label(string)`Display name in the switcher`->mode('light'|'dark')` / `->dark()`Colour mode`->primary(string)` / `->secondary(string)` / `->gray(string)`Accent colours (any CSS colour)`->surfaces(bg:, surface:, sidebar:, topbar:, text:, muted:, border:)`Surface coloursYou can also add themes declaratively in `config/filament-themes.php` under `presets` (same shape as the built-ins).

### Domain-based tenancy &amp; login

[](#domain-based-tenancy--login)

For **path-based** tenancy (`tenant(Team::class)`) the tenant is exposed as a route parameter, so the theme resolves automatically — even on the login screen.

For **domain-based** tenancy (`tenantDomain('{tenant}.example.com')`) the tenant lives in the request **host**, not a route parameter. Filament has not bound the tenant yet on the login / register screen (`Filament::getTenant()`is `null`), so the per-tenant store would otherwise fall back to the global / default theme. Provide a `tenantResolver` so the package can find the tenant from the host and apply its theme before authentication:

```
use App\Models\Unit;
use Filament\Facades\Filament;
use Illuminate\Http\Request;
use Silasrm\FilamentThemes\ThemesPlugin;

ThemesPlugin::make()
    ->perTenant(
        get: fn (?Unit $tenant): ?string => $tenant?->branding_settings['theme'] ?? null,
        set: fn (string $key): void => Filament::getTenant()?->update(['branding_settings' => [...]]),
    )
    ->tenantResolver(
        fn (Request $request): ?Unit => Unit::where('domain', $request->getHost())->first(),
    );
```

The `tenantResolver` is tried after `Filament::getTenant()` and before the `{tenant}` route parameter, so it only kicks in when Filament has not yet bound the tenant (login, register, etc.).

### Disable the switcher page

[](#disable-the-switcher-page)

```
ThemesPlugin::make()->switcherPage(false);
```

Configuration
-------------

[](#configuration)

After publishing (`--tag=filament-themes-config`), `config/filament-themes.php`supports:

KeyDefaultPurpose`default``azul`Fallback theme key`global_store``cache`Store used by `global()` mode — `cache`, `session` or `database``cache_key``filament-themes.global`Cache key for the global store`session_key``filament-themes.theme`Session key for the global/session store`user_attribute``theme`Column/attribute used by `perUser()``presets`*(many)*Theme definitions available in the switcherBuilt-in themes
---------------

[](#built-in-themes)

**Light:** Azul, Verde, Oceano, Laranja, Rosa, Lavanda, Ouro, Nord (claro), Sunset **Pastel:** Menta, Lavanda, Pêssego, Céu, Rosa, Amarelo, Sálvia **Dark:** Dracula, Nord, Meia-noite, Floresta escura, Grafite, Vinho

How it works
------------

[](#how-it-works)

The plugin styles the panel in two complementary ways:

1. **Accent colours** — on each request the middleware registers the active theme's `primary`/`secondary`/`gray` with Filament's native colour system (`$panel->colors()` + `FilamentColor::register()`). Filament then generates the matching CSS variables (`--primary-500`, etc.) used across buttons, badges, links and focus rings.
2. **Surface restyle** — a `` block is injected at `PanelsRenderHook::HEAD_END` with CSS generated from the active theme, targeting Filament's `fi-*` classes (background, sidebar, topbar, cards, tables, inputs, text, borders). The rules use `!important` so they win over Filament's own utility/component CSS — including the styles Vite injects at runtime in **development** (`npm run dev`), which would otherwise override a plain-specificity overlay.

It is a pure CSS overlay — no build step or asset compilation required.

> **Compatibility note:** because it targets `fi-*` classes, a Filament minor release could change selectors. The CI matrix covers Filament 4 &amp; 5; CSS output is snapshot-tested.

Testing
-------

[](#testing)

```
composer test
composer analyse
composer lint
```

License
-------

[](#license)

MIT. See [LICENSE.md](LICENSE.md).

###  Health Score

40

—

FairBetter than 86% of packages

Maintenance91

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

45d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/4303414783efee854cef1390ccd9aa197b6d6b9bcc6623cd475bba87fdcbfc66?d=identicon)[silasrm](/maintainers/silasrm)

---

Top Contributors

[![openhands-agent](https://avatars.githubusercontent.com/u/175740463?v=4)](https://github.com/openhands-agent "openhands-agent (5 commits)")

---

Tags

laraveluithemesfilamentfilament-plugindark-mode

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/silasrm-filament-themes/health.svg)

```
[![Health](https://phpackages.com/badges/silasrm-filament-themes/health.svg)](https://phpackages.com/packages/silasrm-filament-themes)
```

###  Alternatives

[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3915.5k](/packages/rawilk-profile-filament-plugin)[codewithdennis/filament-select-tree

The multi-level select field enables you to make single selections from a predefined list of options that are organized into multiple levels or depths.

331634.0k37](/packages/codewithdennis-filament-select-tree)[croustibat/filament-jobs-monitor

Background Jobs monitoring like Horizon for all drivers for FilamentPHP

278359.3k12](/packages/croustibat-filament-jobs-monitor)[finity-labs/fin-mail

A powerful email template manager and composer for Filament with dynamic token replacement, template versioning, and inline email sending.

316.8k2](/packages/finity-labs-fin-mail)[stephenjude/filament-jetstream

A Laravel starter kit built with Filament inspired by Jetstream.

17863.7k3](/packages/stephenjude-filament-jetstream)[tapp/filament-maillog

Filament plugin to view outgoing mail

3084.0k1](/packages/tapp-filament-maillog)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
