╔══════════════════════════════════════════════════╗
║  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.

repolangupdated
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"