List
A list of items: pages, people, sites, results.
Native list items
Use li for ordinary content. The list owns the spacing between its items.
- Roadmap
- Onboarding
- Glossary
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-list/ui-list.html"
>
<ui-list
aria-label="Recently updated"
>
<li>Roadmap</li>
<li>Onboarding</li>
<li>Glossary</li>
</ui-list><script setup>
import { UiList } from "@threadlabs/looma";
</script>
<template>
<UiList
aria-label="Recently updated"
>
<li>Roadmap</li>
<li>Onboarding</li>
<li>Glossary</li>
</UiList>
</template>import { UiList } from "@threadlabs/looma";
export default function Example() {
return <UiList
aria-label="Recently updated"
>
<li>Roadmap</li>
<li>Onboarding</li>
<li>Glossary</li>
</UiList>;
}/** @jsxImportSource preact */
import { UiList } from "@threadlabs/looma";
export default function Example() {
return <UiList
aria-label="Recently updated"
>
<li>Roadmap</li>
<li>Onboarding</li>
<li>Glossary</li>
</UiList>;
}<script>
import { UiList } from "@threadlabs/looma";
</script>
<UiList
aria-label="Recently updated"
>
<li>Roadmap</li>
<li>Onboarding</li>
<li>Glossary</li>
</UiList>import { Component } from "@angular/core";
import { UiList } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiList],
template: `
<ng-template
ui-list
[hnAttributes]="{ 'aria-label': 'Recently updated' }"
>
<li>Roadmap</li>
<li>Onboarding</li>
<li>Glossary</li>
</ng-template>
`,
})
export default class Example {}Grid
layout="grid" sets card items in as many columns as fit, each at least 14rem wide.
- Roadmap Planning
- Onboarding People
- Glossary Reference
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-list/ui-list.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-list-item/ui-list-item.html"
>
<ui-list
layout="grid"
aria-label="Pinned"
>
<ui-list-item
variant="card"
><a
href="#roadmap"
>Roadmap</a><span
slot="description"
>Planning</span></ui-list-item>
<ui-list-item
variant="card"
><a
href="#onboarding"
>Onboarding</a><span
slot="description"
>People</span></ui-list-item>
<ui-list-item
variant="card"
><a
href="#glossary"
>Glossary</a><span
slot="description"
>Reference</span></ui-list-item>
</ui-list><script setup>
import { UiList, UiListItem } from "@threadlabs/looma";
</script>
<template>
<UiList
layout="grid"
aria-label="Pinned"
>
<UiListItem
variant="card"
>
<a
href="#roadmap"
>Roadmap</a>
<template #description>
<span>Planning</span>
</template>
</UiListItem>
<UiListItem
variant="card"
>
<a
href="#onboarding"
>Onboarding</a>
<template #description>
<span>People</span>
</template>
</UiListItem>
<UiListItem
variant="card"
>
<a
href="#glossary"
>Glossary</a>
<template #description>
<span>Reference</span>
</template>
</UiListItem>
</UiList>
</template>import { UiList, UiListItem } from "@threadlabs/looma";
export default function Example() {
return <UiList
layout="grid"
aria-label="Pinned"
>
<UiListItem
variant="card"
slots={{
description: <span>Planning</span>,
}}
><a
href="#roadmap"
>Roadmap</a></UiListItem>
<UiListItem
variant="card"
slots={{
description: <span>People</span>,
}}
><a
href="#onboarding"
>Onboarding</a></UiListItem>
<UiListItem
variant="card"
slots={{
description: <span>Reference</span>,
}}
><a
href="#glossary"
>Glossary</a></UiListItem>
</UiList>;
}/** @jsxImportSource preact */
import { UiList, UiListItem } from "@threadlabs/looma";
export default function Example() {
return <UiList
layout="grid"
aria-label="Pinned"
>
<UiListItem
variant="card"
slots={{
description: <span>Planning</span>,
}}
><a
href="#roadmap"
>Roadmap</a></UiListItem>
<UiListItem
variant="card"
slots={{
description: <span>People</span>,
}}
><a
href="#onboarding"
>Onboarding</a></UiListItem>
<UiListItem
variant="card"
slots={{
description: <span>Reference</span>,
}}
><a
href="#glossary"
>Glossary</a></UiListItem>
</UiList>;
}<script>
import { UiList, UiListItem } from "@threadlabs/looma";
</script>
{#snippet description()}
<span>Planning</span>
{/snippet}
{#snippet listItemDescription()}
<span>People</span>
{/snippet}
{#snippet listItemDescription2()}
<span>Reference</span>
{/snippet}
<UiList
layout="grid"
aria-label="Pinned"
>
<UiListItem
variant="card"
slots={{ description }}
><a
href="#roadmap"
>Roadmap</a></UiListItem>
<UiListItem
variant="card"
slots={{ description: listItemDescription }}
><a
href="#onboarding"
>Onboarding</a></UiListItem>
<UiListItem
variant="card"
slots={{ description: listItemDescription2 }}
><a
href="#glossary"
>Glossary</a></UiListItem>
</UiList>import { Component } from "@angular/core";
import { UiList, UiListItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiList, UiListItem],
template: `
<ng-template
ui-list
[layout]="'grid'"
[hnAttributes]="{ 'aria-label': 'Pinned' }"
>
<li
ui-list-item
[variant]="'card'"
>
<a
href="#roadmap"
>Roadmap</a>
<ng-container ngProjectAs="[slot='description']">
<span>Planning</span>
</ng-container>
</li>
<li
ui-list-item
[variant]="'card'"
>
<a
href="#onboarding"
>Onboarding</a>
<ng-container ngProjectAs="[slot='description']">
<span>People</span>
</ng-container>
</li>
<li
ui-list-item
[variant]="'card'"
>
<a
href="#glossary"
>Glossary</a>
<ng-container ngProjectAs="[slot='description']">
<span>Reference</span>
</ng-container>
</li>
</ng-template>
`,
})
export default class Example {}Compact
density="compact" sets a short list of facts close together, such as what a plan includes.
- Up to 50 people
- Multiple workspaces
- Multi-document review
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-list/ui-list.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-list-item/ui-list-item.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-icon/ui-icon.html"
>
<ui-list
density="compact"
aria-label="Pro includes"
>
<ui-list-item>
<ui-icon
slot="leading"
name="circle-check"
></ui-icon>
Up to 50 people
</ui-list-item>
<ui-list-item>
<ui-icon
slot="leading"
name="circle-check"
></ui-icon>
Multiple workspaces
</ui-list-item>
<ui-list-item>
<ui-icon
slot="leading"
name="circle-check"
></ui-icon>
Multi-document review
</ui-list-item>
</ui-list><script setup>
import { UiIcon, UiList, UiListItem } from "@threadlabs/looma";
</script>
<template>
<UiList
density="compact"
aria-label="Pro includes"
>
<UiListItem><template #leading>
<UiIcon
name="circle-check"
/>
</template>Up to 50 people</UiListItem>
<UiListItem><template #leading>
<UiIcon
name="circle-check"
/>
</template>Multiple workspaces</UiListItem>
<UiListItem><template #leading>
<UiIcon
name="circle-check"
/>
</template>Multi-document review</UiListItem>
</UiList>
</template>import { UiIcon, UiList, UiListItem } from "@threadlabs/looma";
export default function Example() {
return <UiList
density="compact"
aria-label="Pro includes"
>
<UiListItem
slots={{
leading: <UiIcon
name="circle-check"
/>,
}}
>Up to 50 people</UiListItem>
<UiListItem
slots={{
leading: <UiIcon
name="circle-check"
/>,
}}
>Multiple workspaces</UiListItem>
<UiListItem
slots={{
leading: <UiIcon
name="circle-check"
/>,
}}
>Multi-document review</UiListItem>
</UiList>;
}/** @jsxImportSource preact */
import { UiIcon, UiList, UiListItem } from "@threadlabs/looma";
export default function Example() {
return <UiList
density="compact"
aria-label="Pro includes"
>
<UiListItem
slots={{
leading: <UiIcon
name="circle-check"
/>,
}}
>Up to 50 people</UiListItem>
<UiListItem
slots={{
leading: <UiIcon
name="circle-check"
/>,
}}
>Multiple workspaces</UiListItem>
<UiListItem
slots={{
leading: <UiIcon
name="circle-check"
/>,
}}
>Multi-document review</UiListItem>
</UiList>;
}<script>
import { UiIcon, UiList, UiListItem } from "@threadlabs/looma";
</script>
{#snippet leading()}
<UiIcon
name="circle-check"
/>
{/snippet}
{#snippet listItemLeading()}
<UiIcon
name="circle-check"
/>
{/snippet}
{#snippet listItemLeading2()}
<UiIcon
name="circle-check"
/>
{/snippet}
<UiList
density="compact"
aria-label="Pro includes"
>
<UiListItem
slots={{ leading }}
>Up to 50 people</UiListItem>
<UiListItem
slots={{ leading: listItemLeading }}
>Multiple workspaces</UiListItem>
<UiListItem
slots={{ leading: listItemLeading2 }}
>Multi-document review</UiListItem>
</UiList>import { Component } from "@angular/core";
import { UiIcon, UiList, UiListItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiList, UiListItem, UiIcon],
template: `
<ng-template
ui-list
[density]="'compact'"
[hnAttributes]="{ 'aria-label': 'Pro includes' }"
>
<li
ui-list-item
><ng-container ngProjectAs="[slot='leading']">
<span
ui-icon
name="circle-check"
></span>
</ng-container>Up to 50 people</li>
<li
ui-list-item
><ng-container ngProjectAs="[slot='leading']">
<span
ui-icon
name="circle-check"
></span>
</ng-container>Multiple workspaces</li>
<li
ui-list-item
><ng-container ngProjectAs="[slot='leading']">
<span
ui-icon
name="circle-check"
></span>
</ng-container>Multi-document review</li>
</ng-template>
`,
})
export default class Example {}Structured rows
Use ui-list-item when a row needs leading, title, description, trailing, or full-row link behavior.
- Roadmap Planning · Edited 2h ago
- Onboarding People · Edited yesterday
- Glossary Edited 3 days ago
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-list/ui-list.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-list-item/ui-list-item.html"
>
<ui-list
aria-label="Recently updated"
>
<ui-list-item><a
href="#roadmap"
>Roadmap</a><span
slot="description"
>Planning · Edited 2h ago</span></ui-list-item>
<ui-list-item><a
href="#onboarding"
>Onboarding</a><span
slot="description"
>People · Edited yesterday</span></ui-list-item>
<ui-list-item><a
href="#glossary"
>Glossary</a><span
slot="description"
>Edited 3 days ago</span></ui-list-item>
</ui-list><script setup>
import { UiList, UiListItem } from "@threadlabs/looma";
</script>
<template>
<UiList
aria-label="Recently updated"
>
<UiListItem>
<a
href="#roadmap"
>Roadmap</a>
<template #description>
<span>Planning · Edited 2h ago</span>
</template>
</UiListItem>
<UiListItem>
<a
href="#onboarding"
>Onboarding</a>
<template #description>
<span>People · Edited yesterday</span>
</template>
</UiListItem>
<UiListItem>
<a
href="#glossary"
>Glossary</a>
<template #description>
<span>Edited 3 days ago</span>
</template>
</UiListItem>
</UiList>
</template>import { UiList, UiListItem } from "@threadlabs/looma";
export default function Example() {
return <UiList
aria-label="Recently updated"
>
<UiListItem
slots={{
description: <span>Planning · Edited 2h ago</span>,
}}
><a
href="#roadmap"
>Roadmap</a></UiListItem>
<UiListItem
slots={{
description: <span>People · Edited yesterday</span>,
}}
><a
href="#onboarding"
>Onboarding</a></UiListItem>
<UiListItem
slots={{
description: <span>Edited 3 days ago</span>,
}}
><a
href="#glossary"
>Glossary</a></UiListItem>
</UiList>;
}/** @jsxImportSource preact */
import { UiList, UiListItem } from "@threadlabs/looma";
export default function Example() {
return <UiList
aria-label="Recently updated"
>
<UiListItem
slots={{
description: <span>Planning · Edited 2h ago</span>,
}}
><a
href="#roadmap"
>Roadmap</a></UiListItem>
<UiListItem
slots={{
description: <span>People · Edited yesterday</span>,
}}
><a
href="#onboarding"
>Onboarding</a></UiListItem>
<UiListItem
slots={{
description: <span>Edited 3 days ago</span>,
}}
><a
href="#glossary"
>Glossary</a></UiListItem>
</UiList>;
}<script>
import { UiList, UiListItem } from "@threadlabs/looma";
</script>
{#snippet description()}
<span>Planning · Edited 2h ago</span>
{/snippet}
{#snippet listItemDescription()}
<span>People · Edited yesterday</span>
{/snippet}
{#snippet listItemDescription2()}
<span>Edited 3 days ago</span>
{/snippet}
<UiList
aria-label="Recently updated"
>
<UiListItem
slots={{ description }}
><a
href="#roadmap"
>Roadmap</a></UiListItem>
<UiListItem
slots={{ description: listItemDescription }}
><a
href="#onboarding"
>Onboarding</a></UiListItem>
<UiListItem
slots={{ description: listItemDescription2 }}
><a
href="#glossary"
>Glossary</a></UiListItem>
</UiList>import { Component } from "@angular/core";
import { UiList, UiListItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiList, UiListItem],
template: `
<ng-template
ui-list
[hnAttributes]="{ 'aria-label': 'Recently updated' }"
>
<li
ui-list-item
>
<a
href="#roadmap"
>Roadmap</a>
<ng-container ngProjectAs="[slot='description']">
<span>Planning · Edited 2h ago</span>
</ng-container>
</li>
<li
ui-list-item
>
<a
href="#onboarding"
>Onboarding</a>
<ng-container ngProjectAs="[slot='description']">
<span>People · Edited yesterday</span>
</ng-container>
</li>
<li
ui-list-item
>
<a
href="#glossary"
>Glossary</a>
<ng-container ngProjectAs="[slot='description']">
<span>Edited 3 days ago</span>
</ng-container>
</li>
</ng-template>
`,
})
export default class Example {}Native cards
A direct li with class="card" gets the same outlined surface in the grid layout.
- Roadmap
- Onboarding
- Glossary
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-list/ui-list.html"
>
<ui-list
layout="grid"
aria-label="Highlights"
>
<li
class="card"
>Roadmap</li>
<li
class="card"
>Onboarding</li>
<li
class="card"
>Glossary</li>
</ui-list><script setup>
import { UiList } from "@threadlabs/looma";
</script>
<template>
<UiList
layout="grid"
aria-label="Highlights"
>
<li
class="card"
>Roadmap</li>
<li
class="card"
>Onboarding</li>
<li
class="card"
>Glossary</li>
</UiList>
</template>import { UiList } from "@threadlabs/looma";
export default function Example() {
return <UiList
layout="grid"
aria-label="Highlights"
>
<li
className="card"
>Roadmap</li>
<li
className="card"
>Onboarding</li>
<li
className="card"
>Glossary</li>
</UiList>;
}/** @jsxImportSource preact */
import { UiList } from "@threadlabs/looma";
export default function Example() {
return <UiList
layout="grid"
aria-label="Highlights"
>
<li
className="card"
>Roadmap</li>
<li
className="card"
>Onboarding</li>
<li
className="card"
>Glossary</li>
</UiList>;
}<script>
import { UiList } from "@threadlabs/looma";
</script>
<UiList
layout="grid"
aria-label="Highlights"
>
<li
class="card"
>Roadmap</li>
<li
class="card"
>Onboarding</li>
<li
class="card"
>Glossary</li>
</UiList>import { Component } from "@angular/core";
import { UiList } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiList],
template: `
<ng-template
ui-list
[layout]="'grid'"
[hnAttributes]="{ 'aria-label': 'Highlights' }"
>
<li
class="card"
>Roadmap</li>
<li
class="card"
>Onboarding</li>
<li
class="card"
>Glossary</li>
</ng-template>
`,
})
export default class Example {}Bullets
markers="disc" shows native bullets for a list of ordinary li elements.
- Draft the outline
- Review the content
- Publish the page
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-list/ui-list.html"
>
<ui-list
markers="disc"
>
<li>Draft the outline</li>
<li>Review the content</li>
<li>Publish the page</li>
</ui-list><script setup>
import { UiList } from "@threadlabs/looma";
</script>
<template>
<UiList
markers="disc"
>
<li>Draft the outline</li>
<li>Review the content</li>
<li>Publish the page</li>
</UiList>
</template>import { UiList } from "@threadlabs/looma";
export default function Example() {
return <UiList
markers="disc"
>
<li>Draft the outline</li>
<li>Review the content</li>
<li>Publish the page</li>
</UiList>;
}/** @jsxImportSource preact */
import { UiList } from "@threadlabs/looma";
export default function Example() {
return <UiList
markers="disc"
>
<li>Draft the outline</li>
<li>Review the content</li>
<li>Publish the page</li>
</UiList>;
}<script>
import { UiList } from "@threadlabs/looma";
</script>
<UiList
markers="disc"
>
<li>Draft the outline</li>
<li>Review the content</li>
<li>Publish the page</li>
</UiList>import { Component } from "@angular/core";
import { UiList } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiList],
template: `
<ng-template
ui-list
[markers]="'disc'"
>
<li>Draft the outline</li>
<li>Review the content</li>
<li>Publish the page</li>
</ng-template>
`,
})
export default class Example {}Numbered steps
as="ol" uses a native ordered list and shows its numbers.
- Draft the outline
- Review the content
- Publish the page
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-list/ui-list.html"
>
<ui-list
as="ol"
>
<li>Draft the outline</li>
<li>Review the content</li>
<li>Publish the page</li>
</ui-list><script setup>
import { UiList } from "@threadlabs/looma";
</script>
<template>
<UiList
as="ol"
>
<li>Draft the outline</li>
<li>Review the content</li>
<li>Publish the page</li>
</UiList>
</template>import { UiList } from "@threadlabs/looma";
export default function Example() {
return <UiList
as="ol"
>
<li>Draft the outline</li>
<li>Review the content</li>
<li>Publish the page</li>
</UiList>;
}/** @jsxImportSource preact */
import { UiList } from "@threadlabs/looma";
export default function Example() {
return <UiList
as="ol"
>
<li>Draft the outline</li>
<li>Review the content</li>
<li>Publish the page</li>
</UiList>;
}<script>
import { UiList } from "@threadlabs/looma";
</script>
<UiList
as="ol"
>
<li>Draft the outline</li>
<li>Review the content</li>
<li>Publish the page</li>
</UiList>import { Component } from "@angular/core";
import { UiList } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiList],
template: `
<ng-template
ui-list
[as]="'ol'"
>
<li>Draft the outline</li>
<li>Review the content</li>
<li>Publish the page</li>
</ng-template>
`,
})
export default class Example {}Link
A link in the title is the whole item. Its icon and padding follow the link, and the item takes the link's hover surface and focus ring.
- Roadmap Planning · Edited 2h ago
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-list/ui-list.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-list-item/ui-list-item.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-icon/ui-icon.html"
>
<ui-list
aria-label="Pages"
>
<ui-list-item>
<ui-icon
slot="leading"
name="file-text"
></ui-icon>
<a
href="#roadmap"
>Roadmap</a>
<span
slot="description"
>Planning · Edited 2h ago</span>
</ui-list-item>
</ui-list><script setup>
import { UiIcon, UiList, UiListItem } from "@threadlabs/looma";
</script>
<template>
<UiList
aria-label="Pages"
>
<UiListItem>
<template #leading>
<UiIcon
name="file-text"
/>
</template>
<a
href="#roadmap"
>Roadmap</a>
<template #description>
<span>Planning · Edited 2h ago</span>
</template>
</UiListItem>
</UiList>
</template>import { UiIcon, UiList, UiListItem } from "@threadlabs/looma";
export default function Example() {
return <UiList
aria-label="Pages"
>
<UiListItem
slots={{
leading: <UiIcon
name="file-text"
/>,
description: <span>Planning · Edited 2h ago</span>,
}}
>{" "}{" "}<a
href="#roadmap"
>Roadmap</a>{" "}{" "}</UiListItem>
</UiList>;
}/** @jsxImportSource preact */
import { UiIcon, UiList, UiListItem } from "@threadlabs/looma";
export default function Example() {
return <UiList
aria-label="Pages"
>
<UiListItem
slots={{
leading: <UiIcon
name="file-text"
/>,
description: <span>Planning · Edited 2h ago</span>,
}}
>{" "}{" "}<a
href="#roadmap"
>Roadmap</a>{" "}{" "}</UiListItem>
</UiList>;
}<script>
import { UiIcon, UiList, UiListItem } from "@threadlabs/looma";
</script>
{#snippet leading()}
<UiIcon
name="file-text"
/>
{/snippet}
{#snippet description()}
<span>Planning · Edited 2h ago</span>
{/snippet}
<UiList
aria-label="Pages"
>
<UiListItem
slots={{ leading, description }}
> <a
href="#roadmap"
>Roadmap</a> </UiListItem>
</UiList>import { Component } from "@angular/core";
import { UiIcon, UiList, UiListItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiList, UiListItem, UiIcon],
template: `
<ng-template
ui-list
[hnAttributes]="{ 'aria-label': 'Pages' }"
>
<li
ui-list-item
>
<ng-container ngProjectAs="[slot='leading']">
<span
ui-icon
name="file-text"
></span>
</ng-container>
<a
href="#roadmap"
>Roadmap</a>
<ng-container ngProjectAs="[slot='description']">
<span>Planning · Edited 2h ago</span>
</ng-container>
</li>
</ng-template>
`,
})
export default class Example {}Trailing control
A trailing control stays its own: pressing Restore does not follow a title link. An item with no link has no hover surface.
- Old roadmap Deleted 3 days ago
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-list/ui-list.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-list-item/ui-list-item.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-icon/ui-icon.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<ui-list
aria-label="Deleted pages"
>
<ui-list-item>
<ui-icon
slot="leading"
name="file-text"
></ui-icon>
Old roadmap
<span
slot="description"
>Deleted 3 days ago</span>
<ui-button
slot="trailing"
size="sm"
variant="outline"
>Restore</ui-button>
</ui-list-item>
</ui-list><script setup>
import { UiButton, UiIcon, UiList, UiListItem } from "@threadlabs/looma";
</script>
<template>
<UiList
aria-label="Deleted pages"
>
<UiListItem> <template #leading>
<UiIcon
name="file-text"
/>
</template> Old roadmap <template #description>
<span>Deleted 3 days ago</span>
</template> <template #trailing>
<UiButton
size="sm"
variant="outline"
>Restore</UiButton>
</template> </UiListItem>
</UiList>
</template>import { UiButton, UiIcon, UiList, UiListItem } from "@threadlabs/looma";
export default function Example() {
return <UiList
aria-label="Deleted pages"
>
<UiListItem
slots={{
leading: <UiIcon
name="file-text"
/>,
description: <span>Deleted 3 days ago</span>,
trailing: <UiButton
size="sm"
variant="outline"
>Restore</UiButton>,
}}
>{" "}{" Old roadmap "}{" "}{" "}</UiListItem>
</UiList>;
}/** @jsxImportSource preact */
import { UiButton, UiIcon, UiList, UiListItem } from "@threadlabs/looma";
export default function Example() {
return <UiList
aria-label="Deleted pages"
>
<UiListItem
slots={{
leading: <UiIcon
name="file-text"
/>,
description: <span>Deleted 3 days ago</span>,
trailing: <UiButton
size="sm"
variant="outline"
>Restore</UiButton>,
}}
>{" "}{" Old roadmap "}{" "}{" "}</UiListItem>
</UiList>;
}<script>
import { UiButton, UiIcon, UiList, UiListItem } from "@threadlabs/looma";
</script>
{#snippet leading()}
<UiIcon
name="file-text"
/>
{/snippet}
{#snippet description()}
<span>Deleted 3 days ago</span>
{/snippet}
{#snippet trailing()}
<UiButton
size="sm"
variant="outline"
>Restore</UiButton>
{/snippet}
<UiList
aria-label="Deleted pages"
>
<UiListItem
slots={{ leading, description, trailing }}
> Old roadmap </UiListItem>
</UiList>import { Component } from "@angular/core";
import { UiButton, UiIcon, UiList, UiListItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiList, UiListItem, UiIcon, UiButton],
template: `
<ng-template
ui-list
[hnAttributes]="{ 'aria-label': 'Deleted pages' }"
>
<li
ui-list-item
> <ng-container ngProjectAs="[slot='leading']">
<span
ui-icon
name="file-text"
></span>
</ng-container> Old roadmap <ng-container ngProjectAs="[slot='description']">
<span>Deleted 3 days ago</span>
</ng-container> <ng-container ngProjectAs="[slot='trailing']">
<ng-template
ui-button
[size]="'sm'"
[variant]="'outline'"
>Restore</ng-template>
</ng-container> </li>
</ng-template>
`,
})
export default class Example {}Card and current
variant="card" draws the same parts on a bordered surface, for a grid. current marks the item you are in.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-list/ui-list.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-list-item/ui-list-item.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-badge/ui-badge.html"
>
<ui-list
layout="grid"
aria-label="Sites"
>
<ui-list-item
variant="card"
current
>
<a
href="#acme"
>Acme</a>
<span
slot="description"
>acme.example.com</span>
<ui-badge
slot="trailing"
>Current</ui-badge>
</ui-list-item>
<ui-list-item
variant="card"
><a
href="#globex"
>Globex</a><span
slot="description"
>globex.example.com</span></ui-list-item>
</ui-list><script setup>
import { UiBadge, UiList, UiListItem } from "@threadlabs/looma";
</script>
<template>
<UiList
layout="grid"
aria-label="Sites"
>
<UiListItem
variant="card"
:current="true"
>
<a
href="#acme"
>Acme</a>
<template #description>
<span>acme.example.com</span>
</template>
<template #trailing>
<UiBadge>Current</UiBadge>
</template>
</UiListItem>
<UiListItem
variant="card"
>
<a
href="#globex"
>Globex</a>
<template #description>
<span>globex.example.com</span>
</template>
</UiListItem>
</UiList>
</template>import { UiBadge, UiList, UiListItem } from "@threadlabs/looma";
export default function Example() {
return <UiList
layout="grid"
aria-label="Sites"
>
<UiListItem
variant="card"
current
slots={{
description: <span>acme.example.com</span>,
trailing: <UiBadge>Current</UiBadge>,
}}
><a
href="#acme"
>Acme</a></UiListItem>
<UiListItem
variant="card"
slots={{
description: <span>globex.example.com</span>,
}}
><a
href="#globex"
>Globex</a></UiListItem>
</UiList>;
}/** @jsxImportSource preact */
import { UiBadge, UiList, UiListItem } from "@threadlabs/looma";
export default function Example() {
return <UiList
layout="grid"
aria-label="Sites"
>
<UiListItem
variant="card"
current
slots={{
description: <span>acme.example.com</span>,
trailing: <UiBadge>Current</UiBadge>,
}}
><a
href="#acme"
>Acme</a></UiListItem>
<UiListItem
variant="card"
slots={{
description: <span>globex.example.com</span>,
}}
><a
href="#globex"
>Globex</a></UiListItem>
</UiList>;
}<script>
import { UiBadge, UiList, UiListItem } from "@threadlabs/looma";
</script>
{#snippet description()}
<span>acme.example.com</span>
{/snippet}
{#snippet trailing()}
<UiBadge>Current</UiBadge>
{/snippet}
{#snippet listItemDescription()}
<span>globex.example.com</span>
{/snippet}
<UiList
layout="grid"
aria-label="Sites"
>
<UiListItem
variant="card"
current
slots={{ description, trailing }}
><a
href="#acme"
>Acme</a></UiListItem>
<UiListItem
variant="card"
slots={{ description: listItemDescription }}
><a
href="#globex"
>Globex</a></UiListItem>
</UiList>import { Component } from "@angular/core";
import { UiBadge, UiList, UiListItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiList, UiListItem, UiBadge],
template: `
<ng-template
ui-list
[layout]="'grid'"
[hnAttributes]="{ 'aria-label': 'Sites' }"
>
<li
ui-list-item
[variant]="'card'"
[current]="true"
>
<a
href="#acme"
>Acme</a>
<ng-container ngProjectAs="[slot='description']">
<span>acme.example.com</span>
</ng-container>
<ng-container ngProjectAs="[slot='trailing']">
<span
ui-badge
>Current</span>
</ng-container>
</li>
<li
ui-list-item
[variant]="'card'"
>
<a
href="#globex"
>Globex</a>
<ng-container ngProjectAs="[slot='description']">
<span>globex.example.com</span>
</ng-container>
</li>
</ng-template>
`,
})
export default class Example {}New items
Several items can have a quiet highlight without becoming selected or current. Keep the reason in their text.
- Alex sent a new message New today
- Sam shared a document New today
- Earlier conversation Yesterday
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-list/ui-list.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-list-item/ui-list-item.html"
>
<ui-list
aria-label="Messages"
>
<ui-list-item
highlighted
>
Alex sent a new message
<span
slot="description"
>New today</span>
</ui-list-item>
<ui-list-item
highlighted
>
Sam shared a document
<span
slot="description"
>New today</span>
</ui-list-item>
<ui-list-item>
Earlier conversation
<span
slot="description"
>Yesterday</span>
</ui-list-item>
</ui-list><script setup>
import { UiList, UiListItem } from "@threadlabs/looma";
</script>
<template>
<UiList
aria-label="Messages"
>
<UiListItem
:highlighted="true"
>Alex sent a new message<template #description>
<span>New today</span>
</template></UiListItem>
<UiListItem
:highlighted="true"
>Sam shared a document<template #description>
<span>New today</span>
</template></UiListItem>
<UiListItem>Earlier conversation<template #description>
<span>Yesterday</span>
</template></UiListItem>
</UiList>
</template>import { UiList, UiListItem } from "@threadlabs/looma";
export default function Example() {
return <UiList
aria-label="Messages"
>
<UiListItem
highlighted
slots={{
description: <span>New today</span>,
}}
>Alex sent a new message</UiListItem>
<UiListItem
highlighted
slots={{
description: <span>New today</span>,
}}
>Sam shared a document</UiListItem>
<UiListItem
slots={{
description: <span>Yesterday</span>,
}}
>Earlier conversation</UiListItem>
</UiList>;
}/** @jsxImportSource preact */
import { UiList, UiListItem } from "@threadlabs/looma";
export default function Example() {
return <UiList
aria-label="Messages"
>
<UiListItem
highlighted
slots={{
description: <span>New today</span>,
}}
>Alex sent a new message</UiListItem>
<UiListItem
highlighted
slots={{
description: <span>New today</span>,
}}
>Sam shared a document</UiListItem>
<UiListItem
slots={{
description: <span>Yesterday</span>,
}}
>Earlier conversation</UiListItem>
</UiList>;
}<script>
import { UiList, UiListItem } from "@threadlabs/looma";
</script>
{#snippet description()}
<span>New today</span>
{/snippet}
{#snippet listItemDescription()}
<span>New today</span>
{/snippet}
{#snippet listItemDescription2()}
<span>Yesterday</span>
{/snippet}
<UiList
aria-label="Messages"
>
<UiListItem
highlighted
slots={{ description }}
>Alex sent a new message</UiListItem>
<UiListItem
highlighted
slots={{ description: listItemDescription }}
>Sam shared a document</UiListItem>
<UiListItem
slots={{ description: listItemDescription2 }}
>Earlier conversation</UiListItem>
</UiList>import { Component } from "@angular/core";
import { UiList, UiListItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiList, UiListItem],
template: `
<ng-template
ui-list
[hnAttributes]="{ 'aria-label': 'Messages' }"
>
<li
ui-list-item
[highlighted]="true"
>Alex sent a new message<ng-container ngProjectAs="[slot='description']">
<span>New today</span>
</ng-container></li>
<li
ui-list-item
[highlighted]="true"
>Sam shared a document<ng-container ngProjectAs="[slot='description']">
<span>New today</span>
</ng-container></li>
<li
ui-list-item
>Earlier conversation<ng-container ngProjectAs="[slot='description']">
<span>Yesterday</span>
</ng-container></li>
</ng-template>
`,
})
export default class Example {}Wrapped descriptions
Complete explanatory text stays readable at narrow widths.
- A person asked you to review the updated account recovery guide The request includes the recovery steps, device checks, and contact details. Read the full description before choosing what to do.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-list/ui-list.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-list-item/ui-list-item.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<ui-list>
<ui-list-item
wrap
highlighted
>
A person asked you to review the updated account recovery guide
<span
slot="description"
>The request includes the recovery steps, device checks, and contact details. Read the full description before choosing what to do.</span>
<ui-button
slot="trailing"
variant="outline"
size="sm"
>Review</ui-button>
</ui-list-item>
</ui-list><script setup>
import { UiButton, UiList, UiListItem } from "@threadlabs/looma";
</script>
<template>
<UiList>
<UiListItem
:wrap="true"
:highlighted="true"
> A person asked you to review the updated account recovery guide <template #description>
<span>The request includes the recovery steps, device checks, and contact details. Read the full description before choosing what to do.</span>
</template> <template #trailing>
<UiButton
variant="outline"
size="sm"
>Review</UiButton>
</template> </UiListItem>
</UiList>
</template>import { UiButton, UiList, UiListItem } from "@threadlabs/looma";
export default function Example() {
return <UiList>
<UiListItem
wrap
highlighted
slots={{
description: <span>The request includes the recovery steps, device checks, and contact details. Read the full description before choosing what to do.</span>,
trailing: <UiButton
variant="outline"
size="sm"
>Review</UiButton>,
}}
>{" A person asked you to review the updated account recovery guide "}{" "}{" "}</UiListItem>
</UiList>;
}/** @jsxImportSource preact */
import { UiButton, UiList, UiListItem } from "@threadlabs/looma";
export default function Example() {
return <UiList>
<UiListItem
wrap
highlighted
slots={{
description: <span>The request includes the recovery steps, device checks, and contact details. Read the full description before choosing what to do.</span>,
trailing: <UiButton
variant="outline"
size="sm"
>Review</UiButton>,
}}
>{" A person asked you to review the updated account recovery guide "}{" "}{" "}</UiListItem>
</UiList>;
}<script>
import { UiButton, UiList, UiListItem } from "@threadlabs/looma";
</script>
{#snippet description()}
<span>The request includes the recovery steps, device checks, and contact details. Read the full description before choosing what to do.</span>
{/snippet}
{#snippet trailing()}
<UiButton
variant="outline"
size="sm"
>Review</UiButton>
{/snippet}
<UiList>
<UiListItem
wrap
highlighted
slots={{ description, trailing }}
> A person asked you to review the updated account recovery guide </UiListItem>
</UiList>import { Component } from "@angular/core";
import { UiButton, UiList, UiListItem } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiList, UiListItem, UiButton],
template: `
<ng-template
ui-list
>
<li
ui-list-item
[wrap]="true"
[highlighted]="true"
> A person asked you to review the updated account recovery guide <ng-container ngProjectAs="[slot='description']">
<span>The request includes the recovery steps, device checks, and contact details. Read the full description before choosing what to do.</span>
</ng-container> <ng-container ngProjectAs="[slot='trailing']">
<ng-template
ui-button
[variant]="'outline'"
[size]="'sm'"
>Review</ng-template>
</ng-container> </li>
</ng-template>
`,
})
export default class Example {}API
Package: @threadlabs/looma · Native root: ul
Attributes
| Name | Description | Property | Type | Default | Allowed values |
|---|---|---|---|---|---|
as | The native list element. Use ol when item order matters; its native numbers are shown. | as | keyword | ul | ul, ol |
density | How much room each item takes. comfortable is a full row, easy to point at, for lists of things to open; compact sets items close together with no row padding, for a short list of facts inside other content, such as the features a plan includes. | density | keyword | comfortable | comfortable, compact |
layout | How the items are laid out. Rows stack the items one per line, for a list read top to bottom. Grid sets them in as many columns as fit, each at least --ui-list-min-item-width (14rem) wide, for card items such as pinned or featured entries. | layout | keyword | rows | rows, grid |
markers | Show disc bullets for a ul of native li elements. An ol always shows native numbers. | markers | keyword | none | none, disc |
Framework props
| Name | Type | Default | Allowed values |
|---|---|---|---|
as | "ul" | "ol" | ul | ul, ol |
density | "comfortable" | "compact" | comfortable | comfortable, compact |
layout | "rows" | "grid" | rows | rows, grid |
markers | "none" | "disc" | none | none, disc |
Events
No events.
Slots
| Name | Purpose |
|---|---|
default | Default child content. |
Design tokens
Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.
Component tokens
| Token | Declared values | Fallback |
|---|---|---|
--ui-list-gap | — | var(--ui-space-1) | var(--ui-space-2) | var(--ui-space-3) |
--ui-list-min-item-width | — | 14rem |
Shared tokens
| Token | Fallback |
|---|---|
--ui-border-strong | — |
--ui-border-width | — |
--ui-font-normal | — |
--ui-radius-lg | — |
--ui-radius-md | — |
--ui-space-1 | — |
--ui-space-2 | — |
--ui-space-3 | — |
--ui-space-4 | — |
--ui-space-5 | — |
--ui-surface-raised | — |
--ui-text-primary | — |
List Item API
One entry in a ui-list: leading, title, description, and trailing, with one-line title and description.
Package: @threadlabs/looma · Native root: li
Attributes
| Name | Description | Property | Type | Default | Allowed values |
|---|---|---|---|---|---|
align | Where the leading and trailing regions sit vertically. center aligns them with the whole row. title aligns them with the first title line, keeping an identifying icon beside its name when the title or description wraps. Taller controls retain their natural height. | align | keyword | center | center, title |
current | Marks the item as the current one, such as the site you are in: it takes the selected surface and aria-current="true", which assistive technology announces. Set it on one item at a time. | current | boolean | false | — |
highlighted | Gives an item a quiet accent wash without changing its text or announcing selection. Several items may be highlighted, such as new messages. Describe the reason in the item's text; use current only for the current destination. | highlighted | boolean | false | — |
leading-interactive | Makes the leading region an independent control above the row link. Use it for a checkbox or button; keep noninteractive row icons in the title link when combining them with a leading control. | leadingInteractive | boolean | false | — |
variant | How the item is drawn. A row is flat and takes a hover surface when it is a link, for items read down a list. A card sits on a raised, bordered surface, for items set in a grid (a List with layout="grid"). Both have the same parts: leading, title, description, and trailing. | variant | keyword | row | row, card |
wrap | Lets the title and description wrap so explanatory text stays readable on narrow screens. Omit it for the default equal-height, one-line rows. | wrap | boolean | false | — |
Framework props
| Name | Type | Default | Allowed values |
|---|---|---|---|
align | "center" | "title" | center | center, title |
current | boolean | false | — |
highlighted | boolean | false | — |
leadingInteractive | boolean | false | — |
variant | "row" | "card" | row | row, card |
wrap | boolean | false | — |
Events
No events.
Slots
| Name | Purpose |
|---|---|
leading | Named content for the leading region. |
default | Default child content. |
description | Named content for the description region. |
trailing | Named content for the trailing region. |
Design tokens
Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.
Component tokens
| Token | Declared values | Fallback |
|---|---|---|
--ui-list-item-gap | — | var(--ui-space-3) |
--ui-list-item-icon-size | — | var(--ui-font-size-lg) |
--ui-list-item-padding-block | — | var(--_ui-list-item-padding-block, var(--ui-space-2)) | var(--ui-space-3) | var(--ui-space-2) |
--ui-list-item-padding-inline | — | var(--_ui-list-item-padding-inline, var(--ui-space-3)) | var(--ui-space-4) | var(--ui-space-3) |
Shared tokens
| Token | Fallback |
|---|---|
--ui-border-strong | — |
--ui-border-width | — |
--ui-control-size | 2.5rem |
--ui-focus-ring | — |
--ui-focus-width | — |
--ui-font-medium | — |
--ui-font-size-lg | — |
--ui-font-size-md | — |
--ui-font-size-sm | — |
--ui-line-height-tight | — |
--ui-motion-ease | — |
--ui-motion-fast | — |
--ui-radius-lg | — |
--ui-selection-border | — |
--ui-selection-radius | — |
--ui-selection-surface | — |
--ui-selection-text | — |
--ui-space-0-5 | 2px |
--ui-space-2 | — |
--ui-space-3 | — |
--ui-space-4 | — |
--ui-surface-raised | — |
--ui-text-muted | — |
--ui-text-primary | — |