From 8d75c8b678338b54d8f857435e8fc4e3d2ca9eaf Mon Sep 17 00:00:00 2001 From: Juan Andrade Date: Wed, 11 Feb 2026 16:35:19 -0500 Subject: [PATCH 1/4] [wb-color2sc-2] Migrate color to semanticColor in perseus --- .../perseus/src/components/math-input.tsx | 8 ++--- packages/perseus/src/util/colors.ts | 12 ++++---- .../widgets/free-response/free-response.tsx | 9 ++---- .../graded-group-set/graded-group-set.tsx | 8 ++--- .../graded-group/graded-group-answer-bar.tsx | 16 +++++++--- .../src/widgets/graded-group/graded-group.tsx | 6 ++-- .../graphs/components/angle-indicators.tsx | 8 +++-- .../graphs/components/hairlines.tsx | 6 ++-- .../interactive-graph.test.tsx | 16 +++++----- .../locked-figures/locked-ellipse.tsx | 4 +-- .../locked-figures/locked-line.tsx | 6 ++-- .../locked-figures/locked-point.tsx | 4 +-- .../locked-figures/locked-polygon.tsx | 4 +-- .../src/widgets/label-image/answer-pill.tsx | 6 ++-- .../src/widgets/label-image/marker.tsx | 30 ++++++++++--------- 15 files changed, 76 insertions(+), 67 deletions(-) diff --git a/packages/perseus/src/components/math-input.tsx b/packages/perseus/src/components/math-input.tsx index e5978a4b252..9f37b95dbcc 100644 --- a/packages/perseus/src/components/math-input.tsx +++ b/packages/perseus/src/components/math-input.tsx @@ -11,7 +11,7 @@ import { import Clickable from "@khanacademy/wonder-blocks-clickable"; import {View} from "@khanacademy/wonder-blocks-core"; import {Popover, PopoverContentCore} from "@khanacademy/wonder-blocks-popover"; -import {color, semanticColor, sizing} from "@khanacademy/wonder-blocks-tokens"; +import {semanticColor, sizing} from "@khanacademy/wonder-blocks-tokens"; import {HeadingMedium} from "@khanacademy/wonder-blocks-typography"; import {StyleSheet} from "aphrodite"; import classNames from "classnames"; @@ -550,17 +550,17 @@ const styles = StyleSheet.create({ }, iconInactive: { border: "2px solid transparent", - backgroundColor: color.offBlack8, + backgroundColor: semanticColor.core.background.neutral.subtle, }, iconActive: { border: `2px solid ${semanticColor.core.border.knockout.default}`, - backgroundColor: color.offBlack64, + backgroundColor: semanticColor.core.background.neutral.default, }, outerWrapper: { display: "inline-block", borderStyle: "solid", borderWidth: 1, - borderColor: color.offBlack50, + borderColor: semanticColor.core.border.neutral.default, borderRadius: 3, background: semanticColor.core.background.base.default, ":hover": inputFocused, diff --git a/packages/perseus/src/util/colors.ts b/packages/perseus/src/util/colors.ts index e766dd28806..60857faeca8 100644 --- a/packages/perseus/src/util/colors.ts +++ b/packages/perseus/src/util/colors.ts @@ -6,13 +6,13 @@ * khan-exercises submodule, as graphie-to-png still relies on the palette * provided on KhanUtil. */ -import {color} from "@khanacademy/wonder-blocks-tokens"; +import {semanticColor} from "@khanacademy/wonder-blocks-tokens"; const KhanColors = { - BLUE: color.blue, + BLUE: semanticColor.core.foreground.instructive.default, ORANGE: "#FFA500", PINK: "#FF00AF", - GREEN: color.green, + GREEN: semanticColor.core.foreground.success.default, PURPLE: "#9D38BD", RED: "#DF0030", GRAY: "gray", @@ -87,9 +87,9 @@ const KhanColors = { // background color used in exercises is subject to change at the whim // of any redesigns. _BACKGROUND: "#FDFDFD", - INTERACTING: color.green, - INTERACTIVE: color.green, - DYNAMIC: color.blue, + INTERACTING: semanticColor.core.foreground.success.default, + INTERACTIVE: semanticColor.core.foreground.success.default, + DYNAMIC: semanticColor.core.foreground.instructive.default, } as const; /** diff --git a/packages/perseus/src/widgets/free-response/free-response.tsx b/packages/perseus/src/widgets/free-response/free-response.tsx index 0b9399836e1..420ef5d3380 100644 --- a/packages/perseus/src/widgets/free-response/free-response.tsx +++ b/packages/perseus/src/widgets/free-response/free-response.tsx @@ -9,12 +9,7 @@ import {Text, View} from "@khanacademy/wonder-blocks-core"; import {TextArea} from "@khanacademy/wonder-blocks-form"; import {PhosphorIcon} from "@khanacademy/wonder-blocks-icon"; import {LabeledField} from "@khanacademy/wonder-blocks-labeled-field"; -import { - color, - font, - spacing, - semanticColor, -} from "@khanacademy/wonder-blocks-tokens"; +import {font, spacing, semanticColor} from "@khanacademy/wonder-blocks-tokens"; import warningCircleIcon from "@phosphor-icons/core/regular/warning-circle.svg"; import {StyleSheet} from "aphrodite"; import * as React from "react"; @@ -158,7 +153,7 @@ const styles = StyleSheet.create({ fontSize: font.size.small, }, overCharacterLimit: { - color: color.red, + color: semanticColor.core.foreground.critical.default, }, textarea: { padding: spacing.medium_16, diff --git a/packages/perseus/src/widgets/graded-group-set/graded-group-set.tsx b/packages/perseus/src/widgets/graded-group-set/graded-group-set.tsx index 8c14c900f60..4a8e1b22fdd 100644 --- a/packages/perseus/src/widgets/graded-group-set/graded-group-set.tsx +++ b/packages/perseus/src/widgets/graded-group-set/graded-group-set.tsx @@ -2,7 +2,7 @@ import {linterContextDefault} from "@khanacademy/perseus-linter"; import Clickable from "@khanacademy/wonder-blocks-clickable"; import {View} from "@khanacademy/wonder-blocks-core"; -import {color} from "@khanacademy/wonder-blocks-tokens"; +import {semanticColor} from "@khanacademy/wonder-blocks-tokens"; import {StyleSheet, css} from "aphrodite"; import classNames from "classnames"; import * as React from "react"; @@ -263,7 +263,7 @@ const styles = StyleSheet.create({ title: { fontSize: 12, - color: color.offBlack64, + color: semanticColor.core.foreground.neutral.subtle, textTransform: "uppercase", marginBottom: 11, letterSpacing: 0.8, @@ -302,7 +302,7 @@ const styles = StyleSheet.create({ height: 10, borderRadius: "100%", borderWidth: 2, - borderColor: color.blue, + borderColor: semanticColor.core.border.instructive.default, borderStyle: "solid", }, @@ -312,7 +312,7 @@ const styles = StyleSheet.create({ }, indicatorDotActive: { - backgroundColor: color.blue, + backgroundColor: semanticColor.core.background.instructive.default, width: "100%", height: "100%", }, diff --git a/packages/perseus/src/widgets/graded-group/graded-group-answer-bar.tsx b/packages/perseus/src/widgets/graded-group/graded-group-answer-bar.tsx index a7600ba2b22..9a94ae911e2 100644 --- a/packages/perseus/src/widgets/graded-group/graded-group-answer-bar.tsx +++ b/packages/perseus/src/widgets/graded-group/graded-group-answer-bar.tsx @@ -2,7 +2,7 @@ * Renders answer bar for mobile graded groups. [STATELESS] */ import Button from "@khanacademy/wonder-blocks-button"; -import {color} from "@khanacademy/wonder-blocks-tokens"; +import {semanticColor} from "@khanacademy/wonder-blocks-tokens"; import * as React from "react"; import {PerseusI18nContext} from "../../components/i18n-context"; @@ -50,7 +50,9 @@ class GradedGroupAnswerBar extends React.Component { const answerBarStyle = { ...styles.answerBar, backgroundColor: - answerBarState === "CORRECT" ? color.offWhite : "white", + answerBarState === "CORRECT" + ? semanticColor.core.background.base.subtle + : semanticColor.core.background.base.default, // Center the "Correct!" message only when there's no next question justifyContent: answerBarState === "CORRECT" && !onNextQuestion @@ -91,7 +93,13 @@ class GradedGroupAnswerBar extends React.Component { return (
- + )} {showAngles && ( - + {angleLabel}° )} diff --git a/packages/perseus/src/widgets/interactive-graphs/graphs/components/hairlines.tsx b/packages/perseus/src/widgets/interactive-graphs/graphs/components/hairlines.tsx index 690fc7f9fe0..c08af3b06de 100644 --- a/packages/perseus/src/widgets/interactive-graphs/graphs/components/hairlines.tsx +++ b/packages/perseus/src/widgets/interactive-graphs/graphs/components/hairlines.tsx @@ -1,4 +1,4 @@ -import {color} from "@khanacademy/wonder-blocks-tokens"; +import {semanticColor} from "@khanacademy/wonder-blocks-tokens"; import * as React from "react"; import useGraphConfig from "../../reducer/use-graph-config"; @@ -34,14 +34,14 @@ export default function Hairlines(props: Props) { y1={y} x2={verticalEndX} y2={y} - stroke={color.blue} + stroke={semanticColor.core.border.instructive.default} /> ); diff --git a/packages/perseus/src/widgets/interactive-graphs/interactive-graph.test.tsx b/packages/perseus/src/widgets/interactive-graphs/interactive-graph.test.tsx index e5cee9df85a..197e1aadb5d 100644 --- a/packages/perseus/src/widgets/interactive-graphs/interactive-graph.test.tsx +++ b/packages/perseus/src/widgets/interactive-graphs/interactive-graph.test.tsx @@ -5,7 +5,7 @@ import { splitPerseusItem, getDefaultFigureForType, } from "@khanacademy/perseus-core"; -import {color as wbColor} from "@khanacademy/wonder-blocks-tokens"; +import {semanticColor} from "@khanacademy/wonder-blocks-tokens"; import {waitFor} from "@testing-library/react"; import {userEvent as userEventLib} from "@testing-library/user-event"; import {Plot} from "mafs"; @@ -413,7 +413,7 @@ describe("Interactive Graph", function () { stroke: lockedFigureColors.grayH, }); expect(points[1]).toHaveStyle({ - fill: wbColor.white, + fill: semanticColor.core.background.base.default, stroke: lockedFigureColors.grayH, }); }); @@ -535,7 +535,7 @@ describe("Interactive Graph", function () { stroke: lockedFigureColors.grayH, }); expect(points[1]).toHaveStyle({ - fill: wbColor.white, + fill: semanticColor.core.background.base.default, stroke: lockedFigureColors.grayH, }); }); @@ -785,11 +785,11 @@ describe("Interactive Graph", function () { stroke: lockedFigureColors.green, }); expect(linePoints[1]).toHaveStyle({ - fill: wbColor.white, + fill: semanticColor.core.background.base.default, stroke: lockedFigureColors.green, }); expect(linePoints[2]).toHaveStyle({ - fill: wbColor.white, + fill: semanticColor.core.background.base.default, stroke: lockedFigureColors.grayH, }); expect(linePoints[3]).toHaveStyle({ @@ -797,7 +797,7 @@ describe("Interactive Graph", function () { stroke: lockedFigureColors.grayH, }); expect(rayPoints[0]).toHaveStyle({ - fill: wbColor.white, + fill: semanticColor.core.background.base.default, stroke: lockedFigureColors.pink, }); }); @@ -1015,7 +1015,7 @@ describe("Interactive Graph", function () { // Assert expect(whiteCircle).toHaveStyle({ "fill-opacity": 1, - fill: wbColor.white, + fill: semanticColor.core.background.base.default, stroke: lockedFigureColors["green"], }); expect(translucentCircle).toHaveStyle({ @@ -1186,7 +1186,7 @@ describe("Interactive Graph", function () { // Assert expect(whitePolygon).toHaveStyle({ "fill-opacity": 1, - fill: wbColor.white, + fill: semanticColor.core.background.base.default, stroke: lockedFigureColors["green"], }); expect(translucentPolygon).toHaveStyle({ diff --git a/packages/perseus/src/widgets/interactive-graphs/locked-figures/locked-ellipse.tsx b/packages/perseus/src/widgets/interactive-graphs/locked-figures/locked-ellipse.tsx index 8cf4a558833..29489d1220c 100644 --- a/packages/perseus/src/widgets/interactive-graphs/locked-figures/locked-ellipse.tsx +++ b/packages/perseus/src/widgets/interactive-graphs/locked-figures/locked-ellipse.tsx @@ -3,7 +3,7 @@ import { lockedFigureColors, type LockedEllipseType, } from "@khanacademy/perseus-core"; -import {color as wbColor} from "@khanacademy/wonder-blocks-tokens"; +import {semanticColor} from "@khanacademy/wonder-blocks-tokens"; import {Ellipse} from "mafs"; import * as React from "react"; @@ -45,7 +45,7 @@ const LockedEllipse = (props: LockedEllipseType) => { style: { fill: fillStyle === "white" - ? wbColor.white + ? semanticColor.core.background.base.default : lockedFigureColors[color], }, }} diff --git a/packages/perseus/src/widgets/interactive-graphs/locked-figures/locked-line.tsx b/packages/perseus/src/widgets/interactive-graphs/locked-figures/locked-line.tsx index df40951c7d1..b3d87cf01bd 100644 --- a/packages/perseus/src/widgets/interactive-graphs/locked-figures/locked-line.tsx +++ b/packages/perseus/src/widgets/interactive-graphs/locked-figures/locked-line.tsx @@ -1,6 +1,6 @@ import {angles} from "@khanacademy/kmath"; import {lockedFigureColors} from "@khanacademy/perseus-core"; -import {color as wbColor, spacing} from "@khanacademy/wonder-blocks-tokens"; +import {semanticColor, spacing} from "@khanacademy/wonder-blocks-tokens"; import {Point, Line, vec} from "mafs"; import * as React from "react"; @@ -136,7 +136,7 @@ const LockedLine = (props: Props) => { style: { fill: point1.filled ? lockedFigureColors[point1.color] - : wbColor.white, + : semanticColor.core.background.base.default, stroke: lockedFigureColors[point1.color], strokeWidth: spacing.xxxxSmall_2, }, @@ -151,7 +151,7 @@ const LockedLine = (props: Props) => { style: { fill: point2.filled ? lockedFigureColors[point2.color] - : wbColor.white, + : semanticColor.core.background.base.default, stroke: lockedFigureColors[point2.color], strokeWidth: spacing.xxxxSmall_2, }, diff --git a/packages/perseus/src/widgets/interactive-graphs/locked-figures/locked-point.tsx b/packages/perseus/src/widgets/interactive-graphs/locked-figures/locked-point.tsx index 2b061299600..4c48dd246fe 100644 --- a/packages/perseus/src/widgets/interactive-graphs/locked-figures/locked-point.tsx +++ b/packages/perseus/src/widgets/interactive-graphs/locked-figures/locked-point.tsx @@ -2,7 +2,7 @@ import { lockedFigureColors, type LockedPointType, } from "@khanacademy/perseus-core"; -import {color as wbColor, spacing} from "@khanacademy/wonder-blocks-tokens"; +import {semanticColor, spacing} from "@khanacademy/wonder-blocks-tokens"; import {Point} from "mafs"; import * as React from "react"; @@ -26,7 +26,7 @@ const LockedPoint = (props: LockedPointType) => { style: { fill: filled ? lockedFigureColors[color] - : wbColor.white, + : semanticColor.core.background.base.default, stroke: lockedFigureColors[color], strokeWidth: spacing.xxxxSmall_2, }, diff --git a/packages/perseus/src/widgets/interactive-graphs/locked-figures/locked-polygon.tsx b/packages/perseus/src/widgets/interactive-graphs/locked-figures/locked-polygon.tsx index f12e9fa7d3f..eef5a0cdb4b 100644 --- a/packages/perseus/src/widgets/interactive-graphs/locked-figures/locked-polygon.tsx +++ b/packages/perseus/src/widgets/interactive-graphs/locked-figures/locked-polygon.tsx @@ -2,7 +2,7 @@ import { lockedFigureColors, lockedFigureFillStyles, } from "@khanacademy/perseus-core"; -import {color as wbColor} from "@khanacademy/wonder-blocks-tokens"; +import {semanticColor} from "@khanacademy/wonder-blocks-tokens"; import {Point, Polygon} from "mafs"; import * as React from "react"; @@ -37,7 +37,7 @@ const LockedPolygon = (props: LockedPolygonType) => { style: { fill: fillStyle === "white" - ? wbColor.white + ? semanticColor.core.background.base.default : lockedFigureColors[color], }, }} diff --git a/packages/perseus/src/widgets/label-image/answer-pill.tsx b/packages/perseus/src/widgets/label-image/answer-pill.tsx index 0fcde846646..348d0736b79 100644 --- a/packages/perseus/src/widgets/label-image/answer-pill.tsx +++ b/packages/perseus/src/widgets/label-image/answer-pill.tsx @@ -1,5 +1,5 @@ import Pill from "@khanacademy/wonder-blocks-pill"; -import {color} from "@khanacademy/wonder-blocks-tokens"; +import {semanticColor} from "@khanacademy/wonder-blocks-tokens"; import {StyleSheet, type CSSProperties} from "aphrodite"; import * as React from "react"; import {useId} from "react"; @@ -9,7 +9,7 @@ import {usePerseusI18n} from "../../components/i18n-context"; import Renderer from "../../renderer"; const BringToFront: CSSProperties = { - boxShadow: `0 8px 8px ${color.offBlack64}`, + boxShadow: `0 8px 8px ${semanticColor.core.border.neutral.default}`, zIndex: 1000, }; @@ -89,7 +89,7 @@ const styles = StyleSheet.create({ backgroundColor: "#00880b", }, incorrect: { - backgroundColor: color.offBlack64, + backgroundColor: semanticColor.core.background.neutral.default, }, pill: { // Reset the Pill's default height in order to account diff --git a/packages/perseus/src/widgets/label-image/marker.tsx b/packages/perseus/src/widgets/label-image/marker.tsx index b3bf4541ba7..c151c39b3c0 100644 --- a/packages/perseus/src/widgets/label-image/marker.tsx +++ b/packages/perseus/src/widgets/label-image/marker.tsx @@ -6,7 +6,7 @@ */ import {View, type StyleType} from "@khanacademy/wonder-blocks-core"; -import {color} from "@khanacademy/wonder-blocks-tokens"; +import {boxShadow, semanticColor} from "@khanacademy/wonder-blocks-tokens"; import {StyleSheet} from "aphrodite"; import * as React from "react"; @@ -90,7 +90,7 @@ export default class Marker extends React.Component { // default dot let args: Icon["props"] = { size: MARKER_SIZE, - color: color.white, + color: semanticColor.core.foreground.knockout.default, icon: iconNull, }; @@ -186,7 +186,7 @@ const styles = StyleSheet.create({ marker: { position: "absolute", - backgroundColor: color.white, + backgroundColor: semanticColor.core.background.base.default, borderRadius: MARKER_SIZE, // Center marker position based on its maximum size. @@ -196,7 +196,7 @@ const styles = StyleSheet.create({ marginTop: MARKER_SIZE / -2, // Add a shadow to the marker to make it stand out from the image. - boxShadow: `0 8px 8px ${color.offBlack8}`, + boxShadow: boxShadow.mid, }, // The base and unfilled marker style. @@ -210,7 +210,7 @@ const styles = StyleSheet.create({ width: MARKER_SIZE, height: MARKER_SIZE, - border: `2px solid ${color.offBlack64}`, + border: `2px solid ${semanticColor.core.border.neutral.default}`, borderRadius: MARKER_SIZE, }, @@ -219,12 +219,14 @@ const styles = StyleSheet.create({ animationName: { "0%": { transform: "scale(1)", - backgroundColor: color.blue, + backgroundColor: + semanticColor.core.background.instructive.default, }, "100%": { transform: "scale(1.3)", - backgroundColor: color.blue, + backgroundColor: + semanticColor.core.background.instructive.default, }, }, @@ -247,16 +249,16 @@ const styles = StyleSheet.create({ }, markerActive: { - outline: `2px solid ${color.blue}`, + outline: `2px solid ${semanticColor.core.border.instructive.default}`, outlineOffset: 2, }, // The learner is making an initial selection markerSelected: { - boxShadow: `0 8px 8px ${color.offBlack8}`, + boxShadow: boxShadow.mid, - border: `solid 4px ${color.white}`, - backgroundColor: color.blue, + border: `solid 4px ${semanticColor.core.border.knockout.default}`, + backgroundColor: semanticColor.core.background.instructive.default, borderRadius: MARKER_SIZE, transform: "rotate(180deg)", }, @@ -264,7 +266,7 @@ const styles = StyleSheet.create({ // The learner has made a selection markerFilled: { backgroundColor: "#ECF3FE", - border: `4px solid ${color.blue}`, + border: `4px solid ${semanticColor.core.border.instructive.default}`, }, markerGraded: { @@ -273,7 +275,7 @@ const styles = StyleSheet.create({ justifyContent: "center", alignItems: "center", - border: `2px solid ${color.white}`, + border: `2px solid ${semanticColor.core.border.knockout.default}`, }, markerCorrect: { @@ -281,6 +283,6 @@ const styles = StyleSheet.create({ }, markerIncorrect: { - background: color.offBlack64, + background: semanticColor.core.background.neutral.default, }, }); From a02153bd17b2767ecb12d2c1934826a8397001b4 Mon Sep 17 00:00:00 2001 From: Juan Andrade Date: Wed, 11 Feb 2026 16:40:06 -0500 Subject: [PATCH 2/4] [wb-color2sc-2] Update jest snapshots --- .../graded-group-set-jipt.test.ts.snap | 6 +- .../graded-group-set.test.ts.snap | 12 +- .../__snapshots__/graded-group.test.ts.snap | 6 +- .../__snapshots__/grapher.test.ts.snap | 20 +-- .../__snapshots__/interaction.test.ts.snap | 8 +- .../__snapshots__/number-line.test.ts.snap | 118 +++++++++--------- 6 files changed, 85 insertions(+), 85 deletions(-) diff --git a/packages/perseus/src/widgets/graded-group-set/__snapshots__/graded-group-set-jipt.test.ts.snap b/packages/perseus/src/widgets/graded-group-set/__snapshots__/graded-group-set-jipt.test.ts.snap index 40c52e8072b..9fa6ea31779 100644 --- a/packages/perseus/src/widgets/graded-group-set/__snapshots__/graded-group-set-jipt.test.ts.snap +++ b/packages/perseus/src/widgets/graded-group-set/__snapshots__/graded-group-set-jipt.test.ts.snap @@ -97,7 +97,7 @@ exports[`graded-group-set should render all graded groups 1`] = ` @@ -88,7 +88,7 @@ exports[`graded group set widget should snapshot 1`] = ` type="button" >
@@ -160,7 +160,7 @@ exports[`graded group set widget should snapshot 1`] = `