# Facet
Facet is a plain HTML, CSS and JS design library. It is one CSS file and
one JS file (plus a small optional service-worker engine, facet-sw.js,
that powers offline/PWA) hosted at a public URL. Any page consumes it
with one link tag and one script tag. No React, no build step, no npm
install, never minified.
This file is the full usage guide as plain text, for AI crawlers and
builders — the exhaustive inventory of what Facet does and how to use it.
The human version is the website (start at the site root); the rules for
building and maintaining the library itself are in CLAUDE.md. Repo:
https://github.com/tanishksharma/Facet
Write pages as clean, semantic HTML: real header / nav / main / section /
article / footer, real buttons and links with accessible names, one h1 per
page, and not one extra wrapper (a container exists only to enable a real
feature, like scrolling). The full markup rules are under "Rules for
building on Facet" below.
## Building a whole product
Facet ships a product-building method, not just parts. To build a
complete product for a person, fetch /build.txt — the prompt pack:
interview first, agree a v1 plan, build with the library, verify like
a user, plus the backend addendum and the quality bar. The
human-readable version is /product.html; both carry the same rules.
## Features — the complete capability inventory
This list is the exhaustive inventory: if a capability is not on it, it
does not exist. (The Features section on the homepage is a curated,
human-friendly subset of this list.)
Distribution: two files, one link + one script tag (plus the small
optional service-worker engine, facet-sw.js, that powers offline/PWA),
public URL, no framework, no npm, no build step, never minified. Until
v1 every project always gets the latest straight from the web; from v1,
frozen /lib/v1/ copies pin old projects and cached updates apply only
at page boundaries. This file (llms.txt) is the exhaustive usage
guide, and the readable, commented source of lib/facet.css and
lib/facet.js is the ground truth — an AI reads those directly.
App-ready (PWA): a one-line sw.js stub — importScripts of
/lib/facet-sw.js — gives any project the shared caching strategy:
pages network-first so deploys land on the very next refresh, cache as
the offline fallback, other static assets cached instantly and
revalidated behind.
data-service-worker="/sw.js" on the script tag registers it;
[data-install] buttons show only when the browser offers installation
(facet.install() for programmatic prompts); /templates/manifest.json is
the fill-in manifest with the icon checklist.
Theming: one attribute (data-theme) restyles the whole page including
layout containers; dark mode is its own attribute (data-mode="dark") and
composes with every theme; both attributes work on any element for subtree
theming; system dark preference honoured when the page does not choose;
theme and mode ride the URL (?theme=, ?mode=) so shared links open
identically; one-line switcher buttons via data-theme-switch and
data-mode-toggle; setup by configuration — data attributes on the
facet.js script tag boot a page into its look, and facet.set() changes
theme, mode or any token live at runtime (skin keys persist across
visits; a {persist: false} second argument applies them for this load
only — per-load color cycles, session previews); native scrollbars and form
controls follow via color-scheme and facet.js keeps the theme-color
meta synced so the browser's UI follows the theme; custom accent
as a supported recipe — override
a rank's four tokens in one block, page-wide on :root or per-subtree on
any element, and every component using that rank follows; Style Mixer
on the Build-a-theme page — a picker per semantic token restyles the page live
over any theme, derives hover/pressed/on-colors, rides the URL (?mix=)
and exports a paste-ready facet.set config block; Skin Lab on the
Build-a-theme page — every theme in light and dark side by side, the same real
layout from identical markup, one attribute apart.
Tokens: every design decision is a role-named CSS variable — never a
number. The neutral ramp is a set of semantic roles: --background (the
page), --surface (cards/inputs, one step above), --surface-hover and
--surface-active (interactive fills), --border-subtle / --border /
--border-strong (three line weights), and --text / --text-muted /
--text-subtle (three text weights) — so a component picks the exact
neutral it needs. Plus per-theme status colors, the vivid decorative palette
(--color-1 … --color-5) with an AA text-safe -text companion per color,
and type and spacing named by
intent and each scaled by a global small/medium/large control. Typography
is fully tokenised by role: five weights (--weight-body/-medium/-strong/
-heading/-eyebrow), line-height per role (--leading-display/-heading/-snug/-body/
-ui, tighter as type grows), tracking per role (--tracking-display/
-heading/-snug/-body/-label/-caps/-wide — large type tightens, caps labels open
up), reading measure (--measure, --measure-narrow), and prose rhythm
(--flow-heading-above/-below, --flow-paragraph, --flow-tight — em-based
vertical margins that give raw semantic HTML its flow; gap layouts zero
them so rhythm and gap never stack). Eighteen font roles, one face per
job: the core seven (--font-heading/-body/-mono/-quote/-reading/
-numeric/-hand), the special-job faces (--font-hero/-signature/
-typewriter/-receipt/-barcode/-pixel/-children) and the script pairings
(--font-japanese/-chinese/-korean/-devanagari) — all loaded by
facet.css itself, every stack ending in a system fallback (full detail
under Fonts below). Plus
container widths, radii by role (--radius-control/-panel/-card/-pill),
border WIDTHS by role (--border-hairline for separators, --border-element
for outlines, --border-focus for the focus ring, --border-highlight for
emphasis), layered whisper-light shadows, motion durations and easings,
focus ring. Ranked accents: the base family plus
accent-1 (the one primary action), accent-2 (every secondary action's
fill) and accent-3 (links, labels, quiet affordances), each with hover,
pressed and on-colors in every theme — components pick ranks, never
colors.
Adapts to the reader: all sizes in rem, so device/browser text-size
settings scale the whole interface; fluid hero type via clamp(); 44px
touch targets; no hover-only interactions; configurable motion —
Lively springs by default, data-motion="calm" for quiet, "off" for
none, prefers-reduced-motion above all of it; color eases by default —
a base rule cross-fades paint-only properties on every element, so
hover tints, status flips and whole theme or mode switches never snap; ambient motion from one
engine (facet.motion) — data-parallax drifts elements and data-shine
sweeps a specular light across them, both from the device tilt, the
cursor, or an idle drift when no input exists; smooth page
transitions — data-transition="page" animates cross-page navigation
(old page eases away, new one springs in, URL changes normally), with
a[data-no-transition] per-link opt-out and facet.go(url) for
programmatic moves, instant fallback where unsupported; a full
print & export system (see its section).
Base layer: raw semantic HTML looks designed with no classes; one visible
keyboard focus ring everywhere; selection colors, thin scrollbars,
skip-to-content link, placeholder styling; the hidden attribute always
wins; tabular figures on numeric UI.
Components and behaviours: themed layout primitives (container in four
widths, stack in three gaps, full-viewport snap sections); one-attribute
page width — data-width="narrow|wide|xwide|full" on remaps every
container from reading column to edge to edge, with a matching control
in the settings panel; background
materials in two variants (the grid looks and the breathing fluid
field), pinnable to the viewport — .bg-fixed for a whole-page still
backdrop, data-bg-fluid-fixed for pools the surface scrolls over; the
view-stack app shell for screen-to-screen apps (main.view-stack of
.view screens, hash-routed, facet.views.go/back, safe-area padded);
buttons in
three variants and three sizes working on