From 56d82b71f33416d735530e59dd7568d89cb45b4c Mon Sep 17 00:00:00 2001 From: Andreas Reinhold Date: Mon, 14 Sep 2026 06:11:52 +0200 Subject: [PATCH] Put the badge on M3's anchor geometry and a visible edge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A floating dot sat 2px inside the icon's top-trailing corner where M3 puts it flush, and a floating count 4px above it where M3 says 2px. An `outline` status label drew its edge in outline-variant, the decorative role dividers use, which is about 1.5:1 on surface — M3 asks a badge for 3:1, so it takes `outline`. Plan step 18, actions.md ACT-23, ACT-24. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9 --- .../skills/livewire-material-development/SKILL.md | 4 ++-- resources/views/components/badge.blade.php | 15 +++++++++------ tests/Feature/Components/BadgeTest.php | 10 +++++----- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/resources/boost/skills/livewire-material-development/SKILL.md b/resources/boost/skills/livewire-material-development/SKILL.md index 14e8904b..a470c087 100644 --- a/resources/boost/skills/livewire-material-development/SKILL.md +++ b/resources/boost/skills/livewire-material-development/SKILL.md @@ -371,8 +371,8 @@ A value the server changes animates after a morph (the SVG is `wire:ignore`; onl ### `` - `` — M3's small badge, a dot. `` — M3's large badge, a count. Both `error` by default. `floating` pins it to the top-end corner of a `relative` parent: ``. A dot or count is `aria-hidden` unless it has a `label`; name the control instead ("Messages, 4 unread"). -- ``, ``, ``, `` — a status label (not an M3 badge) in the colour's container, in the colour itself (`solid`, for a label that has to stand out), or a neutral edge. `color` (alias `tone`): `error` default, `primary`, `secondary`, `tertiary`, `success`, `warning`, `info`, `neutral`, `plain`; an unknown colour is `error`. -- `color="neutral"` — neutral ink on every variant: a dot or count in on-surface-variant with surface text, `tonal` in surface-container-high with on-surface-variant text, `outline` in the outline-variant edge with on-surface-variant text. +- ``, ``, ``, `` — a status label (not an M3 badge) in the colour's container, in the colour itself (`solid`, for a label that has to stand out), or a neutral `outline` edge (the role that has to be seen, not the decorative `outline-variant` dividers use). `color` (alias `tone`): `error` default, `primary`, `secondary`, `tertiary`, `success`, `warning`, `info`, `neutral`, `plain`; an unknown colour is `error`. +- `color="neutral"` — neutral ink on every variant: a dot or count in on-surface-variant with surface text, `tonal` in surface-container-high with on-surface-variant text, `outline` in the outline edge with on-surface-variant text. - `color="plain"` — no background, text or border colour in any variant (shape, size and type stay), so the classes you pass paint it: ``. Pass both a background and a text class; an `outline` badge's edge takes the text colour unless you pass a `border-*` colour. - The value is `value` or the slot; the slot renders as HTML: ` Pro`. `value` is escaped. A slot that holds only whitespace or comments is still a dot. diff --git a/resources/views/components/badge.blade.php b/resources/views/components/badge.blade.php index 181642b2..36afc916 100644 --- a/resources/views/components/badge.blade.php +++ b/resources/views/components/badge.blade.php @@ -4,8 +4,11 @@ - `` — the small badge, a 6px dot: something new, no number. - `` — the large badge, 16px tall, label-small: a count. `max` caps what is shown ("99+"). - Both are `error` by default, as M3 draws them. `floating` pins one to the top-end corner of a - `relative` parent — an icon or an icon button: + Both are `error` by default, as M3 draws them, and an `outline` status label draws its edge in + `outline`, not `outline-variant`: M3 asks a badge for 3:1 against what is behind it, and + `outline-variant` is the decorative role dividers use. `floating` pins one to the top-end + corner of a `relative` parent, on M3's anchor geometry — the dot flush in the corner, the + count 2px above it — an icon or an icon button: @@ -16,7 +19,7 @@ and two without a hue of their own: - `neutral` — neutral ink on every variant: on-surface-variant with surface text as a dot or count (the ink an outline badge already writes in), surface-container-high with - on-surface-variant text when `tonal`, the outline-variant edge when `outline`. + on-surface-variant text when `tonal`, the outline edge when `outline`. - `plain` — no background, text or border colour at all, only shape, size and type, so the caller's classes paint it: ``. @@ -65,7 +68,7 @@ $color === 'plain' => '', ! $status, $solid => $filled[$color], $tonal => $container[$color], - default => 'border-outline-variant text-on-surface-variant', + default => 'border-outline text-on-surface-variant', }; $attributes = $attributes @@ -76,8 +79,8 @@ 'h-6 gap-1 rounded-corner-sm px-2 type-label-md' => $status, 'border' => $outline && ! $tonal && ! $solid && ! $dot, $paint => $paint !== '', - 'absolute top-0.5 end-0.5' => $floating && $dot, - 'absolute -top-1 start-[calc(100%-0.75rem)]' => $floating && ! $dot, + 'absolute top-0 end-0' => $floating && $dot, + 'absolute -top-0.5 start-[calc(100%-0.75rem)]' => $floating && ! $dot, ]) ->merge(array_filter([ 'aria-label' => $label, diff --git a/tests/Feature/Components/BadgeTest.php b/tests/Feature/Components/BadgeTest.php index 056603e4..4dad40a8 100644 --- a/tests/Feature/Components/BadgeTest.php +++ b/tests/Feature/Components/BadgeTest.php @@ -14,9 +14,9 @@ it('is M3\'s large badge with a count, capped by max', function () { }); it('pins itself to the corner of an icon when floating', function () { - expect((string) $this->blade(''))->toContain('absolute top-0.5 end-0.5') + expect((string) $this->blade(''))->toContain('absolute top-0 end-0') ->and((string) $this->blade('')) - ->toContain('absolute -top-1') + ->toContain('absolute -top-0.5') ->toContain('aria-label="3 new messages"') ->not->toContain('aria-hidden'); }); @@ -27,7 +27,7 @@ it('draws a status label in a container or an outline', function () { ->toContain('bg-success-container text-on-success-container') ->not->toContain('aria-hidden') ->and((string) $this->blade('')) - ->toContain('border border-outline-variant text-on-surface-variant') + ->toContain('border border-outline text-on-surface-variant') ->not->toContain('bg-error'); }); @@ -48,7 +48,7 @@ it('draws neutral ink on every variant', function () { expect((string) $this->blade(''))->toContain('size-1.5 rounded-corner-full bg-on-surface-variant text-surface') ->and((string) $this->blade(''))->toContain('tabular-nums bg-on-surface-variant text-surface') ->and((string) $this->blade(''))->toContain('type-label-md bg-surface-container-high text-on-surface-variant') - ->and((string) $this->blade(''))->toContain('type-label-md border border-outline-variant text-on-surface-variant'); + ->and((string) $this->blade(''))->toContain('type-label-md border border-outline text-on-surface-variant'); }); it('leaves a plain badge to the caller\'s colour classes', function (string $badge, string $shape) { @@ -73,7 +73,7 @@ it('keeps its default colours, and falls back to error on a colour it does not k ->and((string) $this->blade('')) ->toContain('class="inline-flex shrink-0 items-center justify-center whitespace-nowrap h-6 gap-1 rounded-corner-sm px-2 type-label-md bg-error-container text-on-error-container"') ->and((string) $this->blade('')) - ->toContain('class="inline-flex shrink-0 items-center justify-center whitespace-nowrap h-6 gap-1 rounded-corner-sm px-2 type-label-md border border-outline-variant text-on-surface-variant"') + ->toContain('class="inline-flex shrink-0 items-center justify-center whitespace-nowrap h-6 gap-1 rounded-corner-sm px-2 type-label-md border border-outline text-on-surface-variant"') ->and((string) $this->blade(''))->toContain('bg-error text-on-error'); });