thalysjuvenal/aurum-filament-theme
An executive gold-on-graphite theme engine and UI kit for FilamentPHP v5 panels, with gold, emerald, sapphire and ruby presets, dark and light modes, and styled login, dashboard, and table screens.
Three commands to a themed panel:
composer require thalysjuvenal/aurum-filament-theme
php artisan filament:assets
Then register the plugin in the PanelProvider you want themed:
use ThalysJuvenal\Aurum\AurumTheme;
public function panel(Panel $panel): Panel
{
return $panel
->id('admin')
// existing config...
->plugin(AurumTheme::make());
}
That single line registers the colors, fonts, and theme CSS scoped to that panel. There is no Vite/Tailwind step — the CSS ships with the plugin and is served through Filament's asset pipeline. If you skip php artisan filament:assets, you'll get an unstyled panel and waste an hour debugging a problem that isn't a problem.
Where to look first:
README.md → Presets section — pick Gold/Emerald/Sapphire/Ruby and decide whether a custom preset is needed.src/Widgets/ (AurumStat, AurumValueList, ValueListItem) and src/Schemas/ (Timeline, TotalsBlock) — these are small, source-readable classes, not a framework. Skim them before building custom UI; most dashboard needs are covered.src/Presets/AurumPreset — the abstract base you'll extend for anything brand-specific.First realistic use case: an admin dashboard that needs to look "executive" without a designer. Drop in AurumTheme::make(), build a RevenueOverview extends AurumStatsOverview with 3–4 AurumStat metrics, and a top-clients AurumValueList. That gives you 80% of a polished dashboard in an afternoon, and you can layer a login brand block on top in five minutes.
Because the plugin is scoped to the panel it's attached to, this is the natural pattern for apps with an admin panel and a customer-facing panel that should not share an aesthetic:
// AdminPanelProvider
->plugin(AurumTheme::make()->preset(Gold::class)->brandName('ACME OPS'))
// CustomerPanelProvider
->plugin(AurumTheme::make()->preset(Sapphire::class)->brandName('ACME'))
AurumTheme::make() returns a fresh instance — don't share one across providers.
Compose, don't reimplement. The intended workflow is: subclass AurumStatsOverview / AurumValueList, override getStats() / getItems(), and let the theme handle presentation.
class RevenueOverview extends AurumStatsOverview
{
protected function getStats(): array
{
return [
AurumStat::make('Revenue', 'R$ 128.450,00')
->description('vs. last month')
->trend('up', '+12.4%')
->icon('heroicon-o-banknotes'),
AurumStat::make('Open orders', '37')
->trend('down', '-4.1%'),
];
}
}
AurumStat mirrors the Filament Stat API closely, so porting existing stats is mostly a class swap plus adding ->trend().
Timeline and TotalsBlock live under src/Schemas, which means they slot into resource view pages and custom pages, not just widgets. The high-value integration is an order detail page:
// view page schema
Section::make('Order history')
->schema([
Timeline::make()->items([
TimelineItem::make('Order placed')->timestamp('2026-07-01 09:12'),
TimelineItem::make('Payment confirmed')->timestamp('2026-07-01 10:03')->active(),
]),
]),
Section::make('Totals')
->schema([
TotalsBlock::make()
->rows([
TotalsRow::make('Subtotal', 'R$ 1.250,00'),
TotalsRow::make('Tax', 'R$ 125,00'),
])
->total(TotalsRow::make('Total', 'R$ 1.375,00')),
]),
TotalsBlock already emits tabular numerals, so you don't need to add .aurum-mono manually there.
The reason to reach for the preset object rather than hardcoding hex values in your chart widgets is that the chart follows whichever preset the panel is configured with, including per-panel differences and future custom presets:
use Filament\Facades\Filament;
$preset = Filament::getCurrentPanel()->getPlugin('aurum-theme')?->getPreset();
'backgroundColor' => $preset?->accentRgba(0.38),
'borderColor' => $preset?->accentRgba(1.0),
Guard with ?-> — see Gotchas — and cache the preset in a property if you build many datasets in one widget.
The .aurum-mono utility is opt-in, so make it a team convention for currency, date, and code columns:
TextColumn::make('total')->money('BRL')->extraAttributes(['class' => 'aurum-mono']);
A cheap way to enforce it is a MoneyColumn::make() helper in app/Filament/Columns/ that wraps TextColumn and injects the attribute. Do it once, early, or you'll retrofit dozens of resources.
Extend AurumPreset, implement name(), palette() (OKLCH 50–950 for $panel->colors()), and accentHex() (reference hex stops for the --aurum-* variables). Note the asymmetry: palette() feeds Filament's own color system, accentHex() feeds the theme's CSS variables. Both must agree on the same color, or buttons and charts will drift apart.
The plugin is panel-scoped, not global. A panel without ->plugin(AurumTheme::make()) gets none of it — not the colors, not the fonts, not the components' styling. In a multi-panel app this is a feature, but it's also the #1 source of "the theme doesn't work" reports. Confirm which panel you're actually looking at before anything else.
getPlugin('aurum-theme') returns null on unthemed panels. Any chart widget or custom component that reads the preset must null-guard. The string 'aurum-theme' is the plugin ID and is not documented as a constant — if you hardcode it in more than one place, extract it into a class constant of your own.
The brand block and the sidebar are always dark. This is the Aurum signature, not a bug. Two consequences:
->darkModeBrandLogo() over ->brandLogo() when both are set — if you only have one light-background asset, it will look wrong.->hideBrandText() only takes effect when a logo exists. Without one, the wordmark always renders (otherwise you'd be left with an empty square). Don't design a layout that assumes it disappears.Trim your logo before publishing. The brandLogoHeight() value applies to the full canvas, so a PNG with generous transparent padding renders as a tiny mark inside a tall box. magick logo.png -trim +repage logo.png (or sips on macOS) before you ship it. This is called out in the README because it's the most common visual complaint.
OKLCH palettes require modern browsers. The default presets are OKLCH-based, and custom presets are expected to be too. On older corporate browsers you may get unsupported-color fallbacks and unexpected rendering. If you're delivering to locked-down environments, verify before committing to a custom palette.
Contrast gates are on the package's presets, not yours. The four shipped presets are tested (tests/PresetContrastTest.php) against AA for dark-mode button text, large-text AA for light-mode, and a 3:1 accent-on-background ratio. A custom preset you invent inherits none of that validation. Eyeball it in both modes on real screens, or add a small test of your own — the failure mode is invisible gold-on-gold button text in light mode.
Zero-build is great until you already have a build. If the consuming app uses ->viteTheme(), both the plugin's CSS and yours are loaded; load order and specificity may surprise you. Test overrides in both light and dark mode after adding viteTheme, and prefer the plugin's exposed --aurum-* CSS variables over !important.
Filament v5 only. The requirement is filament/filament ^5.0 and PHP ^8.2 in the consuming app (contributing needs ^8.3). If you're migrating a v3/v4 panel, this isn't a drop-in — check the rest of your stack first.
It's a young package (10 stars, v-early maturity). Pin the version in composer.json and read CHANGELOG.md before bumping. There are zero dependents, so upstream breakage won't be caught by anyone else. In exchange, the surface area is small — src/Widgets, src/Schemas, src/Presets — and every class is short enough to fork selectively if you need to.
Re-run php artisan filament:assets after every upgrade, and clear view/config caches if a theme change doesn't appear. The CSS is published through Filament's asset pipeline, not compiled by the app, so a stale asset copy is the usual culprit.
Debugging order when styling misbehaves: (1) is the plugin on this panel, (2) did assets publish, (3) is a viteTheme overriding, (4) is your class actually aurum-mono and not aurum_mono. The theme adds no JS, so anything behavioural is a Filament problem, not an Aurum one.
How can I help you explore Laravel packages today?