Text input
<gui-textinput> is a single-line text field. It renders a native <input type="text"> with its
label, hint and errors, and keeps them connected for assistive technology.
import { GuiTextinput } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function TextInputBasic() { return <GuiTextinput label="Full name" />;}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/textinput';
@Component({ selector: 'app-text-input-basic', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `<gui-textinput label="Full name"></gui-textinput>`,})export class TextInputBasicComponent {}<script setup lang="ts">import '@golemui/gui-components/textinput';import '@golemui/gui-components/index.css';</script>
<template> <gui-textinput label="Full name"></gui-textinput></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/textinput';import '@golemui/gui-components/index.css';
@customElement('text-input-basic')export class TextInputBasic extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-textinput label="Full name"></gui-textinput> `; }}<gui-textinput label="Full name"></gui-textinput>The element’s entry point is @golemui/gui-components/textinput, which registers
<gui-textinput> 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 { GuiTextinput } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function EmailField() { const [email, setEmail] = useState('');
return ( <GuiTextinput label="Email" name="email" value={email} onGuiChange={(event) => setEmail(event.detail.value ?? '')} /> );}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, GuiTextinputEvents } from '@golemui/gui-components';import '@golemui/gui-components/textinput';
type ChangeEvent = GuiEventMap<typeof GuiTextinputEvents>['gui-change'];
@Component({ selector: 'app-email-field', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-textinput label="Email" name="email" [value]="email" (gui-change)="onChange($event)" ></gui-textinput> `,})export class EmailFieldComponent { email = '';
onChange(event: Event) { this.email = (event as ChangeEvent).detail.value ?? ''; }}"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/textinput';import '@golemui/gui-components/index.css';
const email = ref('');</script>
<template> <gui-textinput label="Email" name="email" :value="email" @gui-change="email = $event.detail.value ?? ''" /></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/textinput';import '@golemui/gui-components/index.css';
@customElement('email-field')export class EmailField extends LitElement { @state() private email = '';
protected override createRenderRoot() { return this; }
override render() { return html` <gui-textinput label="Email" name="email" .value=${this.email} @gui-change=${(event: CustomEvent) => (this.email = event.detail.value)} ></gui-textinput> `; }}With any bundler, such as Vite. Import the element once, anywhere, and use the tag in your markup.
<gui-textinput label="Email" name="email"></gui-textinput>
<script type="module" src="./main.js"></script>import '@golemui/gui-components/textinput';import '@golemui/gui-components/index.css';
const element = document.querySelector('gui-textinput');
element.addEventListener('gui-change', (event) => { console.log(event.detail.value);});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 | string | – | The text. |
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”Placeholder
Section titled “Placeholder”placeholder shows while the field is empty. It is not a label: screen readers may skip it, and
it disappears as soon as the user types, so always set label too.
import { GuiTextinput } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function TextInputPlaceholder() { return <GuiTextinput label="City" placeholder="For example, Lisbon" />;}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/textinput';
@Component({ selector: 'app-text-input-placeholder', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-textinput label="City" placeholder="For example, Lisbon" ></gui-textinput> `,})export class TextInputPlaceholderComponent {}<script setup lang="ts">import '@golemui/gui-components/textinput';import '@golemui/gui-components/index.css';</script>
<template> <gui-textinput label="City" placeholder="For example, Lisbon"></gui-textinput></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/textinput';import '@golemui/gui-components/index.css';
@customElement('text-input-placeholder')export class TextInputPlaceholder extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-textinput label="City" placeholder="For example, Lisbon" ></gui-textinput> `; }}<gui-textinput label="City" placeholder="For example, Lisbon"></gui-textinput>hint shows under the label. Screen readers read it as the field’s description, after its name.
import { GuiTextinput } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function TextInputHint() { return ( <GuiTextinput label="Username" hint="Letters and numbers only. You can change it later." /> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/textinput';
@Component({ selector: 'app-text-input-hint', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-textinput label="Username" hint="Letters and numbers only. You can change it later." ></gui-textinput> `,})export class TextInputHintComponent {}<script setup lang="ts">import '@golemui/gui-components/textinput';import '@golemui/gui-components/index.css';</script>
<template> <gui-textinput label="Username" hint="Letters and numbers only. You can change it later." ></gui-textinput></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/textinput';import '@golemui/gui-components/index.css';
@customElement('text-input-hint')export class TextInputHint extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-textinput label="Username" hint="Letters and numbers only. You can change it later." ></gui-textinput> `; }}<gui-textinput label="Username" hint="Letters and numbers only. You can change it later."></gui-textinput>icon takes the class names of an icon, from any icon font or your own CSS, and shows it at the
start of the field. The icon is decorative and hidden from assistive technology. See
Icons for the ways to draw it.
import { GuiTextinput } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
// Any icon font works: icon sets these classes on the icon element.// This one draws a Material Icons ligature.const iconStyles = ` .icon-search::before { content: 'search'; font-family: 'Material Icons'; }`;
export function TextInputIcon() { return ( <> <GuiTextinput label="Search" icon="icon-search" placeholder="Search the docs" /> <style>{iconStyles}</style> </> );}import { Component, CUSTOM_ELEMENTS_SCHEMA, ViewEncapsulation,} from '@angular/core';import '@golemui/gui-components/textinput';
@Component({ selector: 'app-text-input-icon', schemas: [CUSTOM_ELEMENTS_SCHEMA], encapsulation: ViewEncapsulation.None, template: ` <gui-textinput label="Search" icon="icon-search" placeholder="Search the docs" ></gui-textinput> `, styles: ` /* Any icon font works: icon sets these classes on the icon element. This one draws a Material Icons ligature. */ .icon-search::before { content: 'search'; font-family: 'Material Icons'; } `,})export class TextInputIconComponent {}<script setup lang="ts">import '@golemui/gui-components/textinput';import '@golemui/gui-components/index.css';</script>
<template> <gui-textinput label="Search" icon="icon-search" placeholder="Search the docs" ></gui-textinput></template>
<style>/* Any icon font works: icon sets these classes on the icon element. This one draws a Material Icons ligature. */.icon-search::before { content: 'search'; font-family: 'Material Icons';}</style>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/textinput';import '@golemui/gui-components/index.css';
@customElement('text-input-icon')export class TextInputIcon extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-textinput label="Search" icon="icon-search" placeholder="Search the docs" ></gui-textinput>
<style> /* Any icon font works: icon sets these classes on the icon element. This one draws a Material Icons ligature. */ .icon-search::before { content: 'search'; font-family: 'Material Icons'; } </style> `; }}<gui-textinput label="Search" icon="icon-search" placeholder="Search the docs"></gui-textinput>
<style> /* Any icon font works: icon sets these classes on the icon element. This one draws a Material Icons ligature. */ .icon-search::before { content: 'search'; font-family: 'Material Icons'; }</style>Disabled and read-only
Section titled “Disabled and read-only”A disabled field can’t be focused or changed, and a form doesn’t submit it. A readonly field
can be focused, selected and copied, and a form submits its value. A disabled <fieldset>
disables every element inside it.
import { GuiTextinput } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function TextInputDisabledReadonly() { return ( <div className="gui-grid gui-grid--row"> <div className="gui-grid__cell"> <GuiTextinput label="Plan" value="Team" disabled /> </div> <div className="gui-grid__cell"> <GuiTextinput label="Account ID" value="ACME-2041" readOnly /> </div> </div> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/textinput';
@Component({ selector: 'app-text-input-disabled-readonly', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-textinput label="Plan" value="Team" disabled></gui-textinput> </div> <div class="gui-grid__cell"> <gui-textinput label="Account ID" value="ACME-2041" readonly ></gui-textinput> </div> </div> `,})export class TextInputDisabledReadonlyComponent {}<script setup lang="ts">import '@golemui/gui-components/textinput';import '@golemui/gui-components/index.css';</script>
<template> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-textinput label="Plan" value="Team" :disabled="true"></gui-textinput> </div> <div class="gui-grid__cell"> <gui-textinput label="Account ID" value="ACME-2041" :readOnly="true" ></gui-textinput> </div> </div></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/textinput';import '@golemui/gui-components/index.css';
@customElement('text-input-disabled-readonly')export class TextInputDisabledReadonly extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-textinput label="Plan" value="Team" disabled></gui-textinput> </div> <div class="gui-grid__cell"> <gui-textinput label="Account ID" value="ACME-2041" readonly ></gui-textinput> </div> </div> `; }}<div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-textinput label="Plan" value="Team" disabled></gui-textinput> </div> <div class="gui-grid__cell"> <gui-textinput label="Account ID" value="ACME-2041" readonly></gui-textinput> </div></div>Errors
Section titled “Errors”errors takes a list of messages, usually from your own validation. As an attribute it is a JSON
array. Errors show as soon as you set them, and the field is marked invalid.
import { GuiTextinput } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
const errors = ['Enter a complete email address, like ada@example.com.'];
export function TextInputErrors() { return <GuiTextinput label="Email" value="ada@" errors={errors} />;}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/textinput';
@Component({ selector: 'app-text-input-errors', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-textinput label="Email" value="ada@" [errors]="errors"></gui-textinput> `,})export class TextInputErrorsComponent { errors = ['Enter a complete email address, like ada@example.com.'];}<script setup lang="ts">import '@golemui/gui-components/textinput';import '@golemui/gui-components/index.css';
const errors = ['Enter a complete email address, like ada@example.com.'];</script>
<template> <gui-textinput label="Email" value="ada@" :errors.prop="errors" ></gui-textinput></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/textinput';import '@golemui/gui-components/index.css';
const errors = ['Enter a complete email address, like ada@example.com.'];
@customElement('text-input-errors')export class TextInputErrors extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-textinput label="Email" value="ada@" .errors=${errors} ></gui-textinput> `; }}<gui-textinput label="Email" value="ada@" errors='["Enter a complete email address, like ada@example.com."]'></gui-textinput>To validate as the user works, set the errors property from a gui-change listener. An empty
list clears them. If you validate on interaction and set errors before the user has touched the
field, set touched to false until they do: it holds the errors back.
import { useState } from 'react';import { GuiTextinput } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function TextInputValidation() { const [errors, setErrors] = useState<string[]>([]);
return ( <GuiTextinput id="signup-email" label="Email" hint="Leave the field to check it." errors={errors} onGuiChange={(event) => { const valid = /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test( event.detail.value ?? '', ); setErrors( valid ? [] : ['Enter a complete email address, like ada@example.com.'], ); }} /> );}import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';import type { GuiEventMap, GuiTextinputEvents } from '@golemui/gui-components';import '@golemui/gui-components/textinput';
type TextinputEvent<K extends keyof typeof GuiTextinputEvents> = GuiEventMap< typeof GuiTextinputEvents>[K];
@Component({ selector: 'app-text-input-validation', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-textinput id="signup-email" label="Email" hint="Leave the field to check it." [errors]="errors()" (gui-change)="onChange($event)" ></gui-textinput> `,})export class TextInputValidationComponent { errors = signal<string[]>([]);
onChange(event: Event) { const { value } = (event as TextinputEvent<'gui-change'>).detail; const valid = /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(value ?? ''); this.errors.set( valid ? [] : ['Enter a complete email address, like ada@example.com.'], ); }}<script setup lang="ts">import { ref } from 'vue';import '@golemui/gui-components/textinput';import '@golemui/gui-components/index.css';
const errors = ref<string[]>([]);
function onChange(email: string | undefined) { const valid = /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email ?? ''); errors.value = valid ? [] : ['Enter a complete email address, like ada@example.com.'];}</script>
<template> <gui-textinput id="signup-email" label="Email" hint="Leave the field to check it." :errors.prop="errors" @gui-change="onChange($event.detail.value)" ></gui-textinput></template>import { LitElement, html } from 'lit';import { customElement, state } from 'lit/decorators.js';import type { GuiEventMap, GuiTextinputEvents } from '@golemui/gui-components';import '@golemui/gui-components/textinput';import '@golemui/gui-components/index.css';
type TextinputEvent<K extends keyof typeof GuiTextinputEvents> = GuiEventMap< typeof GuiTextinputEvents>[K];
@customElement('text-input-validation')export class TextInputValidation extends LitElement { @state() private errors: string[] = [];
protected override createRenderRoot() { return this; }
private onChange(event: TextinputEvent<'gui-change'>) { const valid = /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(event.detail.value ?? ''); this.errors = valid ? [] : ['Enter a complete email address, like ada@example.com.']; }
override render() { return html` <gui-textinput id="signup-email" label="Email" hint="Leave the field to check it." .errors=${this.errors} @gui-change=${this.onChange} ></gui-textinput> `; }}<gui-textinput id="signup-email" label="Email" hint="Leave the field to check it."></gui-textinput>
<script type="module"> const email = document.querySelector('#signup-email');
email.addEventListener('gui-change', (event) => { const valid = /^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(event.detail.value); email.errors = valid ? [] : ['Enter a complete email address, like ada@example.com.']; });</script>In a form
Section titled “In a form”With a name, <gui-textinput> submits its text under that name, and required blocks an empty
submit. Submit the empty field below, then fill it in and submit again.
import { useState, type SubmitEvent } from 'react';import { GuiButton, GuiTextinput } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function TextInputNativeForm() { 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="newsletter" onSubmit={onSubmit}> <div className="gui-grid gui-grid--row"> <div className="gui-grid__cell gui-grid__cell--span-3"> <GuiTextinput label="Email" name="email" required /> </div> <div className="gui-grid__cell"> <GuiButton type="submit" label="Subscribe" /> </div> </div> <p id="newsletter-result" role="status"> {result} </p> </form> );}import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';import '@golemui/gui-components/button';import '@golemui/gui-components/textinput';
@Component({ selector: 'app-text-input-native-form', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <form id="newsletter" #form (submit)="onSubmit($event, form)"> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell gui-grid__cell--span-3"> <gui-textinput label="Email" name="email" required></gui-textinput> </div> <div class="gui-grid__cell"> <gui-button type="submit" label="Subscribe"></gui-button> </div> </div> <p id="newsletter-result" role="status">{{ result() }}</p> </form> `,})export class TextInputNativeFormComponent { 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/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="newsletter" @submit.prevent="onSubmit"> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell gui-grid__cell--span-3"> <gui-textinput label="Email" name="email" :required="true" ></gui-textinput> </div> <div class="gui-grid__cell"> <gui-button type="submit" label="Subscribe"></gui-button> </div> </div> <p id="newsletter-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/textinput';import '@golemui/gui-components/index.css';
@customElement('text-input-native-form')export class TextInputNativeForm extends LitElement { @query('#newsletter') 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="newsletter" @submit=${this.onSubmit}> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell gui-grid__cell--span-3"> <gui-textinput label="Email" name="email" required></gui-textinput> </div> <div class="gui-grid__cell"> <gui-button type="submit" label="Subscribe"></gui-button> </div> </div> <p id="newsletter-result" role="status">${this.result}</p> </form> `; }}<form id="newsletter"> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell gui-grid__cell--span-3"> <gui-textinput label="Email" name="email" required></gui-textinput> </div> <div class="gui-grid__cell"> <gui-button type="submit" label="Subscribe"></gui-button> </div> </div> <p id="newsletter-result" role="status"></p></form>
<script type="module"> const form = document.querySelector('#newsletter');
form.addEventListener('submit', (event) => { event.preventDefault(); const data = Object.fromEntries(new FormData(form)); document.querySelector('#newsletter-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-textinput> renders into the light DOM, so you can style every part with plain CSS. These
are the parts and their public classes:
<gui-textinput class="gui-field"> <label class="gui-label" for="email" id="email_label"> <span class="gui-label__text">Email<span aria-hidden="true"> *</span></span> <div class="gui-widget-hint" id="email_hint" aria-hidden="true"> We never share it. </div> </label> <div class="gui-widget"> <input type="text" id="email" class="gui-widget-input gui-textinput--icon" aria-describedby="email_hint" aria-required="true" aria-invalid="true" aria-errormessage="email_errors" /> <span class="gui-widget-icon icon-mail" data-icon="icon-mail" aria-hidden="true" ></span> </div> <div class="gui-validator" id="email_errors" role="alert"> <div class="gui-validator__error">Enter a complete email address.</div> </div></gui-textinput>| 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. |
gui-textinput--icon | On the <input> when there is an icon: it makes room for it. |
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__error | One error message. |
The ids derive from uid, or from a generated id such as gui-textinput-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. |
--gui-radius-md | The corner radius. |
--gui-bg-surface | The field’s background. |
--gui-border-default | The border. |
--gui-text-default | The text, the label and the icon. |
--gui-font-family | The font. |
--gui-font-sm | The text size of the label and the field. |
--gui-font-xs | The text size of the hint and the errors. |
--gui-text-secondary | The hint. |
--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. |
--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. |
/* Taller, rounder fields everywhere. */:root { --gui-widget-size: 48px; --gui-radius-md: 12px;}
/* A brand focus color in one form only. */.checkout { --gui-border-focus: #d96c3f;}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. - 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.
| Key | Action |
|---|---|
| Tab | Moves focus to the field, and away from it. |
| Enter | Commits the value, as leaving the field does, and fires gui-change. |
| Every editing key | Works as in any native text input. |
What every element shares, such as translating the browser messages, is in Accessibility.