diff --git a/design-system/apps/design-lab/src/i18n/messages.ts b/design-system/apps/design-lab/src/i18n/messages.ts
index d2a322d87b..8234115c20 100644
--- a/design-system/apps/design-lab/src/i18n/messages.ts
+++ b/design-system/apps/design-lab/src/i18n/messages.ts
@@ -410,6 +410,8 @@ export const enUSMessages = {
"detail.livePlayground": "Live playground",
"detail.allStates": "Preview states",
"detail.preview": "Preview",
+ "detail.iconComposition": "Icons in components",
+ "detail.iconCompositionHint": "Catalog icons (left) and SVG icons (right) share slot sizes. Standalone icons below retain their own size.",
"detail.previewUnavailable": "Preview not implemented",
"components.preview.inputError": "Please check this value.",
"detail.code": "Code",
@@ -1105,6 +1107,8 @@ export const zhCNMessages = {
"detail.livePlayground": "实时工作台",
"detail.allStates": "状态预览",
"detail.preview": "预览",
+ "detail.iconComposition": "组件中的图标尺寸",
+ "detail.iconCompositionHint": "左侧为图标库图标,右侧为 SVG 图标,均使用组件插槽尺寸;下方独立图标保留自身尺寸。",
"detail.previewUnavailable": "此组件尚未实现预览",
"components.preview.inputError": "请检查输入内容。",
"detail.code": "代码",
@@ -1745,6 +1749,8 @@ export const zhTWMessages = {
"detail.livePlayground": "即時工作台",
"detail.allStates": "狀態預覽",
"detail.preview": "預覽",
+ "detail.iconComposition": "元件中的圖示尺寸",
+ "detail.iconCompositionHint": "左側為圖示庫圖示,右側為 SVG 圖示,皆使用元件插槽尺寸;下方獨立圖示保留自身尺寸。",
"detail.previewUnavailable": "此元件尚未實作預覽",
"components.preview.inputError": "請檢查輸入內容。",
"detail.code": "程式碼",
diff --git a/design-system/apps/design-lab/src/pages/ComponentDetailPage.tsx b/design-system/apps/design-lab/src/pages/ComponentDetailPage.tsx
index 584d01a3c4..e5f8935ac5 100644
--- a/design-system/apps/design-lab/src/pages/ComponentDetailPage.tsx
+++ b/design-system/apps/design-lab/src/pages/ComponentDetailPage.tsx
@@ -98,6 +98,7 @@ import {
} from "@bitfun/ui";
import type { ComponentMeta } from "@bitfun/ui/registry";
import previewImage from "../assets/design-system-hero.webp";
+import { IconCompositionPreview } from "../preview/IconCompositionPreview";
import { NestedMenuPattern } from "./ReferencePatterns";
import { useI18n, type MessageKey } from "../i18n";
import {
@@ -1749,14 +1750,17 @@ export function ComponentDetailPage({
))}
) : component.name === "Icon" ? (
-
+
{t("detail.iconComposition")}
+
{t("detail.iconCompositionHint")}
+ {(["xs", "sm", "md", "lg"] as const).map(size => (
+
+ Button / {size}
+ } trailingIcon={}>{label}
+ } trailingIcon={}>{label}
+ } />
+ } />
+
+ ))}
+
+ TabGroup
+ },
+ { value: "settings", label, icon: },
+ { value: "assistant", label: t("components.preview.assistant"), icon: },
+ ]}
+ />
+
+
+ Input
+ } trailing={} defaultValue={label} />
+ } trailing={} defaultValue={label} />
+
+
+ );
+}
diff --git a/design-system/apps/design-lab/src/styles.css b/design-system/apps/design-lab/src/styles.css
index 2472ffdd89..a0ab0df073 100644
--- a/design-system/apps/design-lab/src/styles.css
+++ b/design-system/apps/design-lab/src/styles.css
@@ -3948,6 +3948,38 @@ input.lab-force-focus {
padding: var(--bf-space-5);
}
+.component-icon-composition {
+ display: grid;
+ gap: var(--bf-space-4);
+ min-inline-size: 0;
+ padding: var(--bf-space-5);
+ border-block-end: var(--bf-border-width-default) solid var(--bf-color-border-subtle);
+}
+
+.component-icon-composition h3,
+.component-icon-composition p {
+ margin: 0;
+}
+
+.component-icon-composition p {
+ color: var(--bf-color-content-muted);
+ font-size: var(--bf-font-size-small);
+}
+
+.component-icon-composition__row {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: var(--bf-space-4);
+ min-inline-size: 0;
+}
+
+.component-icon-composition__row > code {
+ min-inline-size: 112px;
+ color: var(--bf-color-content-secondary);
+ font-size: var(--bf-font-size-caption);
+}
+
.component-icon-catalog__item {
display: grid;
min-block-size: 88px;
diff --git a/design-system/apps/design-lab/vite/component-preview-render.test.mjs b/design-system/apps/design-lab/vite/component-preview-render.test.mjs
index 527aef5c61..cc34ebaf41 100644
--- a/design-system/apps/design-lab/vite/component-preview-render.test.mjs
+++ b/design-system/apps/design-lab/vite/component-preview-render.test.mjs
@@ -83,3 +83,16 @@ test("Switch is explicit and unknown components never silently become switches",
assert.match(preview, /此组件尚未实现预览/);
assert.doesNotMatch(preview, /data-bf-component="switch"/);
});
+
+test("Icon details include real mixed-icon compositions at every button size", () => {
+ const { preview } = render("Icon");
+ assert.match(preview, /component-icon-composition/);
+ for (const size of ["xs", "sm", "md", "lg"]) {
+ assert.match(preview, new RegExp(`Button / ${size}`));
+ assert.match(preview, new RegExp(`aria-label="SVG / ${size}"`));
+ assert.match(preview, new RegExp(`aria-label="Icon / ${size}"`));
+ }
+ assert.match(preview, /data-bf-component="tab-group"/);
+ assert.match(preview, /data-bf-component="input"/);
+ assert.match(preview, /component-icon-catalog/);
+});
diff --git a/design-system/packages/ui/README.md b/design-system/packages/ui/README.md
index 465ead7939..5ac5cafa34 100644
--- a/design-system/packages/ui/README.md
+++ b/design-system/packages/ui/README.md
@@ -23,6 +23,12 @@ uncontrolled open state, trigger/region accessibility wiring, focus exclusion
while collapsed, reduced-motion behavior, and independent header actions.
Product copy and the revealed content remain consumer-owned.
+Sized icon slots in buttons, tabs, menu items and fields own their glyph geometry.
+Pass catalog `Icon` nodes through `leadingIcon`, `trailingIcon`, `icon` or the
+matching component slot, just as for SVG icons. These slots constrain catalog
+icons to the component's size; a standalone `Icon` retains its explicit size
+(24px by default). Do not shrink the catalog globally to correct a slot mismatch.
+
## Advanced selection and menus
Use native `Select` for simple options. `Combobox` adds search, grouped options,
diff --git a/design-system/packages/ui/src/components/ActionCard/ActionCard.module.css b/design-system/packages/ui/src/components/ActionCard/ActionCard.module.css
index 730bd2d355..43713e6a71 100644
--- a/design-system/packages/ui/src/components/ActionCard/ActionCard.module.css
+++ b/design-system/packages/ui/src/components/ActionCard/ActionCard.module.css
@@ -92,7 +92,8 @@
color: var(--bf-color-content-secondary);
}
- .leading > :where(svg, img) {
+ .leading > :where(svg, img),
+ .leading > [data-bf-component="icon"] {
display: block;
inline-size: var(--bf-control-action-card-icon-size);
block-size: var(--bf-control-action-card-icon-size);
diff --git a/design-system/packages/ui/src/components/ActionItem/ActionItem.module.css b/design-system/packages/ui/src/components/ActionItem/ActionItem.module.css
index 334adba05d..8a7410e344 100644
--- a/design-system/packages/ui/src/components/ActionItem/ActionItem.module.css
+++ b/design-system/packages/ui/src/components/ActionItem/ActionItem.module.css
@@ -83,7 +83,8 @@
block-size: var(--bf-font-size-small);
}
- .leading > :where(svg, img) {
+ .leading > :where(svg, img),
+ .leading > [data-bf-component="icon"] {
display: block;
inline-size: 100%;
block-size: 100%;
diff --git a/design-system/packages/ui/src/components/ActivityItem/ActivityItem.module.css b/design-system/packages/ui/src/components/ActivityItem/ActivityItem.module.css
index 13da75ef7b..d5760b8a64 100644
--- a/design-system/packages/ui/src/components/ActivityItem/ActivityItem.module.css
+++ b/design-system/packages/ui/src/components/ActivityItem/ActivityItem.module.css
@@ -68,7 +68,8 @@
justify-content: center;
}
- .leading > :where(svg, img) {
+ .leading > :where(svg, img),
+ .leading > [data-bf-component="icon"] {
display: block;
inline-size: 100%;
block-size: 100%;
diff --git a/design-system/packages/ui/src/components/Avatar/Avatar.module.css b/design-system/packages/ui/src/components/Avatar/Avatar.module.css
index e05153751c..6f4256d49f 100644
--- a/design-system/packages/ui/src/components/Avatar/Avatar.module.css
+++ b/design-system/packages/ui/src/components/Avatar/Avatar.module.css
@@ -5,7 +5,7 @@
.root[data-bf-shape="square"] { border-radius: var(--bf-radius-base); }
.image { inline-size: 100%; block-size: 100%; object-fit: cover; }
.content { display: inline-flex; align-items: center; justify-content: center; inline-size: 100%; block-size: 100%; }
- .content > :where(svg, img) { inline-size: 55%; block-size: 55%; }
+ .content > :where(svg, img), .content > [data-bf-component="icon"] { inline-size: 55%; block-size: 55%; }
.group { display: inline-flex; align-items: center; }
.group > .root + .root { margin-inline-start: calc(var(--bf-space-2) * -1); }
}
diff --git a/design-system/packages/ui/src/components/Button/Button.module.css b/design-system/packages/ui/src/components/Button/Button.module.css
index 96d7b55448..6217833ab9 100644
--- a/design-system/packages/ui/src/components/Button/Button.module.css
+++ b/design-system/packages/ui/src/components/Button/Button.module.css
@@ -191,7 +191,8 @@
color: currentColor;
}
- .icon > :where(svg, img) {
+ .icon > :where(svg, img),
+ .icon > [data-bf-component="icon"] {
display: block;
inline-size: 100%;
block-size: 100%;
diff --git a/design-system/packages/ui/src/components/Combobox/Combobox.module.css b/design-system/packages/ui/src/components/Combobox/Combobox.module.css
index 80bb7cce2d..d9cb9d98cf 100644
--- a/design-system/packages/ui/src/components/Combobox/Combobox.module.css
+++ b/design-system/packages/ui/src/components/Combobox/Combobox.module.css
@@ -23,7 +23,7 @@
.option { display: flex; align-items: center; gap: var(--bf-space-2); min-block-size: var(--bf-overlay-menu-item-height); padding: var(--bf-space-2) var(--bf-overlay-menu-item-padding-inline); border-radius: var(--bf-overlay-menu-item-radius); cursor: pointer; overflow-wrap: anywhere; }
.option[data-active="true"], .option[data-selected="true"] { background: var(--bf-color-action-neutral-surface); }
.option[aria-disabled="true"] { color: var(--bf-color-content-disabled); cursor: not-allowed; }
- .option > svg { flex-shrink: 0; inline-size: var(--bf-overlay-menu-item-icon-size); block-size: var(--bf-overlay-menu-item-icon-size); }
+ .option > svg, .option > [data-bf-component="icon"] { flex-shrink: 0; inline-size: var(--bf-overlay-menu-item-icon-size); block-size: var(--bf-overlay-menu-item-icon-size); }
.copy { display: flex; flex-direction: column; flex: 1; min-inline-size: 0; }
.copy small { color: var(--bf-color-content-muted); }
.group, .empty { padding: var(--bf-space-2); }
diff --git a/design-system/packages/ui/src/components/ConfirmDialog/ConfirmDialog.module.css b/design-system/packages/ui/src/components/ConfirmDialog/ConfirmDialog.module.css
index 8e6c921751..47cae3f4e9 100644
--- a/design-system/packages/ui/src/components/ConfirmDialog/ConfirmDialog.module.css
+++ b/design-system/packages/ui/src/components/ConfirmDialog/ConfirmDialog.module.css
@@ -33,7 +33,8 @@
background: var(--bf-color-status-info-surface);
}
- .icon > svg {
+ .icon > svg,
+ .icon > [data-bf-component="icon"] {
inline-size: var(--bf-layout-confirm-dialog-icon-glyph-size);
block-size: var(--bf-layout-confirm-dialog-icon-glyph-size);
}
diff --git a/design-system/packages/ui/src/components/Empty/Empty.module.css b/design-system/packages/ui/src/components/Empty/Empty.module.css
index 7cb1e6de83..610bef1a15 100644
--- a/design-system/packages/ui/src/components/Empty/Empty.module.css
+++ b/design-system/packages/ui/src/components/Empty/Empty.module.css
@@ -30,7 +30,8 @@
block-size: calc(var(--bf-control-height-lg) + var(--bf-space-4));
}
- .media > :where(svg, img) {
+ .media > :where(svg, img),
+ .media > [data-bf-component="icon"] {
display: block;
max-inline-size: 100%;
max-block-size: 100%;
diff --git a/design-system/packages/ui/src/components/IconButton/IconButton.module.css b/design-system/packages/ui/src/components/IconButton/IconButton.module.css
index 7c060b49d3..f505e0abde 100644
--- a/design-system/packages/ui/src/components/IconButton/IconButton.module.css
+++ b/design-system/packages/ui/src/components/IconButton/IconButton.module.css
@@ -108,7 +108,8 @@
block-size: var(--_icon-button-icon-size);
}
- .icon > :where(svg, img) {
+ .icon > :where(svg, img),
+ .icon > [data-bf-component="icon"] {
display: block;
inline-size: 100%;
block-size: 100%;
diff --git a/design-system/packages/ui/src/components/Input/Input.module.css b/design-system/packages/ui/src/components/Input/Input.module.css
index f203dd4aab..56e780077f 100644
--- a/design-system/packages/ui/src/components/Input/Input.module.css
+++ b/design-system/packages/ui/src/components/Input/Input.module.css
@@ -92,7 +92,9 @@
}
.leading > :where(svg, img),
- .trailing > :where(svg, img) {
+ .trailing > :where(svg, img),
+ .leading > [data-bf-component="icon"],
+ .trailing > [data-bf-component="icon"] {
display: block;
inline-size: var(--_field-icon-size);
block-size: var(--_field-icon-size);
diff --git a/design-system/packages/ui/src/components/KeyHint/KeyHint.module.css b/design-system/packages/ui/src/components/KeyHint/KeyHint.module.css
index 940de0f869..194941a6a1 100644
--- a/design-system/packages/ui/src/components/KeyHint/KeyHint.module.css
+++ b/design-system/packages/ui/src/components/KeyHint/KeyHint.module.css
@@ -32,7 +32,8 @@
min-inline-size: 0;
}
- .icon > :where(svg, img) {
+ .icon > :where(svg, img),
+ .icon > [data-bf-component="icon"] {
display: block;
inline-size: 1em;
block-size: 1em;
diff --git a/design-system/packages/ui/src/components/NavigationPanel/NavigationPanel.meta.ts b/design-system/packages/ui/src/components/NavigationPanel/NavigationPanel.meta.ts
index 26f3058b3f..1ea0681a41 100644
--- a/design-system/packages/ui/src/components/NavigationPanel/NavigationPanel.meta.ts
+++ b/design-system/packages/ui/src/components/NavigationPanel/NavigationPanel.meta.ts
@@ -18,6 +18,7 @@ export const navigationPanelMeta = {
tokens: [
"color.surface.subtle",
"color.border.subtle",
+ "color.content.primary",
"color.content.muted",
"color.action.neutral.content",
"color.action.neutral.contentDisabled",
diff --git a/design-system/packages/ui/src/components/NavigationPanel/NavigationPanel.module.css b/design-system/packages/ui/src/components/NavigationPanel/NavigationPanel.module.css
index ebca8565cb..3b0ee9b612 100644
--- a/design-system/packages/ui/src/components/NavigationPanel/NavigationPanel.module.css
+++ b/design-system/packages/ui/src/components/NavigationPanel/NavigationPanel.module.css
@@ -19,7 +19,7 @@
block-size: 100%;
min-block-size: 0;
flex-direction: column;
- color: var(--bf-color-action-neutral-content);
+ color: var(--bf-color-content-primary);
background: var(--bf-color-surface-subtle);
}
@@ -81,7 +81,8 @@
overflow: hidden;
flex: 1 1 auto;
min-inline-size: 0;
- color: var(--bf-color-content-muted);
+ /* Group captions carry half the ink of destinations, not the action label tone. */
+ color: color-mix(in srgb, var(--bf-color-content-primary) 50%, transparent);
font-family: var(--bf-font-family-control);
font-size: var(--bf-layout-navigation-panel-heading-font-size);
font-weight: var(--bf-font-weight-medium);
@@ -109,6 +110,10 @@
border-radius: var(--bf-layout-navigation-panel-item-radius);
}
+ .item[data-bf-tone="neutral"]:not([data-disabled="true"]) {
+ color: var(--bf-color-content-primary);
+ }
+
.item > [data-bf-part="trigger"] {
gap: var(--bf-layout-navigation-panel-item-gap);
}
@@ -122,7 +127,7 @@
background: var(--bf-color-action-neutral-surface-pressed);
}
- .item:has(> [data-bf-part="trigger"][aria-current]) [data-bf-part="label"] {
+ .item > [data-bf-part="trigger"][aria-current] > [data-bf-part="label"] {
font-weight: var(--bf-font-weight-semibold);
}
@@ -145,7 +150,22 @@
border-block-start: var(--bf-border-width-default) solid var(--bf-color-border-subtle);
}
+ :global([data-contrast="high"]) .headingLabel {
+ color: var(--bf-color-content-muted);
+ }
+
+ @media (prefers-contrast: more) {
+ .headingLabel {
+ color: var(--bf-color-content-muted);
+ }
+ }
+
@media (forced-colors: active) {
+ .headingLabel,
+ .item[data-bf-tone="neutral"]:not([data-disabled="true"]) {
+ color: CanvasText;
+ }
+
.item:has(> [data-bf-part="trigger"][aria-current]) {
color: HighlightText;
background: Highlight;
diff --git a/design-system/packages/ui/src/components/SegmentedControl/SegmentedControl.module.css b/design-system/packages/ui/src/components/SegmentedControl/SegmentedControl.module.css
index 290a72fe76..0db6e35417 100644
--- a/design-system/packages/ui/src/components/SegmentedControl/SegmentedControl.module.css
+++ b/design-system/packages/ui/src/components/SegmentedControl/SegmentedControl.module.css
@@ -76,7 +76,8 @@
color: currentColor;
}
- .icon > :where(svg, img) {
+ .icon > :where(svg, img),
+ .icon > [data-bf-component="icon"] {
display: block;
inline-size: 100%;
block-size: 100%;
diff --git a/design-system/packages/ui/src/components/Select/Select.module.css b/design-system/packages/ui/src/components/Select/Select.module.css
index a70375475b..86e37adcdd 100644
--- a/design-system/packages/ui/src/components/Select/Select.module.css
+++ b/design-system/packages/ui/src/components/Select/Select.module.css
@@ -116,7 +116,9 @@
margin-inline-end: var(--bf-control-select-trailing-inset);
}
- .leading > :where(svg, img, [data-bf-component="icon"]) {
+ .leading > :where(svg, img),
+ .leading > [data-bf-component="icon"],
+ .indicator > [data-bf-component="icon"] {
display: block;
inline-size: 100%;
block-size: 100%;
diff --git a/design-system/packages/ui/src/components/StatusPill/StatusPill.module.css b/design-system/packages/ui/src/components/StatusPill/StatusPill.module.css
index 72632e4982..9750d36e39 100644
--- a/design-system/packages/ui/src/components/StatusPill/StatusPill.module.css
+++ b/design-system/packages/ui/src/components/StatusPill/StatusPill.module.css
@@ -31,7 +31,8 @@
block-size: var(--bf-control-status-pill-icon-size);
}
- .leading > :where(svg, img, [data-bf-component="icon"]) {
+ .leading > :where(svg, img),
+ .leading > [data-bf-component="icon"] {
display: block;
inline-size: 100%;
block-size: 100%;
diff --git a/design-system/packages/ui/src/components/TabGroup/TabGroup.module.css b/design-system/packages/ui/src/components/TabGroup/TabGroup.module.css
index 19538b9693..cfaf44725f 100644
--- a/design-system/packages/ui/src/components/TabGroup/TabGroup.module.css
+++ b/design-system/packages/ui/src/components/TabGroup/TabGroup.module.css
@@ -93,7 +93,8 @@
color: currentColor;
}
- .icon > :where(svg, img) {
+ .icon > :where(svg, img),
+ .icon > [data-bf-component="icon"] {
display: block;
inline-size: 100%;
block-size: 100%;
diff --git a/design-system/packages/ui/tests/icon-slots.test.mjs b/design-system/packages/ui/tests/icon-slots.test.mjs
new file mode 100644
index 0000000000..95098aa4d7
--- /dev/null
+++ b/design-system/packages/ui/tests/icon-slots.test.mjs
@@ -0,0 +1,78 @@
+import assert from "node:assert/strict";
+import { readFile } from "node:fs/promises";
+import test from "node:test";
+import { createElement } from "react";
+import { renderToStaticMarkup } from "react-dom/server";
+import { Button, Icon, IconButton, SessionIcon, TabGroup } from "../dist/index.js";
+
+const slots = [
+ ["Button", "icon", "inline-size", "100%"],
+ ["IconButton", "icon", "inline-size", "100%"],
+ ["TabGroup", "icon", "inline-size", "100%"],
+ ["ActionCard", "leading", "inline-size", "var(--bf-control-action-card-icon-size)"],
+ ["ActionItem", "leading", "inline-size", "100%"],
+ ["ActivityItem", "leading", "inline-size", "100%"],
+ ["SegmentedControl", "icon", "inline-size", "100%"],
+ ["KeyHint", "icon", "inline-size", "1em"],
+ ["Input", "leading", "inline-size", "var(--_field-icon-size)"],
+ ["Input", "trailing", "inline-size", "var(--_field-icon-size)"],
+ ["Select", "leading", "inline-size", "100%"],
+ ["Select", "indicator", "inline-size", "100%"],
+ ["StatusPill", "leading", "inline-size", "100%"],
+ ["Avatar", "content", "inline-size", "55%"],
+ ["Empty", "media", "max-inline-size", "100%"],
+ ["ConfirmDialog", "icon", "inline-size", "var(--bf-layout-confirm-dialog-icon-glyph-size)"],
+ ["Combobox", "option", "inline-size", "var(--bf-overlay-menu-item-icon-size)"],
+];
+
+test("sized slots apply the same geometry to SVG and catalog icons, regardless of stylesheet order", async () => {
+ for (const [component, slot, property, value] of slots) {
+ const css = await readFile(new URL(`../src/components/${component}/${component}.module.css`, import.meta.url), "utf8");
+ // Class + attribute specificity beats Icon's class-only dimensions, even if
+ // the Icon stylesheet loads later during source HMR or a production build.
+ const selector = `.${slot} > [data-bf-component="icon"]`;
+ const rule = [...css.matchAll(/([^{}]+)\{([^{}]+)\}/g)].find(([, selectors]) => selectors.includes(selector));
+ assert.ok(rule, `${component}.${slot} must directly size catalog icons`);
+ assert.match(rule[1], /svg/, `${component}.${slot} must retain SVG support`);
+ assert.ok(rule[2].includes(`${property}: ${value}`), `${component}.${slot}: ${property}`);
+ assert.ok(rule[2].includes(`${property.replace("inline", "block")}: ${value}`), `${component}.${slot}: block size`);
+ }
+});
+
+test("buttons route native and default-size catalog icons through identical slots at every size", () => {
+ for (const size of ["xs", "sm", "md", "lg"]) {
+ const catalog = createElement(Icon, { name: "settings" });
+ const svg = createElement("svg", { width: 24, height: 24 });
+ for (const [Component, props] of [
+ [Button, { children: "Settings", leadingIcon: catalog, trailingIcon: svg }],
+ [IconButton, { "aria-label": "Settings", icon: catalog }],
+ ]) {
+ const markup = renderToStaticMarkup(createElement(Component, { ...props, size }));
+ assert.match(markup, new RegExp(`data-size="${size}"`));
+ assert.match(markup, /
]*class="[^"]*_icon_[^"]*"[^>]*>]*data-bf-component="icon"/);
+ assert.match(markup, /data-bf-name="settings"[^>]*data-size="lg"/);
+ }
+ }
+});
+
+test("tabs keep native session icons and catalog scene icons in the same sized region", () => {
+ const markup = renderToStaticMarkup(createElement(TabGroup, {
+ "aria-label": "Scenes",
+ defaultValue: "session",
+ items: [
+ { value: "session", label: "Session", icon: createElement(SessionIcon) },
+ { value: "settings", label: "Settings", icon: createElement(Icon, { name: "settings" }) },
+ { value: "assistant", label: "Assistant", icon: createElement(Icon, { name: "user" }) },
+ ],
+ }));
+ assert.equal((markup.match(/data-bf-part="icon"/g) ?? []).length, 3);
+ assert.match(markup, /data-bf-part="icon"[^>]*>