From 5b85902edeb60f347f13641c84c4d42c63eab26b Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Mon, 4 May 2026 17:18:52 +0200 Subject: [PATCH 1/3] fix(ui): show focus indicator on Card when used as a link Card's focus-ring CSS keys off data-focus-visible on the bui-CardTrigger, but Link (the trigger when href is set) wasn't emitting it because useLink does not track focus-visible state. Compose useFocusRing in Link so keyboard focus is now properly indicated on Card href=... and any other Link styled via [data-focus-visible]. Co-Authored-By: Claude Opus 4.6 Signed-off-by: Emma Indal --- .changeset/link-focus-visible.md | 5 +++++ packages/ui/src/components/Link/Link.tsx | 6 ++++-- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 .changeset/link-focus-visible.md diff --git a/.changeset/link-focus-visible.md b/.changeset/link-focus-visible.md new file mode 100644 index 0000000000..4af0f661e4 --- /dev/null +++ b/.changeset/link-focus-visible.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Fix `Card href=...` not showing a focus indicator on keyboard navigation. `Link` now composes `useFocusRing` and emits `data-focus-visible`, so the Card's existing focus-ring CSS matches when the trigger is focused. diff --git a/packages/ui/src/components/Link/Link.tsx b/packages/ui/src/components/Link/Link.tsx index a66506deb0..47564ad24c 100644 --- a/packages/ui/src/components/Link/Link.tsx +++ b/packages/ui/src/components/Link/Link.tsx @@ -15,7 +15,7 @@ */ import { forwardRef, useRef } from 'react'; -import { useLink } from 'react-aria'; +import { mergeProps, useFocusRing, useLink } from 'react-aria'; import type { LinkProps } from './types'; import { useDefinition } from '../../hooks/useDefinition'; import { useResolvedHref } from '../../hooks/useResolvedHref'; @@ -33,6 +33,7 @@ const LinkInternal = forwardRef((props, ref) => { const linkRef = (ref || internalRef) as React.RefObject; const { linkProps } = useLink(restProps, linkRef); + const { isFocusVisible, focusProps } = useFocusRing(); const resolvedHref = useResolvedHref(restProps.href); const handleClick = (e: React.MouseEvent) => { @@ -48,13 +49,14 @@ const LinkInternal = forwardRef((props, ref) => { return ( )} href={resolvedHref} ref={linkRef} title={title} className={classes.root} + data-focus-visible={isFocusVisible || undefined} onClick={handleClick} > {children} From c41606a22d94ebdca4933c4af911479a023d6b6f Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Tue, 5 May 2026 08:25:39 +0200 Subject: [PATCH 2/3] Update .changeset/link-focus-visible.md Co-authored-by: Johan Persson Signed-off-by: Emma Indal --- .changeset/link-focus-visible.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.changeset/link-focus-visible.md b/.changeset/link-focus-visible.md index 4af0f661e4..e43b027f9d 100644 --- a/.changeset/link-focus-visible.md +++ b/.changeset/link-focus-visible.md @@ -3,3 +3,5 @@ --- Fix `Card href=...` not showing a focus indicator on keyboard navigation. `Link` now composes `useFocusRing` and emits `data-focus-visible`, so the Card's existing focus-ring CSS matches when the trigger is focused. + +*Affected components*: Card, Link From 9c16bf9707cb9bc6c33033268a57d55c7109ab48 Mon Sep 17 00:00:00 2001 From: Emma Indal Date: Tue, 5 May 2026 09:48:53 +0200 Subject: [PATCH 3/3] fix(ui): render focus ring on Link via data-focus-visible Style the bui-Link itself when keyboard-focused using the ring token, matching the established pattern from ButtonLink. Also tweak the changeset wording to reflect the new behavior. Co-Authored-By: Claude Opus 4.6 Signed-off-by: Emma Indal --- .changeset/link-focus-visible.md | 4 ++-- packages/ui/src/components/Link/Link.module.css | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.changeset/link-focus-visible.md b/.changeset/link-focus-visible.md index e43b027f9d..34c7f3e496 100644 --- a/.changeset/link-focus-visible.md +++ b/.changeset/link-focus-visible.md @@ -2,6 +2,6 @@ '@backstage/ui': patch --- -Fix `Card href=...` not showing a focus indicator on keyboard navigation. `Link` now composes `useFocusRing` and emits `data-focus-visible`, so the Card's existing focus-ring CSS matches when the trigger is focused. +Fix `Card href=...` not showing a focus indicator on keyboard navigation. `Link` now composes `useFocusRing`, emits `data-focus-visible`, and renders a `--bui-ring` outline when keyboard-focused. The Card's existing focus-ring CSS matches when the trigger is focused. -*Affected components*: Card, Link +_Affected components_: Card, Link diff --git a/packages/ui/src/components/Link/Link.module.css b/packages/ui/src/components/Link/Link.module.css index 27ffe244fb..d5cd176c96 100644 --- a/packages/ui/src/components/Link/Link.module.css +++ b/packages/ui/src/components/Link/Link.module.css @@ -37,6 +37,11 @@ text-underline-offset: calc(0.025em + 2px); text-decoration-color: color-mix(in srgb, currentColor 30%, transparent); } + + &[data-focus-visible] { + outline: 2px solid var(--bui-ring); + outline-offset: 2px; + } } .bui-Link[data-variant='title-large'] {