Draw the chip set without Tailwind

<x-chip-set> renders data-md-chip-set (data-md-scroll), its row, label,
hint, errors and scroll buttons as data-md-* attributes, and its row,
fade mask, scroll padding and pointer-fine buttons move into
components/chip-set.css on tokens (plan step 36). chips.js marks the
row data-md-scroll-start and data-md-scroll-end. Browser tests cover the
scroll buttons in LTR and RTL and the arrow keys with a roving tab stop
that scrolls the focused chip clear of the button.

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 14:54:41 +02:00
co-authored by Claude Opus 5
parent d8072d878a
commit 55cf8ceed7
6 changed files with 259 additions and 43 deletions
+5 -5
View File
@@ -6,8 +6,8 @@
* chip taking itself off the page. Before that, focus moves to the chip before it (Backspace) or
* after it (Delete), because a focused element that disappears leaves focus on the body.
*
* A scrolling set marks the edges it can still scroll towards (`data-scroll-start`,
* `data-scroll-end`), and the set fades those edges and — where the pointer is fine, so there is no
* A scrolling set marks the edges it can still scroll towards (`data-md-scroll-start`,
* `data-md-scroll-end`), and the set fades those edges and — where the pointer is fine, so there is no
* swipe — puts a button over each of them, which is the visible affordance M3's chips accessibility
* page asks a scrolling row for. Its row carries `wire:ignore.self`, so a Livewire morph keeps the
* marks. Wrapping, the row is the element with `x-data`; scrolling, it is that element's `row` ref,
@@ -68,7 +68,7 @@ document.addEventListener('alpine:init', () => {
handOffFocus(backwards) {
const chip = this.$root
const set = chip.closest('[data-chip-set]') ?? chip.parentElement
const set = chip.closest('[data-md-chip-set-row]') ?? chip.parentElement
const chips = [...(set?.querySelectorAll('[data-md-chip]') ?? [])]
const index = chips.indexOf(chip)
const before = chips.slice(0, index).reverse()
@@ -140,8 +140,8 @@ document.addEventListener('alpine:init', () => {
const travelled = Math.abs(row.scrollLeft)
const room = row.scrollWidth - row.clientWidth
row.toggleAttribute('data-scroll-start', travelled > 1)
row.toggleAttribute('data-scroll-end', room - travelled > 1)
row.toggleAttribute('data-md-scroll-start', travelled > 1)
row.toggleAttribute('data-md-scroll-end', room - travelled > 1)
}
// A filter chip's check changes its width without resizing the row.