Improve layout components

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
Charles de Dreuille
2024-11-25 17:14:01 +00:00
parent c53524ef47
commit 63f975ccc4
28 changed files with 457 additions and 45 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ function getAbsolutePath(value: string): any {
}
const config: StorybookConfig = {
stories: [
'../src/docs/**/*.mdx',
'../docs/**/*.mdx',
'../src/components/**/*.mdx',
'../src/components/**/*.stories.@(js|jsx|mjs|ts|tsx)',
],
@@ -1,7 +1,5 @@
import { Unstyled } from '@storybook/blocks';
import { Text } from '../storybook/text';
import { ComponentStatus } from '../storybook/component-status';
import { Columns } from '../storybook/columns';
import { Columns, Text, ComponentStatus } from './components';
<Unstyled>
+47
View File
@@ -0,0 +1,47 @@
import { Unstyled, Source } from '@storybook/blocks';
import { Title, Text, LayoutComponents } from './components';
import * as Table from './components/table';
<Unstyled>
<Title type="h1">Layout</Title>
<Text>
Canon is made for extensibility. We built this library to make it easy for any
Backstage plugin creator to be able to build their ideas at speed ensuring
consistency across the rest of your ecosystem. Each component is designed to
be editable to match your need but sometimes you want to have more control
over the layout of your page. To help you with that, we created a set of
layout components that you can use to build your own layouts. All of these
components are built to extend on our theming system, making it easy for you
to build your own layouts. Sometimes these components are not enough so we
created a set of helpers to be used with any CSS-in-JS library.
</Text>
<Title type="h2">Layout Components</Title>
<Text>
We built a couple of layout components to help you build responsive elements
that will be consistent with the rest of your Bacvkstage instance. These
components are opinionated and use typescript to ensure that the props you
provide are the ones coming from the theme.
</Text>
<Source
code={`<Stack direction="column" gap="md">
<Box>Hello World</Box>
<Inline gap="sm">
<Box>Project 1</Box>
<Box>Project 2</Box>
</Inline>
</Stack>`}
language="tsx"
dark
/>
<LayoutComponents />
<Title type="h2">Layout Helpers</Title>
<Text>TBD</Text>
</Unstyled>
@@ -1,6 +1,6 @@
import { Unstyled, Source } from '@storybook/blocks';
import { Title, Text, Chip } from '../storybook';
import * as Table from '../storybook/table';
import { Title, Text, Chip } from './components';
import * as Table from './components/table';
<Unstyled>
@@ -79,49 +79,49 @@ import * as Table from '../storybook/table';
<Table.Body>
<Table.Row>
<Table.Cell>
<Chip>--canon-accent</Chip>
<Chip head>--canon-accent</Chip>
</Table.Cell>
<Table.Cell>The accent color for the theme.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip>--canon-bg</Chip>
<Chip head>--canon-bg</Chip>
</Table.Cell>
<Table.Cell>The background color for the theme.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip>--canon-surface-1</Chip>
<Chip head>--canon-surface-1</Chip>
</Table.Cell>
<Table.Cell>The first surface color for the theme.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip>--canon-surface-2</Chip>
<Chip head>--canon-surface-2</Chip>
</Table.Cell>
<Table.Cell>The second surface color for the theme.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip>--canon-outline</Chip>
<Chip head>--canon-outline</Chip>
</Table.Cell>
<Table.Cell>The outline color for the theme.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip>--canon-outline-focus</Chip>
<Chip head>--canon-outline-focus</Chip>
</Table.Cell>
<Table.Cell>The outline focus color for the theme.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip>--canon-text-primary</Chip>
<Chip head>--canon-text-primary</Chip>
</Table.Cell>
<Table.Cell>The primary text color for the theme.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip>--canon-text-secondary</Chip>
<Chip head>--canon-text-secondary</Chip>
</Table.Cell>
<Table.Cell>The secondary text color for the theme.</Table.Cell>
</Table.Row>
@@ -145,17 +145,86 @@ import * as Table from '../storybook/table';
<Table.Body>
<Table.Row>
<Table.Cell>
<Chip>--canon-font-sans</Chip>
<Chip head>--canon-font-sans</Chip>
</Table.Cell>
<Table.Cell>The sans-serif font for the theme.</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip>--canon-font-mono</Chip>
<Chip head>--canon-font-mono</Chip>
</Table.Cell>
<Table.Cell>The monospace font for the theme.</Table.Cell>
</Table.Row>
</Table.Body>
</Table.Root>
<Title type="h2">Spacing</Title>
<Text>
Our default spacing system is made to made to work in most scenarios. We have
7 scale values from `xxs` to `xxl`. We use the values on padding and margin in
our layout components mostly. If you prefer to use a different spacing system,
you can do that by changing the values below.
</Text>
<Table.Root>
<Table.Header>
<Table.HeaderRow>
<Table.HeaderCell>Prop</Table.HeaderCell>
<Table.HeaderCell>Description</Table.HeaderCell>
</Table.HeaderRow>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
<Chip head>--canon-space-unit</Chip>
</Table.Cell>
<Table.Cell>
The base unit for the spacing system. Default value is `1em`
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-space-xxs</Chip>
</Table.Cell>
<Table.Cell>Default value is `0.25 x space unit`</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-space-xs</Chip>
</Table.Cell>
<Table.Cell>Default value is `0.5 x space unit`</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-space-sm</Chip>
</Table.Cell>
<Table.Cell>Default value is `0.75 x space unit`</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-space-md</Chip>
</Table.Cell>
<Table.Cell>Default value is `1.25 x space unit`</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-space-lg</Chip>
</Table.Cell>
<Table.Cell>Default value is `2 x space unit`</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-space-xl</Chip>
</Table.Cell>
<Table.Cell>Default value is `3.25 x space unit`</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-space-xxl</Chip>
</Table.Cell>
<Table.Cell>Default value is `5.25 x space unit`</Table.Cell>
</Table.Row>
</Table.Body>
</Table.Root>
</Unstyled>
+7
View File
@@ -0,0 +1,7 @@
export * from './chip';
export * from './table';
export * from './title';
export * from './text';
export * from './columns';
export * from './component-status';
export * from './layout-components';
@@ -0,0 +1,93 @@
import React from 'react';
import {
container,
box,
content,
title,
description,
whiteBox,
whiteBoxStack,
stack,
verticalDivider,
horizontalDivider,
columns,
inline,
tiles,
whiteBoxColumns,
whiteBoxInline,
whiteBoxTiles,
} from './layout-components.css';
export const LayoutComponents = () => {
return (
<div className={container}>
<div className={box}>
<a className={content} href="/?path=/docs/components-box--docs">
<div className={whiteBox} />
</a>
<div className={title}>Box</div>
<div className={description}>The most basic layout component.</div>
</div>
<div className={box}>
<a
className={`${content} ${stack}`}
href="/?path=/docs/components-stack--docs"
>
<div className={whiteBoxStack} />
<div className={verticalDivider} />
<div className={whiteBoxStack} />
</a>
<div className={title}>Stack</div>
<div className={description}>Arrange your components vertically.</div>
</div>
<div className={box}>
<a
className={`${content} ${columns}`}
href="/?path=/docs/components-columns--docs"
>
<div className={whiteBoxColumns} />
<div className={horizontalDivider} />
<div className={whiteBoxColumns} />
</a>
<div className={title}>Columns</div>
<div className={description}>Arrange your components horizontally.</div>
</div>
<div className={box}>
<a className={content} href="/?path=/docs/components-inline--docs">
<div className={inline}>
<div className={whiteBoxInline} />
<div className={whiteBoxInline} />
<div className={whiteBoxInline} />
<div className={whiteBoxInline} />
<div className={whiteBoxInline} />
<div className={whiteBoxInline} />
<div className={whiteBoxInline} />
<div className={whiteBoxInline} />
<div className={whiteBoxInline} />
<div className={whiteBoxInline} />
<div className={whiteBoxInline} />
<div className={whiteBoxInline} />
</div>
</a>
<div className={title}>Inline</div>
<div className={description}>Arrange your components in a row.</div>
</div>
<div className={box}>
<a className={content} href="/?path=/docs/components-tiles--docs">
<div className={tiles}>
<div className={whiteBoxTiles} />
<div className={whiteBoxTiles} />
<div className={whiteBoxTiles} />
<div className={whiteBoxTiles} />
<div className={whiteBoxTiles} />
<div className={whiteBoxTiles} />
<div className={whiteBoxTiles} />
<div className={whiteBoxTiles} />
</div>
</a>
<div className={title}>Tiles</div>
<div className={description}>Arrange your components in a grid.</div>
</div>
</div>
);
};
@@ -0,0 +1,197 @@
/*
* 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 { style } from '@vanilla-extract/css';
export const container = style({
display: 'flex',
justifyContent: 'flex-start',
gap: '1rem',
flexWrap: 'wrap',
});
export const box = style({
display: 'flex',
flexDirection: 'column',
width: 'calc(33.33% - 0.67rem)',
marginBottom: '1rem',
alignItems: 'flex-start',
});
export const content = style({
flex: 'none',
background: '#f2f2f2',
borderRadius: '4px',
width: '100%',
height: '180px',
border: '1px solid #e0e0e0',
transition: 'border-color 0.2s ease-in-out',
marginBottom: '0.75rem',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
selectors: {
[`${box}:hover &`]: {
borderColor: '#1f47ff',
},
},
});
export const title = style({
fontSize: '16px',
fontFamily: 'var(--canon-font-sans)',
transition: 'color 0.2s ease-in-out',
marginBottom: '0.25rem',
selectors: {
[`${box}:hover &`]: {
color: '#1f47ff',
},
},
});
export const description = style({
fontSize: '16px',
fontFamily: 'var(--canon-font-sans)',
color: '#9e9e9e',
});
export const whiteBox = style({
background: '#fff',
width: '40px',
height: '40px',
borderRadius: '4px',
boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)',
});
export const whiteBoxStack = style([
whiteBox,
{
width: '40%',
height: '28px',
},
]);
export const whiteBoxColumns = style([
whiteBox,
{
width: '30%',
height: '32px',
},
]);
export const whiteBoxInline = style([
whiteBox,
{
width: '32px',
height: '32px',
},
]);
export const whiteBoxTiles = style([
whiteBox,
{
width: '100%',
height: '32px',
},
]);
export const stack = style({
display: 'flex',
flexDirection: 'column',
});
export const columns = style({
display: 'flex',
flexDirection: 'row',
});
export const inline = style({
display: 'flex',
flexWrap: 'wrap',
flexDirection: 'row',
justifyContent: 'flex-start',
alignItems: 'flex-start',
gap: '0.5rem',
paddingLeft: '1rem',
});
export const tiles = style({
display: 'grid',
gridTemplateColumns: 'repeat(4, 1fr)',
gap: '0.5rem',
paddingLeft: '1rem',
paddingRight: '1rem',
width: '100%',
});
export const verticalDivider = style({
display: 'flex',
flexShrink: 0,
flexDirection: 'column',
position: 'relative',
height: '20px',
width: '2px',
backgroundColor: '#1f47ff',
selectors: {
'&::before': {
content: '""',
position: 'absolute',
top: 0,
left: '-9px',
width: '20px',
height: '2px',
backgroundColor: '#1f47ff',
},
'&::after': {
content: '""',
position: 'absolute',
bottom: 0,
left: '-9px',
width: '20px',
height: '2px',
backgroundColor: '#1f47ff',
},
},
});
export const horizontalDivider = style({
flexShrink: 0,
display: 'flex',
flexDirection: 'row',
position: 'relative',
height: '2px',
width: '20px',
backgroundColor: '#1f47ff',
selectors: {
'&::before': {
content: '""',
position: 'absolute',
left: 0,
top: '-9px',
width: '2px',
height: '20px',
backgroundColor: '#1f47ff',
},
'&::after': {
content: '""',
position: 'absolute',
right: 0,
top: '-9px',
width: '2px',
height: '20px',
backgroundColor: '#1f47ff',
},
},
});
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { style } from '@vanilla-extract/css';
import { style, globalStyle } from '@vanilla-extract/css';
export const wrapperStyles = style({
border: '1px solid #e7e7e7',
@@ -39,6 +39,11 @@ export const cellStyles = style({
fontWeight: '300',
});
// Apply global styles to <p> elements inside cellStyles
globalStyle(`${cellStyles} p`, {
margin: '0',
});
export const headerRowStyles = style({
borderBottom: '1px solid #e7e7e7',
});
+3 -3
View File
@@ -1,10 +1,10 @@
import { Canvas, Meta, Unstyled } from '@storybook/blocks';
import * as BoxStories from './box.stories';
import { Chip } from '../../storybook/chip';
import * as Table from '../../storybook/table';
import { Chip } from '../../../docs/components/chip';
import * as Table from '../../../docs/components/table';
import { Box } from './box';
import { Stack } from '../stack/stack';
import { Title, Text } from '../../storybook';
import { Title, Text } from '../../../docs/components';
import { Padding } from './docs/padding';
import { listResponsiveValues, listColorValues } from '../../utils/list-values';
import { responsiveProperties } from './sprinkles.css';
@@ -110,7 +110,7 @@ sortedArgTypes['children'] = {
};
const meta = {
title: 'Layout/Box',
title: 'Components/Box',
component: Box,
parameters: {
layout: 'centered',
@@ -1,6 +1,6 @@
import React from 'react';
import * as Table from '../../../storybook';
import { Chip } from '../../../storybook';
import * as Table from '../../../../docs/components';
import { Chip } from '../../../../docs/components';
import { listResponsiveValues } from '../../../utils/list-values';
import { responsiveProperties } from '../sprinkles.css';
@@ -9,11 +9,11 @@ export const breakpoints = {
export const space = {
none: 0,
xxs: 'var(--space-xxs)',
xs: 'var(--space-xs)',
sm: 'var(--space-sm)',
md: 'var(--space-md)',
lg: 'var(--space-lg)',
xl: 'var(--space-xl)',
xxl: 'var(--space-xxl)',
xxs: 'var(--canon-space-xxs)',
xs: 'var(--canon-space-xs)',
sm: 'var(--canon-space-sm)',
md: 'var(--canon-space-md)',
lg: 'var(--canon-space-lg)',
xl: 'var(--canon-space-xl)',
xxl: 'var(--canon-space-xxl)',
};
@@ -19,7 +19,7 @@ import { Inline } from './inline';
import { Box } from '../box/box';
const meta = {
title: 'Layout/Inline',
title: 'Components/Inline',
component: Inline,
} satisfies Meta<typeof Inline>;
@@ -19,7 +19,7 @@ import { Stack } from './stack';
import { Box } from '../box/box';
const meta = {
title: 'Layout/Stack',
title: 'Components/Stack',
component: Stack,
} satisfies Meta<typeof Stack>;
-4
View File
@@ -1,4 +0,0 @@
export * from './chip';
export * from './table';
export * from './title';
export * from './text';
+8 -8
View File
@@ -18,14 +18,14 @@
--canon-font-emoji: 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
--space-unit: 1em;
--space-xxs: calc(0.25 * var(--space-unit));
--space-xs: calc(0.5 * var(--space-unit));
--space-sm: calc(0.75 * var(--space-unit));
--space-md: calc(1.25 * var(--space-unit));
--space-lg: calc(2 * var(--space-unit));
--space-xl: calc(3.25 * var(--space-unit));
--space-xxl: calc(5.25 * var(--space-unit));
--canon-space-unit: 1em;
--canon-space-xxs: calc(0.25 * var(--canon-space-unit));
--canon-space-xs: calc(0.5 * var(--canon-space-unit));
--canon-space-sm: calc(0.75 * var(--canon-space-unit));
--canon-space-md: calc(1.25 * var(--canon-space-unit));
--canon-space-lg: calc(2 * var(--canon-space-unit));
--canon-space-xl: calc(3.25 * var(--canon-space-unit));
--canon-space-xxl: calc(5.25 * var(--canon-space-unit));
}
/* Dark theme */
+1 -1
View File
@@ -1,6 +1,6 @@
{
"extends": "@backstage/cli/config/tsconfig.json",
"include": ["src"],
"include": ["src", "docs/components"],
"exclude": ["node_modules"],
"compilerOptions": {
"outDir": "dist-types",