Skip to content
UI Brok

Loading…

No results

Browse catalog

Theming

All components read CSS variables defined in resources/css/ui.css. Re-theming never requires editing component markup — you change tokens only. Light and dark themes, plus admin and customer surfaces, are all expressed as token layers that cascade via standard CSS specificity.

Semantic color tokens

primary
secondary
muted
destructive
success
warning

Every swatch resolves to a CSS variable. Editing resources/css/ui.css re-themes the whole system.

How re-theming works

Existing themes store bare HSL channels ("H S% L%"). Each semantic mapping also accepts a full-color override such as --primary-color: oklch(...), so teams can migrate to perceptually uniform OKLCH without breaking an existing HSL theme. Tailwind opacity modifiers such as bg-primary/90 continue to use the same semantic utility.

To swap your brand color, open resources/css/ui.css and change the --primary channel values in both :root (light) and .dark:

resources/css/ui.css CSS
/* resources/css/ui.css — change --primary for light + dark */

:root {
    /* Default: dark navy */
    --primary: 222.2 47.4% 11.2%;
    --primary-foreground: 210 40% 98%;

    /* Example: swap to indigo */
    /* --primary: 243 75% 59%; */
    /* --primary-foreground: 210 40% 98%; */
}

.dark {
    /* Default dark: near-white */
    --primary: 210 40% 98%;
    --primary-foreground: 222.2 47.4% 11.2%;

    /* Example: swap to indigo (lighter for dark bg) */
    /* --primary: 243 75% 75%; */
    /* --primary-foreground: 222.2 47.4% 11.2%; */
}

Note

Only change the channel values (H S% L%) — the hsl() wrapper is already provided by the @theme inline block. Never write hsl(...) directly into a token value; the system will double-wrap it.

Token layers

ui.css is structured as five explicit layers that cascade in order:

Tailwind variants

Three custom variants are registered: dark, surface-admin, and surface-customer. These are the CSS hooks that allow Tailwind utilities to respond to the active theme and surface.

@@theme inline — utility mapping

Maps every token onto a Tailwind utility via @theme inline. The inline keyword is required: it inlines hsl(var(--primary)) into each utility so the variable resolves at the element — not once at :root. This is what makes descendant .dark / [data-surface] overrides work correctly. This layer also defines the radius, typography, shadow, and motion easing scales.

:root — light theme

All primitive token values for the light theme, including color channels, the base --radius (default 0.5rem), motion durations (--duration-fast/base/slow), and the z-index scale.

.dark — dark theme overrides

Overrides the color channels for dark mode. Applied when the .dark class is present on any ancestor element (Tailwind's class-based dark mode strategy).

[data-surface] — surface overrides

[data-surface="admin"] and [data-surface="customer"] override product-context tokens on any subtree. The admin surface has a denser, cooler aesthetic with a blue primary and a tighter radius (0.375rem); the customer surface keeps a warmer orange primary. Light and dark mode remain controlled by .dark, not by the surface. Add data-surface="admin" to your layout wrapper to activate it.

Token reference

The tokens below are the real custom properties defined in ui.css. Tailwind utilities are derived automatically: e.g. --primarybg-primary / text-primary / border-primary.

Color tokens

Token Pair Purpose
--background --foreground Page background and body text
--card --card-foreground Card surfaces and their text
--popover --popover-foreground Dropdown, tooltip and popover surfaces
--primary --primary-foreground Brand accent — buttons, active states, focus rings
--secondary --secondary-foreground Secondary actions and subtle fills
--muted --muted-foreground De-emphasized backgrounds and helper text
--accent --accent-foreground Hover fills on ghost/icon buttons
--destructive --destructive-foreground Errors, delete actions, danger badges
--success --success-foreground Confirmations, success badges
--warning --warning-foreground Warnings and advisory notices
--info --info-foreground Informational alerts and callouts
--border --input Dividers and form field borders
--ring Keyboard focus ring color

Radius scale

Token Value Utility
--radius 0.5rem (base) Edit this single var to shift all rounded-* utilities
--radius-sm calc(var(--radius) - 4px) rounded-sm
--radius-md calc(var(--radius) - 2px) rounded-md
--radius-lg var(--radius) rounded-lg
--radius-xl calc(var(--radius) + 4px) rounded-xl

Changing --radius in :root shifts the whole scale proportionally. Surface overrides can pin a different base radius (e.g. admin uses 0.375rem).

Shadow scale

Utility Description
shadow-xs Subtle lift — inputs, inline chips
shadow-sm Card resting state
shadow-md Dropdowns, popovers
shadow-lg Modals, dialogs

All shadow values use hsl(var(--foreground) / opacity) so they automatically invert between light and dark themes.

Motion

Token Value
--duration-fast 120ms
--duration-base 180ms
--duration-slow 240ms
--ease-standard cubic-bezier(0.2,0,0,1)
--ease-emphasized cubic-bezier(0.3,0,0,1)

Z-index scale

Token Value
--z-dropdown 1000
--z-sticky 1100
--z-overlay 1200
--z-modal 1300
--z-popover 1400
--z-toast 1500
--z-tooltip 1600

Typography scale

Type sizes and weights are mapped in @theme inline. The step values match Tailwind's defaults so existing utility classes work unchanged.

Token Size / Line-height
--text-xs 0.75rem / 1rem
--text-sm 0.875rem / 1.25rem
--text-base 1rem / 1.5rem
--text-lg 1.125rem / 1.75rem
--text-xl 1.25rem / 1.75rem
--text-2xl 1.5rem / 2rem

Note

Components must use semantic tokens — never raw colours. Using a raw Tailwind color utility such as bg-blue-500 or an inline hsl(...) style inside a component or block is forbidden and is enforced by BlockRenderTest (test_block_never_ships_raw_colour_utilities). All color expression must go through the semantic token layer so that surfaces, dark mode, and future theme changes are respected automatically.

Using surfaces

Surfaces let you scope a completely different token set to a subtree without touching the global theme. Add the data-surface attribute to any wrapper element.

resources/views/layouts/app.blade.php Blade
{{-- Admin surface: blue primary, 0.375rem radius --}}
<div data-surface="admin">
    <x-ui.sidebar />
    <x-ui.data-table />
</div>

{{-- Customer surface: orange primary, 0.5rem radius --}}
<div data-surface="customer">
    <x-ui.hero />
    <x-ui.pricing-card />
</div>

Surface overrides apply to the entire subtree — all descendant components automatically pick up the correct --primary, --radius, and other tokens without any changes to component markup. Admin blocks must pin to the admin surface; this is enforced by the block test suite.

Density and component metrics

Control heights, horizontal padding, field gaps, card padding, dialog padding, and section rhythm are semantic tokens. Set data-density="compact" or data-density="comfortable" on any subtree to change its density without editing individual components.

resources/views/layouts/app.blade.php Blade
<main data-density="compact">
    <x-ui.button>Compact action</x-ui.button>
    <x-ui.input placeholder="Compact input" />
</main>

<section data-density="comfortable">
    <x-ui.button>Comfortable action</x-ui.button>
</section>

The five physical control tiers are 32, 36, 40, 44, and 48px. Component APIs expose semantic sm / md / lg sizes; the normal md control resolves to a 44px-friendly target. Density remaps those semantic sizes to the fixed tiers instead of inventing new heights.

Layout grammar

Build screens from constrained layout primitives. Their props map to literal, discoverable classes and unknown values fall back safely, so feature code never needs arbitrary pixel gaps.

resources/views/dashboard.blade.php Blade
<x-ui.section space="lg" surface="muted">
    <x-ui.container size="lg">
        <x-ui.stack gap="lg">
            <x-ui.prose :centered="false">
                <h2>Workspace activity</h2>
                <p>Readable copy wraps safely, including long translated strings and URLs.</p>
            </x-ui.prose>

            <x-ui.grid cols="3" gap="md">
                <x-ui.surface>…</x-ui.surface>
                <x-ui.surface>…</x-ui.surface>
                <x-ui.surface>…</x-ui.surface>
            </x-ui.grid>

            <x-ui.cluster gap="sm" justify="end">
                <x-ui.button variant="outline">Cancel</x-ui.button>
                <x-ui.button>Save</x-ui.button>
            </x-ui.cluster>
        </x-ui.stack>
    </x-ui.container>
</x-ui.section>

Use stack for flow, cluster for wrapping inline groups, grid for columns, container and section for page rhythm, surface for panels, prose for readable text, and bleed scroll="true" for intentionally wide tables or code.

OKLCH overrides

Prefer OKLCH for new brand palettes. Keep values inside the sRGB gamut unless you also provide a wider-gamut enhancement, and verify foreground/background pairs in both light and dark modes.

resources/css/ui.css CSS
:root {
    /* Full-color values override the legacy HSL channel fallback. */
    --primary-color: oklch(0.637 0.237 25.331);
    --primary-foreground-color: oklch(0.985 0 0);
}

@media (color-gamut: p3) {
    :root {
        /* Optional wider-gamut enhancement after testing clipping. */
        --primary-color: oklch(0.637 0.26 25.331);
    }
}

Custom theme example

To build a green-primary, fully rounded theme, override only the tokens you need. Every other value inherits from the defaults.

resources/css/ui.css CSS
/* resources/css/ui.css — custom brand theme */

:root {
    /* Brand: forest green */
    --primary: 142 71% 45%;
    --primary-foreground: 0 0% 100%;

    /* Rounder corners */
    --radius: 0.75rem;

    /* Warmer muted tones */
    --muted: 120 10% 94%;
    --muted-foreground: 120 6% 45%;
}

.dark {
    --primary: 142 65% 55%;
    --primary-foreground: 142 10% 10%;
}

Note

You only need to declare the tokens you want to change. Unset tokens fall through to the :root defaults. The CSS variables remain the editable theme source. Curated presets can also be installed as editable registry layers with php artisan ui:add themes/zinc; custom Theme Generator JSON exports install with php artisan ui:theme ./theme.json --name=my-brand.