@nextgensoftwares/folio-plugin-dark-mode
Document colour themes: adaptive dark mode, sepia, high contrast and brand palettes, display only. Guide: Dark mode & colour themes.
Plugin
| Export | Signature | Description |
|---|---|---|
colorThemesPlugin | (options?: ColorThemesOptions) => ColorThemesPlugin | a FolioPlugin with colors (the adapter) plus the theme API below; contributes a settings section (and optionally a toolbar picker) |
darkModePlugin | (options?: DarkModeOptions) => ColorThemesPlugin | the same with mode: 'off' | 'on' | 'system' (off = light, on = dark) |
type ColorThemesOptions | theme? (id or 'system'), themes? (extra themes, e.g. PRESET_THEMES), system?: { light?, dark? }, storage?: ThemeStorage, matchMedia?, settings?: false | { slot?, label?, order? }, toolbar?: boolean | { order? } | |
type DarkModeOptions | ColorThemesOptions with mode instead of theme | |
type DarkMode | 'off' | 'on' | 'system' | |
type ColorThemesPlugin | FolioPlugin & ColorThemesApi & { colors: ColorAdapter } | |
type ColorThemesApi | see below |
ColorThemesApi
| Method | Description |
|---|---|
theme(), setTheme(choice) | the choice: a theme id or 'system' (refuses unknown ids) |
current() | the theme in effect now (system resolved through prefers-color-scheme) |
setSystemThemes({ light?, dark? }) | which pair "follow system" switches between |
defineTheme(def) | add or replace a custom theme; validated like untrusted JSON → ThemeResult |
updateTheme(id, patch) | tweak any theme, built-ins included (stored as patches); null clears a field |
resetTheme(id), isCustomized(id) | built-ins lose their tweaks; host presets return to their definition |
removeTheme(id) | custom themes only; a removed choice falls back to light |
themes() | built-ins (with tweaks) then custom themes |
exportThemes(), importThemes(json) | JSON round trip; imports are validated → ThemeResult & { imported } |
state() | the persisted ThemeState |
mode(), setMode(mode) | dark-mode shorthand |
adapter() | the compiled ThemeAdapter for the theme in effect |
subscribe(fn), version() | change notifications (OS scheme flips included while subscribed); version is a snapshot counter |
| Type | Description |
|---|---|
ThemeResult | { ok: boolean; errors: string[] } |
ColorThemeDef | { id, label?, base: 'light' | 'dark', page?, text?, link?, codeBackground?, codeText?, tableBorder?, selection?, caret?, highlightMap?, map?, contrastMin?, chromaScale?, lightnessCurve?, mediaFilter? } |
ThemeChoice | 'system' or a theme id |
ThemeState | { v: 1, theme, system: { light, dark }, themes: ColorThemeDef[], patches: Record<id, Partial<ColorThemeDef>> } |
Themes
| Export | Description |
|---|---|
BUILTIN_THEMES | light (pass-through until tweaked) and dark (adaptive) |
PRESET_THEMES | sepia, night-blue, high-contrast, ready to register |
BASE_DEFAULTS | per base: page, text, contrastMin, chromaScale, lightnessCurve, mediaFilter used for every field a theme leaves out |
sanitizeTheme(input, errors?) | untrusted input → ColorThemeDef | null (unknown keys dropped, colours normalized, numbers clamped, filters restricted) |
sanitizeState(input, errors?) | a stored/exported state (string or object) → Partial<ThemeState> |
THEME_LIMITS | { themes: 50, mapEntries: 256, label: 60, json: 262144, filter: 200 } |
localStorageThemes(key?, storage?), ThemeStorage | guarded localStorage persistence ({ load(), save(state) }) |
systemScheme(matchMedia?), MatchMedia, MediaQueryLike | prefers-color-scheme, watched only while someone listens |
Algorithm
| Export | Description |
|---|---|
createThemeAdapter(def) | compile a theme → ThemeAdapter |
ThemeAdapter | { palette, adapt(color, role), adaptOn(color, role, background), cacheSize() }, cached per (role, colour) |
compileTheme(def), Palette | the numbers a theme turns into (paper, ink, ramp end, contrast targets) |
contrastOf(fg, bg) | WCAG ratio of two CSS colours, or null |
contrastRatio(y1, y2), luminance(rgba) | WCAG 2.x helpers |
parseColor(css), Rgba | hex 3/4/6/8, rgb[a](), hsl[a]() (legacy and modern syntax), named colours, transparent → { r, g, b, a } or null |
formatHex(rgba), normalizeColor(css) | #rrggbb / #rrggbbaa; parsing the output gives the same colour |
themeContrastReport(def), ContrastCheck | WCAG checks of the combinations readers see (text, greys, table header, links, code, highlights, caret) |
ROLE_FIELDS, RoleField, effectiveColor(adapter, key) | the editable role colours and what each displays as |
React UI
| Export | Description |
|---|---|
ThemePicker, ThemePickerProps | Follow system / every theme, optionally the system light/dark pair |
ThemeEditor | colour inputs per role, knobs, overrides, live preview, contrast warnings, duplicate/reset/delete, import/export |
ThemePreview | a mini page drawn with an adapter |
createThemeSettings(api), createToolbarPicker(api) | the slot components the plugin contributes |
useColorThemes(api) | re-render on theme changes |