Skip to content

A11y: Handle lang attribute throughout preview#35321

Open
Sidnioulz wants to merge 23 commits into
nextfrom
sidnioulz/fix-html-lang-refreshed
Open

A11y: Handle lang attribute throughout preview#35321
Sidnioulz wants to merge 23 commits into
nextfrom
sidnioulz/fix-html-lang-refreshed

Conversation

@Sidnioulz

@Sidnioulz Sidnioulz commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Closes #11706

What I did

  • Added support for a lang attribute to be set at the root of stories
  • Added support for an extra lang attribute for docs content (descriptions, etc.)
  • Marked our own doc blocks UI as lang="en"
  • Fixed up required attribute accessibility in ArgRow because I noticed it there and then and it was a trivial change

This should ensure the whole tree is declaring the right language for screen readers regardless of what our users need to do.

Checklist for Contributors

Testing

The changes in this PR are covered in the following automated tests:

  • stories
  • unit tests
  • integration tests
  • end-to-end tests

Manual testing

Open the UI. Write some docgen in Spanish and some stories in French, or something like that. Run your favourite screenreader and observe how it handles languages as you go through the UI.

Documentation

  • Add or update documentation reflecting your changes
  • If you are deprecating/removing a feature, make sure to update
    MIGRATION.MD

Checklist for Maintainers

  • When this PR is ready for testing, make sure to add ci:normal, ci:merged or ci:daily GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in code/lib/cli-storybook/src/sandbox-templates.ts

  • Declare whether manual QA will be needed for this PR during the next release, through qa:needed or qa:skip

  • Make sure this PR contains one of the labels below:

    Available labels
    • bug: Internal changes that fixes incorrect behavior.
    • maintenance: User-facing maintenance tasks.
    • dependencies: Upgrading (sometimes downgrading) dependencies.
    • build: Internal-facing build tooling & test updates. Will not show up in release changelog.
    • cleanup: Minor cleanup style change. Will not show up in release changelog.
    • documentation: Documentation only changes. Will not show up in release changelog.
    • feature request: Introducing a new feature.
    • BREAKING CHANGE: Changes that break compatibility in some way with current major version.
    • other: Changes that don't fit in the above categories.

🦋 Canary release

This PR does not have a canary release associated. You can request a canary release of this pull request by mentioning the @storybookjs/core team here.

core team members can create a canary release here or locally with gh workflow run --repo storybookjs/storybook publish.yml --field pr=<PR_NUMBER>

Summary by CodeRabbit

  • New Features

    • Added language support for docs and stories, including per-page and per-story language settings.
    • Story and docs content now respect configured language tags in rendered output.
  • Bug Fixes

    • Fixed language propagation across docs pages, tables, controls, and inline stories.
    • Improved consistency so embedded story canvases and docs prose keep the expected language settings.
  • Documentation

    • Updated API docs to describe the new language-related settings and their behavior.

mehm8128 and others added 22 commits June 29, 2026 13:55
Removes the StorybookConfig htmlLang option and its build-time template
plumbing (vite/webpack preview templates, common-preset, config types,
main-config docs). htmlLang becomes a runtime story parameter instead.
Migrates the internal test value from main.ts to preview parameters.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… English

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
These changes — `aria-required` on controls and the hover/focus rework of
the "Setup controls" link — are unrelated to the `lang` attribute work in
this PR and have been split out into #35306.

This reverts commits 6a38434 and 25385d3.
@Sidnioulz Sidnioulz requested a review from ndelangen June 29, 2026 11:58
@Sidnioulz Sidnioulz self-assigned this Jun 29, 2026
@Sidnioulz Sidnioulz added the bug label Jun 29, 2026
@Sidnioulz Sidnioulz added accessibility ci:normal Run our default set of CI jobs (choose this for most PRs). labels Jun 29, 2026
@Sidnioulz Sidnioulz added a11y: aria Accessibility issues related to ARIA markup usage qa:needed Pull Requests that will need manual QA prior to release. labels Jun 29, 2026
@Sidnioulz Sidnioulz moved this to In Progress in Core Team Projects Jun 29, 2026
Comment thread docs/api/parameters.mdx
Comment on lines +55 to +76
### `htmlLang`

Type: `string`

Default: `'en'`

A [BCP-47 language tag](https://en.wikipedia.org/wiki/IETF_language_tag) (e.g. `'ja'`, `'de'`, `'fr-CH'`) describing the language of the **rendered story UI**. In story view it sets the `lang` attribute on the preview's `<html>` element; in docs view it sets `lang` on each embedded story canvas. Inherited project → meta → story.

Use this to language the components you render — for example, a German story added for copy-length validation inside an otherwise English design system.

Storybook's own interface (the manager UI and docs chrome such as toolbars and ArgTypes headers) always reports as English and is unaffected by this parameter.

### `docs.lang`

Type: `string`

Default: `'en'`

A BCP-47 language tag describing the language of the **docs prose** that Storybook renders — component and story descriptions, ArgTypes description cells, and free-form MDX prose. Resolved project → meta for page-level content and project → meta → story for per-story content.

This is independent of [`htmlLang`](#htmllang): the language you _document_ in can differ from the language your stories _render_ in. Storybook chrome (toolbars, ArgTypes column headers, anchor links) stays English regardless.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jonniebigodes this is AI generated, full disclosure.

Could you please do a pass over the copy? I think it's well explained but likely not matching our doc style.

(I recreated the branch due to Chromatic ancestor issues)

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds two new BCP-47 parameters — htmlLang on StorybookParameters and docs.lang on DocsParameters — and propagates them through WebView.prepareForStory, DocsContainer, DocsPageWrapper, Description, ArgsTable/ArgRow, inline story <div>, and Toolbar/Preview components. Storybook chrome elements are hardcoded to lang="en". Includes tests and API docs.

Changes

BCP-47 lang attribute support for stories and docs

Layer / File(s) Summary
Type declarations: htmlLang and docs.lang
code/core/src/types/modules/csf.ts, code/addons/docs/src/types.ts
Adds optional htmlLang?: string to StorybookParameters and lang?: string to DocsParameters['docs'] with JSDoc.
WebView: apply htmlLang to story root
code/core/src/preview-api/modules/preview-web/WebView.ts, ...WebView.test.ts
prepareForStory captures story root and calls new applyHtmlLang to set/remove the lang attribute; prepareForDocs leaves document.documentElement.lang unchanged.
DocsPageWrapper lang prop and DocsContainer wiring
code/addons/docs/src/blocks/components/DocsPage.tsx, ...DocsPage.test.tsx, code/addons/docs/src/blocks/blocks/DocsContainer.tsx
DocsPageWrapper gains lang?: string (default 'en') forwarded to DocsContent; DocsContainer computes lang from metaParameters or project annotations and passes it through.
Description, Title, and TableOfContents lang propagation
code/addons/docs/src/blocks/blocks/Description.tsx, code/addons/docs/src/blocks/components/TableOfContents.tsx, ...docsLangProse.test.tsx
DescriptionBody forwards lang to Markdown; service and fallback paths compute lang from parameters.docs.lang; TableOfContents heading sets lang="en" when title is absent.
Inline story canvas and Toolbar chrome lang
code/addons/docs/src/blocks/components/Story.tsx, ...Preview.tsx, ...Toolbar.tsx, code/core/src/components/components/Toolbar/Toolbar.tsx
Inline story <div> sets lang from story.parameters.htmlLang (default 'en'); ActionBar and AbstractToolbar gain lang="en" to stay English.
ArgsTable/ArgRow docsLang propagation and English chrome
code/addons/docs/src/blocks/blocks/ArgTypes.tsx, ...Controls.tsx, code/addons/docs/src/blocks/components/ArgsTable/ArgsTable.tsx, ...ArgRow.tsx, ...TabbedArgsTable.tsx, ...ArgsTable.lang.test.tsx
docsLang derived from parameters.docs.lang flows from ArgTypes/Controls blocks through ArgsTable, TabbedArgsTable, and ArgRow to the Description cell; table, reset button, and header are hardcoded lang="en".
Example stories and preview config
code/addons/docs/src/blocks/examples/StoryParameters.stories.tsx, code/addons/docs/src/blocks/blocks/Story.stories.tsx, code/.storybook/preview.tsx
Adds HtmlLang example story (htmlLang: 'fr'), InlineHtmlLang story block with play assertion on lang="fr", and sets htmlLang/docs.lang in internal preview config.
API docs and sidebar reordering
docs/api/parameters.mdx, docs/api/main-config/main-config-*.mdx
Documents htmlLang and docs.lang parameters; increments sidebar.order across main-config MDX pages to insert a new entry.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • storybookjs/storybook#35169: Modifies the same Description.tsx service-based rendering path that this PR extends with lang computation and forwarding.
  • storybookjs/storybook#35306: Modifies ArgsTable/ArgRow.tsx in the same component this PR extends with docsLang/lang plumbing.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch sidnioulz/fix-html-lang-refreshed

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (5)
code/addons/docs/src/blocks/blocks/DocsContainer.tsx (1)

13-13: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add the .ts extension to this relative import.

./docsLang is a new relative TypeScript import, so it should be written with an explicit extension to match the repo rule. As per coding guidelines, **/*.{ts,tsx}: "Use explicit file extensions on relative imports and exports in TypeScript source (for example ./foo.ts or ./bar.tsx)."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/addons/docs/src/blocks/blocks/DocsContainer.tsx` at line 13, The
relative import in DocsContainer should use an explicit TypeScript extension to
match the repo rule. Update the import of resolveDocsLang in DocsContainer.tsx
to reference the file with its .ts extension, keeping the same symbol and
relative path otherwise.

Source: Coding guidelines

code/addons/docs/src/blocks/components/ArgsTable/ArgsTable.lang.test.tsx (2)

9-10: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add explicit extensions to these relative imports.

Repo rules require extensionful relative imports in TypeScript source, so these should be ./ArgRow.stories.tsx and ./ArgsTable.tsx. As per coding guidelines, "**/*.{ts,tsx}: Use explicit file extensions on relative imports and exports in TypeScript source (for example ./foo.ts or ./bar.tsx)."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/addons/docs/src/blocks/components/ArgsTable/ArgsTable.lang.test.tsx`
around lines 9 - 10, The relative imports in ArgsTable.lang.test.tsx use
extensionless TypeScript paths, which violates the repo rule for explicit
extensions. Update the import of ArgRow.stories and ArgsTable to reference their
full TypeScript file names, using the same symbols ArgRow and ArgsTable so the
test file resolves them via extensionful relative imports.

Source: Coding guidelines


12-35: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move this component coverage into a story play test instead of a new *.test.tsx file.

This file is asserting React component behavior/accessibility, which the repo wants in Storybook stories rather than standalone *.test.tsx tests. Please add/update a story and put these assertions in a play function, then verify it with the Storybook Vitest config. As per coding guidelines, "**/*.test.tsx: For React components, do not add *.test.tsx unit tests; behavior, accessibility, and interaction assertions should live in Storybook stories with play functions." and "**/*.stories.tsx: When updating component stories, verify play assertions with vitest --config code/vitest.config.storybook.ts <story-file>."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/addons/docs/src/blocks/components/ArgsTable/ArgsTable.lang.test.tsx`
around lines 12 - 35, This ArgsTable coverage should not live in a standalone
.test.tsx file; move the English-lang assertions into the component’s Storybook
story play function. Update the relevant ArgsTable story so it renders the same
wrapped setup and uses play to assert the header and arg-name cell keep
lang="en", referencing ArgsTable and the story export that exercises it. Then
verify the story behavior with the Storybook Vitest config instead of
adding/keeping the separate unit test.

Source: Coding guidelines

code/addons/docs/src/blocks/blocks/Description.tsx (1)

7-13: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use explicit extensions for the new relative imports.

This block mixes ./use-service-docgen.ts / ./use-service-story-docs.ts with extensionless imports like ./DocsContext, ./docsLang, ./Markdown, and ./useOf, which breaks the repo’s TS import convention. As per coding guidelines, **/*.{ts,tsx}: Use explicit file extensions on relative imports and exports in TypeScript source (for example ./foo.ts or ./bar.tsx).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/addons/docs/src/blocks/blocks/Description.tsx` around lines 7 - 13,
Update the relative imports in Description.tsx to follow the repository’s
TypeScript import convention by using explicit file extensions everywhere. The
issue is that this block currently mixes extensionless imports like DocsContext,
resolveDocsLang, Markdown, and useOf with explicit .ts imports; make all
relative imports consistent by adding the proper extensions to each referenced
module in this file.

Source: Coding guidelines

code/addons/docs/src/blocks/blocks/docsLangProse.test.tsx (1)

31-58: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move this coverage into Storybook play tests instead of a *.test.tsx file.

This is a new React component behavior suite, but the repo’s test policy keeps those assertions in stories. As per coding guidelines, **/*.test.tsx: For React components, do not add *.test.tsx unit tests; behavior, accessibility, and interaction assertions should live in Storybook stories with play functions. As per coding guidelines, **/*.stories.tsx: When writing tests for components, add or update <Component>.stories.tsx and cover each behavior with play functions using expect, userEvent, and within from storybook/test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/addons/docs/src/blocks/blocks/docsLangProse.test.tsx` around lines 31 -
58, Move the `Title` and `Subtitle` language assertions out of
`docsLangProse.test.tsx` and into the corresponding Storybook story file for
these components. Update the relevant stories to cover the `docs.lang` behavior
with `play` functions using `expect`/`within` (and `userEvent` if needed), and
remove the React component unit test file so the coverage follows the repo’s
Storybook test policy.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@code/addons/docs/src/blocks/blocks/docsLang.test.ts`:
- Line 3: The relative import in docsLang.test.ts should use an explicit
TypeScript extension to match the repo import rule. Update the import of
resolveDocsLang from ./docsLang to reference ./docsLang.ts so the test file
follows the same explicit-extension convention used across TypeScript sources.

In `@code/addons/docs/src/blocks/blocks/docsLang.ts`:
- Around line 10-11: The resolveDocsLang helper currently uses nullish
coalescing, which preserves empty docs.lang strings instead of falling back.
Update resolveDocsLang to treat blank or whitespace-only parameters?.docs?.lang
and projectParameters?.docs?.lang as unset, so it falls through to the project
value or the 'en' default. Keep the change localized to resolveDocsLang and
preserve the existing fallback order.

In `@code/addons/docs/src/blocks/blocks/Subtitle.tsx`:
- Around line 51-54: The Subtitle lang lookup is missing the project-level docs
language fallback, so update the `Subtitle` component to resolve the language
using both `preparedMeta?.parameters` and the project annotations’ docs
parameters. Mirror the `Title` fix by passing
`projectAnnotations.parameters.docs.lang` (or the equivalent merged docs
parameters source) into `resolveDocsLang` so a global docs language is respected
when no local override is present.

In `@code/addons/docs/src/blocks/blocks/Title.tsx`:
- Around line 53-56: The Title block is not passing the project-level docs
language into resolveDocsLang, so it can fall back to the default language
instead of the globally configured one. Update the Title component to thread the
docs lang from the project context into resolveDocsLang alongside
preparedMeta?.parameters, matching the pattern used by Description and ensuring
the rendered PureTitle gets the correct lang value.

In `@code/addons/docs/src/blocks/components/DocsPage.test.tsx`:
- Around line 83-94: Move this assertion out of the DocsContent unit test
because it bypasses DocsPageWrapper and does not validate the changed forwarding
behavior in DocsPage.tsx. Add the lang forwarding check to the appropriate
Storybook story using a play function that renders the wrapper and asserts the
content region receives the lang attribute, and remove the *.test.tsx component
test for this case.

In `@code/addons/docs/src/blocks/components/Preview.tsx`:
- Around line 234-238: The `ActionBar` in `Preview` is applying `lang="en"` to
both Storybook-owned controls and caller-provided `additionalActionItems`, which
can incorrectly force user content to be announced as English. Move the English
override off the `ActionBar` container and apply it only to the built-in
buttons/actions rendered by `Preview`, keeping `additionalActionItems` outside
that language scope.

In `@code/core/src/types/modules/csf.ts`:
- Around line 83-90: Update the JSDoc for htmlLang in csf.ts to match actual
behavior: document that WebView.prepareForStory() applies the value to the story
root element, not the document <html> element, and remove the claim that story
view sets <html lang> or that the default is always 'en'. Keep the inheritance
note (project → meta → story) and align the description with the tests around
document.documentElement.lang remaining unchanged.

---

Nitpick comments:
In `@code/addons/docs/src/blocks/blocks/Description.tsx`:
- Around line 7-13: Update the relative imports in Description.tsx to follow the
repository’s TypeScript import convention by using explicit file extensions
everywhere. The issue is that this block currently mixes extensionless imports
like DocsContext, resolveDocsLang, Markdown, and useOf with explicit .ts
imports; make all relative imports consistent by adding the proper extensions to
each referenced module in this file.

In `@code/addons/docs/src/blocks/blocks/DocsContainer.tsx`:
- Line 13: The relative import in DocsContainer should use an explicit
TypeScript extension to match the repo rule. Update the import of
resolveDocsLang in DocsContainer.tsx to reference the file with its .ts
extension, keeping the same symbol and relative path otherwise.

In `@code/addons/docs/src/blocks/blocks/docsLangProse.test.tsx`:
- Around line 31-58: Move the `Title` and `Subtitle` language assertions out of
`docsLangProse.test.tsx` and into the corresponding Storybook story file for
these components. Update the relevant stories to cover the `docs.lang` behavior
with `play` functions using `expect`/`within` (and `userEvent` if needed), and
remove the React component unit test file so the coverage follows the repo’s
Storybook test policy.

In `@code/addons/docs/src/blocks/components/ArgsTable/ArgsTable.lang.test.tsx`:
- Around line 9-10: The relative imports in ArgsTable.lang.test.tsx use
extensionless TypeScript paths, which violates the repo rule for explicit
extensions. Update the import of ArgRow.stories and ArgsTable to reference their
full TypeScript file names, using the same symbols ArgRow and ArgsTable so the
test file resolves them via extensionful relative imports.
- Around line 12-35: This ArgsTable coverage should not live in a standalone
.test.tsx file; move the English-lang assertions into the component’s Storybook
story play function. Update the relevant ArgsTable story so it renders the same
wrapped setup and uses play to assert the header and arg-name cell keep
lang="en", referencing ArgsTable and the story export that exercises it. Then
verify the story behavior with the Storybook Vitest config instead of
adding/keeping the separate unit test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 01c85115-612d-4875-89fc-ea8bca4f9960

📥 Commits

Reviewing files that changed from the base of the PR and between d0ef37e and b4939cd.

📒 Files selected for processing (34)
  • code/addons/docs/src/blocks/blocks/Description.tsx
  • code/addons/docs/src/blocks/blocks/DocsContainer.tsx
  • code/addons/docs/src/blocks/blocks/Story.stories.tsx
  • code/addons/docs/src/blocks/blocks/Subtitle.tsx
  • code/addons/docs/src/blocks/blocks/Title.tsx
  • code/addons/docs/src/blocks/blocks/docsLang.test.ts
  • code/addons/docs/src/blocks/blocks/docsLang.ts
  • code/addons/docs/src/blocks/blocks/docsLangProse.test.tsx
  • code/addons/docs/src/blocks/components/ArgsTable/ArgRow.tsx
  • code/addons/docs/src/blocks/components/ArgsTable/ArgsTable.lang.test.tsx
  • code/addons/docs/src/blocks/components/ArgsTable/ArgsTable.tsx
  • code/addons/docs/src/blocks/components/DocsPage.test.tsx
  • code/addons/docs/src/blocks/components/DocsPage.tsx
  • code/addons/docs/src/blocks/components/Preview.tsx
  • code/addons/docs/src/blocks/components/Story.tsx
  • code/addons/docs/src/blocks/examples/StoryParameters.stories.tsx
  • code/addons/docs/src/types.ts
  • code/core/src/preview-api/modules/preview-web/WebView.test.ts
  • code/core/src/preview-api/modules/preview-web/WebView.ts
  • code/core/src/types/modules/csf.ts
  • docs/api/main-config/main-config-indexers.mdx
  • docs/api/main-config/main-config-log-level.mdx
  • docs/api/main-config/main-config-manager-head.mdx
  • docs/api/main-config/main-config-preview-annotations.mdx
  • docs/api/main-config/main-config-preview-body.mdx
  • docs/api/main-config/main-config-preview-head.mdx
  • docs/api/main-config/main-config-refs.mdx
  • docs/api/main-config/main-config-static-dirs.mdx
  • docs/api/main-config/main-config-swc.mdx
  • docs/api/main-config/main-config-tags.mdx
  • docs/api/main-config/main-config-typescript.mdx
  • docs/api/main-config/main-config-vite-final.mdx
  • docs/api/main-config/main-config-webpack-final.mdx
  • docs/api/parameters.mdx

Comment thread code/addons/docs/src/blocks/blocks/docsLang.test.ts Outdated
Comment on lines +10 to +11
export const resolveDocsLang = (parameters?: Parameters, projectParameters?: Parameters): string =>
parameters?.docs?.lang ?? projectParameters?.docs?.lang ?? 'en';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Treat blank docs.lang values as unset.

Using ?? here preserves '', so docs blocks can end up rendering lang="" instead of inheriting the project value or the documented 'en' fallback.

Suggested fix
+const normalizeLang = (lang?: string) => (lang?.trim() ? lang : undefined);
+
 export const resolveDocsLang = (parameters?: Parameters, projectParameters?: Parameters): string =>
-  parameters?.docs?.lang ?? projectParameters?.docs?.lang ?? 'en';
+  normalizeLang(parameters?.docs?.lang) ??
+  normalizeLang(projectParameters?.docs?.lang) ??
+  'en';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const resolveDocsLang = (parameters?: Parameters, projectParameters?: Parameters): string =>
parameters?.docs?.lang ?? projectParameters?.docs?.lang ?? 'en';
const normalizeLang = (lang?: string) => (lang?.trim() ? lang : undefined);
export const resolveDocsLang = (parameters?: Parameters, projectParameters?: Parameters): string =>
normalizeLang(parameters?.docs?.lang) ??
normalizeLang(projectParameters?.docs?.lang) ??
'en';
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/addons/docs/src/blocks/blocks/docsLang.ts` around lines 10 - 11, The
resolveDocsLang helper currently uses nullish coalescing, which preserves empty
docs.lang strings instead of falling back. Update resolveDocsLang to treat blank
or whitespace-only parameters?.docs?.lang and projectParameters?.docs?.lang as
unset, so it falls through to the project value or the 'en' default. Keep the
change localized to resolveDocsLang and preserve the existing fallback order.

Comment thread code/addons/docs/src/blocks/blocks/Subtitle.tsx Outdated
Comment thread code/addons/docs/src/blocks/blocks/Title.tsx Outdated
Comment on lines +83 to +94
it('forwards the lang attribute to the content region for docs prose', () => {
const { container } = render(
<ThemeProvider theme={convert(themes.light)}>
<DocsContent lang="de">
<p>Beschreibung</p>
</DocsContent>
</ThemeProvider>
);

const content = container.firstElementChild;
expect(content?.getAttribute('lang')).toBe('de');
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

This test bypasses the code you changed.

Rendering DocsContent directly will still pass even if DocsPageWrapper stops forwarding lang, so this doesn't cover the new behavior in DocsPage.tsx. Please move this to a Storybook play assertion for the wrapper instead of adding a *.test.tsx component test. As per coding guidelines, **/*.test.tsx: "For React components, do not add *.test.tsx unit tests; behavior, accessibility, and interaction assertions should live in Storybook stories with play functions."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/addons/docs/src/blocks/components/DocsPage.test.tsx` around lines 83 -
94, Move this assertion out of the DocsContent unit test because it bypasses
DocsPageWrapper and does not validate the changed forwarding behavior in
DocsPage.tsx. Add the lang forwarding check to the appropriate Storybook story
using a play function that renders the wrapper and asserts the content region
receives the lang attribute, and remove the *.test.tsx component test for this
case.

Source: Coding guidelines

Comment on lines +234 to +238
<ActionBar
className="sbdocs sbdocs-preview-actions"
lang="en"
innerStyle={{ paddingInline: 0 }}
>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Scope the English override to Storybook-owned controls only.

This lang="en" also covers additionalActionItems later in the same ActionBar, so caller-supplied title/ariaLabel text will be announced as English even when it is user content. Move the override onto the built-in buttons instead of the container.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/addons/docs/src/blocks/components/Preview.tsx` around lines 234 - 238,
The `ActionBar` in `Preview` is applying `lang="en"` to both Storybook-owned
controls and caller-provided `additionalActionItems`, which can incorrectly
force user content to be announced as English. Move the English override off the
`ActionBar` container and apply it only to the built-in buttons/actions rendered
by `Preview`, keeping `additionalActionItems` outside that language scope.

Comment on lines +83 to +90
/**
* The BCP-47 language tag applied to the rendered story's document.
*
* In story view this sets `<html lang>`; in docs view it sets `lang` on the embedded story canvas.
* Inherited project → meta → story.
*
* @default 'en'
*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the htmlLang contract text.

WebView.prepareForStory() applies this value to the story root element and its tests assert that document.documentElement.lang stays unchanged, so the current JSDoc overstates the behavior by documenting <html lang> and a story-view default of 'en'.

Suggested doc fix
   /**
-   * The BCP-47 language tag applied to the rendered story's document.
+   * The BCP-47 language tag applied to the rendered story root.
    *
-   * In story view this sets `<html lang>`; in docs view it sets `lang` on the embedded story canvas.
+   * In story view this sets `lang` on the story root; in docs view it sets `lang` on the
+   * embedded story canvas.
    * Inherited project → meta → story.
-   *
-   * `@default` 'en'
    */
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* The BCP-47 language tag applied to the rendered story's document.
*
* In story view this sets `<html lang>`; in docs view it sets `lang` on the embedded story canvas.
* Inherited project meta story.
*
* @default 'en'
*/
/**
* The BCP-47 language tag applied to the rendered story root.
*
* In story view this sets `lang` on the story root; in docs view it sets `lang` on the
* embedded story canvas.
* Inherited project meta story.
*/
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/core/src/types/modules/csf.ts` around lines 83 - 90, Update the JSDoc
for htmlLang in csf.ts to match actual behavior: document that
WebView.prepareForStory() applies the value to the story root element, not the
document <html> element, and remove the claim that story view sets <html lang>
or that the default is always 'en'. Keep the inheritance note (project → meta →
story) and align the description with the tests around
document.documentElement.lang remaining unchanged.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
code/addons/docs/src/blocks/components/ArgsTable/ArgsTable.tsx (1)

472-525: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Pass docsLang to every ArgRow, not just the ungrouped branch.

Line 472 forwards docsLang, but the sectioned and subsectioned ArgRow call sites below still omit it. Since TableWrapper is now lang="en", grouped description cells will keep inheriting English instead of the configured docs language.

Suggested fix
                 {subsection.map((row) => (
                   <ArgRow
                     key={row.key}
                     row={row}
                     arg={args && args[row.key]}
                     expandable={expandable}
+                    docsLang={docsLang}
                     {...common}
                   />
                 ))}
@@
-                {section.ungrouped.map((row) => (
-                  <ArgRow key={row.key} row={row} arg={args && args[row.key]} {...common} />
-                ))}
+                {section.ungrouped.map((row) => (
+                  <ArgRow
+                    key={row.key}
+                    row={row}
+                    arg={args && args[row.key]}
+                    docsLang={docsLang}
+                    {...common}
+                  />
+                ))}
@@
                     {subsection.map((row) => (
                       <ArgRow
                         key={row.key}
                         row={row}
                         arg={args && args[row.key]}
                         expandable={expandable}
+                        docsLang={docsLang}
                         {...common}
                       />
                     ))}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/addons/docs/src/blocks/components/ArgsTable/ArgsTable.tsx` around lines
472 - 525, Pass docsLang through every ArgRow render in ArgsTable, not only the
top ungrouped branch. Update the ArgRow call sites inside
groups.ungroupedSubsections, section.ungrouped, and section.subsections so they
all receive docsLang alongside the existing props, using the same pattern
already present in the first ArgRow branch.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@code/addons/docs/src/blocks/blocks/DocsContainer.tsx`:
- Around line 31-42: The DocsContainer logic leaves lang implicitly typed,
triggering the noImplicitAnyLet lint rule. Update the lang declaration in
DocsContainer so it is explicitly a string (or initialized to a string value)
and keep the existing meta/projectAnnotations fallback assignment logic intact.
Use the lang variable in the try/catch block as the uniquely identifying symbol
to locate and fix this.

---

Outside diff comments:
In `@code/addons/docs/src/blocks/components/ArgsTable/ArgsTable.tsx`:
- Around line 472-525: Pass docsLang through every ArgRow render in ArgsTable,
not only the top ungrouped branch. Update the ArgRow call sites inside
groups.ungroupedSubsections, section.ungrouped, and section.subsections so they
all receive docsLang alongside the existing props, using the same pattern
already present in the first ArgRow branch.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b3fe8e0a-8bb0-47d6-a622-bfbb799f2dcc

📥 Commits

Reviewing files that changed from the base of the PR and between b4939cd and 83d73cb.

📒 Files selected for processing (11)
  • code/.storybook/preview.tsx
  • code/addons/docs/src/blocks/blocks/ArgTypes.tsx
  • code/addons/docs/src/blocks/blocks/Controls.tsx
  • code/addons/docs/src/blocks/blocks/Description.tsx
  • code/addons/docs/src/blocks/blocks/DocsContainer.tsx
  • code/addons/docs/src/blocks/components/ArgsTable/ArgRow.tsx
  • code/addons/docs/src/blocks/components/ArgsTable/ArgsTable.tsx
  • code/addons/docs/src/blocks/components/ArgsTable/TabbedArgsTable.tsx
  • code/addons/docs/src/blocks/components/TableOfContents.tsx
  • code/addons/docs/src/blocks/components/Toolbar.tsx
  • code/core/src/components/components/Toolbar/Toolbar.tsx
✅ Files skipped from review due to trivial changes (1)
  • code/addons/docs/src/blocks/components/Toolbar.tsx

Comment on lines +31 to +42
// Language of docs prose (descriptions, ArgTypes description cells, free MDX prose).
let lang;

try {
const meta = context.resolveOf('meta', ['meta']);
toc = meta.preparedMeta.parameters?.docs?.toc;
const metaParameters = meta.preparedMeta.parameters;
toc = metaParameters?.docs?.toc;
lang = metaParameters?.docs?.lang ?? 'en';
} catch (err) {
// No meta, falling back to project annotations
toc = context?.projectAnnotations?.parameters?.docs?.toc;
lang = context?.projectAnnotations?.parameters?.docs?.lang ?? 'en';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Type lang explicitly to avoid the implicit-any lint error.

Line 32's let lang; trips Biome's noImplicitAnyLet rule, so this change will fail linting unless lang is declared or initialized as a string.

Suggested fix
-  let lang;
+  let lang: string;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Language of docs prose (descriptions, ArgTypes description cells, free MDX prose).
let lang;
try {
const meta = context.resolveOf('meta', ['meta']);
toc = meta.preparedMeta.parameters?.docs?.toc;
const metaParameters = meta.preparedMeta.parameters;
toc = metaParameters?.docs?.toc;
lang = metaParameters?.docs?.lang ?? 'en';
} catch (err) {
// No meta, falling back to project annotations
toc = context?.projectAnnotations?.parameters?.docs?.toc;
lang = context?.projectAnnotations?.parameters?.docs?.lang ?? 'en';
// Language of docs prose (descriptions, ArgTypes description cells, free MDX prose).
let lang: string;
try {
const meta = context.resolveOf('meta', ['meta']);
const metaParameters = meta.preparedMeta.parameters;
toc = metaParameters?.docs?.toc;
lang = metaParameters?.docs?.lang ?? 'en';
} catch (err) {
// No meta, falling back to project annotations
toc = context?.projectAnnotations?.parameters?.docs?.toc;
lang = context?.projectAnnotations?.parameters?.docs?.lang ?? 'en';
🧰 Tools
🪛 Biome (2.5.1)

[error] 32-32: This variable implicitly has the any type.

(lint/suspicious/noImplicitAnyLet)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@code/addons/docs/src/blocks/blocks/DocsContainer.tsx` around lines 31 - 42,
The DocsContainer logic leaves lang implicitly typed, triggering the
noImplicitAnyLet lint rule. Update the lang declaration in DocsContainer so it
is explicitly a string (or initialized to a string value) and keep the existing
meta/projectAnnotations fallback assignment logic intact. Use the lang variable
in the try/catch block as the uniquely identifying symbol to locate and fix
this.

Source: Linters/SAST tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a11y: aria Accessibility issues related to ARIA markup usage accessibility bug ci:normal Run our default set of CI jobs (choose this for most PRs). qa:needed Pull Requests that will need manual QA prior to release.

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

Option to change the language attribute

3 participants