diff --git a/.changeset/fn-8599-task-card-size-badge.md b/.changeset/fn-8599-task-card-size-badge.md new file mode 100644 index 0000000000..c4cd94e711 --- /dev/null +++ b/.changeset/fn-8599-task-card-size-badge.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Fix task card size badge alignment when a card shows two status badges. +category: fix +dev: TaskCard.css anchors .card-size-badge to the first chip-height header row instead of centering over a wrapped header. diff --git a/packages/dashboard/app/components/TaskCard.css b/packages/dashboard/app/components/TaskCard.css index b47196fed4..97ca51a097 100644 --- a/packages/dashboard/app/components/TaskCard.css +++ b/packages/dashboard/app/components/TaskCard.css @@ -547,13 +547,16 @@ FN-6774 removes the saturated stuck-task edge stripe from board cards. Keep the } /* -FNXC:TaskCardLayout 2026-07-17-14:00 (FN-8253): -As a direct header child after .card-id, the size chip must match sibling header-badge height rather than use a bespoke chip-height minimum. Center it on the header row to preserve its baseline alignment with the id and trailing actions. Its existing color coding remains unchanged. +FNXC:TaskCardLayout 2026-07-26-08:29 (FN-8599): +As a direct header child after .card-id, the size chip must stay in the first chip-height row. Whole-header centering misaligned it when the middle badge group wrapped, so lock its tokenized height and anchor it at the header start while the shared badge rule centers its text. */ .card-size-badge { - align-self: center; + align-self: flex-start; flex-shrink: 0; box-sizing: border-box; + height: var(--card-chip-height); + min-height: var(--card-chip-height); + max-height: var(--card-chip-height); letter-spacing: 0.5px; } @@ -1672,6 +1675,18 @@ FNXC:TaskCardLayout 2026-07-13-00:59: The mobile/short-landscape 28px ⋯ touch target must not expand .card-header-actions' flex line. Without a negative vertical margin (same cancel pattern as .card-edit-btn/.card-delete-btn), align-items:center sinks the Actions/Send-back trigger below the mono task-id baseline on mobile. Keep the 28px hit box; cancel only the layout contribution so trailing actions stay vertically aligned with the task id (follow-up to FN-7933's within-cluster centerline). */ @media (max-width: 768px), (max-height: 480px) { + /* + FNXC:TaskCardLayout 2026-07-26-08:29 (FN-8599): + Narrow and short-landscape cards use the mobile chip rhythm. Keep the size chip first-row anchored instead of centering it over wrapped header badges. + */ + .card-size-badge { + align-self: flex-start; + box-sizing: border-box; + height: var(--card-chip-height-mobile); + min-height: var(--card-chip-height-mobile); + max-height: var(--card-chip-height-mobile); + } + .card-menu-btn { opacity: 1; width: 28px; @@ -1894,8 +1909,8 @@ The three-dot menu is the sole card move/action entry point. Keep this shared bu } /* - FNXC:TaskCardLayout 2026-07-17-14:00 (FN-8253): - At narrow and short-landscape breakpoints, the id, wrapping badge group, and trailing actions retain the mobile chip-height row. The direct id-adjacent size chip instead derives its height from shared badge geometry so it matches sibling header badges while its centered alignment preserves the non-wrapping header baseline. + FNXC:TaskCardLayout 2026-07-26-08:29 (FN-8599): + At narrow breakpoints, the id, wrapping badge group, and trailing actions retain the mobile chip-height row. The direct id-adjacent size chip has its own first-row anchor in the shared narrow/short-landscape block so wrapping cannot center it over the whole header. */ .card-id, .card-header-badges, diff --git a/packages/dashboard/app/components/__tests__/TaskCard.badge-height.test.tsx b/packages/dashboard/app/components/__tests__/TaskCard.badge-height.test.tsx index c364424778..6ae6ec0649 100644 --- a/packages/dashboard/app/components/__tests__/TaskCard.badge-height.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskCard.badge-height.test.tsx @@ -148,12 +148,13 @@ describe("TaskCard badge heights (FN-4369)", () => { ".card-execution-mode-badge", ".card-pr-node-badge", ".card-mission-badge", - ".card-size-badge", ".card-oversight-badge", ].map((selector) => container.querySelector(selector)); + const sizeBadge = container.querySelector(".card-size-badge"); expect(baseline).toBeTruthy(); chips.forEach((chip) => expect(chip).toBeTruthy()); + expect(sizeBadge).toBeTruthy(); const baselineStyles = getComputedStyle(baseline!); for (const chip of chips) { @@ -167,6 +168,16 @@ describe("TaskCard badge heights (FN-4369)", () => { expect(styles.minHeight).toBe(baselineStyles.minHeight); } + const sizeStyles = getComputedStyle(sizeBadge!); + expect(sizeStyles.height).toBe("var(--card-chip-height)"); + expect(sizeStyles.minHeight).toBe("var(--card-chip-height)"); + expect(sizeStyles.maxHeight).toBe("var(--card-chip-height)"); + expect(sizeStyles.paddingTop).toBe(baselineStyles.paddingTop); + expect(sizeStyles.paddingBottom).toBe(baselineStyles.paddingBottom); + expect(sizeStyles.borderTopWidth).toBe(baselineStyles.borderTopWidth); + expect(sizeStyles.borderBottomWidth).toBe(baselineStyles.borderBottomWidth); + expect(sizeStyles.lineHeight).toBe(baselineStyles.lineHeight); + cleanupCss(); }); }); diff --git a/packages/dashboard/app/components/__tests__/TaskCard.badge-wrap.test.tsx b/packages/dashboard/app/components/__tests__/TaskCard.badge-wrap.test.tsx index bc65613d35..1657649a19 100644 --- a/packages/dashboard/app/components/__tests__/TaskCard.badge-wrap.test.tsx +++ b/packages/dashboard/app/components/__tests__/TaskCard.badge-wrap.test.tsx @@ -184,7 +184,11 @@ function expectSizeBadgeAfterTaskId(container: HTMLElement, expected: boolean) { const sizeStyles = getComputedStyle(sizeBadge!); expect(sizeStyles.display).toBe("inline-flex"); expect(sizeStyles.alignItems).toBe("center"); + expect(sizeStyles.alignSelf).toBe("flex-start"); expect(sizeStyles.lineHeight).toBe("1"); + expect(sizeStyles.height).toMatch(resolvedChipHeightPattern); + expect(sizeStyles.minHeight).toMatch(resolvedChipHeightPattern); + expect(sizeStyles.maxHeight).toMatch(resolvedChipHeightPattern); expect(sizeBadge!.parentElement).toBe(header); expect(cardId.nextElementSibling).toBe(sizeBadge); expect(actions?.contains(sizeBadge)).toBe(false); @@ -519,11 +523,63 @@ describe("TaskCard badge wrapping (FN-5162)", () => { expect(loadedCss).toContain("min-height: var(--card-chip-height-mobile);"); }); - it("derives the size badge height from shared header-badge geometry", () => { + it("anchors the size badge to the first header chip row across desktop and mobile", () => { const sizeBadgeRule = loadedCss.match(/\.card-size-badge\s*\{(?
[^}]*)\}/)?.groups?.body ?? ""; + const mobileSection = getCssBlocks(loadedCss, "max-width: 768px").join("\n"); + const shortLandscapeSection = getCssBlocks(loadedCss, "max-height: 480px").join("\n"); - expect(sizeBadgeRule).toContain("align-self: center;"); - expect(sizeBadgeRule).not.toContain("min-height:"); + expect(sizeBadgeRule).toContain("align-self: flex-start;"); + expect(sizeBadgeRule).toContain("box-sizing: border-box;"); + expect(sizeBadgeRule).toContain("height: var(--card-chip-height);"); + expect(sizeBadgeRule).toContain("min-height: var(--card-chip-height);"); + expect(sizeBadgeRule).toContain("max-height: var(--card-chip-height);"); + expect(sizeBadgeRule).not.toContain("align-self: center;"); + for (const section of [mobileSection, shortLandscapeSection]) { + expectCssRuleToContain(section, ".card-size-badge", "align-self: flex-start;"); + expectCssRuleToContain(section, ".card-size-badge", "height: var(--card-chip-height-mobile);"); + expectCssRuleToContain(section, ".card-size-badge", "min-height: var(--card-chip-height-mobile);"); + expectCssRuleToContain(section, ".card-size-badge", "max-height: var(--card-chip-height-mobile);"); + } + }); + + it("keeps the size chip beside the id when paused and reviewing badges fill the wrapping group", () => { + const { container: pausedContainer } = render( +