Instruction file imported from Mikeys-Tech-Lab/poc (
.cursor/rules/visual-design.mdc). Copyright stays with the author.
Visual design principles
Readability first
Text must be readable against its background in both themes. Busy backgrounds need treatment (blur, tint, or overlay) before placing text on them. Check contrast in both Catppuccin Frappé (dark) and Latte (light).
Theme coherence
Visual elements should feel like they belong to the active Catppuccin palette. When overriding backgrounds, use the design tokens (--poc-*) so values stay consistent across themes. Do not introduce colors that clash with the palette.
For underlying modular and extraction principles, see engineering.mdc.
CSS architecture
Styles are modular, imported via custom.css:
| File | Responsibility |
|---|---|
tokens.css |
Design tokens: palette channels, surfaces, borders, shadows, blur. Single source of truth for theming. |
atmosphere.css |
Fullscreen blurred background image + Starlight variable overrides for transparency. |
solid.css |
Overrides for the solid style variant: opaque backgrounds, no blur, no atmosphere. |
surfaces.css |
Frosted glass treatment for header, sidebar, cards, search, pagination, mobile ToC. |
hero.css |
Landing page hero: image, tagline, CTA button. |
controls.css |
Header controls: hamburger, search, theme selector styling + layout. |
typography.css |
Inline code and code links adapted for atmospheric backgrounds. |
All files live in apps/site/src/styles/ and are imported via apps/site/src/styles/custom.css.
Design token system
Tokens use space-separated RGB channels with modern rgb() syntax:
--poc-base: 48 52 70; /* Frappé base channel */
--poc-surface: rgb(var(--poc-base) / 0.3); /* composed surface */
The light theme overrides --poc-base and --poc-overlay channels. Most surface/border tokens auto-resolve — only tokens whose formula differs between themes (e.g. --poc-surface-faint, shadows) need explicit light overrides. This eliminates duplicate :root[data-theme="light"] selectors in component files.
Do not treat inherited light-mode output as equivalent to a designed light mode. If a custom atmospheric surface, callout, reading box, or control is introduced, verify whether light mode needs explicit background, border, shadow, or text adjustments instead of relying on dark-first tokens to carry the design.
Box token contract
When adding or restyling any box-like surface, do not invent local color logic in the component first.
Use the shared token families in tokens.css:
--poc-box-*for the calm shared base surface used by box-like elements in a theme--poc-box-control-*for controls that live inside those boxes--poc-box-row-*for neutral row-style controls, such as accessibility rows--poc-box-boundary-*for boundary frames that need a sober semantic shade--poc-box-note-*,tip,caution, anddangerfor semantic callouts
If a new box needs a different pastel family or emphasis model, extend the token
contract in tokens.css first, then consume those tokens from the component. Do
not solve light mode by adding one-off :root[data-theme="light"] overrides to
each component unless the contract truly cannot express the need.
Default posture: keep the base box surface broadly shared within a theme, then differentiate type through the highlight system (rail, border, title, and accent glow), not through unrelated background colors on each component.
Semantic box accents use one restrained pastel system with fixed hues across themes. Theme changes should only adjust presentation variables such as title contrast, lightness, and opacity for dark vs light contexts. Do not invent a new hue family for light mode.
Style variants
The site supports two visual styles, controlled by the data-style attribute on <html>:
- Atmospheric (default, no
data-styleattribute): blurred background image, frosted glass surfaces, translucent overlays. - Solid (
data-style="solid"): opaque Catppuccin backgrounds, no blur, no atmosphere pseudo-element. Defined inapps/site/src/styles/solid.css.
The theme selector (apps/site/src/components/ThemeSelect.astro) offers 4 explicit options: Dark Atmospheric, Light Atmospheric, Dark Solid, Light Solid. It stores a composite value in localStorage and sets both data-theme and data-style on <html>.
FOUC prevention: apps/site/src/components/ThemeProvider.astro has an inline script that reads localStorage and sets both attributes before render.
Adding a new style variant
Override the --poc-surface-*, --poc-border-*, --poc-blur-*, and --poc-shadow-* tokens under a new [data-style="yourvariant"] selector. Hide or adjust body::before if the variant does not use the atmospheric background. Consumer files (surfaces.css, controls.css, etc.) need no changes — they consume tokens.
Catppuccin hex maintenance
solid.css hardcodes Frappé and Latte surface hex values (Surface0/1/2) for opaque backgrounds. If the Catppuccin flavor changes in astro.config.mjs, update both solid.css and the theme-color meta tags in the head array of astro.config.mjs.
Frosted glass pattern
For translucent UI over the atmospheric background:
- Use a
--poc-surface-*token for the background color. - Apply
backdrop-filter: blur(var(--poc-blur-*))for the glass effect. - Use
--poc-border*tokens for borders. - Do not use
!important. Starlight's CSS layers let custom CSS win naturally.
Calm over flashy
Prefer subtle effects. Gentle blur, muted overlays, and soft borders over sharp contrasts, heavy shadows, or saturated accents. The site should feel quiet and focused.
Psychological safety for thinking
The UI is part of the practice. It should lower cognitive pressure so readers can slow down and think.
Guardrails:
- Calm, not performative: avoid visual noise, attention traps, and novelty for its own sake.
- Generous navigation: keep the context triad intact (sidebar = place, center = reading, right rail = structure).
- No epistemic hierarchy: the register system (Everyday/Orientation/Practitioner) must never hide structure. Only entry, pacing, and voice change.
Density and spacing
Prefer a clear, repeatable vertical rhythm over one-off margin tweaks.
- Use layout gaps over margins where possible (e.g.
flex/gridwithgap) to avoid margin-collapsing surprises. - Match gaps intentionally: lede → grid spacing should usually equal the grid’s own
gapunless there is a specific reason not to. - When a section feels “stuffed”: reduce list length, split content, or demote secondary items (e.g. “Honorable Mentions”) rather than shrinking font sizes.
Quick visual checks (required for frontend changes)
- Toggle theme: Dark Atmospheric ↔ Light Atmospheric.
- If you changed custom surfaces or controls, tune both atmospheric themes explicitly. Dark done and light inherited is not complete.
- If the same choice appears in more than one UI surface, check that each surface exposes the same option model and selected state.
- If a control label names an action, check that the behavior still matches the label.
- Toggle register: check all available registers on the changed page. At minimum confirm Practitioner ↔ Orientation parity, and include Everyday on route-scoped pages where it exists.
- Spot-check the active locale surface:
/en-us/for the changed page or component.
Site-wide atmospheric background
The blurred, theme-tinted background image applies to all pages. Starlight's --sl-color-bg, --sl-color-bg-nav, and --sl-color-bg-sidebar are overridden via tokens in atmosphere.css so the atmosphere shows through everywhere.
© 2026 Mikey Sebastian Drozd. Licensed under CC BY 4.0. Repository code and tooling: MIT.
Source: https://github.com/Mikeys-Tech-Lab/poc