Design
Console: the design system the whole Pumasi commons is built on. Monospace throughout, square corners, one accent in every product — with the tokens rendered live from the stylesheet in use.
Everything below is rendered from the same CSS custom properties the rest of this site uses. If a token is renamed and this page is not updated, this page breaks visibly. A style guide that can drift silently from its theme is worse than none.
One system, four surfaces#
This is not a website stylesheet that other repositories may borrow from. It is the design system for the commons — this site, Pumasi Sign, Pumasi Booking and Pumasi Tunnel — and the products are surfaces of it, not neighbours of it.
Before it was written down there were four palettes, four ideas of what a button was, and four corner radii; three of the four products had invented their own red because the shared theme had no failure colour at all. A reader arriving at a signing link from this site had no way to tell they were still in the same place.
The fix is one rule and one exception. The rule: the interaction colour is
the same everywhere. --accent is green on this site, in Sign, in Booking
and in Tunnel — every button, every link, every focus ring. The exception:
each product carries a --signal — blue for Sign, clay for Booking, cyan for
Tunnel — which marks which product you are in, on the mark and on one edge
rule, and is never allowed near a control. Colouring by product is what made
four brands in the first place.
The full written system, including the per-product themes and what an
application surface is licensed to do differently, is
DESIGN_SYSTEM.md
in this repository. Everything below is the part of it this page can render
live.
The idea#
Console. One typeface — the reader's own monospace — for headings, body and
navigation alike. Square corners. Visible borders. Console punctuation: ##
before a section heading, brackets around a navigation item, a block caret after
the headline.
A commons whose whole argument is that it is legible to machines should look like the artefact it is. The site is a listing, a set of records, and a merge gate; setting it in the type those things are actually written in is the honest choice, and it happens to be where developer-facing design has landed.
Because there is one typeface, hierarchy cannot come from switching families. It comes from size, weight, rules and boxes — which is a constraint worth having, because it is impossible to fake emphasis with it.
Two files, one of them swappable#
/base.css is the structural layer: reset, layout, landmarks,
accessibility, print. It holds not one colour, not one font and not one
size — every visual decision is a token it reads.
/theme.css supplies those tokens, plus the components built on
them. Swapping it swaps the design without touching a line of markup.
Tokens are named for their role: --accent, never --green. A token named
for what it looks like has to be renamed when the look changes, which is how a
theme layer stops being one. Green carries interaction here; amber carries in
progress; those are the two states this project talks about most.
The pictures#
Every illustration is SVG generated at build time and inlined into the page.
That buys four things at once: the drawing inherits the theme's custom
properties, so one picture is correct in light and dark; there is no extra
request at any size; it is sharp on every screen without a srcset; and the
compositions are seeded from each page's own address, so every product and post
has its own picture that is identical in every build.
Nothing drawn here has a curve in it, because nothing in the theme does.
The marks#
Four marks, in /brand/, each on the same 32-unit
grid with a 2-unit stroke and no curve anywhere. Every one was checked at 16
pixels before it was checked at 96 — which is why Booking's calendar has six
cells and not thirty, and why the three product marks have deliberately
different silhouettes rather than sharing the commons' brackets. Four bracketed
glyphs would be indistinguishable in a tab strip.
Each file resolves its colours twice over: it reads the page's token when there
is one, and falls back to a literal when there is not, with its own
prefers-color-scheme block choosing which literal. One file is therefore
correct inlined and correct in an <img>, in light and in dark — including
the four architecture drawings, which are the same asset class at
960 units wide.
No web fonts#
A font request is a third-party request, a blocking paint and a layout shift, in exchange for a typeface most readers will not consciously notice. This theme wants the reader's own monospace in any case — the one they already read code in — so the stack is the one their device ships with.
That is a decision, not a law. Self-hosting one face would be a single file and one token change.
There is a second stack in the theme, --font-ui, and nothing on this site
uses it. It is for the product applications, where a forty-row table set in
monospace costs real columns. Those surfaces move --font-body to it and leave
--font-display where it is, so every heading, label and number stays in the
console face. The brand lives where the eye lands first; the density lives in
the rows.
Colour
Every pair used for text meets WCAG AA in both schemes. Switch your system between light and dark and this page repaints — there is no toggle here, because there is no JavaScript here, and the operating system already knows the answer.
Neutrals
Warm, never grey-blue. Named by role, so a repaint does not require a rename.
Accent — the one colour
Links, the primary action, and exactly one emphasis per view. One accent is a constraint, not a shortage: it means the accented thing is always the thing to do next.
Warn — in progress
Anything provisional: a limitation, a caution, a maturity that is not yet stable.
OK — settled
Anything verified: a passing gate, a stable release, a machine affordance that works.
Danger — failed, or destructive
Anything that failed or that destroys something. New in the v1 system: its absence is exactly why three products each invented their own red.
Product signals — identity, never interaction
One hue per product, matched at roughly 6:1 on paper. A signal marks which product a surface belongs to — the mark, one edge rule, the live path in a diagram — and never colours a control. The thing you click is green in all three products, which is the whole reason these exist.
Illustration
Fills for the art system. Tuned to sit behind text without competing with it.
Type
System stacks only, and on this site one of them: the reader's own monospace, for headings, body and navigation alike. No font is fetched, so nothing blocks the first paint and nothing shifts after it. JetBrains Mono and Geist Mono are named first, so a developer who already has one installed gets it for free.
A second stack, --font-ui, exists in the theme and is
used by nothing here. It is for the product applications — a signing
canvas, a calendar grid, a request log — where a forty-row table in
monospace costs real columns. Those surfaces move
--font-body to it and leave
--font-display where it is, so the console voice stays
in every heading, label and number.
Every step is fluid — clamp() between a phone and a
desktop — so the scale never needs a breakpoint to stay readable.
Space
A 4px base on roughly a 1.5 ratio. A one-off value is a bug.
Components
The pieces this site is assembled from, rendered live.
Buttons
Four ranks, and a screen is allowed exactly one primary. Every rank keeps the prompt caret, so a quiet button still reads as the same kind of object as a loud one. Danger is outlined rather than filled: a delete button that is the loudest thing on the screen gets pressed by accident.
Primary Secondary Tertiary Delete
The stage ladder
One badge, five rungs, the same object in every Pumasi repository.
The brackets are drawn by CSS and never typed. The ladder gets louder
as the claim gets stronger — [LAUNCHED] is the only
filled badge in the system, so it cannot be reached by accident.
planned seed alpha beta launched
Elevation
Four steps, and this surface spends none of them on blur: a thing that is higher here is higher because its border is heavier. The application layer re-expresses the same four steps as real shadows, because a menu opening over a table has to look like it is floating. Same tokens, same markup, different surface.
Card
Elevation 2. The commons' one container: square, one visible border, and its whole hover state is that border going accent.
Signal card
The same card with one edge rule in a product's signal colour. A card is identified along one edge and nowhere else.
Callouts
The marks
Every mark is a 32-unit grid, a 3-unit margin, a 2-unit stroke, and no curve anywhere — because nothing in this theme has one. Each was checked at 16 pixels before it was checked at 96, which is why Booking's calendar has six cells and not thirty. Exactly one element per mark is saturated, and it is that product's signal.
Each file resolves its colours twice: it reads the page's token when
there is one, and falls back to a literal when there is not — with
its own prefers-color-scheme block choosing which
literal. So one file is correct inlined and correct in an
<img>, in light and in dark. The architecture
drawings work the same way:
commons,
sign,
booking,
tunnel.
Taking the theme
Tokens are named for their role — --accent, never
--green. A token named for what it looks like has to be
renamed when the look changes, which is how a theme layer stops being
one. Another Pumasi product can link this file and inherit the
palette, the type scale and the spacing without inheriting this
website's layout:
<link rel="stylesheet" href="https://pumasi.ai/theme.css">
<link rel="stylesheet" href="https://pumasi.ai/base.css">
<link rel="stylesheet" href="https://pumasi.ai/brand/product-theme.css">
<body data-product="sign">
The third file is optional and is for applications rather than
documents: it moves body text to the proportional stack, turns the
four elevation steps into real shadows, and adds the form controls a
website does not have. It changes no colour, no type scale and no
corner. data-product binds --signal, and a
signal never colours a control.
Or copy the file. It is Apache-2.0, it has no dependencies, and it is under three hundred lines. Copying it is the expected thing to do — this is a commons, and a vendored copy that cannot break when someone else deploys is worth more than a shared URL that can.