From 8719b0ee1e50ec02772f470df050a1241dd4a3ff Mon Sep 17 00:00:00 2001 From: Claude Fable 5 Date: Tue, 21 Jul 2026 14:26:54 +0200 Subject: [PATCH] =?UTF-8?q?#168:=20Formulare=20=E2=80=94=20Fehler-Verdraht?= =?UTF-8?q?ung=20und=20Namensl=C3=BCcken?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Der Field-Baustein verdrahtet Hinweis/Fehler jetzt per aria-describedby und aria-invalid mit dem Eingabefeld (cloneElement auf das einzelne Kind; Fragmente bleiben unangetastet) — Screenreader nennen den Fehler damit auch beim Feld-Fokus. Quota-Typ-Select mit Namen; die leeren Aktions-/Erledigt-Spaltenköpfe in API-Tokens, Feed-Tokens, Sitzungen und der Aufgabenübersicht (NodeView UND Server-Renderpfad) tragen visually-hidden-Beschriftungen. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01AGM8jo3hwoV9wsCVGfy8iq --- apps/api/src/pages/tasks.service.ts | 3 ++- apps/web/src/api-tokens/ApiTokensSection.tsx | 4 +++- apps/web/src/api-tokens/FeedTokensSection.tsx | 4 +++- apps/web/src/components/forms.tsx | 22 ++++++++++++++++--- apps/web/src/editor/nodes/task-overview.tsx | 4 +++- apps/web/src/pages/QuotaManager.tsx | 1 + apps/web/src/pages/SettingsPage.tsx | 4 +++- packages/shared/i18n/de/quotas.json | 3 ++- packages/shared/i18n/de/tasks.json | 1 + packages/shared/i18n/en/quotas.json | 3 ++- packages/shared/i18n/en/tasks.json | 1 + 11 files changed, 40 insertions(+), 10 deletions(-) diff --git a/apps/api/src/pages/tasks.service.ts b/apps/api/src/pages/tasks.service.ts index 3298de6..2a58587 100644 --- a/apps/api/src/pages/tasks.service.ts +++ b/apps/api/src/pages/tasks.service.ts @@ -171,7 +171,8 @@ export class TasksService { } return ( `` + - `` + + `` + + `` + `` + `` + `${rows.join('')}
${escapeHtml(t('colTask'))}${escapeHtml(t('colMentions'))}${escapeHtml(t('colDone'))}${escapeHtml(t('colTask'))}${escapeHtml(t('colMentions'))}${escapeHtml(t('colStart'))}${escapeHtml(t('colDue'))}${escapeHtml(t('colPage'))}
` diff --git a/apps/web/src/api-tokens/ApiTokensSection.tsx b/apps/web/src/api-tokens/ApiTokensSection.tsx index d7895bf..22d0a9d 100644 --- a/apps/web/src/api-tokens/ApiTokensSection.tsx +++ b/apps/web/src/api-tokens/ApiTokensSection.tsx @@ -182,7 +182,9 @@ function TokenList({ tokens }: { tokens: ApiTokenView[] }): React.JSX.Element { {t('list.lastUsed')} {t('list.expires')} {t('list.status')} - + + {t('common:tableActions')} + diff --git a/apps/web/src/api-tokens/FeedTokensSection.tsx b/apps/web/src/api-tokens/FeedTokensSection.tsx index 30256d5..1b2e40d 100644 --- a/apps/web/src/api-tokens/FeedTokensSection.tsx +++ b/apps/web/src/api-tokens/FeedTokensSection.tsx @@ -91,7 +91,9 @@ export function FeedTokensSection(): React.JSX.Element { {t('fields.name')} {t('list.created')} {t('list.lastUsed')} - + + {t('common:tableActions')} + diff --git a/apps/web/src/components/forms.tsx b/apps/web/src/components/forms.tsx index ea9626c..c16a986 100644 --- a/apps/web/src/components/forms.tsx +++ b/apps/web/src/components/forms.tsx @@ -1,3 +1,4 @@ +import { cloneElement, isValidElement, useId } from 'react'; import { useTranslation } from 'react-i18next'; import { ApiError } from '../lib/api'; @@ -20,13 +21,28 @@ export function Field({ children: React.ReactNode; }): React.JSX.Element { const { t } = useTranslation(); + const noteId = useId(); + // Tie the hint/error text to the control itself (#168, WCAG 3.3.1): + // screen readers then repeat it when the field receives focus. Only a + // single element child can be wired; fragments render unchanged. + const wired = + isValidElement(children) && (error || hint) + ? cloneElement(children as React.ReactElement>, { + 'aria-describedby': noteId, + ...(error ? { 'aria-invalid': true } : {}), + }) + : children; return (