The sodium lamp turns the whole city this colour.
A dark colour scheme with São Paulo as its reference — the sodium street lamp,
exposed concrete, the free span of the MASP, the drizzle before the rain.
sp-night.github.io · palette · spec · ports · contribute
All three are dark, by decision. They differ in hue and chroma, never in brightness — nothing gets easier or harder to read when you switch.
Five surfaces · four levels of text · eight accents · six bright pairs — 23 colours, each named after the thing in São Paulo it came from. Every pairing is measured before it ships, never eyeballed.
Full detail — glosses, OKLCH, WCAG ratios, the ANSI map — lives at sp-night.github.io/palette.
The 23 hex values, all three flavours
| colour | noite |
garoa |
jaragua |
|---|---|---|---|
vao |
#0f101a |
#151719 |
#050806 |
laje |
#151723 |
#1c1e20 |
#0c100d |
concreto |
#1d1f2d |
#26282a |
#151a17 |
vidro |
#272937 |
#313436 |
#202622 |
fiacao |
#373943 |
#414346 |
#323733 |
fg_vivo |
#e7ebff |
#dae1ea |
#e7eee9 |
fg |
#d3d7eb |
#c7cdd6 |
#d3dad5 |
fg_dim |
#868999 |
#8d949f |
#868b87 |
fg_muted |
#707380 |
#767d88 |
#707471 |
brasa |
#ea5d5d |
#d87676 |
#ea5d5d |
sodio |
#f2984a |
#e3a068 |
#f2984a |
taxi |
#f5c66b |
#e7cb8a |
#f5c66b |
ibira |
#89d093 |
#9acaa2 |
#79d488 |
estaiada |
#38b59e |
#56b19f |
#05b89e |
sereno |
#5dbec4 |
#77babf |
#60bfb8 |
marginal |
#6e92de |
#7993cb |
#6e92de |
temporal |
#b094e2 |
#af97d3 |
#b094e2 |
brasa_vivo |
#ff716f |
#ed8988 |
#ff716f |
taxi_vivo |
#ffdc9c |
#fbdf9d |
#ffdc9c |
ibira_vivo |
#9ce4a6 |
#addeb5 |
#8ce99b |
sereno_vivo |
#71d1d7 |
#8acdd2 |
#73d2cb |
marginal_vivo |
#80a5f2 |
#8ba6df |
#80a5f2 |
temporal_vivo |
#c3a7f6 |
#c2aae7 |
#c3a7f6 |
One repository per themed app, each holding finished theme files — plain text, no build step to use them. A port is listed only once it is published, so everything below can be installed today, and each one has its own page with install instructions, the key-to-role table and a preview.
The catalogue lives in sp-night/sp-night
at registry/ports.yml and is vendored here as
src/data/ports.yml. The table below is generated from it
by npm run assets, and npm test fails if it falls behind.
| Port | Group | Installs to |
|---|---|---|
| Ghostty | Terminals | ~/.config/ghostty/themes/sp_night_{flavor} |
| kitty | Terminals | ~/.config/kitty/sp_night_{flavor}.conf |
| Alacritty | Terminals | ~/.config/alacritty/themes/sp_night_{flavor}.toml |
| eza | Shell & CLI | ~/.config/eza/theme.yml |
| Helix | Editors | ~/.config/helix/themes/sp_night_{flavor}.toml |
| herdr | Multiplexers | ~/.config/herdr/config.toml |
Theming another app? See CONTRIBUTING.md. The palette and the
role layer are published as data — /palette.json
and /roles.json — so the colour decisions
are already made and nothing needs to be copied by hand.
The website. A static Astro site, no UI framework. Colour data
is vendored in src/data/ and flows through one typed module, src/data/palette.ts:
- No component reads the JSON directly — everything derives at build time.
- No hex literal exists outside
src/data/— enforced by a test that scans the tree. - The contrast policy (body text AAA on every surface, dim text AA, accents AA on the deepest background) is asserted in CI, so a palette drop that breaks it never deploys.
npm ci
npm run dev # dev server
npm run check # astro type-check
npm test # vitest: colour maths, data shape, contrast policy, no-raw-hex rule
npm run build # static build into dist/
npm run assets # regenerate favicons, logos, OG cards, palette strips and the README tablesEvery image in this README is generated from the palette by npm run assets —
if the colours change, the brand follows.
Push to main → deploy.yml runs type-check, tests and build, then publishes to
GitHub Pages. Other branches and PRs run the same checks via ci.yml without deploying.
Everything in src/data/ except content.ts, palette.ts and ports.ts is
vendored from sp-night/sp-night:
the palette, the role layer, the audit summary and the port catalogue. Editing
those copies here is a change that disappears at the next sync.
- Palette, roles or catalogue change → make it in the engine. Its
sync-ports.ymlcopies the files across, redrawspublic/previews/and opens a pull request here, gated on this repository's ownnpm run check && npm test && npm run build. - A new port → list it in the engine's
registry/ports.yml. Merging the sync pull request publishes/ports/<slug>— the page is generated from the entry, so there is nothing to write here. - Contributor list →
npm run contributors, or leave it to the weekly workflow.
npm run assets regenerates the images and the README tables, and is the one
step still run by hand — npm test fails when its output is stale.
Contributions are welcome — see CONTRIBUTING.md for how to
propose a port or report a colour problem, and SITE.md for the full
design plan and decision log.
SP Night — built by its contributors · MIT