Skip to content

Commit 535cd97

Browse files
authored
Merge pull request #2902 from kev1n77/fmy/ui-main
fix(ui): align icon buttons and icon opacity with UIKit
2 parents 14e4d95 + b5014e7 commit 535cd97

89 files changed

Lines changed: 281 additions & 143 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎design-system/apps/design-lab/src/pages/ComponentDetailPage.tsx‎

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,8 @@ type PageHeaderAlign = "center" | "start";
153153
type PageHeaderSize = "display" | "lg" | "md" | "sm";
154154

155155
const buttonVariants = ["outline", "fill", "secondary", "primary", "text"] as const;
156-
const iconButtonVariants = ["quiet", "fill", "primary"] as const;
156+
const iconButtonVariants = ["quiet", "outline", "fill", "primary"] as const;
157+
const iconButtonSizes = ["xs", "standard", "sm", "md", "lg"] as const;
157158
const buttonInspectorStates = ["default", "hover", "active"] as const;
158159
const fieldOrientations = ["vertical", "horizontal"] as const;
159160
const pageHeaderAlignments = ["start", "center"] as const;
@@ -309,6 +310,8 @@ export function ComponentDetailPage({
309310
const { t } = useI18n();
310311
const stateLabel = (state: string) => optionLabelKeys[state] ? t(optionLabelKeys[state]) : state;
311312
const [variant, setVariant] = useState<(typeof buttonVariants)[number]>("fill");
313+
const [iconButtonSize, setIconButtonSize] = useState<(typeof iconButtonSizes)[number]>("xs");
314+
const [iconButtonShape, setIconButtonShape] = useState<"square" | "circle">("square");
312315
const [iconButtonVariant, setIconButtonVariant] = useState<(typeof iconButtonVariants)[number]>("quiet");
313316
const [iconName, setIconName] = useState<IconName>("search");
314317
const [numberBadgeValue, setNumberBadgeValue] = useState("18");
@@ -519,7 +522,7 @@ export function ComponentDetailPage({
519522

520523
if (component.name === "IconButton") {
521524
const stateProps = `${inspectorDisabled ? " disabled" : ""}${inspectorLoading ? " loading" : ""}`;
522-
return `import { IconButton } from "@openbitfun/ui";\nimport { List } from "lucide-react";\n\n<IconButton\n aria-label="${t("components.preview.listView")}"\n icon={<List />}\n variant="${iconButtonVariant}"${stateProps}\n/>`;
525+
return `import { IconButton } from "@openbitfun/ui";\nimport { List } from "lucide-react";\n\n<IconButton\n aria-label="${t("components.preview.listView")}"\n icon={<List />}\n variant="${iconButtonVariant}"\n size="${iconButtonSize}"\n shape="${iconButtonShape}"${stateProps}\n/>`;
523526
}
524527
if (component.name === "Field") {
525528
const labelAction = fieldShowLabelAction
@@ -633,6 +636,8 @@ export function ComponentDetailPage({
633636
fieldShowLabelAction,
634637
flowChatPreview,
635638
iconButtonVariant,
639+
iconButtonSize,
640+
iconButtonShape,
636641
iconName,
637642
iconSize,
638643
iconTone,
@@ -854,7 +859,8 @@ export function ComponentDetailPage({
854859
disabled={state === "disabled" || applyInspectorControls && inspectorDisabled}
855860
icon={<List aria-hidden="true" />}
856861
loading={state === "loading" || applyInspectorControls && inspectorLoading}
857-
size={size}
862+
size={iconButtonSize}
863+
shape={iconButtonShape}
858864
variant={previewVariant}
859865
/>
860866
);
@@ -2393,6 +2399,12 @@ export function ComponentDetailPage({
23932399
value={iconButtonVariant}
23942400
/>
23952401
)}
2402+
{component.name === "IconButton" && (
2403+
<>
2404+
<InspectorSelect label={t("detail.size")} options={iconButtonSizes} value={iconButtonSize} onChange={(value) => setIconButtonSize(value as (typeof iconButtonSizes)[number])} translateOptions={false} />
2405+
<InspectorSelect label="shape" options={["square", "circle"]} value={iconButtonShape} onChange={(value) => setIconButtonShape(value as "square" | "circle")} translateOptions={false} />
2406+
</>
2407+
)}
23962408
{component.name === "Field" && (
23972409
<InspectorSelect
23982410
label={t("detail.orientation")}
@@ -2486,7 +2498,7 @@ export function ComponentDetailPage({
24862498
value={scrollAreaOrientation}
24872499
/>
24882500
)}
2489-
{(component.name === "Button" || component.name === "IconButton") && (
2501+
{component.name === "Button" && (
24902502
<InspectorSelect
24912503
label={t("detail.size")}
24922504
onChange={(value) => setSize(value as PreviewSize)}

‎design-system/apps/design-lab/src/preview/IconCompositionPreview.tsx‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,24 @@ export function IconCompositionPreview() {
88
<section className="component-icon-composition" aria-label={t("detail.iconComposition")}>
99
<h3>{t("detail.iconComposition")}</h3>
1010
<p>{t("detail.iconCompositionHint")}</p>
11+
{(["panel", "tertiary"] as const).map(surface => (
12+
<div className="component-icon-composition__surface" data-surface={surface} key={surface}>
13+
{(["outline", "primary"] as const).flatMap(variant => [false, true].map(disabled => (
14+
<div className="component-icon-composition__row" key={`${variant}-${disabled}`}>
15+
<code>{variant} / {disabled ? "disabled" : "default"}</code>
16+
<Button variant={variant} disabled={disabled} leadingIcon={<Icon name="session" />} trailingIcon={<Icon name="chevron-down" />}>{label}</Button>
17+
<Button variant={variant} disabled={disabled} leadingIcon={<SessionIcon aria-hidden="true" />} trailingIcon={<Icon name="chevron-right" />}>{label}</Button>
18+
</div>
19+
)))}
20+
{(["default", "hover", "active", "disabled"] as const).map(state => (
21+
<div className="component-icon-composition__row" key={state}>
22+
<code>IconButton / {state}</code>
23+
<IconButton aria-label={`22px / ${state}`} icon={<Icon name="session" />} size="xs" disabled={state === "disabled"} data-openbitfun-preview-state={state} />
24+
<IconButton aria-label={`30px / ${state}`} icon={<Icon name="session" />} size="standard" shape="circle" variant="outline" disabled={state === "disabled"} data-openbitfun-preview-state={state} />
25+
</div>
26+
))}
27+
</div>
28+
))}
1129
{(["xs", "sm", "md", "lg"] as const).map(size => (
1230
<div className="component-icon-composition__row" key={size}>
1331
<code>Button / {size}</code>

‎design-system/apps/design-lab/src/styles.css‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3911,6 +3911,17 @@ input.lab-force-focus {
39113911
font-size: var(--openbitfun-type-body-xs-font-size);
39123912
}
39133913

3914+
.component-icon-composition__surface {
3915+
display: grid;
3916+
gap: var(--openbitfun-space-4);
3917+
padding: var(--openbitfun-space-4);
3918+
background: var(--openbitfun-color-surface-panel);
3919+
}
3920+
3921+
.component-icon-composition__surface[data-surface="tertiary"] {
3922+
background: var(--openbitfun-color-surface-tertiary);
3923+
}
3924+
39143925
.component-icon-catalog__item {
39153926
display: grid;
39163927
min-block-size: 88px;

‎design-system/apps/design-lab/vite/component-detail-contract.test.mjs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,11 +280,13 @@ test("IconButton preview exposes its icon-only presentation contract", async ()
280280
assert.ok(declaration);
281281
assert.deepEqual(
282282
[...declaration[1].matchAll(/"([^"]+)"/g)].map((match) => match[1]),
283-
["quiet", "fill", "primary"],
283+
["quiet", "outline", "fill", "primary"],
284284
);
285285
assert.match(source, /data-component="icon-button"/);
286286
assert.match(source, /aria-label=\{t\("components\.preview\.listView"\)\}/);
287287
assert.match(source, /icon=\{<List aria-hidden="true" \/>\}/);
288+
assert.match(source, /size=\{iconButtonSize\}/);
289+
assert.match(source, /shape=\{iconButtonShape\}/);
288290
});
289291

290292
test("Icon preview exposes the complete named catalog and semantic controls", async () => {

‎design-system/packages/design-tokens/src/system.tokens.json‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
{
22
"$description": "Theme-independent system scales.",
3+
"opacity": {
4+
"iconArtwork": { "$type": "number", "$value": 0.8 }
5+
},
36
"space": {
47
"$type": "dimension",
58
"0": { "$value": "0px" },
@@ -620,7 +623,9 @@
620623
"iconButton": {
621624
"$type": "dimension",
622625
"xsSize": { "$value": "22px" },
623-
"xsIconSize": { "$value": "14px" }
626+
"xsIconSize": { "$value": "14px" },
627+
"standardSize": { "$value": "30px" },
628+
"standardIconSize": { "$value": "16px" }
624629
},
625630
"button": {
626631
"$type": "dimension",

‎design-system/packages/ui/README.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,20 @@ focus ring. Use it for toolbar, dialog, and row utilities. `fill` and `primary`
198198
keep an opaque backing surface for persistent emphasis. Disabled quiet actions
199199
remain transparent and do not show hover or pressed feedback.
200200

201+
Use `size="xs"` for 22px square controls with 14px glyphs and a 4px radius.
202+
`size="standard" shape="circle" variant="outline"` provides the 30px outlined
203+
circle with a 16px glyph. Quiet and outline controls use the shared neutral
204+
hover surface for both hover and pressed states; outline keeps its border when
205+
disabled. Existing sm/md/lg sizes and the default sm size remain available.
206+
207+
The 62 reviewed single-path, single-tone masks have opaque paths.
208+
`Icon` and `SessionIcon` retain their original 80% artwork opacity standalone;
209+
Button, IconButton, ActionItem and TabGroup slots own this opacity in controls
210+
through the public `--openbitfun-opacity-icon-artwork` contract. Button trailing
211+
slots use half the content opacity and restore full disabled content opacity.
212+
The progress-25 and legacy turn assets retain their internal transparency.
213+
Product callers should not add opacity or dimensions inside these owned slots.
214+
201215
The catalog uses exported vectors, including their view boxes and per-path
202216
opacity. Theme colors remain caller-owned through `currentColor`. Asset
203217
fingerprints are reviewed with intentional resource updates so replacing a

‎design-system/packages/ui/src/components/ActionItem/ActionItem.meta.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export const actionItemMeta = {
1717
],
1818
states: ["default", "hover", "active", "focus-visible", "disabled", "danger"],
1919
tokens: [
20+
"opacity.iconArtwork",
2021
"color.action.neutral.content",
2122
"color.action.neutral.contentDisabled",
2223
"color.action.neutral.surface",

‎design-system/packages/ui/src/components/ActionItem/ActionItem.module.css‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
}
7676

7777
.leading {
78+
--openbitfun-opacity-icon-artwork: 1;
7879
display: inline-flex;
7980
flex: 0 0 auto;
8081
align-items: center;

‎design-system/packages/ui/src/components/Button/Button.meta.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export const buttonMeta = {
1515
],
1616
states: ["default", "hover", "active", "disabled"],
1717
tokens: [
18+
"opacity.iconArtwork",
1819
"component.button.content",
1920
"component.button.outlineBorder",
2021
"component.button.outlineBorderInteractive",

‎design-system/packages/ui/src/components/Button/Button.module.css‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@
236236
}
237237

238238
.icon {
239+
--openbitfun-opacity-icon-artwork: 1;
239240
display: inline-flex;
240241
flex: 0 0 auto;
241242
align-items: center;
@@ -258,6 +259,11 @@
258259
.trailingIcon {
259260
inline-size: var(--_button-trailing-icon-size);
260261
block-size: var(--_button-trailing-icon-size);
262+
opacity: 0.5;
263+
}
264+
265+
.button:disabled .trailingIcon {
266+
opacity: 1;
261267
}
262268

263269
.progress {

0 commit comments

Comments
 (0)