Plan step 36 (containment group): <x-drawer>'s class lists move into
resources/css/components/drawer.css, keyed on data-md-drawer (with
data-md-open, data-md-standard) and data-md-drawer-scrim/-sheet
(data-md-side)/-head/-head-row/-heading/-title/-subtitle/-close/-body/
-actions. The modal sheet slides in on the emphasized-decelerate
easing and out on emphasized-accelerate (an asymmetric CSS transition
expressed by giving the open-state selector its own
transition-duration/-timing-function, which the "after-change style"
rule picks up); the sheet stays x-show-driven, with @starting-style +
transition-behavior: allow-discrete taking the place of x-transition's
enter/leave classes, matching resources/css/components/search.css's
already-rewritten scrim/view. A standard sheet drops the scrim and
trap from 840px and only fades in place (an in-flow box does not
travel); data-md-drawer-collapsed (from the view's `wide` Alpine
state) takes it out of the layout while closed there.
<x-drawer pane> is removed: step 35's <x-list-detail> is M3's form of
that job (the plan's decision, docs/plans/material-3-alignment.md
"Decisions"). Removed the prop, its CSS classes and its tests
(OverlayTest's two pane-only tests deleted, the others' pane cases
dropped); pointed each mention at <x-list-detail> in the material-3
guideline, the material-3-design and livewire-material-development
skills, and added a line to UPGRADE.md's 2.0.0 notes. No other living
file named `<x-drawer pane>` (grepped views, tests, README, boost,
workbench excluding build output); docs/plans and docs/audits keep
their history unchanged.
Imported from the Containment block of components.css, importing
button.css and divider.css for what the view renders.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (containment group): <x-modal>'s class lists move into
resources/css/components/modal.css, keyed on data-md-modal (with
data-md-fullscreen) and data-md-modal-box/-bar/-head/-title/-subtitle/
-body/-content/-actions. Sizes stay px (560/280/48px matching
DialogTokens.kt/FullScreenDialogTokens.kt and the 56px full-screen
header, C-17), spacing the measurement tokens, breakpoints a literal
600px range query.
The phone-only header bar and the pinned head/actions keep their
existing responsive behaviour (a subtitle or an icon keeps the head on
screen below 600px; only the bar's own divider-eligibility needs a
computed flag, since it alone can sit beside a head that is also on
screen) — reproduced with :has() against data-md-icon/
data-md-modal-subtitle wherever that is self-contained, and with an
explicit data-md-modal-divider attribute where it is not. The
scroll-driven dividers rename data-dialog-head/-actions ->
data-md-modal-head/-actions, data-overflow-top/-bottom ->
data-md-overflow-top/-bottom (resources/js/dialog.js); the
dialog-dividers Alpine directive keeps its name, since it isn't a
data-md-* hook.
resources/css/components/dialog.css (Tailwind-era) is replaced by
modal.css, imported from the Containment block of components.css;
its import leaves resources/css/tailwind.css. No other component
renders <x-modal> or dialog.css's rules (checked: datepicker,
timepicker and search draw their own dialog-shaped surfaces
independently).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (containment group, last of its four components): moves
into resources/css/components/list.css (the container: display, the
2px segmented gap) and list-item.css (everything else — anatomy,
states, the segmented item's own background/corner, and the row
interaction pattern list.css used to carry). One commit for both:
list-item.css keys heavily on list's own data-md-list="segmented"
attribute and the two files are designed together.
Kept, now on data-md-* hooks: selectable/selection listbox semantics
(role="listbox"/"option", aria-selected, a trailing check as the
second cue, C-03), a disabled item's link dropped entirely rather than
left focusable (C-02), the segmented row's own state layer no longer
racing its background through an unlayered escape hatch (C-08 doesn't
recur — nothing here is unlayered), leading video/icon/avatar slots,
three-line top alignment and 12px vertical padding (C-15), 16px
leading/trailing gap (C-14), dividers inset 16px (C-16), the segmented
list's surface fill (C-21) and its hover/focus/pressed/selected corner
morph (C-22). The segmented icon size (20px) is now the `size` prop
`<x-icon>` already takes from the parent's @aware(['segmented']),
replacing the CSS override C-26's fix suggested.
data-md-lines="1|2|3" replaces the old line-count classes; data-md-list-item-*
hooks replace every other class list.
Hooks renamed data-list -> data-md-list, data-list-item -> data-md-list-item,
data-dividers -> data-md-dividers, data-selected -> data-md-selected, the
generic data-list-row/-open row pattern's last consumer (list-item)
moved, so list-rows.js drops the transitional dual-hook fallback the
card commit added. table.css now @imports list-item.css, completing
what its own comment already claimed (a row answers a pointer as a
list row does) under the new per-component-import architecture.
Imported from the Containment block of components.css; leaves
tailwind.css, which now carries only dialog.css for this group.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (containment group): <x-card>'s class lists move into
resources/css/components/card.css, keyed on data-md-card (per variant),
data-md-card-figure/-body/-header/-heading/-title/-subtitle/-menu/
-content/-actions. A row (data-md-list-row) renders the shared
md-state-layer and md-focus-ring classes for its tint and ring
(foundation/interaction.css) instead of copying their rules; card.css
adds only the per-state elevation the shared class has no opinion on
(ElevatedCardTokens.kt/FilledCardTokens.kt/OutlinedCardTokens.kt), the
press/hover exclusion for a card's own nested buttons, and the ring for
a non-actionable row whose focus lands on its opener rather than the
card. data-md-dragged is read automatically by the shared class's own
16% tint; card.css only adds its elevation levels 3/4.
Hooks renamed data-list-row -> data-md-list-row, data-list-open ->
data-md-list-open, data-list-actionable -> data-md-list-actionable,
data-dragged -> data-md-dragged, updated in the same commit:
resources/js/search.js and list-rows.js's consumers, table.css's
comment, the showcase (index, data and containment sections),
tests/Browser/ContainmentTest.php, and the development skill's card
and table examples.
Imported from the Containment block of components.css.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (containment group): <x-collapse>'s Tailwind-era
resources/css/components/collapse.css (the height animation, C-24)
becomes the plain stylesheet, keyed on data-md-collapse and
data-md-variant; the summary renders the shared md-state-layer and
md-focus-ring classes instead of copying their rules. Leaves
tailwind.css for the Containment block of components.css.
Icon-to-title and title-to-chevron gaps stay 12px (not an M3 number;
collapse isn't an M3 component), written as the average of the two
adjacent spacing steps since the scale has no exact token for it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (containment group): <x-divider> moves its class lists
into resources/css/components/divider.css, keyed on data-md-divider,
data-md-orientation, data-md-inset/middle and data-md-divider-heading/
-text for the subheader form (DividerTokens.kt; docs/reference/m3
§ Divider). Imported from the Containment block of components.css.
Hooks renamed: none of divider's own (it had no data-* hooks before),
but its rendered attribute order moved data-md-* ahead of role/aria-*,
which ToolbarTest.php's regex for a toolbar's vertical divider depended
on; fixed in the same commit. form.css picks up the @import './divider.css'
its TODO(step 36) marker was waiting on, since form.blade.php renders
<x-divider> and divider.css now carries the material.components layer.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
tests/Feature/Components/ContainmentStylesheetsTest.php is the
containment twin of InputStylesheetsTest.php and ActionStylesheetsTest.php:
same shape, import and ViewClasses checks, keyed on the "Containment"
block of components.css. Its dataset starts empty and grows by one name
per component commit in this stream (plan step 36).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 review of d46b07c1. The landscape queries moved from rem
to px (560/364/346, equal at the default 16px) while every size they
make room for stayed in rem, so with a larger browser text size the
dial grew past a threshold that no longer moved with it. The sizes are
px now, as M3 gives them in dp, spacing the measurement tokens, the
selected period corner corner-md, and disabled the 38% and 12% state
tokens; the supporting text's two-line room stays in rem, being text.
The header's arithmetic is in px, and its note on targets says why the
period halves render no md-touch-target instead of claiming nothing
here reaches 48px.
The landscape browser test expected the display and the dial to share a
top edge, but the display is centred in the dial's 256px row, 61px
lower; it now checks the display sits inside that row with the dial
36px after it. Feature tests pin the classes and the three queries.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 review of 72d9dbfc. The year, the three menu buttons and
the docked lists' options hand-rolled md-state-layer and md-focus-ring
on themselves, in their own ink, on the claim that each was a bigger box
than an indicator inside it; only the day is. They now render the
classes, with the option's inset ring as the one refinement. The day
keeps its own layer on the 40px indicator, now at M3's state-opacity
tokens, gone from a disabled or blank day (a focused disabled day still
showed it), and in its own stacking context so it lies over a chosen
day's primary fill instead of under it.
The month announcement is md-visually-hidden again, not a hook with a
copy of its clip. The stylesheet kept every length in rem although the
commit said px: sizes are now px, spacing the measurement tokens, and
disabled ink the 38% state token, as search and slider were drawn. The
view header's "below sm" is 600px, medium.
Six browser assertions still read the old unprefixed hooks
(dataset.presentation, data-selected, data-between, data-start,
data-end); one of them would have passed vacuously.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 review. 52fd65a3 replaced the md-visually-hidden class on
field's spoken counter, search's result count and a bound filter chip's
native checkbox with hooks carrying copies of its clip, on the reading
that a package view writes no class but the interaction classes. The
brief and step 34 let a view write the text classes too, and one shared
source beats three copies: the class is back, the copies and the
data-md-field-live and data-md-chip-native hooks are gone
(data-md-search-status predates them and stays). chip.css keeps its
tooltip.css import.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 review. The brief and step 34 let a package view write the
md-* text classes beside the three interaction classes, but both
stylesheet tests allowed only the latter, and each kept its own copy of
the check. tests/Support/ViewClasses.php is now the one rule: the
interaction classes, the text classes read from text.css, and a caller's
class handed on whole. It also closes the holes the copies left: a
single-quoted :class, an echo mixed into a class list, a hint-, icon- or
box-class literal, a 'class' => entry that is not a plain literal, and
->class('…') as a string.
InputStylesheetsTest's exemptions narrow too: the autofill transition is
skipped in field.css alone; an orientation query may name any px height
but never a width off a breakpoint; a box-shadow must be none, an
elevation level, or a 0 0 0 ring in a colour role.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (inputs group, last of the "not started" pair): <x-timepicker>
renders data-md-timepicker-* throughout (data-md-mode, data-md-cycle,
data-md-view, data-md-inner, data-md-dragging, data-md-disabled,
data-md-error and data-md-value replace their unprefixed hooks), and
timepicker.css moves into material.components, importing field.css,
icon.css and button.css. The dialog's Tailwind utility class (position,
the scrim, the spatial-fast pop and its @starting-style) becomes plain
declarations under data-md-timepicker-dialog, matching the datepicker's
modal dialog. The landscape rule stays keyed on orientation and viewport
height, in px (560/364/346), never a breakpoint, as M3 asks.
The hour and minute boxes and the period buttons swap their Tailwind
`state-layer focus-ring` utilities for `md-state-layer md-focus-ring`:
same declarations, so nothing else in the stylesheet changes for them.
The dial swaps in `md-focus-ring` for its own hand-rolled, byte-identical
outline-on-:focus-visible rule, which timepicker.css no longer carries.
The period selector was already the radio-in-a-list M3's labelling table
asks for (role="radio" in a role="radiogroup", moved by the arrow keys),
and Shift+M/Y already reached the date picker's dropdowns — both
predate this rewrite.
field.css's data-md-field-button note is tidied now that every component
that selects it (input, password, datepicker, timepicker) is rewritten.
Browser tests written but not run (the inputs group's Chromium run
follows this stream): the period radios moving with the arrow keys, and
the dial lying on its side below the landscape height threshold.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (inputs group): <x-datepicker> renders data-md-datepicker
with every part data-md-datepicker-* (data-md-presentation replaces
data-presentation; data-md-range, data-md-docked, data-md-concealed and
data-md-value replace their unprefixed hooks), and datepicker.css moves
into material.components on the same px, spacing and colour tokens,
importing field.css, icon.css and button.css for what the view renders.
The month's live-announced text drops its `sr-only` class for a
data-md-datepicker-month-live hook with the same visually-hidden clip
(a package view writes no class but the interaction classes), matching
field's and search's counter/status live regions. The day, year, menu
button and list option keep their own hand-rolled state layer and focus
ring rather than the foundation's classes, as data-md-field-button
already does: each is a bigger interactive box than the round indicator
drawn inside it, which the foundation's fixed-geometry classes can't
draw, and `:focus-visible` only ever matches the actually-focused
element. Cancel, OK and the icon buttons are <x-button>, which already
draws from the classes. datepicker.js follows the renamed hooks and
dataset properties.
Shift+M/Y (reaching the month/year dropdowns) and the full-screen range
picker at compact were already implemented; this commit adds the browser
tests the plan owed for them plus the month list's keyboard-driven growth
in both directions and Save/close from the full-screen app bar
(docs/plans/material-3-browser-tests.md), written but not run — the
inputs group's Chromium run follows this stream.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36, step 0 of the inputs stream: mirrors ActionStylesheetsTest's
five checks over the seventeen inputs components already rewritten, plus
pagination (its four views live under resources/views/pagination/**, not
components/, so it gets its own small versions of the two view-reading
checks). The "no class list" check also recognises the wrapper fields'
`:class="$attributes->get('class')"` (Blade's own prop-forwarding syntax,
the only way a component with no root element of its own hands the caller's
class to its field) and a `'class' => '…'` attribute-bag entry, and exempts
field.css's week-long autofill transition from the motion-token check.
The new test caught three already-rewritten components writing a bare
`md-visually-hidden` class, which a package view no longer does (only the
three interaction classes, or the caller's own class, reach a view): field's
character-count live region, search's result-count live region, and chip's
native checkbox under a bound filter chip. Fixed by giving each its own
data-md-* hook with the same visually-hidden declarations in its stylesheet,
and chip.css was also missing its `tooltip.css` import for the tooltip
chip.blade.php renders.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Follow-up to menu-item's move to the interaction classes (this batch): the
app bar's own test anchored a menu item's link right after <a, which the
item's new class="md-state-layer md-focus-ring" now precedes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 audit. field.css's trailing button (clear, copy, reveal)
scales its state layer with the field's own icon size and doubles it as
the touch target, which the foundation's fixed-size interaction classes
cannot draw, and the hook stays data-md-field-button rather than a class
because datepicker and timepicker, not yet rewritten, still select it —
so it keeps its own declarations. It never hid the layer on a disabled
button, unlike every rewritten component's copy; it now does.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 ("Interaction is the shared classes", the user, 2026-09-14).
A prior pass on this same step had hand-rolled the page step's state
layer, focus ring and touch target to keep the views free of a class
list; the user's decision reverses that: a live step (a link or button,
never the current-page or disabled span) now renders md-state-layer,
md-focus-ring and md-touch-target, and pagination.css keeps only the
step's own size, colour and shape.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 ("Interaction is the shared classes", the user, 2026-09-14).
The dismiss button hand-rolled md-state-layer, md-focus-ring and
md-touch-target, and alert.css's own header said so; it now renders the
classes and keeps only its own size, offset and colour.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 ("Interaction is the shared classes", the user, 2026-09-14).
The action and the close button hand-rolled md-state-layer, md-focus-ring
and md-touch-target, and toast.css's own header said so; both now render
the classes and keep only their own size and colour.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 ("Interaction is the shared classes", the user, 2026-09-14).
The trigger button (fab-menu.css) and each item (fab-menu-item.css)
hand-rolled md-state-layer and md-focus-ring; both now render the classes
and keep only their own colour, shape and motion. Neither needs
md-touch-target: the trigger and every item draw at 56px.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 ("Interaction is the shared classes", the user, 2026-09-14).
menu-item.css hand-rolled md-state-layer and md-focus-ring, including a
separate aria-disabled rule the class already carries; the row now renders
the classes and keeps only the inward -3px ring offset a row edge to edge
in the list needs. No md-touch-target: the row is already 48px tall.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 ("Interaction is the shared classes", the user, 2026-09-14).
A segment's hover and press opacities were group.css's own copy of
md-state-layer's declarations; the label now renders md-state-layer
(and, below medium's two smallest sizes, md-touch-target) and the
stylesheet keeps only what the class cannot draw: focus and disabled read
off the real radio or checkbox inside with :has(), not the label itself,
so the focus ring's layer tint and the disabled hide are kept as a
refinement (the latter a real gap the old rule left open). button-group.css,
which only shapes corners and spacing, needed no change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 ("Interaction is the shared classes", the user, 2026-09-14).
fab.css hand-rolled the same declarations as foundation/interaction.css's
md-state-layer and md-focus-ring; it now renders those classes and keeps
only its own hover elevation, colour and motion. Every FAB size draws at
56px or more, so it needs no md-touch-target.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 ("Interaction is the shared classes", the user, 2026-09-14).
button.css hand-rolled the same declarations as foundation/interaction.css's
md-state-layer, md-focus-ring and md-touch-target; it now renders those
classes (the touch target only at xs and sm, where it draws under 48px) and
keeps only its own geometry, colour and motion. The stylesheet test's "writes
no class list" check now allows the three interaction classes, in any subset,
and $attributes->class() as the way a root merges them with a caller's own
class; split-button and button-group, which nest a button, are updated to match.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36. fab-menu.css claimed the item sets no translate or opacity
of its own once it has entered; fab-menu-item.css sets both at rest, and
the closing rule wins on specificity, which the header now says.
fab-menu-item.css named the rule by the root's hook instead of the
popover's, and a render test named for the list's scroll asserted the
exit; it is named for what it checks.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36. The paginators put md-state-layer, md-focus-ring and
md-touch-target on their live steps, where the rest of the actions and
communication group (button, FAB, menu item, snackbar, alert) writes no
class list and carries those declarations keyed on its own hooks, as
the plan's data-md-* rule and the brief's "text classes, nothing else"
ask. pagination.css now draws them on a link or button step, the layer
hidden while Livewire disables previous and next; the views render no
class at all, which the render tests pin.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36. The old `state-layer` utility hid its layer on
`[aria-disabled='true']`; the menu item's hand-written layer dropped
that, so a disabled item, which stays focusable, drew the 10% focus
layer. Its 38% ink now reads --md-sys-state-disabled-content-opacity,
as button.css does, instead of a literal.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36. The rewrite drew Tailwind's `flex-1` (flex: 1 1 0%) as
`flex: 1 1 auto`. In a wrapping row that measures the text at its full
line length, so a long title pushed the action onto a line of its own
from 600px, where the two used to share one. The header also gave a
reason for drawing the buttons' state layer by hand that does not hold
(the layer is currentColor either way); it now says why: the view writes
no class list, as button.css.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36. menu.css left Tailwind's unlayered half for
material.components, where its `appearance: base-select` on the select
ties select.css's `appearance: none` in layer and specificity and loses
on order: select.css's block always follows the menu.css it imports. The
customizable select would have fallen back to the native list. The
select's own opt-in now sits in select.css's @supports block; menu.css
keeps the picker's. A test pins where each is written.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (actions, last of the batch). <x-empty-state> renders
data-md-empty-state with data-md-empty-state-illustration/-shape/
-icon/-title/-description/-actions; no class list. empty-state.css
draws the secondary-container shape behind the on-secondary-container
icon (the icon needs its own stacking context to paint above the
shape's absolute position), the title-large title and the 448px
body-medium description. The docblock's illustration example moves
from a caller class to a caller style, since the shape rule scans
the whole file. EmptyStateTest is rewritten on the hooks and
ComponentStylesheet.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (actions). <x-stat> renders data-md-stat with
data-md-stat-header/-value/-description/-extra; no class list.
stat.css draws the surface-container panel, the label-large header,
the value as an emphasized-headline-md hero with tabular figures, and
the body-small description. StatTest is rewritten on the hooks and
ComponentStylesheet.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (actions). <x-alert> renders data-md-alert with
data-md-color and data-md-alert-content/-title/-description/-actions/
-dismiss; no class list. alert.css draws the state's tinted container
colour across all eight colours, the medium corner, and the dismiss
button reaching M3's 48px target from its 40px (ACT-18) with its own
state layer, focus ring and touch target. AlertTest is rewritten on
the hooks and ComponentStylesheet.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (actions). <x-progress> renders data-md-progress with
data-md-color and data-md-circular/-wavy/-thick, and
data-md-value/-max in place of data-value/data-max; no class list.
progress.css draws LinearProgressIndicatorTokens and
CircularProgressIndicatorTokens' stroke, sizes and colours, and the
linear indicator's unconditional right-to-left mirror. Its sizing is
a default only, same as loading.css: a caller's own class, unlayered
or in Tailwind's utilities layer, still outranks it, so the width/
size detection regex the view carried is gone with it.
resources/js/progress.js's WATCHED array and its reads follow the
renamed hooks. ProgressTest (Feature and Browser) is rewritten on the
hooks and ComponentStylesheet.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (actions). <x-toast> renders data-md-toast with
data-md-position, its snackbar data-md-toast-snackbar with a bound
data-md-two-line, data-md-toast-content with a bound
data-md-toast-wrap, and data-md-toast-action/-dismiss; no class list.
toast.css draws SnackbarTokens' inverse surface, 48/68px container,
the two 40px controls reaching 48px targets with their own state
layer and touch target (ACT-18), the enter transition and the
position-start margin that grows from medium.
data-toast becomes data-md-toast-snackbar and data-toast-action
becomes data-md-toast-action (snackbar.js, navigation.js's
hide-on-scroll guard, NavigationBarTest, CommunicationTest); a new
data-md-toast-dismiss hook names the close button. ToastTest is
rewritten on the hooks and ComponentStylesheet. CommunicationTest
gains the owed tests: an actioned snackbar past the default timeout,
Escape on a focused one, the live region before any message, and the
two-line height with Alt+G reaching the action from elsewhere on the
page.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (actions). <x-rich-tooltip> renders data-md-rich-tooltip
with the bubble's data-md-rich-tooltip-bubble and data-md-side, and
data-md-rich-tooltip-title/-text/-actions on its plain text; no class
list. rich-tooltip.css draws RichTooltipTokens' surface-container,
medium corner, elevation 2, 312px bubble, its corner-to-corner anchor
positioning and its fade, the same shape as tooltip.css.
rich-tooltip.js's leave grace goes from 200ms to M3's 1.5s (ACT-25),
matching tooltip.js. RichTooltipTest is rewritten on the hooks and
ComponentStylesheet.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
<x-fab-menu> renders data-md-fab-menu with its trigger
data-md-fab-menu-trigger, data-md-color and data-md-position; a
close icon swaps in on aria-expanded instead of Tailwind's group
variant. fab-menu.css draws FabMenuBaselineTokens' 56px trigger,
its round-to-full morph on the default springs (ACT-19), the 8px
list padding and its calc(100dvh-128px) scroll cap behind the fixed
close button (ACT-32), and the [data-md-fab-menu-popover]:not(:popover-open)
rule that drives fab-menu-item's exit (ACT-33) — actions.css's last
two rules, moved here, empty it, so it and its tailwind.css import
are deleted; fab.js's stale reference to it is corrected to fab.css
(plan step 36, actions — the group is now fully off Tailwind).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
<x-fab-menu-item> renders data-md-fab-menu-item and data-md-color
with no class list; fab-menu-item.css draws FabMenuBaselineTokens'
56px pill, 24px sides, 24px icon, full corner and elevation 3, and
the entry/exit rise-and-sink transition (ACT-33) that fab-menu.css's
[data-md-fab-menu]:not(:popover-open) rule will drive once the FAB
menu itself is rewritten. Its tests move out of FabTest.php into a
new FabMenuTest.php (plan step 36, actions).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
<x-menu> renders data-md-menu with data-md-sheet-at-compact, and its
popover data-md-menu-popover with data-md-position and data-md-vibrant;
menu.css caps a long menu at 288px so it scrolls instead of running
off the top layer (ACT-04) and ties the open/close transition to the
trigger's corner (ACT-26), restyles a submenu to open in place inside
a sheet-at-compact sheet, and still carries the dropdown a select's
picker and a searchable choices field draw, so select.css and
choices.css finish their step-36 TODO import. actions.css sheds the
state-transition-fast and popover-transition utilities menu-item and
menu no longer use; state-transition-default and [data-fab-menu]
stay for fab-menu's own rewrite.
data-menu, data-menu-filter, data-menu-sheet, data-menu-empty,
data-sheet-at-compact, data-vibrant and data-active are renamed to
data-md-*, also in menu.js, split-button.css (which renders a
<x-menu> and now imports its stylesheet) and AppBarTest's overflow
menu (plan step 36, actions).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
<x-menu-item> renders data-md-menu-item, data-md-description and its
aria-* state, with data-md-keep-open renamed from data-keep-open;
menu-item.css draws SegmentedMenuTokens' 48px row, 16px sides, 12px
gaps, 4/12px corners, the selected/current colours and the shared
--md-menu-item-ink icon-class outranks except when disabled
(ACT-11/19/27/28); a submenu's popover shares its container colour
with the menu it opens from and menu.js follows the rename (plan
step 36, actions).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
<x-menu-group> renders data-md-menu-group with data-md-gap and no
class list; menu-group.css draws the plain cluster's 4px padding and
the gapped Expressive layout's 2px item gap and 8px cluster margin
from SegmentedMenuTokens (plan step 36, actions).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Laravel's and Livewire's paginator views render data-md-pagination
hooks; pagination.css draws the row, the 40px steps with their 48px
targets, the selected page and the swap to "Page n of m" below 600px
(plan step 36, inputs). It imports button.css for the simple
paginators, now that the button is rewritten.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
The separator renders data-md-menu-separator and takes its 1px line,
8px block margin and 16px inset from menu-separator.css in
material.components (plan step 36, actions).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
<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
<x-table> renders data-md-table with data-md-size and data-md-dense,
and table.css moves into material.components on px and spacing tokens,
its cell selectors still inside :where() so a caller's cell rule wins
(plan step 36). The showcase's layout table follows the hook. A browser
test pins 52px rows and 36px dense ones.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36. <x-fab> renders data-md-fab with data-md-size,
data-md-color, data-md-variant, data-md-extended and, collapsing on
scroll, data-md-collapse-on-scroll with a bound data-md-collapsed; it
passes its glyph's size to <x-icon>. fab.css draws FabBaseline/Medium/
LargeTokens and ExtendedFab*Tokens per size, the container and filled
colours, elevation 3 and 4 on hover, the state layer and focus ring,
and the collapse morph that leaves actions.css.
data-fab becomes data-md-fab, also in navigation.css (the rail flattens
a nested FAB) and toolbar.css (a docked toolbar's FAB), with their tests.
The actions browser test gains the owed collapse-on-scroll test.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
<x-search> renders data-md-search with data-md-trigger and binds
data-md-open and data-md-full-screen; its parts become data-md-search-*
and the empty row data-md-search-empty. search.css moves into
material.components on px, spacing and state tokens (plan step 36).
search.js and the showcase layout follow the renamed hooks.
Browser tests cover the polite result count as it changes, the icon
entry point expanding full screen and returning focus, and suggestions
swapping to results on the first key.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36. <x-split-button> renders data-md-split-button and
data-md-size on the pair and data-md-split on its halves. split-button.css
draws SplitButton*Tokens' shape — full outer corners, inner corners that
grow under the finger, the trailing half round while its menu is open —
the narrower leading padding and 48px trailing half at xs and sm, and the
chevron's own size, nudge and turn on the standard scheme. groups.css is
empty and leaves tailwind.css.
The actions browser test follows data-md-split and data-md-button-group,
and gains the owed tests for a connected segment's 48px edge and a
button group's single-required and multi selection.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
<x-slider> renders data-md-slider with its size, colour, value label,
orientation and centring as data-md-* attributes and its parts as
data-md-slider-*; every size, colour role, disabled state, the vertical
quarter turn, RTL mirroring and the no-script fallback move into
components/slider.css as custom properties per size and colour (plan
step 36). slider.js reads the props from the root and marks the
renamed handle, tick and icon hooks.
Browser tests pin the handle centred on the track at each size, Space
held with an arrow, and the vertical slider's drag along Y, keys and
value label beside the handle.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9