From 2cbd8e4a44ef05807cfbab78752034e25cd59f49 Mon Sep 17 00:00:00 2001 From: Nandor_Czegledi Date: Wed, 27 May 2026 18:12:06 +0200 Subject: [PATCH 1/8] docs(many): rework documentation --- packages/__docs__/src/withStyleForDocs.tsx | 53 ++++++++++++++++++++-- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/packages/__docs__/src/withStyleForDocs.tsx b/packages/__docs__/src/withStyleForDocs.tsx index 9a955aad4d..e3b1ebe0b0 100644 --- a/packages/__docs__/src/withStyleForDocs.tsx +++ b/packages/__docs__/src/withStyleForDocs.tsx @@ -132,9 +132,56 @@ const defaultValues = { * * @module withStyleForDocs * - * @param {function} generateStyle - Returns the component's style object - * @param {function} generateComponentTheme - Returns the component's theme variables object - * @returns {ReactElement} The decorated component + * A themeable component’s theme can be configured via wrapping it in an + * [InstUISettingsProvider](InstUISettingsProvider) component, and/or set + * explicitly via its `themeOverride` prop. + * + * InstUISettingsProvider provides a theme object (e.g. the [canvas theme](/#canvas)). + * These variables are mapped to the component's own variables in `theme.js`. + * + * With the `themeOverride` prop you can directly set/override the component theme variables declared in theme.js. It accepts an object or a function. The function has the component's theme and the currently active main theme as its parameter. + * + * See more about the overrides on the [Legacy theme overrides](/#legacy-theme-overrides) docs page. + * + * ```js-code + * // ExampleComponent/theme.js + * const generateComponentTheme = (theme) => { + * const { colors } = theme + * + * const componentVariables = { + * background: colors?.backgroundMedium, + * color: colors?.textDarkest, + * + * hoverColor: colors?.textLightest, + * hoverBackground: colors?.backgroundDarkest + * } + * + * return componentVariables + * } + * export default generateComponentTheme + * ``` + * + * ```jsx-code + * {// global theme override} + * + * {// component theme override} + * + * + * {// component theme override with function} + * ({ + * hoverBackground: componentTheme.background, + * activeBackground: currentTheme.colors.backgroundBrand + * })} /> + * + * ``` + * + * @module withStyleNew + * + * @param {function} generateStyle - The function that returns the component's style object + * @param {function} generateComponentTheme - The function that returns the component's theme variables object + * @returns {ReactElement} The decorated WithStyle Component */ const withStyleForDocs = decorator( ( From e02cf76079edeae9a0e1d071e6b881b835bd336c Mon Sep 17 00:00:00 2001 From: Nandor_Czegledi Date: Wed, 3 Jun 2026 10:39:56 +0200 Subject: [PATCH 2/8] docs: improve multi-version docs behavior and routing and add new Component versioning guide page --- docs/guides/accessing-the-dom.md | 2 +- docs/guides/component-versioning.md | 95 +++++++++++++++++++++ docs/guides/forms.md | 2 +- docs/guides/module-federation.md | 2 +- docs/theming/legacy-theme-overrides.md | 9 -- docs/theming/new-theme-overrides.md | 56 +++++++++--- packages/__docs__/src/App/index.tsx | 103 +++++++++++++++++++++-- packages/__docs__/src/Document/index.tsx | 35 ++++---- packages/__docs__/src/Header/index.tsx | 80 +----------------- packages/__docs__/src/Header/props.ts | 6 +- 10 files changed, 263 insertions(+), 127 deletions(-) create mode 100644 docs/guides/component-versioning.md diff --git a/docs/guides/accessing-the-dom.md b/docs/guides/accessing-the-dom.md index 28fdad1049..1a942ebaa9 100644 --- a/docs/guides/accessing-the-dom.md +++ b/docs/guides/accessing-the-dom.md @@ -1,7 +1,7 @@ --- title: Accessing the DOM category: Guides -order: 3 +order: 4 relevantForAI: true --- diff --git a/docs/guides/component-versioning.md b/docs/guides/component-versioning.md new file mode 100644 index 0000000000..872be858ca --- /dev/null +++ b/docs/guides/component-versioning.md @@ -0,0 +1,95 @@ +--- +title: Component versioning +category: Guides +order: 2 +--- + +## Why components are versioned + +When InstUI needs to make a breaking change to a component (renamed props, changed behaviour, etc.), the old version is **kept alongside the new one** instead of being replaced. Upgrading the library no longer forces you to immediately rewrite every component usage — you can migrate to new versions on your own schedule. + +New component versions appear with InstUI **minor** version bumps (e.g. `11.7` → `11.8`). Patch releases never include breaking changes. + +> The docs UI "Component version" selector labels library `v11_6` as **`v1 (legacy)`** and library `v11_7` as **`v2`**. This page uses the `v11_X` form because it matches the actual NPM import paths. + +## Import paths + +Every InstUI component package supports three import styles: + +### Default — `@instructure/ui-` + +Always points to the **oldest** still-supported component version. Upgrading the library without changing your imports will keep your code working without surprises. + +```js +--- +type: code +--- +import { Alert } from '@instructure/ui-alerts' +``` + +### Pinned — `@instructure/ui-/v11_X` + +Locks the import to a specific InstUI minor version of the component. When you are ready to adopt a breaking change, update the path to the next pinned version. + +```js +--- +type: code +--- +import { Alert } from '@instructure/ui-alerts/v11_7' +``` + +### Latest — `@instructure/ui-/latest` + +Always points to the newest component version. This may bring breaking changes when you upgrade InstUI itself. + +```js +--- +type: code +--- +import { Alert } from '@instructure/ui-alerts/latest' +``` + +### Per-package or umbrella package + +InstUI also ships an umbrella package, `@instructure/ui`, which re-exports every component from the individual `@instructure/ui-*` packages. Two equivalent import styles work — both resolve to the same component: + +```js +--- +type: code +--- +// per-package import +import { Alert } from '@instructure/ui-alerts/v11_7' + +// umbrella package import +import { Alert } from '@instructure/ui/v11_7' +``` + +The same three path styles (default / `/v11_X` / `/latest`) work on the umbrella package as well. Pick per-package imports when you want better tree-shaking and only pull in what you use, or the umbrella package when you'd rather depend on a single `@instructure/ui` entry in your `package.json`. + +## Versions and theming engines + +InstUI is in the middle of a transition between two theming systems. Which engine a component uses depends on which version you import: + +- **`v1`** (library `v11_6` and earlier) — legacy theming. Components are configured through the Canvas theme variables and the `themeOverride` prop, which accepts a function or object that maps to the component's own theme map. See the [Legacy theme overrides](legacy-theme-overrides) guide. + +- **`v2`** (library `v11_7` and newer) — new theming system. Components consume pre-resolved design tokens, and theming is done through the new token override structure. See the [New theme overrides](new-theme-overrides) guide. + +Mixing imports from both groups in the same app is fully supported — the two engines run side-by-side without conflict. + +### Supported themes per version + +Each version of the library is only compatible with the themes built for its theming engine: + +- **`v1`** (library `v11_6` and earlier) supports the legacy themes: + + - `canvas` + - `canvasHighContrast` + +- **`v2`** (library `v11_7` and newer) supports themes built on the new token system. The legacy Canvas looks are preserved (re-implemented on the new engine) and two brand-new themes are added: + + - `legacyCanvas` — same visual style as the old `canvas`, but on the new engine + - `legacyCanvasHighContrast` — same visual style as the old `canvasHighContrast`, on the new engine + - `light` — new light theme + - `dark` — new dark theme + +This means that when you move a component import from `/v11_6` to `/v11_7` you don't have to change anything visually — you can stay on the `legacyCanvas` theme and opt in to `light` or `dark` only when you're ready. diff --git a/docs/guides/forms.md b/docs/guides/forms.md index 20ba36b808..83f4c4855e 100644 --- a/docs/guides/forms.md +++ b/docs/guides/forms.md @@ -1,7 +1,7 @@ --- title: Forms category: Guides -order: 4 +order: 5 relevantForAI: true --- diff --git a/docs/guides/module-federation.md b/docs/guides/module-federation.md index 9ca53786c1..efea854e4f 100644 --- a/docs/guides/module-federation.md +++ b/docs/guides/module-federation.md @@ -1,7 +1,7 @@ --- title: Module federation category: Guides -order: 2 +order: 3 relevantForAI: true --- diff --git a/docs/theming/legacy-theme-overrides.md b/docs/theming/legacy-theme-overrides.md index 7a118e0624..a163a42961 100644 --- a/docs/theming/legacy-theme-overrides.md +++ b/docs/theming/legacy-theme-overrides.md @@ -7,15 +7,6 @@ relevantForAI: true ## Using theme overrides -```js ---- -type: embed ---- - - The examples on this page use the legacy theming system and are designed for v11.6 components. If you are viewing the v11.7 version, switch to v11.6 to see the examples working correctly. - -``` - This document gives an overview on how you can customize Instructure UI components by tweaking their theme variables. While this gives you a level of flexibility on the look and feel of the components you should be aware of 2 things: diff --git a/docs/theming/new-theme-overrides.md b/docs/theming/new-theme-overrides.md index 5fcdc279e2..e3d4c5cd76 100644 --- a/docs/theming/new-theme-overrides.md +++ b/docs/theming/new-theme-overrides.md @@ -7,15 +7,6 @@ relevantForAI: true ## New Theme Override Patterns -```js ---- -type: embed ---- - - The examples on this page use the new theming system and require v11.7+ components. If you are viewing the v11.6 version, switch to v11.7 to see the examples working correctly. - -``` - This guide covers all the override patterns available in the new theming system (v11.7+). The new system uses a layered token architecture: **primitives** (raw values) -> **semantics** (meaning) -> **components** (per-component tokens). Overrides are applied via the `themeOverride` prop on `InstUISettingsProvider`, which is separate from the `theme` prop. The `theme` prop replaces the active theme entirely; `themeOverride` layers modifications on top. @@ -527,9 +518,52 @@ type: example ``` -### 13. Provider-level overrides cannot target a child component selectively +### 13. Independent overrides for child parts of compound components + +Most compound components expose each part as a separate component with its own `componentId`. This means you can independently override each part via `components` — both overrides take effect: + +```js +--- +type: example +--- + + + + + + TableColHeader — purple + TableColHeader — purple + + + + + TableRowHeader — deeppink + TableCell — unchanged + + + TableRowHeader — deeppink + TableCell — unchanged + + +
+
+
+``` -Because `Button` uses `BaseButton`'s theme internally, a `components.Button` entry in the provider's `themeOverride` does **not** override `BaseButton`'s tokens for `Button` instances only. Both `BaseButton` and `Button` share the same `BaseButton` theme variables, so a `components.BaseButton` override affects both, regardless of whether a separate `components.Button` entry is also present. +**Exception — `Button` and `BaseButton`:** `Button` uses `BaseButton`'s `componentId` internally, so `components.Button` has no effect. A `components.BaseButton` override affects all `BaseButton` instances including those rendered inside `Button` — there is no way to target only one: ```js --- diff --git a/packages/__docs__/src/App/index.tsx b/packages/__docs__/src/App/index.tsx index 4a50793ded..6a9452bb8b 100644 --- a/packages/__docs__/src/App/index.tsx +++ b/packages/__docs__/src/App/index.tsx @@ -303,9 +303,10 @@ class App extends Component { fetchMinorVersionData(signal) .then((minorVersionsData) => { if (minorVersionsData && minorVersionsData.libraryVersions.length > 0) { - // If URL has a version, use it; otherwise use default - const selectedMinorVersion = - urlMinorVersion ?? minorVersionsData.defaultVersion + const { libraryVersions } = minorVersionsData + const latestVersion = libraryVersions[libraryVersions.length - 1] + // If URL has a version, use it; otherwise use the latest version + const selectedMinorVersion = urlMinorVersion ?? latestVersion // Update globals before fetching docs so renders use correct components updateGlobalsForVersion(selectedMinorVersion) this.setState({ @@ -343,6 +344,48 @@ class App extends Component { ) { this.handleNavigationFocusRegion() } + + if (prevState.key !== this.state.key) { + this.handleMinorVersionForPage(this.state.key) + } + + if (!prevState.minorVersionsData && this.state.minorVersionsData) { + this.handleMinorVersionForPage(this.state.key) + } + } + + getLatestMinorVersion = () => { + const { minorVersionsData } = this.state + if (!minorVersionsData) return undefined + const { libraryVersions } = minorVersionsData + return libraryVersions[libraryVersions.length - 1] + } + + handleMinorVersionForPage = (key: string | undefined) => { + const { selectedMinorVersion, minorVersionsData, docsData } = this.state + if (!minorVersionsData || !selectedMinorVersion || !key) return + + const latestVersion = this.getLatestMinorVersion()! + + if (key === 'legacy-theme-overrides') { + if (selectedMinorVersion !== 'v11_6') { + this.handleMinorVersionChange('v11_6') + } + } else if (key === 'new-theme-overrides') { + if (selectedMinorVersion !== latestVersion) { + this.handleMinorVersionChange(latestVersion) + } + } else { + const category = docsData?.docs[key]?.category + const isGuidePage = + !!category && + !category.startsWith('components') && + !category.startsWith('utilities') + if (isGuidePage && selectedMinorVersion !== latestVersion) { + // Guide pages (.md) always show with the latest version + this.handleMinorVersionChange(latestVersion) + } + } } componentWillUnmount() { @@ -554,8 +597,9 @@ class App extends Component { } renderThemeSelect() { + const { minorVersionsData, selectedMinorVersion } = this.state const allThemeKeys = Object.keys(this.state.docsData!.themes) - const showNewThemes = this.state.selectedMinorVersion !== 'v11_6' + const showNewThemes = selectedMinorVersion !== 'v11_6' const themeKeys = showNewThemes ? allThemeKeys.filter( @@ -578,17 +622,56 @@ class App extends Component { return themeKey } + const formatMinorVersion = (version: string) => { + if (version === 'v11_6') return 'v1 (legacy)' + if (version === 'v11_7') return 'v2' + return version.replace(/_/g, '.') + } + const smallScreen = this.state.layout === 'small' const currentThemeKey = this.state.themeKey && themeKeys.includes(this.state.themeKey) ? this.state.themeKey : themeKeys[0] + const { key, docsData } = this.state + const versionLockedPages = ['legacy-theme-overrides', 'new-theme-overrides'] + if (versionLockedPages.includes(key ?? '')) return null + + // Only show on Components pages — other categories (guides, utilities, + // themes, etc.) don't need theme or component-version switching. + const category = key ? docsData?.docs[key]?.category : undefined + if (!category || !category.startsWith('components')) return null + + const showMinorVersionSelect = + minorVersionsData && minorVersionsData.libraryVersions.length > 1 + return themeKeys.length > 1 ? ( + {showMinorVersionSelect && ( + + + + )}