From 29cb3993753f011095f8567036e192650a5a7961 Mon Sep 17 00:00:00 2001 From: Nandor_Czegledi Date: Wed, 27 May 2026 18:12:06 +0200 Subject: [PATCH 1/4] docs(many): rework documentation --- docs/theming/legacy-theme-overrides.md | 9 +++++++++ docs/theming/new-theme-overrides.md | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/docs/theming/legacy-theme-overrides.md b/docs/theming/legacy-theme-overrides.md index a163a42961..7a118e0624 100644 --- a/docs/theming/legacy-theme-overrides.md +++ b/docs/theming/legacy-theme-overrides.md @@ -7,6 +7,15 @@ 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 cbd36566a5..671cfaaf55 100644 --- a/docs/theming/new-theme-overrides.md +++ b/docs/theming/new-theme-overrides.md @@ -7,6 +7,15 @@ 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. From 30afc83c65687230f70b48826d6d53c035482e11 Mon Sep 17 00:00:00 2001 From: Nandor_Czegledi Date: Tue, 9 Jun 2026 08:26:25 +0200 Subject: [PATCH 2/4] docs: cache parsed color results to speed up rerenders --- packages/__docs__/src/ColorCard/index.tsx | 109 +++++++++++++--------- 1 file changed, 66 insertions(+), 43 deletions(-) diff --git a/packages/__docs__/src/ColorCard/index.tsx b/packages/__docs__/src/ColorCard/index.tsx index 8046589237..21df88e8a4 100644 --- a/packages/__docs__/src/ColorCard/index.tsx +++ b/packages/__docs__/src/ColorCard/index.tsx @@ -29,58 +29,81 @@ import { View } from '@instructure/ui-view' import { ColorName } from '../ColorName' import type { ColorCardProps } from './props' import { allowedProps } from './props' -class ColorCard extends Component { - static displayName = 'ColorCard' - static allowedProps = allowedProps - static defaultProps = { - minimal: false - } - parseColor(value: string) { - // 6-digit hex: #RRGGBB - const hex6 = /^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(value) - if (hex6) { - return { - hex: value.toUpperCase(), - rgb: `${parseInt(hex6[1], 16)},${parseInt(hex6[2], 16)},${parseInt( - hex6[3], - 16 - )}` - } +type ParsedColor = { + hex?: string + rgb?: string + alpha?: string +} | null + +// Cache parsed results so theme re-renders skip the regex work. +const colorCache = new Map() + +function parseColor(value: string): ParsedColor { + if (colorCache.has(value)) return colorCache.get(value)! + + // 6-digit hex: #RRGGBB + const hex6 = /^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(value) + if (hex6) { + const result: ParsedColor = { + hex: value.toUpperCase(), + rgb: `${parseInt(hex6[1], 16)},${parseInt(hex6[2], 16)},${parseInt( + hex6[3], + 16 + )}` + } + colorCache.set(value, result) + return result + } + // 8-digit hex with alpha: #RRGGBBAA + const hex8 = /^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec( + value + ) + if (hex8) { + const alpha = Math.round((parseInt(hex8[4], 16) / 255) * 100) + const result: ParsedColor = { + hex: value.toUpperCase(), + rgb: `${parseInt(hex8[1], 16)},${parseInt(hex8[2], 16)},${parseInt( + hex8[3], + 16 + )}`, + alpha: `${alpha}%` } - // 8-digit hex with alpha: #RRGGBBAA - const hex8 = /^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec( + colorCache.set(value, result) + return result + } + // rgba(r,g,b,a) + const rgba = + /^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*([\d.]+))?\s*\)/i.exec( value ) - if (hex8) { - const alpha = Math.round((parseInt(hex8[4], 16) / 255) * 100) - return { - hex: value.toUpperCase(), - rgb: `${parseInt(hex8[1], 16)},${parseInt(hex8[2], 16)},${parseInt( - hex8[3], - 16 - )}`, - alpha: `${alpha}%` - } - } - // rgba(r,g,b,a) - const rgba = - /^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)(?:\s*,\s*([\d.]+))?\s*\)/i.exec( - value - ) - if (rgba) { - const alpha = - rgba[4] !== undefined - ? `${Math.round(parseFloat(rgba[4]) * 100)}%` - : undefined - return { rgb: `${rgba[1]},${rgba[2]},${rgba[3]}`, alpha } + if (rgba) { + const alpha = + rgba[4] !== undefined + ? `${Math.round(parseFloat(rgba[4]) * 100)}%` + : undefined + const result: ParsedColor = { + rgb: `${rgba[1]},${rgba[2]},${rgba[3]}`, + alpha } - return null + colorCache.set(value, result) + return result + } + + colorCache.set(value, null) + return null +} + +class ColorCard extends Component { + static displayName = 'ColorCard' + static allowedProps = allowedProps + static defaultProps = { + minimal: false } render() { const { name, hex, minimal } = this.props - const parsed = this.parseColor(hex) + const parsed = parseColor(hex) return ( Date: Tue, 9 Jun 2026 09:21:01 +0200 Subject: [PATCH 3/4] docs: use css for truncation instead of the TruncateText in ColorCards --- packages/__docs__/src/ColorName/index.tsx | 38 ++++++++++++++++---- packages/__docs__/src/ColorName/props.ts | 7 ++-- packages/__docs__/src/ColorName/styles.ts | 42 +++++++++++++++++++++++ 3 files changed, 79 insertions(+), 8 deletions(-) create mode 100644 packages/__docs__/src/ColorName/styles.ts diff --git a/packages/__docs__/src/ColorName/index.tsx b/packages/__docs__/src/ColorName/index.tsx index 3b53350065..bc517ac1c6 100644 --- a/packages/__docs__/src/ColorName/index.tsx +++ b/packages/__docs__/src/ColorName/index.tsx @@ -25,11 +25,17 @@ import { Component } from 'react' import { Text } from '@instructure/ui-text' -import { TruncateText } from '@instructure/ui-truncate-text' import { Tooltip } from '@instructure/ui-tooltip' import { ScreenReaderContent } from '@instructure/ui-a11y-content' -import type { ColorNameProps, ColorNameState } from './props' + +import { withStyleForDocs } from '../withStyleForDocs' +import generateStyle from './styles' + import { allowedProps } from './props' +import type { ColorNameProps, ColorNameState } from './props' + +// CSS truncation instead of heavy TruncateText +@withStyleForDocs(generateStyle, () => ({})) class ColorName extends Component { static displayName = 'ColorName' static allowedProps = allowedProps @@ -41,18 +47,38 @@ class ColorName extends Component { isTruncated: false } - handleUpdate = (isTruncated: boolean) => { + private strongRef: HTMLElement | null = null + + componentDidMount() { + this.props.makeStyles?.() + this.checkTruncation() + } + + componentDidUpdate(prevProps: ColorNameProps) { + this.props.makeStyles?.() + if (prevProps.name !== this.props.name) { + this.checkTruncation() + } + } + + checkTruncation = () => { + if (!this.strongRef) return + const isTruncated = this.strongRef.scrollWidth > this.strongRef.clientWidth if (this.state.isTruncated !== isTruncated) { this.setState({ isTruncated }) } } + handleStrongRef = (el: HTMLElement | null) => { + this.strongRef = el + } + renderText() { - const { name, ...passthrough } = this.props + const { name, styles, makeStyles, ...passthrough } = this.props return ( - - {name} + + {name} {name} diff --git a/packages/__docs__/src/ColorName/props.ts b/packages/__docs__/src/ColorName/props.ts index ba0ad39fab..4ba761c5f7 100644 --- a/packages/__docs__/src/ColorName/props.ts +++ b/packages/__docs__/src/ColorName/props.ts @@ -23,6 +23,8 @@ */ import type { AsElementType } from '@instructure/shared-types' +import type { ComponentStyle, WithStyleProps } from '@instructure/emotion' + type ColorNameOwnProps = { name: string as?: AsElementType @@ -30,11 +32,12 @@ type ColorNameOwnProps = { } type PropKeys = keyof ColorNameOwnProps type AllowedPropKeys = Readonly> -type ColorNameProps = ColorNameOwnProps +type ColorNameProps = ColorNameOwnProps & WithStyleProps const allowedProps: AllowedPropKeys = ['name', 'as', 'lineHeight'] type ColorNameState = { isTruncated: boolean } +type ColorNameStyle = ComponentStyle<'truncate'> export { allowedProps } -export type { ColorNameState, ColorNameProps } +export type { ColorNameState, ColorNameProps, ColorNameStyle } diff --git a/packages/__docs__/src/ColorName/styles.ts b/packages/__docs__/src/ColorName/styles.ts new file mode 100644 index 0000000000..1073181e41 --- /dev/null +++ b/packages/__docs__/src/ColorName/styles.ts @@ -0,0 +1,42 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2015 - present Instructure, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +import type { ColorNameStyle } from './props' + +/** + * Generates the style object from the theme and provided additional information + * @return {Object} The final style object, which will be used in the component + */ +const generateStyle = (): ColorNameStyle => { + return { + truncate: { + label: 'colorName__truncate', + display: 'block', + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'nowrap' + } + } +} + +export default generateStyle From 156b8cac649166036a1123dd023056e243433bcc Mon Sep 17 00:00:00 2001 From: Nandor_Czegledi Date: Fri, 12 Jun 2026 10:35:45 +0200 Subject: [PATCH 4/4] docs: move transpile-diff into contributing group --- docs/contributing/transpile-diff.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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