Eingabefelder ohne korrekte Beschriftung gehören zu den am häufigsten gemeldeten Barrierefreiheitsfehlern im Web. Im Shopify-Store betrifft das fast jeden Baustein, in den ein Besucher etwas eintippt: das Newsletter-Feld im Footer, das Login- und Registrierungsformular, das Kontaktformular und das Suchfeld. Wenn ein solches Feld nur einen grauen Platzhaltertext zeigt und kein programmatisch verknüpftes Label besitzt, verstößt es gegen WCAG 3.3.2 (Labels or Instructions, Stufe A) und je nach Umsetzung auch gegen WCAG 4.1.2 (Name, Role, Value, Stufe A). Über die harmonisierte Norm EN 301 549 v3.2.1 sind beide Kriterien nach dem Barrierefreiheitsstärkungsgesetz (BFSG) verbindlich, das seit dem 28. Juni 2025 für alle Anbieter oberhalb der Kleinstunternehmen-Schwelle gilt. Dieser Beitrag zeigt, warum ein Platzhalter kein Ersatz für ein Label ist und wie Sie jeden dieser vier Bausteine mit konkretem Liquid- und HTML-Code korrekt beschriften.
Der Platzhaltertext (das placeholder-Attribut) wird gern als Beschriftung missbraucht, weil er optisch im Feld steht und Platz spart. Für die Barrierefreiheit hat er drei Schwächen. Erstens verschwindet er, sobald der Nutzer zu tippen beginnt. Zweitens ist er meist hellgrau und unterschreitet damit häufig die Kontrastanforderung von WCAG 1.4.3. Drittens, und rechtlich am wichtigsten, wird ein placeholder von vielen Screenreadern nicht zuverlässig als zugänglicher Name (Accessible Name) eines Feldes ausgewertet.
Ein Label dagegen bleibt sichtbar, ist programmatisch mit dem Feld verknüpft und wird vom Screenreader vorgelesen, sobald der Fokus auf dem Feld landet. Sie dürfen einen Platzhalter zusätzlich verwenden, etwa für ein Beispielformat, aber niemals als alleinige Beschriftung.
Das BFSG (BGBl. 2021 I S. 2970) setzt die EU-Richtlinie 2019/882 (European Accessibility Act, EAA) in deutsches Recht um. Online-Shops fallen nach § 1 Abs. 3 Nr. 5 BFSG als "Dienstleistungen im elektronischen Geschäftsverkehr" in den Anwendungsbereich. Über Anlage I der EU-Richtlinie 2019/882 verweist das BFSG auf die harmonisierte Norm EN 301 549 v3.2.1, die in ihrem Kapitel 9 die WCAG-2.1-AA-Erfolgskriterien für Webinhalte übernimmt. Die hier behandelten Kriterien 3.3.2, 4.1.2 und 3.3.1 sind allesamt Stufe A und damit Teil des verbindlichen Mindestmaßstabs.
Bei Verstößen sieht § 37 BFSG Bußgelder vor. Nach § 37 Abs. 2 BFSG beträgt der Rahmen für das nicht konforme Anbieten oder Erbringen einer Dienstleistung bis zu 100.000 EUR. AccessifyAI hilft, solche Verstöße zu finden und zu beheben, garantiert aber keine Rechtskonformität oder BFSG-Konformität. Die rechtliche Verantwortung trägt nach § 8 BFSG der Diensteanbieter, also der Händler. Das gilt besonders für eigene Felder, etwa selbst hinzugefügte Checkout-Felder über Checkout Extensibility.
Vom Anwendungsbereich ausgenommen sind nach § 3 Abs. 3 BFSG nur Kleinstunternehmen, also Unternehmen mit weniger als zehn Beschäftigten UND einem Jahresumsatz oder einer Jahresbilanzsumme unter zwei Millionen EUR. Beide Bedingungen müssen kumulativ erfüllt sein.
Drei Erfolgskriterien aus EN 301 549 v3.2.1 bilden zusammen das Pflichtenheft für Eingabefelder. Alle drei sind Stufe A und über das BFSG verbindlich.
| Kriterium |
Inhalt |
Stufe |
| WCAG 3.3.2 Labels or Instructions |
Jedes Eingabefeld braucht eine sichtbare Beschriftung oder Anleitung |
A |
| WCAG 4.1.2 Name, Role, Value |
Name und Rolle des Feldes müssen programmatisch ermittelbar sein |
A |
| WCAG 3.3.1 Error Identification |
Eingabefehler müssen in Textform benannt und beschrieben werden |
A |
WCAG 3.3.2 verlangt die sichtbare Beschriftung. WCAG 4.1.2 verlangt, dass diese Beschriftung auch technisch (über das for-Attribut oder ARIA) mit dem Feld verbunden ist, sodass Hilfsmittel sie auslesen können. Ein Feld kann also ein optisch danebenstehendes Label haben und trotzdem 4.1.2 verletzen, wenn die programmatische Verknüpfung fehlt.
Das robusteste Muster ist immer dasselbe: ein <label>-Element mit einem for-Attribut, dessen Wert exakt der id des zugehörigen Feldes entspricht.
<label for="newsletter-email">E-Mail-Adresse</label>
<input type="email" id="newsletter-email" name="contact[email]">
Diese Verknüpfung hat zwei Vorteile. Der Screenreader liest "E-Mail-Adresse, Eingabefeld" vor, sobald der Fokus auf dem Feld landet. Und ein Klick auf das Label setzt den Fokus ins Feld, was die Klickfläche vergrößert.
Wenn aus Designgründen kein sichtbares Label erwünscht ist, etwa bei einem reinen Suchfeld mit Lupensymbol, gibt es zwei konforme Wege. Der erste ist ein visuell verstecktes, aber für Screenreader vorhandenes Label. Der zweite ist ein aria-label direkt am Feld.
<label for="search-input" class="visually-hidden">Suche</label>
<input type="search" id="search-input" name="q">
Wichtig ist die Wahl der Versteck-Technik. Ein Label per display: none oder visibility: hidden auszublenden ist falsch, denn beide entfernen das Element auch aus dem Zugänglichkeitsbaum, sodass der Screenreader es gar nicht mehr findet. Konform ist die visually-hidden-Technik, die das Element optisch entfernt, aber für Hilfsmittel erhält:
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
margin: -1px;
padding: 0;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
border: 0;
}
Viele Shopify-Themes bringen bereits eine solche Klasse mit, oft unter dem Namen visually-hidden oder sr-only. Prüfen Sie, ob die Klasse in Ihrem Theme existiert, bevor Sie sie neu anlegen.
Das Newsletter-Feld ist der häufigste Fundort eines fehlenden Labels, weil es aus Platzgründen gern nur mit einem Platzhalter wie "Ihre E-Mail-Adresse" auskommt. In den meisten Themes steckt es in einer Section-Datei wie sections/footer.liquid oder in einem Snippet, das {% form 'customer' %} nutzt.
Eine konforme Variante mit verstecktem Label sieht so aus:
{% form 'customer' %}
<input type="hidden" name="contact[tags]" value="newsletter">
<label for="footer-newsletter" class="visually-hidden">
{{ 'general.newsletter_form.email_label' | t }}
</label>
<input
type="email"
name="contact[email]"
id="footer-newsletter"
autocomplete="email"
placeholder="{{ 'general.newsletter_form.placeholder' | t }}"
required>
<button type="submit">{{ 'general.newsletter_form.submit' | t }}</button>
{% endform %}
Das sichtbare Beispiel im Feld bleibt als placeholder erhalten, die eigentliche Beschriftung liefert aber das versteckte <label>. Das autocomplete="email"-Attribut ist über WCAG 1.3.5 (Identify Input Purpose) gute Praxis. Die | t-Filter binden den Text an Ihre Übersetzungsdateien, damit das Label in jeder Storesprache erscheint.
Login- und Registrierungsformulare liegen in templates/customers/login.liquid und templates/customers/register.liquid beziehungsweise den entsprechenden Sections. Hier sind oft mehrere Felder betroffen: E-Mail, Passwort und bei der Registrierung zusätzlich Vor- und Nachname.
Jedes Feld braucht ein eigenes, eindeutig verknüpftes Label. Vergeben Sie sprechende id-Werte, damit for und id nicht versehentlich kollidieren, wenn auf einer Seite mehrere Formulare liegen.
{% form 'customer_login' %}
<label for="login-email">{{ 'customer.login.email' | t }}</label>
<input
type="email"
name="customer[email]"
id="login-email"
autocomplete="email"
required>
<label for="login-password">{{ 'customer.login.password' | t }}</label>
<input
type="password"
name="customer[password]"
id="login-password"
autocomplete="current-password"
required>
<button type="submit">{{ 'customer.login.sign_in' | t }}</button>
{% endform %}
Bei der Passworteingabe ist autocomplete="current-password" (Login) beziehungsweise autocomplete="new-password" (Registrierung) korrekt. Das ist nicht nur Komfort, sondern unterstützt WCAG 3.3.8 (Accessible Authentication), das mit WCAG 2.2 hinzugekommen ist und das Einfügen aus einem Passwortmanager nicht behindern darf.
Das Kontaktformular liegt meist in templates/page.contact.liquid oder einer Section, die {% form 'contact' %} verwendet. Hier kommen zu den Eingabefeldern oft ein mehrzeiliges <textarea> und eine Pflichtfeldkennzeichnung hinzu.
{% form 'contact' %}
<label for="contact-name">{{ 'templates.contact.form.name' | t }}</label>
<input type="text" name="contact[name]" id="contact-name" autocomplete="name">
<label for="contact-email">
{{ 'templates.contact.form.email' | t }}
<span aria-hidden="true">*</span>
</label>
<input
type="email"
name="contact[email]"
id="contact-email"
autocomplete="email"
aria-required="true"
required>
<label for="contact-body">{{ 'templates.contact.form.message' | t }}</label>
<textarea name="contact[body]" id="contact-body" rows="6"></textarea>
<button type="submit">{{ 'templates.contact.form.send' | t }}</button>
{% endform %}
Beachten Sie die Pflichtfeldkennzeichnung. Ein reines Sternchen ist für sehende Nutzer ein verbreitetes Signal, aber ein Screenreader liest "Stern" oder überspringt es. Deshalb steht das * mit aria-hidden="true" da und die Pflicht wird zusätzlich über aria-required="true" und das native required-Attribut programmatisch transportiert.
Das Suchfeld liegt häufig in sections/header.liquid oder einem eigenen Snippet wie snippets/search-form.liquid. Es ist der klassische Fall für ein verstecktes Label, weil das Design oft nur ein Lupensymbol vorsieht.
<form action="{{ routes.search_url }}" method="get" role="search">
<label for="store-search" class="visually-hidden">
{{ 'general.search.search' | t }}
</label>
<input
type="search"
name="q"
id="store-search"
value="{{ search.terms | escape }}">
<button type="submit">
<span class="visually-hidden">{{ 'general.search.submit' | t }}</span>
{% render 'icon-search' %}
</button>
</form>
Hier sind zwei Stellen wichtig. Das Suchfeld bekommt sein verstecktes Label. Und der Absende-Button, der nur ein Icon zeigt, braucht ebenfalls einen zugänglichen Namen, sonst meldet der Screenreader nur "Schaltfläche" ohne Funktion. Das versteckte <span> im Button löst das. Das role="search" am Formular weist das Suchformular als Landmarke aus, ist aber kein Ersatz für das Feld-Label.
Ein korrekt beschriftetes Feld ist die halbe Miete. Die andere Hälfte sind die Fehlermeldungen, wenn die Eingabe nicht passt. WCAG 3.3.1 (Error Identification, Stufe A) verlangt, dass ein automatisch erkannter Eingabefehler in Textform benannt wird und beschreibt, was falsch ist. Eine rein farbliche Markierung (rotes Feld) genügt nicht, denn farbenblinde Nutzer und Screenreader-Nutzer erhalten diese Information nicht.
Verknüpfen Sie die Fehlermeldung programmatisch mit dem Feld über aria-describedby und melden Sie den Fehlerzustand über aria-invalid:
<label for="reg-email">E-Mail-Adresse</label>
<input
type="email"
id="reg-email"
name="customer[email]"
aria-invalid="true"
aria-describedby="reg-email-error">
<p id="reg-email-error">
Bitte geben Sie eine gültige E-Mail-Adresse ein.
</p>
Shopify gibt bei Formularfehlern serverseitig ein form.errors-Objekt aus, aus dem viele Themes bereits eine Fehlerliste rendern. Prüfen Sie, ob diese Liste mit den einzelnen Feldern verknüpft ist oder nur als loser Textblock am Seitenanfang steht. Die programmatische Verknüpfung über aria-describedby ist der Punkt, der in der Praxis oft fehlt.
Fehlende Formularbeschriftungen gehören zu den Fehlern, die automatisierte Werkzeuge zuverlässig erkennen, weil sich ein Feld ohne zugänglichen Namen eindeutig im Code identifizieren lässt. Die folgenden Werkzeuge helfen:
| Werkzeug |
Zweck |
Kosten |
| axe DevTools |
Erkennt Felder ohne zugänglichen Namen |
kostenlose Variante |
| WAVE (WebAIM) |
Markiert fehlende und verwaiste Labels visuell |
kostenlos |
| Lighthouse (Chrome) |
Teilprüfung der Formularzugänglichkeit |
kostenlos |
| NVDA Screenreader |
Manuelle Prüfung, was vorgelesen wird |
kostenlos |
| AccessifyAI |
Shopify-spezifische Überprüfung mit Liquid-Korrekturvorschlägen |
Free-Tier, ab 9,99 USD/Monat |
Ein schneller manueller Test ergänzt jedes Werkzeug: Klicken Sie auf den sichtbaren Text neben einem Feld. Springt der Fokus ins Feld, ist das Label korrekt verknüpft. Passiert nichts, fehlt die Verknüpfung, auch wenn optisch ein Label danebensteht.
Welche dieser Formularprobleme in Ihrem konkreten Store stecken, zeigt nur ein echter Test auf Ihrer aktuellen Theme-Version und im installierten Zustand mit allen aktiven Apps. Newsletter-Pop-ups und Anmeldeformulare stammen häufig aus installierten Apps, nicht aus dem Theme selbst, und müssen separat geprüft werden. Ich habe AccessifyAI entwickelt, weil allgemeine Werkzeuge zwar ein unbeschriftetes Feld melden, aber nicht sagen, in welcher Liquid-Datei oder welchem Section-Snippet die Korrektur erfolgen muss. AccessifyAI ordnet den Befund dem Shopify-Code-Modell zu und gibt statt eines Overlays einen Korrekturvorschlag als Liquid-Diff aus, den Sie vor der Übernahme begutachten können. Wenn Sie zunächst nur sehen wollen, wie es um Ihren Store steht, können Sie ohne Installation den kostenlosen On-Page-Scanner auf accessify.ensomedia.pl nutzen. Die App selbst finden Sie im Shopify App Store.
- Gehen Sie die vier Bausteine durch: Newsletter-Feld, Login und Registrierung, Kontaktformular, Suchfeld.
- Klicken Sie bei jedem Feld auf den sichtbaren Beschriftungstext. Springt der Fokus nicht ins Feld, fehlt die Verknüpfung.
- Suchen Sie in den Section- und Snippet-Dateien nach Eingabefeldern, deren einzige Beschriftung im
placeholder steht, und ergänzen Sie ein verknüpftes <label>.
- Stellen Sie sicher, dass jedes versteckte Label die
visually-hidden-Technik nutzt und nicht display: none.
- Prüfen Sie Icon-Buttons (Suche, Schließen) auf einen zugänglichen Namen.
- Verknüpfen Sie Fehlermeldungen über
aria-describedby und aria-invalid mit dem betroffenen Feld.
- Prüfen Sie Newsletter-Pop-ups und andere App-Formulare separat im installierten Zustand.
Weitere fachliche Vertiefung finden Sie in unserem Beitrag zur Tastaturbedienung im Shopify-Store und in der BFSG-Pflichtcheckliste mit 14 Punkten.
Nein. Ein placeholder verschwindet beim Tippen, hat oft zu wenig Kontrast und wird von vielen Screenreadern nicht als zugänglicher Name ausgewertet. WCAG 3.3.2 (Stufe A) verlangt eine eigene Beschriftung. Ein Platzhalter darf zusätzlich verwendet werden, nie als alleinige Beschriftung.
Ja, aber nur mit der richtigen Technik. Die visually-hidden-Technik (mit position: absolute und clip) entfernt das Label optisch, erhält es aber für Screenreader. display: none und visibility: hidden sind falsch, weil sie das Label auch aus dem Zugänglichkeitsbaum entfernen.
Ein <label for> ist ein sichtbares oder visuell verstecktes Element, das mit dem Feld über die id verknüpft ist und das Anklicken den Fokus setzen lässt. Ein aria-label ist ein unsichtbarer Textwert direkt am Feld. Beide erfüllen WCAG 4.1.2. Das <label> ist robuster und meist vorzuziehen, aria-label ist sinnvoll, wenn kein sichtbares Element infrage kommt.
Ja. Der Standard-Checkout von Shopify wird von Shopify gepflegt. Eigene Felder über Checkout Extensibility fallen jedoch nach § 8 BFSG in Ihre Verantwortung und müssen beschriftet, programmatisch verknüpft und fehlerrückmeldend sein wie jedes andere Feld.
Verlassen Sie sich nicht allein auf ein farbiges Sternchen. Setzen Sie das native required-Attribut sowie aria-required="true" am Feld, damit die Pflicht programmatisch transportiert wird. Ein optisches Sternchen können Sie zusätzlich mit aria-hidden="true" versehen, damit es nicht doppelt vorgelesen wird.
Nein. Kein Werkzeug kann Rechtskonformität garantieren. Automatisierte Werkzeuge, auch AccessifyAI, helfen beim Finden und Beheben von Verstößen. Die rechtliche Verantwortung trägt nach § 8 BFSG der Händler.
Formularfelder ohne korrekte Beschriftung verstoßen gegen WCAG 3.3.2 (Labels or Instructions, A) und oft gegen WCAG 4.1.2 (Name, Role, Value, A), beide über EN 301 549 v3.2.1 und damit über das BFSG verbindlich. Ein Platzhalter ist kein Ersatz für ein Label. Das robuste Grundmuster ist ein <label> mit for, dessen Wert der id des Feldes entspricht, alternativ ein per visually-hidden verstecktes Label oder ein aria-label. In Shopify betrifft das vor allem das Newsletter-Feld im Footer, Login und Registrierung, das Kontaktformular und das Suchfeld, jeweils in den Section- und Snippet-Dateien. Ergänzend verlangt WCAG 3.3.1, dass Fehlermeldungen in Textform und programmatisch mit dem Feld verknüpft erscheinen. Welche dieser Punkte in Ihrem konkreten Store auftreten, ergibt erst eine echte Überprüfung des installierten Stores auf der aktuell ausgespielten Theme-Version, inklusive der von Apps eingespielten Formulare.