Button
<gui-button> is a button. It renders a native <button>, so it submits and resets forms like
one, or a link styled as a button when you give it an href.
import { GuiButton } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function ButtonBasic() { return <GuiButton label="Save changes" />;}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/button';
@Component({ selector: 'app-button-basic', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `<gui-button label="Save changes"></gui-button>`,})export class ButtonBasicComponent {}<script setup lang="ts">import '@golemui/gui-components/button';import '@golemui/gui-components/index.css';</script>
<template> <gui-button label="Save changes"></gui-button></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/button';import '@golemui/gui-components/index.css';
@customElement('button-basic')export class ButtonBasic extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-button label="Save changes"></gui-button> `; }}<gui-button label="Save changes"></gui-button>The element’s entry point is @golemui/gui-components/button, which registers <gui-button> 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 { GuiButton } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function ButtonExample() { return ( <GuiButton label="Save changes" /> );}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 '@golemui/gui-components/button';
@Component({ selector: 'app-button-example', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-button label="Save changes" ></gui-button> `,})export class ButtonExampleComponent {}"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 '@golemui/gui-components/button';import '@golemui/gui-components/index.css';</script>
<template> <gui-button label="Save changes" /></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 } from 'lit/decorators.js';import '@golemui/gui-components/button';import '@golemui/gui-components/index.css';
@customElement('button-example')export class ButtonExample extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-button label="Save changes" ></gui-button> `; }}With any bundler, such as Vite. Import the element once, anywhere, and use the tag in your markup.
<gui-button label="Save changes"></gui-button>
<script type="module" src="./main.js"></script>import '@golemui/gui-components/button';import '@golemui/gui-components/index.css';Listen for click on the element, as on any button. It has no events of its own.
Properties
| Property | Type | Default | Description |
|---|---|---|---|
label | string | – | Text of the button. |
disabled | boolean | false | Disables the button. A disabled link loses its href. |
loading | boolean | false | Shows a spinner and ignores clicks, while keeping the button focusable, for an action in progress. |
icon | string | – | Icon class name shown next to the text. |
variant | 'filled' | 'outlined' | 'link' | 'filled' | Visual style of the button: link looks like a link. |
size | 'sm' | 'md' | 'lg' | 'md' | Height of the button. |
iconPosition
attribute icon-position | 'left' | 'right' | 'left' | Side of the text the icon is on. |
type | 'button' | 'submit' | 'reset' | 'button' | The native button type: submit submits the form it is in, and reset resets it. |
name | string | – | Name sent with the form submission when this button submits it. |
value | string | – | Value sent under name when this button submits the form. |
form | string | – | The id of the form the button belongs to, when it is outside it. |
href | string | – | Makes the button a link to this URL. |
target | string | – | Where the link opens, such as _blank. |
rel | string | – | The link's relationship to the page, such as noopener. |
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. |
Examples
Section titled “Examples”Variants
Section titled “Variants”variant sets the style: filled, the default, for the main action, outlined for a secondary
one, and link for a button that looks like a link.
import { GuiButton } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function ButtonVariants() { return ( <div className="gui-grid gui-grid--row gui-grid--justify-start"> <div className="gui-grid__cell"> <GuiButton label="Publish" /> </div> <div className="gui-grid__cell"> <GuiButton variant="outlined" label="Preview" /> </div> <div className="gui-grid__cell"> <GuiButton variant="link" label="Discard draft" /> </div> </div> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/button';
@Component({ selector: 'app-button-variants', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <div class="gui-grid gui-grid--row gui-grid--justify-start"> <div class="gui-grid__cell"> <gui-button label="Publish"></gui-button> </div> <div class="gui-grid__cell"> <gui-button variant="outlined" label="Preview"></gui-button> </div> <div class="gui-grid__cell"> <gui-button variant="link" label="Discard draft"></gui-button> </div> </div> `,})export class ButtonVariantsComponent {}<script setup lang="ts">import '@golemui/gui-components/button';import '@golemui/gui-components/index.css';</script>
<template> <div class="gui-grid gui-grid--row gui-grid--justify-start"> <div class="gui-grid__cell"> <gui-button label="Publish"></gui-button> </div> <div class="gui-grid__cell"> <gui-button variant="outlined" label="Preview"></gui-button> </div> <div class="gui-grid__cell"> <gui-button variant="link" label="Discard draft"></gui-button> </div> </div></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/button';import '@golemui/gui-components/index.css';
@customElement('button-variants')export class ButtonVariants extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <div class="gui-grid gui-grid--row gui-grid--justify-start"> <div class="gui-grid__cell"> <gui-button label="Publish"></gui-button> </div> <div class="gui-grid__cell"> <gui-button variant="outlined" label="Preview"></gui-button> </div> <div class="gui-grid__cell"> <gui-button variant="link" label="Discard draft"></gui-button> </div> </div> `; }}<div class="gui-grid gui-grid--row gui-grid--justify-start"> <div class="gui-grid__cell"> <gui-button label="Publish"></gui-button> </div> <div class="gui-grid__cell"> <gui-button variant="outlined" label="Preview"></gui-button> </div> <div class="gui-grid__cell"> <gui-button variant="link" label="Discard draft"></gui-button> </div></div>size sets the height: sm, md, the default, or lg.
import { GuiButton } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function ButtonSizes() { return ( <div className="gui-grid gui-grid--row gui-grid--justify-start"> <div className="gui-grid__cell"> <GuiButton size="sm" label="Small" /> </div> <div className="gui-grid__cell"> <GuiButton label="Medium" /> </div> <div className="gui-grid__cell"> <GuiButton size="lg" label="Large" /> </div> </div> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/button';
@Component({ selector: 'app-button-sizes', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <div class="gui-grid gui-grid--row gui-grid--justify-start"> <div class="gui-grid__cell"> <gui-button size="sm" label="Small"></gui-button> </div> <div class="gui-grid__cell"> <gui-button label="Medium"></gui-button> </div> <div class="gui-grid__cell"> <gui-button size="lg" label="Large"></gui-button> </div> </div> `,})export class ButtonSizesComponent {}<script setup lang="ts">import '@golemui/gui-components/button';import '@golemui/gui-components/index.css';</script>
<template> <div class="gui-grid gui-grid--row gui-grid--justify-start"> <div class="gui-grid__cell"> <gui-button size="sm" label="Small"></gui-button> </div> <div class="gui-grid__cell"> <gui-button label="Medium"></gui-button> </div> <div class="gui-grid__cell"> <gui-button size="lg" label="Large"></gui-button> </div> </div></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/button';import '@golemui/gui-components/index.css';
@customElement('button-sizes')export class ButtonSizes extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <div class="gui-grid gui-grid--row gui-grid--justify-start"> <div class="gui-grid__cell"> <gui-button size="sm" label="Small"></gui-button> </div> <div class="gui-grid__cell"> <gui-button label="Medium"></gui-button> </div> <div class="gui-grid__cell"> <gui-button size="lg" label="Large"></gui-button> </div> </div> `; }}<div class="gui-grid gui-grid--row gui-grid--justify-start"> <div class="gui-grid__cell"> <gui-button size="sm" label="Small"></gui-button> </div> <div class="gui-grid__cell"> <gui-button label="Medium"></gui-button> </div> <div class="gui-grid__cell"> <gui-button size="lg" label="Large"></gui-button> </div></div>icon takes the class names of an icon, from any icon font or your own CSS, and shows it before
the text. icon-position="right" puts it after. The icon is decorative and hidden from assistive
technology. See Icons for the ways to draw it.
import { GuiButton } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
// Any icon font works: icon sets these classes on the icon element.// These draw Material Icons ligatures.const iconStyles = ` .icon-download::before { content: 'download'; font-family: 'Material Icons'; }
.icon-arrow::before { content: 'arrow_forward'; font-family: 'Material Icons'; }`;
export function ButtonIcon() { return ( <> <div className="gui-grid gui-grid--row gui-grid--justify-start"> <div className="gui-grid__cell"> <GuiButton icon="icon-download" label="Download invoice" /> </div> <div className="gui-grid__cell"> <GuiButton variant="outlined" icon="icon-arrow" iconPosition="right" label="Next step" /> </div> </div> <style>{iconStyles}</style> </> );}import { Component, CUSTOM_ELEMENTS_SCHEMA, ViewEncapsulation,} from '@angular/core';import '@golemui/gui-components/button';
@Component({ selector: 'app-button-icon', schemas: [CUSTOM_ELEMENTS_SCHEMA], encapsulation: ViewEncapsulation.None, template: ` <div class="gui-grid gui-grid--row gui-grid--justify-start"> <div class="gui-grid__cell"> <gui-button icon="icon-download" label="Download invoice"></gui-button> </div> <div class="gui-grid__cell"> <gui-button variant="outlined" icon="icon-arrow" icon-position="right" label="Next step" ></gui-button> </div> </div> `, styles: ` /* Any icon font works: icon sets these classes on the icon element. These draw Material Icons ligatures. */ .icon-download::before { content: 'download'; font-family: 'Material Icons'; }
.icon-arrow::before { content: 'arrow_forward'; font-family: 'Material Icons'; } `,})export class ButtonIconComponent {}<script setup lang="ts">import '@golemui/gui-components/button';import '@golemui/gui-components/index.css';</script>
<template> <div class="gui-grid gui-grid--row gui-grid--justify-start"> <div class="gui-grid__cell"> <gui-button icon="icon-download" label="Download invoice"></gui-button> </div> <div class="gui-grid__cell"> <gui-button variant="outlined" icon="icon-arrow" icon-position="right" label="Next step" ></gui-button> </div> </div></template>
<style>/* Any icon font works: icon sets these classes on the icon element. These draw Material Icons ligatures. */.icon-download::before { content: 'download'; font-family: 'Material Icons';}
.icon-arrow::before { content: 'arrow_forward'; font-family: 'Material Icons';}</style>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/button';import '@golemui/gui-components/index.css';
@customElement('button-icon')export class ButtonIcon extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <div class="gui-grid gui-grid--row gui-grid--justify-start"> <div class="gui-grid__cell"> <gui-button icon="icon-download" label="Download invoice" ></gui-button> </div> <div class="gui-grid__cell"> <gui-button variant="outlined" icon="icon-arrow" icon-position="right" label="Next step" ></gui-button> </div> </div>
<style> /* Any icon font works: icon sets these classes on the icon element. These draw Material Icons ligatures. */ .icon-download::before { content: 'download'; font-family: 'Material Icons'; }
.icon-arrow::before { content: 'arrow_forward'; font-family: 'Material Icons'; } </style> `; }}<div class="gui-grid gui-grid--row gui-grid--justify-start"> <div class="gui-grid__cell"> <gui-button icon="icon-download" label="Download invoice"></gui-button> </div> <div class="gui-grid__cell"> <gui-button variant="outlined" icon="icon-arrow" icon-position="right" label="Next step" ></gui-button> </div></div>
<style> /* Any icon font works: icon sets these classes on the icon element. These draw Material Icons ligatures. */ .icon-download::before { content: 'download'; font-family: 'Material Icons'; }
.icon-arrow::before { content: 'arrow_forward'; font-family: 'Material Icons'; }</style>Loading
Section titled “Loading”loading shows a spinner in place of the icon and ignores clicks: your click listeners don’t
run, so a second click can’t send the request twice. Unlike disabled, the button keeps its
focus, and screen readers hear it is busy. Click the button to try it.
import { useState } from 'react';import { GuiButton } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function ButtonLoading() { const [loading, setLoading] = useState(false);
const send = () => { setLoading(true); // Stands in for a request to your server. setTimeout(() => setLoading(false), 2000); };
return ( <GuiButton id="send-invite" label={loading ? 'Sending…' : 'Send invite'} loading={loading} onClick={send} /> );}import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';import '@golemui/gui-components/button';
@Component({ selector: 'app-button-loading', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <gui-button id="send-invite" [label]="sending() ? 'Sending…' : 'Send invite'" [loading]="sending()" (click)="send()" ></gui-button> `,})export class ButtonLoadingComponent { sending = signal(false);
send() { this.sending.set(true); // Stands in for a request to your server. setTimeout(() => this.sending.set(false), 2000); }}<script setup lang="ts">import { ref } from 'vue';import '@golemui/gui-components/button';import '@golemui/gui-components/index.css';
const sending = ref(false);
function send() { sending.value = true; // Stands in for a request to your server. setTimeout(() => { sending.value = false; }, 2000);}</script>
<template> <gui-button id="send-invite" :label="sending ? 'Sending…' : 'Send invite'" :loading="sending" @click="send" ></gui-button></template>import { LitElement, html } from 'lit';import { customElement, state } from 'lit/decorators.js';import '@golemui/gui-components/button';import '@golemui/gui-components/index.css';
@customElement('button-loading')export class ButtonLoading extends LitElement { @state() private sending = false;
protected override createRenderRoot() { return this; }
private send() { this.sending = true; // Stands in for a request to your server. setTimeout(() => { this.sending = false; }, 2000); }
override render() { return html` <gui-button id="send-invite" label=${this.sending ? 'Sending…' : 'Send invite'} ?loading=${this.sending} @click=${this.send} ></gui-button> `; }}<gui-button id="send-invite" label="Send invite"></gui-button>
<script type="module"> const button = document.querySelector('#send-invite');
button.addEventListener('click', () => { button.loading = true; button.label = 'Sending…'; // Stands in for a request to your server. setTimeout(() => { button.loading = false; button.label = 'Send invite'; }, 2000); });</script>Disabled
Section titled “Disabled”A disabled button can’t be focused or clicked. Prefer to leave a button enabled and explain what
is missing when it’s clicked: a disabled button gives no reason.
import { GuiButton } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function ButtonDisabled() { return <GuiButton label="Delete project" disabled />;}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/button';
@Component({ selector: 'app-button-disabled', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: `<gui-button label="Delete project" disabled></gui-button>`,})export class ButtonDisabledComponent {}<script setup lang="ts">import '@golemui/gui-components/button';import '@golemui/gui-components/index.css';</script>
<template> <gui-button label="Delete project" :disabled="true"></gui-button></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/button';import '@golemui/gui-components/index.css';
@customElement('button-disabled')export class ButtonDisabled extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <gui-button label="Delete project" disabled></gui-button> `; }}<gui-button label="Delete project" disabled></gui-button>With href, the element renders a link that looks like a button, for navigation rather than
actions. target and rel go on the link. A disabled link loses its href, so it goes nowhere.
import { GuiButton } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function ButtonLink() { return ( <div className="gui-grid gui-grid--row gui-grid--justify-start"> <div className="gui-grid__cell"> <GuiButton href="/components/docs/" label="Read the docs" /> </div> <div className="gui-grid__cell"> <GuiButton variant="outlined" href="https://github.com/" target="_blank" rel="noopener" label="Open GitHub" /> </div> <div className="gui-grid__cell"> <GuiButton variant="link" href="/components/docs/" label="Archived plans" disabled /> </div> </div> );}import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';import '@golemui/gui-components/button';
@Component({ selector: 'app-button-link', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <div class="gui-grid gui-grid--row gui-grid--justify-start"> <div class="gui-grid__cell"> <gui-button href="/components/docs/" label="Read the docs"></gui-button> </div> <div class="gui-grid__cell"> <gui-button variant="outlined" href="https://github.com/" target="_blank" rel="noopener" label="Open GitHub" ></gui-button> </div> <div class="gui-grid__cell"> <gui-button variant="link" href="/components/docs/" label="Archived plans" disabled ></gui-button> </div> </div> `,})export class ButtonLinkComponent {}<script setup lang="ts">import '@golemui/gui-components/button';import '@golemui/gui-components/index.css';</script>
<template> <div class="gui-grid gui-grid--row gui-grid--justify-start"> <div class="gui-grid__cell"> <gui-button href="/components/docs/" label="Read the docs"></gui-button> </div> <div class="gui-grid__cell"> <gui-button variant="outlined" href="https://github.com/" target="_blank" rel="noopener" label="Open GitHub" ></gui-button> </div> <div class="gui-grid__cell"> <gui-button variant="link" href="/components/docs/" label="Archived plans" :disabled="true" ></gui-button> </div> </div></template>import { LitElement, html } from 'lit';import { customElement } from 'lit/decorators.js';import '@golemui/gui-components/button';import '@golemui/gui-components/index.css';
@customElement('button-link')export class ButtonLink extends LitElement { protected override createRenderRoot() { return this; }
override render() { return html` <div class="gui-grid gui-grid--row gui-grid--justify-start"> <div class="gui-grid__cell"> <gui-button href="/components/docs/" label="Read the docs" ></gui-button> </div> <div class="gui-grid__cell"> <gui-button variant="outlined" href="https://github.com/" target="_blank" rel="noopener" label="Open GitHub" ></gui-button> </div> <div class="gui-grid__cell"> <gui-button variant="link" href="/components/docs/" label="Archived plans" disabled ></gui-button> </div> </div> `; }}<div class="gui-grid gui-grid--row gui-grid--justify-start"> <div class="gui-grid__cell"> <gui-button href="/components/docs/" label="Read the docs"></gui-button> </div> <div class="gui-grid__cell"> <gui-button variant="outlined" href="https://github.com/" target="_blank" rel="noopener" label="Open GitHub" ></gui-button> </div> <div class="gui-grid__cell"> <gui-button variant="link" href="/components/docs/" label="Archived plans" disabled></gui-button> </div></div>In a form
Section titled “In a form”The element renders a native <button> inside the form, so it works like one:
type="submit"submits the form.requiredand the other checks of the form’s fields run first. Enter in a field submits the form too, through its first submit button.nameandvalueare sent with the submission when that button submits it. The form’ssubmitevent has the inner<button>asevent.submitter: pass it toFormDatato read them.type="reset"resets the form: each field gets back the value it started with.type="button", the default, does nothing in a form: handle itsclickyourself.
Pick a decision below: the form submits the comment and the button’s decision.
import { useState, type SubmitEvent } from 'react';import { GuiButton, GuiTextinput } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
const initialComment = 'Looks good to me.';
export function ButtonNativeForm() { const [comment, setComment] = useState<string | undefined>(initialComment); const [result, setResult] = useState('');
const onSubmit = (event: SubmitEvent<HTMLFormElement>) => { event.preventDefault(); // event.submitter is the inner <button> that submitted the form. const data = Object.fromEntries( new FormData(event.currentTarget, event.submitter), ); setResult(`Submitted ${JSON.stringify(data)}`); };
return ( <form id="review-form" onSubmit={onSubmit} onReset={() => setComment(initialComment)} > <div className="gui-grid gui-grid--row"> <div className="gui-grid__cell gui-grid__cell--span-2"> <GuiTextinput label="Comment" name="comment" value={comment} onGuiInput={(event) => setComment(event.detail.value)} /> </div> <div className="gui-grid__cell"> <GuiButton type="submit" name="decision" value="approve" label="Approve" /> </div> <div className="gui-grid__cell"> <GuiButton type="submit" name="decision" value="decline" variant="outlined" label="Decline" /> </div> <div className="gui-grid__cell"> <GuiButton type="reset" variant="link" label="Reset" /> </div> </div> <p id="review-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-button-native-form', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <form id="review-form" #form (submit)="onSubmit($event, form)"> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell gui-grid__cell--span-2"> <gui-textinput label="Comment" name="comment" value="Looks good to me." ></gui-textinput> </div> <div class="gui-grid__cell"> <gui-button type="submit" name="decision" value="approve" label="Approve" ></gui-button> </div> <div class="gui-grid__cell"> <gui-button type="submit" name="decision" value="decline" variant="outlined" label="Decline" ></gui-button> </div> <div class="gui-grid__cell"> <gui-button type="reset" variant="link" label="Reset"></gui-button> </div> </div> <p id="review-result" role="status">{{ result() }}</p> </form> `,})export class ButtonNativeFormComponent { result = signal('');
onSubmit(event: SubmitEvent, form: HTMLFormElement) { event.preventDefault(); // event.submitter is the inner <button> that submitted the form. const data = Object.fromEntries(new FormData(form, event.submitter)); 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; // event.submitter is the inner <button> that submitted the form. const data = Object.fromEntries( new FormData(event.currentTarget, event.submitter), ); result.value = `Submitted ${JSON.stringify(data)}`;}</script>
<template> <form id="review-form" @submit.prevent="onSubmit"> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell gui-grid__cell--span-2"> <gui-textinput label="Comment" name="comment" value="Looks good to me." ></gui-textinput> </div> <div class="gui-grid__cell"> <gui-button type="submit" name="decision" value="approve" label="Approve" ></gui-button> </div> <div class="gui-grid__cell"> <gui-button type="submit" name="decision" value="decline" variant="outlined" label="Decline" ></gui-button> </div> <div class="gui-grid__cell"> <gui-button type="reset" variant="link" label="Reset"></gui-button> </div> </div> <p id="review-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('button-native-form')export class ButtonNativeForm extends LitElement { @query('#review-form') private form!: HTMLFormElement; @state() private result = '';
protected override createRenderRoot() { return this; }
private onSubmit(event: SubmitEvent) { event.preventDefault(); // event.submitter is the inner <button> that submitted the form. const data = Object.fromEntries(new FormData(this.form, event.submitter)); this.result = `Submitted ${JSON.stringify(data)}`; }
override render() { return html` <form id="review-form" @submit=${this.onSubmit}> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell gui-grid__cell--span-2"> <gui-textinput label="Comment" name="comment" value="Looks good to me." ></gui-textinput> </div> <div class="gui-grid__cell"> <gui-button type="submit" name="decision" value="approve" label="Approve" ></gui-button> </div> <div class="gui-grid__cell"> <gui-button type="submit" name="decision" value="decline" variant="outlined" label="Decline" ></gui-button> </div> <div class="gui-grid__cell"> <gui-button type="reset" variant="link" label="Reset"></gui-button> </div> </div> <p id="review-result" role="status">${this.result}</p> </form> `; }}<form id="review-form"> <div class="gui-grid gui-grid--row"> <div class="gui-grid__cell gui-grid__cell--span-2"> <gui-textinput label="Comment" name="comment" value="Looks good to me."></gui-textinput> </div> <div class="gui-grid__cell"> <gui-button type="submit" name="decision" value="approve" label="Approve"></gui-button> </div> <div class="gui-grid__cell"> <gui-button type="submit" name="decision" value="decline" variant="outlined" label="Decline" ></gui-button> </div> <div class="gui-grid__cell"> <gui-button type="reset" variant="link" label="Reset"></gui-button> </div> </div> <p id="review-result" role="status"></p></form>
<script type="module"> const form = document.querySelector('#review-form');
form.addEventListener('submit', (event) => { event.preventDefault(); // event.submitter is the inner <button> that submitted the form. const data = Object.fromEntries(new FormData(form, event.submitter)); document.querySelector('#review-result').textContent = `Submitted ${JSON.stringify(data)}`; });</script>A button outside its form submits it with form, the id of the form, like a native button:
import { useState, type SubmitEvent } from 'react';import { GuiButton, GuiTextinput } from '@golemui/gui-components/react';import '@golemui/gui-components/index.css';
export function ButtonOutsideForm() { 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-signup" onSubmit={onSubmit}> <GuiTextinput label="Email" name="email" required /> <p id="newsletter-signup-result" role="status"> {result} </p> </form>
<GuiButton type="submit" form="newsletter-signup" label="Subscribe" /> </> );}import { Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';import '@golemui/gui-components/button';import '@golemui/gui-components/textinput';
@Component({ selector: 'app-button-outside-form', schemas: [CUSTOM_ELEMENTS_SCHEMA], template: ` <form id="newsletter-signup" #form (submit)="onSubmit($event, form)"> <gui-textinput label="Email" name="email" required></gui-textinput> <p id="newsletter-signup-result" role="status">{{ result() }}</p> </form>
<gui-button type="submit" form="newsletter-signup" label="Subscribe" ></gui-button> `,})export class ButtonOutsideFormComponent { 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-signup" @submit.prevent="onSubmit"> <gui-textinput label="Email" name="email" :required="true"></gui-textinput> <p id="newsletter-signup-result" role="status">{{ result }}</p> </form>
<gui-button type="submit" form="newsletter-signup" label="Subscribe" ></gui-button></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('button-outside-form')export class ButtonOutsideForm extends LitElement { @query('#newsletter-signup') 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-signup" @submit=${this.onSubmit}> <gui-textinput label="Email" name="email" required></gui-textinput> <p id="newsletter-signup-result" role="status">${this.result}</p> </form>
<gui-button type="submit" form="newsletter-signup" label="Subscribe" ></gui-button> `; }}<form id="newsletter-signup"> <gui-textinput label="Email" name="email" required></gui-textinput> <p id="newsletter-signup-result" role="status"></p></form>
<gui-button type="submit" form="newsletter-signup" label="Subscribe"></gui-button>
<script type="module"> const form = document.querySelector('#newsletter-signup');
form.addEventListener('submit', (event) => { event.preventDefault(); const data = Object.fromEntries(new FormData(form)); document.querySelector('#newsletter-signup-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-button> renders into the light DOM, so you can style every part with plain CSS. These
are the parts and their public classes:
<gui-button class="gui-field"> <div class="gui-widget"> <button type="submit" id="gui-button-1" class="gui-button-with-icon gui-button-icon-left gui-button--outlined" > <span class="gui-widget-icon gui-button-icon icon-download" data-icon="icon-download" aria-hidden="true" ></span> <span>Download invoice</span> </button> </div></gui-button>With href, an <a> with the same classes takes the place of the <button>. While loading,
the spinner takes the place of the icon:
<button type="button" class="gui-button-with-icon gui-button-icon-left gui-button--loading" aria-disabled="true" aria-busy="true"> <span class="gui-button-icon gui-spinner"><svg>…</svg></span> <span>Sending…</span></button>| Class | Part |
|---|---|
gui-field | The element itself. Lines the button up with the fields of a gui-grid row. |
gui-widget | The box around the button. |
gui-button-with-icon | On the button when it has an icon or a spinner. |
gui-button-icon-left, gui-button-icon-right | On the button: the side of the text the icon is on. |
gui-button--outlined, gui-button--link | On the button: the outlined and link variants. |
gui-button--sm, gui-button--lg | On the button: the sm and lg sizes. |
gui-button--loading | On the button while loading. |
gui-button-icon | The icon or the spinner. |
gui-widget-icon | The icon, with your icon classes. |
gui-spinner | The spinner. |
The stylesheet styles the native <button> and <a> inside gui-button, so select them as
gui-button button and gui-button a.
Tokens
Section titled “Tokens”The button reads these design tokens. Set them on :root to change every button, or on any
element to change the buttons inside it:
| Token | What it styles |
|---|---|
--gui-intent-primary | The background of filled, and the border and text of the others. |
--gui-intent-primary-text | The text of filled. |
--gui-intent-primary-hover | The background of filled on hover. |
--gui-intent-primary-active | The background of filled while pressed, and the text of link. |
--gui-radius-md | The corner radius. |
--gui-shadow-focus | The focus ring. |
--gui-border-focus | The border of outlined while focused. |
--gui-transition-normal | The speed of the background change on hover. |
--gui-font-family | The font. |
--gui-font-sm | The text size. |
The heights are fixed, 32px, 42px and 50px for sm, md and lg: set height on
gui-button button to change them.
/* Pill-shaped buttons in a brand color, in one section only. */.pricing gui-button { --gui-radius-md: 999px; --gui-intent-primary: #d96c3f; --gui-intent-primary-hover: #c25a30; --gui-intent-primary-active: #a84b26;}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 button. Always set it: an icon-only button has no other way to get a name. - Loading. While
loading, the button hasaria-busy="true"andaria-disabled="true". It stays in the tab order and keeps focus, so the user isn’t lost when the action finishes. - Disabled. A disabled button is a native disabled
<button>: it leaves the tab order. A disabled link has nohref, keeps thelinkrole and hasaria-disabled="true". - Link or button. Use
hreffor navigation and a button for actions, so screen readers announce what will happen.
| Key | Action |
|---|---|
| Tab | Moves focus to the button, and away from it. |
| Enter | Activates the button, or follows the link. |
| Space | Activates the button, not a link. |
What every element shares is in Accessibility.