Skip to content
Components, switch product

Number

<gui-number> is a number field. It renders a native <input type="number"> with its label, hint and errors. The arrow keys step the number up and down, within the bounds you set.

Code of the example above
NumberBasic.tsx
import { GuiNumber } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function NumberBasic() {
return <GuiNumber label="Guests" value={2} />;
}

The element’s entry point is @golemui/gui-components/number, which registers <gui-number> 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.

GuestsField.tsx
import { useState } from 'react';
import { GuiNumber } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function GuestsField() {
const [guests, setGuests] = useState(1);
return (
<GuiNumber
label="Guests"
name="guests"
value={guests}
onGuiChange={(event) => setGuests(event.detail.value ?? 1)}
/>
);
}

The value is a number, or undefined while the field is empty. The snippets fall back to 1 when the user clears the field.

Properties

Property Type Default Description
localeId attribute locale-id string 'en' BCP 47 locale for formatting and parsing, such as en-US or es.
value number – The number, or undefined when empty.
step number – The step of the ArrowUp and ArrowDown keys. Defaults to 1. An empty field steps from 0.
placeholder string – Text shown while the control is empty.
autocomplete string – The autocomplete hint passed to the inner native control.
minimum number – Smallest allowed number.
maximum number – Largest allowed number.
autoGrow attribute auto-grow boolean false Grows the field with its content instead of scrolling.
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 number: on blur or Enter, or at once with a step. detail.value is the number.
gui-blur Focus left the control.

↑ and ↓ add or take away step, which is 1 by default. An empty field steps from 0. minimum and maximum stop the arrow keys at the bounds.

Code of the example above
NumberBounds.tsx
import { GuiNumber } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function NumberBounds() {
return (
<div className="gui-grid gui-grid--row">
<div className="gui-grid__cell">
<GuiNumber label="Tickets" value={1} minimum={1} maximum={6} />
</div>
<div className="gui-grid__cell">
<GuiNumber
label="Hours"
value={1.5}
step={0.5}
minimum={0.5}
maximum={8}
/>
</div>
</div>
);
}

The user can still type a number outside the bounds. The field keeps it: in a form, it blocks the submit (see In a form). To show a message as the user works, set errors from your own validation.

The field is narrow, made for short numbers, and a longer number scrolls inside it. With auto-grow, the field starts narrower still and widens to fit the number as the user types.

Code of the example above
NumberAutoGrow.tsx
import { GuiNumber } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function NumberAutoGrow() {
return (
<div className="gui-grid gui-grid--row">
<div className="gui-grid__cell">
<GuiNumber label="Fixed width" hint="Type 8100000000." />
</div>
<div className="gui-grid__cell">
<GuiNumber label="Auto-grow" hint="Type 8100000000." autoGrow />
</div>
</div>
);
}

errors takes a list of messages from your own validation, and the field is marked invalid. To check the number as the user works, set the errors property from a gui-change listener. Its detail.value is a number, or undefined for an empty field.

Code of the example above
NumberValidation.tsx
import { useState } from 'react';
import { GuiNumber } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function NumberValidation() {
const [errors, setErrors] = useState<string[]>([]);
return (
<GuiNumber
id="team-size"
label="Team size"
hint="Even numbers only, for pair work."
errors={errors}
onGuiChange={(event) => {
const value = event.detail.value;
setErrors(
value === undefined || value % 2 === 0
? []
: ['Pick an even number.'],
);
}}
/>
);
}

placeholder, hint, disabled and readonly work as on the text input. A read-only field doesn’t step.

The field takes digits, a decimal point, a sign and an exponent, and drops any other character the user types or pastes. It shows the number as the browser’s own number input does: locale-id doesn’t change it.

With a name, <gui-number> submits its number as text, such as 3 or 1.5, under that name. 0 is submitted; an empty field submits nothing. These block a submit:

  • required, while the field is empty;
  • a number below minimum or above maximum, with the message “Value must be greater than or equal to …” or “less than or equal to …”.

step is only the step of the arrow keys: any number, decimals included, is valid.

Submit the form below empty, then with too many adults.

Code of the example above
NumberNativeForm.tsx
import { useState, type SubmitEvent } from 'react';
import { GuiButton, GuiNumber } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function NumberNativeForm() {
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="booking" onSubmit={onSubmit}>
<div className="gui-grid gui-grid--row">
<div className="gui-grid__cell">
<GuiNumber
label="Adults"
name="adults"
minimum={1}
maximum={8}
required
/>
</div>
<div className="gui-grid__cell">
<GuiNumber label="Children" name="children" minimum={0} maximum={6} />
</div>
</div>
<GuiButton type="submit" label="Check availability" />
<p id="booking-result" role="status">
{result}
</p>
</form>
);
}

How the elements submit, validate and reset is in Native forms.

<gui-number> renders into the light DOM, so you can style every part with plain CSS. These are the parts and their public classes:

gui-number
<gui-number class="gui-field">
<label class="gui-label" for="guests" id="guests_label">
<span class="gui-label__text">Guests</span>
</label>
<div class="gui-widget">
<input
type="number"
inputmode="decimal"
id="guests"
class="gui-widget-input"
step="any"
aria-valuemin="1"
aria-valuemax="12"
style="min-width: 23px;"
/>
<span class="gui-number__decoration">
<span class="gui-caret gui-caret--up" aria-hidden="true"></span>
<span class="gui-caret" aria-hidden="true"></span>
</span>
</div>
<div
class="gui-validator gui-validator--empty"
id="guests_errors"
role="alert"
></div>
</gui-number>
ClassPart
gui-fieldThe element itself. Lines its parts up with the fields next to it in a gui-grid row.
gui-labelThe label, with the text and the hint.
gui-label__textThe label text and the required marker.
gui-widget-hintThe hint.
gui-widgetThe box around the number and the carets: it draws the border.
gui-widget-inputThe <input>.
gui-number__decorationThe two carets at the end of the field. They are decorative: the arrow keys step.
gui-caretOne caret. gui-caret--up turns it up. See Carets.
gui-validatorThe errors container. It is always there, so the alert role announces new errors.
gui-validator--emptyOn the errors container while there are no errors.
gui-validator__errorOne error message.

The element sets the input’s width in an inline style. The ids derive from uid, or from a generated id such as gui-number-3 when you don’t set one. Select by class or by tag, not by id.

The field reads these design tokens. Set them on :root to change every field, or on any element to change the fields inside it:

TokenWhat it styles
--gui-widget-sizeThe height of the field.
--gui-radius-mdThe corner radius.
--gui-bg-surfaceThe field’s background.
--gui-border-defaultThe border.
--gui-text-defaultThe number, the label and the carets.
--gui-font-familyThe font.
--gui-font-smThe text size of the label.
--gui-font-xsThe text size of the hint and the errors.
--gui-text-secondaryThe hint.
--gui-border-focusThe border while focused.
--gui-shadow-focusThe focus ring.
--gui-intent-errorThe border and the messages when there are errors.
--gui-shadow-focus-errorThe focus ring when there are errors.
--gui-bg-disabledThe background when disabled.
--gui-text-disabledThe number when disabled.
--gui-space-1The space between the label, hint and errors, and the carets’ inset.
--gui-caret-shape, --gui-caret-imageThe carets, see Carets.

The number is 20 pixels, in tabular figures, whatever the tokens. To restyle beyond the tokens, see Overriding styles; for dark mode and the Clay theme, see Themes and dark mode.

  • Name. The label names the field. Without one the field has no accessible name.
  • Role. The input is a native number input, which screen readers announce as a spin button with its value. minimum and maximum become its aria-valuemin and aria-valuemax.
  • Description. The hint is the field’s description, through aria-describedby.
  • Errors. While errors show, the input has aria-invalid="true" and points at them with aria-errormessage, and screen readers announce new errors as they appear. A number out of bounds doesn’t set it: only errors does.
  • Required. required sets aria-required; the visual marker is hidden from screen readers.
  • Carets. The carets are hidden from assistive technology and don’t respond to clicks; the arrow keys step.
  • Keyboard on phones. inputmode="decimal" opens a keypad with a decimal point.
KeyAction
TabMoves focus to the field, and away from it.
↑Adds step, up to maximum, and fires gui-input and gui-change.
↓Takes away step, down to minimum, and fires gui-input and gui-change.
EnterCommits a typed number, as leaving the field does, and fires gui-change.
Number keysType 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.