Display
Accordions, avatars, badges, stat cards, spinners, progress bars, skeletons, dividers, tooltips, and async containers.
Accordion
Composable expandable/collapsible content sections with four visual variants and two expand modes (single and multi).
Jan Kowalski · PESEL 80010112345 · Mazowieckie
Salary, freelance, rental income.
Internet relief, home office, child allowance.
<x-pajak::accordion> <x-pajak::accordion-item title="Personal information" subtitle="Your name and address" :open="true"> <p class="ex-p">Jan Kowalski · PESEL 80010112345 · Mazowieckie</p> </x-pajak::accordion-item> <x-pajak::accordion-item title="Income sources" badge="3 added"> <p class="ex-p">Salary, freelance, rental income.</p> </x-pajak::accordion-item> <x-pajak::accordion-item title="Deductions & reliefs"> <p class="ex-p">Internet relief, home office, child allowance.</p> </x-pajak::accordion-item> </x-pajak::accordion>
FAQ variant · multi-expand
Filled variant
PIT-11_acme_2025.pdf, ZUS_RCA_Q4.pdf
Internet relief · PLN 760
@use('Pajak\Ui\Common\Enums\AccordionVariant')
@use('Pajak\Ui\Common\Enums\AccordionMode')
<div class="ex-col ex-col--gap-lg">
<div>
<p class="ex-label">FAQ variant · multi-expand</p>
<x-pajak::accordion :variant="AccordionVariant::Faq" :mode="AccordionMode::Multi">
<x-pajak::accordion-item title="When is my tax return due?" :open="true">
For most individuals the PIT return is due by <strong>30 April</strong>.
</x-pajak::accordion-item>
<x-pajak::accordion-item title="Can I file jointly with my spouse?">
Yes — joint filing is supported for married couples.
</x-pajak::accordion-item>
</x-pajak::accordion>
</div>
<div>
<p class="ex-label">Filled variant</p>
<x-pajak::accordion :variant="AccordionVariant::Filled">
<x-pajak::accordion-item title="Documents" badge="2">
<p class="ex-p">PIT-11_acme_2025.pdf, ZUS_RCA_Q4.pdf</p>
</x-pajak::accordion-item>
<x-pajak::accordion-item title="Deductions">
<p class="ex-p">Internet relief · PLN 760</p>
</x-pajak::accordion-item>
</x-pajak::accordion>
</div>
</div>
Avatar
Circular user avatar displaying initials. Six color variants, five sizes, optional status dot and ring. Groups with overflow count via x-pajak::avatar-group.
@use('Pajak\Ui\Common\Enums\AvatarColor')
<div class="ex-row ex-row--gap-md">
<x-pajak::avatar initials="JK" />
<x-pajak::avatar initials="AW" :color="AvatarColor::Sand" />
<x-pajak::avatar initials="MN" :color="AvatarColor::Teal" />
<x-pajak::avatar initials="PR" :color="AvatarColor::Purple" />
<x-pajak::avatar initials="EZ" :color="AvatarColor::Coral" />
<x-pajak::avatar initials="TS" :color="AvatarColor::Slate" />
</div>
@use('Pajak\Ui\Common\Enums\AvatarSize')
<div class="ex-row ex-row--gap-md">
<x-pajak::avatar initials="JK" :size="AvatarSize::Xs" />
<x-pajak::avatar initials="JK" :size="AvatarSize::Sm" />
<x-pajak::avatar initials="JK" />
<x-pajak::avatar initials="JK" :size="AvatarSize::Lg" />
<x-pajak::avatar initials="JK" :size="AvatarSize::Xl" />
</div>
@use('Pajak\Ui\Common\Enums\AvatarColor')
@use('Pajak\Ui\Common\Enums\AvatarStatus')
<div class="ex-row ex-row--gap-md">
<x-pajak::avatar initials="JK" :status="AvatarStatus::Online" />
<x-pajak::avatar initials="AW" :color="AvatarColor::Sand" :status="AvatarStatus::Away" />
<x-pajak::avatar initials="MN" :color="AvatarColor::Teal" :status="AvatarStatus::Offline" />
<x-pajak::avatar initials="PR" :color="AvatarColor::Purple" :ring="true" />
</div>
@use('Pajak\Ui\Common\Enums\AvatarColor')
<x-pajak::avatar-group :overflow="4">
<x-pajak::avatar initials="JK" />
<x-pajak::avatar initials="AW" :color="AvatarColor::Sand" />
<x-pajak::avatar initials="MN" :color="AvatarColor::Teal" />
<x-pajak::avatar initials="PR" :color="AvatarColor::Purple" />
</x-pajak::avatar-group>
Badge
Small inline label for status, categories, and counts. Six color variants, three sizes, outline and dot modifiers.
@use('Pajak\Ui\Common\Enums\BadgeColor')
<div class="ex-row">
<x-pajak::badge>Default</x-pajak::badge>
<x-pajak::badge :color="BadgeColor::Success">Complete</x-pajak::badge>
<x-pajak::badge :color="BadgeColor::Warning">In Progress</x-pajak::badge>
<x-pajak::badge :color="BadgeColor::Error">Overdue</x-pajak::badge>
<x-pajak::badge :color="BadgeColor::Info">Pending</x-pajak::badge>
<x-pajak::badge :color="BadgeColor::Neutral">Draft</x-pajak::badge>
</div>
@use('Pajak\Ui\Common\Enums\BadgeColor')
@use('Pajak\Ui\Common\Enums\Size')
<div class="ex-row">
<x-pajak::badge :color="BadgeColor::Success" :dot="true">Verified</x-pajak::badge>
<x-pajak::badge :color="BadgeColor::Error" :outline="true">Rejected</x-pajak::badge>
<x-pajak::badge :color="BadgeColor::Warning" :size="Size::Sm">Small</x-pajak::badge>
<x-pajak::badge :color="BadgeColor::Info" :size="Size::Lg">Large</x-pajak::badge>
</div>
Divider
Flexible separator for horizontal rules, vertical inline separators, and labeled "or"-style dividers.
Subtle
Default
Strong
Dashed
Dotted
Labeled
@use('Pajak\Ui\Common\Enums\Divider\DividerStrength')
@use('Pajak\Ui\Common\Enums\Divider\DividerStyle')
<div class="ex-col">
<div>
<p class="ex-label">Subtle</p>
<x-pajak::divider :strength="DividerStrength::Subtle" />
</div>
<div>
<p class="ex-label">Default</p>
<x-pajak::divider />
</div>
<div>
<p class="ex-label">Strong</p>
<x-pajak::divider :strength="DividerStrength::Strong" />
</div>
<div>
<p class="ex-label">Dashed</p>
<x-pajak::divider :style="DividerStyle::Dashed" />
</div>
<div>
<p class="ex-label">Dotted</p>
<x-pajak::divider :style="DividerStyle::Dotted" />
</div>
<div>
<p class="ex-label">Labeled</p>
<x-pajak::divider :labeled="true">or</x-pajak::divider>
</div>
</div>
@use('Pajak\Ui\Common\Enums\Divider\DividerOrientation')
<div class="ex-row ex-row--no-wrap ex-row--sm-text">
<span>Invoice #142</span>
<x-pajak::divider :orientation="DividerOrientation::Vertical" />
<span>Acme Sp. z o.o.</span>
<x-pajak::divider :orientation="DividerOrientation::Vertical" />
<span>Mar 12, 2026</span>
</div>
Progress
Linear determinate progress bar. Three sizes, four colors, optional label and value display.
@use('Pajak\Ui\Common\Enums\ProgressColor')
<x-pajak::progress :value="65" label="Income sources" :show-value="true" />
<x-pajak::progress :value="100" :color="ProgressColor::Success" label="Personal details" :show-value="true" />
<x-pajak::progress :value="30" :color="ProgressColor::Warning" label="Deductions" :show-value="true" />
<x-pajak::progress :value="0" label="Review & submit" :show-value="true" />
@use('Pajak\Ui\Common\Enums\ProgressSize')
@use('Pajak\Ui\Common\Enums\ProgressColor')
<x-pajak::progress :value="65" :size="ProgressSize::Sm" />
<x-pajak::progress :value="65" :size="ProgressSize::Md" />
<x-pajak::progress :value="65" :size="ProgressSize::Lg" />
<x-pajak::progress :value="100" :color="ProgressColor::Success" :size="ProgressSize::Md" />
<x-pajak::progress :value="30" :color="ProgressColor::Warning" :size="ProgressSize::Md" />
<x-pajak::progress :value="10" :color="ProgressColor::Danger" :size="ProgressSize::Md" />
Skeleton
Shimmer placeholder shown while content is loading. Six shape variants; width and height are set via the style attribute.
@use('Pajak\Ui\Common\Enums\SkeletonShape')
<div class="ex-col ex-col--gap-sm ex-col--full">
<x-pajak::skeleton style="width: 40%" :shape="SkeletonShape::LineLg" />
<x-pajak::skeleton style="width: 100%" />
<x-pajak::skeleton style="width: 80%" />
<x-pajak::skeleton style="width: 60%" :shape="SkeletonShape::LineSm" />
</div>
@use('Pajak\Ui\Common\Enums\SkeletonShape')
<div class="ex-skeleton-profile">
<x-pajak::skeleton :shape="SkeletonShape::Circle" style="width:40px;height:40px;" />
<div class="ex-skeleton-lines">
<x-pajak::skeleton style="width: 60%" />
<x-pajak::skeleton style="width: 40%" :shape="SkeletonShape::LineSm" />
</div>
</div>
<x-pajak::skeleton :shape="SkeletonShape::Rect" style="width:100%;height:100px;margin-top:0.75rem;" />
<div class="ex-skeleton-pills">
<x-pajak::skeleton :shape="SkeletonShape::Pill" style="width:64px;" />
<x-pajak::skeleton :shape="SkeletonShape::Pill" style="width:80px;" />
</div>
Spinner
Loading indicators in three styles: arc (primary), dot pulse, and indeterminate bar.
@use('Pajak\Ui\Common\Enums\SpinnerSize')
<div class="ex-row ex-row--gap-lg">
<x-pajak::spinner :size="SpinnerSize::Xs" />
<x-pajak::spinner :size="SpinnerSize::Sm" />
<x-pajak::spinner />
<x-pajak::spinner :size="SpinnerSize::Lg" />
<x-pajak::spinner :size="SpinnerSize::Xl" />
<x-pajak::spinner :size="SpinnerSize::Xxl" />
</div>
@use('Pajak\Ui\Common\Enums\SpinnerType')
@use('Pajak\Ui\Common\Enums\SpinnerColor')
<div class="ex-col">
<div class="ex-row ex-row--gap-lg ex-row--no-wrap">
<x-pajak::spinner :type="SpinnerType::Arc" />
<x-pajak::spinner :type="SpinnerType::Arc" :color="SpinnerColor::Neutral" />
<x-pajak::spinner :type="SpinnerType::Arc" :color="SpinnerColor::Success" />
<x-pajak::spinner :type="SpinnerType::Arc" :color="SpinnerColor::Danger" />
</div>
<div class="ex-row ex-row--gap-lg ex-row--no-wrap">
<x-pajak::spinner :type="SpinnerType::Dots" />
<x-pajak::spinner :type="SpinnerType::Dots" :color="SpinnerColor::Neutral" />
</div>
<x-pajak::spinner :type="SpinnerType::Bar" />
</div>
Tooltip
Wrapper that shows a tooltip bubble on hover or keyboard focus. Pure CSS — no JavaScript required.
@use('Pajak\Ui\Common\Enums\TooltipPlacement')
@use('Pajak\Ui\Common\Enums\Variant')
<div class="ex-row ex-row--gap-lg ex-row--justify-center ex-row--padded-y">
<x-pajak::tooltip text="Upload document" :placement="TooltipPlacement::Top">
<x-pajak::button>Top</x-pajak::button>
</x-pajak::tooltip>
<x-pajak::tooltip text="Save draft" :placement="TooltipPlacement::Right">
<x-pajak::button>Right</x-pajak::button>
</x-pajak::tooltip>
<x-pajak::tooltip text="Delete record" :placement="TooltipPlacement::Bottom">
<x-pajak::button :variant="Variant::Danger">Bottom</x-pajak::button>
</x-pajak::tooltip>
<x-pajak::tooltip text="View details" :placement="TooltipPlacement::Left">
<x-pajak::button :variant="Variant::Ghost">Left</x-pajak::button>
</x-pajak::tooltip>
</div>
Async
Fetches HTML from a URL on page load and injects the response into the content area. A skeleton placeholder is shown while the request is in flight to prevent layout shift.
| Prop | Type | Default | Description |
|---|---|---|---|
url | string | — | URL to fetch HTML from (required) |
size | SpinnerSize | SpinnerSize::Md | Arc spinner size shown in the refresh overlay |
label | string | 'Loading' | Text shown next to the spinner in the refresh overlay |
| Slot | Description |
|---|---|
skeleton | Placeholder shown while the initial fetch is in flight. Should match the shape of the loaded content to prevent layout shift. |
@use('Pajak\Ui\Common\Enums\SkeletonShape')
<x-pajak::async :url="route('async.fragment')" label="Loading stats">
<x-slot:skeleton>
<div class="ex-async-skeleton">
<x-pajak::skeleton :shape="SkeletonShape::Line" style="width:30%" />
<x-pajak::skeleton :shape="SkeletonShape::LineSm" style="width:70%" />
</div>
</x-slot:skeleton>
</x-pajak::async>
@use('Pajak\Ui\Common\Enums\SkeletonShape')
<x-pajak::async :url="route('async.fragment')">
<x-slot:skeleton>
<div class="ex-async-skeleton">
<div class="ex-skeleton-profile">
<x-pajak::skeleton :shape="SkeletonShape::Circle" style="width:2.5rem;height:2.5rem;flex-shrink:0" />
<div class="ex-skeleton-lines">
<x-pajak::skeleton :shape="SkeletonShape::Line" style="width:40%" />
<x-pajak::skeleton :shape="SkeletonShape::LineSm" style="width:65%" />
</div>
</div>
</div>
</x-slot:skeleton>
</x-pajak::async>
@use('Pajak\Ui\Common\Enums\SkeletonShape')
<x-pajak::async :url="route('async.card-fragment')">
<x-slot:skeleton>
<div class="ex-async-skeleton">
<x-pajak::skeleton :shape="SkeletonShape::LineSm" style="width:25%" />
<x-pajak::skeleton :shape="SkeletonShape::Line" style="width:55%" />
<x-pajak::skeleton :shape="SkeletonShape::LineSm" style="width:80%" />
<x-pajak::skeleton :shape="SkeletonShape::LineSm" style="width:20%" />
</div>
</x-slot:skeleton>
</x-pajak::async>
Stat Card
Dashboard metric card with a label, value, icon bubble, optional trend line, and sub-text. The icon bubble colour and trend text colour are controlled by enums.
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | Small uppercase label above the value |
value | string | — | Large display value |
:color | StatCardColor | Primary | Icon bubble background colour |
:trend-direction | StatCardTrend|null | null | Colours the $trend slot text (Up = green, Down = red, Warn = amber) |
| Slot | Description |
|---|---|
$icon | Icon placed inside the colour-tinted bubble (top-right). Use <x-heroicon-o-*> at 20×20 px. |
$trend | Trend text. Colour is set by :trend-direction. |
$sub | Secondary description in muted tertiary colour. |
@use('Pajak\Ui\Common\Enums\StatCard\StatCardColor')
@use('Pajak\Ui\Common\Enums\StatCard\StatCardTrend')
<x-pajak::stat-card
label="Tenants"
value="8"
:color="StatCardColor::Success"
:trend-direction="StatCardTrend::Up"
>
<x-slot:icon>
<x-heroicon-o-home width="20" height="20" />
</x-slot:icon>
<x-slot:trend>+2 this month · 7 active, 1 trial</x-slot:trend>
<x-slot:sub>Team members across tenants</x-slot:sub>
</x-pajak::stat-card>
@use('Pajak\Ui\Common\Enums\StatCard\StatCardColor')
<x-pajak::stat-card label="Primary" value="12" :color="StatCardColor::Primary">
<x-slot:icon><x-heroicon-o-star width="20" height="20" /></x-slot:icon>
</x-pajak::stat-card>
<x-pajak::stat-card label="Success" value="34" :color="StatCardColor::Success">
<x-slot:icon><x-heroicon-o-check-circle width="20" height="20" /></x-slot:icon>
</x-pajak::stat-card>
<x-pajak::stat-card label="Warning" value="5" :color="StatCardColor::Warning">
<x-slot:icon><x-heroicon-o-exclamation-triangle width="20" height="20" /></x-slot:icon>
</x-pajak::stat-card>
<x-pajak::stat-card label="Sand" value="21" :color="StatCardColor::Sand">
<x-slot:icon><x-heroicon-o-banknotes width="20" height="20" /></x-slot:icon>
</x-pajak::stat-card>
<x-pajak::stat-card label="Error" value="2" :color="StatCardColor::Error">
<x-slot:icon><x-heroicon-o-x-circle width="20" height="20" /></x-slot:icon>
</x-pajak::stat-card>
@use('Pajak\Ui\Common\Enums\StatCard\StatCardColor')
@use('Pajak\Ui\Common\Enums\StatCard\StatCardTrend')
<x-pajak::stat-card
label="Revenue"
value="PLN 4,280"
:color="StatCardColor::Success"
:trend-direction="StatCardTrend::Up"
>
<x-slot:icon><x-heroicon-o-arrow-trending-up width="20" height="20" /></x-slot:icon>
<x-slot:trend>+18% vs last month</x-slot:trend>
</x-pajak::stat-card>
<x-pajak::stat-card
label="Churn"
value="3"
:color="StatCardColor::Error"
:trend-direction="StatCardTrend::Down"
>
<x-slot:icon><x-heroicon-o-arrow-trending-down width="20" height="20" /></x-slot:icon>
<x-slot:trend>−2 accounts this week</x-slot:trend>
</x-pajak::stat-card>
<x-pajak::stat-card
label="Pending"
value="11"
:color="StatCardColor::Warning"
:trend-direction="StatCardTrend::Warn"
>
<x-slot:icon><x-heroicon-o-clock width="20" height="20" /></x-slot:icon>
<x-slot:trend>Needs review</x-slot:trend>
</x-pajak::stat-card>
<x-pajak::stat-card
label="Users"
value="142"
:color="StatCardColor::Primary"
>
<x-slot:icon><x-heroicon-o-users width="20" height="20" /></x-slot:icon>
<x-slot:sub>No trend set</x-slot:sub>
</x-pajak::stat-card>