Andreas Reinhold / reiniandClaude Sonnet 5 ed93222d22 Draw the scaffold without Tailwind
Plan step 36 (navigation group, third batch): <x-scaffold>'s own
styling moves into resources/css/layout/scaffold.css (the content
region, the bar and rail row, the banner, the actions row and its
rail-collapsed column layout, --material-bottom-bar and
--material-margin publishing, the skip link) alongside step 35's FAB
and content-margin rules already there. Every data-app-shell* hook
becomes data-md-scaffold-* (data-app-shell-bar, -actions, -banner);
the skip link is data-md-skip-link; data-app-shell itself is dropped,
data-md-scaffold already named the root.

The actions row's rail-collapsed:flex-col is written out branch for
branch as the navigation rail's own rewrite did for its internal
parts: the three width-independent conditions in one :where() group,
the four width-gated ones each in their own @media block. With that
gone, resources/css/tailwind.css's rail-collapsed custom-variant
shim (its last use) is removed; tailwind.css now carries only
tokens/theme.css and tokens/utilities.css, which the showcase still
needs until step 38.

navigation-bar.css's hide-on-scroll rule reading --material-bottom-bar
stayed unlayered only because <x-scaffold> published that variable
through a Tailwind utility, which no layered rule could outrank; now
scaffold.css sets it itself in material.layout, a layer
navigation-bar.css's own material.components always beats, so the
rule moves into the layer and the file fits one
@layer material.components block like every other navigation
stylesheet. navigation-bar rejoins NavigationStylesheetsTest.php's
dataset and NavigationBarTest.php's own duplicate shape test is
retired in favour of it.

Browser tests added (docs/plans/material-3-browser-tests.md): the
scaffold's FAB dropping the bar's own height once hide-bar-on-scroll
slides it away, at the trailing edge in a right-to-left document, and
clearing a safe area an application sets on its inline-end and bottom
edges.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
2026-09-15 00:37:22 +02:00
2026-09-13 09:56:00 +02:00
2026-09-14 22:06:56 +02:00
2026-09-15 00:37:22 +02:00
2026-09-13 04:59:21 +02:00
2026-09-15 00:37:22 +02:00

Livewire Material

Material 3 Expressive components for Laravel and Livewire, built on Tailwind CSS 4.

  • 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, Tailwind CSS 4 with Vite, and Node (for material:scheme).

Installation

The package is served from Gitea. Add the repository and require it:

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.

/* resources/css/app.css */
@import 'tailwindcss';
@import '../../vendor/nonameweb/livewire-material/resources/css/material.css';
@import './material-scheme.css';

@source '../../vendor/nonameweb/livewire-material/resources/views';
@source '../../vendor/nonameweb/livewire-material/src';
// resources/js/app.js
import '../../vendor/nonameweb/livewire-material/resources/js/material.js'

Do not install Alpine separately; Livewire provides it.

Importing the package's CSS replaces parts of Tailwind's theme with M3's, so some default utilities no longer compile. Breakpoints are the most visible: they are M3's window size classes and only those — medium: 600px, expanded: 840px, large: 1200px and extra-large: 1600px, with max-medium: and friends for "below", compact being everything under medium. Tailwind's sm:2xl: are cleared, so an sm:grid-cols-2 left over from another project compiles to nothing; rewrite it as medium:grid-cols-2. The same goes for the default radius, shadow, text-size, weight, leading, tracking and easing scales, which the M3 sets (rounded-corner-*, shadow-elevation-*, type-*, ease-spatial-*) replace. Scripts that need a window size class import from() / upTo() from the package's resources/js/breakpoints.js rather than writing their own media query.

Layout

The theme script goes in <head>, before @vite, so the page paints in the visitor's theme:

<!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 class="bg-surface font-sans text-on-surface antialiased">
        {{ $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):

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

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

<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>
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

use NoNameWeb\LivewireMaterial\Testing\DesignGuard;

it('uses only what compiles', function () {
    expect(DesignGuard::scan([resource_path('views'), resource_path('js'), app_path()])
        ->forbidColours(['tertiary'])
        ->violations())->toBe([]);
});

The guard fails on maryUI tags, daisyUI classes, colours the theme does not declare, unknown Material Symbol names and Blade directives written inside component tags. It also fails on everything 2.0 cleared, and every line names the replacement: Tailwind's breakpoint prefixes (sm: and md:medium:, lg:expanded:, xl:large:, 2xl:extra-large:, max- likewise), its radius (rounded-lgrounded-corner-lg), shadow (shadow-mdshadow-elevation-2), text size, weight, leading and tracking (text-sm, leading-6, tracking-wide → a type-* style, font-medium → a type-emphasized-* style), easing and duration (ease-in-outease-standard, duration-300 → a duration-(--md-sys-motion-…-duration) with the easing it pairs with), and a colour written as a value rather than a role (bg-[#1d7afc], text-[rgb(…)], border-[color-mix(…)]).

Two checks are opt-in: 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/60text-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, 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

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.

S
Description
No description provided
Readme MIT
7.8 MiB
Languages
PHP 40.9%
CSS 22.2%
Blade 20.5%
JavaScript 16.3%
Shell 0.1%