fix(frontend): renforce la couleur projet dans les onglets non sélectionnés — teinte inactive 5% (+25% vs 4%), hover thématisé, actif préservé — #339 (QA verte)

This commit is contained in:
2026-09-12 16:22:13 +02:00
parent d537ae75df
commit 987b33dbb4
2 changed files with 56 additions and 11 deletions

View File

@ -7,9 +7,15 @@
import { cn } from "../lib/cn"; import { cn } from "../lib/cn";
import { IconButton } from "./IconButton"; import { IconButton } from "./IconButton";
import type { ReactNode } from "react"; import type { CSSProperties, ReactNode } from "react";
import { useI18n } from "@/i18n"; import { useI18n } from "@/i18n";
interface ThemedTabStyle extends CSSProperties {
"--tab-theme-active-bg": string;
"--tab-theme-inactive-bg": string;
"--tab-theme-hover-bg": string;
}
export interface TabItem { export interface TabItem {
/** Stable id, returned by `onSelect`/`onClose`. */ /** Stable id, returned by `onSelect`/`onClose`. */
id: string; id: string;
@ -44,22 +50,32 @@ export function Tabs({ items, value, onSelect, onClose, className }: TabsProps)
> >
{items.map((tab) => { {items.map((tab) => {
const active = tab.id === value; const active = tab.id === value;
const themedStyle: ThemedTabStyle | undefined = tab.themeColor
? {
"--tab-theme-active-bg": `color-mix(in srgb, var(--color-raised) 90%, ${tab.themeColor})`,
// 5% project colour is 25% stronger than the previous 4% tint.
"--tab-theme-inactive-bg": `color-mix(in srgb, var(--color-raised) 95%, ${tab.themeColor})`,
"--tab-theme-hover-bg": `color-mix(in srgb, var(--color-raised) 93%, ${tab.themeColor})`,
...(active ? { borderColor: tab.themeAccent ?? tab.themeColor } : {}),
}
: undefined;
return ( return (
<div <div
key={tab.id} key={tab.id}
style={ style={themedStyle}
tab.themeColor
? {
backgroundColor: `color-mix(in srgb, var(--color-raised) ${active ? 90 : 96}%, ${tab.themeColor})`,
...(active ? { borderColor: tab.themeAccent ?? tab.themeColor } : {}),
}
: undefined
}
className={cn( className={cn(
"group flex items-center gap-1 rounded-md border px-1 transition-colors", "group flex items-center gap-1 rounded-md border px-1 transition-colors",
active active
? "border-border-strong bg-raised" ? cn(
: "border-transparent hover:bg-raised", "border-border-strong",
tab.themeColor ? "bg-[var(--tab-theme-active-bg)]" : "bg-raised",
)
: cn(
"border-transparent",
tab.themeColor
? "bg-[var(--tab-theme-inactive-bg)] hover:bg-[var(--tab-theme-hover-bg)]"
: "hover:bg-raised",
),
)} )}
> >
<button <button

View File

@ -115,6 +115,35 @@ describe("Tabs", () => {
fireEvent.click(screen.getByRole("button", { name: "fermer beta" })); fireEvent.click(screen.getByRole("button", { name: "fermer beta" }));
expect(onClose).toHaveBeenCalledWith("b"); expect(onClose).toHaveBeenCalledWith("b");
}); });
it("keeps themed inactive, hover and active backgrounds visually ordered (#339)", () => {
render(
<Tabs
items={[
{ id: "a", label: "alpha", themeColor: "#AA3366", themeAccent: "#CC5588" },
{ id: "b", label: "beta", themeColor: "#22AA66", themeAccent: "#44CC88" },
]}
value="a"
onSelect={() => {}}
/>,
);
const active = screen.getByRole("tab", { name: "alpha" }).parentElement!;
const inactive = screen.getByRole("tab", { name: "beta" }).parentElement!;
expect(active.style.getPropertyValue("--tab-theme-active-bg"))
.toBe("color-mix(in srgb, var(--color-raised) 90%, #AA3366)");
expect(active.className).toContain("bg-[var(--tab-theme-active-bg)]");
expect(active.style.borderColor).toBe("rgb(204, 85, 136)");
expect(inactive.style.getPropertyValue("--tab-theme-inactive-bg"))
.toBe("color-mix(in srgb, var(--color-raised) 95%, #22AA66)");
expect(inactive.style.getPropertyValue("--tab-theme-hover-bg"))
.toBe("color-mix(in srgb, var(--color-raised) 93%, #22AA66)");
expect(inactive.className).toContain("bg-[var(--tab-theme-inactive-bg)]");
expect(inactive.className).toContain("hover:bg-[var(--tab-theme-hover-bg)]");
expect(inactive.style.borderColor).toBe("");
});
}); });
describe("SmallDropdown", () => { describe("SmallDropdown", () => {