From afe82bf57cbdbe8127c52dd4e749d870bbc6f7c2 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Thu, 13 Mar 2025 10:38:44 +0000 Subject: [PATCH 1/4] Add Link component Signed-off-by: Charles de Dreuille --- .../src/components/Link/Link.stories.tsx | 92 +++++++++++++++++++ packages/canon/src/components/Link/Link.tsx | 55 +++++++++++ packages/canon/src/components/Link/index.ts | 18 ++++ packages/canon/src/components/Link/styles.css | 65 +++++++++++++ packages/canon/src/components/Link/types.ts | 33 +++++++ packages/canon/src/css/components.css | 2 +- 6 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 packages/canon/src/components/Link/Link.stories.tsx create mode 100644 packages/canon/src/components/Link/Link.tsx create mode 100644 packages/canon/src/components/Link/index.ts create mode 100644 packages/canon/src/components/Link/styles.css create mode 100644 packages/canon/src/components/Link/types.ts diff --git a/packages/canon/src/components/Link/Link.stories.tsx b/packages/canon/src/components/Link/Link.stories.tsx new file mode 100644 index 0000000000..38e71c310a --- /dev/null +++ b/packages/canon/src/components/Link/Link.stories.tsx @@ -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; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + href: 'https://canon.backstage.io', + children: 'Sign up for Backstage', + }, +}; + +export const AllVariants: Story = { + args: { + ...Default.args, + }, + render: args => ( + + + + + + + ), +}; + +export const AllWeights: Story = { + args: { + ...Default.args, + }, + render: args => ( + + + + + ), +}; + +export const Responsive: Story = { + args: { + variant: { + xs: 'label', + md: 'body', + }, + }, +}; + +export const Playground: Story = { + args: { + ...Default.args, + }, + render: args => ( + + Subtitle + + Body + + Caption + + Label + + + ), +}; diff --git a/packages/canon/src/components/Link/Link.tsx b/packages/canon/src/components/Link/Link.tsx new file mode 100644 index 0000000000..8763575bf9 --- /dev/null +++ b/packages/canon/src/components/Link/Link.tsx @@ -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((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 ( + + {children} + + ); +}); + +Link.displayName = 'Link'; diff --git a/packages/canon/src/components/Link/index.ts b/packages/canon/src/components/Link/index.ts new file mode 100644 index 0000000000..1b38e89d62 --- /dev/null +++ b/packages/canon/src/components/Link/index.ts @@ -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'; diff --git a/packages/canon/src/components/Link/styles.css b/packages/canon/src/components/Link/styles.css new file mode 100644 index 0000000000..40a4f340ca --- /dev/null +++ b/packages/canon/src/components/Link/styles.css @@ -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); +} diff --git a/packages/canon/src/components/Link/types.ts b/packages/canon/src/components/Link/types.ts new file mode 100644 index 0000000000..4c5b34e14e --- /dev/null +++ b/packages/canon/src/components/Link/types.ts @@ -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 { + children: ReactNode; + variant?: + | 'subtitle' + | 'body' + | 'caption' + | 'label' + | Partial>; + weight?: 'regular' | 'bold' | Partial>; + className?: string; + style?: CSSProperties; +} diff --git a/packages/canon/src/css/components.css b/packages/canon/src/css/components.css index 1abee6eb5d..d7d99bbe91 100644 --- a/packages/canon/src/css/components.css +++ b/packages/canon/src/css/components.css @@ -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'; From cb7e99d914018e4283822ba609e9d697593b8641 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Thu, 13 Mar 2025 10:45:04 +0000 Subject: [PATCH 2/4] Add colors to Text Signed-off-by: Charles de Dreuille --- .changeset/smooth-rooms-nail.md | 5 +++++ packages/canon/report.api.md | 13 ++++++++++++ packages/canon/src/components/Text/Text.tsx | 3 +++ packages/canon/src/components/Text/styles.css | 20 +++++++++++++++++++ packages/canon/src/components/Text/types.ts | 12 +++++++++++ 5 files changed, 53 insertions(+) create mode 100644 .changeset/smooth-rooms-nail.md diff --git a/.changeset/smooth-rooms-nail.md b/.changeset/smooth-rooms-nail.md new file mode 100644 index 0000000000..1a193b802c --- /dev/null +++ b/.changeset/smooth-rooms-nail.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': minor +--- + +Updating styles for Text and Link components as well as global surface tokens. diff --git a/packages/canon/report.api.md b/packages/canon/report.api.md index 5ad27f537a..c51d6b6218 100644 --- a/packages/canon/report.api.md +++ b/packages/canon/report.api.md @@ -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?: diff --git a/packages/canon/src/components/Text/Text.tsx b/packages/canon/src/components/Text/Text.tsx index 1d490bc0ef..8bc1daf465 100644 --- a/packages/canon/src/components/Text/Text.tsx +++ b/packages/canon/src/components/Text/Text.tsx @@ -27,6 +27,7 @@ export const Text = forwardRef( children, variant = 'body', weight = 'regular', + color = 'primary', style, className, ...restProps @@ -35,6 +36,7 @@ export const Text = forwardRef( // Get the responsive values for the variant and weight const responsiveVariant = useResponsiveValue(variant); const responsiveWeight = useResponsiveValue(weight); + const responsiveColor = useResponsiveValue(color); return (

( 'canon-Text', responsiveVariant && `canon-Text--variant-${responsiveVariant}`, responsiveWeight && `canon-Text--weight-${responsiveWeight}`, + responsiveColor && `canon-Text--color-${responsiveColor}`, className, )} style={style} diff --git a/packages/canon/src/components/Text/styles.css b/packages/canon/src/components/Text/styles.css index 871960b8db..8252554280 100644 --- a/packages/canon/src/components/Text/styles.css +++ b/packages/canon/src/components/Text/styles.css @@ -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); +} diff --git a/packages/canon/src/components/Text/types.ts b/packages/canon/src/components/Text/types.ts index 4fdc824120..d4f1615694 100644 --- a/packages/canon/src/components/Text/types.ts +++ b/packages/canon/src/components/Text/types.ts @@ -27,6 +27,18 @@ export interface TextProps { | 'label' | Partial>; weight?: 'regular' | 'bold' | Partial>; + color?: + | 'primary' + | 'secondary' + | 'danger' + | 'warning' + | 'success' + | Partial< + Record< + Breakpoint, + 'primary' | 'secondary' | 'danger' | 'warning' | 'success' + > + >; className?: string; style?: CSSProperties; } From 8d72bb631900639dfb6e46d8ebf5501b6e5d26ab Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Thu, 13 Mar 2025 10:55:12 +0000 Subject: [PATCH 3/4] Move from elevated to surface 1 and 2 Signed-off-by: Charles de Dreuille --- canon-docs/src/app/(docs)/theme/theming/page.mdx | 12 ++++++------ .../src/components/CustomTheme/customTheme.tsx | 2 +- canon-docs/src/snippets/_snippets.ts | 4 ++-- packages/canon/src/components/Button/styles.css | 2 +- .../canon/src/components/Input/Input.styles.css | 2 +- packages/canon/src/components/Table/styles.css | 14 +++++++------- packages/canon/src/css/core.css | 6 ++++-- 7 files changed, 22 insertions(+), 20 deletions(-) diff --git a/canon-docs/src/app/(docs)/theme/theming/page.mdx b/canon-docs/src/app/(docs)/theme/theming/page.mdx index 6ecc2cba58..cab9480a14 100644 --- a/canon-docs/src/app/(docs)/theme/theming/page.mdx +++ b/canon-docs/src/app/(docs)/theme/theming/page.mdx @@ -53,7 +53,7 @@ color of your app. - --canon-bg-elevated + --canon-bg-surface-1 Use for any panels or elevated surfaces. @@ -189,7 +189,7 @@ are prefixed with `fg` to make it easier to identify. --canon-fg-primary - 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`. @@ -197,7 +197,7 @@ are prefixed with `fg` to make it easier to identify. --canon-fg-secondary - 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`. @@ -205,7 +205,7 @@ are prefixed with `fg` to make it easier to identify. --canon-fg-link - 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`. @@ -213,7 +213,7 @@ are prefixed with `fg` to make it easier to identify. --canon-fg-link-hover - 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`. @@ -267,7 +267,7 @@ low contrast to help as a separator with the different background colors. --canon-border - It should be used on top of `--canon-bg-elevated`. + It should be used on top of `--canon-bg-surface-1`. diff --git a/canon-docs/src/components/CustomTheme/customTheme.tsx b/canon-docs/src/components/CustomTheme/customTheme.tsx index 174a8d0b24..e8628b83ff 100644 --- a/canon-docs/src/components/CustomTheme/customTheme.tsx +++ b/canon-docs/src/components/CustomTheme/customTheme.tsx @@ -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', diff --git a/canon-docs/src/snippets/_snippets.ts b/canon-docs/src/snippets/_snippets.ts index ad777c48cf..75ff5056f4 100644 --- a/canon-docs/src/snippets/_snippets.ts +++ b/canon-docs/src/snippets/_snippets.ts @@ -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 */ diff --git a/packages/canon/src/components/Button/styles.css b/packages/canon/src/components/Button/styles.css index 0572028e96..b5d6e1dca0 100644 --- a/packages/canon/src/components/Button/styles.css +++ b/packages/canon/src/components/Button/styles.css @@ -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; diff --git a/packages/canon/src/components/Input/Input.styles.css b/packages/canon/src/components/Input/Input.styles.css index 4e3e6771ab..0ff108008d 100644 --- a/packages/canon/src/components/Input/Input.styles.css +++ b/packages/canon/src/components/Input/Input.styles.css @@ -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); diff --git a/packages/canon/src/components/Table/styles.css b/packages/canon/src/components/Table/styles.css index b834485ff5..7d3cb35e3d 100644 --- a/packages/canon/src/components/Table/styles.css +++ b/packages/canon/src/components/Table/styles.css @@ -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); } diff --git a/packages/canon/src/css/core.css b/packages/canon/src/css/core.css index e1b3baf0ca..8c7863953a 100644 --- a/packages/canon/src/css/core.css +++ b/packages/canon/src/css/core.css @@ -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; From ae976943840eeaa0807fbf32c304b1448359c386 Mon Sep 17 00:00:00 2001 From: Charles de Dreuille Date: Thu, 13 Mar 2025 11:04:50 +0000 Subject: [PATCH 4/4] Fix stories Signed-off-by: Charles de Dreuille --- canon-docs/src/components/Tabs/parts.tsx | 5 +- .../src/components/Text/Text.stories.tsx | 68 +++++++++---------- 2 files changed, 34 insertions(+), 39 deletions(-) diff --git a/canon-docs/src/components/Tabs/parts.tsx b/canon-docs/src/components/Tabs/parts.tsx index 7f074f9188..3d02683cef 100644 --- a/canon-docs/src/components/Tabs/parts.tsx +++ b/canon-docs/src/components/Tabs/parts.tsx @@ -31,10 +31,7 @@ export const Tab = (props: React.ComponentProps) => ( {children} diff --git a/packages/canon/src/components/Text/Text.stories.tsx b/packages/canon/src/components/Text/Text.stories.tsx index 547b637619..9cf2a73f6e 100644 --- a/packages/canon/src/components/Text/Text.stories.tsx +++ b/packages/canon/src/components/Text/Text.stories.tsx @@ -39,51 +39,49 @@ export const Default: Story = { }; export const AllVariants: Story = { - render: () => ( - - - 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? - - - 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? - - - 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? - - - 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? - + args: { + ...Default.args, + }, + render: args => ( + + + + + ), }; export const AllWeights: Story = { - render: () => ( - - - 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? - - - 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? - + args: { + ...Default.args, + }, + render: args => ( + + + + + ), +}; + +export const AllColors: Story = { + args: { + ...Default.args, + }, + render: args => ( + + + + + + ), }; 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: () => ( - + Subtitle A man looks at a painting in a museum and says, “Brothers and sisters I