diff --git a/.changeset/hot-panthers-kneel.md b/.changeset/hot-panthers-kneel.md new file mode 100644 index 0000000000..19bf4c2cb4 --- /dev/null +++ b/.changeset/hot-panthers-kneel.md @@ -0,0 +1,9 @@ +--- +"@khanacademy/wonder-blocks-tokens": major +--- + +- Reworked semanticColor actions to use a new structure. Every `action` now includes `default`, `hover` and `press` states, and each state includes `border`, `background` and `foreground` tokens. + +- Renamed `primary` to `progressive`. + +- Added more categories to actions: `filled` and `outline`. diff --git a/__docs__/components/color.tsx b/__docs__/components/color.tsx index 2ea55276fb..9157754f17 100644 --- a/__docs__/components/color.tsx +++ b/__docs__/components/color.tsx @@ -17,6 +17,8 @@ import { } from "@khanacademy/wonder-blocks-tokens"; import {getTokenName} from "./tokens-util"; +type Variant = "primitive" | "semantic" | "compact"; + type Props = { /** * A dictionary of colors to display. @@ -25,7 +27,7 @@ type Props = { /** * The type of color group to display. */ - variant: "primitive" | "semantic"; + variant: Variant; /** * The group name to use as a prefix for the color names. */ @@ -59,15 +61,70 @@ export function ColorGroup({ type ColorProps = { name: string; value: string; - variant: "primitive" | "semantic"; + variant: Variant; }; function Color({name, value, variant}: ColorProps) { - const TypographyComponent = - variant === "semantic" ? LabelLarge : LabelSmall; + function renderInfo() { + if (variant === "compact") { + const tokenName = name.toString().split("."); + return ( + + + {tokenName.at(tokenName.length - 1)} + + + {name} + + + + Primitive:{" "} + {getTokenName(color, value) || value} + + + ); + } + + if (variant === "primitive") { + return ( + <> + + {name} + + {value} + + ); + } + + return ( + <> + + {name} + + + Primitive: {getTokenName(color, value) || value} + + + Value: {value} + + + ); + } return ( - + - - - {name} - - {variant === "semantic" ? ( - <> - - Primitive: {getTokenName(color, value)} - - - Value:{" "} - {value} - - - ) : ( - {value} - )} - + + {renderInfo()} + + ); +} + +type ActionColorGroupProps = { + /** + * A dictionary of colors to display. + */ + category: Record>; + /** + * The group name to use as a prefix for the color names. + */ + group: string; +}; + +export function ActionColorGroup({category, group}: ActionColorGroupProps) { + return Object.entries(category).map(([state, colorGroup]) => ( + + {state} + + + )); +} + +function Example({style}: {style?: any}) { + return ( + <> + + Some Text + + ); } +const itemWidth = 200; +const itemHeight = 120; + const styles = StyleSheet.create({ group: { flexDirection: "row", flexWrap: "wrap", - marginBlock: spacing.large_24, + marginBlock: spacing.medium_16, + }, + actionGroup: { + margin: spacing.xxxSmall_4, + padding: spacing.xxxSmall_4, + gap: spacing.xxxSmall_4, + border: `1px dashed ${semanticColor.border.subtle}`, }, item: { - marginBlockEnd: spacing.medium_16, + maxWidth: itemWidth, + overflowWrap: "break-word", + }, + compactItem: { + marginBlockEnd: spacing.xSmall_8, + maxWidth: "100%", + width: "100%", + + backgroundColor: semanticColor.surface.secondary, + border: `1px solid ${semanticColor.border.primary}`, + borderRadius: border.radius.medium_4, }, pattern: { backgroundImage: `radial-gradient(${color.blue} 0.5px, ${color.offWhite} 0.5px)`, backgroundSize: `${spacing.small_12}px ${spacing.small_12}px`, + boxShadow: `0 0 1px 0 ${semanticColor.border.primary}`, }, thumbnail: { - width: 200, - height: 160, + width: itemWidth, + height: itemHeight, }, primitiveThumbnail: { width: 160, height: spacing.xxxLarge_64, }, + compactThumbnail: { + justifyContent: "space-between", + width: "100%", + height: spacing.xxxLarge_64, + }, + info: { + paddingInlineEnd: spacing.medium_16, + }, + card: { + paddingInline: spacing.xSmall_8, + }, code: { alignSelf: "flex-start", color: semanticColor.text.primary, @@ -137,4 +255,7 @@ const styles = StyleSheet.create({ padding: spacing.xxxxSmall_2, borderRadius: border.radius.medium_4, }, + capitalized: { + textTransform: "capitalize", + }, }); diff --git a/__docs__/foundations-color.mdx b/__docs__/foundations-color.mdx index c80b5b2891..f4e8bbebbb 100644 --- a/__docs__/foundations-color.mdx +++ b/__docs__/foundations-color.mdx @@ -5,8 +5,9 @@ import Banner from "@khanacademy/wonder-blocks-banner"; import {View} from "@khanacademy/wonder-blocks-core"; import Link from "@khanacademy/wonder-blocks-link"; import {color, semanticColor, spacing} from "@khanacademy/wonder-blocks-tokens"; +import {LabelLarge} from "@khanacademy/wonder-blocks-typography"; -import {ColorGroup} from "./components/color"; +import {ColorGroup, ActionColorGroup} from "./components/color"; - - + - + +##### Destructive + + + +#### Outlined + +Communicates secondary actions and less emphasis. + +#### Progressive + + + + + +#### Destructive + + + + + +#### Disabled + + + ### Status For labels, icons, filters, alerts, and other elements where color can add @@ -389,10 +437,12 @@ content is accessible to all users. For more detail, you can check the following Success Criteria documents: -- Use of Color -- Contrast (minimum) -- Non-text - Contrast (Level AA) \ No newline at end of file +- + Use of Color + +- + Contrast (minimum) + +- + Non-text Contrast (Level AA) + diff --git a/packages/wonder-blocks-tokens/src/tokens/semantic-color.ts b/packages/wonder-blocks-tokens/src/tokens/semantic-color.ts index 0f92484977..367dc48302 100644 --- a/packages/wonder-blocks-tokens/src/tokens/semantic-color.ts +++ b/packages/wonder-blocks-tokens/src/tokens/semantic-color.ts @@ -1,18 +1,93 @@ import {color} from "./color"; +const border = { + primary: color.fadedOffBlack16, + subtle: color.fadedOffBlack8, + strong: color.fadedOffBlack50, + inverse: color.white, +}; + export const semanticColor = { /** * For buttons, links, and controls to communicate the presence and meaning * of interaction. */ action: { - primary: { - default: color.blue, - active: color.activeBlue, + // Filled buttons are meant for primary actions. + filled: { + progressive: { + default: { + border: "transparent", + background: color.blue, + foreground: color.white, + }, + hover: { + border: color.blue, + background: color.blue, + foreground: color.white, + }, + press: { + border: color.activeBlue, + background: color.activeBlue, + foreground: color.white, + }, + }, + destructive: { + default: { + border: "transparent", + background: color.red, + foreground: color.white, + }, + hover: { + border: color.red, + background: color.red, + foreground: color.white, + }, + press: { + border: color.activeRed, + background: color.activeRed, + foreground: color.white, + }, + }, }, - destructive: { - default: color.red, - active: color.activeRed, + + // Outlined is meant for use on secondary controls, or controls over + // white/transparent backgrounds. + outlined: { + progressive: { + default: { + border: border.strong, + background: color.white, + foreground: color.blue, + }, + hover: { + border: color.blue, + background: color.white, + foreground: color.blue, + }, + press: { + border: color.activeBlue, + background: color.fadedBlue, + foreground: color.activeBlue, + }, + }, + destructive: { + default: { + border: border.strong, + background: color.white, + foreground: color.red, + }, + hover: { + border: color.red, + background: color.white, + foreground: color.red, + }, + press: { + border: color.activeRed, + background: color.fadedRed, + foreground: color.activeRed, + }, + }, }, disabled: { default: color.fadedOffBlack32, @@ -71,12 +146,7 @@ export const semanticColor = { * elements would use -Primary, rows and layout elements use -Subtle and * -Strong for when 3:1 contrast is a priority (ex. form elements) */ - border: { - primary: color.fadedOffBlack16, - subtle: color.fadedOffBlack8, - strong: color.fadedOffBlack50, - inverse: color.white, - }, + border: border, /** * Default icon colors that change in context (like actions). */