瀏覽代碼

Barrierefreiehit verbessert

Leonard Schulz 1 年之前
父節點
當前提交
247cf651d1
共有 2 個文件被更改,包括 420 次插入 和 201 次删除
  1. 166 54
      src/main/resources/static/styles.css
  2. 254 147
      src/main/resources/templates/vergleich.html

+ 166 - 54
src/main/resources/static/styles.css

@@ -1,11 +1,11 @@
 :root {
-  /* Standard Farben (Original) */
-  --rot-hell: #ffd6d6;
-  --gelb-hell: #fff5cc;
-  --gruen-hell: #d6f5d6;
-  --rot: #800000;
-  --gelb: #784801;
-  --gruen: #006633;
+  /* Standard Farben mit verbessertem Kontrast */
+  --rot-hell: #ffe6e6;
+  --gelb-hell: #fff8db;
+  --gruen-hell: #e8f5e8;
+  --rot: #b30000;      /* Verbessert von #800000 für besseren Kontrast */
+  --gelb: #9d5502;     /* Verbessert von #784801 für besseren Kontrast */
+  --gruen: #004d26;    /* Verbessert von #006633 für besseren Kontrast */
   --weiss: #ffffff;
   --schwarz: #000000;
   --grau-hell: #f7f9fa;
@@ -13,58 +13,75 @@
   --berlin-rot: #e20613;
   --diwo-blau: #145c9e;
   --diwo-blau-hell: #cfe8fc;
+  
+  /* Focus Ring Farbe für bessere Sichtbarkeit */
+  --focus-color: #0066cc;
+  --focus-outline: 3px solid var(--focus-color);
 }
 
-/* Ampelfarben (Klassische Ampel) */
-:root[data-color-scheme="ampel"] {
-  --rot-hell: #ffcccc;
-  --gelb-hell: #ffff99;
-  --gruen-hell: #ccffcc;
-  --rot: #920000;
-  --gelb: #8b7700;
-  --gruen: #006b00;
-}
-
-/* Alternative Farben (komplett andere Kombination) */
+/* Alternative Farben mit besserem Kontrast */
 :root[data-color-scheme="alternative"] {
-  --rot-hell: #ffd2f0;
-  --gelb-hell: #ffe1b3;
-  --gruen-hell: #c6f8ff;
-  --rot: #ff0055;
-  --gelb: #ca7a02;
-  --gruen: #00a8ad;
+  --rot-hell: #ffe6f5;
+  --gelb-hell: #ffeccc;
+  --gruen-hell: #e6f9ff;
+  --rot: #cc0044;      /* Verbessert für besseren Kontrast */
+  --gelb: #b8620e;     /* Verbessert für besseren Kontrast */
+  --gruen: #008a8a;    /* Verbessert für besseren Kontrast */
 }
+
 * {
   box-sizing: border-box;
 }
+
 body {
   margin: 0;
   padding: 16px;
   background: var(--grau-hell);
   color: var(--schwarz);
-  font: 14px/1.4 system-ui, sans-serif;
+  font: 14px/1.5 system-ui, sans-serif; /* Verbesserte Zeilenhöhe für bessere Lesbarkeit */
+}
+
+/* Focus-Stile für bessere Keyboard-Navigation */
+*:focus {
+  outline: var(--focus-outline);
+  outline-offset: 2px;
+}
+
+/* Verbesserter Focus für interaktive Elemente */
+button:focus,
+select:focus,
+input:focus,
+.person-row:focus,
+.section-header:focus {
+  outline: var(--focus-outline);
+  outline-offset: 2px;
 }
-h1,
-h2,
-h3 {
+
+h1, h2, h3 {
   margin: 0.2rem 0 0.6rem;
+  line-height: 1.3; /* Verbesserte Zeilenhöhe für Überschriften */
 }
+
 .muted {
   opacity: 0.8;
 }
+
 .row {
   display: flex;
   gap: 0.5rem;
   align-items: center;
 }
+
 .grid-2 {
   display: grid;
   grid-template-columns: 1fr 1fr;
   gap: 12px;
 }
+
 .grid-2 .full {
   grid-column: 1/-1;
 }
+
 textarea,
 input,
 select {
@@ -72,20 +89,43 @@ select {
   padding: 0.5rem;
   background: var(--weiss);
   color: var(--schwarz);
-  border: 1px solid var(--grau-stark);
+  border: 2px solid var(--grau-stark); /* Dickerer Border für bessere Sichtbarkeit */
   border-radius: 4px;
+  font-size: 14px; /* Explizite Schriftgröße für bessere Lesbarkeit */
 }
+
+/* Hover-Effekte für bessere Interaktivität */
+textarea:hover,
+input:hover,
+select:hover {
+  border-color: var(--diwo-blau);
+}
+
 button {
   padding: 0.5rem 0.9rem;
-  border: 1px solid var(--grau-stark);
+  border: 2px solid var(--grau-stark);
   background: #fff;
   cursor: pointer;
+  font-size: 14px;
+  min-height: 44px; 
+  transition: all 0.2s ease;
 }
+
+button:hover {
+  background: var(--grau-hell);
+  border-color: var(--diwo-blau);
+}
+
+button:active {
+  transform: translateY(1px);
+}
+
 .error-box {
   background: var(--rot-hell);
   border-left: 4px solid var(--rot);
   padding: 0.6rem;
   margin: 0.6rem 0;
+  border-radius: 4px;
 }
 
 /* DIWO Header Styles */
@@ -94,6 +134,7 @@ button {
   border: 1px solid var(--grau-stark);
   padding: 12px 16px;
   margin-bottom: 16px;
+  border-radius: 4px;
 }
 
 .header-row-1 {
@@ -212,12 +253,14 @@ button {
   width: 200px;
   padding: 4px 8px;
   font-size: 13px;
+  min-height: 32px; 
 }
 
 .wechseln-btn {
   padding: 4px 8px;
   font-size: 12px;
-  min-width: 30px;
+  min-width: 32px;
+  min-height: 32px;
 }
 
 /* Vergleichslegende */
@@ -243,14 +286,17 @@ button {
 
 .legende-item:has(.plus) {
   background: var(--gruen-hell);
+  border: 1px solid var(--gruen); /
 }
 
 .legende-item:has(.minus) {
   background: var(--rot-hell);
+  border: 1px solid var(--rot);
 }
 
 .legende-item:has(.warn) {
   background: var(--gelb-hell);
+  border: 1px solid var(--gelb);
 }
 
 .legende-title {
@@ -268,6 +314,7 @@ button {
   padding: 2px 4px;
   border-radius: 2px;
   display: inline-block;
+  background: var(--diwo-blau-hell);
 }
 
 table {
@@ -276,17 +323,23 @@ table {
   background: var(--weiss);
   border: 1px solid var(--grau-stark);
   margin: 0.6rem 0 1rem;
+  /* Zusätzlicher Platz für Focus-Outlines innerhalb der Tabelle */
+  border-spacing: 0;
 }
-th,
-td {
+
+th, td {
   border: 1px solid var(--grau-stark);
   padding: 0.5rem;
   vertical-align: top;
+  text-align: left; 
 }
+
 th {
   background: var(--grau-stark);
   position: sticky;
   top: 0;
+  font-weight: bold;
+  color: var(--schwarz); 
 }
 .sym {
   width: 2.5rem;
@@ -294,44 +347,58 @@ th {
   max-width: 2.5rem;
   text-align: center;
   font-weight: 700;
+  font-size: 16px; 
 }
+
 .sym.plus {
   color: var(--gruen);
 }
+
 .sym.minus {
   color: var(--rot);
 }
+
 .sym.warn {
   color: var(--gelb);
 }
+
 .diff-row-added {
   background: var(--gruen-hell);
 }
+
 .diff-row-removed {
   background: var(--rot-hell);
 }
+
 .cell-changed,
 td:has(.cell-changed) {
   background: var(--gelb-hell);
 }
+
+/* Verbessertes Icon für geänderte Zellen */
 .cell-changed:not(.sym)::before {
   content: "! ";
   color: var(--gelb);
   font-weight: bold;
   font-size: 0.9em;
+  margin-right: 4px;
 }
+
 .old {
   display: block;
   font-size: 0.85em;
   margin-top: 0.2rem;
   opacity: 0.9;
+  font-style: italic;
 }
+
 .header {
   display: flex;
   justify-content: space-between;
   align-items: center;
   margin-bottom: 8px;
 }
+
 .legend {
   margin: 0.5rem 0 1rem;
 }
@@ -342,23 +409,59 @@ td:has(.cell-changed) {
   outline-offset: -3px;
 }
 
-/* Clickable table rows */
+/* Clickable table rows mit verbesserter Accessibility */
 .person-row {
   cursor: pointer;
   transition: background-color 0.2s ease;
+  position: relative;
 }
 
 .person-row:hover {
   background-color: var(--diwo-blau-hell);
 }
 
+.person-row:focus {
+  background-color: var(--diwo-blau-hell);
+  /* Verbesserte Focus-Umrahmung für Tabellenzeilen */
+  outline: var(--focus-outline);
+  outline-offset: 1px; /* Reduzierter Offset für bessere Sichtbarkeit in Tabellen */
+  z-index: 1; /* Stelle sicher, dass der Focus-Ring über anderen Elementen liegt */
+}
+
+/* Verbesserte Focus-Styles für alle Tabellenzeilen */
+tr:focus {
+  outline: var(--focus-outline);
+  outline-offset: 1px;
+  position: relative;
+  z-index: 1;
+}
+
+/* Skip to content für Screen Reader */
+.skip-link {
+  position: absolute;
+  top: -40px;
+  left: 6px;
+  background: var(--diwo-blau);
+  color: var(--weiss);
+  padding: 8px;
+  text-decoration: none;
+  border-radius: 4px;
+  z-index: 1000;
+}
+
+.skip-link:focus {
+  top: 6px;
+}
+
 .table-action {
   padding: 0.25rem 0.5rem;
   border: 1px solid var(--grau-stark);
   background: #fff;
   cursor: pointer;
   font-size: 0.85rem;
+  min-height: 32px;
 }
+
 .table-action:hover {
   filter: brightness(0.98);
 }
@@ -374,16 +477,19 @@ td:has(.cell-changed) {
 
 .color-picker-toggle {
   background: none;
-  border: none;
+  border: 2px solid var(--grau-stark);
   cursor: pointer;
   font-size: 16px;
-  padding: 2px;
-  border-radius: 3px;
-  transition: background-color 0.2s;
+  padding: 4px;
+  border-radius: 4px;
+  transition: all 0.2s ease;
+  min-height: 32px;
+  min-width: 32px;
 }
 
 .color-picker-toggle:hover {
   background: var(--grau-stark);
+  border-color: var(--diwo-blau);
 }
 
 .color-picker-popup {
@@ -391,13 +497,13 @@ td:has(.cell-changed) {
   top: 100%;
   right: 0;
   background: var(--weiss);
-  border: 1px solid var(--grau-stark);
+  border: 2px solid var(--grau-stark);
   border-radius: 4px;
-  padding: 8px;
-  box-shadow: 0 2px 8px var(--diwo-blau-hell);
+  padding: 12px;
+  box-shadow: 0 4px 12px rgba(20, 92, 158, 0.2);
   z-index: 1000;
-  font-size: 12px;
-  min-width: 140px;
+  font-size: 13px;
+  min-width: 160px;
 }
 
 .color-picker-popup.hidden {
@@ -406,16 +512,17 @@ td:has(.cell-changed) {
 
 .color-picker-popup label {
   font-weight: bold;
-  margin-bottom: 4px;
+  margin-bottom: 6px;
   display: block;
 }
 
 .color-scheme-select {
   width: 100%;
-  padding: 4px 6px;
-  font-size: 12px;
-  border: 1px solid var(--grau-stark);
-  border-radius: 3px;
+  padding: 6px 8px;
+  font-size: 13px;
+  border: 2px solid var(--grau-stark);
+  border-radius: 4px;
+  min-height: 32px;
 }
 
 /* Responsive Layout für Freibeträge und Kinderbetreuung */
@@ -457,21 +564,24 @@ td:has(.cell-changed) {
 
 .section-header {
   background: var(--grau-stark);
-  padding: 8px 12px;
-  border: 1px solid var(--grau-stark);
+  padding: 12px 16px;
+  border: 2px solid var(--grau-stark);
   border-radius: 4px 4px 0 0;
   cursor: pointer;
   display: flex;
   align-items: center;
   gap: 8px;
   user-select: none;
-  transition: background-color 0.2s ease;
+  transition: all 0.2s ease;
+  min-height: 44px;
 }
 
 .section-header:hover {
   background: #c5c6c7;
+  border-color: var(--diwo-blau);
 }
 
+
 .section-header h3 {
   margin: 0;
   font-size: 16px;
@@ -479,8 +589,9 @@ td:has(.cell-changed) {
 }
 
 .toggle-icon {
-  font-size: 12px;
+  font-size: 14px;
   transition: transform 0.2s ease;
+  font-weight: bold;
 }
 
 .toggle-icon.collapsed {
@@ -488,11 +599,12 @@ td:has(.cell-changed) {
 }
 
 .section-content {
-  border: 1px solid var(--grau-stark);
+  border: 2px solid var(--grau-stark);
   border-top: none;
   border-radius: 0 0 4px 4px;
   transition: max-height 0.3s ease;
-  overflow: hidden;
+  overflow: visible; /* Geändert von hidden zu visible für Focus-Outlines */
+  padding: 4px; /* Zusätzlicher Platz für Focus-Outlines */
 }
 
 .section-content.collapsed {

+ 254 - 147
src/main/resources/templates/vergleich.html

@@ -1,14 +1,19 @@
 <!DOCTYPE html>
 <html lang="de" xmlns:th="http://www.thymeleaf.org">
-<head><meta charset="UTF-8"/><title>DIWO Vergleich – Haushaltsmitglieder/Einkommen</title><link rel="stylesheet" href="/styles.css"/></head>
+<head>
+    <meta charset="UTF-8"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
+    <title>DIWO Vergleich – Haushaltsmitglieder/Einkommen</title>
+    <link rel="stylesheet" href="/styles.css"/>
+</head>
 <body>
 
-  <header class="diwo-header">
+  <header class="diwo-header" role="banner">
     <div class="header-row-1">
       <div class="kunde-logo">
         <div th:if="${activeKunde != null and !#strings.isEmpty(activeKunde)}" class="kunde-logo-container">
           <img th:src="@{'/images/' + ${#strings.toLowerCase(activeKunde)} + '.jpg'}" 
-               th:alt="${activeKunde} + ' Logo'" 
+               th:alt="${activeKunde != null and !#strings.isEmpty(activeKunde) ? activeKunde + ' Logo' : 'Kunden-Logo'}" 
                class="kunde-logo-image"
                th:onerror="'this.style.display=\\'none\\''"/>
         </div>
@@ -17,29 +22,29 @@
         <h1>DIWO</h1>
         <h2>Vergleich</h2>
       </div>
-      <div class="page-title">Haushaltsmitglieder/Einkommen</div>
+      <div class="page-title" role="heading" aria-level="2">Haushaltsmitglieder/Einkommen</div>
     </div>
     
     <div class="header-row-2">
-      <div class="aktenzeichen" th:if="${stammakteRight != null and !#strings.isEmpty(stammakteRight.aktenzeichen)}">
+      <div class="aktenzeichen" th:if="${stammakteRight != null and !#strings.isEmpty(stammakteRight.aktenzeichen)}" role="contentinfo" aria-label="Aktenzeichen">
         <span th:text="${stammakteRight.aktenzeichen}">01/01/119979</span>
       </div>
-      <div class="aktenzeichen" th:if="${(stammakteRight == null or #strings.isEmpty(stammakteRight.aktenzeichen)) and stammvorgangRight != null}">
+      <div class="aktenzeichen" th:if="${(stammakteRight == null or #strings.isEmpty(stammakteRight.aktenzeichen)) and stammvorgangRight != null}" role="contentinfo" aria-label="Aktenzeichen">
         <span th:text="${stammvorgangRight.aktenzeichen}">01/01/119979</span>
       </div>
       
-      <div class="antragsteller-info" th:if="${stammakteRight != null}">
+      <div class="antragsteller-info" th:if="${stammakteRight != null}" role="contentinfo" aria-label="Antragsteller-Informationen">
         <span th:if="${!#strings.isEmpty(stammakteRight.antragstellername)}" th:text="${stammakteRight.antragstellername}">Müller, Tobias</span>
         <span th:if="${!#strings.isEmpty(stammakteRight.antragstellerGeburtsdatum)}"> - <span th:text="${stammakteRight.antragstellerGeburtsdatum}">01.01.2000</span></span>
       </div>
       
-      <div class="location-info" th:if="${stammakteRight != null and !#strings.isEmpty(stammakteRight.anschrift)}">
+      <div class="location-info" th:if="${stammakteRight != null and !#strings.isEmpty(stammakteRight.anschrift)}" role="contentinfo" aria-label="Anschrift">
         <span th:text="${stammakteRight.anschrift}">Unter den Linden 1, 10117 Berlin</span>
       </div>
     </div>
     
     <div class="header-row-3">
-      <div class="vorgang-info" th:if="${stammvorgangRight != null}">
+      <div class="vorgang-info" th:if="${stammvorgangRight != null}" role="contentinfo" aria-label="Vorgang in Bearbeitung">
         <span>Vorgang in der Bearbeitung: </span>
         <span th:if="${!#strings.isEmpty(stammvorgangRight.bemerkung)}" th:text="${stammvorgangRight.bemerkung}">Weiterleistungsantrag</span>
         <span th:if="${#strings.isEmpty(stammvorgangRight.bemerkung)}">-</span>
@@ -49,7 +54,7 @@
       </div>
       
       <div class="zeitraum-controls">
-        <form method="post" action="/vergleich" class="zeitraum-form">
+        <form method="post" action="/vergleich" class="zeitraum-form" role="form" aria-label="Zeitraum wechseln">
           <input type="hidden" name="schemaName" th:value="${schemaName}"/>
           <input type="hidden" name="validate" th:value="${validate}"/>
           <input type="hidden" name="leftJson" th:value="${leftJson}"/>
@@ -58,47 +63,49 @@
           <input type="hidden" name="leftZeitraumId" th:value="${leftZeitraumId}"/>
           <input type="hidden" name="kunde" th:value="${activeKunde}"/>
           
-          <label>Zeitraum:</label>
-          <select name="rightZeitraumId" class="zeitraum-select">
+          <label for="rightZeitraumId">Zeitraum:</label>
+          <select name="rightZeitraumId" id="rightZeitraumId" class="zeitraum-select" aria-describedby="zeitraum-hint">
             <option th:each="z : ${zeitraeumeRight}" th:value="${z.id}"
                     th:selected="${z.id == rightZeitraumId}"
                     th:text="${z.beginn} + ' - ' + ${z.ende}"></option>
           </select>
-          <button type="submit" class="wechseln-btn">▼</button>
+          <button type="submit" class="wechseln-btn" aria-label="Zeitraum wechseln">▼</button>
         </form>
       </div>
     </div>
   </header>
 
   <!-- Vergleichslegende -->
-  <section class="vergleich-legende">
-    <div class="legende-title">
+  <section class="vergleich-legende" role="complementary" aria-labelledby="legende-title">
+    <div class="legende-title" id="legende-title">
       <strong>Legende:</strong>
     </div>
     <div class="legende-item">
-      <span class="sym plus">+</span> <span>hinzugefügt (<span id="addedColorName">grün</span>)</span>
+      <span class="sym plus" aria-label="Hinzugefügt">+</span> 
+      <span>hinzugefügt (<span id="addedColorName">grün</span>)</span>
     </div>
-    <div class="legende-spacer">|</div>
+    <div class="legende-spacer" aria-hidden="true">|</div>
     <div class="legende-item">
-      <span class="sym minus">−</span> <span>gelöscht (<span id="removedColorName">rot</span>)</span>
+      <span class="sym minus" aria-label="Gelöscht">−</span> 
+      <span>gelöscht (<span id="removedColorName">rot</span>)</span>
     </div>
-    <div class="legende-spacer">|</div>
+    <div class="legende-spacer" aria-hidden="true">|</div>
     <div class="legende-item">
-      <span class="sym warn">!</span> <span>geändert (<span id="changedColorName">gelb</span>)</span>
+      <span class="sym warn" aria-label="Geändert">!</span> 
+      <span>geändert (<span id="changedColorName">gelb</span>)</span>
     </div>
-    <div class="legende-spacer">|</div>
+    <div class="legende-spacer" aria-hidden="true">|</div>
     <div class="legende-item">
       <span class="selected-indicator-text">ausgewählte Person (blauer Rahmen)</span>
     </div>
     
     <!-- Farbwahl -->
     <div class="color-picker-container">
-      <button class="color-picker-toggle" onclick="toggleColorPicker()">⚙️</button>
-      <div class="color-picker-popup hidden" id="colorPickerPopup">
-        <label>Farbwahl:</label>
-        <select id="colorSchemeSelect" class="color-scheme-select" onchange="changeColorScheme(this.value)">
+      <button class="color-picker-toggle" onclick="toggleColorPicker()" aria-label="Farbschema-Einstellungen öffnen" aria-expanded="false" aria-controls="colorPickerPopup">⚙️</button>
+      <div class="color-picker-popup hidden" id="colorPickerPopup" role="dialog" aria-labelledby="color-picker-label">
+        <label id="color-picker-label" for="colorSchemeSelect">Farbwahl:</label>
+        <select id="colorSchemeSelect" class="color-scheme-select" onchange="changeColorScheme(this.value)" aria-describedby="color-scheme-hint">
           <option value="standard">Standard</option>
-          <option value="ampel">Ampelfarben (neon)</option>
           <option value="alternative">Pastell</option>
         </select>
       </div>
@@ -106,144 +113,199 @@
   </section>
 
   <!-- Haushaltsmitglieder - klickbare Zeilen -->
-  <div class="section-container">
-    <div class="section-header" onclick="toggleSection('haushaltsmitglieder')">
-      <span class="toggle-icon" id="haushaltsmitglieder-icon">▼</span>
-      <h3>Haushaltsmitglieder</h3>
-    </div>
-    <div class="section-content" id="haushaltsmitglieder-content">
-      <table>
-        <thead>
-          <tr>
-            <th>Δ</th><th>Name</th><th>Geburtsdatum</th><th>Familienstand</th>
-            <th>Geschlecht</th><th>Verwandtschaft</th><th>Sozialstellung</th><th>Kind</th>
-          </tr>
-        </thead>
-        <tbody>
-          <tr th:each="r : ${mitgliederRows}"
-              th:class="'person-row ' + ${r.rowClass} + ((${r.personId} == ${personId}) ? ' person-selected' : '')"
-              th:data-person-id="${r.personId}">
-            <td class="sym" th:classappend="' ' + ${r.symbolClass}" th:text="${r.symbol}"></td>
-            <td th:utext="${r.nameHtml}"></td>
-            <td th:utext="${r.geburtsdatumHtml}"></td>
-            <td th:utext="${r.familienstandHtml}"></td>
-            <td th:utext="${r.geschlechtHtml}"></td>
-            <td th:utext="${r.verwandtschaftHtml}"></td>
-            <td th:utext="${r.sozialstellungHtml}"></td>
-            <td th:utext="${r.istKindHtml}"></td>
-          </tr>
-        </tbody>
-      </table>
+  <main role="main">
+    <div class="section-container">
+      <div class="section-header" onclick="toggleSection('haushaltsmitglieder')" role="button" tabindex="0" 
+           onkeypress="handleKeyPress(event, 'haushaltsmitglieder')" aria-expanded="true" aria-controls="haushaltsmitglieder-content">
+        <span class="toggle-icon" id="haushaltsmitglieder-icon" aria-hidden="true">▼</span>
+        <h3>Haushaltsmitglieder</h3>
+      </div>
+      <div class="section-content" id="haushaltsmitglieder-content">
+        <table role="table" aria-labelledby="haushaltsmitglieder-caption">
+          <thead>
+            <tr>
+              <th scope="col">Änderung</th>
+              <th scope="col">Name</th>
+              <th scope="col">Geburtsdatum</th>
+              <th scope="col">Familienstand</th>
+              <th scope="col">Geschlecht</th>
+              <th scope="col">Verwandtschaft</th>
+              <th scope="col">Sozialstellung</th>
+              <th scope="col">Kind</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr th:each="r : ${mitgliederRows}"
+                th:class="'person-row ' + ${r.rowClass} + ((${r.personId} == ${personId}) ? ' person-selected' : '')"
+                th:data-person-id="${r.personId}"
+                role="button"
+                tabindex="0"
+                th:aria-label="'Person auswählen: ' + ${r.nameHtml}"
+                onkeypress="handleRowKeyPress(event, this)">
+              <td class="sym" th:classappend="' ' + ${r.symbolClass}" th:text="${r.symbol}" 
+                  th:aria-label="${r.symbol == '+' ? 'Hinzugefügt' : (r.symbol == '-' ? 'Gelöscht' : (r.symbol == '!' ? 'Geändert' : 'Unverändert'))}"></td>
+              <td th:utext="${r.nameHtml}"></td>
+              <td th:utext="${r.geburtsdatumHtml}"></td>
+              <td th:utext="${r.familienstandHtml}"></td>
+              <td th:utext="${r.geschlechtHtml}"></td>
+              <td th:utext="${r.verwandtschaftHtml}"></td>
+              <td th:utext="${r.sozialstellungHtml}"></td>
+              <td th:utext="${r.istKindHtml}"></td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
     </div>
-  </div>
 
-  <!-- Hidden form for person selection -->
-  <form id="personForm" method="post" action="/vergleich" style="display: none;">
-    <input type="hidden" name="leftJson" th:value="${leftJson}"/>
-    <input type="hidden" name="rightJson" th:value="${rightJson}"/>
-    <input type="hidden" name="validate" th:value="${validate}"/>
-    <input type="hidden" name="schemaName" th:value="${schemaName}"/>
-    <input type="hidden" name="leftVorgangId" th:value="${leftVorgangId}"/>
-    <input type="hidden" name="leftZeitraumId" th:value="${leftZeitraumId}"/>
-    <input type="hidden" name="rightZeitraumId" th:value="${rightZeitraumId}"/>
-    <input type="hidden" name="kunde" th:value="${activeKunde}"/>
-    <input type="hidden" name="personId" id="selectedPersonId"/>
-  </form>
+    <!-- Hidden form for person selection -->
+    <form id="personForm" method="post" action="/vergleich" style="display: none;" aria-hidden="true">
+      <input type="hidden" name="leftJson" th:value="${leftJson}"/>
+      <input type="hidden" name="rightJson" th:value="${rightJson}"/>
+      <input type="hidden" name="validate" th:value="${validate}"/>
+      <input type="hidden" name="schemaName" th:value="${schemaName}"/>
+      <input type="hidden" name="leftVorgangId" th:value="${leftVorgangId}"/>
+      <input type="hidden" name="leftZeitraumId" th:value="${leftZeitraumId}"/>
+      <input type="hidden" name="rightZeitraumId" th:value="${rightZeitraumId}"/>
+      <input type="hidden" name="kunde" th:value="${activeKunde}"/>
+      <input type="hidden" name="personId" id="selectedPersonId"/>
+    </form>
 
-  <h2>Details zu <span th:if="${personLabel != null and !#strings.isEmpty(personLabel) and personLabel != '(keine Person)'}"><span th:text="${personLabel}"> - </span></span></h2>
-  <!-- Einkommen -->
-  <div class="section-container">
-    <div class="section-header" onclick="toggleSection('einkommen')">
-      <span class="toggle-icon" id="einkommen-icon">▼</span>
-      <h3>Einkommen</h3>
-    </div>
-    <div class="section-content" id="einkommen-content">
-      <table>
-        <thead><tr><th>Δ</th><th>Art</th><th>Frequenz</th><th>Betrag</th><th>Zuschlag</th><th>Abschlag</th><th>Werbungskosten</th><th>Berechnungsart</th></tr></thead>
-        <tbody>
-          <tr th:each="z : ${einkommenZeilen}" th:class="${z.rowClass}">
-            <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"></td>
-            <td th:utext="${z.artHtml}"></td>
-            <td th:utext="${z.frequenzHtml}"></td>
-            <td th:utext="${z.betragHtml}"></td>
-            <td th:utext="${z.zuschlagHtml}"></td>
-            <td th:utext="${z.abschlagHtml}"></td>
-            <td th:utext="${z.werbungskostenHtml}"></td>
-            <td th:utext="${z.berechnungsartHtml}"></td>
-          </tr>
-        </tbody>
-      </table>
+    <h2>Details zu <span th:if="${personLabel != null and !#strings.isEmpty(personLabel) and personLabel != '(keine Person)'}"><span th:text="${personLabel}"> - </span></span></h2>
+    
+    <!-- Einkommen -->
+    <div class="section-container">
+      <div class="section-header" onclick="toggleSection('einkommen')" role="button" tabindex="0" 
+           onkeypress="handleKeyPress(event, 'einkommen')" aria-expanded="true" aria-controls="einkommen-content">
+        <span class="toggle-icon" id="einkommen-icon" aria-hidden="true">▼</span>
+        <h3>Einkommen</h3>
+      </div>
+      <div class="section-content" id="einkommen-content">
+        <table role="table" aria-labelledby="einkommen-caption">
+          <thead>
+            <tr>
+              <th scope="col">Änderung</th>
+              <th scope="col">Art</th>
+              <th scope="col">Frequenz</th>
+              <th scope="col">Betrag</th>
+              <th scope="col">Zuschlag</th>
+              <th scope="col">Abschlag</th>
+              <th scope="col">Werbungskosten</th>
+              <th scope="col">Berechnungsart</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr th:each="z : ${einkommenZeilen}" th:class="${z.rowClass}">
+              <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"
+                  th:aria-label="${z.symbol == '+' ? 'Hinzugefügt' : (z.symbol == '-' ? 'Gelöscht' : (z.symbol == '!' ? 'Geändert' : 'Unverändert'))}"></td>
+              <td th:utext="${z.artHtml}"></td>
+              <td th:utext="${z.frequenzHtml}"></td>
+              <td th:utext="${z.betragHtml}"></td>
+              <td th:utext="${z.zuschlagHtml}"></td>
+              <td th:utext="${z.abschlagHtml}"></td>
+              <td th:utext="${z.werbungskostenHtml}"></td>
+              <td th:utext="${z.berechnungsartHtml}"></td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
     </div>
-  </div>
 
-  <div class="freibetrag-container">
-    <div class="freibetrag-section">
-      <div class="section-container">
-        <div class="section-header" onclick="toggleSection('pauschale')">
-          <span class="toggle-icon" id="pauschale-icon">▼</span>
-          <h3>Pauschale Abzüge</h3>
+    <div class="freibetrag-container">
+      <div class="freibetrag-section">
+        <div class="section-container">
+          <div class="section-header" onclick="toggleSection('pauschale')" role="button" tabindex="0" 
+               onkeypress="handleKeyPress(event, 'pauschale')" aria-expanded="true" aria-controls="pauschale-content">
+            <span class="toggle-icon" id="pauschale-icon" aria-hidden="true">▼</span>
+            <h3>Pauschale Abzüge</h3>
+          </div>
+          <div class="section-content" id="pauschale-content">
+            <table role="table" aria-labelledby="pauschale-caption">
+              <thead>
+                <tr>
+                  <th scope="col">Änderung</th>
+                  <th scope="col">Art</th>
+                  <th scope="col">Frequenz</th>
+                  <th scope="col">Betrag</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr th:each="z : ${freibetragZeilen}" th:if="${z.typ == 'P'}" th:class="${z.rowClass}">
+                  <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"
+                      th:aria-label="${z.symbol == '+' ? 'Hinzugefügt' : (z.symbol == '-' ? 'Gelöscht' : (z.symbol == '!' ? 'Geändert' : 'Unverändert'))}"></td>
+                  <td th:utext="${z.artHtml}"></td>
+                  <td th:utext="${z.frequenzHtml}"></td>
+                  <td th:utext="${z.betragHtml}"></td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
         </div>
-        <div class="section-content" id="pauschale-content">
-          <table>
-            <thead><tr><th>Δ</th><th>Art</th><th>Frequenz</th><th>Betrag</th></tr></thead>
-            <tbody>
-              <tr th:each="z : ${freibetragZeilen}" th:if="${z.typ == 'P'}" th:class="${z.rowClass}">
-                <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"></td>
-                <td th:utext="${z.artHtml}"></td>
-                <td th:utext="${z.frequenzHtml}"></td>
-                <td th:utext="${z.betragHtml}"></td>
-              </tr>
-            </tbody>
-          </table>
+      </div>
+      
+      <div class="freibetrag-section">
+        <div class="section-container">
+          <div class="section-header" onclick="toggleSection('freibetraege')" role="button" tabindex="0" 
+               onkeypress="handleKeyPress(event, 'freibetraege')" aria-expanded="true" aria-controls="freibetraege-content">
+            <span class="toggle-icon" id="freibetraege-icon" aria-hidden="true">▼</span>
+            <h3>Freibeträge</h3>
+          </div>
+          <div class="section-content" id="freibetraege-content">
+            <table role="table" aria-labelledby="freibetraege-caption">
+              <thead>
+                <tr>
+                  <th scope="col">Änderung</th>
+                  <th scope="col">Art</th>
+                  <th scope="col">Frequenz</th>
+                  <th scope="col">Betrag</th>
+                  <th scope="col">Frist</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr th:each="z : ${freibetragZeilen}" th:if="${z.typ == 'F'}" th:class="${z.rowClass}">
+                  <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"
+                      th:aria-label="${z.symbol == '+' ? 'Hinzugefügt' : (z.symbol == '-' ? 'Gelöscht' : (z.symbol == '!' ? 'Geändert' : 'Unverändert'))}"></td>
+                  <td th:utext="${z.artHtml}"></td>
+                  <td th:utext="${z.frequenzHtml}"></td>
+                  <td th:utext="${z.betragHtml}"></td>
+                  <td th:utext="${z.fristHtml}"></td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
         </div>
       </div>
     </div>
-    
-    <div class="freibetrag-section">
+
+    <!-- Kinderbetreuungskosten -->
+    <div class="kinderbetreuung-container">
       <div class="section-container">
-        <div class="section-header" onclick="toggleSection('freibetraege')">
-          <span class="toggle-icon" id="freibetraege-icon">▼</span>
-          <h3>Freibeträge</h3>
+        <div class="section-header" onclick="toggleSection('kinderbetreuung')" role="button" tabindex="0" 
+             onkeypress="handleKeyPress(event, 'kinderbetreuung')" aria-expanded="true" aria-controls="kinderbetreuung-content">
+          <span class="toggle-icon" id="kinderbetreuung-icon" aria-hidden="true">▼</span>
+          <h3>Kinderbetreuungskosten</h3>
         </div>
-        <div class="section-content" id="freibetraege-content">
-          <table>
-            <thead><tr><th>Δ</th><th>Art</th><th>Frequenz</th><th>Betrag</th><th>Frist</th></tr></thead>
+        <div class="section-content" id="kinderbetreuung-content">
+          <table role="table" aria-labelledby="kinderbetreuung-caption">
+            <thead>
+              <tr>
+                <th scope="col">Änderung</th>
+                <th scope="col">Kind</th>
+                <th scope="col">Betrag</th>
+              </tr>
+            </thead>
             <tbody>
-              <tr th:each="z : ${freibetragZeilen}" th:if="${z.typ == 'F'}" th:class="${z.rowClass}">
-                <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"></td>
-                <td th:utext="${z.artHtml}"></td>
-                <td th:utext="${z.frequenzHtml}"></td>
+              <tr th:each="z : ${kitaZeilen}" th:class="${z.rowClass}">
+                <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"
+                    th:aria-label="${z.symbol == '+' ? 'Hinzugefügt' : (z.symbol == '-' ? 'Gelöscht' : (z.symbol == '!' ? 'Geändert' : 'Unverändert'))}"></td>
+                <td th:utext="${z.kindNameHtml}"></td>
                 <td th:utext="${z.betragHtml}"></td>
-                <td th:utext="${z.fristHtml}"></td>
               </tr>
             </tbody>
           </table>
         </div>
       </div>
     </div>
-  </div>
-
-  <!-- Kinderbetreuungskosten -->
-  <div class="kinderbetreuung-container">
-    <div class="section-container">
-      <div class="section-header" onclick="toggleSection('kinderbetreuung')">
-        <span class="toggle-icon" id="kinderbetreuung-icon">▼</span>
-        <h3>Kinderbetreuungskosten</h3>
-      </div>
-      <div class="section-content" id="kinderbetreuung-content">
-        <table>
-          <thead><tr><th>Δ</th><th>Kind</th><th>Betrag</th></tr></thead>
-          <tbody>
-            <tr th:each="z : ${kitaZeilen}" th:class="${z.rowClass}">
-              <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"></td>
-              <td th:utext="${z.kindNameHtml}"></td>
-              <td th:utext="${z.betragHtml}"></td>
-            </tr>
-          </tbody>
-        </table>
-      </div>
-    </div>
-  </div>
+  </main>
 
   <script>
     // Cookie Verwaltung
@@ -264,16 +326,40 @@
       return null;
     }
 
+    // Keyboard support for toggles
+    function handleKeyPress(event, sectionId) {
+      if (event.key === 'Enter' || event.key === ' ') {
+        event.preventDefault();
+        toggleSection(sectionId);
+      }
+    }
+
+    // Keyboard support for table rows
+    function handleRowKeyPress(event, row) {
+      if (event.key === 'Enter' || event.key === ' ') {
+        event.preventDefault();
+        row.click();
+      }
+    }
+
     // Farbwahl-Funktionen
     function toggleColorPicker() {
       const popup = document.getElementById('colorPickerPopup');
+      const button = document.querySelector('.color-picker-toggle');
+      const isHidden = popup.classList.contains('hidden');
+      
       popup.classList.toggle('hidden');
+      button.setAttribute('aria-expanded', !isHidden);
+      
+      if (!isHidden) {
+        // Focus the select when opening
+        document.getElementById('colorSchemeSelect').focus();
+      }
     }
 
     function updateColorNames(scheme) {
       const colorNames = {
         'standard': { added: 'grün', removed: 'rot', changed: 'gelb' },
-        'ampel': { added: 'grün', removed: 'rot', changed: 'gelb' },
         'alternative': { added: 'blau', removed: 'pink', changed: 'orange' }
       };
       
@@ -303,7 +389,9 @@
       
       // Schließe das Popup nach der Auswahl
       const popup = document.getElementById('colorPickerPopup');
+      const button = document.querySelector('.color-picker-toggle');
       popup.classList.add('hidden');
+      button.setAttribute('aria-expanded', 'false');
     }
 
     function initializeColorScheme() {
@@ -326,13 +414,28 @@
       }
     }
 
+    // Escape key support for popup
+    document.addEventListener('keydown', function(event) {
+      if (event.key === 'Escape') {
+        const popup = document.getElementById('colorPickerPopup');
+        const button = document.querySelector('.color-picker-toggle');
+        if (!popup.classList.contains('hidden')) {
+          popup.classList.add('hidden');
+          button.setAttribute('aria-expanded', 'false');
+          button.focus();
+        }
+      }
+    });
+
     // Schließe das Popup wenn außerhalb geklickt wird
     document.addEventListener('click', function(event) {
       const container = document.querySelector('.color-picker-container');
       const popup = document.getElementById('colorPickerPopup');
+      const button = document.querySelector('.color-picker-toggle');
       
       if (container && !container.contains(event.target)) {
         popup.classList.add('hidden');
+        button.setAttribute('aria-expanded', 'false');
       }
     });
 
@@ -340,15 +443,18 @@
     function toggleSection(sectionId) {
       const content = document.getElementById(sectionId + '-content');
       const icon = document.getElementById(sectionId + '-icon');
+      const header = content.parentElement.querySelector('.section-header');
       
       if (content.classList.contains('collapsed')) {
         content.classList.remove('collapsed');
         icon.classList.remove('collapsed');
         icon.textContent = '▼';
+        header.setAttribute('aria-expanded', 'true');
       } else {
         content.classList.add('collapsed');
         icon.classList.add('collapsed');
         icon.textContent = '▶';
+        header.setAttribute('aria-expanded', 'false');
       }
     }
 
@@ -369,4 +475,5 @@
       });
     });
   </script>
-</body></html>
+</body>
+</html>