fix(quick-260921-bi2): a11y - Dashboard-Widgets und Portal-Startseite

useButtonType: "Widget hinzufuegen"-, Bearbeiten-Modus- und Katalog-Buttons
(kein Formular in diesen Dateien) erhalten type="button".

noSvgWithoutTitle: Widget-Icons in der Katalog-Kachel (immer neben dem
Widget-Namen) und Buttons mit bestehendem aria-label/title werden dekorativ
(aria-hidden="true"); der bislang unbeschriftete Such-Button im Such-Widget
erhaelt aria-label={t('search.searchButton')} (neuer Schluessel, siehe
vorherige i18n-Festschreibung).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
This commit is contained in:
2026-09-21 09:21:34 +02:00
parent 278aedb201
commit 969fd01f5c
7 changed files with 20 additions and 0 deletions
+2
View File
@@ -81,10 +81,12 @@ export default function DashboardPage() {
<div className="fixed bottom-6 right-6 z-20 flex items-center gap-2"> <div className="fixed bottom-6 right-6 z-20 flex items-center gap-2">
{isEditMode && ( {isEditMode && (
<button <button
type="button"
onClick={() => setCatalogOpen(true)} onClick={() => setCatalogOpen(true)}
className="flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-lg hover:opacity-90 transition-opacity" className="flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-lg hover:opacity-90 transition-opacity"
> >
<svg <svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="16" width="16"
height="16" height="16"
@@ -19,6 +19,7 @@ export function EditModeToggle({ isEditMode, onToggle }: EditModeToggleProps) {
return ( return (
<button <button
type="button"
onClick={onToggle} onClick={onToggle}
className={`inline-flex items-center justify-center rounded-md p-2 shadow-lg transition-colors ${ className={`inline-flex items-center justify-center rounded-md p-2 shadow-lg transition-colors ${
isEditMode isEditMode
@@ -32,6 +33,7 @@ export function EditModeToggle({ isEditMode, onToggle }: EditModeToggleProps) {
{isEditMode ? ( {isEditMode ? (
// Checkmark icon // Checkmark icon
<svg <svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="20" width="20"
height="20" height="20"
@@ -47,6 +49,7 @@ export function EditModeToggle({ isEditMode, onToggle }: EditModeToggleProps) {
) : ( ) : (
// Pencil icon // Pencil icon
<svg <svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="20" width="20"
height="20" height="20"
@@ -76,11 +76,13 @@ export function WidgetCatalogModal({
{t('catalogTitle')} {t('catalogTitle')}
</h2> </h2>
<button <button
type="button"
onClick={onClose} onClick={onClose}
className="rounded-md p-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground" className="rounded-md p-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
aria-label="Close" aria-label="Close"
> >
<svg <svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="20" width="20"
height="20" height="20"
@@ -105,6 +107,7 @@ export function WidgetCatalogModal({
return ( return (
<button <button
key={type} key={type}
type="button"
onClick={() => { onClick={() => {
onAddWidget(type); onAddWidget(type);
onClose(); onClose();
@@ -90,6 +90,7 @@ function PlaceholderWidget({ config }: WidgetProps) {
function ClockIcon({ className }: { className?: string }) { function ClockIcon({ className }: { className?: string }) {
return ( return (
<svg <svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="24" width="24"
height="24" height="24"
@@ -110,6 +111,7 @@ function ClockIcon({ className }: { className?: string }) {
function SearchIcon({ className }: { className?: string }) { function SearchIcon({ className }: { className?: string }) {
return ( return (
<svg <svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="24" width="24"
height="24" height="24"
@@ -130,6 +132,7 @@ function SearchIcon({ className }: { className?: string }) {
function CalendarIcon({ className }: { className?: string }) { function CalendarIcon({ className }: { className?: string }) {
return ( return (
<svg <svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="24" width="24"
height="24" height="24"
@@ -152,6 +155,7 @@ function CalendarIcon({ className }: { className?: string }) {
function NoteIcon({ className }: { className?: string }) { function NoteIcon({ className }: { className?: string }) {
return ( return (
<svg <svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="24" width="24"
height="24" height="24"
@@ -175,6 +179,7 @@ function NoteIcon({ className }: { className?: string }) {
function CalculatorIcon({ className }: { className?: string }) { function CalculatorIcon({ className }: { className?: string }) {
return ( return (
<svg <svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="24" width="24"
height="24" height="24"
@@ -203,6 +208,7 @@ function CalculatorIcon({ className }: { className?: string }) {
function FavoritesIcon({ className }: { className?: string }) { function FavoritesIcon({ className }: { className?: string }) {
return ( return (
<svg <svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="24" width="24"
height="24" height="24"
@@ -222,6 +228,7 @@ function FavoritesIcon({ className }: { className?: string }) {
function StopwatchIcon({ className }: { className?: string }) { function StopwatchIcon({ className }: { className?: string }) {
return ( return (
<svg <svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="24" width="24"
height="24" height="24"
@@ -157,11 +157,13 @@ export function NoteWidget({ instanceId, config }: WidgetProps) {
/> />
)} )}
<button <button
type="button"
onClick={() => setIsEditing((v) => !v)} onClick={() => setIsEditing((v) => !v)}
className={`flex h-6 w-6 shrink-0 items-center justify-center rounded transition-colors hover:bg-muted ${isEditing ? 'text-destructive' : 'text-muted-foreground'}`} className={`flex h-6 w-6 shrink-0 items-center justify-center rounded transition-colors hover:bg-muted ${isEditing ? 'text-destructive' : 'text-muted-foreground'}`}
title={isEditing ? t('note.viewMode') : t('note.editMode')} title={isEditing ? t('note.viewMode') : t('note.editMode')}
> >
<svg <svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="14" width="14"
height="14" height="14"
@@ -116,8 +116,10 @@ export function SearchWidget({ instanceId, config }: WidgetProps) {
type="button" type="button"
className="flex h-8 shrink-0 items-center justify-center rounded bg-primary px-3 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90" className="flex h-8 shrink-0 items-center justify-center rounded bg-primary px-3 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90"
onClick={handleSearch} onClick={handleSearch}
aria-label={t('search.searchButton')}
> >
<svg <svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="16" width="16"
height="16" height="16"
@@ -78,6 +78,7 @@ export function WidgetWrapper({ widget, isEditMode, onRemove }: WidgetWrapperPro
data-no-drag="" data-no-drag=""
> >
<svg <svg
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
width="12" width="12"
height="12" height="12"