@@ -601,6 +601,268 @@ type: example
601601</InstUISettingsProvider>
602602```
603603
604+ ### 15. Branding (user customizable theming)
605+
606+ The new theming system exposes the equivalents of the legacy Canvas `ic-brand-*` variables under `semantics.color.institutional.*`. Overriding these has the same broad effect as Canvas' s [Theme Editor](https: // community.canvaslms.com/t5/Admin-Guide/How-do-I-create-a-theme-for-an-account-using-the-Theme-Editor/ta-p/242) — the Button family, `SideNavBar`, `Link`, `Billboard`, and several other components consume them automatically.
607+
608+ | Legacy variable | New semantic token |
609+ | -------------------------------------------------- - | -------------------------------------- - |
610+ | ` ic-brand-primary` | ` brandPrimary` |
611+ | ` ic-brand-font-color-dark` | ` brandFontColorDark` |
612+ | ` ic-link-color` | ` linkColor` |
613+ | ` ic-brand-button--primary-bgd` | ` brandButtonPrimaryBgd` |
614+ | ` ic-brand-button--primary-text` | ` brandButtonPrimaryText` |
615+ | ` ic-brand-global-nav-bgd` | ` brandGlobalNavBgd` |
616+ | ` ic-global-nav-link-hover` | ` globalNavLinkHover` |
617+ | ` ic-brand-global-nav-menu-item__text-color` | ` brandGlobalNavMenuItemTextColor` |
618+ | ` ic-brand-global-nav-menu-item__text-color--active` | ` brandGlobalNavMenuItemTextColorActive` |
619+
620+ A handful of legacy variables are not in the new system , for two different reasons:
621+
622+ - ** SVG icon- fill variables** (` ic-brand-global-nav-ic-icon-svg-fill` and ` --active` ) — these _were_ consumed by the ` v11.6` to recolor its icons, but ` v11.7` uses a different icon- coloring mechanism so they were not migrated.
623+ - ** Variables that no InstUI component ever consumed** (` ic-brand-button--secondary-bgd` , ` ic-brand-button--secondary-text` , ` ic-link-decoration` )
624+
625+ #### Common variables — broad- impact branding
626+
627+ ` brandPrimary` is the main brand color hook — consumed by the Tabs active indicator, ` Badge` primary color, ` RangeInput` handle, ` TableRow` hover border, and several other accent colors. ` brandFontColorDark` is the default dark text color used across many components.
628+
629+ > ** Heads up: ** unlike the legacy ` ic-brand-primary` , in the new system ` brandPrimary` ** no longer drives** the secondary ` Button` border or the ` TextInput` focus ring . Focus rings are now centrally controlled via ` sharedTokens.focusOutline` — see the [Overriding shared tokens](#new - theme- overrides) section above to customize them.
630+
631+ ` ` ` ts
632+ ---
633+ type: example
634+ ---
635+ const Example = () => {
636+ const [brandPrimary, setBrandPrimary] = useState(canvas['ic-brand-primary'])
637+ const [brandFontColorDark, setBrandFontColorDark] = useState(canvas['ic-brand-font-color-dark'])
638+
639+ return (
640+ <div>
641+ <Flex gap="small">
642+ <Flex.Item size="45%">
643+ <TextInput
644+ renderLabel="brandPrimary"
645+ value={brandPrimary}
646+ onChange={(e, v) => setBrandPrimary(v)}
647+ messages={[{text:'Tabs indicator, Badge, RangeInput handle, etc.', type:'hint'}]}
648+ />
649+ </Flex.Item>
650+ <Flex.Item size="45%">
651+ <TextInput
652+ renderLabel="brandFontColorDark"
653+ value={brandFontColorDark}
654+ onChange={(e, v) => setBrandFontColorDark(v)}
655+ messages={[{text:'default dark text color in many components', type:'hint'}]}
656+ />
657+ </Flex.Item>
658+ </Flex>
659+ <hr style={{width:'100%', margin:'2rem 0 1rem'}}/>
660+ <InstUISettingsProvider theme={canvas}>
661+ <InstUISettingsProvider
662+ themeOverride={{
663+ semantics: {
664+ color: {
665+ institutional: { brandPrimary, brandFontColorDark }
666+ }
667+ }
668+ }}
669+ >
670+ <Tabs>
671+ <Tabs.Panel id="tabA" renderTitle="Tab A" isSelected={true}></Tabs.Panel>
672+ <Tabs.Panel id="tabB" renderTitle="Tab B"></Tabs.Panel>
673+ <Tabs.Panel id="tabC" renderTitle="Tab C"></Tabs.Panel>
674+ </Tabs>
675+ <Flex gap="medium" margin="medium 0 0 0">
676+ <Flex.Item>
677+ <Badge count={42} countUntil={100} margin="0 medium 0 0">
678+ <Button color="secondary">Notifications</Button>
679+ </Badge>
680+ </Flex.Item>
681+ <Flex.Item shouldGrow>
682+ <Text>Default body text — uses brandFontColorDark.</Text>
683+ </Flex.Item>
684+ </Flex>
685+ </InstUISettingsProvider>
686+ </InstUISettingsProvider>
687+ </div>
688+ )
689+ }
690+
691+ render(<Example/>)
692+ ` ` `
693+
694+ #### ` Button` branding
695+
696+ These semantics only affect the ` primary` color ` Button` .
697+
698+ ` ` ` ts
699+ ---
700+ type: example
701+ ---
702+ const Example = () => {
703+ const [brandButtonPrimaryBgd, setBrandButtonPrimaryBgd] = useState(canvas['ic-brand-button--primary-bgd'])
704+ const [brandButtonPrimaryText, setBrandButtonPrimaryText] = useState(canvas['ic-brand-button--primary-text'])
705+
706+ return (
707+ <div>
708+ <Flex gap="small">
709+ <Flex.Item size="45%">
710+ <TextInput
711+ renderLabel="brandButtonPrimaryBgd"
712+ value={brandButtonPrimaryBgd}
713+ onChange={(e, v) => setBrandButtonPrimaryBgd(v)}
714+ messages={[{text:"primary Button background", type:'hint'}]}
715+ />
716+ </Flex.Item>
717+ <Flex.Item size="45%">
718+ <TextInput
719+ renderLabel="brandButtonPrimaryText"
720+ value={brandButtonPrimaryText}
721+ onChange={(e, v) => setBrandButtonPrimaryText(v)}
722+ messages={[{text:"primary Button text color", type:'hint'}]}
723+ />
724+ </Flex.Item>
725+ </Flex>
726+ <hr style={{width:'100%', margin:'2rem 0 1rem'}}/>
727+ <InstUISettingsProvider theme={canvas}>
728+ <InstUISettingsProvider
729+ themeOverride={{
730+ semantics: {
731+ color: {
732+ institutional: { brandButtonPrimaryBgd, brandButtonPrimaryText }
733+ }
734+ }
735+ }}
736+ >
737+ <Button color="primary">I'm a 'primary' color button</Button>
738+ </InstUISettingsProvider>
739+ </InstUISettingsProvider>
740+ </div>
741+ )
742+ }
743+
744+ render(<Example/>)
745+ ` ` `
746+
747+ #### ` Link` and ` Billboard` branding
748+
749+ ` linkColor` is used by non- inverse ` Link` and by clickable ` Billboard` .
750+
751+ ` ` ` ts
752+ ---
753+ type: example
754+ ---
755+ const Example = () => {
756+ const [linkColor, setLinkColor] = useState(canvas['ic-link-color'])
757+
758+ return (
759+ <div>
760+ <Flex gap="small">
761+ <Flex.Item size="60%">
762+ <TextInput
763+ renderLabel="linkColor"
764+ value={linkColor}
765+ onChange={(e, v) => setLinkColor(v)}
766+ messages={[{text:'used by non-inverse Link and clickable Billboard', type:'hint'}]}
767+ />
768+ </Flex.Item>
769+ </Flex>
770+ <hr style={{width:'100%', margin:'2rem 0 1rem'}}/>
771+ <InstUISettingsProvider theme={canvas}>
772+ <InstUISettingsProvider
773+ themeOverride={{
774+ semantics: {
775+ color: {
776+ institutional: { linkColor }
777+ }
778+ }
779+ }}
780+ >
781+ <Flex gap="small">
782+ <Flex.Item size="50%">
783+ <Link href="https://instructure.github.io/instructure-ui/">normal link</Link>
784+ </Flex.Item>
785+ <Flex.Item size="50%">
786+ <Billboard
787+ margin="small"
788+ message="Billboard with link"
789+ href="http://instructure.com"
790+ hero={(size) => <IconGradebookLine size={size} />}
791+ />
792+ </Flex.Item>
793+ </Flex>
794+ </InstUISettingsProvider>
795+ </InstUISettingsProvider>
796+ </div>
797+ )
798+ }
799+
800+ render(<Example/>)
801+ ` ` `
802+
803+ #### ` SideNavBar` branding
804+
805+ These semantics control the ` SideNavBar` background, hover state, and menu item text colors — for both default and active states.
806+
807+ ` ` ` ts
808+ ---
809+ type: example
810+ ---
811+ const Example = () => {
812+ const [brandGlobalNavBgd, setBrandGlobalNavBgd] = useState(canvas['ic-brand-global-nav-bgd'])
813+ const [globalNavLinkHover, setGlobalNavLinkHover] = useState(canvas['ic-global-nav-link-hover'])
814+ const [brandGlobalNavMenuItemTextColor, setBrandGlobalNavMenuItemTextColor] = useState(canvas['ic-brand-global-nav-menu-item__text-color'])
815+ const [brandGlobalNavMenuItemTextColorActive, setBrandGlobalNavMenuItemTextColorActive] = useState(canvas['ic-brand-global-nav-menu-item__text-color--active'])
816+
817+ return (
818+ <div>
819+ <Flex gap="small">
820+ <Flex.Item size="45%">
821+ <TextInput renderLabel="brandGlobalNavBgd" value={brandGlobalNavBgd} onChange={(e, v) => setBrandGlobalNavBgd(v)}/>
822+ <TextInput renderLabel="globalNavLinkHover" value={globalNavLinkHover} onChange={(e, v) => setGlobalNavLinkHover(v)}/>
823+ </Flex.Item>
824+ <Flex.Item size="45%">
825+ <TextInput renderLabel="brandGlobalNavMenuItemTextColor" value={brandGlobalNavMenuItemTextColor} onChange={(e, v) => setBrandGlobalNavMenuItemTextColor(v)}/>
826+ <TextInput renderLabel="brandGlobalNavMenuItemTextColorActive" value={brandGlobalNavMenuItemTextColorActive} onChange={(e, v) => setBrandGlobalNavMenuItemTextColorActive(v)}/>
827+ </Flex.Item>
828+ </Flex>
829+ <hr style={{width:'100%', margin:'2rem 0 1rem'}}/>
830+ <InstUISettingsProvider theme={canvas}>
831+ <InstUISettingsProvider
832+ themeOverride={{
833+ semantics: {
834+ color: {
835+ institutional: {
836+ brandGlobalNavBgd,
837+ globalNavLinkHover,
838+ brandGlobalNavMenuItemTextColor,
839+ brandGlobalNavMenuItemTextColorActive
840+ }
841+ }
842+ }
843+ }}
844+ >
845+ <SideNavBar
846+ label="Main navigation"
847+ toggleLabel={{
848+ expandedLabel: 'Minimize SideNavBar',
849+ minimizedLabel: 'Expand SideNavBar'
850+ }}
851+ >
852+ <SideNavBar.Item icon={<IconUserLine />} label="Home" href="#" />
853+ <SideNavBar.Item icon={<IconAdminLine />} label="Admin" href="#" />
854+ <SideNavBar.Item selected icon={<IconDashboardLine />} label="Dashboard" href="#" />
855+ <SideNavBar.Item icon={<IconInboxLine />} label="Inbox" href="#" />
856+ </SideNavBar>
857+ </InstUISettingsProvider>
858+ </InstUISettingsProvider>
859+ </div>
860+ )
861+ }
862+
863+ render(<Example/>)
864+ ` ` `
865+
604866### Override priority (highest to lowest)
605867
6068681. ** Per- component ` themeOverride` prop** - affects a single instance
0 commit comments