mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
fix(ui): remove sub-AA opacity dimming from informational text
The muted-foreground token is AA-tuned in both themes; the /70 and /80 opacity modifiers silently undid that (2.9-4.2:1 measured). Every informational text node drops the modifier for the full token: sleep hypnogram captions, learning-gate caveat, integration-card setup hints, module lock notes, glucose panel and token-footer meta, invite-token hint, wizard hint, notification channel labels and disabled notes (the disabled state reads through the note and the disabled switches, not through dimming), plus the resting state of the timestamp, suggestion chip and sidebar-collapse controls. User-authored mood notes move to foreground text-sm — user data is never muted. Decorative non-text strokes (sparkline grid, calendar dot, titration connector) keep their opacity; icons keep the /60 graphics floor.
This commit is contained in:
1 parent
5cc7fc6607
commit
4846dcb478
20 files changed
+34
-38
No files matched your search
@@ -205,17 +205,12 @@ export default function NotificationsPage() {
|
||||
key={ch.id}
|
||||
className="px-4 py-3 text-center text-xs font-medium tracking-wider uppercase"
|
||||
>
|
||||
<span
|
||||
className={
|
||||
ch.enabled
|
||||
? "text-muted-foreground"
|
||||
: "text-muted-foreground/70"
|
||||
}
|
||||
>
|
||||
{ch.label}
|
||||
</span>
|
||||
{/* Disabled state reads through the note + the disabled
|
||||
switches below — never through sub-AA opacity dimming
|
||||
on the label or the note itself. */}
|
||||
<span className="text-muted-foreground">{ch.label}</span>
|
||||
{!ch.enabled && (
|
||||
<span className="text-muted-foreground/70 block text-[10px] normal-case">
|
||||
<span className="text-muted-foreground block text-xs normal-case">
|
||||
{t("notifications.channelDisabled")}
|
||||
</span>
|
||||
)}
|
||||
@@ -296,11 +291,11 @@ export default function NotificationsPage() {
|
||||
>
|
||||
<Label
|
||||
htmlFor={switchId}
|
||||
className={`text-sm ${disabled ? "text-muted-foreground/70" : ""}`}
|
||||
className={`text-sm ${disabled ? "text-muted-foreground" : ""}`}
|
||||
>
|
||||
{ch.label}
|
||||
{disabled && (
|
||||
<span className="text-muted-foreground/70 ml-1 text-xs">
|
||||
<span className="text-muted-foreground ml-1 text-xs">
|
||||
({t("notifications.channelDisabled")})
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -325,7 +325,7 @@ export function InviteTokensSection() {
|
||||
<p className="text-muted-foreground mt-1 pl-7 text-xs">
|
||||
{t("admin.invites.description")}
|
||||
</p>
|
||||
<p className="text-muted-foreground/80 mt-1 flex items-center gap-1.5 pl-7 text-xs">
|
||||
<p className="text-muted-foreground mt-1 flex items-center gap-1.5 pl-7 text-xs">
|
||||
<ShieldCheck className="size-3.5 shrink-0" aria-hidden="true" />
|
||||
{t("admin.invites.adminOnlyHint")}
|
||||
</p>
|
||||
|
||||
@@ -289,9 +289,7 @@ function EpisodeGroup({
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{title}
|
||||
<span className="text-muted-foreground/70 normal-case">
|
||||
({parents.length})
|
||||
</span>
|
||||
<span className="normal-case">({parents.length})</span>
|
||||
</button>
|
||||
{open ? grid : null}
|
||||
</section>
|
||||
|
||||
@@ -97,7 +97,7 @@ export function GuidedQuestionsCard({
|
||||
disabled={disabled}
|
||||
onClick={onDismissAll}
|
||||
className={cn(
|
||||
"text-muted-foreground/70 hover:text-foreground inline-flex min-h-8 items-center rounded-full px-2.5 text-xs",
|
||||
"text-muted-foreground hover:text-foreground inline-flex min-h-8 items-center rounded-full px-2.5 text-xs",
|
||||
"hover:bg-dracula-purple/10 focus-visible:ring-dracula-purple/40 focus-visible:ring-2 focus-visible:outline-none",
|
||||
"disabled:opacity-60",
|
||||
)}
|
||||
|
||||
@@ -714,7 +714,7 @@ function BubbleTimestamp({
|
||||
aria-label={t("insights.coach.messageTimeLabel", { time: label })}
|
||||
onClick={() => setOpen((o) => !o)}
|
||||
onBlur={() => setOpen(false)}
|
||||
className="peer text-muted-foreground/70 hover:text-muted-foreground focus-visible:ring-ring/50 inline-flex size-11 items-center justify-center rounded outline-none focus-visible:ring-2 sm:size-8"
|
||||
className="peer text-muted-foreground hover:text-foreground focus-visible:ring-ring/50 inline-flex size-11 items-center justify-center rounded outline-none focus-visible:ring-2 sm:size-8"
|
||||
>
|
||||
<Clock className="size-3" aria-hidden="true" />
|
||||
</button>
|
||||
|
||||
@@ -29,7 +29,7 @@ export function MessageTokenFooter({ tokens, model }: MessageTokenFooterProps) {
|
||||
return (
|
||||
<p
|
||||
data-slot="coach-token-footer"
|
||||
className="text-muted-foreground/80 text-[11px] tabular-nums"
|
||||
className="text-muted-foreground text-[11px] tabular-nums"
|
||||
>
|
||||
{label}
|
||||
</p>
|
||||
|
||||
@@ -223,7 +223,7 @@ export function GlucoseClinicalPanel() {
|
||||
the data. */}
|
||||
{clinical.isSpotEstimate ? (
|
||||
<p
|
||||
className="text-muted-foreground/80 text-xs"
|
||||
className="text-muted-foreground text-xs"
|
||||
data-slot="glucose-spot-caveat"
|
||||
>
|
||||
{t("insights.bloodGlucose.clinical.spotCaveat")}
|
||||
|
||||
@@ -264,7 +264,7 @@ export function SleepHypnogram({ session }: SleepHypnogramProps) {
|
||||
{session.source ? (
|
||||
<span
|
||||
data-slot="sleep-hypnogram-source"
|
||||
className="text-muted-foreground/70 text-xs"
|
||||
className="text-muted-foreground text-xs"
|
||||
>
|
||||
{t("insights.sleep.hypnogram.source", {
|
||||
source: session.source,
|
||||
@@ -278,7 +278,7 @@ export function SleepHypnogram({ session }: SleepHypnogramProps) {
|
||||
{session.reconstructed === true && hasBreakdown ? (
|
||||
<span
|
||||
data-slot="sleep-hypnogram-estimate-note"
|
||||
className="text-muted-foreground/70 text-xs"
|
||||
className="text-muted-foreground text-xs"
|
||||
>
|
||||
{t("insights.sleep.hypnogram.estimateNote")}
|
||||
</span>
|
||||
|
||||
@@ -406,7 +406,7 @@ export function SidebarNav() {
|
||||
<button
|
||||
onClick={toggleCollapsed}
|
||||
className={cn(
|
||||
"text-muted-foreground/80 hover:text-foreground hover:bg-accent z-20 rounded-md p-1 transition-colors",
|
||||
"text-muted-foreground hover:text-foreground hover:bg-accent z-20 rounded-md p-1 transition-colors",
|
||||
className,
|
||||
)}
|
||||
aria-label={t("nav.collapseSidebar")}
|
||||
|
||||
@@ -538,7 +538,7 @@ function WizardDialogShell({
|
||||
knows what the Next tap leads to (or, on the final
|
||||
slot, that Next saves). */}
|
||||
<p
|
||||
className="text-muted-foreground/80 flex items-center gap-0.5 pt-0.5 text-xs"
|
||||
className="text-muted-foreground flex items-center gap-0.5 pt-0.5 text-xs"
|
||||
data-slot="wizard-next-hint"
|
||||
>
|
||||
<ChevronRight
|
||||
|
||||
@@ -1081,9 +1081,12 @@ function MoodNoteText({
|
||||
ref={paragraphRef}
|
||||
data-testid="mood-note-text"
|
||||
className={
|
||||
// User-authored content renders in foreground — user data is never
|
||||
// muted (the row's timestamp stays muted); italic keeps the note
|
||||
// visually distinct from app copy.
|
||||
expanded
|
||||
? "text-muted-foreground/80 text-xs break-words whitespace-pre-wrap italic"
|
||||
: "text-muted-foreground/80 line-clamp-2 text-xs italic"
|
||||
? "text-foreground text-sm break-words whitespace-pre-wrap italic"
|
||||
: "text-foreground line-clamp-2 text-sm italic"
|
||||
}
|
||||
>
|
||||
{note}
|
||||
|
||||
@@ -328,7 +328,7 @@ export function FitbitCard({
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-muted-foreground/80 text-xs">
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t("settings.integrationCredentialsHint")}
|
||||
</p>
|
||||
<div className="flex justify-end">
|
||||
|
||||
@@ -359,7 +359,7 @@ export function GoogleHealthCard({
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-muted-foreground/80 text-xs">
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t("settings.integrationCredentialsHint")}
|
||||
</p>
|
||||
<div className="flex justify-end">
|
||||
|
||||
@@ -282,7 +282,7 @@ export function NightscoutCard({ enabled = true }: { enabled?: boolean }) {
|
||||
autoCapitalize="none"
|
||||
enterKeyHint="done"
|
||||
/>
|
||||
<p className="text-muted-foreground/80 text-xs">
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t("settings.nightscoutTokenHelp")}
|
||||
</p>
|
||||
</div>
|
||||
@@ -292,7 +292,7 @@ export function NightscoutCard({ enabled = true }: { enabled?: boolean }) {
|
||||
<Label htmlFor="nightscout-private" className="text-sm">
|
||||
{t("settings.nightscoutPrivateHost")}
|
||||
</Label>
|
||||
<p className="text-muted-foreground/80 text-xs">
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t("settings.nightscoutPrivateHostHelp")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -322,7 +322,7 @@ export function OAuthProviderCard({
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-muted-foreground/80 text-xs">
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t("settings.integrationCredentialsHint")}
|
||||
</p>
|
||||
<div className="flex justify-end">
|
||||
@@ -357,7 +357,7 @@ export function OAuthProviderCard({
|
||||
|
||||
{serverUnavailable && (
|
||||
<p
|
||||
className="text-muted-foreground/80 text-xs"
|
||||
className="text-muted-foreground text-xs"
|
||||
data-testid={`${provider}-unavailable`}
|
||||
>
|
||||
{t(`${i18nPrefix}Unavailable`)}
|
||||
|
||||
@@ -54,7 +54,7 @@ export function IntegrationCardDescription({
|
||||
return (
|
||||
<>
|
||||
<p className="text-foreground">{t(`${i18nPrefix}Description`)}</p>
|
||||
<p className="text-muted-foreground/80">
|
||||
<p className="text-muted-foreground">
|
||||
{t(`${i18nPrefix}DescriptionSecondary`)}{" "}
|
||||
<a
|
||||
href={integrationDocsHref(provider)}
|
||||
|
||||
@@ -309,7 +309,7 @@ export function WhoopCard({
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-muted-foreground/80 text-xs">
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t("settings.integrationCredentialsHint")}
|
||||
</p>
|
||||
<div className="flex justify-end">
|
||||
|
||||
@@ -353,7 +353,7 @@ export function WithingsCard({
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-muted-foreground/80 text-xs">
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{t("settings.integrationCredentialsHint")}
|
||||
</p>
|
||||
<div className="flex justify-end">
|
||||
|
||||
@@ -106,13 +106,13 @@ export function ModuleToggleRow({
|
||||
)}
|
||||
<p className="text-muted-foreground text-xs">{description}</p>
|
||||
{locked && lockedNote ? (
|
||||
<p className="text-muted-foreground/80 text-xs">{lockedNote}</p>
|
||||
<p className="text-muted-foreground text-xs">{lockedNote}</p>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
{operatorDisabled ? (
|
||||
// Operator kill-switch: read-only note, no toggle.
|
||||
<span className="text-muted-foreground/80 mt-0.5 shrink-0 text-xs">
|
||||
<span className="text-muted-foreground mt-0.5 shrink-0 text-xs">
|
||||
{operatorNote}
|
||||
</span>
|
||||
) : managed ? (
|
||||
|
||||
@@ -83,7 +83,7 @@ export function LearningGate({
|
||||
{caveat ? (
|
||||
<p
|
||||
data-slot="learning-gate-caveat"
|
||||
className="text-muted-foreground/80 text-xs leading-snug"
|
||||
className="text-muted-foreground text-xs leading-snug"
|
||||
>
|
||||
{caveat}
|
||||
</p>
|
||||
|
||||
Reference in new issue
Block a user