feat(status-badge): CSS hover tooltip — replaces native title attribute
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled

title= attribute tooltip is browser-native, tiny, and often invisible.
New StatusBadge component uses a Tailwind group-hover absolute panel
that appears immediately on hover with:
  - Clear heading per status
  - 'Meaning' and 'Action' lines
  - Arrow pointing to the badge
  - 200ms fade-in transition

Used in TechniquesPage (list table) and TechniqueDetailPage (header).
This commit is contained in:
kitos
2026-06-02 10:42:13 +02:00
parent 7e4a44bbde
commit 2bfcc7e58c
3 changed files with 126 additions and 34 deletions

View File

@@ -27,14 +27,7 @@ import { useAuth } from "../context/AuthContext";
import TestFromTemplateForm from "../components/TestFromTemplateForm";
import type { TechniqueStatus, TestState, TestResult } from "../types/models";
const STATUS_TOOLTIPS: Record<TechniqueStatus, string> = {
validated: "✅ Validated — ≥2 tests executed and detected by Blue Team. Technique is covered.",
partial: "🟡 Partial — Some tests done but not all detected, only 1 validated test, or some tests still pending. More testing needed.",
in_progress: "🔵 In Progress — Tests exist but none validated yet (draft, executing, or under review).",
not_covered: "🔴 Not Covered — Tests were run but Blue Team did not detect the attack. Coverage gap.",
not_evaluated: "⚫ Not Evaluated — No tests created for this technique yet.",
review_required:"🟠 Review Required — Technique was recently updated or new intel/rules detected. Needs review.",
};
import StatusBadge from "../components/StatusBadge";
const statusBadgeColors: Record<TechniqueStatus, string> = {
validated: "bg-green-900/50 text-green-400 border-green-500/30",
@@ -251,14 +244,7 @@ export default function TechniqueDetailPage() {
<div>
<div className="flex items-center gap-3">
<h1 className="text-2xl font-bold text-white">{technique.mitre_id}</h1>
<span
className={`inline-flex rounded-full border px-2.5 py-0.5 text-xs font-medium cursor-help ${
statusBadgeColors[technique.status_global]
}`}
title={STATUS_TOOLTIPS[technique.status_global]}
>
{technique.status_global.replace(/_/g, " ")}
</span>
<StatusBadge status={technique.status_global as TechniqueStatus} />
{technique.review_required && (
<span className="inline-flex items-center gap-1 rounded-full border border-orange-500/30 bg-orange-900/50 px-2.5 py-0.5 text-xs font-medium text-orange-400">
<AlertTriangle className="h-3 w-3" />

View File

@@ -17,6 +17,8 @@ const STATUS_OPTIONS: { value: TechniqueStatus | "all"; label: string; color: st
const PLATFORM_OPTIONS = ["all", "windows", "linux", "macos", "cloud", "network"] as const;
import StatusBadge from "../components/StatusBadge";
const statusBadgeColors: Record<TechniqueStatus, string> = {
validated: "bg-green-900/50 text-green-400 border-green-500/30",
partial: "bg-yellow-900/50 text-yellow-400 border-yellow-500/30",
@@ -26,15 +28,6 @@ const statusBadgeColors: Record<TechniqueStatus, string> = {
review_required: "bg-orange-900/50 text-orange-400 border-orange-500/30",
};
const STATUS_TOOLTIPS: Record<TechniqueStatus, string> = {
validated: "✅ Validated — ≥2 tests executed and detected by Blue Team. Technique is covered.",
partial: "🟡 Partial — Some tests done but not all detected, only 1 validated test, or some tests still pending. More testing needed.",
in_progress: "🔵 In Progress — Tests exist but none validated yet (draft, executing, or under review).",
not_covered: "🔴 Not Covered — Tests were run but Blue Team did not detect the attack. Coverage gap.",
not_evaluated: "⚫ Not Evaluated — No tests created for this technique yet.",
review_required:"🟠 Review Required — Technique was recently updated or new intel/rules detected. Needs review.",
};
export default function TechniquesPage() {
const navigate = useNavigate();
const [viewMode, setViewMode] = useState<"matrix" | "list">("matrix");
@@ -247,15 +240,8 @@ export default function TechniquesPage() {
{tech.tactic?.replace(/-/g, " ") || "—"}
</span>
</td>
<td className="px-4 py-3">
<span
className={`inline-flex rounded-full border px-2 py-0.5 text-xs font-medium cursor-help ${
statusBadgeColors[tech.status_global]
}`}
title={STATUS_TOOLTIPS[tech.status_global]}
>
{tech.status_global.replace(/_/g, " ")}
</span>
<td className="px-4 py-3 overflow-visible">
<StatusBadge status={tech.status_global} />
</td>
</tr>
))}