fix(a11y): drop duplicate h2 inside single-section /admin/<slug> routes

Phase 4b's `<SectionFrame>` already paints the canonical h1 + subtitle
for each `/admin/<slug>` route, but every section card kept its v1.4
in-card h2 with the same (or near-same) text. The result was the
section title announcing twice — once as h1, then immediately again as
h2 — both visually redundant and a screen-reader landmark anti-pattern.

Demoting the in-card label to a `<div>` preserves the visual look
(same Tailwind classes) while removing the duplicate heading. The
four sub-cards inside `IntegrationsGroupSection` (umami, glitchtip,
web-push, bug-report) keep their h2 because they live under the
"Integrations" h1 and genuinely deserve a heading level — that is the
non-duplicating case.

Closes H1 from .planning/phase-6-design-findings.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Marc-André BombeckandClaude Opus 4.7 committed 2026-05-09 17:44:05 +02:00
1 parent f0cfd26d96
commit bd24f130ee
10 files changed
+13 -13

No files matched your search

@@ -28,7 +28,7 @@ export function ApiTokenOverviewSection() {
<div className="flex items-center justify-between gap-2">
<div className="flex items-center gap-2">
<Key className="text-primary h-5 w-5" />
<h2 className="text-lg font-semibold">{t("admin.apiTokens")}</h2>
<div className="text-lg font-semibold">{t("admin.apiTokens")}</div>
</div>
<Button
variant="ghost"
+2 -2
View File
@@ -76,9 +76,9 @@ export function BackupsSection() {
<div className="flex items-center justify-between gap-2">
<div className="flex items-center gap-2">
<Database className="text-primary h-5 w-5" />
<h2 className="text-lg font-semibold">
<div className="text-lg font-semibold">
{t("admin.section.backups.title")}
</h2>
</div>
{data && (
<Badge variant="secondary" className="text-xs">
{rows.length}
+2 -2
View File
@@ -68,9 +68,9 @@ export function DangerZoneSection() {
<div className="bg-destructive/5 border-destructive/30 rounded-xl border p-6">
<div className="flex items-center gap-2">
<AlertTriangle className="text-destructive h-5 w-5" />
<h2 className="text-destructive text-lg font-semibold">
<div className="text-destructive text-lg font-semibold">
{t("admin.dangerZone")}
</h2>
</div>
</div>
<div className="mt-4">
<p className="text-sm font-medium">{t("admin.deleteAllData")}</p>
@@ -75,7 +75,7 @@ export function FeedbackInboxSection() {
<div className="bg-card border-border rounded-xl border p-6">
<div className="flex items-center gap-2">
<Inbox className="text-primary h-5 w-5" />
<h2 className="text-lg font-semibold">{t("admin.feedback.title")}</h2>
<div className="text-lg font-semibold">{t("admin.feedback.title")}</div>
</div>
<p className="text-muted-foreground mt-1 text-xs">
{t("admin.feedback.description")}
@@ -13,7 +13,7 @@ export function GeneralSettingsSection() {
<div className="bg-card border-border rounded-xl border p-6">
<div className="flex items-center gap-2">
<Settings className="text-primary h-5 w-5" />
<h2 className="text-lg font-semibold">{t("admin.appSettings")}</h2>
<div className="text-lg font-semibold">{t("admin.appSettings")}</div>
</div>
<div className="mt-4 space-y-4">
<SettingsToggle
@@ -53,7 +53,7 @@ export function LoginOverviewSection() {
<div className="flex items-center justify-between gap-2">
<div className="flex items-center gap-2">
<ScrollText className="text-primary h-5 w-5" />
<h2 className="text-lg font-semibold">{t("admin.loginOverview")}</h2>
<div className="text-lg font-semibold">{t("admin.loginOverview")}</div>
</div>
<Button
variant="ghost"
+2 -2
View File
@@ -116,9 +116,9 @@ export function RemindersSection() {
<div className="bg-card border-border rounded-xl border p-6">
<div className="flex items-center gap-2">
<Clock className="text-primary h-5 w-5" />
<h2 className="text-lg font-semibold">
<div className="text-lg font-semibold">
{t("admin.medicationReminders")}
</h2>
</div>
</div>
<p className="text-muted-foreground mt-1 text-xs">
{t("admin.medicationRemindersDescription")}
+1 -1
View File
@@ -13,7 +13,7 @@ export function ServicesSection() {
<div className="bg-card border-border rounded-xl border p-6">
<div className="flex items-center gap-2">
<Globe className="text-primary h-5 w-5" />
<h2 className="text-lg font-semibold">{t("admin.servicesGlobal")}</h2>
<div className="text-lg font-semibold">{t("admin.servicesGlobal")}</div>
</div>
<p className="text-muted-foreground mt-1 text-xs">
{t("admin.servicesGlobalDescription")}
@@ -28,7 +28,7 @@ export function SystemStatusSection() {
<div className="bg-card border-border rounded-xl border p-6">
<div className="flex items-center gap-2">
<Server className="text-primary h-5 w-5" />
<h2 className="text-lg font-semibold">{t("admin.systemStatus")}</h2>
<div className="text-lg font-semibold">{t("admin.systemStatus")}</div>
</div>
{status ? (
<div className="mt-4 grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
@@ -158,7 +158,7 @@ export function UserManagementSection() {
<div className="flex items-center justify-between gap-2">
<div className="flex items-center gap-2">
<Users className="text-primary h-5 w-5" />
<h2 className="text-lg font-semibold">{t("admin.userManagement")}</h2>
<div className="text-lg font-semibold">{t("admin.userManagement")}</div>
{filteredUsers && (
<Badge variant="secondary" className="text-xs">
{filteredUsers.length}