FN-7601: distinguish task-detail priority chip colors by level

Give the TaskDetailModal priority chip distinct tinted borders/backgrounds per level so low/high/urgent/normal are visually distinguishable at a glance.

- Add per-level border-color and stronger background overrides for .detail-priority-chip.card-priority-badge--{low,high,urgent}, using the matching semantic color token (info/warning/error) with higher specificity than the shared base rule.
- Leave the FN-7585 shared base chip rule and FN-7597 neutral 'normal' treatment untouched; scope changes strictly to .detail-priority-chip so read-only TaskCard badge tints are unaffected.
- Add a regression test asserting each level has distinct, non-var(--border) border-colors and backgrounds, mutually distinct across levels, while the read-only TaskCard badge selectors remain unchanged.

Files changed:
 .../dashboard/app/components/TaskDetailModal.css   | 33 ++++++++++
 ...etailModal.responsive-and-dependencies.test.tsx | 71 ++++++++++++++++++++++
 2 files changed, 104 insertions(+)

Fusion-Task-Id: FN-7601

Fusion-Task-Lineage: 1e2fc574-835c-43b0-8689-02884ad5c2d6

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-05 19:09:14 -07:00
parent dc447304a9
commit 8fb7d51ae9
2 changed files with 104 additions and 0 deletions

View File

@@ -431,6 +431,39 @@ read-only TaskCard priority badge (out of scope) is untouched.
color: var(--text-muted);
}
/*
FNXC:TaskDetail 2026-07-05-13:30:
FN-7601 — the FN-7585 base `.detail-priority-chip` rule pins `border-color:
var(--border)` for every level, so once the chip shrank to a compact 30px box
the only remaining differentiator (the ~15-18% tint backgrounds inherited
from `.card-priority-badge--{low,high,urgent}`) reads as the same washed-out
box for low vs high. Give each non-neutral level its own tinted border AND a
stronger background on top of the shared base/normal rules above (higher
specificity via the `.detail-priority-chip.card-priority-badge--X` compound
selector) so low/high/urgent are unmistakably distinct colors at a glance,
while normal keeps the FN-7597 neutral treatment and the FN-7585 shared
border-width/radius/min-height stay untouched. Scoped to `.detail-priority-chip`
so the read-only `.card-priority-badge--{low,high,urgent}` tints in
TaskCard.css (out of scope) are unaffected.
*/
.detail-priority-chip.card-priority-badge--low {
background: color-mix(in srgb, var(--color-info) 22%, transparent);
border-color: color-mix(in srgb, var(--color-info) 65%, var(--border));
color: var(--color-info);
}
.detail-priority-chip.card-priority-badge--high {
background: color-mix(in srgb, var(--color-warning) 28%, transparent);
border-color: color-mix(in srgb, var(--color-warning) 65%, var(--border));
color: var(--color-warning);
}
.detail-priority-chip.card-priority-badge--urgent {
background: color-mix(in srgb, var(--color-error) 30%, transparent);
border-color: color-mix(in srgb, var(--color-error) 65%, var(--border));
color: var(--color-error-dark);
}
.detail-priority-select {
border: 0;
background: transparent;

View File

@@ -304,6 +304,77 @@ describe("TaskDetailModal", () => {
expect(prioritySavingBlock).not.toMatch(/border|min-height|padding/);
});
it("makes low/high/urgent visibly distinct colors on the detail Priority chip, scoped away from TaskCard (FN-7601)", () => {
const css = readDashboardStylesSource();
// FN-7585's shared base rule and FN-7597's neutral `normal` rule must
// survive untouched — this task only ADDS per-level overrides on top.
const baseChipBlock = getExactCssRuleBlock(css, ".detail-priority-chip");
expect(baseChipBlock).toContain("border-width: var(--btn-border-width);");
expect(baseChipBlock).toContain("border-color: var(--border);");
expect(baseChipBlock).toContain("border-radius: var(--detail-control-border-radius);");
const normalBlock = getExactCssRuleBlock(css, ".detail-priority-chip.card-priority-badge--normal");
expect(normalBlock).toMatch(/background:\s*color-mix\(in srgb, var\(--text-muted\)/);
expect(normalBlock).toContain("color: var(--text-muted);");
const lowBlock = getExactCssRuleBlock(css, ".detail-priority-chip.card-priority-badge--low");
const highBlock = getExactCssRuleBlock(css, ".detail-priority-chip.card-priority-badge--high");
const urgentBlock = getExactCssRuleBlock(css, ".detail-priority-chip.card-priority-badge--urgent");
// Each non-neutral level must declare its own tinted border-color AND
// background, using the matching semantic token family.
for (const [block, token] of [
[lowBlock, "--color-info"],
[highBlock, "--color-warning"],
[urgentBlock, "--color-error"],
] as const) {
expect(block).not.toBe("");
expect(block).toMatch(/border-color\s*:/);
expect(block).toMatch(/background\s*:/);
expect(block).toContain(token);
}
// None of the per-level border-colors may resolve to the plain shared
// `var(--border)` value used by the base rule — that was the original
// bug (every level looked the same washed-out box).
const borderColorOf = (block: string): string => {
const match = block.match(/border-color\s*:\s*([^;]+);/);
return match?.[1]?.trim() ?? "";
};
const backgroundOf = (block: string): string => {
const match = block.match(/background\s*:\s*([^;]+);/);
return match?.[1]?.trim() ?? "";
};
const lowBorder = borderColorOf(lowBlock);
const highBorder = borderColorOf(highBlock);
const urgentBorder = borderColorOf(urgentBlock);
expect(lowBorder).not.toBe("var(--border)");
expect(highBorder).not.toBe("var(--border)");
expect(urgentBorder).not.toBe("var(--border)");
// Mutually distinct — low, high, and urgent must not collapse onto the
// same border-color or background declaration as one another.
expect(new Set([lowBorder, highBorder, urgentBorder]).size).toBe(3);
const lowBg = backgroundOf(lowBlock);
const highBg = backgroundOf(highBlock);
const urgentBg = backgroundOf(urgentBlock);
expect(new Set([lowBg, highBg, urgentBg]).size).toBe(3);
// `normal`'s background/border must remain distinct from all three tinted
// levels (it keeps the FN-7597 neutral treatment, not a semantic tint).
expect(new Set([backgroundOf(normalBlock), lowBg, highBg, urgentBg]).size).toBe(4);
// The read-only TaskCard badge tints referenced by TaskCard.css must be
// untouched by this task — confirm no `.detail-priority-chip` compound
// selector leaks a border-color override into the bare `.card-priority-badge--*`
// selectors (those remain single-class, background/color-only rules).
expect(css).toMatch(/\.card-priority-badge--low\s*\{\s*background:\s*color-mix\(in srgb, var\(--color-info\) 15%, transparent\);\s*color:\s*var\(--color-info\);\s*\}/);
expect(css).toMatch(/\.card-priority-badge--high\s*\{\s*background:\s*color-mix\(in srgb, var\(--color-warning\) 18%, transparent\);\s*color:\s*var\(--color-warning\);\s*\}/);
expect(css).toMatch(/\.card-priority-badge--urgent\s*\{\s*background:\s*color-mix\(in srgb, var\(--color-error\) 20%, transparent\);\s*color:\s*var\(--color-error-dark\);\s*\}/);
});
it("keeps grouped timestamp metadata inline on desktop and mobile", () => {
const css = readDashboardStylesSource();