Input Mask
Free8 built-in mask presets (phone, credit card, date, time, SSN, ZIP, IP) plus custom patterns, with 3 visual variants.
</>sv-input-mask maskformatinput
import { SvInputMaskComponent } from '@styloviz/forms';Demo & usage
Basic usage
Usage example — open the gallery for the full interactive demo.
An interactive demo for this component lives in the gallery.
Open live demoMore examples
Custom pattern
Use customMask for formats outside the presets.
example.htmlAngular
1 lines
<sv-input-mask customMask="AA-0000" label="Asset ID" [(ngModel)]="assetId" />
When to use
- Enforcing a fixed input format: phone, credit card, date, SSN, ZIP, IP.
- You want the display formatted while still capturing the raw value.
- For validation without rigid formatting, a Text Field with `state` is enough.
Async states
None — this component has nothing to load, so it has no loading, empty or error state to drive.
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
| preset | InputMaskPreset | 'phone-us' | Built-in mask preset (phone, credit card, date, SSN, ZIP, IP…). |
| customMask | string | '' | Custom mask pattern, used when `preset` is `custom`. `#` is the only placeholder character and it accepts a digit; every other character is a literal that is typed in for the user (e.g. '###-##' or '## / ##'). A mask with no `#` in it can never accept input, because the only characters this control reads from a keystroke are digits. |
| value | string (two-way) | '' | Formatted value (two-way). |
| size | InputMaskSize | 'md' | Field size: sm, md or lg. |
| variant | InputMaskVariant | 'default' | Visual variant. |
| disabled | boolean | false | Disable the field. |
| label | string | '' | Accessible label for the field. |
| error | string | '' | Error message shown below the field. |
| icon | string | '' | Name of an icon in `@styloviz/icons`, e.g. `'credit-card'`. This used to be a raw `<path d="…">` string. That put SVG geometry in every consumer's config and skipped the library's sizing, stroke and dark-mode handling — pass a name instead. |
| sensitivity | InputMaskSensitivity | 'auto' | Whether this field carries a regulated or personally identifying value. |
| errorMessage | string | '' | Client-side validation message. |
| serverError | SvServerError | null | A rejection reported by the server about this value. Self-expiring. |
Outputs
| Event | Payload type | Description |
|---|---|---|
| changed | string | Emitted with the masked/formatted value. |
| rawChanged | string | Emitted with the raw (unformatted) value. |
| valueChange | string | Emits the new `value` whenever it changes — the output half of `[(value)]`. |
Customization & theming
- Eight presets or a fully custom pattern; three visual variants and a leading `icon`.
- Mask tokens: `#` a digit (any script — `\p{Nd}`, not `[0-9]`), `A` a letter, `*` either. Everything else in the pattern is a literal the user may type or skip.
- `hasRejected()`, `rejectedChars()` and `maskComplete()` report what the mask could and could not hold, without deciding whether the value is right — that stays with the host.
- Pasting is handled as a paste rather than as very fast typing: a marked country code (`+1 …`, `00 1 …`) is dropped when the mask is exactly full without it, so a number copied from a contact list arrives intact instead of shifted a place.
- Emits both the raw (`rawChanged`) and formatted (`changed`) value; `error` shows a validation message.
- Two-way bind `[(value)]` or a reactive control (CVA).
Best practices
- Do: Store the raw value (`rawChanged`) and show the masked value to users.
- Do: Pick a `preset` when one exists rather than re-deriving a `customMask`.
- Don't: Don't reach for a mask for a VARIABLE-width format. UK postcodes are `M1 1AA`, `SW1A 1AA`, `B33 8TH` and `DN55 1PT` — four shapes, and no single mask expresses them. Use a text field with validation instead. The mask tokens (`#` digit, `A` letter, `*` either) make more formats expressible; they do not make a fixed mask the right tool.
- Do: Watch `hasRejected()` / `rejectedChars()`. Characters the mask cannot hold are dropped — that is what a mask does — but they are now counted and announced rather than vanishing. Before this, `SW1A 1AA` in a ZIP field became `11` and `+1 (555) 010-9999` pasted into a US phone field became `(155) 501-0999`, both silently, both looking complete and correct.
- Don't: Don't read `maskComplete()` as valid. `(000) 000-0000` fills every slot and is not a phone number. Completeness is the shape; correctness is yours, and `error` is where your answer goes.
- Tip: `phone-intl`'s mask assumes a ONE-DIGIT country code, which only +1 and +7 are. `+353 1 234 5678` has exactly eleven digits, so nothing overflows and nothing is reported — and the field still renders `+3 (531) 234-5678`. Use a dedicated Phone Input for international numbers.
- Tip: Communicate the expected format via the label or hint for screen-reader users.
- Don't: Don't read `preset="credit-card"` as compliance. Formatting a PAN does not encrypt or shield it — the digits are in a DOM node every script on your page can read, which is the difference between SAQ-A and SAQ-A-EP. SAQ-A-EP's scope includes your analytics, tag manager, chat widget and A/B tool. If you do not need to hold the number, use your processor's hosted field instead.
- Do: Account for every session-replay script on the page. FullStory, Hotjar, Datadog RUM, Clarity, Smartlook and LogRocket record the DOM and read input values — "masked" is masked to the eye only. A sensitive preset sets every opt-out those tools honour, which is a best effort and not a guarantee about a script you chose to load.
- Do: Use `maskedValue()` (`•••• •••• •••• 4242`) or `last4()` for confirmation screens, receipts, logs, analytics and support tooling. Both redact everything when the value has fewer than five digits, because four digits of a four-digit value is not a redaction.
- Do: Set `sensitivity="sensitive"` on a custom mask that carries something regulated — a national ID, a policy number, a date of birth. `auto` only knows about the `credit-card` and `ssn` presets.
- Don't: Don't let a generic "save draft" mechanism persist this field. It is the most common accidental persistence, because the draft code was written for a different form.
Accessibility
- The `label` is associated with the input via `for`/`id`; `inputmode` adapts the on-screen keyboard (tel/numeric) to the preset.
- When `error` is set the input gets `aria-invalid` and is linked to the message via `aria-describedby` (the message uses `role="alert"`).
- Characters the mask could not hold are reported in a `role="status"` line linked by `aria-describedby` — a count, not "invalid input", because the field is showing a complete and plausible value that is not what was entered. It is not `aria-invalid`: a partial fit may be exactly what the host expects.
- Implements ControlValueAccessor — works with `[(ngModel)]`, `[formControl]` and `formControlName`.
Feedback on Input Mask
Spotted a gap in this page, or something that didn't work as described? Comments are GitHub Discussions in the public styloviz/community repo, so posting needs a GitHub account.