From c4dfa6f3e4e507e4210ed04016c567bb3443fa11 Mon Sep 17 00:00:00 2001
From: Linus Rath <139418639+rathlinus@users.noreply.github.com>
Date: Thu, 29 Oct 2026 17:53:45 +0100
Subject: [PATCH] fix: draw the focus outline on plugin kit buttons, fields and
switches
---
app/(sandbox)/plugin-kit.css | 18 ++++++++++++++++++
lib/__tests__/plugin-ui-kit.test.tsx | 9 +++++++++
lib/plugin-sandbox/ui-kit/components.tsx | 24 ++++++++++++------------
3 files changed, 39 insertions(+), 12 deletions(-)
diff --git a/app/(sandbox)/plugin-kit.css b/app/(sandbox)/plugin-kit.css
index ff06f66b4..0459e062a 100644
--- a/app/(sandbox)/plugin-kit.css
+++ b/app/(sandbox)/plugin-kit.css
@@ -74,3 +74,21 @@
color-scheme: dark;
}
}
+
+/* The app's focus outline (app/accessibility.css) for the kit's own
+ elements: a real outline instead of a box-shadow ring, so it also shows in
+ forced colours. Kit components carry no focus ring of their own. Only
+ containers focused from script (tabindex="-1" without a control role) skip
+ it. */
+:is(.bk, .bk *):focus-visible:not([tabindex="-1"]:not(a[href], button, input, select, textarea, [role="button"], [role="menuitem"], [role="option"], [role="tab"], [role="row"], [role="gridcell"], [role="checkbox"], [role="switch"], [role="radio"])) {
+ outline: 2px solid var(--color-ring) !important;
+ outline-offset: 2px !important;
+}
+:is(.bk [role="option"], .bk [role="row"], .bk [role="tab"], .bk .focus-inset, .bk.focus-inset):focus-visible {
+ outline-offset: -2px !important;
+}
+@media (forced-colors: active) {
+ :is(.bk, .bk *):focus-visible {
+ outline-color: Highlight !important;
+ }
+}
diff --git a/lib/__tests__/plugin-ui-kit.test.tsx b/lib/__tests__/plugin-ui-kit.test.tsx
index 320878301..aa4b57a02 100644
--- a/lib/__tests__/plugin-ui-kit.test.tsx
+++ b/lib/__tests__/plugin-ui-kit.test.tsx
@@ -442,4 +442,13 @@ describe('plugin-kit.css', () => {
expect(reset).toMatch(/button\.bk/);
expect(reset).toMatch(/\.bk button/);
}, 60_000);
+
+ it('draws the focus outline on kit controls, since the kit has no focus rings', () => {
+ const css = readFileSync(path.join(ROOT, 'app/(sandbox)/plugin-kit.css'), 'utf8');
+ const rule = css.split('}').find((r) => r.includes(':focus-visible') && /outline:\s*2px solid var\(--color-ring\)/.test(r));
+ expect(rule).toBeDefined();
+ expect(rule).toMatch(/\.bk \*/);
+ expect(source('lib/plugin-sandbox/ui-kit/components.tsx')).not.toMatch(/focus(-visible)?:ring/);
+ expect(source('components/ui/button.tsx')).not.toMatch(/focus(-visible)?:ring/);
+ });
});
diff --git a/lib/plugin-sandbox/ui-kit/components.tsx b/lib/plugin-sandbox/ui-kit/components.tsx
index 18b4cbf5a..c6b76415b 100644
--- a/lib/plugin-sandbox/ui-kit/components.tsx
+++ b/lib/plugin-sandbox/ui-kit/components.tsx
@@ -134,7 +134,7 @@ export const Textarea = React.forwardRef
);
@@ -153,7 +153,7 @@ export function Select({ options, children, onChange, disabled, block = false, c
return (