Plan step 38: the Alpine-scope wrapper divs become <x-row>/<x-stack>
carrying x-data directly, giving Filter and Input chips M3's
full-width stretch in place of w-full/space-y-6. The narrow demo that
forces its chip set to scroll moves to a data-md-showcase-chip-scroll
hook (384px, Tailwind's old max-w-sm).
Every id, value and derived aria-label the Chromium ChipsTest
selectors read (#chips, the filter checkboxes' values, "Remove
<email>") is unchanged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 38: the "bind" example's wrapper div becomes <x-stack>/
<x-row>, and the one enlarged circular indicator moves to a
data-md-showcase-progress-lg hook. Along the way, $examples changes
from a ['Title' => [code, stack]] tuple back to the plain
'Title' => <<<'BLADE' every other section uses, with the row
exceptions listed separately: the tuple's leading `[` broke
Sections::index()'s regex, so every one of this section's examples
was silently missing from the showcase search index. Fixing the shape
recovers them — the Feature ShowcaseTest's search-index assertion
count moves from 259 to 266.
Every aria-label the Chromium ProgressTest reads off #progress
(Linear, Circular, Circular wavy, Bound, the indeterminate variants)
is unchanged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 38: the badge/icon wrapper spans move to
style="position: relative; display: inline-flex", the exact pattern
<x-badge>'s own header documents; Alerts, Stats and the two empty
states move onto <x-stack>/<x-grid> in place of w-full and a
Tailwind grid, so a caller no longer sizes each child by hand. The
"plain" badge (no colour of its own, by design) and the illustration's
two decorative fills move to data-md-showcase-* hooks, as does the
tooltip demo span's focus-ring corner; the shortcut kbds now use the
data-md-showcase-kbd hook the frame batch already drew.
No demo content removed; #communication, the toast/snackbar hooks and
the rich-tooltip triggers (Press for details, About expiry) the
Chromium CommunicationTest selectors read are unchanged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 38: text moves onto the md-* classes; the "Icons in their
own colour" example's two roles outside the fixed md-ink-* set
(tertiary, secondary) move to a data-md-showcase-icon-color hook in
showcase.css targeting the item's [data-md-icon] child, since
icon-class only reaches roles the fixed set already names. Swimming's
info icon keeps icon-class, now icon-class="md-ink-info".
Every id, aria-label and role selector the Chromium ActionsTest
selectors read against /material/menus (#menus, the More/Sort/Share/
Assign to/Photo triggers, the submenu and sheet-at-compact behaviour)
is unchanged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 38: every example moves onto the layout components and the
md-* text classes. Wrapper divs that existed only to give an Alpine
scope to a button group or a connected <x-group> ("A connected group
that owns its selection", "Square groups", "A choice as a connected
group") become <x-stack>/<x-grid> carrying the x-data directly, which
also gives them M3's full-width stretch in place of w-full. The
loading indicator's two sized, tertiary-coloured variants and the FAB
menu's bounded demo box move to data-md-showcase-* hooks in
showcase.css; hint-class="text-warning" becomes hint-class="md-ink-warning",
the exact example the <x-group> header itself documents.
No demo content removed; every id, aria-label and input[name] the
Chromium ActionsTest selectors read (#buttons, showcase-theme,
showcase-days, the "Tonal" tooltip, the progressbar, collapse-on-scroll,
data-md-selection) is unchanged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 38: the Layout section's Overview keeps its breakpoint
table and live "This window" readout (now a shared
<x-showcase::breakpoint-readout> partial, and the five breakpoint
cards a real range-queried data-md-showcase-breakpoint-card instead
of Tailwind's max-medium:/medium:max-expanded: variants), but the
three canonical layouts move off it onto a page of their own:
/material/layout/{list-detail,supporting-pane,feed}
(ShowcaseController::layout(), routes/showcase.php), each built on
the real component, sharing one <x-section-nav> Sections::
layoutPages() feeds. The search index gains an entry per page with
its own URL, and the component-homes scan now also reads the three
pages' own source so <x-list-detail>, <x-supporting-pane> and
<x-feed> keep a home.
The whole section moves onto the layout components and md-* classes:
no bespoke Tailwind grid or table styling remains.
LayoutTest's browser test now visits the three dedicated pages
instead of one shared /material/layout; a new Feature test asserts
each page renders and is linked from the Layout section.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 38: the filter row is now real components — <x-input
icon="search"> and two <x-checkbox>s — instead of a bespoke bordered
label and accent-coloured native inputs; the result grid is
<x-grid min-item="120px"> of <x-surface as="li">/<x-stack> tiles.
The fetched symbol's mask stays the view's own x-bind:style (its
shorthand depends on the fetched name), but the size it toggles
between 24 and 20px is now the data-md-showcase-icon-size attribute
a script sets, never a class, drawn by data-md-showcase-icon-mask in
showcase.css.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 38: the two demo buttons are now real <x-button>s (filled
for Move, text for Change the figure) instead of a hand-rolled state-
layer/focus-ring button; each spring's row and its travelling dot
move onto <x-surface>/<x-stack> plus data-md-showcase-motion-row/
-track/-dot in showcase.css — only the dot's size and colour are
fixed there, since the transition under test stays the spring token
in the view's own inline style.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 38: the shadow-level grid moves onto <x-surface> and
<x-grid>; each tile is a data-md-showcase-elevation-tile keyed on
data-md-elevation, drawing var(--md-sys-elevation-1..5) — the same
tokens the label now names instead of the retired shadow-elevation-*
utility.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 38: the corner grid and the shape gallery move onto
<x-grid> and <x-stack>; each corner swatch is a data-md-showcase-
shape-swatch keyed on data-md-corner, one showcase.css rule per
corner token; <x-shape :size="80"> replaces the size-20 utility, its
fill the caller's style="color: var(--md-sys-color-secondary-
container)" since a shape's colour is inherited text colour, not a
class.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 38: the typescale listing moves onto <x-surface> for its
container and a bespoke data-md-showcase-type-row (a fixed label
column beside two flexible samples from 840px, one column below it,
a rule between rows) since no layout component draws an asymmetric
grid track; the sample text itself is styled by the very md-type-*
class names the row's own label prints, so the code sample and the
rendered specimen are the same string.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 38: swatches move from Tailwind's bg-* role utilities to
data-md-showcase-swatch spans whose fill is the caller's own inline
style="background: var(--md-sys-color-{role})" (a swatch's colour
depends on the role it shows, so that one declaration cannot be a
class), sized and edged in showcase.css; the two grids and every
per-theme panel move onto <x-grid>, <x-surface> and <x-stack>; text
onto the md-type-*/md-ink-* classes. The legacy "Ink and lines" group
(bg-body, bg-meta, bg-quiet, bg-structure, bg-chrome, bg-divider) is
dropped — those Tailwind ink/line names end with Phase F and their
roles already show under Surface.
ThemeTest and ColourProfilesTest read the swatch's computed colour
through the new data-md-showcase-swatch="primary" hook instead of the
retired .bg-primary class.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 38 (first batch): layout.blade.php, index.blade.php,
section.blade.php, shell.blade.php and the example component now
lay out through the layout components (pane, stack, row, grid,
surface) and their props, text through the md-* classes, and
everything else through showcase.css's data-md-showcase-* hooks — no
Tailwind utility remains in any of them. The head links the new
stylesheet route after @vite(...), not before: that entry opens the
document's first @layer statement, and linking the showcase's own
bundle ahead of it re-anchors the material layer before Tailwind's
own and lets its preflight's `padding: 0` beat the search view's
padding-top (found by a failing browser test). @vite(...) stays, its
Tailwind CSS entry included, only for the section views (still
Tailwind until the next batches) and the JS runtime; the config
comment says so.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Review leftover (plan step 36): the searchable list's highlighted-row
hook was still the plain pre-Phase-F name, in both the view's binding
and its scroll-into-view lookup and the shared rule in menu.css that
paints it. No test named it directly, so none needed a change.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36, navigation review. navigation-rail.css claimed each FAB
override beats fab.css by specificity alone; the collapsed FAB's gap
and minimum width tie with fab.css's extended-size rules and win by
order, which the file's own import of fab.css guarantees. The rail
view pointed at one file for the collapsed conditions that four files
write out. UPGRADE.md now says applications lose the `rail-collapsed:`
variant the development skill used to teach.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36, navigation review. The tabs rewrite appended `.live` to
@entangle, so every tab set bound with a plain wire:model sent a request
on each click. @entangle already adds `.live` for wire:model.live, which
the view's own header promises is the caller's choice. Pinned for both
spellings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (navigation group, third batch, last component):
<x-scheme-picker>'s class lists move into resources/css/components/
scheme-picker.css, keyed on data-md-scheme-picker (its legend and
options grid, 2 columns below medium and 4 from it) and
data-md-scheme-picker-option, whose radio inside is read with :has()
for the chosen and the focus states — the input, not the label, is
the real control and carries the ring group.css's own segments keep
the same refinement for. `dark:bg-(--swatch-dark)` becomes
[data-theme='dark'] on the swatch (theme.css's own dark custom
variant reduces to a plain descendant selector here, since the
swatch never carries data-theme itself); the profile's name keeps its
label-large type as a text class on the view, since it is the
caller's own prose.
Hooks renamed: data-scheme-picker to data-md-scheme-picker,
data-scheme-option to data-md-scheme-picker-option, updated in
SchemePickerTest.php and tests/Browser/{ThemeTest,ColourProfilesTest}.php.
Browser tests added (docs/plans/material-3-browser-tests.md): the
contrast switch repaints at once and survives a wire:navigate, and
prefers-contrast: more picks high (Playwright's contrast context
option, the same shape as the existing reducedMotion tests).
This is the last navigation-group component off Tailwind. Every
package component is now drawn without Tailwind; tailwind.css keeps
only tokens/theme.css and tokens/utilities.css, which the showcase
still needs until step 38.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (navigation group, third batch): <x-theme-toggle>'s icon
button (mode="toggle"|"cycle") moves into resources/css/components/
theme-toggle.css, keyed on data-md-theme-toggle="toggle"|"cycle" and
still carrying data-md-icon-button, the hook toolbar.css matches
generically so the toggle takes a toolbar's own icon colour when
placed in one (N-13). The button renders the shared md-state-layer/
md-touch-target/md-focus-ring classes (N-01's 48px target); its
corner morphs from full to sm while pressed on the fast spatial
spring, the same motion <x-button>'s own icon buttons take.
mode="picker"|"contrast" already drew a connected group over native
radios (N-04, N-17's fix), so only its wrapper's hook needed renaming.
Hook renamed: data-theme-toggle to data-md-theme-toggle, updated in
AppBarTest.php, which carries theme-toggle's render tests.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (navigation group, third batch): <x-account-menu>'s class
lists move into resources/css/components/account-menu.css, keyed on
data-md-account-menu (the avatar trigger), data-md-account-menu-avatar
and data-md-account-menu-header. The trigger already carried the
target-extension and state-layer fixes from an earlier pass (N-01,
N-15); this rewrite only moves them onto the shared md-state-layer/
md-touch-target/md-focus-ring classes and their z-index-ordering
comment into the stylesheet. The header's name and email keep their
type and ink as the fixed text classes on the view
(md-type-title-sm/md-ink, md-type-body-sm/md-ink-variant), since they
are the caller's own prose.
Hooks renamed: data-account-menu to data-md-account-menu,
data-account-theme to data-md-account-menu-theme, updated in
AppBarTest.php, which carries account-menu's render tests.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (navigation group, third batch): <x-section-nav>'s class
lists move into resources/css/components/section-nav.css, keyed on
data-md-section-nav (data-md-section-nav-picker, its own trigger and
label) and <x-tabs>'s own hooks, reused whole rather than copied
(N-16's fix keeps the real scrollable tab bar it landed): the bar's
outline-variant divider, active indicator and aria-current colouring
are tabs.css's rules, not this file's. The picker's trigger takes the
shared md-focus-ring class and the fixed md-type-body-lg/md-ink/
md-text-start text classes for its own body-large type, since it is
prose on a plain button rather than a component's own type; icons
take a size prop instead of a size class.
Hooks renamed: data-section-nav to data-md-section-nav,
data-section-picker to data-md-section-nav-picker, updated in
TabsTest.php (which carries section-nav's render tests) and
tests/Browser/BarsTest.php.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (navigation group, third batch): <x-scaffold>'s own
styling moves into resources/css/layout/scaffold.css (the content
region, the bar and rail row, the banner, the actions row and its
rail-collapsed column layout, --material-bottom-bar and
--material-margin publishing, the skip link) alongside step 35's FAB
and content-margin rules already there. Every data-app-shell* hook
becomes data-md-scaffold-* (data-app-shell-bar, -actions, -banner);
the skip link is data-md-skip-link; data-app-shell itself is dropped,
data-md-scaffold already named the root.
The actions row's rail-collapsed:flex-col is written out branch for
branch as the navigation rail's own rewrite did for its internal
parts: the three width-independent conditions in one :where() group,
the four width-gated ones each in their own @media block. With that
gone, resources/css/tailwind.css's rail-collapsed custom-variant
shim (its last use) is removed; tailwind.css now carries only
tokens/theme.css and tokens/utilities.css, which the showcase still
needs until step 38.
navigation-bar.css's hide-on-scroll rule reading --material-bottom-bar
stayed unlayered only because <x-scaffold> published that variable
through a Tailwind utility, which no layered rule could outrank; now
scaffold.css sets it itself in material.layout, a layer
navigation-bar.css's own material.components always beats, so the
rule moves into the layer and the file fits one
@layer material.components block like every other navigation
stylesheet. navigation-bar rejoins NavigationStylesheetsTest.php's
dataset and NavigationBarTest.php's own duplicate shape test is
retired in favour of it.
Browser tests added (docs/plans/material-3-browser-tests.md): the
scaffold's FAB dropping the bar's own height once hide-bar-on-scroll
slides it away, at the trailing edge in a right-to-left document, and
clearing a safe area an application sets on its inline-end and bottom
edges.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 follow-up. Deleting navigation.css also deleted the only
definition of the rail-collapsed: custom variant, which
scaffold.blade.php's data-app-shell-actions row still uses
(rail-collapsed:flex-col) — scaffold's own rewrite is a later batch, so
it is still Tailwind. Left alone, that class would silently stop
matching anything. The definition moves into tailwind.css, updated to
the data-md-* hooks navigation-rail.css now renders (data-rail and
data-rail-auto are unchanged, so those branches needed nothing);
otherwise it reproduces the same seven branches navigation-rail.css's
own header documents, so the two cannot drift silently in what
"collapsed" means. It goes with the scaffold's own rewrite.
theme-script.blade.php's doc comment, which named the old variant, now
points at navigation-rail.css directly.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
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
Plan step 36 (navigation group, second batch): <x-navigation-bar>'s and
<x-navigation-bar-item>'s class lists move into
resources/css/components/navigation-bar.css and navigation-bar-item.css,
keyed on data-md-navigation-bar (data-md-tall, data-md-hide-on-scroll/
-hidden) and data-md-navigation-bar-item (data-md-active), both in
material.components. The indicator's growing fill and its state layer,
shared with the rail's item, move into a new navigation-item.css both
files import — the day's own layer in datepicker.css is the precedent
for keeping it out of the shared foundation classes: the element
focused and pressed is the whole item, the layer drawn only on the
smaller indicator or pill inside it, which md-state-layer cannot do and
:focus-visible never matches. The wash colour moves from a dead
on-surface base overridden by both items to a shared on-secondary-
container declaration, and its opacities from literal 0.08/0.1 to
state.css's own tokens — no visible change, since the numbers matched.
Every size is px (16dp Tailwind quirks aside, this file had none); the
bar's own item-count and container-query layout, N-08's label-medium
fix and N-19's on-secondary-container wash already matched the audit,
so only hooks, units and layer needed to change.
The scaffold's hide-on-scroll offset rule (`--material-bottom-bar`,
navigation-bar.css) stays unlayered: <x-scaffold> still publishes that
variable with a Tailwind utility until its own rewrite, and a rule in
any layer loses to it regardless of specificity — unlike the FAB
overrides the rail commit moves into material.components, which only
have to beat another material.components rule.
navigation.css is not deleted yet (still Tailwind's for the rail); its
"Navigation" comment in tailwind.css narrows as each stream leaves.
navigation-bar.css is not in NavigationStylesheetsTest.php's dataset:
its one unlayered rule breaks that test's "every block is
material.components" assumption, so the same checks are in
NavigationBarTest.php instead, the same reason ErrorPagesTest.php
carries error-page.css's.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (navigation group, third and last stream): <x-tabs>'s
and <x-tab>'s class lists move into resources/css/components/tabs.css,
one commit for both since they share it. Root data-md-tabs; the bar
data-md-tabs-bar with data-md-variant/-stacked/-scrollable, holding
data-md-tab buttons (data-md-tab-content, the new data-md-tab-label
wrapping the label and badge, data-md-tab-indicator); the panel
data-md-tab-panel. The server-rendered hidden panel (N-05), the 52dp
scrollable offset (N-10), the indicator's 2dp inset (N-18), the ring
moved 2px out (N-20) and aria-current sharing the active colour
(N-21) already matched the audit; only hooks, units and layer
changed. Sizes (48/64px tab height, 90px minimum width) stay px;
spacing goes through the measurement tokens where the value matches
one (the icon-label and label-badge gaps, the panel's top padding).
Imports icon.css and badge.css for what the view renders.
Hooks renamed: data-tabs-bar, data-tab, data-tab-content,
data-tab-indicator, data-scrollable, data-stacked and the bar's
data-variant to data-md-*, updated in tabs.js (dataset.tab ->
dataset.mdTab), section-nav.blade.php (which reuses the tab bar's
hooks under its own, still-Tailwind, markup),
tests/Feature/Components/TabsTest.php and tests/Browser/BarsTest.php.
Added the owed browser test: a tab panel never flashes before Alpine
boots, read from an inline script that runs while the page is still
parsing, well before Alpine's own script (docs/plans/material-3-browser-tests.md).
This closes plan step 36: every navigation-group component is now
data-md-* and Tailwind-free; tailwind.css's Navigation block keeps
only navigation.css (the bar and rail, out of this batch's scope).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (navigation group, second stream): <x-toolbar>'s class
lists move into resources/css/components/toolbar.css, keyed on
data-md-toolbar (data-md-variant/-vertical/-vibrant/-rounded/
-toolbar-place) and data-md-toolbar-group/-toolbar-fab. The docked
form with a FAB (N-02's --material-bottom-bar clearance), the vertical
24dp margin (N-12), the standard toolbar's primary icon buttons
(N-13), and the rounded large-screen form with dividers from 840px
were all already correct; only their hooks, units and layer needed to
change. The file no longer sits outside every layer: button.css and
fab.css now draw their own ink through data-md-* attributes rather
than a Tailwind utility, so a toolbar's more specific selector always
beats their single base color/background-color/box-shadow declaration
without needing to escape the cascade layers.
Sizes stay px (64px row, 40px divider); spacing goes through the
measurement tokens where one matches the value (4px leading/trailing
gap, 16/32px docked spread, 8px floating ends, 16/24px placed
margins).
Hooks renamed: every unprefixed data-toolbar* attribute to data-md-*,
updated in tests/Feature/Components/{AppBarTest,ToolbarTest}.php and
tests/Browser/BarsTest.php. toolbar.js needed no change: it reads the
bar through $root and role="toolbar", not by hook name.
Carry-over from the app-bar step: layout/pane.css now imports
app-bar.css and button.css for the pane's own top app bar and back
button, now that both are rewritten. That import crosses into
material.components for the first time from a layout file, so
tests/Feature/StylesheetsTest.php's two layout-shape checks now skip
non-`layout/` files reached through it — each already covered by its
own group's stylesheet test.
Added the owed browser tests (docs/plans/material-3-browser-tests.md):
a docked toolbar clear of the navigation bar below medium, and the
rounded form with its divider height from 840px, clear of the
window's edges.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (navigation group, first stream): <x-app-bar>'s class
lists move into resources/css/components/app-bar.css, keyed on
data-md-app-bar (with data-md-variant/-sticky/-subtitled/-scrolled/
-collapsed) and its parts data-md-app-bar-row/-leading/-headline/
-title/-subtitle/-search/-trailing/-action/-overflow/-expanded. The
overflow menu (two icon buttons below 600px, four from it, N-09's
312px search cap and N-11's three-column centred row) already matched
the audit; only its hooks, units and layer needed to change. Every
length is px, spacing through the measurement tokens where one
matches (M3's own "4dp leading/trailing space" and "16dp" headline
inset had no exact token, so those stay literal). Imports button.css,
menu.css and menu-item.css for the overflow menu the view renders.
Hooks renamed: every unprefixed data-app-bar-* attribute and
data-scrolled/data-collapsed to data-md-*, updated in
tests/Feature/Components/{AppBarTest,PaneTest}.php and
tests/Browser/{BarsTest,NavigationTest}.php. app-bar.js needed no
change: it reads the bar through $root, not by hook name.
tests/Feature/Components/NavigationStylesheetsTest.php is the
navigation twin of ContainmentStylesheetsTest.php, on the same
tests/Support/ViewClasses.php rule; its dataset starts at 'app-bar'
and a 'tabs' entry (once that commit lands) will read both
tabs.blade.php and tab.blade.php, since they share one stylesheet.
Added the owed browser tests (docs/plans/material-3-browser-tests.md):
the overflow at 599/600px, keyboard in its menu, a menu closing once
its width no longer shows it, and a wire:click action from both the
icon button and the menu item form.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 review. The list's header sent dividers to list.css, which
list-item.css draws since 9ff5a7a8; the carousel item's label overlay
shows the `label` prop, not the "n of m" name; the dialog's 560/280px
are its maximum and minimum width, in that order.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 review of 9dd527f3. The old view hid a standard sheet's
scrim with `expanded:hidden`; the stylesheet lost that rule (and the
render test lost its assertion), so an open standard sheet at 840px and
up laid a full-window scrim over the page it sits beside, one click from
closing. The rule is back, pinned by a test.
The same review: the scrim now fades out as well as in (with no closed
opacity, allow-discrete only held it at 32% for 200ms and then dropped
it); the modal sheet slides without the fade the old enter and leave
classes never had; it slides from its own edge in a right-to-left
document, which translate-x-full never did; the state rules match the
root's own sheet and scrim, so a sheet nested in an open one keeps its
state; the script's breakpoint is 840px, not 52.5rem, which disagreed
with the stylesheet's px query once the reader's text size grew; and
`width` and the standard sheet's cap are M3's 400px.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 review of f04e31b3. The card rendered md-state-layer only
on a row, so a card the application marks data-md-dragged without being
a row, as the showcase's "A card being dragged" is, lost the 16% dragged
layer its old inset shadow drew and its header still promises. Every
card now renders the class, since the attribute arrives from script
after render, and card.css keeps the layer off a card that is neither a
row nor dragged. The divider under the header is matched as its child.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
Plan step 36 (containment group, error pages): errors::minimal's class
lists move into resources/css/components/error-page.css, keyed on
data-md-error-page/-art/-shape/-code/-headline/-message/-actions;
body carries no hook of its own and is styled by a bare `body`
selector, matching how ErrorPage::fallbackStyles() already styles it
(no other rewritten view is a full HTML document). The section
contract (title, code, headline, message, shape, actions) and the
framework's 401/402 compatibility are untouched, since neither depends
on a class or a hook. The shape's <x-shape> (a component outside this
batch, and one with no "fill the parent" prop) is coloured and sized
to its container by a plain `svg` descendant selector rather than a
class or a new prop. The page's font stays a literal system stack, not
var(--md-ref-typeface-brand): Tailwind's own `font-sans` utility this
replaces was never the brand font either
(resources/css/tokens/theme.css's --font-sans is the only utility
that is), and an error page must read before any webfont has loaded.
ErrorPage::fallbackStyles() draws onto the same hooks, renamed the
same way (data-error-* -> data-md-error-*, data-error-fallback ->
data-md-error-fallback) so the no-build path keeps working; how it is
built (a hand-written heredoc, not Stylesheets::bundle()) is
unchanged, per step 40. Updated in the same commit:
tests/Feature/ErrorPagesTest.php and tests/Browser/ErrorPagesTest.php.
The error pages' views live outside resources/views/components/, so
they are not in ContainmentStylesheetsTest's dataset (its "imports
what its view renders" check reads a fixed
resources/views/components/<name>.blade.php path); adapted
tests/Feature/ErrorPagesTest.php instead, with the same stylesheet-
shape, import, ViewClasses, token/px-breakpoint and containment-block
checks the dataset gives every other component.
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-carousel>'s and <x-carousel-item>'s
class lists move into resources/css/components/carousel.css and
carousel-item.css, keyed on data-md-carousel (its value is the layout)
and the parts' data-md-carousel-* hooks (-probe, -scroller, -controls
with "auto"/"always", -previous/-next, -item, -surface, -content,
-label/-label-text). Every selector uses a `>` combinator rather than a
bare descendant one, because a carousel item can itself hold a nested
carousel whose own root would otherwise match its parent's layout
rules too (list.css already solves the same problem for segmented
list rows).
Behaviour is unchanged: resources/js/carousel.js (the keyline maths,
C-05's reduced-motion fix, C-11's vertical full-screen layout, C-12's
padding, C-18's item-as-tab-stop, the multi-aspect layout) is touched
only where it reads or writes the renamed hooks and dataset
properties; every inline custom property it writes
(--material-carousel-*) is untouched. The item renders the shared
md-focus-ring class (foundation/interaction.css) instead of a
hand-rolled ring, refined to an inset offset since an outward one
would draw under the neighbouring item. The previous/next buttons
mirror whole in RTL from carousel.css rather than through
<x-icon mirror-rtl>, which <x-button icon> has no prop to reach (a
component outside this batch); the technique matches how the
Tailwind-era markup already mirrored the whole button. The overlay
label's literal white ink over the scrim (C-25) is kept, with the
same reasoning as before.
Hooks renamed data-material-carousel(-item/-surface/-content/-label)
-> data-md-carousel(-item/-surface/-content/-label), data-padding(-end)
-> data-md-padding(-end), data-centered -> data-md-centered, updated in
the same commit: resources/js/carousel.js, tests/Feature/Components/
CarouselTest.php (rewritten on data-md-* and ComponentStylesheet) and
tests/Browser/CarouselTest.php. Browser tests owed by
docs/plans/material-3-browser-tests.md, added but not run: the
multi-aspect carousel's previous/next, arrow keys, Home and End
(scoped by data-md-carousel="multi-aspect" rather than a position in
the showcase, so reordering its examples cannot silently mis-target
the wrong carousel); a reduced-motion click on an item cut off only by
the row's own edge, which documents rather than fixes a real gap —
isMasked()'s inset check is always false once C-05 zeroes every
item's inset, so the click-to-reveal affordance does not fire there
(found by the Chromium baseline, step 32; fixing it is outside a hook
rename).
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-bottom-sheet>'s class lists move
into resources/css/components/bottom-sheet.css, keyed on
data-md-bottom-sheet (data-md-open, data-md-standard) and
data-md-bottom-sheet-scrim/-probe/-panel (data-md-preset while
heights/snap gives it stops)/-handle/-grip/-announce/-body/-title/
-actions. x-transition's enter/leave classes are replaced the same way
as drawer.css: @starting-style + transition-behavior: allow-discrete
on the x-show-driven panel, the asymmetric open (spatial-default,
emphasized-decelerate) vs close (effects-default, emphasized-
accelerate) timing coming from the more specific open-state selector's
own transition-duration/-timing-function, which a CSS transition
always takes from the after-change style. data-md-preset adds the
height-between-stops transition (the spatial spring) alongside it,
independent of the open/close slide.
The drag handle (C-01, already fixed in the Tailwind-era markup this
replaces) renders the shared md-focus-ring and md-touch-target classes
instead of a hand-rolled ring: unlike the datepicker's day, its own
visible bar is not a smaller indicator drawn inside a bigger box those
classes cannot reach, so nothing here refines them further. Its 22px
vertical padding (SheetDefaults.kt's DragHandleVerticalPadding) is a
literal, matching alert.css's 12px for the same reason: 22 is not on
the measurement scale.
Hooks renamed data-drag-handle -> data-md-bottom-sheet-handle,
updated in resources/js/bottom-sheet.js and
tests/Browser/ContainmentTest.php; data-sheet -> data-md-drawer in
that test's comment. <x-menu sheet-at-compact> renders this component
unchanged; menu.css now imports bottom-sheet.css, and
tests/Feature/Components/MenuTest.php's stale Tailwind-class assertion
for the embedded sheet is replaced with a hook check. The two
sheet-at-compact browser tests in ActionsTest.php stay on
assertVisible(): the sheet is still x-show-driven (a deliberate choice
for this batch, keeping the existing modal/side-sheet/bottom-sheet
architecture and converting only their styling), so open still shows
as visibility rather than :popover-open; their comments no longer say
"not rewritten in this batch".
Imported from the Containment block of components.css. Browser tests
owed by docs/plans/material-3-browser-tests.md, added to
ContainmentTest.php but not run: dialog divider marks on scroll
(including that a nested dialog keeps its own marks), the full-screen
phone bar, separator always, no fade under reduced motion, the
scrolling body's inset focus ring; bottom sheet preset-height cycling
with its announcement and a drag settling on the nearest stop; the
standard side sheet from 840px without a scrim or a focus trap, modal
below it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
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
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 (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
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