Every foundations, styles and components page of m3.material.io (238, from the sitemap) extracted into docs/reference/m3, five audit reports with 142 findings in docs/audits/m3-alignment, and the 2.0.0 plan in docs/plans/material-3-alignment.md. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
32 KiB
Audit: core (tokens, scheme, theme, icons, shapes, guard, showcase foundations, AI docs)
Ground truth: docs/reference/m3/styles.md, docs/reference/m3/foundations.md, raw pages under m3/raw/.
Library paths are relative to /Users/surtic86/Privat/Development/livewire-material.
Summary
The core is in good shape: every 2025-spec colour role (the *-dim roles and the fixed roles included) is generated and mapped, the legacy surface-tint/surface-variant are correctly left out, the corner scale is M3 Expressive's ten steps, the five shadow levels are Google's web geometry, the six springs carry the exact Compose damping/stiffness constants, the state layer uses M3's 8/10/10 % and hover is pointer-only, the typescale sizes/line heights/weights match, and theme switching honours both the OS and the visitor's choice. The 35 shapes and the full Symbols set are ported from Google's sources.
The gaps are around the edges of the system rather than in it. (1) The three custom state colours (success/warning/info) are built with the 2021 customColor recipe, so they ignore the --contrast level and the 2025 spec's tone rules that error follows. (2) Only the standard contrast level exists; M3 (May 2025) makes standard/medium/high part of the colour system. (3) The semantic ink utilities (text-meta 60 %, text-quiet 38 %, border-divider 40 %) are M2-style opacity emphasis, which M3 replaced with on-surface-variant/outline-variant, and 38 % collides with M3's disabled opacity. (4) Tailwind's own radius, shadow, type-size, weight, easing and duration utilities still compile, so an application (or an AI agent) can silently leave the M3 scale; only colours were cleared. (5) There are no tokens or variants for M3's five breakpoints, no dragged state, and no shared touch-target utility. (6) Type tracking follows the older material-web numbers rather than Compose's, and the emphasized styles reuse baseline tracking. (7) The AI docs list the utilities but explain none of M3's rules for choosing between them; that is the "core concept" document the user asked for.
Counts: must-fix 1 · should-fix 12 · nice-to-have 11.
Findings
C1 · scheme command · custom colours ignore the contrast level and the 2025 spec
- Severity: must-fix
- M3 says: "Custom components support contrast levels simply by using Material color roles… the role's resolved value changes per contrast level automatically" (reference-styles §Color/Contrast levels; site
styles/color/roles). Aug 2024: on-container roles became "more colourful while remaining accessible"; the 2025 spec that the library asks for (--spec=2025) applies those curves toerror.define-new-colors: custom colours should behave like the built-in roles. - Library does:
resources/node/scheme.mjs(tail) buildssuccess/warning/infowith material-color-utilities'customColor()(pe(...),blend:false): fixed tones 40/100/90/10 in light and 80/20/30/90 in dark, no contrast curve, no spec awareness. Sophp artisan material:scheme --contrast=1raises every M3 role but leaves the three state colours at standard contrast, and theiron-*-containerpairs are tone 10/90 whileon-error-container(2025) is#6e0523-style tone ~25. Visible inresources/css/tokens/scheme.css:66-77vs:62-65. - Fix: in
bin/scheme.mjs(source of the bundle), define the three colours asDynamicColors on their ownTonalPaletteexactly ascolor_spec_2025.jsdefineserror/onError/errorContainer/onErrorContainer(sameContrastCurves andToneDeltaPairs), evaluated against the sameDynamicScheme(socontrastLevel,isDark,specVersion, platform all apply). Rebuildresources/node/scheme.mjs; regeneratetokens/scheme.css,scheme.json, the workbench scheme. Add aSchemeCommandTestcase: contrast 1 changessuccess. - Effort: M
- Breaks API? no (role names unchanged; values change)
C2 · scheme · only the standard contrast level is generated
- Severity: should-fix
- M3 says: "Three levels of contrast: standard, medium (3:1 minimum), high (7:1)… contrast settings apply automatically in both light and dark theme" (reference-styles §Color;
styles/color/roles, "What's new May 2025"). Accessibility principle "Honor individuals — build in customizable features" (reference-foundations §Accessibility). - Library does:
SchemeCommand(src/Console/SchemeCommand.php:24) takes--contrastonce and writes a single light and dark block; nothing at runtime can raise contrast;theme.jshas no contrast state; no@media (prefers-contrast: more). - Fix: have
material:schemealso emit[data-contrast="medium"](contrast 0.5) and[data-contrast="high"](1.0) blocks for light and dark (and per profile), plus@media (prefers-contrast: more) { :root:not([data-contrast]) {…} }as the OS default; addcontrastto$store.theme(resources/js/theme.js) and to the head script's attribute set (theme-script.blade.php, kept acrosswire:navigatelikedata-theme);<x-theme-toggle mode="picker">or a new<x-contrast-picker>exposes it. Document in SKILL.md §Theme. - Effort: M
- Breaks API? no (opt-in)
C3 · theme.css · M2-style opacity inks and lines instead of M3 roles
- Severity: should-fix
- M3 says: "Default typography color is on surface (or on surface variant as a strong alternative)"; dividers use outline variant, not a lighter mix; "Always apply color roles, never static hex values"; 0.38 is the disabled opacity (reference-foundations §States values; reference-styles §Color rules, §Typography accessibility).
- Library does:
resources/css/tokens/theme.css:99-106defines--color-meta= on-surface 60 %,--color-quiet= on-surface 38 %,--color-chrome= outline-variant 60 %,--color-divider= outline-variant 40 % viacolor-mix().text-quiettherefore looks exactly like disabled text (text-on-surface/38, used 21× in components), andborder-divideris lighter than M3's divider. None of these utilities is used by a package component (grep: only the showcase swatches and oneborder-divider/divide-divider), so they exist for ReStride's templates. - Fix: keep the names, repoint them to roles:
--color-body→ on-surface-variant (already),--color-meta→ on-surface-variant,--color-quiet→ outline,--color-structure→ outline-variant (already),--color-chrome→ outline-variant,--color-divider→ outline-variant. Say in SKILL.md that emphasis is a role, not an opacity, and that 38 % means disabled. If ReStride needs the lighter divider, it overrides the variable in its own@theme. - Effort: S
- Breaks API? no (names stay; rendered colour changes in ReStride)
C4 · theme.css · Tailwind's default radius, shadow, type-size, weight, easing and duration utilities still compile
- Severity: should-fix
- M3 says: "Do use design tokens instead of hardcoded values" (reference-foundations §Design tokens); the corner scale, elevation levels, typescale and motion tokens are the only values (reference-styles §Shape, §Elevation, §Typography rule "avoid changing type size", §Motion).
- Library does:
theme.css:19-25clears--color-*only.rounded-lg(Tailwind: 8 px, M3 "large" is 16 px),rounded-xl(12 px vs 28 px),shadow-md,text-sm/text-lg,font-bold,leading-*,tracking-*,ease-in-out,duration-300all still compile with Tailwind's values. The package's own views are clean (onlyrounded-full×4 androunded-none×1, both fine), but consuming apps and AI agents are not stopped.DesignGuarddoes not look for them. - Fix: two steps. (a) Now, non-breaking:
DesignGuardgains a "value outside the M3 scale" check forrounded-(xs|sm|md|lg|xl|2xl|3xl|4xl),shadow-(2xs|xs|sm|md|lg|xl|2xl),text-(xs|sm|base|lg|xl|\dxl),font-(thin|…|black),leading-*,tracking-*,ease-(in|out|in-out),duration-\d+,blur-*? (no), each with the M3 utility to use instead. (b) In the next major:@theme { --radius-*: initial; --shadow-*: initial; --ease-*: initial; --text-*: initial; --font-weight-*: initial; --leading-*: initial; --tracking-*: initial; }before re-declaring the M3 ones, mirroring what was done for colour (keep--radius-full/rounded-nonebehaviour by declaring--radius-full: 9999px). - Effort: S (guard) + S (theme, but a major-version change)
- Breaks API? (a) no · (b) yes for applications using Tailwind defaults
C5 · type.css · tracking follows material-web, not Compose; emphasized styles reuse baseline tracking
- Severity: should-fix
- M3 says (reference-styles §Typography,
TypeScaleTokens.kt): Display Large −0.2 sp; Title Medium 0.2 sp; Body Medium 0.2 sp; emphasized: Display Large 0, Title Medium 0.15, Body Large 0.15, Body Medium 0.25 (others unchanged). - Library does:
resources/css/tokens/type.css:21display-lg tracking −0.015625 rem (−0.25 px, material-web);:39title-md 0.009375 rem (0.15 px, M3: 0.2 sp = 0.0125 rem);:48body-md 0.015625 rem (0.25 px, M3: 0.2 sp); everytype-emphasized-*utility (:101onward) uses the baseline-trackingvariable, so emphasized display-lg keeps −0.25 px instead of 0, emphasized body-lg keeps 0.5 px instead of 0.15. - Fix: add
--md-sys-typescale-emphasized-*-trackingvariables with Compose's values and use them in the emphasized utilities; correct the three baseline values to Compose's (sp/16 rem). Sub-pixel, but it is what the Expressive token file says and the header comment cites material-web, which is pre-Expressive. - Effort: S
- Breaks API? no
C6 · type.css · regular styles do not reset font-variation-settings
- Severity: nice-to-have
- M3 says: emphasized styles are applied deliberately, per element (reference-styles §Typography rules).
- Library does:
type-emphasized-*setsfont-variation-settings: "ROND" 100(type.css:107), an inherited property;type-*utilities set none, sotype-body-mdtext inside an element carryingtype-emphasized-title-md(a card slot under an emphasized wrapper) renders fully rounded. - Fix: every regular utility sets
font-variation-settings: normal(or"ROND" 0). - Effort: S
- Breaks API? no
C7 · font · variable axes of the subset are unverified here
- Severity: nice-to-have (verification)
- M3 says: brand/plain typefaces are the product's choice; Roboto is the default (reference-styles §Typography fonts). Google Sans Flex is Google's Expressive typeface (the site's shape page: "M3 shapes and Google Sans Flex share roundness attributes"), so the choice is aligned.
- Library does:
font.cssclaims weight 400–700 andROND0–100 are kept. I could not open the woff2 (no fontTools on this machine). - Fix: add a test (or a
bin/check) that reads thefvartable and asserts thewghtandRONDaxes exist, so a future re-subset cannot silently drop the axis the emphasized styles depend on. - Effort: S
- Breaks API? no
C8 · motion.css · spring durations differ from the web equivalents Google publishes
- Severity: should-fix (with a counter-argument)
- M3 says (
styles/motion/overview/specs, reference-styles §Motion "Web curve equivalents"): expressive spatial fast/default/slow = 350/500/650 ms, effects = 150/200/300 ms, each with a cubic-bezier; "springs aren't natively supported on the web, so Web should use these approximated curves with matching duration". - Library does:
motion.css:33-52samples the real springs intolinear()(more faithful than Google's cubic-bezier) but stops at the settle time: 360/440/600 ms and 150/240/330 ms. Default spatial is 12 % quicker than Google's web number, slow effects 10 % slower. - Fix: either (a) sample each spring over Google's published web duration (the curve reaches 1 earlier and holds, so the bounce timing is unchanged) so the
*-durationtokens read 350/500/650/150/200/300, or (b) keep the settle times and record why in the header. Also add the six published cubic-bezier curves as--md-sys-motion-spring-*-bezierfallbacks for@supports not (animation-timing-function: linear(0,1))(Safari < 17.2 is below the floor, so this is documentation more than need). (a) is "what Google says". - Effort: S
- Breaks API? no
C9 · motion.css · only the Expressive motion scheme exists
- Severity: nice-to-have
- M3 says: two preset schemes, Expressive (default) and Standard ("minimal bounce, for utilitarian products"), swappable product-wide (reference-styles §Motion principles;
StandardMotionTokens.kt: fast spatial 0.9/1400, default 0.9/700, slow 0.9/300; effects identical). - Library does: six springs, Expressive only.
- Fix: sample the three Standard spatial springs too and let
[data-motion="standard"]on<html>(or a config flag rendered by the theme script) swap the--md-sys-motion-spatial-*variables. Effects are the same in both schemes. - Effort: S
- Breaks API? no
C10 · state.css · no dragged state, no disabled tokens, no shared touch target
- Severity: should-fix
- M3 says: state layer opacities hover 0.08, focus 0.10, pressed 0.10, dragged 0.16; disabled content 0.38; "the size of state layers is 40dp while the interactive target size is 48dp"; target spacing 8 dp (reference-foundations §States, §Accessibility values).
- Library does:
state.css:26-40covers hover/focus/active; nothing for dragged (cards, chips, list items and the slider are the components M3 gives a dragged state); no--md-sys-state-*custom properties, so components hard-code/38,/12,/10(21, 10 and 4 occurrences); the 48 px target is built ad hoc inbutton.blade.php:167with anafter:pseudo-element and nowhere shared. - Fix: in
state.cssdeclare--md-sys-state-hover-opacity: 0.08,focus 0.10,pressed 0.10,dragged 0.16,disabled-content 0.38,disabled-container 0.12and use them instate-layer(add&[data-dragged]::before { opacity: var(--md-sys-state-dragged-opacity) }); add atouch-targetutility (theafter:pseudo-element from the button,min 48×48) so small icon buttons, chips, checkboxes, radios and switch share one implementation. - Effort: S
- Breaks API? no
C11 · state.css · focus ring spec has no source on the M3 site
- Severity: nice-to-have (documentation)
- M3 says: only "a ring-like keyboard focus indicator" (reference-foundations §States; the site defines no thickness/offset/colour — see its "Cross-cutting gaps" 1).
- Library does:
focus-ring= 3 pxsecondary, offset 2 px (state.css:47-53), which is material-web'smd-focus-ringdefault (3 px, outward offset 2 px, secondary). - Fix: none to the code; cite material-web in the header so nobody "corrects" it to the site's silence.
- Effort: S
- Breaks API? no
C12 · core · no tokens or variants for M3's breakpoints
- Severity: should-fix
- M3 says: five breakpoints — compact < 600, medium 600–839, expanded 840–1199, large 1200–1599, extra-large ≥ 1600 dp — with the navigation component, pane count and dialog/menu choice per breakpoint (reference-foundations §Layout breakpoints tables).
- Library does: nothing in the tokens; components switch on Tailwind's
sm640 /md768 /lg1024 /xl1280 (the navigation audit maps each use). An application cannot write "at the medium breakpoint" at all. - Fix:
theme.cssadds--breakpoint-medium: 600px; --breakpoint-expanded: 840px; --breakpoint-large: 1200px; --breakpoint-extra-large: 1600px(Tailwind 4 turns these intomedium:,expanded:,large:,extra-large:variants;max-medium:etc. come for free), keepssm/md/lg/xlfor now, and the components migrate to the M3 variants in their own findings. Add pane tokens as custom properties:--md-sys-layout-pane-fixed: 360px/412px,--md-sys-layout-side-sheet-max: 400px, margins 16 px compact / 24 px otherwise (older M3 layout page values; the current site gives no numeric margin table — mark as such). - Effort: S (tokens) — the component migration is counted in the other audits
- Breaks API? no
C13 · DesignGuard · no check for values outside the M3 scale, hex colours, or white/black
- Severity: should-fix
- M3 says: "Always apply color roles, never static hex values or raw tonal-palette values" (reference-styles §Color rules).
- Library does:
src/Testing/DesignGuard.php:26-33flags the Tailwind palette and daisyUI colours; it does not flag arbitrary values (bg-[#1d7afc],text-[rgb(…)]),bg-white/text-black(re-added intheme.css:21-22and not M3 roles; the M3 white issurface-container-lowestin light), or any of the non-token utilities in C4. - Fix: add patterns for
(bg|text|border|…)-\[#,-\[rgb,-\[hsl,-\[oklch, and an opt-inforbidAbsolutes()forwhite/black; add the C4 list with a hint per match ("rounded-lg→rounded-corner-lg"). - Effort: S
- Breaks API? no
C14 · theme-toggle · the picker is drawn as M3's deprecated segmented button
- Severity: should-fix
- M3 says: segmented buttons are "M3 only", replaced in M3 Expressive by the connected button group (reference-components-a §Segmented buttons / §Button groups; the plan doc lists segmented button among the six deprecated components).
- Library does:
resources/views/components/theme-toggle.blade.php:27-49(mode="picker") draws an outlined 40 px group withborder-outlineandaria-checkedfills — the segmented-button anatomy — while<x-group>(the connected button group) already exists. - Fix: render the picker with
<x-livewire-material::group>(threewire:model-less radio options bound to$store.theme), or reuse its classes; keeprole="radiogroup"and the arrow-key behaviour. - Effort: S
- Breaks API? no (
data-theme-optionhooks can stay)
C15 · scheme · harmonisation of the custom colours is not offered
- Severity: nice-to-have
- M3 says: "you can choose to harmonize your static colors to the scheme's primary color… while retaining the semantic meaning"; "colors can stay completely static and forgo harmonization if their values are tied to literal sources such as brand colors" (
styles/color/advanced/define-new-colors). - Library does:
customColor(..., blend:false)always (scheme.mjs tail); no option. - Fix:
--harmonizeflag onmaterial:scheme(andharmonizeper profile) passed asblend:true; default off, as today. Do together with C1. - Effort: S
- Breaks API? no
C16 · icons · one weight/grade/optical size for every context
- Severity: nice-to-have
- M3 says: grade −25 "for a light icon on dark background"; optical size 20 for 20 dp icons in dense/desktop layouts; 40/48 for display pairing (reference-styles §Icons axes table).
- Library does: Material Symbols Rounded 400/0/24 only (
bin/fetch-symbols,icon.blade.phpheader); buttonsxs/smdraw 20 px icons from the 24-opsz outlines (button.blade.php:147-149), so strokes are ~17 % thinner than a true opsz-20 glyph; dark theme keeps grade 0. - Fix: none that is cheap — each extra cut is another 4,135 SVGs (~2.6 MB). Record the trade-off in the icon header and SKILL.md; if ever needed, ship
opsz20for the outlined set only and let<x-icon size="20">pick it. - Effort: L
- Breaks API? no
C17 · scheme · background/on-background are emitted, surface-tint/surface-variant are not
- Severity: nice-to-have (informational, aligned)
- M3 says: background/on-background are legacy roles with the same values as surface/on-surface; surface-variant was superseded by the surface-container family (Feb 2023); surface tint is deprecated (reference-styles §Color roles table).
- Library does: emits and maps
background/on-background(scheme.css:16-17,theme.css:29-30), omits the two deprecated ones. Correct. Consider markingbg-backgroundas "same asbg-surface; prefer surface" in SKILL.md so agents do not treat them as two surfaces. - Effort: S
- Breaks API? no
C18 · scheme · the package default is the 2025 rendering of the baseline seed, not the site's baseline table
- Severity: nice-to-have (informational)
- M3 says: the baseline palette table (
PaletteTokens.kt, sitestyles/color/static/baseline) is the 2021 spec: primary 40 =#6750A4, on-primary = white. - Library does:
tokens/scheme.cssis#6750a4tonal-spot through the 2025 spec (primary #655789,on-primary #fdf7ff). Both are Google's; the header says so. Nothing to fix; the showcase colour page could say "spec 2025" so a reader comparing with the site's swatches is not puzzled. - Effort: S
- Breaks API? no
C19 · elevation · no expression of "which component rests at which level"
- Severity: nice-to-have (documentation)
- M3 says: level 3 = dialogs, FAB, pickers, search; level 2 = menus, scrolled app bar, navigation bar, rich tooltip, toolbar; level 1 = elevated button/card/chip, modal sheets; level 0 = everything else; +1 level on hover (reference-styles §Elevation tokens table); scrim 32 %.
- Library does: shadow tokens only (
elevation.css); the showcase shows five boxes with no mapping; components pick levels individually (correctly where I looked: menu 2, FAB 3, elevated button 1→2 on hover,bg-scrim/32×4). - Fix: put the table in the new guideline/skill and in the showcase elevation section, with M3's rule "tonal separation first, shadows for floating things and interaction".
- Effort: S
- Breaks API? no
C20 · showcase · the foundation pages show tokens but not M3's rules for them
- Severity: nice-to-have
- M3 says: (as in C19, plus) surface container hierarchy by emphasis; outline vs outline-variant; type roles and when emphasized is used; spatial vs effects; corner scale per component class; optical roundness (outer − padding = inner).
- Library does:
showcase/sections/{colour,type,shape,elevation,motion,icons}.blade.phplist every utility with one sentence each; the colour page groups "Ink and lines" as if they were roles (see C3). - Fix: one short "when to use" paragraph per section, drawn from the same guideline text (C24), so the showcase and the AI docs cannot drift.
- Effort: S
- Breaks API? no
C21 · theme script · data-theme only, no prefers-color-scheme in CSS — aligned
- Severity: nice-to-have (no change)
- M3 says: light/dark is a user preference; honour the OS and the person's choice (reference-foundations §Accessibility principles; §Color "built-in dark theme").
- Library does: resolves
systembefore first paint, follows OS changes whilesystem, keeps the attributes acrosswire:navigate, declarescolor-schemeper block. Correct. - Fix: none. (C2 adds contrast to the same mechanism.)
C22 · reduced motion · stricter than the site, aligned with its intent
- Severity: nice-to-have (no change)
- M3 says: no global reduced-motion rule on
styles/motion; component pages ask for parallax/expansion to be removed under reduced motion (reference-styles §Motion accessibility). - Library does: zeroes every duration token (
motion.css:66-77); carousel, progress, tabs and figure check the media query themselves. Correct; the header's "anything that animates without them is a bug" is a good rule to repeat in the guideline.
C23 · icons · accessibility of <x-icon> — aligned
- Severity: nice-to-have (no change)
- M3 says: decorative icons hidden; meaningful icons labelled; don't put the role in the label (reference-foundations §Accessibility labelling).
- Library does:
aria-hiddenby default,role="img"+aria-labelwhenlabelis given,focusable="false"(icon.blade.php:27-33). Correct.
C24 · AI docs · the M3 core concepts are not written down anywhere an agent reads
- Severity: should-fix (this is the user's explicit request)
- M3 says: see the list under "Concepts missing from the AI docs" below.
- Library does:
resources/boost/guidelines/core.blade.php(6 bullets: what the package is, no maryUI, theme script, showcase, error pages/mail) and SKILL.md §Tokens/§Theme/§Conventions list the utilities and three rules (elevation is for floating things; pair easing with duration;dark:followsdata-theme). Nothing says what a role, a container level, a corner size, a type role or a spring is for, nor any layout/accessibility rule. - Fix: a second always-on guideline
resources/boost/guidelines/material-3.blade.php(short: the rules that change what an agent writes) and a fullerresources/boost/skills/material-3-design/SKILL.md(the concept reference with the tables), both generated from one source or tested against each other; Boost 2.8 loads every file underresources/boost/guidelinesand every skill directory (verified inlaravel/boost/src/Install/GuidelineComposer.php:320-351,SkillComposer.php:127-141). Add the drift test. - Effort: M
- Breaks API? no
Deliberate deviations
| Where | Deviation | Reason given | Holds up? |
|---|---|---|---|
shape.css |
corner-full = 9999px instead of material-web's 50cqmin |
container units need a declared container | Yes. Compose's CircleShape is 50 % of the smaller dimension = a stadium; 9999px gives the same on rectangles, 50% would not. |
elevation.css |
color-mix() instead of hsl(from …) |
browser floor | Yes; same result. |
motion.css |
springs sampled to linear() rather than Google's cubic-bezier approximations |
closer to the physics | Yes for the curve; the durations differ from Google's published web numbers (C8). |
motion.css |
reduced motion zeroes durations | one place instead of per component | Stricter than the site; consistent with "Honor individuals". |
theme.css |
colour blocks are @theme inline |
nested data-theme sections |
Sound; verified by a browser test per the plan doc. |
theme.css |
white/black re-added | absolutes | M3 has no such roles; white is surface-container-lowest (light). Harmless, but the guard should at least be able to flag them (C13). |
theme.css |
--color-meta/quiet/divider/chrome opacity mixes |
ReStride's templates | Does not hold against M3's text (C3): M3 dropped opacity-based emphasis with M3; 38 % is disabled. |
type.css |
Google Sans Flex for brand and plain, ROND 100 on emphasized |
Expressive's typeface | Holds: the site names Google Sans Flex on the shape page and leaves the typeface to the product; ROND is that font's own axis, not an M3 token. |
| scheme | surface-tint/surface-variant omitted |
superseded/deprecated | Holds (reference-styles §Color roles). |
| scheme | success/warning/info via customColor, unharmonised |
"harmonisation off" | Off is allowed; the 2021 recipe and contrast-blindness are not (C1, C15). |
icon |
one Symbols cut, 400/0/24, SVG not font | any name works, no runtime download | Holds for weight; grade/optical size are lost (C16). |
theme-script |
OS never read by CSS | the script resolves system |
Holds. |
Aligned (keep as is)
- All 2025-spec colour roles generated and mapped,
*-dimand fixed roles included;scheme.jsonandScheme.phpfill missing roles from the default; every pair (x/on-x) comes from Google's algorithm. - Corner scale: none 0, xs 4, sm 8, md 12, lg 16, lg-increased 20, xl 28, xl-increased 32, xxl 48, full — exactly M3 Expressive's ten steps, same names.
- Elevation: five levels with material-web's shadow geometry; tonal separation preferred; scrim at 32 % where used.
- Motion: spatial 0.6/800, 0.8/380, 0.8/200; effects 1.0/3800, 1.0/1600, 1.0/800 — the Compose Expressive constants; legacy easing tokens present; reduced motion handled centrally and in the four scripted components.
- Typescale: all 15 sizes, line heights and weights match
TypeScaleTokens.kt, emphasized set present (weights right). - State layer: 8/10/10 %, content colour, hover only on
(hover: hover), disabled removes it; focus ring only on:focus-visible. - Theme:
data-themeresolved before paint, OS followed whilesystem, choice persisted, legacy keys adopted, attributes survivewire:navigate,color-schemedeclared,dark:variant ondata-theme. - Shapes: the 35 Expressive shapes ported from androidx, all fill the same box, decorative and hidden from AT.
- Symbols: Google's own 24 px Rounded files (not the 48-opsz npm cut), outlined and filled,
currentColor, hidden unless labelled. - Safe areas:
--material-safe-*= M3's safety region. - DesignGuard already enforces "roles, not palette" and "no maryUI/daisyUI".
Missing (M3 things the core does not offer at all)
- Contrast levels medium/high (C2).
- Breakpoint tokens/variants and pane/margin tokens (C12).
- Dragged state, disabled tokens, a shared touch target (C10).
- Standard motion scheme (C9).
- Harmonisation option for custom colours (C15).
- An M3 concept guideline for agents (C24).
Breakpoint map (core only)
| Item | Library | M3 breakpoint | Gap |
|---|---|---|---|
| tokens | none; Tailwind sm 640 / md 768 / lg 1024 / xl 1280 / 2xl 1536 |
compact <600 / medium 600 / expanded 840 / large 1200 / extra-large 1600 | no M3 breakpoint is expressible; nearest Tailwind values are 40–88 px off (see navigation audit for each component) |
Concepts missing from the AI docs (input for the "Material 3 core concept" guideline)
Each line: what M3 says → whether the package docs say it today.
Colour
- Roles are "paint-by-number" slots; a colour is always a role, never a hex or palette tone → docs say "every colour class names an M3 role" (yes) but not why or the hex rule.
- Pair
xwithon-xonly; a container is a fill, "on" is for text/icons on it; don't mix pairs (primary + secondary-container + on-surface) → no. - Primary = high-emphasis actions; secondary = less prominent, tonal fills; tertiary = complementary accent; error = static semantic → no.
- Surface = page; surface-container-lowest…highest = emphasis hierarchy, not elevation; navigation on surface-container; same mapping for a region across breakpoints → no.
outlinefor boundaries that must read (text fields),outline-variantfor dividers and cards; neveroutlineon dividers → no.- Fixed/dim roles: "if you aren't sure, you probably shouldn't"; never where contrast matters → no.
- Inverse roles are for the snackbar/inverse surfaces only → no.
- success/warning/info are the package's custom roles, built like error; semantic colours are never dynamic-coloured away → no.
- Low emphasis is
on-surface-variant, 38 % means disabled; links are primary and underlined → no (the opposite:text-quietis documented as decoration). - Contrast targets 4.5:1 small text, 3:1 large text/graphics, 3:1 clustered elements; disabled exempt → no.
- Three contrast levels exist → no.
Elevation
- Tonal separation first; shadows for floating elements and interaction only; the level table per component; +1 on hover; scrim 32 % → partly ("for what floats over content").
Shape
- Which corner for what: full for buttons/chips/FAB-menu, xs 4 for text fields/snackbar, sm 8 for chips, md 12 for cards, lg 16 for FAB, xl 28 for dialogs/sheets/menus in Expressive, xxl 48 for large containers → no.
- Optical roundness: inner radius = outer radius − padding → no.
- Shapes are decoration, never semantic; use sparingly; press morph is the interaction cue → no.
Typography
- Role purposes: display (big, short), headline (short, high emphasis), title (medium, short), body (paragraphs), label (inside components; buttons use label-lg) → no.
- Emphasized styles are opt-in, for selection, primary actions, headlines, badges — not decoration → no.
- 40–60 characters per line; tabular numbers where values change; don't change sizes when customising → no (only "never assemble text-* by hand").
Motion
- Spatial springs for position/size/shape (they overshoot), effects for colour/opacity (never overshoot); fast for small elements, default for most, slow for large → partly (the pairing rule, one sentence).
- Enter = decelerate, permanent exit = accelerate, temporary exit = emphasized; exits shorter than enters → no.
- Reduced motion: everything through the tokens goes instant; parallax/expansion removed → no.
States
- Six states; two visual indicators per state; hover 8 % / focus 10 % / press 10 % / drag 16 %; disabled 38 % content, 12 % container, no hover; state layer takes the content colour → no (utilities only).
- 48 × 48 target, 8 dp between targets, never below 48 by default, density is opt-in → no.
Layout
- Five breakpoints and what changes at each (navigation bar → collapsed rail → expanded rail; 1 → 2 → 3 panes; bottom sheet → menu; full-screen dialog → basic dialog) → no.
- Scaffold: bars, rails, panes; safety regions; pane widths 360/412, side sheet ≤ 400 → partly (safe areas documented).
- RTL: leading/trailing, mirror directional icons, use logical utilities (
ps-,ms-,start-) → no.
Accessibility
- Landmarks (one main/banner/contentinfo; label repeated navs; no role in the label); headings in order, one H1; dialog focus in/out; labels for icon-only controls; decorative images hidden; keyboard shortcuts need a modifier → no.
Icons
- Filled = active/selected; keep one weight per group; 24 default, 20 in dense UI; label complex icons below 20; 48 target → partly (
filleddocumented).