design system

The visual language of Posta.

A natural palette of paper, sky, forest, and lantern. Crimson Pro for headings, IBM Plex Sans for prose, IBM Plex Mono for code.

Mark

A scene-in-a-seal: sky, sun, two layered hills inside a thin ink ring. Three moods — day, sunset, night — that the brand chip rotates through based on the visitor's local time. Pure SVG, no wordmark inside the seal.

Moods

Posta mark, day day · 06–17
Posta mark, sunset sunset · 18–20
Posta mark, night night · 21–05

Scales (day)

128 px
64 px
32 px
20 px

All curves are quadratic Béziers and the ring is a thin 2 px stroke — both survive any rasterisation cleanly down to favicon sizes.

Notes

  • Three files: /static/mark-day.svg, mark-sunset.svg, mark-night.svg. mark.svg is a copy of the day version for any external reference / fallback.
  • The brand chip in the header swaps mood at page-load via a tiny inline script reading new Date().getHours() — local time, not server time.
  • Sunset mood adds two soft halo circles around the sun; night mood adds five small stars in the upper sky band. Neither appears in the day mark.
  • The mark has no wordmark; don't pair type inside the seal. The "Posta" wordmark sits beside it in the header.

Palette

Six families: paper, ink, sky, forest, lantern, surface.

Paper & ink

--paper#f7f1e3
--paper-warm#fbf6e8
--paper-deep#ece3cc
--ink#2d2a24
--ink-soft#524c3f
--ink-faint#8a826f

Sky

--sky-haze#e6eef3
--sky#c8dde9
--sky-deep#88b1cd

Forest

--moss#b8c79a
--forest#6e8b5b
--forest-deep#4d6a3e

Lantern

--gold#e6c073
--sunset#e3a47a
--terracotta#c87b5d

Dark mode proposal

The night mood from the mark, extended to the whole surface. Ink and paper invert; the sky deepens to indigo; lantern and moss lift a touch so they still carry the room. Same tokens, different values — every component follows automatically.

Side-by-side

day

Every participant is a URL.

Cream paper under a hazy morning sky. Forest links, terracotta accents, ink that settles soft on the page.

read the spec POST /inbox

night

Every participant is a URL.

The same room after sundown. Ink turns to paper; the sky deepens; the lantern glows a touch brighter.

read the spec POST /inbox

Proposed surface tokens

--paper → night#14171c
--paper-warm#1b1f25
--paper-deep#232830
--ink → moon#ece3cc
--ink-soft#b8b09a
--ink-faint#7d7768

Accents (lifted for contrast on night)

--sky-deep#a8cce0
--forest (links)#92b27d
--moss / em#b8c79a
--gold#f0cd86
--sunset#eab392
--terracotta#e09478

Tokens

/* night: invert paper/ink, lift accents */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper:        #14171c;
    --paper-warm:   #1b1f25;
    --paper-deep:   #232830;
    --ink:          #ece3cc;
    --ink-soft:     #b8b09a;
    --ink-faint:    #7d7768;

    --sky-haze:     rgba(110, 145, 180, 0.16);
    --sky:          #6f97b3;
    --sky-deep:     #a8cce0;

    --moss:         #b8c79a;
    --forest:       #92b27d;   /* link colour — lifted */
    --forest-deep:  #b8c79a;   /* em / strong */

    --gold:         #f0cd86;
    --sunset:       #eab392;
    --terracotta:   #e09478;

    --line:         rgba(236, 227, 204, 0.10);
    --line-soft:    rgba(236, 227, 204, 0.06);
    --shadow:       0 1px 2px rgba(0, 0, 0, 0.30),
                    0 12px 32px rgba(0, 0, 0, 0.40);
  }
}
/* explicit override (toggle) */
:root[data-theme="dark"] { /* same overrides as above */ }

Notes

  • Same variables, different values. No new tokens — every existing component inherits dark mode automatically.
  • The brand chip already rotates to the night mark after 21:00. In dark mode it stays on night regardless of hour.
  • Paper grain overlay flips from mix-blend-mode: multiply to screen, with opacity dropped to ~0.25 — same texture, doesn't darken an already-dark surface.
  • Hero background gradients become a faint indigo sky-glow and moss shimmer — same shapes, ~25% the opacity of the day version.
  • Code blocks stay almost identical — they're already dark. The background drops one notch (#2a2722 → #1f1c18) so they sit slightly below the page surface instead of punching out of it.
  • Activation: follow prefers-color-scheme by default. Expose an explicit override via data-theme on <html>, persisted in localStorage.

Typography

Three families. Each has one job.

Crimson Pro

Display · headings · pull quotes

'Crimson Pro', serif

Every participant is a URL.

IBM Plex Sans

Body · UI · captions

'IBM Plex Sans', sans-serif

A fresh humanist grotesque, picked for its warmth and the way its terminals don't read as cold or technical. Reads well at 16–18px for long-form prose.

IBM Plex Mono

Code · kickers · metadata

'IBM Plex Mono', monospace

POST /inbox HTTP/1.1
Content-Type: application/posta+json
Posta-Signature: ed25519 base64...

Type scale

Display drops sharply; body floats around 17px.

h1 / display

A url is identity.

h2 / section

How it works

h3 / sub

POST to send

h4 / label

subsection label

lede / 1.18rem

Genuine origin authentication, no central infrastructure.

body / 17px

Receivers return 2xx only after durably storing the verified message.

caption / 0.9rem

a messaging protocol

Components

The handful of building blocks the site repeats.

Cards

1

Numbered card

Used in the "How it works" three-up. Italic numeral in terracotta, Crimson Pro title.

α

Variant title

The same shell with any leading mark — useful when steps aren't ordinal.

Code blocks

// goldmark + chroma render this server-side
res, err := posta.Attempt(ctx, posta.AttemptInputs{
    Sender:    "https://alice.example/inbox",
    Recipient: "https://bob.example/inbox",
    KeyID:     "k1",
    MsgID:     posta.NewRandomID(),
    Payload:   payload,
})

Pull quote / why-block

If you want to receive a message at alice.example/inbox and know it really came from bob.example/inbox, this is the simplest thing that works.

Link list

Prose elements (as used in /spec)

A subsection heading

A paragraph of body text with an inline link, some inline code, and an italic emphasis.

  • List item one
  • List item two with a monospace token
  • List item three

A blockquote sits in moss with a small ornament.

Tables follow:

FieldTypeNotes
vintegerprotocol version
senderstringcanonical URL
idstring≤ 256 bytes

CSS tokens

Every visible decision lives in a custom property in style.css.

/* paper + ink */
--paper:        #f7f1e3;
--paper-warm:   #fbf6e8;
--paper-deep:   #ece3cc;
--ink:          #2d2a24;
--ink-soft:     #524c3f;
--ink-faint:    #8a826f;

/* sky */
--sky-haze:     #e6eef3;
--sky:          #c8dde9;
--sky-deep:     #88b1cd;

/* forest */
--moss:         #b8c79a;
--forest:       #6e8b5b;
--forest-deep:  #4d6a3e;

/* lantern */
--gold:         #e6c073;
--sunset:       #e3a47a;
--terracotta:   #c87b5d;

/* surface */
--line:         rgba(45, 42, 36, 0.10);
--line-soft:    rgba(45, 42, 36, 0.06);
--radius:       14px;
--radius-lg:    22px;