freeCodeCamp presents

Command-line Chic

A dark-first, token-driven, framework-agnostic UI kit by the freeCodeCamp team. Built for learners-by learners. Open source and free to use.

design.freecodecamp.org/llms.txt

Components ship as source - copy them into your project (or point your coding agent at the link above) and make them yours. Full setup in the install guide.

00 · three surfaces, one system

Pick the flavour, keep the behaviour.

  • 01 · React

    <Button variant="cta" />

    Copy-source React + TypeScript. Ark UI under the hood, React 18+ peer. You own the code.

    design.freecodecamp.org/components/button.md
    import { Button } from './ui/button/Button';
  • 02 · HTML

    <button class="btn btn--cta">

    Drop-in vanilla CSS + 3 KB runtime. Download once, self-host.

    <!-- download once from
         design.freecodecamp.org/cdn/styles.min.css -->
    <link rel="stylesheet" href="/styles.min.css">
  • 03 · Tailwind

    bg-cta text-fg-primary

    Preset mirrors every token; palette variants fcc-dark / fcc-light.

    // copy preset.ts + plugin.ts from
    // design.freecodecamp.org/registry/tailwind.md
    export default { presets: [fcc] };

Foundations

Start with the tokens.

The component layer is only honest if the token layer is. Six pillars define how surfaces read, resize, and respond.

Foundations

Tokens before components.

The whole kit reads from a small CSS custom-property layer. Swap a palette class on <html> and the entire surface changes - no component edits, no JavaScript.

Gray ramp

Surface stack - primary through quaternary.

--background-primary-
--background-secondary-
--background-tertiary-
--background-quaternary-
--foreground-primary-
--foreground-secondary-

Accents

CTA + highlight pairs - gold-on-navy, click magnets.

--cta-background-
--cta-foreground-
--highlight-color-

Semantic

Status colors - success / warning / danger / info.

--success-color-
--warning-color-
--danger-color-
--purple-color-

02 · type & spacing

Type scale & spacing

The body type ladder, monospaced display sizes, and the eight-step spacing grid every layout reaches for.

Type scale

Display · --fs-display

Command-line Chic.

Title · --fs-2xl

Ship interfaces that feel like home.

Section · --fs-xl

Composable primitives.

Subhead · --fs-lg

Flat surfaces, square corners.

Lede · --fs-md

Type-heavy, shadow-free, accessible by default.

Body · --fs-base

Eighteen-pixel minimum. Never compromise the body.

Caption · --fs-sm

404-ROUTE - optimized for 120ch lines of code.

Spacing

0px
4px
8px
12px
16px
24px
32px
48px
64px

Group A

Primitives

<Text />

Text

Body-text primitive - semantic-tag agnostic, size + weight + tone variants.

Text

preview

copy sourcesource

Large body - section ledes and emphasis.

Default body - eighteen pixels minimum.

Caption - annotations, metadata, footnotes.

Bold - single-word emphasis only.

import { Text } from './ui/text/Text';

<Text size="lg">Large body - section ledes and emphasis.</Text>
<Text>Default body - eighteen pixels minimum.</Text>
<Text size="sm" tone="muted">Caption - annotations, metadata, footnotes.</Text>
<p class="text text--lg">Large body</p>
<p class="text">Default body</p>
<p class="text text--sm text--muted">Caption</p>
Anatomy
Status
stable
Since
1.0.0
Tokens
  • --fs-body
  • --fs-sm
  • --fs-lg
  • --foreground-secondary
Props
PropTypeRequiredDefaultDescription
asenumnop
sizeenumnobase
weightenumnoregular
toneenumnodefault

<Heading />

Heading

Headline primitive - independent of HTML level. Five visual sizes; tag chosen for outline.

Heading

preview

copy sourcesource

Command-line Chic.

Ship interfaces.

Composable primitives.

Flat surfaces.

Square corners.
import { Heading } from './ui/heading/Heading';

<Heading level={1} size="display">Command-line Chic.</Heading>
<Heading level={2} size="xl">Ship interfaces.</Heading>
<Heading level={3} size="lg">Composable primitives.</Heading>
<Heading level={4} size="md">Flat surfaces.</Heading>
<Heading level={5} size="sm">Square corners.</Heading>
<h1 class="heading heading--display">Command-line Chic.</h1>
<h2 class="heading heading--xl">Ship interfaces.</h2>
<h3 class="heading heading--lg">Composable primitives.</h3>
Anatomy
Status
stable
Since
1.0.0
Tokens
  • --fs-display
  • --fs-xl
  • --fs-lg
  • --fs-md
Props
PropTypeRequiredDefaultDescription
levelenumno2
sizeenumnomd

<Badge />

Badge

Inline status chip - sparingly. Max 2 per row.

Badge

preview

copy sourcesource
DefaultPassedIn reviewFailedPreviewNew
import { Badge } from './ui/badge/Badge';

<Badge>Default</Badge>
<Badge variant="success">Passed</Badge>
<Badge variant="warning">In review</Badge>
<Badge variant="danger">Failed</Badge>
<span class="badge badge--success">Passed</span>
<span class="badge badge--warning">In review</span>
<span class="badge badge--danger">Failed</span>
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
variantenumnodefault

<Avatar />

Avatar

User mark - image, initials fallback, optional online/away status dot.

Avatar

preview

copy sourcesource
import { Avatar } from './ui/avatar/Avatar';

<Avatar size="sm" initials="RW" />
<Avatar size="md" initials="QC" status="online" />
<Avatar size="lg" src="/u/quincy.jpg" alt="Quincy Larson" status="away" />
<span class="avatar avatar--md">
  <span class="avatar__initials">QC</span>
  <span class="avatar__status avatar__status--online"></span>
</span>
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
namestringyes-
srcstringno-
sizeenumnomd
statusenumno-

<Divider />

Divider

Hairline rule. Solid or dashed; horizontal or vertical.

Divider

preview

copy sourcesource

Solid

Dashed

kbdshortcut
import { Divider } from './ui/divider/Divider';

<Divider />
<Divider variant="dashed" />
<Divider orientation="vertical" />
<hr class="divider" />
<hr class="divider divider--dashed" />
<span class="divider divider--vertical"></span>
Anatomy
Status
stable
Since
1.0.0
Tokens
  • --border-strong
Props
PropTypeRequiredDefaultDescription
orientationenumnohorizontal
variantenumnosolid
decorativebooleannofalse

<Spacer />

Spacer

Explicit whitespace on an 8-step scale.

Spacer

preview

copy sourcesource
startend
import { Spacer } from './ui/spacer/Spacer';

<div style={{ display: 'flex' }}>
  <span>start</span>
  <Spacer size={6} />
  <span>end</span>
</div>
<span class="spacer" style="width:48px"></span>
Anatomy
Status
stable
Since
1.0.0
Tokens
  • --space-1
  • --space-2
  • --space-3
  • --space-4
  • --space-5
Props
PropTypeRequiredDefaultDescription
sizenumberno4Step 0–8 maps onto the token scale (0, 4, 8, 12, 16, 24, 32, 48, 64 px). Any other number is treated as a raw pixel value.
axisenumnohorizontal

<Image />

Image

Responsive image wrapper with built-in aspect caption and alt enforcement.

Image

preview

copy sourcesource
freeCodeCamp mark
freeCodeCamp mark on surface-two
import { Image } from './ui/image/Image';

<Image src="/brand/fcc-secondary.svg" alt="freeCodeCamp mark" caption="freeCodeCamp mark" />
<figure>
  <img class="img--responsive" src="/brand/fcc-secondary.svg" alt="freeCodeCamp mark" />
  <figcaption>freeCodeCamp mark</figcaption>
</figure>
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
captionReactNodeno-
figureClassNamestringno

Group B

Actions

<Button />

Button

The workhorse. 3px square border, inverted fill on primary, no shadow.

Button

preview

copy sourcesource
import { Button } from './ui/button/Button';

export function Actions() {
  return (
    <div style={{ display: 'flex', gap: 12 }}>
      <Button variant="cta">Start curriculum</Button>
      <Button variant="secondary">Secondary</Button>
      <Button variant="danger">Dangerous</Button>
      <Button variant="ghost">Ghost</Button>
    </div>
  );
}
<button class="btn btn--cta">Start curriculum</button>
<button class="btn">Secondary</button>
<button class="btn btn--danger">Dangerous</button>
<button class="btn btn--ghost">Ghost</button>
Anatomy
Status
stable
Since
1.0.0
Tokens
  • --cta-background
  • --cta-foreground
  • --foreground-primary
A11y pattern
https://www.w3.org/WAI/ARIA/apg/patterns/button/
Props
PropTypeRequiredDefaultDescription
variantenumnodefault
sizeenumnomd
blockbooleannofalse
isLoadingbooleannofalse

<ToggleButton />

Toggle button

For stateful binary choices - hours pressed, unit selected, dark mode on.

Toggle button

preview

copy sourcesource
import { useState } from 'react';
import { ToggleButton } from './ui/toggle-button/ToggleButton';
export function Pressed() {
  const [on, setOn] = useState(false);
  return (
    <ToggleButton pressed={on} onPressedChange={setOn}>
      {on ? 'On' : 'Off'}
    </ToggleButton>
  );
}
<button class="toggle-btn" aria-pressed="false">Off</button>
<button class="toggle-btn" aria-pressed="true">On</button>
Anatomy
Status
stable
Since
1.0.0
A11y pattern
https://www.w3.org/WAI/ARIA/apg/patterns/button/
Props
PropTypeRequiredDefaultDescription
pressedbooleanno-
defaultPressedbooleanno-
onPressedChange((pressed: boolean) => void)no-
sizeenumnomd

<CloseButton />

Close button

Inline dismiss affordance - modals, alerts, drawers.

Close button

preview

copy sourcesource
import { CloseButton } from './ui/close-button/CloseButton';

<CloseButton onClick={onDismiss} aria-label="Dismiss" />
<button class="close-btn" aria-label="Dismiss">×</button>
Anatomy
Status
stable
Since
1.0.0
A11y pattern
https://www.w3.org/WAI/ARIA/apg/patterns/button/

Group C

Forms

<Input />

Input

Text input with a built-in label, helper text, and error slot.

Input

preview

copy sourcesource

We send one curriculum update per week.

import { FormGroup } from './ui/form-group/FormGroup';
import { Input } from './ui/input/Input';
import { HelpBlock } from './ui/help-block/HelpBlock';

<FormGroup>
  <label htmlFor="email">Email address</label>
  <Input id="email" type="email" placeholder="camper@example.com" />
  <HelpBlock>We send one curriculum update per week.</HelpBlock>
</FormGroup>
<div class="form-group">
  <label class="form-label" for="email">Email address</label>
  <input class="input" id="email" type="email" placeholder="camper@example.com" />
  <p class="form-help">We send one curriculum update per week.</p>
</div>
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
invalidbooleanno-

<Textarea />

Textarea

Multi-line text control with mono variant for code snippets.

Textarea

preview

copy sourcesource

Markdown supported. 240 character cap.

import { Textarea } from './ui/textarea/Textarea';

<Textarea
  id="bio"
  rows={3}
  placeholder="What are you learning right now?"
/>
<textarea class="input input--textarea" id="bio" rows="3"></textarea>
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
variantenumnodefault
invalidbooleanno-
autoResizebooleanno-

<Select />

Select

Native select wrapper - square chevron, matching height with Input.

Select

preview

copy sourcesource
import { Select } from './ui/select/Select';

<Select id="difficulty" defaultValue="intermediate">
  <option value="beginner">Beginner</option>
  <option value="intermediate">Intermediate</option>
  <option value="advanced">Advanced</option>
</Select>
<select class="select" id="difficulty">
  <option>Beginner</option>
  <option selected>Intermediate</option>
  <option>Advanced</option>
</select>
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
invalidbooleanno-

<Checkbox />

Checkbox

Flat, square, accent-filled checkmark.

Checkbox

preview

copy sourcesource
import { Checkbox } from './ui/checkbox/Checkbox';

<Checkbox defaultChecked label="I accept the honor code" />
<Checkbox label="Email me certificate alerts" />
<label class="check"><input type="checkbox" checked /> I accept the honor code</label>
<label class="check"><input type="checkbox" /> Email me certificate alerts</label>
Anatomy
Status
stable
Since
1.0.0
A11y pattern
https://www.w3.org/WAI/ARIA/apg/patterns/checkbox/
Props
PropTypeRequiredDefaultDescription
labelReactNodeno-
labelClassNamestringno

<Radio />

Radio

Mutually exclusive choice - group with `name` or `radio-group`.

Radio

preview

copy sourcesource
import { Radio, RadioGroup } from './ui/radio/Radio';

<RadioGroup name="theme" defaultValue="dark" label="Theme">
  <Radio value="dark" label="Dark - default" />
  <Radio value="light" label="Light" />
  <Radio value="system" label="System" />
</RadioGroup>
<div class="radio-group" role="radiogroup">
  <label class="radio"><input type="radio" name="theme" checked /> Dark</label>
  <label class="radio"><input type="radio" name="theme" /> Light</label>
</div>
Anatomy
Status
stable
Since
1.0.0
A11y pattern
https://www.w3.org/WAI/ARIA/apg/patterns/radio/
Props
PropTypeRequiredDefaultDescription
labelReactNodeno-
labelClassNamestringno

<Switch />

Switch

For settings that take effect immediately - theme, audio, keybinds.

Switch

preview

copy sourcesource
import { Switch } from './ui/switch/Switch';

<Switch defaultChecked label="Keyboard shortcuts" />
<Switch label="Sound effects" />
<label class="switch">
  <input type="checkbox" checked />
  <span class="switch__track"><span class="switch__thumb"></span></span>
  <span class="switch__label">Keyboard shortcuts</span>
</label>
Anatomy
Status
stable
Since
1.0.0
A11y pattern
https://www.w3.org/WAI/ARIA/apg/patterns/switch/
Props
PropTypeRequiredDefaultDescription
labelReactNodeno-
labelClassNamestringno

<Fieldset />

Fieldset

Groups related controls under a legend - required for radio groups.

Fieldset

preview

copy sourcesource
Notification cadence
import { Fieldset } from './ui/fieldset/Fieldset';
import { RadioGroup, Radio } from './ui/radio/Radio';

<Fieldset legend="Notification cadence">
  <RadioGroup name="cadence" defaultValue="weekly">
    <Radio value="weekly" label="Weekly digest" />
    <Radio value="per-cert" label="Per-cert" />
    <Radio value="never" label="Never" />
  </RadioGroup>
</Fieldset>
<fieldset class="fieldset">
  <legend class="fieldset__legend">Notification cadence</legend>
  <label class="radio"><input type="radio" name="cadence" /> Weekly digest</label>
</fieldset>
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
legendReactNodeno-
toneenumnodefault

<FormControl />

Form control

Low-level text control - no label wrapper, for custom layouts.

Form control

preview

copy sourcesource
import { FormControl } from './ui/form-control/FormControl';

<FormControl placeholder="Search the curriculum" />
<input class="input" placeholder="Search the curriculum" />
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
classNamestringno-
invalidbooleanno-
asenumno-

<FormGroup />

Form group

Groups a label, control, and help text with consistent spacing.

Form group

preview

copy sourcesource

Letters, numbers, and dashes. Public.

import { FormGroup } from './ui/form-group/FormGroup';
import { Input } from './ui/input/Input';
import { HelpBlock } from './ui/help-block/HelpBlock';

<FormGroup>
  <label htmlFor="username">Username</label>
  <Input id="username" defaultValue="camper-42" />
  <HelpBlock>Letters, numbers, and dashes. Public.</HelpBlock>
</FormGroup>
<div class="form-group">
  <label class="form-label" for="username">Username</label>
  <input class="input" id="username" value="camper-42" />
  <p class="form-help">Letters, numbers, and dashes. Public.</p>
</div>
Anatomy
Status
stable
Since
1.0.0

<HelpBlock />

Help block

Field-level guidance - neutral, success, or error tone.

Help block

preview

copy sourcesource

We send one curriculum update per week.

Username available.

Username already in use.

import { HelpBlock } from './ui/help-block/HelpBlock';

<HelpBlock>We send one curriculum update per week.</HelpBlock>
<HelpBlock variant="success">Username available.</HelpBlock>
<HelpBlock variant="error">Username already in use.</HelpBlock>
<p class="form-help">We send one curriculum update per week.</p>
<p class="form-help form-help--success">Username available.</p>
<p class="form-help form-help--error">Username already in use.</p>
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
variantenumnodefault

<FormStepper />

Form stepper

Multi-step form progress - Ark UI machine, keyboard navigable.

Form stepper

preview

copy sourcesource
import { FormStepper } from './ui/form-stepper/FormStepper';
import { useState } from 'react';

const STEPS = [
  { id: 'account',  label: 'Account',  description: 'Email + handle' },
  { id: 'goals',    label: 'Goals',    description: 'What to learn first' },
  { id: 'confirm',  label: 'Confirm',  description: 'Review + start' }
];

const [current, setCurrent] = useState('account');

<FormStepper steps={STEPS} current={current} onStepChange={setCurrent} />
<ol class="form-stepper">
  <li class="form-stepper__step" aria-current="step">…</li>
</ol>
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
stepsreadonly FormStepperStep[]yes-
currentstringyes-
onStepChange(id: string) => voidyes-
classNamestringno
isStepAccessible((step: FormStepperStep, state: StepState) => boolean)no-Override step gating. Defaults to array-index comparison.
ariaLabelstringnoProgress

Group D

Navigation

<Tabs />

Tabs

Inverted-fill selected state, matches the platform editor panel.

Tabs

preview

copy sourcesource

Build a function that returns the sum of all odd Fibonacci numbers up to n.

import { Tabs, Tab } from './ui/tabs/Tabs';

<Tabs defaultActiveKey="instructions">
  <Tab eventKey="instructions" title="Instructions">...</Tab>
  <Tab eventKey="tests" title="Tests">...</Tab>
  <Tab eventKey="console" title="Console">...</Tab>
</Tabs>
<div class="tabs">
  <div role="tablist" class="tabs__list">
    <button class="tabs__tab" aria-selected="true">Instructions</button>
    <button class="tabs__tab" aria-selected="false">Tests</button>
    <button class="tabs__tab" aria-selected="false">Console</button>
  </div>
  <div class="tabs__panel">...</div>
</div>
Anatomy
Status
stable
Since
1.0.0
A11y pattern
https://www.w3.org/WAI/ARIA/apg/patterns/tabs/
Props
PropTypeRequiredDefaultDescription
activeKeystringno-
defaultActiveKeystringno-
onSelect((key: string) => void)no-

<Pagination />

Pagination

Page selector - Ark UI pagination machine, keyboard navigable.

Pagination

preview

copy sourcesource
import { Pagination } from './ui/pagination/Pagination';

<Pagination
  count={120}
  pageSize={10}
  page={2}
  onPageChange={(page) => setPage(page)}
/>
<nav class="pagination" aria-label="Pagination">
  <ul class="pagination__list">
    <li><button class="pagination__btn" disabled>‹</button></li>
    <li><button class="pagination__btn" aria-current="page">2</button></li>
    <li><span class="pagination__ellipsis">…</span></li>
  </ul>
</nav>
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
countnumberyes-
pageSizenumberyes-
pagenumberyes-
siblingCountnumberno1
onPageChange((page: number) => void)no-
prevLabelReactNodenoPrevious
nextLabelReactNodenoNext

<Listbox />

Listbox

Persistent selection list - keyboard navigable, single or multi-select.

Listbox

preview

copy sourcesource
  • Frontend
  • Backend
  • DevOps
  • Design
import { Listbox } from './ui/listbox/Listbox';
import { useState } from 'react';

const ITEMS = [
  { value: 'frontend', label: 'Frontend' },
  { value: 'backend',  label: 'Backend' }
];

const [value, setValue] = useState<string | string[]>('frontend');

<Listbox items={ITEMS} value={value} onValueChange={setValue} />
<ul class="listbox" role="listbox">
  <li class="listbox__option" role="option" aria-selected="true">Responsive Web Design</li>
  <li class="listbox__option" role="option">JavaScript Algorithms</li>
</ul>
Anatomy
Status
stable
Since
1.0.0
A11y pattern
https://www.w3.org/WAI/ARIA/apg/patterns/listbox/
Props
PropTypeRequiredDefaultDescription
itemsListboxItem[]yes-
valuestring | string[] | nullnonull
selectionModeenumnosingle
onValueChange((value: string | string[]) => void)no-

<Combobox />

Combobox

Typeahead select - Ark UI combobox machine. Filters as you type.

Combobox

preview

copy sourcesource
  • Responsive Web Design
  • JavaScript Algorithms
  • Front-End Libraries
  • Back End Development
  • Relational Databases
import { Combobox, filterItemsByLabel } from './ui/combobox/Combobox';
import { useMemo, useState } from 'react';

const ALL = [
  { value: 'rwd', label: 'Responsive Web Design' },
  { value: 'js',  label: 'JavaScript Algorithms' }
];

const [query, setQuery] = useState('');
const [value, setValue] = useState<string | null>(null);
const items = useMemo(() => filterItemsByLabel(ALL, query), [query]);

<Combobox
  inputValue={query}
  onInputValueChange={setQuery}
  value={value}
  onValueChange={setValue}
  items={items}
  placeholder='Pick a certification'
/>
<div class="combobox" role="combobox" aria-expanded="true">
  <input class="combobox__input" type="text" />
  <ul class="combobox__list" role="listbox">
    <li class="combobox__item" role="option">Responsive Web Design</li>
  </ul>
</div>
Anatomy
Status
stable
Since
1.0.0
A11y pattern
https://www.w3.org/WAI/ARIA/apg/patterns/combobox/
Props
PropTypeRequiredDefaultDescription
itemsComboboxItem[]yes-
valuestring | nullnonull
inputValuestringno-
placeholderstringno-
disabledbooleanno-
loadingbooleanno-When true, render a `data-part="loading"` row instead of empty/items. Useful during async fetches; pair with `useAsyncComboboxItems` for debounce + cancellation.
errorReactNodeno-Render a `data-part="error"` row with this message. Takes priority over the empty state so transient fetch errors surface clearly.
emptyMessageReactNodeno-Message for the empty state. Rendered when `items.length === 0` and we're not loading. Defaults to "No results".
loadingMessageReactNodeno-Message for the loading state. Defaults to "Loading…".
onValueChange((value: string) => void)no-
onInputValueChange((inputValue: string) => void)no-
renderItem((item: ComboboxItem) => ReactNode)no-
aria-labelstringno-Defines a string value that labels the current element.
aria-labelledbystringno-Identifies the element (or elements) that labels the current element.

<CommandPalette />

Command palette

⌘K spotlight - global commands, grouped, keyboard-shortcut hinted.

Command palette

preview

copy sourcesource
import { CommandPalette } from './ui/command-palette/CommandPalette';

const GROUPS = [
  { label: 'Navigation', items: [
    { id: 'curriculum', label: 'Go to curriculum', shortcut: 'G C' }
  ]}
];

<CommandPalette
  open={open}
  onClose={() => setOpen(false)}
  onSelect={id => navigate(id)}
  groups={GROUPS}
  placeholder='Type a command or search…'
/>
<div class="command-palette" role="dialog">
  <input class="command-palette__search" type="text" />
  <ul class="command-palette__list">…</ul>
</div>
Anatomy
Status
stable
Since
1.0.0
A11y pattern
https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/
Props
PropTypeRequiredDefaultDescription
openbooleanyes-
onClose() => voidyes-
onSelect(id: string) => voidyes-
groupsreadonly CommandPaletteGroup[]yes-
placeholderstringnoType a command…
emptyStateReactNodeno-Slot rendered when `groups` is empty (after filtering).
valuestringno-Controlled search value. Omit for uncontrolled.
onValueChange((next: string) => void)no-
classNamestringno

Group E

Overlays

<Tooltip />

Tooltip

Contextual hint on hover or focus - keep to 80 chars.

Tooltip

preview

copy sourcesource
Runs the public test suite against your code.
import { Tooltip } from './ui/tooltip/Tooltip';
import { Button } from './ui/button/Button';

<Tooltip content="Runs the public test suite against your code.">
  <Button>Run tests</Button>
</Tooltip>
<span class="tip" data-tip="Runs the public test suite against your code.">
  <button class="btn">Run tests</button>
</span>
Anatomy
Status
stable
Since
1.0.0
A11y pattern
https://www.w3.org/WAI/ARIA/apg/patterns/tooltip/
Props
PropTypeRequiredDefaultDescription
contentReactNodeyes-
classNamestringno

<Toast />

Toast

Transient status surface - Ark UI toaster machine, reduced-motion aware.

Toast

preview

copy sourcesource

Heads up

Two failing tests since last run.

New cert available

Backend Development is now public.

import { Toast, Toaster, createToaster } from './ui/toast/Toast';

// 1) Static - render Toast directly (the showcase variant).
<Toast variant='success' title='Saved' description='Synced.' />

// 2) Dynamic - drive a stack via createToaster + <Toaster>.
const toaster = createToaster({});

<Toaster toaster={toaster} />
toaster.create({ title: 'Saved', type: 'success' });
<div class="toaster">
  <div class="toast toast--success">
    <div class="toast__title">Saved</div>
    <div class="toast__description">Your progress is synced.</div>
    <button class="toast__close">×</button>
  </div>
</div>
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
variantenumnoinfo
titleReactNodeno-
descriptionReactNodeno-
dismissiblebooleannotrueEmit a close (`×`) button wired to `onDismiss`. Default: true.
onDismiss(() => void)no-

Group F

Feedback

<Alert />

Alert

Full-width status surface for page-level state - saved, failed, queued.

Alert

preview

copy sourcesource
import { Alert } from './ui/alert/Alert';

<Alert variant="success">All 28 tests pass. Next challenge unlocked.</Alert>
<Alert variant="warning">You have one unsaved edit.</Alert>
<Alert variant="danger">Sign-in failed - check your email address.</Alert>
<div class="alert alert--info">All 28 tests pass.</div>
<div class="alert alert--warning">You have one unsaved edit.</div>
<div class="alert alert--danger">Sign-in failed - check your email address.</div>
Anatomy
Status
stable
Since
1.0.0
A11y pattern
https://www.w3.org/WAI/ARIA/apg/patterns/alert/
Props
PropTypeRequiredDefaultDescription
variantenumnoinfo
titleReactNodeno-
iconReactNodeno-

<Callout />

Callout

Inline sidebar for curriculum notes, tips, and cautions.

Callout

preview

copy sourcesource
import { Callout } from './ui/callout/Callout';

<Callout variant="tip" label="Tip">
  Open the editor fullscreen with <kbd>F11</kbd>.
</Callout>
<aside class="callout callout--tip">
  <p class="callout__label">Tip</p>
  <p class="callout__body">Open the editor fullscreen with <kbd>F11</kbd>.</p>
</aside>
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
variantenumnotip
labelReactNodeno-

<Skeleton />

Skeleton

Placeholder bars during data fetch - reduced-motion aware shimmer.

Skeleton

preview

copy sourcesource
import { Skeleton } from './ui/skeleton/Skeleton';

<Skeleton variant="circle" size={48} />
<Skeleton variant="text" width="80%" />
<Skeleton variant="text" width="60%" />
<span class="skeleton skeleton--circle"></span>
<span class="skeleton skeleton--text" style="width:80%"></span>
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
variantenumnorect
widthstring | numberno-
heightstring | numberno-
linesnumberno-For variant="text": render N stacked line bars.
labelReactNodeno-Screen-reader label announced via visually-hidden span.

<EmptyState />

Empty state

No-data placeholder with title, description, and a primary action.

Empty state

preview

copy sourcesource

No certifications yet

Pick a track and complete five projects to claim your first cert.

import { EmptyState } from './ui/empty-state/EmptyState';
import { Button } from './ui/button/Button';

<EmptyState
  title="No certifications yet"
  description="Pick a track and complete five projects."
  action={<Button variant="cta">Browse curriculum →</Button>}
/>
<div class="empty-state">
  <p class="empty-state__title">No certifications yet</p>
  <p class="empty-state__description">Pick a track…</p>
  <div class="empty-state__action"><a class="btn btn--cta">Browse →</a></div>
</div>
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
iconReactNodeno-
titleReactNodeno-
descriptionReactNodeno-
actionReactNodeno-

Group G

Data display

<Card />

Card

Flat bordered container. Grid-friendly. No shadow, no hover lift.

Card

preview

copy sourcesource

300 HOURS

Responsive Web Design

Build five certification projects using HTML, CSS, flexbox, grid, and accessibility primitives.

import { Card } from './ui/card/Card';
import { Link } from './ui/link/Link';

<Card>
  <Card.Header>
    <span className="card__dot card__dot--purple" aria-hidden="true" />
    <p className="card__hours">300 HOURS</p>
  </Card.Header>
  <Card.Title>Responsive Web Design</Card.Title>
  <Card.Body>Build five certification projects...</Card.Body>
  <div className="progress">
    <div className="progress__bar progress__bar--purple" style={{ width: '62%' }} />
  </div>
  <Card.Footer>
    <span>62% complete</span>
    <Link href="/learn/rwd">Resume →</Link>
  </Card.Footer>
</Card>
<article class="card">
  <header class="card__header">
    <span class="card__dot card__dot--purple"></span>
    <p class="card__hours">300 HOURS</p>
  </header>
  <h3 class="card__title">Responsive Web Design</h3>
  <p class="card__body">Build five certification projects...</p>
  <div class="progress"><div class="progress__bar progress__bar--purple" style="width:62%"></div></div>
</article>
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
borderedbooleanno-

<Panel />

Panel

Borderless surface-two container - sidebars, inspectors, nested regions.

Panel

preview

copy sourcesource

Editor hints

Your code runs against the first test each time you save. Open the console tab to inspect logs.
import { Panel } from './ui/panel/Panel';

<Panel title="Editor hints">
  Your code runs against the first test each time you save.
</Panel>
<div class="panel">
  <p class="panel__heading">Editor hints</p>
  <p class="panel__body">Your code runs against the first test each time you save.</p>
</div>
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
variantenumnodefault
titleReactNodeno-

<Table />

Table

Rule-heavy, square, mono-numeric - for progress and diagnostic data.

Table

preview

copy sourcesource
CertificationProjectsHoursStatus
Responsive Web Design5 / 5300Passed
JavaScript Algorithms4 / 5300In review
Relational Databases0 / 5300Not started
import { Table } from './ui/table/Table';
import { Badge } from './ui/badge/Badge';

<Table>
  <thead>
    <tr><th>Certification</th><th>Projects</th><th>Status</th></tr>
  </thead>
  <tbody>
    <tr><td>Responsive Web Design</td><td>5 / 5</td><td><Badge variant="success">Passed</Badge></td></tr>
  </tbody>
</Table>
<table class="table">
  <thead><tr><th>Certification</th><th>Projects</th><th>Status</th></tr></thead>
  <tbody>
    <tr><td>Responsive Web Design</td><td>5 / 5</td><td><span class="badge badge--success">Passed</span></td></tr>
  </tbody>
</table>
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
stripedbooleanno-
condensedbooleanno-

<DataTable />

Data table

Sortable, selectable, paginated table - header sort buttons, optional row select.

Data table

preview

copy sourcesource
CertHoursStatus
Responsive Web Design300Passed
JavaScript Algorithms300In review
Passed
import { DataTable } from './ui/data-table/DataTable';
import { useState } from 'react';

const [sortBy, setSortBy] = useState({ columnId: 'cert', direction: 'asc' });

<DataTable
  columns={[
    { id: 'cert', header: 'Cert', sortable: true },
    { id: 'hours', header: 'Hours', align: 'right' },
    { id: 'status', header: 'Status', align: 'center' }
  ]}
  rows={rows}
  sortBy={sortBy}
  onSortChange={setSortBy}
/>
<div class="data-table">
  <table class="data-table__table">
    <thead><tr><th class="data-table__header">Cert</th></tr></thead>
    <tbody><tr><td class="data-table__cell">Responsive Web Design</td></tr></tbody>
  </table>
</div>
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
columnsreadonly DataTableColumn<TRow>[]yes-
rowsreadonly TRow[]yes-
rowId((row: TRow) => string)no<TRow,>(row: TRow): string =>Row id accessor. Defaults to `row.id`.
sortByDataTableSort | nullno-
onSortChange((next: DataTableSort | null) => void)no-
selectionReadonlySet<string>no-
onSelectionChange((next: Set<string>) => void)no-
loadingbooleannofalse
emptyStateReactNodeno-
classNamestringno
captionReactNodeno-
skeletonRowsnumberno3Number of skeleton rows to emit while `loading`.

<DescriptionList />

Description list

Term + detail metadata pair - stacked or inline layout.

Description list

preview

copy sourcesource
Username
camper-42
Joined
2014-04-12
Certifications
3 of 14
import { DescriptionList } from './ui/description-list/DescriptionList';

<DescriptionList
  items={[
    { term: 'Username', detail: 'camper-42' },
    { term: 'Joined', detail: '2014-04-12' },
    { term: 'Certifications', detail: '3 of 14' }
  ]}
/>
<dl class="dl">
  <dt class="dl__term">Username</dt>
  <dd class="dl__detail">camper-42</dd>
</dl>
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
itemsDescriptionListItem[]yes-
layoutenumnovertical

Group H

Layouts

<StackedLayout />

Stacked layout

Header + main + footer - the marketing/news chrome shape.

Stacked layout

preview

copy sourcesource
Header
Main content
Footer
import { StackedLayout } from './ui/stacked-layout/StackedLayout';
import { Navbar } from './ui/navbar/Navbar';

<StackedLayout
  header={<Navbar />}
  footer={<footer>…</footer>}
>
  {children}
</StackedLayout>
<div class="stacked-layout">
  <header class="stacked-layout__header">…</header>
  <main class="stacked-layout__main">…</main>
  <footer class="stacked-layout__footer">…</footer>
</div>
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
headerReactNodeno-
footerReactNodeno-

<AuthLayout />

Auth layout

Centered card on a tile-pattern backdrop - sign-in, sign-up, password reset.

Auth layout

preview

copy sourcesource
freeCodeCamp
import { AuthLayout } from './ui/auth-layout/AuthLayout';
import { Input } from './ui/input/Input';
import { Button } from './ui/button/Button';

<AuthLayout brand="freeCodeCamp" footer={<Link href="/forgot">Forgot your password?</Link>}>
  <Input label="Email" type="email" />
  <Button variant="cta" block>Continue</Button>
</AuthLayout>
<div class="auth-layout auth-layout--pattern">
  <div class="auth-layout__card">
    <p class="auth-layout__brand">freeCodeCamp</p>
    <button class="btn btn--cta btn--block">Continue</button>
    <p class="auth-layout__footer"><a class="fcc-link">Forgot?</a></p>
  </div>
</div>
Anatomy
Status
stable
Since
1.0.0
Props
PropTypeRequiredDefaultDescription
brandReactNodeno-
footerReactNodeno-
patternbooleanno-

Group I

Games

<TileMatcher />

Tile matcher

Data-driven memory / matching game for interactive curricula. Flip to reveal, match pairs, fire callbacks.

Tile matcher

preview

copy sourcesource
import { TileMatcher } from './ui/tile-matcher/TileMatcher';

const pairs = [
  { id: 'html', faces: ['HTML', 'Structure'] },
  { id: 'css', faces: ['CSS', 'Style'] },
  { id: 'js', faces: ['JS', 'Behavior'] }
];

export function Drill() {
  return (
    <TileMatcher
      pairs={pairs}
      columns={3}
      onMatch={(id) => console.log('matched', id)}
      onComplete={({ moves }) => console.log('done in', moves, 'moves')}
    />
  );
}
<!-- TileMatcher is a stateful React component.
     Use the React package for curriculum embeds. -->
<div class="tile-matcher">
  <div class="tile-matcher__grid">
    <button class="tile-matcher__tile" data-state="down">…</button>
  </div>
</div>
Anatomy
Status
beta
Since
0.2.0
Tokens
  • --dur-base
  • --ease-out
  • --success-color
  • --highlight-color
Props
PropTypeRequiredDefaultDescription
pairsTileMatcherPair[]yes-Deck definition. Tile count is `2 × pairs.length`.
columnsnumberno-Fixed column count. Omit for a responsive auto-fit grid.
animateFlipbooleannotrueFlip animation on reveal. `false` swaps faces instantly. Default `true`.
faceDownbooleannotrueStart tiles face-down (memory game). `false` shows every face. Default `true`.
mismatchDelaynumberno900Delay before a mismatched pair flips back, in ms. Default `900`.
disabledbooleannofalseLock the whole board (no flips).
shufflebooleannotrueShuffle the deck. Default `true`.
seednumberno-Seed for a deterministic shuffle (tests, visual snapshots).
onMatch((pairId: string, tileIds: [string, string]) => void)no-Fires when a pair is matched.
onMismatch((tileIds: [string, string]) => void)no-Fires when two flipped tiles do not match.
onComplete((stats: { moves: number; matches: number; }) => void)no-Fires once every pair is matched.

<Hotspots />

Hotspots

Clickable regions overlaid on a background image or diagram. Quiz mode with target, feedback, and hints.

Hotspots

preview

copy sourcesource

Click the fire

freecodecamp logo
import { Hotspots } from './ui/hotspots/Hotspots';
import { RectHotspot, CircleHotspot, EllipseHotspot } from './ui/hotspot-shapes/HotspotShapes';

const Diagram = (): JSX.Element => (
  <img src='/favicon.svg' alt='freecodecamp logo' />
);

const HOTSPOTS: HotspotItem[] = [
  {
    id: 'bracket-left',
    label: 'Opening Paren',
    shape: <RectHotspot x={33} y={25} width={29} height={92} />
  },
  {
    id: 'fire',
    label: 'Fire',
    shape: <EllipseHotspot cx={100} cy={75} rx={30} ry={45} />
  },
  {
    id: 'bracket-right',
    label: 'Closing Paren',
    shape: <RectHotspot x={138} y={25} width={29} height={92} />
  }
];

export function HotspotsDemo(): JSX.Element {
  return (
    <div style={{ width: '100%', maxWidth: 360, margin: '0 auto' }}>
      <Hotspots
        background={<Diagram />}
        width={200}
        height={140}
        hotspots={HOTSPOTS}
        targetId='fire'
        prompt='Click the fire'
        onCorrect={id => console.log('correct', id)}
      />
    </div>
  );
}
<!-- Hotspots is a stateful React component.
     Use the React package for curriculum embeds. -->
<div class="hotspots">
  <div class="hotspots__stage">
    <div class="hotspots__background"><img src="/bird.png" alt="A songbird" /></div>
    <svg class="hotspots__overlay" viewBox="0 0 200 140" preserveAspectRatio="none">
      <g class="hotspots__hotspot" role="button" aria-label="Head">
        <circle class="hotspots__shape" cx="55" cy="55" r="24" />
      </g>
    </svg>
  </div>
</div>
Anatomy
Status
beta
Since
0.2.0
Tokens
  • --highlight-color
  • --success-color
  • --danger-color
  • --dur-fast
Props
PropTypeRequiredDefaultDescription
backgroundReactNodeyes-Background layer: an image `src` string, or any node (component, `<svg>`).
backgroundAltstringnoAlt text used when `background` is an image `src` string.
widthnumberyes-Coordinate space width - hotspot geometry is expressed against this.
heightnumberyes-Coordinate space height. Also sets the container aspect ratio.
hotspotsHotspotItem[]yes-Clickable regions overlaid on the background.
targetIdstringno-Quiz mode: the id of the correct hotspot. Omit for free selection.
promptReactNodeno-Instruction shown above the image (quiz mode).
hintAfternumberno3Reveal a hint naming the target after this many wrong attempts. Default `3`.
selectedIdstring | nullno-Controlled selection. Omit for uncontrolled.
disabledbooleannofalseLock the whole widget.
onSelect((id: string) => void)no-Fires on every pick with the chosen hotspot id.
onCorrect((id: string) => void)no-Quiz mode: fires when the target is picked.
onIncorrect((id: string) => void)no-Quiz mode: fires when a non-target is picked.