Detent web components
By Shane Logsdon

Navigation

Theme Switcher

Implemented

Light, dark or system, remembered

detent-theme-switcher

Port status
Catalog status
implemented
Origin
native-only
Phase
6
Wave
native-only
Group
Navigation

4.5 KB gzip for the class module

Variants

Example control: choose Light, Dark or System. This demo scopes the result to the preview panel with the property-only target, so it never overrides the site's own theme choice; without a target the element applies color-scheme and data-detent-theme to document.documentElement. The choice persists under storage-key and is announced politely. System follows prefers-color-scheme live. One detent-theme-change event fires per user change; a programmatic value write emits nothing.

Recorded states

  • checked recorded state; no setter
  • focus-visible recorded state; no setter
  • invalid recorded state; no setter

Usage

Import

import '/components/theme-switcher/define.js';

// or register it yourself
import { DetentThemeSwitcher } from '/components/theme-switcher/theme-switcher.js';

Attributes

  • value
  • label
  • storage-key
  • persist

Properties

  • value
  • label
  • storageKey
  • persist
  • target
  • resolved

Slots

No slots are recorded for this component.

Events

  • detent-theme-change — { theme: 'light' | 'dark' | 'system'; resolved: 'light' | 'dark' }

Example

Runnable example: site/examples/theme-switcher.ts. The snippet below is generated from the catalogued attributes.

<detent-theme-switcher
  value="system"
  label="Color theme"
  storage-key="detent-theme"
  persist
></detent-theme-switcher>

Property-only values

const element = document.querySelector('detent-theme-switcher');
if (element !== null) {
  // target: HTMLElement
  element.target = undefined;
  // resolved: 'light' | 'dark'
  element.resolved = undefined;
}

Source

Native-only row: there is no upstream source. The native module lives at:

  • src/components/theme-switcher/theme-switcher.ts

Native files

The native module and its artifacts:

  • src/components/theme-switcher/theme-switcher.ts
  • src/components/theme-switcher/styles.css
  • src/components/theme-switcher/define.ts
  • docs/components/theme-switcher.md
  • site/examples/theme-switcher.ts
  • tests/browser/theme-switcher.spec.ts
  • evidence/components/theme-switcher.md

API

Boolean attributes follow HTML presence semantics: present means true, absent means false, and disabled="false" still means disabled.

Attributes

Attributes
AttributePropertyTypeDefaultNotes
valuevalue'light' | 'dark' | 'system''system'Kebab-case attribute; camelCase property.
labellabelstring'Color theme'Kebab-case attribute; camelCase property.
storage-keystorageKeystring'detent-theme'Kebab-case attribute; camelCase property.
persistpersistbooleantruePresence semantics: present means true, absent means false.

Properties

Properties
PropertyTypeDefaultNotes
value'light' | 'dark' | 'system''system'Property-only: assign from script; not accepted as an attribute.
labelstring'Color theme'Property-only: assign from script; not accepted as an attribute.
storageKeystring'detent-theme'Property-only: assign from script; not accepted as an attribute.
persistbooleantrueProperty-only: assign from script; not accepted as an attribute.
targetHTMLElementdocument.documentElementProperty-only: assign from script; not accepted as an attribute.
resolved'light' | 'dark'from matchMediaProperty-only: assign from script; not accepted as an attribute.

Events

Events
EventUpstream propPayload typeNotes
detent-theme-change(native)(theme, resolved) => voidComposed, bubbles; fires only for a user action.

Slots

No slots are recorded for this component.

CSS customisation

  • Native-only row: no upstream source file, no upstream route (docs/migration/native-only.md).
  • Three-way choice with persistence; the set only has binary toggles.
  • Persistence is best-effort: storage failures fall back to in-memory state without throwing.
  • Applies color-scheme plus data-detent-theme="light|dark" carrying the resolved theme; the chosen value stays on the element value attribute, so no third attribute is invented.
  • A stored choice is restored only when no explicit value was supplied; an explicit value always wins.
  • An invalid value resolves to system, sets aria-invalid together with data-invalid (the recorded invalid state) and logs one diagnostic.
  • Target state written by the element is removed from a previous target when target changes, and deliberately left in place on disconnection to avoid a palette flash.

What it does

Rationale. A three-way theme choice with persistence, a live system mode, a radiogroup keyboard model and a polite announcement of the resolved theme.

Upstream behaviour
  • Three explicit choices: light, dark and system
  • Applies color-scheme and a data attribute to the target element
  • Persists the choice under storage-key and restores it on connect
  • System mode follows prefers-color-scheme live
  • Radiogroup keyboard model with arrows, Home and End
  • Announces the resolved theme in a polite live region
  • One event per user change, none for programmatic writes, reduced motion honoured
Differences from upstream
  • Native-only row: no upstream source file, no upstream route (docs/migration/native-only.md).
  • Three-way choice with persistence; the set only has binary toggles.
  • Persistence is best-effort: storage failures fall back to in-memory state without throwing.
  • Applies color-scheme plus data-detent-theme="light|dark" carrying the resolved theme; the chosen value stays on the element value attribute, so no third attribute is invented.
  • A stored choice is restored only when no explicit value was supplied; an explicit value always wins.
  • An invalid value resolves to system, sets aria-invalid together with data-invalid (the recorded invalid state) and logs one diagnostic.
  • Target state written by the element is removed from a previous target when target changes, and deliberately left in place on disconnection to avoid a palette flash.
Previous: Wallet Card Next: Docs TOC

Docs

Components

Project