@codecademy/styleguide 79.0.1-alpha.4fa3a1.0 → 79.0.1-alpha.5950e5.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.
package/CHANGELOG.md CHANGED
@@ -3,7 +3,7 @@
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.0.1-alpha.4fa3a1.0](https://github.com/Codecademy/gamut/compare/@codecademy/styleguide@79.0.0...@codecademy/styleguide@79.0.1-alpha.4fa3a1.0) (2026-02-02)
6
+ ### [79.0.1-alpha.5950e5.0](https://github.com/Codecademy/gamut/compare/@codecademy/styleguide@79.0.0...@codecademy/styleguide@79.0.1-alpha.5950e5.0) (2026-02-03)
7
7
 
8
8
  **Note:** Version bump only for package @codecademy/styleguide
9
9
 
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.0.1-alpha.4fa3a1.0",
4
+ "version": "79.0.1-alpha.5950e5.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": "bcc5f0d8a1a6bb0fa31053767e304e387b8c1683"
11
+ "gitHead": "296228a5f09e2d520a512458cf7d001d45d549eb"
12
12
  }
@@ -11,7 +11,7 @@ export const parameters = {
11
11
  type: 'figma',
12
12
  url: 'https://www.figma.com/file/ReGfRNillGABAj5SlITalN/branch/ayKNSg6QvZUjsgw0FFysW4/%F0%9F%93%90-Gamut?type=design&node-id=41538-55277&mode=design&t=fGkWf5GSl5cj5fQo-0',
13
13
  },
14
- status: 'updating',
14
+ status: 'current',
15
15
  source: {
16
16
  repo: 'gamut',
17
17
  githubLink:
@@ -16,7 +16,7 @@ export const parameters = {
16
16
  type: 'figma',
17
17
  url: 'https://www.figma.com/design/ReGfRNillGABAj5SlITalN/%F0%9F%93%90-Gamut?node-id=55123-4176',
18
18
  },
19
- status: 'updating',
19
+ status: 'current',
20
20
  source: {
21
21
  repo: 'gamut',
22
22
  githubLink:
@@ -119,7 +119,41 @@ Rows can be made interactive with `onClick` handlers or `href` links.
119
119
 
120
120
  <Canvas of={BarChartStories.Interactive} />
121
121
 
122
- ### Sorting
122
+ ### With Links
123
+
124
+ When a bar has an `href` link, an `aria-label` is automatically generated from the bar's data and applied to the anchor. The label summarizes the bar's values (e.g., "100 XP in Python" for a simple bar, or "200 XP gained - now at 1500 XP in Python" for a stacked bar), so screen reader users get a clear, data-driven description without manual `aria-label` props.
125
+
126
+ <Canvas of={BarChartStories.WithLinks} />
127
+
128
+ ## Title and description
129
+
130
+ The BarChart component uses semantic HTML to provide context and accessibility. The chart is wrapped in a [`<figure>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure) element, and the description is displayed in a [`<figcaption>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figcaption) element. This semantic structure helps screen readers and other assistive technologies understand the relationship between the chart and its description. **Visual title + description is the greatly preferred pattern** for accessibility and user experience.
131
+
132
+ **Preferred pattern: Visual title + description**
133
+
134
+ Provide both `title` and `description` props to make the chart's purpose and key takeaways clear to all users:
135
+
136
+ - `title`: A heading that identifies the chart. Can be a string or an object with `{ as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6', title: string, variant?: Text['variant'] }` to specify the heading level and optionally override the default styling with a <LinkTo id="Typography/Text">Text variant</LinkTo>.
137
+ - `description`: A summary of the information or the overall takeaway displayed in the `<figcaption>` element.
138
+
139
+ <Canvas of={BarChartStories.WithVisualTitleAndDescription} />
140
+
141
+ **Alternative patterns:**
142
+ `hideTitle` and `hideDescription` will keep the screenreader text but hide the visual descriptions.
143
+
144
+ <Canvas of={BarChartStories.WithHiddenTitleAndDescription} />
145
+
146
+ **External title with visual description:** Use `aria-labelledby` for the title when the title exists elsewhere but you want to display the description.
147
+
148
+ <Canvas of={BarChartStories.WithExternalTitle} />
149
+
150
+ **Best practices:**
151
+
152
+ - Always provide a `description` prop - it's required and should summarize the chart's key information
153
+ - Use semantic heading levels for titles (h1-h6) to maintain proper document structure
154
+ - When using `aria-labelledby`, ensure the referenced element exists and is properly labeled
155
+
156
+ ## Sorting
123
157
 
124
158
  BarChart includes an optional sorting dropdown in the title area. The dropdown only renders when the `sortFns` prop is provided. You can control which sort options appear by including string literals or custom sort functions in the array.
125
159
 
@@ -193,32 +227,6 @@ Custom sort functions can access additional properties on `BarProps` for domain-
193
227
 
194
228
  <Canvas of={BarChartStories.WithCustomSorting} />
195
229
 
196
- When a bar has an `onClick` handler or `href` link, an `aria-label` is automatically generated from the bar's data and applied to the button or link element. The label summarizes the bar's values (e.g., "100 XP in Python" for a simple bar, or "200 XP gained - now at 1500 XP in Python" for a stacked bar).
197
-
198
- This ensures that screen reader users receive a clear, data-driven description when interacting with bars, without requiring manual `aria-label` props.
199
-
200
- <Canvas of={BarChartStories.WithLinks} />
201
-
202
- ## Interactive states
203
-
204
- BarChart supports both interactive and static bar rows, each with distinct visual states.
205
-
206
- ### Static bars
207
-
208
- When bars have neither `onClick` handlers nor `href` links, they are rendered as static, non-interactive elements.
209
-
210
- <Canvas of={BarChartStories.Default} />
211
-
212
- ### Interactive bars
213
-
214
- When bars have `onClick` handlers or `href` links, they become interactive. Interactive bars are rendered as buttons or anchor elements, providing full keyboard accessibility and proper semantic HTML.
215
-
216
- <Canvas of={BarChartStories.Interactive} />
217
-
218
- ## Color modes
219
-
220
- BarChart automatically adapts to both light and dark color modes, ensuring proper contrast and readability in all themes. The component uses semantic color tokens that adjust based on the current color mode. The `styleConfig` prop should be provided by semantic color tokens to ensure the `BarChart` is accessible in all color modes.
221
-
222
230
  ## Custom styling
223
231
 
224
232
  BarChart supports custom color styling through the `styleConfig` prop, allowing you to customize the appearance of chart elements to match your design needs.
@@ -228,8 +236,8 @@ BarChart supports custom color styling through the `styleConfig` prop, allowing
228
236
  The `styleConfig` prop accepts an object with the following optional properties:
229
237
 
230
238
  - **`textColor`**: Color for text labels (y-axis labels). Defaults to `'text'`
231
- - **`foregroundBarColor`**: Color for the foreground/progress bar (used in stacked charts for `seriesOneValue`). Defaults to `'primary'`
232
- - **`backgroundBarColor`**: Color for the background/total bar (used for `seriesTwoValue` in stacked charts, or `seriesOneValue` in simple charts). Defaults to `'background-primary'`
239
+ - **`seriesOneBarColor`**: Color for the series one bar (seriesOneValue: overlay in stacked charts, only bar in simple charts). Defaults to `'text'`
240
+ - **`seriesTwoBarColor`**: Color for the series two bar (seriesTwoValue: full bar in stacked charts; unused in simple charts). Defaults to `'primary'`
233
241
  - **`seriesOneLabel`**: Color for the series one value label (first right label when stacked, or displayValue when not stacked). Defaults to `'text-secondary'`
234
242
  - **`seriesTwoLabel`**: Color for the series two value label (displayValue when stacked). Defaults to `'primary'`
235
243
 
@@ -242,8 +250,8 @@ If `styleConfig` is not provided, BarChart uses these default colors:
242
250
  ```typescript
243
251
  {
244
252
  textColor: 'text',
245
- foregroundBarColor: 'primary',
246
- backgroundBarColor: 'background-primary',
253
+ seriesOneBarColor: 'text',
254
+ seriesTwoBarColor: 'primary',
247
255
  seriesOneLabel: 'text-secondary',
248
256
  seriesTwoLabel: 'primary',
249
257
  }
@@ -281,6 +289,10 @@ The number of tick marks is automatically calculated as: `Math.ceil((maxRange -
281
289
 
282
290
  <Canvas of={BarChartStories.CustomScale} />
283
291
 
292
+ ## Color modes
293
+
294
+ BarChart automatically adapts to both light and dark color modes, ensuring proper contrast and readability in all themes. The component uses semantic color tokens that adjust based on the current color mode. The `styleConfig` prop should be provided by semantic color tokens to ensure the `BarChart` is accessible in all color modes.
295
+
284
296
  ## Accessibility considerations
285
297
 
286
298
  ### Chart and row labeling
@@ -302,154 +314,101 @@ The number of tick marks is automatically calculated as: `Math.ceil((maxRange -
302
314
  - Use consistent unit labels (e.g., "XP", "hours", "points")
303
315
  - Consider locale-aware number formatting for international audiences
304
316
 
305
- ### Title and description
306
-
307
- The BarChart component uses semantic HTML to provide context and accessibility. The chart is wrapped in a [`<figure>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure) element, and the description is displayed in a [`<figcaption>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figcaption) element. This semantic structure helps screen readers and other assistive technologies understand the relationship between the chart and its description. **Visual title + description is the greatly preferred pattern** for accessibility and user experience.
308
-
309
- **Preferred pattern: Visual title + description**
310
-
311
- Provide both `title` and `description` props to make the chart's purpose and key takeaways clear to all users:
312
-
313
- - `title`: A heading that identifies the chart. Can be a string or an object with `{ as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6', title: string, variant?: Text['variant'] }` to specify the heading level and optionally override the default styling with a <LinkTo id="Typography/Text">Text variant</LinkTo>.
314
- - `description`: A summary of the information or the overall takeaway displayed in the `<figcaption>` element.
315
-
316
- <Canvas of={BarChartStories.WithVisualTitleAndDescription} />
317
-
318
- **Alternative patterns:**
319
- `hideTitle` and `hideDescription` will keep the screenreader text but hide the visual descriptions.
320
-
321
- <Canvas of={BarChartStories.WithHiddenTitleAndDescription} />
317
+ ## Interactive states
322
318
 
323
- - **External title with visual description**: Use `aria-labelledby` for the title but show the description visually when the title exists elsewhere but you want to display the description.
319
+ BarChart supports both interactive and static bar rows, each with distinct visual states.
324
320
 
325
- <Canvas of={BarChartStories.WithExternalTitle} />
321
+ ### Static bars
326
322
 
327
- **Best practices:**
323
+ When bars have neither `onClick` handlers nor `href` links, they are rendered as static, non-interactive elements.
328
324
 
329
- - Always provide a `description` prop - it's required and should summarize the chart's key information
330
- - Use semantic heading levels for titles (h1-h6) to maintain proper document structure
331
- - When using `aria-labelledby`, ensure the referenced element exists and is properly labeled
325
+ <Canvas of={BarChartStories.Default} />
332
326
 
333
- ## Playground
327
+ ### Interactive bars
334
328
 
335
- <Canvas sourceState="shown" of={BarChartStories.Default} />
329
+ When bars have `onClick` handlers or `href` links, they become interactive. Interactive bars are rendered as buttons or anchor elements, providing full keyboard accessibility and proper semantic HTML.
336
330
 
337
- <Controls />
331
+ <Canvas of={BarChartStories.Interactive} />
338
332
 
339
333
  ## Internationalization
340
334
 
341
335
  <Callout text="We are currently updating global internationalization in Gamut, so the following features are subject to change." />
342
336
 
343
- BarChart supports internationalization through the `translations` prop, which allows you to customize all user-facing strings and configure locale-aware number formatting.
337
+ BarChart supports internationalization through the `translations` prop. You can customize user-facing strings (including sort dropdown and accessibility summaries) and configure locale-aware number formatting. The prop accepts `PartialBarChartTranslations`: override only the keys you need; nested `accessibility` and `sortOptions` are merged with defaults at runtime.
344
338
 
345
- **Translation Structure:**
339
+ ### Translation structure
346
340
 
347
- ```typescript
348
- type BarChartTranslations = {
349
- sortLabel: string;
350
- sortOptions: {
351
- none: string;
352
- labelAsc: string;
353
- labelDesc: string;
354
- valueAsc: string;
355
- valueDesc: string;
356
- };
357
- accessibility: {
358
- gainedNowAt: string; // Template: "{value} {unit} gained - now at {value} {unit} in {label}"
359
- inLabel: string; // Template: "{value} {unit} in {label}"
360
- inOnly: string; // Template: "{value} {unit} in "
361
- };
362
- locale: string; // For Intl.NumberFormat, e.g., 'en', 'es', 'fr', 'de-DE'
363
- };
364
- ```
341
+ - **sortLabel**, **sortOptions**, and **locale** — Always strings. Control the sort dropdown label, option labels, and number-formatting locale.
342
+ - **Accessibility keys** (`gainedNowAt`, `inLabel`, `inOnly`) — Used in auto-generated `aria-label` and screen reader text for bars. Each key can be:
343
+ - **String** — A fragment interpolated into the built-in template (e.g. `"in"`, `"gained - now at"`). Use when the default word order works for your language.
344
+ - **Function** — Receives scoped context (values, label, unit, locale) and returns the **entire** summary string. Use for pluralization, different word order, or locale-specific phrasing.
365
345
 
366
- **Basic Usage:**
346
+ ### Basic usage
367
347
 
368
- The `translations` prop is optional and accepts partial translations that will be merged with the default English translations. This means you only need to provide the translations you want to override.
348
+ Provide only the translations you want to override; the rest fall back to default English.
369
349
 
370
350
  ```tsx
371
351
  <BarChart
372
- title="Habilidades"
373
352
  barValues={barData}
374
353
  description="Progreso de habilidades"
375
- minRange={0}
376
354
  maxRange={200}
355
+ minRange={0}
377
356
  sortFns={['alphabetically', 'none']}
357
+ title="Habilidades"
378
358
  translations={{
359
+ accessibility: {
360
+ gainedNowAt: 'ganado - ahora en',
361
+ inLabel: 'en',
362
+ inOnly: 'en ',
363
+ },
364
+ locale: 'es',
379
365
  sortLabel: 'Ordenar por:',
380
366
  sortOptions: {
381
- none: 'Ninguno',
382
367
  labelAsc: 'Etiqueta (A-Z)',
383
368
  labelDesc: 'Etiqueta (Z-A)',
369
+ none: 'Ninguno',
384
370
  valueAsc: 'Valor (Bajo-Alto)',
385
371
  valueDesc: 'Valor (Alto-Bajo)',
386
372
  },
387
- locale: 'es',
388
373
  }}
389
374
  />
390
375
  ```
391
376
 
392
- **Partial Translations:**
377
+ <Canvas of={BarChartStories.WithStringTranslations} />
393
378
 
394
- You can provide only the translations you need to customize. All other strings will use the default English values:
379
+ ### Partial translations and locale
380
+
381
+ You can override a single key; the rest keep their defaults. The **locale** property controls number formatting (scale labels, value labels) via `Intl.NumberFormat`:
395
382
 
396
383
  ```tsx
397
- <BarChart
398
- // ... other props
399
- translations={{
400
- sortLabel: 'Sort by:',
401
- // sortOptions will use default English values
402
- locale: 'en-GB', // Use British English number formatting
403
- }}
404
- />
384
+ // Only change locale; sort and accessibility use defaults
385
+ translations={{ locale: 'de-DE' }}
386
+
387
+ // Override just sort label and locale
388
+ translations={{
389
+ locale: 'en-GB',
390
+ sortLabel: 'Sort by:',
391
+ }}
405
392
  ```
406
393
 
407
- **Locale-Aware Number Formatting:**
408
-
409
- The `locale` property controls how numbers are formatted throughout the chart using `Intl.NumberFormat`. This affects:
394
+ ### Accessibility: strings vs functions
410
395
 
411
- - Scale labels on the x-axis
412
- - Value labels on bars
413
- - All numeric displays
396
+ **Strings** — Use when the built-in template order fits your language. Fragments are plugged into the default pattern (e.g. `"{value} {unit} {inLabel} {yLabel}"`).
414
397
 
415
- ```tsx
416
- // German locale (1.000 instead of 1,000)
417
- <BarChart
418
- // ... other props
419
- translations={{
420
- locale: 'de-DE',
421
- }}
422
- />
398
+ **Functions** — Use when you need full control. Each function receives context (`yLabel`, values, `unit`, `locale`) and returns the complete summary string.
423
399
 
424
- // French locale
425
- <BarChart
426
- // ... other props
427
- translations={{
428
- locale: 'fr-FR',
429
- }}
430
- />
431
- ```
400
+ <Canvas of={BarChartStories.WithFunctionTranslations} />
432
401
 
433
- **Accessibility Translations:**
402
+ **Best practices:**
434
403
 
435
- The accessibility strings are used in automatically generated `aria-label` attributes for interactive bars and screen reader text for non-interactive bars. These should be translated to match the language of your application:
404
+ - Provide complete translations for the language your app uses; use partial overrides for one-off changes.
405
+ - Prefer string accessibility keys when the default order works; use functions for pluralization, word order, or locale-specific phrasing.
406
+ - Use appropriate locale codes (e.g. `'en-US'`, `'es-ES'`, `'fr-FR'`) for number formatting.
407
+ - Test accessibility summaries with screen readers in the target language.
408
+ - Reuse translation objects across BarChart instances where possible.
436
409
 
437
- ```tsx
438
- <BarChart
439
- // ... other props
440
- translations={{
441
- accessibility: {
442
- gainedNowAt: 'ganado - ahora en', // Spanish: "gained - now at"
443
- inLabel: 'en', // Spanish: "in"
444
- inOnly: 'en ', // Spanish: "in "
445
- },
446
- }}
447
- />
448
- ```
410
+ ## Playground
449
411
 
450
- **Best Practices:**
412
+ <Canvas sourceState="shown" of={BarChartStories.Default} />
451
413
 
452
- - Always provide complete translations for the language your application uses
453
- - Use appropriate locale codes (e.g., `'en-US'`, `'es-ES'`, `'fr-FR'`) for number formatting
454
- - Test accessibility strings with screen readers in the target language
455
- - Consider creating translation objects that can be reused across multiple BarChart instances
414
+ <Controls />
@@ -1,4 +1,9 @@
1
- import { BarChart, BarProps, Box } from '@codecademy/gamut';
1
+ import {
2
+ BarChart,
3
+ BarProps,
4
+ Box,
5
+ PartialBarChartTranslations,
6
+ } from '@codecademy/gamut';
2
7
  import {
3
8
  BookFlipPageIcon,
4
9
  DataScienceIcon,
@@ -16,6 +21,14 @@ const meta: Meta<typeof BarChart> = {
16
21
  maxRange: 2000,
17
22
  unit: 'XP',
18
23
  },
24
+ parameters: {
25
+ docs: {
26
+ description: {
27
+ component:
28
+ 'BarChart supports i18n via the `translations` prop. Accessibility keys (`gainedNowAt`, `inLabel`, `inOnly`) may be strings (fragments in the built-in template) or functions that receive scoped context (values, label, unit, locale) and return the full accessibility summary—useful for pluralization, word order, or locale-specific phrasing.',
29
+ },
30
+ },
31
+ },
19
32
  };
20
33
 
21
34
  export default meta;
@@ -42,6 +55,30 @@ const stackedBarData: BarProps[] = [
42
55
  { yLabel: 'React', seriesOneValue: 300, seriesTwoValue: 450 },
43
56
  ];
44
57
 
58
+ const accessibilityBarData: BarProps[] = [
59
+ { yLabel: 'Python', seriesOneValue: 200, seriesTwoValue: 1500 },
60
+ {
61
+ yLabel: 'JavaScript',
62
+ seriesOneValue: 1800,
63
+ seriesTwoValue: 2000,
64
+ href: '/javascript',
65
+ },
66
+ { yLabel: 'HTML/CSS', seriesOneValue: 600, seriesTwoValue: 800 },
67
+ { yLabel: 'SQL', seriesOneValue: 550, href: '/sql' },
68
+ { yLabel: 'Rust', seriesOneValue: 400 },
69
+ { yLabel: 'React', seriesOneValue: 300, seriesTwoValue: 450 },
70
+ ];
71
+
72
+ const accessibilityTranslations: PartialBarChartTranslations = {
73
+ accessibility: {
74
+ gainedNowAt: (ctx) =>
75
+ `${ctx.seriesOneValue} ${ctx.unit} gained — now at ${ctx.seriesTwoValue} ${ctx.unit} in ${ctx.yLabel}`,
76
+ inLabel: (ctx) => `${ctx.value} ${ctx.unit} in ${ctx.yLabel}`,
77
+ inOnly: (ctx) => `${ctx.value} ${ctx.unit}`.trim(),
78
+ },
79
+ locale: 'en',
80
+ };
81
+
45
82
  const barDataWithIcons: BarProps[] = [
46
83
  {
47
84
  yLabel: 'Python',
@@ -271,8 +308,8 @@ export const CustomStyles: Story = {
271
308
  args: {
272
309
  barValues: stackedBarData,
273
310
  styleConfig: {
274
- backgroundBarColor: 'text',
275
- foregroundBarColor: 'primary',
311
+ seriesTwoBarColor: 'text',
312
+ seriesOneBarColor: 'primary',
276
313
  textColor: 'primary',
277
314
  seriesOneLabel: 'feedback-error',
278
315
  seriesTwoLabel: 'feedback-success',
@@ -294,3 +331,48 @@ export const CustomScale: Story = {
294
331
  description: 'Custom scale intervals for more granular value display',
295
332
  },
296
333
  };
334
+
335
+ /**
336
+ * Bar chart with string-based translations (e.g. Spanish).
337
+ * Partial translations are merged with defaults.
338
+ */
339
+ export const WithStringTranslations: Story = {
340
+ args: {
341
+ barValues: stackedBarData,
342
+ sortFns: ['alphabetically', 'numerically', 'none'],
343
+ title: 'Gráfico de habilidades',
344
+ description: 'Progreso hacia los objetivos totales por lenguaje',
345
+ unit: 'XP',
346
+ translations: {
347
+ locale: 'es',
348
+ sortLabel: 'Ordenar por:',
349
+ sortOptions: {
350
+ none: 'Ninguno',
351
+ labelAsc: 'Etiqueta (A-Z)',
352
+ labelDesc: 'Etiqueta (Z-A)',
353
+ valueAsc: 'Valor (Bajo-Alto)',
354
+ valueDesc: 'Valor (Alto-Bajo)',
355
+ },
356
+ accessibility: {
357
+ gainedNowAt: 'ganado - ahora en',
358
+ inLabel: 'en',
359
+ inOnly: 'en ',
360
+ },
361
+ },
362
+ },
363
+ };
364
+
365
+ /**
366
+ * Bar chart with function-based accessibility translations.
367
+ * Exercises gainedNowAt (stacked), inLabel (link/button single bar), and inOnly (non-interactive single bar).
368
+ */
369
+ export const WithFunctionTranslations: Story = {
370
+ args: {
371
+ barValues: accessibilityBarData,
372
+ description:
373
+ 'Custom aria-label summaries via translation functions (stacked, link, and non-interactive bars)',
374
+ title: 'Skills experience (accessibility functions)',
375
+ translations: accessibilityTranslations,
376
+ unit: 'XP',
377
+ },
378
+ };