Files
livewire-material/resources/views/components/navigation-rail-item.blade.php
T
Andreas Reinhold / reiniandClaude Sonnet 5 25549374ec Draw the navigation rail without Tailwind
Plan step 36 (navigation group, second batch): <x-navigation-rail>'s,
<x-navigation-rail-item>'s and <x-navigation-rail-section>'s class
lists move into navigation-rail.css, navigation-rail-item.css and
navigation-rail-section.css, keyed on data-md-navigation-rail (the
mode, data-md-width, data-md-align, data-md-hide-when-collapsed,
data-md-divider, data-md-fill, data-md-open), data-md-navigation-rail-
item (data-md-active) and data-md-navigation-rail-section. Every
Tailwind wrapper class in the view — the menu row's centring padding,
the FAB row, the two swapped menu glyphs, the brand's visibility —
becomes a hook the stylesheet draws instead; the menu button itself
renders the shared md-state-layer/md-focus-ring/md-touch-target
classes (N-01's pattern) since it draws its own layer on itself, not a
child. `<x-icon>` and `<x-badge>` take size and floating props instead
of size/position classes; a small data-md-navigation-icon hook (the
shared navigation-item.css) replaces the ad hoc "relative inline-flex"
wrapper a floating badge anchors to.

`rail-collapsed` (a Tailwind @custom-variant, forbidden in Phase F) is
reproduced as plain selectors, branch for branch: the three width-
independent conditions (a fixed collapsed mode; a collapsible rail the
visitor collapsed and not open; a modal rail not open) merge into one
:where() group, provably the same match set as three separate rules
since :where(A, B, C) on an element is true exactly when :where(A) or
:where(B) or :where(C) is; the four width-gated conditions stay
separate media blocks, since CSS cannot merge different `@media`
queries. Every rem length becomes px, since these are dp-based M3
tokens, not a text measure (unlike <x-pane>'s rem widths). The FAB
overrides for a rail's header — elevation 0 (N-03), morphing into an
extended FAB instead of swapping two by display (N-23) — move from
unlayered into this file's own material.components, like toolbar.css's
FAB override: fab.css's `[data-md-fab]` is one attribute, so a doubled
selector here always outranks it without needing to sit outside the
layer.

navigation.js: the arriving-indicator stylesheet and every code comment
follow the new hooks; resources/css/components/navigation.css is
deleted (nothing imports it any more) and its line in tailwind.css with
it. Behaviour is unchanged except one thing Tailwind's `rail-collapsed:`
variant could do that plain CSS in this shape cannot: it is gone for
consuming applications too, since the definition lived only in the file
this commit removes. The development skill's guidance for it is
rewritten to point at navigation-rail.css's own selectors instead of
teaching a Tailwind variant that no longer exists.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 00:09:06 +02:00

75 lines
3.2 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{{-- One destination in an `<x-navigation-rail>`, drawn in whichever shape the rail has.
<x-navigation-rail-item label="Inbox" icon="inbox" link="{{ route('inbox') }}" :active="request()->routeIs('inbox')" badge="12" />
Expanded, it is a 56px full-width pill: the icon, the label-large label beside it and a count
at its end. Collapsed, the icon sits in a 56×32 indicator over a label-medium label (two lines
at most), with the count on the icon. The current destination (`active`) is
`aria-current="page"` with the filled icon on secondary-container, its label in secondary when
collapsed.
`link` renders an anchor with `wire:navigate` (unless `external` or `no-wire-navigate`);
without one it is a button. `badge`: `true` for M3's small dot on the icon, a number or a few
characters for the large badge (999+ at most). Screen readers hear a count after the label
(", 12"); `badge-label` replaces it with words ("12 unread") and gives a dot something to say.
Values from NavigationRailVerticalItemTokens.kt, NavigationRailHorizontalItemTokens.kt,
NavigationRailBaselineItemTokens.kt and NavigationRailColorTokens.kt (androidx Compose
Material 3, Apache-2.0); see `<x-navigation-rail>`. Compose's expanded item hugs its label; the
package draws the full-width pill, which leaves room for the count at the end. --}}
@props([
'label' => null,
'icon' => null,
'link' => null,
'external' => false,
'noWireNavigate' => false,
'active' => false,
'badge' => null,
'badgeLabel' => null,
])
@php
$isLink = filled($link);
$tag = $isLink ? 'a' : 'button';
$dot = $badge === true;
$count = ! $dot && $badge !== null && $badge !== false && $badge !== '';
$spoken = $badgeLabel ?? ($count ? $badge : null);
$attributes = $attributes->merge(array_filter([
'href' => $isLink ? $link : null,
'target' => $isLink && $external ? '_blank' : null,
'rel' => $isLink && $external ? 'noopener' : null,
'wire:navigate' => $isLink && ! $external && ! $noWireNavigate && ! $attributes->has('wire:navigate') ? true : null,
'type' => $isLink ? null : 'button',
'aria-current' => $active ? 'page' : null,
'data-md-active' => $active ? true : null,
], fn ($value): bool => $value !== null));
@endphp
<{{ $tag }} data-md-navigation-rail-item {{ $attributes }}>
<span data-md-navigation-indicator>
<span data-md-navigation-icon>
@if ($icon)
<x-livewire-material::icon :name="$icon" :filled="$active" />
@endif
@if ($dot)
<x-livewire-material::badge floating />
@elseif ($count)
<span data-md-navigation-badge-icon><x-livewire-material::badge :value="$badge" max="999" floating /></span>
@endif
</span>
</span>
<span data-md-navigation-label>{{ $label ?? $slot }}</span>
@if ($count)
<span data-md-navigation-badge-end><x-livewire-material::badge :value="$badge" max="999" /></span>
@endif
@if ($spoken !== null)
<span class="md-visually-hidden">, {{ $spoken }}</span>
@endif
</{{ $tag }}>