
Design system
Rubric
CodePrepped's shared visual and interaction criteria — so the product feels consistent the same way prep makes interviews less random. One system. Built to make you interview-ready.
Toggle light / dark in the header to preview tokens in both themes.
Foundations
What is Rubric?
A rubric is a scoring guide: shared criteria for what “good” looks like. Rubric is that idea applied to UI — tokens, type, and components that keep every screen evaluated against the same standard.
Mission fit
CodePrepped turns the unwritten interview scorecard into a daily system. Rubric does the same for the product: visible standards, fewer surprises.
What it covers
Color and type tokens in globals.css, primitives in @/components/ui, and this living page at /rubric.
Foundations
How we use it
Build product UI from Rubric primitives and tokens — don’t invent one-off styles when a token or component already exists.
- Prefer semantic tokens (
bg-primary,text-muted-foreground) over raw hex. - Compose from
@/components/ui/*— Button, Card, NativeSelect, and the rest documented below. - Readable UI uses
text-body/text-ui(≥16px). Long-form markdown (blog, Arena, Learn) usesMarkdownContentat 18px. Only section eyebrows may stay smaller. - Native
<select>must useNativeSelectfor Input-parity field chrome and an inset caret. - When you add or change a UI primitive, update this page in the same change.
Foundations
Logo
Two assets: the full wordmark for spacious layouts, and the R mark for mobile or condensed chrome.

Wordmark
/brand/rubric-wordmark.png— transparent PNG; desktop hero, docs headers, wide nav.

Mark
/brand/rubric-mark.png— transparent PNG; mobile, condensed layouts, favicon-adjacent uses.
Use RubricLogo (@/components/rubric/rubric-logo) to switch mark ↔ wordmark at the md breakpoint. Assets are transparent — place them on a near-black plate so the gradient stays readable in light and dark themes.
Foundations
Colors
Semantic HSL tokens in globals.css, mapped to Tailwind via @theme. Values shift with light / dark theme.
background
foreground
card
primary
secondary
muted
accent
destructive
brand-blue
brand-purple
border
ring
.brand-gradient
Interview-ready
.brand-gradient-text
Foundations
Typography
Outfit for UI (sans). JetBrains Mono for code and eyebrows (mono). Chrome copy stays at ≥16px. Long-form markdown (blog, Arena, Learn, legal) reads at 18px.
| Utility | Size | Use | Sample |
|---|---|---|---|
| text-lg | 18px | Markdown body, lists, quotes, table cells | Article body sample |
| text-body | 16px | UI descriptions, card copy, menus | Body copy sample |
| text-ui | 16px | Nav, chrome, controls | UI label sample |
| text-label | 12px | Rare status chips only | Soon |
| text-eyebrow | 11px | Section labels only | Foundations |
Markdown h2 — 24px
Markdown h3 — 20px
Outfit body — 18px / 1.625 leading for blog, Arena bouts, Learn, and legal.
JetBrains Mono — const ready = true;
Foundations
Markdown
One renderer: MarkdownContent. Same 18px reading style on the blog, Arena bouts, Learn, Trainer, compare, and legal. Inline code is a quiet muted pill; syntax color stays on fenced blocks. Links are underlined at rest.
Restate the problem
Name the interface before you type useState. Ask the questions that change the code.
| Path | What I mark |
|---|---|
| Empty | Visible empty state |
| Error | Retry on the page |
- Ugly paths before the happy path
- Trade-offs out loud, then a recap
Foundations
Accessibility pledge
We aim for WCAG 2.2 Level AA so everyone can access CodePrepped information and complete core flows.
- Contrast & color — Semantic tokens are tuned for readable contrast; don't rely on color alone to convey meaning.
- Readable type — Chrome stays at ≥16px (
text-body/text-ui). Long-form markdown uses 18px body, 1.625 line-height, and underlined links viaMarkdownContent. - Keyboard & focus — Interactive controls expose visible focus rings (
ring). - Motion — Loading and decorative motion respect
prefers-reduced-motionwhere implemented (e.g. BrandSpinner). - Inclusive by default — Labels, accessible names, and semantic HTML on primitives so assistive tech can use the same criteria sighted users see.
Questions or gaps? Reach us via product updates — we treat accessibility as part of the rubric, not an afterthought.
Components
Primitive showcase
Live demos of every primitive in src/components/ui. New primitives belong here in the same PR.
Component
Badge
Non-interactive status or meta pills. Use Chip when the control is a filter people tap. Use variant count for unread / new totals in nav.
Count — sits beside the label, not a solid sticker. Same coral as Admin accents.
Component
Chip
Toggleable rounded filters. Pass accent as a hex for branded topic chips.
Component
Card
Surface for grouped content. CardTitle accepts as={h1–h4}; CardDescription uses text-body.
Daily prep loop
Finish today's goal, keep your streak, earn XP.
Cards hold interactive or related content without competing with the page hierarchy.
Component
Input, Textarea & Label
Native form controls with consistent height, radius, and focus rings.
Component
Field
Label plus control. Use instead of repeating Label wrappers. tone="muted" for filter panels; srOnly for toolbar selects that still need an accessible name.
Component
SearchInput
Text field with a leading search icon. Replaces ad-hoc Search + Input stacks.
Component
FileInput
Styled file picker with an Upload icon, selected filename, and hover/focus motion. Prefer FileInput over a bare Input type=file.
Component
NativeSelect
Bordered field matching Input, with an inset caret. Prefer NativeSelect over raw select.
Component
FilterBar
Search plus a Filters button. The panel stays closed until requested. Compose Field and NativeSelect inside.
Component
Switch
Binary preference control. Uses primary when checked.
Component
Tooltip
Short hover/focus hint. Prefer for exact values behind compact labels (e.g. 1.2k XP).
Component
Tabs
In-page mode switching without route changes.
Component
Accordion
Radix accordion for sectioned content. Supports single or multiple open panels.
Component
Table
Tabular data with a scroll wrapper for overflow.
| Skill | Track | Stars |
|---|---|---|
| React hooks | Frontend | 3 / 5 |
| SQL joins | Fullstack | 2 / 5 |
| Prompt design | AI | 4 / 5 |
Component
Separator
Horizontal or vertical divider for grouping related content.
Above the line
Below the line
Component
Skeleton
Loading placeholders that reserve layout space. Prefer presets that match the loaded UI; use Spinner only for compact or inline waits. Shimmer respects prefers-reduced-motion.
Primitive
SkeletonText
SkeletonInput
SkeletonList (goal-style rows)
SkeletonBlock
Component
Spinner & BrandSpinner
Inline Spinner for buttons; BrandSpinner for branded loading states — mark breathes while a blue→purple arc orbits (respects reduced motion).
Component
Toaster
Sonner-based toasts, themed with the app. Anchored bottom-right so they clear the landing header. Root layout mounts Toaster once.
Component
VideoPlayer
Mux-backed player with Rubric custom controls — seek, volume, speed, fullscreen, and keyboard shortcuts. Pass null playbackId for the coming-soon shell. Optional pause overlay (eyebrow / title / description) fades out while playing; fill stretches edge-to-edge in a sized parent. playButtonSize accepts sm | md | lg.
With playback
0:00 / 0:00
Space / K play-pause · ← → seek · ↑ ↓ volume · M mute · F fullscreen
Pause overlay
Intro
How your personal plan works
A quick tour of phases, next steps, and how to move through your plan.
0:00 / 0:00
Coming soon shell
Lesson placeholder
Video coming soon
Component
WistiaVideoPlayer
Wistia-backed player in the Rubric video shell. appearance="glass" uses a darker purple chrome, a pulsing frosted play button, and a short fade-in when playback starts. Pass poster for a custom pre-play still. When showCodePreppedBranding is on, Wistia’s playbar wordmark is swapped for the CodePrepped C (or covered if the player won’t allow it).
Wistia embed
