Merge pull request #29200 from backstage/canon-styles-updates

Canon: Text styles updates
This commit is contained in:
Charles de Dreuille
2025-03-13 15:05:55 +00:00
committed by GitHub
20 changed files with 373 additions and 60 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/canon': minor
---
Updating styles for Text and Link components as well as global surface tokens.
@@ -53,7 +53,7 @@ color of your app.
</Table.Row>
<Table.Row>
<Table.Cell>
<Chip head>--canon-bg-elevated</Chip>
<Chip head>--canon-bg-surface-1</Chip>
</Table.Cell>
<Table.Cell>Use for any panels or elevated surfaces.</Table.Cell>
</Table.Row>
@@ -189,7 +189,7 @@ are prefixed with `fg` to make it easier to identify.
<Chip head>--canon-fg-primary</Chip>
</Table.Cell>
<Table.Cell>
It should be used on top of `--canon-bg-app` or `--canon-bg-elevated`.
It should be used on top of `--canon-bg-app` or `--canon-bg-surface-1`.
</Table.Cell>
</Table.Row>
<Table.Row>
@@ -197,7 +197,7 @@ are prefixed with `fg` to make it easier to identify.
<Chip head>--canon-fg-secondary</Chip>
</Table.Cell>
<Table.Cell>
It should be used on top of `--canon-bg-app` or `--canon-bg-elevated`.
It should be used on top of `--canon-bg-app` or `--canon-bg-surface-1`.
</Table.Cell>
</Table.Row>
<Table.Row>
@@ -205,7 +205,7 @@ are prefixed with `fg` to make it easier to identify.
<Chip head>--canon-fg-link</Chip>
</Table.Cell>
<Table.Cell>
It should be used on top of `--canon-bg-app` or `--canon-bg-elevated`.
It should be used on top of `--canon-bg-app` or `--canon-bg-surface-1`.
</Table.Cell>
</Table.Row>
<Table.Row>
@@ -213,7 +213,7 @@ are prefixed with `fg` to make it easier to identify.
<Chip head>--canon-fg-link-hover</Chip>
</Table.Cell>
<Table.Cell>
It should be used on top of `--canon-bg-app` or `--canon-bg-elevated`.
It should be used on top of `--canon-bg-app` or `--canon-bg-surface-1`.
</Table.Cell>
</Table.Row>
<Table.Row>
@@ -267,7 +267,7 @@ low contrast to help as a separator with the different background colors.
<Chip head>--canon-border</Chip>
</Table.Cell>
<Table.Cell>
It should be used on top of `--canon-bg-elevated`.
It should be used on top of `--canon-bg-surface-1`.
</Table.Cell>
</Table.Row>
<Table.Row>
@@ -17,7 +17,7 @@ const defaultTheme = `:root {
const myTheme = createTheme({
theme: 'light',
settings: {
background: 'var(--canon-bg-elevated)',
background: 'var(--canon-bg-surface-1)',
backgroundImage: '',
foreground: '#6182B8',
caret: '#5d00ff',
+1 -4
View File
@@ -31,10 +31,7 @@ export const Tab = (props: React.ComponentProps<typeof TabsPrimitive.Tab>) => (
<Text
variant="subtitle"
weight="bold"
{...rest}
style={{
color: state.selected ? 'var(--primary)' : 'var(--secondary)',
}}
color={state.selected ? 'primary' : 'secondary'}
>
{children}
</Text>
+2 -2
View File
@@ -5,7 +5,7 @@ export const customTheme = `:root {
--canon-font-weight-regular: 400;
--canon-font-weight-bold: 600;
--canon-bg: #f8f8f8;
--canon-bg-elevated: #fff;
--canon-bg-surface-1: #fff;
/* ... other CSS variables */
/* Add your custom components styles here */
@@ -20,7 +20,7 @@ export const customTheme = `:root {
--canon-font-weight-regular: 400;
--canon-font-weight-bold: 600;
--canon-bg: #f8f8f8;
--canon-bg-elevated: #fff;
--canon-bg-surface-1: #fff;
/* ... other CSS variables */
/* Add your custom components styles here */
+13
View File
@@ -957,6 +957,19 @@ export interface TextProps {
// (undocumented)
className?: string;
// (undocumented)
color?:
| 'primary'
| 'secondary'
| 'danger'
| 'warning'
| 'success'
| Partial<
Record<
Breakpoint,
'primary' | 'secondary' | 'danger' | 'warning' | 'success'
>
>;
// (undocumented)
style?: CSSProperties;
// (undocumented)
variant?:
@@ -57,7 +57,7 @@
}
.canon-Button--variant-secondary {
background-color: var(--canon-bg-elevated);
background-color: var(--canon-bg-surface-1);
box-shadow: inset 0 0 0 1px var(--canon-border);
color: var(--canon-fg-primary);
transition: box-shadow 150ms ease;
@@ -18,7 +18,7 @@
border-radius: var(--canon-radius-3);
border: 1px solid var(--canon-border);
padding: 0 var(--canon-space-4);
background-color: var(--canon-bg-elevated);
background-color: var(--canon-bg-surface-1);
font-size: var(--canon-font-size-3);
font-family: var(--canon-font-regular);
font-weight: var(--canon-font-weight-regular);
@@ -0,0 +1,92 @@
/*
* 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 React from 'react';
import type { Meta, StoryObj } from '@storybook/react';
import { Link } from './Link';
import { Flex } from '../Flex';
import { Text } from '../Text';
const meta = {
title: 'Components/Link',
component: Link,
args: {
children: 'Link',
},
} satisfies Meta<typeof Link>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
href: 'https://canon.backstage.io',
children: 'Sign up for Backstage',
},
};
export const AllVariants: Story = {
args: {
...Default.args,
},
render: args => (
<Flex gap="4" direction="column">
<Link href="https://canon.backstage.io" variant="subtitle" {...args} />
<Link href="https://canon.backstage.io" variant="body" {...args} />
<Link href="https://canon.backstage.io" variant="caption" {...args} />
<Link href="https://canon.backstage.io" variant="label" {...args} />
</Flex>
),
};
export const AllWeights: Story = {
args: {
...Default.args,
},
render: args => (
<Flex gap="4" direction="column">
<Link weight="regular" style={{ maxWidth: '600px' }} {...args} />
<Link weight="bold" style={{ maxWidth: '600px' }} {...args} />
</Flex>
),
};
export const Responsive: Story = {
args: {
variant: {
xs: 'label',
md: 'body',
},
},
};
export const Playground: Story = {
args: {
...Default.args,
},
render: args => (
<Flex gap="4" direction="column">
<Text>Subtitle</Text>
<Link variant="subtitle" style={{ maxWidth: '600px' }} {...args} />
<Text>Body</Text>
<Link variant="body" style={{ maxWidth: '600px' }} {...args} />
<Text>Caption</Text>
<Link variant="caption" style={{ maxWidth: '600px' }} {...args} />
<Text>Label</Text>
<Link variant="label" style={{ maxWidth: '600px' }} {...args} />
</Flex>
),
};
@@ -0,0 +1,55 @@
/*
* 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 React, { forwardRef } from 'react';
import { useResponsiveValue } from '../../hooks/useResponsiveValue';
import clsx from 'clsx';
import type { LinkProps } from './types';
/** @public */
export const Link = forwardRef<HTMLAnchorElement, LinkProps>((props, ref) => {
const {
children,
variant = 'body',
weight = 'regular',
style,
className,
...restProps
} = props;
// Get the responsive values for the variant and weight
const responsiveVariant = useResponsiveValue(variant);
const responsiveWeight = useResponsiveValue(weight);
return (
<a
ref={ref}
className={clsx(
'canon-Link',
responsiveVariant && `canon-Link--variant-${responsiveVariant}`,
responsiveWeight && `canon-Link--weight-${responsiveWeight}`,
className,
)}
style={style}
{...restProps}
>
{children}
</a>
);
});
Link.displayName = 'Link';
@@ -0,0 +1,18 @@
/*
* 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 { Link } from './Link';
export type { LinkProps } from './types';
@@ -0,0 +1,65 @@
/*
* 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.
*/
.canon-Link {
font-family: var(--canon-font-regular);
color: var(--canon-fg-link);
padding: 0;
margin: 0;
cursor: pointer;
text-decoration-line: none;
&:hover {
color: var(--canon-fg-link-hover);
text-decoration-line: underline;
text-decoration-style: solid;
text-decoration-thickness: min(2px, max(1px, 0.05em));
text-underline-offset: calc(0.025em + 2px);
text-decoration-color: color-mix(
in srgb,
var(--canon-fg-link-hover) 30%,
transparent
);
}
}
.canon-Link--variant-body {
font-size: var(--canon-font-size-3);
line-height: 140%;
}
.canon-Link--variant-subtitle {
font-size: var(--canon-font-size-4);
line-height: 140%;
}
.canon-Link--variant-caption {
font-size: var(--canon-font-size-2);
line-height: 140%;
}
.canon-Link--variant-label {
font-size: var(--canon-font-size-1);
line-height: 140%;
}
.canon-Link--weight-regular {
font-weight: var(--canon-font-weight-regular);
}
.canon-Link--weight-bold {
font-weight: var(--canon-font-weight-bold);
}
@@ -0,0 +1,33 @@
/*
* 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 type { CSSProperties, ReactNode } from 'react';
import type { Breakpoint } from '../../types';
/** @public */
export interface LinkProps
extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
children: ReactNode;
variant?:
| 'subtitle'
| 'body'
| 'caption'
| 'label'
| Partial<Record<Breakpoint, 'subtitle' | 'body' | 'caption' | 'label'>>;
weight?: 'regular' | 'bold' | Partial<Record<Breakpoint, 'regular' | 'bold'>>;
className?: string;
style?: CSSProperties;
}
@@ -2,7 +2,7 @@
position: relative;
overflow: auto;
width: 100%;
background-color: var(--canon-bg-elevated);
background-color: var(--canon-bg-surface-1);
border-radius: var(--canon-radius-2);
padding-bottom: var(--canon-space-0_5);
padding-top: var(--canon-space-0_5);
@@ -39,16 +39,16 @@
& .table-cell:first-child {
border-top-left-radius: var(--canon-radius-2);
border-bottom-left-radius: var(--canon-radius-2);
box-shadow: inset 4px 2px 0 0 var(--canon-bg-elevated),
inset 4px -2px 0 0 var(--canon-bg-elevated);
box-shadow: inset 4px 2px 0 0 var(--canon-bg-surface-1),
inset 4px -2px 0 0 var(--canon-bg-surface-1);
padding-left: var(--canon-space-3);
}
& .table-cell:last-child {
border-top-right-radius: var(--canon-radius-2);
border-bottom-right-radius: var(--canon-radius-2);
box-shadow: inset -4px 2px 0 0 var(--canon-bg-elevated),
inset -4px -2px 0 0 var(--canon-bg-elevated);
box-shadow: inset -4px 2px 0 0 var(--canon-bg-surface-1),
inset -4px -2px 0 0 var(--canon-bg-surface-1);
padding-right: var(--canon-space-3);
}
}
@@ -56,6 +56,6 @@
.table-cell {
padding: var(--canon-space-3);
background-color: var(--canon-bg);
box-shadow: inset 0px 2px 0 0 var(--canon-bg-elevated),
inset 0px -2px 0 0 var(--canon-bg-elevated);
box-shadow: inset 0px 2px 0 0 var(--canon-bg-surface-1),
inset 0px -2px 0 0 var(--canon-bg-surface-1);
}
@@ -39,51 +39,49 @@ export const Default: Story = {
};
export const AllVariants: Story = {
render: () => (
<Flex 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>
args: {
...Default.args,
},
render: args => (
<Flex gap="4" direction="column">
<Text variant="subtitle" {...args} />
<Text variant="body" {...args} />
<Text variant="caption" {...args} />
<Text variant="label" {...args} />
</Flex>
),
};
export const AllWeights: Story = {
render: () => (
<Flex 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>
args: {
...Default.args,
},
render: args => (
<Flex gap="4" direction="column">
<Text weight="regular" {...args} />
<Text weight="bold" {...args} />
</Flex>
),
};
export const AllColors: Story = {
args: {
...Default.args,
},
render: args => (
<Flex gap="4" direction="column">
<Text color="primary" {...args} />
<Text color="secondary" {...args} />
<Text color="danger" {...args} />
<Text color="warning" {...args} />
<Text color="success" {...args} />
</Flex>
),
};
export const Responsive: Story = {
args: {
...Default.args,
variant: {
xs: 'label',
md: 'body',
@@ -93,7 +91,7 @@ export const Responsive: Story = {
export const Playground: Story = {
render: () => (
<Flex>
<Flex gap="4" direction="column">
<Text>Subtitle</Text>
<Text variant="subtitle" style={{ maxWidth: '600px' }}>
A man looks at a painting in a museum and says, Brothers and sisters I
@@ -27,6 +27,7 @@ export const Text = forwardRef<HTMLParagraphElement, TextProps>(
children,
variant = 'body',
weight = 'regular',
color = 'primary',
style,
className,
...restProps
@@ -35,6 +36,7 @@ export const Text = forwardRef<HTMLParagraphElement, TextProps>(
// Get the responsive values for the variant and weight
const responsiveVariant = useResponsiveValue(variant);
const responsiveWeight = useResponsiveValue(weight);
const responsiveColor = useResponsiveValue(color);
return (
<p
@@ -43,6 +45,7 @@ export const Text = forwardRef<HTMLParagraphElement, TextProps>(
'canon-Text',
responsiveVariant && `canon-Text--variant-${responsiveVariant}`,
responsiveWeight && `canon-Text--weight-${responsiveWeight}`,
responsiveColor && `canon-Text--color-${responsiveColor}`,
className,
)}
style={style}
@@ -48,3 +48,23 @@
.canon-Text--weight-bold {
font-weight: var(--canon-font-weight-bold);
}
.canon-Text--color-primary {
color: var(--canon-fg-primary);
}
.canon-Text--color-secondary {
color: var(--canon-fg-secondary);
}
.canon-Text--color-danger {
color: var(--canon-fg-danger);
}
.canon-Text--color-warning {
color: var(--canon-fg-warning);
}
.canon-Text--color-success {
color: var(--canon-fg-success);
}
@@ -27,6 +27,18 @@ export interface TextProps {
| 'label'
| Partial<Record<Breakpoint, 'subtitle' | 'body' | 'caption' | 'label'>>;
weight?: 'regular' | 'bold' | Partial<Record<Breakpoint, 'regular' | 'bold'>>;
color?:
| 'primary'
| 'secondary'
| 'danger'
| 'warning'
| 'success'
| Partial<
Record<
Breakpoint,
'primary' | 'secondary' | 'danger' | 'warning' | 'success'
>
>;
className?: string;
style?: CSSProperties;
}
+1 -1
View File
@@ -14,7 +14,6 @@
* limitations under the License.
*/
/* Components */
@import '../components/Box/styles.css';
@import '../components/Button/styles.css';
@import '../components/Flex/styles.css';
@@ -27,3 +26,4 @@
@import '../components/Heading/styles.css';
@import '../components/Input/Input.styles.css';
@import '../components/Field/Field.styles.css';
@import '../components/Link/styles.css';
+4 -2
View File
@@ -72,7 +72,8 @@
/* Background Colors */
--canon-bg: #f8f8f8;
--canon-bg-elevated: #fff;
--canon-bg-surface-1: #fff;
--canon-bg-surface-2: #ececec;
--canon-bg-solid: #1f5493;
--canon-bg-solid-hover: #163a66;
--canon-bg-solid-pressed: #0f2b4e;
@@ -116,7 +117,8 @@
[data-theme='dark'] {
/* Background Colors */
--canon-bg: #000000;
--canon-bg-elevated: #191919;
--canon-bg-surface-1: #191919;
--canon-bg-surface-2: #242424;
--canon-bg-solid: #9cc9ff;
--canon-bg-solid-hover: #83b9fd;
--canon-bg-solid-pressed: #83b9fd;