How We Rebuilt Khan Academy’s Color System from the Ground Up

By Caitlyn Mayers, Staff Product Designer, Accessibility & Design Systems

A colorful pie chart divided into seven segments in shades of blue, green, yellow, orange, pink, and purple, set against a light blue background with abstract geometric shapes surrounding it.

In 2025, we reimagined Khan Academy for our district partners, rebuilding it to better serve every kind of learner in every kind of classroom in order to ensure every student has the opportunity to master grade-level material. This reimagination included a completely new user interface that is visually distinct from previous Khan Academy experiences. 

The brand-new interface required a refresh of our Wonder Blocks design system’s code components and Figma libraries, as well as changes to its foundational settings, including color, typography, sizing, and spacing. In this post, we’re going to take a deep dive into the creation and architecture of our new color system.

Previous Palette

Our previous color palette contained a limited set of primitives and semantic tokens, with solid versions of every color and alpha versions of specific color sets. This had a few gaps that created some use problems across our teams.

A grid of 40 squares showing the color palette semantic swatches, labeled with their semantic token name and their primitive color value. The color values show whites, grays, blues, reds, purples, greens, yellows of varying shades and intensities.

Despite having the specific semantic categories needed for color use across our products, the previous color system was fairly rigid and narrow. Teams often used the wrong semantic tokens to achieve a specific visual presentation that the system’s semantic structure wasn’t designed to support (e.g., using an Icon color for text or vice versa). This led to incorrect token use across the product.

Because we had alpha versions of some but not all colors, it was not always clear when teams should use solid versions of colors vs. the alpha versions of colors. Incorrectly-used alpha tokens resulted in insufficient contrast.

When we needed a new color somewhere (e.g., we needed darker hues in a few places to address contrast failures), we had to start the color process over from scratch to create the new color and did not have a complete primitives palette from which we could pull an existing hue and apply.

Starting from first principles: Building a new palette

In the process of redesigning the interface, we threw out all of our priors in order to create the new color system. Staff Visual Designer Camilla Hernandez began early brand explorations of three divergent interface presentations. Each one used a narrow and distinct palette of core hues. 

Six square color swatches centered on a black background, with lighter hue narrow rectangle swatches underneath each square swatch. The swatch colors, from left to right, are: blueish purple, orange, pink, yellow, green, and tan.

Those core hues were immediately tested for contrast against solid white, black, and typical muted-surface colors. A few of the colors did not meet the 4.5:1 contrast ratio required for normal text, so we made some slight tweaks to those colors in order to avoid having to rework major portions of the color palettes after they had been defined.

A contrast grid showing the contrast ratios between three combinations of the primary blue value and white, black, and two tan shades. The original hue passes against black and white, while the other options with tweaked blue values pass with higher contrast ratios across more colors.

Once aligned on the core swatches for our palette, we built a full primitives palette that included all possible hues in our system’s color spectrum so we could pull from existing hues when we needed to expand our color use in the future.

Hues in the new primitives palette are organized numerically following the lightness scale in the HSL space: 0 is the darkest shade of a hue; 100 is the lightest shade of a hue.

A grid of 88 color swatches arranged from darkest to lightest. There are red, orange, yellow, green, cyan, blue, magenta, and gray hues in this palette.

After completing the primitives palette, we updated the existing semantic tokens to reference the new primitives and began designing the new UI. The design team explored the new UI designs using both the primitives and semantics to shape color in the experience, but we quickly felt constrained by the old semantic schema. We needed a wider variety of semantic hues to support the ways in which we were working with color in the new UI.

Within a design system, semantic tokens serve as an important abstraction layer that allows us to organize colors by concept, specifically communicating the purpose they serve within the system and our interfaces. This abstraction helps teams understand how a color can be used and shows them where to correctly apply it to the interface in a consistent manner.

Figuring out how we could change our semantics to be more expansive while still preserving the integrity of the schema and value of semantic color tokens was a real struggle, and the pieces did not easily fit together in the previous architecture.

The design system team (one designer and three engineers) discussed this struggle. One of our engineers, Juan Andrade, pointed out that we had a potential scaffolding pattern in the border colors and suggested that we might be able to extend that across other tokens. The border tokens had three values of varying intensity: Primary, Subtle, and Strong.

Three gray border swatches arranged from lightest to darkest: subtle, primary, and strong.

This pattern unlocked the entire token schema for us. We took this core concept, which we now call our Intensity Scale, and crafted a new token architecture around it.

A new semantic color architecture

Within the semantic palette, tokens are organized using the following schema:

Domain

  • Layer
    • Context
      • Intensity

Domain

A domain represents the scope, role, or application of a collection of tokens within the system. Wonder Blocks color tokens have three domains: Core, Learning, and Component.

Layer

Tokens within a domain are organized by role. Each represents different layers of the UI. 

  • Background
  • Border
  • Foreground
  • Shadow

Background

Background tokens style the fill color of a component or element’s container.

Border

Border tokens style defining borders around or separators between elements.

Foreground

Foreground tokens style text or icon elements that sit on top of the background of a component or view.

Shadow

Shadow tokens add depth and elevation to a component or element in a design.

Context

Within each layer, tokens are organized by context, the expected application or purpose of the hue.

Core Contexts

  • Base
  • Instructive
  • Neutral
  • Disabled
  • Success
  • Warning
  • Critical
  • Knockout
  • Overlay

The Intensity Scale: Subtle, Default, Strong

The intensity scale provides three values available to use within each Context. The intensity is determined by the amount of contrast and visual weight a given value has relative to the Core Base Default token, which serves as the anchor color from which the Subtle and Strong values are determined. Subtle intensity has lowest contrast and Strong intensity has the highest contrast against the anchor color.

Example

Domain: Core

  • Layer: Background
    • Context: Instructive
      • Intensity: Subtle
      • Intensity: Default
      • Intensity: Strong
The intensity scale demonstrated with the Core Background Base color tokens. The subtle value is a light, almost white gray. The default value is pure white and the strong value is almost black.

Now that we’ve outlined the schema for our semantic tokens, let’s take a deeper look at how these concepts work in practice across the Core Tokens domain.

Putting the schema into practice

Core tokens are intended to power a majority of the components in the Khan Academy experience. These tokens are largely scoped to the system’s functions, rules, and opinions.

Layers

Core Tokens have four layer categories:

  • Background
  • Border
  • Foreground
  • Shadow

Contexts

Core Token Layers share the following nine contexts:

Base

Base tokens are available only as Background tokens. Base tokens style a majority of the background colors across the Khan Academy experience. The Default Base token is the anchor token used to calculate the intensity of the other tokens in the system.

Instructive

Instructive tokens style elements that serve as main elements and actions within a view. Instructive tokens guide users to the most-intended actions in a view, typically actions that advance the user to another step in the flow, complete a progressive (non-destructive) action, or communicate general interactivity.

Neutral

Neutral tokens style elements without conveying additional meaning through the visual presentation. Neutral tokens help preserve hierarchy balance when multiple elements with differing visual weights are present in a view.

Disabled

Disabled tokens style elements that users are actively prevented from interacting with using a disabled state in code. Disabled tokens have low contrast and visual weight so that a user knows that a control is not an active, interactable control.

Success

Success tokens style elements that confirm that an action or objective is successfully completed.

Warning

Warning tokens style elements that show non-urgent information that could require intervention. Warning tokens also style elements that are in -progress, incomplete, or pending.

Critical

Critical tokens style elements that require immediate attention, as well as actions that are considered destructive within the experience. Critical tokens imply that an element or action is blocked or they have an error that needs to be fixed.

Knockout

Knockout tokens, available for Border and Foreground categories, style elements that need a strong visual separation or contrast from other surrounding colors (typically darker values).

Overlay

Overlay tokens are transparent, background-only tokens that style the full-page background that separates or masks an overlay UI component, such as a modal, from the main UI elements underneath it.

All Wonder Blocks core token swatches in a grid showing Background tokens on the left, Foreground in the middle, and Border on the right. Each swatch has three horizontal bars showing the Subtle, Default, and Strong values in each layer. The instructive context is large, while the other tokens display in smaller versions underneath.

Rethinking semantics: instructive and knockout

There are two contexts created in this schema that were departures from the first semantic schema—our Instructive and Knockout contexts. In the first schema, Inverse tokens were used inconsistently to try to support dark sections of the legacy interface. The final presentation of an inverse token varied depending on the context of the use. An inverse background could be dark but the inverse foreground color would be light. This inconsistency between when inverse meant light and when it meant dark was frustrating. So we eliminated this category across our tokens in favor of using the Strong background tokens and a Knockout token category for any foreground colors on Strong or Default backgrounds.

Our Knockout tokens borrow their purpose from the knockout printing technique—knocking out the background color of an element so the foreground contents are visible with sufficient contrast. Knockout is a more agnostic term, allowing its color value to be light or dark relative to the design of other elements in close proximity to the element using the knockout token.

Examples of Wonder Blocks button, badge, tooltip, and toast components that use the Knockout tokens for their foreground or border styles in light and dark mode.

Another change to our contexts is the addition of the instructive category. In most design systems, what we call Instructive tokens are referenced as Brand or Primary tokens. Given how we use these tokens and the context of our platform, Instructive felt like it captured and represented the function of these tokens in our system more intentionally than Brand or Primary.

Designing for accessibility and contrast

We stress-tested the semantic palette and calculated contrast ratios for all core tokens in the system to ensure that the palette serves a wide range of users and meets accessibility standards. The following guidance outlines the combinations that meet these benchmarks, helping teams understand how to work with color in the system and use accessible combinations across the layers and elements being designed and built.

A right triangular grid of semantic token swatches that shows all of the possible combinations and the details of their color contrast ratios. A small section of the grid is zoomed in to show the contrast ratios of the instructive foreground tokens against the core background tokens. Each swatch shows its contrast ratio, large and normal text samples, and then pass or fail ratings for normal text, large text, and background elements.

Backgrounds

  • Default and Strong Background tokens (minus Warning) pass at 3:1+ on the Default and Subtle Base Background tokens
  • Strong Background tokens pass at 3:1+ on all Subtle Background tokens

Borders

  • Strong Border tokens pass at 3:1+ on all Subtle Background tokens and the Default Base Background token
  • Default and Subtle Border tokens (minus Warning) pass at 3:1+ on all Subtle Background tokens and the Default Base Background token

Foreground

  • Strong Foreground tokens pass at 4.5:1+ on all Subtle and Base Background tokens
  • Default Foreground tokens pass at 4.5:1 on Default and Subtle Base Background tokens and most Subtle Background tokens (with a few 3:1 only passing exception

Supporting multiple themes without breaking the system

Our system uses themes to change the visual presentation of the components, so the same components can be used in both the legacy and new Districts experiences and align to the UI paradigm of each theme. To accomplish this, we mapped our old primitive color values to the new semantic schema. The old design language does not perfectly map to our new design language, and we had to decide how to approach resolving these conflicts. The design system team chose to optimize the system for the best presentation in the new theme, even when that would result in a less-than-ideal design in the old theme, so the design of some legacy components changed as a result of the schema alignment.

A split view of the learner dashboard showing the light mode theme on the left and the dark mode theme on the right.

Dark mode

Our team is currently in the process of expanding our UI themes and adding dark modes to them. The new schema and variable setup in Figma allowed us to create and deploy our first iteration of true dark mode in a matter of days. 

We expanded the primitives by adding another stop in the ramps for darker background hues that allowed us to achieve a look and feel in dark mode that had parity with that of the light mode. We also made minor adjustments to a small number of primitive hues to meet contrast requirements on the dark backgrounds. Lastly, we modified some semantic surface-color choices across components, like our modal, so the designs maintain identical hierarchies in both light and dark modes.

There were some constructs in the schema that didn’t hold up as well in dark mode because of the contrast choices we made with the Instructive hue, opting for higher contrast in that hue against light colors vs. darker colors. Instead of inverting the Knockout color in dark mode, we maintain the maximum lightness hue here so we can continue to meet contrast requirements (you can see this in the Knockout token example image in the previous section).

A more flexible, accessible future for color

We are absolutely thrilled with this new token architecture. It’s not easy to introduce a brand-new, unifying token schema when you have two color systems to merge. But we are so proud of how this work turned out.

This new system gives us a wider range of semantics to use in our UI, provides more specific guidance as to how each color should be used within a given design, and is fully stress-tested for accessibility requirements.

If you’d like to give this schema a spin, we built a small generator tool with Claude that builds a full primitives palette and semantic tokens based on this schema. This tool allows you to:

  • Customize primitive values
  • Select your primary anchor (brand) color
  • Set the color values assigned to Success, Warning, and Critical contexts
  • Download palette as CSV
  • Export palette as JSON for Figma Variables
Wonder Blocks Schema Color Generator

If you or your teams decide to apply this schema to your systems (especially new ones!), let us know how it works for you. Design-systems work is better when it’s done out in the open, and we’d love to see what you do with the framework.