Implements user interfaces using the Wonder Blocks (WB) design system — Khan Academy's React component library. Use this skill whenever the user asks you to build, modify, or review UI components in a project that uses Wonder Blocks, even when similar usage patterns already exist in the codebase; mentions any WB package (e.g. wonder-blocks-button, wonder-blocks-modal, wonder-blocks-tokens); wants to use or map WB tokens for colors/spacing/typography (including translating Figma designs to WB components and tokens); or asks how to do something "the Wonder Blocks way". If the user is building any kind of form, layout, modal, button, dropdown, or typography treatment in a WB-enabled codebase, this skill applies — even if they don't explicitly say "Wonder Blocks". Do NOT trigger for debugging TypeScript errors, writing tests, setting up Storybook stories, or fixing CI/lint issues in WB packages.
Wonder Blocks is Khan Academy's React component library (@khanacademy/wonder-blocks-*).
All components are TypeScript-friendly, use aphrodite for styling, and follow WAI-ARIA
accessibility patterns.
If the Figma MCP or WB Storybook MCP is used, use each for its purpose:
If the WB Storybook MCP is not available, refer to the type definitions for WB components to learn more about the API.
IMPORTANT: This skill is required even when similar patterns already exist in the codebase. Do not skip it because you found a nearby file to copy from.
| Package | Key exports |
|---|---|
wonder-blocks-core | View,addStyle |
wonder-blocks-tokens | semanticColor, sizing, border, boxShadow, font, breakpoint |
wonder-blocks-typography | BodyText, Heading |
wonder-blocks-button | Button, ActivityButton |
wonder-blocks-link | Link |
wonder-blocks-clickable | Clickable, ClickableBehavior |
wonder-blocks-icon-button | IconButton, ActivityIconButton, ConversationIconButton |
wonder-blocks-icon | Icon, PhosphorIcon |
wonder-blocks-form | TextField, TextArea, Checkbox, CheckboxGroup, Choice, RadioGroup |
wonder-blocks-labeled-field | LabeledField |
wonder-blocks-dropdown | SingleSelect, MultiSelect, ActionMenu, Combobox, OptionItem, ActionItem, SeparatorItem |
wonder-blocks-modal | ModalLauncher, OnePaneDialog, FlexibleDialog, DrawerLauncher, DrawerDialog |
wonder-blocks-accordion | Accordion, AccordionSection |
wonder-blocks-badge | Badge, StatusBadge, GemBadge, StreakBadge, DueBadge, NeutralBadge |
wonder-blocks-banner | Banner |
wonder-blocks-breadcrumbs | Breadcrumbs, BreadcrumbsItem |
wonder-blocks-card | Card |
wonder-blocks-cell | CompactCell, DetailCell |
wonder-blocks-popover | Popover, PopoverContent, PopoverContentCore |
wonder-blocks-progress-spinner | CircularSpinner |
wonder-blocks-search-field | SearchField |
wonder-blocks-switch | Switch |
wonder-blocks-tabs | ResponsiveTabs, ResponsiveNavigationTabs |
wonder-blocks-toolbar | Toolbar |
wonder-blocks-tooltip | Tooltip, TooltipContent |
wonder-blocks-theming | Theme providers |
wonder-blocks-styles | Global style helpers like focusStyles |
WB uses aphrodite for scoped CSS. Never use inline style objects for complex styles —
define them with StyleSheet.create so they're type-safe and mergeable.
import {StyleSheet} from "aphrodite";
import {View} from "@khanacademy/wonder-blocks-core";
import {sizing, semanticColor} from "@khanacademy/wonder-blocks-tokens";
const MyComponent = () => (
<View style={styles.container}>
...
</View>
);
const styles = StyleSheet.create({
container: {
padding: sizing.size_160,
backgroundColor: semanticColor.core.background.base.default,
},
});
Apply multiple styles with an array: style={[styles.base, isActive && styles.active]}.
Avoid applying too many custom styles to a Wonder Blocks component. Layout related properties
are okay like margin, but prefer using props for choosing supported variants.
If custom styling is necessary for a Wonder Blocks component, use the style or
styles prop depending on the component. Prompt the user to reach out to the Wonder
Blocks team if many styles need to be overridden. This may mean there is a limitation
with the component.
import { StyleSheet } from "aphrodite";
import { sizing } from "@khanacademy/wonder-blocks-tokens";
import { BodyText } from "@khanacademy/wonder-blocks-typography";
const styles = StyleSheet.create({
text: {
margin: sizing.size_160,
},
});
const Example = () => (
<BodyText style={styles.text}>Hello world</BodyText>
);
Always reach for tokens rather than hardcoded values. The two main namespaces:
semanticColor — the right choice for most UI work. Tokens like
semanticColor.core.background.base.default, semanticColor.core.foreground.neutral.strong,
semanticColor.core.border.neutral.default, semanticColor.feedback.success.background.
These automatically adapt to themes. Use these values with the appropriate CSS properties.
sizing — Use sizing tokens for spacing. These values use rem values so
they scale with the font size. 1rem = 10px
border — Always use border tokens instead of hardcoded pixel values.
border.radius.radius_040, border.radius.radius_full, etc.border.width.thin, border.width.medium, border.width.thick// ✅ correct