Skip to content

feat: cjk font support spike#6036

Open
marissahuysentruyt wants to merge 26 commits intomainfrom
marissahuysentruyt/swc-1430-spike-cjk-s2-font
Open

feat: cjk font support spike#6036
marissahuysentruyt wants to merge 26 commits intomainfrom
marissahuysentruyt/swc-1430-spike-cjk-s2-font

Conversation

@marissahuysentruyt
Copy link
Collaborator

@marissahuysentruyt marissahuysentruyt commented Feb 18, 2026

Description

OVERVIEW: When a user selects a locale from the Language toolbar, the corresponding Adobe Fonts kit is injected and activated via Typekit.load(). Latin, Arabic, and Hebrew locales continue to use the default kit loaded at startup. Arabic and Hebrew locales also control the dir="rtl".

This PR adds CJK (Chinese, Japanese, Korean) font support to the 2nd-gen Storybook, but introduces a few extra things that felt relevant to validation. It introduces a language decorator to verify the Adobe Fonts kit loading on-demand, and updated CJK typography tokens. In the Fonts guide, this PR also introduces a small internationalized status light story that uses translations.

Key changes:

  • Language decorator (decorators/language.ts): Sets lang and dir on the document root based on the selected locale, injects CJK font kit scripts on demand, and calls Typekit.load() to register and activate the fonts. Also exposes a textDirection global for stories that need to know the current direction.
  • Font kit mapping (preview-head.html): Defines SWC_FONT_KIT_IDS — a locale-to-kit-ID mapping consumed by the language decorator. The default Latin kit loads on first paint; CJK kits load only when needed.
  • Language toolbar (preview.ts): Adds a globe icon toolbar with locale options (English, Arabic, Hebrew, Japanese, Korean, Chinese Simplified, Traditional, and Hong Kong).
  • Window type augmentation (storybook-env.d.ts): Adds FontsLoading, currentKitId, getKitIdForLang, and Typekit to the global Window interface. Added export {} so declare global properly augments the interface.
  • Typography tokens (typography.json): Adds individual CJK font family tokens (Japanese, Korean, Chinese Simplified, Traditional, Hong Kong) and updates the CJK font stack to include all variants.
  • Translation infrastructure (intl/translations.json, helpers/get-translation-key.ts): Provides sample translations for 8 locales and a helper to map toolbar locale values to translation keys.
  • Status light locale demo (status-light.stories.ts): Adds a WithLocaleWrapper story that renders a status light with translated labels, used by the Fonts documentation guide to demonstrate font loading.
  • Fonts documentation (fonts.mdx): Documents RTL behavior, programmatic font verification, CJK-specific troubleshooting, and how to add a new locale.

Motivation and context

2nd-gen Storybook had no support for CJK fonts. This PR enables developers to preview components to verify that CJK font kits are loaded via JS. It also provides onboarding documentation so contributors understand the font loading architecture.

Related issue(s)

  • fixes [SWC-1430, SWC-1454]

Screenshots (if appropriate)


Author's checklist

  • I have read the CONTRIBUTING and PULL_REQUESTS documents.
  • I have reviewed at the Accessibility Practices for this feature, see: Aria Practices
  • I have added automated tests to cover my changes.
  • I have included a well-written changeset if my change needs to be published.
  • I have included updated documentation if my change required it.

Reviewer's checklist

  • Includes a Github Issue with appropriate flag or Jira ticket number without a link
  • Includes thoughtfully written changeset if changes suggested include patch, minor, or major features
  • Automated tests cover all use cases and follow best practices for writing
  • Validated on all supported browsers
  • All VRTs are approved before the author can update Golden Hash

Manual review test cases

  • CJK font loading: Verify each CJK locale loads the correct font
    • Go to any component story in Storybook (e.g. Status Light Playground)
    • Select Japanese from the Language toolbar (globe icon)
    • Open DevTools on the preview iframe and confirm wf-adobe-clean-han-japanese-n4-active (and n5, n7, n9) classes appear on .
    • Verify window.currentKitId is fhs4zuy in the browser console (window.curentKitId
    • Repeat for Korean (cbs0ykg), Chinese Simplified (dck3siu), Chinese Traditional (ueg0fjf), and Chinese Hong Kong (kje4xva)
    • Reviewers can validate the font kit IDs by viewing the separate projects (for Korean, Chinese Simplified, Chinese Traditional, and Hong Kong) in the SWC Adobe Fonts account. They are also mapped in the preview-head.html:
__SWC_FONT_KIT_IDS__
    default/en/ar/he: 'obc6cux',
    ja: 'fhs4zuy',
    ko: 'cbs0ykg',
    'zh-Hant': 'ueg0fjf', (Traditional Chinese)
    'zh-Hans': 'dck3siu', (Simplified Chinese)
    'zh-HK': 'kje4xva', (Hong Kong)
  • Switching back to Latin: Verify default kit is restored

    • While on a CJK locale, switch back to English in the Language toolbar
    • Confirm window.currentKitId is obc6cux and the dynamic CJK script tag is removed from the DOM
  • RTL direction: Verify direction attribute updates

    • Select Arabic or Hebrew from the Language toolbar
    • Confirm dir="rtl" is set on the iframe's element
    • Switch back to English and confirm dir="ltr" is restored
  • Fonts documentation page: Verify the guide renders correctly

    • Go to the Customization / Fonts docs page
    • Confirm the "Try it with translations" section renders the WithLocaleWrapper status light with translated labels
    • Switch locales in the toolbar and confirm the label text and font update

Device review

  • Did it pass in Desktop?
  • Did it pass in (emulated) Mobile?
  • Did it pass in (emulated) iPad?

@changeset-bot
Copy link

changeset-bot bot commented Feb 18, 2026

⚠️ No Changeset found

Latest commit: 2462b06

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@marissahuysentruyt marissahuysentruyt force-pushed the marissahuysentruyt/swc-1430-spike-cjk-s2-font branch from 1ee9eee to 663c899 Compare February 20, 2026 19:01
@marissahuysentruyt marissahuysentruyt changed the title Marissahuysentruyt/swc 1430 spike cjk s2 font feat: cjk font support spike Feb 20, 2026
@marissahuysentruyt marissahuysentruyt self-assigned this Feb 20, 2026
@marissahuysentruyt marissahuysentruyt added the 2nd gen These issues or PRs map to our 2nd generation work to modernizing infrastructure. label Feb 20, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 20, 2026

📚 Branch Preview Links

🔍 First Generation Visual Regression Test Results

When a visual regression test fails (or has previously failed while working on this branch), its results can be found in the following URLs:

Deployed to Azure Blob Storage: pr-6036

If the changes are expected, update the current_golden_images_cache hash in the circleci config to accept the new images. Instructions are included in that file.
If the changes are unexpected, you can investigate the cause of the differences and update the code accordingly.

@marissahuysentruyt marissahuysentruyt force-pushed the marissahuysentruyt/swc-1430-spike-cjk-s2-font branch 2 times, most recently from 6e4689f to 6f5b191 Compare February 23, 2026 21:50
@marissahuysentruyt marissahuysentruyt added the Status:WIP PR is a work in progress or draft label Feb 23, 2026
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Stole this from adobe/spectrum-css#3368

Comment on lines +340 to +355
function withLocaleWrapperRender(
args: Record<string, unknown> & { lang?: string; 'default-slot'?: string },
context: { globals: { lang?: string } }
): ReturnType<typeof html> {
const contextLang = context.globals?.lang;
const lang = args.lang ?? contextLang;
const key = getTranslationKey(lang) as TranslationKey;
const langTranslations = translations[key] ?? translations.en;
// Always use translated label so Language toolbar drives the visible text
const labelText = langTranslations['fieldlabel.label'];

return html`
<div lang=${ifDefined(lang ?? undefined)}>
${template({ ...args, 'default-slot': labelText })}
</div>
`;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Stole this from adobe/spectrum-css#3368

@marissahuysentruyt marissahuysentruyt force-pushed the marissahuysentruyt/swc-1430-spike-cjk-s2-font branch from 6f5b191 to 9a7eba8 Compare February 23, 2026 23:12
@marissahuysentruyt marissahuysentruyt marked this pull request as ready for review February 23, 2026 23:12
@marissahuysentruyt marissahuysentruyt requested a review from a team as a code owner February 23, 2026 23:12
@marissahuysentruyt marissahuysentruyt added Status:Ready for review PR ready for review or re-review. and removed Status:WIP PR is a work in progress or draft labels Feb 23, 2026
@5t3ph
Copy link
Contributor

5t3ph commented Feb 24, 2026

@marissahuysentruyt great start on this! I think the switching of the kits is working however the actual font-family is getting blocked by component level overrides, example for Status light:

image

I think we should let the font-family be inheritable (as in, remove it from being set within the component styles). The font-size also needs adjusted per t-shirt sizes but that can probably be another ticket.

There is a secondary setting of the font-family for Storybook that is also blocking that global style, so we may need to duplicate the lang() font-family rule in preview-head.html. Probably nest in the current rule that is setting the font-family.

Maybe we need to use !important on the lang rule in the generated typography stylesheet? It would get blocked by for example the Storybook rule, but might be reasonably worth the bit of extra defense.


```css
adobe-clean-han-japanese, Adobe Clean Han, sans-serif
adobe-clean-han-japanese, 'Adobe Clean Han', sans-serif
Copy link
Contributor

Choose a reason for hiding this comment

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

Probably encourage to use the token for this font-family? You could expose the string in a comment or something if you like. Otherwise the note about the "stack" has a disconnect if we don't show the token.

/* adobe-clean-han-traditional, "Adobe Clean Han Traditional Chinese", adobe-clean-han-simplified-c, "Adobe Clean Han Simplified Chinese", adobe-clean-han-hong-kong, "Adobe Clean Han Hong Kong", adobe-clean-han-japanese, "Adobe Clean Han Japanese", adobe-clean-han-korean, "Adobe Clean Han Korean", Adobe Clean Han, sans-serif */

font-family: var(--swc-cjk-font-family-stack);

2. Look for `wf-*-active` classes on the iframe's `<html>` element (e.g. `wf-adobe-clean-han-japanese-n4-active`). If missing, the kit script loaded but `Typekit.load()` may not have been called
3. Check the browser console for `Typekit.load() failed` warnings
4. Verify the kit ID in `preview-head.html` (`__SWC_FONT_KIT_IDS__`) is correct and the Adobe Fonts web project is published
5. Try a hard refresh — `Ctrl+Shift+R` / `Cmd+Shift+R` — to clear cached scripts
Copy link
Contributor

Choose a reason for hiding this comment

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

I would add another note aboute actually inspecting the element, possibly as item number 1 since simple inheritance/font-family property value might be interferring.

* textfield.value from translations.json based on context.globals.lang.
* Used by the Fonts guide and for locale/font demos. This story is "docs-only."
*/
function withLocaleWrapperRender(
Copy link
Contributor

Choose a reason for hiding this comment

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

Would love to see this worked into the Typography stories - another ticket would be fine!

"animation-linear": "cubic-bezier(0, 0, 1, 1)",
"cjk-font": "var(--swc-cjk-font-family-stack)",
"cjk-font-family-stack": "adobe-clean-han-japanese, Adobe Clean Han, sans-serif;",
"cjk-font-family-stack": "adobe-clean-han-traditional, 'Adobe Clean Han Traditional Chinese', adobe-clean-han-simplified-c, 'Adobe Clean Han Simplified Chinese', adobe-clean-han-hong-kong, 'Adobe Clean Han Hong Kong', adobe-clean-han-japanese, 'Adobe Clean Han Japanese', adobe-clean-han-korean, 'Adobe Clean Han Korean', Adobe Clean Han, sans-serif;",
Copy link
Contributor

Choose a reason for hiding this comment

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

If these are finalized, will you please also run yarn deploy in this package to re-generate the extension file?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Actually, this comment was a great reminder for a related question I had!

On Adobe Fonts, there's only 2 or 3 fonts listed in the stack for each of the new CJK projects. For example, for the Japanese kit, Adobe Fonts recommends: font-family: adobe-clean-han-japanese, sans-serif;. However, after reviewing your typography PR, I saw that S2 design guidance site lists quite a few more just for :lang(ja).

What are your thoughts on me updating the variable stacks to reflect the S2 site fallbacks? I think that more or less achieves the "support CJK fonts," and leans more towards the "full fidelity."

Then I'll run the yarn deploy!

Copy link
Contributor

Choose a reason for hiding this comment

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

Interesting - maybe do a little comparison to React's font stacks? It is also a bit different than the S2 site, however I'm worried that the list on the S2 site wouldn't be font names that an actual system would recognize, hence why I looked to see if React offered any clues. Looks like they haven't adapted for a couple years either, so maybe it's a general follow-up to check with design/the fonts team?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I got confirmation from Matt K in the implementations channel that the font stack is accurate. I also found a thread in the i18n channel that said the fallbacks with the Japanese characters are fine- that's how the fonts would show up on a Japanese user's system.

Overall, the fallbacks haven't change from S1 to S2 (and now it makes sense why React's stack hasn't changed). I elected to follow the S2 site more to include the Japanese characters (and some of the numbering/suffixes like "Pr6N" 🤷‍♀️)

},
"cjk-font-family-stack": {
"value": "adobe-clean-han-japanese, {cjk-font-family}, sans-serif;"
"value": "{font-family-chinese-traditional}, {font-family-chinese-simplified}, {font-family-hong-kong}, {font-family-japanese}, {font-family-korean}, {cjk-font-family}, sans-serif;"
Copy link
Contributor

Choose a reason for hiding this comment

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

🎉

@marissahuysentruyt marissahuysentruyt added Status:Addressing feedback PR owner is addressing review comments and will change label back to "Ready for review" when ready. and removed Status:Ready for review PR ready for review or re-review. labels Feb 24, 2026
- adds withLocaleWrapperRender() function to wrap the status light in a
new div with a lang attribute
- the WithLocaleWrapper status light story is docs only. it is used in
the fonts.mdx guide to showcase the language decorator loading the
correct CJK font kits, as well as internationalization/translations.
- when the decorator runs, it injects the specific CJK font kit and
calls typekit.load properly
@marissahuysentruyt marissahuysentruyt force-pushed the marissahuysentruyt/swc-1430-spike-cjk-s2-font branch from 9a7eba8 to ee2fbe4 Compare February 24, 2026 21:25
- ensures all functionality from original adobe font embed script is
supported in language decorator
- adds mutation observer to clean up TypeKit-injected style tags (so
there's not a bunch of "dead" style tags of unrelated locale classes)
- Replaces the single combined CJK :lang() rule with individual locale
font-family rules for Arabic, Hebrew, Chinese (Traditional, Simplified,
Hong Kong), Japanese, and Korean. Each locale now resolves to its own
font-family token instead of the generic cjk-font stack.
- Adds LOCALE_FONT_MAP to typography.js mapping locale selectors to
their font-family tokens
- Removes langSelectorList and CJK_SELECTOR_LIST (replaced by direct
iteration over LOCALE_FONT_MAP with cssBlock)
- Regenerate typography.css with new :lang() rules
- Update preview-head.html nav overrides to match new pattern
@marissahuysentruyt marissahuysentruyt added Status:Ready for re-review PR has had its feedback addressed and is once again ready for review. and removed Status:Addressing feedback PR owner is addressing review comments and will change label back to "Ready for review" when ready. labels Feb 26, 2026
Copy link
Collaborator

@pfulton pfulton left a comment

Choose a reason for hiding this comment

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

Thanks for getting this taken care of!

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

Labels

2nd gen These issues or PRs map to our 2nd generation work to modernizing infrastructure. Status:Ready for re-review PR has had its feedback addressed and is once again ready for review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants