diff --git a/src/app/insights/medikamente/page.tsx b/src/app/insights/medikamente/page.tsx
index e2b2b5356..ff982be9e 100644
--- a/src/app/insights/medikamente/page.tsx
+++ b/src/app/insights/medikamente/page.tsx
@@ -110,7 +110,7 @@ export default function InsightsMedikamentePage() {
return (
-
+
);
@@ -250,7 +250,7 @@ function MedicationComplianceCalendar({
if (isLoading) {
return (
-
+
);
}
diff --git a/src/app/insights/page.tsx b/src/app/insights/page.tsx
index 00df750b8..62b698b49 100644
--- a/src/app/insights/page.tsx
+++ b/src/app/insights/page.tsx
@@ -133,7 +133,7 @@ export default function InsightsPage() {
if (isLoading) {
return (
-
+
);
}
diff --git a/src/app/measurements/page.tsx b/src/app/measurements/page.tsx
index 9901029ff..c075842fe 100644
--- a/src/app/measurements/page.tsx
+++ b/src/app/measurements/page.tsx
@@ -32,7 +32,7 @@ export default function MeasurementsPage() {
if (isLoading) {
return (
-
+
);
}
diff --git a/src/app/medications/[id]/history/page.tsx b/src/app/medications/[id]/history/page.tsx
index 4904d5fbd..1c495ef65 100644
--- a/src/app/medications/[id]/history/page.tsx
+++ b/src/app/medications/[id]/history/page.tsx
@@ -51,7 +51,7 @@ export default function IntakeHistoryPage({
if (authLoading || medLoading) {
return (
-
+
);
}
diff --git a/src/app/medications/page.tsx b/src/app/medications/page.tsx
index 70ba80ec8..373721e66 100644
--- a/src/app/medications/page.tsx
+++ b/src/app/medications/page.tsx
@@ -113,7 +113,7 @@ export default function MedicationsPage() {
if (authLoading) {
return (
-
+
);
}
@@ -161,7 +161,7 @@ export default function MedicationsPage() {
{isLoading ? (
-
+
) : isError ? (
@@ -499,7 +499,7 @@ function IntakeImportDialog({
onClick={handleImport}
disabled={importing || !jsonText.trim()}
>
- {importing &&
}
+ {importing &&
}
{t("common.import")}
diff --git a/src/app/mood/page.tsx b/src/app/mood/page.tsx
index 03cebed31..38f0e0ad2 100644
--- a/src/app/mood/page.tsx
+++ b/src/app/mood/page.tsx
@@ -32,7 +32,7 @@ export default function MoodPage() {
if (isLoading) {
return (
-
+
);
}
diff --git a/src/app/notifications/page.tsx b/src/app/notifications/page.tsx
index 0ac110b2e..1d4ab703c 100644
--- a/src/app/notifications/page.tsx
+++ b/src/app/notifications/page.tsx
@@ -129,7 +129,7 @@ export default function NotificationsPage() {
if (authLoading || isLoading) {
return (
-
+
);
}
diff --git a/src/app/targets/page.tsx b/src/app/targets/page.tsx
index b36940b9e..10333214e 100644
--- a/src/app/targets/page.tsx
+++ b/src/app/targets/page.tsx
@@ -196,7 +196,7 @@ export default function TargetsPage() {
if (isLoading) {
return (
-
+
);
}
diff --git a/src/components/admin/ai-quality-section.tsx b/src/components/admin/ai-quality-section.tsx
index 9c3de650d..be96674e5 100644
--- a/src/components/admin/ai-quality-section.tsx
+++ b/src/components/admin/ai-quality-section.tsx
@@ -75,7 +75,7 @@ export function AiQualitySection() {
if (query.isLoading) {
return (
-
+
{t("admin.aiQuality.loading")}
diff --git a/src/components/admin/api-token-overview-section.tsx b/src/components/admin/api-token-overview-section.tsx
index 1ab015c19..f8ed57313 100644
--- a/src/components/admin/api-token-overview-section.tsx
+++ b/src/components/admin/api-token-overview-section.tsx
@@ -170,7 +170,7 @@ export function ApiTokenOverviewSection() {
) : !tokens?.length ? (
// v1.4.15 phase-C5: empty state explains where tokens come
// from (native client sign-in / Settings → Account) so a
- // fresh admin doesn't think they need to create them here.
+ // fresh admin doesn motion-reduce:animate-none't think they need to create them here.
}
title={t("admin.tokensEmptyTitle")}
diff --git a/src/components/admin/app-log-preview-section.tsx b/src/components/admin/app-log-preview-section.tsx
index 509a0f523..9d6d9fd51 100644
--- a/src/components/admin/app-log-preview-section.tsx
+++ b/src/components/admin/app-log-preview-section.tsx
@@ -133,7 +133,7 @@ export function AppLogPreviewSection() {
aria-label={t("admin.section.app-logs.refresh")}
>
@@ -195,7 +195,7 @@ export function AppLogPreviewSection() {
{isLoading ? (
-
+
) : events.length === 0 ? (
{pending ? (
-
+
) : (
)}
diff --git a/src/components/admin/bug-report-section.tsx b/src/components/admin/bug-report-section.tsx
index 871b26c85..fd8a42149 100644
--- a/src/components/admin/bug-report-section.tsx
+++ b/src/components/admin/bug-report-section.tsx
@@ -119,7 +119,7 @@ export function BugReportSection() {
disabled={updateSettings.isPending}
>
{updateSettings.isPending && (
-
+
)}
{t("common.save")}
diff --git a/src/components/admin/coach-feedback-section.tsx b/src/components/admin/coach-feedback-section.tsx
index 4121c4fd6..05db00be1 100644
--- a/src/components/admin/coach-feedback-section.tsx
+++ b/src/components/admin/coach-feedback-section.tsx
@@ -99,7 +99,7 @@ export function CoachFeedbackSection() {
{query.isLoading ? (
-
+
{t("admin.coachFeedback.loading")}
diff --git a/src/components/admin/danger-zone-section.tsx b/src/components/admin/danger-zone-section.tsx
index 6d1cec635..fcb45722b 100644
--- a/src/components/admin/danger-zone-section.tsx
+++ b/src/components/admin/danger-zone-section.tsx
@@ -85,7 +85,7 @@ export function DangerZoneSection() {
disabled={wipeAllData.isPending}
>
{wipeAllData.isPending ? (
-
+
) : (
)}
diff --git a/src/components/admin/feedback-inbox-section.tsx b/src/components/admin/feedback-inbox-section.tsx
index c65ae1d3c..589808fa3 100644
--- a/src/components/admin/feedback-inbox-section.tsx
+++ b/src/components/admin/feedback-inbox-section.tsx
@@ -104,7 +104,7 @@ export function FeedbackInboxSection() {
{isLoading ? (
-
+
{t("admin.feedback.loading")}
@@ -435,7 +435,7 @@ function FeedbackDetailDialog({
onClick={() => update.mutate({ adminNote: note || null })}
>
{update.isPending && (
-
+
)}
{t("admin.feedback.saveNote")}
@@ -497,7 +497,7 @@ function FeedbackDetailDialog({
className="ml-auto"
>
{publish.isPending ? (
-
+
) : (
)}
diff --git a/src/components/admin/glitchtip-section.tsx b/src/components/admin/glitchtip-section.tsx
index 50f7c234e..44944de3e 100644
--- a/src/components/admin/glitchtip-section.tsx
+++ b/src/components/admin/glitchtip-section.tsx
@@ -148,7 +148,7 @@ export function GlitchtipSection() {
disabled={testGlitchtip.isPending || updateSettings.isPending}
>
{testGlitchtip.isPending && (
-
+
)}
{t("common.test")}
@@ -158,7 +158,7 @@ export function GlitchtipSection() {
disabled={updateSettings.isPending}
>
{updateSettings.isPending && (
-
+
)}
{t("common.save")}
diff --git a/src/components/admin/login-overview-section.tsx b/src/components/admin/login-overview-section.tsx
index d0e193263..22266f65c 100644
--- a/src/components/admin/login-overview-section.tsx
+++ b/src/components/admin/login-overview-section.tsx
@@ -340,7 +340,7 @@ export function LoginOverviewSection() {
{isLoading ? (
-
+
) : !entries.length ? (
diff --git a/src/components/admin/reminders-section.tsx b/src/components/admin/reminders-section.tsx
index 213afc4ba..40a2ccef5 100644
--- a/src/components/admin/reminders-section.tsx
+++ b/src/components/admin/reminders-section.tsx
@@ -177,7 +177,7 @@ export function RemindersSection() {
disabled={testNotification.isPending}
>
{testNotification.isPending && (
-
+
)}
{t("admin.notificationTestSend")}
@@ -189,7 +189,7 @@ export function RemindersSection() {
disabled={reminderCheck.isPending}
>
{reminderCheck.isPending && (
-
+
)}
{t("admin.reminderCheckRun")}
@@ -220,7 +220,7 @@ export function RemindersSection() {
}
>
{updateSettings.isPending && (
-
+
)}
{t("common.save")}
diff --git a/src/components/admin/system-status-summary.tsx b/src/components/admin/system-status-summary.tsx
index a16bd5903..fe269e43b 100644
--- a/src/components/admin/system-status-summary.tsx
+++ b/src/components/admin/system-status-summary.tsx
@@ -127,7 +127,7 @@ export function SystemStatusSummary() {
) : (
diff --git a/src/components/admin/umami-section.tsx b/src/components/admin/umami-section.tsx
index 8656c4423..6ff7c756f 100644
--- a/src/components/admin/umami-section.tsx
+++ b/src/components/admin/umami-section.tsx
@@ -149,7 +149,7 @@ export function UmamiSection() {
disabled={testUmami.isPending || updateSettings.isPending}
>
{testUmami.isPending && (
-
+
)}
{t("common.test")}
@@ -159,7 +159,7 @@ export function UmamiSection() {
disabled={updateSettings.isPending}
>
{updateSettings.isPending && (
-
+
)}
{t("common.save")}
diff --git a/src/components/admin/user-management-section.tsx b/src/components/admin/user-management-section.tsx
index 836a2eee4..754c8ea2b 100644
--- a/src/components/admin/user-management-section.tsx
+++ b/src/components/admin/user-management-section.tsx
@@ -407,7 +407,7 @@ export function UserManagementSection() {
)
) : (
-
+
{t("admin.loadingUsers")}
@@ -460,7 +460,7 @@ export function UserManagementSection() {
}
>
{updateUser.isPending && (
-
+
)}
{t("common.save")}
@@ -510,7 +510,7 @@ export function UserManagementSection() {
}
>
{resetPw.isPending && (
-
+
)}
{t("admin.reset")}
@@ -562,7 +562,7 @@ export function UserManagementSection() {
className="bg-destructive hover:bg-destructive/90"
>
{forceLogout.isPending ? (
-
+
) : null}
{t("admin.section.users.forceLogout")}
diff --git a/src/components/admin/web-push-vapid-section.tsx b/src/components/admin/web-push-vapid-section.tsx
index f8c77a841..1ee3d2c09 100644
--- a/src/components/admin/web-push-vapid-section.tsx
+++ b/src/components/admin/web-push-vapid-section.tsx
@@ -135,7 +135,7 @@ export function WebPushVapidSection() {
disabled={updateSettings.isPending}
>
{updateSettings.isPending && (
-
+
)}
{t("common.save")}
diff --git a/src/components/charts/health-chart.tsx b/src/components/charts/health-chart.tsx
index 5da206e54..3a6447571 100644
--- a/src/components/charts/health-chart.tsx
+++ b/src/components/charts/health-chart.tsx
@@ -1164,7 +1164,7 @@ export function HealthChart({
) : !chartData?.length ? null : (chartData?.length ?? 0) < 3 ? (
// v1.4.16 B1a — sparse-data placeholder. <3 daily points is too
// few to render a meaningful trend; paint a friendly hint
- // instead so the dashboard doesn't look broken.
+ // instead so the dashboard doesn motion-reduce:animate-none't look broken.
-
+
) : !hasData ? (
diff --git a/src/components/charts/mood-chart.tsx b/src/components/charts/mood-chart.tsx
index 44046bad7..7dd0f18c2 100644
--- a/src/components/charts/mood-chart.tsx
+++ b/src/components/charts/mood-chart.tsx
@@ -623,7 +623,7 @@ export function MoodChart({
) : chartData.length < 3 ? (
// v1.4.16 B1a — sparse-data placeholder consistent with the
- // BP/weight/pulse charts. Height tracks the chart strip's
+ // BP/weight/pulse charts. Height tracks the chart strip motion-reduce:animate-none's
// shared CHART_HEIGHT_PX so the empty state preserves the
// trend-row rhythm (v1.4.27 — was 280, now 240 to match every
// other dashboard chart card).
diff --git a/src/components/dashboard/glp1-tile.tsx b/src/components/dashboard/glp1-tile.tsx
index 1470f7e39..6b55e3b52 100644
--- a/src/components/dashboard/glp1-tile.tsx
+++ b/src/components/dashboard/glp1-tile.tsx
@@ -430,7 +430,7 @@ export function Glp1Tile() {
}
/**
- * v1.4.27 B1 — small segmented-control button used by the tile's
+ * v1.4.27 B1 — small segmented-control button used by the tile motion-reduce:animate-none's
* tab strip. Carved into its own component so the active/inactive
* styling lives in one place and the test can pin the data-slot
* markers per tab.
diff --git a/src/components/insights/coach-panel/coach-input.tsx b/src/components/insights/coach-panel/coach-input.tsx
index 24e24648e..7a2e40ac2 100644
--- a/src/components/insights/coach-panel/coach-input.tsx
+++ b/src/components/insights/coach-panel/coach-input.tsx
@@ -218,7 +218,7 @@ export function CoachInput({
className="gap-1.5"
>
{isStreaming ? (
-
+
) : (
)}
diff --git a/src/components/insights/coach-panel/coach-settings-sheet.tsx b/src/components/insights/coach-panel/coach-settings-sheet.tsx
index b07be2e0f..f85383b2e 100644
--- a/src/components/insights/coach-panel/coach-settings-sheet.tsx
+++ b/src/components/insights/coach-panel/coach-settings-sheet.tsx
@@ -351,7 +351,7 @@ export function CoachSettingsSheet({
data-slot="coach-prefs-save"
>
{save.isPending ? (
-
+
) : null}
{t("insights.coach.settingsSave")}
diff --git a/src/components/insights/coach-panel/message-thread.tsx b/src/components/insights/coach-panel/message-thread.tsx
index 915ebf061..57251d52a 100644
--- a/src/components/insights/coach-panel/message-thread.tsx
+++ b/src/components/insights/coach-panel/message-thread.tsx
@@ -1,6 +1,6 @@
"use client";
-import { useEffect, useMemo, useRef, useState } from "react";
+import { useEffect, useId, useMemo, useRef, useState } from "react";
import { useMutation } from "@tanstack/react-query";
import {
Bot,
@@ -315,6 +315,9 @@ function ChatBubble({
}: ChatBubbleProps) {
const { t } = useTranslations();
const { user } = useAuth();
+ // v1.4.27 B7 / L3 — pair the evidence `` and its disclosed
+ // list explicitly so screen-readers announce the panel relationship.
+ const evidencePanelId = useId();
if (role === "user") {
// v1.4.22 B3 — pull the user's Gravatar so the user bubble's
// avatar matches the Coach avatar in size and visual weight.
@@ -434,6 +437,7 @@ function ChatBubble({
>
{t("insights.coach.evidenceLabel")}
diff --git a/src/components/insights/insight-status-card.tsx b/src/components/insights/insight-status-card.tsx
index 44b1cfda9..d9dffd95e 100644
--- a/src/components/insights/insight-status-card.tsx
+++ b/src/components/insights/insight-status-card.tsx
@@ -34,7 +34,7 @@ export function InsightStatusCard({
return (
-
+
{t("common.loading")}
diff --git a/src/components/insights/recommendation-feedback.tsx b/src/components/insights/recommendation-feedback.tsx
index c0e2311b0..02661a67d 100644
--- a/src/components/insights/recommendation-feedback.tsx
+++ b/src/components/insights/recommendation-feedback.tsx
@@ -267,7 +267,7 @@ export function RecommendationFeedback({
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"
>
{submittingUp ? (
-
+
) : (
)}
@@ -282,7 +282,7 @@ export function RecommendationFeedback({
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"
>
{submittingDown ? (
-
+
) : (
)}
diff --git a/src/components/insights/therapy-timeline.tsx b/src/components/insights/therapy-timeline.tsx
index d8ce593aa..dd025a9db 100644
--- a/src/components/insights/therapy-timeline.tsx
+++ b/src/components/insights/therapy-timeline.tsx
@@ -108,6 +108,13 @@ export function TherapyTimeline({ limit }: TherapyTimelineProps) {
style={{ backgroundColor: colourForKind(entry.kind) }}
/>
+ {/* v1.4.27 B7 / L4 — sr-only drug-name heading per
+ entry so screen-reader users can jump between
+ medication blocks; the visible row keeps the
+ inline
rendering. */}
+ {entry.medicationName && (
+ {entry.medicationName}
+ )}
{fmt.dateWithWeekday(new Date(entry.date))}
diff --git a/src/components/insights/weekly-report-view.tsx b/src/components/insights/weekly-report-view.tsx
index 290eac02c..5c783d8e0 100644
--- a/src/components/insights/weekly-report-view.tsx
+++ b/src/components/insights/weekly-report-view.tsx
@@ -89,7 +89,7 @@ export function WeeklyReportView({
data-slot="weekly-report-loading"
className="flex h-64 items-center justify-center"
>
-
+
);
}
diff --git a/src/components/layout/auth-shell.tsx b/src/components/layout/auth-shell.tsx
index e92d5aaaf..87b504585 100644
--- a/src/components/layout/auth-shell.tsx
+++ b/src/components/layout/auth-shell.tsx
@@ -59,7 +59,7 @@ export function AuthShell({ children }: { children: React.ReactNode }) {
if (isLoading) {
return (
-
+
{t("nav.loadingScreen")}
);
@@ -88,7 +88,7 @@ export function AuthShell({ children }: { children: React.ReactNode }) {
if (!isAuthenticated) {
return (
-
+
{t("nav.loadingScreen")}
);
diff --git a/src/components/layout/top-bar.tsx b/src/components/layout/top-bar.tsx
index 9bc4cfe82..0266cac5e 100644
--- a/src/components/layout/top-bar.tsx
+++ b/src/components/layout/top-bar.tsx
@@ -56,7 +56,7 @@ export function TopBar() {
{/* Mobile-only auth section (desktop uses sidebar user section) */}
{isLoading ? (
-
+
) : user ? (
{loading ? (
-
+
) : (
)}
diff --git a/src/components/measurements/measurement-list.tsx b/src/components/measurements/measurement-list.tsx
index c95a6d10b..c62a2e0b9 100644
--- a/src/components/measurements/measurement-list.tsx
+++ b/src/components/measurements/measurement-list.tsx
@@ -338,7 +338,7 @@ export function MeasurementList({ onEdit, onAddFirst }: MeasurementListProps) {
{isLoading ? (
-
+
) : !data?.measurements?.length ? (
// v1.4.15 phase-C5: replaces the bare-text empty rectangle.
@@ -692,7 +692,7 @@ export function MeasurementList({ onEdit, onAddFirst }: MeasurementListProps) {
}
>
{updateMutation.isPending ? (
-
+
) : null}
{t("common.save")}
@@ -722,7 +722,7 @@ export function MeasurementList({ onEdit, onAddFirst }: MeasurementListProps) {
disabled={deleteMutation.isPending}
>
{deleteMutation.isPending ? (
-
+
) : null}
{t("common.delete")}
diff --git a/src/components/medications/DrugLevelChart.tsx b/src/components/medications/DrugLevelChart.tsx
index 4d8964c7d..f15fb8c90 100644
--- a/src/components/medications/DrugLevelChart.tsx
+++ b/src/components/medications/DrugLevelChart.tsx
@@ -365,7 +365,7 @@ function GatedPlaceholder({
// Three distinguishable states:
// - Research Mode is off entirely.
// - Research Mode is on but the acknowledged version is stale.
- // - We don't know yet (server returned null) — still render the
+ // - We don motion-reduce:animate-none't know yet (server returned null) — still render the
// opt-in CTA so the user has a path forward.
const stale = !!knownState?.enabled && !versionsAligned;
return (
diff --git a/src/components/medications/ResearchModeAcknowledgmentDialog.tsx b/src/components/medications/ResearchModeAcknowledgmentDialog.tsx
index 59852c5d9..e1ad4cca8 100644
--- a/src/components/medications/ResearchModeAcknowledgmentDialog.tsx
+++ b/src/components/medications/ResearchModeAcknowledgmentDialog.tsx
@@ -269,7 +269,7 @@ export function ResearchModeAcknowledgmentDialog({
data-slot="research-mode-acknowledge"
>
{mutation.isPending ? (
-
+
) : null}
{t("medications.researchMode.dialog.acknowledgeCta")}
diff --git a/src/components/medications/SchedulingSection.tsx b/src/components/medications/SchedulingSection.tsx
index 28ae712f6..6c3619b61 100644
--- a/src/components/medications/SchedulingSection.tsx
+++ b/src/components/medications/SchedulingSection.tsx
@@ -151,7 +151,7 @@ export function SchedulingSection({
>
{isLoading && (
-
+
{t("medications.scheduling.loading")}
)}
diff --git a/src/components/medications/SideEffectsSection.tsx b/src/components/medications/SideEffectsSection.tsx
index 823b1bae2..c78dd6568 100644
--- a/src/components/medications/SideEffectsSection.tsx
+++ b/src/components/medications/SideEffectsSection.tsx
@@ -256,7 +256,7 @@ export function SideEffectsSection({ medicationId }: SideEffectsSectionProps) {
>
{isLoading && (
-
+
{t("medications.sideEffects.loading")}
)}
@@ -473,7 +473,7 @@ export function SideEffectsSection({ medicationId }: SideEffectsSectionProps) {
diff --git a/src/components/medications/TitrationSection.tsx b/src/components/medications/TitrationSection.tsx
index 1a2207d8c..8e3e22d9d 100644
--- a/src/components/medications/TitrationSection.tsx
+++ b/src/components/medications/TitrationSection.tsx
@@ -119,7 +119,7 @@ export function TitrationSection({ medicationId }: TitrationSectionProps) {
>
{isLoading && (
-
+
{t("medications.titration.loading")}
)}
diff --git a/src/components/medications/glp1-medication-card.tsx b/src/components/medications/glp1-medication-card.tsx
index d81aedefa..8d35b4fcd 100644
--- a/src/components/medications/glp1-medication-card.tsx
+++ b/src/components/medications/glp1-medication-card.tsx
@@ -511,5 +511,5 @@ function siteSuffix(site: InjectionSiteKey): string {
.join("");
}
-/** Re-export so the parent doesn't need to import describeInjectionSite. */
+/** Re-export so the parent doesn motion-reduce:animate-none't need to import describeInjectionSite. */
export { describeInjectionSite };
diff --git a/src/components/medications/intake-history-list.tsx b/src/components/medications/intake-history-list.tsx
index 1f2977384..c40cf9105 100644
--- a/src/components/medications/intake-history-list.tsx
+++ b/src/components/medications/intake-history-list.tsx
@@ -370,7 +370,7 @@ export function IntakeHistoryList({
{isLoading ? (
-
+
) : !data?.events?.length ? (
@@ -631,7 +631,7 @@ export function IntakeHistoryList({
}
>
{updateMutation.isPending ? (
-
+
) : null}
{t("common.save")}
@@ -661,7 +661,7 @@ export function IntakeHistoryList({
disabled={deleteMutation.isPending}
>
{deleteMutation.isPending ? (
-
+
) : null}
{t("common.delete")}
@@ -739,7 +739,7 @@ export function IntakeHistoryList({
diff --git a/src/components/medications/inventory-section.tsx b/src/components/medications/inventory-section.tsx
index c0910ae1c..d73614402 100644
--- a/src/components/medications/inventory-section.tsx
+++ b/src/components/medications/inventory-section.tsx
@@ -1,6 +1,6 @@
"use client";
-import { useMemo, useState } from "react";
+import { useId, useMemo, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { ChevronDown, Loader2, Plus, Trash2 } from "lucide-react";
@@ -75,6 +75,10 @@ export function InventorySection({
const [open, setOpen] = useState(false);
const [addOpen, setAddOpen] = useState(false);
+ // v1.4.27 B7 / L3 — explicit aria-controls between summary and disclosed
+ // panel for parity with the W8e Provenance accordion.
+ const panelId = useId();
+ const pastPanelId = useId();
const [dosesTotal, setDosesTotal] = useState
(
defaultDosesPerUnit ? String(defaultDosesPerUnit) : "",
);
@@ -247,7 +251,11 @@ export function InventorySection({
open={open}
onToggle={(e) => setOpen((e.target as HTMLDetailsElement).open)}
>
-
+
{t("medications.inventory.title")}
{liveItems.length > 0 && (
@@ -262,10 +270,13 @@ export function InventorySection({
}`}
/>
-
+
{isLoading && (
-
+
{t("medications.inventory.loading")}
)}
@@ -357,12 +368,18 @@ export function InventorySection({
{pastItems.length > 0 && (
-
+
{t("medications.inventory.pastTitle", {
count: pastItems.length,
})}
-
+
{pastItems.map((item) => (
-
diff --git a/src/components/medications/medication-card.tsx b/src/components/medications/medication-card.tsx
index b8f8f2045..183806899 100644
--- a/src/components/medications/medication-card.tsx
+++ b/src/components/medications/medication-card.tsx
@@ -567,7 +567,7 @@ export function MedicationCard({ medication, onEdit }: MedicationCardProps) {
disabled={!!intakeLoading}
>
{intakeLoading === "take" ? (
-
+
) : (
)}
@@ -580,7 +580,7 @@ export function MedicationCard({ medication, onEdit }: MedicationCardProps) {
disabled={!!intakeLoading}
>
{intakeLoading === "skip" ? (
-
+
) : (
)}
diff --git a/src/components/medications/medication-form.tsx b/src/components/medications/medication-form.tsx
index a94d98269..6d299a4fa 100644
--- a/src/components/medications/medication-form.tsx
+++ b/src/components/medications/medication-form.tsx
@@ -768,7 +768,7 @@ export function MedicationForm({
-
+
@@ -786,7 +786,7 @@ export function MedicationForm({
maxLength={5}
/>
-
+
@@ -804,7 +804,7 @@ export function MedicationForm({
maxLength={5}
/>
-
+
@@ -816,7 +816,7 @@ export function MedicationForm({
maxLength={50}
/>
-
+
@@ -838,7 +838,7 @@ export function MedicationForm({
{/* Day-of-week selection */}
{s.showAdvanced && (
-
+
@@ -862,7 +862,7 @@ export function MedicationForm({
-
+
@@ -870,7 +870,7 @@ export function MedicationForm({
)}
@@ -1057,7 +1057,7 @@ export function MedicationForm({
disabled={purging}
>
{purging ? (
-
+
) : null}
{t("medications.purgeRecords")}
diff --git a/src/components/medications/phase-config-dialog.tsx b/src/components/medications/phase-config-dialog.tsx
index 66d17d159..577545afb 100644
--- a/src/components/medications/phase-config-dialog.tsx
+++ b/src/components/medications/phase-config-dialog.tsx
@@ -171,7 +171,7 @@ export function PhaseConfigDialog({
{isLoading ? (
-
+
) : (
@@ -253,7 +253,7 @@ export function PhaseConfigDialog({
disabled={saveMutation.isPending}
>
{saveMutation.isPending && (
-
+
)}
{t("common.save")}
diff --git a/src/components/mood/mood-form.tsx b/src/components/mood/mood-form.tsx
index d5f2d4320..958f6b642 100644
--- a/src/components/mood/mood-form.tsx
+++ b/src/components/mood/mood-form.tsx
@@ -271,7 +271,7 @@ export function MoodForm({ onSuccess, onCancel }: MoodFormProps) {
)}
@@ -680,7 +680,7 @@ export function MoodList({ onAddFirst }: MoodListProps = {}) {
disabled={deleteMutation.isPending}
>
{deleteMutation.isPending ? (
-
+
) : null}
{t("common.delete")}
diff --git a/src/components/onboarding/BaselineForm.tsx b/src/components/onboarding/BaselineForm.tsx
index 404f46fed..f1ddbba08 100644
--- a/src/components/onboarding/BaselineForm.tsx
+++ b/src/components/onboarding/BaselineForm.tsx
@@ -240,7 +240,7 @@ export function BaselineForm() {
disabled={saving}
className="min-h-11 min-w-11"
>
- {saving ?
: null}
+ {saving ?
: null}
{t("onboarding.baseline.saveCta")}
diff --git a/src/components/settings/about-section.tsx b/src/components/settings/about-section.tsx
index c21c48e4b..298d6be75 100644
--- a/src/components/settings/about-section.tsx
+++ b/src/components/settings/about-section.tsx
@@ -265,7 +265,7 @@ export function AboutSection() {
{/* Updates — proper heading + last-check timestamp + manual button
that proxies through `/api/version/check-updates`. The v1.4.2
version called `api.github.com` directly from the browser, which
- the production CSP blocked silently — that's why "nothing
+ the production CSP blocked silently — that motion-reduce:animate-none's why "nothing
happened" when the user clicked the button. */}
{version && (
@@ -284,7 +284,7 @@ export function AboutSection() {
disabled={checking}
>
{checking ? (
-
+
) : (
)}
diff --git a/src/components/settings/account-section.tsx b/src/components/settings/account-section.tsx
index 8d8ca5266..7c7698286 100644
--- a/src/components/settings/account-section.tsx
+++ b/src/components/settings/account-section.tsx
@@ -407,7 +407,7 @@ export function AccountSection() {
wide on sm+). Date of birth is the bottom of the
biological-profile block; language is the only UI
preference on this card. They sit together to close the
- "single-cell row" gap that broke the form's grid. */}
+ "single-cell row" gap that broke the form motion-reduce:animate-none's grid. */}
@@ -459,7 +459,7 @@ export function AccountSection() {