latentspace.

Design System — PARKCHEOLHEE-lab.github.io

Design System — PARKCHEOLHEE-lab.github.io

This is the rule book for any visual change to this Jekyll blog. Read it before touching _sass/, _layouts/, _includes/, post HTML, or assets/css/. Token discipline is enforced by convention and review, not by a linter — this repo uses Sass with Jekyll, and the JS-ecosystem stylelint flow does not apply. The agent (Claude / Codex) is the enforcer.

Single source of truth

All design tokens live in _sass/index.sass as Sass variables. assets/css/index.sass imports them first, so every other partial (basic, layout, classes, latentspace) sees them.

When introducing a new color, spacing, or font value, add a Sass variable to _sass/index.sass and reference it from the partial. Do not write a raw hex literal in any partial except the two excluded zones below.

Tokens

Type

Token Value Use
$font-family "PT Sans", -apple-system, ... All body and headings
$font-size calc(0.7rem + 0.2vw) Fluid base body size
$font-weight 400 Body
$heading-weight 600 h1–h6
$line-height 1.6 Body

Smaller utility sizes are written inline as calc(0.6rem + 0.2vw) / calc(0.45rem + 0.2vw) / calc(0.35rem + 0.2vw). These follow the same fluid pattern (rem floor + 0.2vw scaling). When adding a new size, keep the same shape; do not introduce a non-fluid px-only size.

Color

Token Value Use
$dark #000000 Body text on light surfaces
$light #ffffff Page background
$text-dark rgb(70, 70, 70) Strong / table heading text, post titles
$link-color #66a1ff Standard <a> link
$featured-color #0011e3 Featured underline (u.wavy-underline), pinned title bg
$inprogress-color #1aff00 WIP underline
$bg-subtle #f7f7f8 Chat bubble + code block background
$text-chat #343541 Chat-style body text
$divider #e5e5e6 Horizontal rule, hairline divider
$link-chat #4b83ec Link inside chat bubbles

For tinted overlays (borders, hover states, disabled text) use the existing reduce($percent) function — it returns rgba(mix($dark, $light), $percent / 100), so the same percentage gives a consistent tint across the site.

Spacing

The repo uses an em-based rhythm, not a fixed pixel grid. Common steps: 0.2em, 0.4em, 0.5em, 1em, 1.5em, 2em. Pick the existing step nearest to what you need; do not invent intermediate values like 0.7em unless there is a clear reason. Body padding scales by viewport via calc(20vw - 6em) (mid) and calc(40vw - 17em) (wide).

Breakpoints

Two breakpoints, both in em:

Defined in assets/css/index.sass. Do not introduce a third breakpoint without a concrete reason; the layout is content-width driven, not device-class driven.

Excluded zones (intentional raw values)

Two scopes are exempt from the “no raw hex” rule. The exemption is scope-bound, not file-bound — same-file rules outside the scope must still use tokens:

  1. _sass/classes.sass .highlight { ... } block (lines ~59–155) — Pygments / Rouge syntax-highlighting palette. The hex values (#f8f8f2, #f92672, #ae81ff, #a6e22e, #e6db74, #66d9ef, #75715e, …) are the Monokai theme. They are a fixed external palette, not design choices, and should not be renamed. Rules outside .highlight in the same file (.title, .more, .archive, .icon, .katex, .rouge-table) are general utilities and DO follow token discipline.

  2. _sass/latentspace.sass (whole file) — the latent-space post map’s stylesheet. Two kinds of raw values live here and both are intentionally exempt:

    • The D3 categorical --label-* palette (#f19cb0, #00b341, …). Each color encodes a post category and is read by js/latentspace.js; renaming would silently change the visualization.
    • Map-UI raw values (map border / hover border, control color, the #969696 control text, tooltip background, zoom button border). These are data-viz infrastructure tuned to look right against the scatter; folding them into general tokens would risk visual drift in the map without benefiting the rest of the site.

If you find yourself wanting to add a third excluded zone, that is a sign the new feature should be a token instead.

Anti-patterns (don’t do these)

What this project does NOT use

Migration backlog (raw hex still present)

Two rows remain unmigrated by design. Each needs a per-case decision before it can be folded into the token system:

File Line Raw value Why deferred
_sass/basic.sass ~130 #f6f6f6 (pre bg) One digit off from $bg-subtle (#f7f7f8). Folding requires a visual check of code blocks across posts; until then, keep separate.
_sass/basic.sass ~433 #9c9c9c (.site-description) No matching token yet. Decide whether to introduce $text-muted or replace with reduce(N) for a single source-of-truth muted gray.

The previous backlog (chat-message bubbles, hr, blockquote, .title-main color, .message-content content) was migrated in the same pass that created this document. Future raw-hex additions should fail review.

Math display overflow

Display-math equations rendered by KaTeX (.katex-display) and MathJax v2 CHTML (.mjx-chtml.MJXc-display) become their own horizontal scroll boundary via a universal rule in _sass/basic.sass. This means wide equations scroll inside the equation block on mobile; you do not need to wrap each \[ ... \] in a <div class="latex-container">.

The legacy .latex-container utility (overflow: auto on a wrapper div) still exists and is harmless when used. Going forward, prefer the implicit universal rule — leaving math markup unwrapped is the correct convention. Existing wrapper divs in older posts are load-bearing-but-redundant; do not remove them in a sweep.

Note: the rule targets the parent display container, not the inner .mjx-full-width span. MathJax v2 sets max-width: none directly on .mjx-full-width, so any rule on that selector gets overridden. The parent .mjx-chtml.MJXc-display is the reliable scroll boundary.

Verification

After any Sass change:

bundle exec jekyll build

If the build succeeds, the Sass compiles. Visual regressions still need a bundle exec jekyll serve + browser check — there is no automated visual diff in this repo.