Files
livewire-material/resources/views/components/checkbox.blade.php
T
Andreas Reinhold / reiniandClaude Fable 5.1 cc91e99a5d Say how M3 wants a checkbox group laid out at expanded
M3 § Checkbox, Behaviour asks that from expanded (840px) related checkboxes be
gathered into a contained region rather than left as one long column. That is a
rule about the page around the control, not about the control, so it is written
down rather than built: the component's header and its SKILL.md entry now say
it, and the showcase lays a group out in an `expanded:grid-cols-2` card (plan
step 24, audit docs/audits/m3-alignment/inputs.md § Missing).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-14 06:24:02 +02:00

78 lines
3.9 KiB
PHP

{{-- A checkbox, M3's: an 18px box that fills with primary when ticked, in a 40px state layer. The
tick and the dash fill the box at CheckboxTokens' 18px icon size, drawn from the 20 optical cut.
The label and its hint sit beside it and are its accessible name the whole row is the
`<label>`, so either can be pressed. `right` puts the box at the end of the row, for a setting
read left to right. `indeterminate` shows the mixed state of a "select all" whose items are
partly ticked: HTML has no attribute for it, so the input is marked `data-indeterminate` and
resources/js/field.js keeps the property in step, on load and after every morph. Errors are
read from the bag under the `wire:model` name. Every other attribute reaches the `<input>`
(resources/css/components/selection.css).
Without a label a "select all" in a table header, a row's tick — the row is only the 18px box,
so the box carries `touch-target` and catches presses over M3's 48px minimum.
Grouping is the caller's, not this component's: M3 asks that from `expanded` (840px) a set of
related checkboxes be gathered into a contained region rather than left as one long column
(docs/reference/m3/components-navigation-selection-inputs.md § Checkbox, Behaviour). Lay the
group out yourself `<div class="grid gap-4 expanded:grid-cols-2">` around the checkboxes, or a
`<x-card>` or side sheet holding them and give the group a heading that names what it asks. --}}
@props([
'label' => null,
'hint' => null,
'right' => false,
'indeterminate' => false,
])
@php
$model = $attributes->whereStartsWith('wire:model')->first();
$id = $attributes->get('id') ?? 'check-'.substr(md5($model.'|'.$label.'|'.$attributes->get('value')), 0, 12);
// A plain form's field is named, not bound: its errors are under its name (`files[]` → `files`, `a[b]` → `a.b`).
$errorKey = $model ?? (filled($attributes->get('name')) ? str_replace(['[]', '[', ']'], ['', '.', ''], (string) $attributes->get('name')) : null);
$messages = $errorKey !== null && isset($errors) ? \Illuminate\Support\Arr::flatten($errors->get($errorKey)) : [];
@endphp
<div {{ $attributes->only(['class', 'wire:key'])->class(['min-w-0']) }}>
<label for="{{ $id }}" data-selection @if ($messages !== []) data-invalid @endif @class([
'flex gap-4',
'items-start' => filled($hint),
'items-center' => blank($hint),
'flex-row-reverse justify-between' => $right,
])>
<span data-checkbox @class([
'mt-[3px]' => filled($hint),
'touch-target' => blank($label) && blank($hint),
])>
<input
{{ $attributes->except(['class', 'id', 'wire:key']) }}
id="{{ $id }}"
type="checkbox"
@if ($indeterminate) data-indeterminate @endif
@if ($messages !== []) aria-invalid="true" aria-describedby="{{ $id }}-support" @endif
/>
<x-livewire-material::icon name="check" class="size-4.5" optical="20" data-check />
<x-livewire-material::icon name="remove" class="size-4.5" optical="20" data-mixed />
</span>
@if (filled($label) || filled($hint))
<span class="min-w-0">
@if (filled($label))
<span class="block type-body-lg text-on-surface" data-selection-label>{{ $label }}</span>
@endif
@if (filled($hint))
<span class="mt-0.5 block type-body-sm text-on-surface-variant">{{ $hint }}</span>
@endif
</span>
@endif
</label>
@if ($messages !== [])
<div id="{{ $id }}-support" class="mt-1 type-body-sm text-error" role="alert">
@foreach ($messages as $message)
<p>{{ $message }}</p>
@endforeach
</div>
@endif
</div>