Commit Graph
349 Commits
Author SHA1 Message Date
Andreas Reinhold / reiniandClaude Sonnet 5 6147221b89 Document the design guard's 2.0.0 API in the README and development skill
Plan step 41: short updates only (the full docs rewrite is Phase H) --
missingStylesheets(), the family-table replacements now named (a layout
component and prop, an md-* class, or a var(--md-sys-*) token instead of a
dead Tailwind utility), and a note that forbidAbsolutes()/forbidOpacityInk()
matter mainly to an application still migrating off Tailwind.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 08:18:41 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 e1fe573182 Bundle the error page's fallback instead of hand-writing it
Plan step 40. ErrorPage::fallbackStyles() now inlines
Stylesheets::bundle() of the foundation and error-page.css (which
imports button.css and shape.css) rather than a hand-copied
stylesheet, so the fallback can never drift from the built version.
Every @font-face block is dropped structurally (withoutFontFace(),
brace-balanced, not a text search) since there is no build to serve
the font file; --md-ref-typeface-brand already lists ui-sans-serif,
system-ui and sans-serif after the brand name, so the page still gets
a sensible system stack. The scheme half comes from
Scheme::forStylesheet() drawn through SchemeStylesheet::levels(), in
material-scheme.css's own selector shape: standard, the medium and
high contrast levels, and a block per colour profile keyed on
[data-scheme] (the theme script has already resolved and written the
active one to <html> before this stylesheet is read, so nothing here
picks one in PHP). Both halves are cached per worker, the scheme half
by the scheme file's path and mtime.

Tests: both render paths, the inlined CSS's shape (no @import, no
relative url(), no @font-face, the button/shape/error-page rules, the
scheme's roles, [data-contrast='high'] and a profile block), and a new
browser test that forces the fallback and checks the button, the
shape and a dark-mode repaint.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 07:30:05 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 58ef33dc95 Select on [dir='rtl'] now that no build rewrites :dir()
Plan step 39 (part 4). With Tailwind gone the Workbench's Vite build
still turns every `:dir(rtl)` into a long `:lang()` list (its own CSS
minifier, not Tailwind's doing — confirmed by rebuilding and grepping
the output). Every RTL mirror rule across the eleven stylesheets that
had one now selects `:is([dir='rtl'], [dir='rtl'] *)` instead, matching
`:dir(rtl)`'s own specificity (one pseudo-class) and its inherited
"this element or a descendant of one carrying the attribute" reach; the
built CSS now keeps the selector as written (no `:lang(` or `:dir(` left).

RTL browser tests drop the `lang="ar"` workaround the old rewrite
needed (ContainmentTest's side-sheet probe, LayoutTest's shared
layoutPage() helper) and set only dir="rtl"; CarouselTest's probe
already did. Feature tests asserting the selector's literal text
(Progress, Overlay, Menu, ListDetail, Icon, Carousel) updated to match.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 06:54:16 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 f21943746e Delete Tailwind's half and build the Workbench without it
Plan step 39 (parts 1-3, folded into one commit: PHP tests read the
deleted files directly, so they cannot land apart from it). Tailwind
leaves the whole stack:

- Delete resources/css/tailwind.css, tokens/theme.css and
  tokens/utilities.css. Neither token file declared an --md-sys-*
  custom property of its own (both only referenced tokens declared
  elsewhere), so nothing loses a value; the md-* interaction and text
  classes already mirror utilities.css's declarations exactly
  (foundation/interaction.css, text.css).
- npm uninstall tailwindcss @tailwindcss/vite; vite.config.js drops the
  plugin and its import; composer.json drops the tailwindcss keyword
  (no lock change — keywords are outside Composer's content hash).
- The Workbench now builds one CSS entry, workbench/resources/css/app.css
  (all.css, showcase.css and the scheme; package.css is folded in and
  removed) instead of two, used by ErrorPage::assets() and every
  browser-test probe page's raw @vite() call; the showcase's own pages
  still take their CSS from the bundle route.
- TokensTest and StylesheetsTest: the two facts theme.css and
  utilities.css carried (every scheme role becomes a colour, resolved
  on the element; md-type-* matches the type-* utilities) are asserted
  directly against the scheme and text.css now that there is no second
  copy to cross-check; StylesheetsTest gained a full-tree scan (every
  .css file under resources/css/ is reached from all.css or
  showcase.css, no exclusions left for Tailwind); the Workbench-entry
  test and every "moved out of tailwind.css" assertion updated for the
  single entry and its removal.
- DesignGuard.php's comments and the development skill's setup section
  no longer name the deleted files or a second Tailwind entry.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 06:49:03 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 cfc57a9867 Land a caller's class and style on the scaffold and tooltip roots
Plan step 39 (part 0): the step 38 review found <x-scaffold> and
<x-tooltip> never referencing $attributes, so a caller's class and
style were silently dropped. The scaffold's root is its single
data-md-scaffold div; the tooltip's root, documented in its header, is
the standalone wrapper it draws around a trigger (data-md-tooltip-anchor,
merged with its own anchor-name) — passed an anchor instead, it renders
only a popover fragment beside another component's root and takes
nothing of the caller's.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 06:37:20 +02:00
Andreas Reinhold / reiniandClaude Opus 5 783f7902cc Name each canonical layout page in its window title
Plan step 38 review: ShowcaseController::layout() passes a title
("List-detail · Layout"), but the frame overwrote it with the section's
own, so all three pages and the Layout overview shared one title. The
frame keeps a title it was given; the layout page test asserts each.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 06:28:31 +02:00
Andreas Reinhold / reiniandClaude Opus 5 13b975358f Teach the ink classes and lines on the Colour page again
Plan step 38 review: the Colour section dropped its "Ink and lines"
group with the retired Tailwind ink names and put nothing in its
place, so no page showed the md-ink-* classes that replaced them or
how to draw a line. A new "Ink and lines" example writes md-ink,
md-ink-variant and md-ink-quiet with their roles, the state inks, and
a line as <x-divider> and an edge as <x-surface outlined>.

Doc comments teach too: the Icons intro showed optical="20" alone for
a 20px icon, where <x-icon size="20"> picks the cut itself; icon's
header still spoke of a caller's size-5 class, the navigation rail's
usage wrapped it in flex/min-w-0 utilities, and the menu item's
icon-class example named a Tailwind-style text- class.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 06:22:26 +02:00
Andreas Reinhold / reiniandClaude Opus 5 ebdcdb82b5 Write the showcase examples without showcase-only hooks
Plan step 38 review: the component sections' code samples, which an
application copies, carried 60 data-md-showcase-* hooks that only
showcase.css draws (a sized loading indicator, the plain badge's own
colours, a bordered bar frame, the navigation demos' frames, a table's
scroll wrapper...), so a copied sample did nothing in the application.
They now write what an application has: <x-surface outlined corner>
for a frame and a line, <x-row> for the FAB menu's corner, and an
inline style from the tokens for a size, a scroll box or a colour,
as the Containment batch already did. The side sheet demo takes
align="stretch", which holds in both of <x-row stack-below>'s modes,
instead of a hook that undid align="start" once stacked. The menu
icon colours go through icon-class with two unlayered application-like
classes, which the sample's comment names, and the fields' and date
picker's widths are inline styles now that style reaches their roots.
The radio hint said "from sm"; it is medium.

showcase.css keeps only the frame and the foundation specimens; its
header describes the file instead of the batches that wrote it. The
tests reject a showcase hook or a class outside md-* and the named
application classes in any example, a showcase.css selector outside
its own hooks, and a hook no showcase view renders.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 06:21:18 +02:00
Andreas Reinhold / reiniandClaude Opus 5 cd47aa4b0f Land a caller's style with its class on field and picker roots
Plan step 38 review: the showcase kept two unlayered width classes
because <x-input> and <x-datepicker> "don't forward style usefully".
They did not: the Phase F rule is that a caller's class and style land
on the component root, and the field family sent class to the field's
root but style to the inner control, where a width sizes nothing.
Input, password, textarea, select and file now pass style to the field
root with class; datepicker and timepicker put it on their root;
<x-group> dropped style entirely and <x-split-button> gave it to the
leading button; <x-search> rendered it twice, on the root and the
input. A textarea given a style also lost its autogrow rows, since
its own style attribute came second and the browser ignores it.
A dataset test renders each component with both and requires them on
the root, once.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 06:14:51 +02:00
Andreas Reinhold / reiniandClaude Opus 5 3fb0a404db Write the showcase examples' layout tags as an application does
Plan step 38 review: nine sections' examples wrapped their components
in <x-livewire-material::stack>, ::row and ::grid (148 tags) beside an
unprefixed <x-button>, while bars, containment and the layout pages
wrote <x-stack>. The code sample is what an application copies, and
the example component rewrites only unprefixed tags to a configured
prefix, so the namespaced ones taught a second form and escaped it.
Every example now writes <x-stack>, <x-row> and <x-grid>; a test
rejects a namespaced tag inside an example heredoc.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 06:11:08 +02:00
Andreas Reinhold / reiniandClaude Opus 5 79ca9a28b4 Stop drawing each section's title twice on its page
Plan step 38 review: the frame's structural rule that kept a section's
own <h2> for screen readers (Tailwind's [&>section>h2]:sr-only before,
a showcase.css rule during the rewrite) was removed as dead weight
once every section had a real heading, so each page drew its title
twice, the <h1> and an identical headline under it. The <h2>s are
md-visually-hidden again, the frame's comment says why, and the
section page test asserts it. The Layout section's description said
"window size classes"; M3's term is breakpoints.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 06:10:01 +02:00
Andreas Reinhold / reiniandClaude Opus 5 2f649983fa Keep the application's stylesheets off the scaffold page too
Plan step 38 review: /material/shell still passed the whole configured
Vite list, so it loaded Tailwind and all.css a second time beside the
showcase bundle. Both frames now take ShowcaseAssetController::scripts():
every entry but a stylesheet, by the same extensions Laravel's Vite
treats as CSS (not only .css), and skip @vite() when nothing is left,
since a list of stylesheets alone would read a build manifest for
nothing. A test renders the overview, a section, a layout page and the
shell against a hot file. The frame's header said the rail collapses
from `lg`; it is `expanded` (840px). The config comment is shorter and
true.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 06:07:44 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 067626c52e Pare down the showcase's unlayered sizing classes
Plan step 38 (last batch): only two of the previous batch's three
call sites actually need a plain unlayered class for their demo width
— <x-input> forwards `style` to the raw <input>, not the field wrapper
that is sized, and <x-datepicker> forwards neither, only `class`,
`wire:key` and `x-model` — so .showcase-w-sm and .showcase-w-xs stay.
<x-slider> does forward `style` to its root, so its vertical demo's
shared height is one now instead of the .showcase-slider-vertical
class. .showcase-w-narrow stays a class on purpose rather than an
inline style: fields.blade.php's "Narrower" input demonstrates an
application's own unlayered rule beating the package's layered
default, and an inline style would win regardless of layers, proving
nothing. The header explains all three.

StylesheetsTest gains showcase.css's own shape check — the same rule
every package stylesheet passes (header, layer statement, plain
imports, no Tailwind directive, breakpoints only at M3's four) — with
its three documented unlayered exceptions as the one allowed gap.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 05:34:06 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 42009f3b0c Stop loading Tailwind on the showcase page
Plan step 38 (last batch): every showcase view is now rewritten
without Tailwind, so the showcase's layout keeps only the JavaScript
entries of the application's configured Vite list (Livewire, Alpine)
before its own bundle link, instead of the whole array — its own CSS
already comes solely from ShowcaseAssetController's bundle
(Stylesheets::bundle() of all.css, showcase.css and the scheme), and
linking the application's build too meant the package's own CSS
(all.css) loaded twice on every showcase page (once through the
Workbench's package.css entry, once through the showcase's own
bundle), besides pulling in Tailwind's CSS for nothing. ErrorPage::
assets() keeps passing the whole configured list: the error pages have
no bundle of their own to fall back on, and step 40 is where their
fallback is redesigned. The config's own comment says so.

The frame's temporary visually-hidden <h2> rule goes too — it existed
only because the first batch could not reach into the still-Tailwind
sections/*.blade.php partials it @include's; every section now
carries its own real `class="md-type-headline-md"` heading, so the
structural rule that quietly hid all of them is dead weight.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 05:33:33 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 ab2fcdb0d3 Rewrite the showcase's Pages section without Tailwind
Plan step 38 (last batch): the section wraps in <x-stack as="section">
and the error-page button row in <x-row>, text on the md-* classes;
needs no showcase.css hook of its own. The error-page and mail links,
every route and every visible label stay as they were.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 05:33:04 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 a62800e8ff Rewrite the showcase's Navigation section without Tailwind
Plan step 38 (last batch): the navigation-bar and navigation-rail
demos' bounded frames become data-md-showcase-nav-demo (`bar`,
`bar-wide` inside its own data-md-showcase-nav-scroll wrapper, `rail`,
`rail-wide`), the "the page" filler is data-md-showcase-nav-page and
the content pane beside a rail data-md-showcase-nav-content; a group
of demos moves onto <x-row>/<x-stack> with inline max-width styles for
the two arbitrary demo widths (400px, 800px) Tailwind's arbitrary
values gave them. The "Open the scaffold" link's md-link/md-ink-primary
classes match bars.blade.php's.

Every #navigation id, aria-label and visible label the Chromium
NavigationTest (against its own probes and /material/shell) relies on
is unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 05:32:58 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 7b7e7279f5 Rewrite the showcase's App bars and tabs section without Tailwind
Plan step 38 (last batch): the app-bar/toolbar row becomes <x-stack>/
<x-row>, each bordered demo box a data-md-showcase-bar-frame hook
(`visible` for the search bar, whose docked suggestions must not clip;
`rounded` for the large-screen docked toolbar, which only gains its
edge and padding from `expanded`, 840px), and the search bar's own
avatar reuses the existing data-md-showcase-avatar hook. The
scrollable-tabs demo's forced-narrow box is
data-md-showcase-tabs-narrow, and the "Open the scaffold" link uses
the shared md-link/md-ink-primary classes in place of the old
Tailwind-era link/text-primary utilities.

Every #bars id and visible label/tooltip the Chromium BarsTest
(against its own probes) and the search index rely on is unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 05:32:31 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 295673554e Rewrite the showcase's Carousel section without Tailwind
Plan step 38 (last batch): every item's colour role pair moves from a
Tailwind bg-*/text-*/bg-linear-to-br utility string to a plain
`background-color`/`color`/`background: linear-gradient(...)` inline
style built from --md-sys-color-* tokens (the same "caller's own
style" mechanism as the foundation pages' colour swatch), and each
<x-shape> takes its px size as a prop instead of a size-* class.
data-md-showcase-carousel-swatch only sizes and centres the shape
inside the item.

Every #carousel id and data-md-carousel-item/multi-aspect selector the
Chromium CarouselTest reads is unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 05:32:15 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 c53c94174d Rewrite the showcase's Containment section without Tailwind
Plan step 38 (last batch): every example moves onto the layout
components, the md-* text classes and tokens. Wrapper divs that only
sized or grouped a demo (grid/list widths, the divider-and-collapse
stack, the dragged-card scope) become <x-grid>/<x-stack>/<x-row> with
an inline style where a specific px width still matters; a video
thumbnail's Tailwind gradient utility becomes an inline
background/color pair since each list-item's own colours differ. The
standard side sheet demo keeps M3's row-then-column breakpoint through
<x-row stack-below="expanded">, with a data-md-showcase-side-sheet-demo
hook that restores the row's own stretch alignment once it collapses
to a column (an explicit align="start", needed for the row layout,
would otherwise leave it shrink-wrapped).

Every id, data-md-* selector and visible string the Chromium
ContainmentTest reads (#containment, its dialog/sheet/card queries) is
unchanged; no demo content removed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 05:32:02 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 9e661836d6 Rewrite the showcase's Data section without Tailwind
Plan step 38: text.css already has the three classes this section
needed (md-text-end, md-tabular, md-visually-hidden), so only the
table's own scroll wrapper and the pagination list move — the wrapper
to a data-md-showcase-table-scroll hook, the two paginators onto
<x-stack>.

Every #data id and data-md-list-row/aria-selected selector the
Chromium DataTest reads is unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 04:57:09 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 8bef785f51 Rewrite the showcase's Sliders section without Tailwind
Plan step 38: single-column groups move onto <x-stack>, per-breakpoint
ones onto <x-grid>, and the "Vertical" row's shared height (M3's
second orientation needs one on the wrapper, which the label and hint
share) moves to a showcase-slider-vertical class — <x-slider> forwards
only class, style and wire:key to that wrapper, so a data attribute
would have been dropped silently.

Every #sliders id and data-bound span the Chromium SliderTest reads is
unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 04:56:21 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 58a5dca8d5 Rewrite the showcase's Time pickers section without Tailwind
Plan step 38: both examples move onto <x-grid> with <x-stack>
columns, x-data landing on the bound example's own stack.

TimepickerTest's Chromium selectors run against an isolated timeProbe
fixture, not this page, so nothing there is at risk.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 04:55:21 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 6f7dccc5de Rewrite the showcase's Date pickers section without Tailwind
Plan step 38: every two-column layout moves onto <x-grid> with
<x-stack> columns, x-data landing on the grid itself; the small demo
picker reuses the showcase-w-sm class the Text fields section already
defines.

DatepickerTest's Chromium selectors run against isolated probe
fixtures, not this page, so nothing there is at risk.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 04:54:04 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 0eb0343008 Rewrite the showcase's Text fields and selection section without Tailwind
Plan step 38: every two-column layout moves onto <x-grid> with
<x-stack> columns; the checkbox group's card, its legend and the two
search demos' bespoke geometry (an avatar chip, the compact
search-icon-button bar) move to data-md-showcase-* hooks. The two
small demo controls and the "Narrower" field's override move to
.showcase-w-sm/-xs/-narrow — plain, unlayered classes, since
<x-input> and <x-datepicker> forward only class and style to the
element that needs sizing, never a data attribute. "Narrower"'s hint
is reworded from "A max-w-* class" to "An unlayered rule from the
call site wins", since that is now the mechanism being shown.

FieldsTest's Chromium selectors run against an isolated fieldProbe
fixture, not this page, so nothing there is at risk; #fields and every
id (showcase-error, showcase-error-filled, showcase-audience,
showcase-radio-theme) is unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 04:53:58 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 dfb5997474 Rewrite the showcase's Chips section without Tailwind
Plan step 38: the Alpine-scope wrapper divs become <x-row>/<x-stack>
carrying x-data directly, giving Filter and Input chips M3's
full-width stretch in place of w-full/space-y-6. The narrow demo that
forces its chip set to scroll moves to a data-md-showcase-chip-scroll
hook (384px, Tailwind's old max-w-sm).

Every id, value and derived aria-label the Chromium ChipsTest
selectors read (#chips, the filter checkboxes' values, "Remove
<email>") is unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 04:50:29 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 e1cdc52b48 Rewrite the showcase's Progress section without Tailwind
Plan step 38: the "bind" example's wrapper div becomes <x-stack>/
<x-row>, and the one enlarged circular indicator moves to a
data-md-showcase-progress-lg hook. Along the way, $examples changes
from a ['Title' => [code, stack]] tuple back to the plain
'Title' => <<<'BLADE' every other section uses, with the row
exceptions listed separately: the tuple's leading `[` broke
Sections::index()'s regex, so every one of this section's examples
was silently missing from the showcase search index. Fixing the shape
recovers them — the Feature ShowcaseTest's search-index assertion
count moves from 259 to 266.

Every aria-label the Chromium ProgressTest reads off #progress
(Linear, Circular, Circular wavy, Bound, the indeterminate variants)
is unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 04:49:30 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 c87fc8ca41 Rewrite the showcase's Communication section without Tailwind
Plan step 38: the badge/icon wrapper spans move to
style="position: relative; display: inline-flex", the exact pattern
<x-badge>'s own header documents; Alerts, Stats and the two empty
states move onto <x-stack>/<x-grid> in place of w-full and a
Tailwind grid, so a caller no longer sizes each child by hand. The
"plain" badge (no colour of its own, by design) and the illustration's
two decorative fills move to data-md-showcase-* hooks, as does the
tooltip demo span's focus-ring corner; the shortcut kbds now use the
data-md-showcase-kbd hook the frame batch already drew.

No demo content removed; #communication, the toast/snackbar hooks and
the rich-tooltip triggers (Press for details, About expiry) the
Chromium CommunicationTest selectors read are unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 04:47:59 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 4d1481c832 Rewrite the showcase's Menus section without Tailwind
Plan step 38: text moves onto the md-* classes; the "Icons in their
own colour" example's two roles outside the fixed md-ink-* set
(tertiary, secondary) move to a data-md-showcase-icon-color hook in
showcase.css targeting the item's [data-md-icon] child, since
icon-class only reaches roles the fixed set already names. Swimming's
info icon keeps icon-class, now icon-class="md-ink-info".

Every id, aria-label and role selector the Chromium ActionsTest
selectors read against /material/menus (#menus, the More/Sort/Share/
Assign to/Photo triggers, the submenu and sheet-at-compact behaviour)
is unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 04:46:00 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 9aab3fbe67 Rewrite the showcase's Buttons section without Tailwind
Plan step 38: every example moves onto the layout components and the
md-* text classes. Wrapper divs that existed only to give an Alpine
scope to a button group or a connected <x-group> ("A connected group
that owns its selection", "Square groups", "A choice as a connected
group") become <x-stack>/<x-grid> carrying the x-data directly, which
also gives them M3's full-width stretch in place of w-full. The
loading indicator's two sized, tertiary-coloured variants and the FAB
menu's bounded demo box move to data-md-showcase-* hooks in
showcase.css; hint-class="text-warning" becomes hint-class="md-ink-warning",
the exact example the <x-group> header itself documents.

No demo content removed; every id, aria-label and input[name] the
Chromium ActionsTest selectors read (#buttons, showcase-theme,
showcase-days, the "Tonal" tooltip, the progressbar, collapse-on-scroll,
data-md-selection) is unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 04:45:38 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 487176d345 Give each canonical layout a page of its own in the Layout section
Plan step 38: the Layout section's Overview keeps its breakpoint
table and live "This window" readout (now a shared
<x-showcase::breakpoint-readout> partial, and the five breakpoint
cards a real range-queried data-md-showcase-breakpoint-card instead
of Tailwind's max-medium:/medium:max-expanded: variants), but the
three canonical layouts move off it onto a page of their own:
/material/layout/{list-detail,supporting-pane,feed}
(ShowcaseController::layout(), routes/showcase.php), each built on
the real component, sharing one <x-section-nav> Sections::
layoutPages() feeds. The search index gains an entry per page with
its own URL, and the component-homes scan now also reads the three
pages' own source so <x-list-detail>, <x-supporting-pane> and
<x-feed> keep a home.

The whole section moves onto the layout components and md-* classes:
no bespoke Tailwind grid or table styling remains.

LayoutTest's browser test now visits the three dedicated pages
instead of one shared /material/layout; a new Feature test asserts
each page renders and is linked from the Layout section.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 04:22:38 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 44a29a8ea2 Rewrite the showcase's Icons section without Tailwind
Plan step 38: the filter row is now real components — <x-input
icon="search"> and two <x-checkbox>s — instead of a bespoke bordered
label and accent-coloured native inputs; the result grid is
<x-grid min-item="120px"> of <x-surface as="li">/<x-stack> tiles.
The fetched symbol's mask stays the view's own x-bind:style (its
shorthand depends on the fetched name), but the size it toggles
between 24 and 20px is now the data-md-showcase-icon-size attribute
a script sets, never a class, drawn by data-md-showcase-icon-mask in
showcase.css.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 04:18:37 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 3669415d57 Rewrite the showcase's Motion section without Tailwind
Plan step 38: the two demo buttons are now real <x-button>s (filled
for Move, text for Change the figure) instead of a hand-rolled state-
layer/focus-ring button; each spring's row and its travelling dot
move onto <x-surface>/<x-stack> plus data-md-showcase-motion-row/
-track/-dot in showcase.css — only the dot's size and colour are
fixed there, since the transition under test stays the spring token
in the view's own inline style.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 04:17:45 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 a4b368386d Rewrite the showcase's Elevation section without Tailwind
Plan step 38: the shadow-level grid moves onto <x-surface> and
<x-grid>; each tile is a data-md-showcase-elevation-tile keyed on
data-md-elevation, drawing var(--md-sys-elevation-1..5) — the same
tokens the label now names instead of the retired shadow-elevation-*
utility.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 04:17:02 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 3cc0025b33 Rewrite the showcase's Shape section without Tailwind
Plan step 38: the corner grid and the shape gallery move onto
<x-grid> and <x-stack>; each corner swatch is a data-md-showcase-
shape-swatch keyed on data-md-corner, one showcase.css rule per
corner token; <x-shape :size="80"> replaces the size-20 utility, its
fill the caller's style="color: var(--md-sys-color-secondary-
container)" since a shape's colour is inherited text colour, not a
class.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 04:16:49 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 9ad9d3ec13 Rewrite the showcase's Type section without Tailwind
Plan step 38: the typescale listing moves onto <x-surface> for its
container and a bespoke data-md-showcase-type-row (a fixed label
column beside two flexible samples from 840px, one column below it,
a rule between rows) since no layout component draws an asymmetric
grid track; the sample text itself is styled by the very md-type-*
class names the row's own label prints, so the code sample and the
rendered specimen are the same string.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 04:16:33 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 365df791c1 Rewrite the showcase's Colour section without Tailwind
Plan step 38: swatches move from Tailwind's bg-* role utilities to
data-md-showcase-swatch spans whose fill is the caller's own inline
style="background: var(--md-sys-color-{role})" (a swatch's colour
depends on the role it shows, so that one declaration cannot be a
class), sized and edged in showcase.css; the two grids and every
per-theme panel move onto <x-grid>, <x-surface> and <x-stack>; text
onto the md-type-*/md-ink-* classes. The legacy "Ink and lines" group
(bg-body, bg-meta, bg-quiet, bg-structure, bg-chrome, bg-divider) is
dropped — those Tailwind ink/line names end with Phase F and their
roles already show under Surface.

ThemeTest and ColourProfilesTest read the swatch's computed colour
through the new data-md-showcase-swatch="primary" hook instead of the
retired .bg-primary class.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 04:14:22 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 9c8b39600b Rewrite the showcase frame without Tailwind
Plan step 38 (first batch): layout.blade.php, index.blade.php,
section.blade.php, shell.blade.php and the example component now
lay out through the layout components (pane, stack, row, grid,
surface) and their props, text through the md-* classes, and
everything else through showcase.css's data-md-showcase-* hooks — no
Tailwind utility remains in any of them. The head links the new
stylesheet route after @vite(...), not before: that entry opens the
document's first @layer statement, and linking the showcase's own
bundle ahead of it re-anchors the material layer before Tailwind's
own and lets its preflight's `padding: 0` beat the search view's
padding-top (found by a failing browser test). @vite(...) stays, its
Tailwind CSS entry included, only for the section views (still
Tailwind until the next batches) and the JS runtime; the config
comment says so.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 03:55:25 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 62b09c45b5 Add the showcase's own chrome stylesheet
Plan step 38: resources/css/showcase.css, in material.components
beside components/error-page.css for the same reason (a whole page's
chrome, not a reusable component, still outranked by an application's
unlayered CSS by design). Keyed on data-md-showcase-* hooks; batch 1
covers only what the frame (layout, index, section, shell, the example
component) needs beyond the layout components and the text classes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 03:55:11 +02:00
Andreas Reinhold / reiniandClaude Opus 5 3365730610 Import the components in the documented installation
Plan step 37 review. Removing material.css left README.md, the
development skill, UPGRADE.md and the tailwind.css and foundation.css
headers telling an application to import foundation.css, tailwindcss and
tailwind.css in its place. material.css was also what brought layout.css
and components.css, so that install rendered every component unstyled,
and without a layer statement Tailwind's preflight would have outranked
the package's layers.

The skill's snippet is now the Tailwind-free one 2.0.0 ends with: the
foundation, then the stylesheet of each component the views render, or
all.css. README.md, whose layout and prose still assume Tailwind, shows
all.css in its own entry and Tailwind in a second, both opening with the
layer statement, the shape the Workbench now builds; the skill and
UPGRADE.md describe that interim in a sentence.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 03:15:05 +02:00
Andreas Reinhold / reiniandClaude Opus 5 eda16ba727 Make the rail header FAB's collapsed shape win by specificity
Plan step 37 review. The collapsed rail FAB's gap and minimum width tied
with fab.css's extended-size and collapse-on-scroll rules at three
attributes and won only by coming after them. A deduplicating bundle
keeps that order (navigation-rail.css imports fab.css), but a build that
repeats a shared stylesheet need not: all.css imports fab.css again after
the scaffold has reached the rail, and the Workbench's Tailwind entry
inlines every occurrence. Doubling [data-md-extended] settles it by
specificity, as the flat shadow already was, so the order no longer
matters.

A scan of every component and layout stylesheet pair for same-layer,
same-property ties on a shared hook found no other override that relies
on order: the remaining ties are on mutually exclusive elements or sit
behind an import edge.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 03:08:39 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 a967cfcc2b Remove material.css, the 1.x single-import shortcut
Plan step 37: an application on 1.x-style imports now imports
foundation.css before @import 'tailwindcss' and tailwind.css after it,
the way foundation.css's own header has described since Phase F
started — material.css only ever saved writing those two lines.
Updates every place that named it: tailwind.css's and
foundation/hidden.css's and tokens/utilities.css's header comments,
the development skill's install snippet and Tokens section, README.md's
install snippet, and a new line in UPGRADE.md's migration notes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 02:49:43 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 925d9a4439 Fold layout.css and components.css into all.css
Plan step 37: all.css replaces the two interim import lists with one
entry for an application that wants everything — the foundation, every
layout stylesheet and every component stylesheet, grouped under the
same block comments components.css used, plus a Layout block. It also
directly imports the three files nothing imported by name before
(layout/spacing.css, layout/visibility.css, components/selection.css),
so every file under components/ and layout/ is now one @import away.

Every test that read a block of components.css or layout.css now reads
the matching block of all.css through one shared helper (allCssBlock(),
in tests/Pest.php so it loads for any test run) instead of repeating
the same substr() search in each file. StylesheetsTest.php's shape,
Tailwind-free and breakpoint checks, previously run twice (once for
the foundation, once for the layout tree), now run once over the whole
tree all.css reaches, since every component and layout stylesheet is
plain CSS after step 36; a new test asserts all.css imports everything
under components/ and layout/ exactly once. The Workbench imports
all.css in place of layout.css and components.css.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 02:49:29 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 3f41bffbdc Rename <x-choices>'s data-active hook to data-md-active
Review leftover (plan step 36): the searchable list's highlighted-row
hook was still the plain pre-Phase-F name, in both the view's binding
and its scroll-into-view lookup and the shared rule in menu.css that
paints it. No test named it directly, so none needed a change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 01:57:39 +02:00
Andreas Reinhold / reiniandClaude Sonnet 5 bd2bb07106 Close a menu whose trigger a breakpoint hides instead of leaving it open
An app bar's overflow menu is two separate <x-menu> instances trading
places as the width crosses 600px, each display: none where the other
shows. Resizing away from a menu's trigger left its popover in an open
state forever, anchored to a wrapper nobody could see or reach anymore.
A ResizeObserver on the trigger now closes the menu once it has no box
left to render (plan step 36, navigation group).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 01:37:03 +02:00
Andreas Reinhold / reiniandClaude Opus 5 26c338be25 Say how the rail's FAB overrides win, and that rail-collapsed: is gone
Plan step 36, navigation review. navigation-rail.css claimed each FAB
override beats fab.css by specificity alone; the collapsed FAB's gap
and minimum width tie with fab.css's extended-size rules and win by
order, which the file's own import of fab.css guarantees. The rail
view pointed at one file for the collapsed conditions that four files
write out. UPGRADE.md now says applications lose the `rail-collapsed:`
variant the development skill used to teach.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 01:14:42 +02:00
Andreas Reinhold / reiniandClaude Opus 5 27028bfb7a Import the button stylesheet from list-detail.css for its back button
Plan step 36, navigation review sweep. <x-list-detail> renders an
<x-button> as the single-pane back action, but only pane.css picked up
button.css when the carry-over landed; an application importing
layout/list-detail.css alone got an unstyled back button. A new check
reads every layout view's component tags against its stylesheet's
imports, as the group stylesheet tests do for components.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 01:14:07 +02:00
Andreas Reinhold / reiniandClaude Opus 5 cbf6c36191 Match only the scaffold's own main, and correct its header
Plan step 36, navigation review. `[data-md-scaffold] main` carried only
step 35's `--md-layout-margin`; the rewrite added the region's flex,
padding and overflow clip to it, which would also land on a <main> an
application nests in the page. It is `[data-md-scaffold-content] > main`
now. The header said panes read `--material-margin` (only the region
does), that the bar wrapper carries `--material-bottom-bar` (the root
does), and that the old utility and the hidden-bar override both sit in
material.components (the scaffold's is material.layout).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 01:13:29 +02:00
Andreas Reinhold / reiniandClaude Opus 5 52beb676bc Leave a disabled icon button's colours alone inside a toolbar
Plan step 36, navigation review. The toolbar's recolouring rules
(primary in a standard toolbar, on-primary-container and the pressed
fill in a vibrant one) outrank button.css's disabled rule by
specificity, so a disabled icon button in a toolbar drew in full
primary. They now skip `:disabled` and `aria-disabled`, and the header
no longer claims each override only meets a button's base selector.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 01:10:29 +02:00
Andreas Reinhold / reiniandClaude Opus 5 ed92e916b9 Lay out the section nav's list items as flex rows again
Plan step 36, navigation review. The rewrite kept `min-w-0` from the
view's `<li class="flex min-w-0">` but dropped `flex`, so tabs.css's
`li > [data-md-tab] { flex: 1 1 0% }` no longer applied inside the
section nav. The header also called body-large "the tab bar's own"
type, which is title-small.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 01:09:55 +02:00
Andreas Reinhold / reiniandClaude Opus 5 348803484a Keep a deferred wire:model on the tabs deferred
Plan step 36, navigation review. The tabs rewrite appended `.live` to
@entangle, so every tab set bound with a plain wire:model sent a request
on each click. @entangle already adds `.live` for wire:model.live, which
the view's own header promises is the caller's choice. Pinned for both
spellings.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 01:09:13 +02:00