diff --git a/.changeset/small-rice-arrive.md b/.changeset/small-rice-arrive.md new file mode 100644 index 0000000000..a30bec6a82 --- /dev/null +++ b/.changeset/small-rice-arrive.md @@ -0,0 +1,14 @@ +--- +'@backstage/plugin-mui-to-bui': patch +--- + +Updated MUI to BUI theme converter to align with latest token changes + +**Changes:** + +- Removed generation of deprecated tokens: `--bui-fg-link`, `--bui-fg-link-hover`, `--bui-fg-tint`, `--bui-fg-tint-disabled`, `--bui-bg-tint` and all its variants +- Added generation of new `info` status tokens: `--bui-fg-info`, `--bui-fg-info-on-bg`, `--bui-bg-info`, `--bui-border-info` +- Updated status color mapping to generate both standalone and `-on-bg` variants for danger, warning, success, and info +- Status colors now use `.main` for standalone variants and `.dark` for `-on-bg` variants, providing better visual hierarchy + +The converter now generates tokens that match the updated BUI design system structure, with clear distinction between status colors for standalone use vs. use on colored backgrounds. diff --git a/.changeset/wicked-walls-accept.md b/.changeset/wicked-walls-accept.md new file mode 100644 index 0000000000..dc5e86a0d4 --- /dev/null +++ b/.changeset/wicked-walls-accept.md @@ -0,0 +1,59 @@ +--- +'@backstage/ui': minor +--- + +**BREAKING**: Removed link and tint color tokens, added new status foreground tokens, and improved Link component styling + +The following color tokens have been removed: + +- `--bui-fg-link` (and all related tokens: `-hover`, `-pressed`, `-disabled`) +- `--bui-fg-tint` (and all related tokens: `-hover`, `-pressed`, `-disabled`) +- `--bui-bg-tint` (and all related tokens: `-hover`, `-pressed`, `-disabled`) +- `--bui-border-tint` (and all related tokens) + +**New Status Tokens:** + +Added dedicated tokens for status colors that distinguish between usage on status backgrounds vs. standalone usage: + +- `--bui-fg-danger-on-bg` / `--bui-fg-danger` +- `--bui-fg-warning-on-bg` / `--bui-fg-warning` +- `--bui-fg-success-on-bg` / `--bui-fg-success` +- `--bui-fg-info-on-bg` / `--bui-fg-info` + +The `-on-bg` variants are designed for text on colored backgrounds, while the base variants are for standalone status indicators with improved visibility and contrast. + +**Migration:** + +For link colors, migrate to one of the following alternatives: + +```diff +.custom-link { +- color: var(--bui-fg-link); ++ color: var(--bui-fg-info); /* For informational links */ ++ /* or */ ++ color: var(--bui-fg-primary); /* For standard text links */ +} +``` + +For tint colors (backgrounds, foregrounds, borders), migrate to appropriate status or neutral colors: + +```diff +.info-section { +- background: var(--bui-bg-tint); ++ background: var(--bui-bg-info); /* For informational sections */ ++ /* or */ ++ background: var(--bui-bg-neutral-on-surface-0); /* For neutral emphasis */ +} +``` + +If you're using status foreground colors on colored backgrounds, update to the new `-on-bg` tokens: + +```diff +.error-badge { +- color: var(--bui-fg-danger); ++ color: var(--bui-fg-danger-on-bg); + background: var(--bui-bg-danger); +} +``` + +**Affected components:** Link diff --git a/.storybook/themes/spotify.css b/.storybook/themes/spotify.css index 352c633a15..4f8b1b137d 100644 --- a/.storybook/themes/spotify.css +++ b/.storybook/themes/spotify.css @@ -259,13 +259,9 @@ --bui-fg-primary: var(--bui-white); --bui-fg-secondary: var(--bui-gray-7); - --bui-fg-link: var(--bui-white); - --bui-fg-link-hover: var(--bui-white); --bui-fg-disabled: var(--bui-gray-5); --bui-fg-solid: var(--bui-black); --bui-fg-solid-disabled: #072f15; - --bui-fg-tint: var(--bui-white); - --bui-fg-tint-disabled: var(--bui-gray-5); --bui-fg-danger: #e22b2b; --bui-fg-warning: #e36d05; --bui-fg-success: #1db954; diff --git a/docs-ui/src/app/components/link/components.tsx b/docs-ui/src/app/components/link/components.tsx index ac4bd05801..320dd52517 100644 --- a/docs-ui/src/app/components/link/components.tsx +++ b/docs-ui/src/app/components/link/components.tsx @@ -7,7 +7,9 @@ import { MemoryRouter } from 'react-router-dom'; export const Default = () => { return ( - Sign up for Backstage + + Sign up for Backstage + ); }; @@ -15,7 +17,7 @@ export const Default = () => { export const ExternalLink = () => { return ( - + Sign up for Backstage @@ -58,22 +60,25 @@ export const AllVariants = () => { export const AllColors = () => { return ( - - + + Primary - + Secondary - + Danger - + Warning - + Success + + Info + ); @@ -83,13 +88,28 @@ export const Weight = () => { return ( - + Regular - + Bold ); }; + +export const Standalone = () => { + return ( + + + + Default link + + + Standalone link + + + + ); +}; diff --git a/docs-ui/src/app/components/link/page.mdx b/docs-ui/src/app/components/link/page.mdx index 13710ee259..8715bb270b 100644 --- a/docs-ui/src/app/components/link/page.mdx +++ b/docs-ui/src/app/components/link/page.mdx @@ -10,6 +10,7 @@ import { allVariantsSnippet, allColorsSnippet, weightSnippet, + standaloneSnippet, } from './snippets'; import { Default, @@ -17,6 +18,7 @@ import { AllVariants, AllColors, Weight, + Standalone, } from './components'; import { PageTitle } from '@/components/PageTitle'; import { Theming } from '@/components/Theming'; @@ -55,6 +57,7 @@ Use `target="_blank"` to open links in a new tab. py={4} preview={} code={externalLinkSnippet} + layout="side-by-side" /> ### Variants @@ -79,7 +82,25 @@ Status colors for contextual links. ### Weight -} code={weightSnippet} /> +} + code={weightSnippet} + layout="side-by-side" +/> + +### Standalone + +Use `standalone` to remove the underline by default. The underline will appear on hover. + +} + code={standaloneSnippet} + layout="side-by-side" +/> diff --git a/docs-ui/src/app/components/link/props-definition.tsx b/docs-ui/src/app/components/link/props-definition.tsx index 7d3b762a24..f0aa95114e 100644 --- a/docs-ui/src/app/components/link/props-definition.tsx +++ b/docs-ui/src/app/components/link/props-definition.tsx @@ -66,6 +66,12 @@ export const linkPropDefs: Record = { type: 'boolean', description: 'Truncates text with ellipsis when it overflows its container.', + default: 'false', + }, + standalone: { + type: 'boolean', + description: 'Removes underline by default. Underline appears on hover.', + default: 'false', }, ...childrenPropDefs, ...classNamePropDefs, diff --git a/docs-ui/src/app/components/link/snippets.ts b/docs-ui/src/app/components/link/snippets.ts index 234b3fe983..386ad9f1a0 100644 --- a/docs-ui/src/app/components/link/snippets.ts +++ b/docs-ui/src/app/components/link/snippets.ts @@ -4,7 +4,7 @@ export const linkUsageSnippet = `import { Link } from '@backstage/ui'; export const defaultSnippet = `Sign up for Backstage`; -export const externalLinkSnippet = ` +export const externalLinkSnippet = ` Sign up for Backstage `; @@ -25,9 +25,15 @@ export const allColorsSnippet = ` Danger Warning Success + Info `; export const weightSnippet = ` Regular Bold `; + +export const standaloneSnippet = ` + Default link + Standalone link +`; diff --git a/docs-ui/src/app/components/text/components.tsx b/docs-ui/src/app/components/text/components.tsx index ef803ea2fb..8021a1dde6 100644 --- a/docs-ui/src/app/components/text/components.tsx +++ b/docs-ui/src/app/components/text/components.tsx @@ -30,6 +30,7 @@ export const Colors = () => { Danger Warning Success + Info ); }; diff --git a/docs-ui/src/app/components/text/snippets.ts b/docs-ui/src/app/components/text/snippets.ts index 7f2c222186..b8cc26bd6b 100644 --- a/docs-ui/src/app/components/text/snippets.ts +++ b/docs-ui/src/app/components/text/snippets.ts @@ -21,6 +21,7 @@ export const colorsSnippet = ` Danger Warning Success + Info `; export const weightsSnippet = ` diff --git a/docs-ui/src/app/tokens/page.mdx b/docs-ui/src/app/tokens/page.mdx index 97266afbf4..fdc8aa9a90 100644 --- a/docs-ui/src/app/tokens/page.mdx +++ b/docs-ui/src/app/tokens/page.mdx @@ -240,30 +240,6 @@ color of your app. Used for solid background colors when disabled. - - - --bui-bg-tint - - Used for tint background colors. - - - - --bui-bg-tint-hover - - Used for tint background colors when hovered. - - - - --bui-bg-tint-focus - - Used for tint background colors when active. - - - - --bui-bg-tint-disabled - - Used for tint background colors when disabled. - --bui-bg-danger @@ -282,6 +258,12 @@ color of your app. Used to show success information. + + + --bui-bg-info + + Used to show informational content. + @@ -315,22 +297,6 @@ are prefixed with `fg` to make it easier to identify. It should be used on top of main background surfaces. - - - --bui-fg-link - - - It should be used on top of main background surfaces. - - - - - --bui-fg-link-hover - - - It should be used on top of main background surfaces. - - --bui-fg-disabled @@ -347,33 +313,45 @@ are prefixed with `fg` to make it easier to identify. It should be used on top of solid background colors. - - - --bui-fg-tint - - - It should be used on top of tint background colors. - - - - - --bui-fg-tint-disabled - - - It should be used on top of tint background colors when disabled. - - --bui-fg-danger + Used for error states and destructive actions. + + + + --bui-fg-warning + + + Used for warning states and cautionary information. + + + + + --bui-fg-success + + Used for success states and positive feedback. + + + + --bui-fg-info + + + Used for informational content and neutral status. + + + + + --bui-fg-danger-on-bg + It should be used on top of danger background colors. - --bui-fg-warning + --bui-fg-warning-on-bg It should be used on top of warning background colors. @@ -381,12 +359,20 @@ are prefixed with `fg` to make it easier to identify. - --bui-fg-success + --bui-fg-success-on-bg It should be used on top of success background colors. + + + --bui-fg-info-on-bg + + + It should be used on top of info background colors. + + @@ -449,6 +435,12 @@ low contrast to help as a separator with the different background colors. It should be used on top of `--bui-bg-success`. + + + --bui-border-info + + It should be used on top of `--bui-bg-info`. + diff --git a/docs-ui/src/css/theme-backstage.css b/docs-ui/src/css/theme-backstage.css index f3bf8bf83a..e1ba6ba5b0 100644 --- a/docs-ui/src/css/theme-backstage.css +++ b/docs-ui/src/css/theme-backstage.css @@ -79,17 +79,17 @@ --bui-bg-info: #dbeafe; --bui-fg-primary: var(--bui-black); --bui-fg-secondary: var(--bui-gray-7); - --bui-fg-link: #1f5493; - --bui-fg-link-hover: #1f2d5c; --bui-fg-disabled: #9e9e9e; --bui-fg-solid: var(--bui-white); --bui-fg-solid-disabled: #98a8bc; - --bui-fg-tint: #1f5493; - --bui-fg-tint-disabled: var(--bui-gray-5); - --bui-fg-danger: #991919; - --bui-fg-warning: #92310a; - --bui-fg-success: #116932; - --bui-fg-info: #173da6; + --bui-fg-danger-on-bg: #991919; + --bui-fg-warning-on-bg: #92310a; + --bui-fg-success-on-bg: #116932; + --bui-fg-info-on-bg: #173da6; + --bui-fg-danger: #ec3b18; + --bui-fg-warning: #ef7a32; + --bui-fg-success: #1ed760; + --bui-fg-info: #0d74ce; --bui-border: #0000001a; --bui-border-hover: #0003; --bui-border-pressed: #0006; @@ -143,17 +143,17 @@ --bui-bg-info: #132049; --bui-fg-primary: var(--bui-white); --bui-fg-secondary: var(--bui-gray-7); - --bui-fg-link: #9cc9ff; - --bui-fg-link-hover: #7eb5f7; --bui-fg-disabled: var(--bui-gray-7); --bui-fg-solid: #101821; --bui-fg-solid-disabled: #6191cc; - --bui-fg-tint: #9cc9ff; - --bui-fg-tint-disabled: var(--bui-gray-5); - --bui-fg-danger: #fca5a5; - --bui-fg-warning: #fdba74; - --bui-fg-success: #86efac; - --bui-fg-info: #a3cfff; + --bui-fg-danger-on-bg: #fca5a5; + --bui-fg-warning-on-bg: #fdba74; + --bui-fg-success-on-bg: #86efac; + --bui-fg-info-on-bg: #a3cfff; + --bui-fg-danger: #ff5a30; + --bui-fg-warning: #ffa057; + --bui-fg-success: #1ed760; + --bui-fg-info: #70b8ff; --bui-border: #ffffff1f; --bui-border-hover: #fff6; --bui-border-pressed: #ffffff80; diff --git a/docs-ui/src/css/theme-spotify.css b/docs-ui/src/css/theme-spotify.css index f8b18d6a31..dc660b5b5d 100644 --- a/docs-ui/src/css/theme-spotify.css +++ b/docs-ui/src/css/theme-spotify.css @@ -222,13 +222,9 @@ --bui-bg-success: #132d21; --bui-fg-primary: var(--bui-white); --bui-fg-secondary: var(--bui-gray-7); - --bui-fg-link: var(--bui-white); - --bui-fg-link-hover: var(--bui-white); --bui-fg-disabled: var(--bui-gray-5); --bui-fg-solid: var(--bui-black); --bui-fg-solid-disabled: #072f15; - --bui-fg-tint: var(--bui-white); - --bui-fg-tint-disabled: var(--bui-gray-5); --bui-fg-danger: #e22b2b; --bui-fg-warning: #e36d05; --bui-fg-success: #1db954; diff --git a/docs/conf/user-interface/index.md b/docs/conf/user-interface/index.md index e0fb645d78..002383c6f4 100644 --- a/docs/conf/user-interface/index.md +++ b/docs/conf/user-interface/index.md @@ -131,7 +131,10 @@ And if you’d like to go even further, you can target specific component class | `--bui-fg-solid` | This is for texts or icons on top of a solid backgrounds. | | `--bui-fg-primary` | Your primary text or icon colours. | | `--bui-fg-secondary` | Your secondary text or icon colours. | -| `--bui-fg-link` | Used for links. | +| `--bui-fg-danger` | Used for error states and destructive actions. | +| `--bui-fg-warning` | Used for warning states and cautionary information. | +| `--bui-fg-success` | Used for success states and positive feedback. | +| `--bui-fg-info` | Used for informational content and neutral status. | | `--bui-border` | Main borders around surfaces like `Card`, `Dialog`, ... | | `--bui-font-regular` | The main font of your app. | @@ -169,31 +172,29 @@ These colors are used for the background of your application. We are mostly usin | `--bui-bg-solid-hover` | Used for solid background colors when hovered. | | `--bui-bg-solid-pressed` | Used for solid background colors when pressed. | | `--bui-bg-solid-disabled` | Used for solid background colors when disabled. | -| `--bui-bg-tint` | Used for tint background colors. | -| `--bui-bg-tint-hover` | Used for tint background colors when hovered. | -| `--bui-bg-tint-focus` | Used for tint background colors when active. | -| `--bui-bg-tint-disabled` | Used for tint background colors when disabled. | | `--bui-bg-danger` | Used to show errors information. | | `--bui-bg-warning` | Used to show warnings information. | | `--bui-bg-success` | Used to show success information. | +| `--bui-bg-info` | Used to show informational content. | #### Foreground colors Foreground colours are meant to work in pair with a background colours. Typically this would work for icons, texts, shapes, ... Use a matching name to know what foreground color to use. These colors are prefixed with `fg` to make it easier to identify. -| Token Name | Description | -| ------------------------ | ----------------------------------------------------------------- | -| `--bui-fg-primary` | It should be used on top of main background surfaces. | -| `--bui-fg-secondary` | It should be used on top of main background surfaces. | -| `--bui-fg-link` | It should be used on top of main background surfaces. | -| `--bui-fg-link-hover` | It should be used on top of main background surfaces. | -| `--bui-fg-disabled` | It should be used on top of main background surfaces. | -| `--bui-fg-solid` | It should be used on top of solid background colors. | -| `--bui-fg-tint` | It should be used on top of tint background colors. | -| `--bui-fg-tint-disabled` | It should be used on top of tint background colors when disabled. | -| `--bui-fg-danger` | It should be used on top of danger background colors. | -| `--bui-fg-warning` | It should be used on top of warning background colors. | -| `--bui-fg-success` | It should be used on top of success background colors. | +| Token Name | Description | +| ------------------------ | ------------------------------------------------------ | +| `--bui-fg-primary` | It should be used on top of main background surfaces. | +| `--bui-fg-secondary` | It should be used on top of main background surfaces. | +| `--bui-fg-disabled` | It should be used on top of main background surfaces. | +| `--bui-fg-solid` | It should be used on top of solid background colors. | +| `--bui-fg-danger` | Used for error states and destructive actions. | +| `--bui-fg-warning` | Used for warning states and cautionary information. | +| `--bui-fg-success` | Used for success states and positive feedback. | +| `--bui-fg-info` | Used for informational content and neutral status. | +| `--bui-fg-danger-on-bg` | It should be used on top of danger background colors. | +| `--bui-fg-warning-on-bg` | It should be used on top of warning background colors. | +| `--bui-fg-success-on-bg` | It should be used on top of success background colors. | +| `--bui-fg-info-on-bg` | It should be used on top of info background colors. | #### Border colors diff --git a/packages/ui/css/styles.css b/packages/ui/css/styles.css index 1564855309..7538f53bbe 100644 --- a/packages/ui/css/styles.css +++ b/packages/ui/css/styles.css @@ -81,17 +81,17 @@ --bui-bg-info: #dbeafe; --bui-fg-primary: var(--bui-black); --bui-fg-secondary: var(--bui-gray-7); - --bui-fg-link: #1f5493; - --bui-fg-link-hover: #1f2d5c; --bui-fg-disabled: #9e9e9e; --bui-fg-solid: var(--bui-white); --bui-fg-solid-disabled: #98a8bc; - --bui-fg-tint: #1f5493; - --bui-fg-tint-disabled: var(--bui-gray-5); - --bui-fg-danger: #991919; - --bui-fg-warning: #92310a; - --bui-fg-success: #116932; - --bui-fg-info: #173da6; + --bui-fg-danger-on-bg: #991919; + --bui-fg-warning-on-bg: #92310a; + --bui-fg-success-on-bg: #116932; + --bui-fg-info-on-bg: #173da6; + --bui-fg-danger: #ec3b18; + --bui-fg-warning: #ef7a32; + --bui-fg-success: #1ed760; + --bui-fg-info: #0d74ce; --bui-border: #0000001a; --bui-border-hover: #0003; --bui-border-pressed: #0006; @@ -146,17 +146,17 @@ --bui-bg-info: #132049; --bui-fg-primary: var(--bui-white); --bui-fg-secondary: var(--bui-gray-7); - --bui-fg-link: #9cc9ff; - --bui-fg-link-hover: #7eb5f7; --bui-fg-disabled: var(--bui-gray-7); --bui-fg-solid: #101821; --bui-fg-solid-disabled: #6191cc; - --bui-fg-tint: #9cc9ff; - --bui-fg-tint-disabled: var(--bui-gray-5); - --bui-fg-danger: #fca5a5; - --bui-fg-warning: #fdba74; - --bui-fg-success: #86efac; - --bui-fg-info: #a3cfff; + --bui-fg-danger-on-bg: #fca5a5; + --bui-fg-warning-on-bg: #fdba74; + --bui-fg-success-on-bg: #86efac; + --bui-fg-info-on-bg: #a3cfff; + --bui-fg-danger: #ff5a30; + --bui-fg-warning: #ffa057; + --bui-fg-success: #1ed760; + --bui-fg-info: #70b8ff; --bui-border: #ffffff1f; --bui-border-hover: #fff6; --bui-border-pressed: #ffffff80; diff --git a/packages/ui/report.api.md b/packages/ui/report.api.md index b4211d2499..ae76c8a8d0 100644 --- a/packages/ui/report.api.md +++ b/packages/ui/report.api.md @@ -1113,8 +1113,10 @@ export const LinkDefinition: { 'danger', 'warning', 'success', + 'info', ]; readonly truncate: readonly [true, false]; + readonly standalone: readonly [true, false]; }; }; @@ -1128,6 +1130,8 @@ export interface LinkProps extends LinkProps_2 { | TextColorStatus | Partial>; // (undocumented) + standalone?: boolean; + // (undocumented) title?: string; // (undocumented) truncate?: boolean; @@ -1906,7 +1910,7 @@ export { Text_2 as Text }; export type TextColors = 'primary' | 'secondary'; // @public (undocumented) -export type TextColorStatus = 'danger' | 'warning' | 'success'; +export type TextColorStatus = 'danger' | 'warning' | 'success' | 'info'; // @public export const TextDefinition: { @@ -1922,6 +1926,7 @@ export const TextDefinition: { 'danger', 'warning', 'success', + 'info', ]; readonly truncate: readonly [true, false]; }; diff --git a/packages/ui/src/components/Alert/Alert.module.css b/packages/ui/src/components/Alert/Alert.module.css index 643a7d09b0..9c2fc6a586 100644 --- a/packages/ui/src/components/Alert/Alert.module.css +++ b/packages/ui/src/components/Alert/Alert.module.css @@ -36,22 +36,22 @@ .bui-Alert[data-status='info'] { --alert-bg: var(--bui-bg-info); - --alert-fg: var(--bui-fg-info); + --alert-fg: var(--bui-fg-info-on-bg); } .bui-Alert[data-status='success'] { --alert-bg: var(--bui-bg-success); - --alert-fg: var(--bui-fg-success); + --alert-fg: var(--bui-fg-success-on-bg); } .bui-Alert[data-status='warning'] { --alert-bg: var(--bui-bg-warning); - --alert-fg: var(--bui-fg-warning); + --alert-fg: var(--bui-fg-warning-on-bg); } .bui-Alert[data-status='danger'] { --alert-bg: var(--bui-bg-danger); - --alert-fg: var(--bui-fg-danger); + --alert-fg: var(--bui-fg-danger-on-bg); } .bui-AlertIcon { diff --git a/packages/ui/src/components/HeaderPage/HeaderPage.tsx b/packages/ui/src/components/HeaderPage/HeaderPage.tsx index efa7ea8d58..8b1ef31874 100644 --- a/packages/ui/src/components/HeaderPage/HeaderPage.tsx +++ b/packages/ui/src/components/HeaderPage/HeaderPage.tsx @@ -56,6 +56,7 @@ export const HeaderPage = (props: HeaderPageProps) => { color="secondary" truncate style={{ maxWidth: '240px' }} + standalone > {breadcrumb.label} diff --git a/packages/ui/src/components/Link/Link.module.css b/packages/ui/src/components/Link/Link.module.css index a319dfb66b..27ffe244fb 100644 --- a/packages/ui/src/components/Link/Link.module.css +++ b/packages/ui/src/components/Link/Link.module.css @@ -22,9 +22,14 @@ padding: 0; margin: 0; cursor: pointer; - text-decoration-line: none; display: inline-block; + 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, currentColor 30%, transparent); + &:hover { text-decoration-line: underline; text-decoration-style: solid; @@ -102,9 +107,25 @@ color: var(--bui-fg-success); } + .bui-Link[data-color='info'] { + color: var(--bui-fg-info); + } + .bui-Link[data-truncate] { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } + + .bui-Link[data-standalone] { + text-decoration-line: none; + + &: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, currentColor 30%, transparent); + } + } } diff --git a/packages/ui/src/components/Link/Link.stories.tsx b/packages/ui/src/components/Link/Link.stories.tsx index 7d680e5616..fafb5948d9 100644 --- a/packages/ui/src/components/Link/Link.stories.tsx +++ b/packages/ui/src/components/Link/Link.stories.tsx @@ -102,6 +102,12 @@ export const AllColors = meta.story({ color="success" children="I am success" /> + ), }); @@ -235,6 +241,25 @@ export const Truncate = meta.story({ }, }); +export const Standalone = meta.story({ + args: { + href: '/', + children: 'Standalone link (no underline by default)', + standalone: true, + }, +}); + +export const StandaloneComparison = meta.story({ + render: () => ( + + Default link (underline by default): + + Standalone link (underline on hover only): + + + ), +}); + export const Responsive = meta.story({ args: { ...Default.input.args, @@ -244,27 +269,3 @@ export const Responsive = meta.story({ }, }, }); - -export const Playground = meta.story({ - args: { - ...Default.input.args, - }, - render: args => ( - - Title X Small - - Body X Small - - Body Small - - Body Medium - - Body Large - - Title Small - - Title Medium - - - ), -}); diff --git a/packages/ui/src/components/Link/Link.tsx b/packages/ui/src/components/Link/Link.tsx index fb9d25e805..efdca26750 100644 --- a/packages/ui/src/components/Link/Link.tsx +++ b/packages/ui/src/components/Link/Link.tsx @@ -44,6 +44,7 @@ const LinkInternal = forwardRef((props, ref) => { weight, color, truncate, + standalone, slot, ...restProps } = cleanedProps; diff --git a/packages/ui/src/components/Link/definition.ts b/packages/ui/src/components/Link/definition.ts index cece929f11..58b780c812 100644 --- a/packages/ui/src/components/Link/definition.ts +++ b/packages/ui/src/components/Link/definition.ts @@ -27,7 +27,15 @@ export const LinkDefinition = { dataAttributes: { variant: ['subtitle', 'body', 'caption', 'label'] as const, weight: ['regular', 'bold'] as const, - color: ['primary', 'secondary', 'danger', 'warning', 'success'] as const, + color: [ + 'primary', + 'secondary', + 'danger', + 'warning', + 'success', + 'info', + ] as const, truncate: [true, false] as const, + standalone: [true, false] as const, }, } as const satisfies ComponentDefinition; diff --git a/packages/ui/src/components/Link/types.ts b/packages/ui/src/components/Link/types.ts index 796676e593..9259a8a1d7 100644 --- a/packages/ui/src/components/Link/types.ts +++ b/packages/ui/src/components/Link/types.ts @@ -33,6 +33,7 @@ export interface LinkProps extends AriaLinkProps { | TextColorStatus | Partial>; truncate?: boolean; + standalone?: boolean; // This is used to set the title attribute on the link title?: string; diff --git a/packages/ui/src/components/Text/Text.module.css b/packages/ui/src/components/Text/Text.module.css index 57de6f0ef8..b7b7b9f203 100644 --- a/packages/ui/src/components/Text/Text.module.css +++ b/packages/ui/src/components/Text/Text.module.css @@ -91,6 +91,10 @@ color: var(--bui-fg-success); } + .bui-Text[data-color='info'] { + color: var(--bui-fg-info); + } + .bui-Text[data-truncate] { overflow: hidden; text-overflow: ellipsis; diff --git a/packages/ui/src/components/Text/Text.stories.tsx b/packages/ui/src/components/Text/Text.stories.tsx index 09a317716a..e8dff19318 100644 --- a/packages/ui/src/components/Text/Text.stories.tsx +++ b/packages/ui/src/components/Text/Text.stories.tsx @@ -101,6 +101,7 @@ export const AllColors = meta.story({ + ), }); diff --git a/packages/ui/src/components/Text/definition.ts b/packages/ui/src/components/Text/definition.ts index dbbfd6b72a..ad676405c7 100644 --- a/packages/ui/src/components/Text/definition.ts +++ b/packages/ui/src/components/Text/definition.ts @@ -27,7 +27,14 @@ export const TextDefinition = { dataAttributes: { variant: ['subtitle', 'body', 'caption', 'label'] as const, weight: ['regular', 'bold'] as const, - color: ['primary', 'secondary', 'danger', 'warning', 'success'] as const, + color: [ + 'primary', + 'secondary', + 'danger', + 'warning', + 'success', + 'info', + ] as const, truncate: [true, false] as const, }, } as const satisfies ComponentDefinition; diff --git a/packages/ui/src/css/tokens.css b/packages/ui/src/css/tokens.css index 992a49b2f2..9cd6ea98e5 100644 --- a/packages/ui/src/css/tokens.css +++ b/packages/ui/src/css/tokens.css @@ -122,17 +122,19 @@ /* Foreground colors */ --bui-fg-primary: var(--bui-black); --bui-fg-secondary: var(--bui-gray-7); - --bui-fg-link: #1f5493; - --bui-fg-link-hover: #1f2d5c; --bui-fg-disabled: #9e9e9e; --bui-fg-solid: var(--bui-white); --bui-fg-solid-disabled: #98a8bc; - --bui-fg-tint: #1f5493; - --bui-fg-tint-disabled: var(--bui-gray-5); - --bui-fg-danger: #991919; - --bui-fg-warning: #92310a; - --bui-fg-success: #116932; - --bui-fg-info: #173da6; + + /* Foreground Statuses */ + --bui-fg-danger-on-bg: #991919; + --bui-fg-warning-on-bg: #92310a; + --bui-fg-success-on-bg: #116932; + --bui-fg-info-on-bg: #173da6; + --bui-fg-danger: #ec3b18; + --bui-fg-warning: #ef7a32; + --bui-fg-success: #1ed760; + --bui-fg-info: #0d74ce; /* Border colors */ --bui-border: rgba(0, 0, 0, 0.1); @@ -210,17 +212,19 @@ /* Foreground colors */ --bui-fg-primary: var(--bui-white); --bui-fg-secondary: var(--bui-gray-7); - --bui-fg-link: #9cc9ff; - --bui-fg-link-hover: #7eb5f7; --bui-fg-disabled: var(--bui-gray-7); --bui-fg-solid: #101821; --bui-fg-solid-disabled: #6191cc; - --bui-fg-tint: #9cc9ff; - --bui-fg-tint-disabled: var(--bui-gray-5); - --bui-fg-danger: #fca5a5; - --bui-fg-warning: #fdba74; - --bui-fg-success: #86efac; - --bui-fg-info: #a3cfff; + + /* Foreground statuses */ + --bui-fg-danger-on-bg: #fca5a5; + --bui-fg-warning-on-bg: #fdba74; + --bui-fg-success-on-bg: #86efac; + --bui-fg-info-on-bg: #a3cfff; + --bui-fg-danger: #ff5a30; + --bui-fg-warning: #ffa057; + --bui-fg-success: #1ed760; + --bui-fg-info: #70b8ff; /* Border colors */ --bui-border: rgba(255, 255, 255, 0.12); diff --git a/packages/ui/src/types.ts b/packages/ui/src/types.ts index 0b23dfd10e..933e1d8aa9 100644 --- a/packages/ui/src/types.ts +++ b/packages/ui/src/types.ts @@ -131,7 +131,7 @@ export type TextVariants = export type TextColors = 'primary' | 'secondary'; /** @public */ -export type TextColorStatus = 'danger' | 'warning' | 'success'; +export type TextColorStatus = 'danger' | 'warning' | 'success' | 'info'; /** @public */ export type TextWeights = 'regular' | 'bold'; diff --git a/plugins/mui-to-bui/src/components/BuiThemerPage/convertMuiToBuiTheme.test.ts b/plugins/mui-to-bui/src/components/BuiThemerPage/convertMuiToBuiTheme.test.ts index 48bb318a71..a5f7cfd36f 100644 --- a/plugins/mui-to-bui/src/components/BuiThemerPage/convertMuiToBuiTheme.test.ts +++ b/plugins/mui-to-bui/src/components/BuiThemerPage/convertMuiToBuiTheme.test.ts @@ -151,6 +151,10 @@ describe('convertMuiToBuiTheme', () => { expect(result.css).toContain('--bui-bg-danger: #ffcdd2;'); expect(result.css).toContain('--bui-bg-warning: #ffe0b2;'); expect(result.css).toContain('--bui-bg-success: #c8e6c9;'); + // Info background may be hex or rgba depending on theme + expect(result.css).toMatch( + /--bui-bg-info:\s*(#[0-9a-f]{6}|rgba?\([^)]*\));/i, + ); }); it('should generate foreground colors correctly', () => { @@ -178,10 +182,8 @@ describe('convertMuiToBuiTheme', () => { const result = convertMuiToBuiTheme(theme); - expect(result.css).toContain('--bui-fg-link: #1976d2;'); - expect(result.css).toContain('--bui-fg-link-hover: #115293;'); expect(result.css).toContain('--bui-fg-disabled: #cccccc;'); - // Foreground danger/warning/success may be hex or rgb depending on theme + // Foreground status colors (standalone) expect(result.css).toMatch( /--bui-fg-danger:\s*(#[0-9a-f]{6}|rgb\([^)]*\));/i, ); @@ -191,6 +193,22 @@ describe('convertMuiToBuiTheme', () => { expect(result.css).toMatch( /--bui-fg-success:\s*(#[0-9a-f]{6}|rgb\([^)]*\));/i, ); + expect(result.css).toMatch( + /--bui-fg-info:\s*(#[0-9a-f]{6}|rgb\([^)]*\));/i, + ); + // Foreground status colors (on background) + expect(result.css).toMatch( + /--bui-fg-danger-on-bg:\s*(#[0-9a-f]{6}|rgb\([^)]*\));/i, + ); + expect(result.css).toMatch( + /--bui-fg-warning-on-bg:\s*(#[0-9a-f]{6}|rgb\([^)]*\));/i, + ); + expect(result.css).toMatch( + /--bui-fg-success-on-bg:\s*(#[0-9a-f]{6}|rgb\([^)]*\));/i, + ); + expect(result.css).toMatch( + /--bui-fg-info-on-bg:\s*(#[0-9a-f]{6}|rgb\([^)]*\));/i, + ); }); it('should generate border colors correctly', () => { @@ -215,6 +233,10 @@ describe('convertMuiToBuiTheme', () => { expect(result.css).toContain('--bui-border-danger: #f44336;'); expect(result.css).toContain('--bui-border-warning: #ff9800;'); expect(result.css).toContain('--bui-border-success: #4caf50;'); + // Info border may be hex or rgb depending on theme + expect(result.css).toMatch( + /--bui-border-info:\s*(#[0-9a-f]{6}|rgb\([^)]*\));/i, + ); }); it('should handle function-based spacing', () => { diff --git a/plugins/mui-to-bui/src/components/BuiThemerPage/convertMuiToBuiTheme.ts b/plugins/mui-to-bui/src/components/BuiThemerPage/convertMuiToBuiTheme.ts index 029d70cec7..dc72e3d597 100644 --- a/plugins/mui-to-bui/src/components/BuiThemerPage/convertMuiToBuiTheme.ts +++ b/plugins/mui-to-bui/src/components/BuiThemerPage/convertMuiToBuiTheme.ts @@ -94,16 +94,17 @@ function generateBuiVariables(theme: Mui5Theme): Record { Object.entries({ primary: palette.text.primary, secondary: palette.textSubtle, - link: palette.link ?? palette.primary.main, - 'link-hover': palette.linkHover ?? palette.primary.dark, disabled: palette.text.disabled, solid: palette.primary.contrastText, 'solid-disabled': palette.text.disabled, - tint: palette.textSubtle, - 'tint-disabled': palette.textVerySubtle, - danger: palette.error.dark, - warning: palette.warning.dark, - success: palette.success.dark, + danger: palette.error.main, + warning: palette.warning.main, + success: palette.success.main, + info: palette.info?.main ?? palette.primary.main, + 'danger-on-bg': palette.error.dark, + 'warning-on-bg': palette.warning.dark, + 'success-on-bg': palette.success.dark, + 'info-on-bg': palette.info?.dark ?? palette.primary.dark, }).forEach(([key, value]) => { styleObject[`--bui-fg-${key}`] = value; }); @@ -118,13 +119,10 @@ function generateBuiVariables(theme: Mui5Theme): Record { 'solid-hover': blend(palette.primary.main, palette.primary.dark, 0.5), 'solid-pressed': palette.primary.dark, 'solid-disabled': palette.action.disabledBackground, - tint: 'transparent', - 'tint-hover': alpha(palette.primary.main, 0.4), - 'tint-pressed': alpha(palette.primary.main, 0.6), - 'tint-disabled': palette.action.disabledBackground, danger: palette.error.light, warning: palette.warning.light, success: palette.success.light, + info: palette.info?.light ?? alpha(palette.primary.main, 0.1), }).forEach(([key, value]) => { styleObject[`--bui-bg-${key}`] = value; }); @@ -134,6 +132,7 @@ function generateBuiVariables(theme: Mui5Theme): Record { danger: palette.error.main, warning: palette.warning.main, success: palette.success.main, + info: palette.info?.main ?? palette.primary.main, }).forEach(([key, value]) => { styleObject[`--bui-border-${key}`] = value; }); diff --git a/yarn.lock b/yarn.lock index 9c6c9c034f..d5683239f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -27589,6 +27589,13 @@ __metadata: languageName: node linkType: hard +"cookie-signature@npm:1.0.7, cookie-signature@npm:~1.0.6": + version: 1.0.7 + resolution: "cookie-signature@npm:1.0.7" + checksum: 10/1a62808cd30d15fb43b70e19829b64d04b0802d8ef00275b57d152de4ae6a3208ca05c197b6668d104c4d9de389e53ccc2d3bc6bcaaffd9602461417d8c40710 + languageName: node + linkType: hard + "cookie-signature@npm:^1.2.1, cookie-signature@npm:^1.2.2": version: 1.2.2 resolution: "cookie-signature@npm:1.2.2" @@ -27596,14 +27603,7 @@ __metadata: languageName: node linkType: hard -"cookie-signature@npm:~1.0.6, cookie-signature@npm:~1.0.7": - version: 1.0.7 - resolution: "cookie-signature@npm:1.0.7" - checksum: 10/1a62808cd30d15fb43b70e19829b64d04b0802d8ef00275b57d152de4ae6a3208ca05c197b6668d104c4d9de389e53ccc2d3bc6bcaaffd9602461417d8c40710 - languageName: node - linkType: hard - -"cookie@npm:0.7.2, cookie@npm:^0.7.0, cookie@npm:^0.7.1, cookie@npm:^0.7.2, cookie@npm:~0.7.1, cookie@npm:~0.7.2": +"cookie@npm:0.7.2, cookie@npm:^0.7.0, cookie@npm:^0.7.1, cookie@npm:^0.7.2, cookie@npm:~0.7.1": version: 0.7.2 resolution: "cookie@npm:0.7.2" checksum: 10/24b286c556420d4ba4e9bc09120c9d3db7d28ace2bd0f8ccee82422ce42322f73c8312441271e5eefafbead725980e5996cc02766dbb89a90ac7f5636ede608f @@ -28487,7 +28487,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:2.6.9, debug@npm:^2.6.0, debug@npm:~2.6.9": +"debug@npm:2.6.9, debug@npm:^2.6.0": version: 2.6.9 resolution: "debug@npm:2.6.9" dependencies: @@ -31059,18 +31059,18 @@ __metadata: linkType: hard "express-session@npm:^1.17.1, express-session@npm:^1.17.3": - version: 1.19.0 - resolution: "express-session@npm:1.19.0" + version: 1.18.2 + resolution: "express-session@npm:1.18.2" dependencies: - cookie: "npm:~0.7.2" - cookie-signature: "npm:~1.0.7" - debug: "npm:~2.6.9" + cookie: "npm:0.7.2" + cookie-signature: "npm:1.0.7" + debug: "npm:2.6.9" depd: "npm:~2.0.0" on-headers: "npm:~1.1.0" parseurl: "npm:~1.3.3" - safe-buffer: "npm:~5.2.1" + safe-buffer: "npm:5.2.1" uid-safe: "npm:~2.1.5" - checksum: 10/5f36b51d344ec40adb5b9cd8a915fd694cdaf64832632411d4f6e6de8e9a83d873a20389e9c3cce966fece76a18c0384c5f2763b3ac089f801e8d6e92cb543e6 + checksum: 10/1a89a4d3e579e1d2a729dc604c254c3e942b24a279681371c2d8042b1080f9e6de4ca787a091ec1a9178c9f2cc5c19bcb9f3929632d44db8972263e26c2cc4b8 languageName: node linkType: hard @@ -45678,7 +45678,7 @@ __metadata: languageName: node linkType: hard -"safe-buffer@npm:5.2.1, safe-buffer@npm:>=5.1.0, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.1, safe-buffer@npm:^5.1.2, safe-buffer@npm:^5.2.1, safe-buffer@npm:~5.2.0, safe-buffer@npm:~5.2.1": +"safe-buffer@npm:5.2.1, safe-buffer@npm:>=5.1.0, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.1, safe-buffer@npm:^5.1.2, safe-buffer@npm:^5.2.1, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" checksum: 10/32872cd0ff68a3ddade7a7617b8f4c2ae8764d8b7d884c651b74457967a9e0e886267d3ecc781220629c44a865167b61c375d2da6c720c840ecd73f45d5d9451