Design system
Everything in static/ds.css, in the current theme. The painter's Dark button switches it.
Direction
Web 2.0, built on the 90s web. Things have clear edges and vivid colour, like the web around 2006, and sit on bones from the oekaki boards a decade earlier: the painter's window chrome, graph paper under everything, hard pixels. The site should feel like a place where people draw, not like a product dashboard that happens to show pictures.
- Edges are drawn. Every box, control and bar has a line around it or between it and the next. Nothing is marked out by a shadow or a change of shade alone. Rows share one line between them, as the toolbar and its notices do.
- Colour is vivid. The ground is NEO's lavender and night blue. The cucumber lime means "do this" and appears once per view. Orange is for counts, red for what cannot be undone. Use saturated colours from the palette rather than neutral greys.
- The ground is graph paper. NEO's 14px grid runs under every page, so the site and the painter share one surface. Cards, fields and rows sit on it with solid fills and never let it show through.
- Depth is shallow and hard. Shadows have no blur: a raised control sits on a 1px ledge, and a floating menu or dialog casts a 3px offset block. There are no soft, wide glows, and frosted glass appears only where the platform uses it, as in the macOS app's toolbar.
- The 90s stay in the details. What belongs to drawing wears the painter's window: bevel, title bar, grab dots. Drawings keep square corners and a frame, and are never smoothed when scaled. Type is small and dense (13–14px for the chrome), and corners are rounded only a little.
-
No gradients. Every fill is one flat colour: no gloss
on buttons, no fades on bars, no sheen on cards. Depth comes from
lines and the one small shadow, never from shading. CSS may still
use
linear-gradient()as a tool when both stops are the same colour or meet at a hard edge: the grid's 1px lines, a flat tint laid over the ground, a slider's filled part. What you see must never fade from one colour into another. The one exception is the painter itself, whose title bars are NEO's and stay exactly as NEO draws them. -
Native before custom. Use the browser's own element
for a job whenever one exists (
select,details, checkbox, radio, range,fieldset,table) and style it here, rather than building a widget fromdivs and script. Native elements work with the keyboard, screen readers and the phone's own pickers for free, and they survive old engines. Build a custom widget only when no native element does the job, or when the Firefox 56 floor rules the native one out. - Avoid: gradients, borderless flat cards, grey-on-white minimalism, pill-shaped or heavily rounded boxes, large empty margins, and thin light type.
It all has to hold in Firefox 56, the engine oekaki users keep for the old applets. That is one more reason to use lines and solid fills rather than filters and blends.
Colour
Ground and surfaces
--ds-ground
--ds-grid
--ds-surface
--ds-field
--ds-track
--ds-thumb
Ink
--ds-ink
--ds-muted
--ds-faint
Lines
--ds-line
--ds-line-strong
--ds-frame
Action and signals
--ds-accent
--ds-accent-hover
--ds-accent-ink
--ds-accent-line
--ds-focus
--ds-badge
--ds-danger
--ds-danger-hover
States, laid over what is beneath
--ds-hover
--ds-selected
--ds-press
Loading
--ds-skeleton
--ds-skeleton-drawing
The painter's window
--ds-neo-panel
--ds-neo-ink
--ds-neo-text
--ds-neo-light
--ds-neo-shadow
--ds-neo-title
--ds-neo-well
Notices: tint and the ink that reads on it
--ds-success-*
--ds-warning-*
--ds-danger-*
Type
Headings and text
Heading one · 그림 게시판
Heading two · お絵かき
Heading three
Body text is the page's own weight. A link in a sentence is underlined; strong and emphasis read as they do anywhere. Marked text for a search's match.
Help text, under a field or beside a count.
Inline code, and keys: G then R.
Space, corners and depth
Space: a 4px scale
--ds-space-1--ds-space-2--ds-space-3--ds-space-4--ds-space-5--ds-space-6Corners and shadows
Buttons
Native, with no class
Fields
Native, with no class
Drop-downs
Choices
Forms
A plain fieldset is a card and its legend the title; the submit is the lime one.
Native elements
Elements the browser already knows how to do, styled rather than rebuilt.
What is oekaki?
Drawing in the browser, on a small canvas, with a replay of every stroke.
An open disclosure
Opened by default with the open attribute.
A replay that renders differently from the canvas it was recorded on is the worst failure this codebase can produce.
ssh mini 'zsh -l -c deploy.sh'
Table
| User | Drawings | Joined |
|---|---|---|
| 날다람지 | 128 | 2024-03-02 |
| Tandemaus | 41 | 2025-11-19 |
| きゅうり | 7 | 2026-09-01 |
Notices
Flash messages and the htmx error banner, as rows of the header. The level is the colour; Info and Debug are plain, with the accent's dot.
- Your drawing is saved as a draft.
- Welcome, 날다람지!
- This community is about to be archived.
- The passwords did not match. 비밀번호가 일치하지 않습니다.
Surfaces
Settings groups, forms, anything that is a box on the ground.
.ds-stack drops its children's own margins and puts one gap between each.
Whatever comes next is the same distance away.
An alert, as hx-confirm asks through it
People
person() in person_macro.jinja: the name, then the handle as a pill. Everyone's pill is a ring; a supporter's is the accent, with their mark inside. Someone from here is @login_name; someone from another server keeps @[email protected]. handle() is the pill alone, for a line that says more after it. Short of room, a remote pill takes the next line and its name gives way to an ellipsis; its host never does.