Draw the switch without Tailwind

<x-toggle> renders data-md-toggle, its row (data-md-right), track,
handle and icons as data-md-* attributes; the handle's icons take size
16 and a label-less track md-touch-target (plan step 36). toggle.css
gains the root; a browser test pins the handle's 16px/24px sizes and
16px/36px centres off and on.

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:43:17 +02:00
co-authored by Claude Opus 5
parent 8747163fce
commit b42069503d
4 changed files with 43 additions and 22 deletions
+8 -2
View File
@@ -11,8 +11,10 @@
* state layer is a 40px circle around the handle. Its icons are 16px, on-primary-container when on.
* Disabled, the track is on-surface at 12% and the handle on-surface at 38% (surface when on).
*
* [data-md-switch] the track; `data-md-icons` = "both" or "selected"
* input, [data-md-switch-handle] (with [data-md-switch-on] and [data-md-switch-off] icons)
* [data-md-toggle] the root; `class` and `style` land here
* [data-md-selection-row] data-md-right puts the switch at the end of the row
* [data-md-switch] the track; `data-md-icons` = "both" or "selected"
* input, [data-md-switch-handle] (with [data-md-switch-on] and [data-md-switch-off] icons)
*/
@layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility;
@@ -21,6 +23,10 @@
@import './icon.css';
@layer material.components {
[data-md-toggle] {
min-width: 0;
}
/* The positions below are the handle's centres less half its size, measured inside the track's
2px outline. */
[data-md-switch] {
+11 -16
View File
@@ -7,8 +7,8 @@
cross when off; `icons="selected"` only the check. Every other attribute reaches the `<input>`
(resources/css/components/toggle.css).
Without a label the row is only the 52×32 track, so the track carries `touch-target` and catches
presses over M3's 48px minimum. --}}
Without a label the row is only the 52×32 track, so the track carries `md-touch-target` and
catches presses over M3's 48px minimum. `class` and `style` land on the root, `data-md-toggle`. --}}
@props([
'label' => null,
@@ -23,35 +23,30 @@
$icons = $icons === 'selected' ? 'selected' : ($icons ? 'both' : null);
@endphp
<div {{ $attributes->only(['class', 'wire:key'])->class(['min-w-0']) }}>
<label for="{{ $id }}" data-md-selection-row @class([
'flex gap-4',
'items-start' => filled($hint),
'items-center' => blank($hint),
'flex-row-reverse justify-between' => $right,
])>
<span data-md-switch @if ($icons) data-md-icons="{{ $icons }}" @endif @class(['touch-target' => blank($label) && blank($hint)])>
<div data-md-toggle {{ $attributes->only(['class', 'style', 'wire:key']) }}>
<label for="{{ $id }}" data-md-selection-row @if ($right) data-md-right @endif>
<span data-md-switch @if ($icons) data-md-icons="{{ $icons }}" @endif @if (blank($label) && blank($hint)) class="md-touch-target" @endif>
<input
{{ $attributes->except(['class', 'id', 'wire:key']) }}
{{ $attributes->except(['class', 'style', 'id', 'wire:key']) }}
id="{{ $id }}"
type="checkbox"
role="switch"
/>
<span data-md-switch-handle>
@if ($icons)
<x-livewire-material::icon name="check" class="size-4" optical="20" data-md-switch-on />
<x-livewire-material::icon name="close" class="size-4" optical="20" data-md-switch-off />
<x-livewire-material::icon name="check" size="16" data-md-switch-on />
<x-livewire-material::icon name="close" size="16" data-md-switch-off />
@endif
</span>
</span>
@if (filled($label) || filled($hint))
<span class="min-w-0">
<span data-md-selection-text>
@if (filled($label))
<span class="block type-body-lg text-on-surface" data-md-selection-label>{{ $label }}</span>
<span data-md-selection-label>{{ $label }}</span>
@endif
@if (filled($hint))
<span class="mt-0.5 block type-body-sm text-on-surface-variant">{{ $hint }}</span>
<span data-md-selection-hint>{{ $hint }}</span>
@endif
</span>
@endif