Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions apps/frontend/scripts/token-lint-baseline.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"src/components/DemoBanner.tsx": 4,
"src/components/ui/badge.tsx": 9,
"src/components/ui/badge.tsx": 4,
"src/components/ui/toast.tsx": 3,
"src/features/admin/backup/AdminBackupPage.tsx": 7,
"src/features/admin/disk/AdminDiskPage.tsx": 3,
Expand All @@ -27,9 +27,8 @@
"src/features/projects/components/SettingsTab.tsx": 4,
"src/features/projects/components/VulnerabilityStatusBadge.tsx": 3,
"src/features/scan/SbomConformancePanel.tsx": 2,
"src/features/scan/ScanProgress.tsx": 3,
"src/features/scan/ScanProgress.tsx": 2,
"src/features/scan/ToolLogLine.tsx": 6,
"src/features/scans/ScanCancelButton.tsx": 1,
"src/pages/auth/DemoCredentialsHint.tsx": 12,
"src/pages/auth/LoginPage.tsx": 4,
"src/pages/dev/BrandCandidates.tsx": 5
Expand Down
38 changes: 20 additions & 18 deletions apps/frontend/src/components/ui/badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,31 +36,33 @@
},
tone: {
none: "",
// Risk tones — W11-H (a11y sweep).
// Risk tones — W11-H (a11y sweep), retinted in G0-7.
//
// Background uses the `--risk-X` token at low alpha so the chip reads
// as a coloured tint while preserving brand semantics. The text colour
// is intentionally a darker shade of the SAME hue family from the
// Tailwind palette so the rendered text/tint contrast clears
// WCAG AA 4.5:1 — `text-risk-X` (the raw token) measured 2.5 ~ 4.5:1
// on the blended tint and was failing AA. Measurements (alpha-blended
// against #ffffff card surface):
// Background uses the `--risk-X` token at the standard /10 tint so the
// chip reads as coloured while preserving brand semantics. The text is
// a darker shade of the SAME hue family, because `text-risk-X` (the
// severity hex itself) measures 2.5 ~ 4.5:1 on that tint and fails AA.
//
// critical text-red-700 → 5.54:1
// high text-orange-800 → 6.47:1
// medium text-yellow-800 → 5.91:1
// low text-blue-700 → 5.83:1
// info text-slate-600 → 6.41:1
// W11-H spelled those darker shades as raw palette classes
// (`text-red-700`) and wrote the measurements in this comment. G0-7
// moved them into `--risk-X-foreground` tokens: the same hexes, but
// now named, reused by every other tinted surface, and measured by
// `statusTokenContrast.test.ts` instead of by a comment. The tones
// below also carry the medium/info tint down from /15 to the single
// /10 step — until G0-7 no risk tint painted at all, so the two
// densities were a distinction nobody had ever seen.
//
// The dot indicators (SeverityBadge / DependencyScopeBadge / chart
// legends) still use `bg-risk-X` raw so the brand colour stays
// recognisable — only the text shade is darkened. Token values in
// `index.css` are NOT changed (W11 prohibition: "Severity 색 변경 0").
critical: "border-transparent bg-risk-critical/10 text-red-700",
high: "border-transparent bg-risk-high/10 text-orange-800",
medium: "border-transparent bg-risk-medium/15 text-yellow-800",
low: "border-transparent bg-risk-low/10 text-blue-700",
info: "border-transparent bg-risk-info/15 text-slate-600",
critical:
"border-transparent bg-risk-critical/10 text-risk-critical-foreground",
high: "border-transparent bg-risk-high/10 text-risk-high-foreground",
medium:
"border-transparent bg-risk-medium/10 text-risk-medium-foreground",
low: "border-transparent bg-risk-low/10 text-risk-low-foreground",
info: "border-transparent bg-risk-info/10 text-risk-info-foreground",
success: "border-transparent bg-emerald-100 text-emerald-700",
},
},
Expand All @@ -86,4 +88,4 @@
);
Badge.displayName = "Badge";

export { badgeVariants };

Check warning on line 91 in apps/frontend/src/components/ui/badge.tsx

View workflow job for this annotation

GitHub Actions / lint (frontend)

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
15 changes: 8 additions & 7 deletions apps/frontend/src/features/dashboard/PortfolioGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,15 @@ const BUCKETS: Bucket[] = ["critical", "high", "medium", "low"];

/** Tint + dot per bucket.
*
* The `risk-tint-*` classes are declared in `index.css` rather than written
* as `bg-risk-critical/10` here: Tailwind emits no rule for an opacity
* modifier on a `var()` colour, so that spelling paints nothing at all. */
* W16 wrote these as `risk-tint-*` classes hand-declared in `index.css`,
* because `bg-risk-critical/10` emitted no rule and painted nothing. G0-7
* fixed that in `tailwind.config.ts`, so the ordinary spelling is back and
* the bespoke classes are gone. */
const CELL_TONE: Record<Bucket | "clean" | "unscanned", string> = {
critical: "risk-tint-critical",
high: "risk-tint-high",
medium: "risk-tint-medium",
low: "risk-tint-low",
critical: "border-risk-critical/40 bg-risk-critical/10",
high: "border-risk-high/40 bg-risk-high/10",
medium: "border-risk-medium/40 bg-risk-medium/10",
low: "border-risk-low/40 bg-risk-low/10",
clean: "border-border bg-card",
// Deliberately not a risk tint and deliberately not the clean surface: an
// unmeasured project is its own state, and dashed edges say "no data" in a
Expand Down
6 changes: 3 additions & 3 deletions apps/frontend/src/features/projects/components/KevBadge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ function dueVisuals(state: DueDateStatus["state"] | null): {
return { badge: "bg-risk-critical text-white", dot: "bg-white" };
}
if (state === "imminent") {
// Amber — mirrors the Badge `medium` tone pair (risk-medium tint +
// yellow-800 text, the AA-audited combination from W11-H).
// Amber — mirrors the Badge `medium` tone pair (risk-medium tint at the
// standard /10 step + its foreground token, the AA-audited combination).
return {
badge: "bg-risk-medium/15 text-risk-medium-foreground",
badge: "bg-risk-medium/10 text-risk-medium-foreground",
dot: "bg-risk-medium",
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ function DrawerReviewFlagSection({ flag }: ReviewFlagSectionProps) {
const { t } = useTranslation("project_detail");
return (
<section
className="flex flex-col gap-2 rounded-md border border-risk-medium/40 bg-risk-medium/5 p-3"
className="flex flex-col gap-2 rounded-md border border-risk-medium/40 bg-risk-medium/10 p-3"
data-testid="license-drawer-review-flag"
data-review-flag={flag}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,24 @@ export function SandboxScanNotice({ onUploadSbom }: SandboxScanNoticeProps) {
<div
role="note"
data-testid="sandbox-scan-notice"
className="flex flex-col gap-2 border-b border-risk-low/30 bg-risk-low/5 px-6 py-3 text-sm text-foreground sm:flex-row sm:items-start sm:justify-between"
className="flex flex-col gap-2 border-b border-risk-low/40 bg-risk-low/10 px-6 py-3 text-sm text-foreground sm:flex-row sm:items-start sm:justify-between"
>
<div className="flex items-start gap-2.5">
<FlaskConical
className="mt-0.5 h-4 w-4 shrink-0 text-risk-low"
aria-hidden
/>
{/* G0-7 — the secondary lines below are `text-foreground`, not the
muted grey they read as. The notice sits on the page ground
(#fafafa) where the muted token clears AA by 0.03, and the tint
this banner now actually paints takes it under. Size and weight
still carry the hierarchy. */}
<div className="space-y-1">
<p className="font-medium">{t("sandbox.title")}</p>
<p className="text-xs text-muted-foreground">
<p className="text-xs">
{t("sandbox.live_limit", { max: DEMO_SANDBOX_SCAN_MAX_MB })}
</p>
<p className="text-xs text-muted-foreground">
<p className="text-xs">
{/* ICU-safe: the BomLens link is a placeholder slot, not a
concatenated string, so translators keep the sentence intact. */}
<Trans
Expand All @@ -55,7 +60,7 @@ export function SandboxScanNotice({ onUploadSbom }: SandboxScanNoticeProps) {
href={bomLensUrl()}
target="_blank"
rel="noreferrer"
className="font-medium text-risk-low underline underline-offset-2 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
className="font-medium text-risk-low-foreground underline underline-offset-2 hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
data-testid="sandbox-bomlens-link"
/>
),
Expand All @@ -66,7 +71,7 @@ export function SandboxScanNotice({ onUploadSbom }: SandboxScanNoticeProps) {
project + demo account, and uploads are visible to other visitors
until the periodic reset. Warn before anyone uploads. */}
<p
className="flex items-start gap-1.5 text-xs font-medium text-risk-high"
className="flex items-start gap-1.5 text-xs font-medium text-risk-high-foreground"
data-testid="sandbox-shared-warning"
>
<AlertTriangle className="mt-0.5 h-3.5 w-3.5 shrink-0" aria-hidden />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,9 @@ export function SourceFileViewer({
{data.truncated ? (
<div
// W11-H a11y — `text-risk-medium` (#ca8a04) on `bg-risk-medium/10`
// measures 2.54:1; swap text to `text-yellow-800` (same yellow
// family, deeper shade) for 5.91:1 AA. Background tint preserved.
// measures 2.54:1, so the text is the deeper shade of the same
// yellow (`--risk-medium-foreground`) for 5.98:1 AA. Background
// tint preserved.
className="flex items-center justify-between gap-3 border-b bg-risk-medium/10 px-4 py-2 text-xs text-risk-medium-foreground"
data-testid="source-file-truncated-banner"
>
Expand Down Expand Up @@ -266,14 +267,14 @@ function SourceLineRow({ line }: SourceLineRowProps) {
aria-hidden
className={cn(
"w-12 shrink-0 select-none border-r pr-2 text-right tabular-nums text-muted-foreground",
highlighted && "border-risk-low/40 text-risk-low",
highlighted && "border-risk-low/40 text-risk-low-foreground",
)}
>
{line.number}
</span>
{highlighted ? (
<span
className="mt-0.5 shrink-0 rounded-sm bg-risk-low/20 px-1 text-[9px] font-medium uppercase tracking-wide text-risk-low"
className="mt-0.5 shrink-0 rounded-sm bg-risk-low/20 px-1 text-[9px] font-medium uppercase tracking-wide text-risk-low-foreground"
data-testid="source-line-license-chip"
data-spdx-ids={spdxIds}
>
Expand Down
2 changes: 1 addition & 1 deletion apps/frontend/src/features/scan/SbomIngestDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ export function SbomIngestDialog({
{/* M-3 — shared public sandbox warning, shown directly above the
submit button so it is unmissable right before an upload. */}
<p
className="flex items-start gap-1.5 rounded-md border border-risk-high/30 bg-risk-high/5 px-3 py-2 text-xs font-medium text-risk-high"
className="flex items-start gap-1.5 rounded-md border border-risk-high/40 bg-risk-high/10 px-3 py-2 text-xs font-medium text-risk-high-foreground"
data-testid="sbom-ingest-shared-warning"
>
<AlertTriangle className="mt-0.5 h-3.5 w-3.5 shrink-0" aria-hidden />
Expand Down
9 changes: 5 additions & 4 deletions apps/frontend/src/features/scan/ScanProgress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -222,9 +222,10 @@ export function ScanProgress({

{cachedFromDtDown ? (
<Alert
// W11-H a11y — same hue/family swap (yellow-800) as Badge `medium`
// tone, lifts AlertDescription to AA 5.91:1.
className="border-risk-medium/40 bg-risk-medium/5 text-yellow-800"
// W11-H a11y — same hue/family swap as the Badge `medium` tone,
// lifting AlertDescription to AA 5.98:1. G0-7 named that shade
// `--risk-medium-foreground` and made the tint behind it paint.
className="border-risk-medium/40 bg-risk-medium/10 text-risk-medium-foreground"
data-testid="scan-dt-cached-alert"
>
<AlertTriangle className="h-4 w-4" aria-hidden />
Expand All @@ -241,7 +242,7 @@ export function ScanProgress({
<Progress
value={percent}
className={cn(
failed && "bg-risk-critical/15",
failed && "bg-risk-critical/10",
succeeded && "bg-status-success-subtle",
cancelled && "bg-muted",
)}
Expand Down
24 changes: 18 additions & 6 deletions apps/frontend/src/features/scan/ToolLogLine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { ScanLogMessage } from "@/hooks/useScanWebSocket";
* - HH:MM:SS timestamp in mono.
* - Stage badge tinted per {@link STAGE_BADGE_CLASS}.
* - `err` badge when the line was emitted on stderr.
* - The line text itself, tinted with `text-risk-critical` for stderr.
* - The line text itself, tinted for stderr.
*
* Extracted from `ScanProgress.tsx` so the dedicated `/scans/:scanId` log
* panel can render the same rows without duplicating the styling logic. The
Expand All @@ -26,7 +26,9 @@ export const STAGE_BADGE_CLASS: Record<string, string> = {
scancode: "border-amber-500/30 bg-amber-500/10 text-amber-700",
// W6-#43f: Trivy is the vulnerability scanner — colour the badge with the
// risk-high token so it reads as "security tool" in the unified log stream.
trivy: "border-risk-high/30 bg-risk-high/10 text-risk-high",
// G0-7: the tint behind this badge paints for the first time, and the
// severity hex is not readable on it (3.05:1) — hence the foreground token.
trivy: "border-risk-high/40 bg-risk-high/10 text-risk-high-foreground",
};

export interface ToolLogLineProps {
Expand All @@ -42,12 +44,22 @@ export function ToolLogLine({ msg }: ToolLogLineProps) {
<li
className={cn(
"flex items-baseline gap-2 border-b px-3 py-1 last:border-b-0",
isErr && "bg-risk-critical/5",
isErr && "bg-risk-critical/10",
)}
data-stage={msg.stage}
data-stream={msg.stream}
>
<span className="shrink-0 text-muted-foreground" title={msg.ts}>
<span
// The muted grey clears AA on the panel's own ground with almost no
// headroom (4.53:1), and an error row now tints that ground — which
// takes it under. On a row already marked as an error the timestamp
// does not need to recede, so it takes the full foreground there.
className={cn(
"shrink-0",
isErr ? "text-foreground" : "text-muted-foreground",
)}
title={msg.ts}
>
{msg.ts.slice(11, 19)}
</span>
<span
Expand All @@ -60,7 +72,7 @@ export function ToolLogLine({ msg }: ToolLogLineProps) {
</span>
{isErr ? (
<span
className="shrink-0 rounded border border-risk-critical/30 bg-risk-critical/10 px-1 py-0.5 text-[9px] uppercase tracking-wide text-risk-critical"
className="shrink-0 rounded border border-risk-critical/40 bg-risk-critical/10 px-1 py-0.5 text-[9px] uppercase tracking-wide text-risk-critical-foreground"
aria-label="stderr"
>
err
Expand All @@ -69,7 +81,7 @@ export function ToolLogLine({ msg }: ToolLogLineProps) {
<span
className={cn(
"min-w-0 whitespace-pre-wrap break-words",
isErr ? "text-risk-critical" : "text-foreground",
isErr ? "text-risk-critical-foreground" : "text-foreground",
)}
>
{msg.line}
Expand Down
13 changes: 7 additions & 6 deletions apps/frontend/src/features/scans/ScanCancelButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export function ScanCancelButton({
{confirming ? (
<div
className={cn(
"flex flex-col gap-2 rounded-md border border-risk-high/40 bg-risk-high/5 px-3 py-2 text-sm",
"flex flex-col gap-2 rounded-md border border-risk-high/40 bg-risk-high/10 px-3 py-2 text-sm",
className,
)}
data-testid="scan-cancel-confirm"
Expand Down Expand Up @@ -130,12 +130,13 @@ export function ScanCancelButton({
// W11-F polish — the cancel CTA already inherits Button's
// transition-all duration-fast ease-out-soft; the override here
// only swaps colour tokens, which now follow the same timing.
// W11-H a11y — `text-risk-high` (#ea580c) on `hover:bg-risk-high/5`
// measured 3.0 ~ 3.6:1 (below WCAG AA 4.5:1 for body text). Swap
// to `text-orange-800` from the same hue family — same orange
// brand, deeper shade — for 6.47:1. Border keeps `risk-high` so
// W11-H a11y — `text-risk-high` (#ea580c) on the hover tint
// measured 3.0 ~ 3.6:1 (below WCAG AA 4.5:1 for body text), so
// the text is a deeper shade of the same orange. G0-7 named that
// shade `--risk-high-foreground` and made the hover tint actually
// paint; the pair measures 6.21:1. Border keeps `risk-high` so
// the warning identity stays intact.
"gap-1 border-risk-high/40 text-orange-800 hover:bg-risk-high/5",
"gap-1 border-risk-high/40 text-risk-high-foreground hover:bg-risk-high/10",
className,
)}
>
Expand Down
Loading
Loading