Draw the sort header without Tailwind

<x-sort-header> renders data-md-sort-header (data-md-active on the
sorted column) with its button and arrow as data-md-* attributes; the
button keeps md-focus-ring and md-touch-target, the arrow takes size 16,
and the hover and hint-arrow states move into components/sort-header.css
(plan step 36). A browser test presses the button at the edge of its
48px target.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
Andreas Reinhold / reini
2026-09-14 15:07:37 +02:00
co-authored by Claude Opus 5
parent 25e1bb4788
commit c68a1fdbef
5 changed files with 85 additions and 16 deletions
@@ -3,10 +3,12 @@
Pressing it sorts by this column, ascending first and then flipping; the arrow says which way,
and `aria-sort` says it to a screen reader. A column that cannot be sorted is a plain `<th>`.
`class` lands on the `<th>` (`text-end` for a number column moves the button with it).
`class` lands on the `<th>` (end-aligning a number column moves the button with it).
The button is only as tall as its title-small line, so it carries `touch-target` and catches
presses over M3's 48px minimum; the cell's own padding belongs to the cell, not to it. --}}
The button is only as tall as its title-small line, so it carries `md-touch-target` and catches
presses over M3's 48px minimum; the cell's own padding belongs to the cell, not to it. The cell
renders `data-md-sort-header` (`data-md-active` on the sorted column) and the button
`data-md-sort-header-button`, drawn by resources/css/components/sort-header.css. --}}
@props([
'sortBy' => [],
@@ -20,21 +22,14 @@
$next = $active && $direction === 'asc' ? 'desc' : 'asc';
@endphp
<th @if ($active) aria-sort="{{ $direction === 'asc' ? 'ascending' : 'descending' }}" @endif {{ $attributes }}>
<th data-md-sort-header @if ($active) data-md-active aria-sort="{{ $direction === 'asc' ? 'ascending' : 'descending' }}" @endif {{ $attributes }}>
<button
type="button"
wire:click="$set('{{ $model }}', {{ json_encode(['column' => $column, 'direction' => $next]) }})"
data-sort-header
@class([
'group/sort focus-ring touch-target inline-flex cursor-pointer items-center gap-1 rounded-corner-xs',
'text-on-surface' => $active,
'hover:text-on-surface' => ! $active,
])
data-md-sort-header-button
class="md-focus-ring md-touch-target"
>
{{ $slot }}
<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)',
'opacity-0 group-hover/sort:opacity-60 group-focus-visible/sort:opacity-60' => ! $active,
])" />
<x-livewire-material::icon :name="$direction === 'desc' ? 'arrow_downward' : 'arrow_upward'" size="16" data-md-sort-header-arrow />
</button>
</th>