WCAG 2.2 AA Assessment Report
The result first: the public appointment booking flow of termin.email meets the Web Content Accessibility Guidelines (WCAG) 2.2 at level AA and therefore also WCAG 2.1 AA. Every success criterion is met or does not apply to the flow. This page documents the assessment criterion by criterion.
This statement is a self-assessment (self-declaration) by the operator based on a criterion-by-criterion review of the source code. It is not an independent certification by an accredited body.
Scope
The assessment covers exclusively the public appointment booking flow of the tenants hosted on termin.email (profile, calendar, booking form, confirmation, management, waiting list and poll). The logged-in administration area (/app, /admin) is not part of this statement.
Standard and method applied
The basis is the success criteria of WCAG 2.1 and WCAG 2.2 at conformance levels A and AA. Each criterion was checked against the actual code (templates, CSS, JavaScript), colour contrasts were calculated using the WCAG formula, and target sizes and focus for WCAG 2.2 were measured in the browser. The findings of the full assessment of 6 July 2026 were additionally cross-checked sceptically in an adversarial multi-agent review.
Summary
- 37 Supported
- 0 Partially supported
- 0 Not supported
- 13 Not applicable
All 50 success criteria of levels A and AA under WCAG 2.1 were assessed.
Assessment per success criterion
| No. | Success criterion | Level | Assessment | Note |
|---|---|---|---|---|
| 1.1.1 | Non-text Content | A | Supported | The only image in the flow is the logo a tenant may upload (kopf__logo in the page header of the booking page and of the embedded window); its alt text names the tenant. There are no further graphics, neither <img> nor <svg>; the surfaces and gradients of the stylesheet are purely decorative and carry no information. Decorative glyphs such as month arrows and weekday abbreviations are correctly exposed via aria-label, aria-hidden or <abbr title>; the honeypot field is aria-hidden. Checked on 6 July 2026, re-checked against the markup of the templates on 10 September 2026. |
| 1.2.1 | Audio-only and Video-only (Prerecorded) | A | Not applicable | No time-based media present — no <video>/<audio>/autoplay in the booking flow. Checked on 6 July 2026. |
| 1.2.2 | Captions (Prerecorded) | A | Not applicable | No synchronised media with audio that would require captions. Checked on 6 July 2026. |
| 1.2.3 | Audio Description or Media Alternative (Prerecorded) | A | Not applicable | No prerecorded video for which audio description or a text alternative would be needed. Checked on 6 July 2026. |
| 1.2.4 | Captions (Live) | AA | Not applicable | No live media; the flow consists of statically rendered pages. Checked on 6 July 2026. |
| 1.2.5 | Audio Description (Prerecorded) | AA | Not applicable | No prerecorded video, hence no content requiring audio description. Checked on 6 July 2026. |
| 1.3.1 | Info and Relationships | A | Supported | Consistently semantic structure: <main>, exactly one <h1> per page, logical <h2>, labels linked via for/id, calendar and poll matrix as real <table> with <caption>/<th scope>, detail views as <dl>; invalid fields carry aria-invalid and aria-describedby. Checked on 6 July 2026. |
| 1.3.2 | Meaningful Sequence | A | Supported | Linear source order header → main → footer; the two-column calendar layout is produced via CSS grid without DOM reordering and collapses responsively to one column. No order/float that distorts reading order. Checked on 6 July 2026. |
| 1.3.3 | Sensory Characteristics | A | Supported | Instructions and controls never rely on shape, size, position or colour alone; every control carries text or an aria-label, and error hints link to the field by text anchor. Checked on 6 July 2026. |
| 1.3.4 | Orientation | AA | Supported | No orientation lock — layout is purely flow/grid based with relative units, no @media (orientation) and no transform:rotate. Checked on 6 July 2026. |
| 1.3.5 | Identify Input Purpose | AA | Supported | The personal-data fields carry the appropriate autocomplete tokens (name, email); the honeypot deliberately uses autocomplete=off. Note and custom questions have no normative HTML autocomplete purpose. Checked on 6 July 2026. |
| 1.4.1 | Use of Color | A | Supported | Information is never conveyed by colour alone: errors combine area + text + role=alert + aria-invalid, the selected day is additionally marked via aria-current and button semantics. In the booking form the optional note carries the visible suffix "(optional)", required custom questions of the host a textual " *". Name, email address and the privacy consent are marked as required through the required attribute but not visibly marked. Checked on 6 July 2026, statement on marking required fields corrected on 6 October 2026. |
| 1.4.2 | Audio Control | A | Not applicable | No automatically playing audio (no <audio>/<video>/autoplay). Checked on 6 July 2026. |
| 1.4.3 | Contrast (Minimum) | AA | Supported | All body and control text recalculated at ≥ 4.5:1 (body text on white 13.91:1, on the body background 12.45:1, primary button 8.60:1, hint text 7.84:1, error text 7.68:1, calendar button 11.20:1); the muted secondary text stays at 6.48:1. Checked on 6 July 2026, values recalculated against the stylesheet on 9 September 2026 after the palette changed. |
| 1.4.4 | Resize Text | AA | Supported | Font sizes throughout in rem, buttons/inputs inherit the font; the viewport meta allows zoom without maximum-scale/user-scalable=no — text scales to 200 % without loss. Checked on 6 July 2026. |
| 1.4.5 | Images of Text | AA | Not applicable | No images of text — all visible text is real HTML text using system fonts. Checked on 6 July 2026. |
| 1.4.10 | Reflow | AA | Supported | Responsive grid with relative units; the calendar collapses to one column at narrow widths, wide elements are wrapped with overflow-x:auto — no forced 2D scrolling at 320 px. Checked on 6 July 2026. |
| 1.4.11 | Non-text Contrast | AA | Supported | The focus indicator reaches 8.39:1 against the page background. The resting border of inputs, selects and textareas uses --farbe-rand-eingabe #6f7874 = 4.55:1 on the white field fill / 4.07:1 on the page background #f6f2e9, i.e. above the 3:1 threshold. Secondary buttons carry a decorative border #cfc8b8 = 1.49:1; they are identified by their label at 12.45:1, so the border does not carry information required to identify the control. Decorative card and table borders are containers, not active UI components, and are out of scope for 1.4.11. Checked on 6 July 2026, values recalculated against the stylesheet on 9 September 2026. |
| 1.4.12 | Text Spacing | AA | Supported | Line-height 1.55 exceeds the requirement; no !important lock on line/letter/word spacing and no fixed height on text containers — larger spacing via a user stylesheet causes no loss of content. Checked on 6 July 2026. |
| 1.4.13 | Content on Hover or Focus | AA | Not applicable | No additional overlapping content appears on hover/focus; hover/focus rules only change the element itself, no tooltip/popover (only native <abbr title>). Checked on 6 July 2026. |
| 2.1.1 | Keyboard | A | Supported | All interactive elements are native, keyboard-operable controls (<button>, <a>, <input>, <select>, <textarea>); no div/span click handler, the arrow-key JS is pure progressive enhancement. Checked on 6 July 2026. |
| 2.1.2 | No Keyboard Trap | A | Supported | No keyboard trap — preventDefault applies only to arrow/Home/End calendar navigation, Tab stays free; no modals or focus loops. Checked on 6 July 2026. |
| 2.1.4 | Character Key Shortcuts | A | Supported | No accesskey attributes; the JS reacts only to non-character navigation keys — no single-character shortcuts. Checked on 6 July 2026. |
| 2.2.1 | Timing Adjustable | A | Supported | Verified in code: no tight UI timeout, no meta-refresh, no timeout script. The only client-side window is the form token with 2 h (BookingFormToken MAX_AGE 7200 s) — a security/replay freshness limit. On expiry the submit action re-renders the page with retained input (name/email/note + custom answers via staleContext) and bookable alternatives — no data loss. The 30-minute slot reservation (bookings:expire-pending) is a real-time reservation preventing double-booking. Both limits meet the WCAG exceptions "real-time event" (a contested slot, as in an auction) and "essential" (token freshness = CSRF/replay protection); extending them would invalidate the core function or the security. Checked on 6 July 2026. |
| 2.2.2 | Pause, Stop, Hide | A | Not applicable | No moving, blinking or auto-updating content > 5 s; the only aria-live region updates only after a user action. Checked on 6 July 2026. |
| 2.3.1 | Three Flashes or Below Threshold | A | Not applicable | No flashing content — no media, no blink keyframes, no JS colour-cycling loop. Checked on 6 July 2026. |
| 2.4.1 | Bypass Blocks | A | Supported | The skip link ("skip to content", .sprung-link → #inhalt) is now also present in the public booking flow (public.twig, before the header); the embed layout has <main id="inhalt"> as its very first element with no preceding block. The landmarks (main/nav) additionally provide an ARIA bypass. Checked on 6 July 2026. |
| 2.4.2 | Page Titled | A | Supported | Every page sets a meaningful, page-specific <title> (e.g. "event type | tenant", "poll title | tenant"). Checked on 6 July 2026. |
| 2.4.3 | Focus Order | A | Supported | DOM order equals a meaningful operation order (error list → fields → submit); no positive tabindex, only tabindex=-1 on the honeypot and a roving tabindex in the calendar. Checked on 6 July 2026. |
| 2.4.4 | Link Purpose (In Context) | A | Supported | Link purposes are clear from text or context: icon arrows with aria-label, slot links showing the time as text, footer and error links in plain text. Checked on 6 July 2026. |
| 2.4.5 | Multiple Ways | AA | Not applicable | The public flow consists solely of steps within a booking/voting process; the 2.4.5 exception applies to process steps. The profile page additionally lists all event types as an entry point. Checked on 6 July 2026. |
| 2.4.6 | Headings and Labels | AA | Supported | One apt <h1> per page and fitting <h2> sections; form labels are concrete and linked via <label for>, poll radios carry meaningful aria-labels. Checked on 6 July 2026. |
| 2.4.7 | Focus Visible | AA | Supported | focus-visible sets a 3px outline in the primary colour (no outline:none); the reduced-motion block leaves the focus ring untouched, ring contrast 8.39:1 against the page background. Checked on 6 July 2026, value recalculated against the stylesheet on 9 September 2026. |
| 2.5.1 | Pointer Gestures | A | Not applicable | No path-based or multipoint gestures — operation solely via single click/tap; no touch/pointermove/drag handler. Checked on 6 July 2026. |
| 2.5.2 | Pointer Cancellation | A | Supported | All actions via native activation of <a>/<button type=submit> (up event, cancel by moving away possible); no custom mousedown/pointerdown activation. Checked on 6 July 2026. |
| 2.5.3 | Label in Name | A | Supported | Visible label text is contained in the accessible name; only the icon-only arrows without visible word text legitimately use aria-label. Checked on 6 July 2026. |
| 2.5.4 | Motion Actuation | A | Not applicable | No function is triggered by device or user motion — no devicemotion/deviceorientation listeners. Checked on 6 July 2026. |
| 3.1.1 | Language of Page | A | Supported | Both base layouts set <html lang> with a valid BCP-47 code (de/en) and fallback "de"; all booking templates inherit from them. Checked on 6 July 2026. |
| 3.1.2 | Language of Parts | AA | Supported | Only partially met until 5 October 2026, because the customer’s texts had no language markup. Since then every text the customer writes carries a lang attribute with the language set for that customer: name and description of the appointment type, questions in the booking form with their answer options, title, description and location of a poll, legal notice, privacy policy and the customer’s own accessibility notes. If a guest views the page in English, a screen reader therefore reads the customer’s German texts in German. No page title mixes platform and customer text, because parts of a title cannot be marked up in HTML. The customer’s name and the names of the hosts are proper names and exempt from the criterion. Precondition: the customer writes in the language that is set. The only foreign-language platform text (“Website” in the hidden spam protection field) sits in aria-hidden and is not announced. Checked on 5 October 2026 against all templates of the flow; a test pins every output of customer text. |
| 3.2.1 | On Focus | A | Supported | No onfocus/autofocus in the flow; focusing triggers no context change, the timezone switch requires an explicit submit. Checked on 6 July 2026. |
| 3.2.2 | On Input | A | Supported | No onchange auto-submits; every state-changing action requires an explicit submit button, the timezone redirect runs on load, not on field input. Checked on 6 July 2026. |
| 3.2.3 | Consistent Navigation | AA | Supported | Header and footer navigation are defined centrally in the shared layout and inherited by all pages — their relative order is necessarily identical. Checked on 6 July 2026. |
| 3.2.4 | Consistent Identification | AA | Supported | Functionally identical components consistently use the same translation keys (name/email/privacy identical across form, waiting list and poll). Checked on 6 July 2026. |
| 3.3.1 | Error Identification | A | Supported | Validation errors appear as a text list in role=alert above the form, each entry links to the field by anchor, affected fields carry aria-invalid and aria-describedby. Checked on 6 July 2026. |
| 3.3.2 | Labels or Instructions | A | Supported | Every field has a linked <label for>; required fields carry the required attribute. Visibly marked are, in the booking form, the optional note ("(optional)") and the required custom questions of the host ("*"), in the poll name and email address ("*"). Name and email address in the booking form carry only the attribute. Poll radios have a meaningful aria-label. Checked on 6 July 2026, statement on marking required fields corrected on 6 October 2026. |
| 3.3.3 | Error Suggestion | AA | Supported | Error messages state the concrete suggestion (e.g. "Please enter a valid email address.") and are linked to the affected field by anchor. Checked on 6 July 2026. |
| 3.3.4 | Error Prevention (Legal, Financial, Data) | AA | Supported | The legally binding booking is reversible (cancel/reschedule) and confirmed (explicit confirmation step with summary); no financial transactions in the flow. Checked on 6 July 2026. |
| 4.1.1 | Parsing | A | Supported | Still normative in WCAG 2.1: Twig auto-escaping, escaped tenant free text, unique IDs per rendered page (error and honeypot IDs also collision-free). Checked on 6 July 2026. |
| 4.1.2 | Name, Role, Value | A | Supported | Every input has an associated <label for>; calendar days are native buttons with aria-label and aria-current, poll radios carry aria-label, invalid fields aria-invalid and aria-describedby, the embed iframe a title. Checked on 6 July 2026. |
| 4.1.3 | Status Messages | AA | Supported | Error summaries use role=alert, success messages role=status, the reloaded slot list sits in an aria-live=polite region. Minor refinement note: one poll error message uses role=status instead of role=alert (still a valid status message). Checked on 6 July 2026. |
Additionally: the new criteria from WCAG 2.2
WCAG 2.2 has been a W3C Recommendation since 5 October 2023. Compared with WCAG 2.1 it adds nine criteria, six of them at levels A and AA. For the German Accessibility Act the decisive standard is currently EN 301 549 in version 3.2.1 and therefore WCAG 2.1. Version 4.1.1, which requires WCAG 2.2 at level AA, was published in September 2026 and becomes binding once the European Commission cites it in the Official Journal. We have therefore assessed the six new criteria already. Criterion 4.1.1 “Parsing” is removed in WCAG 2.2; in the table above it still carries its assessment under WCAG 2.1.
- 4 Supported
- 0 Partially supported
- 0 Not supported
- 2 Not applicable
The 6 new success criteria at levels A and AA of WCAG 2.2 were assessed on 05.10.2026. The three new criteria at level AAA are not part of the scope.
| No. | Success criterion | Level | Assessment | Note |
|---|---|---|---|---|
| 2.4.11 | Focus Not Obscured (Minimum) | AA | Supported | The whole stylesheet contains no element with position: fixed or sticky and no dialogs, banners or overlays that could hide a focused element. The skip link appears at the top left above the content when focused. Checked in the browser on 5 October 2026: focus was set one after the other on every operable element of the booking form at 375 pixels width, none was obscured. In the embedded window a fixed header of the embedding website can cover content; that lies outside this application. |
| 2.5.7 | Dragging Movements | AA | Not applicable | No function requires dragging. Appointments are chosen by tap, click or keyboard. The JavaScript of the flow (buchung.js, embed.js, embed-frame.js) handles only keyboard, load, resize and messages, there is no drag or swipe handling (no drag, pointermove or touchmove), and the templates contain no draggable. Checked on 5 October 2026. |
| 2.5.8 | Target Size (Minimum) | AA | Supported | Measured in the browser on 5 October 2026: calendar days (86 by 44 pixels), time slots, buttons and the time zone selector are at least 24 by 24 pixels. Only the language links, the footer links and individual links such as “Cancel” stay below 24 pixels. They meet the spacing rule: a circle of 24 pixels diameter centred on the target intersects no other target. The checkbox of the privacy consent and the radio buttons of the date poll were 18 pixels and have been 24 by 24 pixels since 5 October 2026. Measured at 375 pixels width (phone): calendar with time slots, booking form, accessibility statement, embedded window, management page, rescheduling and date poll; at 1280 pixels: calendar with time slots, booking form and profile. |
| 3.2.6 | Consistent Help | A | Supported | The flow offers no chat, no hotline and no help area of its own on every page. The way to contact details and help leads through the footer with legal notice, privacy policy and accessibility. It stands in the same order on the booking page and in the embedded window (templates public.twig and embed.twig). The contact address for barriers is on the accessibility statement. Checked on 5 October 2026. |
| 3.3.7 | Redundant Entry | A | Supported | Name and email address are asked for once per process, without a repeat field such as “confirm email”. After a rejected entry, name, email, message and answers are back in the fields. If the chosen appointment is taken, the page offers alternatives and carries the details over. Rescheduling and cancelling ask for nothing again. Checked on 5 October 2026 against the templates for booking, waiting list, poll and management. |
| 3.3.8 | Accessible Authentication (Minimum) | AA | Not applicable | Guests do not sign in. Booking, rescheduling and cancelling work without an account, and management works through a personal link from the confirmation email. The signed-in area of the tenants is not part of the scope of this statement. Checked in the source code, without assessment: the sign-in there uses email and password with autocomplete attributes for password managers, the two-factor code is marked up as a one-time code, and pasting is blocked nowhere. Checked on 5 October 2026. |
Report a barrier
Did you encounter a barrier when using the booking flow, or do you need content in a more accessible form? Please get in touch — we will fix it:
Status of this statement
This conformance statement is based on the criterion-by-criterion assessment of all 50 success criteria carried out on 06.07.2026. It was last re-checked on 06.10.2026; the note on each criterion states when that criterion was checked.