Files
livewire-material/README.md
T
Andreas Reinhold / reiniandClaude Opus 5 c047fefd8b Remove the design guard's maryUI and daisyUI checks
Plan step 41 review (the user, 2026-09-15): SealShare and ReStride have
both left maryUI and daisyUI, and no application of 2.0.0 carries either,
so the <x-mary-*> tag check, the daisyUI class and colour lists and the
Tailwind look-alike exclusions they needed go, with their fixture lines
and the mentions in the header, README and development skill.

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

181 lines
12 KiB
Markdown

# Livewire Material
Material 3 Expressive components for Laravel and Livewire, in plain CSS.
- Anonymous Blade components for the current M3 Expressive catalogue: buttons and FABs, menus, chips, text fields, selection controls, sliders, pickers, dialogs and sheets, lists, cards, carousels, progress and loading indicators, snackbars, tabs, app bars, toolbars, navigation bars and rails, an adaptive scaffold, the layout components (panes, list-detail, supporting pane, feed), data tables and pagination.
- A colour scheme generated from one seed colour with Google's colour science (`php artisan material:scheme`), light and dark, and a theme that is chosen before the first paint.
- The full Material Symbols Rounded set and the M3 Expressive shapes, drawn inline without an icon package.
- Error pages and a Markdown mail theme in the same scheme.
- A showcase of every component in the application's own scheme, a design guard for tests, and for AI agents two Laravel Boost guidelines and two skills: the library's own, and Material 3's rules and tables beside its utilities.
No JavaScript libraries beyond the Alpine that ships with Livewire. Browsers: Chrome 125+, Firefox 147+, Safari 18.4+.
## Requirements
PHP 8.4+, Laravel 13, Livewire 4, Vite, and Node (for `material:scheme`).
## Installation
The package is served from Gitea. Add the repository and require it:
```bash
composer config repositories.livewire-material vcs https://gitea.nonameweb.ch/noNameWEB/livewire-material.git
composer require nonameweb/livewire-material
```
### Stylesheet and script
The application's build imports from `vendor/`, so Composer packages must be installed before `npm run build` — in a Dockerfile, copy `composer.json`, run `composer install`, then build the assets.
The package's stylesheets are plain CSS, no build step of its own and no Tailwind anywhere in the stack. `all.css` brings the foundation and every component; an application can instead import `foundation.css` first and then the stylesheet of each component its views render (`resources/css/components/button.css`, `resources/css/layout/scaffold.css`, …), since each imports the stylesheets of the components it draws and Vite keeps a file several of them import once.
```css
/* resources/css/app.css */
@layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility;
@import '../../vendor/nonameweb/livewire-material/resources/css/all.css';
@import './material-scheme.css';
```
```js
// resources/js/app.js
import '../../vendor/nonameweb/livewire-material/resources/js/material.js'
```
Do not install Alpine separately; Livewire provides it.
An application writes no utility layer of its own: layout components (`<x-scaffold>`, `<x-pane>`, `<x-stack>`, `<x-row>`, `<x-grid>`, the canonical layouts) take M3's spacing tokens and breakpoints as props, a small set of `md-type-*` and `md-ink-*` classes covers text on plain elements, and `--md-sys-*` custom properties serve the rest of an application's own stylesheet. Breakpoints are M3's window size classes and only those — compact below `medium` (600px), then `expanded` (840px), `large` (1200px) and `extra-large` (1600px) — written as literal range media queries (`@media (width >= 840px)`) in the package's CSS; a script asks `resources/js/breakpoints.js` (`from()` / `upTo()`) for the same numbers rather than writing its own query.
### Layout
The theme script goes in `<head>`, before `@vite`, so the page paints in the visitor's theme:
```blade
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<x-theme-script />
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body>
{{ $slot }}
<x-toast />
</body>
</html>
```
### Colour scheme
Generate the scheme from a seed colour. It writes `resources/css/material-scheme.css` (imported above) and `material-scheme.json` (read by the mail theme):
```bash
php artisan material:scheme "#4f46e5" --variant=tonal-spot
```
Variants: `tonal-spot`, `vibrant`, `expressive`, `neutral`, `fidelity`, `content`, `monochrome`, `rainbow`, `fruit-salad`. `--spec` is the colour spec: `2025` (M3 Expressive, the default) or `2021` (M3 as it first shipped, for a palette generated before Expressive). `--success`, `--warning` and `--info` seed the state colours, which are built exactly as M3 builds `error`; `--harmonize` pulls them towards the seed. The stylesheet's header records the command that regenerates it; regenerate instead of editing the file.
Every scheme is written at M3's three contrast levels — standard, medium (3:1) and high (7:1), for light and dark — keyed on `<html data-contrast>`, which the head script sets before the first paint from the visitor's choice or the operating system's. `--contrast` sets the standard level alone (below 0.5); `<x-theme-toggle mode="contrast" />` lets someone choose.
#### Colour profiles
To let an installation switch between several schemes, list them as `profiles` in the config (name ⇒ `label`, `seed`, `variant`, and optionally `contrast`, `harmonize`, `spec`, `success`, `warning`, `info`, which otherwise come from the command's options) and run `php artisan material:scheme` without a seed: every profile lands in the same stylesheet under `<html data-scheme>`. Tell the package which one is active — `Scheme::resolveProfileUsing(fn () => Setting::get('color_profile'))` in a service provider — and the head script, mails and error pages follow it. `<x-scheme-picker wire:model="colorProfile" />` lets someone choose, previewing each profile on the page.
### Configuration
```bash
php artisan vendor:publish --tag=livewire-material-config
```
- `prefix` — components are `<x-button>`, `<x-card>`… Set `'m'` when a name clashes with the application's own components, and they become `<x-m::button>`. `<x-livewire-material::button>` always works.
- `theme.default` (`light`, `dark` or `system`), `theme.storage_key`, `theme.legacy_keys` (an earlier toggle's localStorage keys, adopted once).
- `theme.contrast.default` (`system`, `standard`, `medium` or `high`) and `theme.contrast.storage_key` — M3's contrast level, resolved before the first paint into `<html data-contrast>` and followed on the operating system while `system`.
- `theme.meta` — keep `<meta name="theme-color">` (an installed web app's or a mobile browser's bar) on the resolved theme's `surface` and the active colour profile, before the first paint and after every change, `wire:navigate` included; one is added when the page has none (default `false`).
- `motion.scheme` — M3's motion scheme: `expressive` (default) or `standard`, the restrained springs, written to `<html data-motion>`.
- `profiles`, `profile` — colour profiles and the default one (see Colour profiles).
- `fields.variant` — text fields `outlined` (default) or `filled`.
- `pagination` — draw Laravel's and Livewire's paginators in M3 (default `true`).
- `showcase.enabled`, `showcase.path`, `showcase.middleware`, `showcase.vite`.
- `node` — the Node binary for `material:scheme`.
## Usage
```blade
<x-card title="holiday-photos.zip" subtitle="248 MB · expires in 3 days" variant="outlined">
<x-slot:actions>
<x-button label="Copy link" icon="content_copy" wire:click="copy" />
<x-button label="Delete" danger wire:click="$set('confirming', true)" />
</x-slot:actions>
</x-card>
<x-modal wire:model="confirming" title="Delete this share?" icon="delete">
Recipients lose access at once.
<x-slot:actions>
<x-button label="Cancel" x-on:click="close()" />
<x-button label="Delete" danger wire:click="delete" />
</x-slot:actions>
</x-modal>
```
```php
use NoNameWeb\LivewireMaterial\Concerns\Toasts;
class Shares extends Component
{
use Toasts;
public function copy(): void
{
$this->success('Link copied');
}
}
```
Every component, prop and slot is documented in the Boost skill (`resources/boost/skills/livewire-material-development/SKILL.md`) and shown in the showcase.
## Showcase
While the application runs locally (or with `MATERIAL_SHOWCASE=true`), `/material` shows every token and component, in every variant, in the application's own scheme and theme: an overview, and a page per section behind a navigation rail (the package's own scaffold), with a search over every section, example and component (press `/`).
## Testing the design
```php
use NoNameWeb\LivewireMaterial\Testing\DesignGuard;
it('uses only what compiles', function () {
expect(DesignGuard::scan([resource_path('views'), resource_path('js'), app_path()])
->missingStylesheets(resource_path('css/app.css'))
->forbidColours(['tertiary'])
->violations())->toBe([]);
});
```
The guard fails on Tailwind palette colours, unknown Material Symbol names and Blade directives written inside component tags. It also fails on every Tailwind utility or variant still sitting in a view, PHP or JS file — a breakpoint prefix, a radius, shadow, type size/weight/leading/tracking, easing or duration, a flex/grid/spacing/sizing/display utility, a text-layout utility, an M3 role utility (`text-on-surface-variant`, `bg-primary`), a pseudo-class variant, or an arbitrary `[…]` value — none of which compile any more, since the application carries no Tailwind: every line names its 2.0.0 replacement, a layout component and prop (`flex gap-4``<x-row gap="space200">`), an `md-*` class (`truncate``md-truncate`), or a token for the application's own CSS (`rounded-lg``var(--md-sys-shape-corner-lg)`). A class the application's own stylesheets declare is exempt, and so is every `md-*` class.
`missingStylesheets($cssEntry)` checks the CSS entry's `@import` graph (followed through every package file's own imports) against the package tags a view actually renders — unprefixed, under the configured prefix, or `<x-livewire-material::…>` — and `->links()`; each missing one names the exact `@import` line to add, and a tag the application shadows with its own component of the same name is reported instead. It also turns on a check of the application's own CSS (the entry and what it imports outside the package, plus any `.css` file among the scanned paths), which reports a literal colour, radius, shadow, font size, weight, line height, letter spacing, easing, duration or off-scale media query with its token or breakpoint — a value inside `var(--md-sys-…)` or `calc()` is always fine, and the generated `material-scheme.css` is skipped.
Two checks are opt-in, and matter mainly to an application still migrating off Tailwind: `forbidAbsolutes()` also fails on `bg-white` and `text-black` (M3's white is `surface-container-lowest`), and `forbidOpacityInk()` on opacity used as emphasis (`text-on-surface/60``text-on-surface-variant` or `text-outline`). M3 reserves 38 % on content and 12 % on a container for the disabled state, which is what the package's own components use them for, so neither is on by default.
## AI agents
With [Laravel Boost](https://github.com/laravel/boost), `php artisan boost:install` (or `boost:update --discover`) picks up the package's two guidelines — the library's own, and `material-3`, a page of M3's rules an agent reads in every session — and two skills: `livewire-material-development` (every component, prop, slot and trap) and `material-3-design` (M3's colour roles, surfaces, elevation, shape, type, motion, states, window size classes and accessibility, with the library's utility beside each M3 name and Google's source page for each chapter).
## Developing the package
```bash
composer install && npm install
npm run build # the Workbench's assets (or `npm run dev` while working)
composer serve # the showcase at http://127.0.0.1:8000/material
vendor/bin/pest --testsuite=Feature
npx playwright install
vendor/bin/pest --testsuite=Browser --browser chrome # also firefox, safari
```
## Credits
Material Symbols, the M3 Expressive shapes, Google Sans Flex, material-color-utilities and Jetpack Compose Material 3's tokens and algorithms are Google's and the Android Open Source Project's; see `NOTICE`.
## License
MIT. See `LICENSE`.