Password
<gui-password> is a password field. It renders a native <input type="password"> with its
label, hint and errors, and a button that shows the password as text and hides it again.
import { GuiPassword } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function PasswordBasic() { return <GuiPassword label="Password" />;}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/password';
@Component({ selector: 'app-password-basic', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `<gui-password label="Password"></gui-password>`,})export class PasswordBasicComponent {}<script setup lang="ts">import '@golemui/gui-components/password';import '@golemui/gui-components/index.css';</script>
<template> <gui-password label="Password"></gui-password></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/password';import '@golemui/gui-components/index.css';
@customElement('password-basic')export class PasswordBasic extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-password label="Password"></gui-password> `; }}<gui-password label="Password"></gui-password>The element’s entry point is @golemui/gui-components/password, which registers
<gui-password> 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 { GuiPassword } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function PasswordField() { const [password, setPassword] = useState('');
return ( <GuiPassword label="Password" name="password" autocomplete="current-password" value={password} onGuiChange={(event) => setPassword(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, GuiPasswordEvents } from '@golemui/gui-components';import '@golemui/gui-components/password';
type ChangeEvent = GuiEventMap<typeof GuiPasswordEvents>['gui-change'];
@Component({ selector: 'app-password-field', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-password label="Password" name="password" autocomplete="current-password" [value]="password" (gui-change)="onChange($event)" ></gui-password> `,})export class PasswordFieldComponent { password = '';
onChange(event: Event) { this.password = (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/password';import '@golemui/gui-components/index.css';
const password = ref('');</script>
<template> <gui-password label="Password" name="password" autocomplete="current-password" :value="password" @gui-change="password = $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/password';import '@golemui/gui-components/index.css';
@customElement('password-field')export class PasswordField extends LitElement { @state() private password = '';
protected override createRenderRoot() { return this; }
override render() { return html` <gui-password label="Password" name="password" autocomplete="current-password" .value=${this.password} @gui-change=${(event: CustomEvent) => (this.password = event.detail.value)} ></gui-password> `; }}With any bundler, such as Vite. Import the element once, anywhere, and use the tag in your markup.
<gui-password label="Password" name="password" autocomplete="current-password"></gui-password>
<script type="module" src="./main.js"></script>import '@golemui/gui-components/password';import '@golemui/gui-components/index.css';
const element = document.querySelector('gui-password');
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 password. |
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. |
showPasswordIcon
attribute show-password-icon | string | – | Icon class name of the button that shows the password, while the password is hidden. |
hidePasswordIcon
attribute hide-password-icon | string | – | Icon class name of the button that hides the password, while the password is shown. |
showPasswordLabel
attribute show-password-label | string | – | Accessible name of the button that shows the password. An empty value keeps the default. |
hidePasswordLabel
attribute hide-password-label | string | – | Accessible name of the button that hides the password. An empty value keeps the default. |
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”The show and hide button
Section titled “The show and hide button”The button shows the password as plain text, and hides it again. Without icons it reads “Show” or
“Hide”. show-password-icon and hide-password-icon replace the text with icons: each takes the
class names of an icon, and the button shows the icon of what it does next. See
Icons for the ways to draw them.
import { GuiPassword } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
// Any icon font works. These rules draw Material Icons ligatures.const iconStyles = ` .icon-visibility::before { content: 'visibility'; font-family: 'Material Icons'; }
.icon-visibility-off::before { content: 'visibility_off'; font-family: 'Material Icons'; }`;
export function PasswordToggle() { return ( <> <GuiPassword label="Wi-Fi password" showPasswordIcon="icon-visibility" hidePasswordIcon="icon-visibility-off" /> <style>{iconStyles}</style> </> );}import { Component, CUSTOM_ELEMENTS_SCHEMA, ViewEncapsulation,} from '@angular/core';import '@golemui/gui-components/password';
@Component({ selector: 'app-password-toggle', schemas: [CUSTOM_ELEMENTS_SCHEMA], encapsulation: ViewEncapsulation.None, template: ` <gui-password label="Wi-Fi password" show-password-icon="icon-visibility" hide-password-icon="icon-visibility-off" ></gui-password> `, styles: ` /* Any icon font works. These rules draw Material Icons ligatures. */ .icon-visibility::before { content: 'visibility'; font-family: 'Material Icons'; }
.icon-visibility-off::before { content: 'visibility_off'; font-family: 'Material Icons'; } `,})export class PasswordToggleComponent {}<script setup lang="ts">import '@golemui/gui-components/password';import '@golemui/gui-components/index.css';</script>
<template> <gui-password label="Wi-Fi password" show-password-icon="icon-visibility" hide-password-icon="icon-visibility-off" ></gui-password></template>
<style>/* Any icon font works. These rules draw Material Icons ligatures. */.icon-visibility::before { content: 'visibility'; font-family: 'Material Icons';}
.icon-visibility-off::before { content: 'visibility_off'; font-family: 'Material Icons';}</style>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/password';import '@golemui/gui-components/index.css';
@customElement('password-toggle')export class PasswordToggle extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-password label="Wi-Fi password" show-password-icon="icon-visibility" hide-password-icon="icon-visibility-off" ></gui-password>
<style> /* Any icon font works. These rules draw Material Icons ligatures. */ .icon-visibility::before { content: 'visibility'; font-family: 'Material Icons'; }
.icon-visibility-off::before { content: 'visibility_off'; font-family: 'Material Icons'; } </style> `; }}<gui-password label="Wi-Fi password" show-password-icon="icon-visibility" hide-password-icon="icon-visibility-off"></gui-password>
<style> /* Any icon font works. These rules draw Material Icons ligatures. */ .icon-visibility::before { content: 'visibility'; font-family: 'Material Icons'; }
.icon-visibility-off::before { content: 'visibility_off'; font-family: 'Material Icons'; }</style>The button’s accessible name is “Show password” or “Hide password”. Change it with
show-password-label and hide-password-label, or translate it for the whole app, see
Accessibility. Without icons, these labels also replace the visible text.
Errors
Section titled “Errors”errors takes a list of messages from your own validation, and the field is marked invalid. To
check the password as the user works, set the errors property from a gui-change listener,
which fires when they leave the field or press Enter.
import { useState } from 'react';import { GuiPassword } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function PasswordValidation() { const [errors, setErrors] = useState<string[]>([]);
return ( <GuiPassword id="new-password" label="New password" hint="At least 12 characters." autocomplete="new-password" errors={errors} onGuiChange={(event) => { const length = (event.detail.value ?? '').length; setErrors( length >= 12 ? [] : [`Use at least 12 characters. This one has ${length}.`], ); }} /> );}import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';import type { GuiEventMap, GuiPasswordEvents } from '@golemui/gui-components';import '@golemui/gui-components/password';
type PasswordEvent<K extends keyof typeof GuiPasswordEvents> = GuiEventMap< typeof GuiPasswordEvents>[K];
@Component({ selector: 'app-password-validation', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-password id="new-password" label="New password" hint="At least 12 characters." autocomplete="new-password" [errors]="errors()" (gui-change)="onChange($event)" ></gui-password> `,})export class PasswordValidationComponent { errors = signal<string[]>([]);
onChange(event: Event) { const length = ((event as PasswordEvent<'gui-change'>).detail.value ?? '') .length; this.errors.set( length >= 12 ? [] : [`Use at least 12 characters. This one has ${length}.`], ); }}<script setup lang="ts">import { ref } from 'vue';import '@golemui/gui-components/password';import '@golemui/gui-components/index.css';
const errors = ref<string[]>([]);
function onChange(value: string | undefined) { const length = (value ?? '').length; errors.value = length >= 12 ? [] : [`Use at least 12 characters. This one has ${length}.`];}</script>
<template> <gui-password id="new-password" label="New password" hint="At least 12 characters." autocomplete="new-password" :errors.prop="errors" @gui-change="onChange($event.detail.value)" ></gui-password></template>import { LitElement, html } from 'lit';import { customElement, state } from 'lit/decorators.js';import type { GuiEventMap, GuiPasswordEvents } from '@golemui/gui-components';import '@golemui/gui-components/password';import '@golemui/gui-components/index.css';
type PasswordEvent<K extends keyof typeof GuiPasswordEvents> = GuiEventMap< typeof GuiPasswordEvents>[K];
@customElement('password-validation')export class PasswordValidation extends LitElement { @state() private errors: string[] = [];
protected override createRenderRoot() { return this; }
private onChange(event: PasswordEvent<'gui-change'>) { const length = (event.detail.value ?? '').length; this.errors = length >= 12 ? [] : [`Use at least 12 characters. This one has ${length}.`]; }
override render() { return html` <gui-password id="new-password" label="New password" hint="At least 12 characters." autocomplete="new-password" .errors=${this.errors} @gui-change=${this.onChange} ></gui-password> `; }}<gui-password id="new-password" label="New password" hint="At least 12 characters." autocomplete="new-password"></gui-password>
<script type="module"> const password = document.querySelector('#new-password');
password.addEventListener('gui-change', (event) => { const length = (event.detail.value ?? '').length; password.errors = length >= 12 ? [] : [`Use at least 12 characters. This one has ${length}.`]; });</script>disabled and readonly work as on the text input. A
disabled field disables its button too; a read-only field keeps the button, so the user can still
reveal the password.
In a form
Section titled “In a form”With a name, <gui-password> submits the password as text under that name, and required
blocks an empty submit. The field submits the same value whether the password shows or not.
Set autocomplete so password managers know what to fill in: current-password on a sign-in
form, as below, and new-password where the user picks a password.
import { useState, type SubmitEvent } from 'react';import { GuiButton, GuiPassword, GuiTextinput,} from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function PasswordNativeForm() { 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="sign-in" onSubmit={onSubmit}> <div className="gui-grid gui-grid--row"> <div className="gui-grid__cell"> <GuiTextinput label="Email" name="email" autocomplete="username" required /> </div> <div className="gui-grid__cell"> <GuiPassword label="Password" name="password" autocomplete="current-password" required /> </div> </div> <GuiButton type="submit" label="Sign in" /> <p id="sign-in-result" role="status"> {result} </p> </form> );}import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';import '@golemui/gui-components/button';import '@golemui/gui-components/password';import '@golemui/gui-components/textinput';
@Component({ selector: 'app-password-native-form', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <form id="sign-in" #form (submit)="onSubmit($event, form)"> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-textinput label="Email" name="email" autocomplete="username" required ></gui-textinput> </div> <div class="gui-grid__cell"> <gui-password label="Password" name="password" autocomplete="current-password" required ></gui-password> </div> </div> <gui-button type="submit" label="Sign in"></gui-button> <p id="sign-in-result" role="status">{{ result() }}</p> </form> `,})export class PasswordNativeFormComponent { 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/password';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="sign-in" @submit.prevent="onSubmit"> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-textinput label="Email" name="email" autocomplete="username" :required="true" ></gui-textinput> </div> <div class="gui-grid__cell"> <gui-password label="Password" name="password" autocomplete="current-password" :required="true" ></gui-password> </div> </div> <gui-button type="submit" label="Sign in"></gui-button> <p id="sign-in-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/password';import '@golemui/gui-components/textinput';import '@golemui/gui-components/index.css';
@customElement('password-native-form')export class PasswordNativeForm extends LitElement { @query('#sign-in') 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="sign-in" @submit=${this.onSubmit}> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-textinput label="Email" name="email" autocomplete="username" required ></gui-textinput> </div> <div class="gui-grid__cell"> <gui-password label="Password" name="password" autocomplete="current-password" required ></gui-password> </div> </div> <gui-button type="submit" label="Sign in"></gui-button> <p id="sign-in-result" role="status">${this.result}</p> </form> `; }}<form id="sign-in"> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell"> <gui-textinput label="Email" name="email" autocomplete="username" required></gui-textinput> </div> <div class="gui-grid__cell"> <gui-password label="Password" name="password" autocomplete="current-password" required ></gui-password> </div> </div> <gui-button type="submit" label="Sign in"></gui-button> <p id="sign-in-result" role="status"></p></form>
<script type="module"> const form = document.querySelector('#sign-in');
form.addEventListener('submit', (event) => { event.preventDefault(); const data = Object.fromEntries(new FormData(form)); document.querySelector('#sign-in-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-password> renders into the light DOM, so you can style every part with plain CSS. These
are the parts and their public classes:
<gui-password class="gui-field"> <label class="gui-label" for="password" id="password_label"> <span class="gui-label__text" >Password<span aria-hidden="true"> *</span></span > <div class="gui-widget-hint" id="password_hint" aria-hidden="true"> At least 12 characters. </div> </label> <div class="gui-widget"> <input type="password" id="password" class="gui-widget-input gui-password--icon" aria-describedby="password_hint" aria-required="true" /> <span class="gui-widget-icon icon-lock" data-icon="icon-lock" aria-hidden="true" ></span> <button type="button" class="gui-password__toggle gui-widget-icon" aria-label="Show password" > <span aria-hidden="true">Show</span> </button> </div> <div class="gui-validator gui-validator--empty" id="password_errors" role="alert" ></div></gui-password>| 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-password--icon | On the <input> when there is an icon: it makes room for it. |
gui-widget-icon | The icon, with your icon classes. The button has it too. |
gui-password__toggle | The show and hide button. With icons it also has the classes of the icon it shows, and their value in data-icon. |
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 input’s type turns to text while the password shows. The ids derive from uid, or from a
generated id such as gui-password-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. |
--gui-border-default | The border. |
--gui-text-default | The text, the label, the icon and the button. |
--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. |
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. - The show and hide button. Its accessible name says what it does: “Show password”, then
“Hide password”.
show-password-labelandhide-password-labelchange it on one field; an empty value keeps the default. To translate it for the whole app, use the keysshowPasswordandhidePassword, andshowandhidefor the visible text, see i18n. - Icons. The icons are hidden from assistive technology; the button’s name carries the meaning.
| Key | Action |
|---|---|
| Tab | Moves focus to the field, then to the show and hide button. |
| Enter in the field | Commits the value, as leaving the field does, and fires gui-change. |
| Enter or Space on the button | Shows or hides the password. |
| Every editing key | Works as in any native password input. |
What every element shares, such as translating the browser messages, is in Accessibility.