first commit
This commit is contained in:
75
docs/ui/react/react-ui/src/theme/themes/dark.md
Normal file
75
docs/ui/react/react-ui/src/theme/themes/dark.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# `darkTheme` — pure inversion of `lightTheme`
|
||||
|
||||
## Purpose
|
||||
|
||||
The dark counterpart of `lightTheme`. Same variant surface, same token
|
||||
names, lightness anchors inverted so foreground reads on backgrounds
|
||||
and vice versa. Ships as one of the three built-in themes for the
|
||||
chassis so consumers can offer a light/dark toggle out-of-the-box
|
||||
without authoring any tokens themselves.
|
||||
|
||||
## Features
|
||||
|
||||
- Inverted lightness anchors: white-ish foreground on near-black
|
||||
background, secondary / muted ramped through `oklch(0.269 0 0)`
|
||||
- WCAG AA at default sizes (foreground vs background contrast > 4.5:1)
|
||||
- `colorScheme: 'dark'` so the chassis FOUC-prevention script applies
|
||||
`data-theme="dark"` when `prefers-color-scheme: dark` is the system
|
||||
default and the user has not overridden via the storage key
|
||||
- Same `respectReducedMotion: 'strict'` policy as `lightTheme`
|
||||
|
||||
## Usage: Basic example
|
||||
|
||||
```tsx
|
||||
import { ReactUiProvider } from "@suaveplan/react-ui/provider";
|
||||
import { darkTheme } from "@suaveplan/react-ui/theme/themes/dark";
|
||||
|
||||
<ReactUiProvider
|
||||
i18n={i18n}
|
||||
themes={[darkTheme]}
|
||||
defaultTheme="dark"
|
||||
>
|
||||
<YourApp />
|
||||
</ReactUiProvider>;
|
||||
```
|
||||
|
||||
## Usage: Advanced example
|
||||
|
||||
```tsx
|
||||
import { lightTheme, darkTheme } from "@suaveplan/react-ui/theme";
|
||||
|
||||
// Pair with the matching `<ThemeSwitcher>` so users can toggle:
|
||||
import { ThemeSwitcher } from "@suaveplan/react-ui/theme/theme-switcher";
|
||||
|
||||
<ReactUiProvider themes={[lightTheme, darkTheme]} defaultTheme="dark" i18n={i18n}>
|
||||
<ThemeSwitcher>
|
||||
{({ themes, current, setTheme }) => (
|
||||
<select
|
||||
value={current.name}
|
||||
onChange={(e) => setTheme(e.target.value)}
|
||||
>
|
||||
{themes.map((t) => (
|
||||
<option key={t.name} value={t.name}>
|
||||
{t.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
</ThemeSwitcher>
|
||||
</ReactUiProvider>;
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `darkTheme: ThemePlugin<'dark'>` — the named export.
|
||||
- Same shape as `lightTheme`; only the lightness anchors differ.
|
||||
|
||||
## Implementation notes
|
||||
|
||||
The implementation deliberately avoids deriving the dark tokens from
|
||||
the light tokens at runtime — that would couple the two themes and
|
||||
make per-token tweaks awkward. Each theme is a hand-written object
|
||||
literal so a designer can adjust dark-only contrast without leaking
|
||||
into light's palette. The variant-key parity with `lightTheme` is
|
||||
asserted by `themes.test.ts` so a future drift in either theme fails
|
||||
CI before it ships.
|
||||
75
docs/ui/react/react-ui/src/theme/themes/high-contrast.md
Normal file
75
docs/ui/react/react-ui/src/theme/themes/high-contrast.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# `highContrastTheme` — WCAG AAA-compliant theme
|
||||
|
||||
## Purpose
|
||||
|
||||
Accessibility-first theme that meets WCAG AAA contrast for normal and
|
||||
large text alike. Pure-white foreground (`#FFFFFF`) on a pure-black
|
||||
background (`#000000`) gives a 21:1 contrast ratio — three times the
|
||||
AAA threshold for normal text. Borders are intentionally rendered in
|
||||
the same `#FFFFFF` foreground colour so every interactive surface has
|
||||
a clear visual boundary. Motion is collapsed to `0ms` regardless of
|
||||
the `prefers-reduced-motion` setting since users on this theme have
|
||||
explicitly opted out of decorative motion.
|
||||
|
||||
## Features
|
||||
|
||||
- Foreground / background WCAG AAA contrast (21:1)
|
||||
- `accent: '#FFFF00'` (saturated yellow) and `ring: '#FFFF00'` so focus
|
||||
rings are unmissable on every surface
|
||||
- `destructive: '#FF0000'` (pure red) for unambiguous danger semantics
|
||||
- All motion-duration tokens fixed at `0ms`; easings collapsed to
|
||||
`linear` so consumers wiring a custom CSS layer cannot accidentally
|
||||
re-introduce motion via the theme tokens
|
||||
|
||||
## Usage: Basic example
|
||||
|
||||
```tsx
|
||||
import { ReactUiProvider } from "@suaveplan/react-ui/provider";
|
||||
import { highContrastTheme } from "@suaveplan/react-ui/theme/themes/high-contrast";
|
||||
|
||||
<ReactUiProvider
|
||||
i18n={i18n}
|
||||
themes={[highContrastTheme]}
|
||||
defaultTheme="high-contrast"
|
||||
>
|
||||
<YourApp />
|
||||
</ReactUiProvider>;
|
||||
```
|
||||
|
||||
## Usage: Advanced example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
darkTheme,
|
||||
highContrastTheme,
|
||||
lightTheme,
|
||||
} from "@suaveplan/react-ui/theme";
|
||||
|
||||
// Offer high-contrast as a third option alongside light/dark:
|
||||
<ReactUiProvider
|
||||
i18n={i18n}
|
||||
themes={[lightTheme, darkTheme, highContrastTheme]}
|
||||
defaultTheme="light"
|
||||
>
|
||||
<YourApp />
|
||||
</ReactUiProvider>;
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `highContrastTheme: ThemePlugin<'high-contrast'>` — the named export.
|
||||
- Same `cssVariables` shape as `lightTheme` and `darkTheme`; values
|
||||
are tuned for WCAG AAA.
|
||||
- `motionTokens`: `durationFast / durationBase / durationSlow` all set
|
||||
to `0ms`; easings to `linear`.
|
||||
|
||||
## Implementation notes
|
||||
|
||||
The colour values are intentionally named hex strings rather than OKLCH
|
||||
expressions so consumers debugging the rendered DOM can copy-paste the
|
||||
value into a colour picker without an OKLCH-to-hex conversion. Using
|
||||
`#FFFFFF` for the border lets the accent ring contrast against the
|
||||
border ring without any additional ring-offset tokens. The 21:1 ratio
|
||||
gives the test suite a comfortable margin above the 7:1 AAA threshold
|
||||
so contrast assertions remain green even if the chassis is later
|
||||
re-tuned for a slightly off-white foreground.
|
||||
78
docs/ui/react/react-ui/src/theme/themes/light.md
Normal file
78
docs/ui/react/react-ui/src/theme/themes/light.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# `lightTheme` — chassis-default light theme
|
||||
|
||||
## Purpose
|
||||
|
||||
The default `ThemePlugin` instance shipped with `@suaveplan/react-ui`.
|
||||
`lightTheme` matches the shadcn `nova` token preset: neutral OKLCH
|
||||
greyscale anchored on a near-white background and a near-black
|
||||
foreground. The chassis registers it as the first theme handed to
|
||||
`<ThemeProvider>` so apps that do not override the theme registry get
|
||||
a sensible visual default.
|
||||
|
||||
## Features
|
||||
|
||||
- 20 CSS-variable tokens covering background / foreground, card,
|
||||
popover, primary, secondary, muted, accent, destructive plus the
|
||||
border / input / ring / radius palette
|
||||
- Motion tokens (`durationFast` 120ms, `durationBase` 200ms,
|
||||
`durationSlow` 320ms) with `respectReducedMotion: 'strict'` so users
|
||||
who set `prefers-reduced-motion: reduce` see instant transitions
|
||||
- `labelKey: 'react-ui.themeToggle.light'` resolves through the active
|
||||
i18n instance — `<ThemeSwitcher>` never renders a literal
|
||||
- `colorScheme: 'light'` paired with the chassis `<ThemeProvider>`'s
|
||||
inline FOUC-prevention script so pre-hydration `<html>` already
|
||||
carries `data-theme="light"`
|
||||
|
||||
## Usage: Basic example
|
||||
|
||||
```tsx
|
||||
import { ReactUiProvider } from "@suaveplan/react-ui/provider";
|
||||
import { lightTheme } from "@suaveplan/react-ui/theme/themes/light";
|
||||
|
||||
export function App() {
|
||||
return (
|
||||
<ReactUiProvider i18n={i18n} themes={[lightTheme]} defaultTheme="light">
|
||||
<YourApp />
|
||||
</ReactUiProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced example
|
||||
|
||||
```tsx
|
||||
import {
|
||||
darkTheme,
|
||||
highContrastTheme,
|
||||
lightTheme,
|
||||
} from "@suaveplan/react-ui/theme";
|
||||
|
||||
const themes = [lightTheme, darkTheme, highContrastTheme];
|
||||
|
||||
<ReactUiProvider
|
||||
i18n={i18n}
|
||||
themes={themes}
|
||||
defaultTheme="light"
|
||||
>
|
||||
<YourApp />
|
||||
</ReactUiProvider>;
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `lightTheme: ThemePlugin<'light'>` — the named export.
|
||||
- `lightTheme.name === 'light'` — literal-typed so narrowed
|
||||
`useThemedVariants<'light', 'Button'>('Button')` calls compile.
|
||||
- `lightTheme.cssVariables` — the 20 OKLCH tokens.
|
||||
- `lightTheme.motionTokens` — six motion tokens + the policy.
|
||||
|
||||
## Implementation notes
|
||||
|
||||
The OKLCH expressions are forwarded to consumers' Tailwind v4 layers
|
||||
via the chassis `<ThemeProvider>`'s inline `style.setProperty()` calls.
|
||||
Consumers wiring their own Tailwind layer mirror the token names in
|
||||
their `@theme inline` block (`--background`, `--foreground`, etc.).
|
||||
The `radius` token is intentionally a single shared anchor (`0.5rem`)
|
||||
that downstream cva configs scale via Tailwind's `calc()` arithmetic;
|
||||
themes that want a different rounding per variant override at the cva
|
||||
config level rather than emitting per-variant tokens here.
|
||||
Reference in New Issue
Block a user