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
9.6 KiB
Upgrading
From 1.x to 2.0.0
2.0.0 aligns the library with Material Design 3 Expressive as Google documents it
(m3.material.io, checked page by page; the audits are in docs/audits/m3-alignment/). Most of
the change is inside the components. What reaches an application is below, in the order to do it.
1. Breakpoints are M3's window size classes
Tailwind's sm:, md:, lg:, xl: and 2xl: no longer compile. The variants are
medium: (600px), expanded: (840px), large: (1200px) and extra-large: (1600px), plus
max-medium: and friends for "below"; compact is everything below medium. Replace every
prefix in the application's views:
| Was | Becomes |
|---|---|
sm: / max-sm: (640) |
medium: / max-medium: (600) |
md: / max-md: (768) |
medium: or expanded: — choose by what the layout means |
lg: / max-lg: (1024) |
expanded: / max-expanded: (840) |
xl: / max-xl: (1280) |
large: / max-large: (1200) |
2xl: (1536) |
extra-large: (1600) |
Scripts read the same numbers from resources/js/breakpoints.js (from('expanded'),
upTo('medium')). DesignGuard reports every leftover prefix with its replacement.
2. Only M3's scales compile
Tailwind's default radius, shadow, text-size, weight, leading, tracking and easing utilities are cleared like its palette was:
| Was | Becomes |
|---|---|
rounded-sm … rounded-4xl |
rounded-corner-xs … rounded-corner-xxl (rounded-corner-full, rounded-corner-none) |
shadow-sm … shadow-2xl |
shadow-elevation-1 … shadow-elevation-5 |
text-xs … text-9xl, leading-*, tracking-*, font-medium … font-black |
one type-* style (type-body-md, type-emphasized-title-md …) |
ease-in, ease-out, ease-in-out, duration-300 |
ease-spatial-* / ease-effects-* with duration-(--md-sys-motion-…-duration) |
The 4px spacing scale is unchanged. DesignGuard names each one with its replacement.
3. Regenerate the colour scheme
php artisan material:scheme "#4f46e5" --variant=tonal-spot # the command in the file's header
success,warningandinfoare now built on the 2025 colour spec with the contrast level, likeerror; their values change.--harmonizeblends them toward the seed (off by default).- The stylesheet gains medium and high contrast blocks, and
material-scheme.jsongains acontrastkey;light/darkat the top level are still the standard scheme, so the mail theme needs nothing.--contrastmust now be below 0.5; medium (0.5) and high (1.0) are generated alongside. - Config:
theme.contrast(defaultsystem|standard|medium|high,storage_key) andmotion.scheme(expressive|standard).$store.themegainscontrast,resolvedContrastandsetContrast();<x-theme-toggle mode="contrast">is a row of three.
4. Inks are roles, not opacities
text-meta, text-quiet, border-chrome and border-divider keep their names but now resolve
to on-surface-variant, outline, outline-variant and outline-variant. Where the old
translucent grey was intended, nothing to do; where a template relied on the opacity to blend
over a colour, use the role directly.
5. Changed defaults and props
<x-toast>no longer draws a state icon;typeonly picks the announcement role (alertfor error and warning,statusotherwise). An actioned snackbar stays until acted on; Escape dismisses a focused one.<x-fab>has nodisabledprop: M3 says to remove a FAB whose action is unavailable, so hide it instead (a form-submit FAB useswire:loading.attr="disabled").<x-alert>isrole="status"for every colour; passassertivewhere the notice answers something the person just did.<x-button size="xs">and<x-group size="xs">are 8px wider; a<x-button-group>no longer wraps; connectedxs/smsegments have a 48px minimum width.<x-menu-item>rows are 48px with 16px sides; a selected item draws a trailing check unless it hasicon-right; a long menu scrolls.<x-drawer>renders a close button by default (:with-close-button="false"to drop it, ignored on astandardsheet or when Escape and the scrim are off) and left-aligns itsactionsin a 72px row.<x-drawer pane>is removed; use<x-list-detail>for the second pane of a list-detail layout.<x-bottom-sheet>opens at50dvh(was90dvh); anyheightis capped atcalc(100dvh - 72px).<x-carousel>paddingdefaults to16;layout="full-screen"scrolls vertically with edge-to-edge items; items, not the row, are the tab stops.<x-list dividers>draws a 16px-inset rule;<x-list selectable>orselection="single|multi"makes it alistboxofoptions witharia-selected.<x-card>changes elevation on hover instead of its corner;data-md-cardcarries the variant.<x-modal>pins its header and actions and scrolls only the body; abox-classthat setoverflowno longer applies.<x-table>rows are 52px and the automatic fine-pointer density is gone: passdensefor 36px rows (size="xs" denseis 24px).- A field in error draws a trailing
erroricon unless it already trails something; a read-only field no longer draws a dashed outline. - The time picker's AM/PM buttons are radios (
aria-checked); the password reveal no longer setsaria-pressed. <x-slider size="md">grows from 44 to 52px and every value indicator from 32 to 44px tall.<x-theme-toggle mode="picker"|"contrast">is a connected group over native radios: thedata-theme-optionhooks are gone; targetinput[name="material-theme"][value="…"].<x-tab>panels renderid,aria-labelledbyanddisplay: nonefrom the server.<x-section-nav>with five or more sections is a scrollable tab bar, not a grid. The tab bar is 10px taller; the focus ring sits outside.- The navigation bar's horizontal label is
label-md;place="bottom"toolbars sit above--material-bottom-bar; a standard toolbar's icon buttons areprimary. - The centred app bar headline is a grid column; the
searchvariant bounds its own width, so drop hand-writtenmax-w-*wrappers. <x-app-shell>is renamed<x-scaffold>, with no alias; it is a column with a nested row and gainsbannerandfabslots.<x-navigation-rail>'sheaderslot takes one<x-fab label icon>that morphs (replace the two-FABrail-collapsed:swap); newdividerandfillprops.data-app-shell,data-app-shell-bar,data-app-shell-actionsanddata-app-shell-banneraredata-md-scaffold,data-md-scaffold-bar,data-md-scaffold-actionsanddata-md-scaffold-banner; the skip link isdata-md-skip-link.<x-icon optical="20">selects the optical-size-20 cut; the components pass it for their own small icons, applications pass it for icons drawn at 20px or less.- A text field stops at 40rem wide from
medium:, as M3 bounds fields on wider windows; amax-w-*class on the field beats it, andfullremoves it. - Every
<x-modal>draws a rule under its header and over its actions while its body scrolls, and neither when the body fits.separatornow means "always draw both rules" and no longer renders two<x-divider>elements; the spacing between header, body and actions moved to M3's split gaps, so a dialog that fits is a few pixels shorter. - A snackbar with a
descriptionis 68px tall, and belowmedium:a two-line snackbar with an action puts the action under the text. Alt+G moves focus to an actioned snackbar. <x-bottom-sheet>'s drag now follows the pointer and settles on the nearest preset height whenheightsorsnapis set; without stops it behaves as before.- A chip set with
scrollshows a scroll button over each overflowing edge on fine pointers.
6. New in 2.0.0
Nothing to migrate, but worth knowing: submenus (<x-menu-item submenu>), a filtering menu
(<x-menu filter>), a menu that opens as a bottom sheet on compact windows
(<x-menu sheet-at-compact>), gap-grouped menu items (<x-menu-group gap>), square button groups
(shape="square"), managed selection on connected groups (selection="single|multi" required),
FAB collapse on scroll (<x-fab collapse-on-scroll>), the tall navigation bar and hide-on-scroll
(tall, hide-on-scroll; tall-bar, hide-bar-on-scroll on the shell), the narrow, centred and
hide-when-collapsed rail (width="narrow", align="center", hide-when-collapsed), a docked
toolbar with a FAB and a rounded large-screen form (rounded), app bar actions that overflow into
a menu (:actions="[…]"), the list item's video slot, the card's dragged state, the standard
side sheet (<x-drawer standard>), bottom sheet preset heights, the multi-aspect carousel
(layout="multi-aspect"), the divider with a subheader (<x-divider text>), the character counter
(counter), the vertical slider (orientation="vertical"), the full-screen range date picker on
compact windows, search's icon entry point and suggestions (trigger="icon", suggestions slot),
the contrast toggle (<x-theme-toggle mode="contrast">) and the Standard motion scheme.
7. Tests and guards
Add DesignGuard's new checks to the application's design test (forbidOpacityInk() and
forbidAbsolutes() are opt-in). Browser tests that assert widths switch at 640/1024/1280 now
switch at 600/840/1200; tests that read role="alert" on an alert, aria-pressed on the time
picker's period buttons or data-theme-option need the new hooks above.
8. For AI agents
php artisan boost:update --discover picks up the new material-3 guideline and the
material-3-design skill, which state M3's rules and tables beside the library's utilities.