Rubric

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) uses MarkdownContent at 18px. Only section eyebrows may stay smaller.
  • Native <select> must use NativeSelect for Input-parity field chrome and an inset caret.
  • When you add or change a UI primitive, update this page in the same change.

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.

UtilitySizeUseSample
text-lg18pxMarkdown body, lists, quotes, table cellsArticle body sample
text-body16pxUI descriptions, card copy, menusBody copy sample
text-ui16pxNav, chrome, controlsUI label sample
text-label12pxRare status chips onlySoon
text-eyebrow11pxSection labels onlyFoundations

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.

PathWhat I mark
EmptyVisible empty state
ErrorRetry 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 via MarkdownContent.
  • Keyboard & focus — Interactive controls expose visible focus rings (ring).
  • Motion — Loading and decorative motion respect prefers-reduced-motion where 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

Button

Primary actions. Variants: default, brand, secondary, outline, ghost, link, destructive. Sizes: default, sm, lg, icon.

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.

DefaultMutedNewIn reviewResolvedOverduePageArena

Count — sits beside the label, not a solid sticker. Same coral as Admin accents.

Admin1Users12Feedback99+

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.

Timed curriculum bouts by track and skill.

Component

Accordion

Radix accordion for sectioned content. Supports single or multiple open panels.

Code Arena, Keywords, Sandbox, Job Tracker, and Job Prep tools — everything for the core daily loop.

Component

Table

Tabular data with a scroll wrapper for overflow.

SkillTrackStars
React hooksFrontend3 / 5
SQL joinsFullstack2 / 5
Prompt designAI4 / 5

Component

Separator

Horizontal or vertical divider for grouping related content.

Above the line

Below the line

LeftRight

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)

Loading example list

SkeletonBlock

Component

Spinner & BrandSpinner

Inline Spinner for buttons; BrandSpinner for branded loading states — mark breathes while a blue→purple arc orbits (respects reduced motion).

Spinner
Loadingsm
Loadingmd
Loadinglg

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

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