Draw the snackbar's action and close buttons from the classes

Plan step 36 ("Interaction is the shared classes", the user, 2026-09-14).
The action and the close button hand-rolled md-state-layer, md-focus-ring
and md-touch-target, and toast.css's own header said so; both now render
the classes and keep only their own size and colour.

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:10:16 +02:00
co-authored by Claude Opus 5
parent 5a9211acd4
commit 480387a64e
3 changed files with 9 additions and 57 deletions
+3 -51
View File
@@ -17,11 +17,9 @@
* Below `medium` it is full width; from there it hugs its line length, which an application's own
* class can still bound.
*
* The action and the close button are both drawn at 40px and reach M3's 48px target (ACT-18)
* through an `::after`. Their state layer, focus ring and target are the declarations of the
* foundation's `md-state-layer`, `md-focus-ring` and `md-touch-target`
* (foundation/interaction.css), keyed on the buttons themselves as button.css does, because the
* view writes no class list.
* The action and the close button are both drawn at 40px and render the foundation's
* `md-state-layer`, `md-focus-ring` and `md-touch-target` (foundation/interaction.css) to reach
* M3's 48px target (ACT-18); nothing here copies their rules.
*
* It enters on a translate and a fade on the fast spatial spring, `@starting-style` giving both a
* start. No state icon: M3 says to avoid one in a snackbar (ACT-20). The live region is the host
@@ -113,55 +111,9 @@
}
:is([data-md-toast-action], [data-md-toast-dismiss]) {
position: relative;
isolation: isolate;
flex-shrink: 0;
border-radius: var(--md-sys-shape-corner-full);
cursor: pointer;
outline: none;
&::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);
}
&::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);
translate: -50% -50%;
}
@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);
}
}
[data-md-toast-action] {
+2 -2
View File
@@ -86,11 +86,11 @@
</div>
<template x-if="current.action">
<button type="button" data-md-toast-action x-text="current.action.label" x-on:click="act()"></button>
<button type="button" class="md-state-layer md-focus-ring md-touch-target" data-md-toast-action x-text="current.action.label" x-on:click="act()"></button>
</template>
<template x-if="current.action || ! current.timeout">
<button type="button" data-md-toast-dismiss aria-label="{{ __('Dismiss') }}" x-on:click="dismiss()">
<button type="button" class="md-state-layer md-focus-ring md-touch-target" data-md-toast-dismiss aria-label="{{ __('Dismiss') }}" x-on:click="dismiss()">
<x-livewire-material::icon name="close" size="20" />
</button>
</template>
+4 -4
View File
@@ -25,12 +25,12 @@ it('draws no state icon, and reaches 48px from its 40px controls', function () {
->and($html)->not->toContain('text-inverse-success')
->toContain('data-md-toast-action')
->toContain('data-md-toast-dismiss')
// 48px, from the foundation's md-touch-target class, not a copy of its own.
->toMatch('/class="md-state-layer md-focus-ring md-touch-target" data-md-toast-action/')
->toMatch('/class="md-state-layer md-focus-ring md-touch-target" data-md-toast-dismiss/')
->and($css->declarations(':is([data-md-toast-action], [data-md-toast-dismiss])'))
->toHaveKey('border-radius', 'var(--md-sys-shape-corner-full)')
->and($css->declarations(':is([data-md-toast-action], [data-md-toast-dismiss])::after'))->toMatchArray([
'min-width' => 'var(--md-sys-measurement-space600)',
'min-height' => 'var(--md-sys-measurement-space600)',
])
->and($css->has(':is([data-md-toast-action], [data-md-toast-dismiss])::after'))->toBeFalse()
->and($css->declarations('[data-md-toast-dismiss]'))->toMatchArray([
'width' => 'var(--md-sys-measurement-space500)',
'height' => 'var(--md-sys-measurement-space500)',