Implements the StockTaper / Loosely Organized Research Facility design system for building UI components, pages, and layouts. Use when the user asks to "build a component", "create a page", "add a section", "style this", "design a layout", "update the UI", "add a card", "create a form", "add dark mode", "fix dark mode", "theme toggle", mentions design tokens, or references the StockTaper visual language. Covers color tokens, typography (IBM Plex Mono), dark mode (CSS variable swap via next-themes), dashed-border aesthetic, component patterns, responsive grids, and MDX prose styling.
This design system defines the visual language for the Loosely Organized Research Facility. Every component, page, and layout MUST adhere to these constraints. The aesthetic is monochromatic, monospace, minimal -- inspired by vintage research documents and financial tickers.
IMPORTANT: Never introduce these anti-patterns:
@import for Tailwind plugins (use @plugin in Tailwind v4)@theme inline (breaks dark mode -- use @theme so utilities emit CSS variable references)dark: prefixed Tailwind utilities (dark mode is handled by CSS variable swap, not utility prefixes).dark class, so both themes maintain the same vintage monochrome aestheticAll colors are CSS custom properties defined in src/app/globals.css and exposed as Tailwind utilities via @theme (NOT @theme inline -- this is critical for dark mode to work, see Dark Mode section).
| Token | Hex | Usage |
|---|---|---|
--color-cream | #FBF7EB | Page background, card backgrounds |
--color-ink | #141414 | Primary text, headings, high-contrast |
--color-charcoal | #393939 | Secondary text, borders, subtle UI |
--color-muted | #474747 | Tertiary text, metadata, placeholders |
--color-divider | #d4c9a8 | Borders, separators, horizontal rules |
--color-positive | #2F7D31 | Success states, gains, published badge |
--color-negative | #C6392C | Error states, losses, warnings |
--color-warning | #D97706 | Warning states, caution indicators |
bg-creamtext-inktext-charcoal or text-mutedborder-divider with border-dashedDark mode is built into the system and requires NO special handling from component authors. It works automatically through CSS custom property overrides.
.dark class on <html> (class strategy, system-aware)@theme in globals.css defines light-mode CSS variables as Tailwind utilities.dark {} in globals.css overrides those same variables with dark-mode values@theme (not @theme inline) is used, Tailwind utilities emit var(--color-*) references that respond to the .dark overrideCRITICAL: Using @theme inline would bake literal hex values into utilities, breaking dark mode. Always use @theme.
| Token | Light (default) | Dark (.dark class) |
|---|---|---|
--color-cream | #FBF7EB (warm parchment) | #1E1E1E (near black) |
--color-ink | #141414 (near black) | #E8E2D4 (warm white) |
--color-charcoal | #393939 (dark gray) | #C4BFAF (light gray) |
--color-muted | #474747 (mid gray) | #9E9888 (muted warm) |
--color-divider | #d4c9a8 (tan) | #333333 (dark gray) |
--color-positive | #2F7D31 (deep green) | #4CAF50 (bright green) |
--color-negative | #C6392C (deep red) | #EF5350 (bright red) |
--color-warning | #D97706 (amber) | #F59E0B (bright amber) |
--color-code-bg | #393939 | #1e1e1e |
--color-code-text | #FBF7EB | #E8E2D4 |
--color-code-inline-bg | rgba(57,57,57,0.1) | rgba(255,255,255,0.1) |
| Component | Location | Purpose |
|---|---|---|
| ThemeProvider | src/components/ThemeProvider.tsx | Wraps app in next-themes provider (class strategy, system default) |
| ThemeToggle | src/components/ui/ThemeToggle.tsx | Moon/sun SVG icon button for manual toggle |
ThemeToggle lives in the Header alongside navigation links.
dark: prefixed utilities -- the variable-swap approach handles everythingbg-cream, text-ink, etc.)@theme inline -- this bakes literal values and breaks the .dark overridebg-charcoal/10 will use the correct charcoal value in both themes.prose overrides in globals.css reference var() valuesFor the full dark mode color table and usage patterns, consult references/design-tokens.md.
For detailed scale and patterns, consult references/typography-scale.md.
font-mono everywhere (maps to IBM Plex Mono)text-base (16px) with leading-relaxedtext-4xl md:text-5xl font-bold tracking-tighttext-2xl font-bold tracking-tighttext-xl font-bold uppercasetext-xs uppercase tracking-[0.5px]Dashed borders are the defining visual element. Use them consistently.
| Context | Classes |
|---|---|
| Cards | border border-dashed border-divider rounded-[var(--radius-card)] |
| Code blocks | border border-dashed border-divider |
| Tables | border border-dashed border-divider divide-y divide-dashed divide-divider |
| Horizontal rules | border-divider (solid, exception to dashed) |
| Links | underline decoration-dashed underline-offset-4 |
| Link hover | decoration-solid (dashed -> solid transition) |
--radius-card: 6px /* Cards, containers */
--radius-button: 4.8px /* Buttons, inputs */
--radius-full: 9999px /* Pill shapes: badges, topic tags */
For full grid and spacing patterns, consult references/layout-patterns.md.
All page content wraps in Container:
max-w-[1152px]px-6mx-autogrid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6
Adjusts column count by passing columns prop (1, 2, or 3).
For the full component catalog with props and examples, consult references/component-catalog.md.
| Component | Location | Purpose |
|---|---|---|
| Container | src/components/layout/ | Max-width page wrapper |
| Header | src/components/layout/ | Site navigation |
| Footer | src/components/layout/ | Copyright bar |
| Button | src/components/ui/ | Primary/secondary actions |
| Badge | src/components/ui/ | Status indicators |
| DossierCard | src/components/ui/ | Bordered content card with badge |
| Divider | src/components/ui/ | Horizontal separator |
| ArrowLink | src/components/ui/ | Text link with -> glyph |
| TopicTag | src/components/ui/ | Pill-style topic label |
| ContentCard | src/components/content/ | Post preview card |
| ContentGrid | src/components/content/ | Responsive card grid |
| ContentMeta | src/components/content/ | Date, reading time, status |
| CopyButton | src/components/mdx/ | Clipboard copy for code blocks |
| ExperimentEmbed | src/components/mdx/ | Lazy-loaded interactive experiments |
| ThemeProvider | src/components/ | next-themes wrapper (class strategy) |
| ThemeToggle | src/components/ui/ | Dark/light mode toggle button |
src/components/layout/): Structural wrappers, navigationsrc/components/ui/): Reusable atomic elementssrc/components/content/): Content-specific compositionssrc/components/mdx/): Components used inside MDX renderingEvery component in this system follows these conventions:
// 1. Import cn utility for conditional classes
import { cn } from "@/lib/utils"
// 2. Define props interface
interface ComponentNameProps {
className?: string // ALWAYS accept className for composition
children?: React.ReactNode
// ... specific props
}
// 3. Export named function component
export function ComponentName({ className, children, ...props }: ComponentNameProps) {
return (
<div className={cn(
// Base classes first
"font-mono text-ink",
// Then structural classes
"border border-dashed border-divider rounded-[var(--radius-card)]",
// Then allow override
className
)}>
{children}
</div>
)
}
Before finalizing any component:
font-mono (no sans/serif)dashed where applicableclassName prop for compositioncn() from @/lib/utils for class mergingdark: prefixed utilities (dark mode uses CSS variable swap)For full MDX prose configuration, consult references/mdx-content-guide.md.
All MDX rendering is handled through mdx-components.tsx at the project root. Custom components override every HTML element to enforce the design system within prose content.
text-4xl (h1) to text-base (h6), with h3/h6 uppercasetext-charcoal decoration-dashed with decoration-solid on hoverbg-charcoal/10 rounded px-1.5 py-0.5 text-smbg-charcoal text-cream border-dashed with language badge + copy buttondivide-dashedborder-l-2 border-divider pl-4 italic text-mutedrounded-[var(--radius-card)] with optional figcaption