The visual language behind s17 Labs — one dark-first system shared by the
homepage, the footer, and every tool. Tokens live in
src/styles/tool.css (:root) and
src/styles/global.css; components below are live specimens
using the real classes, so this page can never drift from production.
Brand
The mark is a solid accent square — the .cube — set in
Aldrich next to the s17 Labs. wordmark (period included).
It appears in the homepage hero, every tool header, and the footer,
always at 0.82em of the surrounding type size.
Voice: dark-first, minimal, no gradients on chrome. Everything ships self-hosted — fonts, icon data, styles — except Icon Maker emoji SVGs, which load from a CDN at runtime.
Foundations
Core tokens from global.css, then the tool palette from tool.css.
Foundations
Three families, each with one job. Aldrich is the brand
voice (self-hosted woff2, latin subset, preloaded); body copy is
Verdana; anything machine-read — inputs, hex codes,
badges, labels-as-data — is Courier New monospace.
Build lightly. Ship fast. Aa 0123456789
Your data never leaves your browser. Tools run entirely on-device — fast, private, and free.
#ff4136 → #ffffff @ 90° · 512×512 · ic_launcher.xml
| Usage | Spec |
|---|---|
| Tool title | Aldrich 1.8rem (1.5rem under 580px), cube + 0.25em gap |
| Panel title | Aldrich 0.7rem, uppercase, 0.14em tracking, secondary text |
| Form label | Verdana 0.74rem, uppercase, 0.09em tracking, secondary text |
| Body / tagline | Verdana 0.95–1rem, 1.6 line-height, 520px measure |
| Inputs / meta | Courier New 0.88rem values · 0.65–0.75rem badges and hints |
Icon Maker ships 13 typefaces for icon text (first three are system stacks, the rest bundled woff2 under SIL OFL): System Sans · System Serif · System Mono · Alien Block · Anton · Archivo Black · Bebas Neue · Lobster · Montserrat · Oswald · Playfair Display · Roboto · JetBrains Mono.
Foundations
Product glyphs come from Font Awesome (-solid/regular/brands via npm)
through a single registry — src/icons.ts. UI code renders
them with the <Icon name> component (Astro) or
iconSvg(name) (vanilla TS). Every bundled glyph:
Foundations
One radius token (--radius: 4px) with three functional
exceptions. Borders are hairlines — 1px solid var(--border) —
or the 2px dashed drop-zone. Motion is a single easing
(--t: 0.25s ease).
| Token | Spec |
|---|---|
| .page / .tool-top | 680px default (ToolLayout); most tools override 640px, Icon Maker 900px · 20px side padding |
| Icon Maker page | 900px max (ToolLayout maxWidth) |
| .tool-desc | 520px centered measure |
| .panel | 1.4rem padding, 1rem bottom margin |
| breakpoints | 580px (tools, footer) · 680px (icon-maker only) |
Components
One primary button that fills its container and turns accent when it
has something to do (.ready), plus a family of small
controls. All transitions run on var(--t).
Components
Fields stack label → control → hint with a 0.45rem rhythm. Text inputs
speak Courier; selects speak Verdana with a CSS chevron. Focus is never
color-shift alone — every control also draws the accent
:focus-visible ring.
Drop images here or click to browse
PNG, JPG, SVG, WebP — processed on-deviceComponents
Messages reserve their line (min-height: 1.1em) so showing
one never shifts layout. Errors speak accent red, success speaks
Android green. Long operations use the 3px progress track; blocking
choices use the modal.
Guidelines
New tool pages must use ToolLayout plus the
tool.css tokens above — no bespoke styling. Anything used
twice gets promoted from the tool stylesheet into
tool.css. A new tool touches exactly three places:
src/pages/tools/<slug>.astro,
src/scripts/tools/<slug>.ts, and the registry in
src/data/tools.ts. Conventions, commits, and PR rules live
in AGENTS.md.
| Rule | Spec |
|---|---|
| Client-side only | No server endpoints, no analytics — data never leaves the browser |
| Typecheck | Client TS is checked by astro check — no Node-only APIs in tools |
| Commit | type(scope): short imperative summary, atomic, no AI trailers |
| CSS order | Page stylesheets load before tool.css in prod — beat shared rules with specificity, never order |
| Headers | Back-link label collapses by measurement (BackLinkFit + back-link.ts) — flash-free, no breakpoint guessing |