Theming and palettes
Built-in palettes
Section titled “Built-in palettes”Four palettes ship with every scene:
| Name | Mood | Background |
|---|---|---|
abyss |
Deep water, cool ink, orange accents | Black |
neon |
Terminal green, cyan, hot pink | Near-black blue |
mono |
Quiet grayscale | Black |
paper |
Warm paper with dark ink | #f3eee4 |
<AsciiSharks palette="abyss" /><AsciiRobots palette="paper" /><AsciiJellyfish palette="neon" /><AsciiDog palette="mono" />palette accepts a built-in name, "auto", or a full Palette object. The default is
"abyss".
"auto" follows the host’s colour scheme: paper when prefers-color-scheme: light
matches, abyss otherwise. It is resolved when the scene is created, so remount the scene
if the OS theme changes while it is running. On the server it resolves to abyss.
What a palette controls
Section titled “What a palette controls”A palette is semantic, not a list of arbitrary colors:
| Token | Type | Meaning |
|---|---|---|
name |
string |
Identifier for your own bookkeeping |
dark |
boolean |
Inverts body shading; true for dark backgrounds |
background |
string |
Canvas background |
grid |
string |
Optional grid and depth lines |
fish |
[string × 5] |
Five brightness levels, dimmest first |
wake |
RGB |
Motion trails and water disturbance |
heat |
RGB |
Alert, heat, or danger accents |
heatHot |
RGB |
Brighter heat accent |
accents |
string[] |
Species tints and small marks |
eye |
string |
Eye and sensor ink |
mark |
RGB |
Body markings |
textOnBody |
string |
Glyph color on a solid body cell |
textOnBodyAlt |
string |
Alternate glyph color on body |
edge |
RGB |
Anti-aliased silhouette edge |
RGB is [number, number, number]. Strings are complete CSS colors, so rgba(...) works.
Each scene keeps its own species tint for bodies. The palette controls the water, school, wake, eyes, and ink around them.
Custom palette
Section titled “Custom palette”import type { Palette } from "ascii-things/sharks";
const blueprint: Palette = { name: "blueprint", dark: true, background: "#081524", grid: "rgba(120, 190, 255, 0.08)", fish: [ "rgba(160, 210, 255, 0.28)", "rgba(160, 210, 255, 0.42)", "rgba(160, 210, 255, 0.58)", "rgba(160, 210, 255, 0.74)", "rgba(200, 230, 255, 0.9)", ], wake: [110, 190, 255], heat: [255, 150, 70], heatHot: [255, 220, 150], accents: ["#6fb8ff", "#a5d8ff", "#ffb26b"], eye: "#d8ecff", mark: [40, 80, 120], textOnBody: "#04101c", textOnBodyAlt: "#0a1c2e", edge: [120, 190, 255],};Pass it anywhere a palette is accepted:
<AsciiJellyfish palette={blueprint} seed={3} />const bloom = createJellyfish(canvas, { palette: blueprint, seed: 3 });Palettes are JS objects, not CSS variables, because the renderer paints glyphs cell by cell and needs numeric RGB for blending. Treat a custom palette as a JS constant, not a new object on every render.
resolvePalette() is exported for adapter authors. It returns the matching built-in palette,
the host-appropriate palette for "auto", your object unchanged, or abyss for an unknown
name.
import { PALETTES, resolvePalette } from "ascii-things/sharks";
const paper = resolvePalette("paper");const abyss = resolvePalette();const available = Object.keys(PALETTES);Canvas font
Section titled “Canvas font”Scenes inherit the canvas font-family and pass it to the glyph renderer. Set it from CSS:
.ascii-things { font-family: "IBM Plex Mono", ui-monospace, monospace;}The default is the inherited monospace face. You can also set it directly with the React
fontFamily prop or the font-family custom-element attribute:
<AsciiDog fontFamily='"IBM Plex Mono", ui-monospace, monospace' /><ascii-dog font-family='"IBM Plex Mono", ui-monospace, monospace'></ascii-dog>UI chrome is separate
Section titled “UI chrome is separate”ascii-things/ui.css uses its own variables:
:root { --ascii-accent: #ff7a1a; --ascii-bg: #070809; --ascii-panel: #0b0d0f; --ascii-ink: #ece8df; --ascii-muted: rgba(236, 232, 223, 0.56); --ascii-faint: rgba(236, 232, 223, 0.32); --ascii-line: rgba(236, 232, 223, 0.14); --ascii-line-soft: rgba(236, 232, 223, 0.08); --ascii-accent-soft: rgba(255, 122, 26, 0.14); --ascii-font: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, monospace; --ascii-radius: 2px;}Changing these variables restyles the stage, prompt, readout, tabs, and hero. It does not change canvas pixels. To theme the scene itself, pass a palette.
This split is deliberate:
- Canvas palette is data the renderer blends per cell.
- UI variables are ordinary CSS for DOM chrome.
Using CSS variables for canvas colors would mean reading computed styles every frame or invalidating the glyph cache on theme changes. Keeping them separate is faster and simpler.
Dark and light pages
Section titled “Dark and light pages”The built-in paper palette is designed for light pages, and abyss, neon, and mono are
designed for dark pages. A scene does not know its host page’s theme; pick the palette that
matches.
For a theme toggle:
const [dark, setDark] = useState(true);
<AsciiDog palette={dark ? "abyss" : "paper"} seed={7} />;The UI CSS variables are the opposite: set them from your app’s theme, and the frame follows.
Calm is not a palette
Section titled “Calm is not a palette”calm removes flicker, sparks, shimmer, and other high-frequency decoration. It preserves
the palette. Use it for quieter pages and for users who prefer less motion:
<AsciiRobots palette="paper" calm />Reduced motion is separate and automatic: the engine settles the world and renders one still frame. See Accessibility.