Repository navigation
Theme consolidation: one tokens file, parity checks, Studio light and Dusk dark - #195
Merged
Merged
Conversation
Add scripts/check-theme-tokens.mjs (yarn theme:check, wired into CI after stylelint). It checks that the dark and light theme blocks define the same names in the same order, that every var(--x) resolves, that every defined custom property is read, and that [data-theme only appears in the tokens file. Today's offenders are listed in KNOWN_OFFENDERS and print as warnings until steps 1, 2 and 5 remove them. Add useTheme unit tests and a parity test that evaluates the inline FOUC script from index.html against the same storage and media cases. Extend the visual theme sweep with nine seeded routes on CS101-2024 from seed_e2e_data: student course detail and three problem-set solve pages (MCQ, probeable, refute), instructor overview, submissions and problem sets, admin students and problem sets. The instructor students page is left out: it lists every accumulated test student and the full-page screenshot times out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cut App.vue's :root and [data-theme="light"] blocks into src/styles/tokens.css, imported first from style.css. The file has a theme-independent :root block (spacing, radius, type, transitions, layout, and --color-focus-ring defined once as an alias that resolves per theme), then ":root, [data-theme="dark"]" with every colour, gradient and shadow token, then [data-theme="light"] with the same names in the same order. A header comment states the rules. The three segmentation colour aliases are replaced by the status tokens they pointed at; the four dead --seg-* entries and the three dead --hint-* aliases are deleted. The stylelint colour exemption now covers only tokens.css. The theme check reads tokens.css and also flags App.vue's footer logo [data-theme] override, listed for step 5. Token dump: no value differences in either theme; only the seven removed --seg-* aliases are gone. Pixel sweep: 0 on all 58 images. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rewrite the 7 var(--color-border, var(--color-bg-input)) and 5 var(--color-surface, var(--color-bg-hover)) sites to the token that was actually rendering, which frees both names for later steps. Map the undefined names: --font-mono and --font-family-mono to a new --font-mono, --font-sans defined from the body stack (body now uses it), --color-background and --color-bg-page/--color-bg-base to bg-main, --color-bg-card and --color-bg-primary to bg-panel, --color-bg-code to bg-dark, --color-bg-muted to bg-section, --color-danger to error, --color-text-code to text-primary, and --shadow-xl to --shadow-lg until the shadow merge defines xl. Delete the dead --color-bg-table, --color-info-text, --max-width-panel and --transition-slow. color-scheme is now set only in tokens.css (dark and light blocks), so the root scrollbar follows the theme; the body rules in style.css and the inline body.style.colorScheme in useTheme are gone. setTheme no longer throws when localStorage is unavailable. The theme check is now blocking. The only listed offenders are the five files with [data-theme] overrides that step 5 moves into tokens. Visible: monospace in the refute input and feedback, probe panel, trace overlay and the course ID chip; the toast hover shadow; the MCQ and refute result cards take the panel background like the code feedback panel; the refute claim label turns error red; the refute argument input takes the panel background. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t tokens --color-primary-gradient-start carried four roles: focus ring, solid accent, gradient stop and the start of the brand gradient. Split it by property and selector: - outline* values and border*/box-shadow inside :focus rules use --color-focus-ring (57 + 41), now defined once as var(--color-accent); - the 34 identical 135deg start/end gradients use --gradient-primary, the five admin/end hybrids use --gradient-admin and the NavBar logo uses --gradient-logo, replacing the three --color-logo-* stops; - the remaining stops inside gradients use --color-gradient-start/-end; - remaining solid uses take --color-accent (140) and --color-accent-strong (10). --color-primary-overlay/-glow/-shadow become --color-accent-*, and --shadow-colored becomes --shadow-accent. Every new token has the value of the token it replaces in both themes. Stylelint now rejects gradient stops outside gradients, a background that is a bare stop, and outlines on --color-accent instead of the focus ring. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rename the tokens whose names say the wrong thing: --color-bg-border is the line colour, now --color-border (256 uses); --color-bg-dark is the sunken well, now --color-bg-sunken; --color-segment-1..6 become --color-category-1..6, the one categorical ramp. --color-bg-panel-light had one consumer (segmentation.css) and merges into --color-bg-panel, which moves that panel from #1f1f1f to #1e1e1e in dark (light unchanged). The two getComputedStyle reads of the ramp now fall back to the token itself instead of the dark-only #9f7aea, and the unused connectionColor data field with its dark-only #667eea is gone. The unused .bg-dark utility class is deleted. The theme check's dynamic prefix follows the ramp to --color-category-. The poisoned var(--color-border, ...) and var(--color-surface, ...) fallbacks were already rewritten in step 2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No component carries a [data-theme] selector any more; every per-theme difference is a token value in styles/tokens.css. - Sub-navigation bars: new --nav-sub-bg, --color-nav-sub-text, --color-nav-sub-active-bg, --color-nav-sub-active-text, --color-nav-sub-hover-border and --color-nav-sub-focus carry the dark values of the base rules and the light values of the mirrored override blocks. AdminNavBar and InstructorNavBar consume them and lose their light blocks. The active tab's border uses the active background (light had a transparent border over the same fill). - NavBar: the light patch for .instructor-item/.admin-item never applied (0,2,0 loses to the scoped hover and active rules), so it is deleted without changing the selectors. - InstructorCourseOverview: --gradient-admin is plain --color-admin in light, --gradient-admin-hover covers the light-only back-button hover, and --marker-line-bg replaces the light opacity hack. Light block gone. Side effect: the instructor dashboard's create button is flat admin in light instead of the admin to accent-strong gradient. - App.vue footer logo filter: --logo-filter and --logo-filter-hover. - Stylelint disallows [data-theme] selectors outside tokens.css and the theme check's KNOWN_OFFENDERS list is empty. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every alpha token whose RGB equals a base token is now color-mix(in srgb, var(--base) P%, transparent) instead of a copied literal, so a base colour change carries its overlays, borders and glows with it: the four status -overlay and -border tokens, the accent overlay/glow/shadow, --color-ace-selection (primary), --color-segment-shadow (category-1), --color-warning-pulse (dark: warning-accent, light: warning), --color-admin-shadow (dark), --color-info-shadow (light) and --marker-line-bg (light). --color-warning-pulse-end is plain transparent. Literals whose RGB drifted from their base stay, marked inline: dark --color-info-shadow and --shadow-info-*, light --color-admin-shadow and --shadow-admin-*. The tokens.css header states the browser floor (Chrome 111, Safari 16.2, Firefox 113). Stylelint disallows color-mix() and light-dark() outside tokens.css. Computed colours are equal to the old literals; pixels differ by at most 1/255 where Chrome kept 8-bit alpha for rgb() and keeps float alpha for color-mix(). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Applies the two chosen palettes as token values in styles/tokens.css. Light (Studio): one violet accent (#6d28d9) that primary, info, admin and the gradient stops alias; --gradient-primary and --gradient-admin are flat; cool grey surfaces with --color-bg-header equal to the panel, so header bands merge with their card and only the hairline stays; quieter shadows and no nav shadow; sub-navigation bars on the panel with an admin tint for the active tab. Dark (Dusk): warm violet-grey surfaces, lavender accent (#c3a6ff) for text, borders and the focus ring, #7c3aed for filled buttons; the CTA keeps a soft violet gradient, admin fills and gradient text go solid; filled header bands stay. Status colours move to the Tailwind 300/400 steps. The drifted alpha literals (dark --color-info-shadow and --shadow-info-*, light --color-admin-shadow and --shadow-admin-*) are now color-mix of their bases. --color-nav-sub-hover-border points at --color-border in both themes. Ace: tomorrow in light and tomorrow_night_eighties in dark; the light syntax tokens follow tomorrow (number darkened to #b45309 for AA) and the dark ones follow tomorrow_night_eighties. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Component changes that go with the Studio and Dusk palettes, all through tokens (no [data-theme] outside tokens.css). - NavBar: Instructor and Admin share one role style on the new --color-nav-item-bg/-text/-hover-bg/-border tokens (tinted in Dusk, plain text in Studio); the per-role fills and glows are gone. Account sits on the panel with a hairline. Active route is --color-nav-item-hover-bg plus accent text and a solid accent underline; the rule follows hover at equal specificity, so it holds while hovered. - Probe button and TestLadder Debug button: tinted (--color-info-bg with the restored --color-info-text). Debug shows a visible "Debug"/"Depurar" label (feedback.tests.debug). Passing pips get a success border; the failing pip is solid --color-error-dark without the double ring. - ProblemSet progress strip: in-progress moves from warning to info. - Semantic sites: --color-instructor (new) for StatusBadge and the AccountModal role badge (outline, since lavender cannot carry white text in Dusk); admin badges are solid --color-admin. StatusBadge gains category-1..6 outline variants used for ProblemList problem types (border carries the ramp colour, text stays neutral because the ramp clears 3:1 but not 4.5:1). Data-type badges move to one global styles/data-types.css on the ramp; the dead copy in EiplProblemEditor is removed. SegmentAnalysisModal goal badge uses the accent; ProbePanel .fn-name uses --color-syntax-builtin. - Dead after the restyle and deleted: --color-info-shadow, --color-admin-shadow, --shadow-info-hover/-active, --shadow-admin-hover/-active. - Docs: DEVELOPMENT.md "Dark Theme Only" replaced by a short Theming section pointing at tokens.css and yarn theme:check; TECH_DEBT.md item 25 removed; stale dark-only comment in style.css removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
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.
Summary
Consolidates the colour theme system into one file and applies the two new palettes: Studio (light) and Dusk (dark). After this PR a palette change is an edit to the light or dark block of
purplex/client/src/styles/tokens.cssplus, at most, one line for the Ace theme name. CI now refuses a token that exists in one theme and not the other, a token that is defined but unused, avar()that resolves to nothing, and any[data-theme]selector outside the tokens file.Design review and plan: the "Purplex Theme Consolidation" artifact. Palette mock-ups and live contrast tables: the "Purplex Light Studies" artifact (Studio tab for light, Dusk tab for dark).
What was wrong
--color-primary-gradient-start, did five jobs across 280 call sites: gradient stop, link colour, active border, selected state and 98 focus indicators. The purpose-built--color-focus-ringhad 2 consumers.[data-theme="light"]blocks across 5 components, including a NavBar patch that, it turns out, never applied because of specificity.Commits, one per step
Steps 0 to 6 are visually no-ops, proven two ways per step: a dump of every computed custom property in both themes diffed through the rename map, and the two-theme Playwright screenshot sweep (29 routes per theme, up from 20) compared with ImageMagick. Residual diffs are listed in each commit message.
scripts/check-theme-tokens.mjs(yarn theme:check, wired into CI),useThemeunit tests, a FOUC-script parity test, 9 seeded routes added to the visual sweepApp.vueintosrc/styles/tokens.css: a theme-independent block, then mirrored dark and light blocks in identical order; stylelint's colour-literal exemption narrows to that filecolor-schemeset in one place. Visible on purpose: monospace returns in Refute and Probe, the toast gets a shadow, Refute and MCQ feedback cards get the panel background they were silently missing--color-accent,--color-accent-strong,--color-focus-ring(98 sites) and whole-gradient tokens--gradient-primary/-admin/-logo; 78 files rewritten by a property-aware codemod; stylelint now forbids gradient stops outside gradients and outlines that bypass the focus tokenbg-border → border,bg-dark → bg-sunken,bg-panel-lightmerged intobg-panel(the one 1/255 change),segment-N → category-Nincluding the two dynamic reads and their dark-only hex fallbacksnav-subtokens; their light blocks, the course overview's andApp.vue's go away;selector-disallowed-listturns on for[data-theme]color-mix()from their bases (browser floor: Chrome 111, Safari 16.2, Firefox 113, stated in the file header); computed colours proven equaltomorrowin light andtomorrow_night_eightiesin dark--color-instructor; categorical problem-type badges; shared data-type legend; docs updatedDecisions taken
color-mix()with a 2023 browser floor.--color-instructoradded and used by StatusBadge, the NavBar pill and AccountModal.bg-headerintobg-section,text-tertiaryintosecondary) are deferred to follow-up PRs with their own screenshots.Visible changes to expect
tomorrow.tomorrow_night_eighties.Follow-ups, not in this PR
bg-header → bg-section,text-tertiary → secondary(decision 5).src/styles/hints.cssis imported nowhere and is dead code; delete it in its own PR.instructor-course-studentsroute is excluded from the sweep: it lists thousands of accumulated test students and the full-page screenshot times out.Step 7 notes for the reviewer
Contrast was asserted by script on 16 text pairs in both themes after step 7; the lowest is white on the dark hover fill at 4.56:1. The full table is in the step 7a commit message.
Choices made while applying the palettes, each a judgement call you may want to look at:
Left for your eye, unchanged in this PR: the refute page's Test button still uses the primary gradient token (a soft gradient in dark, flat in light, same as Submit); the dark login wordmark uses the primary gradient and is dimmer than the navbar logo gradient; the light warning banner is unchanged from before; the fixed footer overlaps long pages in full-page screenshots as it did before.
How to review
Pull the branch, run
yarn theme:checkandyarn stylelint:checkinpurplex/client, then open the problem page in both themes. Each step's commit message lists its residual pixel diffs. The Studio and Dusk tabs of the studies artifact are the reference renderings.🤖 Generated with Claude Code