Put the badge on M3's anchor geometry and a visible edge
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) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
co-authored by
Claude Opus 5
parent
c33d90c653
commit
56d82b71f3
@@ -371,8 +371,8 @@ A value the server changes animates after a morph (the SVG is `wire:ignore`; onl
|
|||||||
### `<x-badge>`
|
### `<x-badge>`
|
||||||
|
|
||||||
- `<x-badge />` — M3's small badge, a dot. `<x-badge value="4" max="99" />` — M3's large badge, a count. Both `error` by default. `floating` pins it to the top-end corner of a `relative` parent: `<span class="relative inline-flex"><x-icon name="mail" /><x-badge value="4" floating /></span>`. A dot or count is `aria-hidden` unless it has a `label`; name the control instead ("Messages, 4 unread").
|
- `<x-badge />` — M3's small badge, a dot. `<x-badge value="4" max="99" />` — M3's large badge, a count. Both `error` by default. `floating` pins it to the top-end corner of a `relative` parent: `<span class="relative inline-flex"><x-icon name="mail" /><x-badge value="4" floating /></span>`. A dot or count is `aria-hidden` unless it has a `label`; name the control instead ("Messages, 4 unread").
|
||||||
- `<x-badge value="Expired" tonal />`, `<x-badge value="Active" color="success" tonal />`, `<x-badge value="Built in" color="primary" solid />`, `<x-badge value="Pro" outline />` — 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`.
|
- `<x-badge value="Expired" tonal />`, `<x-badge value="Active" color="success" tonal />`, `<x-badge value="Built in" color="primary" solid />`, `<x-badge value="Pro" outline />` — 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-variant edge with on-surface-variant text.
|
- `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: `<x-badge value="Run" tonal color="plain" class="bg-tertiary-container text-on-tertiary-container" />`. Pass both a background and a text class; an `outline` badge's edge takes the text colour unless you pass a `border-*` colour.
|
- `color="plain"` — no background, text or border colour in any variant (shape, size and type stay), so the classes you pass paint it: `<x-badge value="Run" tonal color="plain" class="bg-tertiary-container text-on-tertiary-container" />`. 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: `<x-badge tonal><x-icon name="bolt" class="size-3" /> Pro</x-badge>`. `value` is escaped. A slot that holds only whitespace or comments is still a dot.
|
- The value is `value` or the slot; the slot renders as HTML: `<x-badge tonal><x-icon name="bolt" class="size-3" /> Pro</x-badge>`. `value` is escaped. A slot that holds only whitespace or comments is still a dot.
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,11 @@
|
|||||||
- `<x-badge />` — the small badge, a 6px dot: something new, no number.
|
- `<x-badge />` — the small badge, a 6px dot: something new, no number.
|
||||||
- `<x-badge value="3" />` — the large badge, 16px tall, label-small: a count. `max` caps what is
|
- `<x-badge value="3" />` — the large badge, 16px tall, label-small: a count. `max` caps what is
|
||||||
shown ("99+").
|
shown ("99+").
|
||||||
Both are `error` by default, as M3 draws them. `floating` pins one to the top-end corner of a
|
Both are `error` by default, as M3 draws them, and an `outline` status label draws its edge in
|
||||||
`relative` parent — an icon or an icon button:
|
`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:
|
||||||
|
|
||||||
<span class="relative inline-flex"><x-icon name="notifications" /><x-badge value="4" floating /></span>
|
<span class="relative inline-flex"><x-icon name="notifications" /><x-badge value="4" floating /></span>
|
||||||
|
|
||||||
@@ -16,7 +19,7 @@
|
|||||||
and two without a hue of their own:
|
and two without a hue of their own:
|
||||||
- `neutral` — neutral ink on every variant: on-surface-variant with surface text as a dot or
|
- `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
|
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
|
- `plain` — no background, text or border colour at all, only shape, size and type, so the
|
||||||
caller's classes paint it: `<x-badge value="Run" tonal color="plain" class="bg-tertiary-container text-on-tertiary-container" />`.
|
caller's classes paint it: `<x-badge value="Run" tonal color="plain" class="bg-tertiary-container text-on-tertiary-container" />`.
|
||||||
|
|
||||||
@@ -65,7 +68,7 @@
|
|||||||
$color === 'plain' => '',
|
$color === 'plain' => '',
|
||||||
! $status, $solid => $filled[$color],
|
! $status, $solid => $filled[$color],
|
||||||
$tonal => $container[$color],
|
$tonal => $container[$color],
|
||||||
default => 'border-outline-variant text-on-surface-variant',
|
default => 'border-outline text-on-surface-variant',
|
||||||
};
|
};
|
||||||
|
|
||||||
$attributes = $attributes
|
$attributes = $attributes
|
||||||
@@ -76,8 +79,8 @@
|
|||||||
'h-6 gap-1 rounded-corner-sm px-2 type-label-md' => $status,
|
'h-6 gap-1 rounded-corner-sm px-2 type-label-md' => $status,
|
||||||
'border' => $outline && ! $tonal && ! $solid && ! $dot,
|
'border' => $outline && ! $tonal && ! $solid && ! $dot,
|
||||||
$paint => $paint !== '',
|
$paint => $paint !== '',
|
||||||
'absolute top-0.5 end-0.5' => $floating && $dot,
|
'absolute top-0 end-0' => $floating && $dot,
|
||||||
'absolute -top-1 start-[calc(100%-0.75rem)]' => $floating && ! $dot,
|
'absolute -top-0.5 start-[calc(100%-0.75rem)]' => $floating && ! $dot,
|
||||||
])
|
])
|
||||||
->merge(array_filter([
|
->merge(array_filter([
|
||||||
'aria-label' => $label,
|
'aria-label' => $label,
|
||||||
|
|||||||
@@ -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 () {
|
it('pins itself to the corner of an icon when floating', function () {
|
||||||
expect((string) $this->blade('<x-badge floating />'))->toContain('absolute top-0.5 end-0.5')
|
expect((string) $this->blade('<x-badge floating />'))->toContain('absolute top-0 end-0')
|
||||||
->and((string) $this->blade('<x-badge value="3" floating label="3 new messages" />'))
|
->and((string) $this->blade('<x-badge value="3" floating label="3 new messages" />'))
|
||||||
->toContain('absolute -top-1')
|
->toContain('absolute -top-0.5')
|
||||||
->toContain('aria-label="3 new messages"')
|
->toContain('aria-label="3 new messages"')
|
||||||
->not->toContain('aria-hidden');
|
->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')
|
->toContain('bg-success-container text-on-success-container')
|
||||||
->not->toContain('aria-hidden')
|
->not->toContain('aria-hidden')
|
||||||
->and((string) $this->blade('<x-badge value="Pro" outline />'))
|
->and((string) $this->blade('<x-badge value="Pro" outline />'))
|
||||||
->toContain('border border-outline-variant text-on-surface-variant')
|
->toContain('border border-outline text-on-surface-variant')
|
||||||
->not->toContain('bg-error');
|
->not->toContain('bg-error');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ it('draws neutral ink on every variant', function () {
|
|||||||
expect((string) $this->blade('<x-badge color="neutral" />'))->toContain('size-1.5 rounded-corner-full bg-on-surface-variant text-surface')
|
expect((string) $this->blade('<x-badge color="neutral" />'))->toContain('size-1.5 rounded-corner-full bg-on-surface-variant text-surface')
|
||||||
->and((string) $this->blade('<x-badge value="7" color="neutral" />'))->toContain('tabular-nums bg-on-surface-variant text-surface')
|
->and((string) $this->blade('<x-badge value="7" color="neutral" />'))->toContain('tabular-nums bg-on-surface-variant text-surface')
|
||||||
->and((string) $this->blade('<x-badge value="Draft" tone="neutral" tonal />'))->toContain('type-label-md bg-surface-container-high text-on-surface-variant')
|
->and((string) $this->blade('<x-badge value="Draft" tone="neutral" tonal />'))->toContain('type-label-md bg-surface-container-high text-on-surface-variant')
|
||||||
->and((string) $this->blade('<x-badge value="Draft" color="neutral" outline />'))->toContain('type-label-md border border-outline-variant text-on-surface-variant');
|
->and((string) $this->blade('<x-badge value="Draft" color="neutral" outline />'))->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) {
|
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('<x-badge value="Active" tonal />'))
|
->and((string) $this->blade('<x-badge value="Active" tonal />'))
|
||||||
->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"')
|
->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('<x-badge value="Pro" outline color="success" />'))
|
->and((string) $this->blade('<x-badge value="Pro" outline color="success" />'))
|
||||||
->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('<x-badge value="3" color="sport-run" />'))->toContain('bg-error text-on-error');
|
->and((string) $this->blade('<x-badge value="3" color="sport-run" />'))->toContain('bg-error text-on-error');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user