refactor(ui): migrate to component-owned definitions

Eliminates the central componentDefinitions registry in favor of component-owned
definition files. Each of the 33 components now exports its own definition object.

Changes:
- Created definition.ts for all 33 components (Accordion, Avatar, Box, Button, etc.)
- Updated useStyles hook to accept definition objects instead of string lookups
- Removed central componentDefinitions file and related type utilities
- Updated docs-ui to import and use component definitions directly
- Added @backstage/ui/definitions subpath export to avoid importing component code
  in docs-ui, preventing Next.js SSR issues

Breaking changes:
- componentDefinitions object removed
- ComponentDefinitionName type removed
- ComponentClassNames<T> type removed
- useStyles now accepts ComponentDefinition object instead of string (internal)

Migration:
Component definitions are primarily for documenting the CSS class API for theming.
If using definitions programmatically, migrate to either:
- Use Backstage UI components directly as building blocks, or
- Duplicate component CSS in your own stylesheets

Signed-off-by: Johan Persson <johanopersson@gmail.com>
This commit is contained in:
Johan Persson
2025-11-10 16:35:28 +01:00
parent f3ae275cfd
commit a67670d240
148 changed files with 1915 additions and 1042 deletions
+7 -12
View File
@@ -1,18 +1,13 @@
import { MDXRemote } from 'next-mdx-remote-client/rsc';
import { formattedMDXComponents } from '@/mdx-components';
import { Component } from '@/utils/changelog';
import { componentDefinitions } from '../../../../packages/ui/src/utils/componentDefinitions';
import type { DataAttributeValues } from '../../../../packages/ui/src/types';
import type {
ComponentDefinition,
DataAttributeValues,
} from '../../../../packages/ui/src/types';
export function Theming({ component }: { component: Component }) {
const componentDefinition = componentDefinitions[component];
if (!componentDefinition) {
return null;
}
const classNames = componentDefinition.classNames;
const dataAttributes = componentDefinition.dataAttributes;
export function Theming({ definition }: { definition: ComponentDefinition }) {
const classNames = definition.classNames;
const dataAttributes = definition.dataAttributes;
// Get the first class name
const firstClassName = Object.values(classNames)[0];
+2 -1
View File
@@ -16,6 +16,7 @@ import {
} from './accordion.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { AccordionDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -123,6 +124,6 @@ Here's a view when multiple accordions can be open simultaneously.
code={accordionGroupMultipleOpenSnippet}
/>
<Theming component="Accordion" />
<Theming definition={AccordionDefinition} />
<ChangelogComponent component="accordion" />
+2 -1
View File
@@ -11,6 +11,7 @@ import {
} from './avatar.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { AvatarDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -70,6 +71,6 @@ Control how the avatar is announced to screen readers using the `purpose` prop.
code={snippetPurpose}
/>
<Theming component="Avatar" />
<Theming definition={AvatarDefinition} />
<ChangelogComponent component="avatar" />
+2 -1
View File
@@ -12,6 +12,7 @@ import {
import { spacingPropDefs } from '@/utils/propDefs';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { BoxDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -60,6 +61,6 @@ Here's a view when buttons are responsive.
<CodeBlock code={boxResponsiveSnippet} />
<Theming component="Box" />
<Theming definition={BoxDefinition} />
<ChangelogComponent component="box" />
+2 -1
View File
@@ -15,6 +15,7 @@ import {
} from './button-icon.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ButtonIconDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -93,6 +94,6 @@ Here's a view when buttons are responsive.
<CodeBlock code={buttonIconResponsiveSnippet} />
<Theming component="ButtonIcon" />
<Theming definition={ButtonIconDefinition} />
<ChangelogComponent component="button-icon" />
+2 -1
View File
@@ -13,6 +13,7 @@ import {
} from './button-link.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ButtonLinkDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -91,6 +92,6 @@ Here's a view when buttons are responsive.
<CodeBlock code={buttonLinkResponsiveSnippet} />
<Theming component="ButtonLink" />
<Theming definition={ButtonLinkDefinition} />
<ChangelogComponent component="button-link" />
+2 -1
View File
@@ -16,6 +16,7 @@ import {
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ButtonDefinition } from '../utils/definitions';
<PageTitle
title="Button"
@@ -117,6 +118,6 @@ If you want to use a button as a link, please use the `ButtonLink` component.
code={buttonAsLinkSnippet}
/>
<Theming component="Button" />
<Theming definition={ButtonDefinition} />
<ChangelogComponent component="button" />
+2 -1
View File
@@ -14,6 +14,7 @@ import {
} from './card.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { CardDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -84,6 +85,6 @@ Here's a view when card has a list.
open
/>
<Theming component="Card" />
<Theming definition={CardDefinition} />
<ChangelogComponent component="card" />
+2 -1
View File
@@ -10,6 +10,7 @@ import {
} from './checkbox.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { CheckboxDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -46,6 +47,6 @@ Here's a view when checkboxes have different variants.
code={checkboxVariantsSnippet}
/>
<Theming component="Checkbox" />
<Theming definition={CheckboxDefinition} />
<ChangelogComponent component="checkbox" />
+2 -1
View File
@@ -11,6 +11,7 @@ import {
} from './container.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ContainerDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -48,6 +49,6 @@ create responsive designs.
<CodeBlock code={containerResponsiveSnippet} />
<Theming component="Container" />
<Theming definition={ContainerDefinition} />
<ChangelogComponent component="container" />
+2 -1
View File
@@ -18,6 +18,7 @@ import {
} from './dialog.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { DialogDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -102,6 +103,6 @@ You can also control the dialog using your own states.
<CodeBlock code={dialogWithNoTriggerSnippet} />
<Theming component="Dialog" />
<Theming definition={DialogDefinition} />
<ChangelogComponent component="dialog" />
+2 -1
View File
@@ -14,6 +14,7 @@ import {
import { spacingPropDefs } from '@/utils/propDefs';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { FlexDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -63,6 +64,6 @@ create responsive designs.
<CodeBlock code={flexAlignSnippet} />
<Theming component="Flex" />
<Theming definition={FlexDefinition} />
<ChangelogComponent component="flex" />
+2 -1
View File
@@ -16,6 +16,7 @@ import {
} from './grid.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { GridDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -93,6 +94,6 @@ The start and end props can be used to position the item in the grid.
<CodeBlock code={gridStartEndSnippet} />
<Theming component="Grid" />
<Theming definition={GridDefinition} />
<ChangelogComponent component="grid" />
+2 -1
View File
@@ -14,6 +14,7 @@ import {
} from './header-page.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { HeaderPageDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -82,6 +83,6 @@ prop is an array of objects with a `label`, `value` and `onClick` property.
code={withMenuItems}
/>
<Theming component="HeaderPage" />
<Theming definition={HeaderPageDefinition} />
<ChangelogComponent component="Header" />
+2 -1
View File
@@ -13,6 +13,7 @@ import {
} from './header.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { HeaderDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -80,6 +81,6 @@ You can use the `Header` component inside the [HeaderPage](/components/header-pa
open
/>
<Theming component="Header" />
<Theming definition={HeaderDefinition} />
<ChangelogComponent component="Header" />
+2 -1
View File
@@ -19,6 +19,7 @@ import {
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { LinkDefinition } from '../utils/definitions';
<PageTitle
title="Link"
@@ -97,6 +98,6 @@ The `Link` component has a `truncate` prop that can be used to truncate the text
code={linkTruncateSnippet}
/>
<Theming component="Link" />
<Theming definition={LinkDefinition} />
<ChangelogComponent component="link" />
+2 -1
View File
@@ -27,6 +27,7 @@ import {
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { MenuDefinition } from '../utils/definitions';
<PageTitle
title="Menu"
@@ -231,6 +232,6 @@ allow multiple selection.
code={autocompleteListboxMultiple}
/>
<Theming component="Menu" />
<Theming definition={MenuDefinition} />
<ChangelogComponent component="menu" />
+2 -1
View File
@@ -12,6 +12,7 @@ import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { CodeBlock } from '@/components/CodeBlock';
import { PasswordFieldDefinition } from '../utils/definitions';
<PageTitle
title="PasswordField"
@@ -59,6 +60,6 @@ Here's a simple PasswordField with a description.
code={passwordFieldDescriptionSnippet}
/>
<Theming component="PasswordField" />
<Theming definition={PasswordFieldDefinition} />
<ChangelogComponent component="password-field" />
+2 -1
View File
@@ -16,6 +16,7 @@ import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { CodeBlock } from '@/components/CodeBlock';
import { RadioGroupDefinition } from '../utils/definitions';
<PageTitle
title="RadioGroup"
@@ -99,6 +100,6 @@ You can make the radio group read only by adding the `isReadOnly` prop to the `R
code={radioGroupReadOnlySnippet}
/>
<Theming component="RadioGroup" />
<Theming definition={RadioGroupDefinition} />
<ChangelogComponent component="radio-group" />
+2 -1
View File
@@ -13,6 +13,7 @@ import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { CodeBlock } from '@/components/CodeBlock';
import { SearchFieldDefinition } from '../utils/definitions';
<PageTitle
title="SearchField"
@@ -72,6 +73,6 @@ You can make the SearchField collapsible by setting the `startCollapsed` prop to
code={searchFieldCollapsibleSnippet}
/>
<Theming component="SearchField" />
<Theming definition={SearchFieldDefinition} />
<ChangelogComponent component="search-field" />
+2 -1
View File
@@ -18,6 +18,7 @@ import {
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { SelectDefinition } from '../utils/definitions';
<PageTitle
title="Select"
@@ -132,6 +133,6 @@ Here's a view when the select is responsive.
<CodeBlock code={selectResponsiveSnippet} />
<Theming component="Select" />
<Theming definition={SelectDefinition} />
<ChangelogComponent component="select" />
+2 -1
View File
@@ -12,6 +12,7 @@ import {
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { SkeletonDefinition } from '../utils/definitions';
<PageTitle
title="Skeleton"
@@ -59,6 +60,6 @@ You can use a mix of different sizes to create a more complex skeleton.
open
/>
<Theming component="Skeleton" />
<Theming definition={SkeletonDefinition} />
<ChangelogComponent component="skeleton" />
+2 -1
View File
@@ -6,6 +6,7 @@ import { switchPropDefs, snippetUsage } from './switch.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { SwitchDefinition } from '../utils/definitions';
<PageTitle
title="Switch"
@@ -41,6 +42,6 @@ A switch can be disabled using the `isDisabled` prop.
code={`<Switch isDisabled />`}
/>
<Theming component="Switch" />
<Theming definition={SwitchDefinition} />
<ChangelogComponent component="switch" />
+2 -1
View File
@@ -22,6 +22,7 @@ import {
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { TableDefinition } from '../utils/definitions';
<PageTitle
title="Table"
@@ -128,6 +129,6 @@ This feature is not available yet — let us know if you'd like us to explore it
This feature is not available yet — let us know if you'd like us to explore it!
<Theming component="Table" />
<Theming definition={TableDefinition} />
<ChangelogComponent component="table" />
+2 -1
View File
@@ -13,6 +13,7 @@ import {
} from './tabs.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { TabsDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -79,6 +80,6 @@ You can use the `matchStrategy` prop on the tab to control how the tab is matche
open
/>
<Theming component="Tabs" />
<Theming definition={TabsDefinition} />
<ChangelogComponent component="tabs" />
+2 -1
View File
@@ -15,6 +15,7 @@ import {
} from './tag-group.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { TagGroupDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -109,6 +110,6 @@ A switch can be disabled using the `isDisabled` prop.
code={disabled}
/>
<Theming component="Switch" />
<Theming definition={TagGroupDefinition} />
<ChangelogComponent component="switch" />
+2 -1
View File
@@ -10,6 +10,7 @@ import {
} from './text-field.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { TextFieldDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { CodeBlock } from '@/components/CodeBlock';
@@ -59,6 +60,6 @@ Here's a simple TextField with a description.
code={textFieldDescriptionSnippet}
/>
<Theming component="TextField" />
<Theming definition={TextFieldDefinition} />
<ChangelogComponent component="text-field" />
+2 -1
View File
@@ -14,6 +14,7 @@ import {
} from './text.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { TextDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -87,6 +88,6 @@ on the screen size.
<CodeBlock code={textResponsiveSnippet} />
<Theming component="Text" />
<Theming definition={TextDefinition} />
<ChangelogComponent component="text" />
+2 -1
View File
@@ -10,6 +10,7 @@ import {
} from './tooltip.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { TooltipDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -42,6 +43,6 @@ The tooltip will wrap the content of the tooltip.
<PropsTable data={tooltipPropDefs} />
<Theming component="Tooltip" />
<Theming definition={TooltipDefinition} />
<ChangelogComponent component="tooltip" />
+2 -1
View File
@@ -10,6 +10,7 @@ import {
} from './visually-hidden.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { VisuallyHiddenDefinition } from '../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
@@ -46,6 +47,6 @@ Here's an example of providing screen reader context for a list of links in a fo
open
/>
<Theming component="VisuallyHidden" />
<Theming definition={VisuallyHiddenDefinition} />
<ChangelogComponent component="visually-hidden" />
+1
View File
@@ -0,0 +1 @@
export * from '../../../packages/ui/src/definitions';