Add cards, lists, dialogs, sheets and the rest of M3's containment
tests / lint (push) Failing after 2m6s
tests / feature (8.4) (push) Successful in 1m3s
tests / feature (8.5) (push) Successful in 1m7s
tests / browser (safari, webkit) (push) Successful in 3m14s
tests / browser (chrome, chromium) (push) Successful in 2m9s
tests / browser (firefox, firefox) (push) Successful in 2m29s

<x-card> (filled, elevated, outlined), <x-list> and <x-list-item>
(plain or M3 Expressive's segmented list), <x-divider>, <x-collapse> on
<details>, <x-modal> on the native <dialog>, <x-drawer> as a side sheet or
list-detail pane, and <x-bottom-sheet> with drag to dismiss. Dialogs and
sheets bind to a Livewire flag or id and write back false or null on close,
or use the surrounding Alpine scope. Rows open from anywhere on them through
data-list-row and data-list-open. DesignGuard now also reports Blade
directives written inside a component tag, where they do not compile.

Browser test helpers wait for a complete document with Alpine and Livewire
running: in Firefox, networkidle alone could return before a repeated visit
had loaded.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V9NnLxnPp8vaaurb3Z1MFy
This commit is contained in:
Andreas Reinhold / reini
2026-09-13 07:28:05 +02:00
co-authored by Claude Opus 5
parent e57063b0f4
commit fef20a9178
30 changed files with 1490 additions and 16 deletions
+1
View File
@@ -16,5 +16,6 @@
@include('livewire-material::showcase.sections.menus')
@include('livewire-material::showcase.sections.communication')
@include('livewire-material::showcase.sections.progress')
@include('livewire-material::showcase.sections.containment')
</main>
@endsection
+5 -5
View File
@@ -14,16 +14,16 @@
</head>
<body class="min-h-screen bg-surface font-sans text-on-surface antialiased">
<header class="sticky top-0 z-10 border-b border-divider bg-surface-container">
<div class="mx-auto flex max-w-6xl flex-wrap items-center gap-x-6 gap-y-2 px-4 py-3">
<a href="{{ route('livewire-material.showcase') }}" class="type-title-lg">Livewire Material</a>
<div class="mx-auto flex max-w-6xl items-center gap-x-6 px-4 py-3">
<a href="{{ route('livewire-material.showcase') }}" class="shrink-0 type-title-lg max-sm:hidden">Livewire Material</a>
<nav class="flex flex-wrap gap-x-4 gap-y-1 type-label-lg text-on-surface-variant" aria-label="Sections">
@foreach (['colour' => 'Colour', 'type' => 'Type', 'shape' => 'Shape', 'elevation' => 'Elevation', 'motion' => 'Motion', 'icons' => 'Icons', 'buttons' => 'Buttons', 'menus' => 'Menus', 'communication' => 'Communication', 'progress' => 'Progress'] as $anchor => $section)
<nav class="-my-2 flex min-w-0 flex-1 gap-x-4 overflow-x-auto py-2 whitespace-nowrap type-label-lg text-on-surface-variant [scrollbar-width:none]" aria-label="Sections">
@foreach (['colour' => 'Colour', 'type' => 'Type', 'shape' => 'Shape', 'elevation' => 'Elevation', 'motion' => 'Motion', 'icons' => 'Icons', 'buttons' => 'Buttons', 'menus' => 'Menus', 'communication' => 'Communication', 'progress' => 'Progress', 'containment' => 'Containment'] as $anchor => $section)
<a href="#{{ $anchor }}" class="rounded-corner-xs hover:text-on-surface focus-ring">{{ $section }}</a>
@endforeach
</nav>
<div class="ms-auto flex rounded-corner-full border border-outline" role="group" aria-label="Theme" x-data x-cloak>
<div class="ms-auto flex shrink-0 rounded-corner-full border border-outline" role="group" aria-label="Theme" x-data x-cloak>
@foreach (['light' => 'Light', 'dark' => 'Dark', 'system' => 'System'] as $choice => $label)
<button
type="button"
@@ -0,0 +1,126 @@
@php
$examples = [
'Cards' => <<<'BLADE'
<div class="grid w-full gap-4 md:grid-cols-3">
<x-card title="Filled" subtitle="surface-container-highest">
The default card.
<x-slot:actions><x-button label="Open" /></x-slot:actions>
</x-card>
<x-card title="Elevated" subtitle="surface-container-low, elevation 1" variant="elevated">
<x-slot:menu><x-button icon="more_vert" tooltip="More" /></x-slot:menu>
For content that floats a little.
</x-card>
<x-card title="Outlined" subtitle="an outline-variant edge" variant="outlined" separator>
Quiet, on the surface itself.
</x-card>
</div>
BLADE,
'A card that opens from anywhere on it' => <<<'BLADE'
<x-card variant="outlined" data-list-row class="w-full max-w-sm">
<a href="#containment" data-list-open class="type-title-md">holiday-photos.zip</a>
<p class="mt-1 text-on-surface-variant">248 MB · expires in 3 days</p>
<x-slot:actions>
<x-button label="Copy link" icon="content_copy" x-on:click="materialToast('Link copied', { type: 'success' })" />
</x-slot:actions>
</x-card>
BLADE,
'Lists' => <<<'BLADE'
<div class="grid w-full gap-6 md:grid-cols-2">
<x-list dividers label="Files">
<x-list-item title="holiday-photos.zip" description="248 MB" icon="folder_zip" trailing="3 days" />
<x-list-item title="contract.pdf" overline="Password protected" description="1.2 MB · downloaded twice" icon="picture_as_pdf" trailing="1 hour" />
<x-list-item title="Settings" icon="settings" icon-right="chevron_right" link="#containment" />
</x-list>
<x-list segmented label="Recipients">
<x-list-item title="Anna Müller" description="Downloaded yesterday" avatar="AM" :selected="true" />
<x-list-item title="Ben Keller" description="Not opened yet" avatar="BK" link="#containment" />
<x-list-item title="Chiara Rossi" avatar="CR">
<x-slot:end><x-button icon="more_vert" tooltip="More" /></x-slot:end>
</x-list-item>
</x-list>
</div>
BLADE,
'Dividers and collapse' => <<<'BLADE'
<div class="w-full space-y-4">
<x-collapse title="How long do links last?" icon="schedule" open>
Until the expiry you chose, from one hour to thirty days.
</x-collapse>
<x-divider />
<x-collapse title="Can recipients upload files?" variant="filled">
No. They can only download what you shared.
</x-collapse>
<div class="flex h-10 items-center gap-4"><span>Left</span><x-divider vertical /><span>Right</span></div>
</div>
BLADE,
'Dialogs' => <<<'BLADE'
<div x-data="{ open: false }">
<x-button label="Basic dialog" variant="tonal" x-on:click="open = true" />
<x-modal title="Delete this share?" subtitle="Recipients lose access at once. This cannot be undone.">
<x-slot:actions>
<x-button label="Cancel" x-on:click="close()" />
<x-button label="Delete" danger x-on:click="close()" />
</x-slot:actions>
</x-modal>
</div>
<div x-data="{ open: false }">
<x-button label="With a hero icon" variant="tonal" x-on:click="open = true" />
<x-modal title="Reset the password?" icon="lock_reset">
Recipients with the old password will need the new one.
<x-slot:actions>
<x-button label="Cancel" x-on:click="close()" />
<x-button label="Reset" x-on:click="close()" />
</x-slot:actions>
</x-modal>
</div>
<div x-data="{ open: false }">
<x-button label="Full screen on a phone" variant="tonal" x-on:click="open = true" />
<x-modal title="Share settings" fullscreen>
A form that needs the room.
<x-slot:actions><x-button label="Save" variant="filled" x-on:click="close()" /></x-slot:actions>
</x-modal>
</div>
BLADE,
'Sheets' => <<<'BLADE'
<div x-data="{ open: false }">
<x-button label="Side sheet" variant="tonal" x-on:click="open = true" />
<x-drawer title="Share details" subtitle="holiday-photos.zip" with-close-button separator>
<x-list>
<x-list-item title="Size" trailing="248 MB" />
<x-list-item title="Expires" trailing="in 3 days" />
<x-list-item title="Downloads" trailing="12" />
</x-list>
<x-slot:actions><x-button label="Delete" icon="delete" x-on:click="close()" /></x-slot:actions>
</x-drawer>
</div>
<div x-data="{ open: false }">
<x-button label="Bottom sheet" variant="tonal" x-on:click="open = true" />
<x-bottom-sheet title="Share via">
<x-list>
<x-list-item title="Copy link" icon="content_copy" />
<x-list-item title="Email" icon="mail" />
<x-list-item title="QR code" icon="qr_code_2" />
</x-list>
</x-bottom-sheet>
</div>
BLADE,
];
@endphp
<section id="containment" class="scroll-mt-24 space-y-6">
<h2 class="type-headline-md">Containment</h2>
<p class="max-w-3xl type-body-md text-on-surface-variant">
<code>&lt;x-card&gt;</code>, <code>&lt;x-list&gt;</code> and <code>&lt;x-list-item&gt;</code>, <code>&lt;x-divider&gt;</code>, <code>&lt;x-collapse&gt;</code>,
<code>&lt;x-modal&gt;</code>, <code>&lt;x-drawer&gt;</code> and <code>&lt;x-bottom-sheet&gt;</code>.
</p>
@foreach ($examples as $title => $code)
<x-showcase::example :$title :$code />
@endforeach
</section>