Blade Components
Components are reusable UI elements. All components from views/components/ are automatically available via the <x-name> syntax.
Button
Button Types
{{-- Basic types --}}
<x-button type="accent">Accent (main)</x-button>
<x-button type="primary">Primary</x-button>
<x-button type="success">Success</x-button>
<x-button type="error">Error / Danger</x-button>
<x-button type="warning">Warning</x-button>
{{-- Outline variants --}}
<x-button type="outline-accent">Outline Accent</x-button>
<x-button type="outline-primary">Outline Primary</x-button>
<x-button type="outline-error">Outline Error</x-button>Sizes
<x-button size="tiny">Tiny</x-button>
<x-button size="small">Small</x-button>
<x-button size="medium">Medium (default)</x-button>
<x-button size="large">Large</x-button>Link Button
<x-button href="/profile">Go to Profile</x-button>
<x-button href="/external" target="_blank">External Link</x-button>States
{{-- Disabled --}}
<x-button :disabled="true">Disabled</x-button>
{{-- Form Submit --}}
<x-button :submit="true" type="accent">Submit</x-button>
{{-- With loading indicator --}}
<x-button :withLoading="true">Loading...</x-button>With Icon
<x-button type="accent">
<x-icon path="ph.regular.check" />
Save
</x-button>
<x-button type="error">
<x-icon path="ph.regular.trash" />
Delete
</x-button>
{{-- Icon only --}}
<x-button type="outline-primary" size="small">
<x-icon path="ph.regular.gear" />
</x-button>HTMX Integration
{{-- Button with HTMX navigation --}}
<x-button href="/page" :swap="true" swapTarget="#main">
Load
</x-button>Additional Attributes
<x-button
type="accent"
class="my-custom-class"
id="submit-btn"
data-action="submit"
onclick="handleClick()">
Button
</x-button>Card
Simple Card
<x-card>
<p>Card content</p>
</x-card>With Title
<x-card title="Title">
<p>Content</p>
</x-card>
<x-card title="Title" subtitle="Subtitle">
<p>Content</p>
</x-card>With Custom Header (via slot)
<x-card>
<x-slot:header>
<div class="flex-between">
<h5>Users</h5>
<x-button size="small" type="accent">Add</x-button>
</div>
</x-slot:header>
<p>List of users...</p>
</x-card>With Footer
<x-card title="Form">
<form>
<input type="text" class="input" placeholder="Name">
</form>
<x-slot:footer>
<div class="flex-between">
<x-button type="outline-primary">Cancel</x-button>
<x-button type="accent" :submit="true">Save</x-button>
</div>
</x-slot:footer>
</x-card>Modifiers
{{-- Without padding --}}
<x-card :withoutPadding="true">
<img src="/image.jpg" style="width: 100%">
</x-card>
{{-- Custom classes --}}
<x-card
headerClass="bg-accent-100"
bodyClass="p-4"
class="shadow-lg">
...
</x-card>Alert
Types
<x-alert type="success">Operation completed successfully!</x-alert>
<x-alert type="error">An error occurred</x-alert>
<x-alert type="warning">Warning! Check data</x-alert>
<x-alert type="info">Information message</x-alert>Options
{{-- Without close button --}}
<x-alert type="info" :withClose="false">
This message cannot be closed
</x-alert>
{{-- Borders only (no fill) --}}
<x-alert type="success" :onlyBorders="true">
Alert with border
</x-alert>
{{-- Custom icon --}}
<x-alert type="info" icon="ph.regular.bell">
Notification
</x-alert>Complex Content
<x-alert type="warning">
<strong>Important!</strong>
<p>Your session will expire soon.</p>
<x-button size="small" type="warning">Extend</x-button>
</x-alert>Modal
Basic Usage
{{-- Open button --}}
<x-button data-modal-open="my-modal">Open Modal</x-button>
{{-- Modal window --}}
<x-modal id="my-modal" title="Title">
<p>Modal content</p>
</x-modal>With Footer
<x-modal id="confirm-modal" title="Confirmation">
<p>Are you sure you want to delete?</p>
<x-slot:footer>
<x-button type="outline-primary" data-modal-close="confirm-modal">
Cancel
</x-button>
<x-button type="error">
Delete
</x-button>
</x-slot:footer>
</x-modal>Sizes
<x-modal id="small" title="Small" size="sm">...</x-modal>
<x-modal id="default" title="Normal">...</x-modal>
<x-modal id="large" title="Large" size="lg">...</x-modal>
<x-modal id="xlarge" title="Extra Large" size="xl">...</x-modal>Lazy Content Loading
<x-modal
id="lazy-modal"
title="Data"
loadUrl="/api/modal-content"
loadTarget="#modal-body">
<div id="modal-body">
<div class="skeleton" style="height: 200px;"></div>
</div>
</x-modal>Closing via JavaScript
// Open
openModal('my-modal');
// Close
closeModal('my-modal');Icons
Uses Phosphor Icons — https://phosphoricons.com/
Variants
{{-- Regular (default) --}}
<x-icon path="ph.regular.user" />
<x-icon path="ph.regular.gear" />
<x-icon path="ph.regular.bell" />
<x-icon path="ph.regular.check" />
<x-icon path="ph.regular.x" />
{{-- Bold --}}
<x-icon path="ph.bold.check-bold" />
<x-icon path="ph.bold.x-bold" />
{{-- Fill --}}
<x-icon path="ph.fill.star-fill" />
<x-icon path="ph.fill.heart-fill" />
{{-- Light --}}
<x-icon path="ph.light.user-light" />
{{-- Thin --}}
<x-icon path="ph.thin.house-thin" />
{{-- Duotone --}}
<x-icon path="ph.duotone.bell-duotone" />With Parameters
<x-icon
path="ph.regular.gear"
width="24"
height="24"
class="text-muted" />Font Awesome
<x-icon path="fa.solid.home" />
<x-icon path="fa.regular.envelope" />
<x-icon path="fa.brands.github" />Link
<x-link href="/page">Regular link</x-link>
<x-link href="/page" type="accent">Accent</x-link>
<x-link href="/page" type="primary">Primary</x-link>
<x-link href="/page" type="error">Error</x-link>
<x-link href="/page" type="success">Success</x-link>Forms
Field (field wrapper)
<x-forms.field>
<x-slot:label>
<x-forms.label for="email" :required="true">Email</x-forms.label>
</x-slot:label>
<input type="email" name="email" id="email" class="input">
</x-forms.field>Label
<x-forms.label for="name">Name</x-forms.label>
<x-forms.label for="email" :required="true">Email</x-forms.label>Complete Form
<form method="POST" action="/submit">
@csrf
<x-forms.field>
<x-slot:label>
<x-forms.label for="name" :required="true">Name</x-forms.label>
</x-slot:label>
<input type="text" name="name" id="name" class="input" required>
</x-forms.field>
<x-forms.field>
<x-slot:label>
<x-forms.label for="email" :required="true">Email</x-forms.label>
</x-slot:label>
<input type="email" name="email" id="email" class="input" required>
</x-forms.field>
<x-forms.field>
<x-slot:label>
<x-forms.label for="message">Message</x-forms.label>
</x-slot:label>
<textarea name="message" id="message" class="input" rows="4"></textarea>
</x-forms.field>
<x-button type="accent" :submit="true">Send</x-button>
</form>Creating Your Own Component
1. Create a File
views/components/stat-card.blade.php:
@props([
'title',
'value',
'icon' => null,
'change' => null,
'changeType' => 'neutral'
])
<div {{ $attributes->merge(['class' => 'stat-card']) }}>
@if ($icon)
<div class="stat-card__icon">
<x-icon :path="$icon" />
</div>
@endif
<div class="stat-card__content">
<span class="stat-card__title">{{ $title }}</span>
<span class="stat-card__value">{{ $value }}</span>
@if ($change)
<span class="stat-card__change stat-card__change--{{ $changeType }}">
{{ $change }}
</span>
@endif
</div>
</div>2. Add Styles
assets/sass/components/_stat-card.scss:
.stat-card {
display: flex;
gap: var(--space-md);
padding: var(--space-lg);
background: var(--secondary);
border-radius: var(--border1);
&__icon {
display: flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
background: var(--accent-100);
color: var(--accent);
border-radius: var(--border05);
}
&__title {
font-size: var(--small);
color: var(--text-muted);
}
&__value {
font-size: var(--h3);
font-weight: 700;
}
&__change {
font-size: var(--small);
&--positive { color: var(--success); }
&--negative { color: var(--error); }
&--neutral { color: var(--text-muted); }
}
}3. Use It
<x-stat-card
title="Users"
value="12,345"
icon="ph.regular.users"
change="+12%"
changeType="positive" />Overriding Standard Component
To change a standard component, create a file with the same name:
views/components/button.blade.php → replaces <x-button>
views/components/card.blade.php → replaces <x-card>
views/components/modal.blade.php → replaces <x-modal>Working with Attributes
$attributes
{{-- Merge classes --}}
<div {{ $attributes->merge(['class' => 'my-class']) }}>
{{-- Exclude attributes --}}
<div {{ $attributes->except(['class']) }}>
{{-- Only specific attributes --}}
<div {{ $attributes->only(['id', 'class']) }}>
{{-- Check for existence --}}
@if ($attributes->has('disabled'))
...
@endif
{{-- Get value --}}
{{ $attributes->get('data-id', 'default') }}@class directive
<div @class([
'card',
'card--featured' => $featured,
'card--disabled' => $disabled,
])>Slots
Named Slots
{{-- Component --}}
@props(['title'])
<div class="card">
@if (isset($header))
<div class="card-header">{{ $header }}</div>
@endif
<div class="card-body">{{ $slot }}</div>
@if (isset($footer))
<div class="card-footer">{{ $footer }}</div>
@endif
</div>{{-- Usage --}}
<x-card>
<x-slot:header>
<h5>Title</h5>
</x-slot:header>
<p>Main content</p>
<x-slot:footer>
<x-button>Action</x-button>
</x-slot:footer>
</x-card>