feat: add theme provider and control components for light/dark mode

- Implemented ThemeProvider to manage theme state and preferences.
- Added ThemeControl component for users to switch between system, light, and dark themes.
- Integrated localStorage for theme preference persistence.
- Updated document styles based on theme changes.

test: add tests for permanent deletion of habits and related records

- Created tests to ensure permanent deletion of habits removes all dependent records.
- Verified that deletion requires ownership, authentication, and checks for archived habits.
- Added tests to confirm rollback behavior on deletion failures.

test: add tests for habit palette and progress card functionality

- Implemented tests for habit color progression and theme-based color shading.
- Added tests for progress card calculations and calendar display logic.

feat: define theme types and preferences

- Introduced Theme and ThemePreference types for better type safety.
- Created utility functions for resolving theme based on user preference and system settings.
- Defined theme palettes for light and dark modes to ensure adequate contrast.
This commit is contained in:
syntaxbullet
2026-09-05 10:07:28 +02:00
parent 9e9871c08a
commit 00729096bf
47 changed files with 780 additions and 337 deletions

View File

@@ -21,6 +21,11 @@
--ds-paper: #fff;
--ds-surface: #f5f5f5;
--ds-empty: #eee;
--ds-selection: #edf2ec;
--ds-selection-border: #94ac9b;
--ds-preview-panel: #eff0eb;
--ds-preview-switch: #e6e8e1;
--ds-error: #a12c2c;
--ds-serif: "Instrument Serif", Georgia, serif;
--ds-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
position: relative;
@@ -940,13 +945,6 @@
outline: 2px solid var(--ds-ink);
outline-offset: 2px;
}
.ds-calendar-caption {
display: flex;
justify-content: space-between;
gap: 16px;
@apply type-small;
margin: 17px 0 23px;
}
.ds-calendar-legend {
display: flex;
justify-content: space-between;
@@ -977,13 +975,9 @@
flex: 0 0 11px;
}
/* Calendar detail and task controls share a compact, readable hierarchy. */
.ds-calendar-help { color: var(--ds-secondary); max-width: 30rem; }
.ds-calendar-help summary { display: flex; align-items: center; gap: 8px; min-height: 44px; cursor: pointer; list-style: none; }
.ds-calendar-help summary::-webkit-details-marker { display: none; }
.ds-calendar-help summary:hover { color: var(--ds-ink); }
.ds-calendar-help p { padding: 4px 0 12px; }
.ds-calendar-caption { align-items: baseline; margin-bottom: 16px; }
.ds-calendar-demo-label { @apply type-small; margin-top: 16px !important; }
.ds-date-inspector {
margin-top: 20px;
background: var(--ds-surface);
border-left: 3px solid var(--calendar-color, var(--ds-ink));
padding: 18px 20px;
@@ -1128,7 +1122,6 @@
.ds-chart-selection {
gap: 10px 18px;
}
.ds-calendar-caption { flex-direction: column; gap: 0; }
.ds-big-value small {
margin-left: 10px;
}
@@ -1214,7 +1207,6 @@
grid-column: 1 / -1;
margin-top: -12px !important;
}
.ds-calendar--compact .ds-calendar-caption { margin-bottom: 12px; }
.ds-task-accordion {
margin-top: 16px;
border: 1px solid var(--ds-rule);
@@ -1411,25 +1403,69 @@
.ds-editable-task-row > .ds-checkbox { flex: 1; min-height: 76px; border-top: 0; }
.ds-checkbox-copy { display: grid; gap: 4px; min-width: 0; }
.ds-checkbox-description { color: var(--ds-secondary); @apply type-small; }
.ds-share-layout { display: grid; grid-template-columns: minmax(240px, 300px) minmax(0, 1fr); gap: 36px; align-items: start; }
.ds-share-controls { display: grid; gap: 24px; min-width: 0; }
/* Sharing composer: preview first, quiet controls, one clear delivery action. */
#share-progress { width: min(1120px, calc(100vw - 48px)); }
#share-progress .ds-modal-body { padding: 0; scrollbar-gutter: auto; }
#share-progress .ds-modal-heading h2 { @apply type-title; line-height: 1.15; }
.ds-share-layout { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(280px, 1fr); align-items: start; }
.ds-share-controls { display: grid; gap: 24px; min-width: 0; padding: 28px; }
.ds-share-fieldset { border: 0; margin: 0; padding: 0; display: grid; gap: 10px; min-width: 0; }
.ds-share-fieldset legend { padding: 0; margin-bottom: 10px; }
.ds-share-fieldset input:not([type="checkbox"]), .ds-share-fieldset select { width: 100%; min-width: 0; }
.ds-share-habits { display: grid; max-height: 220px; overflow: auto; margin-bottom: 8px; }
.ds-share-habits .ds-checkbox-copy { overflow-wrap: anywhere; }
.ds-share-dates { display: grid; gap: 12px; }
.ds-share-feedback { color: var(--ds-ink); @apply type-small; }
.ds-share-preview-column { display: grid; gap: 14px; min-width: 0; }
.ds-share-preview { display: grid; place-items: center; padding: 22px; min-height: 240px; background: var(--ds-surface); border: 1px solid var(--ds-rule); }
.ds-share-preview img { display: block; width: 100%; height: auto; box-shadow: 0 3px 14px #0000000d; }
.ds-share-preview > div { display: grid; gap: 16px; }
.ds-share-actions { display: flex; flex-wrap: wrap; gap: 12px; }
.ds-share-actions .ds-button { display: inline-flex; align-items: center; justify-content: center; gap: 8px; }
@media (max-width: 900px) {
.ds-share-layout { grid-template-columns: minmax(0, 1fr); gap: 28px; }
.ds-share-preview { padding: 12px; }
.ds-share-fieldset legend { width: 100%; padding: 0; margin-bottom: 12px; @apply type-small; font-weight: 600; line-height: 20px; }
.ds-share-fieldset input:not([type="checkbox"]) { width: 100%; min-width: 0; @apply type-small; }
.ds-share-count { float: right; color: var(--ds-secondary); @apply type-small; font-weight: 400; font-variant-numeric: tabular-nums; }
.ds-share-habits { display: grid; gap: 8px; max-height: 204px; overflow: auto; padding: 2px; margin: -2px; }
.ds-share-habits .ds-checkbox { border: 1px solid var(--ds-rule); padding: 12px; margin: 0; min-height: 46px; align-items: center; border-radius: 6px; }
.ds-share-habits .ds-checkbox:has(input:checked) { background: var(--ds-selection); border-color: var(--ds-selection-border); }
.ds-share-habits .ds-checkbox-copy { overflow-wrap: anywhere; @apply type-small; line-height: 20px; }
.ds-share-hint, .ds-share-caption { @apply type-small; line-height: 1.5; color: var(--ds-secondary); }
.ds-share-periods, .ds-share-size { display: flex; padding: 3px; gap: 3px; background: var(--ds-surface); border: 1px solid var(--ds-rule); border-radius: 7px; }
.ds-share-periods button, .ds-share-size button { flex: 1; border: 0; background: transparent; color: var(--ds-secondary); padding: 9px 6px; @apply type-small; font-weight: 500; line-height: 18px; border-radius: 4px; cursor: pointer; white-space: nowrap; }
.ds-share-periods button[aria-pressed="true"], .ds-share-size button[aria-pressed="true"] { color: var(--ds-ink); background: var(--ds-paper); box-shadow: 0 1px 4px #00000015; }
.ds-share-periods button:disabled { cursor: default; opacity: .5; }
.ds-share-dates { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; }
.ds-share-privacy { border-top: 1px solid var(--ds-rule); border-bottom: 1px solid var(--ds-rule); }
.ds-share-privacy summary { display: flex; align-items: center; gap: 9px; padding: 16px 0; cursor: pointer; @apply type-small; font-weight: 500; list-style: none; }
.ds-share-privacy summary::-webkit-details-marker { display: none; }
.ds-share-privacy summary span { flex: 1; }
.ds-share-privacy[open] summary > svg:last-child { transform: rotate(180deg); }
.ds-share-privacy .ds-share-fieldset { padding-bottom: 16px; gap: 2px; }
.ds-share-privacy .ds-checkbox { @apply type-small; min-height: 36px; }
.ds-share-destination { display: flex; align-items: flex-start; gap: 12px; min-width: 0; }
.ds-share-destination-icon { padding: 10px; border: 1px solid var(--ds-rule); border-radius: 10px; background: var(--ds-surface); }
.ds-share-destination > div { display: grid; gap: 3px; min-width: 0; overflow-wrap: anywhere; }
.ds-share-destination a { @apply type-small; font-weight: 600; text-underline-offset: 3px; }
.ds-share-preview-column { display: grid; gap: 16px; min-width: 0; padding: 28px; background: var(--ds-preview-panel); border-right: 1px solid var(--ds-rule); align-self: stretch; align-content: start; }
.ds-share-preview-heading { display: flex; justify-content: space-between; align-items: center; gap: 12px; }
.ds-share-preview-heading > span { display: inline-flex; gap: 8px; align-items: center; @apply type-small; font-weight: 600; }
.ds-share-size { background: var(--ds-preview-switch); border-color: transparent; }
.ds-share-size button { padding: 5px 9px; }
.ds-share-preview { display: grid; place-items: center; min-height: 280px; padding: 24px; border-radius: 10px; background: #313338; }
.ds-share-preview img { display: block; width: 100%; height: auto; border-radius: 4px; box-shadow: 0 6px 24px #00000020; }
.ds-share-preview--thumbnail img { max-width: 360px; }
.ds-share-placeholder { display: grid; justify-items: center; gap: 16px; color: #f2f3f5; text-align: center; @apply type-small; padding: 24px 0; }
.ds-share-placeholder p { max-width: 30ch; }
.ds-share-caption { max-width: 48ch; }
.ds-share-footer { position: sticky; bottom: 0; display: flex; align-items: center; justify-content: space-between; gap: 24px; padding: 20px 28px; border-top: 1px solid var(--ds-rule); background: var(--ds-paper); z-index: 1; }
.ds-share-footer-copy { @apply type-small; line-height: 1.5; min-width: 0; }
.ds-share-footer-copy span { display: block; color: var(--ds-secondary); @apply type-small; margin-top: 3px; }
.ds-share-footer-copy a { text-decoration: underline; text-underline-offset: 3px; }
.ds-share-error { color: var(--ds-error); }
.ds-share-actions { display: flex; gap: 10px; flex-shrink: 0; }
.ds-share-actions .ds-button { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 44px; @apply type-small; padding: 12px 16px; border-radius: 6px; }
.ds-share-actions .ds-button--primary { background: #285d49; border-color: #285d49; color: #fff; }
.ds-share-actions .ds-button--primary:hover:not(:disabled) { background: #1d4938; }
@media (max-width: 760px) {
#share-progress { width: calc(100vw - 24px); }
#share-progress .ds-modal-heading h2 { @apply type-title; }
.ds-share-layout { grid-template-columns: minmax(0, 1fr); }
.ds-share-preview-column { padding: 20px; border-right: 0; border-bottom: 1px solid var(--ds-rule); }
.ds-share-controls { padding: 20px; gap: 20px; }
.ds-share-preview { padding: 16px; min-height: 200px; }
.ds-share-footer { padding: 16px 20px; flex-wrap: wrap; gap: 12px; }
.ds-share-actions { width: 100%; }
.ds-share-actions .ds-button { flex: 1; padding-inline: 10px; }
}
.ds-task-add-action { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; padding: 8px 0; }
@media (max-width: 640px) {
.ds-habit-title-row { gap: 8px; flex-wrap: wrap; }
@@ -1529,7 +1565,7 @@
.ds-welcome-panel--compact .ds-welcome-identity { gap: 14px; margin: 0; min-width: 0; }
.ds-welcome-panel--compact .ds-welcome-identity > div:last-child { min-width: 0; }
.ds-welcome-panel--compact .ds-avatar { width: 44px; height: 44px; flex-basis: 44px; background: var(--ds-paper); @apply type-lead; }
.ds-welcome-panel--compact h1 { margin-top: 4px; overflow-wrap: anywhere; }
.ds-welcome-panel--compact h1 { margin-top: 0; overflow-wrap: anywhere; }
.ds-welcome-panel--compact h1 em { display: inline; }
.ds-welcome-panel--compact .ds-welcome-calendar { padding: 0; border: 0; text-align: right; flex-shrink: 0; }
.ds-welcome-panel--compact .ds-welcome-timezone { justify-content: flex-end; margin-top: 4px !important; }
@@ -1541,7 +1577,7 @@
.ds-welcome-panel--compact .ds-utility-row .ds-button { padding-inline: 12px; }
.ds-habits-section { padding-top: 28px; border-top: 0; }
.ds-habits-section .ds-section-top { margin-bottom: 24px; align-items: center; flex-wrap: wrap; }
.ds-habits-section .ds-section-top h2 { @apply type-title; margin-top: 8px; }
.ds-habits-section .ds-section-top h2 { @apply type-title; margin-top: 0; }
.ds-habit-filters { display: inline-flex; width: fit-content; gap: 4px; padding: 4px; background: var(--ds-surface); border: 1px solid var(--ds-rule); border-radius: 6px; }
.ds-habit-filters .ds-button { @apply type-small; color: var(--ds-secondary); border: 1px solid transparent; background: transparent; border-radius: 3px; }
.ds-habit-filters .ds-button[aria-pressed="true"] { color: var(--ds-ink); background: var(--ds-paper); border-color: var(--ds-rule); box-shadow: 0 1px 2px #00000008; }
@@ -1589,53 +1625,89 @@
.ds-counter-input { border-color: ButtonText; }
}
/* Each goal puts its completion control before secondary history. */
/* Habits share one identity and graph layout, with controls tailored to the goal. */
.ds-visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
.ds-habit-list > .ds-goal { display: flex; flex-direction: column; align-items: stretch; gap: 0; padding: 20px 0; }
.ds-goal-body { min-width: 0; }
.ds-goal-overview { min-width: 0; }
.ds-goal-heading { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.ds-goal-title { display: flex; align-items: center; gap: 12px; min-width: 0; }
.ds-goal-title :is(h3, h4) { overflow-wrap: anywhere; min-width: 0; }
.ds-goal-title label { cursor: pointer; }
.ds-goal-title > .ds-checkbox { flex: 0 0 44px; justify-content: center; gap: 0; }
.ds-goal--manual .ds-goal-body { padding: 4px 0; }
.ds-goal--manual .ds-goal-title:has(input:checked) :is(h3, h4) { color: var(--ds-secondary); }
.ds-goal--manual .ds-goal-title input { width: 26px; height: 26px; }
.ds-goal--manual .ds-goal-title input:checked { background: var(--habit-color); border-color: var(--habit-color); }
.ds-goal-schedule { @apply type-small; color: var(--ds-secondary); margin-top: 6px; }
.ds-goal--manual .ds-goal-schedule { padding-left: 56px; }
.ds-goal--count .ds-goal-body { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, auto); align-items: center; gap: 24px; padding: 24px; border-radius: 8px; border: 1px solid var(--ds-rule); border-inline-start: 3px solid var(--habit-color); background: var(--habit-tint); }
.ds-goal-count { display: flex; flex-direction: column; align-items: center; gap: 8px; min-width: 0; }
.ds-goal-count .ds-counter { justify-content: center; padding: 6px; gap: 6px; background: var(--ds-paper); border-color: var(--ds-rule); }
.ds-goal-count .ds-counter-input { @apply type-section; width: 4ch; min-height: 60px; background: transparent; }
.ds-goal-count .ds-counter .ds-button { min-height: 44px; min-width: 44px; }
.ds-goal-count .ds-counter > .ds-muted { @apply type-ui-heading; padding-right: 0; }
.ds-goal-unit { @apply type-small; color: var(--ds-secondary); overflow-wrap: anywhere; text-align: center; }
.ds-habit-list .ds-goal .ds-habit-history { align-self: flex-end; margin-top: 10px; }
.ds-habit-list .ds-goal .ds-habit-history:has([aria-expanded="true"]) { align-self: stretch; }
.ds-goal--tasks .ds-goal-body { border: 1px solid var(--ds-rule); border-radius: 8px; overflow: clip; }
.ds-goal--tasks .ds-goal-overview { padding: 20px 24px 16px; background: var(--ds-surface); border-bottom: 1px solid var(--ds-rule); }
.ds-goal-task-progress { display: flex; align-items: center; gap: 12px; color: var(--ds-secondary); margin-top: 12px; @apply type-small; font-variant-numeric: tabular-nums; }
.ds-goal-task-progress progress { appearance: none; width: 100px; height: 4px; border: 0; border-radius: 4px; overflow: hidden; background: var(--ds-rule); accent-color: var(--habit-color); }
.ds-habit-list > .ds-goal { display: block; padding: 32px 0; }
.ds-goal-heading { display: flex; align-items: center; justify-content: space-between; gap: 24px; margin-bottom: 24px; }
.ds-goal-identity { min-width: 0; }
.ds-goal-identity h3, .ds-goal-identity h4 { margin-top: 8px; overflow-wrap: anywhere; }
.ds-goal-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; color: var(--ds-secondary); @apply type-small; }
.ds-goal-swatch { width: 12px; height: 12px; flex-shrink: 0; background: var(--habit-color); }
.ds-goal-schedule { border-left: 1px solid var(--ds-rule); padding-left: 8px; }
.ds-goal-heading-actions { display: flex; align-items: center; gap: 16px; flex-shrink: 0; }
.ds-goal-status { display: inline-flex; align-items: center; gap: 8px; @apply type-small; }
.ds-goal-body { display: grid; grid-template-columns: minmax(0, 1fr) 304px; gap: 32px; align-items: start; }
.ds-goal-graph { grid-column: 1; grid-row: 1; min-width: 0; }
.ds-goal-completion { grid-column: 2; grid-row: 1; min-width: 0; padding: 24px; background: var(--ds-surface); border-top: 3px solid var(--habit-color); }
.ds-goal-count { display: grid; gap: 12px; }
.ds-goal-target { @apply type-body; font-weight: 500; overflow-wrap: anywhere; }
.ds-goal-count .ds-counter { width: fit-content; max-width: 100%; border-radius: 0; gap: 4px; padding: 0; justify-content: flex-start; }
.ds-goal-count .ds-counter-input { @apply type-body; font-family: var(--ds-sans); font-weight: 500; width: 5ch; min-width: 44px; height: 44px; min-height: 44px; padding: 8px 4px; background: var(--ds-paper); border-radius: 0; }
.ds-goal-count .ds-counter .ds-button { @apply type-lead; height: 44px; }
.ds-goal-count .ds-counter .ds-counter-save { @apply type-small; flex-basis: 100%; border-width: 1px 0 0; }
.ds-goal-count .ds-counter > .ds-muted { padding: 0 12px 0 4px; white-space: nowrap; }
.ds-goal-check-in { display: grid; grid-template-columns: 20px minmax(0, 1fr); align-items: center; gap: 0 12px; }
.ds-goal-check-in > .ds-checkbox { justify-content: center; }
.ds-goal-check-in > label[for] { @apply type-body; font-weight: 500; cursor: pointer; }
.ds-goal-completion .ds-checkbox input { width: 20px; height: 20px; min-width: 20px; min-height: 20px; flex: 0 0 20px; aspect-ratio: 1; }
.ds-goal-task-progress { display: grid; gap: 12px; margin-bottom: 16px; @apply type-body; font-weight: 500; }
.ds-goal-task-progress progress { appearance: none; width: 100%; height: 4px; border: 0; background: var(--ds-rule); accent-color: var(--habit-color); }
.ds-goal-task-progress progress::-webkit-progress-bar { background: var(--ds-rule); }
.ds-goal-task-progress progress::-webkit-progress-value { background: var(--habit-color); }
.ds-goal-task-progress progress::-moz-progress-bar { background: var(--habit-color); }
.ds-goal-tasks { padding: 4px 16px 8px; }
.ds-routine-items { list-style: none; padding: 0; margin: 0; }
.ds-routine-items .ds-editable-task:first-child { border-top: 0; }
.ds-routine-items .ds-editable-task-row { padding: 4px 8px; gap: 12px; border-radius: 4px; }
.ds-routine-items .ds-editable-task-row > .ds-checkbox { min-height: 48px; }
.ds-routine-items .ds-editable-task-row:has(input:checked) .ds-checkbox-label { color: var(--ds-secondary); text-decoration: line-through; text-decoration-color: var(--ds-rule); }
.ds-routine-items .ds-editable-task-row:hover { background: var(--ds-surface); }
.ds-goal-tasks .ds-task-add-action { padding: 8px; border-top: 1px solid var(--ds-rule); }
.ds-routine-items .ds-editable-task-row { gap: 8px; align-items: flex-start; padding-block: 0; }
.ds-routine-items .ds-item-actions { padding-top: 2px; }
.ds-routine-items .ds-editable-task-row > .ds-checkbox { min-height: 48px; min-width: 0; align-items: flex-start; gap: 12px; padding-block: 12px; @apply type-body; }
.ds-routine-items .ds-checkbox input { margin-top: 2px; }
.ds-routine-items .ds-checkbox-copy { gap: 4px; overflow-wrap: anywhere; }
.ds-goal-completion input:checked { background: var(--habit-color); border-color: var(--habit-color); }
.ds-routine-items .ds-editable-task-row:has(input:checked) .ds-checkbox-label { color: var(--ds-secondary); text-decoration: line-through; }
.ds-goal-tasks .ds-task-add-action { padding: 8px 0 0; border-top: 1px solid var(--ds-rule); }
.ds-goal-tasks .ds-task-add-action .ds-button { justify-content: flex-start; gap: 12px; padding: 0; border: 0; min-height: 44px; @apply type-body; }
.ds-goal-tasks .ds-task-add-action svg { width: 20px; height: 20px; flex: 0 0 20px; }
.ds-habit-starter-options { margin-top: 0; }
@media (max-width: 700px) {
.ds-goal-heading { gap: 8px; }
.ds-goal--count .ds-goal-body { grid-template-columns: minmax(0, 1fr); gap: 20px; padding: 20px 16px; }
.ds-goal--tasks .ds-goal-overview { padding: 16px; }
.ds-goal-tasks { padding-inline: 8px; }
.ds-goal-count { gap: 8px; }
.ds-goal-count .ds-counter { gap: 4px; }
.ds-habit-list .ds-goal .ds-habit-history { align-self: flex-start; }
@media (max-width: 1000px) {
.ds-goal-body { grid-template-columns: minmax(0, 1fr) 280px; gap: 24px; }
.ds-goal-completion { padding: 20px; }
}
@media (max-width: 760px) {
.ds-habit-list > .ds-goal { padding: 24px 0; }
.ds-goal-heading { align-items: flex-start; flex-wrap: wrap; gap: 16px; }
.ds-goal-heading-actions { justify-content: space-between; width: 100%; }
.ds-goal-body { display: flex; flex-direction: column; gap: 24px; }
.ds-goal-completion, .ds-goal-graph { width: 100%; }
.ds-goal-count .ds-counter { justify-content: flex-start; width: fit-content; }
}
@media (forced-colors: active) {
.ds-goal-completion { border: 1px solid CanvasText; }
.ds-goal-completion input:checked { appearance: auto; }
}
:root[data-theme="dark"] .ds-root {
--ds-ink: #edf2ee;
--ds-secondary: #b3beb7;
--ds-rule: #414b44;
--ds-control-border: #89968c;
--ds-paper: #191e1b;
--ds-surface: #262e28;
--ds-empty: #39413b;
--ds-selection: #2c4033;
--ds-selection-border: #8cab94;
--ds-preview-panel: #222a24;
--ds-preview-switch: #354038;
--ds-error: #ffacac;
}
.ds-header-actions { display: flex; align-items: center; justify-content: flex-end; gap: 20px; flex-wrap: wrap; }
/* Native radios keep arrow-key selection while presenting one compact control. */
.ds-theme-control { display: inline-flex; flex: 0 0 auto; width: fit-content; padding: 4px; gap: 2px; border: 1px solid var(--ds-rule); border-radius: 12px; background: var(--ds-surface); @apply type-small; }
.ds-theme-option { position: relative; cursor: pointer; }
.ds-theme-option input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.ds-theme-option > span { display: flex; align-items: center; justify-content: center; gap: 7px; min-height: 36px; padding: 0 11px; border: 1px solid transparent; border-radius: 8px; color: var(--ds-secondary); white-space: nowrap; transition: background-color 120ms ease, color 120ms ease; }
.ds-theme-option svg { flex: 0 0 auto; }
.ds-theme-option:hover > span { color: var(--ds-ink); background: var(--ds-selection); }
.ds-theme-option input:checked + span { color: var(--ds-ink); background: var(--ds-paper); border-color: var(--ds-rule); box-shadow: 0 1px 3px rgb(0 0 0 / 8%); }
.ds-theme-option input:focus-visible + span { outline: 2px solid var(--ds-ink); outline-offset: 2px; }
.ds-theme-option input:disabled + span { opacity: .45; cursor: not-allowed; }
@media (prefers-reduced-motion: reduce) { .ds-theme-option > span { transition: none; } }
@media (max-width: 600px) { .ds-header { flex-wrap: wrap; } .ds-header-actions { gap: 12px; } }