@lovince/themes
Light and dark themes for Lovince UI. A theme maps the primitives in
@lovince/tokens onto semantic roles (surface, foreground, line,
accent, danger…), so components ask for what a colour is for, never which
colour it is.
Status:
0.3.0, experimental. Role names may change before1.0.
bun add @lovince/themes @lovince/tokens
Use it
Plain CSS:
@import '@lovince/tokens/tokens.css';
@import '@lovince/themes/themes.css';
.card {
background: var(--lovince-color-surface-raised);
color: var(--lovince-color-fg);
border: 1px solid var(--lovince-color-line);
box-shadow: var(--lovince-shadow-md);
}
Tailwind v4. This brings in both sheets, a Tailwind theme, base styles,
enter/exit animations, and a dark: variant that follows [data-theme]:
@import 'tailwindcss';
@import '@lovince/themes/tailwind.css';
Utilities use the shadcn vocabulary (bg-primary, text-muted-foreground,
border-input, ring-ring) mapped onto Lovince roles, so shadcn-style
components are themed with no edits. Type is by role: text-title-md,
text-body-sm.
TypeScript:
import { themes, SEMANTIC_COLORS } from '@lovince/themes'
Switching themes
Light is the default. prefers-color-scheme: dark switches automatically unless
data-theme="light" is set. Set data-theme="dark" on <html>, or on any
subtree, to force it. Derived values (hover tints, shadows, focus ring) are
re-resolved inside each [data-theme] subtree, so a dark panel on a light page
is fully dark.
Guarantees
- Every role is defined by every theme.
- WCAG AA contrast is tested for every text/surface pairing in both themes: 4.5:1 for text, 3:1 for focus rings and borders.
dangeris a fill anddanger-inkis for text and borders, because no single red passes AA in both roles on dark surfaces.- The theme sheet holds no raw colours, only references to tokens.
Editing
The source is src/. css/themes.css and css/tailwind.css are generated:
run bun run generate from the repository root.
License
MIT