<x-toggle> renders data-md-toggle, its row (data-md-right), track, handle and icons as data-md-* attributes; the handle's icons take size 16 and a label-less track md-touch-target (plan step 36). toggle.css gains the root; a browser test pins the handle's 16px/24px sizes and 16px/36px centres off and on. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
152 lines
6.2 KiB
CSS
152 lines
6.2 KiB
CSS
/*
|
||
* <x-toggle>: M3's switch (resources/views/components/toggle.blade.php), on the row the selection
|
||
* controls share (components/selection.css). The hooks say `switch` because `toggle` is daisyUI's
|
||
* class, which the design guard rejects.
|
||
*
|
||
* SwitchTokens (androidx Compose Material 3, Apache-2.0): a 52×32 track with a 2px outline, in
|
||
* surface-container-highest when off and primary when on. The handle's centre is 16px from the
|
||
* track's start off and 36px on; it is 16px across off, 24px with an icon or on, and 28px while
|
||
* pressed, moving and growing on the fast spatial spring. Off it is outline, on-surface-variant
|
||
* while hovered, focused or pressed; on it is on-primary, primary-container in those states. The
|
||
* state layer is a 40px circle around the handle. Its icons are 16px, on-primary-container when on.
|
||
* Disabled, the track is on-surface at 12% and the handle on-surface at 38% (surface when on).
|
||
*
|
||
* [data-md-toggle] the root; `class` and `style` land here
|
||
* [data-md-selection-row] data-md-right puts the switch at the end of the row
|
||
* [data-md-switch] the track; `data-md-icons` = "both" or "selected"
|
||
* input, [data-md-switch-handle] (with [data-md-switch-on] and [data-md-switch-off] icons)
|
||
*/
|
||
|
||
@layer material.reset, material.tokens, material.base, material.layout, material.components, material.text, material.visibility;
|
||
|
||
@import './selection.css';
|
||
@import './icon.css';
|
||
|
||
@layer material.components {
|
||
[data-md-toggle] {
|
||
min-width: 0;
|
||
}
|
||
|
||
/* The positions below are the handle's centres less half its size, measured inside the track's
|
||
2px outline. */
|
||
[data-md-switch] {
|
||
position: relative;
|
||
display: grid;
|
||
flex: none;
|
||
width: 52px;
|
||
height: 32px;
|
||
}
|
||
|
||
[data-md-switch] input {
|
||
width: 100%;
|
||
height: 100%;
|
||
border: 2px solid var(--md-sys-color-outline);
|
||
border-radius: var(--md-sys-shape-corner-full);
|
||
background-color: var(--md-sys-color-surface-container-highest);
|
||
}
|
||
|
||
[data-md-switch] input:focus-visible {
|
||
outline: 3px solid var(--md-sys-color-secondary);
|
||
outline-offset: 2px;
|
||
}
|
||
|
||
[data-md-switch-handle] {
|
||
--handle: 16px;
|
||
|
||
position: absolute;
|
||
top: 50%;
|
||
inset-inline-start: calc(16px - var(--handle) / 2);
|
||
display: grid;
|
||
place-items: center;
|
||
width: var(--handle);
|
||
height: var(--handle);
|
||
translate: 0 -50%;
|
||
border-radius: var(--md-sys-shape-corner-full);
|
||
background-color: var(--md-sys-color-outline);
|
||
color: var(--md-sys-color-surface-container-highest);
|
||
pointer-events: none;
|
||
transition-property: inset-inline-start, width, height, background-color;
|
||
transition-duration: var(--md-sys-motion-spatial-fast-duration);
|
||
transition-timing-function: var(--md-sys-motion-spatial-fast);
|
||
}
|
||
|
||
[data-md-switch-handle]::before {
|
||
inset: 50% auto auto 50%;
|
||
width: 40px;
|
||
height: 40px;
|
||
translate: -50% -50%;
|
||
}
|
||
|
||
[data-md-switch-handle] > svg {
|
||
grid-area: 1 / 1;
|
||
opacity: 0;
|
||
transition: opacity var(--md-sys-motion-effects-fast-duration) var(--md-sys-motion-effects-fast);
|
||
}
|
||
|
||
[data-md-switch][data-md-icons='both'] [data-md-switch-handle] {
|
||
--handle: 24px;
|
||
}
|
||
|
||
[data-md-switch][data-md-icons='both'] input:not(:checked) ~ [data-md-switch-handle] [data-md-switch-off],
|
||
[data-md-switch][data-md-icons] input:checked ~ [data-md-switch-handle] [data-md-switch-on] {
|
||
opacity: 1;
|
||
}
|
||
|
||
[data-md-switch] input:checked {
|
||
border-color: var(--md-sys-color-primary);
|
||
background-color: var(--md-sys-color-primary);
|
||
}
|
||
|
||
[data-md-switch] input:checked ~ [data-md-switch-handle] {
|
||
--handle: 24px;
|
||
|
||
inset-inline-start: calc(36px - var(--handle) / 2);
|
||
background-color: var(--md-sys-color-on-primary);
|
||
color: var(--md-sys-color-on-primary-container);
|
||
}
|
||
|
||
[data-md-selection-row]:active [data-md-switch] input:not(:disabled) ~ [data-md-switch-handle] {
|
||
--handle: 28px;
|
||
}
|
||
|
||
@media (hover: hover) {
|
||
[data-md-selection-row]:hover [data-md-switch] input:not(:disabled) ~ [data-md-switch-handle] {
|
||
background-color: var(--md-sys-color-on-surface-variant);
|
||
}
|
||
|
||
[data-md-selection-row]:hover [data-md-switch] input:checked:not(:disabled) ~ [data-md-switch-handle] {
|
||
background-color: var(--md-sys-color-primary-container);
|
||
}
|
||
}
|
||
|
||
[data-md-switch] input:focus-visible ~ [data-md-switch-handle],
|
||
[data-md-selection-row]:active [data-md-switch] input:not(:disabled) ~ [data-md-switch-handle] {
|
||
background-color: var(--md-sys-color-on-surface-variant);
|
||
}
|
||
|
||
[data-md-switch] input:checked:focus-visible ~ [data-md-switch-handle],
|
||
[data-md-selection-row]:active [data-md-switch] input:checked:not(:disabled) ~ [data-md-switch-handle] {
|
||
background-color: var(--md-sys-color-primary-container);
|
||
}
|
||
|
||
[data-md-switch] input:disabled {
|
||
border-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-disabled-container-opacity) * 100%), transparent);
|
||
background-color: color-mix(in srgb, var(--md-sys-color-surface-container-highest) calc(var(--md-sys-state-disabled-container-opacity) * 100%), transparent);
|
||
}
|
||
|
||
[data-md-switch] input:disabled ~ [data-md-switch-handle] {
|
||
background-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-disabled-content-opacity) * 100%), transparent);
|
||
color: color-mix(in srgb, var(--md-sys-color-surface-container-highest) calc(var(--md-sys-state-disabled-content-opacity) * 100%), transparent);
|
||
}
|
||
|
||
[data-md-switch] input:disabled:checked {
|
||
border-color: transparent;
|
||
background-color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-disabled-container-opacity) * 100%), transparent);
|
||
}
|
||
|
||
[data-md-switch] input:disabled:checked ~ [data-md-switch-handle] {
|
||
background-color: var(--md-sys-color-surface);
|
||
color: color-mix(in srgb, var(--md-sys-color-on-surface) calc(var(--md-sys-state-disabled-content-opacity) * 100%), transparent);
|
||
}
|
||
}
|