Button

Essential button primitive that enhances semantic button markup and styling hooks. Ghost choices use the shared selection surface and text tokens for their selected visual state while the composing control owns ARIA selection semantics. Unselected disabled ghost actions stay transparent; an unavailable selected choice keeps a muted selection wash.

Default

With no properties supplied, Button uses the outline variant at its default size.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>

<ui-button>Button</ui-button>
<script setup>
  import { UiButton } from "@threadlabs/looma";
</script>

<template>
  <UiButton>Button</UiButton>
</template>
import { UiButton } from "@threadlabs/looma";

export default function Example() {
  return <UiButton>Button</UiButton>;
}
/** @jsxImportSource preact */
import { UiButton } from "@threadlabs/looma";

export default function Example() {
  return <UiButton>Button</UiButton>;
}
<script>
  import { UiButton } from "@threadlabs/looma";
</script>

<UiButton>Button</UiButton>
import { Component } from "@angular/core";
import { UiButton } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiButton],
  template: `
    <ng-template
      ui-button
    >Button</ng-template>
  `,
})
export default class Example {}

variant

Appearance sets emphasis independently from tone: solid neutral is a standard action, and danger can be an outline. Named presets combine those shared choices.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-cluster/ui-cluster.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>

<ui-cluster
  gap="s"
>
  <ui-button
    variant="solid"
    tone="accent"
  >Solid</ui-button>
  <ui-button
    variant="outline"
    tone="accent"
  >Outline</ui-button>
  <ui-button
    variant="ghost"
    tone="accent"
  >Ghost</ui-button>
  <ui-button
    variant="danger"
  >Danger</ui-button>
  <ui-button
    variant="solid"
    tone="neutral"
  >Solid neutral</ui-button>
  <ui-button
    variant="outline"
    tone="danger"
  >Danger outline</ui-button>
  <ui-button
    preset="primary"
  >Primary</ui-button>
  <ui-button
    preset="standard"
  >Standard</ui-button>
  <ui-button
    preset="quiet"
  >Quiet</ui-button>
  <ui-button
    preset="destructive"
  >Destructive</ui-button>
</ui-cluster>
<script setup>
  import { UiButton, UiCluster } from "@threadlabs/looma";
</script>

<template>
  <UiCluster
    gap="s"
  >
    <UiButton
      variant="solid"
      tone="accent"
    >Solid</UiButton>
    <UiButton
      variant="outline"
      tone="accent"
    >Outline</UiButton>
    <UiButton
      variant="ghost"
      tone="accent"
    >Ghost</UiButton>
    <UiButton
      variant="danger"
    >Danger</UiButton>
    <UiButton
      variant="solid"
      tone="neutral"
    >Solid neutral</UiButton>
    <UiButton
      variant="outline"
      tone="danger"
    >Danger outline</UiButton>
    <UiButton
      preset="primary"
    >Primary</UiButton>
    <UiButton
      preset="standard"
    >Standard</UiButton>
    <UiButton
      preset="quiet"
    >Quiet</UiButton>
    <UiButton
      preset="destructive"
    >Destructive</UiButton>
  </UiCluster>
</template>
import { UiButton, UiCluster } from "@threadlabs/looma";

export default function Example() {
  return <UiCluster
    gap="s"
  >
    <UiButton
      variant="solid"
      tone="accent"
    >Solid</UiButton>
    <UiButton
      variant="outline"
      tone="accent"
    >Outline</UiButton>
    <UiButton
      variant="ghost"
      tone="accent"
    >Ghost</UiButton>
    <UiButton
      variant="danger"
    >Danger</UiButton>
    <UiButton
      variant="solid"
      tone="neutral"
    >Solid neutral</UiButton>
    <UiButton
      variant="outline"
      tone="danger"
    >Danger outline</UiButton>
    <UiButton
      preset="primary"
    >Primary</UiButton>
    <UiButton
      preset="standard"
    >Standard</UiButton>
    <UiButton
      preset="quiet"
    >Quiet</UiButton>
    <UiButton
      preset="destructive"
    >Destructive</UiButton>
  </UiCluster>;
}
/** @jsxImportSource preact */
import { UiButton, UiCluster } from "@threadlabs/looma";

export default function Example() {
  return <UiCluster
    gap="s"
  >
    <UiButton
      variant="solid"
      tone="accent"
    >Solid</UiButton>
    <UiButton
      variant="outline"
      tone="accent"
    >Outline</UiButton>
    <UiButton
      variant="ghost"
      tone="accent"
    >Ghost</UiButton>
    <UiButton
      variant="danger"
    >Danger</UiButton>
    <UiButton
      variant="solid"
      tone="neutral"
    >Solid neutral</UiButton>
    <UiButton
      variant="outline"
      tone="danger"
    >Danger outline</UiButton>
    <UiButton
      preset="primary"
    >Primary</UiButton>
    <UiButton
      preset="standard"
    >Standard</UiButton>
    <UiButton
      preset="quiet"
    >Quiet</UiButton>
    <UiButton
      preset="destructive"
    >Destructive</UiButton>
  </UiCluster>;
}
<script>
  import { UiButton, UiCluster } from "@threadlabs/looma";
</script>

<UiCluster
  gap="s"
>
  <UiButton
    variant="solid"
    tone="accent"
  >Solid</UiButton>
  <UiButton
    variant="outline"
    tone="accent"
  >Outline</UiButton>
  <UiButton
    variant="ghost"
    tone="accent"
  >Ghost</UiButton>
  <UiButton
    variant="danger"
  >Danger</UiButton>
  <UiButton
    variant="solid"
    tone="neutral"
  >Solid neutral</UiButton>
  <UiButton
    variant="outline"
    tone="danger"
  >Danger outline</UiButton>
  <UiButton
    preset="primary"
  >Primary</UiButton>
  <UiButton
    preset="standard"
  >Standard</UiButton>
  <UiButton
    preset="quiet"
  >Quiet</UiButton>
  <UiButton
    preset="destructive"
  >Destructive</UiButton>
</UiCluster>
import { Component } from "@angular/core";
import { UiButton, UiCluster } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiCluster, UiButton],
  template: `
    <div
      ui-cluster
      [gap]="'s'"
    >
      <ng-template
        ui-button
        [variant]="'solid'"
        [tone]="'accent'"
      >Solid</ng-template>
      <ng-template
        ui-button
        [variant]="'outline'"
        [tone]="'accent'"
      >Outline</ng-template>
      <ng-template
        ui-button
        [variant]="'ghost'"
        [tone]="'accent'"
      >Ghost</ng-template>
      <ng-template
        ui-button
        [variant]="'danger'"
      >Danger</ng-template>
      <ng-template
        ui-button
        [variant]="'solid'"
        [tone]="'neutral'"
      >Solid neutral</ng-template>
      <ng-template
        ui-button
        [variant]="'outline'"
        [tone]="'danger'"
      >Danger outline</ng-template>
      <ng-template
        ui-button
        [preset]="'primary'"
      >Primary</ng-template>
      <ng-template
        ui-button
        [preset]="'standard'"
      >Standard</ng-template>
      <ng-template
        ui-button
        [preset]="'quiet'"
      >Quiet</ng-template>
      <ng-template
        ui-button
        [preset]="'destructive'"
      >Destructive</ng-template>
    </div>
  `,
})
export default class Example {}

Link

variant="link" is an inline text action: it takes the surrounding font, has no box or padding, and its underline strengthens on hover. It is still a button, with a visible focus ring.

Draft saved ·

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>

<p
  class="demo-inline-action"
> Draft saved · <ui-button
    variant="link"
  >3 unpublished changes</ui-button> </p>
<script setup>
  import { UiButton } from "@threadlabs/looma";
</script>

<template>
  <p
    class="demo-inline-action"
  > Draft saved · <UiButton
      variant="link"
    >3 unpublished changes</UiButton> </p>
</template>
import { UiButton } from "@threadlabs/looma";

export default function Example() {
  return <p
    className="demo-inline-action"
  >{" Draft saved · "}<UiButton
      variant="link"
    >3 unpublished changes</UiButton>{" "}</p>;
}
/** @jsxImportSource preact */
import { UiButton } from "@threadlabs/looma";

export default function Example() {
  return <p
    className="demo-inline-action"
  >{" Draft saved · "}<UiButton
      variant="link"
    >3 unpublished changes</UiButton>{" "}</p>;
}
<script>
  import { UiButton } from "@threadlabs/looma";
</script>

<p
  class="demo-inline-action"
> Draft saved · <UiButton
    variant="link"
  >3 unpublished changes</UiButton> </p>
import { Component } from "@angular/core";
import { UiButton } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiButton],
  template: `
    <p
      class="demo-inline-action"
    > Draft saved · <ng-template
        ui-button
        [variant]="'link'"
      >3 unpublished changes</ng-template> </p>
  `,
})
export default class Example {}

size

Size changes the control's height and padding (xs 24px, sm 32px, md 40px, lg 48px), not just its text. Use xs for a control that sits inside a line of small text.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-cluster/ui-cluster.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>

<ui-cluster
  gap="s"
  align="center"
>
  <ui-button
    size="xs"
  >Extra small</ui-button>
  <ui-button
    size="sm"
  >Small</ui-button>
  <ui-button>Medium</ui-button>
  <ui-button
    size="lg"
  >Large</ui-button>
</ui-cluster>
<script setup>
  import { UiButton, UiCluster } from "@threadlabs/looma";
</script>

<template>
  <UiCluster
    gap="s"
    align="center"
  >
    <UiButton
      size="xs"
    >Extra small</UiButton>
    <UiButton
      size="sm"
    >Small</UiButton>
    <UiButton>Medium</UiButton>
    <UiButton
      size="lg"
    >Large</UiButton>
  </UiCluster>
</template>
import { UiButton, UiCluster } from "@threadlabs/looma";

export default function Example() {
  return <UiCluster
    gap="s"
    align="center"
  >
    <UiButton
      size="xs"
    >Extra small</UiButton>
    <UiButton
      size="sm"
    >Small</UiButton>
    <UiButton>Medium</UiButton>
    <UiButton
      size="lg"
    >Large</UiButton>
  </UiCluster>;
}
/** @jsxImportSource preact */
import { UiButton, UiCluster } from "@threadlabs/looma";

export default function Example() {
  return <UiCluster
    gap="s"
    align="center"
  >
    <UiButton
      size="xs"
    >Extra small</UiButton>
    <UiButton
      size="sm"
    >Small</UiButton>
    <UiButton>Medium</UiButton>
    <UiButton
      size="lg"
    >Large</UiButton>
  </UiCluster>;
}
<script>
  import { UiButton, UiCluster } from "@threadlabs/looma";
</script>

<UiCluster
  gap="s"
  align="center"
>
  <UiButton
    size="xs"
  >Extra small</UiButton>
  <UiButton
    size="sm"
  >Small</UiButton>
  <UiButton>Medium</UiButton>
  <UiButton
    size="lg"
  >Large</UiButton>
</UiCluster>
import { Component } from "@angular/core";
import { UiButton, UiCluster } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiCluster, UiButton],
  template: `
    <div
      ui-cluster
      [gap]="'s'"
      [align]="'center'"
    >
      <ng-template
        ui-button
        [size]="'xs'"
      >Extra small</ng-template>
      <ng-template
        ui-button
        [size]="'sm'"
      >Small</ng-template>
      <ng-template
        ui-button
      >Medium</ng-template>
      <ng-template
        ui-button
        [size]="'lg'"
      >Large</ng-template>
    </div>
  `,
})
export default class Example {}

disabled

Disabled buttons keep their variant but read as unavailable and ignore activation.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-cluster/ui-cluster.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>

<ui-cluster
  gap="s"
>
  <ui-button
    disabled
  >Outline</ui-button>
  <ui-button
    variant="solid"
    disabled
  >Solid</ui-button>
  <ui-button
    variant="danger"
    disabled
  >Danger</ui-button>
</ui-cluster>
<script setup>
  import { UiButton, UiCluster } from "@threadlabs/looma";
</script>

<template>
  <UiCluster
    gap="s"
  >
    <UiButton
      :disabled="true"
    >Outline</UiButton>
    <UiButton
      variant="solid"
      :disabled="true"
    >Solid</UiButton>
    <UiButton
      variant="danger"
      :disabled="true"
    >Danger</UiButton>
  </UiCluster>
</template>
import { UiButton, UiCluster } from "@threadlabs/looma";

export default function Example() {
  return <UiCluster
    gap="s"
  >
    <UiButton
      disabled
    >Outline</UiButton>
    <UiButton
      variant="solid"
      disabled
    >Solid</UiButton>
    <UiButton
      variant="danger"
      disabled
    >Danger</UiButton>
  </UiCluster>;
}
/** @jsxImportSource preact */
import { UiButton, UiCluster } from "@threadlabs/looma";

export default function Example() {
  return <UiCluster
    gap="s"
  >
    <UiButton
      disabled
    >Outline</UiButton>
    <UiButton
      variant="solid"
      disabled
    >Solid</UiButton>
    <UiButton
      variant="danger"
      disabled
    >Danger</UiButton>
  </UiCluster>;
}
<script>
  import { UiButton, UiCluster } from "@threadlabs/looma";
</script>

<UiCluster
  gap="s"
>
  <UiButton
    disabled
  >Outline</UiButton>
  <UiButton
    variant="solid"
    disabled
  >Solid</UiButton>
  <UiButton
    variant="danger"
    disabled
  >Danger</UiButton>
</UiCluster>
import { Component } from "@angular/core";
import { UiButton, UiCluster } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiCluster, UiButton],
  template: `
    <div
      ui-cluster
      [gap]="'s'"
    >
      <ng-template
        ui-button
        [disabled]="true"
      >Outline</ng-template>
      <ng-template
        ui-button
        [variant]="'solid'"
        [disabled]="true"
      >Solid</ng-template>
      <ng-template
        ui-button
        [variant]="'danger'"
        [disabled]="true"
      >Danger</ng-template>
    </div>
  `,
})
export default class Example {}

align and stretch

align="start" lays content out from the start edge, like a list option; stretch fills the container's width.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-stack/ui-stack.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>

<ui-stack
  gap="s"
  style="max-inline-size: 22rem"
>
  <ui-button
    variant="ghost"
    align="start"
    stretch
  >New page at the project root</ui-button>
  <ui-button
    variant="solid"
    stretch
  >Continue</ui-button>
</ui-stack>
<script setup>
  import { UiButton, UiStack } from "@threadlabs/looma";
</script>

<template>
  <UiStack
    gap="s"
    style="max-inline-size: 22rem"
  >
    <UiButton
      variant="ghost"
      align="start"
      :stretch="true"
    >New page at the project root</UiButton>
    <UiButton
      variant="solid"
      :stretch="true"
    >Continue</UiButton>
  </UiStack>
</template>
import { UiButton, UiStack } from "@threadlabs/looma";

export default function Example() {
  return <UiStack
    gap="s"
    style={{"maxInlineSize":"22rem"}}
  >
    <UiButton
      variant="ghost"
      align="start"
      stretch
    >New page at the project root</UiButton>
    <UiButton
      variant="solid"
      stretch
    >Continue</UiButton>
  </UiStack>;
}
/** @jsxImportSource preact */
import { UiButton, UiStack } from "@threadlabs/looma";

export default function Example() {
  return <UiStack
    gap="s"
    style={{"maxInlineSize":"22rem"}}
  >
    <UiButton
      variant="ghost"
      align="start"
      stretch
    >New page at the project root</UiButton>
    <UiButton
      variant="solid"
      stretch
    >Continue</UiButton>
  </UiStack>;
}
<script>
  import { UiButton, UiStack } from "@threadlabs/looma";
</script>

<UiStack
  gap="s"
  style="max-inline-size: 22rem"
>
  <UiButton
    variant="ghost"
    align="start"
    stretch
  >New page at the project root</UiButton>
  <UiButton
    variant="solid"
    stretch
  >Continue</UiButton>
</UiStack>
import { Component } from "@angular/core";
import { UiButton, UiStack } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiStack, UiButton],
  template: `
    <div
      ui-stack
      [gap]="'s'"
      style="max-inline-size: 22rem"
    >
      <ng-template
        ui-button
        [variant]="'ghost'"
        [align]="'start'"
        [stretch]="true"
      >New page at the project root</ng-template>
      <ng-template
        ui-button
        [variant]="'solid'"
        [stretch]="true"
      >Continue</ng-template>
    </div>
  `,
})
export default class Example {}

as a link

Supplying href automatically renders a real link that looks and responds exactly like the button, in every variant. target and rel pass to the link. A disabled link drops its href and states aria-disabled, so it cannot be followed.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-cluster/ui-cluster.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>

<ui-cluster
  gap="s"
>
  <ui-button
    href="#get-started"
    variant="solid"
  >Get started</ui-button>
  <ui-button
    href="https://example.com/"
    target="_blank"
    rel="noreferrer"
  >Read the guide</ui-button>
  <ui-button
    href="#get-started"
    disabled
  >Unavailable</ui-button>
</ui-cluster>
<script setup>
  import { UiButton, UiCluster } from "@threadlabs/looma";
</script>

<template>
  <UiCluster
    gap="s"
  >
    <UiButton
      href="#get-started"
      variant="solid"
    >Get started</UiButton>
    <UiButton
      href="https://example.com/"
      target="_blank"
      rel="noreferrer"
    >Read the guide</UiButton>
    <UiButton
      href="#get-started"
      :disabled="true"
    >Unavailable</UiButton>
  </UiCluster>
</template>
import { UiButton, UiCluster } from "@threadlabs/looma";

export default function Example() {
  return <UiCluster
    gap="s"
  >
    <UiButton
      href="#get-started"
      variant="solid"
    >Get started</UiButton>
    <UiButton
      href="https://example.com/"
      target="_blank"
      rel="noreferrer"
    >Read the guide</UiButton>
    <UiButton
      href="#get-started"
      disabled
    >Unavailable</UiButton>
  </UiCluster>;
}
/** @jsxImportSource preact */
import { UiButton, UiCluster } from "@threadlabs/looma";

export default function Example() {
  return <UiCluster
    gap="s"
  >
    <UiButton
      href="#get-started"
      variant="solid"
    >Get started</UiButton>
    <UiButton
      href="https://example.com/"
      target="_blank"
      rel="noreferrer"
    >Read the guide</UiButton>
    <UiButton
      href="#get-started"
      disabled
    >Unavailable</UiButton>
  </UiCluster>;
}
<script>
  import { UiButton, UiCluster } from "@threadlabs/looma";
</script>

<UiCluster
  gap="s"
>
  <UiButton
    href="#get-started"
    variant="solid"
  >Get started</UiButton>
  <UiButton
    href="https://example.com/"
    target="_blank"
    rel="noreferrer"
  >Read the guide</UiButton>
  <UiButton
    href="#get-started"
    disabled
  >Unavailable</UiButton>
</UiCluster>
import { Component } from "@angular/core";
import { UiButton, UiCluster } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiCluster, UiButton],
  template: `
    <div
      ui-cluster
      [gap]="'s'"
    >
      <ng-template
        ui-button
        [href]="'#get-started'"
        [variant]="'solid'"
      >Get started</ng-template>
      <ng-template
        ui-button
        [href]="'https://example.com/'"
        [target]="'_blank'"
        [rel]="'noreferrer'"
      >Read the guide</ng-template>
      <ng-template
        ui-button
        [href]="'#get-started'"
        [disabled]="true"
      >Unavailable</ng-template>
    </div>
  `,
})
export default class Example {}

Loading action

Press Save changes to see the loading state. Its spinner appears beside the label, and the action stays focusable while another press is blocked.

Code

<script
  type="module"
>
  import "@threadlabs/looma";
  import { componentRegistry } from "@nextwebwg/html-next/runtime";

  const button = document.querySelector("#docs-save-changes");
  button.addEventListener("click", async () => {
    componentRegistry.update(button, { loading: true });
    try {
      await new Promise((resolve) => setTimeout(resolve, 1500));
    } finally {
      componentRegistry.update(button, { loading: false });
    }
  });
</script>

<ui-button
  id="docs-save-changes"
  variant="solid"
>Save changes</ui-button>
<script setup lang="ts">
import { ref } from "vue";
import { UiButton as Button } from "@threadlabs/looma";

const loading = ref(false);
async function saveChanges() {
  loading.value = true;
  try {
    await new Promise((resolve) => setTimeout(resolve, 1500));
  } finally {
    loading.value = false;
  }
}
</script>

<template>
  <Button variant="solid" :loading="loading" @click="saveChanges">Save changes</Button>
</template>
import { UiButton } from "@threadlabs/looma";

export default function Example() {
  return <UiButton
    id="docs-save-changes"
    data-example-id="docs-save-changes"
    variant="solid"
  >Save changes</UiButton>;
}
/** @jsxImportSource preact */
import { UiButton } from "@threadlabs/looma";

export default function Example() {
  return <UiButton
    id="docs-save-changes"
    data-example-id="docs-save-changes"
    variant="solid"
  >Save changes</UiButton>;
}
<script>
  import { UiButton } from "@threadlabs/looma";
</script>

<UiButton
  id="docs-save-changes"
  data-example-id="docs-save-changes"
  variant="solid"
>Save changes</UiButton>
import { Component } from "@angular/core";
import { UiButton } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiButton],
  template: `
    <ng-template
      ui-button
      [variant]="'solid'"
      [hnAttributes]="{ id: 'docs-save-changes', 'data-example-id': 'docs-save-changes' }"
    >Save changes</ng-template>
  `,
})
export default class Example {}

Pill

shape="pill" rounds the ends fully. A small ghost pill suits a compact choice in a line of text, such as a status or a person, and opens its menu like any button.

Early
On track
Stable

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-cluster/ui-cluster.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-menu/ui-menu.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-menu-item/ui-menu-item.html"
>

<ui-cluster
  gap="s"
  align="center"
>
  <ui-button
    id="pill-stage"
    variant="ghost"
    tone="neutral"
    size="xs"
    shape="pill"
  >On track</ui-button>
  <ui-menu
    for="pill-stage"
    size="sm"
    aria-label="Stage"
  >
    <ui-menu-item
      type="radio"
      value="early"
    >Early</ui-menu-item>
    <ui-menu-item
      type="radio"
      value="on-track"
      checked
    >On track</ui-menu-item>
    <ui-menu-item
      type="radio"
      value="stable"
    >Stable</ui-menu-item>
  </ui-menu>
  <ui-button
    shape="pill"
  >Pill</ui-button>
</ui-cluster>
<script setup>
  import { UiButton, UiCluster, UiMenu, UiMenuItem } from "@threadlabs/looma";
</script>

<template>
  <UiCluster
    gap="s"
    align="center"
  >
    <UiButton
      id="pill-stage"
      variant="ghost"
      tone="neutral"
      size="xs"
      shape="pill"
    >On track</UiButton>
    <UiMenu
      for="pill-stage"
      size="sm"
      aria-label="Stage"
    >
      <UiMenuItem
        type="radio"
        value="early"
      >Early</UiMenuItem>
      <UiMenuItem
        type="radio"
        value="on-track"
        :checked="true"
      >On track</UiMenuItem>
      <UiMenuItem
        type="radio"
        value="stable"
      >Stable</UiMenuItem>
    </UiMenu>
    <UiButton
      shape="pill"
    >Pill</UiButton>
  </UiCluster>
</template>
import { UiButton, UiCluster, UiMenu, UiMenuItem } from "@threadlabs/looma";

export default function Example() {
  return <UiCluster
    gap="s"
    align="center"
  >
    <UiButton
      id="pill-stage"
      variant="ghost"
      tone="neutral"
      size="xs"
      shape="pill"
    >On track</UiButton>
    <UiMenu
      htmlFor="pill-stage"
      size="sm"
      aria-label="Stage"
    >
      <UiMenuItem
        type="radio"
        value="early"
      >Early</UiMenuItem>
      <UiMenuItem
        type="radio"
        value="on-track"
        checked
      >On track</UiMenuItem>
      <UiMenuItem
        type="radio"
        value="stable"
      >Stable</UiMenuItem>
    </UiMenu>
    <UiButton
      shape="pill"
    >Pill</UiButton>
  </UiCluster>;
}
/** @jsxImportSource preact */
import { UiButton, UiCluster, UiMenu, UiMenuItem } from "@threadlabs/looma";

export default function Example() {
  return <UiCluster
    gap="s"
    align="center"
  >
    <UiButton
      id="pill-stage"
      variant="ghost"
      tone="neutral"
      size="xs"
      shape="pill"
    >On track</UiButton>
    <UiMenu
      htmlFor="pill-stage"
      size="sm"
      aria-label="Stage"
    >
      <UiMenuItem
        type="radio"
        value="early"
      >Early</UiMenuItem>
      <UiMenuItem
        type="radio"
        value="on-track"
        checked
      >On track</UiMenuItem>
      <UiMenuItem
        type="radio"
        value="stable"
      >Stable</UiMenuItem>
    </UiMenu>
    <UiButton
      shape="pill"
    >Pill</UiButton>
  </UiCluster>;
}
<script>
  import { UiButton, UiCluster, UiMenu, UiMenuItem } from "@threadlabs/looma";
</script>

<UiCluster
  gap="s"
  align="center"
>
  <UiButton
    id="pill-stage"
    variant="ghost"
    tone="neutral"
    size="xs"
    shape="pill"
  >On track</UiButton>
  <UiMenu
    for="pill-stage"
    size="sm"
    aria-label="Stage"
  >
    <UiMenuItem
      type="radio"
      value="early"
    >Early</UiMenuItem>
    <UiMenuItem
      type="radio"
      value="on-track"
      checked
    >On track</UiMenuItem>
    <UiMenuItem
      type="radio"
      value="stable"
    >Stable</UiMenuItem>
  </UiMenu>
  <UiButton
    shape="pill"
  >Pill</UiButton>
</UiCluster>
import { Component } from "@angular/core";
import { UiButton, UiCluster, UiMenu, UiMenuItem } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiCluster, UiButton, UiMenu, UiMenuItem],
  template: `
    <div
      ui-cluster
      [gap]="'s'"
      [align]="'center'"
    >
      <ng-template
        ui-button
        [variant]="'ghost'"
        [tone]="'neutral'"
        [size]="'xs'"
        [shape]="'pill'"
        [hnAttributes]="{ id: 'pill-stage' }"
      >On track</ng-template>
      <div
        ui-menu
        [for]="'pill-stage'"
        [size]="'sm'"
        aria-label="Stage"
      >
        <ng-template
          ui-menu-item
          [type]="'radio'"
          [value]="'early'"
        >Early</ng-template>
        <ng-template
          ui-menu-item
          [type]="'radio'"
          [value]="'on-track'"
          [checked]="true"
        >On track</ng-template>
        <ng-template
          ui-menu-item
          [type]="'radio'"
          [value]="'stable'"
        >Stable</ng-template>
      </div>
      <ng-template
        ui-button
        [shape]="'pill'"
      >Pill</ng-template>
    </div>
  `,
})
export default class Example {}

Square minimum size

Short numeric and icon actions share the control's minimum height and width. Longer labels grow naturally; the ordinary Button default stays content-sized.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-cluster/ui-cluster.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-icon/ui-icon.html"
>

<ui-cluster
  gap="xs"
>
  <ui-button
    square
    size="sm"
    variant="ghost"
    tone="neutral"
    aria-label="Previous"
  >
    <ui-icon
      name="chevron-left"
    ></ui-icon>
  </ui-button>
  <ui-button
    square
    size="sm"
    variant="ghost"
    tone="neutral"
  >1</ui-button>
  <ui-button
    square
    size="sm"
    variant="solid"
  >2</ui-button>
  <ui-button
    square
    size="sm"
    variant="ghost"
    tone="neutral"
  >10</ui-button>
  <ui-button
    square
    size="sm"
    variant="ghost"
    tone="neutral"
    aria-label="Next"
  >
    <ui-icon
      name="chevron-right"
    ></ui-icon>
  </ui-button>
</ui-cluster>
<script setup>
  import { UiButton, UiCluster, UiIcon } from "@threadlabs/looma";
</script>

<template>
  <UiCluster
    gap="xs"
  >
    <UiButton
      :square="true"
      size="sm"
      variant="ghost"
      tone="neutral"
      aria-label="Previous"
    >
      <UiIcon
        name="chevron-left"
      />
    </UiButton>
    <UiButton
      :square="true"
      size="sm"
      variant="ghost"
      tone="neutral"
    >1</UiButton>
    <UiButton
      :square="true"
      size="sm"
      variant="solid"
    >2</UiButton>
    <UiButton
      :square="true"
      size="sm"
      variant="ghost"
      tone="neutral"
    >10</UiButton>
    <UiButton
      :square="true"
      size="sm"
      variant="ghost"
      tone="neutral"
      aria-label="Next"
    >
      <UiIcon
        name="chevron-right"
      />
    </UiButton>
  </UiCluster>
</template>
import { UiButton, UiCluster, UiIcon } from "@threadlabs/looma";

export default function Example() {
  return <UiCluster
    gap="xs"
  >
    <UiButton
      square
      size="sm"
      variant="ghost"
      tone="neutral"
      aria-label="Previous"
    >
      <UiIcon
        name="chevron-left"
      />
    </UiButton>
    <UiButton
      square
      size="sm"
      variant="ghost"
      tone="neutral"
    >1</UiButton>
    <UiButton
      square
      size="sm"
      variant="solid"
    >2</UiButton>
    <UiButton
      square
      size="sm"
      variant="ghost"
      tone="neutral"
    >10</UiButton>
    <UiButton
      square
      size="sm"
      variant="ghost"
      tone="neutral"
      aria-label="Next"
    >
      <UiIcon
        name="chevron-right"
      />
    </UiButton>
  </UiCluster>;
}
/** @jsxImportSource preact */
import { UiButton, UiCluster, UiIcon } from "@threadlabs/looma";

export default function Example() {
  return <UiCluster
    gap="xs"
  >
    <UiButton
      square
      size="sm"
      variant="ghost"
      tone="neutral"
      aria-label="Previous"
    >
      <UiIcon
        name="chevron-left"
      />
    </UiButton>
    <UiButton
      square
      size="sm"
      variant="ghost"
      tone="neutral"
    >1</UiButton>
    <UiButton
      square
      size="sm"
      variant="solid"
    >2</UiButton>
    <UiButton
      square
      size="sm"
      variant="ghost"
      tone="neutral"
    >10</UiButton>
    <UiButton
      square
      size="sm"
      variant="ghost"
      tone="neutral"
      aria-label="Next"
    >
      <UiIcon
        name="chevron-right"
      />
    </UiButton>
  </UiCluster>;
}
<script>
  import { UiButton, UiCluster, UiIcon } from "@threadlabs/looma";
</script>

<UiCluster
  gap="xs"
>
  <UiButton
    square
    size="sm"
    variant="ghost"
    tone="neutral"
    aria-label="Previous"
  >
    <UiIcon
      name="chevron-left"
    />
  </UiButton>
  <UiButton
    square
    size="sm"
    variant="ghost"
    tone="neutral"
  >1</UiButton>
  <UiButton
    square
    size="sm"
    variant="solid"
  >2</UiButton>
  <UiButton
    square
    size="sm"
    variant="ghost"
    tone="neutral"
  >10</UiButton>
  <UiButton
    square
    size="sm"
    variant="ghost"
    tone="neutral"
    aria-label="Next"
  >
    <UiIcon
      name="chevron-right"
    />
  </UiButton>
</UiCluster>
import { Component } from "@angular/core";
import { UiButton, UiCluster, UiIcon } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiCluster, UiButton, UiIcon],
  template: `
    <div
      ui-cluster
      [gap]="'xs'"
    >
      <ng-template
        ui-button
        [square]="true"
        [size]="'sm'"
        [variant]="'ghost'"
        [tone]="'neutral'"
        [hnAttributes]="{ 'aria-label': 'Previous' }"
      >
        <span
          ui-icon
          name="chevron-left"
        ></span>
      </ng-template>
      <ng-template
        ui-button
        [square]="true"
        [size]="'sm'"
        [variant]="'ghost'"
        [tone]="'neutral'"
      >1</ng-template>
      <ng-template
        ui-button
        [square]="true"
        [size]="'sm'"
        [variant]="'solid'"
      >2</ng-template>
      <ng-template
        ui-button
        [square]="true"
        [size]="'sm'"
        [variant]="'ghost'"
        [tone]="'neutral'"
      >10</ng-template>
      <ng-template
        ui-button
        [square]="true"
        [size]="'sm'"
        [variant]="'ghost'"
        [tone]="'neutral'"
        [hnAttributes]="{ 'aria-label': 'Next' }"
      >
        <span
          ui-icon
          name="chevron-right"
        ></span>
      </ng-template>
    </div>
  `,
})
export default class Example {}

card action

Button's card variant keeps its tone palette and places top-aligned icon and content beside a centered action. The action slot defaults to a caret.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-stack/ui-stack.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-icon/ui-icon.html"
>

<ui-stack
  gap="s"
>
  <ui-button
    variant="card"
    tone="accent"
  >
    <ui-icon
      slot="icon"
      name="book-user"
      aria-hidden="true"
    ></ui-icon>
    Read the latest project notes and decisions
  </ui-button>
  <ui-button
    variant="card"
    tone="neutral"
  >Open document history</ui-button>
  <ui-button
    variant="card"
    tone="warning"
  >
    <ui-icon
      slot="icon"
      name="triangle-alert"
      aria-hidden="true"
    ></ui-icon>
    Inspect items that need attention
    <ui-icon
      slot="action"
      name="chevron-down"
      aria-hidden="true"
    ></ui-icon>
  </ui-button>
</ui-stack>
<script setup>
  import { UiButton, UiIcon, UiStack } from "@threadlabs/looma";
</script>

<template>
  <UiStack
    gap="s"
  >
    <UiButton
      variant="card"
      tone="accent"
    ><template #icon>
        <UiIcon
          name="book-user"
          aria-hidden="true"
        />
      </template>Read the latest project notes and decisions</UiButton>
    <UiButton
      variant="card"
      tone="neutral"
    >Open document history</UiButton>
    <UiButton
      variant="card"
      tone="warning"
    ><template #icon>
        <UiIcon
          name="triangle-alert"
          aria-hidden="true"
        />
      </template>Inspect items that need attention<template #action>
        <UiIcon
          name="chevron-down"
          aria-hidden="true"
        />
      </template></UiButton>
  </UiStack>
</template>
import { UiButton, UiIcon, UiStack } from "@threadlabs/looma";

export default function Example() {
  return <UiStack
    gap="s"
  >
    <UiButton
      variant="card"
      tone="accent"
      slots={{
        icon: <UiIcon
          name="book-user"
          aria-hidden="true"
        />,
      }}
    >{"Read the latest project notes and decisions"}</UiButton>
    <UiButton
      variant="card"
      tone="neutral"
    >Open document history</UiButton>
    <UiButton
      variant="card"
      tone="warning"
      slots={{
        icon: <UiIcon
          name="triangle-alert"
          aria-hidden="true"
        />,
        action: <UiIcon
          name="chevron-down"
          aria-hidden="true"
        />,
      }}
    >Inspect items that need attention</UiButton>
  </UiStack>;
}
/** @jsxImportSource preact */
import { UiButton, UiIcon, UiStack } from "@threadlabs/looma";

export default function Example() {
  return <UiStack
    gap="s"
  >
    <UiButton
      variant="card"
      tone="accent"
      slots={{
        icon: <UiIcon
          name="book-user"
          aria-hidden="true"
        />,
      }}
    >{"Read the latest project notes and decisions"}</UiButton>
    <UiButton
      variant="card"
      tone="neutral"
    >Open document history</UiButton>
    <UiButton
      variant="card"
      tone="warning"
      slots={{
        icon: <UiIcon
          name="triangle-alert"
          aria-hidden="true"
        />,
        action: <UiIcon
          name="chevron-down"
          aria-hidden="true"
        />,
      }}
    >Inspect items that need attention</UiButton>
  </UiStack>;
}
<script>
  import { UiButton, UiIcon, UiStack } from "@threadlabs/looma";
</script>

{#snippet icon()}
  <UiIcon
    name="book-user"
    aria-hidden="true"
  />
{/snippet}

{#snippet buttonIcon()}
  <UiIcon
    name="triangle-alert"
    aria-hidden="true"
  />
{/snippet}

{#snippet action()}
  <UiIcon
    name="chevron-down"
    aria-hidden="true"
  />
{/snippet}

<UiStack
  gap="s"
>
  <UiButton
    variant="card"
    tone="accent"
    slots={{ icon }}
  >Read the latest project notes and decisions</UiButton>
  <UiButton
    variant="card"
    tone="neutral"
  >Open document history</UiButton>
  <UiButton
    variant="card"
    tone="warning"
    slots={{ icon: buttonIcon, action }}
  >Inspect items that need attention</UiButton>
</UiStack>
import { Component } from "@angular/core";
import { UiButton, UiIcon, UiStack } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiStack, UiButton, UiIcon],
  template: `
    <div
      ui-stack
      [gap]="'s'"
    >
      <ng-template #icon>
        <span
          ui-icon
          name="book-user"
          aria-hidden="true"
        ></span>
      </ng-template>
      <ng-template
        ui-button
        [variant]="'card'"
        [tone]="'accent'"
        [slots]="{ icon: icon }"
      >Read the latest project notes and decisions</ng-template>
      <ng-template
        ui-button
        [variant]="'card'"
        [tone]="'neutral'"
      >Open document history</ng-template>
      <ng-template #buttonIcon>
        <span
          ui-icon
          name="triangle-alert"
          aria-hidden="true"
        ></span>
      </ng-template>
      <ng-template #action>
        <span
          ui-icon
          name="chevron-down"
          aria-hidden="true"
        ></span>
      </ng-template>
      <ng-template
        ui-button
        [variant]="'card'"
        [tone]="'warning'"
        [slots]="{ icon: buttonIcon, action: action }"
      >Inspect items that need attention</ng-template>
    </div>
  `,
})
export default class Example {}

Value picker

A custom value picker shares Input and Select's quiet edge and height, with a slight resting lift.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-cluster/ui-cluster.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-input/ui-input.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-select/ui-select.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-icon/ui-icon.html"
>

<ui-cluster>
  <ui-input
    aria-label="Name"
    value="General"
  ></ui-input>
  <ui-select
    aria-label="Category"
  >
    <option>General</option>
  </ui-select>
  <ui-button
    variant="field"
    aria-label="Choose a color"
    aria-expanded="false"
  >Choose color <ui-icon
      name="chevron-down"
    ></ui-icon></ui-button>
</ui-cluster>
<script setup>
  import { UiButton, UiCluster, UiIcon, UiInput, UiSelect } from "@threadlabs/looma";
</script>

<template>
  <UiCluster>
    <UiInput
      aria-label="Name"
      value="General"
    />
    <UiSelect
      aria-label="Category"
    >
      <option>General</option>
    </UiSelect>
    <UiButton
      variant="field"
      aria-label="Choose a color"
      aria-expanded="false"
    >Choose color <UiIcon
        name="chevron-down"
      /></UiButton>
  </UiCluster>
</template>
import { UiButton, UiCluster, UiIcon, UiInput, UiSelect } from "@threadlabs/looma";

export default function Example() {
  return <UiCluster>
    <UiInput
      aria-label="Name"
      value="General"
    />
    <UiSelect
      aria-label="Category"
    >
      <option>General</option>
    </UiSelect>
    <UiButton
      variant="field"
      aria-label="Choose a color"
      aria-expanded="false"
    >Choose color <UiIcon
        name="chevron-down"
      /></UiButton>
  </UiCluster>;
}
/** @jsxImportSource preact */
import { UiButton, UiCluster, UiIcon, UiInput, UiSelect } from "@threadlabs/looma";

export default function Example() {
  return <UiCluster>
    <UiInput
      aria-label="Name"
      value="General"
    />
    <UiSelect
      aria-label="Category"
    >
      <option>General</option>
    </UiSelect>
    <UiButton
      variant="field"
      aria-label="Choose a color"
      aria-expanded="false"
    >Choose color <UiIcon
        name="chevron-down"
      /></UiButton>
  </UiCluster>;
}
<script>
  import { UiButton, UiCluster, UiIcon, UiInput, UiSelect } from "@threadlabs/looma";
</script>

<UiCluster>
  <UiInput
    aria-label="Name"
    value="General"
  />
  <UiSelect
    aria-label="Category"
  >
    <option>General</option>
  </UiSelect>
  <UiButton
    variant="field"
    aria-label="Choose a color"
    aria-expanded="false"
  >Choose color <UiIcon
      name="chevron-down"
    /></UiButton>
</UiCluster>
import { Component } from "@angular/core";
import { UiButton, UiCluster, UiIcon, UiInput, UiSelect } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiCluster, UiInput, UiSelect, UiButton, UiIcon],
  template: `
    <div
      ui-cluster
    >
      <input
        ui-input
        aria-label="Name"
        [value]="'General'"
      >
      <select
        ui-select
        aria-label="Category"
      >
        <option>General</option>
      </select>
      <ng-template
        ui-button
        [variant]="'field'"
        [hnAttributes]="{ 'aria-label': 'Choose a color', 'aria-expanded': 'false' }"
      >Choose color <span
          ui-icon
          name="chevron-down"
        ></span></ng-template>
    </div>
  `,
})
export default class Example {}

Appearance and tone

Appearance controls emphasis; tone expresses intent. Semantic and optional supporting brand tones work as solid, outline and quiet actions, including solid neutral. Icon actions use the same palette.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-stack/ui-stack.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-cluster/ui-cluster.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-icon-button/ui-icon-button.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-icon/ui-icon.html"
>

<ui-stack>
  <ui-cluster>
    <ui-button
      variant="solid"
      tone="accent"
    >Send memo</ui-button>
    <ui-button
      variant="solid"
      tone="neutral"
    >File notes</ui-button>
    <ui-button
      variant="solid"
      tone="danger"
    >Delete draft</ui-button>
    <ui-button
      variant="solid"
      tone="success"
    >Approve leave</ui-button>
    <ui-button
      variant="solid"
      tone="warning"
    >Review route</ui-button>
    <ui-button
      variant="solid"
      tone="info"
    >See briefing</ui-button>
  </ui-cluster>
  <ui-cluster>
    <ui-button
      variant="outline"
      tone="accent"
    >Send memo</ui-button>
    <ui-button
      variant="outline"
      tone="neutral"
    >File notes</ui-button>
    <ui-button
      variant="outline"
      tone="danger"
    >Delete draft</ui-button>
    <ui-button
      variant="outline"
      tone="success"
    >Approve leave</ui-button>
    <ui-button
      variant="outline"
      tone="warning"
    >Review route</ui-button>
    <ui-button
      variant="outline"
      tone="info"
    >See briefing</ui-button>
  </ui-cluster>
  <ui-cluster>
    <ui-button
      variant="ghost"
      tone="accent"
    >Send memo</ui-button>
    <ui-button
      variant="ghost"
      tone="neutral"
    >File notes</ui-button>
    <ui-button
      variant="ghost"
      tone="danger"
    >Delete draft</ui-button>
    <ui-button
      variant="ghost"
      tone="success"
    >Approve leave</ui-button>
    <ui-button
      variant="ghost"
      tone="warning"
    >Review route</ui-button>
    <ui-button
      variant="ghost"
      tone="info"
    >See briefing</ui-button>
  </ui-cluster>
  <ui-cluster>
    <ui-icon-button
      preset="primary"
      match-button
      label="Send memo"
    >
      <ui-icon
        name="check"
      ></ui-icon>
    </ui-icon-button>
    <ui-icon-button
      preset="standard"
      match-button
      label="File notes"
    >
      <ui-icon
        name="check"
      ></ui-icon>
    </ui-icon-button>
    <ui-icon-button
      preset="destructive"
      match-button
      label="Delete draft"
    >
      <ui-icon
        name="check"
      ></ui-icon>
    </ui-icon-button>
    <ui-icon-button
      variant="solid"
      tone="success"
      match-button
      label="Approve leave"
    >
      <ui-icon
        name="check"
      ></ui-icon>
    </ui-icon-button>
    <ui-icon-button
      variant="solid"
      tone="warning"
      match-button
      label="Review route"
    >
      <ui-icon
        name="check"
      ></ui-icon>
    </ui-icon-button>
    <ui-icon-button
      variant="solid"
      tone="info"
      match-button
      label="See briefing"
    >
      <ui-icon
        name="check"
      ></ui-icon>
    </ui-icon-button>
  </ui-cluster>
  <ui-cluster>
    <ui-button
      variant="outline"
      tone="accent-secondary"
    >Explore category</ui-button>
    <ui-button
      variant="ghost"
      tone="accent-tertiary"
    >Related collection</ui-button>
  </ui-cluster>
</ui-stack>
<script setup>
  import { UiButton, UiCluster, UiIcon, UiIconButton, UiStack } from "@threadlabs/looma";
</script>

<template>
  <UiStack>
    <UiCluster>
      <UiButton
        variant="solid"
        tone="accent"
      >Send memo</UiButton>
      <UiButton
        variant="solid"
        tone="neutral"
      >File notes</UiButton>
      <UiButton
        variant="solid"
        tone="danger"
      >Delete draft</UiButton>
      <UiButton
        variant="solid"
        tone="success"
      >Approve leave</UiButton>
      <UiButton
        variant="solid"
        tone="warning"
      >Review route</UiButton>
      <UiButton
        variant="solid"
        tone="info"
      >See briefing</UiButton>
    </UiCluster>
    <UiCluster>
      <UiButton
        variant="outline"
        tone="accent"
      >Send memo</UiButton>
      <UiButton
        variant="outline"
        tone="neutral"
      >File notes</UiButton>
      <UiButton
        variant="outline"
        tone="danger"
      >Delete draft</UiButton>
      <UiButton
        variant="outline"
        tone="success"
      >Approve leave</UiButton>
      <UiButton
        variant="outline"
        tone="warning"
      >Review route</UiButton>
      <UiButton
        variant="outline"
        tone="info"
      >See briefing</UiButton>
    </UiCluster>
    <UiCluster>
      <UiButton
        variant="ghost"
        tone="accent"
      >Send memo</UiButton>
      <UiButton
        variant="ghost"
        tone="neutral"
      >File notes</UiButton>
      <UiButton
        variant="ghost"
        tone="danger"
      >Delete draft</UiButton>
      <UiButton
        variant="ghost"
        tone="success"
      >Approve leave</UiButton>
      <UiButton
        variant="ghost"
        tone="warning"
      >Review route</UiButton>
      <UiButton
        variant="ghost"
        tone="info"
      >See briefing</UiButton>
    </UiCluster>
    <UiCluster>
      <UiIconButton
        preset="primary"
        :match-button="true"
        label="Send memo"
      >
        <UiIcon
          name="check"
        />
      </UiIconButton>
      <UiIconButton
        preset="standard"
        :match-button="true"
        label="File notes"
      >
        <UiIcon
          name="check"
        />
      </UiIconButton>
      <UiIconButton
        preset="destructive"
        :match-button="true"
        label="Delete draft"
      >
        <UiIcon
          name="check"
        />
      </UiIconButton>
      <UiIconButton
        variant="solid"
        tone="success"
        :match-button="true"
        label="Approve leave"
      >
        <UiIcon
          name="check"
        />
      </UiIconButton>
      <UiIconButton
        variant="solid"
        tone="warning"
        :match-button="true"
        label="Review route"
      >
        <UiIcon
          name="check"
        />
      </UiIconButton>
      <UiIconButton
        variant="solid"
        tone="info"
        :match-button="true"
        label="See briefing"
      >
        <UiIcon
          name="check"
        />
      </UiIconButton>
    </UiCluster>
    <UiCluster>
      <UiButton
        variant="outline"
        tone="accent-secondary"
      >Explore category</UiButton>
      <UiButton
        variant="ghost"
        tone="accent-tertiary"
      >Related collection</UiButton>
    </UiCluster>
  </UiStack>
</template>
import { UiButton, UiCluster, UiIcon, UiIconButton, UiStack } from "@threadlabs/looma";

export default function Example() {
  return <UiStack>
    <UiCluster>
      <UiButton
        variant="solid"
        tone="accent"
      >Send memo</UiButton>
      <UiButton
        variant="solid"
        tone="neutral"
      >File notes</UiButton>
      <UiButton
        variant="solid"
        tone="danger"
      >Delete draft</UiButton>
      <UiButton
        variant="solid"
        tone="success"
      >Approve leave</UiButton>
      <UiButton
        variant="solid"
        tone="warning"
      >Review route</UiButton>
      <UiButton
        variant="solid"
        tone="info"
      >See briefing</UiButton>
    </UiCluster>
    <UiCluster>
      <UiButton
        variant="outline"
        tone="accent"
      >Send memo</UiButton>
      <UiButton
        variant="outline"
        tone="neutral"
      >File notes</UiButton>
      <UiButton
        variant="outline"
        tone="danger"
      >Delete draft</UiButton>
      <UiButton
        variant="outline"
        tone="success"
      >Approve leave</UiButton>
      <UiButton
        variant="outline"
        tone="warning"
      >Review route</UiButton>
      <UiButton
        variant="outline"
        tone="info"
      >See briefing</UiButton>
    </UiCluster>
    <UiCluster>
      <UiButton
        variant="ghost"
        tone="accent"
      >Send memo</UiButton>
      <UiButton
        variant="ghost"
        tone="neutral"
      >File notes</UiButton>
      <UiButton
        variant="ghost"
        tone="danger"
      >Delete draft</UiButton>
      <UiButton
        variant="ghost"
        tone="success"
      >Approve leave</UiButton>
      <UiButton
        variant="ghost"
        tone="warning"
      >Review route</UiButton>
      <UiButton
        variant="ghost"
        tone="info"
      >See briefing</UiButton>
    </UiCluster>
    <UiCluster>
      <UiIconButton
        preset="primary"
        matchButton
        label="Send memo"
      >
        <UiIcon
          name="check"
        />
      </UiIconButton>
      <UiIconButton
        preset="standard"
        matchButton
        label="File notes"
      >
        <UiIcon
          name="check"
        />
      </UiIconButton>
      <UiIconButton
        preset="destructive"
        matchButton
        label="Delete draft"
      >
        <UiIcon
          name="check"
        />
      </UiIconButton>
      <UiIconButton
        variant="solid"
        tone="success"
        matchButton
        label="Approve leave"
      >
        <UiIcon
          name="check"
        />
      </UiIconButton>
      <UiIconButton
        variant="solid"
        tone="warning"
        matchButton
        label="Review route"
      >
        <UiIcon
          name="check"
        />
      </UiIconButton>
      <UiIconButton
        variant="solid"
        tone="info"
        matchButton
        label="See briefing"
      >
        <UiIcon
          name="check"
        />
      </UiIconButton>
    </UiCluster>
    <UiCluster>
      <UiButton
        variant="outline"
        tone="accent-secondary"
      >Explore category</UiButton>
      <UiButton
        variant="ghost"
        tone="accent-tertiary"
      >Related collection</UiButton>
    </UiCluster>
  </UiStack>;
}
/** @jsxImportSource preact */
import { UiButton, UiCluster, UiIcon, UiIconButton, UiStack } from "@threadlabs/looma";

export default function Example() {
  return <UiStack>
    <UiCluster>
      <UiButton
        variant="solid"
        tone="accent"
      >Send memo</UiButton>
      <UiButton
        variant="solid"
        tone="neutral"
      >File notes</UiButton>
      <UiButton
        variant="solid"
        tone="danger"
      >Delete draft</UiButton>
      <UiButton
        variant="solid"
        tone="success"
      >Approve leave</UiButton>
      <UiButton
        variant="solid"
        tone="warning"
      >Review route</UiButton>
      <UiButton
        variant="solid"
        tone="info"
      >See briefing</UiButton>
    </UiCluster>
    <UiCluster>
      <UiButton
        variant="outline"
        tone="accent"
      >Send memo</UiButton>
      <UiButton
        variant="outline"
        tone="neutral"
      >File notes</UiButton>
      <UiButton
        variant="outline"
        tone="danger"
      >Delete draft</UiButton>
      <UiButton
        variant="outline"
        tone="success"
      >Approve leave</UiButton>
      <UiButton
        variant="outline"
        tone="warning"
      >Review route</UiButton>
      <UiButton
        variant="outline"
        tone="info"
      >See briefing</UiButton>
    </UiCluster>
    <UiCluster>
      <UiButton
        variant="ghost"
        tone="accent"
      >Send memo</UiButton>
      <UiButton
        variant="ghost"
        tone="neutral"
      >File notes</UiButton>
      <UiButton
        variant="ghost"
        tone="danger"
      >Delete draft</UiButton>
      <UiButton
        variant="ghost"
        tone="success"
      >Approve leave</UiButton>
      <UiButton
        variant="ghost"
        tone="warning"
      >Review route</UiButton>
      <UiButton
        variant="ghost"
        tone="info"
      >See briefing</UiButton>
    </UiCluster>
    <UiCluster>
      <UiIconButton
        preset="primary"
        matchButton
        label="Send memo"
      >
        <UiIcon
          name="check"
        />
      </UiIconButton>
      <UiIconButton
        preset="standard"
        matchButton
        label="File notes"
      >
        <UiIcon
          name="check"
        />
      </UiIconButton>
      <UiIconButton
        preset="destructive"
        matchButton
        label="Delete draft"
      >
        <UiIcon
          name="check"
        />
      </UiIconButton>
      <UiIconButton
        variant="solid"
        tone="success"
        matchButton
        label="Approve leave"
      >
        <UiIcon
          name="check"
        />
      </UiIconButton>
      <UiIconButton
        variant="solid"
        tone="warning"
        matchButton
        label="Review route"
      >
        <UiIcon
          name="check"
        />
      </UiIconButton>
      <UiIconButton
        variant="solid"
        tone="info"
        matchButton
        label="See briefing"
      >
        <UiIcon
          name="check"
        />
      </UiIconButton>
    </UiCluster>
    <UiCluster>
      <UiButton
        variant="outline"
        tone="accent-secondary"
      >Explore category</UiButton>
      <UiButton
        variant="ghost"
        tone="accent-tertiary"
      >Related collection</UiButton>
    </UiCluster>
  </UiStack>;
}
<script>
  import { UiButton, UiCluster, UiIcon, UiIconButton, UiStack } from "@threadlabs/looma";
</script>

<UiStack>
  <UiCluster>
    <UiButton
      variant="solid"
      tone="accent"
    >Send memo</UiButton>
    <UiButton
      variant="solid"
      tone="neutral"
    >File notes</UiButton>
    <UiButton
      variant="solid"
      tone="danger"
    >Delete draft</UiButton>
    <UiButton
      variant="solid"
      tone="success"
    >Approve leave</UiButton>
    <UiButton
      variant="solid"
      tone="warning"
    >Review route</UiButton>
    <UiButton
      variant="solid"
      tone="info"
    >See briefing</UiButton>
  </UiCluster>
  <UiCluster>
    <UiButton
      variant="outline"
      tone="accent"
    >Send memo</UiButton>
    <UiButton
      variant="outline"
      tone="neutral"
    >File notes</UiButton>
    <UiButton
      variant="outline"
      tone="danger"
    >Delete draft</UiButton>
    <UiButton
      variant="outline"
      tone="success"
    >Approve leave</UiButton>
    <UiButton
      variant="outline"
      tone="warning"
    >Review route</UiButton>
    <UiButton
      variant="outline"
      tone="info"
    >See briefing</UiButton>
  </UiCluster>
  <UiCluster>
    <UiButton
      variant="ghost"
      tone="accent"
    >Send memo</UiButton>
    <UiButton
      variant="ghost"
      tone="neutral"
    >File notes</UiButton>
    <UiButton
      variant="ghost"
      tone="danger"
    >Delete draft</UiButton>
    <UiButton
      variant="ghost"
      tone="success"
    >Approve leave</UiButton>
    <UiButton
      variant="ghost"
      tone="warning"
    >Review route</UiButton>
    <UiButton
      variant="ghost"
      tone="info"
    >See briefing</UiButton>
  </UiCluster>
  <UiCluster>
    <UiIconButton
      preset="primary"
      matchButton
      label="Send memo"
    >
      <UiIcon
        name="check"
      />
    </UiIconButton>
    <UiIconButton
      preset="standard"
      matchButton
      label="File notes"
    >
      <UiIcon
        name="check"
      />
    </UiIconButton>
    <UiIconButton
      preset="destructive"
      matchButton
      label="Delete draft"
    >
      <UiIcon
        name="check"
      />
    </UiIconButton>
    <UiIconButton
      variant="solid"
      tone="success"
      matchButton
      label="Approve leave"
    >
      <UiIcon
        name="check"
      />
    </UiIconButton>
    <UiIconButton
      variant="solid"
      tone="warning"
      matchButton
      label="Review route"
    >
      <UiIcon
        name="check"
      />
    </UiIconButton>
    <UiIconButton
      variant="solid"
      tone="info"
      matchButton
      label="See briefing"
    >
      <UiIcon
        name="check"
      />
    </UiIconButton>
  </UiCluster>
  <UiCluster>
    <UiButton
      variant="outline"
      tone="accent-secondary"
    >Explore category</UiButton>
    <UiButton
      variant="ghost"
      tone="accent-tertiary"
    >Related collection</UiButton>
  </UiCluster>
</UiStack>
import { Component } from "@angular/core";
import { UiButton, UiCluster, UiIcon, UiIconButton, UiStack } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiStack, UiCluster, UiButton, UiIconButton, UiIcon],
  template: `
    <div
      ui-stack
    >
      <div
        ui-cluster
      >
        <ng-template
          ui-button
          [variant]="'solid'"
          [tone]="'accent'"
        >Send memo</ng-template>
        <ng-template
          ui-button
          [variant]="'solid'"
          [tone]="'neutral'"
        >File notes</ng-template>
        <ng-template
          ui-button
          [variant]="'solid'"
          [tone]="'danger'"
        >Delete draft</ng-template>
        <ng-template
          ui-button
          [variant]="'solid'"
          [tone]="'success'"
        >Approve leave</ng-template>
        <ng-template
          ui-button
          [variant]="'solid'"
          [tone]="'warning'"
        >Review route</ng-template>
        <ng-template
          ui-button
          [variant]="'solid'"
          [tone]="'info'"
        >See briefing</ng-template>
      </div>
      <div
        ui-cluster
      >
        <ng-template
          ui-button
          [variant]="'outline'"
          [tone]="'accent'"
        >Send memo</ng-template>
        <ng-template
          ui-button
          [variant]="'outline'"
          [tone]="'neutral'"
        >File notes</ng-template>
        <ng-template
          ui-button
          [variant]="'outline'"
          [tone]="'danger'"
        >Delete draft</ng-template>
        <ng-template
          ui-button
          [variant]="'outline'"
          [tone]="'success'"
        >Approve leave</ng-template>
        <ng-template
          ui-button
          [variant]="'outline'"
          [tone]="'warning'"
        >Review route</ng-template>
        <ng-template
          ui-button
          [variant]="'outline'"
          [tone]="'info'"
        >See briefing</ng-template>
      </div>
      <div
        ui-cluster
      >
        <ng-template
          ui-button
          [variant]="'ghost'"
          [tone]="'accent'"
        >Send memo</ng-template>
        <ng-template
          ui-button
          [variant]="'ghost'"
          [tone]="'neutral'"
        >File notes</ng-template>
        <ng-template
          ui-button
          [variant]="'ghost'"
          [tone]="'danger'"
        >Delete draft</ng-template>
        <ng-template
          ui-button
          [variant]="'ghost'"
          [tone]="'success'"
        >Approve leave</ng-template>
        <ng-template
          ui-button
          [variant]="'ghost'"
          [tone]="'warning'"
        >Review route</ng-template>
        <ng-template
          ui-button
          [variant]="'ghost'"
          [tone]="'info'"
        >See briefing</ng-template>
      </div>
      <div
        ui-cluster
      >
        <button
          ui-icon-button
          [preset]="'primary'"
          [matchButton]="true"
          [label]="'Send memo'"
        >
          <span
            ui-icon
            name="check"
          ></span>
        </button>
        <button
          ui-icon-button
          [preset]="'standard'"
          [matchButton]="true"
          [label]="'File notes'"
        >
          <span
            ui-icon
            name="check"
          ></span>
        </button>
        <button
          ui-icon-button
          [preset]="'destructive'"
          [matchButton]="true"
          [label]="'Delete draft'"
        >
          <span
            ui-icon
            name="check"
          ></span>
        </button>
        <button
          ui-icon-button
          [variant]="'solid'"
          [tone]="'success'"
          [matchButton]="true"
          [label]="'Approve leave'"
        >
          <span
            ui-icon
            name="check"
          ></span>
        </button>
        <button
          ui-icon-button
          [variant]="'solid'"
          [tone]="'warning'"
          [matchButton]="true"
          [label]="'Review route'"
        >
          <span
            ui-icon
            name="check"
          ></span>
        </button>
        <button
          ui-icon-button
          [variant]="'solid'"
          [tone]="'info'"
          [matchButton]="true"
          [label]="'See briefing'"
        >
          <span
            ui-icon
            name="check"
          ></span>
        </button>
      </div>
      <div
        ui-cluster
      >
        <ng-template
          ui-button
          [variant]="'outline'"
          [tone]="'accent-secondary'"
        >Explore category</ng-template>
        <ng-template
          ui-button
          [variant]="'ghost'"
          [tone]="'accent-tertiary'"
        >Related collection</ng-template>
      </div>
    </div>
  `,
})
export default class Example {}

Interaction emphasis

Keep a choice neutral at rest, then use accent on hover and press. Interaction tone and variant inherit the resting settings unless supplied; pressed feedback remains stronger than hover.

Code

<link
  rel="component"
  href="@threadlabs/looma/components/ui-stack/ui-stack.html"
>
<link
  rel="component"
  href="@threadlabs/looma/components/ui-button/ui-button.html"
>

<ui-stack
  gap="s"
>
  <ui-button
    tone="neutral"
    variant="outline"
    interaction-tone="accent"
    align="start"
    stretch
  > Choose a project </ui-button>
  <ui-button
    tone="neutral"
    variant="ghost"
    interaction-tone="accent"
    interaction-variant="outline"
  > Browse projects </ui-button>
</ui-stack>
<script setup>
  import { UiButton, UiStack } from "@threadlabs/looma";
</script>

<template>
  <UiStack
    gap="s"
  >
    <UiButton
      tone="neutral"
      variant="outline"
      interaction-tone="accent"
      align="start"
      :stretch="true"
    > Choose a project </UiButton>
    <UiButton
      tone="neutral"
      variant="ghost"
      interaction-tone="accent"
      interaction-variant="outline"
    > Browse projects </UiButton>
  </UiStack>
</template>
import { UiButton, UiStack } from "@threadlabs/looma";

export default function Example() {
  return <UiStack
    gap="s"
  >
    <UiButton
      tone="neutral"
      variant="outline"
      interactionTone="accent"
      align="start"
      stretch
    >{" Choose a project "}</UiButton>
    <UiButton
      tone="neutral"
      variant="ghost"
      interactionTone="accent"
      interactionVariant="outline"
    >{" Browse projects "}</UiButton>
  </UiStack>;
}
/** @jsxImportSource preact */
import { UiButton, UiStack } from "@threadlabs/looma";

export default function Example() {
  return <UiStack
    gap="s"
  >
    <UiButton
      tone="neutral"
      variant="outline"
      interactionTone="accent"
      align="start"
      stretch
    >{" Choose a project "}</UiButton>
    <UiButton
      tone="neutral"
      variant="ghost"
      interactionTone="accent"
      interactionVariant="outline"
    >{" Browse projects "}</UiButton>
  </UiStack>;
}
<script>
  import { UiButton, UiStack } from "@threadlabs/looma";
</script>

<UiStack
  gap="s"
>
  <UiButton
    tone="neutral"
    variant="outline"
    interactionTone="accent"
    align="start"
    stretch
  > Choose a project </UiButton>
  <UiButton
    tone="neutral"
    variant="ghost"
    interactionTone="accent"
    interactionVariant="outline"
  > Browse projects </UiButton>
</UiStack>
import { Component } from "@angular/core";
import { UiButton, UiStack } from "@threadlabs/looma";

@Component({
  selector: "example-view",
  standalone: true,
  imports: [UiStack, UiButton],
  template: `
    <div
      ui-stack
      [gap]="'s'"
    >
      <ng-template
        ui-button
        [tone]="'neutral'"
        [variant]="'outline'"
        [interactionTone]="'accent'"
        [align]="'start'"
        [stretch]="true"
      > Choose a project </ng-template>
      <ng-template
        ui-button
        [tone]="'neutral'"
        [variant]="'ghost'"
        [interactionTone]="'accent'"
        [interactionVariant]="'outline'"
      > Browse projects </ng-template>
    </div>
  `,
})
export default class Example {}

API

Package: @threadlabs/looma · Native root: button

Attributes

NameDescriptionPropertyTypeDefaultAllowed values
alignContent alignment. start lays the content out like a list option: from the start edge, with start-aligned text.alignkeywordcentercenter, start
disabledMakes the button unavailable: it cannot be pressed, uses muted neutral colors, and loses its raised look. An unselected disabled ghost keeps its transparent surface; a selected ghost retains a muted selection wash. A disabled link (with href) drops its href and states aria-disabled="true" instead, because a link has no native disabled state.disabledbooleanfalse—
hrefThe link's destination. Supplying href automatically renders a native link instead of a button, including an empty href for the current document. A disabled link drops its href and states aria-disabled="true", so it cannot be followed.hrefstring——
interaction-toneThe action palette while hovered or pressed. inherit follows tone and preset. Choose a different tone for transient emphasis, such as a neutral choice that uses accent feedback. Hover and pressed keep their distinct shared treatments; disabled and loading actions keep their resting treatment.interactionTonekeywordinheritinherit, accent, accent-secondary, accent-tertiary, neutral, danger, success, warning, info
interaction-variantThe paint while hovered or pressed. inherit follows variant and preset. outline, solid and ghost change emphasis without changing the control's layout, size or content. The shared hover and pressed treatments remain distinct.interactionVariantkeywordinheritinherit, outline, solid, ghost
loadingKeeps the action focusable and its label visible while work is in progress. It shows a decorative spinner, states aria-disabled and aria-busy, and prevents activation or form submission until cleared.loadingbooleanfalse—
pendingDeprecated alias for loading.pendingbooleanfalse—
presetA shortcut that sets appearance and tone together: primary is solid accent, standard is solid neutral, quiet is ghost neutral, and destructive is solid danger. A named preset takes precedence over variant and tone. Leave it at none to choose variant and tone independently.presetkeywordnonenone, primary, standard, quiet, destructive
relThe link's relationship to its destination, as on a native link. Only meaningful with href.relstring——
selectedKeeps a ghost choice highlighted with the shared selection surface and text colors, matching selected menu choices independently of its action tone. Composite controls own the appropriate selection semantics, such as aria-selected on a grid cell; this visual state does not make the button a toggle.selectedbooleanfalse—
shaperounded uses the theme's action radius; pill rounds the ends fully, for a compact choice such as a status or a person.shapekeywordroundedrounded, pill
sizeHeight, padding, and type size. xs is 1.5rem with the smallest text, for a control inside a line of small text; sm is the small control height (2rem by default) with tighter padding and smaller text, md is the standard control height (2.5rem), and lg is taller (3rem) with roomier padding. Visible sizes stay the same on touch. Inline xs and link actions can gain an adjustable invisible hit area.sizekeywordmdxs, sm, md, lg
squareGives short labels and icons the same square minimum footprint at each size. Longer labels grow naturally. Applies to boxed actions; link and card variants keep their normal layout.squarebooleanfalse—
stretchFills the inline size of its container.stretchbooleanfalse—
targetWhere a link opens, as on a native link. Only meaningful with href.targetstring——
toneWhich colour the button speaks in. It applies to every variant, so an outline, a ghost, and a solid in the same tone are the same action at three volumes: tone="danger" variant="outline" is a destructive secondary action. neutral is the quiet one, for a button that should not claim a colour. The field variant always uses neutral field colors. Supporting brand families use accent-secondary and accent-tertiary; their usable colors derive from the theme, and unset families follow the primary accent.tonekeywordneutralaccent, accent-secondary, accent-tertiary, neutral, danger, success, warning, info
typeThe button's type, as on a native button. button by default, so it never submits a form by accident. A link has no type.typekeywordbuttonbutton, submit, reset
variantEmphasis: how loudly the button asks. solid fills with its tone, outline draws its tone at the edge over a wash of it, ghost carries only text until it is hovered or selected, and link is an inline text action that reads as a link. danger is solid in the danger tone, kept for the call sites that name it. card uses the same tone on a full-width action with an icon slot, wrapping content, and a centered action slot. field opens a custom value picker: it shares Select's quiet outline, neutral text and slightly raised surface, independently of action tone.variantkeywordoutlineoutline, solid, danger, ghost, link, card, field

Framework props

NameTypeDefaultAllowed values
align"center" | "start"centercenter, start
disabledbooleanfalse—
hrefstring——
interactionTone"inherit" | "accent" | "accent-secondary" | "accent-tertiary" | "neutral" | "danger" | "success" | "warning" | "info"inheritinherit, accent, accent-secondary, accent-tertiary, neutral, danger, success, warning, info
interactionVariant"inherit" | "outline" | "solid" | "ghost"inheritinherit, outline, solid, ghost
loadingbooleanfalse—
pendingbooleanfalse—
preset"none" | "primary" | "standard" | "quiet" | "destructive"nonenone, primary, standard, quiet, destructive
relstring——
selectedbooleanfalse—
shape"rounded" | "pill"roundedrounded, pill
size"xs" | "sm" | "md" | "lg"mdxs, sm, md, lg
squarebooleanfalse—
stretchbooleanfalse—
targetstring——
tone"accent" | "accent-secondary" | "accent-tertiary" | "neutral" | "danger" | "success" | "warning" | "info"neutralaccent, accent-secondary, accent-tertiary, neutral, danger, success, warning, info
type"button" | "submit" | "reset"buttonbutton, submit, reset
variant"outline" | "solid" | "danger" | "ghost" | "link" | "card" | "field"outlineoutline, solid, danger, ghost, link, card, field

Events

No events.

Slots

NamePurpose
iconNamed content for the icon region.
defaultDefault child content.
actionNamed content for the action region.
badgeNamed content for the badge region.

Design tokens

Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.

Component tokens

TokenDeclared valuesFallback
--_tone-outline-bordervar(--ui-accent-outline-border, var(--ui-accent-subtle-text)) | var(--ui-accent-secondary-outline-border, var(--ui-accent-secondary-subtle-text)) | var(--ui-accent-tertiary-outline-border, var(--ui-accent-tertiary-subtle-text)) | var(--ui-control-border) | var(--ui-danger-outline-border, var(--ui-danger-subtle-text)) | var(--ui-success-outline-border, var(--ui-success-subtle-text)) | var(--ui-warning-outline-border, var(--ui-warning-subtle-text)) | var(--ui-info-outline-border, var(--ui-info-subtle-text)) | var(--_hue-text)—
--ui-action-shadowvar(--_ui-action-edge), 0 1px 2px light-dark(rgb(0 0 0 / 0.10), rgb(0 0 0 / 0.20))—
--ui-button-active-surfaceoklch(from var(--_tone) l c h / calc(18% * var(--ui-tonal-opacity, 1))) | var(--ui-neutral-active) | var(--_tone-active) | var(--ui-neutral-active)oklch(from var(--_tone) l c h / calc(18% * var(--ui-tonal-opacity, 1))) | var(--ui-neutral-active)
--ui-button-bordervar(--_hue-text) | var(--ui-accent-border, var(--ui-accent)) | var(--ui-accent-secondary-border, var(--ui-accent-secondary)) | var(--ui-accent-tertiary-border, var(--ui-accent-tertiary)) | var(--ui-control-border, var(--ui-neutral-surface)) | var(--ui-danger-border, var(--ui-danger)) | var(--ui-success-border, var(--ui-success)) | var(--ui-warning-border, var(--ui-warning)) | var(--ui-info-border, var(--ui-info))var(--_tone-outline-border, var(--_tone-text)) | var(--ui-border) | var(--ui-control-border) | var(--ui-control-border, var(--ui-border-strong))
--ui-button-disabled-filter—var(--ui-disabled-filter)
--ui-button-disabled-opacity—1
--ui-button-ghost-active-surface—oklch(from var(--_tone) l c h / 18%) | var(--ui-selection-surface)
--ui-button-ghost-hover-surfacevar(--ui-accent-ghost-hover) | var(--ui-accent-secondary-ghost-hover) | var(--ui-accent-tertiary-ghost-hover) | var(--ui-neutral-ghost-hover) | var(--ui-danger-ghost-hover) | var(--ui-success-ghost-hover) | var(--ui-warning-ghost-hover) | var(--ui-info-ghost-hover)—
--ui-button-ghost-textvar(--_hue-text)var(--ui-selection-text)
--ui-button-hover-bordervar(--ui-accent-outline-border, var(--ui-accent-subtle-text)) | var(--ui-accent-secondary-outline-border, var(--ui-accent-secondary-subtle-text)) | var(--ui-accent-tertiary-outline-border, var(--ui-accent-tertiary-subtle-text)) | var(--ui-control-border) | var(--ui-danger-outline-border, var(--ui-danger-subtle-text)) | var(--ui-success-outline-border, var(--ui-success-subtle-text)) | var(--ui-warning-outline-border, var(--ui-warning-subtle-text)) | var(--ui-info-outline-border, var(--ui-info-subtle-text)) | var(--_hue-text) | var(--ui-accent-hover-border, var(--ui-accent-hover)) | var(--ui-accent-secondary-hover-border, var(--ui-accent-secondary-hover)) | var(--ui-accent-tertiary-hover-border, var(--ui-accent-tertiary-hover)) | var(--ui-control-border-hover, var(--ui-neutral-hover)) | var(--ui-danger-hover-border, var(--ui-danger-hover)) | var(--ui-success-hover-border, var(--ui-success-hover)) | var(--ui-warning-hover-border, var(--ui-warning-hover)) | var(--ui-info-hover-border, var(--ui-info-hover)) | var(--_tone-outline-border) | var(--ui-control-border-hover) | var(--_tone-text) | var(--ui-border-strong)var(--ui-border-strong) | var(--ui-control-border-hover) | var(--ui-control-border-hover, var(--ui-text-secondary))
--ui-button-hover-surfacevar(--_tone-hover) | var(--ui-neutral-hover)transparent | oklch(from var(--_tone-text) l c h / calc(12% * var(--ui-tonal-opacity, 1))) | var(--ui-neutral-hover) | var(--ui-control-surface, var(--ui-surface-elevated))
--ui-button-link-hover-textvar(--_hue-text)var(--ui-text-primary)
--ui-button-link-text—var(--ui-text-secondary)
--ui-button-min-block-size—var(--ui-control-size-md)
--ui-button-radius—var(--ui-action-radius, var(--ui-radius-md)) | var(--ui-radius-round) | var(--ui-field-radius, var(--ui-radius-md))
--ui-button-shadowvar(--ui-shadow-sm) | var(--ui-pressed)var(--ui-action-shadow, var(--_ui-action-shadow)) | var(--ui-neutral-shadow)
--ui-button-solid-shadow—var(--ui-action-shadow, var(--_ui-action-shadow)) | var(--ui-button-shadow, var(--ui-neutral-shadow))
--ui-button-surface—oklch(from var(--_tone) l c h / calc(5% * var(--ui-tonal-opacity, 1))) | transparent | var(--ui-neutral-surface) | var(--ui-control-surface, var(--ui-surface-elevated))
--ui-button-textvar(--_hue-text)var(--ui-text) | var(--ui-text-primary)
--ui-button-white-space—inherit
--ui-neutral-hover-highlightvar(--ui-accent-highlight, none) | var(--ui-accent-secondary-highlight, none) | var(--ui-accent-tertiary-highlight, none) | var(--ui-neutral-highlight, none) | var(--ui-danger-highlight, none) | var(--ui-success-highlight, none) | var(--ui-warning-highlight, none) | var(--ui-info-highlight, none)—

Shared tokens

TokenFallback
--ui-accent—
--ui-accent-active—
--ui-accent-active-bordervar(--ui-accent-active)
--ui-accent-bordervar(--ui-accent)
--ui-accent-ghost-hover—
--ui-accent-glow0 0 transparent
--ui-accent-highlightnone
--ui-accent-hover—
--ui-accent-hover-bordervar(--ui-accent-hover)
--ui-accent-hover-highlightnone
--ui-accent-outline-bordervar(--ui-accent-subtle-text)
--ui-accent-secondary—
--ui-accent-secondary-active—
--ui-accent-secondary-active-bordervar(--ui-accent-secondary-active)
--ui-accent-secondary-bordervar(--ui-accent-secondary)
--ui-accent-secondary-ghost-hover—
--ui-accent-secondary-glow0 0 transparent
--ui-accent-secondary-highlightnone
--ui-accent-secondary-hover—
--ui-accent-secondary-hover-bordervar(--ui-accent-secondary-hover)
--ui-accent-secondary-hover-highlightnone
--ui-accent-secondary-on-strong—
--ui-accent-secondary-outline-bordervar(--ui-accent-secondary-subtle-text)
--ui-accent-secondary-subtle-text—
--ui-accent-secondary-wash—
--ui-accent-secondary-wash-hover—
--ui-accent-solid—
--ui-accent-subtle-text—
--ui-accent-tertiary—
--ui-accent-tertiary-active—
--ui-accent-tertiary-active-bordervar(--ui-accent-tertiary-active)
--ui-accent-tertiary-bordervar(--ui-accent-tertiary)
--ui-accent-tertiary-ghost-hover—
--ui-accent-tertiary-glow0 0 transparent
--ui-accent-tertiary-highlightnone
--ui-accent-tertiary-hover—
--ui-accent-tertiary-hover-bordervar(--ui-accent-tertiary-hover)
--ui-accent-tertiary-hover-highlightnone
--ui-accent-tertiary-on-strong—
--ui-accent-tertiary-outline-bordervar(--ui-accent-tertiary-subtle-text)
--ui-accent-tertiary-subtle-text—
--ui-accent-tertiary-wash—
--ui-accent-tertiary-wash-hover—
--ui-accent-wash—
--ui-accent-wash-hover—
--ui-action-edgeinset var(--_ui-action-start) 1px 0 light-dark(rgb(255 255 255 / 0.08), rgb(255 255 255 / 0.06)), inset calc(-1 * var(--_ui-action-start)) -1px 0 light-dark(rgb(0 0 0 / 0.08), rgb(0 0 0 / 0.08))
--ui-action-inset-lgvar(--ui-space-4)
--ui-action-inset-mdvar(--ui-space-3)
--ui-action-inset-smvar(--ui-space-2)
--ui-action-radiusvar(--ui-radius-md)
--ui-border—
--ui-border-strong—
--ui-border-width—
--ui-control-bordervar(--ui-neutral-surface) | var(--ui-border-strong)
--ui-control-border-hovervar(--ui-neutral-hover) | var(--ui-neutral-active) | var(--ui-text-secondary)
--ui-control-focusvar(--ui-accent-solid)
--ui-control-focus-halo-shadow—
--ui-control-inset-block-lgvar(--ui-space-3)
--ui-control-inset-block-mdvar(--ui-space-2)
--ui-control-inset-block-smvar(--ui-space-1)
--ui-control-min-block-size—
--ui-control-size-lg—
--ui-control-size-md—
--ui-control-size-sm—
--ui-control-size-xs—
--ui-control-surfacevar(--ui-surface-elevated)
--ui-danger—
--ui-danger-activevar(--ui-danger-hover)
--ui-danger-active-bordervar(--ui-danger-active)
--ui-danger-bordervar(--ui-danger)
--ui-danger-ghost-hover—
--ui-danger-glow0 0 transparent
--ui-danger-highlightnone
--ui-danger-hover—
--ui-danger-hover-bordervar(--ui-danger-hover)
--ui-danger-hover-highlightnone
--ui-danger-outline-bordervar(--ui-danger-subtle-text)
--ui-danger-subtle-text—
--ui-danger-wash—
--ui-danger-wash-hover—
--ui-disabled-border—
--ui-disabled-filter—
--ui-disabled-surface—
--ui-disabled-text—
--ui-field-radiusvar(--ui-radius-md)
--ui-focus-halo—
--ui-focus-ring—
--ui-focus-width—
--ui-font-medium—
--ui-font-normal—
--ui-font-size-md—
--ui-font-size-sm—
--ui-font-size-xs—
--ui-info—
--ui-info-active—
--ui-info-active-bordervar(--ui-info-active)
--ui-info-bordervar(--ui-info)
--ui-info-ghost-hover—
--ui-info-glow0 0 transparent
--ui-info-highlightnone
--ui-info-hover—
--ui-info-hover-bordervar(--ui-info-hover)
--ui-info-hover-highlightnone
--ui-info-on-strong—
--ui-info-outline-bordervar(--ui-info-subtle-text)
--ui-info-subtle-text—
--ui-info-wash—
--ui-info-wash-hover—
--ui-line-height-tight—
--ui-motion-base—
--ui-motion-ease—
--ui-motion-fast—
--ui-motion-pressvar(--ui-motion-fast)
--ui-neutral-active—
--ui-neutral-ghost-hover—
--ui-neutral-glow0 0 transparent
--ui-neutral-highlightnone
--ui-neutral-hover—
--ui-neutral-shadow—
--ui-neutral-surface—
--ui-neutral-wash-hover—
--ui-on-accent—
--ui-on-danger—
--ui-press-scale1
--ui-pressed—
--ui-radius-md—
--ui-radius-round—
--ui-radius-sm—
--ui-selection-border—
--ui-selection-surface—
--ui-selection-text—
--ui-shadow-sm—
--ui-space-0-5—
--ui-space-1—
--ui-space-2—
--ui-space-3—
--ui-space-4—
--ui-success—
--ui-success-active—
--ui-success-active-bordervar(--ui-success-active)
--ui-success-bordervar(--ui-success)
--ui-success-ghost-hover—
--ui-success-glow0 0 transparent
--ui-success-highlightnone
--ui-success-hover—
--ui-success-hover-bordervar(--ui-success-hover)
--ui-success-hover-highlightnone
--ui-success-on-strong—
--ui-success-outline-bordervar(--ui-success-subtle-text)
--ui-success-subtle-text—
--ui-success-wash—
--ui-success-wash-hover—
--ui-surface—
--ui-surface-elevated—
--ui-surface-highlightnone
--ui-text—
--ui-text-primary—
--ui-text-secondary—
--ui-tonal-opacity1
--ui-warning—
--ui-warning-active—
--ui-warning-active-bordervar(--ui-warning-active)
--ui-warning-bordervar(--ui-warning)
--ui-warning-ghost-hover—
--ui-warning-glow0 0 transparent
--ui-warning-highlightnone
--ui-warning-hover—
--ui-warning-hover-bordervar(--ui-warning-hover)
--ui-warning-hover-highlightnone
--ui-warning-on-strong—
--ui-warning-outline-bordervar(--ui-warning-subtle-text)
--ui-warning-subtle-text—
--ui-warning-wash—
--ui-warning-wash-hover—