@codecademy/styleguide 79.2.0-alpha.f9ca97.0 → 79.2.1-alpha.193cb6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -175,20 +175,21 @@ export const globalTypes = {
175
175
  toolbar: {
176
176
  icon: 'transfer',
177
177
  items: [
178
- { value: 'true', title: 'Logical' },
179
178
  { value: 'false', title: 'Physical' },
179
+ { value: 'true', title: 'Logical' },
180
180
  ],
181
181
  showName: true,
182
182
  },
183
183
  },
184
184
  direction: {
185
185
  name: 'Direction',
186
- description: 'Text direction for the page',
186
+ description: 'Text direction (LTR or RTL)',
187
187
  defaultValue: 'ltr',
188
188
  toolbar: {
189
+ icon: 'arrowright',
189
190
  items: [
190
- { value: 'ltr', icon: 'arrowright', title: 'Left-To-Right' },
191
- { value: 'rtl', icon: 'arrowleft', title: 'Right-To-Left' },
191
+ { value: 'ltr', title: 'Left-to-Right (LTR)', icon: 'arrowright' },
192
+ { value: 'rtl', title: 'Right-to-Left (RTL)', icon: 'arrowleft' },
192
193
  ],
193
194
  showName: true,
194
195
  },
@@ -12,6 +12,8 @@ import {
12
12
  } from '@codecademy/gamut-styles/src';
13
13
  import { Theme } from '@emotion/react';
14
14
 
15
+ const STORYBOOK_CSP_NONCE = 'storybook-csp-nonce';
16
+
15
17
  /**
16
18
  * Story functions must be called as a regular function to avoid full-remounts
17
19
  * See: https://github.com/storybookjs/storybook/issues/12255
@@ -42,7 +44,7 @@ type GlobalsContext = {
42
44
  export const withEmotion = (Story: any, context: GlobalsContext) => {
43
45
  const colorMode = context.globals.colorMode ?? 'light';
44
46
  const selectedTheme = context.globals.theme;
45
- const useLogicalProperties = context.globals.logicalProps !== 'false';
47
+ const useLogicalProperties = context.globals.logicalProps === 'true';
46
48
  const direction = context.globals.direction ?? 'ltr';
47
49
  const background = corePalette[themeBackground[colorMode]];
48
50
  const storyRef = useRef<HTMLDivElement>(null);
@@ -68,9 +70,8 @@ export const withEmotion = (Story: any, context: GlobalsContext) => {
68
70
  alwaysSetVariables
69
71
  bg={themeBackground[colorMode]}
70
72
  ref={storyRef}
71
- dir={direction}
72
73
  >
73
- {Story()}
74
+ <div dir={direction}>{Story()}</div>
74
75
  </Background>
75
76
  </GamutProvider>
76
77
  );
@@ -86,9 +87,8 @@ export const withEmotion = (Story: any, context: GlobalsContext) => {
86
87
  alwaysSetVariables
87
88
  bg={themeBackground[colorMode]}
88
89
  ref={storyRef}
89
- dir={direction}
90
90
  >
91
- {Story()}
91
+ <div dir={direction}>{Story()}</div>
92
92
  </Background>
93
93
  </GamutProvider>
94
94
  );
package/CHANGELOG.md CHANGED
@@ -3,12 +3,29 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
- ## [79.2.0-alpha.f9ca97.0](https://github.com/Codecademy/gamut/compare/@codecademy/styleguide@79.1.1...@codecademy/styleguide@79.2.0-alpha.f9ca97.0) (2026-02-13)
6
+ ### [79.2.1-alpha.193cb6.0](https://github.com/Codecademy/gamut/compare/@codecademy/styleguide@79.2.0...@codecademy/styleguide@79.2.1-alpha.193cb6.0) (2026-03-12)
7
+
8
+ **Note:** Version bump only for package @codecademy/styleguide
9
+
10
+ ## [79.2.0](https://github.com/Codecademy/gamut/compare/@codecademy/styleguide@79.1.4...@codecademy/styleguide@79.2.0) (2026-03-12)
7
11
 
8
12
  ### Features
9
13
 
10
- - Updates to ThemeProvider, tokens, and transform to allow Logical vs Physical prop resolution ([#3234](https://github.com/Codecademy/gamut/issues/3234)) ([7efbb9a](https://github.com/Codecademy/gamut/commit/7efbb9aabcd07dc6429432d007eaa5603ac1d70c))
11
- - Updates to ThemeProvider, tokens, and transform to allow Logical vs Physical prop resolution ([#3234](https://github.com/Codecademy/gamut/issues/3234)) ([567a6ae](https://github.com/Codecademy/gamut/commit/567a6aeffb3e8628db4b5c9a37dab965b716d969))
14
+ - Logical properties ([4c340c3](https://github.com/Codecademy/gamut/commit/4c340c3949bad30fce4f9ffabb2018763df5c4ba))
15
+
16
+ ### [79.1.4](https://github.com/Codecademy/gamut/compare/@codecademy/styleguide@79.1.3...@codecademy/styleguide@79.1.4) (2026-03-09)
17
+
18
+ **Note:** Version bump only for package @codecademy/styleguide
19
+
20
+ ### [79.1.3](https://github.com/Codecademy/gamut/compare/@codecademy/styleguide@79.1.2...@codecademy/styleguide@79.1.3) (2026-03-02)
21
+
22
+ ### Bug Fixes
23
+
24
+ - **GamutProvider:** CSP improvements ([1026353](https://github.com/Codecademy/gamut/commit/10263537c190aff0e5686872da2be2a30b1d9201)), closes [/github.com/adobe/react-spectrum/issues/8273#issuecomment-3897820426](https://github.com/Codecademy//github.com/adobe/react-spectrum/issues/8273/issues/issuecomment-3897820426)
25
+
26
+ ### [79.1.2](https://github.com/Codecademy/gamut/compare/@codecademy/styleguide@79.1.1...@codecademy/styleguide@79.1.2) (2026-02-12)
27
+
28
+ **Note:** Version bump only for package @codecademy/styleguide
12
29
 
13
30
  ### [79.1.1](https://github.com/Codecademy/gamut/compare/@codecademy/styleguide@79.1.0...@codecademy/styleguide@79.1.1) (2026-02-11)
14
31
 
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@codecademy/styleguide",
3
3
  "description": "Styleguide & Component library for codecademy.com",
4
- "version": "79.2.0-alpha.f9ca97.0",
4
+ "version": "79.2.1-alpha.193cb6.0",
5
5
  "author": "Codecademy Engineering",
6
6
  "license": "MIT",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
10
10
  "repository": "git@github.com:Codecademy/gamut.git",
11
- "gitHead": "d73668577cae898037fa05bed7b91cc4c7312a9d"
11
+ "gitHead": "36c25d9a8a2de3b244792dfd2d3ccb1dd01eca09"
12
12
  }
@@ -24,6 +24,7 @@ export const Default: Story = {
24
24
  export const StaticLight: Story = {
25
25
  render: () => (
26
26
  <Background bg="white">
27
+ <p>Test test</p>
27
28
  <FlexBox border={1} gap={16} p={16} row>
28
29
  <Box flexGrow={1}>
29
30
  <Card variant="white">White variant</Card>
@@ -1,8 +1,9 @@
1
- import { Meta } from '@storybook/blocks';
1
+ import { Canvas, Meta } from '@storybook/blocks';
2
2
 
3
3
  import { AboutHeader, TokenTable } from '~styleguide/blocks';
4
4
 
5
5
  import { defaultColumns, getPropRows } from '../../shared/elements';
6
+ import * as LayoutStories from './Layout.stories';
6
7
 
7
8
  export const parameters = {
8
9
  title: 'Layout',
@@ -11,12 +12,22 @@ export const parameters = {
11
12
  status: 'updating',
12
13
  };
13
14
 
14
- <Meta title="Foundations/System/Props/Layout" />
15
+ <Meta title="Foundations/System/Props/Layout" of={LayoutStories} />
15
16
 
16
17
  <AboutHeader {...parameters} />
17
18
 
18
19
  Layout props control the visual structure and dimensions of elements. These properties determine how components take up space, their display behavior, and how they align within their containers. Use these props to set widths, heights, overflow behavior, and container types for responsive layouts.
19
20
 
21
+ ## Examples
22
+
23
+ ### Overflow X
24
+
25
+ <Canvas of={LayoutStories.OverflowXExample} />
26
+
27
+ ### Overflow Y
28
+
29
+ <Canvas of={LayoutStories.OverflowYExample} />
30
+
20
31
  ```tsx
21
32
  import styled from '@emotion/styled';
22
33
  import { system } from '@codecademy/gamut-styles';
@@ -31,4 +42,14 @@ const LayoutExample = styled.div(system.layout);
31
42
  />;
32
43
  ```
33
44
 
45
+ ## Examples
46
+
47
+ ### Width
48
+
49
+ <Canvas of={LayoutStories.WidthExample} />
50
+
51
+ ### Direction
52
+
53
+ <Canvas of={LayoutStories.DirectionExample} />
54
+
34
55
  <TokenTable rows={getPropRows('layout')} columns={defaultColumns} />
@@ -0,0 +1,85 @@
1
+ import { Box, Markdown } from '@codecademy/gamut';
2
+ import type { Meta, StoryObj } from '@storybook/react';
3
+
4
+ const meta: Meta<typeof Box> = {
5
+ title: 'Foundations/System/Props/Layout',
6
+ component: Box,
7
+ };
8
+
9
+ export default meta;
10
+ type Story = StoryObj<typeof Box>;
11
+
12
+ export const OverflowXExample: Story = {
13
+ render: () => (
14
+ <Box
15
+ bg="background-selected"
16
+ overflowX="scroll"
17
+ p={16}
18
+ whiteSpace="nowrap"
19
+ width="200px"
20
+ >
21
+ <Box
22
+ bg="primary"
23
+ color="background-contrast"
24
+ display="inline-block"
25
+ p={8}
26
+ >
27
+ This content is wider than its container and has{' '}
28
+ <Markdown text="`overflowX='scroll'`." /> Inspect the example to see
29
+ what CSS properties are rendered.
30
+ </Box>
31
+ </Box>
32
+ ),
33
+ };
34
+
35
+ export const OverflowYExample: Story = {
36
+ render: () => (
37
+ <Box bg="background-selected" height="100px" overflowY="scroll" p={16}>
38
+ <Box bg="primary" color="background-contrast" p={8}>
39
+ This content is taller than its container and has{' '}
40
+ <Markdown text="`overflowY='scroll'`." /> Inspect the example to see
41
+ what CSS properties are rendered.
42
+ <Box mt={16}>Line 2</Box>
43
+ <Box mt={16}>Line 3</Box>
44
+ <Box mt={16}>Line 4</Box>
45
+ <Box mt={16}>Line 5</Box>
46
+ </Box>
47
+ </Box>
48
+ ),
49
+ };
50
+
51
+ export const WidthExample: Story = {
52
+ render: () => (
53
+ <Box bg="background-selected" p={16}>
54
+ <Box
55
+ bg="primary"
56
+ color="background-contrast"
57
+ height="300px"
58
+ p={16}
59
+ width="50%"
60
+ >
61
+ This box has <Markdown text="`width='50%' and height='300px'`." />{' '}
62
+ Inspect the box to see the rendered CSS property.
63
+ </Box>
64
+ </Box>
65
+ ),
66
+ };
67
+
68
+ export const DirectionExample: Story = {
69
+ render: () => (
70
+ <Box display="flex" flexDirection="row" gap={16}>
71
+ <Box bg="background-selected" p={16} width="50%">
72
+ <Box bg="primary" color="background-contrast" direction="ltr" p={16}>
73
+ <Markdown text="`direction='ltr'`." /> Left-to-right text direction
74
+ (default for English).
75
+ </Box>
76
+ </Box>
77
+ <Box bg="background-selected" p={16} width="50%">
78
+ <Box bg="primary" color="background-contrast" direction="rtl" p={16}>
79
+ <Markdown text="`direction='rtl'`." /> Right-to-left text direction
80
+ (used for Arabic, Hebrew, etc.).
81
+ </Box>
82
+ </Box>
83
+ </Box>
84
+ ),
85
+ };
@@ -11,7 +11,7 @@ type Story = StoryObj<typeof Box>;
11
11
 
12
12
  export const PositionExample: Story = {
13
13
  render: () => (
14
- <Box bg="background-selected" height="150px" position="relative">
14
+ <Box bg="background-selected" height="250px" position="relative">
15
15
  <Box
16
16
  bg="primary"
17
17
  color="background-contrast"
@@ -68,7 +68,7 @@ GamutProvider handles a few critical tasks that need to happen in order for comp
68
68
  - **Next** `_app.tsx`
69
69
  - **Gatsby** `gatsby-ssr.js` `gatsby-browser.js` and use `wrapRootElement` in each.
70
70
 
71
- 4. Add required types for your theme, which will determine what props Gamut components allow.
71
+ 5. Add required types for your theme, which will determine what props Gamut components allow.
72
72
 
73
73
  ```tsx
74
74
  // theme.d.ts
@@ -84,7 +84,7 @@ declare module '@emotion/react' {
84
84
 
85
85
  For more information this declaration please checkout the [official emotion documentation](https://emotion.sh/docs/typescript#define-a-theme)!
86
86
 
87
- 5. Start Building!
87
+ 6. Start Building!
88
88
 
89
89
  ```tsx
90
90
  import { Background } from '@codecademy/gamut-styles';
@@ -96,3 +96,15 @@ export const App = () => (
96
96
  </Background>
97
97
  );
98
98
  ```
99
+
100
+ ### Content Security Policy (CSP)
101
+
102
+ If your app uses a strict Content-Security-Policy (e.g. `style-src` without `'unsafe-inline'`), pass a nonce to `GamutProvider` so Emotion and other Gamut-managed style tags are allowed:
103
+
104
+ ```tsx
105
+ <GamutProvider nonce={yourCspNonce}>
106
+ <App />
107
+ </GamutProvider>
108
+ ```
109
+
110
+ Your nonce should be the same value you use in your CSP header (e.g. `style-src 'self' 'nonce-{value}'`).
@@ -80,6 +80,7 @@ export const Above: Story = {
80
80
  export const Below: Story = {
81
81
  render: (args) => <PopoverExample {...args} beak="center" position="below" />,
82
82
  };
83
+
83
84
  export const CenterLeft: Story = {
84
85
  render: (args) => (
85
86
  <PopoverExample
@@ -113,6 +114,7 @@ export const PopoverCheckerDense: Story = {
113
114
  />
114
115
  ),
115
116
  };
117
+
116
118
  export const PopoverCheckerLoose: Story = {
117
119
  render: (args) => (
118
120
  <PopoverExample
@@ -122,6 +124,7 @@ export const PopoverCheckerLoose: Story = {
122
124
  />
123
125
  ),
124
126
  };
127
+
125
128
  export const PopoverCheckerRegular: Story = {
126
129
  render: (args) => (
127
130
  <PopoverExample
@@ -131,6 +134,7 @@ export const PopoverCheckerRegular: Story = {
131
134
  />
132
135
  ),
133
136
  };
137
+
134
138
  export const PopoverDiagonalADense: Story = {
135
139
  render: (args) => (
136
140
  <PopoverExample
@@ -140,6 +144,7 @@ export const PopoverDiagonalADense: Story = {
140
144
  />
141
145
  ),
142
146
  };
147
+
143
148
  export const PopoverDiagonalALoose: Story = {
144
149
  render: (args) => (
145
150
  <PopoverExample
@@ -149,6 +154,7 @@ export const PopoverDiagonalALoose: Story = {
149
154
  />
150
155
  ),
151
156
  };
157
+
152
158
  export const PopoverDiagonalARegular: Story = {
153
159
  render: (args) => (
154
160
  <PopoverExample
@@ -1,11 +1,11 @@
1
1
  import { Meta } from '@storybook/blocks';
2
2
 
3
- import { AboutHeader, LinkTo } from '~styleguide/blocks';
3
+ import { AboutHeader, ImageWrapper, LinkTo } from '~styleguide/blocks';
4
4
 
5
5
  export const parameters = {
6
6
  title: 'Confirmation dialogs',
7
7
  subtitle:
8
- 'Simplify the language, prioritize the message, and make sure the implication of what learners are saying "Yes" (or "No") to is crystal clear.',
8
+ 'Use the same verb from the triggering button, heading, to action confirmation button; clearly communicate the consequences; and keep the copy decision-focused.',
9
9
  status: 'static',
10
10
  design: {
11
11
  type: 'figma',
@@ -17,39 +17,57 @@ export const parameters = {
17
17
 
18
18
  <AboutHeader {...parameters} />
19
19
 
20
- Confirmation dialog boxes are used to verify that a learner wants to take a specific action. They are generally used for actions that are irreversible, may result in critical consequences or loss of data, have other severe consequences, or happen infrequently.
20
+ Confirmation dialogs use the <LinkTo id="Molecules/Modals/Dialog">Dialog component in Gamut</LinkTo> to create intentional friction to verify that a learner wants to take a high-impact action, such as:
21
21
 
22
- They use the <LinkTo id="Molecules/Modals/Dialog">Dialog component in Gamut</LinkTo> and, for actions with serious or irreversible consequences, the `Danger` variant should be used.
22
+ - Irreversible actions (e.g., submitting payment)
23
+ - Loss of data, time, or work (e.g., deleting a course)
24
+ - Unexpected consequences (e.g., losing learning history on an existing prototype when when generating a new prototype)
25
+
26
+ Adding friction for these purposes helps improve trust and avoid unintentional actions by making sure learners clearly understand the consequences before continuing. It also lets us offer alternatives or undo options when needed.
23
27
 
24
28
  ## Best practices
25
29
 
26
- ### Headline
30
+ ### Heading
31
+
32
+ - **Ask or inform about one main action**, mirroring the button that triggered the confirmation dialog.
33
+ - **Frame your headline as a binary question**, when possible, with 2 unambiguous answers.
34
+ - **Avoid generic “Are you sure?” headings and body text.** This phrasing takes up space, increases cognitive load, and may undermine users' confidence or be interpreted as patronizing.
35
+
36
+ ### Body (optional)
37
+
38
+ - **Add essential information about the contextual consequences.** State what will happen, what will be lost/changed, and any critical conditions.
39
+ - **Avoid redundancy.** If the heading is already self-explanatory, the body is not needed.
40
+ - **Keep to 1–2 lines, unless more is required to get all the information across.**
41
+
42
+ ### Buttons (CTA1 and CTA2)
27
43
 
28
- - **Ask or inform about one main action**, clearly and simply.
29
- - **Frame your headline as a binary question**, when possible, with 2 unambiguous answers (i.e. Yes/No, Stay/Leave).
30
- - **Be specific.** Instead of "Are you sure?" focus on what you want to ensure they're sure about (i.e. "Reset your progress?" or "Delete the file?").
44
+ - **Avoid using “Yes” or “No,”** as they can be misinterpreted in global English and internationalization contexts.
45
+ - **CTA1 matches the verb from the heading** to confirm the action.
46
+ - **CTA2 clarifies the alternative or undo** path.
47
+ - Whenever possible, be specific about the alternative. However, when space is limited, 'Cancel' can be used.
31
48
 
32
- ### Explanation
49
+ ### Examples — putting it all together
33
50
 
34
- - **Share only relevant information** that may help the learner make their decision.
35
- - **Avoid redundancy.** If you've already set the stage in your headline, there's no need to re-ask the same question in your explanation. If the explanation doesn't add anything new, leave it out (i.e. "Permanently delete this item? Yes/No").
36
- - **Avoid filler.** Questions like "Are you sure you want to \_\_\_?" take up space, increase cognitive load, and may undermine users' confidence or be interpreted as patronizing.
37
- - **Keep to 1-2 lines**, unless more is required to get all the information across.
51
+ <ImageWrapper
52
+ src="./ux writing/delete_this_course.png"
53
+ alt="Delete this course confirmation dialog"
54
+ />
38
55
 
39
- ### Button copy
56
+ <ImageWrapper
57
+ src="./ux writing/delete_study_plan.png"
58
+ alt="Delete study plan confirmation dialog"
59
+ />
40
60
 
41
- - **Options should be clear and distinct.** Each option should be distinctly different and there should be no opportunity for learners to mix them up (i.e. "Delete" and "Cancel" are ambiguous choices whereas "Yes, remove" and "Cancel" clear up the confusion.
42
- - **Add context to reaffirm the action.** Instead of "Yes," use "Yes, reset progress."
43
- - **Match the verb in your headline.** If you use "Save" in your headline, use "Save" in your button copy, rather than keep. Consistency helps keep the message clear. All of this should also match whatever the learner clicked on that triggered the confirmation dialog.
61
+ <ImageWrapper
62
+ src="./ux writing/clear_chat.png"
63
+ alt="Clear chat confirmation dialog"
64
+ />
44
65
 
45
66
  ## Checklist
46
67
 
47
- - Is the language consistent from the wording on the button that opened the confirmation box, to the headline,
48
- - Does the headline make the action clear?
49
- - Is the headline framed as a question, if possible?
50
- - Does the explanation provide relevant details and consequences of the action?
51
- - Is the explanation 1-2 lines long?
52
- - Are the words on the buttons clear and distinct?
53
- - Do the buttons include context to reaffirm the action?
54
- - Is your copy at a reading level of grade 7 or below? Test with [Hemingway App](https://hemingwayapp.com).
55
- - Have you asked someone unrelated to the project to read the message and did they understand it?
68
+ - Is the action irreversible, destructive, or has unexpected consequences? If not, consider using a different pattern.
69
+ - Is the same verb used from the action triggering the confirmation dialog, to the heading, to CTA1?
70
+ - Did you avoid filler language such as “Are you sure you want to...?”
71
+ - Does the body front-load the critical consequence in 1–3 lines?
72
+ - Are the buttons mutually exclusive (and avoid using “Yes/No”)?
73
+ - Is there a safer alternative or undo to mention? (If available, offer the option as CTA2.)