Files
livewire-material/resources/css/showcase.css
T
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

580 lines
23 KiB
CSS

/*
* The showcase's own chrome: what its frame needs beyond the layout components, the text classes
* and the shared interaction classes (plan step 38) — a brand wordmark, the search box's own
* geometry, a small icon avatar, an example's card and its code sample. Everything else the frame
* draws is a layout component with its props, or a `md-*` text class; this file is only what those
* two do not cover.
*
* In `material.components`: the showcase is one more thing drawn over the foundation and the
* layout components, exactly where `components/error-page.css` sits for the same reason (a whole
* page's own chrome, not a reusable package component, but still something an application's own
* unlayered CSS should outrank without a specificity fight). Keyed on `data-md-showcase-*` hooks,
* never a bare element selector, since this file is always bundled together with `all.css`
* (`ShowcaseAssetController`) and must not restyle anything outside the showcase's own pages.
*
* No imports: batch 1 (the frame — layout, index, section, shell, the example component) uses no
* selector this file needs to draw over, only tokens already in `all.css`, which the bundle always
* carries ahead of this file. A later batch that needs a component's own hook imports its
* stylesheet here, as every other package file does.
*
* Batch 2 (the foundation and layout sections) adds the swatches, specimens and demo boxes those
* pages draw beyond a layout component and a `md-*` text class: a colour swatch (its own fill is
* the role it shows, so that one declaration is the caller's inline `style`, never a class), a
* type-scale row, a shape swatch, an elevation tile, a motion track and its moving dot, an icon
* search result and the Layout page's breakpoint cards.
*
* Batch 3 (the component sections) adds each section's own demo geometry: a sized loading
* indicator, a bounded box for the FAB menu, a narrow chip row that forces its own scroll
* affordance, a scrolling table wrapper, the legend above a checkbox grid, a bordered demo card,
* a small circular avatar and a compact search bar, and the two ink colours a menu item's `icon-class`
* needs beyond the fixed `md-ink-*` set.
*
* Batch 4 (the last: containment, carousel, bars, navigation, pages) adds the standard side
* sheet demo's own breakpoint (a `<x-row>` beats its own `stack-below` alignment back to `stretch`
* once collapsed to a column, which an `align` the row mode needs would otherwise leave
* shrink-wrapped), a bordered app-bar/toolbar frame (plain, `visible` for the search bar whose
* docked suggestions must not clip, `rounded` for the large-screen docked toolbar), a narrow
* scrollable-tabs box, and the navigation bar and rail demo frames (`bar`, `bar-wide` inside its
* own horizontal-scroll wrapper, `rail`, `rail-wide`) with their filler "page" and content panes.
* Every per-item carousel colour stays the caller's own inline `style`, the same mechanism as the
* colour swatch above.
*
* Of the three call sites batch 3 found, two still cannot take a `style` for the width their
* demos need: `<x-input>` forwards `style` to the raw `<input>`, not the field wrapper that is
* actually sized, and `<x-datepicker>` forwards neither — its root takes only `class`, `wire:key`
* and `x-model`. `.showcase-w-sm`, `.showcase-w-xs` stay plain unlayered classes for that reason,
* the same "caller's class" mechanism any application would reach for, deliberately not named
* `md-*` since they are not part of the fixed set text.css declares. `.showcase-w-narrow` stays
* one too, but to make a point on purpose (fields.blade.php's "Narrower" input): a small unlayered
* rule from an application's own stylesheet beats the package's own layered default, so it has to
* be a real class, not an inline `style` (which would win trivially and prove nothing). `<x-slider>`
* does forward `style` to its root, so its vertical demo's height became one
* (resources/views/showcase/sections/sliders.blade.php), and `.showcase-slider-vertical` is gone.
*/
@layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility;
@layer material.components {
[data-md-showcase] {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* The rail's wordmark: `md-type-title-lg md-truncate md-focus-ring` cover the type, the
ellipsis and the ring; `display: block` gives the truncated text a width to ellipsize
within, and the corner rounds the ring to match the state layer every other rail control
draws (foundation/interaction.css's `md-focus-ring` takes its shape from the element). */
[data-md-showcase-brand] {
display: block;
border-radius: var(--md-sys-shape-corner-xs);
}
/* The search bar fills the app bar's search slot. */
[data-md-showcase-search] {
width: 100%;
}
[data-md-showcase-search-hint] {
padding: var(--md-sys-measurement-space125) var(--md-sys-measurement-space200);
}
/* A kbd in running text: the search hint's "/", and the intro paragraph's own. */
[data-md-showcase-kbd] {
display: inline-block;
border-radius: var(--md-sys-shape-corner-xs);
background-color: var(--md-sys-color-surface-container-highest);
padding: 0 var(--md-sys-measurement-space75);
}
/* One search result: title over kind and context, centred in a 56px row (space700, M3's list
row) the way a menu item is. */
[data-md-showcase-search-result] {
display: flex;
flex-direction: column;
justify-content: center;
min-height: var(--md-sys-measurement-space700);
padding: var(--md-sys-measurement-space100) var(--md-sys-measurement-space200);
}
/* The bottom breathing room under a showcase page's content, inside the pane's own margins. */
[data-md-showcase-page] {
padding-block-end: var(--md-sys-measurement-space600);
}
/* Each section's group of components on the overview: a tonal box behind its icon, the way a
list's leading icon sits in a container (M3's small tonal container). */
[data-md-showcase-section-icon] {
display: grid;
place-items: center;
width: var(--md-sys-measurement-space600);
height: var(--md-sys-measurement-space600);
border-radius: var(--md-sys-shape-corner-lg);
background-color: var(--md-sys-color-secondary-container);
color: var(--md-sys-color-on-secondary-container);
}
/* An example: scroll-margin so a hash link (the search result, the "next section" nav) lands
clear of the sticky app bar above it. */
[data-md-showcase-example] {
scroll-margin-top: var(--md-sys-measurement-space900);
}
[data-md-showcase-code-summary] {
display: inline-block;
cursor: pointer;
border-radius: var(--md-sys-shape-corner-xs);
}
[data-md-showcase-code] {
margin-top: var(--md-sys-measurement-space100);
overflow-x: auto;
border-radius: var(--md-sys-shape-corner-sm);
background-color: var(--md-sys-color-surface-container-highest);
padding: var(--md-sys-measurement-space125);
}
/* Colour: a role swatch. Its fill is the one declaration that has to be the caller's inline
`style` (the role it shows decides it), so this rule only gives it a size and an edge; `lg`
is the big per-theme grid's, the plain attribute the smaller contrast-level list's. */
[data-md-showcase-swatch] {
display: block;
flex-shrink: 0;
width: var(--md-sys-measurement-space300);
height: var(--md-sys-measurement-space300);
border-radius: var(--md-sys-shape-corner-xs);
border: 1px solid var(--md-sys-color-outline-variant);
}
[data-md-showcase-swatch][data-md-showcase-size='lg'] {
width: var(--md-sys-measurement-space400);
height: var(--md-sys-measurement-space400);
}
/* Lets the role name beside a swatch ellipsize inside its row instead of widening the grid. */
[data-md-showcase-swatch-label] {
min-width: 0;
}
/* Type: one typescale style beside its label and its emphasized twin, a row of the surface
they share. A single column below `expanded` (840px), where the label, the regular sample
and the emphasized one no longer fit a line each. */
[data-md-showcase-type-row] {
display: grid;
gap: var(--md-sys-measurement-space100);
padding: var(--md-sys-measurement-space200);
}
[data-md-showcase-type-row] + [data-md-showcase-type-row] {
border-top: 1px solid var(--md-sys-color-outline-variant);
}
@media (width >= 840px) {
[data-md-showcase-type-row] {
grid-template-columns: 192px 1fr 1fr;
align-items: baseline;
}
}
/* Shape: a corner swatch, filled with a tonal colour that is decoration only (never meaning,
same as the corner it demonstrates), one per corner token. */
[data-md-showcase-shape-swatch] {
display: block;
height: 80px;
background-color: var(--md-sys-color-primary-container);
}
[data-md-showcase-shape-swatch][data-md-corner='none'] {
border-radius: var(--md-sys-shape-corner-none);
}
[data-md-showcase-shape-swatch][data-md-corner='xs'] {
border-radius: var(--md-sys-shape-corner-xs);
}
[data-md-showcase-shape-swatch][data-md-corner='sm'] {
border-radius: var(--md-sys-shape-corner-sm);
}
[data-md-showcase-shape-swatch][data-md-corner='md'] {
border-radius: var(--md-sys-shape-corner-md);
}
[data-md-showcase-shape-swatch][data-md-corner='lg'] {
border-radius: var(--md-sys-shape-corner-lg);
}
[data-md-showcase-shape-swatch][data-md-corner='lg-increased'] {
border-radius: var(--md-sys-shape-corner-lg-increased);
}
[data-md-showcase-shape-swatch][data-md-corner='xl'] {
border-radius: var(--md-sys-shape-corner-xl);
}
[data-md-showcase-shape-swatch][data-md-corner='xl-increased'] {
border-radius: var(--md-sys-shape-corner-xl-increased);
}
[data-md-showcase-shape-swatch][data-md-corner='xxl'] {
border-radius: var(--md-sys-shape-corner-xxl);
}
[data-md-showcase-shape-swatch][data-md-corner='full'] {
border-radius: var(--md-sys-shape-corner-full);
}
/* Elevation: a tile at one shadow level, on the surface-container-high M3 pairs a shadow with. */
[data-md-showcase-elevation-tile] {
display: flex;
align-items: center;
justify-content: center;
height: 96px;
border-radius: var(--md-sys-shape-corner-md);
background-color: var(--md-sys-color-surface-container-high);
}
[data-md-showcase-elevation-tile][data-md-elevation='1'] {
box-shadow: var(--md-sys-elevation-1);
}
[data-md-showcase-elevation-tile][data-md-elevation='2'] {
box-shadow: var(--md-sys-elevation-2);
}
[data-md-showcase-elevation-tile][data-md-elevation='3'] {
box-shadow: var(--md-sys-elevation-3);
}
[data-md-showcase-elevation-tile][data-md-elevation='4'] {
box-shadow: var(--md-sys-elevation-4);
}
[data-md-showcase-elevation-tile][data-md-elevation='5'] {
box-shadow: var(--md-sys-elevation-5);
}
/* Motion: one spring's row (its name, and a track the dot travels), and the dot itself — its
colour is decoration, its size is the only thing this file fixes; the transition is the
spring under test, so it stays the view's own inline style. */
[data-md-showcase-motion-row] {
display: grid;
grid-template-columns: 144px 1fr;
align-items: center;
gap: var(--md-sys-measurement-space200);
}
[data-md-showcase-motion-track] {
height: var(--md-sys-measurement-space400);
}
[data-md-showcase-motion-dot] {
width: var(--md-sys-measurement-space400);
height: var(--md-sys-measurement-space400);
border-radius: var(--md-sys-shape-corner-full);
background-color: var(--md-sys-color-tertiary);
}
/* Icons: one search result's mask (its `mask` shorthand is the fetched symbol, so it stays the
view's own x-bind:style), 24px by default and 20px once optical size 20 is on — the state a
script toggles as data-md-showcase-icon-size, never a class. */
[data-md-showcase-icon-mask] {
display: block;
width: 24px;
height: 24px;
background-color: currentColor;
}
[data-md-showcase-icon-mask][data-md-showcase-icon-size='20'] {
width: 20px;
height: 20px;
}
/* Layout: the five breakpoint cards, each lit by the same range query the layout components'
own stylesheets use, so the page proves the breakpoints rather than only describing them. */
[data-md-showcase-breakpoint-card] {
border-radius: var(--md-sys-shape-corner-md);
background-color: var(--md-sys-color-surface-container);
padding: var(--md-sys-measurement-space200);
}
@media (width < 600px) {
[data-md-showcase-breakpoint-card='compact'] {
background-color: var(--md-sys-color-primary-container);
color: var(--md-sys-color-on-primary-container);
}
}
@media (600px <= width < 840px) {
[data-md-showcase-breakpoint-card='medium'] {
background-color: var(--md-sys-color-primary-container);
color: var(--md-sys-color-on-primary-container);
}
}
@media (840px <= width < 1200px) {
[data-md-showcase-breakpoint-card='expanded'] {
background-color: var(--md-sys-color-primary-container);
color: var(--md-sys-color-on-primary-container);
}
}
@media (1200px <= width < 1600px) {
[data-md-showcase-breakpoint-card='large'] {
background-color: var(--md-sys-color-primary-container);
color: var(--md-sys-color-on-primary-container);
}
}
@media (width >= 1600px) {
[data-md-showcase-breakpoint-card='extra-large'] {
background-color: var(--md-sys-color-primary-container);
color: var(--md-sys-color-on-primary-container);
}
}
/* Buttons: the loading indicator sized and coloured beyond its 48px primary default, and the
bounded box a FAB menu opens into (M3 anchors it to a scaffold's own corner; this is only
room enough to show the menu open). */
[data-md-showcase-loading='lg'] {
width: 96px;
height: 96px;
color: var(--md-sys-color-tertiary);
}
[data-md-showcase-loading='sm'] {
width: 32px;
height: 32px;
}
[data-md-showcase-fab-menu-demo] {
display: flex;
align-items: flex-end;
justify-content: flex-end;
height: 288px;
}
/* Menus: an item's icon in a colour beyond the fixed md-ink-* set (icon-class covers the
roles that set already has, md-ink-info among them). */
[data-md-showcase-icon-color='tertiary'] [data-md-icon] {
color: var(--md-sys-color-tertiary);
}
[data-md-showcase-icon-color='secondary'] [data-md-icon] {
color: var(--md-sys-color-secondary);
}
/* Communication: the "plain" badge's own colours (that variant draws none, by design, so the
caller paints it), a focus ring's corner on a plain span standing in for a control, and the
illustration's two decorative colours beyond currentColor. */
[data-md-showcase-badge-plain] {
background-color: var(--md-sys-color-primary-fixed);
color: var(--md-sys-color-on-primary-fixed);
}
[data-md-showcase-focus-demo] {
border-radius: var(--md-sys-shape-corner-xs);
}
[data-md-showcase-illustration='container'] {
fill: var(--md-sys-color-primary-container);
}
[data-md-showcase-illustration='accent'] {
fill: var(--md-sys-color-tertiary);
}
[data-md-showcase-illustration='on-accent'] {
fill: var(--md-sys-color-on-tertiary);
}
/* Progress: one circular indicator drawn larger than its 48px default, to show the ring
scales with its box. */
[data-md-showcase-progress-lg] {
width: 96px;
height: 96px;
}
/* Chips: a row narrow enough that its set has to scroll, to show the fade and the arrow
buttons M3 asks an overflowing row for. */
[data-md-showcase-chip-scroll] {
width: 100%;
max-width: 384px;
}
/* Text fields: the legend above a checkbox grid, a bordered demo card at two widths (a form,
a checkbox group), a small circular avatar for a search bar's trailing slot, and the
compact search-icon-button bar M3 uses as a secondary entry point. */
[data-md-showcase-legend] {
margin-bottom: var(--md-sys-measurement-space200);
}
[data-md-showcase-demo-card] {
width: 100%;
}
[data-md-showcase-demo-card][data-md-showcase-size='md'] {
max-width: 576px;
}
[data-md-showcase-demo-card][data-md-showcase-size='lg'] {
max-width: 672px;
}
[data-md-showcase-avatar] {
display: grid;
place-items: center;
width: var(--md-sys-measurement-space400);
height: var(--md-sys-measurement-space400);
border-radius: var(--md-sys-shape-corner-full);
background-color: var(--md-sys-color-primary-container);
color: var(--md-sys-color-on-primary-container);
}
[data-md-showcase-search-bar] {
display: flex;
align-items: center;
gap: var(--md-sys-measurement-space100);
border-radius: var(--md-sys-shape-corner-full);
background-color: var(--md-sys-color-surface-container-high);
padding: var(--md-sys-measurement-space50) var(--md-sys-measurement-space100);
}
[data-md-showcase-search-bar-title] {
flex: 1 1 auto;
}
/* Data: the table's own horizontal scroll stays the caller's, as table.css's header says. */
[data-md-showcase-table-scroll] {
width: 100%;
overflow-x: auto;
}
/* Containment: the standard side sheet demo is a row from `expanded` (840px), M3's own
condition for a co-planar sheet, and a column below it — `<x-row>`'s own `stack-below`
stacks it there, but leaves the `align="start"` the row layout needs (so the sheet does not
stretch to the text column's height) in place, which would otherwise shrink-wrap both
columns instead of stacking them full width. */
@media (width < 840px) {
[data-md-showcase-side-sheet-demo] {
align-items: stretch;
}
}
/* Carousel: an item's own colour is the caller's inline `style` (its role pair is chosen per
item), the same mechanism as the colour swatch above; this only sizes and centres it. */
[data-md-showcase-carousel-swatch] {
display: grid;
place-items: center;
width: 100%;
height: 100%;
}
/* Bars: a bordered frame around one app-bar or docked-toolbar demo, matching the rounding a
real scaffold would clip its bar to. `visible` lets the search bar's docked suggestions
overflow it instead of clipping; `rounded` is the large-screen docked toolbar, which has no
edge of its own and only gains its padding from `expanded` (840px), M3's own condition for
the rounded (rather than square) docked toolbar. */
[data-md-showcase-bar-frame] {
width: 100%;
overflow: hidden;
border-radius: var(--md-sys-shape-corner-lg);
border: 1px solid var(--md-sys-color-outline-variant);
}
[data-md-showcase-bar-frame='visible'] {
overflow: visible;
}
[data-md-showcase-bar-frame='rounded'] {
border: 0;
background-color: var(--md-sys-color-surface);
}
@media (width >= 840px) {
[data-md-showcase-bar-frame='rounded'] {
padding: var(--md-sys-measurement-space200);
}
}
/* Bars: the scrollable-tabs demo needs a width narrow enough to force its own scroll. */
[data-md-showcase-tabs-narrow] {
max-width: 384px;
}
/* Navigation: a bounded frame around one navigation-bar or navigation-rail demo. `bar` and
`bar-wide` stack the bar under a "the page" filler the way a scaffold does; `bar-wide` drops
its own edge because its horizontal-scroll wrapper (`data-md-showcase-nav-scroll`) already
draws one, and is wide enough (600px) that a bar showing icon beside label never wraps.
`rail` and `rail-wide` sit the rail beside a content pane the way a scaffold does; `rail-wide`
is full width, for the two examples with real body text beside the rail. */
[data-md-showcase-nav-demo='bar'] {
display: flex;
flex-direction: column;
height: 224px;
overflow: hidden;
border-radius: var(--md-sys-shape-corner-lg);
border: 1px solid var(--md-sys-color-outline-variant);
background-color: var(--md-sys-color-surface);
}
[data-md-showcase-nav-demo='bar-wide'] {
display: flex;
flex-direction: column;
height: 224px;
min-width: 600px;
background-color: var(--md-sys-color-surface);
}
[data-md-showcase-nav-demo='rail'] {
display: flex;
height: 576px;
overflow: hidden;
border-radius: var(--md-sys-shape-corner-lg);
border: 1px solid var(--md-sys-color-outline-variant);
}
[data-md-showcase-nav-demo='rail-wide'] {
display: flex;
width: 100%;
height: 480px;
overflow: hidden;
border-radius: var(--md-sys-shape-corner-lg);
border: 1px solid var(--md-sys-color-outline-variant);
}
[data-md-showcase-nav-scroll] {
overflow-x: auto;
border-radius: var(--md-sys-shape-corner-lg);
border: 1px solid var(--md-sys-color-outline-variant);
}
[data-md-showcase-nav-page] {
flex: 1 1 auto;
padding: var(--md-sys-measurement-space200);
}
[data-md-showcase-nav-content] {
flex: 1 1 auto;
min-width: 0;
background-color: var(--md-sys-color-surface-container-low);
padding: var(--md-sys-measurement-space300);
}
}
/* Two call sites still cannot take a `style` for the width their demo needs — see the file
header — so these stay plain unlayered classes. */
.showcase-w-sm {
width: 192px;
}
.showcase-w-xs {
width: 144px;
}
/* Deliberately unlayered, to make the point fields.blade.php's hint names: an application's own
rule beats the package's layered default even though both set the same property. An inline
`style` would win regardless of layers, which would not demonstrate that. */
.showcase-w-narrow {
max-width: 320px;
}