Two gaps found rewriting the Boost skill for 2.0's plain CSS, each
closed the way M3 describes it.
`<x-loading size="96">`: M3 gives the loading indicator a responsive
size, 48dp by default and flexible from 24 to 240dp ("never exceed that
range"), with the container and the active shape in a fixed ratio. The
size had no prop, so an application wrote a width and height of its own;
`size` now takes a whole number of px in that range, written as
`--md-loading-size`, and the SVG keeps the 48:38 ratio as it scales. A
value outside the range is ignored, as `<x-icon>` and `<x-shape>` ignore
theirs, and an application's own width and height still win.
`--md-navigation-rail-value`: M3 Expressive's rail has two values,
collapsed and expanded (Compose's WideNavigationRailValue), and content
in a rail follows it. Without a hook an application copied the rail's
seven conditions — mode, `data-rail`, `data-rail-auto`, open, and the
window band — out of navigation-rail.css. The rail now publishes the
answer from the same branches that narrow it: `expanded` by default,
`collapsed` wherever it is drawn collapsed, so a style query in the
application's CSS switches at the first paint and at the same moment
as the rail's own items. A rail open over a scrim reads `expanded`, and
outside a rail the property is unset. A JS attribute would have missed
the first paint; a width container query would have lagged the collapse
animation.
Style queries on a custom property need Firefox 151, so the browser
floor moves from Firefox 147 to 151 (README, the CI note, UPGRADE's new
2.1.0 section); Chrome 125 and Safari 18.4 are unchanged.
Browser tests pin both in Chrome, Firefox and Safari: the indicator's
drawn box at 96 and 32px, and the rail's value — with a style query
acting on it — at the first paint for fixed modes, across the window
classes and the menu button for an adaptive rail, and open and closed
for a modal one. NavigationRailTest pins the value in each of the five
collapsed branches.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
232 lines
15 KiB
Markdown
232 lines
15 KiB
Markdown
# Upgrading
|
|
|
|
## From 2.0.0 to 2.1.0
|
|
|
|
- **Browsers:** Firefox 151 or later (was 147), for container style queries on a custom property;
|
|
Chrome 125 and Safari 18.4 are unchanged.
|
|
- **`<x-navigation-rail>`** publishes its value as `--md-navigation-rail-value`, `collapsed` or
|
|
`expanded` (M3's two rail values). Content an application puts in a rail reads it with
|
|
`@container style(--md-navigation-rail-value: collapsed)` instead of copying the rail's
|
|
conditions from `navigation-rail.css`.
|
|
- **`<x-loading size="96">`** sizes the loading indicator in px, 24 to 240 (M3's responsive range),
|
|
with the container and the shape in proportion. A width and height of the application's own
|
|
still work.
|
|
|
|
## 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, and there are no responsive
|
|
variants in their place: the breakpoints are M3's window size classes — compact below 600px, then
|
|
medium (600px), expanded (840px), large (1200px) and extra-large (1600px) — and only those. A
|
|
layout component takes the class as a prop (`hide-below`, `hide-from`, `stack-below`, `<x-grid>`'s
|
|
`columns` map); the application's own CSS writes the width as a range media query:
|
|
|
|
| Was | Becomes |
|
|
|---|---|
|
|
| `sm:` / `max-sm:` (640) | `medium` · `@media (width >= 600px)` / `(width < 600px)` |
|
|
| `md:` / `max-md:` (768) | `medium` or `expanded` — choose by what the layout means |
|
|
| `lg:` / `max-lg:` (1024) | `expanded` · `@media (width >= 840px)` / `(width < 840px)` |
|
|
| `xl:` / `max-xl:` (1280) | `large` · `@media (width >= 1200px)` / `(width < 1200px)` |
|
|
| `2xl:` (1536) | `extra-large` · `@media (width >= 1600px)` |
|
|
|
|
`<div class="hidden lg:block">` is `<x-stack hide-below="expanded">`; `flex flex-col sm:flex-row` is
|
|
`<x-row stack-below="medium">`; `grid-cols-1 lg:grid-cols-2` is
|
|
`<x-grid :columns="['compact' => 1, 'expanded' => 2]">`. 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, as tokens
|
|
|
|
Tailwind's radius, shadow, text-size, weight, leading, tracking and easing utilities compile to
|
|
nothing, and so do the 1.x utilities that stood for M3's scales. A text style is a class; the
|
|
rest is a token in the application's own CSS:
|
|
|
|
| Was | Becomes |
|
|
|---|---|
|
|
| `rounded-sm` … `rounded-4xl`, `rounded-corner-*` (1.x) | `var(--md-sys-shape-corner-xs)` … `var(--md-sys-shape-corner-xxl)` (`-full`, `-none`), or `<x-surface corner="xs">` |
|
|
| `shadow-sm` … `shadow-2xl`, `shadow-elevation-*` (1.x) | `var(--md-sys-elevation-1)` … `var(--md-sys-elevation-5)` |
|
|
| `text-xs` … `text-9xl`, `leading-*`, `tracking-*`, `font-medium` … `font-black`, `type-*` (1.x) | one `md-type-*` class (`md-type-body-md`, `md-type-emphasized-title-md` …), or `font: var(--md-sys-typescale-body-md)` with its `-tracking` |
|
|
| `ease-in`, `ease-out`, `ease-in-out`, `duration-300`, `ease-spatial-*` (1.x) | `var(--md-sys-motion-spatial-*)` / `var(--md-sys-motion-effects-*)` with its `-duration`, in a `transition` |
|
|
| `gap-4`, `p-4`, `space-y-2` | `gap="space200"`, `<x-surface padding="space200">`, `<x-stack gap="space100">`, or `var(--md-sys-measurement-space200)` |
|
|
| `state-layer`, `focus-ring`, `touch-target`, `link` (1.x) | `md-state-layer`, `md-focus-ring`, `md-touch-target`, `md-link` |
|
|
|
|
The spacing tokens are the 4px grid Tailwind's scale was (`space200` is 16px). `DesignGuard` names
|
|
each one with its replacement.
|
|
|
|
### 3. Regenerate the colour scheme
|
|
|
|
```bash
|
|
php artisan material:scheme "#4f46e5" --variant=tonal-spot # the command in the file's header
|
|
```
|
|
|
|
- `success`, `warning` and `info` are now built on the 2025 colour spec with the contrast level,
|
|
like `error`; their values change. `--harmonize` blends them toward the seed (off by default).
|
|
- The stylesheet gains medium and high contrast blocks, and `material-scheme.json` gains a
|
|
`contrast` key; `light`/`dark` at the top level are still the standard scheme, so the mail
|
|
theme needs nothing. `--contrast` must now be below 0.5; medium (0.5) and high (1.0) are
|
|
generated alongside.
|
|
- Config: `theme.contrast` (`default` `system` | `standard` | `medium` | `high`, `storage_key`)
|
|
and `motion.scheme` (`expressive` | `standard`). `$store.theme` gains `contrast`,
|
|
`resolvedContrast` and `setContrast()`; `<x-theme-toggle mode="contrast">` is a row of three.
|
|
|
|
### 4. Inks are roles, not opacities
|
|
|
|
1.x's `text-meta`, `text-body`, `text-quiet`, `border-chrome`, `border-structure` and
|
|
`border-divider` are gone with the rest of the utilities. Text takes the role's `md-ink-*` class —
|
|
`md-ink-variant` (on-surface-variant) for `text-meta` and `text-body`, `md-ink-quiet` (outline) for
|
|
`text-quiet` — and a line is `<x-divider>`, `<x-surface outlined>` or
|
|
`var(--md-sys-color-outline-variant)` in the application's CSS. Where the old translucent grey was
|
|
intended, the role is the same colour; 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; `type` only picks the announcement role (`alert` for
|
|
error and warning, `status` otherwise). An actioned snackbar stays until acted on; Escape
|
|
dismisses a focused one.
|
|
- `<x-fab>` has no `disabled` prop: M3 says to remove a FAB whose action is unavailable, so hide
|
|
it instead (a form-submit FAB uses `wire:loading.attr="disabled"`).
|
|
- `<x-alert>` is `role="status"` for every colour; pass `assertive` where 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; connected `xs`/`sm` segments have a 48px minimum width.
|
|
- `<x-menu-item>` rows are 48px with 16px sides; a selected item draws a trailing check unless it
|
|
has `icon-right`; a long menu scrolls.
|
|
- `<x-drawer>` renders a close button by default (`:with-close-button="false"` to drop it, ignored
|
|
on a `standard` sheet or when Escape and the scrim are off) and left-aligns its `actions` in 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 at `50dvh` (was `90dvh`); any `height` is capped at
|
|
`calc(100dvh - 72px)`.
|
|
- `<x-carousel>` `padding` defaults to `16`; `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>` or `selection="single|multi"`
|
|
makes it a `listbox` of `option`s with `aria-selected`.
|
|
- `<x-card>` changes elevation on hover instead of its corner; `data-md-card` carries the variant.
|
|
- A button directly in `<x-stack>` (stretched, the default) or `<x-form>` keeps its label's width at
|
|
the start edge instead of filling the width, as M3 asks; a full-width submit is your own CSS.
|
|
- `<x-modal>` pins its header and actions and scrolls only the body; a `box-class` that set
|
|
`overflow` no longer applies.
|
|
- `<x-table>` rows are 52px and the automatic fine-pointer density is gone: pass `dense` for 36px
|
|
rows (`size="xs" dense` is 24px).
|
|
- A field in error draws a trailing `error` icon 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 sets
|
|
`aria-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: the
|
|
`data-theme-option` hooks are gone; target `input[name="material-theme"][value="…"]`.
|
|
- `<x-tab>` panels render `id`, `aria-labelledby` and `display: none` from 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 are `primary`. An application that
|
|
set `--material-bottom-bar` to lift the snackbar over its own bottom toolbar removes it: the
|
|
toolbar now reads it to place itself, and publishes `--material-bottom-toolbar`, which the
|
|
snackbar clears and the page pads its end with.
|
|
- The centred app bar headline is a grid column; the `search` variant bounds its own width, so
|
|
drop hand-written `max-w-*` wrappers.
|
|
- `<x-app-shell>` is renamed `<x-scaffold>`, with no alias; it is a column with a nested row
|
|
and gains `banner` and `fab` slots. `<x-navigation-rail>`'s `header` slot takes one
|
|
`<x-fab label icon>` that morphs (replace the two-FAB `rail-collapsed:` swap); new `divider`
|
|
and `fill` props. The `rail-collapsed:` variant is gone: style what the application puts in a
|
|
rail by the rail's value, `@container style(--md-navigation-rail-value: collapsed)` (2.1.0).
|
|
`data-app-shell`, `data-app-shell-bar`, `data-app-shell-actions` and
|
|
`data-app-shell-banner` are `data-md-scaffold`, `data-md-scaffold-bar`, `data-md-scaffold-actions`
|
|
and `data-md-scaffold-banner`; the skip link is `data-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 (600px), as M3 bounds fields on wider windows; a
|
|
width rule of the application's own on the field beats it, and `full` removes it.
|
|
- Every `<x-modal>` draws a rule under its header and over its actions while its body scrolls,
|
|
and neither when the body fits. `separator` now 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 `description` is 68px tall, and below medium (600px) 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 when
|
|
`heights` or `snap` is set; without stops it behaves as before.
|
|
- A chip set with `scroll` shows a scroll button over each overflowing edge on fine pointers.
|
|
|
|
### 6. `material.css` is gone, and so is Tailwind
|
|
|
|
The 1.x single-import shortcut no longer exists, and Tailwind has left the whole stack — the
|
|
package, its showcase, error pages and Workbench build carry none, and an application drops it
|
|
too. The package's CSS is plain, no build step of its own, in one entry:
|
|
`resources/css/all.css` for everything, or `foundation.css` first and then the stylesheet of each
|
|
component the views render, opening with the layer statement every package stylesheet does
|
|
(see Installation in `README.md`). An application's views write no utility layer of their own
|
|
either: layout components (`<x-scaffold>`, `<x-pane>`, `<x-stack>`, `<x-row>`, `<x-grid>`, the
|
|
canonical layouts) take M3's spacing tokens and breakpoints as props, a small set of `md-type-*`
|
|
and `md-ink-*` classes covers text on plain elements, and `--md-sys-*` custom properties serve the
|
|
rest of an application's own stylesheet. The foundation smooths text in grayscale, as Tailwind's
|
|
`antialiased` class did: drop the class.
|
|
|
|
### 7. 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.
|
|
|
|
### 8. Tests and guards
|
|
|
|
`DesignGuard` changes with the stack:
|
|
|
|
- **Removed:** the maryUI tag and daisyUI class checks, `forbidAbsolutes()` and `forbidOpacityInk()`.
|
|
Every Tailwind utility now compiles to nothing and is reported with its replacement, so
|
|
`bg-white` and `text-on-surface/60` still are.
|
|
- **Retargeted:** `forbidColours([...])` keeps its signature and reports a left-out role where 2.0.0
|
|
writes one: `var(--md-sys-color-…)` in CSS or an inline `style`, an `md-ink-*` class, a
|
|
component's `color`/`tone` prop.
|
|
- **New:** `missingStylesheets($cssEntry)` names each `@import` the views need, and
|
|
`unusedStylesheets($cssEntry)` each one they no longer do; a `.css` file passed
|
|
to `scan()` is checked for literal values and off-scale media queries.
|
|
|
|
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.
|
|
|
|
Every component hook is prefixed `data-md-`: `data-toolbar-place` is `data-md-toolbar-place`,
|
|
`data-account-menu` is `data-md-account-menu`, `data-field-copy` is `data-md-field-copy`. The
|
|
attributes on `<html>` keep their names (`data-theme`, `data-contrast`, `data-scheme`,
|
|
`data-motion`, `data-rail`). A component's inner parts also carry its name:
|
|
|
|
| 1.x | 2.0.0 |
|
|
|---|---|
|
|
| `data-app-shell`, `-bar`, `-actions`, `-banner` | `data-md-scaffold`, `-bar`, `-actions`, `-banner` |
|
|
| `data-theme-option="dark"` | `input[name="material-theme"][value="dark"]` |
|
|
| `data-scheme-option="teal"` | `data-md-scheme-picker-option="teal"` |
|
|
| `data-account-theme` | `data-md-account-menu-theme` |
|
|
| `data-section-picker` | `data-md-section-nav-picker` |
|
|
| `data-material-carousel`, `-item`, `-content`, `-label`, `-surface` | `data-md-carousel`, `-item`, `-content`, `-label`, `-surface` |
|
|
| `data-sheet` (drawer) | `data-md-drawer-sheet` |
|
|
| `data-drag-handle` (bottom sheet) | `data-md-bottom-sheet-handle` |
|
|
| `data-check`, `data-mixed` (checkbox) | `data-md-checkbox-check`, `data-md-checkbox-mixed` |
|
|
| `data-on`, `data-off`, `data-handle` (toggle) | `data-md-switch-on`, `data-md-switch-off`, `data-md-switch-handle` |
|
|
| `data-handle`, `data-thumb`, `data-tick`, `data-stop`, `data-segment`, `data-track-icon` (slider) | `data-md-slider-handle`, `-thumb`, `-tick`, `-stop`, `-segment`, `-icon` |
|
|
|
|
Rename the negative assertions too: `->not->toContain('data-app-bar')` passes against 2.0.0
|
|
whatever the page renders.
|
|
|
|
### 9. 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 classes, props
|
|
and tokens.
|