Web Designguide12 min read

Rebranding Without Breaking WCAG: A Color QA Workflow

Token migration, contrast regression matrices, stakeholder sign-off, APCA review, and phased deploy for safe rebrands.

The rebrand looked perfect in the boardroom deck. Midnight blue replaced corporate teal. Accent coral sharpened CTAs. Marketing shipped new guidelines Friday afternoon. Monday morning, support tickets reported unreadable form hints, a failed procurement audit on an enterprise deal, and a chart legend indistinguishable from the background in dark mode. Nobody ran the contrast matrix against production surfaces. The design team checked hero mockups on white and called it done.

Rebrands fail accessibility not because teams ignore WCAG, but because color changes cascade through tokens, themes, third-party embeds, and PDF exports faster than spot checks can follow. A professional color QA workflow treats the rebrand like a schema migration: inventory before diff, pair testing with numeric gates, sign-off artifacts legal can file, secondary perceptual review, and phased rollout with rollback criteria defined before launch day. This essay is that workflow, practical enough for a two-week sprint and rigorous enough for regulated industries citing WCAG 2.2 Level AA.

Inventory before you migrate tokens

Before touching CSS, document what color means across your organization. Web design tokens owned by the design systems team live in CSS or JSON. Native mobile tokens run on a parallel track in Swift and Kotlin. Email templates maintained by lifecycle marketing often use inline hex that nobody remembers until a campaign sends with the old teal. Slide decks and ad banners sit outside engineering scope but create visual inconsistency customers notice. Data visualization palettes hard-coded into analytics dashboards carry high regression risk because chart series colors rarely flow through the same token pipeline as marketing buttons.

Export a token inventory from your source of truth, whether that is Figma variables, Style Dictionary JSON, or a tokens.css file in your monorepo. List every semantic token such as text-secondary, border-subtle, and status-error with its resolved light and dark values before the rebrand touches anything. That snapshot is your diff baseline. Identify contrast-critical pairs: combinations that must meet thresholds under SC 1.4.3 Contrast Minimum and SC 1.4.11 Non-text Contrast. Text on surfaces at every elevation. Icons and borders on those same surfaces. Focus rings against adjacent backgrounds. Button labels on button fills at default, hover, active, focus, disabled, and error states. Links on paragraph backgrounds. Chart series on chart backgrounds. Disabled text, which still must remain perceivable even when it does not require 4.5:1 against every surface.

Translate brand PDF swatches into tokens deliberately. Map each new brand swatch to a role, not a component. Use --brand, not button-blue. Convert pasted values to OKLCH for ramp generation. Rebuild the neutral ramp from scratch. Do not reuse the old gray hue with a new accent dropped on top. Regenerate hover and active states by OKLCH lightness delta, not guessed hex from a designer’s memory of what looked good in a meeting. Update text-on-accent pairs. Sync dark mode tokens explicitly. Auto-inverting light tokens produces illegible secondary text on cool gray cards. Update accent-color if your product uses it for native forms. Bump the token package version and publish a migration guide for external consumers.

:root {
  --brand: oklch(0.45 0.14 265);
  --text-secondary: oklch(0.52 0.02 265);
}

The anti-pattern is changing only --brand while leaving --text-secondary tuned to the old teal temperature. Secondary text inherits wrong chroma and fails on cool gray surfaces that looked fine against the previous palette. Ship parallel deprecated aliases for one release if external consumers exist, then remove them after a documented cutoff.

Contrast regression matrices that gate merges

Spreadsheets beat hero screenshots because spreadsheets scale. Build a contrast regression matrix where rows are foreground tokens, columns are background tokens, and cells contain WCAG 2.x ratio with pass or fail for your required level. Run the matrix for light and dark themes. Run it at representative font sizes because muted text at 12px still needs 4.5:1 for normal text under WCAG 2.2. Use our contrast checker for pair lookups during token tuning, then automate where possible with a script that reads tokens.json, resolves pairs, computes relative luminance per the WCAG formula, and outputs CSV for continuous integration.

Treat the matrix as a regression baseline, not a one-time audit. Tag the CSV artifact in git beside the token version bump so reviewers can diff pass-fail cells the same way they diff hex values. A cell that flips from pass to fail should block merge with the same severity as a broken unit test.

Manual spot checks still matter for pairs automation mishandles: gradients that tokens approximate with a single midpoint hex, images behind text in marketing components, and semi-transparent overlays where computed solid equivalents lie. Note those pairs as manual-required in the spreadsheet so they are not falsely green in CI.

function luminance([r, g, b]) {
  const lin = (c) => {
    c /= 255;
    return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
  };
  return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);
}

function contrastRatio(fg, bg) {
  const L1 = luminance(fg);
  const L2 = luminance(bg);
  return (Math.max(L1, L2) + 0.05) / (Math.min(L1, L2) + 0.05);
}

Gate merges on matrix CI. Fail the build if any required cell drops below threshold compared to the main branch baseline. Beyond text, non-text contrast matters for buttons with hairline borders, chart slices, toggle tracks, and slider thumbs, all of which need 3:1 against adjacent colors per SC 1.4.11. Include border-default versus surface and brand versus surface in the matrix. Hover and active are production states, not optional polish. A hover darken that passes at default may fail when brand-hover crosses a luminance cliff.

Sign-off, APCA, and phased deploy

Legal and brand teams speak different languages than engineering. Produce sign-off artifacts they can approve without reading CSS. A one-page accessibility summary counts pairs tested, failures fixed, and WCAG version cited. Side-by-side screenshots show old versus new on real components like forms, tables, and navigation, not only the landing hero. An exception log captures any intentional failures with business justification and remediation date, which should be rare. A brand compliance sheet confirms primary and secondary usage matches guidelines. Brand may request chroma that fails contrast. Document the conflict and resolve with token adjustment, not silent shipping. Brand approved inaccessible coral is a liability paper trail procurement auditors know how to read.

WCAG 2.x contrast ratio is the legal baseline. APCA, the Advanced Perceptual Contrast Algorithm, models perceived lightness with font size and weight inputs. It catches pairs that pass 4.5:1 but read poorly at 13px light weight. APCA is not a substitute for WCAG 2.2 sign-off in contracts today. Treat it as a second opinion. If WCAG passes but APCA fails for small text, review and likely bump weight or size. If WCAG fails but APCA passes, still fix for WCAG compliance. Run APCA on edge cases WCAG greenlights poorly: muted captions, placeholder text, colored text on tinted marketing panels. Document APCA tool version because constants evolve.

Big-bang CSS deploys at midnight cause rollback nightmares. Prefer phased exposure. Feature-flag the new theme for internal dogfood, then beta tenants, then percentage rollout, then full traffic. Isolate rebrand tokens in a CSS layer toggled by a single data attribute so you roll forward or back without redeploying component CSS.

if (featureFlags.newBrandColors) {
  document.documentElement.dataset.theme = 'brand-2026';
}

Monitor accessibility complaint tickets tagged post-rebrand, support chat volume about unreadability, automated synthetic checks in production with axe-core or Pa11y on a schedule, and analytics on settings or accessibility page views. Predefine rollback triggers. Any SC 1.4.3 failure discovered in production warrants rollback within hours. A procurement blocker on an enterprise deal warrants rollback or hotfix tokens with paired re-test. Keep previous token JSON tagged in git for instant revert. Web rollback does not fix scheduled email campaigns. Coordinate lifecycle marketing send times with web deploy or accept temporary brand mismatch documented in communications.

Case study: midnight blue Monday

A mid-size B2B SaaS company with four hundred enterprise customers attempted a rebrand over a single weekend. The visual identity shifted from teal at oklch(0.52 0.11 195) to midnight blue at oklch(0.45 0.14 265), with coral accent replacing a muted orange CTA. Marketing finalized Figma libraries Thursday. Engineering received exported hex Friday at 4pm. The deploy window opened Saturday at 11pm.

The team changed --brand and --brand-hover in the token package but left --text-secondary, --text-muted, and --border-subtle untouched. Those neutrals still carried the teal undertone from the previous ramp. On the default white surface, secondary text still passed 4.5:1. On the new gray-100 card surface, introduced in the same rebrand for elevated panels, secondary text dropped to 3.8:1. Support forms, which render inside those cards, became the first complaint vector Sunday morning.

Dark mode was worse. The team had bolted dark surfaces on six months earlier by inverting light tokens through a script rather than authoring dark-specific values. Midnight blue on --surface-dark produced a brand button with 3.2:1 label contrast, failing WCAG for normal text. The analytics dashboard, excluded from token scope because it lived in a separate React package, still used hard-coded teal series colors. Against the new dark chart background, the legend for Series B matched the background within one perceptual step. A procurement auditor testing the demo environment Monday flagged both failures before sales could close a renewal.

The rollback took four hours because nobody had tagged the previous token release. Engineers cherry-picked hex from git history while product argued for forward-fix. Forward-fix won for the CTA but required rebuilding the neutral ramp in OKLCH, running a full contrast matrix, and gating the redeploy on CI. The corrected workflow they adopted the following quarter looked nothing like the weekend rush.

Phase zero became mandatory: export all semantic tokens to a spreadsheet snapshot before any hue change. Phase one mapped midnight blue to roles across forty-seven tokens, not five hero swatches. Phase two ran an automated matrix with two hundred twelve pair cells in light and dark. Fourteen failed initially. The dark-mode brand-on-surface failure and the secondary-on-gray-card failure were among them, exactly the pairs the weekend deploy had skipped. Phase three produced a one-page sign-off PDF with signature blocks for design, engineering, accessibility, and brand. Phase four ran APCA on caption and placeholder pairs. Three pairs passed WCAG but failed APCA at 12px weight 400. Engineering bumped caption weight to 500 rather than darkening tokens globally.

Phase five deployed behind a feature flag at ten percent traffic for forty-eight hours. Synthetic checks ran hourly. No accessibility tickets arrived. Rollout reached one hundred percent Wednesday. Email templates updated from the same token export Thursday, preventing the coral CTA mismatch campaigns would have shipped Friday.

The measurable difference was procedural. The failed weekend changed five tokens and tested three pairs visually. The successful quarter changed forty-seven tokens, tested two hundred twelve pairs numerically, and deployed with rollback criteria written before the flag flipped. Midnight blue shipped without breaking WCAG the second time because Sunday night was spent on a spreadsheet, not a slide deck.

After launch and common failure modes

Rebrands are not one-and-done. New components reference old hard-coded hex until grep finds them. Schedule quarterly matrix re-runs when tokens change. Maintain Storybook stories with the a11y addon on every semantic surface combination. Lint against raw hex in components. Onboard contractors with explicit guidance to never paste from Figma without checking the token name.

When product adds a new surface-tinted card, extend the matrix rows and columns before merge. Common failure modes have predictable prevention. Muted text that passes on white but fails on gray cards means you did not matrix all surface variants. Dark mode bolted on via filter invert means you need explicit dark tokens. Charts using old series hex mean you need a centralized viz palette in tokens. Third-party widgets ignoring CSS variables mean iframe audit or documented mismatch acceptance. PDF exports embedding old RGB mean regenerating the asset pipeline. Focus rings unchanged and invisible on new brand fill mean focus pairs were missing from the matrix.

Procurement questionnaires often arrive after the damage is done. A VP forwards an accessibility attestation form the week after launch. The questions ask whether the product conforms to WCAG 2.2 Level AA, whether contrast was verified on all user interface components, and whether a documented QA process exists for visual changes. The sign-off packet from phase three is the difference between checking yes with evidence and scrambling for screenshots that prove you tested something. Build the packet during the rebrand, not in response to the email.

Contractors and agencies amplify rebrand risk because they do not carry institutional memory about which surfaces use tokens versus raw hex. A freelance front-end developer hired to build a landing page may paste midnight blue from the brand PDF into inline styles while the rest of the product uses --brand correctly. Grep for hex outside token files should run in CI before merge and again before rebrand launch. Stylelint rules that forbid raw color declarations except in designated token source files pay for themselves the first time they catch an agency deliverable.

Third-party embeds deserve a row in your inventory spreadsheet even when you cannot change their colors. A payment iframe with teal submit button inside midnight blue chrome is a brand mismatch users notice and an accessibility problem you may not be able to fix without vendor cooperation. Document accepted mismatch or escalate to vendor roadmap. Pretending the iframe does not exist means support inherits complaints your engineering team cannot resolve.

Regression after launch extends beyond web. Native mobile apps on separate release trains may ship the new brand two weeks after web. Users who switch between iOS and browser during that window see inconsistent accent colors and assume the web product is broken. Coordinate release notes across platforms even when codebases do not share tokens. A shared spreadsheet of role-to-hex exported from the same OKLCH source reduces drift.

An accessible rebrand is a measurable rebrand. Token migration proves you updated the right layers. Contrast regression matrices prove legibility with numbers, not opinions. Stakeholder sign-off aligns brand and compliance before code reaches production. APCA second opinion catches perceptual edge cases WCAG math misses. Phased deploy limits blast radius when something slips through. The midnight blue that broke WCAG on Monday and passed on Wednesday was the same hue both times. Only the workflow changed, and that workflow is available to any team willing to treat color with the same discipline they already apply to database migrations and API versioning.