für Telefon, IBAN und Datum
Eingabefelder, die sich automatisch formatieren, wirken professioneller und reduzieren Benutzerfehler. x-mask erledigt genau das – live, ohne Bibliothek, direkt in Alpine.js. Dieser Artikel erklärt statische und dynamische Masken, Zeichenklassen und Integration in Hyvä-Formulare.
Inhaltsverzeichnis
- 1. Was x-mask wirklich löst
- 2. Installation und Einbindung des Plugins
- 3. Zeichenklassen und Platzhalter verstehen
- 4. Telefonnummern live formatieren
- 5. IBAN-Eingabe mit dynamischer Maske
- 6. Datumsfelder und Zeitangaben maskieren
- 7. Dynamische Masken mit x-mask:dynamic
- 8. Validierung kombiniert mit x-mask
- 9. x-mask im Vergleich zu Alternativen
- 10. Zusammenfassung
- 11. FAQ
1. Was x-mask wirklich löst
Formularfelder sind der kritischste Berührungspunkt zwischen Benutzer und Backend. Ein Telefonnummern-Feld, das rohe Ziffernketten ohne Formatierung entgegennimmt, erzeugt Daten in zwanzig verschiedenen Formaten – je nachdem, wie der Benutzer das Feld ausfüllt. Das Backend muss diese Varianten dann normalisieren, oder Validierungsregeln schlagen bei korrekten Eingaben fehl. x-mask verschiebt die Formatierungslogik an die richtige Stelle: direkt in das Eingabefeld, sichtbar für den Benutzer, ohne serverseitige Bereinigung.
Das Alpine.js x-mask Plugin funktioniert grundlegend anders als reguläre Expression-Validierung. Statt zu prüfen, ob eine fertige Eingabe einem Muster entspricht, formt x-mask die Eingabe während des Tippens aktiv um. Nicht-passende Zeichen werden ignoriert, Trennzeichen wie Leerzeichen, Bindestriche und Schrägstriche werden automatisch eingefügt. Der Benutzer tippt nur Ziffern und Buchstaben – das Feld formatiert sich selbst. Das reduziert Eingabefehler, verbessert die UX und liefert konsistente Daten ins Backend.
Im Hyvä-Ökosystem für Magento 2 ist x-mask besonders wertvoll, weil Alpine.js bereits vorhanden ist und kein zusätzliches JavaScript-Bundle geladen werden muss. Die Integration in Checkout-Formulare, Adressfelder und Zahlungsmasken folgt denselben Mustern wie alle anderen Alpine.js-Direktiven – deklarativ im Template, ohne separaten JavaScript-Code.
2. Installation und Einbindung des Plugins
Das x-mask Plugin ist ein offizielles Alpine.js Plugin und gehört zum Kern-Ökosystem. Es wird nicht automatisch mit Alpine.js geladen, sondern muss separat eingebunden werden. In einem Standard-Hyvä-Setup geschieht das über ein <script>-Tag, das vor dem Alpine.js-Initialisierungsscript geladen wird. Die Reihenfolge ist entscheidend: Das Plugin muss registriert sein, bevor Alpine initialisiert wird, sonst kennt Alpine die x-mask-Direktive nicht und wirft einen stillen Fehler.
In Hyvä-Themes mit dem CSP-Modul muss jedes Inline-Script nach dem CSP-Pattern registriert werden. Das bedeutet: nach jedem <script>-Block kommt der Aufruf $hyvaCsp->registerInlineScript() im PHP-Template. Für externe Scripts aus einem CDN kommt der entsprechende Hash oder die Domain auf die CSP-Whitelist. In Produktionsprojekten empfiehlt sich, das Plugin über npm zu installieren und in das Tailwind-Build zu integrieren, damit es mit dem restlichen Frontend-Bundle ausgeliefert wird.
// Installation via npm (recommended for production)
// npm install @alpinejs/mask
// In your main JS entrypoint (e.g., web/tailwind/main.js):
import Alpine from 'alpinejs'
import mask from '@alpinejs/mask'
Alpine.plugin(mask)
Alpine.start()
// Alternative: CDN via script tag (development / prototyping)
// Load BEFORE alpinejs:
// <script src="https://cdn.jsdelivr.net/npm/@alpinejs/mask@3.x.x/dist/cdn.min.js"></script>
// <script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
// Hyvä CSP pattern in .phtml template:
// After every inline script block:
// <?= $hyvaCsp->registerInlineScript() ?>
In einer Hyvä-Phtml-Datei wird das Plugin typischerweise als Abhängigkeit über das Hyva-Modul-System eingebunden. Wichtig: Das Plugin darf nicht doppelt registriert werden, wenn mehrere Komponenten auf derselben Seite x-mask nutzen. Die Plugin-Registrierung gehört in das globale Layout-XML, nicht in jede einzelne Komponente. Mit Hyva_Theme::module/alpine/plugins.phtml als Einstiegspunkt lassen sich Plugins zentral verwalten.
3. Zeichenklassen und Platzhalter verstehen
x-mask verwendet eine minimalistische Maskensprache mit zwei primären Platzhaltertypen. Das Zeichen 9 in der Maske steht für eine beliebige Ziffer (0–9). Das Zeichen a steht für einen beliebigen Buchstaben (a–z, A–Z). Das Sternchen * akzeptiert beliebige alphanumerische Zeichen. Alle anderen Zeichen in der Maske werden als Literale behandelt und automatisch eingefügt: Leerzeichen, Bindestriche, Punkte, Klammern und Schrägstriche erscheinen automatisch an der richtigen Position, ohne dass der Benutzer sie eintippen muss.
Diese einfache Sprache deckt die meisten Alltagsfälle ab. Eine deutsche Telefonnummer im Format +49 (0) 123 456789 lässt sich als Maske +99 (9) 999 999999 ausdrücken. Ein Datum im deutschen Format TT.MM.JJJJ wird zur Maske 99.99.9999. Für komplexere Anforderungen – etwa IBANs, die je nach Land unterschiedlich lang sind – bietet x-mask die dynamische Variante x-mask:dynamic, bei der eine JavaScript-Funktion die passende Maske in Echtzeit zurückgibt.
Ein wichtiger Unterschied zu anderen Masking-Bibliotheken: x-mask manipuliert den tatsächlichen Wert des Inputs, nicht nur die Anzeige. Der Wert, der an x-model gebunden ist und ans Backend übertragen wird, enthält die Formatierung einschließlich der Trennzeichen. Wer den rohen, nicht formatierten Wert braucht, muss ihn in Alpine-Daten selbst herausrechnen, etwa mit einem x-effect, der die Trennzeichen entfernt.
4. Telefonnummern live formatieren
Telefonnummern sind der klassische Anwendungsfall für Input-Masken. Die Herausforderung liegt in der Variabilität: Vorwahlen, Ländervorwahlen und Rufnummernlängen unterscheiden sich erheblich. Für Formulare mit bekanntem Format – etwa ein deutsches Kundenformular mit nationalen Nummern – eignet sich eine statische Maske hervorragend. Der Benutzer tippt Ziffern, das Feld fügt Klammern und Leerzeichen automatisch ein.
Eine Besonderheit bei Telefonnummern: Das Pluszeichen am Anfang einer internationalen Vorwahl ist kein Platzhalter, sondern ein Literal. Es erscheint automatisch, sobald der Benutzer mit dem Tippen beginnt. Falls das unerwünscht ist – weil manche Benutzer Nummern ohne Ländervorwahl eingeben – eignet sich die dynamische Maske, die je nach erstem Zeichen zwischen nationaler und internationaler Formatierung wechselt.
<!-- Static phone mask: German national format +49 (0) 123 4567890 -->
<div x-data="{ phone: '', phoneRaw: '' }">
<input
type="tel"
x-mask="+99 (9) 999 9999999"
x-model="phone"
x-on:input="phoneRaw = phone.replace(/\D/g, '')"
placeholder="+49 (0) 123 4567890"
class="border rounded px-3 py-2 w-full"
>
<!-- phoneRaw contains only digits for backend submission -->
<input type="hidden" name="phone_raw" :value="phoneRaw">
<p x-show="phone.length > 0 && phone.replace(/\D/g,'').length < 10"
class="text-red-600 text-sm mt-1">
Bitte geben Sie eine vollständige Telefonnummer ein.
</p>
</div>
<!-- Dynamic mask: switches between national and international -->
<div x-data="{ phone: '' }">
<input
type="tel"
x-mask:dynamic="phone.startsWith('+') ? '+99 999 99999999' : '(999) 999-9999'"
x-model="phone"
placeholder="Telefon eingeben..."
class="border rounded px-3 py-2 w-full"
>
</div>
5. IBAN-Eingabe mit dynamischer Maske
IBANs sind eine der technisch anspruchsvollsten Masking-Aufgaben, weil ihre Länge vom Länderkürzel abhängt. Eine deutsche IBAN hat 22 Zeichen, eine schweizerische 21, eine französische 27. Das klassische Gruppenformat mit Leerzeichen alle vier Zeichen – DE89 3704 0044 0532 0130 00 – ist für Benutzer deutlich lesbarer als ein ununterbrochener String. Mit x-mask:dynamic lässt sich eine Funktion hinterlegen, die anhand der ersten beiden eingetippten Buchstaben die korrekte IBAN-Länge ermittelt und die Maske entsprechend anpasst.
Die Implementierung nutzt ein JavaScript-Objekt mit Ländercodes und ihren IBAN-Längen. Die dynamische Funktion erhält den aktuellen Eingabewert als Argument und gibt die passende Maske zurück. Während der Benutzer tippt, wechselt die Maske automatisch, sobald die ersten beiden Zeichen einen bekannten Ländercode bilden. Für unbekannte Ländercodes greift eine Fallback-Maske maximaler Länge.
<div x-data="{
iban: '',
ibanLengths: {
DE: 22, AT: 20, CH: 21, FR: 27, NL: 18, BE: 16,
ES: 24, IT: 27, PL: 28, GB: 22, LU: 20, DK: 18
},
getMask(val) {
// Extract country code from current input (letters only)
const cc = val.replace(/[^a-zA-Z]/g, '').substring(0, 2).toUpperCase()
const len = this.ibanLengths[cc] || 34
// Build grouped mask: groups of 4 separated by spaces
const groups = Math.ceil(len / 4)
const parts = []
let remaining = len
// First group starts with 2 letters + 2 digits
parts.push('aa99')
remaining -= 4
while (remaining > 0) {
parts.push('9'.repeat(Math.min(4, remaining)))
remaining -= 4
}
return parts.join(' ')
}
}">
<input
type="text"
x-mask:dynamic="getMask(iban)"
x-model="iban"
placeholder="DE89 3704 0044 0532 0130 00"
class="border rounded px-3 py-2 w-full font-mono"
autocomplete="off"
>
<p class="text-xs text-slate-500 mt-1">
IBAN wird automatisch formatiert. Nur Buchstaben und Ziffern eingeben.
</p>
</div>
6. Datumsfelder und Zeitangaben maskieren
Datumsfelder mit nativen <input type="date">-Elementen sind in ihrer Darstellung browserspezifisch und bieten kein einheitliches deutsches Datumsformat. Viele Projekte verwenden daher Text-Inputs mit einer Datumsmaske, die das Format TT.MM.JJJJ erzwingt. Das ist UX-technisch vertrauter für deutsche Benutzer und gibt dem Entwickler volle Kontrolle über Validierung und Weiterverarbeitung.
Mit x-mask ist ein Datumsfeld in einer Zeile implementiert: x-mask="99.99.9999". Die Punkte werden automatisch eingefügt, der Benutzer tippt nur Ziffern. Für die Validierung prüft ein x-effect oder ein berechnetes Feld, ob Tag, Monat und Jahr plausible Werte haben. Für Zeitangaben gilt dasselbe Prinzip: x-mask="99:99" formatiert Stunden und Minuten, x-mask="99:99:99" fügt Sekunden hinzu.
<div x-data="{
birthdate: '',
get isValidDate() {
if (this.birthdate.length < 10) return null // not yet complete
const [dd, mm, yyyy] = this.birthdate.split('.').map(Number)
if (dd < 1 || dd > 31 || mm < 1 || mm > 12 || yyyy < 1900) return false
const d = new Date(yyyy, mm - 1, dd)
return d.getFullYear() === yyyy && d.getMonth() === mm - 1 && d.getDate() === dd
},
get isoDate() {
if (!this.isValidDate) return ''
const [dd, mm, yyyy] = this.birthdate.split('.')
return `${yyyy}-${mm}-${dd}` // ISO format for backend
}
}">
<label class="block text-sm font-medium mb-1">Geburtsdatum</label>
<input
type="text"
x-mask="99.99.9999"
x-model="birthdate"
placeholder="TT.MM.JJJJ"
:class="isValidDate === false ? 'border-red-500' : 'border-slate-300'"
class="border rounded px-3 py-2 w-full"
>
<p x-show="isValidDate === false" class="text-red-600 text-sm mt-1">
Ungültiges Datum. Bitte Format TT.MM.JJJJ verwenden.
</p>
<input type="hidden" name="birthdate_iso" :value="isoDate">
</div>
7. Dynamische Masken mit x-mask:dynamic
x-mask:dynamic ist die leistungsfähigste Variante des Plugins. Statt einer statischen Maskenzeichenkette erwartet es einen JavaScript-Ausdruck, der eine Funktion aufruft oder direkt eine Maske zurückgibt. Die Funktion erhält den aktuellen Rohwert des Feldes als Argument und kann so je nach bisheriger Eingabe eine andere Maske zurückgeben. Das ermöglicht Szenarien, die mit statischen Masken nicht lösbar sind: Kreditkartennummern mit verschiedenen Formaten je nach Kartentyp, Postleitzahlen mit unterschiedlicher Länge je nach Land, oder Bestellnummern mit wechselnden Präfixen.
Die Kreditkarte ist das Paradebeispiel: Visa und Mastercard haben 16 Ziffern im Format 9999 9999 9999 9999, American Express hat 15 Ziffern im Format 9999 999999 99999. Die ersten vier Ziffern verraten den Kartentyp. Mit x-mask:dynamic erkennt Alpine den Kartentyp während des Tippens und wendet automatisch das korrekte Format an. Das spart dem Benutzer Verwirrung und dem Entwickler Backend-Normalisierungslogik.
8. Validierung kombiniert mit x-mask
x-mask und Validierung ergänzen sich, ersetzen sich aber nicht gegenseitig. Die Maske stellt sicher, dass das Format stimmt – aber sie prüft nicht, ob der Wert semantisch korrekt ist. Eine maskierte IBAN ist noch kein Beweis, dass das Konto existiert. Ein maskiertes Datum ist noch kein Beweis, dass der Tag im angegebenen Monat existiert (der 31. Februar ist syntaktisch möglich, semantisch aber falsch). Validierungslogik muss also zusätzlich implementiert werden.
Alpine.js bietet dafür x-effect, berechnete Eigenschaften via Getter und @input-Handler. Ein bewährtes Muster: Die Maske formatiert das Feld, ein Getter berechnet isValid aus dem formatierten Wert, und ein Tailwind-Klassen-Binding zeigt den Zustand visuell an. Beim Absenden des Formulars prüft ein @submit.prevent-Handler alle Validierungszustände und verhindert das Absenden, wenn Fehler vorliegen. Die Kombination aus x-mask für Format und Alpine-Daten für Semantik deckt beide Ebenen ab.
9. x-mask im Vergleich zu Alternativen
Bevor x-mask als offizielles Alpine.js Plugin erschien, griff man auf externe Bibliotheken wie IMask, Cleave.js oder inputmask zurück. Diese Bibliotheken sind mächtiger, aber sie bringen eigene Bundle-Größen mit, müssen in Alpine integriert werden und sind nicht deklarativ nutzbar. x-mask ist etwa 1 KB groß, braucht keinen JavaScript-Code außerhalb des Templates und folgt dem Alpine-Prinzip der HTML-First-Interaktivität.
| Kriterium | x-mask (Alpine) | IMask.js | Cleave.js |
|---|---|---|---|
| Bundle-Größe | ~1 KB | ~25 KB | ~12 KB |
| Deklarativ im HTML | Ja (x-mask) | Nein (JS required) | Nein (JS required) |
| Dynamische Masken | Ja (x-mask:dynamic) | Ja (komplexer) | Begrenzt |
| Alpine.js Integration | Nativ | Manuell via x-ref | Manuell via x-ref |
| Regex-Masken | Begrenzt | Vollständig | Begrenzt |
Für die überwältigende Mehrheit der Frontend-Formulare in Hyvä-Projekten ist x-mask die richtige Wahl. Die geringe Größe, die deklarative Syntax und die nahtlose Alpine.js-Integration überwiegen die fehlende Regex-Unterstützung. Für Spezialfälle wie komplexe SWIFT-Codes oder Regex-basierte Freitext-Masken kann IMask via Alpine-Lifecycle-Hook (x-init mit $refs) eingebunden werden, ohne x-mask zu ersetzen.
Mironsoft
Alpine.js · Hyvä Themes · Magento 2 Frontend-Entwicklung
Formulare, die Benutzer nicht frustrieren?
Wir implementieren intelligente Eingabemasken, Echtzeit-Validierung und barrierefreie Formularkomponenten für Hyvä-Magento-Projekte – mit Alpine.js und ohne externe Abhängigkeiten.
Input-Masken
x-mask für Telefon, IBAN, Datum und Kreditkarte – deklarativ und wartbar
Validierungslogik
Echtzeit-Validierung mit Alpine.js Gettern und visuelles Feedback ohne Page-Reload
Checkout-Integration
Formulare im Magento Checkout mit Hyvä optimieren – UX und Conversion verbessern
10. Zusammenfassung
Alpine.js x-mask ist die pragmatischste Lösung für Input-Formatierung im Alpine.js-Ökosystem. Die statische Variante mit x-mask="99.99.9999" deckt Datum, Postleitzahl und einfache Telefonnummern ab. Die dynamische Variante x-mask:dynamic ermöglicht kontextabhängige Masken für IBANs, Kreditkartennummern und länderspezifische Formate. Das Plugin ist ~1 KB groß, vollständig deklarativ und integriert sich nahtlos in bestehende Alpine.js-Komponenten ohne einen einzigen Zeile zusätzlichen JavaScript-Code außerhalb des Templates.
Der wichtigste Grundsatz beim Einsatz von x-mask: Die Maske formatiert, sie validiert nicht. Semantische Validierung – ist das Datum gültig, stimmt die IBAN-Prüfziffer – muss separat implementiert werden. Alpine.js bietet dafür Getter, x-effect und @submit.prevent-Handler. Die Kombination beider Ansätze liefert Formulare, die für Benutzer intuitiv, für Entwickler wartbar und für Backends konsistent sind.
Alpine.js x-mask — Das Wichtigste auf einen Blick
Zeichenklassen
9 = Ziffer, a = Buchstabe, * = alphanumerisch. Alle anderen Zeichen sind Literale und werden automatisch eingefügt.
Dynamische Masken
x-mask:dynamic akzeptiert eine Funktion, die den aktuellen Wert erhält und die passende Maske zurückgibt – für IBANs, Kreditkarten und länderspezifische Formate.
Wert vs. Anzeige
x-mask formatiert den tatsächlichen Input-Wert inklusive Trennzeichen. Für rohe Ziffern einen @input-Handler mit replace(/\D/g, '') nutzen.
Plugin-Reihenfolge
Das Mask-Plugin muss vor Alpine.start() registriert werden. In Hyvä: über das zentrale Plugin-Layout, nicht in jeder Komponente einzeln.