On this page
Usage
import { Theme, Text, useTheme } from '@envsemble/envui';
function ThemeLabel() {
const { theme, contrast } = useTheme();
return (
<Text>
Current theme: {theme}, {contrast} contrast
</Text>
);
}
<Theme theme="dark" contrast="high">
<ThemeLabel />
</Theme>;
| Name | Takes |
|---|---|
| Theme theme | Required: light or dark; scopes the tokens and provides the theme to React |
| Theme contrast | high; standard, even when the system asks for more; or system, the default, which follows prefers-contrast: more; inherited from the enclosing Theme |
| Theme scale | system, comfortable or compact, the app's scale; inherited from the enclosing Theme |
| useTheme() | { theme, contrast, scale } of the nearest Theme, also across envUI's portals |
| ThemePreferenceProvider | Stores the preference, follows the system and other windows, and themes the document root; see setup |
| useThemePreference() | A ThemePreferenceValue: preference (light, dark or system), the resolved theme, setPreference and toggleTheme |
| ThemeToggle | An IconButton that switches between light and dark |
The words of the figures
The Design tokens page of the Docs lists every value in each theme, and this guide ends with the palette, which shows each role's value in the theme you are reading in and lets you try another.
- Page:
--env-color-bg-page, the document's background, behind everything: below the panes and cards on it. - Recessed surface:
--env-color-bg-recessed, a well on the page,background="recessed", such as the stage of a preview: below the panes, and in dark a shade deeper than the page too; in light, where nothing deeper than the page keeps edges at 3:1, between the page and the panes. - Surface:
--env-color-bg-default: Surface'sbackground="default", panes, the navigation and sheets, a step lighter than the page. - Raised surface:
--env-color-bg-raised: tables, docked panels andbackground="raised", a step above the panes with the filled card's fill: white in light. - Filled card:
--env-color-bg-muted: a group inside a surface, a Card's fill,background="muted", list rows and tab rails, a step above the surface: white in light. - Text:
--env-color-fg-default, Text's default, for headings and the main words. - Supporting text:
--env-color-fg-muted:emphasis="muted"on Text, details, captions, hints and icons. - Primary:
--env-color-bg-primary-emphasis, the main action's fill;--env-color-fg-primaryandfg-linkin text, such as links and what is selected. - Edges:
--env-color-border-default, the outline of fields and outline buttons, which have no fill. - Selected:
--env-color-bg-selectedwith--env-color-fg-on-selected.
Status tones
Status icons, short status text and badges share these tokens, at least 4.5:1 on the page, surfaces and rows in both themes, and 7:1 at high contrast.
| Token | Meaning |
|---|---|
--env-color-fg-muted | Neutral: no particular state, such as archived, and most actions |
--env-color-fg-primary | Primary: what is current or highlighted |
--env-color-fg-accent | Accent: what stands out, in purple, such as a featured item |
--env-color-bg-info-strong | Info, as a strong fill: something to know, neither good nor bad |
--env-color-fg-success | Success: healthy, ready or saved |
--env-color-fg-warning | Warning: needs attention soon |
--env-color-fg-danger | Danger: failed, or an action that destroys something |
--env-color-fg-done | Done: completed work |
--env-color-bg-{tone}-muted | The tone's tint behind badges and alerts |
--env-color-bg-{tone}-emphasis | A solid fill in the tone, with fg-on-emphasis |
--env-color-fg-{tone}-on-strong | A toast's tone icon on its dark fill: success, info or danger |
.work-status[data-status='blocked'] {
color: var(--env-color-fg-warning);
}
Composition
Semantic tokens
Tokens are named --env-color-{bg|fg|border}-{role}[-{emphasis}][-{state}], emphasis weakest first: subtle, muted, none, emphasis and strong. Every fg-on-X pairs with a bg-X, every state token has its resting token, and the contrast gate measures every pair a component paints, in all four themes.
- Backgrounds:
bg-pageandbg-recessed, a well, below the surfaces;bg-default, andbg-mutedandbg-raiseda step above it, the surfaces;bg-popup, the fill of dialogs, menus and popovers, frosted or solid, white in light and in dark a step lighter than the surfaces they open over;bg-field,bg-selected;bg-backdrop, the backdrop behind dialogs. - Fills:
bg-neutral-muted, a translucent layer of the text color under secondary buttons, chips, switch tracks and skeletons;bg-neutral-subtle, the state layer of controls without a fill, such as ghost and outline buttons;emphasisandstrongsolid fills. - Text:
fg-default,fg-mutedfor supporting text and icons,fg-disabledfor the label and value of a disabled control,fg-control,fg-link,fg-on-selected, and one per tone. - Borders:
border-default, the only boundary of a control, at least 3:1;border-mutedfor the hairlines between a list's items, quote bars and other decorative edges;border-focus;border-media, the inner edge of images.
The colors come from an internal palette of eleven hues, and the pastel hues of avatars, in grades from 0 to 100, where the grade difference alone sets the contrast: 50 grades apart give at least 4.5:1. The light theme takes a nearly neutral gray, the dark theme a slightly blue-tinted one.
States and contrast
- Hover and pressed are tokens of their own, other grades of the same hue, so labels keep their contrast in every state. A component given colors of its own through its color properties draws its states as state layers instead: the content color over the container at
--env-state-hover-opacity,--env-state-focus-opacityand--env-state-pressed-opacity. - Selection stays apart from every other item, at rest and under the pointer, and carries a second cue: its label color, a check or a bar. Tree and table rows hover with a layer of their own,
--env-color-bg-row-hover, quieter than the selection, so the selected row always reads strongest. - High contrast moves text to 7:1 and boundaries, indicators and focus rings to 4.5:1. A nested Theme and envUI's popups take the contrast and theme of where they open.
- Elevation 0โ3 is an edge, shown only where a surface is solid: popups float at 2, dialogs, sheets and the command palette at 3, and at high contrast a solid popup takes level 3's, the strongest. Elsewhere a popup or a dialog separates by its fill and a soft shadow, a sheet by its fill, and a modal by its backdrop too.
- Small popups are frosted: menus, lists, popovers, pickers, hover cards, tooltips and the command palette let a little of what is behind them through, blurred, with a soft shadow and no edge; a tooltip's strong fill covers a little less and its shadow is smaller. Dialogs take the popups' fill and shadow, solid over their backdrop. At high contrast and with reduced transparency all are solid, with their edge.
- Shadows: a popup's, which dialogs share, a tooltip's and an avatar's faint one, which parts the avatars of a group, are envUI's only shadows.
- Sheets keep the navigation's fill, so navigation that opens in one looks as it does at the window's edge, and have no edge but at high contrast.
- Hairlines separate a list's items, never regions: Markdown rules, table rows and accordion items take a hairline across their container's full width, never inset from its edges. Regions, such as a top bar, a footer, a sticky header, a tab bar and two panes, separate by space and background instead; controls keep their edges. A popup or a dialog has none: a popup's groups part by space and a quiet label, a dialog's heading, content and actions by space.
- Forced colors take system colors, such as
HighlightandGrayText, for every state a fill carried.
Guidelines
- Choose the role for what an element is, not the color it should have:
fg-mutedfor any supporting text,border-mutedfor the line between a list's items. Separate regions by space or a background, not a line. - Use one role in both themes; the themes give it its grade.
- Disabled controls take 0.48 opacity, which WCAG exempts from contrast.
- Wrap the application's own portal content in
<Theme {...useTheme()}>, so it takes the theme and contrast of where it opens.
Customize
Each component has public color properties, such as --env-button-container, for colors of its own on one instance. A nested <Theme> whose style sets the semantic tokens for each theme, such as --env-color-bg-primary-emphasis with its hover and pressed tokens, gives an area a color scheme of its own, such as a brand's. Customize and extend describes both.
TypeScript declarations provide the complete public API.
Words in code
| Words | In code |
|---|---|
| A quieter surface | background="page" on a Surface or Card, so it parts from the page by space rather than a fill |
| Success or warning | tone on Badge, Alert, Toast, Button and the components that take one: Badge takes neutral, primary, accent, success, warning, danger and done; Alert info, success, warning and danger; Toast neutral, info, success and danger; Button neutral, accent and danger. In your own CSS, --env-color-fg-{tone} and their tints |
| Its own color | The component's color properties, such as --env-button-container; most components have them, listed on each one's page in the Docs under Color properties; see Customize and extend |
| A dark area | A nested <Theme theme="dark">, or light on a dark page |
| High contrast | contrast="high" on Theme, or system, the default, which follows the reader's system |
Try the palette
Each swatch shows a token's value in the theme you are reading in. Press one to pick another color: the whole catalogue follows, in this theme only, until you reload.
Page & surfaces
--env-color-bg-page--env-color-bg-recessed--env-color-bg-default--env-color-bg-popup--env-color-bg-muted--env-color-bg-raisedText & focus
--env-color-fg-default--env-color-fg-muted--env-color-fg-muted--env-color-fg-muted--env-color-fg-link--env-color-border-focus--env-color-border-mutedCards
--env-color-bg-defaultRows
--env-color-bg-list-item--env-color-bg-raised--env-color-fg-default--env-color-fg-mutedButtons
--env-color-bg-primary-emphasis--env-color-fg-on-emphasis--env-color-bg-neutral-muted--env-color-fg-control--env-color-bg-raised--env-color-fg-primary--env-color-border-default--env-color-bg-accent-strong--env-color-fg-on-strong--env-color-bg-danger-strong--env-color-fg-on-strongNavigation
--env-color-fg-control--env-color-fg-muted--env-color-bg-selected--env-color-fg-on-selectedDirection links
--env-color-bg-raised--env-color-fg-default--env-color-fg-muted--env-color-fg-primary--env-color-border-mutedText fields
--env-color-bg-field--env-color-fg-default--env-color-fg-muted--env-color-fg-muted--env-color-fg-muted--env-color-border-default--env-color-border-primary-emphasis--env-color-fg-dangerSelect options
--env-color-bg-popup--env-color-fg-control--env-color-bg-selected--env-color-fg-on-selectedRadio
--env-color-bg-primary-emphasis--env-color-bg-muted--env-color-fg-default--env-color-fg-mutedCheckboxes
--env-color-bg-primary-emphasis--env-color-fg-on-emphasis--env-color-fg-muted--env-color-fg-defaultSwitches
--env-color-bg-neutral-muted--env-color-bg-primary-emphasis--env-color-fg-control--env-color-fg-on-emphasis--env-color-fg-defaultFeedback
--env-color-bg-neutral-muted--env-color-fg-primary--env-color-fg-muted--env-color-bg-neutral-strong--env-color-fg-success-on-strong--env-color-fg-info-on-strong--env-color-fg-danger-on-strong--env-color-fg-on-strong--env-color-bg-backdropContent & loading
--env-color-border-muted--env-color-bg-neutral-muted