Card
A surface that groups content with shared spacing and corners. Choose outlined, subtle, elevated or placeholder; placeholder marks an empty content area with secondary text and a visible dashed border. Standard or expressive treatment independently follows the theme's material. Tones change colour without changing geometry. All slots are optional; content uses the default slot. Omit the header to keep the title and description together in the body. Use ActionBar in the actions slot, with the main action in its primary slot. CardGroup aligns these regions with native subgrid; ordinary cards keep their stacked layout.
Content card
A single surface with a heading and supporting text.
Team notes
A clear place for shared work.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-card/ui-card.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-stack/ui-stack.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-text/ui-text.html"
>
<ui-card
padding="sm"
>
<ui-stack
gap="xs"
>
<ui-text
variant="heading"
as="h4"
>Team notes</ui-text>
<ui-text
variant="caption"
>A clear place for shared work.</ui-text>
</ui-stack>
</ui-card><script setup>
import { UiCard, UiStack, UiText } from "@threadlabs/looma";
</script>
<template>
<UiCard
padding="sm"
>
<UiStack
gap="xs"
>
<UiText
variant="heading"
as="h4"
>Team notes</UiText>
<UiText
variant="caption"
>A clear place for shared work.</UiText>
</UiStack>
</UiCard>
</template>import { UiCard, UiStack, UiText } from "@threadlabs/looma";
export default function Example() {
return <UiCard
padding="sm"
>
<UiStack
gap="xs"
>
<UiText
variant="heading"
as="h4"
>Team notes</UiText>
<UiText
variant="caption"
>A clear place for shared work.</UiText>
</UiStack>
</UiCard>;
}/** @jsxImportSource preact */
import { UiCard, UiStack, UiText } from "@threadlabs/looma";
export default function Example() {
return <UiCard
padding="sm"
>
<UiStack
gap="xs"
>
<UiText
variant="heading"
as="h4"
>Team notes</UiText>
<UiText
variant="caption"
>A clear place for shared work.</UiText>
</UiStack>
</UiCard>;
}<script>
import { UiCard, UiStack, UiText } from "@threadlabs/looma";
</script>
<UiCard
padding="sm"
>
<UiStack
gap="xs"
>
<UiText
variant="heading"
as="h4"
>Team notes</UiText>
<UiText
variant="caption"
>A clear place for shared work.</UiText>
</UiStack>
</UiCard>import { Component } from "@angular/core";
import { UiCard, UiStack, UiText } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiCard, UiStack, UiText],
template: `
<ng-template
ui-card
[padding]="'sm'"
>
<div
ui-stack
[gap]="'xs'"
>
<ng-template
ui-text
[variant]="'heading'"
[as]="'h4'"
>Team notes</ng-template>
<ng-template
ui-text
[variant]="'caption'"
>A clear place for shared work.</ng-template>
</div>
</ng-template>
`,
})
export default class Example {}Variants
outlined groups content on the page, subtle sets supporting details apart, elevated floats a view's one card, and placeholder marks an empty content area.
Outlined
A bordered panel on the page.
Subtle: supporting details, set apart.
Elevated
The one card of a view, such as sign-in.
No content yet
This area is empty.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-stack/ui-stack.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-card/ui-card.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-text/ui-text.html"
>
<ui-stack
gap="m"
>
<ui-card>
<ui-text
weight="semibold"
>Outlined</ui-text>
<ui-text
tone="secondary"
>A bordered panel on the page.</ui-text>
</ui-card>
<ui-card
variant="subtle"
padding="sm"
>
<ui-text
tone="secondary"
size="sm"
>Subtle: supporting details, set apart.</ui-text>
</ui-card>
<ui-card
variant="elevated"
padding="lg"
>
<ui-text
weight="semibold"
>Elevated</ui-text>
<ui-text
tone="secondary"
>The one card of a view, such as sign-in.</ui-text>
</ui-card>
<ui-card
variant="placeholder"
>
<ui-text
weight="semibold"
>No content yet</ui-text>
<ui-text>This area is empty.</ui-text>
</ui-card>
</ui-stack><script setup>
import { UiCard, UiStack, UiText } from "@threadlabs/looma";
</script>
<template>
<UiStack
gap="m"
>
<UiCard>
<UiText
weight="semibold"
>Outlined</UiText>
<UiText
tone="secondary"
>A bordered panel on the page.</UiText>
</UiCard>
<UiCard
variant="subtle"
padding="sm"
>
<UiText
tone="secondary"
size="sm"
>Subtle: supporting details, set apart.</UiText>
</UiCard>
<UiCard
variant="elevated"
padding="lg"
>
<UiText
weight="semibold"
>Elevated</UiText>
<UiText
tone="secondary"
>The one card of a view, such as sign-in.</UiText>
</UiCard>
<UiCard
variant="placeholder"
>
<UiText
weight="semibold"
>No content yet</UiText>
<UiText>This area is empty.</UiText>
</UiCard>
</UiStack>
</template>import { UiCard, UiStack, UiText } from "@threadlabs/looma";
export default function Example() {
return <UiStack
gap="m"
>
<UiCard>
<UiText
weight="semibold"
>Outlined</UiText>
<UiText
tone="secondary"
>A bordered panel on the page.</UiText>
</UiCard>
<UiCard
variant="subtle"
padding="sm"
>
<UiText
tone="secondary"
size="sm"
>Subtle: supporting details, set apart.</UiText>
</UiCard>
<UiCard
variant="elevated"
padding="lg"
>
<UiText
weight="semibold"
>Elevated</UiText>
<UiText
tone="secondary"
>The one card of a view, such as sign-in.</UiText>
</UiCard>
<UiCard
variant="placeholder"
>
<UiText
weight="semibold"
>No content yet</UiText>
<UiText>This area is empty.</UiText>
</UiCard>
</UiStack>;
}/** @jsxImportSource preact */
import { UiCard, UiStack, UiText } from "@threadlabs/looma";
export default function Example() {
return <UiStack
gap="m"
>
<UiCard>
<UiText
weight="semibold"
>Outlined</UiText>
<UiText
tone="secondary"
>A bordered panel on the page.</UiText>
</UiCard>
<UiCard
variant="subtle"
padding="sm"
>
<UiText
tone="secondary"
size="sm"
>Subtle: supporting details, set apart.</UiText>
</UiCard>
<UiCard
variant="elevated"
padding="lg"
>
<UiText
weight="semibold"
>Elevated</UiText>
<UiText
tone="secondary"
>The one card of a view, such as sign-in.</UiText>
</UiCard>
<UiCard
variant="placeholder"
>
<UiText
weight="semibold"
>No content yet</UiText>
<UiText>This area is empty.</UiText>
</UiCard>
</UiStack>;
}<script>
import { UiCard, UiStack, UiText } from "@threadlabs/looma";
</script>
<UiStack
gap="m"
>
<UiCard>
<UiText
weight="semibold"
>Outlined</UiText>
<UiText
tone="secondary"
>A bordered panel on the page.</UiText>
</UiCard>
<UiCard
variant="subtle"
padding="sm"
>
<UiText
tone="secondary"
size="sm"
>Subtle: supporting details, set apart.</UiText>
</UiCard>
<UiCard
variant="elevated"
padding="lg"
>
<UiText
weight="semibold"
>Elevated</UiText>
<UiText
tone="secondary"
>The one card of a view, such as sign-in.</UiText>
</UiCard>
<UiCard
variant="placeholder"
>
<UiText
weight="semibold"
>No content yet</UiText>
<UiText>This area is empty.</UiText>
</UiCard>
</UiStack>import { Component } from "@angular/core";
import { UiCard, UiStack, UiText } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiStack, UiCard, UiText],
template: `
<div
ui-stack
[gap]="'m'"
>
<ng-template
ui-card
>
<ng-template
ui-text
[weight]="'semibold'"
>Outlined</ng-template>
<ng-template
ui-text
[tone]="'secondary'"
>A bordered panel on the page.</ng-template>
</ng-template>
<ng-template
ui-card
[variant]="'subtle'"
[padding]="'sm'"
>
<ng-template
ui-text
[tone]="'secondary'"
[size]="'sm'"
>Subtle: supporting details, set apart.</ng-template>
</ng-template>
<ng-template
ui-card
[variant]="'elevated'"
[padding]="'lg'"
>
<ng-template
ui-text
[weight]="'semibold'"
>Elevated</ng-template>
<ng-template
ui-text
[tone]="'secondary'"
>The one card of a view, such as sign-in.</ng-template>
</ng-template>
<ng-template
ui-card
[variant]="'placeholder'"
>
<ng-template
ui-text
[weight]="'semibold'"
>No content yet</ng-template>
<ng-template
ui-text
>This area is empty.</ng-template>
</ng-template>
</div>
`,
})
export default class Example {}Danger
tone="danger" tints a consequential section while retaining the same corners and border geometry as other cards.
Delete this site
Everything in it is deleted for everyone.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-card/ui-card.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-text/ui-text.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"
>
<ui-card
tone="danger"
>
<ui-text
weight="semibold"
>Delete this site</ui-text>
<ui-text
tone="secondary"
size="sm"
>Everything in it is deleted for everyone.</ui-text>
<ui-cluster>
<ui-button
variant="solid"
tone="danger"
>Delete site</ui-button>
</ui-cluster>
</ui-card><script setup>
import { UiButton, UiCard, UiCluster, UiText } from "@threadlabs/looma";
</script>
<template>
<UiCard
tone="danger"
>
<UiText
weight="semibold"
>Delete this site</UiText>
<UiText
tone="secondary"
size="sm"
>Everything in it is deleted for everyone.</UiText>
<UiCluster>
<UiButton
variant="solid"
tone="danger"
>Delete site</UiButton>
</UiCluster>
</UiCard>
</template>import { UiButton, UiCard, UiCluster, UiText } from "@threadlabs/looma";
export default function Example() {
return <UiCard
tone="danger"
>
<UiText
weight="semibold"
>Delete this site</UiText>
<UiText
tone="secondary"
size="sm"
>Everything in it is deleted for everyone.</UiText>
<UiCluster>
<UiButton
variant="solid"
tone="danger"
>Delete site</UiButton>
</UiCluster>
</UiCard>;
}/** @jsxImportSource preact */
import { UiButton, UiCard, UiCluster, UiText } from "@threadlabs/looma";
export default function Example() {
return <UiCard
tone="danger"
>
<UiText
weight="semibold"
>Delete this site</UiText>
<UiText
tone="secondary"
size="sm"
>Everything in it is deleted for everyone.</UiText>
<UiCluster>
<UiButton
variant="solid"
tone="danger"
>Delete site</UiButton>
</UiCluster>
</UiCard>;
}<script>
import { UiButton, UiCard, UiCluster, UiText } from "@threadlabs/looma";
</script>
<UiCard
tone="danger"
>
<UiText
weight="semibold"
>Delete this site</UiText>
<UiText
tone="secondary"
size="sm"
>Everything in it is deleted for everyone.</UiText>
<UiCluster>
<UiButton
variant="solid"
tone="danger"
>Delete site</UiButton>
</UiCluster>
</UiCard>import { Component } from "@angular/core";
import { UiButton, UiCard, UiCluster, UiText } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiCard, UiText, UiCluster, UiButton],
template: `
<ng-template
ui-card
[tone]="'danger'"
>
<ng-template
ui-text
[weight]="'semibold'"
>Delete this site</ng-template>
<ng-template
ui-text
[tone]="'secondary'"
[size]="'sm'"
>Everything in it is deleted for everyone.</ng-template>
<div
ui-cluster
>
<ng-template
ui-button
[variant]="'solid'"
[tone]="'danger'"
>Delete site</ng-template>
</div>
</ng-template>
`,
})
export default class Example {}Quiet semantic surfaces
Cards can group supporting content on a softly tinted surface. Their child controls retain the normal design-system treatment.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-stack/ui-stack.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-card/ui-card.html"
>
<ui-stack
gap="l"
>
<ui-card
variant="subtle"
tone="accent"
>Suggestions to consider</ui-card>
<ui-card
variant="subtle"
tone="warning"
>Work in progress</ui-card>
<ui-card
variant="subtle"
tone="success"
>Completed work</ui-card>
<ui-card
variant="subtle"
tone="info"
>Useful context</ui-card>
<ui-card
variant="subtle"
tone="accent-secondary"
>Supporting feature</ui-card>
<ui-card
variant="subtle"
tone="accent-tertiary"
>Related collection</ui-card>
</ui-stack><script setup>
import { UiCard, UiStack } from "@threadlabs/looma";
</script>
<template>
<UiStack
gap="l"
>
<UiCard
variant="subtle"
tone="accent"
>Suggestions to consider</UiCard>
<UiCard
variant="subtle"
tone="warning"
>Work in progress</UiCard>
<UiCard
variant="subtle"
tone="success"
>Completed work</UiCard>
<UiCard
variant="subtle"
tone="info"
>Useful context</UiCard>
<UiCard
variant="subtle"
tone="accent-secondary"
>Supporting feature</UiCard>
<UiCard
variant="subtle"
tone="accent-tertiary"
>Related collection</UiCard>
</UiStack>
</template>import { UiCard, UiStack } from "@threadlabs/looma";
export default function Example() {
return <UiStack
gap="l"
>
<UiCard
variant="subtle"
tone="accent"
>Suggestions to consider</UiCard>
<UiCard
variant="subtle"
tone="warning"
>Work in progress</UiCard>
<UiCard
variant="subtle"
tone="success"
>Completed work</UiCard>
<UiCard
variant="subtle"
tone="info"
>Useful context</UiCard>
<UiCard
variant="subtle"
tone="accent-secondary"
>Supporting feature</UiCard>
<UiCard
variant="subtle"
tone="accent-tertiary"
>Related collection</UiCard>
</UiStack>;
}/** @jsxImportSource preact */
import { UiCard, UiStack } from "@threadlabs/looma";
export default function Example() {
return <UiStack
gap="l"
>
<UiCard
variant="subtle"
tone="accent"
>Suggestions to consider</UiCard>
<UiCard
variant="subtle"
tone="warning"
>Work in progress</UiCard>
<UiCard
variant="subtle"
tone="success"
>Completed work</UiCard>
<UiCard
variant="subtle"
tone="info"
>Useful context</UiCard>
<UiCard
variant="subtle"
tone="accent-secondary"
>Supporting feature</UiCard>
<UiCard
variant="subtle"
tone="accent-tertiary"
>Related collection</UiCard>
</UiStack>;
}<script>
import { UiCard, UiStack } from "@threadlabs/looma";
</script>
<UiStack
gap="l"
>
<UiCard
variant="subtle"
tone="accent"
>Suggestions to consider</UiCard>
<UiCard
variant="subtle"
tone="warning"
>Work in progress</UiCard>
<UiCard
variant="subtle"
tone="success"
>Completed work</UiCard>
<UiCard
variant="subtle"
tone="info"
>Useful context</UiCard>
<UiCard
variant="subtle"
tone="accent-secondary"
>Supporting feature</UiCard>
<UiCard
variant="subtle"
tone="accent-tertiary"
>Related collection</UiCard>
</UiStack>import { Component } from "@angular/core";
import { UiCard, UiStack } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiStack, UiCard],
template: `
<div
ui-stack
[gap]="'l'"
>
<ng-template
ui-card
[variant]="'subtle'"
[tone]="'accent'"
>Suggestions to consider</ng-template>
<ng-template
ui-card
[variant]="'subtle'"
[tone]="'warning'"
>Work in progress</ng-template>
<ng-template
ui-card
[variant]="'subtle'"
[tone]="'success'"
>Completed work</ng-template>
<ng-template
ui-card
[variant]="'subtle'"
[tone]="'info'"
>Useful context</ng-template>
<ng-template
ui-card
[variant]="'subtle'"
[tone]="'accent-secondary'"
>Supporting feature</ng-template>
<ng-template
ui-card
[variant]="'subtle'"
[tone]="'accent-tertiary'"
>Related collection</ng-template>
</div>
`,
})
export default class Example {}Expressive surface
Expressive treatment uses the theme's material independently of elevation. Bookish catches light through a frosted rim; a matte theme stays opaque. Ordinary elevated cards retain their subtle lift.
Fellowship office
Second breakfast briefing
Sam has prepared the agenda. Frodo is bringing the maps, and Sméagol has volunteered to take minutes.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-card/ui-card.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-text/ui-text.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"
>
<ui-card
variant="elevated"
treatment="expressive"
>
<ui-text
variant="eyebrow"
tone="secondary"
>Fellowship office</ui-text>
<ui-text
variant="heading"
as="h2"
>Second breakfast briefing</ui-text>
<ui-text>Sam has prepared the agenda. Frodo is bringing the maps, and Sméagol has volunteered to take minutes.</ui-text>
<ui-cluster>
<ui-button
preset="primary"
>Join briefing</ui-button>
<ui-button
preset="quiet"
>Read agenda</ui-button>
</ui-cluster>
</ui-card><script setup>
import { UiButton, UiCard, UiCluster, UiText } from "@threadlabs/looma";
</script>
<template>
<UiCard
variant="elevated"
treatment="expressive"
>
<UiText
variant="eyebrow"
tone="secondary"
>Fellowship office</UiText>
<UiText
variant="heading"
as="h2"
>Second breakfast briefing</UiText>
<UiText>Sam has prepared the agenda. Frodo is bringing the maps, and Sméagol has volunteered to take minutes.</UiText>
<UiCluster>
<UiButton
preset="primary"
>Join briefing</UiButton>
<UiButton
preset="quiet"
>Read agenda</UiButton>
</UiCluster>
</UiCard>
</template>import { UiButton, UiCard, UiCluster, UiText } from "@threadlabs/looma";
export default function Example() {
return <UiCard
variant="elevated"
treatment="expressive"
>
<UiText
variant="eyebrow"
tone="secondary"
>Fellowship office</UiText>
<UiText
variant="heading"
as="h2"
>Second breakfast briefing</UiText>
<UiText>Sam has prepared the agenda. Frodo is bringing the maps, and Sméagol has volunteered to take minutes.</UiText>
<UiCluster>
<UiButton
preset="primary"
>Join briefing</UiButton>
<UiButton
preset="quiet"
>Read agenda</UiButton>
</UiCluster>
</UiCard>;
}/** @jsxImportSource preact */
import { UiButton, UiCard, UiCluster, UiText } from "@threadlabs/looma";
export default function Example() {
return <UiCard
variant="elevated"
treatment="expressive"
>
<UiText
variant="eyebrow"
tone="secondary"
>Fellowship office</UiText>
<UiText
variant="heading"
as="h2"
>Second breakfast briefing</UiText>
<UiText>Sam has prepared the agenda. Frodo is bringing the maps, and Sméagol has volunteered to take minutes.</UiText>
<UiCluster>
<UiButton
preset="primary"
>Join briefing</UiButton>
<UiButton
preset="quiet"
>Read agenda</UiButton>
</UiCluster>
</UiCard>;
}<script>
import { UiButton, UiCard, UiCluster, UiText } from "@threadlabs/looma";
</script>
<UiCard
variant="elevated"
treatment="expressive"
>
<UiText
variant="eyebrow"
tone="secondary"
>Fellowship office</UiText>
<UiText
variant="heading"
as="h2"
>Second breakfast briefing</UiText>
<UiText>Sam has prepared the agenda. Frodo is bringing the maps, and Sméagol has volunteered to take minutes.</UiText>
<UiCluster>
<UiButton
preset="primary"
>Join briefing</UiButton>
<UiButton
preset="quiet"
>Read agenda</UiButton>
</UiCluster>
</UiCard>import { Component } from "@angular/core";
import { UiButton, UiCard, UiCluster, UiText } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiCard, UiText, UiCluster, UiButton],
template: `
<ng-template
ui-card
[variant]="'elevated'"
[treatment]="'expressive'"
>
<ng-template
ui-text
[variant]="'eyebrow'"
[tone]="'secondary'"
>Fellowship office</ng-template>
<ng-template
ui-text
[variant]="'heading'"
[as]="'h2'"
>Second breakfast briefing</ng-template>
<ng-template
ui-text
>Sam has prepared the agenda. Frodo is bringing the maps, and Sméagol has volunteered to take minutes.</ng-template>
<div
ui-cluster
>
<ng-template
ui-button
[preset]="'primary'"
>Join briefing</ng-template>
<ng-template
ui-button
[preset]="'quiet'"
>Read agenda</ng-template>
</div>
</ng-template>
`,
})
export default class Example {}Fill a bounded column
Place this stack in a bounded-height parent. The surface fills the remaining space while its body scrolls independently.
Reference panel
A short list still has a full-height surface.
Longer content scrolls inside this panel.
Persistent controls stay below the panel.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-stack/ui-stack.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-text/ui-text.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-card/ui-card.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-scroll-area/ui-scroll-area.html"
>
<ui-stack
fill
gap="s"
>
<ui-text
weight="semibold"
>Reference panel</ui-text>
<ui-card
fill
variant="subtle"
tone="accent"
padding="xs"
>
<ui-scroll-area
fill
>
<ui-stack
gap="s"
>
<ui-text>A short list still has a full-height surface.</ui-text>
<ui-text>Longer content scrolls inside this panel.</ui-text>
</ui-stack>
</ui-scroll-area>
</ui-card>
<ui-text
size="sm"
tone="secondary"
>Persistent controls stay below the panel.</ui-text>
</ui-stack><script setup>
import { UiCard, UiScrollArea, UiStack, UiText } from "@threadlabs/looma";
</script>
<template>
<UiStack
:fill="true"
gap="s"
>
<UiText
weight="semibold"
>Reference panel</UiText>
<UiCard
:fill="true"
variant="subtle"
tone="accent"
padding="xs"
>
<UiScrollArea
:fill="true"
>
<UiStack
gap="s"
>
<UiText>A short list still has a full-height surface.</UiText>
<UiText>Longer content scrolls inside this panel.</UiText>
</UiStack>
</UiScrollArea>
</UiCard>
<UiText
size="sm"
tone="secondary"
>Persistent controls stay below the panel.</UiText>
</UiStack>
</template>import { UiCard, UiScrollArea, UiStack, UiText } from "@threadlabs/looma";
export default function Example() {
return <UiStack
fill
gap="s"
>
<UiText
weight="semibold"
>Reference panel</UiText>
<UiCard
fill
variant="subtle"
tone="accent"
padding="xs"
>
<UiScrollArea
fill
>
<UiStack
gap="s"
>
<UiText>A short list still has a full-height surface.</UiText>
<UiText>Longer content scrolls inside this panel.</UiText>
</UiStack>
</UiScrollArea>
</UiCard>
<UiText
size="sm"
tone="secondary"
>Persistent controls stay below the panel.</UiText>
</UiStack>;
}/** @jsxImportSource preact */
import { UiCard, UiScrollArea, UiStack, UiText } from "@threadlabs/looma";
export default function Example() {
return <UiStack
fill
gap="s"
>
<UiText
weight="semibold"
>Reference panel</UiText>
<UiCard
fill
variant="subtle"
tone="accent"
padding="xs"
>
<UiScrollArea
fill
>
<UiStack
gap="s"
>
<UiText>A short list still has a full-height surface.</UiText>
<UiText>Longer content scrolls inside this panel.</UiText>
</UiStack>
</UiScrollArea>
</UiCard>
<UiText
size="sm"
tone="secondary"
>Persistent controls stay below the panel.</UiText>
</UiStack>;
}<script>
import { UiCard, UiScrollArea, UiStack, UiText } from "@threadlabs/looma";
</script>
<UiStack
fill
gap="s"
>
<UiText
weight="semibold"
>Reference panel</UiText>
<UiCard
fill
variant="subtle"
tone="accent"
padding="xs"
>
<UiScrollArea
fill
>
<UiStack
gap="s"
>
<UiText>A short list still has a full-height surface.</UiText>
<UiText>Longer content scrolls inside this panel.</UiText>
</UiStack>
</UiScrollArea>
</UiCard>
<UiText
size="sm"
tone="secondary"
>Persistent controls stay below the panel.</UiText>
</UiStack>import { Component } from "@angular/core";
import { UiCard, UiScrollArea, UiStack, UiText } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiStack, UiText, UiCard, UiScrollArea],
template: `
<div
ui-stack
[fill]="true"
[gap]="'s'"
>
<ng-template
ui-text
[weight]="'semibold'"
>Reference panel</ng-template>
<ng-template
ui-card
[fill]="true"
[variant]="'subtle'"
[tone]="'accent'"
[padding]="'xs'"
>
<div
ui-scroll-area
[fill]="true"
>
<div
ui-stack
[gap]="'s'"
>
<ng-template
ui-text
>A short list still has a full-height surface.</ng-template>
<ng-template
ui-text
>Longer content scrolls inside this panel.</ng-template>
</div>
</div>
</ng-template>
<ng-template
ui-text
[size]="'sm'"
[tone]="'secondary'"
>Persistent controls stay below the panel.</ng-template>
</div>
`,
})
export default class Example {}API
Package: @threadlabs/looma · Native root: div
Attributes
| Name | Description | Property | Type | Default | Allowed values |
|---|---|---|---|---|---|
as | The native element the card renders. Choose the one that names the content: a section of a page, a self-contained article, or an aside. | as | keyword | div | div, section, article, aside |
fill | In a bounded flex column, grows into the remaining height. Put a filling Scroll Area in the body when its content needs to scroll independently. | fill | boolean | false | — |
padding | The space inside the card: xs is compact (0.5rem), sm is snug (0.75rem), md is the default (1.5rem), and lg is roomy (2rem). | padding | keyword | md | xs, sm, md, lg |
size | Optional size preset. content keeps the existing intrinsic sizing; the other sizes constrain the surface for compact, medium, or large content. | size | keyword | content | content, sm, md, lg |
tone | accent, info, success, warning and danger add a quiet semantic surface without changing card geometry. Row highlights follow the surface hue; action controls retain their own intent. Supporting brand families use accent-secondary and accent-tertiary; their usable colors derive from the theme, and unset families follow the primary accent. | tone | keyword | neutral | neutral, accent, accent-secondary, accent-tertiary, info, success, warning, danger |
treatment | How much of the theme's decorative material the surface expresses. standard preserves its restrained treatment; expressive uses the full material treatment. Its glass rim can transmit and frost the surrounding scene while content stays nearly opaque. Surface separation controls the rim, independently of frosting. This does not add elevation or require glass. | treatment | keyword | standard | standard, expressive |
variant | The surface. outlined is a bordered panel on the page; subtle is a quiet filled one with no border, for supporting details set apart from the flow; elevated adds a subtle lift above the page, for a view's one card, such as sign-in. placeholder marks an empty content area with a visible dashed border and secondary text, keeping the outlined card's dimensions. Treatment independently controls decorative material. | variant | keyword | outlined | outlined, subtle, elevated, placeholder |
Framework props
| Name | Type | Default | Allowed values |
|---|---|---|---|
as | "div" | "section" | "article" | "aside" | div | div, section, article, aside |
fill | boolean | false | — |
padding | "xs" | "sm" | "md" | "lg" | md | xs, sm, md, lg |
size | "content" | "sm" | "md" | "lg" | content | content, sm, md, lg |
tone | "neutral" | "accent" | "accent-secondary" | "accent-tertiary" | "info" | "success" | "warning" | "danger" | neutral | neutral, accent, accent-secondary, accent-tertiary, info, success, warning, danger |
treatment | "standard" | "expressive" | standard | standard, expressive |
variant | "outlined" | "subtle" | "elevated" | "placeholder" | outlined | outlined, subtle, elevated, placeholder |
Events
No events.
Slots
| Name | Purpose |
|---|---|
header | Named content for the header region. |
default | Default child content. |
actions | Named content for the actions region. |
Design tokens
Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.
Component tokens
No component tokens.
Shared tokens
| Token | Fallback |
|---|---|
--ui-accent-secondary-soft | — |
--ui-accent-secondary-soft-border | — |
--ui-accent-secondary-subtle-text | — |
--ui-accent-soft | — |
--ui-accent-soft-border | — |
--ui-accent-subtle-text | — |
--ui-accent-tertiary-soft | — |
--ui-accent-tertiary-soft-border | — |
--ui-accent-tertiary-subtle-text | — |
--ui-border-strong | — |
--ui-border-subtle | — |
--ui-border-width | — |
--ui-danger-soft | — |
--ui-danger-soft-border | — |
--ui-danger-subtle-text | — |
--ui-glassiness | 0 |
--ui-info-soft | — |
--ui-info-soft-border | — |
--ui-info-subtle-text | — |
--ui-material-edge | var(--ui-border-strong) |
--ui-material-glass | var(--ui-surface-raised) |
--ui-material-glow | 0 0 transparent |
--ui-material-highlight | var(--ui-surface-highlight, linear-gradient(transparent, transparent)) |
--ui-radius-lg | — |
--ui-shadow-lg | — |
--ui-space-1 | — |
--ui-space-2 | — |
--ui-space-3 | — |
--ui-space-4 | — |
--ui-space-5 | — |
--ui-space-6 | — |
--ui-success-soft | — |
--ui-success-soft-border | — |
--ui-success-subtle-text | — |
--ui-surface-highlight | linear-gradient(transparent, transparent) |
--ui-surface-muted | — |
--ui-surface-raised | — |
--ui-surface-separation | 0 |
--ui-text-primary | — |
--ui-text-secondary | — |
--ui-warning-soft | — |
--ui-warning-soft-border | — |
--ui-warning-subtle-text | — |