Files
livewire-material/UPGRADE.md
T
Andreas Reinhold / reiniandClaude Opus 5 fb7007c976
tests / feature (8.4) (push) Successful in 2m0s
tests / feature (8.5) (push) Successful in 2m0s
tests / browser (chrome, chromium) (push) Failing after 8m3s
tests / browser (firefox, firefox) (push) Failing after 12m58s
tests / browser (safari, webkit) (push) Failing after 13m8s
Take Tailwind out of the package, and its detection out of the guard
Tailwind left the stack in 2.0.0, but the package still carried about 330
mentions of it. What the guard's Tailwind detection protected — a class
that compiles to nothing — is now protected by a check that does not care
where a dead class came from.

DesignGuard: about 500 lines of Tailwind tables, scales, palettes and
"2.0.0 replacement" hints give way to one check — a class a view or PHP
file writes that neither the application's stylesheets nor the package's
own declare. It catches a utility of any framework, a typo and a class
whose rules were deleted alike, so it also found two classes ReStride
draws nothing with. A stylesheet has to be in reach for it: the `.css`
files among the scanned paths, or what the `missingStylesheets()` entry
imports. The class reader no longer mistakes an array index for a class
list (`$block['base']`), and it reads the array a class helper is given,
where it read nothing before.

The package's own three Tailwind self-guards go with it. Only their one
unique check stays, as a test of its own: every `matchMedia` width in
resources/js is an M3 breakpoint.

The pagination views are `material.blade.php` and
`simple-material.blade.php`; only Laravel's and Livewire's default theme
names ever made them `tailwind`. The provider sets `Paginator`'s default
views and switches `livewire.pagination_theme` to `material` when it is
still Livewire's own default, so no application can forget the config; a
theme an application chose, and a component's own `$paginationTheme` or
`paginationView()`, still win.

The rest is prose: the layer-order guidance for an application that still
builds Tailwind, the Tailwind wording in the README, the Boost guidelines
and the development skill, and about 25 "this used to be a Tailwind
utility" comments, along with every "plan step NN" pointer into a
gitignored folder. The reset keeps its credit, and NOTICE now carries it
too.

Feature suite 1159 passed, Chrome browser suite 299 passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 21:07:39 +02:00

364 lines
27 KiB
Markdown

# Upgrading
## From 2.1.0 to 2.2.0
- **Run `php artisan view:clear` after upgrading.** The components are no longer registered under a
view namespace named after a hash of their folder, which only views compiled before 1.0.1 still
named; such a view shows the tag as text until it is compiled again.
- **`DesignGuard`** no longer reports the 1.x package's own names (`text-meta`, `focus-ring`,
`rounded-corner-lg`, `shadow-elevation-2`, `type-body-md`, `ease-spatial-fast`) with their 2.0.0
replacement. A colour utility on a 1.x ink is still reported, as a colour of the application's own
theme; the rest compile to nothing and pass unnoticed.
- **`<x-slider>`** redraws when a script sets its input's `value`, as before, but no longer when it
sets `valueAsNumber`; write `value` instead.
- **The showcase's stylesheet** is served from `assets/css/showcase.{hash}.css`, prebuilt in
`resources/dist/`, and the error page inlines prebuilt bundles too. `Stylesheets::bundle()` and
`Stylesheets::resetCache()` are gone; nothing outside the package called them.
- **`$store.rail.toggle()`** flips what the first rail on the page draws, as its menu button does.
With nothing stored, `<x-scaffold>`'s rail is drawn collapsed from 840 to 1199px whatever
`rail.default` says, and `toggle()` collapsed it again, so the first press of an application's
shortcut changed nothing; it now expands the rail. Below 840px, where there is no room to expand
it in the layout, `toggle()` opens and closes the modal rail (`show()`, `hide()`) instead of
changing a choice nothing draws there. A shortcut that pressed the rail's menu button to get this
can call `$store.rail.toggle()` again.
- **`DesignGuard`** no longer reads a Markdown mail component's classes as Tailwind: a view under
`mail.markdown.paths` (`resources/views/vendor/mail` by default) is drawn by the mail theme, whose
`table`, `button` and `panel` share Tailwind's names, so only its icon names, the directives in
its component tags and the application's own bans are checked there. A mail theme stylesheet
under that path is no longer checked for literal values, and its classes no longer exempt the
same names in the application's other views. A filter an application wrote around such a
finding can go.
- **`DesignGuard`** counts a row written by hand. `data-md-list-row` on an `<li>`, a `<div>`, a
`<tr>` or `<x-row>` takes its hover, focus and press state layer and its selected fill from
`components/list-item.css`, but `missingStylesheets()` and `unusedStylesheets()` read only tags
and `->links()`: an application had the stylesheet through `table.css` or `<x-list-item>`, lost
it without a finding when the last of those left its views, and an `@import` of `list-item.css`
for its own rows was reported unused. A view of the application that writes `data-md-list-row`
on anything but `<x-card>`, whose row card.css draws, now needs `list-item.css`; a selector such
as `[data-md-list-row]` and the package's own views do not count. A test that expects no findings
may name the `@import` to add, and a filter written around the unused report can go.
- **A selected row written by hand** — `data-md-list-row` with `data-md-selected` on an `<li>`, a
`<div>` or a `<tr>`, not `<x-list-item>` or `<x-card>` — is drawn in `secondary-container` with
`on-secondary-container` ink again, as 1.x's `data-list-row` with `data-selected` was; 2.0.0
dropped the fill without saying so. Hover, focus and press now tint the fill instead of hiding
it. In `<x-table>` a plain row takes `data-md-selected` too, beside `aria-selected="true"`. A
card still takes no selected fill.
- **67 Material Symbols** in each cut (`auto_awesome`, `insights`, `tips_and_updates`,
`battery_50` …) are drawn whole at every size. Google publishes them in 24 units (20 in the 20
cut) with a width and height but no viewBox, and the package dropped the size, so they fitted
only at exactly 24px and were cut off or misplaced at any other — the 20 cut every small button
draws from included. Nothing to change.
- **A focused text field keeps its focus edge under the pointer.** The hover rule outweighed focus,
so a focused field the pointer rested on drew a 2px `on-surface` edge instead of `primary`, and
a field in error `on-error-container` instead of `error` — in the filled variant too, and on a
customizable `<x-select>` while its menu is open. An open select in error now draws `error`
rather than `primary`. A test that read the hover colour off a focused field reads `primary`
now.
- **Escape closes one layer.** A dialog, menu, customizable select or searchable choice opened
over or inside a modal `<x-drawer>`, `<x-bottom-sheet>` or modal navigation rail — or a sheet
opened from a sheet, or inside `<x-modal>` — closed the layer under it on the same Escape, since
each sheet closed on any Escape the window heard. Now only the topmost layer closes; a sheet
with `close-on-escape` off still keeps the Escape from closing what is under it. A dialog or
sheet opened from a modal sheet but rendered elsewhere on the page was also hidden from screen
readers by the sheet's `aria-hidden`, and Tab could not move inside the dialog; both work now.
An application's own guard around a sheet's Escape can go.
- **A standard `<x-drawer>` sticks under the top safe area** (`--material-safe-top`, else the
device's inset) instead of at the window's edge, so its head and close button no longer slide
under an installed app's status bar as the page scrolls; inside `<x-scaffold>` it also sticks
under a sticky `<x-app-bar>` in the `top` slot, which covered it. It is as tall as the window
below that, and every side sheet keeps the bottom safe area inside its bottom padding.
- **A standard `<x-drawer>` takes its place from the first paint.** Until Alpine started, a closed
standard sheet was a zero-wide item that still cost its row one gap, and a sheet bound to an open
Livewire property had no width yet, so the content beside them changed width when the script
ran. A standard sheet is now out of the row until then, and one whose `wire:model` property is
open is rendered open and stands at its width. One opened from an Alpine scope alone still
appears when Alpine starts, which only the script can know.
- **A long value in `<x-select>` ends before the arrow, with an ellipsis.** Where the browser has
the customizable select (Chrome, Safari), the closed select drew its value in a box of its own
that took neither `text-overflow` nor a width from the select, so a long option in a narrow
field, or at `size="sm"` and `xs`, ran on under the arrow; the native select (Firefox) cut it at
the arrow with no ellipsis. The select's first child is now a `<button type="button"
wire:ignore>` holding `<selectedcontent>`, the browser's copy of the chosen option, which
select.css cuts; a browser without the customizable select never renders it. A test that
matched a select's markup up to its first `<option>` finds the button there.
- **A switch beside long text in a row keeps its track.** `<x-toggle>`'s root could shrink to
nothing as a flex item, so beside a paragraph in `<x-row justify="between">`, or with a long
label of its own, the 52px track ran out of the row and past a card's edge on a phone.
`<x-checkbox>` did the same with its box. Both roots now keep their automatic minimum — the
control, and beside a label its longest word — so the text wraps instead. A `flex: none` an
application put on the switch to hold it can go.
- **A disabled `<x-button fab>` is not drawn on a compact window.** Below `medium` it was still
the extended FAB, greyed out and fixed over the content; M3 never shows a disabled FAB and
removes one whose action is unavailable, so a `fab` given `disabled` (on a link too) now renders
`data-md-unavailable` and is `display: none` there, out of the accessibility tree with it. From
`medium` it is the disabled filled button, as before. A `spinner` that disables the button while
its action runs leaves the FAB on screen. An application's own `hide-below` or condition around
a disabled fab button can go.
- **`<x-choices searchable>`, `<x-datepicker>` and `<x-timepicker>` take `full`**, as `<x-input>`,
`<x-textarea>`, `<x-select>` and `<x-file>` do, to take the 40rem bound off a field from
`medium`; the prop did nothing there, so the field stopped at 40rem beside rows that filled their
card. `<x-choices>`' chips have no bound and take `full` without a change, so a call site can
switch `searchable` on and off. A width rule an application wrote around one of them for this
can go.
- **A searchable `<x-choices>`' list is as wide as its field.** It was anchored to a wrapper
around the field, which fills its container, so from `medium`, where the field stops at 40rem,
the list ran on past the field's end across the whole container. The field itself is the anchor
now, and the wrapper `<div>` is gone; the list keeps hanging under the field's supporting text.
- **A sheet or dialog that opens shows no tooltip on the control it focuses.** A modal
`<x-drawer>`, `<x-bottom-sheet>`, the modal rail and `<x-modal>` move the focus to their first
control as they open, and on a page loaded with the sheet open (`wire:model` already set, say
from `?workout=` in the URL) or a layer opened from the keyboard, browsers count that focus as
keyboard focus: the close button's plain tooltip stood over the sheet the moment it appeared, in
Chrome, Firefox and Safari. Plain and rich tooltips now leave out focus a modal layer moves into
itself from outside; a Tab onto the control, including the one that wraps round to it, and focus
moved within the layer still show them. An `autofocus` an application put on another control to
keep the focus off the close button can go.
- **`DesignGuard`** reports any class a view or PHP file writes that no stylesheet declares —
not the package's `md-*`, not the application's own CSS — instead of naming Tailwind's
utilities, variants and theme colours with their 2.0.0 replacement. It now catches a dead class
from any source: a typo, a utility of a framework that isn't in the stack, a class whose rule
was deleted — not only Tailwind's — but a class assembled in a PHP string (`'text-'.$tone`) is
still not seen. A test that asserted on the old Tailwind-shaped message needs the new one
instead.
- **The pagination views are `material.blade.php` and `simple-material.blade.php`**, no longer named
after Tailwind. The provider sets `Paginator::defaultView('pagination::material')` and
`defaultSimpleView('pagination::simple-material')`, and switches `livewire.pagination_theme` to
`material` whenever it still reads as Livewire's own default (`tailwind`, including when the key
is missing), so an application can no longer forget the config and silently render Livewire's own
Tailwind view. An application that already chose another theme, or a component with its own
`$paginationTheme` property or `paginationView()` method, is untouched — either still wins.
- **`foundation.css` no longer explains how to order layers beside Tailwind.** No consumer builds
Tailwind any more, so the paragraph about opening both entries with
`@layer properties, theme, base, material, components, utilities;` is gone; the layer statement
and the order it declares are otherwise unchanged.
## 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). 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 of Tailwind's own utilities with its replacement; the 1.x names are no longer reported.
### 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.