From 5d9e72fd0606f633709984c6a0257421cd519fa9 Mon Sep 17 00:00:00 2001 From: Andreas Reinhold / reini Date: Thu, 17 Sep 2026 21:30:09 +0200 Subject: [PATCH] Move onto livewire-material 2.2.0 and choose the material pagination theme Set pagination_theme explicitly now that the package would otherwise take it over itself; boost:update refreshed the package's guideline and skill. Co-Authored-By: Claude Opus 5 (1M context) --- .../livewire-material-development/SKILL.md | 78 +++++++------------ .claude/skills/material-3-design/SKILL.md | 32 +++----- CLAUDE.md | 5 +- composer.lock | 15 +--- config/livewire.php | 7 +- 5 files changed, 52 insertions(+), 85 deletions(-) diff --git a/.claude/skills/livewire-material-development/SKILL.md b/.claude/skills/livewire-material-development/SKILL.md index a7b52cb..63e1206 100644 --- a/.claude/skills/livewire-material-development/SKILL.md +++ b/.claude/skills/livewire-material-development/SKILL.md @@ -90,7 +90,7 @@ Scheme::resolveProfileUsing(fn (): ?string => Setting::get('color_profile')); ## Tokens -2.0.0's vocabulary is plain CSS. There are no utility classes: a Tailwind class, or one of 1.x's own colour, type, shape, elevation, motion or breakpoint utilities, compiles to nothing, and the design guard names the replacement for each (see Testing the design). `resources/css/foundation.css` is the one required import, first in the entry, followed by the stylesheets of the components the views render (see Setup; 1.x's `material.css` shortcut is gone). It declares the layer order `material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility`, so an application's unlayered CSS beats every package rule, and `hidden` or `x-cloak` hides any element. It brings the reset, the page's `surface` and `on-surface` in the brand typeface smoothed in grayscale (drop Tailwind's `antialiased`), every `--md-sys-*` token, and the classes below. An application's views reach for four things, in this order: +2.0.0's vocabulary is plain CSS. There are no utility classes: one of 1.x's own colour, type, shape, elevation, motion or breakpoint utilities, or any other class no stylesheet declares, compiles to nothing, and the design guard fails on it (see Testing the design). `resources/css/foundation.css` is the one required import, first in the entry, followed by the stylesheets of the components the views render (see Setup; 1.x's `material.css` shortcut is gone). It declares the layer order `material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility`, so an application's unlayered CSS beats every package rule, and `hidden` or `x-cloak` hides any element. It brings the reset, the page's `surface` and `on-surface` in the brand typeface smoothed in grayscale, every `--md-sys-*` token, and the classes below. An application's views reach for four things, in this order: - **A component's own props** — `color`, `variant`, `size`, `dense` — before anything else. - **Layout components** for arrangement and spacing: ``, ``, ``, ``, `` inside ``s (see Layout), with `gap` and `padding` as spacing tokens (`space25` … `space900`, 2–72px) and `hide-below`, `hide-from` and `stack-below` for the window size classes. Nothing arranges by class. @@ -99,14 +99,8 @@ Scheme::resolveProfileUsing(fn (): ?string => Setting::get('color_profile')); - Ink, from the same file: `md-ink` (on-surface), `md-ink-variant` (on-surface-variant: body copy beside a title, metadata), `md-ink-quiet` (outline: decoration only), `md-ink-primary`, `md-ink-error`, `md-ink-success`, `md-ink-warning`, `md-ink-info`, `md-ink-inverse`. On a container the ink is the container's `on-` role, which the component sets. - Text layout: `md-text-start|center|end`, `md-truncate`, `md-line-clamp-2|3`, `md-nowrap`, `md-tabular`, `md-mono`, `md-visually-hidden`. Monospace is for a value read or typed character by character (a token, a recovery code, a file name, a key): put it in ``, `` or ``, which take `--md-ref-typeface-mono` already, or give another element `md-mono` after its `md-type-*` class; `` for a field. `--md-ref-typeface-mono` is the package's addition, the platform's monospace faces, not an M3 token. - Interaction, from `foundation/interaction.css`: `md-state-layer` (M3's hover, focus, press and dragged overlay in the content colour; makes the element `position: relative` and `isolation: isolate`; `data-md-dragged` draws the dragged layer), `md-focus-ring` (the keyboard focus indicator), `md-touch-target` (a 48px target around a smaller drawing), `md-link` (a link in running text, underlined; add `md-ink-primary` where it should read as primary). -- **The application's own stylesheet** for the rest, on the tokens, never on a literal value: - - Colour roles, `var(--md-sys-color-…)`: `primary`, `on-primary`, `primary-container`, `on-primary-container`, `primary-dim`, `inverse-primary`, `primary-fixed`, `primary-fixed-dim`, `on-primary-fixed`, `on-primary-fixed-variant`; the same for `secondary` and `tertiary`; `error`, `on-error`, `error-container`, `on-error-container`, `error-dim`; `success`, `warning` and `info` with their `on-`, `-container` and `on-…-container`; `inverse-error|success|warning|info`; `surface`, `surface-dim`, `surface-bright`, `surface-container-lowest|low||high|highest`, `on-surface`, `on-surface-variant`, `inverse-surface`, `inverse-on-surface`, `outline` (a boundary that must be read), `outline-variant` (a divider, a card's edge), `scrim`, `shadow`. There is no white or black. - - Type: `font: var(--md-sys-typescale-body-md)` with `letter-spacing: var(--md-sys-typescale-body-md-tracking)`, never a size, weight or line height of your own. The typefaces are `--md-ref-typeface-brand` (Google Sans Flex), `--md-ref-typeface-plain` and `--md-ref-typeface-mono`; the weights `--md-ref-typeface-weight-regular|medium|bold`. - - Shape: `var(--md-sys-shape-corner-{none|xs|sm|md|lg|lg-increased|xl|xl-increased|xxl|full})`. - - Elevation: `box-shadow: var(--md-sys-elevation-{1…5})` — for what floats over content, not for panels (a panel separates by its container tone). - - Motion: `var(--md-sys-motion-spatial-{fast|default|slow})` (position, size, shape; springs that overshoot) and `var(--md-sys-motion-effects-{fast|default|slow})` (colour, opacity, which never overshoot), each always with its own `-duration`: `transition: transform var(--md-sys-motion-spatial-fast-duration) var(--md-sys-motion-spatial-fast)` — M3's published web durations, spatial 350/500/650 ms and effects 150/200/300 ms. `motion.scheme` in the config picks `expressive` (the default, with the bounce) or `standard` (minimal bounce), which the head script writes to `` and which swaps the three spatial springs; a rule names a spring, never a scheme. Reduced motion zeroes every duration in both schemes. - - Spacing: `var(--md-sys-measurement-space25)` … `var(--md-sys-measurement-space900)`. - - States: `--md-sys-state-{hover|focus|pressed|dragged}-state-layer-opacity` and `--md-sys-state-disabled-{content|container}-opacity`, mixed into a role: `color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-disabled-content-opacity) * 100%), transparent)`. A scrim is `color-mix(in srgb, var(--md-sys-color-scrim) 32%, transparent)`. +- **The application's own stylesheet** for the rest, on the tokens (`var(--md-sys-color-…)`, `-shape-corner-…`, `-elevation-…`, `-motion-spatial-…`/`-motion-effects-…` each with its own `-duration`, `-measurement-space…`, `-state-…-opacity`), never on a literal value — no white, black, hex, pixel radius, shadow, size, weight, line height or duration of your own. Which family names which role, corner, level, spring or space is the `material-3-design` skill's tables, not repeated here. `motion.scheme` in the config picks `expressive` (the default) or `standard`, written to ``; a rule names a spring, never a scheme. + - Names those tables leave out: the weights `--md-ref-typeface-weight-regular|medium|bold`; the state layers `--md-sys-state-{hover|focus|pressed|dragged}-state-layer-opacity` and `--md-sys-state-disabled-{content|container}-opacity`, mixed into a role with `color-mix()`; and the colour roles `inverse-error|success|warning|info` and `shadow`. The rules behind these names — which role, surface container, corner, type style, elevation level, motion spring, state and window size class to use, and M3's don'ts — are the `material-3` guideline (always on) and the `material-3-design` skill (the tables and Google's source pages); activate that skill before designing a screen. @@ -124,7 +118,12 @@ The rules behind these names — which role, surface container, corner, type sty } ``` -- Breakpoints are M3's window size classes, and only those: compact below 600px, medium 600px, expanded 840px, large 1200px, extra-large 1600px. A layout component takes them as props; your own CSS writes them as range media queries (`@media (width >= 840px)`, `@media (width < 600px)`), and the design guard rejects any other width; a script asks `resources/js/breakpoints.js` (`from('expanded')`, `upTo('medium')`), so a stylesheet and a script never disagree at the boundary pixel. A component's *own* width is a container query (`@container (width >= 28rem)`), which is a different thing. +- Breakpoints are M3's window size classes, and only those (the guideline has the five numbers). A + layout component takes them as props; your own CSS writes them as range media queries, and the + design guard rejects any other width; a script asks `resources/js/breakpoints.js` + (`from('expanded')`, `upTo('medium')`), so a stylesheet and a script never disagree at the + boundary pixel. A component's *own* width is a container query (`@container (width >= 28rem)`), + a different thing from a breakpoint. - The roles switch with the theme by themselves. A rule of your own that only applies in dark keys on `[data-theme='dark']` (the page's theme), never on `prefers-color-scheme` (the operating system's). - `x-figure` on an element holding one number counts it up on first appearance and on change. @@ -263,7 +262,7 @@ Label button, icon button, toggle and responsive FAB in one component. | `link`, `external`, `no-wire-navigate` | | renders ``, with `wire:navigate` unless external | | `spinner` | | `true` shows the loading indicator while its `wire:click` runs; a string names the action | | `tooltip`, `tooltip-left`, `tooltip-right`, `tooltip-bottom` | | plain tooltip; also the icon button's accessible name | -| `disabled`, `type`, `responsive`, `fab` | | `responsive` hides the label below `expanded`; `fab` is an extended FAB on a compact window (below `medium`), a filled button from there | +| `disabled`, `type`, `responsive`, `fab` | | `responsive` hides the label below `expanded`; `fab` is an extended FAB on a compact window (below `medium`), a filled button from there; a `disabled` fab is not drawn below `medium` — M3 removes a FAB whose action is unavailable — and is the disabled button from there (a `spinner` disabling it while its action runs leaves it on screen) | ```blade @@ -273,7 +272,7 @@ Label button, icon button, toggle and responsive FAB in one component. ### `` -M3's plain tooltip, standalone around any trigger: ``. `side`: `top` (default), `bottom`, `left`, `right`. Shows on hover (fine pointers) and keyboard focus, and goes 1.5s after the pointer or the focus leaves it (M3's transient tooltip); only one is on screen at a time. It is `aria-hidden`, so the trigger has to carry the same words itself — as an icon button's `aria-label` does. Where the tip says something the trigger does not, use ``, which points the trigger at its text. Buttons and FABs take a `tooltip` prop instead. +M3's plain tooltip, standalone around any trigger: ``. `side`: `top` (default), `bottom`, `left`, `right`. Shows on hover (fine pointers) and keyboard focus, and goes 1.5s after the pointer or the focus leaves it (M3's transient tooltip); the focus a sheet, a dialog or the modal rail moves to its first control as it opens does not show it, so a close button's tooltip waits for a Tab; only one is on screen at a time. It is `aria-hidden`, so the trigger has to carry the same words itself — as an icon button's `aria-label` does. Where the tip says something the trigger does not, use ``, which points the trigger at its text. Buttons and FABs take a `tooltip` prop instead. ### ``, ``, ``, `` @@ -491,7 +490,7 @@ A few lines of context around a trigger, with an optional `title` and `actions` ``` -Shows on hover and keyboard focus and goes 1.5s after the pointer or the focus leaves, as M3 times a plain tooltip too; `persistent` opens it on press and keeps it until a press elsewhere or Escape (use it when there are actions). The trigger is pointed at the bubble with `aria-describedby`, so its words are read out with the control. An open bubble stays open while the Livewire component around it renders, its actions' `wire:click` included. `side`: `bottom` (default), `top`, `left`, `right`. +Shows on hover and keyboard focus (not the focus a sheet or dialog moves to its trigger as it opens) and goes 1.5s after the pointer or the focus leaves, as M3 times a plain tooltip too; `persistent` opens it on press and keeps it until a press elsewhere or Escape (use it when there are actions). The trigger is pointed at the bubble with `aria-describedby`, so its words are read out with the control. An open bubble stays open while the Livewire component around it renders, its actions' `wire:click` included. `side`: `bottom` (default), `top`, `left`, `right`. ### `` @@ -514,7 +513,7 @@ The `illustration` slot draws the application's own artwork in place of the shap `variant`: `filled` (default, surface-container-highest), `elevated`, `outlined`; medium corner. Props `title`, `subtitle`, `separator`, `heading` (the title's element, `h3` by default: pass `h2` for a card straight under the page's `h1`); slots `figure` (full-bleed media), `menu` (top-end), `actions` (end-aligned). A card holds content and actions about one subject (M3); a table of many rows or a group of settings is a headed section, not a card. Choose its fill with `variant`, not a background of your own. -A card or list item that opens something is a **row**: `data-md-list-row` on it and `data-md-list-open` on its one opener (the title link or a button). A press anywhere else on the row reaches the opener; its other controls keep their own presses. Never wrap a card in `` or use a stretched link. A row answers with the state layer and one step of elevation; its corner does not move. +A card or list item that opens something is a **row**: `data-md-list-row` on it and `data-md-list-open` on its one opener (the title link or a button). A press anywhere else on the row reaches the opener; its other controls keep their own presses. Never wrap a card in `` or use a stretched link. A row answers with the state layer and one step of elevation; its corner does not move. `data-md-selected` on a row that is not a card — your own `
  • `, `
    ` or ``, the one whose sheet is open — fills it secondary-container with on-secondary-container ink, the state layer over the fill (`` writes it itself); a card takes no selected fill. A row that is not a card draws its state layer and that fill from `components/list-item.css`, so the CSS entry imports it even where no `` or `` renders (the design guard's `missingStylesheets()` asks for it). ```blade @@ -583,7 +582,9 @@ Props: `title`, `subtitle`, `icon` (centred hero icon), `separator` (draw the di An M3 side sheet, bound like ``; `close()` in scope. Props: `title`, `subtitle`, `separator`, `side` (`end` default, `start`; mirrored in RTL), `width` (`400px`), `with-close-button` (**default true** — M3 requires a close affordance; `:with-close-button="false"` is ignored when Escape or the scrim is off, or on a `standard` sheet), `close-on-escape` (default true), `without-backdrop-close`, `actions` slot (**left**-aligned in a 72dp row, which is what the side-sheet spec says; a dialog's are trailing-aligned). For the second pane of a list-detail layout use `` instead — `` no longer has a `pane` prop. Its body is a size container — lay out inside it with a container query in your own CSS (`@container (width >= 28rem)`), never a window size class. -`standard` is M3's other side-sheet variant: supplementary content beside the primary content — filters, details, a list of actions — co-planar from `expanded`, flat on `surface` with 0dp elevation and no corner, the window's full height, an outline-variant rule down its inner edge instead of a scrim, nothing inert and no focus trap. Below `expanded` it is the modal sheet. Capped at M3's 400dp whatever `width` says, and it always draws the close button. Render it beside its content in a row that only lays out side by side from `expanded` (``, or a caller's own row). +Modal surfaces stack: a ``, menu, select or searchable choice opened inside or over a modal side sheet, bottom sheet or the modal rail — or one sheet opened from another, nested or rendered elsewhere on the page — takes its own Escape, so one press closes one layer, and the layer on top stays readable to a screen reader while the sheet under it keeps the rest of the page hidden. Render the dialog wherever suits the page; nothing needs to be moved inside the sheet. + +`standard` is M3's other side-sheet variant: supplementary content beside the primary content — filters, details, a list of actions — co-planar from `expanded`, flat on `surface` with 0dp elevation and no corner, sticky and as tall as the window below the top safe area (and below ``'s sticky `top` app bar), an outline-variant rule down its inner edge instead of a scrim, nothing inert and no focus trap. Below `expanded` it is the modal sheet. Capped at M3's 400dp whatever `width` says, and it always draws the close button. Render it beside its content in a row that only lays out side by side from `expanded` (``, or a caller's own row). ### `` @@ -667,9 +668,9 @@ M3 text fields. `variant`: `outlined` or `filled`; without it, `config('livewire - ``: `icon`, `icon-right`, `prefix`, `suffix`, `clearable`, `copyable` (copies the value, confirms with a snackbar), `counter`, `size` (`sm` 40px, `xs` 32px — for unlabelled toolbar controls; give them `aria-label`), `mono`. - ``: a reveal button; `icon`, `size`. - ``: grows from `rows` (3) to `max-rows`, then scrolls; `:autogrow="false"` for a fixed, hand-resizable one; `counter`. -- Width: M3 asks that a text field never span the full width of a large screen, so from `medium` (600px) every field stops at **40rem**; below that it fills its pane. A width rule of your own on its `class` (`max-inline-size: 24rem`) narrows or widens it, because your unlayered CSS outranks the package's, and `full` (on ``, ``, ``) takes the bound off for a field that really is the width of its pane — a search row, an editor. ``'s bar carries M3's own bound, 720px. +- Width: M3 asks that a text field never span the full width of a large screen, so from `medium` (600px) every field stops at **40rem**; below that it fills its pane. A width rule of your own on its `class` (`max-inline-size: 24rem`) narrows or widens it, because your unlayered CSS outranks the package's, and `full` (on ``, ``, ``, ``, ``, ``, ``, `` and ``) takes the bound off for a field that really is the width of its pane — a search row, an editor. ``'s bar carries M3's own bound, 720px. - `counter` (on `` and ``) puts M3's character counter at the end of the supporting-text row, beside the hint or the error: `n/max`, counted on every keystroke against the field's own `maxlength`, and in the error colour once the value is past it. It needs `maxlength` — without one there is nothing to count against and nothing is drawn. It is said as "Character count, 5/20" from a polite region a second after typing stops. -- ``: native `` (M3 menu where the browser supports customizable selects). `options` as `['id' => …, 'name' => …, 'disabled' => bool]`, `option-value`, `option-label`, `placeholder` + `placeholder-value`, or `