{{-- An M3 card: content and actions about one subject. `variant` is M3's three (FilledCardTokens, ElevatedCardTokens, OutlinedCardTokens, androidx Compose Material 3, Apache-2.0): `filled` (surface-container-highest, the default), `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. 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 the next card; on a **directly actionable card** Tab moves to the card, then to the next card (docs/reference/m3/components-actions-communication-containment.md § Cards → Accessibility). Both are a row: give the card `data-md-list-row` and one `data-md-list-open` control inside (the title link or a button), and a press anywhere on it reaches that control while its other buttons keep their own (resources/js/list-rows.js). That is the first kind — the opener is the tab stop, the accessible name and the owner of the `href` or the `wire:click`. `actionable` makes it the second: the card takes `tabindex="0"` and `role="button"` (pass `role="link"` where it goes somewhere), is named by its `title`, and Enter or Space on it reaches the opener. Put `tabindex="-1"` on that opener so the card is the one stop, and leave the card's other actions as they are — they follow it in the tab order. Every card renders the shared `md-state-layer` class and a row (`data-md-list-row`, `actionable` or not) `md-focus-ring` too (foundation/interaction.css), which draw the tint and the ring for a card focused directly; resources/css/components/card.css keeps the layer off a card that is neither a row nor dragged, refines its opacity when the pointer or the keyboard is on one of the card's *own* buttons instead (which should light only itself), and adds the `:has([data-md-list-open]:focus-visible)` ring for the non-actionable row, whose focus never lands on the card itself. Elevation is separate from the tint — M3 gives each variant its own per-state box-shadow (elevated 1dp rest → 2dp hover → 1dp focus/pressed; filled and outlined 0dp rest → 1dp hover → 0dp focus/pressed) — so it is drawn directly on `[data-md-card]`, never inside the shared class. Its corner does not move, because M3 gives a card one shape (docs/audits/m3-alignment/containment.md, C-13, C-23). `data-md-dragged` is M3's dragged card, row or not: the top of a card's elevation scale — 8dp elevated, 6dp filled and outlined — under the shared class's own 16% dragged tint (it already reads `data-md-dragged`, foundation/interaction.css). Nothing in the browser tells a card it is being carried, so the application sets the attribute when its drag starts and takes it off on drop. M3 requires a single-pointer alternative beside any drag, so keep the same reorder or delete actions in a menu on the card (docs/reference/m3/components-actions-communication-containment.md § Cards → Specs, Accessibility). Do not pass a `bg-*` class to change its fill — the package's own rule for the variant sits in `material.components`, and an application's unlayered CSS is the place to override it; use `variant`, or colour a wrapper inside. --}} @props([ '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'); $attributes = $actionable ? $attributes->except('role') : $attributes; @endphp
merge(array_filter([ 'data-md-card' => $variant, 'data-md-list-row' => $actionable ? true : null, 'data-md-list-actionable' => $actionable ? true : null, 'tabindex' => $actionable ? '0' : null, 'role' => $role, 'aria-label' => $actionable && filled($title) ? $title : null, ], fn ($value): bool => $value !== null))->class([ // Every card: the application sets `data-md-dragged` from script, after this renders. 'md-state-layer', 'md-focus-ring' => $isRow, ]) }} > @isset($figure)
{{ $figure }}
@endisset
@if ($title || $subtitle || isset($menu))
@if ($title) <{{ $heading }} data-md-card-title>{{ $title }} @endif @if ($subtitle)

{{ $subtitle }}

@endif
@isset($menu)
{{ $menu }}
@endisset
@if ($separator) @endif
@endif @if ($slot->isNotEmpty())
{{ $slot }}
@endif @isset($actions)
{{ $actions }}
@endisset