diff --git a/docs/contributing/transpile-diff.md b/docs/contributing/transpile-diff.md index bd735c6521..ebe5721fee 100644 --- a/docs/contributing/transpile-diff.md +++ b/docs/contributing/transpile-diff.md @@ -1,6 +1,7 @@ --- title: Comparing Transpiled Output -category: Contributor Guides +category: Contributing +order: 8 --- # Comparing Transpiled Output 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..7b3ff6da4a --- /dev/null +++ b/docs/guides/component-versioning.md @@ -0,0 +1,108 @@ +--- +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. + +## 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: + +- **`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. + +- **`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 + +You import themes the same way as before — from `@instructure/ui-themes` — and pass them to `InstUISettingsProvider`: + +```js +--- +type: code +--- +import { canvas } from '@instructure/ui-themes' + + + + +``` + +The `canvas` (and `canvasHighContrast`) export works for **both `v11_6` and `v11_7+` components in the same app** — internally it carries the data each engine needs. You don't need to switch theme objects when you bump a component import to `/v11_7`. + +- **`v11_6` and earlier** — supports the original two themes: + + - `canvas` — default theme used by Canvas products + - `canvasHighContrast` — same as `canvas`, with colors WCAG-tuned for high-contrast accessibility + +- **`v11_7` and newer** — supports the same two themes (rendered through the new engine, labelled `(legacy)` in the docs UI Theme selector) plus two brand-new ones: + + - `canvas` — same import as above, now driven by the new engine (labelled as `(legacy)`) + - `canvasHighContrast` — same import as above, now driven by the new engine (labelled as `(legacy)`) + - `light` — new light theme + - `dark` — new dark theme + +This means that when you move a component import from `/v11_6` to `/v11_7`, you can continue using the `canvas` theme to maintain a familiar look and feel, and opt in to `light` or `dark` only when you're ready. + +> The `@instructure/ui-themes` package also exports `legacyCanvas` and `legacyCanvasHighContrast`. These are the raw new-engine forms that `canvas` / `canvasHighContrast` wrap internally — most consumers don't need to import them directly. 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..cbd36566a5 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 +--- + + + + + + Row headers column - purple + Cells column - 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..56d300cfbd 100644 --- a/packages/__docs__/src/App/index.tsx +++ b/packages/__docs__/src/App/index.tsx @@ -70,6 +70,7 @@ import { } from '../versionData' import { parseCurrentUrl, + navigateTo, navigateToVersion, getDeployBase, MINOR_VERSION_REGEX @@ -303,9 +304,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 +345,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 +598,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 +623,56 @@ class App extends Component { return themeKey } + const formatMinorVersion = (version: string) => { + const formatted = version.replace(/_/g, '.') + if (version === 'v11_6') return `${formatted} (legacy theming)` + return formatted + } + 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 && ( + + + + )}