Improve Link structure in BUI

Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
This commit is contained in:
Charles de Dreuille
2025-10-25 08:13:01 +01:00
parent 5c614fff85
commit 1059f95fa1
5 changed files with 105 additions and 40 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/ui': patch
---
Improved the Link component structure in Backstage UI.
+8
View File
@@ -553,6 +553,14 @@ export const componentDefinitions: {
readonly dataAttributes: {
readonly variant: readonly ['subtitle', 'body', 'caption', 'label'];
readonly weight: readonly ['regular', 'bold'];
readonly color: readonly [
'primary',
'secondary',
'danger',
'warning',
'success',
];
readonly truncate: readonly [true, false];
};
};
readonly List: {
@@ -33,4 +33,78 @@
text-decoration-color: color-mix(in srgb, currentColor 30%, transparent);
}
}
.bui-Link[data-variant='title-large'] {
font-size: var(--bui-font-size-8);
line-height: 140%;
}
.bui-Link[data-variant='title-medium'] {
font-size: var(--bui-font-size-7);
line-height: 140%;
}
.bui-Link[data-variant='title-small'] {
font-size: var(--bui-font-size-6);
line-height: 140%;
}
.bui-Link[data-variant='title-x-small'] {
font-size: var(--bui-font-size-5);
line-height: 140%;
}
.bui-Link[data-variant='body-large'] {
font-size: var(--bui-font-size-4);
line-height: 140%;
}
.bui-Link[data-variant='body-medium'] {
font-size: var(--bui-font-size-3);
line-height: 140%;
}
.bui-Link[data-variant='body-small'] {
font-size: var(--bui-font-size-2);
line-height: 140%;
}
.bui-Link[data-variant='body-x-small'] {
font-size: var(--bui-font-size-1);
line-height: 140%;
}
.bui-Link[data-weight='regular'] {
font-weight: var(--bui-font-weight-regular);
}
.bui-Link[data-weight='bold'] {
font-weight: var(--bui-font-weight-bold);
}
.bui-Link[data-color='primary'] {
color: var(--bui-fg-primary);
}
.bui-Link[data-color='secondary'] {
color: var(--bui-fg-secondary);
}
.bui-Link[data-color='danger'] {
color: var(--bui-fg-danger);
}
.bui-Link[data-color='warning'] {
color: var(--bui-fg-warning);
}
.bui-Link[data-color='success'] {
color: var(--bui-fg-success);
}
.bui-Link[data-truncate] {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
+16 -40
View File
@@ -21,65 +21,41 @@ import { useStyles } from '../../hooks/useStyles';
import type { LinkProps } from './types';
import { useNavigate, useHref } from 'react-router-dom';
import { isExternalLink } from '../../utils/isExternalLink';
import stylesLink from './Link.module.css';
import stylesText from '../Text/Text.module.css';
import styles from './Link.module.css';
/** @public */
export const Link = forwardRef<HTMLAnchorElement, LinkProps>((props, ref) => {
const navigate = useNavigate();
const { classNames: classNamesLink } = useStyles('Link', props);
const {
classNames: classNamesText,
dataAttributes: textDataAttributes,
cleanedProps,
} = useStyles('Text', {
const { classNames, dataAttributes, cleanedProps } = useStyles('Link', {
variant: 'body',
weight: 'regular',
color: 'primary',
...props,
});
const { className, variant, weight, color, truncate, href, ...restProps } =
cleanedProps;
const { className, href, ...restProps } = cleanedProps;
const isExternal = isExternalLink(href);
const component = (
<AriaLink
ref={ref}
className={clsx(classNames.root, styles[classNames.root], className)}
href={href}
{...dataAttributes}
{...restProps}
/>
);
// If it's an external link, render AriaLink without RouterProvider
if (isExternal) {
return (
<AriaLink
ref={ref}
className={clsx(
classNamesText.root,
classNamesLink.root,
stylesText[classNamesText.root],
stylesLink[classNamesLink.root],
className,
)}
data-truncate={truncate}
href={href}
{...textDataAttributes}
{...restProps}
/>
);
return component;
}
// For internal links, use RouterProvider
return (
<RouterProvider navigate={navigate} useHref={useHref}>
<AriaLink
ref={ref}
className={clsx(
classNamesText.root,
classNamesLink.root,
stylesText[classNamesText.root],
stylesLink[classNamesLink.root],
className,
)}
data-truncate={truncate}
{...textDataAttributes}
href={href}
{...restProps}
/>
{component}
</RouterProvider>
);
});
@@ -228,6 +228,8 @@ export const componentDefinitions = {
dataAttributes: {
variant: ['subtitle', 'body', 'caption', 'label'] as const,
weight: ['regular', 'bold'] as const,
color: ['primary', 'secondary', 'danger', 'warning', 'success'] as const,
truncate: [true, false] as const,
},
},
List: {