Merge pull request #31471 from backstage/bui-improve-install-docs

BUI Docs - Improve installation docs
This commit is contained in:
Charles de Dreuille
2025-10-20 09:51:37 +01:00
committed by GitHub
18 changed files with 184 additions and 244 deletions
+2 -1
View File
@@ -33,10 +33,11 @@
"prop-types": "^15.8.1",
"react": "19.1.1",
"react-dom": "19.1.1",
"shiki": "^1.26.1",
"shiki": "^3.13.0",
"storybook": "^8.6.12"
},
"devDependencies": {
"@shikijs/transformers": "^3.13.0",
"@types/mdx": "^2.0.13",
"@types/node": "^20",
"@types/react": "19.1.9",
-24
View File
@@ -1,24 +0,0 @@
import { CodeBlock } from '@/components/CodeBlock';
import { IconLibrary } from '@/components/IconLibrary';
# Iconography
All our default icons are provided by [Remix Icon](https://remixicon.com/). We
don't import all icons to reduce the bundle size but we cherry pick a nice
selection for you to use in your application. The list of names is set down
below. To use an icon, you can use the `Icon` component and pass the name of
the icon you want to use.
<CodeBlock code={`<Icon name="heart" />`} />
## Icon overrides
You can override any icons in our library by using the `IconProvider` at the root of your application.
<CodeBlock
code={`<IconProvider overrides={{ heart: () => <div>Custom Icon</div> }} />`}
/>
## Icon library
<IconLibrary />
+33 -17
View File
@@ -1,33 +1,49 @@
import { CodeBlock } from '@/components/CodeBlock';
import { Banner } from '@/components/Banner';
import { snippet } from './snippets';
# How to install Backstage UI
## 1. Import the package
## How to import BUI's global styles
Import the package using a package manager.
Backstage UI works by importing a global CSS file at the root of your application. This file includes all the default styles for the components.
First, you'll need to install the package using a package manager. For example, if you're using Yarn:
<CodeBlock lang="shell" code={`yarn add @backstage/ui`} />
<CodeBlock
lang="shell"
title="Run this command in your `packages/app` directory"
code={`yarn add @backstage/ui`}
/>
## 2. Import the css files
<CodeBlock
lang="tsx"
title="Add this line to `packages/app/src/index.tsx`"
code={`import '@backstage/cli/asset-types';
import ReactDOM from 'react-dom/client';
import App from './App';
import '@backstage/ui/css/styles.css'; // [!code ++]
Import the global CSS file at the root of your application.
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);`}
/>
```tsx
import '@backstage/ui/css/styles.css';
```
<Banner
text="Import these styles only once at your application root. Plugin developers should skip this step to avoid conflicts."
variant="warning"
/>
## 3. Start building ✨
## How to use BUI components
Now you can start building your plugin using the new design system.
As a plugin maintainer, you can use BUI components in your plugin. As mentioned above, you should not import the styles
again in your plugin as this will be handled at the root of your application. To get started, just add the library to
your plugin and import the components you need.
```tsx
import { Flex, Button, Text } from '@backstage/ui';
<CodeBlock
lang="shell"
title="Run this command in your `packages/[your-plugin]` directory"
code={`yarn add @backstage/ui`}
/>
<Flex>
<Text>Hello World</Text>
<Button>Click me</Button>
</Flex>;
```
<CodeBlock lang="tsx" title="Let's get started 🚀" code={snippet} />
## Next steps
+6
View File
@@ -0,0 +1,6 @@
export const snippet = `import { Flex, Button, Text } from '@backstage/ui';
<Flex>
<Text>Hello World</Text>
<Button>Click me</Button>
</Flex>;`;
+6 -2
View File
@@ -1,10 +1,14 @@
'use client';
import { CodeBlockProps } from '.';
import { Text } from '@backstage/ui';
import styles from './styles.module.css';
import parse from 'html-react-parser';
// Convert markdown-style backticks to HTML code tags
function parseMarkdownInlineCode(text: string): string {
return text.replace(/`([^`]+)`/g, '<code>$1</code>');
}
export const CodeBlockClient = ({
out,
title,
@@ -16,7 +20,7 @@ export const CodeBlockClient = ({
<div className={styles.codeBlock}>
{title && (
<div className={styles.title}>
<Text variant="body">{title}</Text>
{parse(parseMarkdownInlineCode(title))}
</div>
)}
<div className={styles.code}>{parse(out)}</div>
@@ -1,4 +1,5 @@
import type { BundledLanguage } from 'shiki';
import { transformerNotationDiff } from '@shikijs/transformers';
import { codeToHtml } from 'shiki';
import { CodeBlockClient } from './client';
@@ -15,6 +16,7 @@ export async function CodeBlock({ lang = 'tsx', title, code }: CodeBlockProps) {
light: 'min-light',
dark: 'min-dark',
},
transformers: [transformerNotationDiff({ matchAlgorithm: 'v3' })],
});
return <CodeBlockClient out={out} title={title} />;
@@ -16,8 +16,18 @@
.title {
border-bottom: 1px solid var(--border);
padding: 12px 20px;
font-size: 0.875rem;
font-size: 0.8125rem;
color: var(--primary);
font-family: var(--font-regular);
font-weight: var(--font-weight-bold);
}
.title code {
background-color: var(--surface-1);
padding: 0.2rem 0.375rem;
border-radius: 0.25rem;
color: var(--secondary);
font-size: 0.8125rem;
}
.code {
@@ -6,9 +6,9 @@ import { sass } from '@codemirror/lang-sass';
import styles from './styles.module.css';
import { usePlayground } from '@/utils/playground-context';
import { AnimatePresence, motion } from 'motion/react';
import { Icon } from '../../../../packages/ui';
import { createTheme } from '@uiw/codemirror-themes';
import { tags as t } from '@lezer/highlight';
import { RiArrowDownSLine } from '@remixicon/react';
const defaultTheme = `:root {
--bui-bg-solid: #000;
@@ -125,7 +125,12 @@ export const CustomTheme = () => {
className={styles.buttonClose}
onClick={() => setOpen(!open)}
>
<Icon name={open ? 'chevron-down' : 'chevron-up'} />
<RiArrowDownSLine
aria-hidden="true"
style={{
transform: open ? 'rotate(180deg)' : 'rotate(0deg)',
}}
/>
</button>
</div>
</div>
@@ -1,22 +0,0 @@
'use client';
import { Text, Icon, icons } from '../../../../packages/ui';
import type { IconNames } from '../../../../packages/ui';
import styles from './styles.module.css';
export const IconLibrary = () => {
const list = Object.keys(icons);
return (
<div className={styles.library}>
{list.map(icon => (
<div key={icon} className={styles.item}>
<div className={styles.icon}>
<Icon name={icon as IconNames} />
</div>
<Text variant="body">{icon}</Text>
</div>
))}
</div>
);
};
@@ -1 +0,0 @@
export { IconLibrary } from './IconLibrary';
@@ -1,28 +0,0 @@
.library {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 1rem;
}
.item {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
}
.icon {
display: flex;
width: 100%;
justify-content: center;
align-items: center;
height: 80px;
border-radius: 0.5rem;
border: 1px solid var(--border);
background-color: var(--panel);
transition: background-color 0.2s ease-in-out;
&:hover {
background-color: var(--surface-1);
}
}
@@ -3,7 +3,6 @@
import * as Table from '../Table';
import { Chip } from '../Chip';
import { TypePopup } from './TypePopup';
import { icons } from '../../../../packages/ui';
import { PropDef } from '@/utils/propDefs';
@@ -33,10 +32,8 @@ export const PropsTable = <T extends Record<string, PropData>>({
<Table.Body>
{Object.keys(data).map(n => {
const enumValues =
data[n].values === 'icon'
? Object.keys(icons).map(icon => <Chip key={icon}>{icon}</Chip>)
: Array.isArray(data[n].values) &&
data[n].values.map(t => <Chip key={t}>{t}</Chip>);
Array.isArray(data[n].values) &&
data[n].values.map(t => <Chip key={t}>{t}</Chip>);
return (
<Table.Row key={n}>
-36
View File
@@ -1,36 +0,0 @@
import { PropsTable } from '@/components/PropsTable';
import { IconSnippet } from '@/snippets/stories-snippets';
import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
import {
iconPropDefs,
iconUsageSnippet,
iconDefaultSnippet,
} from './icon.props';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { ChangelogComponent } from '@/components/ChangelogComponent';
<PageTitle
title="Icon"
description="Icons are used to represent an action or a state."
/>
<Snippet
py={4}
align="center"
preview={<IconSnippet story="Default" />}
code={iconDefaultSnippet}
/>
## Usage
<CodeBlock code={iconUsageSnippet} />
## API reference
<PropsTable data={iconPropDefs} />
<Theming component="Icon" />
<ChangelogComponent component="icon" />
@@ -1,25 +0,0 @@
import {
classNamePropDefs,
stylePropDefs,
type PropDef,
} from '@/utils/propDefs';
export const iconPropDefs: Record<string, PropDef> = {
name: {
type: 'enum',
values: 'icon',
responsive: false,
},
size: {
type: 'number',
responsive: false,
},
...classNamePropDefs,
...stylePropDefs,
};
export const iconUsageSnippet = `import { Icon } from '@backstage/ui';
<Icon />`;
export const iconDefaultSnippet = `<Icon name="heart" />`;
+32 -1
View File
@@ -48,10 +48,11 @@ iframe {
.shiki,
.shiki span {
background-color: transparent !important;
background-color: transparent;
font-family: var(--font-mono);
font-size: 0.875rem;
line-height: 1.7;
padding-block: 0.2rem;
}
[data-theme-mode='dark'] .shiki,
@@ -63,6 +64,36 @@ iframe {
text-decoration: var(--shiki-dark-text-decoration) !important;
}
/* Add styles for diff notation */
pre.shiki.has-diff .line.diff {
position: relative;
padding-left: 1.5rem;
border-radius: 0.25rem;
}
pre.shiki.has-diff .line.diff::before {
position: absolute;
left: 0.5rem;
}
pre.shiki.has-diff .line.diff.add {
background-color: rgba(46, 160, 67, 0.15);
}
pre.shiki.has-diff .line.diff.add::before {
content: '+';
color: #2ea043;
}
pre.shiki.has-diff .line.diff.remove {
background-color: rgba(248, 81, 73, 0.15);
}
pre.shiki.has-diff .line.diff.remove::before {
content: '-';
color: #f85149;
}
.cm-editor {
transition: background-color 0.2s ease-in-out;
}
-4
View File
@@ -24,10 +24,6 @@ export const overview: Page[] = [
];
export const coreConcepts: Page[] = [
{
title: 'Iconography',
slug: 'iconography',
},
{
title: 'Layout',
slug: 'layout',
+1 -1
View File
@@ -8,7 +8,7 @@ import {
import { components } from './data';
type Theme = 'light' | 'dark';
type ThemeName = 'backstage' | 'spotify';
type ThemeName = 'backstage' | 'spotify' | 'custom';
// Create a context with an empty array as the default value
const PlaygroundContext = createContext<{
+82 -74
View File
@@ -949,70 +949,78 @@ __metadata:
languageName: node
linkType: hard
"@shikijs/core@npm:1.29.2":
version: 1.29.2
resolution: "@shikijs/core@npm:1.29.2"
"@shikijs/core@npm:3.13.0":
version: 3.13.0
resolution: "@shikijs/core@npm:3.13.0"
dependencies:
"@shikijs/engine-javascript": "npm:1.29.2"
"@shikijs/engine-oniguruma": "npm:1.29.2"
"@shikijs/types": "npm:1.29.2"
"@shikijs/vscode-textmate": "npm:^10.0.1"
"@shikijs/types": "npm:3.13.0"
"@shikijs/vscode-textmate": "npm:^10.0.2"
"@types/hast": "npm:^3.0.4"
hast-util-to-html: "npm:^9.0.4"
checksum: 10/83dc5e86efc587d513268175ff43e8273567d2c6616d725bff15b08cf243d90f9371d6fa76ba49bb0e9823fc9947c8d8c650c81cfb19c3eb726178326f639a55
hast-util-to-html: "npm:^9.0.5"
checksum: 10/c9b301a170cd76fa2ce446adee26b539b19204107a2e60207f3f5e7d96773df322367f6035e2d689d43f97a3e9f741793e976c366015d8e3a9709dcac1399af2
languageName: node
linkType: hard
"@shikijs/engine-javascript@npm:1.29.2":
version: 1.29.2
resolution: "@shikijs/engine-javascript@npm:1.29.2"
"@shikijs/engine-javascript@npm:3.13.0":
version: 3.13.0
resolution: "@shikijs/engine-javascript@npm:3.13.0"
dependencies:
"@shikijs/types": "npm:1.29.2"
"@shikijs/vscode-textmate": "npm:^10.0.1"
oniguruma-to-es: "npm:^2.2.0"
checksum: 10/b49461ff7152650ffdbd77332d1c70e24a2ff1abe869e1038694b410194c6403fe5e8fce104fdd305d10c18702a50c1edbdb87172aa09f11340bc1203ed38488
"@shikijs/types": "npm:3.13.0"
"@shikijs/vscode-textmate": "npm:^10.0.2"
oniguruma-to-es: "npm:^4.3.3"
checksum: 10/acd4e78fdc732738c6a3a9f254011b7d8995a2149f2f72f7396d5e4514f3b0c3fdc767d52bf8852d65066e613dd8d1157bc4900442ab42684db7ef7396a44871
languageName: node
linkType: hard
"@shikijs/engine-oniguruma@npm:1.29.2":
version: 1.29.2
resolution: "@shikijs/engine-oniguruma@npm:1.29.2"
"@shikijs/engine-oniguruma@npm:3.13.0":
version: 3.13.0
resolution: "@shikijs/engine-oniguruma@npm:3.13.0"
dependencies:
"@shikijs/types": "npm:1.29.2"
"@shikijs/vscode-textmate": "npm:^10.0.1"
checksum: 10/bb3e2c01da84d573251ebc289b1ecf815261024dea5bddb93ad56c3504a71cde3630db070be401ed3bbcd23a8a839ec78984a82317f9c9d0bba58daed935b781
"@shikijs/types": "npm:3.13.0"
"@shikijs/vscode-textmate": "npm:^10.0.2"
checksum: 10/bc7f1c69640ccece9a3ac3f3adc9cebde9c24bcd722747cc464c338a32725af2b9087a42646cd0f56fbea98d99414918c558f2ca0beadc13db0a740183e7707f
languageName: node
linkType: hard
"@shikijs/langs@npm:1.29.2":
version: 1.29.2
resolution: "@shikijs/langs@npm:1.29.2"
"@shikijs/langs@npm:3.13.0":
version: 3.13.0
resolution: "@shikijs/langs@npm:3.13.0"
dependencies:
"@shikijs/types": "npm:1.29.2"
checksum: 10/01f62d31c653c718a992918357e54d2d312c8da407997565fc19056fbf47f0fadc0f9f4b5fe1e1ba7b7d08e3984fb1f962159503ef0edd81fab5ee8bfdbf9080
"@shikijs/types": "npm:3.13.0"
checksum: 10/2a0478246ce61745d9012cf5051c9790ef5afad6c89fe5716be4a81d04d7dfd9ffc90af0bad0be0061111bfc001a3bc0c55ffd4fc0b82bc1c09a4c33d7b593ca
languageName: node
linkType: hard
"@shikijs/themes@npm:1.29.2":
version: 1.29.2
resolution: "@shikijs/themes@npm:1.29.2"
"@shikijs/themes@npm:3.13.0":
version: 3.13.0
resolution: "@shikijs/themes@npm:3.13.0"
dependencies:
"@shikijs/types": "npm:1.29.2"
checksum: 10/b81606dd882136e3fd751d0829133b5e66b10b8e32bd52ce16e7eac8755891c23e43f3ce06e65b97a75d2bc3b17e7fdb9115e9812679bb820ab163915868fd8b
"@shikijs/types": "npm:3.13.0"
checksum: 10/4ab0ecf1ddb35e387cef22e267cdf3ed7548a84f24ad58885d19401f75cc01a5a10152ee0196222e01cead8202f20012615aa0076f798599f7dfc2bb7beaa617
languageName: node
linkType: hard
"@shikijs/types@npm:1.29.2":
version: 1.29.2
resolution: "@shikijs/types@npm:1.29.2"
"@shikijs/transformers@npm:^3.13.0":
version: 3.13.0
resolution: "@shikijs/transformers@npm:3.13.0"
dependencies:
"@shikijs/vscode-textmate": "npm:^10.0.1"
"@shikijs/core": "npm:3.13.0"
"@shikijs/types": "npm:3.13.0"
checksum: 10/30f3e66334b60320dc976b9f645f823ba13b312aead4c891ae6db23d076f1570dad488443f603fb5eecd5937973ba7c2907da6678ccae2363a66e0c5b173bb02
languageName: node
linkType: hard
"@shikijs/types@npm:3.13.0":
version: 3.13.0
resolution: "@shikijs/types@npm:3.13.0"
dependencies:
"@shikijs/vscode-textmate": "npm:^10.0.2"
"@types/hast": "npm:^3.0.4"
checksum: 10/579e64b6e8cb83023232b8060b08f51cff3909b199d0d1a0c58ed500c898dd34b74bf0457336fa2e6bee1005889e198d7d924347ad616eee30c6ae4c89a67ab8
checksum: 10/a57cc95847edd84134166de1f11a044e356581834b2dfd75dcd67f2221aef6f20f505839c090a76b7c8e0a79d7d5cee829de50d261f363a633ae9afca29546c4
languageName: node
linkType: hard
"@shikijs/vscode-textmate@npm:^10.0.1":
"@shikijs/vscode-textmate@npm:^10.0.2":
version: 10.0.2
resolution: "@shikijs/vscode-textmate@npm:10.0.2"
checksum: 10/d924cba8a01cd9ca12f56ba097d628fdb81455abb85884c8d8a5ae85b628a37dd5907e7691019b97107bd6608c866adf91ba04a1c3bba391281c88e386c044ea
@@ -2330,6 +2338,7 @@ __metadata:
"@mdx-js/react": "npm:^3.1.0"
"@next/mdx": "npm:15.3.4"
"@remixicon/react": "npm:^4.6.0"
"@shikijs/transformers": "npm:^3.13.0"
"@storybook/react": "npm:^8.6.12"
"@types/mdx": "npm:^2.0.13"
"@types/node": "npm:^20"
@@ -2349,7 +2358,7 @@ __metadata:
prop-types: "npm:^15.8.1"
react: "npm:19.1.1"
react-dom: "npm:19.1.1"
shiki: "npm:^1.26.1"
shiki: "npm:^3.13.0"
storybook: "npm:^8.6.12"
typescript: "npm:^5"
languageName: unknown
@@ -2429,13 +2438,6 @@ __metadata:
languageName: node
linkType: hard
"emoji-regex-xs@npm:^1.0.0":
version: 1.0.0
resolution: "emoji-regex-xs@npm:1.0.0"
checksum: 10/e216ec4270f765e1097cefc1b9518a7166b872b4424c60a85d79765f318d989cd458e036c76c13e9ce2ed1fe1bb5935a7fd5c1fab7600668bc8e92a789045b3c
languageName: node
linkType: hard
"emoji-regex@npm:^8.0.0":
version: 8.0.0
resolution: "emoji-regex@npm:8.0.0"
@@ -3577,7 +3579,7 @@ __metadata:
languageName: node
linkType: hard
"hast-util-to-html@npm:^9.0.4":
"hast-util-to-html@npm:^9.0.5":
version: 9.0.5
resolution: "hast-util-to-html@npm:9.0.5"
dependencies:
@@ -5351,14 +5353,21 @@ __metadata:
languageName: node
linkType: hard
"oniguruma-to-es@npm:^2.2.0":
version: 2.3.0
resolution: "oniguruma-to-es@npm:2.3.0"
"oniguruma-parser@npm:^0.12.1":
version: 0.12.1
resolution: "oniguruma-parser@npm:0.12.1"
checksum: 10/2e7e308ec222f377b4be21e87f009729ce1cc014511d35a50440e7270a17413e4859b8c7ad1985123abe1089bda5ad2d0feccc75f7de25d9cb272b7cc34b4f3c
languageName: node
linkType: hard
"oniguruma-to-es@npm:^4.3.3":
version: 4.3.3
resolution: "oniguruma-to-es@npm:4.3.3"
dependencies:
emoji-regex-xs: "npm:^1.0.0"
regex: "npm:^5.1.1"
regex-recursion: "npm:^5.1.1"
checksum: 10/7ac89fe04791650c21a146e58c985de394597e0ab3eb7d7c46699d09f87d6aca63d9f2470f1473c5adfa16961c360df6858e6a24b2429ebd7c73620ca68aec98
oniguruma-parser: "npm:^0.12.1"
regex: "npm:^6.0.1"
regex-recursion: "npm:^6.0.2"
checksum: 10/49b372569d335077c32bda066ac1da4a3f15dd25b717025cf43417fabd71d56e1152debcd8a832596e180d721b808822c88e65eb12abb26665ab8fe017f3c861
languageName: node
linkType: hard
@@ -5714,13 +5723,12 @@ __metadata:
languageName: node
linkType: hard
"regex-recursion@npm:^5.1.1":
version: 5.1.1
resolution: "regex-recursion@npm:5.1.1"
"regex-recursion@npm:^6.0.2":
version: 6.0.2
resolution: "regex-recursion@npm:6.0.2"
dependencies:
regex: "npm:^5.1.1"
regex-utilities: "npm:^2.3.0"
checksum: 10/9ec7e677743e00c14c3d5c63de1d15e5126a69c58da43e60b2c49abf940981f0269442c919e03f66d6837d0ac55ccf7981581e9d1be2a332a968c016edee226e
checksum: 10/ce25d54bdf79e38ae663c26a7f265754047b918e8b771b4ed3c871e9a926b85b195dcd0dd7a6444c82d2c9db9b2cbcaf51a353a807b469a3ad7a2172f27f21d4
languageName: node
linkType: hard
@@ -5731,12 +5739,12 @@ __metadata:
languageName: node
linkType: hard
"regex@npm:^5.1.1":
version: 5.1.1
resolution: "regex@npm:5.1.1"
"regex@npm:^6.0.1":
version: 6.0.1
resolution: "regex@npm:6.0.1"
dependencies:
regex-utilities: "npm:^2.3.0"
checksum: 10/956d0f34afa8086551dc9a0e2c575cb6d7ed5122d6d440a72cfbcd1f9adb5a3dcce4dcb61ec69eaecfcbc508cfd25075ac60d011bab5f28f1b2a36220e09a245
checksum: 10/8f8c35ce0a74b65ce29495b8c18767a4b2724ca4a4b4acfc4d45a2851a5afdbc4daae447a1a762f8faf06a1efe3d7dd8cfc4ebbee9f7c55411d4bd30f6976026
languageName: node
linkType: hard
@@ -6142,19 +6150,19 @@ __metadata:
languageName: node
linkType: hard
"shiki@npm:^1.26.1":
version: 1.29.2
resolution: "shiki@npm:1.29.2"
"shiki@npm:^3.13.0":
version: 3.13.0
resolution: "shiki@npm:3.13.0"
dependencies:
"@shikijs/core": "npm:1.29.2"
"@shikijs/engine-javascript": "npm:1.29.2"
"@shikijs/engine-oniguruma": "npm:1.29.2"
"@shikijs/langs": "npm:1.29.2"
"@shikijs/themes": "npm:1.29.2"
"@shikijs/types": "npm:1.29.2"
"@shikijs/vscode-textmate": "npm:^10.0.1"
"@shikijs/core": "npm:3.13.0"
"@shikijs/engine-javascript": "npm:3.13.0"
"@shikijs/engine-oniguruma": "npm:3.13.0"
"@shikijs/langs": "npm:3.13.0"
"@shikijs/themes": "npm:3.13.0"
"@shikijs/types": "npm:3.13.0"
"@shikijs/vscode-textmate": "npm:^10.0.2"
"@types/hast": "npm:^3.0.4"
checksum: 10/93de1ebc768ff497ba36720f3b385bff65a853248fdfc0afcb2aea81a43560b1bdc7db9e8417df11a87e106b0de0943989c6301daf62a43d09bd9ac7bc776bf3
checksum: 10/8d7cd039ac8df548f724047b4ba81cd84c09e8b96f411eb7c781d3d105c96ff2ec6f64e1bac724fd6fc56ac2926322bc2466124ea279d04c5ef668d5a04219bb
languageName: node
linkType: hard