Skip to content

Commit

Permalink
[WB-1849] Rework action semantic color tokens (#2428)
Browse files Browse the repository at this point in the history
## Summary:

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.

By doing this change, we'll offer a more consistent and predictable styling
direction for our interactive components.

Issue: https://khanacademy.atlassian.net/browse/WB-1849

## Test plan:

Navigate to `/?path=/docs/foundations-using-color--docs` and check that the
`Action` section includes all the new tokens and the structure is readable and
makes sense.

Author: jandrade

Reviewers: beaesguerra, jandrade

Required Reviewers:

Approved By: beaesguerra

Checks: ✅ Chromatic - Get results on regular PRs (ubuntu-latest, 20.x), ✅ Test / Test (ubuntu-latest, 20.x, 2/2), ✅ Lint / Lint (ubuntu-latest, 20.x), ✅ Test / Test (ubuntu-latest, 20.x, 1/2), ✅ Check build sizes (ubuntu-latest, 20.x), ✅ Chromatic - Build on regular PRs / chromatic (ubuntu-latest, 20.x), ✅ Publish npm snapshot (ubuntu-latest, 20.x), ✅ Prime node_modules cache for primary configuration (ubuntu-latest, 20.x), ⏭️  Chromatic - Skip on Release PR (changesets), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ gerald, ⏭️  dependabot

Pull Request URL: #2428
  • Loading branch information
jandrade authored Jan 21, 2025
1 parent d8d41dc commit 0de25cd
Show file tree
Hide file tree
Showing 4 changed files with 305 additions and 55 deletions.
9 changes: 9 additions & 0 deletions .changeset/hot-panthers-kneel.md
Original file line number Diff line number Diff line change
@@ -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`.
175 changes: 148 additions & 27 deletions __docs__/components/color.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
*/
Expand Down Expand Up @@ -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 (
<View style={styles.card}>
<LabelLarge style={styles.capitalized}>
{tokenName.at(tokenName.length - 1)}
</LabelLarge>
<LabelSmall
style={{
fontStyle: "italic",
}}
>
{name}
</LabelSmall>

<Footnote>
Primitive:{" "}
<em>{getTokenName(color, value) || value}</em>
</Footnote>
</View>
);
}

if (variant === "primitive") {
return (
<>
<LabelSmall
style={{
fontWeight: font.weight.bold,
}}
>
{name}
</LabelSmall>
<Footnote style={styles.code}>{value}</Footnote>
</>
);
}

return (
<>
<LabelSmall
style={{
fontWeight: font.weight.bold,
}}
>
{name}
</LabelSmall>
<Caption>
Primitive: <em>{getTokenName(color, value) || value}</em>
</Caption>
<LabelSmall>
Value: <Footnote style={styles.code}>{value}</Footnote>
</LabelSmall>
</>
);
}

return (
<View style={styles.item}>
<View style={[styles.item, styles[variant + "Item"]]}>
<View
style={[
styles.thumbnail,
Expand All @@ -85,49 +142,110 @@ function Color({name, value, variant}: ColorProps) {
}}
/>
</View>
<View>
<TypographyComponent style={{fontWeight: font.weight.bold}}>
{name}
</TypographyComponent>
{variant === "semantic" ? (
<>
<Caption>
Primitive: <em>{getTokenName(color, value)}</em>
</Caption>
<LabelSmall>
Value:{" "}
<Footnote style={styles.code}>{value}</Footnote>
</LabelSmall>
</>
) : (
<Footnote style={styles.code}>{value}</Footnote>
)}
</View>

<View style={styles.info}>{renderInfo()}</View>
</View>
);
}

type ActionColorGroupProps = {
/**
* A dictionary of colors to display.
*/
category: Record<string, Record<string, string>>;
/**
* 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]) => (
<View style={styles.actionGroup}>
<LabelLarge style={styles.capitalized}>{state}</LabelLarge>
<Example style={colorGroup} />
<ColorGroup
colors={colorGroup}
group={group + "." + state}
variant="compact"
/>
</View>
));
}

function Example({style}: {style?: any}) {
return (
<>
<View
style={{
marginBlock: spacing.xSmall_8,
marginInline: spacing.medium_16,
padding: spacing.medium_16,
backgroundColor: style.background,
color: style.foreground,
outline: `4px solid ${style.border}`,
outlineOffset: 3,
textAlign: "center",
}}
>
Some Text
</View>
</>
);
}

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,
Expand All @@ -137,4 +255,7 @@ const styles = StyleSheet.create({
padding: spacing.xxxxSmall_2,
borderRadius: border.radius.medium_4,
},
capitalized: {
textTransform: "capitalize",
},
});
82 changes: 66 additions & 16 deletions __docs__/foundations-color.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

<Meta
title="Foundations / Using color"
Expand All @@ -28,9 +29,15 @@ export const styles = StyleSheet.create({
display: "grid",
gridTemplateColumns: "repeat(auto-fill, 160px)",
},
gridCompact: {
display: "grid",
gridTemplateColumns: "repeat(3, minmax(100px, auto))",
},

banner: {
marginBottom: spacing.xLarge_32,
},

});

# Color
Expand Down Expand Up @@ -78,18 +85,59 @@ how to use these colors depending on the context.
For buttons, links, and controls to communicate the presence and meaning of
interaction.

<View style={styles.grid}>
<ColorGroup colors={semanticColor.action.primary} group="action.primary" />
<ColorGroup
colors={semanticColor.action.destructive}
group="action.destructive"
#### Filled

Communicates strong emphasis and primary actions.

##### Progressive

<View style={styles.gridCompact}>
<ActionColorGroup
category={semanticColor.action.filled.progressive}
group="action.filled.progressive"
/>
<ColorGroup
colors={semanticColor.action.disabled}
group="action.disabled"
</View>

##### Destructive

<View style={styles.gridCompact}>
<ActionColorGroup
category={semanticColor.action.filled.destructive}
group="action.filled.destructive"
/>
</View>

#### Outlined

Communicates secondary actions and less emphasis.

#### Progressive

<View style={styles.gridCompact}>
<ActionColorGroup
category={semanticColor.action.outlined.progressive}
group="action.outlined.progressive"
/>
</View>

#### Destructive

<View style={styles.gridCompact}>
<ActionColorGroup
category={semanticColor.action.outlined.destructive}
group="action.outlined.destructive"
/>
</View>

#### Disabled

<ColorGroup
colors={semanticColor.action.disabled}
group="action.disabled"
variant="compact"
style={styles.gridCompact}
/>

### Status

For labels, icons, filters, alerts, and other elements where color can add
Expand Down Expand Up @@ -389,10 +437,12 @@ content is accessible to all users.

For more detail, you can check the following Success Criteria documents:

- <a
href="https://www.w3.org/WAI/WCAG22/Understanding/use-of-color">Use of Color</a>
- <a
href="https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum">Contrast (minimum)</a>
- <a
href="https://www.w3.org/WAI/WCAG22/Understanding/non-text-contrast">Non-text
Contrast (Level AA)</a>
- <a href="https://www.w3.org/WAI/WCAG22/Understanding/use-of-color">
Use of Color
</a>
- <a href="https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum">
Contrast (minimum)
</a>
- <a href="https://www.w3.org/WAI/WCAG22/Understanding/non-text-contrast">
Non-text Contrast (Level AA)
</a>
Loading

0 comments on commit 0de25cd

Please sign in to comment.