Skip to content
UI Brok

Loading…

No results

Browse catalog

Component Anatomy

When you run php artisan ui:add button, the registry copies the component's Blade file into resources/views/components/ui/ inside your project. From that moment it is your code — an anonymous Blade component like any other. No runtime dependency on this package is required; you can read, edit, and version it freely.

Blade API

Every component is invoked with the standard Laravel anonymous-component syntax. Props control variant, size, and behaviour; the default slot carries content.

resources/views/example.blade.php Blade
<x-ui.button variant="secondary" size="sm">
    Save changes
</x-ui.button>

Component names use the neutral <x-ui.*> prefix. The Jml/jml brand is confined to the package namespace, registry, and docs site — nothing that gets copied into your app leaks that name. This keeps the installed code portable and rebrandable.

Props, slots & attributes

Anonymous Blade components declare their API through @props() at the top of the file. Anything not in that list is collected into $attributes and forwarded to the root element via $attributes->merge().

resources/views/components/ui/button.blade.php Blade
{{-- @props declares prop names and optional defaults --}}
@props([
    'variant' => 'default',
    'size'    => 'md',
])

@php
    $styles = require base_path(config('ui.component_path', 'resources/views/components/ui').'/_styles.php');
    $button = $styles['button'];
    $classes = trim($button['base'].' '.($button['variants'][$variant] ?? $button['variants']['default']).' '.($button['sizes'][$size] ?? $button['sizes']['md']));
@endphp

<button
    {{ $attributes->merge(['class' => $classes]) }}
    data-slot="button"
>
    {{ $slot }}
</button>

This means you can pass any HTML attribute — disabled, id, aria-*, extra classes — and it lands on the root element automatically:

resources/views/example.blade.php Blade
{{-- Extra attributes you pass flow through $attributes->merge() onto the root element --}}
<x-ui.button variant="primary" class="w-full" disabled>
    Submit
</x-ui.button>

Some components expose named slots for regions like headers, footers, or icons. Named slots are documented on each component's page. Because the file lives in your project, you can inspect — or modify — the slot structure at any time.

The data-slot contract

Every primitive exposes a stable data-slot attribute on its root element (and on significant internal regions, e.g. data-slot="card-header"). This gives you a reliable selector that survives class name refactors:

resources/views/example.blade.php Blade
{{-- Target data-slot in CSS or Alpine expressions --}}
<style>
[data-slot="button"]:focus-visible {
    outline: 2px solid hsl(var(--ring));
}
</style>

{{-- Or in Playwright / Pest browser tests --}}
$page->locator('[data-slot="card-header"]')->assertVisible();

Components also use logical CSS properties (ms-, me-, ps-, pe-, …) so that layout mirrors automatically when the page direction is dir="rtl". See the RTL guide for enabling bidirectional support.

Styling & tokens

Components reference semantic design tokens — Tailwind utilities backed by CSS custom properties defined in ui.css. These tokens adapt automatically to light/dark mode and to admin vs customer surfaces without any per-component changes.

resources/views/components/ui/card.blade.php Blade
{{-- Correct: semantic design tokens --}}
<div class="bg-card text-card-foreground border border-border rounded-radius-md">
    <p class="text-muted-foreground text-sm">Helper text</p>
</div>

{{-- Wrong: raw colour utilities (test-enforced; will fail BlockRenderTest) --}}
{{-- <div class="bg-white text-gray-900 border-gray-200"> --}}

Warning

Raw colour utilities (bg-blue-500, text-gray-900) and inline hsl(…) styles are test-enforced failures. BlockRenderTest will reject a block that ships raw colours — use semantic tokens only.

Common token names you will encounter:

  • bg-background / text-foreground — page surface
  • bg-card / text-card-foreground — card surface
  • bg-primary / text-primary-foreground — primary action
  • bg-muted / text-muted-foreground — de-emphasised content
  • border-input — form field borders
  • ring — focus ring colour

Variant and size class maps belong in packages/ui/resources/views/components/ui/_styles.php, not inlined across every Blade template. Installed components load the copied resources/views/components/ui/_styles.php recipe file so class strings remain Tailwind-scannable and easy to override:

resources/views/components/ui/badge.blade.php Blade
{{-- Variant maps live in packages/ui/resources/views/components/ui/_styles.php --}}
{{-- Components load the copied recipe file directly, so the package is not needed at runtime. --}}
@props(['variant' => 'default', 'size' => 'md'])

@php
    $styles = require base_path(config('ui.component_path', 'resources/views/components/ui').'/_styles.php');
    $badge = $styles['badge'];
    $classes = trim($badge['base'].' '.($badge['variants'][$variant] ?? $badge['variants']['default']).' '.($badge['sizes'][$size] ?? $badge['sizes']['md']));
@endphp

<div
    {{ $attributes->merge(['class' => $classes]) }}
    data-slot="badge"
>{{ $slot }}</div>

Full token definitions — including radius, typography, shadow, motion, and z-index scales — live in packages/ui/resources/css/ui.css. See the Theming guide to customise or extend them.

Where files land & editing

After ui:add, each component lands at the path its registry manifest declares under target. For Blade components that is always inside resources/views/components/ui/; for JavaScript behaviour modules it is resources/js/ui/, with an import appended to resources/js/ui/index.js.

Note

Once a file is copied into your project it is your code. There is no runtime link back to this registry. Edit props, restructure slots, swap tokens, rename classes — the component does exactly what your file says.

Running php artisan ui:update button later is edit-aware: the CLI shows a diff between the upstream version and your local file before writing anything, so your customisations are never silently overwritten. See the Updating guide for the full update and diff workflow.

Files written by ui:add button
resources/views/components/ui/button.blade.php
resources/js/ui/index.js (import appended if component has JS)
ui-lock.json (version record; commit this)