Files
2026-07-13 12:12:04 +08:00

265 lines
7.9 KiB
CSS

/* ==========================================================================
Base checkbox styles
========================================================================== */
.checkbox {
@apply flex flex-col items-start gap-1 outline-none no-highlight;
cursor: var(--cursor-interactive);
/* Field-level help text (siblings of the button), indented to align under the label */
& > [data-slot="description"],
& > [data-slot="field-error"] {
@apply w-full min-w-0 cursor-default ps-7 text-xs text-wrap wrap-break-word text-muted select-none;
}
[data-slot="label"] {
@apply select-none;
}
.checkbox__content [data-slot="label"] {
cursor: var(--cursor-interactive);
}
/* Disabled state */
&[data-disabled="true"] {
@apply status-disabled;
[data-slot="description"],
[data-slot="field-error"] {
@apply status-disabled;
}
}
/* Indicator styles */
&[data-selected="true"],
&[data-indeterminate="true"] {
.checkbox__indicator {
border-color: var(--accent-foreground);
}
}
/* Checkmark SVG indicator */
[data-slot="checkbox-default-indicator--checkmark"] {
@apply size-2.5 stroke-[2.5px] text-accent-foreground;
/**
* Transitions
*/
@apply transition-all duration-200 motion-reduce:transition-none;
}
&[data-selected="true"] [data-slot="checkbox-default-indicator--checkmark"] {
transition: stroke-dashoffset 150ms linear 15ms;
}
/* Invalid + Selected: checkmark icon color */
&[data-invalid="true"][data-selected="true"] [data-slot="checkbox-default-indicator--checkmark"],
&[data-invalid="true"][aria-checked="true"] [data-slot="checkbox-default-indicator--checkmark"],
&[aria-invalid="true"][data-selected="true"] [data-slot="checkbox-default-indicator--checkmark"],
&[aria-invalid="true"][aria-checked="true"] [data-slot="checkbox-default-indicator--checkmark"] {
@apply text-danger-foreground;
}
/* Indeterminate line indicator */
&[data-indeterminate="true"] {
[data-slot="checkbox-default-indicator--indeterminate"] {
@apply size-3;
}
}
/* Invalid + Indeterminate: indeterminate icon color */
&[data-indeterminate="true"][data-invalid="true"]
[data-slot="checkbox-default-indicator--indeterminate"],
&[data-indeterminate="true"][aria-invalid="true"]
[data-slot="checkbox-default-indicator--indeterminate"] {
@apply text-danger-foreground;
}
}
/* ==========================================================================
Checkbox control (the square button)
========================================================================== */
.checkbox__control {
@apply relative inline-flex size-4 shrink-0 items-center justify-center overflow-hidden rounded-md border [border-width:var(--border-width-field)] border-field-border bg-field shadow-field outline-none no-highlight;
/**
* Transitions
* CRITICAL: motion-reduce must be AFTER transition for correct override specificity
*/
transition:
background-color 200ms var(--ease-out),
border-color 200ms var(--ease-out),
transform 100ms var(--ease-out);
@apply motion-reduce:transition-none;
cursor: var(--cursor-interactive);
/* Background indicator pseudo-element */
&::before {
@apply pointer-events-none absolute inset-0 z-0 origin-center scale-70 rounded-md bg-accent opacity-0 content-[''];
/**
* Transitions
* CRITICAL: motion-reduce must be AFTER transition for correct override specificity
*/
transition:
scale 100ms var(--ease-linear),
opacity 200ms var(--ease-linear),
background-color 200ms var(--ease-out);
@apply motion-reduce:transition-none;
}
/* Interactive states */
/* Focus */
.checkbox:focus-visible &,
.checkbox:has([data-slot="checkbox-content"][data-focus-visible="true"]) &,
.checkbox [data-slot="checkbox-content"][data-focus-visible="true"] & {
@apply status-focused;
}
/* Hover */
.checkbox:hover &,
.checkbox:has([data-slot="checkbox-content"][data-hovered="true"]) &,
.checkbox [data-slot="checkbox-content"][data-hovered="true"] & {
@apply border-field-border-hover;
&::before {
@apply bg-accent-hover;
}
}
/* Pressed */
.checkbox:active &,
.checkbox:has([data-slot="checkbox-content"][data-pressed="true"]) &,
.checkbox [data-slot="checkbox-content"][data-pressed="true"] & {
}
/* Selection states */
/* Selected */
.checkbox[aria-checked="true"] &,
.checkbox[data-selected="true"] & {
@apply border-transparent text-accent-foreground;
&::before {
@apply scale-100 opacity-100;
}
}
/* Indeterminate */
.checkbox[data-indeterminate="true"] & {
@apply bg-accent text-accent-foreground;
}
/* Indeterminate + Pressed */
.checkbox:active[data-indeterminate="true"] &,
.checkbox[data-pressed="true"][data-indeterminate="true"] &,
.checkbox:has([data-slot="checkbox-content"][data-pressed="true"])[data-indeterminate="true"] & {
@apply bg-accent-hover;
}
/* Validation states */
/* Invalid (only when not selected/indeterminate) */
.checkbox[data-invalid="true"]:not([aria-checked="true"]):not([data-selected="true"]):not(
[data-indeterminate="true"]
)
&,
.checkbox[aria-invalid="true"]:not([aria-checked="true"]):not([data-selected="true"]):not(
[data-indeterminate="true"]
)
& {
@apply status-invalid-field;
}
/* Invalid + Selected */
.checkbox[data-invalid="true"][aria-checked="true"] &,
.checkbox[data-invalid="true"][data-selected="true"] &,
.checkbox[aria-invalid="true"][aria-checked="true"] &,
.checkbox[aria-invalid="true"][data-selected="true"] & {
@apply border-transparent bg-danger text-danger-foreground;
&::before {
@apply scale-100 bg-danger opacity-100;
}
}
/* Invalid + Indeterminate */
.checkbox[data-indeterminate="true"][aria-invalid="true"] &,
.checkbox[data-indeterminate="true"][data-invalid="true"] & {
@apply bg-danger text-danger-foreground;
}
}
/* ==========================================================================
Checkbox indicator
========================================================================== */
.checkbox__indicator {
@apply relative z-10 flex size-3 items-center justify-center;
& svg {
@apply size-full transform-gpu;
}
}
/* ==========================================================================
Disabled modifier
========================================================================== */
.checkbox--disabled {
@apply status-disabled;
}
/* ==========================================================================
Variant property definitions
========================================================================== */
.checkbox--primary {
/* Default styles */
}
.checkbox--secondary .checkbox__control {
@apply shadow-none;
background-color: var(--checkbox-control-bg);
--checkbox-control-bg: var(--default);
/* Hover state */
.checkbox:hover &,
.checkbox:has([data-slot="checkbox-content"][data-hovered="true"]) &,
.checkbox [data-slot="checkbox-content"][data-hovered="true"] & {
@apply border-field-border-hover;
}
}
/* Checkbox content — the clickable label (control + label text) */
.checkbox__content {
@apply relative inline-flex cursor-[inherit] items-center gap-3 text-sm font-medium text-foreground outline-none select-none no-highlight;
}
/* Unselected state background for secondary variant */
.checkbox--secondary:not([aria-checked="true"]):not([data-selected="true"]):not(
[data-indeterminate="true"]
)
.checkbox__control {
background-color: var(--checkbox-control-bg);
}
/* Selected state on secondary variant */
.checkbox--secondary[aria-checked="true"] .checkbox__control,
.checkbox--secondary[data-selected="true"] .checkbox__control {
&::before {
@apply bg-accent;
}
}
/* Indeterminate state on secondary variant */
.checkbox--secondary[data-indeterminate="true"] .checkbox__control {
@apply bg-accent;
&::before {
@apply bg-accent;
}
}