@sandlada/mdc
Version:
@sandlada/mdc is an open source component library that follows the Material Design 3 design specifications.
335 lines (315 loc) • 15 kB
JavaScript
import{css as e,unsafeCSS as t}from"../../node_modules/@lit/reactive-element/css-tag.js";import"../../node_modules/lit/index.js";import{defineVars as n}from"../../node_modules/@sandlada/jss/dist/define-vars.js";import{defineTokenRefsRecord as r}from"../../node_modules/@sandlada/jss/dist/define-token-refs.js";import{Easing as i}from"../../node_modules/@sandlada/mdk/build/domain/easing.js";import"../../node_modules/@sandlada/mdk/build/index.js";import{overrideComponentTokens as a}from"../../utils/tokens/override-component-tokens.js";import{stringTokens as o}from"../../utils/tokens/string-tokens.js";import"../../utils/tokens/index.js";import{CheckboxDefinition as s}from"../../component-definitions/checkbox.definition.js";
/**
* @license
* Copyright 2026 Kai-Orion & Sandlada
* SPDX-License-Identifier: MIT
*/
const c=r(s,{expandShapes:!1,useBaseFallback:!0,prefix:`--mdc-checkbox`}),l=t(n(c,!0).join(``)),u=t(i.EmphasizedAccelerate.ToCSSValue()),d=t(i.EmphasizedDecelerate.ToCSSValue()),f=e=>o(a(`--mdc-ripple`,{"enabled-hovered-color":`var(--_hovered-state-layer-color-${e})`,"enabled-hovered-opacity":`var(--_hovered-state-layer-opacity-${e})`,"enabled-focused-color":`var(--_focused-state-layer-color-${e})`,"enabled-focused-opacity":`var(--_focused-state-layer-opacity-${e})`,"enabled-pressed-color":`var(--_pressed-state-layer-color-${e})`,"enabled-pressed-opacity":`var(--_pressed-state-layer-opacity-${e})`})),p=()=>o(a(`--mdc-ripple`,{"enabled-hovered-color":`var(--_hovered-error-state-layer-color)`,"enabled-hovered-opacity":`var(--_hovered-error-state-layer-opacity)`,"enabled-focused-color":`var(--_focused-error-state-layer-color)`,"enabled-focused-opacity":`var(--_focused-error-state-layer-opacity)`,"enabled-pressed-color":`var(--_pressed-error-state-layer-color)`,"enabled-pressed-opacity":`var(--_pressed-error-state-layer-opacity)`})),m=o(a(`--mdc-focus-ring`,{"enabled-color":`var(--_focused-indicator-color)`,"outward-offset":`calc((44px - var(--_enabled-container-size)) / 2)`,"shape-end-end":`var(--_focus-ring-shape-end-end)`,"shape-end-start":`var(--_focus-ring-shape-end-start)`,"shape-start-end":`var(--_focus-ring-shape-start-end)`,"shape-start-start":`var(--_focus-ring-shape-start-start)`})),h=e`
mdc.checkbox {
variable {
:host {
${l}
}
}
base {
:host {
display: inline-flex;
vertical-align: top;
cursor: pointer;
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
outline: none;
position: relative;
height: var(--_enabled-container-size);
width: var(--_enabled-container-size);
border-start-start-radius: var(--_container-shape-start-start);
border-start-end-radius: var(--_container-shape-start-end);
border-end-end-radius: var(--_container-shape-end-end);
border-end-start-radius: var(--_container-shape-end-start);
/* Reserve space for the 48px touch target around the 18px box. */
margin: max(0px, ((48px - var(--_enabled-container-size)) / 2));
}
:host([disabled]) {
cursor: default;
}
:host([touch-target='none']) {
margin: 0px;
}
.container {
display: flex;
height: 100%;
place-content: center;
place-items: center;
position: relative;
width: 100%;
z-index: 0;
/* The host carries the container-shape-* corner tokens; the
outline / background / focus ring all derive their radius
from this node. */
border-radius: inherit;
}
.outline,
.background,
.icon {
inset: 0;
position: absolute;
}
.outline,
.background {
border-radius: inherit;
}
.outline {
border-color: var(--_enabled-outline-color-unselected);
border-style: solid;
border-width: var(--_enabled-outline-width-unselected);
box-sizing: border-box;
}
.selected .outline {
border-color: transparent;
border-width: var(--_enabled-outline-width-selected);
}
.background {
background-color: var(--_enabled-container-color-selected);
}
/* 48px touch target. Covers the box plus its margin so hover,
pointer and keyboard activation work over the whole target. */
.touch {
appearance: none;
height: 48px;
margin: 0;
opacity: 0;
outline: none;
position: absolute;
width: 48px;
z-index: 1;
cursor: inherit;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
:host([touch-target='none']) .touch {
height: 100%;
width: 100%;
}
/* Background and icon fade in/out with a vertical 45° flip. */
.background,
.icon {
opacity: 0; /* Fade in */
transition-duration: 150ms, 50ms; /* Exit duration for scale and opacity. */
transition-property: transform, opacity;
/* Exit easing for scale, linear for opacity. */
transition-timing-function: ${u}, linear;
transform: scale(0.6); /* Scale from 60% to 100%. */
}
.selected :is(.background, .icon) {
opacity: 1;
transition-duration: 350ms, 50ms; /* Enter duration for scale and opacity. */
transition-timing-function: ${d}, linear;
transform: scale(1);
}
.icon {
fill: var(--_enabled-icon-color-selected);
height: var(--_enabled-icon-size);
width: var(--_enabled-icon-size);
}
/* The checkmark is two <rect> marks: a short 2×2 leading segment
and a long 10×2 trailing segment (the indeterminate dash). */
.mark.short {
height: 2px;
transition-property: transform, height;
width: 2px;
}
.mark.long {
height: 2px;
transition-property: transform, width;
width: 10px;
}
.mark {
animation-duration: 150ms;
animation-timing-function: ${u};
transition-duration: 150ms;
transition-timing-function: ${u};
}
.selected .mark {
animation-duration: 350ms;
animation-timing-function: ${d};
transition-duration: 350ms;
transition-timing-function: ${d};
}
/* Creates the checkmark icon. The rects' bottom-left corner becomes
the bottom-most point of the checkmark (scaleY(-1) flips them
around the top-left origin, a Safari-compatible workaround). */
.checked .mark,
.prev-checked.unselected .mark {
transform: scaleY(-1) translate(7px, -14px) rotate(45deg);
}
.checked .mark.short,
.prev-checked.unselected .mark.short {
/* Right triangle with X,Y legs of 4dp: √(4² + 4²) */
height: 5.656854249492381px;
}
.checked .mark.long,
.prev-checked.unselected .mark.long {
/* Right triangle with X,Y legs of 8dp: √(8² + 8²) */
width: 11.313708498984761px;
}
/* Creates the indeterminate icon (a horizontal dash). */
.indeterminate .mark,
.prev-indeterminate.unselected .mark {
transform: scaleY(-1) translate(4px, -10px) rotate(0deg);
}
/* When selecting an unselected box, fade the icon in from its final
position rather than animating the mark shape. */
.prev-unselected .mark {
transition-property: none;
}
.prev-unselected.checked .mark.long {
/* Grow the long end of the checkmark from zero width. */
animation-name: prev-unselected-to-checked;
}
prev-unselected-to-checked {
from {
width: 0;
}
}
/* Unselected outline colors across interaction states. */
:host(:hover) .unselected .outline {
border-color: var(--_hovered-outline-color-unselected);
border-width: var(--_hovered-outline-width-unselected);
}
:host(:focus-within) .unselected .outline {
border-color: var(--_focused-outline-color-unselected);
border-width: var(--_focused-outline-width-unselected);
}
:host(:active) .unselected .outline {
border-color: var(--_pressed-outline-color-unselected);
border-width: var(--_pressed-outline-width-unselected);
}
/* Container and icon colors across interaction states. */
:host(:hover) .background {
background: var(--_hovered-container-color-selected);
}
:host(:focus-within) .background {
background: var(--_focused-container-color-selected);
}
:host(:active) .background {
background: var(--_pressed-container-color-selected);
}
:host(:hover) .icon {
fill: var(--_hovered-icon-color-selected);
}
:host(:focus-within) .icon {
fill: var(--_focused-icon-color-selected);
}
:host(:active) .icon {
fill: var(--_pressed-icon-color-selected);
}
/* Error states — mixinDelegatesAria shifts the host's
aria-invalid to data-aria-invalid, which is what these
selectors match. */
:host([data-aria-invalid='true']) .outline {
border-color: var(--_enabled-error-outline-color-unselected);
}
:host([data-aria-invalid='true']:hover) .outline {
border-color: var(--_hovered-error-outline-color-unselected);
}
:host([data-aria-invalid='true']:focus-within) .outline {
border-color: var(--_focused-error-outline-color-unselected);
}
:host([data-aria-invalid='true']:active) .outline {
border-color: var(--_pressed-error-outline-color-unselected);
}
:host([data-aria-invalid='true']) .background {
background: var(--_enabled-error-container-color-selected);
}
:host([data-aria-invalid='true']:hover) .background {
background: var(--_hovered-error-container-color-selected);
}
:host([data-aria-invalid='true']:active) .background {
background: var(--_pressed-error-container-color-unselected);
}
:host([data-aria-invalid='true']) .icon {
fill: var(--_enabled-error-icon-color-selected);
}
:host([data-aria-invalid='true']:hover) .icon {
fill: var(--_hovered-error-icon-color-selected);
}
:host([data-aria-invalid='true']:focus-within) .icon {
fill: var(--_focused-error-icon-color-selected);
}
:host([data-aria-invalid='true']:active) .icon {
fill: var(--_pressed-error-icon-color-selected);
}
/* Disabled: don't animate to/from disabled states because the
outline is hidden when selected — otherwise there'd be a flash
when the state changes programmatically while disabled. */
.disabled :is(.background, .icon, .mark),
.prev-disabled :is(.background, .icon, .mark) {
animation-duration: 0s;
transition-duration: 0s;
}
.disabled .outline {
border-color: var(--_disabled-outline-color-unselected);
border-width: var(--_disabled-outline-width-unselected);
opacity: var(--_disabled-container-opacity-unselected);
}
.selected.disabled .outline {
border-color: transparent;
}
.selected.disabled .background {
background: var(--_disabled-container-color-selected);
opacity: var(--_disabled-container-opacity-selected);
}
.disabled .icon {
fill: var(--_disabled-icon-color-selected);
}
}
composite.ripple {
.container mdc-ripple {
border-end-end-radius: var(--_state-layer-shape-end-end);
border-end-start-radius: var(--_state-layer-shape-end-start);
border-start-end-radius: var(--_state-layer-shape-start-end);
border-start-start-radius: var(--_state-layer-shape-start-start);
height: var(--_state-layer-size);
inset: unset;
width: var(--_state-layer-size);
}
.container.selected mdc-ripple {
${f(`selected`)}
}
.container:not(.selected) mdc-ripple {
${f(`unselected`)}
}
:host([data-aria-invalid='true']) .container mdc-ripple {
${p()}
}
}
composite.focus-ring {
.container {
${m};
}
}
hcm {
(forced-colors: active) {
.background {
background-color: CanvasText;
}
.selected.disabled .background {
background-color: GrayText;
opacity: 1;
}
.outline {
border-color: CanvasText;
}
.disabled .outline {
border-color: GrayText;
opacity: 1;
}
.icon {
fill: Canvas;
}
}
}
}
`;export{h as CheckboxStyles};
//# sourceMappingURL=checkbox.style.js.map