@supramundane/supram
v0.3.0
Published
A design system with a leadlight grid, the Empyrean palette, and Atkinson Hyperlegible type. Plain CSS, no build step, vendored into each project.
Maintainers
Readme
supram
A design system for web and app things, built progressively. So far: a leadlight grid, a typography system set in Atkinson Hyperlegible, and the Empyrean palette, six hues taken from leaded glass.
Plain CSS, no build step. Open demo/index.html to see everything.
Use in a project
Every project keeps its own copy of supram. Nothing loads from a CDN.
With a bundler (Vite, Next, Astro, and so on):
npm install @supramundane/supramimport "@supramundane/supram";Without a bundler, copy the CSS and fonts somewhere the page can reach and link them:
npx supram copy public/supram<link rel="stylesheet" href="/supram/css/supram.css">Keep css/ and fonts/ side by side; the fonts are found relative to the
CSS.
supram.css imports the layers in order: tokens, base, type, grid,
utilities. Anything you write in an unlayered stylesheet wins over all of
them, so overriding is just writing CSS.
Use with an agent
The skill in skills/supram/SKILL.md is the same material as this README,
written for an agent: setup, the vocabulary, the colour rules, and page
recipes. Three ways to give it to a project:
- As a Claude Code plugin, once for every project:
claude plugin marketplace add https://tangled.org/robin.berjon.com/supramthenclaude plugin install supram@supramundane. - Into one project's skills folder:
npx supram skill. - Any agent that reads
node_modulesfinds it atnode_modules/@supramundane/supram/skills/supram/SKILL.md, andllms.txtat the package root points to it.
npm run check verifies that every token and class the skill mentions
exists in the CSS, so the skill cannot drift from what ships.
Files
css/
supram.css entry point, cascade layers
tokens.css custom properties: colour, space, grid, type
base.css reset and element defaults
type.css @font-face, headings, voices, prose
grid.css page frame, the grid, placement classes
utilities.css colour, geometry, spacing, small layouts
fonts/ Atkinson Hyperlegible Next & Mono, variable, woff2
skills/supram/ the agent skill
bin/supram.mjs `supram copy` and `supram skill`
.claude-plugin/ plugin and marketplace manifests
demo/ the demo page
scripts/ screenshot.mjs renders the demo; check-skill.mjs checks the skillThe npm package ships css, fonts, bin, skills and llms.txt only.
npm run pack:dry lists what would be published.
License
MIT. The fonts are the Braille Institute's, under the SIL Open Font License
1.1; their license files ship alongside them in fonts/.
Grid
Twelve equal modules across, a fluid gutter between them, and square modules down when you ask for them. Cells fill the whole row until they get a span.
<body class="page">
<header class="full bg-black">…</header> <!-- bleeds edge to edge -->
<main>
<div class="grid">
<article class="span-md-8">…</article>
<aside class="span-md-4">…</aside>
</div>
</main>
</body>| Class | Effect |
| --- | --- |
| .page | Content column with margins and a max width. Children sit in it. |
| .full | A .page child that bleeds to the edges. Its children realign to the content column. |
| .grid | Twelve columns, --gutter gap. Set --columns to change the count. |
| .grid--modular | Rows become square modules. Content that needs more room grows its row. |
| .grid--auto | Auto-fit cards. --min is the smallest cell width. |
| .grid--flush | No gutters. |
| .grid--dense | Backfill holes left by explicit placement. |
| .grid--debug | Paint the columns behind the content. |
| .span-n | Span n columns. .span-full spans the row. |
| .start-n | Start on column line n. With no span, runs to the end. |
| .rows-n | Span n rows. |
| .row-n | Start on row line n. |
| .self-start .self-center .self-end | Cell alignment. |
Every placement class takes a breakpoint infix: .span-md-6, .start-lg-3,
.rows-xl-2. Breakpoints are container widths, so a grid nested in a
narrow cell behaves like a narrow grid.
| Name | Container width |
| --- | --- |
| sm | 30rem |
| md | 45rem |
| lg | 60rem |
| xl | 75rem |
Placement is driven by four custom properties, --span, --start, --rows
and --row, which the classes set. You can set them inline instead:
style="--start: 3; --span: 7". They are registered as non-inheriting, so a
nested grid always starts clean.
Type
Text is Atkinson Hyperlegible Next, code is Atkinson Hyperlegible Mono. Both
are variable fonts covering weights 200 to 800 with true italics, self-hosted
from fonts/ under the SIL Open Font License.
The scale is fluid between a 320px and a 1440px viewport:
--text-xs sm base md lg xl 2xl 3xl 4xl display.
| Class | Effect |
| --- | --- |
| .display | The biggest voice: black weight, tight leading. |
| .lede | An intro paragraph a step above body copy. |
| .label | Small caps with wide tracking. |
| .caps | Uppercase with tracking. |
| .text-* .weight-* .leading-* .tracking-* | Raw scale, weight, leading, tracking. |
| .measure .measure-narrow .measure-wide | 65, 45, 80 character line lengths. |
| .tabular | Tabular figures. |
| .flow | Space between siblings with --flow. |
| .prose | Long-form: flow, lists, quotes, code, tables, capped measure. |
Tokens
All in css/tokens.css. Colour follows the OS, or set data-theme="dark"
or "light" on <html> to pin it.
Empyrean
Six hues taken from leaded glass: ruby, amber, gold, emerald, cobalt, teal. Each is one OKLCH hue angle at near maximum chroma, in three lightness steps:
| Step | Token | Role |
| --- | --- | --- |
| glass | --ruby-glass | The hue lit from behind. Bright; always takes ink text. |
| ink | --ruby-ink | The hue as pigment. Deep; is text on white, or takes white text. |
| tint | --ruby-tint | A wash for backgrounds, with the hue's own ink as text. |
The lightness band between glass and ink, where neither black nor white text passes 4.5:1, is deliberately empty. Every glass step carries ink text at 5.9:1 or better; every ink step carries white, or sits on white, at 6:1 or better.
--ruby-text and friends are the hue for coloured words: ink on a light page,
glass on a dark one. In the dark the black page is the lead, so glass does
everything.
Utilities: .bg-ruby (glass, ink text), .bg-ruby-ink (white text),
.bg-ruby-tint, and .fg-ruby for text that follows the scheme. Same for
the other five hues.
Semantic
Neutrals are --black (the lead), --white, a warm --paper, and greys.
What the system actually reads are the semantic names: --bg, --surface,
--fg, --muted, --subtle, --hairline, --rule, --accent,
--accent-text, --link, --focus, --selection.
Content sits on --bg, pure white in the light and pure black in the dark.
Chrome such as toolbars and panels sits on --surface, the warm white
#faf3e9 in the light and a blue-tinted dark grey #1d2027 in the dark. Use
.bg-canvas and .bg-surface to place things on either.
The accent is ruby glass with ink text; links are cobalt; focus rings are cobalt; selection is gold.
Space is an 8px module: --space-1 (0.5rem) through --space-16 (8rem).
Screenshots
npm install
npm run screenshotRenders demo/index.html at desktop, mobile and desktop-dark into
screenshots/, named after the page (demo-desktop.png and so on). Pass
another page path to render that instead. Uses the Chrome on your machine;
set CHROME_PATH to point elsewhere.
