fix(ui): route status colours through AA-safe semantic tokens

Raw text-dracula-* utilities used as text fail WCAG AA contrast in light
mode (the Alucard light theme carries no override for the raw primitives).
Swap every text usage and its paired bg/border tint across the settings
integration/notification/sharing/api cards, the insights recommendation,
confidence, daily-briefing, health-score and coach-feedback surfaces, the
bug-report banners, the copy-confirmation icon and the chart comparison
caption to the theme-aware --success / --warning / --info / --destructive /
--dose-accent tokens, which both themes override to an AA-safe tone. Dark
mode is unchanged; decorative aria-hidden gradients and chart series fills
stay on the raw primitives.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marc-André BombeckandClaude Opus 4.8 committed 2026-06-05 14:07:56 +02:00
1 parent b6126db754
commit 4a88db6966
19 files changed
+106 -103

No files matched your search

+7 -7
View File
@@ -122,8 +122,8 @@ export default function BugReportPage() {
{t("bugreport.subtitle")}
</p>
</div>
<div className="bg-card border-border border-l-dracula-orange flex gap-2 rounded-lg border-l-4 p-3 text-sm">
<Info className="text-dracula-orange mt-0.5 h-4 w-4 shrink-0" />
<div className="bg-card border-border border-l-warning flex gap-2 rounded-lg border-l-4 p-3 text-sm">
<Info className="text-warning mt-0.5 h-4 w-4 shrink-0" />
<div>
<p className="text-foreground font-medium">
{t("bugreport.disabledTitle")}
@@ -149,16 +149,16 @@ export default function BugReportPage() {
</div>
{status?.configured && (
<div className="bg-card border-border border-l-dracula-cyan flex gap-2 rounded-lg border-l-4 p-3 text-sm">
<GitPullRequest className="text-dracula-cyan mt-0.5 h-4 w-4 shrink-0" />
<div className="bg-card border-border border-l-info flex gap-2 rounded-lg border-l-4 p-3 text-sm">
<GitPullRequest className="text-info mt-0.5 h-4 w-4 shrink-0" />
<p className="text-muted-foreground">
{t("bugreport.githubEscalationNote")}
</p>
</div>
)}
{!status?.configured && (
<div className="bg-card border-border border-l-dracula-purple flex gap-2 rounded-lg border-l-4 p-3 text-sm">
<Info className="text-dracula-purple mt-0.5 h-4 w-4 shrink-0" />
<div className="bg-card border-border border-l-dose-accent flex gap-2 rounded-lg border-l-4 p-3 text-sm">
<Info className="text-dose-accent mt-0.5 h-4 w-4 shrink-0" />
<p className="text-muted-foreground">
{t("bugreport.internalOnlyNote")}
</p>
@@ -215,7 +215,7 @@ export default function BugReportPage() {
<div
className={`rounded-lg p-3 text-sm ${
result.type === "success"
? "bg-dracula-green/10 text-dracula-green"
? "bg-success/10 text-success"
: "bg-destructive/10 text-destructive"
}`}
>
+1 -1
View File
@@ -1407,7 +1407,7 @@ export function HealthChart({
v1.4.19 A2 — hidden on mobile to free up the title row. */}
{effectiveCompareBaseline !== "none" && hasComparisonData && (
<span
className="text-dracula-purple bg-dracula-purple/10 hidden rounded-md border border-current/30 px-1.5 py-0.5 text-[10px] font-medium tracking-wide uppercase sm:inline-flex"
className="text-dose-accent bg-dose-accent/10 hidden rounded-md border border-current/30 px-1.5 py-0.5 text-[10px] font-medium tracking-wide uppercase sm:inline-flex"
data-slot="chart-compare-caption"
>
{t(
+1 -1
View File
@@ -96,7 +96,7 @@ export function ErrorDetails({
)}
<Button onClick={handleCopy} variant="outline">
{copied ? (
<CheckCircle2 className="text-dracula-green mr-2 h-4 w-4" />
<CheckCircle2 className="text-success mr-2 h-4 w-4" />
) : (
<Copy className="mr-2 h-4 w-4" />
)}
@@ -112,9 +112,11 @@ describe("<DailyBriefing>", () => {
it("uses tone-specific colour classes per finding", () => {
const html = render(<DailyBriefing briefing={baseBriefing} />);
expect(html).toContain("bg-dracula-green"); // tone=good bar
expect(html).toContain("bg-dracula-orange"); // tone=watch bar
expect(html).toContain("bg-dracula-cyan"); // tone=info bar
// Routed through the semantic feedback tokens (AA-safe in light mode);
// raw `--dracula-*` text primitives have no light-mode override.
expect(html).toContain("bg-success"); // tone=good bar
expect(html).toContain("bg-warning"); // tone=watch bar
expect(html).toContain("bg-info"); // tone=info bar
});
it("renders the 'Generated <time>' caption when updatedAt is supplied", () => {
@@ -362,7 +362,7 @@ function ChatBubble({
>
<div
className={cn(
"border-dracula-purple/30 bg-dracula-purple/12 text-foreground",
"border-dose-accent/30 bg-dose-accent/12 text-foreground",
// Budget the avatar column (size-8 + gap-2.5 ≈ 2.625rem) out
// of the 80% cap so the bubble + avatar together never
// overflow a comfortable width on a narrow phone.
@@ -460,7 +460,7 @@ function ChatBubble({
: ""}
</div>
{safeError && (
<p className="text-dracula-orange/90 text-xs">{safeError}</p>
<p className="text-warning/90 text-xs">{safeError}</p>
)}
{/* v1.12.0 — collapse the whole provenance block ("what was
included") behind one disclosure, collapsed by default. The
@@ -620,7 +620,7 @@ function CoachMessageFeedback({ messageId }: CoachMessageFeedbackProps) {
data-slot="coach-message-feedback-helpful"
onClick={() => submit.mutate("helpful")}
disabled={submit.isPending}
className="text-muted-foreground hover:text-dracula-green focus-visible:ring-ring/50 inline-flex min-h-11 items-center gap-1 rounded px-2 py-1.5 text-xs outline-none focus-visible:ring-2 disabled:opacity-50"
className="text-muted-foreground hover:text-success focus-visible:ring-ring/50 inline-flex min-h-11 items-center gap-1 rounded px-2 py-1.5 text-xs outline-none focus-visible:ring-2 disabled:opacity-50"
>
<ThumbsUp className="size-3" aria-hidden="true" />
{t("insights.coach.feedbackHelpful")}
@@ -630,7 +630,7 @@ function CoachMessageFeedback({ messageId }: CoachMessageFeedbackProps) {
data-slot="coach-message-feedback-unhelpful"
onClick={() => submit.mutate("unhelpful")}
disabled={submit.isPending}
className="text-muted-foreground hover:text-dracula-orange focus-visible:ring-ring/50 inline-flex min-h-11 items-center gap-1 rounded px-2 py-1.5 text-xs outline-none focus-visible:ring-2 disabled:opacity-50"
className="text-muted-foreground hover:text-warning focus-visible:ring-ring/50 inline-flex min-h-11 items-center gap-1 rounded px-2 py-1.5 text-xs outline-none focus-visible:ring-2 disabled:opacity-50"
>
<ThumbsDown className="size-3" aria-hidden="true" />
{t("insights.coach.feedbackUnhelpful")}
+3 -3
View File
@@ -2,7 +2,7 @@
* v1.4.20 phase D reconcile — shared confidence-band classnames.
*
* Both `<CorrelationCard>` and `<TrendAnnotation>` paint a discrete
* "low / moderate / high" badge with byte-identical Dracula utility
* "low / moderate / high" badge with byte-identical semantic utility
* classes. Per-component i18n key maps stay local because the copy
* differs (correlationRow.* vs trendAnnotation.*); only the classname
* table is the load-bearing duplication.
@@ -10,7 +10,7 @@
export type ConfidenceBand = "low" | "moderate" | "high";
export const CONFIDENCE_BADGE_CLASS: Record<ConfidenceBand, string> = {
high: "border-dracula-green/40 bg-dracula-green/10 text-dracula-green",
moderate: "border-dracula-orange/40 bg-dracula-orange/10 text-dracula-orange",
high: "border-success/40 bg-success/10 text-success",
moderate: "border-warning/40 bg-warning/10 text-warning",
low: "border-dracula-comment/40 bg-dracula-comment/10 text-muted-foreground",
};
+7 -7
View File
@@ -135,15 +135,15 @@ const METRIC_ICON: Record<
};
const TONE_BAR_CLASSNAME: Record<DailyBriefingKeyFinding["tone"], string> = {
good: "bg-dracula-green",
watch: "bg-dracula-orange",
info: "bg-dracula-cyan",
good: "bg-success",
watch: "bg-warning",
info: "bg-info",
};
const TONE_TEXT_CLASSNAME: Record<DailyBriefingKeyFinding["tone"], string> = {
good: "text-dracula-green",
watch: "text-dracula-orange",
info: "text-dracula-cyan",
good: "text-success",
watch: "text-warning",
info: "text-info",
};
function DeltaBadge({
@@ -275,7 +275,7 @@ export function DailyBriefing({
<div className="flex flex-wrap items-center justify-between gap-2">
<div className="flex items-center gap-2">
<Sparkles
className="text-dracula-purple h-4 w-4 shrink-0"
className="text-dose-accent h-4 w-4 shrink-0"
aria-hidden="true"
/>
<CardTitle className="text-base font-semibold">
+24 -22
View File
@@ -92,17 +92,19 @@ const SOURCE_LABEL_KEY: Record<HealthScoreComponentSource, string> = {
/**
* Per-source pill colour vocabulary — mirrors the Coach provenance
* `<SourceChips>` accent for `withings` (dracula-cyan) so the user
* reads "same provenance grammar across surfaces". Manual rides on
* dracula-purple, Apple Health on dracula-pink, mixed on dracula-yellow.
* `none` is a muted slate so the empty-state row reads as dimmed
* without losing the pill affordance entirely.
* `<SourceChips>` accent for `withings` (info/cyan) so the user reads
* "same provenance grammar across surfaces". Routed through the
* theme-aware semantic tokens (dose-accent / info / warning) which carry
* an AA-safe override in light mode; the raw `--dracula-*` text primitives
* read illegibly there. The pill always renders its source label, so the
* label — not the hue — is the disambiguator. `none` is a muted slate so
* the empty-state row reads as dimmed without losing the pill affordance.
*/
const SOURCE_PILL_CLASS: Record<HealthScoreComponentSource, string> = {
manual: "border-dracula-purple/30 text-dracula-purple/90",
withings: "border-dracula-cyan/30 text-dracula-cyan/90",
appleHealth: "border-dracula-pink/30 text-dracula-pink/90",
mixed: "border-dracula-yellow/30 text-dracula-yellow/90",
manual: "border-dose-accent/30 text-dose-accent/90",
withings: "border-info/30 text-info/90",
appleHealth: "border-dose-accent/30 text-dose-accent/90",
mixed: "border-warning/30 text-warning/90",
none: "border-muted-foreground/30 text-muted-foreground",
};
@@ -119,15 +121,15 @@ const BAND_NUMBER_CLASS: Record<HealthScoreBand, string> = {
};
const BAND_BORDER_CLASS: Record<HealthScoreBand, string> = {
green: "border-dracula-green/40",
yellow: "border-dracula-orange/40",
red: "border-dracula-red/40",
green: "border-success/40",
yellow: "border-warning/40",
red: "border-destructive/40",
};
const BAND_PROGRESS_CLASS: Record<HealthScoreBand, string> = {
green: "bg-dracula-green",
yellow: "bg-dracula-orange",
red: "bg-dracula-red",
green: "bg-success",
yellow: "bg-warning",
red: "bg-destructive",
};
const COMPONENT_LABEL_KEY: Record<
@@ -296,7 +298,7 @@ export function HealthScoreCard({
{delta !== null && delta > 0 && (
<span
data-slot="health-score-card-delta-chip"
className="bg-dracula-green/15 text-dracula-green rounded-full px-2 py-0.5 text-[10px] font-semibold"
className="bg-success/15 text-success rounded-full px-2 py-0.5 text-[10px] font-semibold"
>
+{delta}
</span>
@@ -357,13 +359,13 @@ export function HealthScoreCard({
<>
{delta > 0 && (
<ArrowUp
className="text-dracula-green h-3 w-3"
className="text-success h-3 w-3"
aria-hidden="true"
/>
)}
{delta < 0 && (
<ArrowDown
className="text-dracula-red h-3 w-3"
className="text-destructive h-3 w-3"
aria-hidden="true"
/>
)}
@@ -478,7 +480,7 @@ export function HealthScoreCard({
<p
data-slot="health-score-card-provenance-mixed-banner"
role="status"
className="border-dracula-yellow/30 text-dracula-yellow/90 bg-dracula-yellow/5 rounded border px-2 py-1 text-[10px] leading-snug"
className="border-warning/30 text-warning/90 bg-warning/5 rounded border px-2 py-1 text-[10px] leading-snug"
>
{t("insights.healthScore.provenance.mixedBanner")}
</p>
@@ -534,8 +536,8 @@ export function HealthScoreCard({
>
{row.value === null ? "—" : Math.round(row.value)}
</span>
{/* weight share — second, narrower bar tinted
dracula-cyan to read "provenance grammar"
{/* weight share — second, narrower bar tinted with
the `info` token to read "provenance grammar"
alongside the Coach <SourceChips> accent */}
<div
className="bg-muted/40 h-1 w-10 shrink-0 overflow-hidden rounded-full"
@@ -546,7 +548,7 @@ export function HealthScoreCard({
"h-full",
isEmpty
? "bg-muted"
: "bg-dracula-cyan/60",
: "bg-info/60",
)}
style={{
width: isEmpty
@@ -29,12 +29,12 @@ import { ConfidenceMeter } from "./confidence-meter";
*/
const SEVERITY_BADGE_STYLES: Record<string, string> = {
info: "bg-dracula-cyan/10 text-dracula-cyan border border-dracula-cyan/25",
info: "bg-info/10 text-info border border-info/25",
suggestion:
"bg-dracula-purple/10 text-dracula-purple border border-dracula-purple/25",
"bg-dose-accent/10 text-dose-accent border border-dose-accent/25",
important:
"bg-dracula-orange/10 text-dracula-orange border border-dracula-orange/25",
urgent: "bg-dracula-red/10 text-dracula-red border border-dracula-red/25",
"bg-warning/10 text-warning border border-warning/25",
urgent: "bg-destructive/10 text-destructive border border-destructive/25",
};
interface RecommendationCardProps {
@@ -223,7 +223,7 @@ export function RecommendationFeedback({
className="text-muted-foreground flex items-center gap-1.5 text-xs"
data-slot="rec-feedback-confirmation"
>
<Check className="text-dracula-green h-3.5 w-3.5" aria-hidden="true" />
<Check className="text-success h-3.5 w-3.5" aria-hidden="true" />
<span data-feedback-confirmed={verdict}>
{t("insights.recommendation.feedbackThanks")}
</span>
@@ -240,12 +240,12 @@ export function RecommendationFeedback({
>
{verdict === "up" ? (
<ThumbsUp
className="text-dracula-green h-3.5 w-3.5"
className="text-success h-3.5 w-3.5"
aria-hidden="true"
/>
) : (
<ThumbsDown
className="text-dracula-orange h-3.5 w-3.5"
className="text-warning h-3.5 w-3.5"
aria-hidden="true"
/>
)}
@@ -269,7 +269,7 @@ export function RecommendationFeedback({
title={t("insights.recommendation.feedbackHelpful")}
disabled={disabled}
onClick={() => submit(true)}
className="text-muted-foreground hover:text-dracula-green disabled:hover:text-muted-foreground focus-visible:ring-ring/50 inline-flex min-h-11 min-w-11 items-center justify-center rounded-md transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:opacity-60"
className="text-muted-foreground hover:text-success disabled:hover:text-muted-foreground focus-visible:ring-ring/50 inline-flex min-h-11 min-w-11 items-center justify-center rounded-md transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:opacity-60"
>
{submittingUp ? (
<Loader2 className="h-3.5 w-3.5 animate-spin motion-reduce:animate-none" aria-hidden="true" />
@@ -284,7 +284,7 @@ export function RecommendationFeedback({
title={t("insights.recommendation.feedbackNotHelpful")}
disabled={disabled}
onClick={() => submit(false)}
className="text-muted-foreground hover:text-dracula-orange disabled:hover:text-muted-foreground focus-visible:ring-ring/50 inline-flex min-h-11 min-w-11 items-center justify-center rounded-md transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:opacity-60"
className="text-muted-foreground hover:text-warning disabled:hover:text-muted-foreground focus-visible:ring-ring/50 inline-flex min-h-11 min-w-11 items-center justify-center rounded-md transition-colors focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:opacity-60"
>
{submittingDown ? (
<Loader2 className="h-3.5 w-3.5 animate-spin motion-reduce:animate-none" aria-hidden="true" />
+1 -1
View File
@@ -404,7 +404,7 @@ export function AboutSection() {
role="alert"
className={`mt-2 text-xs ${
tourFeedback.type === "success"
? "text-dracula-green"
? "text-success"
: "text-destructive"
}`}
>
+5 -5
View File
@@ -573,7 +573,7 @@ export function AccountSection() {
role="alert"
className={`text-sm ${
saveMsgType === "success"
? "text-dracula-green"
? "text-success"
: "text-destructive"
}`}
>
@@ -627,7 +627,7 @@ export function AccountSection() {
role="alert"
className={`mt-2 text-right text-sm ${
passkeyMsgType === "success"
? "text-dracula-green"
? "text-success"
: "text-destructive"
}`}
>
@@ -709,7 +709,7 @@ export function AccountSection() {
role="alert"
className={`mt-2 text-xs ${
tourFeedback.type === "success"
? "text-dracula-green"
? "text-success"
: "text-destructive"
}`}
>
@@ -778,7 +778,7 @@ export function AccountSection() {
role="alert"
className={`text-sm ${
passwordMsgType === "success"
? "text-dracula-green"
? "text-success"
: "text-destructive"
}`}
>
@@ -1199,7 +1199,7 @@ function AvatarSection() {
<p
role="alert"
className={`mt-3 text-sm ${
msgType === "success" ? "text-dracula-green" : "text-destructive"
msgType === "success" ? "text-success" : "text-destructive"
}`}
>
{msg}
+6 -6
View File
@@ -248,7 +248,7 @@ function ApiTokensCard() {
status={
<>
{activeTokens.length > 0 && (
<Badge className="border-dracula-green/30 bg-dracula-green/15 text-dracula-green">
<Badge className="border-success/30 bg-success/15 text-success">
{t("settings.configured")}
</Badge>
)}
@@ -284,10 +284,10 @@ function ApiTokensCard() {
{newToken && (
<div
className="bg-dracula-green/10 rounded-lg p-3 text-sm"
className="bg-success/10 rounded-lg p-3 text-sm"
data-slot="settings-api-token-created"
>
<p className="text-dracula-green mb-1 font-medium">
<p className="text-success mb-1 font-medium">
{t("settings.tokenCreated")}
</p>
<div className="flex items-start gap-2">
@@ -304,7 +304,7 @@ function ApiTokensCard() {
data-slot="settings-api-token-copy"
>
{tokenCopied ? (
<Check className="text-dracula-green h-3.5 w-3.5" />
<Check className="text-success h-3.5 w-3.5" />
) : (
<Copy className="h-3.5 w-3.5" />
)}
@@ -377,7 +377,7 @@ function ApiTokensCard() {
{t("settings.tokenExpired")}
</Badge>
) : (
<Badge className="bg-dracula-green/15 text-dracula-green text-xs">
<Badge className="bg-success/15 text-success text-xs">
{t("settings.tokenActive")}
</Badge>
)}
@@ -465,7 +465,7 @@ function ApiTokensCard() {
{t("settings.tokenExpired")}
</Badge>
) : (
<Badge className="bg-dracula-green/15 text-dracula-green text-[10px]">
<Badge className="bg-success/15 text-success text-[10px]">
{t("settings.tokenActive")}
</Badge>
)}
@@ -99,20 +99,19 @@ export function IntegrationStatusPill({
// chipClass only applies to the `connected`, `warning`, and `parked`
// branches — `error` falls back to `variant="destructive"` and
// `disconnected` to `variant="outline"`. The warning chip uses amber
// tokens to signal "connected, but the upstream just returned a
// contract-level error a reauth won't fix" — distinct from the red
// reconnect pill. The parked chip uses the orange tokens to signal
// "we tried to keep going for 24h but the contract mismatch persists
// — manual intervention required" — visually heavier than warning
// (sustained problem), lighter than error (still recoverable without
// re-auth).
// `disconnected` to `variant="outline"`. The warning and parked chips
// both carry the semantic `warning` tone (an AA-safe amber that clears
// contrast in light mode); their distinct labels + icons carry the
// meaning — "connected, but the upstream returned a contract-level error
// a reauth won't fix" vs "tried to keep going for 24h but the mismatch
// persists, manual intervention required" — distinct from the red
// reconnect pill (`error`).
const connectedChipClass =
"border-dracula-green/30 bg-dracula-green/15 text-dracula-green";
"border-success/30 bg-success/15 text-success";
const warningChipClass =
"border-dracula-yellow/30 bg-dracula-yellow/15 text-dracula-yellow";
"border-warning/30 bg-warning/15 text-warning";
const parkedChipClass =
"border-dracula-orange/30 bg-dracula-orange/15 text-dracula-orange";
"border-warning/30 bg-warning/15 text-warning";
let label: string;
let icon: React.ReactNode;
@@ -433,9 +433,9 @@ function WithingsCard({
{pillState === "parked" && (
<div
data-testid="withings-parked-banner"
className="border-dracula-orange/30 bg-dracula-orange/10 flex flex-wrap items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm"
className="border-warning/30 bg-warning/10 flex flex-wrap items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm"
>
<span className="text-dracula-orange min-w-0 break-words text-xs">
<span className="text-warning min-w-0 break-words text-xs">
{t("settings.integrationPill.parkedReconnect")}
</span>
<Button
@@ -468,7 +468,7 @@ function WithingsCard({
{resume.isSuccess && resume.data?.wasParked && (
<p
role="status"
className="text-dracula-green text-xs"
className="text-success text-xs"
data-testid="withings-resume-success"
>
{t("settings.integrationPill.resumeSuccess")}
@@ -553,7 +553,7 @@ function WithingsCard({
{credsMsg && (
<p
role="alert"
className={`text-sm ${credsMsgType === "success" ? "text-dracula-green" : "text-destructive"}`}
className={`text-sm ${credsMsgType === "success" ? "text-success" : "text-destructive"}`}
>
{credsMsg}
</p>
@@ -656,7 +656,7 @@ function WithingsCard({
{syncMsg && (
<p
role="alert"
className={`text-sm ${syncMsgType === "success" ? "text-dracula-green" : "text-destructive"}`}
className={`text-sm ${syncMsgType === "success" ? "text-success" : "text-destructive"}`}
>
{syncMsg}
</p>
@@ -857,9 +857,9 @@ function WhoopCard({
{pillState === "parked" && (
<div
data-testid="whoop-parked-banner"
className="border-dracula-orange/30 bg-dracula-orange/10 flex flex-wrap items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm"
className="border-warning/30 bg-warning/10 flex flex-wrap items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm"
>
<span className="text-dracula-orange min-w-0 break-words text-xs">
<span className="text-warning min-w-0 break-words text-xs">
{t("settings.integrationPill.parkedReconnect")}
</span>
<Button
@@ -892,7 +892,7 @@ function WhoopCard({
{resume.isSuccess && resume.data?.wasParked && (
<p
role="status"
className="text-dracula-green text-xs"
className="text-success text-xs"
data-testid="whoop-resume-success"
>
{t("settings.integrationPill.resumeSuccess")}
@@ -972,7 +972,7 @@ function WhoopCard({
{credsMsg && (
<p
role="alert"
className={`text-sm ${credsMsgType === "success" ? "text-dracula-green" : "text-destructive"}`}
className={`text-sm ${credsMsgType === "success" ? "text-success" : "text-destructive"}`}
>
{credsMsg}
</p>
@@ -1074,7 +1074,7 @@ function WhoopCard({
{syncMsg && (
<p
role="alert"
className={`text-sm ${syncMsgType === "success" ? "text-dracula-green" : "text-destructive"}`}
className={`text-sm ${syncMsgType === "success" ? "text-success" : "text-destructive"}`}
>
{syncMsg}
</p>
@@ -1303,9 +1303,9 @@ function FitbitCard({
{pillState === "parked" && (
<div
data-testid="fitbit-parked-banner"
className="border-dracula-orange/30 bg-dracula-orange/10 flex flex-wrap items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm"
className="border-warning/30 bg-warning/10 flex flex-wrap items-center justify-between gap-2 rounded-md border px-3 py-2 text-sm"
>
<span className="text-dracula-orange min-w-0 break-words text-xs">
<span className="text-warning min-w-0 break-words text-xs">
{t("settings.integrationPill.parkedReconnect")}
</span>
<Button
@@ -1338,7 +1338,7 @@ function FitbitCard({
{resume.isSuccess && resume.data?.wasParked && (
<p
role="status"
className="text-dracula-green text-xs"
className="text-success text-xs"
data-testid="fitbit-resume-success"
>
{t("settings.integrationPill.resumeSuccess")}
@@ -1418,7 +1418,7 @@ function FitbitCard({
{credsMsg && (
<p
role="alert"
className={`text-sm ${credsMsgType === "success" ? "text-dracula-green" : "text-destructive"}`}
className={`text-sm ${credsMsgType === "success" ? "text-success" : "text-destructive"}`}
>
{credsMsg}
</p>
@@ -1520,7 +1520,7 @@ function FitbitCard({
{syncMsg && (
<p
role="alert"
className={`text-sm ${syncMsgType === "success" ? "text-dracula-green" : "text-destructive"}`}
className={`text-sm ${syncMsgType === "success" ? "text-success" : "text-destructive"}`}
>
{syncMsg}
</p>
@@ -1887,7 +1887,7 @@ function MoodLogCard({
{msg && (
<p
role="alert"
className={`text-sm ${msgType === "error" ? "text-destructive" : "text-dracula-green"}`}
className={`text-sm ${msgType === "error" ? "text-destructive" : "text-success"}`}
>
{msg}
</p>
+1 -1
View File
@@ -149,7 +149,7 @@ export function NtfyCard({ isAuthenticated }: { isAuthenticated: boolean }) {
{saveMsg && (
<p
role="alert"
className={`text-sm ${saveMsgType === "success" ? "text-dracula-green" : "text-destructive"}`}
className={`text-sm ${saveMsgType === "success" ? "text-success" : "text-destructive"}`}
>
{saveMsg}
</p>
+4 -4
View File
@@ -336,10 +336,10 @@ function ShareLinksCard() {
{newToken && (
<div
className="bg-dracula-green/10 space-y-2 rounded-lg p-3 text-sm"
className="bg-success/10 space-y-2 rounded-lg p-3 text-sm"
data-testid="share-token-reveal"
>
<p className="text-dracula-green font-medium">
<p className="text-success font-medium">
{t("settings.sharing.tokenCreated")}
</p>
<p className="text-muted-foreground text-[11px]">
@@ -360,7 +360,7 @@ function ShareLinksCard() {
</Button>
</div>
{copied && (
<p className="text-dracula-green text-[11px]">
<p className="text-success text-[11px]">
{t("settings.sharing.copied")}
</p>
)}
@@ -390,7 +390,7 @@ function ShareLinksCard() {
{link.label}
</p>
<div className="flex flex-wrap items-center gap-1.5">
<Badge className="bg-dracula-green/15 text-dracula-green text-[10px]">
<Badge className="bg-success/15 text-success text-[10px]">
{t("settings.sharing.statusActive")}
</Badge>
{link.allowFhirApi && (
+1 -1
View File
@@ -157,7 +157,7 @@ export function TelegramCard({
role="alert"
className={`text-sm ${
msgType === "success"
? "text-dracula-green"
? "text-success"
: "text-destructive"
}`}
>
+1 -1
View File
@@ -216,7 +216,7 @@ export function WebPushCard() {
{msg && (
<p
role="alert"
className={`text-sm ${msgType === "success" ? "text-dracula-green" : "text-destructive"}`}
className={`text-sm ${msgType === "success" ? "text-success" : "text-destructive"}`}
>
{msg}
</p>