diff --git a/.changeset/icon-only-priority-badges.md b/.changeset/icon-only-priority-badges.md
new file mode 100644
index 0000000000..5cfefd74b2
--- /dev/null
+++ b/.changeset/icon-only-priority-badges.md
@@ -0,0 +1,7 @@
+---
+"@runfusion/fusion": patch
+---
+
+summary: Task card priority badges now show icon-only so they no longer wrap to a new line.
+category: fix
+dev: Updates the board TaskCard priority badge to keep labels in title, aria-label, and visually-hidden text.
diff --git a/packages/dashboard/app/components/TaskCard.css b/packages/dashboard/app/components/TaskCard.css
index 3f5492647d..12f7355590 100644
--- a/packages/dashboard/app/components/TaskCard.css
+++ b/packages/dashboard/app/components/TaskCard.css
@@ -516,7 +516,7 @@ FN-6774 removes the saturated stuck-task edge stripe from board cards. Keep the
}
.card-priority-badge {
- /* FNXC:PriorityColorCoding 2026-07-11-00:00: Priority badges add a glyph beside the label; keep inline-flex geometry and token spacing so badge height/wrap invariants do not change. */
+ /* FNXC:PriorityIconOnlyBadge 2026-07-12-00:00: FN-7867 removes visible priority text from board cards, so these shared spacing declarations no longer add card text width; keep them to preserve the Task Detail priority chip/select geometry that reuses the same badge class. */
gap: var(--space-xs);
letter-spacing: 0.4px;
}
diff --git a/packages/dashboard/app/components/TaskCard.tsx b/packages/dashboard/app/components/TaskCard.tsx
index 153bcc2650..064338e275 100644
--- a/packages/dashboard/app/components/TaskCard.tsx
+++ b/packages/dashboard/app/components/TaskCard.tsx
@@ -54,7 +54,7 @@ import { WorkspaceWorktreesSummary, isWorkspaceTask } from "./WorkspaceWorktrees
import { WorkflowIcon } from "./WorkflowIcon";
import { TaskContextMenu, buildTaskActionMenuModel, getTaskPrAutomationLabel, type TaskContextMenuColumnFlags, type TaskContextMenuColumnMetadata, type TaskMenuActionDescriptor } from "./TaskContextMenu";
import { formatCost, hasTaskCost, taskTotalCost } from "../utils/taskTokenCost";
-import { getPriorityColorVar, getPriorityIcon } from "../utils/priorityIndicator";
+import { getPriorityColorVar, getPriorityIcon, getPriorityLabel } from "../utils/priorityIndicator";
/** Per-branch progress snapshot (U13). Surfaced as an optional additive field
* on the task payload for the parallel-window badge (U9). */
@@ -3086,10 +3086,14 @@ function TaskCardComponent({
{hasCardMetaBadges && (
{showPriorityBadge && (
-
- {/* FNXC:PriorityColorCoding 2026-07-11-00:00: Cards render the shared priority glyph with the priorityIndicator urgency color while preserving the existing badge text and non-normal visibility gate. */}
+
+ {/* FNXC:PriorityIconOnlyBadge 2026-07-12-00:00: FN-7867 makes task-card priority badges icon-only so priority text cannot widen .card-meta-badges and force wrapping; preserve the label through title, aria-label, and visually-hidden text while keeping the shared urgency color. */}
- {normalizedPriority}
+ {getPriorityLabel(normalizedPriority)}
)}
{task.executionMode === "fast" && (
diff --git a/packages/dashboard/app/components/__tests__/TaskCard.test.tsx b/packages/dashboard/app/components/__tests__/TaskCard.test.tsx
index 66480933a2..f847aa4721 100644
--- a/packages/dashboard/app/components/__tests__/TaskCard.test.tsx
+++ b/packages/dashboard/app/components/__tests__/TaskCard.test.tsx
@@ -21,7 +21,7 @@ import { NavigationHistoryProvider, useNavigationHistory } from "../../hooks/use
import { useOverlayDismiss } from "../../hooks/useOverlayDismiss";
import type { ConfirmOptions } from "../../hooks/useConfirm";
import { TASK_PRIORITIES, type Task, type TaskPriority } from "@fusion/core";
-import { getPriorityColorVar } from "../../utils/priorityIndicator";
+import { getPriorityColorVar, getPriorityLabel } from "../../utils/priorityIndicator";
// Mock lucide-react to avoid SVG rendering issues in test env
vi.mock("lucide-react", () => ({
@@ -42,10 +42,10 @@ vi.mock("lucide-react", () => ({
RotateCw: () => null,
Zap: () => ,
AlertTriangle: () => null,
- ArrowDown: ({ style }: { style?: React.CSSProperties }) => ,
- Flag: ({ style }: { style?: React.CSSProperties }) => ,
- ArrowUp: ({ style }: { style?: React.CSSProperties }) => ,
- TriangleAlert: ({ style }: { style?: React.CSSProperties }) => ,
+ ArrowDown: ({ style, ...props }: React.SVGProps) => ,
+ Flag: ({ style, ...props }: React.SVGProps) => ,
+ ArrowUp: ({ style, ...props }: React.SVGProps) => ,
+ TriangleAlert: ({ style, ...props }: React.SVGProps) => ,
ArrowUpRight: () => null,
// FN-7592: the overseer badge now renders an icon child instead of a text label,
// so tests must see a real SVG (like Zap) rather than a no-op render.
@@ -2790,7 +2790,7 @@ describe("TaskCard", () => {
]);
});
- it("renders shared urgency-colored glyphs for visible priority badges while normal stays hidden", () => {
+ it("renders icon-only urgency-colored priority badges with accessible labels while normal stays hidden", () => {
for (const priority of TASK_PRIORITIES) {
const { container, unmount } = render(
{
continue;
}
+ const label = getPriorityLabel(priority);
expect(badge).not.toBeNull();
- expect(badge).toHaveTextContent(priority);
+ expect(badge).toHaveAttribute("aria-label", label);
+ expect(badge).toHaveAttribute("title", label);
+ expect(Array.from(badge?.childNodes ?? []).filter((node) => node.nodeType === Node.TEXT_NODE && node.textContent?.trim())).toEqual([]);
+ const visibleLabelSpans = Array.from(badge?.querySelectorAll("span") ?? []).filter((span) => !span.classList.contains("visually-hidden"));
+ expect(visibleLabelSpans).toEqual([]);
+ expect(badge?.querySelector(".visually-hidden")).toHaveTextContent(label);
const icon = badge?.querySelector("svg");
expect(icon).not.toBeNull();
+ expect(icon).toHaveAttribute("aria-hidden", "true");
expect(icon?.getAttribute("style")).toContain(`color: ${getPriorityColorVar(priority)}`);
unmount();
}
+
+ const { container } = render(
+ ,
+ );
+ expect(container.querySelector(".card-priority-badge")).toBeNull();
});
it("renders partial card meta groups without empty wrappers when time is absent", () => {