open-form theme
List reference themes, check contrast, add a starter, and add Google Fonts.
Themes in your project's themes/ folder are references for new forms: a form
starts from one's tokens with
open-form create <id> --theme <theme>, and its own look
lives in forms/<id>/theme/. See Themes.
open-form theme has four subcommands.
theme list
npx open-form theme listLists the project's themes with the one-line description from each THEME.md,
and how to start a form from one. Then it lists the starters: brutalist,
classic, conversational, editorial and terminal.
theme check
npx open-form theme check <id>
npx open-form theme check <form> --formChecks the WCAG contrast of every color pair forms actually use, in light and dark:
| Pair | Needs |
|---|---|
foreground on surface, on background and on input | 4.5:1 |
muted help text on surface | 4.5:1 |
primaryForeground on primary (button text) | 4.5:1 |
error on surface | 4.5:1 |
control outlines on input | 3:1 |
primary focus and selection on surface | 3:1 |
A theme with dark: false is checked in light only, and one with colorScheme
only in that scheme. When the theme has its own Layout, a failing
foreground on background is shown as a hint (▲) instead of a failure: the
layout decides whether any text sits straight on background. It also warns when a theme sets colors without dark,
since dark mode would mix your light colors with the built-in dark defaults.
Colors can be hex, rgb(), hsl(), oklch() or oklab(). Values it can't
resolve, such as var() or color-mix(), show as ? and count as failures.
The command exits with code 1 when any pair fails, except a hint (▲); fix it by changing that
token's lightness and keeping its hue.
| Flag | Default | Description |
|---|---|---|
--form | off | <id> is a form id: check that form's own theme in forms/<id>/theme/. |
--json | off | Machine-readable output. |
theme add
npx open-form theme add editorial
npx open-form theme add editorial --as brandCopies a starter's whole folder into themes/<name>/: its tokens, THEME.md,
and any components and CSS it has. It's a reference to adapt into your own
look; new forms start from its tokens with create --theme, and agents read
the rest while writing a form's look. Editing it later never changes an
existing form.
| Flag | Default | Description |
|---|---|---|
--as <name> | the starter's id | Save it under a new kebab-case id. [data-of-theme="<starter>"] selectors and the name and heading in THEME.md are renamed to match; file names and class names stay as they are. |
-f, --force | off | Replace an existing theme with that id. |
theme font
npx open-form theme font brand Inter
npx open-form theme font brand Noto Sans TC
npx open-form theme font meetup Inter --formDownloads a Google Fonts family into the fonts/
folder next to the theme's file — themes/<id>/fonts/, or
forms/<id>/theme/fonts/ with --form — and imports it from the theme. A
single-file theme, themes/<id>.ts, uses a shared themes/fonts/. Then it
prints the value to use for font or headingFont. The form serves the files
itself, so respondents never contact Google. Variable families get their whole
weight range; static ones get weights 400 to 700. Adding the same family again
replaces its files.
It needs to reach fonts.google.com, fonts.googleapis.com and
fonts.gstatic.com. Behind a proxy set in HTTPS_PROXY, run it with
NODE_USE_ENV_PROXY=1 (Node 22.21 or later) so Node's fetch goes through the proxy.
Chinese, Japanese and Korean families take a few MB in the project, split into small files. Respondents only download the ones holding the characters a page uses.
| Flag | Default | Description |
|---|---|---|
--form | off | <id> is a form id: add the font to that form's own theme in forms/<id>/theme/. |