Show each canonical layout on the showcase's Layout page

Plan step 35: the Layout section gains a working example of list-detail,
supporting pane and feed, and one of surfaces, stacks, rows and grids,
written with the layout components and md-* text classes only; the panes
table names <x-list-detail> and <x-supporting-pane>. The showcase's own
Tailwind stays for step 38.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
Andreas Reinhold / reini
2026-09-14 14:52:01 +02:00
co-authored by Claude Opus 5
parent 0013e0154d
commit d65e25b208
2 changed files with 115 additions and 2 deletions
@@ -1,6 +1,92 @@
{{-- M3's window size classes, live: the one this window is in, what each one changes, and the
variants that key on them. The five cards light up through the variants themselves, so the
page proves the compiled breakpoints rather than describing them. --}}
page proves the compiled breakpoints rather than describing them.
Then the layout components, and an example of each canonical layout — list-detail, supporting
pane, feed — written as an application writes them, with the layout components and the md-*
text classes and no Tailwind. Resize the window to watch each one change at its breakpoints. --}}
@php
$examples = [
'List-detail' => <<<'BLADE'
<x-surface level="surface" corner="lg" outlined x-data="{ message: null }" style="overflow: clip">
<x-list-detail x-model="message">
<x-slot:list>
<x-pane title="Inbox" heading="h3" :sticky="false">
<x-list label="Messages">
<x-list-item title="Anna Müller" description="The photos from Saturday" avatar="AM" link="#example-list-detail" no-wire-navigate x-on:click.prevent="selected = 1" x-bind:aria-current="selected === 1 ? 'true' : null" />
<x-list-item title="Ben Keller" description="Contract, second draft" avatar="BK" link="#example-list-detail" no-wire-navigate x-on:click.prevent="selected = 2" x-bind:aria-current="selected === 2 ? 'true' : null" />
<x-list-item title="Chiara Rossi" description="Train times for Friday" avatar="CR" link="#example-list-detail" no-wire-navigate x-on:click.prevent="selected = 3" x-bind:aria-current="selected === 3 ? 'true' : null" />
</x-list>
</x-pane>
</x-slot:list>
<x-slot:detail>
<x-pane heading="h3" back :sticky="false">
<x-stack gap="space200">
<p class="md-type-body-lg md-ink-variant" x-show="! selected">Select a message to read it.</p>
<p class="md-type-headline-sm" x-show="selected" x-text="{ 1: 'The photos from Saturday', 2: 'Contract, second draft', 3: 'Train times for Friday' }[selected]"></p>
<p class="md-type-body-lg" x-show="selected">Below 840px the list gives way to this message, with a back button that returns you — and your focus — to the list. From 840px both panes show, the list 360px wide and 412px from 1200px.</p>
</x-stack>
</x-pane>
</x-slot:detail>
</x-list-detail>
</x-surface>
BLADE,
'Supporting pane' => <<<'BLADE'
<x-supporting-pane label="Comments" compact="sheet">
<x-slot:main>
<x-surface level="surface" corner="lg" style="overflow: clip">
<x-pane title="Trip proposal" subtitle="Draft · edited today" heading="h3" :sticky="false">
<x-stack gap="space200" style="padding-block-end: var(--md-sys-measurement-space300)">
<p class="md-type-body-lg">Three days by the lake in June: the train on Friday morning, two nights at the harbour, and a walk round the old town on Sunday before the train home.</p>
<p class="md-type-body-lg">From 840px the comments sit beside the proposal, 360px wide (412px from 1200px). Below it they are a bottom sheet docked to the window: press its handle to read them.</p>
</x-stack>
</x-pane>
</x-surface>
</x-slot:main>
<x-slot:supporting>
<x-surface level="surface-container-low" corner="lg" padding="space200">
<x-list label="Comments">
<x-list-item title="Anna Müller" description="Friday works for me." avatar="AM" />
<x-list-item title="Ben Keller" description="Can we stay a third night?" avatar="BK" />
</x-list>
</x-surface>
</x-slot:supporting>
</x-supporting-pane>
BLADE,
'Feed' => <<<'BLADE'
<x-feed min-item="220px">
<x-card title="Harbour at dawn" subtitle="12 photos" variant="outlined">The boats before the ferry.</x-card>
<x-card title="Old town" subtitle="31 photos" variant="outlined">Every lane, twice.</x-card>
<x-card title="Lake swim" subtitle="4 videos" variant="outlined">Colder than it looks.</x-card>
<x-card title="Market" subtitle="18 photos" variant="outlined">Cheese, mostly.</x-card>
<x-card title="The long walk" subtitle="9 photos" variant="outlined">Up to the ridge and back.</x-card>
<x-card title="Train home" subtitle="3 photos" variant="outlined">Asleep by the second stop.</x-card>
</x-feed>
BLADE,
'Surfaces, stacks, rows and grids' => <<<'BLADE'
<x-stack gap="space300">
<x-grid :columns="['compact' => 1, 'medium' => 2, 'expanded' => 4]" gap="space200">
<x-surface level="surface-container-lowest" padding="space200" corner="md" outlined><p class="md-type-label-lg">lowest, outlined</p></x-surface>
<x-surface level="surface-container-low" padding="space200" corner="md"><p class="md-type-label-lg">low</p></x-surface>
<x-surface level="surface-container-high" padding="space200" corner="md"><p class="md-type-label-lg">high</p></x-surface>
<x-surface level="surface-container-highest" padding="space200" corner="md"><p class="md-type-label-lg">highest</p></x-surface>
</x-grid>
<x-row gap="space200" justify="between" stack-below="medium">
<x-stack gap="space50">
<p class="md-type-title-md">A row that stacks below 600px</p>
<p class="md-type-body-md md-ink-variant">Its middle item is hidden from 840px.</p>
</x-stack>
<x-stack hide-from="expanded"><p class="md-type-body-sm md-ink-variant">Widen the window past 840px and this goes.</p></x-stack>
<x-button label="Save" variant="filled" />
</x-row>
</x-stack>
BLADE,
];
@endphp
<section id="layout" class="scroll-mt-24 space-y-6">
<h2 class="type-headline-md">Layout</h2>
@@ -75,7 +161,7 @@
<tr>
<td>Expanded</td>
<td>Navigation rail, collapsible; starts collapsed</td>
<td>Two: list-detail (<code>&lt;x-drawer pane&gt;</code>) or a 360px supporting pane</td>
<td>Two: <code>&lt;x-list-detail&gt;</code>, or <code>&lt;x-supporting-pane&gt;</code> at 360px</td>
<td>Basic dialogs, side sheets</td>
<td>24px</td>
</tr>
@@ -122,4 +208,8 @@
<li><code>&lt;x-stack&gt;</code>, <code>&lt;x-row&gt;</code> and <code>&lt;x-grid&gt;</code> arrangement inside a pane.</li>
</x-livewire-material::stack>
</x-livewire-material::stack>
@foreach ($examples as $title => $code)
<x-showcase::example :$title :$code stack />
@endforeach
</section>
+23
View File
@@ -463,3 +463,26 @@ it('draws no second margin for a pane or a canonical layout inside the scaffold\
->assertScript(layoutRect('#text', 'left').' - '.layoutRect('main', 'left').' === 24')
->assertScript(layoutStyle('#pane [data-md-pane-body]', 'paddingLeft')." === '0px'");
});
it('shows each canonical layout working on the showcase\'s Layout page', function () {
$example = fn (string $title): string => "#example-{$title}";
$page = layoutReady(visit('/material/layout')->resize(1280, 900))
->assertScript("document.querySelectorAll('#example-list-detail [data-md-list-detail-pane]').length === 2")
->assertScript("[...document.querySelectorAll('#example-list-detail [data-md-list-detail-pane]')].every((pane) => pane.checkVisibility())")
->assertScript(layoutRect($example('supporting-pane').' [data-md-supporting-pane-supporting]', 'top').' === '.layoutRect($example('supporting-pane').' [data-md-supporting-pane-main]', 'top'))
->assertScript(layoutColumns($example('feed').' [data-md-feed]').' > 1');
$page->click('#example-list-detail a:has-text("Ben Keller")')
->assertSeeIn('#example-list-detail [data-md-list-detail-pane="detail"]', 'Contract, second draft')
->assertNoJavaScriptErrors();
$compact = layoutReady(visit('/material/layout')->resize(599, 900));
$compact->click('#example-list-detail a:has-text("Chiara Rossi")')
->assertSeeIn('#example-list-detail [data-md-list-detail-pane="detail"]', 'Train times for Friday')
->assertScript("! document.querySelector('#example-list-detail [data-md-list-detail-pane=\"list\"]').checkVisibility()")
->assertScript(layoutColumns($example('feed').' [data-md-feed]').' === 1')
->assertScript("document.querySelector('#example-supporting-pane [data-md-supporting-pane-handle]').checkVisibility()")
->assertNoJavaScriptErrors();
});