CSS tokens and a styled component catalog for Askr apps.
@askrjs/themes is the visual companion to @askrjs/ui and
@askrjs/charts. It owns the default theme and styled component catalog while
behavior stays in @askrjs/ui and chart components stay in @askrjs/charts.
npm install @askrjs/themes @askrjs/uiImport the default theme CSS in your app stylesheet:
@import "@askrjs/themes/default";Add the optional cat preset layer after the default theme when you want the curated preset family:
@import "@askrjs/themes/default";
@import "@askrjs/themes/presets";For a small page that only uses individual controls, import their JavaScript and CSS independently instead of the full default theme:
import { Input } from "@askrjs/themes/input";
import { Label } from "@askrjs/themes/label";
import "@askrjs/themes/default/foundations.css";
import "@askrjs/themes/default/input.css";
import "@askrjs/themes/default/label.css";The foundations entry contains tokens and base/reset styles. Component CSS
entries contain only that component's styles. @askrjs/themes/default remains
the batteries-included theme.
See Acknowledgements for the open-source projects that inspired parts of the design philosophy.
For documentation search and command launchers, use the accessible
CommandPalette composition from @askrjs/themes/command; it owns themed
presentation while @askrjs/ui supplies dialog focus and dismissal behavior.
Then set data-theme to tabby, ginger, tuxedo, calico, or torty.
For picker/toggle composition, import CAT_THEME_OPTIONS and CAT_THEME_NAMES
from @askrjs/themes/theme.
Then use the theme helpers and component catalog:
import { ThemeScope, ThemeToggle } from "@askrjs/themes/theme";
import { Button, ButtonGroup, Field, Input, InputGroup, Label } from "@askrjs/themes/components";
export function AppShell() {
return (
<ThemeScope>
<ButtonGroup>
<Button variant="primary">Save</Button>
<ThemeToggle>{({ nextTheme }) => nextTheme}</ThemeToggle>
</ButtonGroup>
<Field>
<Label for="workspace">Workspace</Label>
<InputGroup>
<Input id="workspace" name="workspace" />
</InputGroup>
</Field>
</ThemeScope>
);
}Layout props on Block, Container, Grid, AspectRatio, and Skeleton
produce CSP-compatible generated rules. Wrap the Askr document renderer so
those rules are serialized into the initial document and adopted during
hydration:
import type { DocumentRenderArgs } from "@askrjs/askr/ssg";
import { withThemeStyles } from "@askrjs/themes/ssr";
function renderDocument({ appHtml }: DocumentRenderArgs) {
return `<!doctype html><html><head></head><body><div id="app">${appHtml}</div></body></html>`;
}
export const staticConfig = {
// ...
document: withThemeStyles(renderDocument),
};The wrapper also applies context.cspNonce to the emitted style registry and
requires the request-local style registrations provided by Askr 0.0.85 or
newer. It fails clearly if generated classes and their registered rules ever
diverge instead of emitting unstyled markup. Use the same wrapper for an SSR
document callback.
@askrjs/themes/componentsfor the styled component catalog.@askrjs/themes/<component>for package subpaths such as@askrjs/themes/button,@askrjs/themes/card, and@askrjs/themes/dialog.@askrjs/themes/themeforThemeScope,ThemePicker,ThemeToggle, andtheme.@askrjs/themes/ssrfor the SSR/SSG generated-style document wrapper.@askrjs/chartsfor charts; chart components are intentionally not exported from@askrjs/themes.
DataTable, ResizablePanelGroup, ResizablePanel, and ResizableHandle
are styling-only catalog compatibility wrappers. Their names do not promise a
data-grid or panel-resizing runtime:
DataTableprovides adata-tablecontainer slot. It does not sort, filter, select, or paginate. Compose semanticTableorVirtualTableprimitives from@askrjs/uiand keep data operations in application-owned state.ResizablePanelGroup,ResizablePanel, andResizableHandleprovide layout and separator slots only. The group does not implement pointer or keyboard resizing, track dimensions, or provide the ARIA value state required by an interactive splitter.
These names remain exported for catalog compatibility. If an application needs
real resize behavior today, supply the pointer, keyboard, sizing, and ARIA
contract in application code or a dedicated behavior dependency. A future Askr
resize primitive must be implemented and tested in @askrjs/ui before themes
can compose and style it.
-
Style public
data-*hooks and token variables, not internal DOM structure. -
Prefer token overrides before component overrides.
-
Keep selectors low specificity so downstream apps can customize them cleanly.
-
Use THEMING.md and docs/architecture.md for the full contract and package boundaries.
-
Use docs/component-anatomy.md for stable slot hooks and docs/customization.md for the KISS customization path.
-
Use docs/recipes.md for copyable login, admin shell, settings form, table, dropdown, and detail-page patterns.
-
Use
visual-check.htmlfor manual QA across light and dark modes.