First pass moving docs to Nextjs 15

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
Charles de Dreuille
2025-06-11 10:58:14 +01:00
parent f1c620f610
commit 09e165498b
120 changed files with 4303 additions and 3613 deletions
+13 -7
View File
@@ -8,19 +8,25 @@
"lint": "next lint",
"start": "npm run build:css && next dev"
},
"resolutions": {
"@types/react": "19.1.7",
"@types/react-dom": "19.1.6"
},
"dependencies": {
"@codemirror/lang-sass": "^6.0.2",
"@lezer/highlight": "^1.2.1",
"@mdx-js/loader": "^3.1.0",
"@mdx-js/react": "^3.1.0",
"@next/mdx": "^15.1.4",
"@next/mdx": "15.3.3",
"@storybook/react": "^8.6.8",
"@uiw/codemirror-themes": "^4.23.7",
"@uiw/react-codemirror": "^4.23.7",
"html-react-parser": "^5.2.5",
"motion": "^12.4.1",
"next": "14.2.26",
"react": "^18.0.2",
"react-dom": "^18.0.2",
"next": "15.3.3",
"next-mdx-remote-client": "^2.1.2",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-frame-component": "^5.2.7",
"shiki": "^1.26.1",
"storybook": "^8.6.8"
@@ -28,10 +34,10 @@
"devDependencies": {
"@types/mdx": "^2.0.13",
"@types/node": "^20",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@types/react": "19.1.7",
"@types/react-dom": "19.1.6",
"eslint": "^8",
"eslint-config-next": "14.2.23",
"eslint-config-next": "15.3.3",
"lightningcss": "^1.28.2",
"typescript": "^5"
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

After

Width:  |  Height:  |  Size: 14 KiB

@@ -0,0 +1,13 @@
import { Changelog } from '@/components/Changelog';
# Changelog
<Changelog />
## Version 0.1.0
We're excited to share the initial release of Canon 💚 In this first alpha version,
you'll find the foundation of our design system: a set of versatile layout components
and a handful of essential atomic elements to help you get started. While Canon is
still in its early stages, it's ready for exploration and we'd love for you to give
it a try and share your feedback.
@@ -0,0 +1,25 @@
import { components, layoutComponents } from '@/utils/data';
export default async function Page({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const { default: Component } = await import(
`@/content/components/${slug}.mdx`
);
return <Component />;
}
export function generateStaticParams() {
const list = [...components, ...layoutComponents];
return list.map(component => ({
slug: component.slug,
}));
}
export const dynamicParams = false;
@@ -1,75 +0,0 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { Tabs } from '@/components/Tabs';
import { CodeBlock } from '@/components/CodeBlock';
import { AvatarSnippet } from '@/snippets/stories-snippets';
import { avatarPropDefs } from './props';
# Avatar
An avatar component with a fallback for initials.
<Snippet
align="center"
py={4}
preview={<AvatarSnippet story="Default" />}
code={`<Avatar src="https://avatars.githubusercontent.com/u/1540635?v=4" name="Charles de Dreuille" />`}
/>
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Usage</Tabs.Tab>
<Tabs.Tab>Theming</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<CodeBlock
code={`import { Avatar } from '@backstage/canon';
<Avatar src="https://avatars.githubusercontent.com/u/1540635?v=4" name="Charles de Dreuille" />`}
/>
</Tabs.Panel>
<Tabs.Panel>
We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
your brand. For additional flexibility, you can use the provided class names for each element listed below.
- `canon-AvatarRoot`
- `canon-AvatarRoot[data-size='small']`
- `canon-AvatarRoot[data-size='medium']`
- `canon-AvatarRoot[data-size='large']`
- `canon-AvatarImage`
- `canon-AvatarFallback`
</Tabs.Panel>
</Tabs.Root>
## API reference
<PropsTable data={avatarPropDefs} />
## Examples
### Sizes
Avatar sizes can be set using the `size` prop.
<Snippet
align="center"
py={4}
open
preview={<AvatarSnippet story="Sizes" />}
code={`<Flex gap="4" direction="column">
<Avatar src="https://avatars.githubusercontent.com/u/1540635?v=4" name="Charles de Dreuille" size="small" />
<Avatar src="https://avatars.githubusercontent.com/u/1540635?v=4" name="Charles de Dreuille" size="medium" />
<Avatar src="https://avatars.githubusercontent.com/u/1540635?v=4" name="Charles de Dreuille" size="large" />
</Flex>`}
/>
### Fallback
If the image is not available, the avatar will show the initials of the name.
<Snippet
align="center"
py={4}
open
preview={<AvatarSnippet story="Fallback" />}
code={`<Avatar src="https://avatars.githubusercontent.com/u/15406AAAAAAAAA" name="Charles de Dreuille" />`}
/>
@@ -1,19 +0,0 @@
import { classNamePropDefs, stylePropDefs } from '@/utils/propDefs';
import type { PropDef } from '@/utils/propDefs';
export const avatarPropDefs: Record<string, PropDef> = {
src: {
type: 'string',
},
name: {
type: 'string',
},
size: {
type: 'enum',
values: ['small', 'medium', 'large'],
default: 'medium',
responsive: true,
},
...classNamePropDefs,
...stylePropDefs,
};
@@ -1,24 +0,0 @@
import {
classNamePropDefs,
displayPropDefs,
heightPropDefs,
positionPropDefs,
stylePropDefs,
widthPropDefs,
} from '../../../../utils/propDefs';
import type { PropDef } from '../../../../utils/propDefs';
export const boxPropDefs: Record<string, PropDef> = {
as: {
type: 'enum',
values: ['div', 'span'],
default: 'div',
responsive: true,
},
...widthPropDefs,
...heightPropDefs,
...positionPropDefs,
...displayPropDefs,
...classNamePropDefs,
...stylePropDefs,
};
@@ -1,150 +0,0 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { Tabs } from '@/components/Tabs';
import { CodeBlock } from '@/components/CodeBlock';
import { ButtonSnippet } from '@/snippets/stories-snippets';
import { buttonVariants } from '@/snippets/code-snippets';
import { buttonPropDefs } from './props';
# Button
A button component that can be used to trigger actions.
<Snippet
align="center"
py={4}
preview={<ButtonSnippet story="Variants" />}
code={`<Inline alignY="center">
<Button iconStart="cloud" variant="primary">
Button
</Button>
<Button iconStart="cloud" variant="secondary">
Button
</Button>
<Button iconStart="cloud" variant="tertiary">
Button
</Button>
</Inline>`}
/>
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Usage</Tabs.Tab>
<Tabs.Tab>Theming</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<CodeBlock
code={`import { Button } from '@backstage/canon';
<Button />
`}
/>
</Tabs.Panel>
<Tabs.Panel>
We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
your brand. For additional flexibility, you can use the provided class names for each element listed below.
<CodeBlock
code={`<Button className="button" />`}
/>
</Tabs.Panel>
</Tabs.Root>
## API reference
<PropsTable data={buttonPropDefs} />
## Examples
### Variants
Here's a view when buttons have different variants.
<Snippet
align="center"
py={4}
open
preview={<ButtonSnippet story="Variants" />}
code={buttonVariants}
/>
### Sizes
Here's a view when buttons have different sizes.
<Snippet
align="center"
py={4}
open
preview={<ButtonSnippet story="Sizes" />}
code={`<Inline alignY="center">
<Button size="small">Small</Button>
<Button size="medium">Medium</Button>
</Inline>`}
/>
### With Icons
Here's a view when buttons have icons.
<Snippet
align="center"
py={4}
open
preview={<ButtonSnippet story="WithIcons" />}
code={`<Inline alignY="center">
<Button iconStart="cloud">Button</Button>
<Button iconEnd="chevronRight">Button</Button>
<Button iconStart="cloud" iconEnd="chevronRight">Button</Button>
</Inline>`}
/>
### Full width
Here's a view when buttons are full width.
<Snippet
align="center"
py={4}
open
preview={<ButtonSnippet story="FullWidth" />}
code={`<Flex style={{ width: '300px' }}>
<Button fullWidth>Full width</Button>
</Flex>`}
/>
### Disabled
Here's a view when buttons are disabled.
<Snippet
align="center"
py={4}
open
preview={<ButtonSnippet story="Disabled" />}
code={`<Button disabled>Button</Button>`}
/>
### As Link
Here's a view when buttons are rendered as a link.
<Snippet
align="center"
py={4}
open
preview={<ButtonSnippet story="AsLink" />}
code={`<Button render={<a href="https://canon.backstage.io" target="_blank" />}>
I am a link
</Button>`}
/>
### Responsive
Here's a view when buttons are responsive.
<CodeBlock
code={`<Button variant={{ initial: 'primary', lg: 'secondary' }}>
Responsive Button
</Button>`}
/>
@@ -1,25 +0,0 @@
import { classNamePropDefs, stylePropDefs } from '../../../../utils/propDefs';
import type { PropDef } from '../../../../utils/propDefs';
export const buttonPropDefs: Record<string, PropDef> = {
variant: {
type: 'enum',
values: ['primary', 'secondary'],
default: 'primary',
responsive: true,
},
size: {
type: 'enum',
values: ['small', 'medium'],
default: 'medium',
responsive: true,
},
render: {
type: 'enum',
values: ['ReactNode'],
responsive: false,
default: '<button />',
},
...classNamePropDefs,
...stylePropDefs,
};
@@ -1,66 +0,0 @@
import { PropsTable } from '@/components/PropsTable';
import { CheckboxSnippet } from '@/snippets/stories-snippets';
import { Snippet } from '@/components/Snippet';
import { Tabs } from '@/components/Tabs';
import { CodeBlock } from '@/components/CodeBlock';
import { BaseUI } from '@/components/HeadlessBanners/BaseUI';
import { checkboxPropDefs } from './props';
# Checkbox
A checkbox component that can be used to trigger actions.
<Snippet
align="center"
py={4}
preview={<CheckboxSnippet story="Default" />}
code={`<Checkbox label="Accept terms and conditions" />`}
/>
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Usage</Tabs.Tab>
<Tabs.Tab>Theming</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<CodeBlock
code={`import { Checkbox } from '@backstage/canon';
<Checkbox />
`}
/>
</Tabs.Panel>
<Tabs.Panel>
We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
your brand. For additional flexibility, you can use the provided class names for each element listed below.
<CodeBlock
code={`<Checkbox className="checkbox" />`}
/>
</Tabs.Panel>
</Tabs.Root>
## API reference
<BaseUI href="https://base-ui.com/react/components/checkbox" />
<PropsTable data={checkboxPropDefs} />
## Examples
### All variants
Here's a view when checkboxes have different variants.
<Snippet
align="center"
py={4}
open
preview={<CheckboxSnippet story="AllVariants" />}
code={`<Inline alignY="center">
<Checkbox />
<Checkbox checked />
<Checkbox label="Checkbox" />
<Checkbox label="Checkbox" checked />
</Inline>`}
/>
@@ -1,113 +0,0 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { Tabs } from '@/components/Tabs';
import { CodeBlock } from '@/components/CodeBlock';
import { CollapsibleSnippet } from '@/snippets/stories-snippets';
import {
collapsibleRootPropDefs,
collapsibleTriggerPropDefs,
collapsiblePanelPropDefs,
} from './props';
# Collapsible
An avatar component with a fallback for initials.
<Snippet
align="center"
py={4}
height={240}
preview={<CollapsibleSnippet story="Default" />}
code={`<Collapsible.Root>
<Collapsible.Trigger render={(props, state) => (
<Button {...props}>
{state.open ? 'Close Panel' : 'Open Panel'}
</Button>
)} />
<Collapsible.Panel>
<Box>
<Text>It's the edge of the world and all of Western civilization</Text>
<Text>The sun may rise in the East, at least it settled in a final location</Text>
<Text>It's understood that Hollywood sells Californication</Text>
</Box>
</Collapsible.Panel>
</Collapsible.Root>`}
/>
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Usage</Tabs.Tab>
<Tabs.Tab>Theming</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<CodeBlock
code={`import { Collapsible } from '@backstage/canon';
<Collapsible.Root>
<Collapsible.Trigger render={(props, state) => (
<Button {...props}>
{state.open ? 'Close Panel' : 'Open Panel'}
</Button>
)} />
<Collapsible.Panel>Your content</Collapsible.Panel>
</Collapsible.Root>`}
/>
</Tabs.Panel>
<Tabs.Panel>
We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
your brand. For additional flexibility, you can use the provided class names for each element listed below.
- `canon-CollapsibleRoot`
- `canon-CollapsibleTrigger`
- `canon-CollapsiblePanel`
</Tabs.Panel>
</Tabs.Root>
## API reference
### Collapsible.Root
Groups all parts of the collapsible. Renders a `<div>` element.
<PropsTable data={collapsibleRootPropDefs} />
### Collapsible.Trigger
The trigger by default render a simple unstyled button. Because menus can be rendered in different ways, we recommend
using the `render` prop to render a custom trigger.
<CodeBlock
code={`<Collapsible.Trigger render={props => <Button {...props} />} />`}
/>
<PropsTable data={collapsibleTriggerPropDefs} />
### Collapsible.Panel
A panel with the collapsible contents. Renders a `<div>` element.
<PropsTable data={collapsiblePanelPropDefs} />
## Examples
Open the panel by default by setting the `defaultOpen` prop to `true`.
<Snippet
align="center"
py={4}
open
preview={<CollapsibleSnippet story="Open" />}
code={`<Collapsible.Root defaultOpen>
<Collapsible.Trigger render={(props, state) => (
<Button {...props}>
{state.open ? 'Close Panel' : 'Open Panel'}
</Button>
)} />
<Collapsible.Panel>
<Box>
<Text>It's the edge of the world and all of Western civilization</Text>
<Text>The sun may rise in the East, at least it settled in a final location</Text>
<Text>It's understood that Hollywood sells Californication</Text>
</Box>
</Collapsible.Panel>
</Collapsible.Root>`}
/>
@@ -1,43 +0,0 @@
import {
classNamePropDefs,
stylePropDefs,
renderPropDefs,
} from '@/utils/propDefs';
import type { PropDef } from '@/utils/propDefs';
export const collapsibleRootPropDefs: Record<string, PropDef> = {
defaultOpen: {
type: 'boolean',
default: 'false',
},
open: {
type: 'boolean',
},
onOpenChange: {
type: 'enum',
values: ['(open) => void'],
},
...renderPropDefs,
...classNamePropDefs,
...stylePropDefs,
};
export const collapsibleTriggerPropDefs: Record<string, PropDef> = {
...renderPropDefs,
...classNamePropDefs,
...stylePropDefs,
};
export const collapsiblePanelPropDefs: Record<string, PropDef> = {
hiddenUntilFound: {
type: 'boolean',
default: 'false',
},
keepMounted: {
type: 'boolean',
default: 'false',
},
...renderPropDefs,
...classNamePropDefs,
...stylePropDefs,
};
@@ -1,12 +0,0 @@
import {
classNamePropDefs,
stylePropDefs,
gapPropDefs,
} from '../../../../utils/propDefs';
import type { PropDef } from '../../../../utils/propDefs';
export const containerPropDefs: Record<string, PropDef> = {
...gapPropDefs,
...classNamePropDefs,
...stylePropDefs,
};
@@ -1,83 +0,0 @@
import { PropsTable } from '@/components/PropsTable';
import { Tabs } from '@/components/Tabs';
import { CodeBlock } from '@/components/CodeBlock';
import {
flexFAQ1,
flexSimple,
flexResponsive,
flexAlign,
} from '@/snippets/code-snippets';
import { Snippet } from '@/components/Snippet';
import { FlexSnippet } from '@/snippets/stories-snippets';
import { flexPropDefs } from './props';
import { spacingPropDefs } from '../../../../utils/propDefs';
# Flex
This is the flex container component. It will help to define the number of
columns that will be used in the grid. You can also define the gap between the
columns. All values are responsive.
<Snippet
py={4}
align="center"
preview={<FlexSnippet story="Default" />}
code={`<Flex>
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
</Flex>`}
/>
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Usage</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<CodeBlock
code={`import { Flex } from '@backstage/canon';
<Flex />
`}
/>
</Tabs.Panel>
</Tabs.Root>
## API reference
<PropsTable data={flexPropDefs} />
The grid component also accepts all the spacing props from the Box component.
<PropsTable data={spacingPropDefs} />
## Common questions
### Can I stack horizontally?
The Flex component only allows for stacking elements vertically. If you want
to create a column layout, please use the Grid component.
<CodeBlock code={flexFAQ1} />
## Examples
### Simple
A simple example of how to use the Flex component.
<CodeBlock code={flexSimple} />
### Responsive
The Flex component also supports responsive values, making it easy to create
responsive designs.
<CodeBlock code={flexResponsive} />
### Align
The Flex component also supports responsive alignment, making it easy to
create responsive designs.
<CodeBlock code={flexAlign} />
@@ -1,27 +0,0 @@
import {
PropDef,
childrenPropDefs,
classNamePropDefs,
stylePropDefs,
} from '../../../../utils/propDefs';
export const flexPropDefs: Record<string, PropDef> = {
align: {
type: 'enum',
values: ['start', 'center', 'end', 'baseline', 'stretch'],
responsive: true,
},
direction: {
type: 'enum',
values: ['row', 'column', 'row-reverse', 'column-reverse'],
responsive: true,
},
justify: {
type: 'enum',
values: ['start', 'center', 'end', 'between'],
responsive: true,
},
...childrenPropDefs,
...classNamePropDefs,
...stylePropDefs,
};
@@ -1,59 +0,0 @@
import {
PropDef,
childrenPropDefs,
classNamePropDefs,
stylePropDefs,
} from '../../../../utils/propDefs';
const columnsValues = [
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'10',
'11',
'12',
];
export const gridPropDefs: Record<string, PropDef> = {
columns: {
type: 'enum | string',
values: [...columnsValues, 'auto'],
responsive: true,
default: 'auto',
},
...childrenPropDefs,
...classNamePropDefs,
...stylePropDefs,
};
export const gridItemPropDefs: Record<string, PropDef> = {
colSpan: {
type: 'enum | string',
values: [...columnsValues, 'full'],
responsive: true,
},
rowSpan: {
type: 'enum | string',
values: [...columnsValues, 'full'],
responsive: true,
},
start: {
type: 'enum | string',
values: [...columnsValues, 'auto'],
responsive: true,
},
end: {
type: 'enum | string',
values: [...columnsValues, 'auto'],
responsive: true,
},
...childrenPropDefs,
...classNamePropDefs,
...stylePropDefs,
};
@@ -1,102 +0,0 @@
import { PropsTable } from '@/components/PropsTable';
import { HeadingSnippet } from '@/snippets/stories-snippets';
import { Snippet } from '@/components/Snippet';
import { Tabs } from '@/components/Tabs';
import { CodeBlock } from '@/components/CodeBlock';
import { headingPropDefs } from './props';
# Heading
Headings are used to structure the content of your page.
<Snippet
py={4}
preview={<HeadingSnippet story="Title1" />}
code={`<Heading variant="title1">Hello World!</Heading>`}
/>
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Usage</Tabs.Tab>
<Tabs.Tab>Theming</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<CodeBlock
code={`import { Heading } from '@backstage/canon';
<Heading />
`}
/>
</Tabs.Panel>
<Tabs.Panel>
We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
your brand. For additional flexibility, you can use the provided class names for each element listed below.
<CodeBlock
code={`<Heading className="heading" />`}
/>
</Tabs.Panel>
</Tabs.Root>
## API reference
<PropsTable data={headingPropDefs} />
## Examples
### All variants
The `Heading` component has a `variant` prop that can be used to change the
appearance of the heading.
<Snippet
py={2}
open
preview={<HeadingSnippet story="AllVariants" />}
code={`<Flex direction="column" gap="4">
<Heading variant="display">Display</Heading>
<Heading variant="title1">Title 1</Heading>
<Heading variant="title2">Title 2</Heading>
<Heading variant="title3">Title 3</Heading>
<Heading variant="title4">Title 4</Heading>
<Heading variant="title5">Title 5</Heading>
</Flex>`}
/>
### Truncate
The `Heading` component has a `truncate` prop that can be used to truncate the
heading.
<Snippet
py={2}
open
preview={<HeadingSnippet story="Truncate" />}
code={`<Heading style={{ maxWidth: '400px' }} truncate>
A man looks at a painting in a museum and says, “Brothers and sisters I
have none, but that man&apos;s father is my father's son.” Who is
in the painting?
</Heading>`}
/>
### Custom render
You can also use the `render` prop to render the heading as a different element.
<Snippet
py={2}
open
preview={<HeadingSnippet story="CustomRender" />}
code={`<Heading render={<h4 />}>Custom render</Heading>`}
/>
### Responsive
You can also use the `variant` prop to change the appearance of the text based
on the screen size.
<CodeBlock
code={`<Heading variant={{ initial: 'title2', lg: 'title1' }}>
Responsive heading
</Heading>`}
/>
@@ -1,21 +0,0 @@
import { classNamePropDefs, stylePropDefs } from '../../../../utils/propDefs';
import type { PropDef } from '../../../../utils/propDefs';
export const headingPropDefs: Record<string, PropDef> = {
variant: {
type: 'enum',
values: ['display', 'title1', 'title2', 'title3', 'title4', 'title5'],
responsive: true,
},
children: {
type: 'enum',
values: ['ReactNode'],
responsive: false,
},
truncate: {
type: 'boolean',
default: 'false',
},
...classNamePropDefs,
...stylePropDefs,
};
@@ -1,97 +0,0 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { Tabs } from '@/components/Tabs';
import { CodeBlock } from '@/components/CodeBlock';
import { IconButtonSnippet } from '@/snippets/stories-snippets';
import { iconButtonVariants } from '@/snippets/code-snippets';
import { iconButtonPropDefs } from './props';
# Icon Button
A button component with a single icon that can be used to trigger actions.
<Snippet
align="center"
py={4}
preview={<IconButtonSnippet story="Variants" />}
code={`<Flex align="center">
<IconButton icon="cloud" variant="primary" />
<IconButton icon="cloud" variant="secondary" />
</Flex>`}
/>
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Usage</Tabs.Tab>
<Tabs.Tab>Theming</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<CodeBlock
code={`import { IconButton } from '@backstage/canon';
<IconButton />
`}
/>
</Tabs.Panel>
<Tabs.Panel>
We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
your brand. For additional flexibility, you can use the provided class names for each element listed below.
<CodeBlock
code={`<IconButton className="canon-IconButton" />`}
/>
</Tabs.Panel>
</Tabs.Root>
## API reference
<PropsTable data={iconButtonPropDefs} />
## Examples
### Variants
Here's a view when buttons have different variants.
<Snippet
align="center"
py={4}
open
preview={<IconButtonSnippet story="Variants" />}
code={iconButtonVariants}
/>
### Sizes
Here's a view when buttons have different sizes.
<Snippet
align="center"
py={4}
open
preview={<IconButtonSnippet story="Sizes" />}
code={`<Flex align="center">
<IconButton icon="cloud" size="small" />
<IconButton icon="cloud" size="medium" />
</Flex>`}
/>
### Disabled
Here's a view when buttons are disabled.
<Snippet
align="center"
py={4}
open
preview={<IconButtonSnippet story="Disabled" />}
code={`<IconButton icon="cloud" disabled />`}
/>
### Responsive
Here's a view when buttons are responsive.
<CodeBlock
code={`<IconButton icon="cloud" variant={{ initial: 'primary', lg: 'secondary' }} />`}
/>
@@ -1,20 +0,0 @@
import { classNamePropDefs, stylePropDefs } from '../../../../utils/propDefs';
import type { PropDef } from '../../../../utils/propDefs';
export const iconButtonPropDefs: Record<string, PropDef> = {
variant: {
type: 'enum',
values: ['primary', 'secondary'],
default: 'primary',
responsive: true,
},
size: {
type: 'enum',
values: ['small', 'medium'],
default: 'medium',
responsive: true,
},
icon: { type: 'enum', values: 'icon', responsive: false },
...classNamePropDefs,
...stylePropDefs,
};
@@ -1,44 +0,0 @@
import { PropsTable } from '@/components/PropsTable';
import { IconSnippet } from '@/snippets/stories-snippets';
import { Snippet } from '@/components/Snippet';
import { Tabs } from '@/components/Tabs';
import { CodeBlock } from '@/components/CodeBlock';
import { iconPropDefs } from './props';
# Icon
Icons are used to represent an action or a state.
<Snippet
py={4}
align="center"
preview={<IconSnippet story="Default" />}
code={`<Icon name="heart" />`}
/>
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Usage</Tabs.Tab>
<Tabs.Tab>Theming</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<CodeBlock
code={`import { Icon } from '@backstage/canon';
<Icon />
`}
/>
</Tabs.Panel>
<Tabs.Panel>
We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
your brand. For additional flexibility, you can use the provided class names for each element listed below.
<CodeBlock
code={`<Icon className="icon" />`}
/>
</Tabs.Panel>
</Tabs.Root>
## API reference
<PropsTable data={iconPropDefs} />
@@ -1,84 +0,0 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { Tabs } from '@/components/Tabs';
import { CodeBlock } from '@/components/CodeBlock';
import {
MenuSnippet,
ButtonSnippet,
LinkSnippet,
} from '@/snippets/stories-snippets';
import { linkPropDefs } from './props';
# Link
A link component that renders a `<a>` element.
<Snippet
align="center"
py={4}
preview={<LinkSnippet story="Default" />}
code={`<Link href="https://backstage.io">Sign up for Backstage</Link>`}
/>
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Usage</Tabs.Tab>
<Tabs.Tab>Theming</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<CodeBlock
code={`import { Link } from '@backstage/canon';
<Link href="https://backstage.io">Sign up for Backstage</Link>`}
/>
</Tabs.Panel>
<Tabs.Panel>
We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
your brand. For additional flexibility, you can use the provided class names for each element listed below.
- `canon-Link`
- `canon-Link[data-variant='body']`
- `canon-Link[data-variant='subtitle']`
- `canon-Link[data-variant='caption']`
- `canon-Link[data-variant='label']`
- `canon-Link[data-weight='regular']`
- `canon-Link[data-weight='bold']`
</Tabs.Panel>
</Tabs.Root>
## API reference
<PropsTable data={linkPropDefs} />
## Examples
### Variants
Here's a view when links have different variants.
<Snippet
align="center"
py={4}
open
preview={<LinkSnippet story="AllVariants" />}
code={`<Flex gap="4" direction="column">
<Link href="https://canon.backstage.io" variant="subtitle" />
<Link href="https://canon.backstage.io" variant="body" />
<Link href="https://canon.backstage.io" variant="caption" />
<Link href="https://canon.backstage.io" variant="label" />
</Flex>`}
/>
### Weights
Here's a view when links have different weights.
<Snippet
align="center"
py={4}
open
preview={<LinkSnippet story="AllWeights" />}
code={`<Flex gap="4" direction="column">
<Link href="https://canon.backstage.io" weight="regular" />
<Link href="https://canon.backstage.io" weight="bold" />
</Flex>`}
/>
@@ -1,22 +0,0 @@
import { classNamePropDefs, stylePropDefs } from '@/utils/propDefs';
import type { PropDef } from '@/utils/propDefs';
export const linkPropDefs: Record<string, PropDef> = {
to: {
type: 'string',
},
variant: {
type: 'enum',
values: ['subtitle', 'body', 'caption', 'label'],
default: 'body',
responsive: true,
},
weight: {
type: 'enum',
values: ['regular', 'bold'],
default: 'regular',
responsive: true,
},
...classNamePropDefs,
...stylePropDefs,
};
@@ -1,202 +0,0 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { Tabs } from '@/components/Tabs';
import { CodeBlock } from '@/components/CodeBlock';
import { MenuSnippet } from '@/snippets/stories-snippets';
import { BaseUI } from '@/components/HeadlessBanners/BaseUI';
import {
menuRootPropDefs,
menuTriggerPropDefs,
menuPositionerPropDefs,
menuItemPropDefs,
} from './props';
# Menu
A list of actions in a dropdown, enhanced with keyboard navigation.
<Snippet
align="center"
py={4}
preview={<MenuSnippet story="Default" />}
code={`<Menu.Root>
<Menu.Trigger
render={props => (
<Button
{...props}
size="medium"
variant="secondary"
iconEnd="chevron-down"
>
Menu
</Button>
)}
/>
<Menu.Portal>
<Menu.Positioner sideOffset={8} align="start">
<Menu.Popup>
<Menu.Item>Settings</Menu.Item>
<Menu.Item>Invite new members</Menu.Item>
<Menu.Item>Download app</Menu.Item>
<Menu.Item>Log out</Menu.Item>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>`}
/>
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Usage</Tabs.Tab>
<Tabs.Tab>Theming</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<CodeBlock
code={`import { Menu } from '@backstage/canon';
<Menu.Root>
<Menu.Trigger />
<Menu.Portal>
<Menu.Positioner>
<Menu.Popup>
<Menu.Item />
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>`}
/>
</Tabs.Panel>
<Tabs.Panel>
We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
your brand. For additional flexibility, you can use the provided class names for each element listed below.
- `canon-MenuTrigger`
- `canon-MenuBackdrop`
- `canon-MenuPositioner`
- `canon-MenuPopup`
- `canon-MenuArrow`
- `canon-MenuItem`
- `canon-MenuGroup`
- `canon-MenuGroupLabel`
- `canon-MenuRadioGroup`
- `canon-MenuRadioItem`
- `canon-MenuRadioItemIndicator`
- `canon-MenuCheckboxItem`
- `canon-MenuCheckboxItemIndicator`
- `canon-MenuSubmenuTrigger`
- `canon-MenuSeparator`
</Tabs.Panel>
</Tabs.Root>
## API reference
<BaseUI href="https://base-ui.com/react/components/menu" />
### Menu.Root
Groups all parts of the menu. Doesn't render its own HTML element.
<PropsTable data={menuRootPropDefs} />
### Menu.Trigger
The trigger by default render a simple unstyled button. Because menus can be rendered in different ways, we recommend
using the `render` prop to render a custom trigger.
<CodeBlock code={`<Menu.Trigger render={props => <Button {...props} />} />`} />
<PropsTable data={menuTriggerPropDefs} />
### Menu.Positioner
Positions the menu popup against the trigger. Renders a `<div>` element.
<PropsTable data={menuPositionerPropDefs} />
### Menu.Item
An individual interactive item in the menu. Renders a `<div>` element.
<PropsTable data={menuItemPropDefs} />
## Examples
### Open on hover
To create a menu that opens on hover, add the `openOnHover` prop to the Root part.
You can additionally configure how quickly the menu opens on hover using the `delay` prop.
<Snippet
align="center"
py={4}
open
preview={<MenuSnippet story="OpenOnHover" />}
code={`<Menu.Root openOnHover delay={100}>
<Menu.Trigger
render={props => (
<Button
{...props}
size="medium"
variant="secondary"
iconEnd="chevron-down"
>
Menu
</Button>
)}
/>
<Menu.Portal>
<Menu.Positioner sideOffset={8} align="start">
<Menu.Popup>
<Menu.Item>Settings</Menu.Item>
<Menu.Item>Invite new members</Menu.Item>
<Menu.Item>Download app</Menu.Item>
<Menu.Item>Log out</Menu.Item>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>`}
/>
### Nested navigation
You can nest menus to create a more complex navigation structure.
<Snippet
align="center"
py={4}
open
preview={<MenuSnippet story="Submenu" />}
code={`<Menu.Trigger
render={props => (
<Button
{...props}
size="small"
variant="secondary"
iconEnd={<Icon name="chevron-down" />}
>
Menu
</Button>
)}
/>
<Menu.Portal>
<Menu.Positioner sideOffset={8} align="start">
<Menu.Popup>
<Menu.Item>Settings</Menu.Item>
<Menu.Item>Invite new members</Menu.Item>
<Menu.Item>Download app</Menu.Item>
<Menu.Item>Log out</Menu.Item>
<Menu.Root>
<Menu.SubmenuTrigger>Submenu</Menu.SubmenuTrigger>
<Menu.Portal>
<Menu.Positioner>
<Menu.Popup>
<Menu.Item>Submenu Item 1</Menu.Item>
<Menu.Item>Submenu Item 2</Menu.Item>
<Menu.Item>Submenu Item 3</Menu.Item>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>
</Menu.Root>
</Menu.Popup>
</Menu.Positioner>
</Menu.Portal>`}
/>
@@ -1,128 +0,0 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { Tabs } from '@/components/Tabs';
import { CodeBlock } from '@/components/CodeBlock';
import { SelectSnippet } from '@/snippets/stories-snippets';
import { selectPropDefs } from './props';
# Select
A common form component for choosing a predefined value in a dropdown menu.
<Snippet
align="center"
py={4}
preview={<SelectSnippet story="Preview" />}
code={`<Select name="font" label="Font Family" options={[
{ value: 'sans', label: 'Sans-serif' },
{ value: 'serif', label: 'Serif' },
{ value: 'mono', label: 'Monospace' },
{ value: 'cursive', label: 'Cursive' },
]} />
`}
/>
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Usage</Tabs.Tab>
<Tabs.Tab>Theming</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<CodeBlock
code={`import { Select } from '@backstage/canon';
<Select
name="font"
options={[
{ value: 'sans', label: 'Sans-serif' },
{ value: 'serif', label: 'Serif' },
{ value: 'mono', label: 'Monospace' },
{ value: 'cursive', label: 'Cursive' },
]}
/>
`}
/>
</Tabs.Panel>
<Tabs.Panel>
We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
your brand. For additional flexibility, you can use the provided class names for each element listed below.
<CodeBlock
code={`<Select className="canon-SelectFieldRoot" />`}
/>
</Tabs.Panel>
</Tabs.Root>
## API reference
<PropsTable data={selectPropDefs} />
## Examples
### With Label and description
Select component with label and description.
<Snippet
align="center"
py={4}
open
preview={<SelectSnippet story="WithDescription" />}
code={`<Select
name="font"
label="Font Family"
description="Choose a font family for your document"
options={[ ... ]}
/>`}
/>
### Sizes
Here's a view when the selects have different sizes.
<Snippet
align="center"
py={4}
open
preview={<SelectSnippet story="Sizes" />}
code={`<Flex>
<Select
size="small"
label="Font family"
options={[ ... ]}
/>
<Select
size="medium"
label="Font family"
options={[ ... ]}
/>
</Flex>`}
/>
### Disabled
Here's a view when the select is disabled.
<Snippet
align="center"
py={4}
open
preview={<SelectSnippet story="Disabled" />}
code={`<Select
disabled
label="Font family"
options={[ ... ]}
/>`}
/>
### Responsive
Here's a view when the select is responsive.
<CodeBlock
code={`<Select
size={{ initial: 'small', lg: 'medium' }}
label="Font family"
options={[ ... ]}
/>`}
/>
@@ -1,54 +0,0 @@
import { classNamePropDefs, stylePropDefs } from '../../../../utils/propDefs';
import type { PropDef } from '../../../../utils/propDefs';
export const selectPropDefs: Record<string, PropDef> = {
label: {
type: 'string',
default: 'Select an option',
responsive: false,
},
description: {
type: 'string',
responsive: false,
},
name: {
type: 'string',
responsive: false,
required: true,
},
options: {
type: 'enum',
values: ['Array<{ value: string, label: string }>'],
required: true,
},
value: {
type: 'string',
responsive: false,
},
defaultValue: {
type: 'string',
responsive: false,
},
placeholder: {
type: 'string',
responsive: false,
},
size: {
type: 'enum',
values: ['small', 'medium'],
default: 'medium',
responsive: true,
},
onValueChange: {
type: 'enum',
values: ['(value: string) => void'],
responsive: false,
},
onOpenChange: {
type: 'enum',
values: ['(open: boolean) => void'],
responsive: false,
},
...classNamePropDefs,
...stylePropDefs,
};
@@ -1,92 +0,0 @@
import { PropsTable } from '@/components/PropsTable';
import { TabsSnippet } from '@/snippets/stories-snippets';
import { Snippet } from '@/components/Snippet';
import { Tabs } from '@/components/Tabs';
import { CodeBlock } from '@/components/CodeBlock';
import {
tabsRootPropDefs,
tabsListPropDefs,
tabsTabPropDefs,
tabsPanelPropDefs,
} from './props';
import { BaseUI } from '@/components/HeadlessBanners/BaseUI';
# Tabs
A component for toggling between related panels on the same page.
<Snippet
py={4}
preview={<TabsSnippet story="Default" />}
code={`<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Tab 1</Tabs.Tab>
<Tabs.Tab>Tab 2</Tabs.Tab>
<Tabs.Tab>Tab 3 With long title</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>Content for Tab 1</Tabs.Panel>
<Tabs.Panel>Content for Tab 2</Tabs.Panel>
<Tabs.Panel>Content for Tab 3</Tabs.Panel>
</Tabs.Root>`}
/>
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Usage</Tabs.Tab>
<Tabs.Tab>Theming</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<CodeBlock
code={`import { Tabs } from '@backstage/canon';
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Tab 1</Tabs.Tab>
<Tabs.Tab>Tab 2</Tabs.Tab>
<Tabs.Tab>Tab 3</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>Content for Tab 1</Tabs.Panel>
<Tabs.Panel>Content for Tab 2</Tabs.Panel>
<Tabs.Panel>Content for Tab 3</Tabs.Panel>
</Tabs.Root>
`}
/>
</Tabs.Panel>
<Tabs.Panel>
We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
your brand. For additional flexibility, you can use the provided class names for each element listed below.
- `canon-TabsRoot`
- `canon-TabsList`
- `canon-TabsTab`
- `canon-TabsPanel`
- `canon-TabsIndicator`
</Tabs.Panel>
</Tabs.Root>
## API reference
<BaseUI href="https://base-ui.com/react/components/menu" />
### Tabs.Root
Groups the tabs and the corresponding panels. Renders a `<div>` element.
<PropsTable data={tabsRootPropDefs} />
### Tabs.List
Groups the individual tab buttons. Renders a `<div>` element.
<PropsTable data={tabsListPropDefs} />
### Tabs.Tab
An individual interactive tab button that toggles the corresponding panel. Renders a `<button>` element.
<PropsTable data={tabsTabPropDefs} />
### Tabs.Panel
A panel displayed when the corresponding tab is active. Renders a `<div>` element.
<PropsTable data={tabsPanelPropDefs} />
@@ -1,80 +0,0 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { Tabs } from '@/components/Tabs';
import { CodeBlock } from '@/components/CodeBlock';
import { TextFieldSnippet } from '@/snippets/stories-snippets';
import { BaseUI } from '@/components/HeadlessBanners/BaseUI';
import { inputPropDefs } from './props';
# TextField
A text field component for your forms.
<Snippet
align="center"
py={4}
preview={<TextFieldSnippet story="WithLabel" />}
code={`<TextField label="Label" placeholder="Enter a URL" />`}
/>
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Usage</Tabs.Tab>
<Tabs.Tab>Theming</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<CodeBlock
code={`import { TextField } from '@backstage/canon';
<TextField />
`}
/>
</Tabs.Panel>
<Tabs.Panel>
We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
your brand. For additional flexibility, you can use the provided class names for each element listed below.
- `canon-TextField`
- `canon-TextFieldLabel`
- `canon-TextFieldRequired`
- `canon-TextFieldInputWrapper`
- `canon-TextFieldIcon`
- `canon-TextFieldInput`
- `canon-TextFieldClearButton`
- `canon-TextFieldClearButtonIcon`
- `canon-TextFieldDescription`
- `canon-TextFieldError`
</Tabs.Panel>
</Tabs.Root>
## API reference
<PropsTable data={inputPropDefs} />
## Examples
### Sizes
We support two different sizes: `small`, `medium`.
<Snippet
align="center"
py={4}
open
preview={<TextFieldSnippet story="Sizes" />}
code={`<Flex direction="row" gap="4">
<TextField size="small" placeholder="Small" icon={<Icon name="sparkling" />} />
<TextField size="medium" placeholder="Medium" icon={<Icon name="sparkling" />} />
</Flex>`}
/>
### With description
Here's a simple TextField with a description.
<Snippet
align="center"
py={4}
open
preview={<TextFieldSnippet story="WithDescription" />}
code={`<TextField label="Label" description="Description" placeholder="Enter a URL" />`}
/>
@@ -1,27 +0,0 @@
import { classNamePropDefs, stylePropDefs } from '../../../../utils/propDefs';
import type { PropDef } from '../../../../utils/propDefs';
export const inputPropDefs: Record<string, PropDef> = {
size: {
type: 'enum',
values: ['small', 'medium'],
default: 'small',
responsive: true,
},
label: {
type: 'string',
},
icon: {
type: 'enum',
values: ['ReactNode'],
},
description: {
type: 'string',
},
name: {
type: 'string',
required: true,
},
...classNamePropDefs,
...stylePropDefs,
};
@@ -1,139 +0,0 @@
import { PropsTable } from '@/components/PropsTable';
import { TextSnippet } from '@/snippets/stories-snippets';
import { Snippet } from '@/components/Snippet';
import { Tabs } from '@/components/Tabs';
import { CodeBlock } from '@/components/CodeBlock';
import { textPropDefs } from './props';
# Text
The `Text` component is used to display content on your page.
<Snippet
py={4}
preview={<TextSnippet story="Default" />}
code={`<Text style={{ maxWidth: '600px' }}>
A man looks at a painting in a museum and says, “Brothers and sisters I
have none, but that man&apos;s father is my father&apos;s son.” Who is in
the painting?
</Text>`}
/>
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Usage</Tabs.Tab>
<Tabs.Tab>Theming</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<CodeBlock
code={`import { Text } from '@backstage/canon';
<Text />
`}
/>
</Tabs.Panel>
<Tabs.Panel>
We recommend starting with our [global tokens](/theme/theming) to customize the library and align it with
your brand. For additional flexibility, you can use the provided class names for each element listed below.
<CodeBlock
code={`<Text className="text" />`}
/>
</Tabs.Panel>
</Tabs.Root>
## API reference
<PropsTable data={textPropDefs} />
## Examples
### All variants
The `Text` component has a `variant` prop that can be used to change the
appearance of the text.
<Snippet
open
preview={<TextSnippet story="AllVariants" />}
code={`<Flex direction="column" gap="4">
<Text variant="subtitle" style={{ maxWidth: '600px' }}>
A man looks at a painting in a museum and says, “Brothers and sisters I
have none, but that man&apos;s father is my father&apos;s son.” Who is
in the painting?
</Text>
<Text variant="body" style={{ maxWidth: '600px' }}>
A man looks at a painting in a museum and says, “Brothers and sisters I
have none, but that man&apos;s father is my father&apos;s son.” Who is
in the painting?
</Text>
<Text variant="caption" style={{ maxWidth: '600px' }}>
A man looks at a painting in a museum and says, “Brothers and sisters I
have none, but that man&apos;s father is my father&apos;s son.” Who is
in the painting?
</Text>
<Text variant="label" style={{ maxWidth: '600px' }}>
A man looks at a painting in a museum and says, “Brothers and sisters I
have none, but that man&apos;s father is my father&apos;s son.” Who is
in the painting?
</Text>
</Flex>`}
/>
### All weights
The `Text` component has a `weight` prop that can be used to change the
appearance of the text.
<Snippet
open
preview={<TextSnippet story="AllWeights" />}
code={`<Flex direction="column" gap="4">
<Text weight="regular" style={{ maxWidth: '600px' }}>
A man looks at a painting in a museum and says, “Brothers and sisters I
have none, but that man&apos;s father is my father&apos;s son.” Who is
in the painting?
</Text>
<Text weight="bold" style={{ maxWidth: '600px' }}>
A man looks at a painting in a museum and says, “Brothers and sisters I
have none, but that man&apos;s father is my father&apos;s son.” Who is
in the painting?
</Text>
</Flex>`}
/>
### Truncate
The `Text` component has a `truncate` prop that can be used to truncate the
text.
<Snippet
open
preview={<TextSnippet story="Truncate" />}
code={`<Text weight="regular" style={{ maxWidth: '600px' }} truncate>
A man looks at a painting in a museum and says, “Brothers and sisters I
have none, but that man&apos;s father is my father&apos;s son.” Who is
in the painting?
</Text>`}
/>
### Custom render
You can also use the `render` prop to render the text as a different element.
<Snippet
open
preview={<TextSnippet story="CustomRender" />}
code={`<Text render={<span />}>Custom render</Text>`}
/>
### Responsive
You can also use the `variant` prop to change the appearance of the text based
on the screen size.
<CodeBlock
code={`<Text variant={{ initial: 'body', lg: 'subtitle' }}>
Responsive text
</Text>`}
/>
@@ -1,26 +0,0 @@
import {
childrenPropDefs,
classNamePropDefs,
stylePropDefs,
} from '../../../../utils/propDefs';
import type { PropDef } from '../../../../utils/propDefs';
export const textPropDefs: Record<string, PropDef> = {
variant: {
type: 'enum',
values: ['display', 'title1', 'title2', 'title3', 'title4', 'title5'],
responsive: true,
},
weight: {
type: 'enum',
values: ['regular', 'bold'],
responsive: true,
},
truncate: {
type: 'boolean',
default: 'false',
},
...childrenPropDefs,
...classNamePropDefs,
...stylePropDefs,
};
-4
View File
@@ -40,10 +40,6 @@ import { Flex, Button, Text } from '@backstage/canon';
</Flex>;
```
## Roadmap
You can check our roadmap [on GitHub](https://github.com/orgs/backstage/projects/10). We'll do our best to keep you updated on the progress.
## Next steps
Now that you have the basics down, you can start building your plugin using the new design system.
@@ -1,80 +0,0 @@
# Releases
## Version 0.4.0
### Main updates
- ✨ Add new `Tab` component - [#29996](https://github.com/backstage/backstage/pull/29996)
- ✨ Add `truncate` prop to `Text` and `Heading` - [#29988](https://github.com/backstage/backstage/pull/29988)
- ✨ Add combobox option to `Menu` - [#29986](https://github.com/backstage/backstage/pull/29986)
- ✨ Add icon prop on `TextField` - [#29820](https://github.com/backstage/backstage/pull/29820)
- Improve icon props on `Button` and `IconButton` - [#29667](https://github.com/backstage/backstage/pull/29667)
- Improve the way we treat custom render on `Text` and `Heading` - [#29989](https://github.com/backstage/backstage/pull/29989)
- Improve `Menu` styles - [#29986](https://github.com/backstage/backstage/pull/29986)
- Improve `TextField` styles - [#29974](https://github.com/backstage/backstage/pull/29974)
- Improve clear button on `TextField` - [#29878](https://github.com/backstage/backstage/pull/29878)
### Notable fixes
- Fix spacing props on all layout components - [#30013](https://github.com/backstage/backstage/pull/30013)
- Fix - Pin Base UI version - [#29782](https://github.com/backstage/backstage/pull/29782)
- Fix - Clicking `Select` label moves focus to trigger - [#29755](https://github.com/backstage/backstage/pull/29755)
- Fix `DataTable.Pagination` count issue - [#29688](https://github.com/backstage/backstage/pull/29688)
## Version 0.3.0
### Main updates
- Add `DataTable` component - [#29484](https://github.com/backstage/backstage/pull/29484), [#29603](https://github.com/backstage/backstage/pull/29603)
- Add `Select` component - [#29440](https://github.com/backstage/backstage/pull/29440)
- Add `Avatar` component - [#29594](https://github.com/backstage/backstage/pull/29594)
- Add `Collapsible` component - [#29617](https://github.com/backstage/backstage/pull/29617)
- Add `TextField` component instead of `Field` + `Input` - [#29364](https://github.com/backstage/backstage/pull/29364)
- Add `TableCellProfile` - [#29600](https://github.com/backstage/backstage/pull/29600)
- Add breakpoint hooks - `up()` and `down()` - [#29564](https://github.com/backstage/backstage/pull/29564)
- Add gray scale css tokens - [#29543](https://github.com/backstage/backstage/pull/29543)
- Update CSS styling API using `[data-___]` instead of class names for props - [#29560](https://github.com/backstage/backstage/pull/29560)
- Update `Checkbox` dark mode - [#29544](https://github.com/backstage/backstage/pull/29544)
- Update `Container` styles - [#29475](https://github.com/backstage/backstage/pull/29475)
- Update `Menu` styles - [#29351](https://github.com/backstage/backstage/pull/29351)
- Fix `Select` styles on small sizes + with long option names - [#29545](https://github.com/backstage/backstage/pull/29545)
- Fix render prop on `Link` - [#29247](https://github.com/backstage/backstage/pull/29247)
- Remove `Field` from `TextField` + `Select` - [#29482](https://github.com/backstage/backstage/pull/29482)
- Update `textDecoration` to `none` on `Text` / `Heading` - [#29357](https://github.com/backstage/backstage/pull/29357)
### Notable fixes
- Docs - Use stories from Storybook for all examples in Nextjs - [#29306](https://github.com/backstage/backstage/pull/29306)
- Docs - Add release page (this one 🤗) - [#29461](https://github.com/backstage/backstage/pull/29461)
- Docs - Add docs for Menu, Link - [#29576](https://github.com/backstage/backstage/pull/29576)
- Fix CSS watch mode - [#29352](https://github.com/backstage/backstage/pull/29352)
## Version 0.2.0
### Main updates
- New `Tooltip` component - [#29241](https://github.com/backstage/backstage/pull/29241)
- New `Menu` component - [#29151](https://github.com/backstage/backstage/pull/29151)
- New `IconButton` component - [#29239](https://github.com/backstage/backstage/pull/29239)
- New `ScrollArea` component - [#29240](https://github.com/backstage/backstage/pull/29240)
- Improve `Button` & `Checkbox` styles - [#29127](https://github.com/backstage/backstage/pull/29127), [#28789](https://github.com/backstage/backstage/pull/28789)
- Improve `Text` styles - [#29200](https://github.com/backstage/backstage/pull/29200)
- Renamed `CanonProvider` to `IconProvider` - [#29002](https://github.com/backstage/backstage/pull/29002)
- Added about 40+ new icons - [#29264](https://github.com/backstage/backstage/pull/29264)
- Simplified styling into a unique styles.css file - [#29199](https://github.com/backstage/backstage/pull/29199)
- Added Canon styles to Backstage - [#29137](https://github.com/backstage/backstage/pull/29137)
- Update global CSS tokens - [#28804](https://github.com/backstage/backstage/pull/28804)
- Merge Stack + Inline into Flex - [#28634](https://github.com/backstage/backstage/pull/28634)
### Notable fixes:
- Improve `Button` types - [#29205](https://github.com/backstage/backstage/pull/29205)
- Move font weight and family back to each components - [#28972](https://github.com/backstage/backstage/pull/28972)
- Fix custom values in spacing props - [#28770](https://github.com/backstage/backstage/pull/28770)
- Multiple updates on the Canon Docs site - [#28760](https://github.com/backstage/backstage/pull/28760), [#28591](https://github.com/backstage/backstage/pull/28591)
## Version 0.1.0
We're excited to share the initial release of Canon 💚 In this first alpha version, you'll find the foundation of our design system:
a set of versatile layout components and a handful of essential atomic elements to help you get started. While Canon is still in its
early stages, it's ready for exploration and we'd love for you to give it a try and share your feedback.
@@ -1,18 +1,3 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import styles from './styles.module.css';
export const Banner = ({
-15
View File
@@ -1,16 +1 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { Banner } from './Banner';
@@ -0,0 +1,43 @@
import { changelog } from '@/utils/changelog';
import { MDXRemote } from 'next-mdx-remote-client/rsc';
import { formattedMDXComponents } from '@/mdx-components';
export function Changelog() {
// Group changelog entries by version
const groupedChangelog = changelog.reduce((acc, entry) => {
if (!acc[entry.version]) {
acc[entry.version] = [];
}
acc[entry.version].push(entry);
return acc;
}, {} as Record<string, typeof changelog>);
// Sort versions in descending order
const sortedVersions = Object.keys(groupedChangelog).sort((a, b) =>
b.localeCompare(a),
);
const content = sortedVersions
.map(version => {
const entries = groupedChangelog[version];
return `## Version ${version}
${entries
.map(e => {
const prs =
e.prs.length > 0 &&
e.prs
.map(
pr =>
`[#${pr}](https://github.com/backstage/backstage/pull/${pr})`,
)
.join(', ');
return `- ${e.description} ${prs}`;
})
.join('\n')}`;
})
.join('\n');
return <MDXRemote components={formattedMDXComponents} source={content} />;
}
-16
View File
@@ -1,19 +1,3 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ReactNode } from 'react';
import styles from './styles.module.css';
-15
View File
@@ -1,16 +1 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { Chip } from './Chip';
@@ -1,19 +1,3 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.chip {
display: inline-flex;
align-items: center;
@@ -39,5 +23,5 @@
}
[data-theme='dark'] .chip.head {
background-color: #26417b;
background-color: #33405b;
}
@@ -1,8 +1,9 @@
'use client';
import { CodeBlockProps } from '.';
import { Text } from '../../../../packages/canon';
import { Text } from '@backstage/canon';
import styles from './styles.module.css';
import parse from 'html-react-parser';
export const CodeBlockClient = ({
out,
@@ -18,7 +19,7 @@ export const CodeBlockClient = ({
<Text variant="body">{title}</Text>
</div>
)}
<div dangerouslySetInnerHTML={{ __html: out }} className={styles.code} />
<div className={styles.code}>{parse(out)}</div>
</div>
);
};
@@ -1,19 +1,3 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ReactNode, CSSProperties } from 'react';
export const Columns = ({
@@ -1,16 +1 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { Columns } from './Columns';
@@ -1,19 +1,3 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.columns {
display: grid;
grid-template-columns: repeat(3, 1fr);
@@ -0,0 +1,68 @@
import { Tabs } from '@/components/Tabs';
import { CodeBlock } from '@/components/CodeBlock';
import type { ComponentInfosProps } from './types';
import styles from '@/css/mdx.module.css';
import Link from 'next/link';
import { changelog } from '@/utils/changelog';
import { MDXRemote } from 'next-mdx-remote-client/rsc';
import { formattedMDXComponents } from '@/mdx-components';
export const ComponentInfos = ({
usageCode,
component,
classNames,
}: ComponentInfosProps) => {
const componentChangelog = changelog.filter(c =>
c.components.includes(component),
);
return (
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Usage</Tabs.Tab>
{classNames && classNames.length > 0 && <Tabs.Tab>Theming</Tabs.Tab>}
<Tabs.Tab>Changelog</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<CodeBlock code={usageCode} />
</Tabs.Panel>
{classNames && classNames.length > 0 && (
<Tabs.Panel>
<p className={styles.p}>
We recommend starting with our{' '}
<Link className={styles.a} href="/theme/theming">
global tokens
</Link>{' '}
to customize the library and align it with your brand. For
additional flexibility, you can use the provided class names for
each element listed below.
</p>
<MDXRemote
components={formattedMDXComponents}
source={`${classNames
?.map(className => `- \`${className}\``)
.join('\n')}`}
/>
</Tabs.Panel>
)}
<Tabs.Panel>
<MDXRemote
components={formattedMDXComponents}
source={`${componentChangelog
?.map(change => {
const prs =
change.prs.length > 0 &&
change.prs
.map(
pr =>
`[#${pr}](https://github.com/backstage/backstage/pull/${pr})`,
)
.join(', ');
return `- \`${change.version}\` - ${change.description} ${prs}`;
})
.join('\n')}`}
/>
</Tabs.Panel>
</Tabs.Root>
);
};
@@ -0,0 +1 @@
export { ComponentInfos } from './ComponentInfos';
@@ -0,0 +1,9 @@
import type { Component } from '@/utils/changelog';
import { ReactNode } from 'react';
export interface ComponentInfosProps {
usageCode?: string;
classNames?: string[];
component: Component;
children: ReactNode;
}
@@ -6,7 +6,7 @@ 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/canon';
import { Icon } from '@backstage/canon';
import { createTheme } from '@uiw/codemirror-themes';
import { tags as t } from '@lezer/highlight';
@@ -37,7 +37,9 @@
justify-content: space-between;
align-items: center;
padding: 0 12px 0 16px;
transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out;
transition:
background-color 0.2s ease-in-out,
border-color 0.2s ease-in-out;
}
.headerLeft {
@@ -1,4 +1,3 @@
/* eslint-disable @next/next/no-css-tags */
'use client';
import { useEffect, useState } from 'react';
@@ -1,6 +1,6 @@
'use client';
import { Icon, Text } from '../../../../packages/canon';
import { Icon, Text } from '@backstage/canon';
import styles from './styles.module.css';
interface BaseUIProps {
@@ -1,22 +1,7 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use client';
import { Text, Icon, icons } from '../../../../packages/canon';
import type { IconNames } from '../../../../packages/canon';
import { Text, Icon, icons } from '@backstage/canon';
import type { IconNames } from '@backstage/canon';
import styles from './styles.module.css';
export const IconLibrary = () => {
@@ -1,16 +1 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { IconLibrary } from './IconLibrary';
@@ -17,12 +17,12 @@
justify-content: center;
align-items: center;
height: 80px;
border: 1px solid #d3d3d3;
border-radius: 0.5rem;
background-color: var(--bg);
border: 1px solid var(--border);
background-color: var(--panel);
transition: background-color 0.2s ease-in-out;
&:hover {
background-color: var(--panel);
background-color: var(--surface-1);
}
}
@@ -1,36 +1,26 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
.layoutComponents {
display: flex;
justify-content: flex-start;
gap: 1rem;
flex-wrap: wrap;
margin-top: 2rem;
& svg rect {
transition: fill 0.2s ease-in-out;
}
& .box {
display: flex;
flex-direction: column;
width: calc(33.33% - 0.67rem);
width: calc(50% - 0.5rem);
margin-bottom: 1rem;
align-items: flex-start;
}
& .content {
flex: none;
background-color: #fff;
background-color: var(--panel);
border: 1px solid var(--border);
border-radius: 4px;
width: 100%;
height: 180px;
@@ -53,6 +43,6 @@
& .description {
font-size: 16px;
color: #9e9e9e;
color: var(--secondary);
}
}
@@ -1,7 +1,6 @@
import { BoxSvg } from './svgs/box';
import { FlexSvg } from './svgs/flex';
import { GridSvg } from './svgs/grid';
import { InlineSvg } from './svgs/inline';
import { ContainerSvg } from './svgs/container';
import styles from './LayoutComponents.module.css';
import Link from 'next/link';
@@ -36,15 +35,6 @@ export const LayoutComponents = () => {
Arrange your components in a grid
</div>
</div>
<div className={styles.box}>
<Link className={styles.content} href="/components/inline">
<InlineSvg />
</Link>
<div className={styles.title}>Inline</div>
<div className={styles.description}>
Arrange your components in a row
</div>
</div>
<div className={styles.box}>
<Link className={styles.content} href="/components/container">
<ContainerSvg />
@@ -1,16 +1 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { LayoutComponents } from './LayoutComponents';
@@ -1,18 +1,3 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const BoxSvg = () => {
return (
<svg
@@ -22,7 +7,7 @@ export const BoxSvg = () => {
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="100" height="60" rx="4" fill="black" fillOpacity="0.06" />
<rect width="100" height="60" rx="4" fill="var(--surface-1)" />
<path
d="M94.5 0.5H96C97.933 0.5 99.5 2.067 99.5 4V5.5"
stroke="#4765FF"
@@ -1,18 +1,3 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const ContainerSvg = () => {
return (
<svg
@@ -178,8 +163,7 @@ export const ContainerSvg = () => {
width="100"
height="60"
rx="4"
fill="black"
fillOpacity="0.06"
fill="var(--surface-1)"
/>
<mask
id="mask1_1922_1559"
@@ -1,18 +1,3 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const FlexSvg = () => {
return (
<svg
@@ -22,7 +7,7 @@ export const FlexSvg = () => {
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="100" height="24" rx="4" fill="black" fillOpacity="0.06" />
<rect width="100" height="24" rx="4" fill="var(--surface-1)" />
<path
fillRule="evenodd"
clipRule="evenodd"
@@ -41,14 +26,7 @@ export const FlexSvg = () => {
d="M44 36C43.7239 36 43.5 35.7761 43.5 35.5V35.5C43.5 35.2239 43.7239 35 44 35L56 35C56.2761 35 56.5 35.2239 56.5 35.5V35.5C56.5 35.7761 56.2761 36 56 36L44 36Z"
fill="#4765FF"
/>
<rect
y="36"
width="100"
height="24"
rx="4"
fill="black"
fillOpacity="0.06"
/>
<rect y="36" width="100" height="24" rx="4" fill="var(--surface-1)" />
<path
fillRule="evenodd"
clipRule="evenodd"
@@ -67,14 +45,7 @@ export const FlexSvg = () => {
d="M44 72C43.7239 72 43.5 71.7761 43.5 71.5V71.5C43.5 71.2239 43.7239 71 44 71L56 71C56.2761 71 56.5 71.2239 56.5 71.5V71.5C56.5 71.7761 56.2761 72 56 72L44 72Z"
fill="#4765FF"
/>
<rect
y="72"
width="100"
height="24"
rx="4"
fill="black"
fillOpacity="0.06"
/>
<rect y="72" width="100" height="24" rx="4" fill="var(--surface-1)" />
</svg>
);
};
@@ -1,18 +1,3 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const GridSvg = () => {
return (
<svg
@@ -22,14 +7,7 @@ export const GridSvg = () => {
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect
x="0.5"
width="52"
height="61"
rx="4"
fill="black"
fillOpacity="0.06"
/>
<rect x="0.5" width="52" height="61" rx="4" fill="var(--surface-1)" />
<path
fillRule="evenodd"
clipRule="evenodd"
@@ -54,8 +32,7 @@ export const GridSvg = () => {
width="100"
height="24"
rx="4"
fill="black"
fillOpacity="0.06"
fill="var(--surface-1)"
/>
<path
fillRule="evenodd"
@@ -81,8 +58,7 @@ export const GridSvg = () => {
width="100"
height="24"
rx="4"
fill="black"
fillOpacity="0.06"
fill="var(--surface-1)"
/>
</svg>
);
@@ -1,90 +0,0 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export const InlineSvg = () => {
return (
<svg
width="159"
height="56"
viewBox="0 0 159 56"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<rect width="24" height="24" rx="4" fill="black" fillOpacity="0.06" />
<rect
x="32"
width="24"
height="24"
rx="4"
fill="black"
fillOpacity="0.06"
/>
<rect
x="64"
width="24"
height="24"
rx="4"
fill="black"
fillOpacity="0.06"
/>
<rect
x="96"
width="24"
height="24"
rx="4"
fill="black"
fillOpacity="0.06"
/>
<rect
x="128"
width="24"
height="24"
rx="4"
fill="black"
fillOpacity="0.06"
/>
<rect
y="32"
width="24"
height="24"
rx="4"
fill="black"
fillOpacity="0.06"
/>
<rect
x="32"
y="32"
width="24"
height="24"
rx="4"
fill="black"
fillOpacity="0.06"
/>
<rect
x="64"
y="32"
width="24"
height="24"
rx="4"
fill="black"
fillOpacity="0.06"
/>
<path
d="M12 12H138.811C141.238 12 141.569 15.5106 139.185 15.9647L12.8145 40.0353C10.4306 40.4894 10.762 44 13.1888 44H77"
stroke="#4765FF"
/>
</svg>
);
};
@@ -2,7 +2,7 @@
import * as Table from '../Table';
import { Chip } from '../Chip';
import { icons } from '../../../../packages/canon';
import { icons } from '@backstage/canon';
// Define a more specific type for the data object
type PropData = {
@@ -53,6 +53,12 @@ export const PropsTable = <T extends Record<string, PropData>>({
{data[n].type === 'number' && <Chip>number</Chip>}
{data[n].type === 'boolean' && <Chip>boolean</Chip>}
{data[n].type === 'enum' && enumValues}
{data[n].type === 'spacing' && (
<>
<Chip>0.5, 1, 1.5, 2, 3, ..., 14</Chip>
<Chip>string</Chip>
</>
)}
{data[n].type === 'enum | string' && (
<>
{enumValues}
@@ -1,16 +1 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { PropsTable } from './PropsTable';
@@ -1,18 +1,3 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { RoadmapItem } from './list';
export const Roadmap = ({ list }: { list: RoadmapItem[] }) => {
@@ -1,16 +1 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { Roadmap } from './Roadmap';
-15
View File
@@ -1,18 +1,3 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export type RoadmapItem = {
title: string;
status: 'notStarted' | 'inProgress' | 'inReview' | 'completed';
@@ -14,7 +14,10 @@
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.025);
color: var(--primary);
background-color: var(--panel);
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
border: 1px solid var(--border);
transition:
background-color 0.2s ease-in-out,
color 0.2s ease-in-out;
overflow: hidden;
}
}
@@ -76,12 +79,12 @@
transition: background-color 0.2s ease-in-out;
&:hover {
background-color: var(--bg);
background-color: var(--surface-1);
}
}
.line.active {
background-color: var(--bg);
background-color: var(--surface-1);
}
.line.active .lineTitle {
@@ -89,13 +92,13 @@
}
.lineTitle {
font-size: var(--canon-font-size-3);
font-weight: var(--canon-font-weight-regular);
font-size: 14px;
font-weight: 400;
color: var(--secondary);
}
.lineStatus {
font-size: var(--canon-font-size-3);
font-size: 14px;
color: var(--secondary);
}
@@ -1,7 +1,7 @@
'use client';
import { components } from '@/utils/data';
import { Checkbox } from '../../../../packages/canon';
import { Checkbox } from '@backstage/canon';
import { motion } from 'motion/react';
import styles from './Sidebar.module.css';
import { usePathname } from 'next/navigation';
@@ -0,0 +1,51 @@
'use client';
import { CSSProperties, useState } from 'react';
import { Collapsible } from '@base-ui-components/react/collapsible';
import styles from './styles.module.css';
interface SnippetProps {
preview: JSX.Element;
codeContent: JSX.Element;
align?: 'left' | 'center';
px?: number;
py?: number;
open?: boolean;
height?: CSSProperties['height'];
}
export const SnippetClient = ({
preview,
codeContent,
align = 'left',
px = 2,
py = 2,
open = false,
height = 'auto',
}: SnippetProps) => {
const [isOpen, setIsOpen] = useState(open);
return (
<Collapsible.Root
className={styles.container}
defaultOpen={open}
open={isOpen}
onOpenChange={setIsOpen}
>
<div className={styles.preview} style={{ height }}>
<div
className={`${styles.previewContent} ${styles[align]}`}
style={{ padding: `${py}rem ${px}rem` }}
>
{preview}
</div>
<Collapsible.Trigger className={styles.trigger}>
{isOpen ? 'Hide code' : 'View code'}
</Collapsible.Trigger>
</div>
<Collapsible.Panel className={styles.panel}>
{codeContent}
</Collapsible.Panel>
</Collapsible.Root>
);
};
+12 -20
View File
@@ -1,10 +1,9 @@
import { ReactNode, CSSProperties } from 'react';
import { CSSProperties } from 'react';
import { CodeBlock } from '../CodeBlock';
import { Collapsible } from '@base-ui-components/react/collapsible';
import styles from './styles.module.css';
import { SnippetClient } from './client';
interface SnippetProps {
preview: ReactNode;
preview: JSX.Element;
code: string;
align?: 'left' | 'center';
px?: number;
@@ -23,21 +22,14 @@ export const Snippet = ({
height = 'auto',
}: SnippetProps) => {
return (
<Collapsible.Root className={styles.container} defaultOpen={open}>
<div className={styles.preview} style={{ height }}>
<div
className={`${styles.previewContent} ${styles[align]}`}
style={{ padding: `${py}rem ${px}rem` }}
>
{preview}
</div>
<Collapsible.Trigger className={styles.trigger}>
View code
</Collapsible.Trigger>
</div>
<Collapsible.Panel className={styles.panel}>
<CodeBlock code={code} />
</Collapsible.Panel>
</Collapsible.Root>
<SnippetClient
preview={preview}
codeContent={<CodeBlock code={code} />}
align={align}
px={px}
py={py}
open={open}
height={height}
/>
);
};
@@ -38,7 +38,7 @@
[data-theme='dark'] .previewContent {
background-image: radial-gradient(
rgba(255, 255, 255, 0.14) 1px,
rgba(255, 255, 255, 0.18) 1px,
transparent 0
);
}
-15
View File
@@ -1,16 +1 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { Root, Header, Body, Row, Cell, HeaderRow, HeaderCell } from './Table';
+1 -1
View File
@@ -1,7 +1,7 @@
'use client';
import { Tabs as TabsPrimitive } from '@base-ui-components/react/tabs';
import { Text } from '../../../../packages/canon';
import { Text } from '@backstage/canon';
import styles from './styles.module.css';
export const Root = ({
@@ -13,7 +13,9 @@
width: 142px;
border-radius: 0.375rem;
background-color: var(--bg);
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
transition:
background-color 0.2s ease-in-out,
color 0.2s ease-in-out;
}
.list {
@@ -13,6 +13,7 @@
padding: 0 var(--canon-space-6);
transition: background-color 0.2s ease-in-out;
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.025);
border: 1px solid var(--border);
}
.actions {
@@ -17,7 +17,7 @@
color: var(--color-gray-900);
cursor: pointer;
user-select: none;
background-color: var(--bg);
background-color: var(--surface-1);
transition: background-color 0.2s ease-in-out;
&:focus-visible {
@@ -48,7 +48,9 @@
border: 1px solid var(--border);
padding-inline: 0.25rem;
transform-origin: var(--transform-origin);
transition: transform 150ms, opacity 150ms;
transition:
transform 150ms,
opacity 150ms;
&[data-starting-style],
&[data-ending-style] {
@@ -64,7 +66,8 @@
@media (prefers-color-scheme: light) {
outline: 1px solid var(--color-gray-200);
box-shadow: 0px 10px 15px -3px var(--color-gray-200),
box-shadow:
0px 10px 15px -3px var(--color-gray-200),
0px 4px 6px -4px var(--color-gray-200);
}
@@ -134,4 +137,5 @@
.ItemText {
grid-column-start: 2;
font-size: 14px;
}
@@ -1,15 +1,19 @@
.tabs {
border-radius: 0.375rem;
width: 100%;
background-color: var(--bg);
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
background-color: var(--surface-1);
transition:
background-color 0.2s ease-in-out,
color 0.2s ease-in-out;
}
.tabsTheme {
width: 100px;
border-radius: 0.375rem;
background-color: var(--bg);
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
background-color: var(--surface-1);
transition:
background-color 0.2s ease-in-out,
color 0.2s ease-in-out;
}
.list {
@@ -0,0 +1,65 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
import { AvatarSnippet } from '@/snippets/stories-snippets';
import {
avatarPropDefs,
snippetUsage,
snippetSizes,
snippetFallback,
} from './avatar.props';
import { ComponentInfos } from '@/components/ComponentInfos';
# Avatar
An avatar component with a fallback for initials.
<Snippet
align="center"
py={4}
preview={<AvatarSnippet story="Default" />}
code={`<Avatar src="https://avatars.githubusercontent.com/u/1540635?v=4" name="Charles de Dreuille" />`}
/>
<ComponentInfos
component="avatar"
usageCode={snippetUsage}
classNames={[
'canon-AvatarRoot',
'canon-AvatarRoot[data-size="small"]',
'canon-AvatarRoot[data-size="medium"]',
'canon-AvatarRoot[data-size="large"]',
'canon-AvatarImage',
'canon-AvatarFallback',
]}
/>
## API reference
<PropsTable data={avatarPropDefs} />
## Examples
### Sizes
Avatar sizes can be set using the `size` prop.
<Snippet
align="center"
py={4}
open
preview={<AvatarSnippet story="Sizes" />}
code={snippetSizes}
/>
### Fallback
If the image is not available, the avatar will show the initials of the name.
<Snippet
align="center"
py={4}
open
preview={<AvatarSnippet story="Fallback" />}
code={snippetFallback}
/>
@@ -0,0 +1,46 @@
import { classNamePropDefs, stylePropDefs } from '@/utils/propDefs';
import type { PropDef } from '@/utils/propDefs';
export const avatarPropDefs: Record<string, PropDef> = {
src: {
type: 'string',
},
name: {
type: 'string',
},
size: {
type: 'enum',
values: ['small', 'medium', 'large'],
default: 'medium',
responsive: true,
},
...classNamePropDefs,
...stylePropDefs,
};
export const snippetUsage = `import { Avatar } from '@backstage/canon';
<Avatar
src="https://avatars.githubusercontent.com/u/1540635?v=4"
name="Charles de Dreuille"
/>`;
export const snippetSizes = `<Flex gap="4" direction="column">
<Avatar
src="https://avatars.githubusercontent.com/u/1540635?v=4"
name="Charles de Dreuille" size="small"
/>
<Avatar
src="https://avatars.githubusercontent.com/u/1540635?v=4"
name="Charles de Dreuille" size="medium"
/>
<Avatar
src="https://avatars.githubusercontent.com/u/1540635?v=4"
name="Charles de Dreuille" size="large"
/>
</Flex>`;
export const snippetFallback = `<Avatar
src="https://avatars.githubusercontent.com/u/15406AAAAAAAAA"
name="Charles de Dreuille"
/>`;
@@ -1,10 +1,16 @@
import { CodeBlock } from '@/components/CodeBlock';
import { PropsTable } from '@/components/PropsTable';
import { Tabs } from '@/components/Tabs';
import { Snippet } from '@/components/Snippet';
import { BoxSnippet } from '@/snippets/stories-snippets';
import { boxPropDefs } from './props';
import { spacingPropDefs } from '../../../../utils/propDefs';
import {
boxPropDefs,
snippetUsage,
boxPreviewSnippet,
boxSimpleSnippet,
boxResponsiveSnippet,
} from './box.props';
import { spacingPropDefs } from '@/utils/propDefs';
import { ComponentInfos } from '@/components/ComponentInfos';
# Box
@@ -13,22 +19,11 @@ Box is the lowest-level component in Canon. It provides a consistent API for sty
<Snippet
py={4}
preview={<BoxSnippet story="Preview" />}
code={`<Box>
<DecorativeBox />
</Box>`}
code={boxPreviewSnippet}
align="center"
/>
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Usage</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<CodeBlock code={`import { Box } from "@backstage/canon";
<Box />`} />
</Tabs.Panel>
</Tabs.Root>
<ComponentInfos component="box" usageCode={snippetUsage} />
## API reference
@@ -51,16 +46,10 @@ avoid collapsing margins but both are available.
A simple example of how to use the Box component.
<CodeBlock code={`<Box padding="md" borderRadius="md">Hello World</Box>`} />
<CodeBlock code={boxSimpleSnippet} />
### Responsive
Here's a view when buttons are responsive.
<CodeBlock
code={`<Box
padding={{ xs: 'sm', md: 'md' }}
borderRadius={{ xs: 'sm', md: 'md' }}>
Hello World
</Box>`}
/>
<CodeBlock code={boxResponsiveSnippet} />
@@ -0,0 +1,40 @@
import {
classNamePropDefs,
displayPropDefs,
heightPropDefs,
positionPropDefs,
stylePropDefs,
widthPropDefs,
type PropDef,
} from '@/utils/propDefs';
export const boxPropDefs: Record<string, PropDef> = {
as: {
type: 'enum',
values: ['div', 'span'],
default: 'div',
responsive: true,
},
...widthPropDefs,
...heightPropDefs,
...positionPropDefs,
...displayPropDefs,
...classNamePropDefs,
...stylePropDefs,
};
export const snippetUsage = `import { Box } from '@backstage/canon';
<Box />`;
export const boxPreviewSnippet = `<Box>
<DecorativeBox />
</Box>`;
export const boxSimpleSnippet = `<Box padding="md" borderRadius="md">Hello World</Box>`;
export const boxResponsiveSnippet = `<Box
padding={{ xs: 'sm', md: 'md' }}
borderRadius={{ xs: 'sm', md: 'md' }}>
Hello World
</Box>`;
@@ -0,0 +1,104 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
import { ButtonSnippet } from '@/snippets/stories-snippets';
import {
buttonPropDefs,
buttonSnippetUsage,
buttonVariantsSnippet,
buttonSizesSnippet,
buttonIconsSnippet,
buttonFullWidthSnippet,
buttonDisabledSnippet,
buttonResponsiveSnippet,
} from './button.props';
import { ComponentInfos } from '@/components/ComponentInfos';
# Button
A button component that can be used to trigger actions.
<Snippet
align="center"
py={4}
preview={<ButtonSnippet story="Variants" />}
code={buttonVariantsSnippet}
/>
<ComponentInfos
component="button"
classNames={['canon-Button', 'canon-ButtonIcon']}
usageCode={buttonSnippetUsage}
/>
## API reference
<PropsTable data={buttonPropDefs} />
## Examples
### Variants
Here's a view when buttons have different variants.
<Snippet
align="center"
py={4}
open
preview={<ButtonSnippet story="Variants" />}
code={buttonVariantsSnippet}
/>
### Sizes
Here's a view when buttons have different sizes.
<Snippet
align="center"
py={4}
open
preview={<ButtonSnippet story="Sizes" />}
code={buttonSizesSnippet}
/>
### With Icons
Here's a view when buttons have icons.
<Snippet
align="center"
py={4}
open
preview={<ButtonSnippet story="WithIcons" />}
code={buttonIconsSnippet}
/>
### Full width
Here's a view when buttons are full width.
<Snippet
align="center"
py={4}
open
preview={<ButtonSnippet story="FullWidth" />}
code={buttonFullWidthSnippet}
/>
### Disabled
Here's a view when buttons are disabled.
<Snippet
align="center"
py={4}
open
preview={<ButtonSnippet story="Disabled" />}
code={buttonDisabledSnippet}
/>
### Responsive
Here's a view when buttons are responsive.
<CodeBlock code={buttonResponsiveSnippet} />
@@ -0,0 +1,63 @@
import { classNamePropDefs, stylePropDefs } from '../../utils/propDefs';
import type { PropDef } from '../../utils/propDefs';
export const buttonPropDefs: Record<string, PropDef> = {
variant: {
type: 'enum',
values: ['primary', 'secondary'],
default: 'primary',
responsive: true,
},
size: {
type: 'enum',
values: ['small', 'medium'],
default: 'medium',
responsive: true,
},
...classNamePropDefs,
...stylePropDefs,
};
export const buttonSnippetUsage = `import { Button } from '@backstage/canon';
<Button />`;
export const buttonVariantsSnippet = `<Flex align="center">
<Button iconStart="cloud" variant="primary">
Button
</Button>
<Button iconStart="cloud" variant="secondary">
Button
</Button>
<Button iconStart="cloud" variant="tertiary">
Button
</Button>
</Flex>`;
export const buttonSizesSnippet = `<Flex align="center">
<Button size="small">Small</Button>
<Button size="medium">Medium</Button>
</Flex>`;
export const buttonIconsSnippet = `<Flex align="center">
<Button iconStart="cloud">Button</Button>
<Button iconEnd="chevronRight">Button</Button>
<Button iconStart="cloud" iconEnd="chevronRight">Button</Button>
</Flex>`;
export const buttonFullWidthSnippet = `<Flex direction="column" gap="4" style={{ width: '300px' }}>
<Button fullWidth>Full width</Button>
</Flex>`;
export const buttonDisabledSnippet = `<Flex gap="4">
<Button variant="primary" disabled>
Primary
</Button>
<Button variant="secondary" disabled>
Secondary
</Button>
</Flex>`;
export const buttonResponsiveSnippet = `<Button variant={{ initial: 'primary', lg: 'secondary' }}>
Responsive Button
</Button>`;
@@ -0,0 +1,46 @@
import { PropsTable } from '@/components/PropsTable';
import { CheckboxSnippet } from '@/snippets/stories-snippets';
import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
import {
checkboxPropDefs,
checkboxUsageSnippet,
checkboxDefaultSnippet,
checkboxVariantsSnippet,
} from './checkbox.props';
import { ComponentInfos } from '@/components/ComponentInfos';
# Checkbox
A checkbox component that can be used to trigger actions.
<Snippet
align="center"
py={4}
preview={<CheckboxSnippet story="Default" />}
code={checkboxDefaultSnippet}
/>
<ComponentInfos
component="checkbox"
classNames={['canon-Checkbox']}
usageCode={checkboxUsageSnippet}
/>
## API reference
<PropsTable data={checkboxPropDefs} />
## Examples
### All variants
Here's a view when checkboxes have different variants.
<Snippet
align="center"
py={4}
open
preview={<CheckboxSnippet story="AllVariants" />}
code={checkboxVariantsSnippet}
/>
@@ -1,5 +1,8 @@
import { classNamePropDefs, stylePropDefs } from '../../../../utils/propDefs';
import type { PropDef } from '../../../../utils/propDefs';
import {
classNamePropDefs,
stylePropDefs,
type PropDef,
} from '@/utils/propDefs';
export const checkboxPropDefs: Record<string, PropDef> = {
label: {
@@ -42,3 +45,16 @@ export const checkboxPropDefs: Record<string, PropDef> = {
...classNamePropDefs,
...stylePropDefs,
};
export const checkboxUsageSnippet = `import { Checkbox } from '@backstage/canon';
<Checkbox />`;
export const checkboxDefaultSnippet = `<Checkbox label="Accept terms and conditions" />`;
export const checkboxVariantsSnippet = `<Inline alignY="center">
<Checkbox />
<Checkbox checked />
<Checkbox label="Checkbox" />
<Checkbox label="Checkbox" checked />
</Inline>`;
@@ -0,0 +1,71 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
import { CollapsibleSnippet } from '@/snippets/stories-snippets';
import {
collapsibleRootPropDefs,
collapsibleTriggerPropDefs,
collapsiblePanelPropDefs,
collapsibleUsageSnippet,
collapsibleDefaultSnippet,
collapsibleTriggerSnippet,
collapsibleOpenSnippet,
} from './collapsible.props';
import { ComponentInfos } from '@/components/ComponentInfos';
# Collapsible
An avatar component with a fallback for initials.
<Snippet
align="center"
py={4}
height={240}
preview={<CollapsibleSnippet story="Default" />}
code={collapsibleDefaultSnippet}
/>
<ComponentInfos
component="collapsible"
classNames={[
'canon-CollapsibleRoot',
'canon-CollapsibleTrigger',
'canon-CollapsiblePanel',
]}
usageCode={collapsibleUsageSnippet}
/>
## API reference
### Collapsible.Root
Groups all parts of the collapsible. Renders a `<div>` element.
<PropsTable data={collapsibleRootPropDefs} />
### Collapsible.Trigger
The trigger by default render a simple unstyled button. Because menus can be rendered in different ways, we recommend
using the `render` prop to render a custom trigger.
<CodeBlock code={collapsibleTriggerSnippet} />
<PropsTable data={collapsibleTriggerPropDefs} />
### Collapsible.Panel
A panel with the collapsible contents. Renders a `<div>` element.
<PropsTable data={collapsiblePanelPropDefs} />
## Examples
Open the panel by default by setting the `defaultOpen` prop to `true`.
<Snippet
align="center"
py={4}
open
preview={<CollapsibleSnippet story="Open" />}
code={collapsibleOpenSnippet}
/>
@@ -0,0 +1,86 @@
import {
classNamePropDefs,
stylePropDefs,
renderPropDefs,
type PropDef,
} from '@/utils/propDefs';
export const collapsibleRootPropDefs: Record<string, PropDef> = {
defaultOpen: {
type: 'boolean',
default: 'false',
},
open: {
type: 'boolean',
},
onOpenChange: {
type: 'enum',
values: ['(open) => void'],
},
...renderPropDefs,
...classNamePropDefs,
...stylePropDefs,
};
export const collapsibleTriggerPropDefs: Record<string, PropDef> = {
...renderPropDefs,
...classNamePropDefs,
...stylePropDefs,
};
export const collapsiblePanelPropDefs: Record<string, PropDef> = {
hiddenUntilFound: {
type: 'boolean',
default: 'false',
},
keepMounted: {
type: 'boolean',
default: 'false',
},
...renderPropDefs,
...classNamePropDefs,
...stylePropDefs,
};
export const collapsibleUsageSnippet = `import { Collapsible } from '@backstage/canon';
<Collapsible.Root>
<Collapsible.Trigger render={(props, state) => (
<Button {...props}>
{state.open ? 'Close Panel' : 'Open Panel'}
</Button>
)} />
<Collapsible.Panel>Your content</Collapsible.Panel>
</Collapsible.Root>`;
export const collapsibleDefaultSnippet = `<Collapsible.Root>
<Collapsible.Trigger render={(props, state) => (
<Button {...props}>
{state.open ? 'Close Panel' : 'Open Panel'}
</Button>
)} />
<Collapsible.Panel>
<Box>
<Text>It's the edge of the world and all of Western civilization</Text>
<Text>The sun may rise in the East, at least it settled in a final location</Text>
<Text>It's understood that Hollywood sells Californication</Text>
</Box>
</Collapsible.Panel>
</Collapsible.Root>`;
export const collapsibleTriggerSnippet = `<Collapsible.Trigger render={props => <Button {...props} />} />`;
export const collapsibleOpenSnippet = `<Collapsible.Root defaultOpen>
<Collapsible.Trigger render={(props, state) => (
<Button {...props}>
{state.open ? 'Close Panel' : 'Open Panel'}
</Button>
)} />
<Collapsible.Panel>
<Box>
<Text>It's the edge of the world and all of Western civilization</Text>
<Text>The sun may rise in the East, at least it settled in a final location</Text>
<Text>It's understood that Hollywood sells Californication</Text>
</Box>
</Collapsible.Panel>
</Collapsible.Root>`;
@@ -1,9 +1,15 @@
import { CodeBlock } from '@/components/CodeBlock';
import { PropsTable } from '@/components/PropsTable';
import { Tabs } from '@/components/Tabs';
import { Snippet } from '@/components/Snippet';
import { ContainerSnippet } from '@/snippets/stories-snippets';
import { containerPropDefs } from './props';
import {
containerPropDefs,
containerUsageSnippet,
containerDefaultSnippet,
containerSimpleSnippet,
containerResponsiveSnippet,
} from './container.props';
import { ComponentInfos } from '@/components/ComponentInfos';
# Container
@@ -13,22 +19,14 @@ content on the page.
<Snippet
py={4}
preview={<ContainerSnippet story="Preview" />}
code={`<Container>
<DecorativeBox />
</Container>`}
code={containerDefaultSnippet}
/>
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Usage</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<CodeBlock code={`import { Container } from "@backstage/canon";
<Container>Hello World!</Container>
`} />
</Tabs.Panel>
</Tabs.Root>
<ComponentInfos
component="container"
classNames={['canon-Container']}
usageCode={containerUsageSnippet}
/>
## API reference
@@ -40,23 +38,11 @@ content on the page.
A simple example of how to use the Container component.
<CodeBlock
code={`<Container>
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
</Container>`}
/>
<CodeBlock code={containerSimpleSnippet} />
### Responsive padding & margin
The Container component also supports responsive values, making it easy to
create responsive designs.
<CodeBlock
code={`<Container paddingY={{ xs: 'sm', md: 'md' }}>
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
</Container>`}
/>
<CodeBlock code={containerResponsiveSnippet} />
@@ -0,0 +1,32 @@
import {
classNamePropDefs,
stylePropDefs,
gapPropDefs,
type PropDef,
} from '@/utils/propDefs';
export const containerPropDefs: Record<string, PropDef> = {
...gapPropDefs,
...classNamePropDefs,
...stylePropDefs,
};
export const containerUsageSnippet = `import { Container } from "@backstage/canon";
<Container>Hello World!</Container>`;
export const containerDefaultSnippet = `<Container>
<DecorativeBox />
</Container>`;
export const containerSimpleSnippet = `<Container>
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
</Container>`;
export const containerResponsiveSnippet = `<Container paddingY={{ xs: 'sm', md: 'md' }}>
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
</Container>`;
@@ -0,0 +1,62 @@
import { PropsTable } from '@/components/PropsTable';
import { CodeBlock } from '@/components/CodeBlock';
import { Snippet } from '@/components/Snippet';
import { FlexSnippet } from '@/snippets/stories-snippets';
import {
flexPropDefs,
flexUsageSnippet,
flexDefaultSnippet,
flexFAQ1Snippet,
flexSimpleSnippet,
flexResponsiveSnippet,
flexAlignSnippet,
} from './flex.props';
import { spacingPropDefs } from '@/utils/propDefs';
import { ComponentInfos } from '@/components/ComponentInfos';
# Flex
A responsive flex container component for vertical stacking with customizable gaps.
<Snippet
py={4}
align="center"
preview={<FlexSnippet story="Default" />}
code={flexDefaultSnippet}
/>
<ComponentInfos
component="flex"
classNames={['canon-Flex']}
usageCode={flexUsageSnippet}
/>
## API reference
<PropsTable data={flexPropDefs} />
The grid component also accepts all the spacing props from the Box component.
<PropsTable data={spacingPropDefs} />
## Examples
### Simple
A simple example of how to use the Flex component.
<CodeBlock code={flexSimpleSnippet} />
### Responsive
The Flex component also supports responsive values, making it easy to create
responsive designs.
<CodeBlock code={flexResponsiveSnippet} />
### Align
The Flex component also supports responsive alignment, making it easy to
create responsive designs.
<CodeBlock code={flexAlignSnippet} />
@@ -0,0 +1,55 @@
import {
childrenPropDefs,
classNamePropDefs,
stylePropDefs,
type PropDef,
} from '@/utils/propDefs';
export const flexPropDefs: Record<string, PropDef> = {
align: {
type: 'enum',
values: ['start', 'center', 'end', 'baseline', 'stretch'],
responsive: true,
},
direction: {
type: 'enum',
values: ['row', 'column', 'row-reverse', 'column-reverse'],
responsive: true,
},
justify: {
type: 'enum',
values: ['start', 'center', 'end', 'between'],
responsive: true,
},
...childrenPropDefs,
...classNamePropDefs,
...stylePropDefs,
};
export const flexUsageSnippet = `import { Flex } from '@backstage/canon';
<Flex />`;
export const flexDefaultSnippet = `<Flex>
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
</Flex>`;
export const flexSimpleSnippet = `<Flex gap="md">
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
</Flex>`;
export const flexResponsiveSnippet = `<Flex gap={{ xs: 'xs', md: 'md' }}>
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
</Flex>`;
export const flexAlignSnippet = `<Flex align={{ xs: 'start', md: 'center' }}>
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
</Flex>`;
@@ -1,11 +1,20 @@
import { CodeBlock } from '@/components/CodeBlock';
import { PropsTable } from '@/components/PropsTable';
import { Tabs } from '@/components/Tabs';
import { grid } from '@/snippets/code-snippets';
import { Snippet } from '@/components/Snippet';
import { GridSnippet } from '@/snippets/stories-snippets';
import { spacingPropDefs } from '../../../../utils/propDefs';
import { gridPropDefs, gridItemPropDefs } from './props';
import { spacingPropDefs } from '@/utils/propDefs';
import {
gridPropDefs,
gridItemPropDefs,
gridUsageSnippet,
gridDefaultSnippet,
gridSimpleSnippet,
gridComplexSnippet,
gridMixingRowsSnippet,
gridResponsiveSnippet,
gridStartEndSnippet,
} from './grid.props';
import { ComponentInfos } from '@/components/ComponentInfos';
# Grid
@@ -15,25 +24,18 @@ more complex ones.
<Snippet
py={4}
preview={<GridSnippet story="Default" />}
code={`<Grid>
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
</Grid>`}
code={gridDefaultSnippet}
/>
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Usage</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<CodeBlock code={grid} />
</Tabs.Panel>
</Tabs.Root>
<ComponentInfos
component="grid"
classNames={['canon-Grid', 'canon-GridItem']}
usageCode={gridUsageSnippet}
/>
## API reference
### Grid
### Grid.Root
This is the grid container component. It will help to define the number of
columns that will be used in the grid. You can also define the gap between the
@@ -60,31 +62,14 @@ elements directly if you prefer.
A simple grid with 3 columns and a gap of md.
<CodeBlock
code={`<Grid columns={3} gap="md">
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
</Grid>
`}
/>
<CodeBlock code={gridSimpleSnippet} />
### Complex grid
You can also use the grid item to create more complex layouts. In this example
the first column will span 1 column and the second column will span 2 columns.
<CodeBlock
code={`<Grid columns={3} gap="md">
<Grid.Item colSpan={1}>
<Box>Hello World</Box>
</Grid.Item>
<Grid.Item colSpan={2}>
<Box>Hello World</Box>
</Grid.Item>
</Grid>
`}
/>
<CodeBlock code={gridComplexSnippet} />
### Mixing rows and columns
@@ -92,47 +77,17 @@ The grid item component also supports the `rowSpan` prop, which allows you to
span multiple rows within the grid layout. In this example, the first item
will span 2 rows to achieve a dynamic and flexible grid structure.
<CodeBlock
code={`<Grid columns={3} gap="md">
<Grid.Item colSpan={1} rowSpan={2}>
<Box>Hello World</Box>
</Grid.Item>
<Grid.Item colSpan={2}>
<Box>Hello World</Box>
</Grid.Item>
<Grid.Item colSpan={2}>
<Box>Hello World</Box>
</Grid.Item>
</Grid>
`}
/>
<CodeBlock code={gridMixingRowsSnippet} />
### Responsive grid
The grid component also supports responsive values, making it easy to create
responsive designs.
<CodeBlock
code={`<Grid columns={{ xs: 1, md: 3 }} gap={{ xs: 'xs', md: 'md' }}>
<Grid.Item colSpan={{ xs: 1, md: 2 }}>
<Box>Hello World</Box>
</Grid.Item>
<Grid.Item colSpan={{ xs: 1, md: 1 }}>
<Box>Hello World</Box>
</Grid.Item>
</Grid>
`}
/>
<CodeBlock code={gridResponsiveSnippet} />
### Start and End
The start and end props can be used to position the item in the grid.
<CodeBlock
code={`<Grid columns={3} gap="md">
<Grid.Item start={2} end={4}>
<Box>Hello World</Box>
</Grid.Item>
</Grid>
`}
/>
<CodeBlock code={gridStartEndSnippet} />
@@ -0,0 +1,111 @@
import {
childrenPropDefs,
classNamePropDefs,
stylePropDefs,
type PropDef,
} from '@/utils/propDefs';
const columnsValues = [
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'10',
'11',
'12',
];
export const gridPropDefs: Record<string, PropDef> = {
columns: {
type: 'enum | string',
values: [...columnsValues, 'auto'],
responsive: true,
default: 'auto',
},
...childrenPropDefs,
...classNamePropDefs,
...stylePropDefs,
};
export const gridItemPropDefs: Record<string, PropDef> = {
colSpan: {
type: 'enum | string',
values: [...columnsValues, 'full'],
responsive: true,
},
rowSpan: {
type: 'enum | string',
values: [...columnsValues, 'full'],
responsive: true,
},
start: {
type: 'enum | string',
values: [...columnsValues, 'auto'],
responsive: true,
},
end: {
type: 'enum | string',
values: [...columnsValues, 'auto'],
responsive: true,
},
...childrenPropDefs,
...classNamePropDefs,
...stylePropDefs,
};
export const gridUsageSnippet = `import { Grid } from '@backstage/canon';
<Grid />`;
export const gridDefaultSnippet = `<Grid.Root>
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
</Grid.Root>`;
export const gridSimpleSnippet = `<Grid columns={3} gap="md">
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
</Grid>`;
export const gridComplexSnippet = `<Grid columns={3} gap="md">
<Grid.Item colSpan={1}>
<Box>Hello World</Box>
</Grid.Item>
<Grid.Item colSpan={2}>
<Box>Hello World</Box>
</Grid.Item>
</Grid>`;
export const gridMixingRowsSnippet = `<Grid columns={3} gap="md">
<Grid.Item colSpan={1} rowSpan={2}>
<Box>Hello World</Box>
</Grid.Item>
<Grid.Item colSpan={2}>
<Box>Hello World</Box>
</Grid.Item>
<Grid.Item colSpan={2}>
<Box>Hello World</Box>
</Grid.Item>
</Grid>`;
export const gridResponsiveSnippet = `<Grid columns={{ xs: 1, md: 3 }} gap={{ xs: 'xs', md: 'md' }}>
<Grid.Item colSpan={{ xs: 1, md: 2 }}>
<Box>Hello World</Box>
</Grid.Item>
<Grid.Item colSpan={{ xs: 1, md: 1 }}>
<Box>Hello World</Box>
</Grid.Item>
</Grid>`;
export const gridStartEndSnippet = `<Grid columns={3} gap="md">
<Grid.Item start={2} end={4}>
<Box>Hello World</Box>
</Grid.Item>
</Grid>`;
@@ -0,0 +1,66 @@
import { PropsTable } from '@/components/PropsTable';
import { HeadingSnippet } from '@/snippets/stories-snippets';
import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
import {
headingPropDefs,
headingUsageSnippet,
headingDefaultSnippet,
headingVariantsSnippet,
headingTruncateSnippet,
headingResponsiveSnippet,
} from './heading.props';
import { ComponentInfos } from '@/components/ComponentInfos';
# Heading
Headings are used to structure the content of your page.
<Snippet
py={4}
preview={<HeadingSnippet story="Title1" />}
code={headingDefaultSnippet}
/>
<ComponentInfos
component="heading"
classNames={['canon-Heading']}
usageCode={headingUsageSnippet}
/>
## API reference
<PropsTable data={headingPropDefs} />
## Examples
### All variants
The `Heading` component has a `variant` prop that can be used to change the
appearance of the heading.
<Snippet
py={2}
open
preview={<HeadingSnippet story="AllVariants" />}
code={headingVariantsSnippet}
/>
### Truncate
The `Heading` component has a `truncate` prop that can be used to truncate the
heading.
<Snippet
py={2}
open
preview={<HeadingSnippet story="Truncate" />}
code={headingTruncateSnippet}
/>
### Responsive
You can also use the `variant` prop to change the appearance of the text based
on the screen size.
<CodeBlock code={headingResponsiveSnippet} />
@@ -0,0 +1,48 @@
import {
classNamePropDefs,
stylePropDefs,
type PropDef,
} from '@/utils/propDefs';
export const headingPropDefs: Record<string, PropDef> = {
variant: {
type: 'enum',
values: ['display', 'title1', 'title2', 'title3', 'title4', 'title5'],
responsive: true,
},
children: {
type: 'enum',
values: ['ReactNode'],
responsive: false,
},
truncate: {
type: 'boolean',
default: 'false',
},
...classNamePropDefs,
...stylePropDefs,
};
export const headingUsageSnippet = `import { Heading } from '@backstage/canon';
<Heading />`;
export const headingDefaultSnippet = `<Heading variant="title1">Hello World!</Heading>`;
export const headingVariantsSnippet = `<Flex direction="column" gap="4">
<Heading variant="display">Display</Heading>
<Heading variant="title1">Title 1</Heading>
<Heading variant="title2">Title 2</Heading>
<Heading variant="title3">Title 3</Heading>
<Heading variant="title4">Title 4</Heading>
</Flex>`;
export const headingTruncateSnippet = `<Heading style={{ maxWidth: '400px' }} truncate>
A man looks at a painting in a museum and says, "Brothers and sisters I
have none, but that man's father is my father's son." Who is
in the painting?
</Heading>`;
export const headingResponsiveSnippet = `<Heading variant={{ initial: 'title2', lg: 'title1' }}>
Responsive heading
</Heading>`;
@@ -0,0 +1,79 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
import { IconButtonSnippet } from '@/snippets/stories-snippets';
import {
iconButtonPropDefs,
iconButtonUsageSnippet,
iconButtonDefaultSnippet,
iconButtonVariantsSnippet,
iconButtonSizesSnippet,
iconButtonDisabledSnippet,
iconButtonResponsiveSnippet,
} from './icon-button.props';
import { ComponentInfos } from '@/components/ComponentInfos';
# Icon Button
A button component with a single icon that can be used to trigger actions.
<Snippet
align="center"
py={4}
preview={<IconButtonSnippet story="Variants" />}
code={iconButtonDefaultSnippet}
/>
<ComponentInfos
component="icon-button"
classNames={['canon-IconButton']}
usageCode={iconButtonUsageSnippet}
/>
## API reference
<PropsTable data={iconButtonPropDefs} />
## Examples
### Variants
Here's a view when buttons have different variants.
<Snippet
align="center"
py={4}
open
preview={<IconButtonSnippet story="Variants" />}
code={iconButtonVariantsSnippet}
/>
### Sizes
Here's a view when buttons have different sizes.
<Snippet
align="center"
py={4}
open
preview={<IconButtonSnippet story="Sizes" />}
code={iconButtonSizesSnippet}
/>
### Disabled
Here's a view when buttons are disabled.
<Snippet
align="center"
py={4}
open
preview={<IconButtonSnippet story="Disabled" />}
code={iconButtonDisabledSnippet}
/>
### Responsive
Here's a view when buttons are responsive.
<CodeBlock code={iconButtonResponsiveSnippet} />
@@ -0,0 +1,46 @@
import {
classNamePropDefs,
stylePropDefs,
type PropDef,
} from '@/utils/propDefs';
export const iconButtonPropDefs: Record<string, PropDef> = {
variant: {
type: 'enum',
values: ['primary', 'secondary'],
default: 'primary',
responsive: true,
},
size: {
type: 'enum',
values: ['small', 'medium'],
default: 'medium',
responsive: true,
},
icon: { type: 'enum', values: 'icon', responsive: false },
...classNamePropDefs,
...stylePropDefs,
};
export const iconButtonUsageSnippet = `import { IconButton } from '@backstage/canon';
<IconButton />`;
export const iconButtonDefaultSnippet = `<Flex align="center">
<IconButton icon="cloud" variant="primary" />
<IconButton icon="cloud" variant="secondary" />
</Flex>`;
export const iconButtonVariantsSnippet = `<Flex align="center">
<IconButton icon="cloud" variant="primary" />
<IconButton icon="cloud" variant="secondary" />
</Flex>`;
export const iconButtonSizesSnippet = `<Flex align="center">
<IconButton icon="cloud" size="small" />
<IconButton icon="cloud" size="medium" />
</Flex>`;
export const iconButtonDisabledSnippet = `<IconButton icon="cloud" disabled />`;
export const iconButtonResponsiveSnippet = `<IconButton icon="cloud" variant={{ initial: 'primary', lg: 'secondary' }} />`;
@@ -0,0 +1,31 @@
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 { ComponentInfos } from '@/components/ComponentInfos';
# Icon
Icons are used to represent an action or a state.
<Snippet
py={4}
align="center"
preview={<IconSnippet story="Default" />}
code={iconDefaultSnippet}
/>
<ComponentInfos
component="icon"
classNames={['canon-Icon']}
usageCode={iconUsageSnippet}
/>
## API reference
<PropsTable data={iconPropDefs} />
@@ -1,5 +1,8 @@
import { classNamePropDefs, stylePropDefs } from '../../../../utils/propDefs';
import type { PropDef } from '../../../../utils/propDefs';
import {
classNamePropDefs,
stylePropDefs,
type PropDef,
} from '@/utils/propDefs';
export const iconPropDefs: Record<string, PropDef> = {
name: {
@@ -14,3 +17,9 @@ export const iconPropDefs: Record<string, PropDef> = {
...classNamePropDefs,
...stylePropDefs,
};
export const iconUsageSnippet = `import { Icon } from '@backstage/canon';
<Icon />`;
export const iconDefaultSnippet = `<Icon name="heart" />`;
@@ -0,0 +1,71 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { CodeBlock } from '@/components/CodeBlock';
import {
MenuSnippet,
ButtonSnippet,
LinkSnippet,
} from '@/snippets/stories-snippets';
import {
linkPropDefs,
linkUsageSnippet,
linkDefaultSnippet,
linkVariantsSnippet,
linkWeightsSnippet,
} from './link.props';
import { ComponentInfos } from '@/components/ComponentInfos';
# Link
A link component that renders a `<a>` element.
<Snippet
align="center"
py={4}
preview={<LinkSnippet story="Default" />}
code={linkDefaultSnippet}
/>
<ComponentInfos
component="link"
classNames={[
'canon-Link',
"canon-Link[data-variant='body']",
"canon-Link[data-variant='subtitle']",
"canon-Link[data-variant='caption']",
"canon-Link[data-variant='label']",
"canon-Link[data-weight='regular']",
"canon-Link[data-weight='bold']",
]}
usageCode={linkUsageSnippet}
/>
## API reference
<PropsTable data={linkPropDefs} />
## Examples
### Variants
Here's a view when links have different variants.
<Snippet
align="center"
py={4}
open
preview={<LinkSnippet story="AllVariants" />}
code={linkVariantsSnippet}
/>
### Weights
Here's a view when links have different weights.
<Snippet
align="center"
py={4}
open
preview={<LinkSnippet story="AllWeights" />}
code={linkWeightsSnippet}
/>

Some files were not shown because too many files have changed in this diff Show More