Repository navigation
Sync from monorepo: introduce terria design system based on react-aria - #7981
Merged
Merged
Conversation
MONOREPO_REV_ID: 0f6d11348ce1b905507cbd5bbda873c507f84fed
MONOREPO_REV_ID: 372efb26d5522b2475a432f35e41180e7193a571
MONOREPO_REV_ID: 94557ef49640dc7a5d39c2b313691778cad86ff0
MONOREPO_REV_ID: 7a235a49f7ee04950f57d3fa720f36cbef8983f3
MONOREPO_REV_ID: 7c1df6ccb5db56c7d1dd9d6f95e26995f58bb8fd
MONOREPO_REV_ID: 08d7222c1d6de5f039eba8fa8d5fee7321cdac86
MONOREPO_REV_ID: 1544486ceb7d0057bf38ac54a8b3d18425e51e79
MONOREPO_REV_ID: b90cfe189f05f9f52ea3fc9fe6cd55fcd88811ed
Build CSS custom properties from the Figma variables export with style-dictionary: light theme on :root, dark theme on [data-theme='dark'], colors converted to oklch and Figma number scopes converted to rem/px. MONOREPO_REV_ID: ecc44705f99e63b52cbc0a0dac7e8eb481ce5914
MONOREPO_REV_ID: f5eaf54f6ec0938cd472355c81c3b9c97b36a195
MONOREPO_REV_ID: 5ccc1c5e271363187ad43e198b88a0b222c5001a
- manager and docs use the light Storybook theme - the backgrounds toolbar sets data-theme on the preview, so Dark shows the design system's dark tokens - both backgrounds use the surface-bg token instead of fixed neutrals MONOREPO_REV_ID: 24478a0a4a10e65248c82c364d8e4e4d6a8bbf90
MONOREPO_REV_ID: 07495d5bc6b6e338fd545e6946dea31883f958d9
Compound component (Track, TrackCircle, FillCircle) on react-aria's ProgressBar, drawn with plain svg/circle parts. Colors accent, default, success, warning, danger and current (follows the text color); sizes sm, md and lg. MONOREPO_REV_ID: 17aa2d7dac2b72eb8d71d227e2380b7204c56526
MONOREPO_REV_ID: 432623899d23fe6a731bb8b91f5d36519ca783f2
MONOREPO_REV_ID: 13a70a7e98f25c899cf53ad22f1830a6a361809c
MONOREPO_REV_ID: 293f0cf81ff750361ca56640d88d770a2c6e6bd0
MONOREPO_REV_ID: f14cad92d435742839d98a72be91405f0a3dead4
MONOREPO_REV_ID: 42782d655697253ec41d9883e99399ce23d82f92
MONOREPO_REV_ID: 68792fe8b1f721bc6d912ac569b2b1a4127ab8ee
MONOREPO_REV_ID: ececc1c10e1aa5b4fc2919a3083bed8e870d6a76
…gs visible MONOREPO_REV_ID: 23101ebe5535eaf01cc41787be4ea0ca6a3aa9ee
MONOREPO_REV_ID: 0efcd87ca69b58bf782f16be644b05fc7f4e25f3
MONOREPO_REV_ID: 3157bfa9691cf8d8d20ca5751c64c2479e4075c9
MONOREPO_REV_ID: 64f8347d975569b0882c8a714ee6ff309b5a7288
MONOREPO_REV_ID: a0ba9c9cb19478f8db2d8866ad82df9c4b5c64a1
MONOREPO_REV_ID: 13b34ea221fbf12d82de478b69d38499d6805f18
MONOREPO_REV_ID: d74e7cca51afa8274fca4e57c3c162bf28538146
MONOREPO_REV_ID: f8a1753e9a61267b9042d30e2f86e8d4610fb51a
MONOREPO_REV_ID: f852cbfb72e5bd6505621190fae5c78cc2bbf745
React Aria's year picker goes to its item's date, and over a range ending at maxValue every item is that day of its year: 31 December for the default 1900 to 2099. Picking 2030 from 16 June focused 31 December 2030. The year picker now keeps the month and day it was on. MONOREPO_REV_ID: 632129c329e98e22a578e8c30864014f388dffe4
isCurrentYear compared a Gregorian year from new Date() with years of the calendar's own system, so under a Buddhist or Persian calendar no year was current, and under the Japanese one year 8 of any era was. Today is now read in the calendar's system, era and time zone. MONOREPO_REV_ID: 8d6bb116faa114922d58f5b4ccc307fc43a1465d
A year added to a date in 9999, the Gregorian calendar's last, stays in it, so stepping through the years to a maxValue there never ended. That froze the page as soon as a Calendar with a year picker rendered, with maxValue set to 31 December 9999 as an open end. getYearRange now stops when a year doesn't advance. React Aria's useCalendarYearPicker loops the same way, so the year picker hands it a maxValue a year short of the calendar's last year. MONOREPO_REV_ID: 140cb18c3b858ef157f8e0406a567102bf384a98
…hey run The year picker's right arrow always went to the next year, though in a right-to-left locale its rows run right to left: under ar-EG, the right arrow from 2024 went to 2025, the year on its left. The left and right arrows now follow the locale's direction. MONOREPO_REV_ID: a5c8d54566c684e96021bb0e61abe2f2432302eb
Saturation and brightness were the axes whatever the colour space, so <ColorArea colorSpace="hsl" /> or "rgb" threw on render: neither has both channels. They stay the default in hsb; another space's axes are its own, as React Aria picks them. MONOREPO_REV_ID: 5d720e6566b60d69894a1bfb16611491b2616b23
ListBox.Item took its text only from a single string child, so an item
written as {name} ({count}), which JSX splits into several, had none and
couldn't be reached by typing. It now joins them as Dropdown.Item does,
with the helpers moved to utils for both.
MONOREPO_REV_ID: 8bbbba48e25836b33452db79913485838d82aa91
Turning isEnabled off with visibility="auto" stopped detection but left the data attributes it last set, so the fade stayed painted. The effect that sets controlled visibility now clears them in that case too. MONOREPO_REV_ID: f952cb39cfac54fe5e045d159bf570aebb24f8fb
A collapse reported the keys it ends on only when its rows had finished animating, keys taken when it started. Given other expandedKeys in the meantime, a controlled tree was then told to undo them: starting to collapse A, then expanding A and B from outside, ended with neither. The collapse is now cancelled when the tree's keys change under it. MONOREPO_REV_ID: d494d3f1310854a1e1f5487cc8aab83dba798ce7
Its clear button isn't a tab stop, as a Select's isn't, so its choice could only be cleared with a pointer. Backspace or Delete on its button now clears a field that has a clear button, as a Select's trigger does, through one useClear shared with the clear button. MONOREPO_REV_ID: e643611ea6eaf457df1f32774b00c4e02b2f58e6
The items were spread into a list on every render, though an iterable such as a generator can only be read once: its rows went as soon as the list drew again, as it does when a drag starts. Each iterable given is now read once. MONOREPO_REV_ID: 6b9d1e5c4b1449cefece8bbedd83e838efa95a72
React Aria's progressbar drops aria-hidden, so <Spinner aria-hidden />, as the loading toast draws it, was still an unnamed progressbar. Hidden, it's now a plain span, as AIActivityIndicator already is. Both take a plain className and style rather than React Aria's functions of the progressbar's state, which never change for an indeterminate one, so the hidden span needs only the DOM props. MONOREPO_REV_ID: 2db624f48c0204fbc8ba67b482362d9f721a3f11
Splitting a tag's children from its remove button filtered them for
truthiness, dropping a 0: "Results: {0}" read "Results: ", and a tag
of only {0} was blank. Children.map already leaves out the button taken
out, so the filter is gone.
MONOREPO_REV_ID: 1dfd46f04ed8c1445100d63652771a800aaaeef1
A thumb's tooltip showed whenever the thumb was hovered, dragged or focused, with nothing to dismiss it, so it stayed up after Escape. Escape now hides it, a hovered thumb's too, until the thumb is next focused, hovered, moved or dragged. Escape never shows the focused thumb's tooltip, and goes on to what holds the slider unless it closed a tooltip that showed. MONOREPO_REV_ID: c76bf68e88da64d08d6255414b9c2f7423eaead1
A string built from values had only the values, so plurals were left to each string: the English one picked between two forms itself, and a language with more, such as Serbian's 2 to 4, needed its own rules. A string function is now also given a formatter for its locale, whose plural picks the form by Intl.PluralRules and number writes numbers the locale's way, as React Aria's own strings are. The strings are still looked up a string at a time, falling back to the language and then English. MONOREPO_REV_ID: 5ea1db04191bfe5cd0dba79850ce0969886134e9
Without one, a pending button's spinner is a progress bar with no name. The Pending story now says so, and what the label is for. MONOREPO_REV_ID: e3e78ce3f39f06534fe9a0d07ebd41b8ba6e0907
The report found its comment by its marker alone. On a public repo anyone can comment, so a comment starting with the marker would be taken for the report: the results edited into a comment its author could change, and its text repeated by every re-run. Only a comment by github-actions[bot] is now taken. MONOREPO_REV_ID: 6d4f2b0c709fa6e2a97243e252d1f7e956889b1e
MONOREPO_REV_ID: e96f11e2453d673c733e991fe87ca566be762e90
MONOREPO_REV_ID: 08041fddd8c62bbf6eec395b3aaeea201d8653c1
MONOREPO_REV_ID: 963bceff17ccee6fcd7cd82507be68dc62a7233c
…layout MONOREPO_REV_ID: 7b594a476e9ad8701e1998c2e377bc4e908fd69a
…encies react-aria, react-aria-components, react-stately, @internationalized/date and the @react-aria packages must exist once in an app, so they're peers with ranges, pinned in devDependencies for the package's own builds. The font has no dependencies and nothing shares it, so it stays a dependency. Rollup now takes its externals from package.json, and an import of an undeclared package fails the build instead of being left as an import. MONOREPO_REV_ID: 70ec06982ecdb60e092fd38da04221515106a6a1
MONOREPO_REV_ID: 91201056fb376ba4b2d08ef655dc5a7cf772e4d7
MONOREPO_REV_ID: 9bde07d02532881eadb599bab083dbd495ddb9c0
MONOREPO_REV_ID: 9bde07d02532881eadb599bab083dbd495ddb9c0
MONOREPO_REV_ID: 9bde07d02532881eadb599bab083dbd495ddb9c0
|
Terria Bot seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
soyarsauce
approved these changes
Nov 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this PR does
Adds the Terria design system: React components, icons and design tokens, with Storybook and visual tests. TerriaJS doesn't use it yet; we'll migrate gradually behind a feature flag.
Automated Copybara sync. Merge with a normal merge commit.
Packages
@terria/design-systempackages/design-system/componentsprivate(see below)@terria/design-system-iconspackages/design-system/iconsprivate@terria/design-system-storybookpackages/design-system/storybookprivateThe packages are private for now. TerriaJS will carry them inside its own npm package (
bundleDependencies) instead of publishing them separately.Design tokens
The tokens (
packages/design-system/components/src/css/tokens.css) are generated automatically from the Figma variables. Don't edittokens.cssby hand: token changes come from Figma.Dependencies
@internationalized/dateand@react-aria/*are peer dependencies with ranges, so an app has one copy of each. The design system pins them indevDependenciesfor its own builds and tests.@fontsource-variable/inter) is a normal dependency.Using it in an app
All CSS is in cascade layers (
terria-ds.*). Components are styled throughdata-tdsattributes, so an app's ownclassNamenever clashes. Components' own strings (like "Close") are in English, and an app can translate them withLocalizedStringsProvider.Running it
From the repo root:
pnpm storybookpnpm build-storybookpnpm --filter "@terria/design-system*" -r buildpnpm --filter @terria/design-system devpnpm --filter "@terria/design-system*" -r testpnpm --filter "@terria/design-system*" -r typecheck/-r lintThe first test run needs Playwright's browser:
pnpm --filter @terria/design-system exec playwright install chromium.Visual tests
Every component has screenshot tests (
*.visual.tsx) for each state, in both themes.pnpm --filter @terria/design-system test:visual -utakes the references;pnpm --filter @terria/design-system test:visualcompares./terria-bot visual-test. It compares the PR's components with the base branch's and posts the differences on the PR. Comment again after new commits.CI
design-system-ci.yml: formats, lints, type-checks, runs the tests and builds Storybook. It runs on PRs that touch the design system.design-system-visual*.yml: the/terria-bot visual-testcommand, the comparison and the clean-up of its images.design-system-chromatic.ymlis off until we have a Chromatic account; it can only be started by hand.Test me
pnpm installpnpm storybookand open http://localhost:6006. Check a few components in light and dark (toolbar toggle).pnpm --filter "@terria/design-system*" -r build && pnpm --filter "@terria/design-system*" -r testChecklist
doc/.