| description | Create or update a PowerGrid v7 theme using the Theme abstract class, the ThemeBuilder struct() token map, and the filter()/editable()/toggleable() token methods
|
| name | powergrid-theme |
What I do
- Create a new theme class extending
Theme with a struct() built from ThemeBuilder
- Update an existing theme by adding or overriding tokens in
struct(), filter(), editable(), or toggleable()
- Wire per-component overrides via
customThemeClass() (swap the class) or template() + merge() (patch tokens)
- Register a new default theme in the published config
- Run the theme test suite after changes
When to use me
Use this when:
- A new UI theme needs to be added (e.g. Bootstrap, Flowbite, ShadCN)
- An existing theme's tokens need to be changed or extended
- A single PowerGrid component needs a one-off theme override
- Token keys are missing or misnamed and produce empty class output
This skill is only about creating and updating v7 themes. It is self-contained — everything you need is here and in REFERENCE.md (same folder).
How to use me
Example 1: Create a new theme class
Use the 'powergrid-theme' skill to create a new theme for Bootstrap 5.
Theme class: src/Themes/Bootstrap.php
Example 2: Update tokens in an existing theme
Use the 'powergrid-theme' skill to update the DaisyUI theme:
- Change table.layout.thead to use 'bg-neutral text-neutral-content'
- Add a filter.boolean.select override for DaisyUI input sizing
Example 3: Per-component override
Use the 'powergrid-theme' skill to apply a partial theme override to
MyTableComponent so that table.layout.tr gets an extra 'stripe' class.
How it fits together (the short version)
A theme is a PHP class in src/Themes/. It extends Theme (src/Themes/Theme.php) and returns a token map. Blade views read tokens with the theme() / theme_view() helpers, e.g. {{ theme('table.layout.td') }} and {{ theme_view('pagination') }}.
The three canonical themes are the patterns to copy:
src/Themes/Tailwind.php — the base theme; every other theme inherits from it
src/Themes/DaisyUI.php — a compact subclass (start here for a new theme)
src/Themes/Flux.php — a subclass that also overrides resolveTokens()
struct() returns a Components\ThemeBuilder (fluent). It is split into top-level sections — layout, header, table, cols, footer — and CSS classes live inside each section's ->layout(Closure). Filters, editable cells, and the toggleable switch are separate methods (filter(), editable(), toggleable()), merged into the token map automatically by resolveTokens().
Inheritance is explicit: Theme::$parentTheme defaults to null. Set protected ?string $parentTheme = Tailwind::class; so any token, filter, editable, or toggleable value you do not declare is inherited from Tailwind. (DaisyUI and Flux both do exactly this.)
Full token maps, the builder API, and the resolution order are in REFERENCE.md.
Workflow
- Identify the goal. New theme, token change on an existing theme, or a per-component override?
- Read the closest real theme (
Tailwind.php for the full map, DaisyUI.php for a lean subclass) and, if updating, the target file.
- Make the change:
- New theme → create
src/Themes/MyTheme.php from the template below.
- Token change → edit the relevant
->layout(...), filter(), editable(), or toggleable() value.
- Per-component → use
customThemeClass() or template() + merge() (see below).
- Register it (new default theme only) in
resources/config/livewire-powergrid.php:
'theme' => \PowerComponents\LivewirePowerGrid\Themes\MyTheme::class,
- Test:
composer test -- --filter="ThemeTest|ThemeBuilderTest|PowerGridComponentThemeTest"
composer test
Minimal new-theme template
Copy this, rename the class, point baseView at your own view folder, and fill the classes. It inherits Tailwind's filter(), editable(), and toggleable() — override those methods only if your framework needs different filter markup or switch colors (see REFERENCE.md).
<?php
namespace PowerComponents\LivewirePowerGrid\Themes;
class MyTheme extends Theme
{
protected ?string $parentTheme = Tailwind::class;
public function struct(): Components\ThemeBuilder
{
return Components\ThemeBuilder::make($this->name())
->baseView('livewire-powergrid::components.themes.my-theme')
->layout(fn (Components\Layout $layout) => $layout
->wrapper('space-y-4')
->outsideFilters('')
)
->header(fn (Components\Header $header) => $header
->view('header')
->layout(fn (Components\Layout $layout) => $layout
->()
->()
->()
->()
)
->(fn (Components\SearchBox ) =>
->()
->()
->()
->()
->()
->()
->()
->()
)
)
->(fn (Components\Table ) =>
->(fn (Components\Layout ) =>
->()
->()
->()
->()
->()
->()
->()
->()
->()
)
->(fn (Components\Body ) =>
->(fn (Components\Tr ) =>
->()
->()
)
)
->(fn (Components\Checkbox ) =>
->()
->()
)
->(fn (Components\Radio ) =>
->()
->()
)
)
->(fn (Components\Cols ) =>
->()
)
->(fn (Components\Footer ) =>
->()
->(fn (Components\Layout ) =>
->()
->()
)
->()
);
}
}
Per-component overrides
Both hooks live on your Livewire PowerGrid component (see src/Concerns/Base.php and src/PowerGridComponent.php), and are applied in boot() — never bind powergrid.theme yourself.
Swap the whole theme class for one component:
public function customThemeClass(): ?string
{
return MyTheme::class;
}
Patch a few tokens for one component (deep merge, so pass only the keys you change):
public function template(): ?Theme
{
return Tailwind::make()->merge([
'table' => [
'layout' => [
'tr' => 'stripe hover:bg-yellow-50',
],
],
]);
}
Rules
struct() returns Components\ThemeBuilder. Type the method public function struct(): Components\ThemeBuilder and return the builder directly. Do not call ->toArray() on it — resolveTokens() handles that. (filter(), editable(), and toggleable() return plain arrays and do end each Component chain with ->toArray().)
- Declare
parentTheme explicitly. Theme::$parentTheme defaults to null; set it to Tailwind::class so undeclared tokens/filters inherit from the base theme. Tokens do not fall through to Tailwind unless you opt in this way.
- Correct closure type-hints. The sub-builders are distinct classes:
->searchBox(fn (Components\SearchBox $s) => ...), ->checkbox(fn (Components\Checkbox $c) => ...), ->radio(fn (Components\Radio $r) => ...), ->body(fn (Components\Body $b) => $b->tr(fn (Components\Tr $tr) => ...)), ->cols(fn (Components\Cols $c) => ...). Hinting these as Components\Component throws a TypeError.
- Pagination is a string.
->pagination('pagination') — Footer::pagination() accepts Closure|array|string; all three shipped themes use the string alias.
- CSS classes live in
->layout(Closure). Direct string properties on Header/Table/Footer are for view aliases only.
filter(), editable(), toggleable() are separate methods, not part of struct(). They are merged into resolveTokens() automatically.
- The filter drawer is styled, not rebuilt.
filter.flyout.* (set with ->flyout(fn (Components\Flyout $f) => ...)) drives the drawer used when config('livewire-powergrid.filter') is flyout. Its blade is shared by every theme through , so override the classes and leave alone. must carry the positioning () and stay above ; / add only the edge anchoring.
Completion checklist
See REFERENCE.md for the full struct() token map, the filter()/editable()/toggleable() shapes, the complete builder API, dot-notation token keys, and the token/view resolution order.