# Livewire Material: a shared Material 3 Expressive component library > Split on 2026-09-13 from SealShare's `docs/plans/livewire-material.md`, where the plan was > made. SealShare's adoption (Phase 11) stays in SealShare's copy. ## Goal ReStride has just left maryUI and daisyUI for **Material 3 Expressive on its own Blade components** (`ReStride/docs/plans/material-expressive.md`, done 2026-09-10). Doing that again, by hand, in every Laravel + Livewire app is what this package avoids. **`nonameweb/livewire-material`** carries the whole current M3 Expressive component catalogue as Blade components for Livewire, together with everything around them — colour scheme generation, tokens, the theme script, Material Symbols, Google Sans Flex, motion, error pages, a mail theme, a showcase, test helpers and AI guidelines. SealShare converts to it once it is complete (`1.0.0`); ReStride adopts it later, in a plan of its own. ## Context **Stacks.** SealShare: Laravel 13.31, Livewire 4.4, maryUI 2.9.10 (no prefix), daisyUI 5.7, Tailwind 4.3, Pest 5.1, Octane on FrankenPHP, PHP 8.5; public on GitHub under MIT, image published to `ghcr.io/surtic86/sealshare`. ReStride: same Laravel / Livewire / Tailwind / Pest, private on `gitea.nonameweb.ch`, CI through Gitea act_runner. **What ReStride already solved** (and the package generalises): - 45 anonymous components in `resources/views/components/`, maryUI's names and props, M3 styling; class components only where PHP earns it. - `resources/css/material/{color,shape,type,motion,elevation,field,menu,table,list}.css`; `@theme` (never `inline`) mapping M3 roles to utilities; `--color-*: initial` so only tokens compile; `:root` dark + `[data-theme=light]`, each with its own `color-scheme`. - Material Symbols Rounded (400/0/24) as local SVGs registered as `blade-icons` sets (`ms`/`msf`), `config/blade-icons.php` turning off blade-icons' own ``. - Google Sans Flex, a 63 KB Latin subset (weight 400–700, ROND 0–100). - Spring motion as CSS `linear()` curves; press shape-morph; Expressive shapes generated by formula into `resources/svg/shapes`. - `App\Livewire\Concerns\Toasts` (protected `success|error|warning|info`, dispatching a window event) and a snackbar ``. - ~30 component tests (`$this->blade()`), browser tests (Pest browser plugin), and guard tests (`DesignLanguageTest`, `MaterialTokensTest`, `MaterialSymbolsTest`). - The Livewire traps, recorded in `../ReStride/.ai/rules/ui.md`: `wire:ignore.self` on a `showModal()` dialog; never pass `hidden` or a position to a component (wrap it); Blade directives do not compile inside a component tag's attributes; `wire('model')->value()` is `false`, not `null`, without `wire:model`; a field's states are CSS selectors (`:has()`), marked with `data-invalid` / `data-floated` / `data-readonly` where the control cannot carry them; the customizable select's traps (`../ReStride/.ai/rules/components-views-components.md`). - Mail on M3 (`../ReStride/docs/plans/material-mail.md`): one theme CSS inlined onto bare tags, light only, because `CssToInlineStyles` strips `@media`. **What in ReStride is app-specific** and does not move: the doctrine (two button weights, no tertiary / primary-container — enforced by its build), `restride-theme`, `$store.install` / `$store.connection` in the shell, `--bottom-bar` set by its layout, sport / zone / route tokens, `training-row`, `map-shell`, `map-chip`, `product-shot`, `star-rating`, `share-button`, the unDraw repaint, the Ace code editor. **Constraints found.** - `gitea.nonameweb.ch` requires sign-in to view anything (`/explore/repos` → login, API 403). A "public" repo there cannot be installed anonymously until that changes. *(Resolved in step 1.)* - Laravel's Markdown mail accepts a namespaced view as theme (`Illuminate/Mail/Markdown.php:114`), so a package can render the theme CSS from data. - Laravel **replaces** the `errors` view namespace at render time with `config('view.paths')` + `/errors` and the framework's own (`Illuminate/Foundation/Exceptions/RegisterErrorViewPaths.php`), so error views a package adds with `addNamespace('errors', …)` are wiped; only a path in `view.paths` survives. - M3 Expressive deprecates bottom app bar, navigation drawer, the original navigation bar, segmented button, small FAB and medium/large top app bar (material-components-android docs). - CSS anchor positioning is in Chrome 125+, Firefox 147+, Safari 18.4+ (flip via `@position-try` from Safari 18.4). - Boost loads a package's `resources/boost/guidelines/core.blade.php` and its skills on `boost:install` / `boost:update --discover`. ## Decisions - **A shared Composer package, SealShare its first consumer; ReStride adopts later in its own plan** — SealShare's small surface proves the API; ReStride's three-day-old migration is not put at risk. - **Public repo on `gitea.nonameweb.ch`, MIT** — SealShare is public; its CI, Docker build, manual install and fork PRs must install the package without credentials. SealShare requires it through a `vcs` repository entry. Material Symbols (Apache-2.0), Google Sans Flex (OFL) and material-web token values (Apache-2.0) are compatible; attributions ship in `NOTICE`. - **Prerequisite: Gitea allows anonymous reads** (`[service] REQUIRE_SIGNIN_VIEW = false`, or `expensive` on 1.23+) — otherwise "public" is not installable. - **Name `nonameweb/livewire-material`, namespace `NoNameWeb\LivewireMaterial`, `config/livewire-material.php`, views `livewire-material::`, commands `material:*`** — says what it is and what it is for. - **Components unprefixed by default, prefix configurable** (maryUI's model, via `Blade::anonymousComponentPath($path, $prefix)`) — ReStride's call sites keep their names; SealShare's maryUI tags keep theirs. The provider sets `blade-icons.components.default` to `null`, or blade-icons' class `` beats ours. - **CSS and JS imported from `vendor/`** (`@import`, `@source`, `import`) — one dependency, one version; consuming Dockerfiles install Composer packages before the Vite build. `@source` covers the package's views and PHP, never its SVG folders. - **The full Material Symbols Rounded set** (400 / 0 / 24, outlined and filled, 4,135 symbols, 5.3 MB), fetched by a maintenance script in the package repo, never at runtime — any name works in any app. **`` reads the SVG files itself; blade-icons is not used for them** (decided after Phase 1): blade-icons registers one Blade component per icon whenever the view factory resolves, ~8,000 registrations per request here. blade-icons stays a dev dependency, only to test that the provider still keeps its `` from shadowing ours in apps that have it. - **Google Sans Flex bundled** (ReStride's subset) via `@font-face` in the package CSS; an app overrides `--font-sans`. - **Full M3, not ReStride's doctrine** — every variant, colour role (tertiary and primary-container included) and container is available; each app enforces its own rules through a configurable guard helper the package ships. - **`variant` + `color` props** — `variant="filled|tonal|outlined|text|elevated"`, `color="primary|secondary|tertiary|error|success|warning|info"`, default `text` in primary; both validated against fixed lists, unknown values fall back to the default. Shorthands kept: `primary` = filled primary, `danger` = filled error, `caution` = filled warning. The same pattern for badge, alert, chip, icon button and FAB (`tone` stays an alias of `color` where ReStride used it). - **`php artisan material:scheme {seed} --variant=`** — runs Google's `material-color-utilities` from a single prebundled Node script inside the package; writes the app's `resources/css/material-scheme.css` (dark and light `--md-sys-color-*`, plus `success`/`warning`/`info` custom colours, harmonisation off) and `resources/css/material-scheme.json` (the light hexes, for the mail theme). Output is committed. - **Theme: a head script component, `light | dark | system`** — sets `data-theme` before paint; CSS keys only on the attribute and never asks `prefers-color-scheme`; only the script reads the OS (and follows its changes while `system`). `$store.theme` is the one state every toggle shares. Config: `theme.default`, `theme.storage_key`, `theme.legacy_keys` (adopted once, then removed). - **The whole M3 Expressive catalogue, current components only** — the six deprecated ones are skipped; their names alias where free (`` renders a connected button group, FAB `size="sm"` renders medium). - **Plus the non-M3 pieces apps need** — data table, sort header, pagination views, file input, password, stat, alert, empty state, collapse, section nav, account menu, theme toggle, an adaptive app-shell composition, error pages, mail theme. - **Modern browser floor, no third-party JS** — Chrome 125+, Firefox 147+, Safari 18.4+: native ``, Popover API, CSS anchor positioning, customizable `