Docs updates

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
Charles de Dreuille
2025-01-03 15:36:57 +01:00
parent 113b7a9000
commit 85478a6360
40 changed files with 965 additions and 254 deletions
@@ -0,0 +1,5 @@
import styles from './styles.module.css';
export const DecorativeBox = () => {
return <div className={styles.box} />;
};
@@ -0,0 +1,8 @@
.box {
min-width: 64px;
min-height: 64px;
background-color: #eaf2fd;
border-radius: 4px;
box-shadow: 0 0 0 1px #2563eb;
background-image: url("data:image/svg+xml,%3Csvg width='6' height='6' viewBox='0 0 6 6' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%232563eb' fill-opacity='0.3' fill-rule='evenodd'%3E%3Cpath d='M5 0h1L0 6V5zM6 5v1H5z'/%3E%3C/g%3E%3C/svg%3E");
}
@@ -0,0 +1,42 @@
import styles from './styles.module.css';
import { Text, Icon } from '@backstage/canon';
interface BaseUIProps {
href: string;
}
export const BaseUI = ({ href }: BaseUIProps) => {
return (
<div className={styles.container}>
<svg
width="17"
height="30"
viewBox="0 0 17 30"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={styles.icon}
>
<path d="M8 12.8V15V26C3.58172 26 0 22.0601 0 17.2V15V4C4.41828 4 8 7.93989 8 12.8Z" />
<path d="M9.5001 10.0154C9.2245 9.99843 9 10.2239 9 10.5V26.0001C13.4183 26.0001 17 22.4184 17 18.0001C17 13.7498 13.6854 10.2736 9.5001 10.0154Z" />
</svg>
<div className={styles.content}>
<Text variant="subtitle" weight="bold">
Base UI
</Text>
<div className={styles.description}>
<Text variant="subtitle">
This component is using Base UI under the hood. While most of the
original props are available, we have made some changes to the API
to fit our needs.
</Text>
</div>
{href && (
<a className={styles.button} href={href} target="_blank">
Discover more
<Icon name="externalLink" />
</a>
)}
</div>
</div>
);
};
@@ -0,0 +1,42 @@
.container {
display: flex;
background-color: var(--canon-surface-1);
padding: var(--canon-spacing-md);
border-radius: var(--canon-border-radius-xs);
border: 1px solid var(--canon-border-base);
margin-bottom: var(--canon-spacing-md);
gap: var(--canon-spacing-md);
transition: background-color 0.2s ease-in-out;
}
.icon path {
fill: var(--canon-text-primary);
}
.content {
display: flex;
flex-direction: column;
align-items: flex-start;
}
.description {
max-width: 700px;
}
.button {
all: unset;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
background-color: var(--canon-surface-2);
border: 1px solid var(--canon-border-base);
border-radius: var(--canon-border-radius-xs);
padding: 0 var(--canon-spacing-xs);
height: 28px;
border-radius: 100px;
margin-top: var(--canon-spacing-xs);
font-size: var(--canon-font-size-body);
color: var(--canon-text-primary);
gap: var(--canon-spacing-2xs);
}
@@ -55,6 +55,7 @@
height: 28px;
padding: 0 12px;
border-radius: 4px;
transition: background-color 0.2s ease-in-out;
&:hover {
background-color: var(--canon-surface-2);
+1 -1
View File
@@ -1,5 +1,5 @@
import styles from './Sidebar.module.css';
import { TabsVersion, TabsPages, TabsTheme } from '../Tabs';
import { TabsVersion, TabsPages, TabsTheme } from './tabs';
import { Docs } from './docs';
import { Playground } from './playground';
import { cookies } from 'next/headers';
+107
View File
@@ -0,0 +1,107 @@
'use client';
import styles from './Tabs.module.css';
import { Tabs } from '@base-ui-components/react/tabs';
import { Icon, Text } from '@backstage/canon';
import { usePathname } from 'next/navigation';
import { useRouter } from 'next/navigation';
import { setThemeCookie, setThemeNameCookie } from './actions';
export const TabsVersion = ({
themeName,
}: {
themeName?: { value: string; name: string };
}) => {
return (
<Tabs.Root
className={styles.tabs}
onValueChange={setThemeNameCookie}
value={themeName?.value || 'default'}
>
<Tabs.List className={styles.list}>
<Tabs.Tab className={styles.tab} value="legacy">
<Text variant="caption" weight="bold">
Theme 1
</Text>
</Tabs.Tab>
<Tabs.Tab className={styles.tab} value="default">
<Text variant="caption" weight="bold">
Theme 2
</Text>
</Tabs.Tab>
<Tabs.Indicator className={styles.indicator} />
</Tabs.List>
</Tabs.Root>
);
};
export const TabsTheme = ({
theme,
}: {
theme?: { value: string; name: string };
}) => {
return (
<Tabs.Root
className={styles.tabsTheme}
onValueChange={setThemeCookie}
value={theme?.value || 'light'}
>
<Tabs.List className={styles.list}>
<Tabs.Tab className={styles.tab} value="light">
<Icon name="sun" />
</Tabs.Tab>
<Tabs.Tab className={styles.tab} value="dark">
<Icon name="moon" />
</Tabs.Tab>
<Tabs.Indicator className={styles.indicator} />
</Tabs.List>
</Tabs.Root>
);
};
export const TabsPages = () => {
const pathname = usePathname();
const router = useRouter();
const onValueChange = (value: string) => {
if (value === 'docs') {
router.push('/');
} else {
router.push('/playground');
}
};
return (
<Tabs.Root
className={styles.tabs}
value={pathname.includes('playground') ? 'playground' : 'docs'}
onValueChange={onValueChange}
>
<Tabs.List className={styles.list}>
<Tabs.Tab
className={styles.tab}
value="docs"
onClick={() => {
router.push('/');
}}
>
<Text variant="caption" weight="bold">
Documentation
</Text>
</Tabs.Tab>
<Tabs.Tab
className={styles.tab}
value="playground"
onClick={() => {
router.push('/playground');
}}
>
<Text variant="caption" weight="bold">
Playground
</Text>
</Tabs.Tab>
<Tabs.Indicator className={styles.indicator} />
</Tabs.List>
</Tabs.Root>
);
};
+8 -6
View File
@@ -23,16 +23,18 @@ export const Snippet = ({
}: SnippetProps) => {
return (
<Collapsible.Root className={styles.container} defaultOpen={open}>
<div
className={`${styles.preview} ${styles[align]}`}
style={{ padding: `${py}rem ${px}rem` }}
>
{preview}
<div className={styles.preview}>
<div
className={`${styles.previewContent} ${styles[align]}`}
style={{ padding: `${py}rem ${px}rem` }}
>
{preview}
</div>
<Collapsible.Trigger className={styles.trigger}>
<Text variant="body">View code</Text>
</Collapsible.Trigger>
</div>
<Collapsible.Panel className={styles.Panel}>
<Collapsible.Panel className={styles.panel}>
<CodeBlock code={code} />
</Collapsible.Panel>
</Collapsible.Root>
@@ -8,11 +8,17 @@
border-radius: 4px;
box-shadow: inset 0 0 0 1px var(--canon-border-base);
background-color: var(--canon-background);
transition: all 0.2s ease-in-out;
padding: 1px;
position: relative;
}
.previewContent {
width: 100%;
height: 100%;
background-image: radial-gradient(rgba(0, 0, 0, 0.08) 1px, transparent 0);
background-position: calc((8px / 2) * -1) calc((8px / 2) * -1);
background-size: 8px 8px;
position: relative;
transition: all 0.2s ease-in-out;
}
.center {
@@ -29,9 +35,28 @@
cursor: pointer;
}
[data-theme='dark'] .preview {
[data-theme='dark'] .previewContent {
background-image: radial-gradient(
rgba(255, 255, 255, 0.1) 1px,
transparent 0
);
}
.panel {
height: var(--collapsible-panel-height);
transition: all 0.2s ease-out;
overflow: hidden;
&[data-starting-style],
&[data-ending-style] {
height: 0;
}
&[data-closed] {
opacity: 0;
}
&[data-open] {
opacity: 1;
}
}
@@ -15,7 +15,7 @@
*/
.wrapper {
border: 1px solid #e7e7e7;
border: 1px solid var(--canon-border-base);
border-radius: 4px;
overflow: hidden;
margin-bottom: 1rem;
@@ -33,8 +33,9 @@
padding: 12px 16px !important;
border: none !important;
text-align: left;
background-color: white !important;
background-color: var(--canon-surface-1) !important;
font-size: 16px;
transition: background-color 0.2s ease-in-out;
& p {
margin: 0;
@@ -43,14 +44,14 @@
.tableHeaderCell {
/* background-color: #f5f5f5 !important; */
border-bottom: 1px solid #e7e7e7 !important;
border-bottom: 1px solid var(--canon-border-base) !important;
font-weight: 500;
font-size: 14px;
}
.tableRow {
border: none;
border-bottom: 1px solid #e7e7e7;
border-bottom: 1px solid var(--canon-border-base);
&:last-child {
border-bottom: none;
}
@@ -59,7 +60,7 @@
.tableChip {
display: inline-block;
font-size: 14px !important;
border: 1px solid #e7e7e7;
border: 1px solid var(--canon-border-base);
border-radius: 6px;
padding: 0px 6px;
height: 24px;
+1 -107
View File
@@ -1,107 +1 @@
'use client';
import styles from './Tabs.module.css';
import { Tabs } from '@base-ui-components/react/tabs';
import { Icon, Text } from '@backstage/canon';
import { usePathname } from 'next/navigation';
import { useRouter } from 'next/navigation';
import { setThemeCookie, setThemeNameCookie } from './actions';
export const TabsVersion = ({
themeName,
}: {
themeName?: { value: string; name: string };
}) => {
return (
<Tabs.Root
className={styles.tabs}
onValueChange={setThemeNameCookie}
value={themeName?.value || 'default'}
>
<Tabs.List className={styles.list}>
<Tabs.Tab className={styles.tab} value="legacy">
<Text variant="caption" weight="bold">
Theme 1
</Text>
</Tabs.Tab>
<Tabs.Tab className={styles.tab} value="default">
<Text variant="caption" weight="bold">
Theme 2
</Text>
</Tabs.Tab>
<Tabs.Indicator className={styles.indicator} />
</Tabs.List>
</Tabs.Root>
);
};
export const TabsTheme = ({
theme,
}: {
theme?: { value: string; name: string };
}) => {
return (
<Tabs.Root
className={styles.tabsTheme}
onValueChange={setThemeCookie}
value={theme?.value || 'light'}
>
<Tabs.List className={styles.list}>
<Tabs.Tab className={styles.tab} value="light">
<Icon name="sun" />
</Tabs.Tab>
<Tabs.Tab className={styles.tab} value="dark">
<Icon name="moon" />
</Tabs.Tab>
<Tabs.Indicator className={styles.indicator} />
</Tabs.List>
</Tabs.Root>
);
};
export const TabsPages = () => {
const pathname = usePathname();
const router = useRouter();
const onValueChange = (value: string) => {
if (value === 'docs') {
router.push('/');
} else {
router.push('/playground');
}
};
return (
<Tabs.Root
className={styles.tabs}
value={pathname.includes('playground') ? 'playground' : 'docs'}
onValueChange={onValueChange}
>
<Tabs.List className={styles.list}>
<Tabs.Tab
className={styles.tab}
value="docs"
onClick={() => {
router.push('/');
}}
>
<Text variant="caption" weight="bold">
Documentation
</Text>
</Tabs.Tab>
<Tabs.Tab
className={styles.tab}
value="playground"
onClick={() => {
router.push('/playground');
}}
>
<Text variant="caption" weight="bold">
Playground
</Text>
</Tabs.Tab>
<Tabs.Indicator className={styles.indicator} />
</Tabs.List>
</Tabs.Root>
);
};
export * as Tabs from './parts';
+54
View File
@@ -0,0 +1,54 @@
'use client';
import { Tabs as TabsPrimitive } from '@base-ui-components/react/tabs';
import styles from './styles.module.css';
import { Text } from '@backstage/canon';
export const Root = ({
className,
...rest
}: React.ComponentProps<typeof TabsPrimitive.Root>) => (
<TabsPrimitive.Root className={`${styles.root} ${className}`} {...rest} />
);
export const List = ({
className,
children,
...rest
}: React.ComponentProps<typeof TabsPrimitive.List>) => (
<TabsPrimitive.List className={`${styles.list} ${className}`} {...rest}>
{children}
<TabsPrimitive.Indicator className={styles.indicator} />
</TabsPrimitive.List>
);
export const Tab = (props: React.ComponentProps<typeof TabsPrimitive.Tab>) => (
<TabsPrimitive.Tab
{...props}
render={({ children, ...rest }, state) => {
return (
<button className={styles.tab} {...rest}>
<Text
variant="subtitle"
weight="bold"
{...rest}
style={{
color: state.selected
? 'var(--canon-text-primary)'
: 'var(--canon-text-secondary)',
}}
>
{children}
</Text>
</button>
);
}}
/>
);
export const Panel = ({
className,
...rest
}: React.ComponentProps<typeof TabsPrimitive.Panel>) => (
<TabsPrimitive.Panel className={`${styles.panel} ${className}`} {...rest} />
);
@@ -0,0 +1,27 @@
.root {
margin-top: 40px;
}
.list {
display: flex;
gap: var(--canon-spacing-md);
border-bottom: 1px solid var(--canon-border-base);
position: relative;
margin-bottom: var(--canon-spacing-md);
}
.tab {
all: unset;
cursor: pointer;
padding-bottom: 8px;
}
.indicator {
position: absolute;
bottom: -1px;
left: var(--active-tab-left);
width: var(--active-tab-width);
height: 1px;
background-color: var(--canon-text-primary);
transition: all 0.2s ease-in-out;
}
+45
View File
@@ -0,0 +1,45 @@
// Sometimes codes are not formatted correctly in the docs, so we need to use snippets
export const grid = `import { Grid } from '@backstage/canon';
<Grid>
<Grid.Item />
</Grid>
`;
export const buttonVariants = `<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>
`;
export const stackFAQ1 = `<Grid columns={3} gap="md">
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
</Grid>`;
export const stackSimple = `<Stack>
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
</Stack>`;
export const stackResponsive = `<Stack gap={{ xs: 'sm', md: 'md' }}>
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
</Stack>`;
export const stackAlign = `<Stack align={{ xs: 'left', md: 'center' }}>
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
</Stack>`;
+23 -10
View File
@@ -1,17 +1,33 @@
import { Story } from '../components/Story';
import { CodeBlock } from '../components/CodeBlock';
import { PropsTable } from '../components/PropsTable';
import { spacePropsList } from '../utils/spaceProps';
import { Tabs } from '../components/Tabs';
import { Snippet } from '../components/Snippet';
import { BoxPreview } from '../snippets/box';
# Box
Box is the lowest-level component in Canon. We use it internally to build all
of our components. It provides a consistent API for styling and layout.
Box is the lowest-level component in Canon. It provides a consistent API for styling and layout.
<CodeBlock
code={`import { Box } from "@backstage/canon";
<Snippet
py={4}
preview={<BoxPreview />}
code={`<Box>
<DecorativeBox />
</Box>`}
align="center"
/>
<Box>Hello World!</Box>`} />
<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>
## API reference
@@ -81,10 +97,7 @@ avoid collapsing margins but both are available.
A simple example of how to use the Box component.
<CodeBlock
title="Usage"
code={`<Box padding="md" borderRadius="md">Hello World</Box>`}
/>
<CodeBlock code={`<Box padding="md" borderRadius="md">Hello World</Box>`} />
### Responsive
+27 -11
View File
@@ -1,5 +1,7 @@
import { PropsTable } from '../components/PropsTable';
import { Snippet } from '../components/Snippet';
import { Tabs } from '../components/Tabs';
import { CodeBlock } from '../components/CodeBlock';
import {
Button1,
Button2,
@@ -8,6 +10,7 @@ import {
Button5,
Button6,
} from '../snippets/button';
import { buttonVariants } from './_snippets';
# Button
@@ -30,6 +33,29 @@ A button component that can be used to trigger actions.
</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
@@ -72,17 +98,7 @@ Here's a view when buttons have different variants.
py={4}
open
preview={<Button1 />}
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>`}
code={buttonVariants}
/>
### Sizes
+52 -7
View File
@@ -1,21 +1,47 @@
import { Story } from '../components/Story';
import { CodeBlock } from '../components/CodeBlock';
import { PropsTable } from '../components/PropsTable';
import { CheckboxPreview, CheckboxAllVariants } from '../snippets/checkbox';
import { Snippet } from '../components/Snippet';
import { Tabs } from '../components/Tabs';
import { CodeBlock } from '../components/CodeBlock';
import { BaseUI } from '../components/HeadlessBanners/BaseUI';
# Checkbox
A checkbox component that can be used to trigger actions.
<Story id="checkbox--primary" />
<Snippet
py={4}
preview={<CheckboxPreview />}
code={`<Checkbox label="Accept terms and conditions" />`}
/>
<CodeBlock
code={`import { Checkbox } from "@backstage/canon";
<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 label="Checkbox" />
`} />
<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={{
label: {
@@ -60,3 +86,22 @@ A checkbox component that can be used to trigger actions.
},
}}
/>
## Examples
### All variants
Here's a view when checkboxes have different variants.
<Snippet
align="center"
py={4}
open
preview={<CheckboxAllVariants />}
code={`<Inline alignY="center">
<Checkbox />
<Checkbox checked />
<Checkbox label="Checkbox" />
<Checkbox label="Checkbox" checked />
</Inline>`}
/>
+19 -2
View File
@@ -1,17 +1,34 @@
import { CodeBlock } from '../components/CodeBlock';
import { PropsTable } from '../components/PropsTable';
import { spacePropsList } from '../utils/spaceProps';
import { Tabs } from '../components/Tabs';
import { Snippet } from '../components/Snippet';
import { ContainerPreview } from '../snippets/container';
# Container
The container component let you use our default max-width and center the
content on the page.
<CodeBlock
code={`import { Container } from "@backstage/canon";
<Snippet
py={4}
preview={<ContainerPreview />}
code={`<Container>
<DecorativeBox />
</Container>`}
/>
<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>
## API reference
+21 -6
View File
@@ -1,19 +1,34 @@
import { CodeBlock } from '../components/CodeBlock';
import { PropsTable } from '../components/PropsTable';
import { spacePropsList } from '../utils/spaceProps';
import { Tabs } from '../components/Tabs';
import { grid } from './_snippets';
import { Snippet } from '../components/Snippet';
import { GridPreview } from '../snippets/grid';
# Grid
A layout component that helps to create simple column-based layouts as well as
more complex ones.
<CodeBlock
code={`import { Grid } from "@backstage/canon";
<Snippet
py={4}
preview={<GridPreview />}
code={`<Grid>
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
</Grid>`}
/>
<Grid>
<Grid.Item>Hello World</Grid.Item>
</Grid>
`} />
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Usage</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<CodeBlock code={grid} />
</Tabs.Panel>
</Tabs.Root>
## API reference
+42 -11
View File
@@ -1,18 +1,45 @@
import { Story } from '../components/Story';
import { CodeBlock } from '../components/CodeBlock';
import { PropsTable } from '../components/PropsTable';
import {
HeadingPreview,
HeadingAllVariants,
HeadingResponsive,
} from '../snippets/heading';
import { Snippet } from '../components/Snippet';
import { Tabs } from '../components/Tabs';
import { CodeBlock } from '../components/CodeBlock';
# Heading
Headings are used to structure the content of your page.
<Story id="heading--title-1" />
<Snippet
py={4}
preview={<HeadingPreview />}
code={`<Heading variant="title1">Hello World!</Heading>`}
/>
<CodeBlock
code={`import { Heading } from "@backstage/canon";
<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 variant="title1">Hello World!</Heading>
`} />
<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
@@ -44,9 +71,10 @@ Headings are used to structure the content of your page.
The `Heading` component has a `variant` prop that can be used to change the
appearance of the heading.
<Story id="heading--all-variants" />
<CodeBlock
<Snippet
py={2}
open
preview={<HeadingAllVariants />}
code={`<Stack gap="md">
<Heading variant="display">Display</Heading>
<Heading variant="title1">Title 1</Heading>
@@ -61,6 +89,9 @@ appearance of the heading.
You can also use the `variant` prop to change the appearance of the text based
on the screen size.
<CodeBlock
<Snippet
py={4}
open
preview={<HeadingResponsive />}
code={`<Heading variant={{ xs: 'title1', md: 'title2' }}>Responsive</Heading>`}
/>
+31 -7
View File
@@ -1,19 +1,43 @@
import { Story } from '../components/Story';
import { CodeBlock } from '../components/CodeBlock';
import { PropsTable } from '../components/PropsTable';
import { icons } from '@backstage/canon';
import { IconPreview } from '../snippets/icon';
import { Snippet } from '../components/Snippet';
import { Tabs } from '../components/Tabs';
import { CodeBlock } from '../components/CodeBlock';
# Icon
Icons are used to represent an action or a state.
<Story id="icon--primary" />
<Snippet
py={4}
align="center"
preview={<IconPreview />}
code={`<Icon name="heart" />`}
/>
<CodeBlock
code={`import { Icon } from "@backstage/canon";
<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 name="heart" />
`} />
<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
+26 -11
View File
@@ -1,22 +1,37 @@
import { CodeBlock } from '../components/CodeBlock';
import { PropsTable } from '../components/PropsTable';
import { spacePropsList } from '../utils/spaceProps';
import { Tabs } from '../components/Tabs';
import { Snippet } from '../components/Snippet';
import { InlinePreview } from '../snippets/inline';
# Inline
The Inline component is used to create a horizontal layout of elements. By
default it uses flex and flexWrap to make sure that your content always flows
responsively.
The Inline component is used to create a horizontal layout of elements.
<CodeBlock
code={`import { Inline } from "@backstage/canon";
<Snippet
py={4}
preview={<InlinePreview />}
code={`<Inline>
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
</Inline>`}
/>
<Inline>
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
</Inline>
`} />
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Usage</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<CodeBlock
code={`import { Inline } from '@backstage/canon';
<Inline />
`}
/>
</Tabs.Panel>
</Tabs.Root>
## API reference
+36 -38
View File
@@ -1,6 +1,15 @@
import { CodeBlock } from '../components/CodeBlock';
import { PropsTable } from '../components/PropsTable';
import { spacePropsList } from '../utils/spaceProps';
import { Tabs } from '../components/Tabs';
import { CodeBlock } from '../components/CodeBlock';
import {
stackFAQ1,
stackSimple,
stackResponsive,
stackAlign,
} from './_snippets';
import { Snippet } from '../components/Snippet';
import { StackPreview } from '../snippets/stack';
# Stack
@@ -8,15 +17,29 @@ This is the stack 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.
<CodeBlock
code={`import { Stack } from "@backstage/canon";
<Snippet
py={4}
preview={<StackPreview />}
code={`<Stack>
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
</Stack>`}
/>
<Stack>
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
</Stack>
`} />
<Tabs.Root>
<Tabs.List>
<Tabs.Tab>Usage</Tabs.Tab>
</Tabs.List>
<Tabs.Panel>
<CodeBlock
code={`import { Stack } from '@backstage/canon';
<Stack />
`}
/>
</Tabs.Panel>
</Tabs.Root>
## API reference
@@ -52,14 +75,7 @@ The grid component also accepts all the spacing props from the Box component.
The Stack component only allows for stacking elements vertically. If you want
to create a column layout, please use the Grid component.
<CodeBlock
code={`<Grid columns={3} gap="md">
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
</Grid>
`}
/>
<CodeBlock code={stackFAQ1} />
## Examples
@@ -67,36 +83,18 @@ to create a column layout, please use the Grid component.
A simple example of how to use the Stack component.
<CodeBlock
code={`<Stack>
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
</Stack>`}
/>
<CodeBlock code={stackSimple} />
### Responsive
The Stack component also supports responsive values, making it easy to create
responsive designs.
<CodeBlock
code={`<Stack gap={{ xs: 'sm', md: 'md' }}>
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
</Stack>`}
/>
<CodeBlock code={stackResponsive} />
### Align
The Stack component also supports responsive alignment, making it easy to
create responsive designs.
<CodeBlock
code={`<Stack align={{ xs: 'left', md: 'center' }}>
<Box>Hello World</Box>
<Box>Hello World</Box>
<Box>Hello World</Box>
</Stack>`}
/>
<CodeBlock code={stackAlign} />
+80 -17
View File
@@ -1,17 +1,50 @@
import { CodeBlock } from '../components/CodeBlock';
import { PropsTable } from '../components/PropsTable';
import { Story } from '../components/Story';
import {
TextPreview,
TextAllVariants,
TextResponsive,
TextAllWeights,
} from '../snippets/text';
import { Snippet } from '../components/Snippet';
import { Tabs } from '../components/Tabs';
import { CodeBlock } from '../components/CodeBlock';
# Text
The `Text` component is used to display content on your page.
<Story id="text--default" />
<Snippet
py={4}
preview={<TextPreview />}
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>`}
/>
<CodeBlock
code={`import { Text } from "@backstage/canon";
<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>Hello World!</Text>`} />
<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
@@ -47,13 +80,31 @@ The `Text` component is used to display content on your page.
The `Text` component has a `variant` prop that can be used to change the
appearance of the text.
<CodeBlock
<Snippet
open
preview={<TextAllVariants />}
code={`<Stack gap="md">
<Text variant="subtitle">Subtitle Lorem ipsum dolor sit amet consectetur...</Text>
<Text variant="body">Body Lorem ipsum dolor sit amet consectetur...</Text>
<Text variant="caption">Caption Lorem ipsum dolor sit amet consectetur...</Text>
<Text variant="label">Label Lorem ipsum dolor sit amet consectetur...</Text>
</Stack>`}
<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>
</Stack>`}
/>
### All weights
@@ -61,11 +112,21 @@ appearance of the text.
The `Text` component has a `weight` prop that can be used to change the
appearance of the text.
<CodeBlock
<Snippet
open
preview={<TextAllWeights />}
code={`<Stack gap="md">
<Text weight="regular">Regular Lorem ipsum dolor sit amet consectetur...</Text>
<Text weight="bold">Bold Lorem ipsum dolor sit amet consectetur...</Text>
</Stack>`}
<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>
</Stack>`}
/>
### Responsive
@@ -73,6 +134,8 @@ appearance of the text.
You can also use the `variant` prop to change the appearance of the text based
on the screen size.
<CodeBlock
<Snippet
open
preview={<TextResponsive />}
code={`<Text variant={{ xs: 'label', md: 'body' }}>Responsive</Text>`}
/>
+1
View File
@@ -24,6 +24,7 @@ that, create a theme.css file and import it in your application. Here's an
example below on how to set your light and dark mode.
<CodeBlock
lang="css"
code={`/** Light theme **/
[data-theme='light'] {
--canon-accent: #1ed760;
+41 -3
View File
@@ -1,4 +1,6 @@
import { Story } from '@/components/Story';
import { HeadingAllVariants } from '../snippets/heading';
import { TextAllVariants } from '../snippets/text';
import { Snippet } from '../components/Snippet';
# Typography
@@ -15,7 +17,18 @@ create a hierarchy of information and to make the content more readable. The
best way to use add these headings to your page is to import the [Heading
component](?path=/docs/components-heading--docs).
<Story id="heading--all-variants" />
<Snippet
py={2}
open
preview={<HeadingAllVariants />}
code={`<Stack gap="md">
<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>
</Stack>`}
/>
## Text
@@ -25,4 +38,29 @@ versatile and can be paired with regular and bold of font weights. You can use
the [Text component](?path=/docs/components-text--docs) to add text to your
page.
<Story id="text--all-variants" />
<Snippet
open
preview={<TextAllVariants />}
code={`<Stack gap="md">
<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>
</Stack>`}
/>
+10
View File
@@ -0,0 +1,10 @@
import { Box } from '@backstage/canon';
import { DecorativeBox } from '../components/DecorativeBox';
export const BoxPreview = () => {
return (
<Box>
<DecorativeBox />
</Box>
);
};
+1
View File
@@ -63,6 +63,7 @@ export const Button5 = () => {
};
export const Button6 = () => {
// TODO: Add responsive button
return null;
return (
<Button
+22 -9
View File
@@ -1,18 +1,31 @@
import { Text } from '@backstage/canon';
import { Stack, Inline, Checkbox } from '@backstage/canon';
export const CheckboxPreview = () => {
return <Checkbox label="Accept terms and conditions" />;
};
export const CheckboxAllVariants = () => {
return (
<Inline alignY="center">
<Checkbox />
<Checkbox checked />
<Checkbox label="Checkbox" />
<Checkbox label="Checkbox" checked />
</Inline>
);
};
export const CheckboxPlayground = () => {
return (
<Stack>
<Stack>
<Text>All variants</Text>
<Inline alignY="center">
<Checkbox />
<Checkbox checked />
<Checkbox label="Checkbox" />
<Checkbox label="Checkbox" checked />
</Inline>
</Stack>
<Text>All variants</Text>
<Inline alignY="center">
<Checkbox />
<Checkbox checked />
<Checkbox label="Checkbox" />
<Checkbox label="Checkbox" checked />
</Inline>
</Stack>
);
};
+10
View File
@@ -0,0 +1,10 @@
import { Container } from '@backstage/canon';
import { DecorativeBox } from '../components/DecorativeBox';
export const ContainerPreview = () => {
return (
<div style={{ maxWidth: '600px', margin: '0 auto' }}>
<DecorativeBox />
</div>
);
};
+12
View File
@@ -0,0 +1,12 @@
import { Grid } from '@backstage/canon';
import { DecorativeBox } from '../components/DecorativeBox';
export const GridPreview = () => {
return (
<Grid>
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
</Grid>
);
};
+23
View File
@@ -1,6 +1,29 @@
import { Text, Heading } from '@backstage/canon';
import { Stack } from '@backstage/canon';
export const HeadingPreview = () => {
return <Heading variant="title1">Look mum, no hands!</Heading>;
};
export const HeadingAllVariants = () => {
return (
<Stack>
<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>
</Stack>
);
};
export const HeadingResponsive = () => {
// TODO: Add responsive heading
return null;
return <Heading variant={{ xs: 'title1', md: 'title2' }}>Responsive</Heading>;
};
export const HeadingPlayground = () => {
return (
<Stack>
+10
View File
@@ -0,0 +1,10 @@
import { Icon } from '@backstage/canon';
import { Stack } from '@backstage/canon';
export const IconPreview = () => {
return (
<Stack>
<Icon name="heart" />
</Stack>
);
};
+26
View File
@@ -0,0 +1,26 @@
import { Inline } from '@backstage/canon';
import { DecorativeBox } from '../components/DecorativeBox';
export const InlinePreview = () => {
return (
<Inline>
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
</Inline>
);
};
+14
View File
@@ -0,0 +1,14 @@
import { Stack } from '@backstage/canon';
import { DecorativeBox } from '../components/DecorativeBox';
export const StackPreview = () => {
return (
<div style={{ maxWidth: '320px', margin: '0 auto' }}>
<Stack>
<DecorativeBox />
<DecorativeBox />
<DecorativeBox />
</Stack>
</div>
);
};
+60
View File
@@ -1,6 +1,66 @@
import { Text } from '@backstage/canon';
import { Stack } from '@backstage/canon';
export const TextPreview = () => {
return (
<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>
);
};
export const TextAllVariants = () => {
return (
<Stack gap="md">
<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>
</Stack>
);
};
export const TextAllWeights = () => {
return (
<Stack gap="md">
<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>
</Stack>
);
};
export const TextResponsive = () => {
// TODO: Add responsive text
return null;
return <Text variant={{ xs: 'label', md: 'body' }}>Responsive</Text>;
};
export const TextPlayground = () => {
return (
<Stack>
@@ -37,6 +37,7 @@ import {
RiCheckLine,
RiMoonLine,
RiSunLine,
RiExternalLinkLine,
} from '@remixicon/react';
/** @public */
@@ -55,6 +56,7 @@ export const icons: IconMap = {
chevronLeft: RiArrowLeftSLine,
chevronRight: RiArrowRightSLine,
cloud: RiCloudFill,
externalLink: RiExternalLinkLine,
heart: RiHeartFill,
moon: RiMoonLine,
plus: RiAddLine,
@@ -30,6 +30,7 @@ export type IconNames =
| 'chevronRight'
| 'chevronUp'
| 'cloud'
| 'externalLink'
| 'heart'
| 'moon'
| 'plus'