Protected

Password required

This work is shared selectively. Reach out at karleeboillot@gmail.com if you need access.

Incorrect password — try again

← Fluent Flex
5 min read

Fluent Flex · Chapter 01

Color

Color tokens existed as a flat list of named values with no structural logic connecting shades to each other. Rebuilt from a mathematical foundation — each step derived, not chosen — with interaction states generated algorithmically rather than stored as separate tokens.

~378

Color-related tokens before — 366 semantic + 12 shadow tokens

66

Semantic color tokens after

0

Stored interaction states — hover and pressed generated algorithmically

Background

The Color System Before Flex

Fluent's V9 color system contained 366 semantic color tokens — a flat list of manually assigned values across foreground, background, stroke, shadow, and palette categories. An additional 12 shadow tokens were also part of the color system, bringing the color-related total to roughly 378.

The redundancy is visible in the structure: colorNeutralForeground1, 2, 3, 4, 5 — each with Hover/Pressed/Selected variants. Enumeration, not derivation. No mathematical relationship between steps meant there was no way to know what came next without looking it up.

Every new theme — Copilot, brand partners, Microsoft sub-brands — required manually picking colors across the full range. There was no algorithmic path. Each theme decision was a judgment call.

Impact

Before & After

V9 — Before

366 semantic color tokens

Manually assigned values. Interaction states (*Hover, *Pressed, *Selected) stored as explicit tokens for nearly every semantic color — each one maintained separately.

  • No mathematical relationship between color steps
  • New themes required 400+ manual decisions
  • AI tools couldn't infer relationships between shades
  • Scale didn't hold cross-platform

Fluent Flex — After

66 semantic color tokens

Derived values. Hover and pressed states generated algorithmically at runtime via OKLCH lightness and alpha shifts. Change one delta value — every component's interaction states update automatically.

  • 300-token reduction — mostly eliminated explicit interaction state tokens
  • New themes from seed values, not 400+ decisions
  • Names encode role + prominence — AI-inferable from the name alone
  • Same semantic token maps to platform-appropriate rendering

The foundation

Why OKLCH

The color scale is built on OKLCH — a perceptually uniform color model where lightness, chroma, and hue angle are controlled mathematically. In sRGB or HSL, colors at the "same lightness" look visibly different in brightness across hues — a red at L50 looks much brighter than a blue at L50. OKLCH corrects for this: each step on the scale feels equally different from its neighbors regardless of which hue you're working in.

In practice, this means: a color ramp in Fluent Flex holds perceptually consistent contrast across every hue by math, not by eye. Hover and pressed states are OKLCH lightness and alpha shifts from the base value — define the delta once, and every interaction state across every theme follows automatically. One update propagates everywhere.

Color ramp diagram showing OKLCH-derived steps across multiple hue families, each step mathematically related to its neighbors with consistent perceptual distance
Each step is derived — the math, not a designer's eye, guarantees contrast holds across every hue.

Architecture

Palette → Semantic → Alias

The three-tier color hierarchy is where the system's flexibility lives:

Palette

Raw derived values — not directly consumed by components. These are the color stops on the OKLCH ramp. Stable across themes. A designer can look at a palette value and know exactly where it falls on the scale.

Semantic

Intent-named tokens: colorNeutralForeground1, colorBrandBackground. The name encodes role, prominence, and the surface context — everything a designer, engineer, or AI agent needs to infer the right value without a lookup table. This is the layer components consume.

Alias

Surface-specific overrides that trace back to the semantic layer. Aliases exist for specific platform or surface needs — but they always reference a semantic token, never a palette value directly. The chain of derivation stays intact.

Semantic families

Five Families, One Structure

Every generic color token belongs to one of five semantic families. The family names the communication purpose; the specific hue is a theme decision. Components reference the family, so swapping a theme's brand color requires no component-level changes.

Family Default ramp Purpose
Neutral Fog (achromatic) Structural and organizational — the backbone of the system
Brand Fog default; themeable Brand identity and ownership — overridable per product theme
Danger Crimson Destructive actions, errors, critical states
Warning Orange Caution, potential risk, states requiring attention
Success Jade Positive outcome, completion, confirmation

Application

What Algorithmic Color Enables

The shift from manually-picked values to a derived scale isn't just a token count reduction. It changes what's possible:

A new brand theme isn't 400 manual decisions — it's one set of seed values run through the algorithm. The same color foundation that powers Fluent 2's default theme now powers Copilot theming and brand theming for 10+ Microsoft brands. An external brand can apply their brand colors through the Fluent Theme Designer and receive a systematically correct, accessible Fluent-themed kit — because the color science already handles the decisions.

One token structure, multiple theme expressions. The seed values change; the architecture stays constant.

The math

Interaction Deltas

Hover and pressed states are computed at runtime from rest token values using fixed OKLCH deltas. The same formula applies to every interactive token — only the rest value and one directional rule change per token.

State L shift (light mode) L shift (dark mode) Alpha add
Hover −0.03 (darkens) +0.03 (lightens) +0.04
Pressed −0.06 (darkens) +0.06 (lightens) +0.08

The inverse rule: tokens with heavy, loud, or onLoud in their name flip the sign — subtracting where normal tokens add and vice versa. They already sit at the extreme end of the ramp; following the standard direction would reduce contrast rather than increase it. Change a delta in one place and every component across every theme updates automatically.

Looking ahead

What's Next