╔══════════════════════════════════════════════════╗
║  luna.sys // design system · phosphor v4.0.1    ║
╚══════════════════════════════════════════════════╝

design system.

a small, opinionated set of tokens and primitives powering imlunahey.com. mono-first, black-first, phosphor accent. every component is plain html + css. no framework required.

version v0.0.0updated apr 19, 2026primitives 18tokens 42status ● stable
01 //

typography

two faces. jetbrains mono does everything structural — body, ui, numbers, labels. doto is reserved for display: hero numerals, section titles, moments of character. nothing else. ever.

faces

2 families
display
Doto Aa
doto, weights 100–900 · variable
mono
Aa 0123
jetbrains mono, weights 100–800 · variable
--font-display · --font-monofont-family

scale

10 steps
display.5xl--fs-5xl
rewriting.
88px / 0.95 / -0.03em
display.4xl--fs-4xl
design system.
56px / 0.98 / -0.02em
display.3xl--fs-3xl
section headline
36px / 1.05 / -0.02em
text.2xl--fs-2xl
large heading
24px / 1.25
text.xl--fs-xl
subhead · 18px
18px / 1.4
text.lg--fs-lg
large body text, used sparingly for emphasis.
15px / 1.5
text.md--fs-md
the default body size — every paragraph, every ui label, every log line. if in doubt, use this.
13px / 1.5
text.sm--fs-sm
dense ui · inline controls · metadata
12px / 1.5
text.xs--fs-xs
tiny labels, timestamps, chips
11px / 1.4
text.micro--fs-micro
micro · legal · footer
10px / 1.4
--fs-{micro,xs,sm,md,lg,xl,2xl,3xl,4xl,5xl}scale
02 //

color.

a near-monochrome palette. blacks and greys do 98% of the work. one phosphor-green accent provides life; one magenta-red alert hue handles errors. never introduce a third accent.

surfaces

4 steps
bg--color-bg#000000
bg.panel--color-bg-panel#070707
bg.raised--color-bg-raised#0a0a0a
border--color-border#1a1a1a

foreground

4 steps
fg--color-fg#e6e6e6
fg.dim--color-fg-dim#9a9a9a
fg.faint--color-fg-faint#555555
fg.ghost--color-fg-ghost#2a2a2a

accents

phosphor + alert
accent--color-accentoklch(0.86 0.19 145)
accent.dim--color-accent-dimoklch(0.55 0.13 145)
alert--color-alertoklch(0.72 0.19 25)
warn--color-warnoklch(0.85 0.17 85)
03 //

spacing.

a 4-px base grid. all gaps, padding, and margins align to this scale. most ui lives between sp-2 and sp-6. use larger steps only for page-level rhythm.

sp-14px · 0.25rem
sp-28px · 0.5rem
sp-312px · 0.75rem
sp-416px · 1rem
sp-520px · 1.25rem
sp-624px · 1.5rem
sp-832px · 2rem
sp-1040px · 2.5rem
sp-1248px · 3rem
--sp-{1,2,3,4,5,6,8,10,12}scale
04 //

borders + radii.

corners are sharp. radii exist only for the tiniest accents (kbd chips). everything else is 0px. shadows are reserved for the phosphor glow — never soft drop shadows.

1px solid --borderborder.default
1px solid --accentborder.focus
1px dashed --border-brightborder.divider
⌘K
radius: 2pxradius.kbd
0 0 16px accent-glowshadow.glow
┌──────┐
│      │
│  ▣   │
│      │
└──────┘
ascii::frameborder.ascii
05 //

motion.

motion is subtle and deterministic. four easings, short durations (120–400ms). the phosphor-blink and eq bars are the only perpetual animations allowed.

linear
cubic-bezier(0,0,1,1)
timers, progress
ease
default · 240ms
hover, reveal
stepped
steps(8)
typewriter, boot
elastic
cubic-bezier(.68,-.6,.32,1.6)
sparingly, ever
06 //

iconography.

no illustrated icons. we use box-drawing + geometric unicode. they render in the mono face, scale with type, and disappear into the text when you want them to.

●status.on
○status.off
◉pin
▣location
◢ascend
◣descend
→next
←prev
↗external
▸expand
▾collapse
⌘command
⎋escape
⏎return
✕close
✓done
♪music
★star
⚑flag
⌂home
─── ─ ─── · elements · ─── ─ ───
07 //

buttons.

three variants. the primary button is reserved for the single most important action on a screen. everything else is default. ghost for dense toolbars.

.btn · .btn.primary · .btn.ghostvariants
:hover · :active · :focusstates
08 //

inputs.

no labels floating inside. always a small label above. the focus ring is a single-px accent border — no haloes.

email
.inputdefault
search
.input:focusfocus
domain
! must include a tld
.input.errorerror
locked
.input[disabled]disabled
10 //

tables.

flat. rows are separated by a single border line. headers lowercase and muted. the row-hover is the only interactive affordance.

repolang★updated
akari● typescript4122d ago
dns-server● rust895d ago
ip2country● typescript1541w ago
xirelta● typescript672w ago
.tblelement
11 //

lists.

bullets are replaced with mono glyphs: · for unordered, right-aligned digits for ordered, ✓ for checklists.

unordered
· build the dns server
· ship the blog rewrite
· fix the canal boat heater
· learn some rust, finally
ul.list
ordered
01  parse the query
02  check the cache
03  recurse if miss
04  respond + cache
ol.list.ordered
checklist
✓ wire up routing
✓ add mdx support
▢ dark-mode auto
▢ rss feed
ul.check.list.check
12 //

progress.

two flavours. the bar for ui; the ascii-bar for logs and terminal copy. never percentages without context.

year.progress107/365
downloading72%
complete100%
.barui
[29%] ███████░░░░░░░░░░░░░░░░
[72%] ██████████████████░░░░░
[100] ███████████████████████
[err] ███▓▒░░░░░░░░░░░░░░░░░░
.bar-asciiterminal
─── ─ ─── · components · ─── ─ ───
13 //

cards + panels.

everything lives in a .panel. optional .ticks modifier adds corner accents for emphasis. panels never nest.

./widgetactive
body copy
a standard panel has a titled header with a live dot, and body content with 16px padding.
.paneldefault
./featuredpinned
with corner ticks
the .ticks modifier adds phosphor corners — use once per screen, for the hero panel only.
.panel.ticksfeatured
14 //

loading states.

three modes. the spinner is forbidden. use a shimmer skeleton when structure is known; a boot log when it isn't.

.skelskeleton
[ok] init kernel
[ok] mount /home
[ok] connect pds
[..] fetching posts
boot.logterminal
▓▒░
loading · do not close
.loadingblock
15 //

code blocks.

syntax-highlighted source via sugar-high. token colours map to phosphor palette via .sh__token--* css vars; theme lives in src/components/CodeBlock.tsx.

with filename + copy

1
src / components / Greeting.tsx
import { SITE } from '../data';

// greet the visitor — defaults to the site handle
export function Greeting({ name = SITE.name }: { name?: string }) {
  return (
    <p className="t-accent">
      hi, i'm {name}. welcome to the design system.
    </p>
  );
}

bare (no chrome)

1
const greet = (name: string) => `hello, ${name}!`;
console.log(greet('world'));

tokens

8
constkeyword--sh-keyword
"hello"string--sh-string
Promiseclass--sh-class
mapentity--sh-entity
.lengthproperty--sh-property
itemsidentifier--sh-identifier
// notecomment--sh-comment
=>sign--sh-sign
16 //

voice + tone.

three rules. be precise. be lowercase. be slightly dry. no exclamation marks unless someone is actually on fire.

do

"social media is doomed (and that's fine)"

"last commit · 37 minutes ago"

"coffee low"

"i write a blog, ship open source, and stream code when the lighting's kind."

don't

"Welcome to My Personal Blog!!! 🚀"

"Leveraging synergies across the stack"

"Click here to learn more →"

"💻 Software Engineer | 🌍 London | ☕ Coffee Lover"