From ff5349759b128968d9474591db51918da6da7a79 Mon Sep 17 00:00:00 2001 From: Andreas Reinhold / reini Date: Tue, 15 Sep 2026 14:09:06 +0200 Subject: [PATCH] Let a card's title take the heading level the page needs always titled itself with an

, so a card straight under a page's

skipped a level, which SealShare's download page did with no way round it from the application. `heading` takes h2 to h6, or div or p for a title that is not a heading, as and already do; h3 stays the default. The skill also says a card holds one subject, as M3 does (plan step 46). Co-Authored-By: Claude Opus 5 (1M context) --- .../boost/skills/livewire-material-development/SKILL.md | 2 +- resources/views/components/card.blade.php | 8 ++++++-- tests/Feature/Components/CardTest.php | 7 +++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/resources/boost/skills/livewire-material-development/SKILL.md b/resources/boost/skills/livewire-material-development/SKILL.md index b01c13e3..c2e9ccfb 100644 --- a/resources/boost/skills/livewire-material-development/SKILL.md +++ b/resources/boost/skills/livewire-material-development/SKILL.md @@ -476,7 +476,7 @@ The `illustration` slot draws the application's own artwork in place of the shap ### `` -`variant`: `filled` (default, surface-container-highest), `elevated`, `outlined`; medium corner. Props `title`, `subtitle`, `separator`; slots `figure` (full-bleed media), `menu` (top-end), `actions` (end-aligned). Do not pass `bg-*`; use `variant`. +`variant`: `filled` (default, surface-container-highest), `elevated`, `outlined`; medium corner. Props `title`, `subtitle`, `separator`, `heading` (the title's element, `h3` by default: pass `h2` for a card straight under the page's `h1`); slots `figure` (full-bleed media), `menu` (top-end), `actions` (end-aligned). A card holds content and actions about one subject (M3); a table of many rows or a group of settings is a headed section, not a card. Do not pass `bg-*`; use `variant`. A card or list item that opens something is a **row**: `data-md-list-row` on it and `data-md-list-open` on its one opener (the title link or a button). A press anywhere else on the row reaches the opener; its other controls keep their own presses. Never wrap a card in `` or use a stretched link. A row answers with the state layer and one step of elevation; its corner does not move. diff --git a/resources/views/components/card.blade.php b/resources/views/components/card.blade.php index 816f588b..4db7f050 100644 --- a/resources/views/components/card.blade.php +++ b/resources/views/components/card.blade.php @@ -5,7 +5,9 @@ `elevated` (surface-container-low at elevation 1) and `outlined` (surface, an outline-variant edge); all with a medium corner. maryUI's props keep their meaning: `title`, `subtitle`, `separator` (a divider under the header), and the `menu` (top-end, beside the title), - `figure` (full-bleed media on top) and `actions` (end-aligned, under the content) slots. + `figure` (full-bleed media on top) and `actions` (end-aligned, under the content) slots. The title + is an `

` unless `heading` names the level the page's outline needs (`h2` … `h6`, or `div`/`p` + for a title that is not a heading), so a card straight under a page's `

` skips no level. M3 draws two kinds of card a person can press, and they differ in the keyboard: on a **non-actionable card with actionable elements** Tab moves through each control inside before @@ -51,12 +53,14 @@ 'title' => null, 'subtitle' => null, 'variant' => 'filled', + 'heading' => 'h3', 'separator' => false, 'actionable' => false, ]) @php $variant = in_array($variant, ['filled', 'elevated', 'outlined'], true) ? $variant : 'filled'; + $heading = in_array($heading, ['h2', 'h3', 'h4', 'h5', 'h6', 'div', 'p'], true) ? $heading : 'h3'; $role = $actionable ? ($attributes->get('role') ?: 'button') : null; $isRow = $actionable || $attributes->has('data-md-list-row'); @@ -87,7 +91,7 @@
@if ($title) -

{{ $title }}

+ <{{ $heading }} data-md-card-title>{{ $title }} @endif @if ($subtitle) diff --git a/tests/Feature/Components/CardTest.php b/tests/Feature/Components/CardTest.php index c7fca4d5..94417490 100644 --- a/tests/Feature/Components/CardTest.php +++ b/tests/Feature/Components/CardTest.php @@ -93,3 +93,10 @@ it('rings the row from the card itself when actionable, or from a has() when its ->and($css->declarations('[data-md-card][data-md-list-row] [data-md-list-open]:focus-visible')) ->toBe(['outline' => 'none']); }); + +it('titles the card at the heading level the page\'s outline needs', function () { + expect((string) $this->blade('Body')) + ->toContain('

Shared files

') + ->and((string) $this->blade('Body')) + ->toContain('

Shared files

'); +});