Grid
Responsive auto-fit grid primitive, with explicit fixed columns for aligned matrices and optional neutral or accent shading for chosen columns using the theme palette.
Default
With no properties supplied, Grid uses its default gap and minimum column size.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-grid/ui-grid.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-card/ui-card.html"
>
<ui-grid>
<ui-card
padding="sm"
>Frodo</ui-card>
<ui-card
padding="sm"
>Sam</ui-card>
<ui-card
padding="sm"
>Sméagol</ui-card>
</ui-grid><script setup>
import { UiCard, UiGrid } from "@threadlabs/looma";
</script>
<template>
<UiGrid>
<UiCard
padding="sm"
>Frodo</UiCard>
<UiCard
padding="sm"
>Sam</UiCard>
<UiCard
padding="sm"
>Sméagol</UiCard>
</UiGrid>
</template>import { UiCard, UiGrid } from "@threadlabs/looma";
export default function Example() {
return <UiGrid>
<UiCard
padding="sm"
>Frodo</UiCard>
<UiCard
padding="sm"
>Sam</UiCard>
<UiCard
padding="sm"
>Sméagol</UiCard>
</UiGrid>;
}/** @jsxImportSource preact */
import { UiCard, UiGrid } from "@threadlabs/looma";
export default function Example() {
return <UiGrid>
<UiCard
padding="sm"
>Frodo</UiCard>
<UiCard
padding="sm"
>Sam</UiCard>
<UiCard
padding="sm"
>Sméagol</UiCard>
</UiGrid>;
}<script>
import { UiCard, UiGrid } from "@threadlabs/looma";
</script>
<UiGrid>
<UiCard
padding="sm"
>Frodo</UiCard>
<UiCard
padding="sm"
>Sam</UiCard>
<UiCard
padding="sm"
>Sméagol</UiCard>
</UiGrid>import { Component } from "@angular/core";
import { UiCard, UiGrid } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiGrid, UiCard],
template: `
<div
ui-grid
>
<ng-template
ui-card
[padding]="'sm'"
>Frodo</ng-template>
<ng-template
ui-card
[padding]="'sm'"
>Sam</ng-template>
<ng-template
ui-card
[padding]="'sm'"
>Sméagol</ng-template>
</div>
`,
})
export default class Example {}Gap and minimum column width
Gap controls spacing while min controls the intrinsic wrap point for columns.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-grid/ui-grid.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-card/ui-card.html"
>
<ui-grid
gap="xs"
min="sm"
>
<ui-card
padding="sm"
>Frodo</ui-card>
<ui-card
padding="sm"
>Sam</ui-card>
<ui-card
padding="sm"
>Sméagol</ui-card>
<ui-card
padding="sm"
>Merry</ui-card>
</ui-grid><script setup>
import { UiCard, UiGrid } from "@threadlabs/looma";
</script>
<template>
<UiGrid
gap="xs"
min="sm"
>
<UiCard
padding="sm"
>Frodo</UiCard>
<UiCard
padding="sm"
>Sam</UiCard>
<UiCard
padding="sm"
>Sméagol</UiCard>
<UiCard
padding="sm"
>Merry</UiCard>
</UiGrid>
</template>import { UiCard, UiGrid } from "@threadlabs/looma";
export default function Example() {
return <UiGrid
gap="xs"
min="sm"
>
<UiCard
padding="sm"
>Frodo</UiCard>
<UiCard
padding="sm"
>Sam</UiCard>
<UiCard
padding="sm"
>Sméagol</UiCard>
<UiCard
padding="sm"
>Merry</UiCard>
</UiGrid>;
}/** @jsxImportSource preact */
import { UiCard, UiGrid } from "@threadlabs/looma";
export default function Example() {
return <UiGrid
gap="xs"
min="sm"
>
<UiCard
padding="sm"
>Frodo</UiCard>
<UiCard
padding="sm"
>Sam</UiCard>
<UiCard
padding="sm"
>Sméagol</UiCard>
<UiCard
padding="sm"
>Merry</UiCard>
</UiGrid>;
}<script>
import { UiCard, UiGrid } from "@threadlabs/looma";
</script>
<UiGrid
gap="xs"
min="sm"
>
<UiCard
padding="sm"
>Frodo</UiCard>
<UiCard
padding="sm"
>Sam</UiCard>
<UiCard
padding="sm"
>Sméagol</UiCard>
<UiCard
padding="sm"
>Merry</UiCard>
</UiGrid>import { Component } from "@angular/core";
import { UiCard, UiGrid } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiGrid, UiCard],
template: `
<div
ui-grid
[gap]="'xs'"
[min]="'sm'"
>
<ng-template
ui-card
[padding]="'sm'"
>Frodo</ng-template>
<ng-template
ui-card
[padding]="'sm'"
>Sam</ng-template>
<ng-template
ui-card
[padding]="'sm'"
>Sméagol</ng-template>
<ng-template
ui-card
[padding]="'sm'"
>Merry</ng-template>
</div>
`,
})
export default class Example {}Cap the column count
At most three cards sit in a row; the grid still drops to two or one as the container narrows.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-grid/ui-grid.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-card/ui-card.html"
>
<ui-grid
columns="3"
min="sm"
>
<ui-card>One</ui-card>
<ui-card>Two</ui-card>
<ui-card>Three</ui-card>
<ui-card>Four</ui-card>
<ui-card>Five</ui-card>
</ui-grid><script setup>
import { UiCard, UiGrid } from "@threadlabs/looma";
</script>
<template>
<UiGrid
:columns="3"
min="sm"
>
<UiCard>One</UiCard>
<UiCard>Two</UiCard>
<UiCard>Three</UiCard>
<UiCard>Four</UiCard>
<UiCard>Five</UiCard>
</UiGrid>
</template>import { UiCard, UiGrid } from "@threadlabs/looma";
export default function Example() {
return <UiGrid
columns={3}
min="sm"
>
<UiCard>One</UiCard>
<UiCard>Two</UiCard>
<UiCard>Three</UiCard>
<UiCard>Four</UiCard>
<UiCard>Five</UiCard>
</UiGrid>;
}/** @jsxImportSource preact */
import { UiCard, UiGrid } from "@threadlabs/looma";
export default function Example() {
return <UiGrid
columns={3}
min="sm"
>
<UiCard>One</UiCard>
<UiCard>Two</UiCard>
<UiCard>Three</UiCard>
<UiCard>Four</UiCard>
<UiCard>Five</UiCard>
</UiGrid>;
}<script>
import { UiCard, UiGrid } from "@threadlabs/looma";
</script>
<UiGrid
columns={3}
min="sm"
>
<UiCard>One</UiCard>
<UiCard>Two</UiCard>
<UiCard>Three</UiCard>
<UiCard>Four</UiCard>
<UiCard>Five</UiCard>
</UiGrid>import { Component } from "@angular/core";
import { UiCard, UiGrid } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiGrid, UiCard],
template: `
<div
ui-grid
[columns]="3"
[min]="'sm'"
>
<ng-template
ui-card
>One</ng-template>
<ng-template
ui-card
>Two</ng-template>
<ng-template
ui-card
>Three</ng-template>
<ng-template
ui-card
>Four</ng-template>
<ng-template
ui-card
>Five</ng-template>
</div>
`,
})
export default class Example {}Fixed columns
A matrix keeps its seven weekday columns at narrow widths. Ordinary grids retain their responsive column behavior.
Mon
Tue
Wed
Thu
Fri
Sat
Sun
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-grid/ui-grid.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-text/ui-text.html"
>
<ui-grid
columns="7"
fixed
gap="none"
>
<ui-text
align="center"
>Mon</ui-text>
<ui-text
align="center"
>Tue</ui-text>
<ui-text
align="center"
>Wed</ui-text>
<ui-text
align="center"
>Thu</ui-text>
<ui-text
align="center"
>Fri</ui-text>
<ui-text
align="center"
>Sat</ui-text>
<ui-text
align="center"
>Sun</ui-text>
</ui-grid><script setup>
import { UiGrid, UiText } from "@threadlabs/looma";
</script>
<template>
<UiGrid
:columns="7"
:fixed="true"
gap="none"
>
<UiText
align="center"
>Mon</UiText>
<UiText
align="center"
>Tue</UiText>
<UiText
align="center"
>Wed</UiText>
<UiText
align="center"
>Thu</UiText>
<UiText
align="center"
>Fri</UiText>
<UiText
align="center"
>Sat</UiText>
<UiText
align="center"
>Sun</UiText>
</UiGrid>
</template>import { UiGrid, UiText } from "@threadlabs/looma";
export default function Example() {
return <UiGrid
columns={7}
fixed
gap="none"
>
<UiText
align="center"
>Mon</UiText>
<UiText
align="center"
>Tue</UiText>
<UiText
align="center"
>Wed</UiText>
<UiText
align="center"
>Thu</UiText>
<UiText
align="center"
>Fri</UiText>
<UiText
align="center"
>Sat</UiText>
<UiText
align="center"
>Sun</UiText>
</UiGrid>;
}/** @jsxImportSource preact */
import { UiGrid, UiText } from "@threadlabs/looma";
export default function Example() {
return <UiGrid
columns={7}
fixed
gap="none"
>
<UiText
align="center"
>Mon</UiText>
<UiText
align="center"
>Tue</UiText>
<UiText
align="center"
>Wed</UiText>
<UiText
align="center"
>Thu</UiText>
<UiText
align="center"
>Fri</UiText>
<UiText
align="center"
>Sat</UiText>
<UiText
align="center"
>Sun</UiText>
</UiGrid>;
}<script>
import { UiGrid, UiText } from "@threadlabs/looma";
</script>
<UiGrid
columns={7}
fixed
gap="none"
>
<UiText
align="center"
>Mon</UiText>
<UiText
align="center"
>Tue</UiText>
<UiText
align="center"
>Wed</UiText>
<UiText
align="center"
>Thu</UiText>
<UiText
align="center"
>Fri</UiText>
<UiText
align="center"
>Sat</UiText>
<UiText
align="center"
>Sun</UiText>
</UiGrid>import { Component } from "@angular/core";
import { UiGrid, UiText } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiGrid, UiText],
template: `
<div
ui-grid
[columns]="7"
[fixed]="true"
[gap]="'none'"
>
<ng-template
ui-text
[align]="'center'"
>Mon</ng-template>
<ng-template
ui-text
[align]="'center'"
>Tue</ng-template>
<ng-template
ui-text
[align]="'center'"
>Wed</ng-template>
<ng-template
ui-text
[align]="'center'"
>Thu</ng-template>
<ng-template
ui-text
[align]="'center'"
>Fri</ng-template>
<ng-template
ui-text
[align]="'center'"
>Sat</ng-template>
<ng-template
ui-text
[align]="'center'"
>Sun</ng-template>
</div>
`,
})
export default class Example {}Shaded columns
Neutral washes identify selected matrix columns without changing their content, layout or writing direction. shade-tone="accent" gives header rows an accent tint. Column indices are one-based and use explicit fixed columns.
Sun
Mon
Tue
Wed
Thu
Fri
Sat
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-grid/ui-grid.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-text/ui-text.html"
>
<ui-grid
fixed
columns="7"
gap="none"
shaded-columns="[1,7]"
>
<ui-text
align="center"
>Sun</ui-text>
<ui-text
align="center"
>Mon</ui-text>
<ui-text
align="center"
>Tue</ui-text>
<ui-text
align="center"
>Wed</ui-text>
<ui-text
align="center"
>Thu</ui-text>
<ui-text
align="center"
>Fri</ui-text>
<ui-text
align="center"
>Sat</ui-text>
</ui-grid><script setup>
import { UiGrid, UiText } from "@threadlabs/looma";
</script>
<template>
<UiGrid
:fixed="true"
:columns="7"
gap="none"
:shaded-columns="[1, 7]"
>
<UiText
align="center"
>Sun</UiText>
<UiText
align="center"
>Mon</UiText>
<UiText
align="center"
>Tue</UiText>
<UiText
align="center"
>Wed</UiText>
<UiText
align="center"
>Thu</UiText>
<UiText
align="center"
>Fri</UiText>
<UiText
align="center"
>Sat</UiText>
</UiGrid>
</template>import { UiGrid, UiText } from "@threadlabs/looma";
export default function Example() {
return <UiGrid
fixed
columns={7}
gap="none"
shadedColumns={[1,7]}
>
<UiText
align="center"
>Sun</UiText>
<UiText
align="center"
>Mon</UiText>
<UiText
align="center"
>Tue</UiText>
<UiText
align="center"
>Wed</UiText>
<UiText
align="center"
>Thu</UiText>
<UiText
align="center"
>Fri</UiText>
<UiText
align="center"
>Sat</UiText>
</UiGrid>;
}/** @jsxImportSource preact */
import { UiGrid, UiText } from "@threadlabs/looma";
export default function Example() {
return <UiGrid
fixed
columns={7}
gap="none"
shadedColumns={[1,7]}
>
<UiText
align="center"
>Sun</UiText>
<UiText
align="center"
>Mon</UiText>
<UiText
align="center"
>Tue</UiText>
<UiText
align="center"
>Wed</UiText>
<UiText
align="center"
>Thu</UiText>
<UiText
align="center"
>Fri</UiText>
<UiText
align="center"
>Sat</UiText>
</UiGrid>;
}<script>
import { UiGrid, UiText } from "@threadlabs/looma";
</script>
<UiGrid
fixed
columns={7}
gap="none"
shadedColumns={[1,7]}
>
<UiText
align="center"
>Sun</UiText>
<UiText
align="center"
>Mon</UiText>
<UiText
align="center"
>Tue</UiText>
<UiText
align="center"
>Wed</UiText>
<UiText
align="center"
>Thu</UiText>
<UiText
align="center"
>Fri</UiText>
<UiText
align="center"
>Sat</UiText>
</UiGrid>import { Component } from "@angular/core";
import { UiGrid, UiText } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiGrid, UiText],
template: `
<div
ui-grid
[fixed]="true"
[columns]="7"
[gap]="'none'"
[shadedColumns]="[1, 7]"
>
<ng-template
ui-text
[align]="'center'"
>Sun</ng-template>
<ng-template
ui-text
[align]="'center'"
>Mon</ng-template>
<ng-template
ui-text
[align]="'center'"
>Tue</ng-template>
<ng-template
ui-text
[align]="'center'"
>Wed</ng-template>
<ng-template
ui-text
[align]="'center'"
>Thu</ng-template>
<ng-template
ui-text
[align]="'center'"
>Fri</ng-template>
<ng-template
ui-text
[align]="'center'"
>Sat</ng-template>
</div>
`,
})
export default class Example {}API
Package: @threadlabs/looma · Native root: div
Attributes
| Name | Description | Property | Type | Default | Allowed values |
|---|---|---|---|---|---|
columns | Maximum columns when there is room. Omit for no cap. Columns normally decrease as the container narrows; fixed opts into an exact count. | columns | integer | — | — |
fixed | Keeps the declared columns at every width, for aligned matrices such as weekday grids. Cells share the available width; omitted columns gives one column. | fixed | boolean | false | — |
gap | Space between cells, on the spacing scale: none removes the gap, xs is 0.5rem, s 0.75rem, m 1rem (also the default), l 1.5rem, and xl 2rem. The --ui-grid-gap custom property, set on :root, an ancestor, or the component itself, overrides it. | gap | keyword | — | none, xs, s, m, l, xl |
min | The narrowest a column may get before the grid drops to fewer columns: sm is 12rem (also the default), md is 16rem, and lg is 20rem. The grid fits as many equal columns as its width allows, and a single column never overflows a narrower container. | min | keyword | — | sm, md, lg |
selection-end | Last selected one-based column, inclusive. The interval combines the theme's selection surface and border colors; --ui-grid-selection-inset and --ui-grid-selection-surface customize its inset and paint. | selectionEnd | integer | — | — |
selection-start | First selected one-based column in a fixed grid row. Pair with selection-end to paint a continuous rounded interval behind the cells. | selectionStart | integer | — | — |
shade-tone | The shaded columns' paint: neutral uses the theme's ink; accent uses its readable accent pigment. Both are subtle washes over the current surface. | shadeTone | keyword | neutral | neutral, accent |
shaded-columns | One-based columns to shade in a fixed grid with explicit columns, such as nonworking days in a date matrix. A soft wash of the theme's ink distinguishes them from the canvas; --ui-grid-shade-surface can replace it. Shading follows writing direction and does not change cell sizing. | shadedColumns | list(integer) | [] | — |
Framework props
| Name | Type | Default | Allowed values |
|---|---|---|---|
columns | number | — | — |
fixed | boolean | false | — |
gap | "none" | "xs" | "s" | "m" | "l" | "xl" | — | none, xs, s, m, l, xl |
min | "sm" | "md" | "lg" | — | sm, md, lg |
selectionEnd | number | — | — |
selectionStart | number | — | — |
shadedColumns | readonly (number)[] | [] | — |
shadeTone | "neutral" | "accent" | neutral | neutral, accent |
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-grid-gap | var(--ui-space-4) | var(--ui-space-2) | 0px | var(--ui-space-3) | var(--ui-space-5) | var(--ui-space-6) | — |
--ui-grid-min | 12rem | 16rem | 20rem | — |
--ui-grid-selection-inset | — | var(--ui-space-1) |
--ui-grid-selection-surface | — | light-dark(
color-mix(in oklab, var(--ui-selection-border) 80%, var(--ui-selection-surface)),
color-mix(in oklab, var(--ui-selection-border) 28%, var(--ui-selection-surface))
) |
--ui-grid-shade-surface | color-mix(in oklab, var(--ui-text) 8%, transparent) | color-mix(in oklab, var(--ui-accent-subtle-text) 12%, transparent) | — |
Shared tokens
| Token | Fallback |
|---|---|
--ui-accent-subtle-text | — |
--ui-radius-round | — |
--ui-selection-border | — |
--ui-selection-surface | — |
--ui-space-1 | — |
--ui-space-2 | — |
--ui-space-3 | — |
--ui-space-4 | — |
--ui-space-5 | — |
--ui-space-6 | — |
--ui-text | — |