Skip to content

Latest commit

 

History

History
446 lines (390 loc) · 20.1 KB

File metadata and controls

446 lines (390 loc) · 20.1 KB
version alpha
name Xe Iaso Design System
description Warm Gruvbox-rooted personal blog system — parchment surfaces, serif headlines, sticker-driven voice, magenta invert link hovers. Derived from xeiaso.net.
colors
primary bg-hard bg-soft bg-0 bg-1 bg-2 bg-3 bg-4 fg-0 fg-1 fg-2 fg-3 fg-4 red green yellow blue purple aqua orange red-bright green-bright yellow-bright blue-bright purple-bright aqua-bright orange-bright link link-hover link-hover-bg link-visited link-visited-hover link-visited-hover-bg code-bg code-fg
#af3a03
#f9f5d7
#f2e5bc
#fbf1c7
#ebdbb2
#d5c4a1
#bdae93
#a89984
#282828
#3c3836
#504945
#665c54
#7c6f64
#9d0006
#79740e
#b57614
#076678
#8f3f71
#427b58
#af3a03
#cc241d
#98971a
#d79921
#458588
#b16286
#689d6a
#d65d0e
#b80050
#fdf4c1
#9e0045
#53493c
#ffffff
#282828
#1d2021
#ebdbb2
typography
display h1 h2 h3 h4 h5 h6 body body-emphasis small code
fontFamily fontSize fontWeight lineHeight
Podkova
3rem
600
1.2
fontFamily fontSize fontWeight lineHeight
Podkova
2.25rem
600
1.2
fontFamily fontSize fontWeight lineHeight
Podkova
1.875rem
600
1.2
fontFamily fontSize fontWeight lineHeight
Podkova
1.5rem
600
1.2
fontFamily fontSize fontWeight lineHeight
Podkova
1.25rem
600
1.2
fontFamily fontSize fontWeight lineHeight
Podkova
1.125rem
600
1.2
fontFamily fontSize fontWeight lineHeight letterSpacing
Podkova
1rem
600
1.2
0.04em
fontFamily fontSize fontWeight lineHeight
Schibsted Grotesk
1rem
400
1.55
fontFamily fontSize fontWeight lineHeight
Schibsted Grotesk
1rem
600
1.55
fontFamily fontSize fontWeight lineHeight
Schibsted Grotesk
0.875rem
400
1.5
fontFamily fontSize fontWeight
Iosevka Curly Iaso
0.95em
400
rounded
xs sm md lg xl
2px
4px
6px
8px
12px
spacing
1 2 3 4 5 6 7 8
4px
8px
12px
16px
24px
32px
48px
64px
components
page surface-raised divider-strong border-hairline text-strong text-subtle text-muted text-caption button-primary button-secondary button-accent button-ghost button-danger card tag details pre code-inline blockquote pullquote chat-avatar chat-row link link-hover link-visited link-visited-hover admonition-info admonition-info-rule admonition-info-dot admonition-warning admonition-warning-rule admonition-warning-dot admonition-tip admonition-tip-rule admonition-tip-dot admonition-note admonition-note-rule admonition-note-dot admonition-danger admonition-danger-rule admonition-danger-dot admonition-success admonition-success-rule admonition-success-dot sponsor-heading sponsor-rule-start sponsor-rule-end
backgroundColor textColor typography
{colors.bg-hard}
{colors.fg-1}
{typography.body}
backgroundColor textColor rounded padding
{colors.bg-0}
{colors.fg-1}
{rounded.md}
{spacing.4}
backgroundColor height
{colors.bg-3}
2px
backgroundColor height
{colors.bg-4}
1px
textColor typography
{colors.fg-0}
{typography.body-emphasis}
textColor typography
{colors.fg-2}
{typography.body}
textColor typography
{colors.fg-3}
{typography.small}
textColor typography
{colors.fg-4}
{typography.small}
backgroundColor textColor typography rounded padding
{colors.primary}
#ffffff
{typography.body-emphasis}
{rounded.xl}
8px 16px
backgroundColor textColor typography rounded padding
{colors.fg-0}
{colors.bg-hard}
{typography.body-emphasis}
{rounded.xl}
8px 16px
backgroundColor textColor typography rounded padding
{colors.purple}
#ffffff
{typography.body-emphasis}
{rounded.xl}
8px 16px
backgroundColor textColor typography rounded padding
transparent
{colors.fg-1}
{typography.body-emphasis}
{rounded.xl}
8px 16px
backgroundColor textColor typography rounded padding
{colors.red}
#ffffff
{typography.body-emphasis}
{rounded.xl}
8px 16px
backgroundColor textColor rounded padding
{colors.bg-2}
{colors.fg-1}
{rounded.md}
{spacing.4}
backgroundColor textColor rounded padding typography
{colors.bg-1}
{colors.fg-1}
{rounded.lg}
6px 10px
{typography.small}
backgroundColor textColor rounded padding
{colors.bg-soft}
{colors.fg-1}
{rounded.md}
{spacing.4}
backgroundColor textColor rounded padding typography
{colors.code-bg}
{colors.code-fg}
{rounded.md}
{spacing.4}
{typography.code}
backgroundColor textColor rounded padding typography
{colors.bg-1}
{colors.fg-1}
{rounded.sm}
0.1em 0.3em
{typography.code}
backgroundColor textColor rounded padding
{colors.bg-2}
{colors.fg-1}
{rounded.lg}
{spacing.4}
backgroundColor textColor rounded padding
{colors.bg-2}
{colors.fg-1}
{rounded.md}
{spacing.4}
backgroundColor rounded size
{colors.bg-1}
{rounded.xs}
64px
backgroundColor textColor padding
{colors.bg-soft}
{colors.fg-1}
{spacing.3}
textColor
{colors.link}
backgroundColor textColor
{colors.link-hover-bg}
{colors.link-hover}
textColor
{colors.link-visited}
backgroundColor textColor
{colors.link-visited-hover-bg}
{colors.link-visited-hover}
backgroundColor textColor rounded padding
{colors.bg-soft}
{colors.fg-1}
{rounded.md}
{spacing.4}
backgroundColor width
{colors.blue}
4px
backgroundColor rounded size
{colors.blue-bright}
{rounded.xs}
8px
backgroundColor textColor rounded padding
{colors.bg-soft}
{colors.fg-1}
{rounded.md}
{spacing.4}
backgroundColor width
{colors.yellow}
4px
backgroundColor rounded size
{colors.yellow-bright}
{rounded.xs}
8px
backgroundColor textColor rounded padding
{colors.bg-soft}
{colors.fg-1}
{rounded.md}
{spacing.4}
backgroundColor width
{colors.green}
4px
backgroundColor rounded size
{colors.green-bright}
{rounded.xs}
8px
backgroundColor textColor rounded padding
{colors.bg-soft}
{colors.fg-1}
{rounded.md}
{spacing.4}
backgroundColor width
{colors.purple}
4px
backgroundColor rounded size
{colors.purple-bright}
{rounded.xs}
8px
backgroundColor textColor rounded padding
{colors.bg-soft}
{colors.fg-1}
{rounded.md}
{spacing.4}
backgroundColor width
{colors.red}
4px
backgroundColor rounded size
{colors.red-bright}
{rounded.xs}
8px
backgroundColor textColor rounded padding
{colors.bg-soft}
{colors.fg-1}
{rounded.md}
{spacing.4}
backgroundColor width
{colors.aqua}
4px
backgroundColor rounded size
{colors.aqua-bright}
{rounded.xs}
8px
textColor typography
{colors.orange}
{typography.h5}
backgroundColor width height
{colors.orange-bright}
50%
2px
backgroundColor width height
{colors.purple-bright}
50%
2px

Overview

A design system derived from xeiaso.net — the personal blog and portfolio of Xe Iaso, a solo blogger, coder, developer advocate, vtuber, and technical educator. The brand voice is conversational, confident, terminally-online, and unapologetically personal. Visually, this is Gruvbox (by Morhetz) warm-neutral palette, serif headlines (Podkova) over sans body (Schibsted Grotesk), soft parchment-like surfaces.

Intended uses. Long-form blog posts, one-person portfolios, zine-style project pages, fictional-conversation explainers, character-driven technical posts. Bad fits: enterprise dashboards, consumer apps, anything that wants to feel "clean modern SaaS."

Content voice. First-person and conversational — Xe writes with you, not at you. The tone swings between earnest technical walkthrough and dry shitpost, often in the same sentence. Sentence case for headings; product and brand names preserve their own casing (NixOS, Kubernetes, Tailscale, Anubis). Xe's pronouns are it/its (also they/them). Emoji is used sparingly — stickers of a fictional cast (Mara, Cadey, Aoi, Nicole) do the emotional-signalling work instead.

Colors

The palette is Gruvbox — nothing is a true neutral. Every "gray" is yellow-leaning beige and every accent has both a muted and a bright variant. The two variants exist so light and dark modes can pick opposite ends of the ramp and stay visually balanced.

Surfaces. bg-hard is the page background (the highest-contrast parchment), bg-0 the body surface, bg-soft sits between them for details and admonitions. bg-1bg-4 are successively darker/warmer — bg-1 is the code-inline and tag chip, bg-2 is the card and blockquote body, bg-3 is the strong divider, bg-4 is the hairline border color. On the foreground side, fg-0 is the highest-contrast heading/link color, fg-1 is body copy, fg-2/fg-3 are subtle text, fg-4 is caption/disabled.

Accent pairs. Each hue — red, green, yellow, blue, purple, aqua, orange — has a muted (darker) and -bright (lighter, more saturated) token. The convention in this system is: muted hue for surfaces against the cream body (buttons, admonition rule bars) because muted + white text hits WCAG AA; bright hue for small non-text accents (admonition-type dot markers, sponsor gradient endpoints) where the hue pops but contrast doesn't matter because no text sits on it. Admonition body text stays on fg-1 (body color) — bright hues against warm cream fail contrast. primary is an alias of muted orange (#af3a03) — the signature CTA and the surface button-primary uses.

Links are a signature. Light-mode links start at magenta #b80050 and invert on hover to cream on #9e0045. Visited links drop to a desaturated brown and get their own near-black hover. This magenta invert is probably the single most recognisable interaction in the system — do not override it.

Dark mode. The live site supports a full dark mode via prefers-color-scheme. This spec documents light mode as canonical because DESIGN.md has no native dark-mode slot. In dark mode: surfaces invert to warm charcoal (#1d2021 hard → #282828 body → #3c3836 raised), foreground swaps to cream, and the accent ramp flips — light mode uses muted hues on surfaces and bright hues on bg-soft text; dark mode swaps, using bright hues on surfaces because they pop on charcoal. The link magenta shifts to pink #ffa8ce / hover #b3004d. Implementations should mirror every surface, foreground, and accent token under a @media (prefers-color-scheme: dark) block.

Discipline: no blue-purple gradients, no neon, no cool grays. The only decorative gradient anywhere is a 2px orange→purple top rule on the SponsorCard (see sponsor-rule-start + sponsor-rule-end); every other surface is flat and opaque.

Typography

Podkova (serif, weight 400–800, used at 600) for every heading, h1 through h6 and any display lockup. It's a warm, slab-ish serif that carries the parchment surfaces.

Schibsted Grotesk (sans, variable 400–900) for body copy — 400 for prose, 600 for emphasis. Line height 1.55 in prose, text-wrap: pretty on headings and paragraphs.

Iosevka Curly Iaso (mono) for code. This is a custom-cut Iosevka variant self-hosted at files.xeiaso.net; it is not bundled with this system. Fallbacks are the broader Iosevka Iaso family (Aile / Etoile / Curly) and then ui-monospace. The code typography token points at the custom face, but any generic mono will read correctly.

Scale. 14 / 16 / 18 / 20 / 24 / 30 / 36 / 48 px. h6 is the only heading that drops to a tight 0.04em tracked, upper-case label style, used sparingly. Everything else is cased naturally.

Layout

Single column, prose width capped at roughly 65–80ch. Chat-bubble sequences go wider (~80ch) so stickers don't squash the text. Vertical rhythm comes from a 16px gap between blocks; no vertical-rhythm grid, just disciplined use of spacing.4.

Mobile is not flashy — it just drops to full-width with light horizontal padding. There is no "hamburger reveal" or slide-out nav; the site is flat enough that things fit.

Prose. Figures are full-bleed with an italic muted caption centred below. Images use <picture> with AVIF/WebP/JPG fallbacks and loading="lazy". The Tailwind Typography plugin decorates <p>, <ul>, <figure>, and <figcaption> to match the tokens here; if you're not on Tailwind, the colors_and_type.css primitives in the companion skill produce the same result from raw HTML.

Elevation & Depth

Two shadows, both soft and low:

  • shadow-sm = 0 1px 2px rgba(40,40,40,.08) — default for cards, buttons, tags.
  • shadow-md = 0 2px 6px rgba(40,40,40,.12) — hover state for interactive elements.

There is no blur, no transparency, no glow. Buttons lift exactly 1px (translateY(-1px)) on hover and their shadow grows from sm→md; that is the entire "depth" vocabulary. Transitions are 200ms or less — no bouncing, no spring, no parallax, no scroll-jacking.

Button hover colour. Buttons darken or saturate one ramp step on hover (e.g. button-primary's primary surface shifts toward orange-bright, button-accent's purple toward purple-bright). Hover states aren't modelled as separate component tokens here — they belong in CSS — but the rule is always "one step warmer or brighter on hover, never lighter to the point of losing the hue."

Shapes

Radii are deliberate and small.

Token Value Used for
rounded.xs 2px Sticker avatar frames — boxy on purpose, the sticker is the star
rounded.sm 4px Inline code
rounded.md 6px Cards, <details>, <pre>, admonitions
rounded.lg 8px Tags, blockquotes
rounded.xl 12px Pill buttons

Borders. 1px solid, colour fg-4 (the border-hairline component models this as a 1px bg-4 strip because DESIGN.md has no borderColor slot). No coloured borders anywhere except on semantic admonitions, where a 4px left rule uses the muted accent hue — see admonition-*-rule components. No "left-border accent" cards — the only non-admonition block with a left bar is PullQuote, and that rule is 4px blue, intentionally.

Components

Buttons come in five flavours: button-primary (muted orange, WCAG-safe white text), button-secondary (inverted — fg-0 surface, bg-hard text), button-accent (muted purple), button-ghost (transparent with a 1px border and no shadow), and button-danger (muted red for destructive actions). All share a 12px pill radius and 8×16 px padding. The live site uses -bright variants for button defaults and drops to muted on hover; this spec flips that mapping because muted + white text is the only pairing that hits WCAG AA at normal body size. Prefer the spec's mapping in new work.

Cards use bg-2 surface, 6px radius, spacing.4 padding, and a hairline border (bg-4). The text ramp — text-strong, text-subtle, text-muted, text-caption — lets card content step from headline to caption without drifting off the fg-0/1/2/3/4 scale.

Tags are pill-radius (8px) chips at small body size, on bg-1 — used for post taxonomies and keyword chips.

Blockquote is custom: bg-2 surface, no left border, prefixed with a literal > character — email-style quoting. PullQuote is the only non-admonition block with a coloured left rule; use admonition-info-rule as a reference for the 4px blue bar.

Chat bubbles (Conv) are the character-dialogue pattern. chat-row rows share a bg-soft background; the first rounds top corners, the last rounds bottom, middle rows pull up 1px to form a continuous surface. A 64×64 chat-avatar (2px radius, boxy) sits on the left of each row, fed by a sticker URL.

Admonitions. Six types — info (blue), warning (yellow), tip (green), note (purple), danger (red), success (aqua). Each is a bg-soft card with body text on fg-1, a 4px left rule in the muted hue (admonition-X-rule), and a small bright-hue marker dot beside the title (admonition-X-dot). Body text is not coloured; the hue lives on the rule and the dot so contrast never becomes an issue. Use plain titles "Note", "Warning", "Tip", "Info" — never "👀 Heads up!".

Stickers. Character portraits are fetched live from https://stickers.xeiaso.net/sticker/{character}/{mood} — not stored anywhere in this repo. Characters include xe, mara, cadey, nicole, aoi; moods include aha, happy, confused, coffee, wat. These are the emotional channel of the entire system, replacing what other systems would use emoji for.

Iconography. Deliberately low-icon. When you do need one, use Tabler Icons — 24×24 viewBox, stroke-width="2", stroke-linecap="round", stroke-linejoin="round", fill="none", colour inherits currentColor. SponsorCard drops to 20×20. Never invent an SVG icon — either use Tabler or leave it out.

Sponsor card has the system's only gradient: a 2px top rule that runs orange-bright → purple-bright. Modelled here as two components, sponsor-rule-start and sponsor-rule-end, each filling half the card width.

Do's and Don'ts

Do

  • Use warm cream surfaces (bg-hard #f9f5d7) in light and warm charcoal (#1d2021) in dark.
  • Pair Podkova 600 headings with Schibsted Grotesk body, always in that order.
  • Keep borders to 1px, shadows to the two-step sm/md pair, and radii to 2/4/6/8/12px.
  • Let links invert to magenta on hover — it's the system's signature.
  • Use stickers from stickers.xeiaso.net/sticker/{char}/{mood} whenever a character speaks.
  • Write sentence-case headings and first-person, conversational body copy. Be specific and slightly funny.
  • Reach for Tabler stroke icons only when text alone won't carry the meaning.
  • Prefer muted accent hues on button surfaces with white text — they're the only pairing that meets WCAG AA.

Don't

  • Don't introduce blue-purple gradients, neon, or cool grays — the palette is warm-neutral Gruvbox.
  • Don't add emoji decoratively; stickers are the emotional channel.
  • Don't use "left-border accent" cards — only PullQuote and admonitions carry a left rule, and both are intentional.
  • Don't invent SVG icons; use Tabler or a placeholder and flag it.
  • Don't override a:hover — the magenta invert is load-bearing.
  • Don't pad with filler copy or hype; the voice is spare and personal.
  • Don't use blur, transparency, glow, or any animation beyond the 1px hover lift and 200ms fades.
  • Don't swap -bright accents onto button surfaces with white text — the contrast dips below 4.5:1.