Draw the button's state layer, focus ring and target from the classes

Plan step 36 ("Interaction is the shared classes", the user, 2026-09-14).
button.css hand-rolled the same declarations as foundation/interaction.css's
md-state-layer, md-focus-ring and md-touch-target; it now renders those
classes (the touch target only at xs and sm, where it draws under 48px) and
keeps only its own geometry, colour and motion. The stylesheet test's "writes
no class list" check now allows the three interaction classes, in any subset,
and $attributes->class() as the way a root merges them with a caller's own
class; split-button and button-group, which nest a button, are updated to match.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
Andreas Reinhold / reini
2026-09-14 18:01:18 +02:00
co-authored by Claude Opus 5
parent aa3e789081
commit bfd368e463
6 changed files with 76 additions and 78 deletions
+3 -54
View File
@@ -35,9 +35,9 @@
* Motion: corners, padding, margins and `bottom` on the fast spatial spring, colour and shadow on
* the fast effects spring beside it — a colour must never overshoot (tokens/motion.css).
*
* The state layer, focus ring and 48px target (xs and sm) are the declarations of the foundation's
* `md-state-layer`, `md-focus-ring` and `md-touch-target` (foundation/interaction.css), keyed on
* the button itself.
* The button renders the foundation's `md-state-layer` and `md-focus-ring`
* (foundation/interaction.css), and, at `xs` and `sm` where it draws under 48px, `md-touch-target`
* too; nothing here copies their rules.
*
* `data-md-compact-fab`: below `medium` (600px) the button is an extended FAB in the thumb zone —
* fixed 16px from the end, 56px tall with 16px padding, the large corner, `title-md`,
@@ -68,8 +68,6 @@
--md-button-outline: transparent;
--md-button-elevation: none;
position: relative;
isolation: isolate;
display: inline-flex;
flex-shrink: 0;
align-items: center;
@@ -82,7 +80,6 @@
cursor: pointer;
-webkit-user-select: none;
user-select: none;
outline: none;
transition-property: border-radius, padding, margin, bottom, background-color, color, box-shadow;
transition-duration:
var(--md-sys-motion-spatial-fast-duration), var(--md-sys-motion-spatial-fast-duration),
@@ -92,37 +89,6 @@
var(--md-sys-motion-spatial-fast), var(--md-sys-motion-spatial-fast),
var(--md-sys-motion-spatial-fast), var(--md-sys-motion-spatial-fast),
var(--md-sys-motion-effects-fast), var(--md-sys-motion-effects-fast), var(--md-sys-motion-effects-fast);
&::before {
content: '';
position: absolute;
inset: 0;
z-index: -1;
border-radius: inherit;
background-color: currentColor;
opacity: 0;
pointer-events: none;
transition: opacity var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast);
}
@media (hover: hover) {
&:hover::before {
opacity: var(--md-sys-state-hover-state-layer-opacity);
}
}
&:focus-visible {
outline: 3px solid var(--md-sys-color-secondary);
outline-offset: 2px;
}
&:focus-visible::before {
opacity: var(--md-sys-state-focus-state-layer-opacity);
}
&:active::before {
opacity: var(--md-sys-state-pressed-state-layer-opacity);
}
}
/* Sizes. */
@@ -238,19 +204,6 @@
}
}
/* M3's minimum target, past a button drawn smaller than 48px. */
[data-md-button]:is([data-md-size='xs'], [data-md-size='sm'])::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 100%;
height: 100%;
min-width: var(--md-sys-measurement-space600);
min-height: var(--md-sys-measurement-space600);
transform: translate(-50%, -50%);
}
/* Shape, without specificity: a group reshapes the buttons in it. */
:where([data-md-button]) {
border-radius: var(--md-sys-shape-corner-full);
@@ -385,10 +338,6 @@
[data-md-button]:is(:disabled, [aria-disabled='true']) {
box-shadow: none;
color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-disabled-content-opacity) * 100%), transparent);
&::before {
display: none;
}
}
[data-md-button]:is([data-md-variant='filled'], [data-md-variant='tonal'], [data-md-variant='elevated'], [data-md-variant='outlined'][data-md-selected='true']):is(:disabled, [aria-disabled='true']) {