Mark a section picker's current page as the page, with its badge

The phone picker of <x-section-nav> drew the current section as a checked
menuitemcheckbox in tertiary-container, which a screen reader announces as a
choice rather than where you are, and it dropped the sections' badges, so an
open support count showed on the tabs but not on a phone. <x-menu-item> gains
current (aria-current="page", the selected shape in secondary-container,
the navigation indicator's colour) and badge (a dot or a count at the end of
the row), and the picker uses both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RHoXZSHc8gGpZjFmA5fPc2
This commit is contained in:
Andreas Reinhold / reini
2026-09-13 19:56:54 +02:00
co-authored by Claude Opus 5
parent 65788d0c61
commit 869ccdf447
5 changed files with 40 additions and 8 deletions
+14 -2
View File
@@ -4,7 +4,10 @@
under the label and a `shortcut` at the end (M3's trailing supporting text: "⌘C"). `link`
makes it an anchor, with `wire:navigate` unless `external` or `no-wire-navigate`. `selected`
(true or false) makes it a `menuitemcheckbox` with `aria-checked`; a selected item takes
Expressive's selected shape and tertiary-container. `disabled` keeps it in the list, out of
Expressive's selected shape and tertiary-container. `current` is for a menu of places rather
than choices a section picker and marks the page you are on: `aria-current="page"`, the
selected shape in secondary-container, the colour M3 gives the navigation indicator. `badge`
draws `<x-badge>` at the end of the row: `true` for a dot, or a count. `disabled` keeps it in the list, out of
reach. `keep-open` leaves the menu open when it is activated for a choice the person may
want to change twice.
@@ -28,6 +31,8 @@
'external' => false,
'noWireNavigate' => false,
'selected' => null,
'current' => false,
'badge' => null,
'disabled' => false,
'keepOpen' => false,
])
@@ -44,11 +49,13 @@
'focus-visible:outline-3 focus-visible:-outline-offset-3 focus-visible:outline-secondary',
'py-2' => filled($description),
'rounded-corner-md bg-tertiary-container text-on-tertiary-container' => $selected === true,
'rounded-corner-md bg-secondary-container text-on-secondary-container' => $current && $selected !== true,
'pointer-events-none text-on-surface/38' => $disabled,
])
->merge(array_filter([
'role' => $selected === null ? 'menuitem' : 'menuitemcheckbox',
'aria-checked' => $selected === null ? null : ($selected ? 'true' : 'false'),
'aria-current' => $current ? 'page' : null,
'aria-disabled' => $disabled ? 'true' : null,
'tabindex' => '-1',
'type' => $isLink ? null : 'button',
@@ -62,6 +69,7 @@
$iconInk = match (true) {
$disabled => 'text-on-surface/38',
$selected === true => 'text-on-tertiary-container',
$current => 'text-on-secondary-container',
default => 'text-on-surface-variant',
};
@@ -69,13 +77,14 @@
blank($iconClass) => 'size-5 '.$iconInk,
$disabled => \Illuminate\Support\Arr::toCssClasses(['size-5', $iconClass, 'text-on-surface/38!']),
$selected === true => \Illuminate\Support\Arr::toCssClasses(['size-5 [:where(&)]:text-on-tertiary-container', $iconClass]),
$current => \Illuminate\Support\Arr::toCssClasses(['size-5 [:where(&)]:text-on-secondary-container', $iconClass]),
default => \Illuminate\Support\Arr::toCssClasses(['size-5 [:where(&)]:text-on-surface-variant', $iconClass]),
};
@endphp
<{{ $tag }} {{ $attributes }}>
@if ($icon)
<x-livewire-material::icon :name="$icon" :filled="$selected === true" :class="$leadingIcon" />
<x-livewire-material::icon :name="$icon" :filled="$selected === true || $current" :class="$leadingIcon" />
@endif
<span class="min-w-0 flex-1">
@@ -86,6 +95,9 @@
@endif
</span>
@if ($badge !== null && $badge !== false && $badge !== '')
<x-livewire-material::badge :value="$badge === true ? null : $badge" class="shrink-0" />
@endif
@if ($shortcut)
<span @class(['shrink-0 type-label-sm', $iconInk])>{{ $shortcut }}</span>
@endif