FN-8186: refine mobile Quick Add action sizing

Refine mobile Quick Add controls so every action glyph is readable without compromising compact layout.

- Size option and primary action glyphs consistently with spacing tokens.
- Tighten mobile control padding while preserving touch-target and desktop behavior.
- Extend CSS contract coverage and add a patch changeset.

Files changed:
 .changeset/fn-8186-quick-add-mobile-icon-sizing.md |  7 +++++
 .../quick-entry-action-row-height-parity.test.tsx  | 35 ++++++++++++++--------
 .../dashboard/app/components/QuickEntryBox.css     | 31 +++++++++++--------
 3 files changed, 48 insertions(+), 25 deletions(-)

Fusion-Task-Id: FN-8186

Fusion-Task-Lineage: 9433521c-6c06-4f89-a486-b8073f2a2961

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-17 00:23:19 -07:00
parent f0c32dc1bf
commit 39887f5c87
3 changed files with 48 additions and 25 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Quick Add action buttons read at a proper size on mobile.
category: fix
dev: Refines FN-8164 — enlarges the mobile-only (@media max-width:768px) tokenized glyph-size override across the .quick-entry-actions row and tightens horizontal spacing in QuickEntryBox.css; preserves the 36px touch-target floor and leaves desktop rendering unchanged.

View File

@@ -205,7 +205,7 @@ describe("quick-entry action row height parity (FN-7680)", () => {
expect(mobileBlockMatch![1].trim()).toBe("var(--quick-entry-action-row-height-mobile)"); expect(mobileBlockMatch![1].trim()).toBe("var(--quick-entry-action-row-height-mobile)");
}); });
it("enlarges only mobile icon-forward glyphs with tokenized sizing and tightens horizontal gaps", () => { it("enlarges every mobile action-row glyph with tokenized sizing and tightens horizontal spacing", () => {
const cssContent = loadAllAppCss(); const cssContent = loadAllAppCss();
const markerStart = cssContent.indexOf("Quick Entry Mobile Touch + Overflow Fixes"); const markerStart = cssContent.indexOf("Quick Entry Mobile Touch + Overflow Fixes");
expect(markerStart).toBeGreaterThan(-1); expect(markerStart).toBeGreaterThan(-1);
@@ -215,28 +215,37 @@ describe("quick-entry action row height parity (FN-7680)", () => {
expect(sectionEnd).toBeGreaterThan(sectionStart); expect(sectionEnd).toBeGreaterThan(sectionStart);
const mobileSection = cssContent.slice(sectionStart, sectionEnd); const mobileSection = cssContent.slice(sectionStart, sectionEnd);
// FNXC:QuickAddActionRow 2026-07-16-15:00: JSDOM cannot resolve CSS vars or // FNXC:QuickAddActionRow 2026-07-16-16:00: JSDOM cannot resolve CSS vars or
// render mocked lucide glyphs, so source-contract assertions prove the // render mocked lucide glyphs, so source-contract assertions prove the
// mobile-only cascade keeps the 36px target while making its small 12/14px // mobile-only cascade covers both option and primary groups. The broad
// icon-forward glyphs read proportionately and compactly. // primary-group selector includes the session-advisor glyph rather than
// leaving it at its inline 14px size.
const iconRule = mobileSection.match( const iconRule = mobileSection.match(
/\.quick-entry-primary-group \.btn-icon svg,\s*\n\s*\.quick-entry-primary-group \[data-testid="quick-entry-priority-button"\] svg,\s*\n\s*\.quick-entry-primary-group \[data-testid="quick-entry-fast-toggle"\] svg\s*\{([^}]*)\}/, /\.quick-entry-options-group svg,\s*\n\s*\.quick-entry-primary-group svg\s*\{([^}]*)\}/,
); );
expect(iconRule).not.toBeNull(); expect(iconRule).not.toBeNull();
expect(iconRule![1]).toMatch(/width:\s*var\(--space-lg\);/); expect(iconRule![1]).toMatch(/width:\s*calc\(var\(--space-md\) \+ var\(--space-sm\)\);/);
expect(iconRule![1]).toMatch(/height:\s*var\(--space-lg\);/); expect(iconRule![1]).toMatch(/height:\s*calc\(var\(--space-md\) \+ var\(--space-sm\)\);/);
expect(iconRule![1]).not.toMatch(/\d+(?:\.\d+)?px/); expect(iconRule![1]).not.toMatch(/\d+(?:\.\d+)?px/);
const tokenValue = loadStylesCss().match(/--space-lg:\s*(\d+)px;/); const stylesCss = loadStylesCss();
expect(tokenValue).not.toBeNull(); const mediumToken = stylesCss.match(/--space-md:\s*(\d+)px;/);
expect(Number(tokenValue![1])).toBeGreaterThan(14); const smallToken = stylesCss.match(/--space-sm:\s*(\d+)px;/);
expect(mediumToken).not.toBeNull();
expect(smallToken).not.toBeNull();
expect(Number(mediumToken![1]) + Number(smallToken![1])).toBeGreaterThan(16);
const actionGapRule = mobileSection.match(/\.quick-entry-actions\s*\{([^}]*)\}/); const actionGapRule = mobileSection.match(/\.quick-entry-actions\s*\{([^}]*)\}/);
const optionGapRule = mobileSection.match(/\.quick-entry-options-group\s*\{([^}]*)\}/); const optionGapRule = mobileSection.match(/\.quick-entry-options-group\s*\{([^}]*)\}/);
const compactControlRule = mobileSection.match(
/\.quick-entry-options-group \.btn,\s*\n\s*\.quick-entry-options-group \.wf-optional-steps-dropdown-trigger,\s*\n\s*\.quick-entry-primary-group \.btn-icon\s*\{([^}]*)\}/,
);
expect(actionGapRule?.[1]).toMatch(/column-gap:\s*var\(--space-xs\);/); expect(actionGapRule?.[1]).toMatch(/column-gap:\s*var\(--space-xs\);/);
expect(optionGapRule?.[1]).toMatch(/column-gap:\s*var\(--space-xs\);/); expect(optionGapRule?.[1]).toMatch(/column-gap:\s*var\(--space-xs\);/);
expect(actionGapRule?.[1]).not.toMatch(/\d+(?:\.\d+)?px/); expect(compactControlRule?.[1]).toMatch(/padding-inline:\s*var\(--space-sm\);/);
expect(optionGapRule?.[1]).not.toMatch(/\d+(?:\.\d+)?px/); for (const ruleBody of [actionGapRule?.[1], optionGapRule?.[1], compactControlRule?.[1], iconRule![1]]) {
expect(ruleBody).not.toMatch(/\d+(?:\.\d+)?px/);
}
const baseOnlyCss = loadAllAppCssBaseOnly(); const baseOnlyCss = loadAllAppCssBaseOnly();
const desktopRule = baseOnlyCss.match( const desktopRule = baseOnlyCss.match(
@@ -245,7 +254,7 @@ describe("quick-entry action row height parity (FN-7680)", () => {
expect(desktopRule).not.toBeNull(); expect(desktopRule).not.toBeNull();
expect(desktopRule![1]).toMatch(/min-height:\s*var\(--quick-entry-action-row-height-desktop\);/); expect(desktopRule![1]).toMatch(/min-height:\s*var\(--quick-entry-action-row-height-desktop\);/);
expect(desktopRule![1]).toMatch(/max-height:\s*var\(--quick-entry-action-row-height-desktop\);/); expect(desktopRule![1]).toMatch(/max-height:\s*var\(--quick-entry-action-row-height-desktop\);/);
expect(desktopRule![1]).not.toMatch(/(?:width|height):\s*var\(--space-lg\)/); expect(desktopRule![1]).not.toMatch(/(?:^|[;\n]\s*)(?:width|height):/);
const mobileHeightRule = mobileSection.match( const mobileHeightRule = mobileSection.match(
/\.quick-entry-actions \.btn,\s*\n\s*\.quick-entry-actions \.wf-optional-steps-dropdown-trigger\s*\{([^}]*)\}/, /\.quick-entry-actions \.btn,\s*\n\s*\.quick-entry-actions \.wf-optional-steps-dropdown-trigger\s*\{([^}]*)\}/,

View File

@@ -830,13 +830,15 @@ FN-7682 — the tokenized-CSS test forbids raw px in the workflow-selector rules
group `gap`s remain as minimum spacing between controls. group `gap`s remain as minimum spacing between controls.
*/ */
/* /*
FNXC:QuickAddActionRow 2026-07-16-15:00: FNXC:QuickAddActionRow 2026-07-16-16:00:
FN-8164 makes the icon-forward mobile controls read proportionately inside the FN-8186 refines FN-8164 after mobile feedback: every rendered action-row
FN-7683 36px touch-target box. Scope the --space-lg glyph override to the glyph uses the tokenized 20px composite size, rather than enlarging only
primary icon controls so Save, Deps, Models, Node, and Agent retain their primary icon controls by 2px. Scoping to the two row groups covers option,
label-friendly inline glyph proportions; their desktop rendering and the text, and icon controls (including the session-advisor toggle) without
mobile touch-target floor remain intentionally unchanged. Tighten only changing desktop. Their horizontal padding is tightened locally so the larger
horizontal gaps with existing spacing tokens so wrapped rows stay compact. glyphs remain compact and Save keeps its never-clipped content width. The
FN-7683 fixed 36px touch-target height and 32px icon-button width floor stay
intact for accessibility.
*/ */
.quick-entry-actions { .quick-entry-actions {
justify-content: space-between; justify-content: space-between;
@@ -855,15 +857,20 @@ FN-7682 — the tokenized-CSS test forbids raw px in the workflow-selector rules
margin-left: 0; margin-left: 0;
} }
.quick-entry-options-group .btn,
.quick-entry-options-group .wf-optional-steps-dropdown-trigger,
.quick-entry-primary-group .btn-icon {
padding-inline: var(--space-sm);
}
.quick-entry-primary-group .btn-icon { .quick-entry-primary-group .btn-icon {
min-width: var(--space-2xl); min-width: var(--space-2xl);
} }
.quick-entry-primary-group .btn-icon svg, .quick-entry-options-group svg,
.quick-entry-primary-group [data-testid="quick-entry-priority-button"] svg, .quick-entry-primary-group svg {
.quick-entry-primary-group [data-testid="quick-entry-fast-toggle"] svg { width: calc(var(--space-md) + var(--space-sm));
width: var(--space-lg); height: calc(var(--space-md) + var(--space-sm));
height: var(--space-lg);
} }
.quick-entry-box .dep-dropdown { .quick-entry-box .dep-dropdown {