Draw the small icons from the 20 optical cut

CheckboxTokens' icon size is 18px, the same as the box it fills; the tick
and the dash were 16. Every icon these components draw at 20px or under —
the tick, the switch's check and cross, the chip's leading, trailing,
remove and filter-check icons, the date picker's menu arrows and the sort
arrow — now asks for the optical size 20 cut, which is drawn for that
size rather than scaled down from 24.

Plan step 20, finding IN-12 (and core C16's optical sizes).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
Andreas Reinhold / reini
2026-09-14 06:03:02 +02:00
co-authored by Claude Fable 5.1
parent 9624d4bfd4
commit 47ed4603d0
6 changed files with 22 additions and 15 deletions
@@ -1,4 +1,5 @@
{{-- A checkbox, M3's: an 18px box that fills with primary when ticked, in a 40px state layer. {{-- 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 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 `<label>`, so either can be pressed. `right` puts the box at the end of the row, for a setting
@@ -44,8 +45,8 @@
@if ($indeterminate) data-indeterminate @endif @if ($indeterminate) data-indeterminate @endif
@if ($messages !== []) aria-invalid="true" aria-describedby="{{ $id }}-support" @endif @if ($messages !== []) aria-invalid="true" aria-describedby="{{ $id }}-support" @endif
/> />
<x-livewire-material::icon name="check" class="size-4" data-check /> <x-livewire-material::icon name="check" class="size-4.5" optical="20" data-check />
<x-livewire-material::icon name="remove" class="size-4" data-mixed /> <x-livewire-material::icon name="remove" class="size-4.5" optical="20" data-mixed />
</span> </span>
@if (filled($label) || filled($hint)) @if (filled($label) || filled($hint))
+6 -6
View File
@@ -230,7 +230,7 @@
<img src="{{ $avatar }}" alt="" @class(['me-2 size-6 shrink-0 rounded-corner-full object-cover', 'opacity-38' => $disabled]) /> <img src="{{ $avatar }}" alt="" @class(['me-2 size-6 shrink-0 rounded-corner-full object-cover', 'opacity-38' => $disabled]) />
@endif @endif
@elseif ($icon) @elseif ($icon)
<x-livewire-material::icon :name="$icon" :class="\Illuminate\Support\Arr::toCssClasses(['me-2 size-4.5', $leadingInk])" /> <x-livewire-material::icon :name="$icon" optical="20" :class="\Illuminate\Support\Arr::toCssClasses(['me-2 size-4.5', $leadingInk])" />
@endif @endif
<span data-chip-label class="truncate">{{ $label ?? $slot }}</span> <span data-chip-label class="truncate">{{ $label ?? $slot }}</span>
@@ -251,7 +251,7 @@
'cursor-not-allowed' => $disabled, 'cursor-not-allowed' => $disabled,
]) ])
> >
<x-livewire-material::icon name="close" class="size-4.5" /> <x-livewire-material::icon name="close" class="size-4.5" optical="20" />
</button> </button>
@endif @endif
@@ -277,18 +277,18 @@
'h-4.5 w-0 transition-[width] duration-(--md-sys-motion-effects-default-duration) ease-effects-default group-has-checked/chip:w-4.5 group-has-checked/chip:duration-(--md-sys-motion-spatial-fast-duration) group-has-checked/chip:ease-spatial-fast group-aria-pressed/chip:w-4.5 group-aria-pressed/chip:duration-(--md-sys-motion-spatial-fast-duration) group-aria-pressed/chip:ease-spatial-fast' => blank($icon), 'h-4.5 w-0 transition-[width] duration-(--md-sys-motion-effects-default-duration) ease-effects-default group-has-checked/chip:w-4.5 group-has-checked/chip:duration-(--md-sys-motion-spatial-fast-duration) group-has-checked/chip:ease-spatial-fast group-aria-pressed/chip:w-4.5 group-aria-pressed/chip:duration-(--md-sys-motion-spatial-fast-duration) group-aria-pressed/chip:ease-spatial-fast' => blank($icon),
])> ])>
@if ($icon) @if ($icon)
<x-livewire-material::icon :name="$icon" :class="\Illuminate\Support\Arr::toCssClasses(['size-4.5 transition-opacity duration-(--md-sys-motion-effects-fast-duration) ease-effects-fast group-has-checked/chip:opacity-0 group-aria-pressed/chip:opacity-0', $leadingInk])" /> <x-livewire-material::icon :name="$icon" optical="20" :class="\Illuminate\Support\Arr::toCssClasses(['size-4.5 transition-opacity duration-(--md-sys-motion-effects-fast-duration) ease-effects-fast group-has-checked/chip:opacity-0 group-aria-pressed/chip:opacity-0', $leadingInk])" />
@endif @endif
<x-livewire-material::icon name="check" data-chip-check :class="$check" /> <x-livewire-material::icon name="check" data-chip-check optical="20" :class="$check" />
</span> </span>
@elseif ($icon) @elseif ($icon)
<x-livewire-material::icon :name="$icon" :class="\Illuminate\Support\Arr::toCssClasses(['me-2 size-4.5', $leadingInk])" /> <x-livewire-material::icon :name="$icon" optical="20" :class="\Illuminate\Support\Arr::toCssClasses(['me-2 size-4.5', $leadingInk])" />
@endif @endif
<span class="truncate">{{ $label ?? $slot }}</span> <span class="truncate">{{ $label ?? $slot }}</span>
@if ($iconRight) @if ($iconRight)
<x-livewire-material::icon :name="$iconRight" :class="\Illuminate\Support\Arr::toCssClasses(['ms-2 size-4.5', $trailingInk])" /> <x-livewire-material::icon :name="$iconRight" optical="20" :class="\Illuminate\Support\Arr::toCssClasses(['ms-2 size-4.5', $trailingInk])" />
@endif @endif
@if ($tooltip !== null) @if ($tooltip !== null)
@@ -292,7 +292,7 @@
x-bind:aria-label="monthYear + ', ' + @js(__('Switch to selecting a year'))" x-bind:aria-label="monthYear + ', ' + @js(__('Switch to selecting a year'))"
> >
<span x-text="monthYear"></span> <span x-text="monthYear"></span>
<x-livewire-material::icon name="arrow_drop_down" class="size-4.5" data-datepicker-menu-arrow /> <x-livewire-material::icon name="arrow_drop_down" class="size-4.5" optical="20" data-datepicker-menu-arrow />
</button> </button>
<span data-datepicker-arrows x-bind:data-concealed="view !== 'days' ? '' : null"> <span data-datepicker-arrows x-bind:data-concealed="view !== 'days' ? '' : null">
@@ -314,7 +314,7 @@
x-bind:aria-label="monthLabel + ', ' + @js(__('Switch to selecting a month'))" x-bind:aria-label="monthLabel + ', ' + @js(__('Switch to selecting a month'))"
> >
<span x-text="monthLabel"></span> <span x-text="monthLabel"></span>
<x-livewire-material::icon name="arrow_drop_down" class="size-4.5" data-datepicker-menu-arrow /> <x-livewire-material::icon name="arrow_drop_down" class="size-4.5" optical="20" data-datepicker-menu-arrow />
</button> </button>
<span data-datepicker-arrows x-bind:data-concealed="view !== 'days' ? '' : null"> <span data-datepicker-arrows x-bind:data-concealed="view !== 'days' ? '' : null">
<x-livewire-material::button icon="chevron_right" :tooltip="__('Next month')" x-on:click="step(1)" x-bind:disabled="view !== 'days' || ! canStep(1)" /> <x-livewire-material::button icon="chevron_right" :tooltip="__('Next month')" x-on:click="step(1)" x-bind:disabled="view !== 'days' || ! canStep(1)" />
@@ -333,7 +333,7 @@
x-bind:aria-label="yearLabel + ', ' + @js(__('Switch to selecting a year'))" x-bind:aria-label="yearLabel + ', ' + @js(__('Switch to selecting a year'))"
> >
<span x-text="yearLabel"></span> <span x-text="yearLabel"></span>
<x-livewire-material::icon name="arrow_drop_down" class="size-4.5" data-datepicker-menu-arrow /> <x-livewire-material::icon name="arrow_drop_down" class="size-4.5" optical="20" data-datepicker-menu-arrow />
</button> </button>
<span data-datepicker-arrows x-bind:data-concealed="view !== 'days' ? '' : null"> <span data-datepicker-arrows x-bind:data-concealed="view !== 'days' ? '' : null">
<x-livewire-material::button icon="chevron_right" :tooltip="__('Next year')" x-on:click="step(12)" x-bind:disabled="view !== 'days' || ! canStep(12)" /> <x-livewire-material::button icon="chevron_right" :tooltip="__('Next year')" x-on:click="step(12)" x-bind:disabled="view !== 'days' || ! canStep(12)" />
@@ -29,7 +29,7 @@
]) ])
> >
{{ $slot }} {{ $slot }}
<x-livewire-material::icon :name="$direction === 'desc' ? 'arrow_downward' : 'arrow_upward'" :class="\Illuminate\Support\Arr::toCssClasses([ <x-livewire-material::icon :name="$direction === 'desc' ? 'arrow_downward' : 'arrow_upward'" optical="20" :class="\Illuminate\Support\Arr::toCssClasses([
'size-4 transition-opacity duration-(--md-sys-motion-effects-fast-duration)', 'size-4 transition-opacity duration-(--md-sys-motion-effects-fast-duration)',
'opacity-0 group-hover/sort:opacity-60 group-focus-visible/sort:opacity-60' => ! $active, 'opacity-0 group-hover/sort:opacity-60 group-focus-visible/sort:opacity-60' => ! $active,
])" /> ])" />
+2 -2
View File
@@ -39,8 +39,8 @@
/> />
<span data-handle> <span data-handle>
@if ($icons) @if ($icons)
<x-livewire-material::icon name="check" class="size-4" data-on /> <x-livewire-material::icon name="check" class="size-4" optical="20" data-on />
<x-livewire-material::icon name="close" class="size-4" data-off /> <x-livewire-material::icon name="close" class="size-4" optical="20" data-off />
@endif @endif
</span> </span>
</span> </span>
@@ -16,6 +16,12 @@ it('draws a checkbox whose row is its label', function () {
->not->toContain('data-indeterminate'); ->not->toContain('data-indeterminate');
}); });
it('fills the box with an 18px tick from the 20 optical cut', function () {
expect((string) $this->blade('<x-checkbox label="Notify me" />'))
->toContain((string) $this->blade('<x-icon name="check" class="size-4.5" optical="20" data-check />'))
->toContain((string) $this->blade('<x-icon name="remove" class="size-4.5" optical="20" data-mixed />'));
});
it('marks a checkbox that is partly ticked', function () { it('marks a checkbox that is partly ticked', function () {
expect((string) $this->blade('<x-checkbox label="Select all" indeterminate />'))->toContain('data-indeterminate'); expect((string) $this->blade('<x-checkbox label="Select all" indeterminate />'))->toContain('data-indeterminate');
}); });