Currency
<gui-currency> is a field for an amount of money. It renders a native <input type="number">
with its label, hint and errors, and shows the amount formatted in its currency, such as
$1,250.00.
import { GuiCurrency } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function CurrencyBasic() { return <GuiCurrency label="Budget" value={1250} />;}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/currency';
@Component({ selector: 'app-currency-basic', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `<gui-currency label="Budget" value="1250"></gui-currency>`,})export class CurrencyBasicComponent {}<script setup lang="ts">import '@golemui/gui-components/currency';import '@golemui/gui-components/index.css';</script>
<template> <gui-currency label="Budget" :value="1250"></gui-currency></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/currency';import '@golemui/gui-components/index.css';
@customElement('currency-basic')export class CurrencyBasic extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-currency label="Budget" value="1250"></gui-currency> `; }}<gui-currency label="Budget" value="1250"></gui-currency>The element’s entry point is @golemui/gui-components/currency, which registers
<gui-currency> and nothing else. To install the package and load the stylesheet, see
Installation.
The @golemui/gui-components/react components have typed props and an on* prop for each event. React 18 or later.
import { useState } from 'react';import { GuiCurrency } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function BudgetField() { const [budget, setBudget] = useState(0);
return ( <GuiCurrency label="Budget" name="budget" currency="EUR" value={budget} onGuiChange={(event) => setBudget(event.detail.value ?? 0)} /> );}Add CUSTOM_ELEMENTS_SCHEMA to the component that uses the tags, and the stylesheet to styles in angular.json.
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import type { GuiEventMap, GuiCurrencyEvents } from '@golemui/gui-components';import '@golemui/gui-components/currency';
type ChangeEvent = GuiEventMap<typeof GuiCurrencyEvents>['gui-change'];
@Component({ selector: 'app-budget-field', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-currency label="Budget" name="budget" currency="EUR" [value]="budget" (gui-change)="onChange($event)" ></gui-currency> `,})export class BudgetFieldComponent { budget = 0;
onChange(event: Event) { this.budget = (event as ChangeEvent).detail.value ?? 0; }}"styles": ["node_modules/@golemui/gui-components/lib/styles/index.css"]Tell Vue's compiler that gui-* tags are custom elements, and add the typings once for typed props and @gui-* handlers.
<script setup lang="ts">import { ref } from 'vue';import '@golemui/gui-components/currency';import '@golemui/gui-components/index.css';
const budget = ref(0);</script>
<template> <gui-currency label="Budget" name="budget" currency="EUR" :value="budget" @gui-change="budget = $event.detail.value ?? 0" /></template>vue({ template: { compilerOptions: { isCustomElement: (tag) => tag.startsWith('gui-') } },});import type {} from '@golemui/gui-components/vue';The page's stylesheet styles the gui-* elements, so it only reaches them outside a shadow root. Render the host component into the light DOM too, as below, or load the stylesheet into its shadow root.
import { LitElement, html } from 'lit';import { customElement, state } from 'lit/decorators.js';import '@golemui/gui-components/currency';import '@golemui/gui-components/index.css';
@customElement('budget-field')export class BudgetField extends LitElement { @state() private budget = 0;
protected override createRenderRoot() { return this; }
override render() { return html` <gui-currency label="Budget" name="budget" currency="EUR" .value=${this.budget} @gui-change=${(event: CustomEvent) => (this.budget = event.detail.value)} ></gui-currency> `; }}With any bundler, such as Vite. Import the element once, anywhere, and use the tag in your markup.
<gui-currency label="Budget" name="budget" currency="EUR"></gui-currency>
<script type="module" src="./main.js"></script>import '@golemui/gui-components/currency';import '@golemui/gui-components/index.css';
const element = document.querySelector('gui-currency');
element.addEventListener('gui-change', (event) => { console.log(event.detail.value);});The value is a plain number, such as 1250.5, or undefined while the field is empty. The
snippets fall back to 0 when the user clears the field.
Properties
| Property | Type | Default | Description |
|---|---|---|---|
localeId
attribute locale-id | string | – | BCP 47 locale for formatting and parsing, such as en-US or es. |
value | number | null | – | The amount, or undefined when empty. |
currency | string | – | ISO 4217 currency code, such as USD or EUR. |
step | number | – | The step of the ArrowUp and ArrowDown keys. |
maximumFractionDigits
attribute maximum-fraction-digits | number | – | Maximum number of decimals shown. |
minimumFractionDigits
attribute minimum-fraction-digits | number | – | Minimum number of decimals shown. |
icon | string | – | Icon class name shown inside the control, for example from an icon font. |
placeholder | string | – | Text shown while the control is empty. |
autocomplete | string | – | The autocomplete hint passed to the inner native control. |
uid | string | – | The id of the element's control. The ids of its label, hint and errors derive from it. Optional: when it is not set, the element generates one from its tag name and a page-wide counter, e.g. gui-textinput-3. Generated ids depend on creation order, so a server-rendered element must set uid or its ids will not match after hydration. |
Form control properties
Every field has these, from the base class of the form controls.
| Property | Type | Default | Description |
|---|---|---|---|
name | string | – | The name the value is submitted under. Without it the element stays out of its form. Reflected, like a native control's: the form reads a single value's name from the attribute. |
label | string | – | The visible label, which also names the control for assistive technology. Without one the control has no accessible name; the date, time, range, tags and file fields warn about it in development builds. |
hint | string | – | Help text shown under the label and announced as the control's description. |
errors | string[] | [] | Error messages to show, typically from your own validation. They show as soon as they are set, unless touched is false. |
touched | boolean | – | Whether the user has interacted with the control. Leave it unset unless you validate on interaction: false holds errors back until it becomes true. |
required | boolean | false | The control needs a value: it is marked as required and, with a name, blocks submission. |
readOnly
attribute readonly | boolean | – | The value can be read and focused but not changed. |
disabled | boolean | – | Disabled by its own attribute or by a disabled <fieldset> around it. Setting the property sets the attribute, like a native control's. |
form read-only, no attribute | HTMLFormElement | null | – | The <form> the element belongs to, if any. |
validity read-only, no attribute | ValidityState | – | The element's validity, like a native control's. |
validationMessage read-only, no attribute | string | – | The message the browser shows for an invalid value. |
Methods
| Method | Description |
|---|---|
checkValidity(): boolean | – |
reportValidity(): boolean | – |
Events
Every event bubbles and is composed, so you can listen on any ancestor.
| Event | Description |
|---|---|
gui-input | The user changed the value. detail.value is the new value. |
gui-change | The user committed the text, on blur or Enter. detail.value is the value. |
gui-blur | Focus left the control. |
Examples
Section titled “Examples”Currency and locale
Section titled “Currency and locale”currency takes an ISO 4217 code, USD by default. locale-id takes a BCP 47 locale, en by
default, and sets the symbol’s place and the separators. The field shows two decimals; for a
currency without cents, set maximum-fraction-digits="0". minimum-fraction-digits sets the
fewest decimals shown.
import { GuiCurrency } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function CurrencyCurrencies() { return ( <div className="gui-grid gui-grid--row"> <div className="gui-grid__cell"> <GuiCurrency label="Price" currency="USD" localeId="en-US" value={1299.5} /> </div> <div className="gui-grid__cell"> <GuiCurrency label="Preis" currency="EUR" localeId="de-DE" value={1299.5} /> </div> <div className="gui-grid__cell"> <GuiCurrency label="価格" currency="JPY" localeId="ja-JP" maximumFractionDigits={0} value={189000} /> </div> </div> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/currency';
@Component({ selector: 'app-currency-currencies', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-currency label="Price" currency="USD" locale-id="en-US" value="1299.5" ></gui-currency> </div> <div class="gui-grid__cell"> <gui-currency label="Preis" currency="EUR" locale-id="de-DE" value="1299.5" ></gui-currency> </div> <div class="gui-grid__cell"> <gui-currency label="価格" currency="JPY" locale-id="ja-JP" maximum-fraction-digits="0" value="189000" ></gui-currency> </div> </div> `,})export class CurrencyCurrenciesComponent {}<script setup lang="ts">import '@golemui/gui-components/currency';import '@golemui/gui-components/index.css';</script>
<template> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-currency label="Price" currency="USD" locale-id="en-US" :value="1299.5" ></gui-currency> </div> <div class="gui-grid__cell"> <gui-currency label="Preis" currency="EUR" locale-id="de-DE" :value="1299.5" ></gui-currency> </div> <div class="gui-grid__cell"> <gui-currency label="価格" currency="JPY" locale-id="ja-JP" :maximumFractionDigits="0" :value="189000" ></gui-currency> </div> </div></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/currency';import '@golemui/gui-components/index.css';
@customElement('currency-currencies')export class CurrencyCurrencies extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-currency label="Price" currency="USD" locale-id="en-US" value="1299.5" ></gui-currency> </div> <div class="gui-grid__cell"> <gui-currency label="Preis" currency="EUR" locale-id="de-DE" value="1299.5" ></gui-currency> </div> <div class="gui-grid__cell"> <gui-currency label="価格" currency="JPY" locale-id="ja-JP" maximum-fraction-digits="0" value="189000" ></gui-currency> </div> </div> `; }}<div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-currency label="Price" currency="USD" locale-id="en-US" value="1299.5"></gui-currency> </div> <div class="gui-grid__cell"> <gui-currency label="Preis" currency="EUR" locale-id="de-DE" value="1299.5"></gui-currency> </div> <div class="gui-grid__cell"> <gui-currency label="価格" currency="JPY" locale-id="ja-JP" maximum-fraction-digits="0" value="189000" ></gui-currency> </div></div>Focus a field to edit it. While it has focus, the field shows the plain number to type in, and the formatted amount moves up over its top border. It formats the amount again as the user types, and when they leave.
↑ and ↓ add or take away step, 1 by default. The field has
no bounds: the arrow keys go below zero too.
import { GuiCurrency } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function CurrencyStep() { return ( <GuiCurrency label="Monthly donation" currency="GBP" localeId="en-GB" step={5} value={20} hint="Arrow keys change it by £5." /> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/currency';
@Component({ selector: 'app-currency-step', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-currency label="Monthly donation" currency="GBP" locale-id="en-GB" step="5" value="20" hint="Arrow keys change it by £5." ></gui-currency> `,})export class CurrencyStepComponent {}<script setup lang="ts">import '@golemui/gui-components/currency';import '@golemui/gui-components/index.css';</script>
<template> <gui-currency label="Monthly donation" currency="GBP" locale-id="en-GB" :step="5" :value="20" hint="Arrow keys change it by £5." ></gui-currency></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/currency';import '@golemui/gui-components/index.css';
@customElement('currency-step')export class CurrencyStep extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-currency label="Monthly donation" currency="GBP" locale-id="en-GB" step="5" value="20" hint="Arrow keys change it by £5." ></gui-currency> `; }}<gui-currency label="Monthly donation" currency="GBP" locale-id="en-GB" step="5" value="20" hint="Arrow keys change it by £5."></gui-currency>Errors
Section titled “Errors”The element has no minimum or maximum. Check the amount in your own validation and set
errors, for example from a gui-change listener. Its detail.value is the plain number, or
undefined for an empty field.
import { useState } from 'react';import { GuiCurrency } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function CurrencyValidation() { const [errors, setErrors] = useState<string[]>([]);
return ( <GuiCurrency id="tip" label="Tip" currency="EUR" localeId="es-ES" hint="Between 1 € and 50 €." errors={errors} onGuiChange={(event) => { const value = event.detail.value; const valid = value === undefined || (value !== null && value >= 1 && value <= 50); setErrors(valid ? [] : ['Enter an amount between 1 € and 50 €.']); }} /> );}import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';import type { GuiCurrencyEvents, GuiEventMap } from '@golemui/gui-components';import '@golemui/gui-components/currency';
type CurrencyEvent<K extends keyof typeof GuiCurrencyEvents> = GuiEventMap< typeof GuiCurrencyEvents>[K];
@Component({ selector: 'app-currency-validation', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-currency id="tip" label="Tip" currency="EUR" locale-id="es-ES" hint="Between 1 € and 50 €." [errors]="errors()" (gui-change)="onChange($event)" ></gui-currency> `,})export class CurrencyValidationComponent { errors = signal<string[]>([]);
onChange(event: Event) { const { value } = (event as CurrencyEvent<'gui-change'>).detail; const valid = value === undefined || (value !== null && value >= 1 && value <= 50); this.errors.set(valid ? [] : ['Enter an amount between 1 € and 50 €.']); }}<script setup lang="ts">import { ref } from 'vue';import '@golemui/gui-components/currency';import '@golemui/gui-components/index.css';
const errors = ref<string[]>([]);
function onChange(value: number | null | undefined) { const valid = value === undefined || (value !== null && value >= 1 && value <= 50); errors.value = valid ? [] : ['Enter an amount between 1 € and 50 €.'];}</script>
<template> <gui-currency id="tip" label="Tip" currency="EUR" locale-id="es-ES" hint="Between 1 € and 50 €." :errors.prop="errors" @gui-change="onChange($event.detail.value)" ></gui-currency></template>import { LitElement, html } from 'lit';import { customElement, state } from 'lit/decorators.js';import type { GuiCurrencyEvents, GuiEventMap } from '@golemui/gui-components';import '@golemui/gui-components/currency';import '@golemui/gui-components/index.css';
type CurrencyEvent<K extends keyof typeof GuiCurrencyEvents> = GuiEventMap< typeof GuiCurrencyEvents>[K];
@customElement('currency-validation')export class CurrencyValidation extends LitElement { @state() private errors: string[] = [];
protected override createRenderRoot() { return this; }
private onChange(event: CurrencyEvent<'gui-change'>) { const value = event.detail.value; const valid = value === undefined || (value !== null && value >= 1 && value <= 50); this.errors = valid ? [] : ['Enter an amount between 1 € and 50 €.']; }
override render() { return html` <gui-currency id="tip" label="Tip" currency="EUR" locale-id="es-ES" hint="Between 1 € and 50 €." .errors=${this.errors} @gui-change=${this.onChange} ></gui-currency> `; }}<gui-currency id="tip" label="Tip" currency="EUR" locale-id="es-ES" hint="Between 1 € and 50 €."></gui-currency>
<script type="module"> const tip = document.querySelector('#tip');
tip.addEventListener('gui-change', (event) => { const value = event.detail.value; const valid = value === undefined || (value >= 1 && value <= 50); tip.errors = valid ? [] : ['Enter an amount between 1 € and 50 €.']; });</script>icon, placeholder, hint, disabled and readonly work as on the
text input.
In a form
Section titled “In a form”With a name, <gui-currency> submits the plain number as text under that name, such as 42.5:
no symbol, no separators and no currency code. Submit the currency along with it, in a field of
its own, if your server needs it. An empty field submits nothing, and required blocks an empty
submit.
step is only the step of the arrow keys: any amount, cents included, is valid.
import { useState, type SubmitEvent } from 'react';import { GuiButton, GuiCurrency, GuiTextinput,} from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function CurrencyNativeForm() { const [result, setResult] = useState('');
const onSubmit = (event: SubmitEvent<HTMLFormElement>) => { event.preventDefault(); const data = Object.fromEntries(new FormData(event.currentTarget)); setResult(`Submitted ${JSON.stringify(data)}`); };
return ( <form id="expense" onSubmit={onSubmit}> <div className="gui-grid gui-grid--row"> <div className="gui-grid__cell"> <GuiTextinput label="Description" name="description" required /> </div> <div className="gui-grid__cell"> <GuiCurrency label="Amount" name="amount" currency="EUR" localeId="fr-FR" required /> </div> </div> <GuiButton type="submit" label="Add expense" /> <p id="expense-result" role="status"> {result} </p> </form> );}import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';import '@golemui/gui-components/button';import '@golemui/gui-components/currency';import '@golemui/gui-components/textinput';
@Component({ selector: 'app-currency-native-form', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <form id="expense" #form (submit)="onSubmit($event, form)"> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-textinput label="Description" name="description" required ></gui-textinput> </div> <div class="gui-grid__cell"> <gui-currency label="Amount" name="amount" currency="EUR" locale-id="fr-FR" required ></gui-currency> </div> </div> <gui-button type="submit" label="Add expense"></gui-button> <p id="expense-result" role="status">{{ result() }}</p> </form> `,})export class CurrencyNativeFormComponent { result = signal('');
onSubmit(event: SubmitEvent, form: HTMLFormElement) { event.preventDefault(); const data = Object.fromEntries(new FormData(form)); this.result.set(`Submitted ${JSON.stringify(data)}`); }}<script setup lang="ts">import { ref } from 'vue';import '@golemui/gui-components/button';import '@golemui/gui-components/currency';import '@golemui/gui-components/textinput';import '@golemui/gui-components/index.css';
const result = ref('');
function onSubmit(event: SubmitEvent) { if (!(event.currentTarget instanceof HTMLFormElement)) return; const data = Object.fromEntries(new FormData(event.currentTarget)); result.value = `Submitted ${JSON.stringify(data)}`;}</script>
<template> <form id="expense" @submit.prevent="onSubmit"> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-textinput label="Description" name="description" :required="true" ></gui-textinput> </div> <div class="gui-grid__cell"> <gui-currency label="Amount" name="amount" currency="EUR" locale-id="fr-FR" :required="true" ></gui-currency> </div> </div> <gui-button type="submit" label="Add expense"></gui-button> <p id="expense-result" role="status">{{ result }}</p> </form></template>import { LitElement, html } from 'lit';import { customElement, query, state } from 'lit/decorators.js';import '@golemui/gui-components/button';import '@golemui/gui-components/currency';import '@golemui/gui-components/textinput';import '@golemui/gui-components/index.css';
@customElement('currency-native-form')export class CurrencyNativeForm extends LitElement { @query('#expense') private form!: HTMLFormElement; @state() private result = '';
protected override createRenderRoot() { return this; }
private onSubmit(event: SubmitEvent) { event.preventDefault(); const data = Object.fromEntries(new FormData(this.form)); this.result = `Submitted ${JSON.stringify(data)}`; }
override render() { return html` <form id="expense" @submit=${this.onSubmit}> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-textinput label="Description" name="description" required ></gui-textinput> </div> <div class="gui-grid__cell"> <gui-currency label="Amount" name="amount" currency="EUR" locale-id="fr-FR" required ></gui-currency> </div> </div> <gui-button type="submit" label="Add expense"></gui-button> <p id="expense-result" role="status">${this.result}</p> </form> `; }}<form id="expense"> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-textinput label="Description" name="description" required></gui-textinput> </div> <div class="gui-grid__cell"> <gui-currency label="Amount" name="amount" currency="EUR" locale-id="fr-FR" required ></gui-currency> </div> </div> <gui-button type="submit" label="Add expense"></gui-button> <p id="expense-result" role="status"></p></form>
<script type="module"> const form = document.querySelector('#expense');
form.addEventListener('submit', (event) => { event.preventDefault(); const data = Object.fromEntries(new FormData(form)); document.querySelector('#expense-result').textContent = `Submitted ${JSON.stringify(data)}`; });</script>How the elements submit, validate and reset is in Native forms.
Styling
Section titled “Styling”Anatomy
Section titled “Anatomy”<gui-currency> renders into the light DOM, so you can style every part with plain CSS. These
are the parts and their public classes:
<gui-currency class="gui-field"> <label class="gui-label" for="budget" id="budget_label"> <span class="gui-label__text">Budget</span> </label> <div class="gui-widget"> <input type="number" inputmode="decimal" id="budget" class="gui-widget-input gui-currency--icon" /> <span aria-hidden="true" class="gui-currency__format-value gui-currency__format-value--icon" >$1,250.00</span > <span class="gui-widget-icon icon-wallet" data-icon="icon-wallet" aria-hidden="true" ></span> </div> <div class="gui-validator gui-validator--empty" id="budget_errors" role="alert" ></div></gui-currency>| Class | Part |
|---|---|
gui-field | The element itself. Lines its parts up with the fields next to it in a gui-grid row. |
gui-label | The label, with the text and the hint. |
gui-label__text | The label text and the required marker. |
gui-widget-hint | The hint. |
gui-widget | The box around the control. |
gui-widget-input | The <input>, the part that looks like a field. Its text is transparent until it has focus. |
gui-currency--icon | On the <input> when there is an icon: it makes room for it. |
gui-currency__format-value | The formatted amount, over the input. Only while there is an amount. |
gui-currency__format-value--icon | On the formatted amount when there is an icon. |
gui-widget-icon | The icon, with your icon classes. |
gui-validator | The errors container. It is always there, so the alert role announces new errors. |
gui-validator--empty | On the errors container while there are no errors. |
gui-validator__error | One error message. |
The ids derive from uid, or from a generated id such as gui-currency-3 when you don’t set one.
Select by class or by tag, not by id.
Tokens
Section titled “Tokens”The field reads these design tokens. Set them on :root to change every field, or on any element
to change the fields inside it:
| Token | What it styles |
|---|---|
--gui-widget-size | The height of the field, and the size of the icon. |
--gui-radius-md | The corner radius. |
--gui-bg-surface | The field’s background, and the formatted amount’s. |
--gui-border-default | The border. |
--gui-text-default | The amount, the label and the icon. |
--gui-font-family | The font. |
--gui-font-sm | The text size of the label, the field and the formatted amount. |
--gui-font-xs | The text size of the hint and the errors. |
--gui-text-secondary | The hint. |
--gui-space-10 | The room for the icon. |
--gui-border-focus | The border while focused. |
--gui-shadow-focus | The focus ring. |
--gui-intent-error | The border and the messages when there are errors, and the formatted amount of a focused field with errors. |
--gui-shadow-focus-error | The focus ring when there are errors. |
--gui-bg-disabled | The background when disabled. |
--gui-text-disabled | The text when disabled. |
--gui-space-1 | The space between the label, hint and errors. |
To restyle beyond the tokens, see Overriding styles; for dark mode and the Clay theme, see Themes and dark mode.
Accessibility
Section titled “Accessibility”- Name. The
labelnames the field. Without one the field has no accessible name. - The amount. The formatted amount is hidden from assistive technology: screen readers read
the plain number in the input, such as 1250, without the currency. Put the currency in the
labelor thehint, such as “Budget (EUR)”. - Description. The
hintis the field’s description, througharia-describedby. - Errors. While errors show, the input has
aria-invalid="true"and points at them witharia-errormessage, and screen readers announce new errors as they appear. - Required.
requiredsetsaria-required; the visual marker is hidden from screen readers. - Keyboard on phones.
inputmode="decimal"opens a keypad with a decimal point.
| Key | Action |
|---|---|
| Tab | Moves focus to the field, and away from it. |
| ↑ | Adds step and fires gui-input and gui-change. |
| ↓ | Takes away step and fires gui-input and gui-change. |
| Enter | Commits a typed amount, as leaving the field does, and fires gui-change. |
| Number keys | Type digits, a decimal separator, a sign or an exponent. Other characters are dropped. |
What every element shares, such as translating the browser messages, is in Accessibility.