npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@formancy/themes

v0.13.0

Published

Reference themes for formancy renderers — four unrelated looks targeting data-formancy-part hooks — with the builders' stylesheet and the admin's palette. CSS only.

Readme

@formancy/themes

Four reference themes, the builders' stylesheet and the admin's palette. CSS only — no JavaScript, no build step, no dependency on any renderer.

What they are for

The renderers ship zero CSS. That claim is easy to make and hard to trust, so these files exist to test it: the same markup and the same engine, dressed as four products that do not look related.

| | Blueprint | Dusk | Pop | Paper | | --- | --- | --- | --- | --- | | Feels like | a technical drawing | a dark app | a sticker sheet | a printed page | | Ground | white | dark | cream or white | paper or white | | Controls | boxes, 3px corners | inset wells, 10px corners | outlined, hard shadow | a line to write on | | Type | IBM Plex Sans | Space Grotesk | Archivo, heavy | Fraunces, spaced small capitals for labels | | Choices | drawn boxes and circles | lit wells | chips | hairline boxes, italic when chosen | | Invalid shown as | a bar on the leading edge | a ring around the control | a pink sticker | a vermilion line and an italic note | | Required shown as | an asterisk | the word "required" | a star | a footnote asterisk | | Active wizard step | a numbered, underlined word | a lit dot on a track | a yellow badge | a roman numeral, underlined |

If supporting any of them had required changing a component, the headless claim would be false. None did.

Use

import '@formancy/themes/blueprint.css'
import '@formancy/themes/dusk.css'
import '@formancy/themes/pop.css'
import '@formancy/themes/paper.css'
<div data-formancy-theme="blueprint">…your form…</div>

Every theme is scoped under [data-formancy-theme], so a page can load all of them and switch by changing one attribute — which is what the playground and the landing page do. workbench.css and room.css, below, are not themes and are not scoped: each sets its variables on :root, for the whole page.

Each theme sets its own text colour, colour scheme and box-sizing on its subtree, so it does not depend on the host page's reset. It does not paint the page behind the form: Pop and Paper look best on the ground colour they declare as --fm-ground, and fine on white.

Fonts are referenced, not bundled, and every theme falls back to a system stack. formancy.ai serves its faces itself from Fontsource's packages rather than from a font service, so opening a page tells no other site who opened it. Fontsource names a variable face <Family> Variable, so Pop asks for Archivo then Archivo Variable, and Paper for Fraunces then Fraunces Variable: either spelling of the face, whichever way a page loads it.

The builders: workbench.css

workbench.css dresses both builders — the tree, the palettes, the inspector, the logic panel, the prompt pane and the relay beside it, the scenarios, blocks, a datagrid's columns and the translations — through the data-formancy-part hooks they emit, and every part they emit has a rule here. It is not a form theme and nothing depends on it. Change how it looks through its tokens rather than with rules of your own:

| Token | What it colours | |---|---| | --wb-ink, --wb-muted | text, and quiet text | | --wb-bench, --wb-panel, --wb-surface | the ground, a pane, a control or a card | | --wb-rule | lines and borders | | --wb-signal | the selection, focus and the primary action | | --wb-derived | what the builder worked out rather than what was typed: a type, a path | | --wb-invalid, --wb-ok, --wb-caution | broken, fine, and costing something | | --wb-sans, --wb-mono | the two faces |

The room: room.css

room.css is the palette formancy.ai's self-hosted admin is drawn in — a dark ground, glass panels, violet for what a person acts on and teal for what the system did — so that an application meant to be the same room imports it rather than copying the values. The admin imports it the same way. It is custom properties on :root and color-scheme: dark, and nothing else: no rule for any element, and no face loaded. Your own stylesheet reads them.

import '@formancy/themes/workbench.css'
import '@formancy/themes/room.css' // after the workbench

| Variable | What it is | |---|---| | --ground | the page, behind everything | | --glass | a panel over the ground, which shows it through | | --text, --muted | text, and quiet text | | --edge, --edge-strong | a line, and a line that bounds a panel | | --client | violet: what the person acts on | | --server | teal: what the system did | | --invalid, --ok, --warn | broken, fine, and half-way | | --display | the face for headings: Archivo, then IBM Plex Sans |

It also sets every colour of workbench.css to the room's — --wb-ink, --wb-bench, --wb-panel, --wb-surface, --wb-rule, --wb-muted, --wb-signal, --wb-derived, --wb-invalid, --wb-ok and --wb-caution — and leaves its faces as the workbench sets them. Those take effect only after workbench.css: both say :root, so whichever loads later wins, and loaded first the builder keeps its light panes in the dark room.

The faces are not loaded. --display names Archivo and IBM Plex Sans, and the workbench's faces are IBM Plex; a page loads them itself, as the admin does from Fontsource, or falls back to the system's.

The names are what you write var() around, so they are this package's API, and a value is the room: a change to one changes every page that imports it, and the changelog says so.

Right to left, and your bundler

Every file here that lays anything out — the four themes, and workbench.css, which styles the two builders — says which side a thing is on in reading order: logical properties, and :dir(rtl) where CSS has no logical form, as for an icon's background-position or the bar an inset shadow draws down one side of a selected item. Set dir="rtl" on the page, or on anything above the form or the builder, and the layout follows.

That holds only while :dir() reaches the browser as written. A bundler targeting browsers older than the ones :dir() shipped in rewrites it. Vite's default target had Lightning CSS turn every :dir(rtl) into :is(:lang(ar), :lang(he), …), which follows the page's language instead: dir="rtl" on a page whose lang is not on that list changes nothing, and the builder's marks stay on the left. Measured in this repository's own playground. Set the CSS target to the browsers these stylesheets are written for — each the first release with both :dir() and color-mix():

// vite.config.ts
export default defineConfig({
  build: { cssTarget: ['chrome120', 'edge120', 'firefox113', 'safari16.4', 'ios16.4'] },
})

A stylesheet loaded with a plain <link> is not rewritten and needs nothing.

Writing your own

Copy one and change it. The surface is data-formancy-part attributes, and every part either renderer emits that has anything to draw is styled in every form theme shipped here — apps/docs/src/themes.test.ts fails on one that is not — so a copy already names them all. These are where to start, not the whole list:

  • fields: field (with data-state="invalid|valid"), label, error, required-hint, radio-option, checkbox-option, static
  • repeaters: repeater, repeater-legend, row
  • layouts: layout-row, layout-column, layout-section, layout-section-heading, layout-table (with data-columns), layout-tabs, tablist, tab, tabpanel
  • wizard and submit: stepper, wizard-nav, submit
  • errors: error-summary, error-summary-heading
  • rich text: richtext-toolbar, richtext-button, richtext-preview, richtext, visually-hidden
  • files: file-list, file-item, file-status, file-unavailable

visually-hidden has to be hidden by every theme — it is the only name the rich text toolbar's buttons have, so it is moved off screen, never removed.

Controls are plain input, select and textarea inside [data-formancy-part="field"]. Nothing is a class, so nothing collides with your design system.