Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion docs/docs/ui/builders.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,37 @@ ThemePicker(OxygenThemes.graphiteFamilyPacks) // filter to a pack family

Both are self-contained (backed by a shared `GlobalState` seeded from the stored value): they
reflect the current selection and call `ColorMode.setAndPersist` / `Theme.applyAndPersist` on
select. They do not re-highlight on cross-tab changes until the page re-renders.
select.

**`ColorModePicker`** is a first-class, standalone Light/Dark/System control — independent of the
theme-pack machinery (depends only on the `ColorMode` service). It is a config builder (à la
`ToggleThumb` / `HorizontalRadio`):

```scala
ColorModePicker() // segmented radiogroup (default)
ColorModePicker.compact // single icon button that cycles modes (top-bar friendly)
ColorModePicker.segmentedWithIcons // segmented + Light/Dark/System glyphs
ColorModePicker().lightDarkOnly // drop the System option
ColorModePicker().large.label("Theme")
```

- **a11y:** segmented is a `role=radiogroup` with `role=radio` + `aria-checked` per option, roving
`tabindex`, and keyboard nav (Left/Up = prev, Right/Down = next, Home/End = first/last with wrap,
Space/Enter select) that moves selection **and** DOM focus together. Compact is a native
`<button>` with a descriptive `aria-label`/`title`.
- **Live cross-tab highlight (opt-in):** by default the highlight reflects the mode at render time.
Wire `ColorModePicker.syncAcrossTabs` once (e.g. from `prePageLoad`) to have every mounted picker
re-highlight on cross-tab / programmatic `ColorMode` changes:

```scala
override protected def prePageLoad: RIO[Env & Scope, Unit] =
ColorTheme.install *> ColorModePicker.syncAcrossTabs
```

(For labeled *form* contexts, use the generic `HorizontalRadio.form[ColorMode.Mode]` instead — a
dedicated dropdown variant is intentionally omitted.)

`ThemePicker` does not re-highlight on cross-tab changes until the page re-renders.

### Mobile / viewport

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,29 @@ object ThemePage extends RoutablePage.NoParams[Any] {
"plus surface personalities (Aurora / Ember / Violet / Ocean). " +
"Top bar = solid primary + on-primary ink. Light/Dark flips surfaces only.",
),
// mode — reusable picker
// mode — reusable picker, shown in its several variants
h3("Color mode picker", marginBottom := S.spacing._3),
p(
color := S.color.fg.moderate,
fontSize := S.fontSize._2,
marginBottom := S.spacing._4,
"Standalone Light/Dark/System control (independent of theme packs). " +
"Keyboard: arrows / Home / End move + select; Space/Enter select. " +
"All instances share one persisted preference.",
),
div(
display.flex,
flexDirection.column,
gap := S.spacing._4,
marginBottom := S.spacing._5,
ColorModePicker(label = Some("Color mode")),
// default segmented, with a leading caption
ColorModePicker(label = Some("Segmented")),
// segmented with icons
ColorModePicker.segmentedWithIcons.label("With icons"),
// Light/Dark only (no System)
ColorModePicker().lightDarkOnly.label("Light / Dark only"),
// compact icon cycle button (top-bar style)
ColorModePicker.compact.label("Compact"),
),
h3("Oxygen theme packs", marginBottom := S.spacing._3),
p(
Expand Down
Loading
Loading