diff --git a/CHANGELOG.md b/CHANGELOG.md index f85d0d5ac..b927ae7b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## [Unreleased] +## [1.12.11] — 2026-06-05 — settings notices read the same everywhere + +### Changed + +- **Settings notices and status colours are unified.** Every callout, badge and status note in Settings now draws from the same set of meaning-based colours — success, caution, info — so a green "connected" or an amber "needs attention" looks identical from one card to the next and stays legible in both light and dark themes. Caution notes share one consistent boxed style with a leading icon. + ## [1.12.10] — 2026-06-05 — a "taken" tap no longer back-fills the wrong dose ### Fixed diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml index 2300119bf..43e433df7 100644 --- a/docs/api/openapi.yaml +++ b/docs/api/openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.1.0 info: title: HealthLog API - version: 1.12.10 + version: 1.12.11 description: >- Self-hosted personal-health-tracking PWA — public API surface for the iOS native client and external ingest. diff --git a/package.json b/package.json index 444c5b722..7551d7932 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "healthlog", - "version": "1.12.10", + "version": "1.12.11", "description": "Self-hosted personal-health-tracking PWA with Withings integration, AI insights, and doctor-report PDF export.", "license": "AGPL-3.0-only", "homepage": "https://healthlog.dev", diff --git a/src/components/settings/_info-tile.tsx b/src/components/settings/_info-tile.tsx new file mode 100644 index 000000000..8fa89f6c1 --- /dev/null +++ b/src/components/settings/_info-tile.tsx @@ -0,0 +1,90 @@ +"use client"; + +/** + * `` — the one callout shape for Settings. + * + * Before this primitive, every section hand-rolled its own notice box: + * `border-l-2`/`border-l-4`, `rounded-md`/`rounded-lg`, mismatched + * border/background tone intensities, and raw palette colours used as + * text (which fail AA in light mode). The tile fixes the surface to a + * single contract — icon left, short text right, a tone-coloured left + * border — and routes every tone through the semantic tokens so the + * same notice reads identically in light and dark. + * + * Tones map to the project's semantic colour tokens: + * - `info` → `--info` (neutral guidance) + * - `warning` → `--warning` (caution / override / reconnect) + * - `success` → `--success` (confirmation) + * - `primary` → `--primary` (brand-accented instruction) + * - `neutral` → muted surface (low-emphasis aside) + */ + +import * as React from "react"; +import type { LucideIcon } from "lucide-react"; + +import { cn } from "@/lib/utils"; + +export type SettingsInfoTileTone = + | "info" + | "warning" + | "success" + | "primary" + | "neutral"; + +const TONE_SURFACE: Record = { + info: "border-info/40 bg-info/10", + warning: "border-warning/40 bg-warning/10", + success: "border-success/40 bg-success/10", + primary: "border-primary/40 bg-primary/10", + neutral: "border-border bg-muted/30", +}; + +const TONE_ICON: Record = { + info: "text-info", + warning: "text-warning", + success: "text-success", + primary: "text-primary", + neutral: "text-muted-foreground", +}; + +export interface SettingsInfoTileProps { + /** Lucide icon component; rendered in the tone colour. */ + icon: LucideIcon; + /** Visual tone — routes through the semantic colour tokens. */ + tone?: SettingsInfoTileTone; + /** Optional emphasised first line. */ + title?: React.ReactNode; + /** Body content — muted text. */ + children?: React.ReactNode; + /** Optional extra classes on the outer wrapper. */ + className?: string; +} + +export function SettingsInfoTile({ + icon: Icon, + tone = "info", + title, + children, + className, +}: SettingsInfoTileProps) { + return ( +
+
+ ); +} diff --git a/src/components/settings/advanced-section.tsx b/src/components/settings/advanced-section.tsx index ef993b414..e1802e895 100644 --- a/src/components/settings/advanced-section.tsx +++ b/src/components/settings/advanced-section.tsx @@ -156,7 +156,7 @@ function ResearchModeCard() { data-slot="settings-research-mode-card" >
- +

{t("settings.researchMode.sectionTitle")}

@@ -349,7 +349,7 @@ function DataResetCard() { {msg && (

{msg}

@@ -493,7 +493,7 @@ function AccountDeleteCard() { {msg && (

{msg}

diff --git a/src/components/settings/ai-section.tsx b/src/components/settings/ai-section.tsx index 4e1cfb54e..feff839c2 100644 --- a/src/components/settings/ai-section.tsx +++ b/src/components/settings/ai-section.tsx @@ -520,7 +520,7 @@ function ProviderStatusBadges({ {settings?.codexStatus !== "connected" && settings?.hasAdminKey && activeProvider !== "codex" && ( - + {t("settings.ai.adminAiActiveBadge")} )} @@ -837,7 +837,7 @@ function CodexProviderForm({ {t("settings.ai.codex.disconnectButton")} ) : deviceCode ? ( -
+

{t("settings.ai.deviceCodeHeading")}

@@ -848,7 +848,7 @@ function CodexProviderForm({ href={deviceCode.verificationUrl} target="_blank" rel="noopener noreferrer" - className="text-dracula-purple font-medium underline" + className="text-primary font-medium underline" > {deviceCode.verificationUrl} diff --git a/src/components/settings/health-record-export-panel.tsx b/src/components/settings/health-record-export-panel.tsx index 9bfdbcb58..cefedf981 100644 --- a/src/components/settings/health-record-export-panel.tsx +++ b/src/components/settings/health-record-export-panel.tsx @@ -201,7 +201,7 @@ export function HealthRecordExportPanel() { >