Skip to content

feat: design system v1, with the mechanism as the landing page - #2

Merged
benatespina merged 11 commits into
mainfrom
feat/visual-identity
Oct 8, 2026
Merged

benatespina merged 11 commits into
mainfrom
feat/visual-identity

Conversation

@benatespina

Copy link
Copy Markdown
Member

bounda.dev takes on design system v1 (written down in DESIGN.md) and shows what Bounda does instead of illustrating it. The documentation follows in bounda-dev/bounda#156.

The system

  • Palette. Basalt (dark) and Bone (light), each first-class; the page follows the system's mode unless the visitor picks one. One ochre accent, never used for a state. Every text colour at least 4.5:1 in both modes. Tokens in src/styles/tokens.css, mirrored by the docs.
  • Type. Science Gothic 600 for display and labels, IBM Plex Sans for text, Red Hat Mono for code and positions. Self-hosted through Astro's font API with metric-matched fallbacks.
  • Brand. The Ledger lockup, drawn on the ⅃ module; the ⅃ favicon set; source files in brand/; a social card.
  • Ledger grammar. Positions read 004 212; a table of one stream shows a correction as a new row and the closed balance under a double rule.

The landing

  • The mechanism in real-time 3D (three.js, loaded after first paint, only with WebGL 2): the ⅃ as a machined monolith whose foot becomes the global stream. Scrolling plays a command, an event appended at the head, the order's state folded into a channel, a projection, a query, and a rebuild that rewinds to position 1 and replays.
  • An intro in which the page catches up with the stream: a ruler draws itself to the stream's head while the scene loads, then the scene lights up in the same place and the hero enters. Held to about two seconds; once per session; any input skips it.
  • Motion. The camera follows a monotone cubic through its keys and the story follows the scroll on a critically damped spring, so a steady scroll never lurches. Shadows redraw only when something moves; resolution steps down when frames run long.
  • Fallbacks. Without JavaScript or with reduced motion the page is static (the story as a grid); without WebGL a drawing stands in.
  • Tailwind, Inter and the old illustrations are gone; astro check runs in CI.

Checks

  • pnpm check and pnpm build green.
  • 60 fps measured on an M1 at 2x, idle and through the whole story.
  • Reviewed in both modes on desktop, tablet and phone widths, without JavaScript, with reduced motion and without WebGL. Not yet checked on real phones.

Bounda's design system v1, written down in DESIGN.md: one brand in two modes, Basalt (dark) and
Bone (light), with an ochre accent that is never a state; Science Gothic for display and labels,
IBM Plex Sans for text and Red Hat Mono for code and positions; and the Ledger lockup, whose
wordmark is drawn on the ⅃ module.

The landing page now shows what Bounda does instead of illustrating it. The ⅃ is a machined
monolith whose foot becomes the global stream, and scrolling plays the mechanism: a command
condenses into an event at the head, the order's state is folded into a channel in the bar, a
projection writes a row, a query reads it, and a rebuild rewinds the stream to position 1 and
replays it. The scene is three.js, loaded after first paint and only where WebGL 2 is available;
without it a drawing stands in, and with reduced motion the story is a still and a grid. Positions
read as the ledger writes them (004 212), and a table of one stream shows a correction as a new
row and the closed balance under a double rule.

- Tokens in src/styles/tokens.css, following the system's mode unless the visitor picks one.
- Fonts self-hosted through Astro's font API, with metric-matched fallbacks; Science Gothic is
  requested at its one weight across the width axis it uses.
- Code samples highlighted by Shiki with a theme made of the tokens, so they follow the mode.
- New favicon set and manifest colours; Tailwind, Inter and the old illustrations are gone.
- astro check runs in CI before the build.
…system

The brand had a repository of its own, bounda-dev/branding, whose assets and decisions predate
design system v1. What still holds now lives next to DESIGN.md:

- brand/ has the Ledger lockup and the ⅃ symbol in currentColor, on Bone and on Basalt, and the
  app icon as SVG and as a 1024px PNG.
- DESIGN.md gains a section on the landing page: what it says and in which order, one scene and
  one accent, no marketing language, and that nothing depends on the 3D.
…import

The scene imports three.js dynamically, so Vite's dev server only found it on first use, re-optimized its dependencies and answered that import with 504 Outdated Optimize Dep; the page then fell back to the drawing. Listing it in optimizeDeps.include bundles it when the server starts. The production build was not affected.
A shared link to bounda.dev showed no image although the page asks for a large card. og.jpg is the hero at 1200 by 630 in Basalt, the live head of the stream in ochre, with its size and a text alternative for Open Graph and X.
The page painted at once and the 3D appeared a moment later, all of a sudden. The scene's fetch now starts before anything else in the story script, every shader is compiled before the first frame (hidden objects included, so nothing stutters later), and the canvas fades in over 1.1s once that frame is drawn. Only the current mode's lighting studio is built at start; the other is built the first time its mode is shown.
…e hero enters

The page used to paint at once and the scene to arrive a moment later. Now the readout counts from position 1 to the head of the global stream while the scene and the fonts load, then the hero enters in order, each line of the headline rising from its own baseline, and the scene lights up with it. It plays once per session, any input skips it, it never plays with reduced motion, the hero shows anyway if the script does not run, and past 3.5s the hero enters without waiting for the scene.
…s up

The intro left the middle of the hero empty, with only a readout in the corner. A ruler of events now draws itself from the left edge to the point where the scene's stream has its head, with an ochre cursor and the position flying beside it; the scene reports that point, so the ruler gives way to the stream in the same place as it lights up. The corner readout stays out of the intro and comes back as the hero enters.
Measured on an M1 at 2x, the scene already held 60 fps; the stutter was the choreography. The
camera eased in and out of every key, so a steady scroll stopped it at each beat, and the beat
followed the scroll with a first-order lag whose speed jumped on every wheel step. The camera now
follows a monotone cubic through the keys (continuous speed, no overshoot), and the beat follows
the scroll on a critically damped spring.

The frame also does less: shadows are redrawn only when something that casts one has moved, the
glyph's reflection uses the plain material, resolution steps down while frames run long, the
story reads its section offsets when the layout changes instead of every frame and skips
unchanged style writes, and the install command no longer blurs the canvas behind it.

A page opened in a background tab skips the intro, and the hero and the scene come in on a
deadline even if frames stop.
The story ended by closing in on the glyph's channel so that it became the code section's rule,
which left most of a screen of blank metal for anyone who stopped scrolling there. The story now
ends on its last framing, the rows rebuilt and its caption in view, and the page scrolls on over
the scene into the code; it is half a viewport shorter. Coming back to the front after the replay,
the camera arcs around the object instead of passing through the rows.
…stive technology

The hero is the page's largest paint, and the intro could hold it back for 3.5s while waiting
for the scene. It now gives up at 1.7s, counting in 1.2s when everything is ready; a slow scene
fades in after the hero instead of delaying it.

The captions out of view were hidden with visibility, which also took them out of the
accessibility tree, so a screen reader only ever had the one on screen. They now hide by opacity
and only stop catching the pointer. The headline's lines kept no space between them once split,
so it read as "Event sourcingwithout theceremony."; they are spaced again.
- The page is static until JavaScript runs and motion is allowed (`.still` on the root, removed by
  the head script), so without JavaScript every section, caption and ledger row shows, with the
  drawing in the hero; reduced motion uses the same layout.
- Below the wide layout the corner readout waits for the story, so it no longer sits on the hero's
  actions on tablets.
- A control with focus in the hero stays visible while the foot fades.
- The intro's ruler is measured in the canvas's frame (the large viewport, without the scrollbar),
  so on phones it ends at the stream's head; the inline fallback timer is cleared by the intro, so
  it cannot cut the intro short; the corner readout stays hidden from first paint.
- Callouts project with the current frame's camera instead of the previous one.
- The resolution step-down judges frames against the display's own pace, so a 30 fps cap in
  low-power mode is not taken for a slow GPU.
- With reduced motion, the still is drawn again once the engraving font arrives.
@benatespina
benatespina merged commit 3d0cbd9 into main Oct 8, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant