orenbj.com
Design System
Two stylesheets and one rule about when not to use them. This page is built from the system it documents — every example below is a live element, not a screenshot.
How it is put together
brand.css holds the tokens and the header styles. The build stamps both into every page, so the bar is in the HTML on first paint rather than waiting on a script — which matters on a page whose head loads a blocking CDN script.
ui.css is the component layer, opted into per page. Build a page from these classes and it matches the home page by construction, and inherits the contrast results below rather than re-earning them.
The build stamps the header and brand.css into every page. A page that wants the component layer adds one line:
<link rel="stylesheet" href="/assets/ui.css">
Colour
One dark ground, three accents drawn from a single gradient. Contrast was measured against --ob-bg, not estimated; the lowest result on the site is 7.4:1 against a 4.5:1 requirement.
Purple is the interaction colour: hover, focus, and the primary button. Orange marks the thing you are on. Blue is reserved for the gradient and the particle field. Keeping those roles fixed is what stops the palette reading as decoration.
Type
System UI throughout — it costs no request, renders natively on every platform, and a personal site does not need a webfont to have a voice. The gradient display face is the one bold move, reserved for a page title.
Oren Ben-Joseph
Section heading — .ob-h2
Subsection — .ob-h3
Lead paragraph, one step up from body copy.
Body copy sits at the browser default, with a measure kept near 65 characters so a line stays readable.
Small print and captions — .ob-small .ob-muted
Tabular figures 1,204,880 · 3,957 KB · 19.3:1
Cards
A card animates on hover only when it is a link or a button. A static card that lifts under the cursor promises an interaction it does not have, so .ob-card stays still and a.ob-card moves.
Interactive card
An a.ob-card — lifts, borders purple, and goes somewhere.
Static card
A plain .ob-card — holds still, because nothing happens if you click it.
Controls
One focus treatment across every control: a two-pixel purple outline, offset so it never crowds the element it marks.
Data
Figures line up with tabular numerals and numeric columns align right, so a column can be scanned rather than read. A wide table scrolls inside .ob-table-scroll instead of pushing the page sideways.
| Page | Self-hosted | Third-party | Status |
|---|---|---|---|
| Census Mobility Map | 1,660 KB | 0 KB | Bundled |
| 3-Lane LA | 898 KB | 0 KB | Vendored |
| Metric Equinox | 144 KB | 509 KB | Exempt |
| Transit Fares | 27 KB | 731 KB | Rebuild |
| DiSC Assessment | 72 KB | 3,957 KB | Rebuild |
Which pages adopt it
A shared look is worth having up to the point where it starts erasing what makes a project worth showing. The header and palette go everywhere. The component layer goes where pages are genuinely alike.
Rules worth keeping
Accent roles are fixed. Purple means interactive, orange means current, blue stays decorative. A palette reads as a system when its colours mean something and as decoration when they do not.
Motion is optional. Everything that animates is wrapped in a reduced-motion query. The home page particle field settles to a static frame rather than stopping blank.
Contrast is measured, not judged. Every pair above was computed. Substituting an untested colour forfeits the result.
The gradient is spent once per page. On the title, and nowhere else.