[Rebrand] Migrate site header and footer to DSCO semantic tokens#73985
Merged
Conversation
…tic tokens Part of the header/footer chrome-migration train (see PhoneFrame, c90bb3a, for the prior exemplar). In-place tokenization only: no data-brand forks, no structural restyle. Replace legacy color.scss vars/hexes in the header, user menu, and hamburger with legacy DSCO semantic tokens (plural --borders-*, brand-family names) so the surfaces recolor under the CADS alias bridge. Mapping (verified by hex): dashboard/app/assets/stylesheets/application.scss (.header, .headerlinks, .header_button, #header_user_create_account, .project-save-error, #sign_in_or_user, .header_separator, .user_options/.help_contents): $contrastTealHeader (#00818f) -> --background-brand-teal-primary (#00818f) $header_text/$white (#fff) -> --text-neutral-white-fixed (#ffffff) $orange (#ffa400, hover underline) -> --background-accent-orange-primary (#ffb42e) [drift] $orange (#ffa400, button hover bg) -> --background-accent-orange-primary (#ffb42e) [drift] $white (border, .header_button) -> --background-neutral-white-fixed (#ffffff) $light_white (#fff) -> --background-neutral-primary (#ffffff) $light_primary_500 (#0093a4) -> --text-brand-teal-primary (#0093a4) $dark_red (#d62911) -> --background-error-primary (#e02d16) [drift] $teal (#0093a4, .header_user chip) -> --background-brand-teal-primary (#00818f) [drift; unifies chip with the bar] $inset_color (#c6cacd, .header_separator) -> --borders-neutral-strong (#b7c1cb) [drift] $black (#000) -> --text-neutral-primary (#292f36) [drift] $dark_color (#8c52ba, a:hover/:active bg) -> --background-brand-purple-primary (#9657c7) [drift] border 1px solid $black -> --borders-neutral-solid (#292f36) [drift] shared/css/user-menu.scss (.user_options/.create_options/.help_contents panels, .display_name, .help_icon, create-menu text): $white (panel bg) -> --background-neutral-primary (#ffffff) $light_gray_200 (#D1D4D8) -> --borders-neutral-primary (#d4dae1) [drift, imperceptible] #0000001a, #0000000f (box-shadow) -> --neutral-black-alpha-10, --neutral-black-alpha-5 [drift; alpha primitives, no elevation token exists] $light_black (#292F36) -> --text-neutral-primary (#292f36) $lightest_gray (#e7e8ea) -> --background-neutral-tertiary (#dfe3e9) [drift, nearest] $charcoal (#5b6770) -> --background-neutral-octonary (#69788a) [drift; same mapping PhoneFrame used for $charcoal] $white (.display_name/.help_icon text) -> --text-neutral-white-fixed (#ffffff) shared/css/hamburger.scss (#hamburger-contents, .item, .divider, #hamburger-icon, .button-signin, .button-create-account): $light_white -> --background-neutral-primary (#ffffff) $light_gray_200 -> --borders-neutral-primary (#d4dae1) [drift] box-shadow alphas -> --neutral-black-alpha-10/-5 (as above) [drift] $lightest_gray -> --background-neutral-tertiary (#dfe3e9) [drift] $light_black -> --text-neutral-primary (#292f36) $white (burger bars) -> --background-neutral-white-fixed (#ffffff) $light_black (.button-signin color/border) -> --text-neutral-primary / --borders-neutral-solid (#292f36) $light_secondary_500 (#8C52BA, .button-create-account) -> --background-brand-purple-primary / --borders-brand-purple-primary (#9657c7) [drift] $white (.button-create-account text) -> --text-neutral-white-fixed (#ffffff) Two workaround sites are flagged inline: --background-neutral-white-fixed used as a border color (no border-white token exists), and --background-accent-orange-primary used for the orange hover underline (no borders-accent-orange token exists). $contrastTealHeader was a local application.scss var with no other callers after this change; removed. All other now-unreferenced variables live in shared/css/color.scss, which is out of scope for this change and left untouched. Out of scope, untouched: the entire SmallFooter (light/dark variants, application.scss ~689-978, and SmallFooter.tsx), levelbuilder/local header env colors (application.scss:423-433), shared/css/color.scss definitions, and the Figma structural reskin. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Continues the header/footer chrome-migration train (see the header/ user-menu/hamburger commit immediately prior, and PhoneFrame, c90bb3a, for the original exemplar). In-place tokenization only, scoped to the full-site .footer block; the SmallFooter utility strip (.small-footer-base, #more-menu, dark/phone variants, SmallFooter.tsx) is untouched, per the user's decision that it has no Figma design of its own. Mapping (verified by hex): dashboard/app/assets/stylesheets/application.scss (.footer): $darkest_gray (#292F36) -> --background-neutral-primary-inverse (#292f36) $header_text/$white (#fff) -> --text-neutral-inverse (#ffffff) Both are EXACT (inverse tokens pair with the inverse background; no visual change on any brand). $darkest_gray and $header_text now have no other callers in the repo, but both live in shared/css/color.scss, which is out of scope for this change and left untouched. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gradient Continues the header/footer chrome-migration train (see the two commits immediately prior, and PhoneFrame, c90bb3a, for the original exemplar). The header vignette's fade-to-edge gradient stops hardcoded rgba(231, 232, 234, a) — the old $lightest_gray hex — while the container it sits on top of already reads var(--background-neutral-secondary) (#f0f2f5), migrated ahead of this change. The vignette hadn't kept up: it was fading to a color that no longer matched its own background. rgba() can't reference a CSS variable directly, so each stop becomes a color-mix() call that reads the same token at the stop's original alpha: rgba(231, 232, 234, 1) -> color-mix(in srgb, var(--background-neutral-secondary) 100%, transparent) rgba(231, 232, 234, 0) -> color-mix(in srgb, var(--background-neutral-secondary) 0%, transparent) Small drift (#e7e8ea-based fade -> #f0f2f5-based fade) that now makes the vignette consistent with the surface it fades into, on every brand. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Design ruling (2026-07-20): the accent-orange hover states on .headerlink and .header_button don't carry over to the rebrand — this hover will eventually be replaced by a marketing-site-style "tag" hover in the nav redesign, not yet designed. Tokenizing it would incorrectly paint it CADS orange under codeai-next. Revert both sites to the original $orange SCSS variable and note why they're intentionally excluded from tokenization. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Design ruling (2026-07-20): complete the white-fixed family so background, text, and borders have a consistent member set across both token schemas — legacy plural --borders-neutral-white-fixed and CADS singular --border-neutral-white-fixed, both resolving to white in every theme and brand. Adds the token to colors.css (legacy) and colors_codeAi.css (CADS canonical export), regenerates brandCodeAiNext.css from the canonical source via scripts/generateBrandCodeAiNext.mjs, and wires the bridge files (brandLegacyShim.css, brandCodeAiNextAliases.css, brandCodeAiAudit.css) so the new name resolves under every brand and theme. Adopts it in the two .header_button border sites in application.scss that previously reused --background-neutral-white-fixed as a workaround, removing the workaround comments. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The left-edge vignette's linear-gradient string has been missing its closing paren since 68b79ff (2020), so the CSSOM dropped the invalid background and the left fade never rendered in the left-anchored scroll state. Surfaced while reviewing the rgba -> color-mix rewrite, which had preserved the truncation byte-for-byte. Behavior change: the left-edge fade now renders, matching what headerVignetteRight has always done. Any Eyes baseline capturing a left-anchored progress header will need a one-time re-accept. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
stephenliang
marked this pull request as ready for review
July 20, 2026 21:14
🖼️ Storybook Visual Comparison Report✅ No Storybook eyes differences detected! |
levadadenys
approved these changes
Jul 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Migrates the legacy site header and footer from hardcoded SCSS colors to DSCO semantic tokens, following the established [Rebrand] in-place tokenization pattern (exemplar: PhoneFrame, #73055). The chrome now responds to the brand router: unchanged (modulo the documented drift below) under the default
codeaibrand, CADS colors (purple#4c42cfbar, near-black footer) undercodeai-next, and pink undercodeai-audit— which is the point: the header/footer were among the last audit-opaque surfaces.Tokenization
One commit per surface, each carrying its full hex-mapping table in the commit message: header + user menu + hamburger (
fdf92c79f72), full footer (28738f2184e), LessonProgress vignette gradient (6ea14067e00). Color-value swaps only — zero selector, markup, or layout changes, so the UI-test selector contract (#header_user_menu,.headerlinks,#hamburger-icon, …) is untouched.Headline exact mappings (no default-brand pixel change): header bar
$contrastTealHeader #00818f→--background-brand-teal-primary, footer$darkest_gray #292F36→--background-neutral-primary-inverse, all whites →--text-neutral-white-fixed/--background-neutral-primary, menu text#292F36→--text-neutral-primary.Documented nearest-token drift on the default brand (per-commit tables have the full list):
#D1D4D8#d4dae1#e7e8ea#dfe3e9#8C52BA#9657c7.display_namechip — no-op: the swapped rule (.user_options .display_name) is pre-existing dead CSS whose selector never matches the HAML (user_header.haml:73puts.display_nameoutside.user_options); the visible chip background is.header_user(row above)#5b6770#69788a.header_userchip teal (now matches the bar)#0093a4#00818f#d62911#e02d16#c6cacd#b7c1cb#0000001a/#0000000f#292f361a/#292f360d#000/#8c52ba#292f36/#9657c7New token:
--borders-neutral-white-fixedDesign ruled the white-fixed family should be complete across background/text/borders, so the border member is added through the whole bridge (
7840c22d255): legacycolors.css(light+dark), canonicalcolors_codeAi.css+ regeneratedbrandCodeAiNext.css(generator--checkgreen),brandLegacyShim.css,brandCodeAiNextAliases.css, andbrandCodeAiAudit.css(pinked#fff8fc, matching siblings). The header buttons' white borders adopt it.colors_codeAi.cssis a CADS Sandbox export ("do not hand-edit"). The token must also be added to the sandbox source, or the next re-export silently drops it — the generator--checkwould still pass and onlycodeai-nextwould lose the header-button borders (default and audit resolve via other files).Design rulings applied
The accent-orange header-link/button hovers are deliberately NOT tokenized (
de0aff56fb3) — design says that hover does not carry over to the new brand (a marketing-style hover replaces it in the nav redesign), so painting it CADS orange undercodeai-nextwould be wrong. The two sites keep$orangewith an explanatory comment and will honestly show non-pink under audit. The small footer (light and dark share variants) is out of scope entirely — no mockup exists; its hexes are untouched, so allfooter.featureEyes baselines stay green. Levelbuilder/local env header colors andshared/css/color.scssare also untouched (note:$header_textincolor.scssis now orphaned — its last four consumers were tokenized here; left in place since the shared palette file was fenced off).Bug fix: left vignette gradient
headerVignetteLeft's gradient string had been missing its closing paren since 2020 (68b79ff4565), so the CSSOM dropped it and the left-edge fade never rendered in the left-anchored scroll state. Fixed (d98d5b6213e) — the left edge now fades like the right edge always has. This is the branch's only behavior change; an Eyes baseline capturing that scroll state would need a one-time re-accept.Token resolution per brand
flowchart LR A["var(--background-brand-teal-primary)<br/>in application.scss"] --> B{data-brand} B -->|"code / codeai"| C["colors.css<br/>brand-teal-60 = #00818f<br/>(pixel-identical)"] B -->|"codeai-next"| D["brandCodeAiNextAliases.css<br/>→ brandCodeAiNext.css<br/>brand-purple-50 = #4c42cf"] B -->|"codeai-audit"| E["brandCodeAiAudit.css<br/>pink"]Screenshots
Links
Testing story
./tools/hooks/pre-commitgreen after every commit batch.generateBrandCodeAiNext.mjs --checkgreen (also the package'snpm test).yarn typecheckshows pre-existing errors only (missing generated build artifacts, unrelated). Header unit-test run blocked by the known missing-yarn buildlocale-artifact issue — the change is CSS-only, lint + generator check are the material gates. Manual browser matrix on local dev (level page, project page, signed-out home): default brand pixel-stable except the documented drift;?brand=codeai-nextrenders the purple bar / near-black footer;?brand=codeai-auditrenders the migrated chrome pink. An xhigh multi-agent code review (10 finder angles + adversarial verification) ran over the branch; all confirmed findings are addressed in commits or documented above.Eyes impact:
footer.featurebaselines are all small-footer snapshots — untouched. Page-level snapshots containing a signed-in header may show the.header_userchip drift (#0093a4→#00818f) and need re-accepts.🤖 Generated with Claude Code