diff --git a/.build/markup-classes-baseline.txt b/.build/markup-classes-baseline.txt
index 3fcc4c5c1..92b96aac2 100644
--- a/.build/markup-classes-baseline.txt
+++ b/.build/markup-classes-baseline.txt
@@ -3,8 +3,6 @@
callout-note
clipboard-feedback
clipboard-label
-datagrid-content
-datagrid-item
datatable
docs-classnames
docs-page-lead
diff --git a/.changeset/datagrid-horizontal.md b/.changeset/datagrid-horizontal.md
new file mode 100644
index 000000000..08a61fc72
--- /dev/null
+++ b/.changeset/datagrid-horizontal.md
@@ -0,0 +1,7 @@
+---
+"@tabler/core": minor
+"@tabler/preview": minor
+"@tabler/docs": minor
+---
+
+Added `.datagrid-horizontal` and `.datagrid-cols-{n}` classes and more datagrid examples and docs.
diff --git a/core/scss/_variables.scss b/core/scss/_variables.scss
index b6273def6..13844c1f2 100644
--- a/core/scss/_variables.scss
+++ b/core/scss/_variables.scss
@@ -1357,6 +1357,8 @@ $btn-close-white-filter: invert(1) grayscale(100%) brightness(200%) !default; //
$datagrid-padding: 1.5rem !default;
$datagrid-item-width: 15rem !default;
$datagrid-title-margin-bottom: 0.25rem !default;
+$datagrid-title-width: 10rem !default;
+$datagrid-columns: 4 !default;
// scss-docs-end datagrid-variables
// Empty
diff --git a/core/scss/ui/_datagrid.scss b/core/scss/ui/_datagrid.scss
index 7bd5490e2..23ef49d82 100644
--- a/core/scss/ui/_datagrid.scss
+++ b/core/scss/ui/_datagrid.scss
@@ -9,6 +9,7 @@
--datagrid-padding: #{$datagrid-padding};
--datagrid-item-width: #{$datagrid-item-width};
--datagrid-title-margin-bottom: #{$datagrid-title-margin-bottom};
+ --datagrid-title-width: #{$datagrid-title-width};
// scss-docs-end datagrid-css-vars
display: grid;
@@ -20,3 +21,26 @@
@include subheader();
margin-bottom: var(--datagrid-title-margin-bottom);
}
+
+.datagrid-content {
+ margin: 0;
+}
+
+.datagrid-horizontal {
+ --datagrid-title-margin-bottom: 0;
+
+ grid-template-columns: minmax(0, 1fr);
+
+ .datagrid-item {
+ display: grid;
+ grid-template-columns: var(--datagrid-title-width) 1fr;
+ gap: 1rem;
+ align-items: baseline;
+ }
+}
+
+@for $i from 1 through $datagrid-columns {
+ .datagrid-cols-#{$i} {
+ grid-template-columns: repeat(#{$i}, minmax(0, 1fr));
+ }
+}
diff --git a/docs/content/ui/components/datagrid.mdx b/docs/content/ui/components/datagrid.mdx
index 7ce89a3d7..12791b5dd 100644
--- a/docs/content/ui/components/datagrid.mdx
+++ b/docs/content/ui/components/datagrid.mdx
@@ -2,7 +2,7 @@
title: Data grid
summary: Use the data grid component to display detailed information about your product. The data is displayed as a column of items consisting of a title and content.
description: Display labeled data in a compact datagrid - a responsive list of title and content pairs, ideal for detail panels and summaries.
-related: [/ui/components/table]
+related: [/ui/components/table, /ui/plugins/clipboard]
source: core/scss/ui/_datagrid.scss
css-vars: datagrid
classnames:
@@ -10,8 +10,18 @@ classnames:
- class: datagrid
desc: Container element, a responsive grid of label and value pairs
part:
+ - class: datagrid-item
+ desc: One label and value pair
- class: datagrid-title
- desc: Label above a value
+ desc: Label of a value, styled as a subheader
+ - class: datagrid-content
+ desc: Value of the pair, with the margin reset so a dd fits
+ modifier:
+ - class: datagrid-cols-{n}
+ desc: Fixed number of columns, from 1 to 4
+ direction:
+ - class: datagrid-horizontal
+ desc: Puts the label beside the value and shows one pair per row
---
import Example from '@components/Example.astro';
import Icon from '@ui/Icon.astro'
@@ -116,14 +126,264 @@ A value is a plain container, so it takes anything: text, [avatars](/ui/componen
+## Horizontal
+
+Add the `.datagrid-horizontal` class to put the label beside the value. Each pair takes a full row, which is easy to scan when the values are long.
+
+
+
+
+
Plan
+
Business
+
+
+
Status
+
Active
+
+
+
Address
+
44 Harbor Street, Portland, OR 97209, United States
+
+
+
+
+Set `--tblr-datagrid-title-width` to change the width of the label column. Add a `.datagrid-cols-{n}` class to show several pairs in a row.
+
+
+
+
+
CPU
+
8 cores
+
+
+
Memory
+
32 GB
+
+
+
Storage
+
512 GB
+
+
+
Region
+
eu-central
+
+
+
+
## Column width
-Columns are generated with `auto-fit`, so the item width decides how many fit in a row. Set the two custom properties on the `.datagrid` element to change the layout:
+Columns are generated with `auto-fit`, so the item width decides how many fit in a row. Set the custom properties on the `.datagrid` element to change the layout:
-| Variable | Description | Default |
-|------------------------------|--------------------------------|----------|
-| `--tblr-datagrid-item-width` | Width of the datagrid item | `15rem` |
-| `--tblr-datagrid-padding` | Gap between the datagrid items | `1.5rem` |
+| Variable | Description | Default |
+|------------------------------|--------------------------------------|----------|
+| `--tblr-datagrid-item-width` | Minimum width of a column | `15rem` |
+| `--tblr-datagrid-padding` | Gap between the items | `1.5rem` |
+| `--tblr-datagrid-title-width`| Label column in a horizontal datagrid| `10rem` |
+
+Use a lower item width for short values, such as numbers or single words. More columns then fit in a row.
+
+
+
+
+
CPU
+
8 cores
+
+
+
Memory
+
32 GB
+
+
+
Storage
+
512 GB
+
+
+
Region
+
eu-central
+
+
+
Uptime
+
99.98%
+
+
+
Backups
+
Daily
+
+
+
+
+### Fixed number of columns
+
+Add a `.datagrid-cols-{n}` class to keep the same number of columns at any container width. The classes go from `.datagrid-cols-1` to `.datagrid-cols-4`. The item width is ignored then.
+
+
+
+
+
Order
+
#48213
+
+
+
Status
+
Paid
+
+
+
Placed
+
September 12, 2026
+
+
+
Total
+
$248.00
+
+
+
+
+A single column works well in a narrow sidebar or an offcanvas.
+
+
+
44 Harbor Street, Portland, OR 97209, United States
+
+
+
Notes
+
Not set
+
+
+
+
+### Gap
+
+Lower `--tblr-datagrid-padding` to make a dense datagrid. The same value is used between columns and between rows.
+
+
+
+
+
CPU
+
8 cores
+
+
+
Memory
+
32 GB
+
+
+
Storage
+
512 GB
+
+
+
Region
+
eu-central
+
+
+
Uptime
+
99.98%
+
+
+
Backups
+
Daily
+
+
+
+
+## Examples
+
+### Inside a card
+
+A datagrid has no border or background of its own. Put it in a [card](/ui/components/card) to build a detail panel. The card header holds the title and the footer holds the actions.
+
+
+
+
+
+### Statuses and progress
+
+Show the state of a resource with a [status](/ui/components/status), a [badge](/ui/components/badge) or a [progress bar](/ui/components/progress).
+
+
+
+
+
State
+
Running
+
+
+
Environment
+
Production
+
+
+
Disk usage
+
+
68%
+
+
+
+
+
+
+
+
+### Values to copy
+
+Add a [clipboard](/ui/plugins/clipboard) button next to a value that users copy, such as an API key. Point `data-bs-target` at the element with the value and give the button an `aria-label` that names it.
+
+
+
+
+
Client ID
+
+ tblr_live_8f3a2c91d7e4
+
+
+
+
+
Webhook URL
+
+ https://example.com/hooks/tabler
+
+
+
+
+
## Accessibility
@@ -138,11 +398,11 @@ The pairs are only visually connected, so tell assistive tech how they belong to
Registrar
-
Third Party
+
Third Party
Expiration date
-
–
+
–
diff --git a/preview/pages/datagrid.astro b/preview/pages/datagrid.astro
index 407b3fd4d..2d6f54891 100644
--- a/preview/pages/datagrid.astro
+++ b/preview/pages/datagrid.astro
@@ -3,17 +3,271 @@ import DefaultLayout from '@shared/layouts/DefaultLayout.astro'
import Card from '@ui/Card.astro'
import CardHeader from '@ui/CardHeader.astro'
import CardBody from '@ui/CardBody.astro'
-import Datagrid from '@shared/components/parts/Datagrid.astro'
+import CardFooter from '@ui/CardFooter.astro'
+import CardTitle from '@ui/CardTitle.astro'
+import CardSubtitle from '@ui/CardSubtitle.astro'
+import CardActions from '@ui/CardActions.astro'
+import Avatar from '@ui/Avatar.astro'
+import Badge from '@ui/Badge.astro'
+import Button from '@ui/Button.astro'
+import ButtonList from '@ui/ButtonList.astro'
+import Icon from '@ui/Icon.astro'
+import Progress from '@ui/Progress.astro'
+import Datagrid from '@ui/Datagrid.astro'
+import DatagridItem from '@ui/DatagridItem.astro'
+import DatagridDemo from '@shared/components/parts/Datagrid.astro'
import DocsLink from '@ui/DocsLink.astro'
+import { people } from '@shared/lib/people'
+import { requireIndex } from '@shared/lib/array'
+
+const customer = requireIndex(people, 2)
+const owner = requireIndex(people, 4)
---
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Basic
+ A .datagrid with plain text values.
+
+ Business
+ Monthly
+ October 18, 2026
+ 8 of 10 used
+
+
+
+
+
+
+
+
+ Description list
+ Use dl, dt and dd so screen readers announce the pairs.
+
+ Tabler Ltd.
+ EU123456789
+ billing@tabler.io
+ Poland
+
+
+
+
+
+
+
+
+ Narrow columns
+ Lower --tblr-datagrid-item-width to fit more columns in a row.
+
+ 8 cores
+ 32 GB
+ 512 GB
+ eu-central
+ 99.98%
+ Daily
+ Enabled
+ 3306
+
+
+
+
+
+
+ Horizontal
+ Add .datagrid-horizontal to put the label beside the value.
+
+ Business
+ Active
+ October 18, 2026
+ 44 Harbor Street, Portland, OR 97209, United States
+
+
+
+
+
+
+
+
+ Horizontal in columns
+ Add .datagrid-cols-2 and lower --tblr-datagrid-title-width to fit two pairs in a row.
+
+ 8 cores
+ 32 GB
+ 512 GB
+ eu-central
+ 99.98%
+ Daily
+
+
+
+
+
+
+
+
+ Single column
+ Add .datagrid-cols-1 to stack the items.
+
+
+
+
+ {customer.full_name}
+
+
+ {customer.email}
+ 44 Harbor Street, Portland, OR 97209, United States
+ Not set
+
+
+
+
+
+
+
+
+ Two columns
+ Add .datagrid-cols-2 to keep two columns at any width.
+
+ #48213
+
+ September 12, 2026
+ September 16, 2026
+ 3
+ $248.00
+ Visa ending 4242
+ DHL Express
+
+
+
+
+
+
+
+
+ Statuses and progress
+ A value can hold a status, a badge or a progress bar.
+
+ Running
+ Degraded
+
+ v1.6.1
+
+
68%
+
+
+
+
41%
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ tabler.io
+ Third Party
+
+
+ Valid until May 2, 2027
+
+
+
+
+
+
+
+ {owner.full_name}
+
+
+ –
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Add a data-bs-toggle="clipboard" button to let users copy a value.
+
+
+
+ tblr_live_8f3a2c91d7e4
+
+
+
+
+
+ https://example.com/hooks/tabler
+
+
+
+ 2 hours ago
+
+
+
+
+
diff --git a/screenshots/pages/datagrid.astro b/screenshots/pages/datagrid.astro
new file mode 100644
index 000000000..34c70b61e
--- /dev/null
+++ b/screenshots/pages/datagrid.astro
@@ -0,0 +1,59 @@
+---
+// Datagrid in its natural place: the details panel of a domain in a hosting
+// dashboard. The values mix plain text, a status, an avatar, an icon and a
+// progress bar, so the picture shows that a value can hold any content.
+import ScreenshotLayout from '../layouts/ScreenshotLayout.astro'
+import Card from '@ui/Card.astro'
+import CardHeader from '@ui/CardHeader.astro'
+import CardBody from '@ui/CardBody.astro'
+import CardActions from '@ui/CardActions.astro'
+import Avatar from '@ui/Avatar.astro'
+import Badge from '@ui/Badge.astro'
+import Button from '@ui/Button.astro'
+import Icon from '@ui/Icon.astro'
+import Progress from '@ui/Progress.astro'
+import Datagrid from '@ui/Datagrid.astro'
+import DatagridItem from '@ui/DatagridItem.astro'
+import { people } from '@shared/lib/people'
+import { requireIndex } from '@shared/lib/array'
+
+const owner = requireIndex(people, 4)
+---
+
+
+
+
+
+
+
+
+
+
+ tabler.io
+ Third Party
+
+ Active
+
+
+
+
+ {owner.full_name}
+
+
+
+
+
+ 3306
+
+
+ Valid
+
+ May 2, 2027
+
+
68%
+
+
+
+
+
+
diff --git a/shared/ui/Datagrid.astro b/shared/ui/Datagrid.astro
index 26e8e3d5c..35b3ed0ec 100644
--- a/shared/ui/Datagrid.astro
+++ b/shared/ui/Datagrid.astro
@@ -1,13 +1,29 @@
---
// Data grid container (.datagrid); holds DatagridItem children.
-interface Props {
+type PolymorphicProps = { as?: 'div' | 'dl' }
+
+interface Props extends PolymorphicProps {
+ /** core/scss/ui/_datagrid.scss `.datagrid-horizontal`: label beside the value, one pair per row */
+ variant?: 'horizontal' | undefined
+ /** `.datagrid-cols-{n}`: a fixed number of columns instead of as many as fit */
+ columns?: 1 | 2 | 3 | 4 | undefined
+ /** sets --tblr-datagrid-item-width, the minimum width of a column (e.g. `10rem`) */
+ itemWidth?: string | undefined
+ /** sets --tblr-datagrid-padding, the gap between items (e.g. `1rem`) */
+ gap?: string | undefined
+ /** sets --tblr-datagrid-title-width, the label column of the horizontal variant (e.g. `8rem`) */
+ titleWidth?: string | undefined
class?: string
+ /** remaining attributes (id, data-*, aria-*, …) are forwarded to the root element */
+ [key: string]: unknown
}
-const { class: className } = Astro.props
+const { as: Tag = 'div', variant, columns, itemWidth, gap, titleWidth, class: className, ...rest }: Props = Astro.props
+
+const style = [itemWidth && `--tblr-datagrid-item-width: ${itemWidth}`, gap && `--tblr-datagrid-padding: ${gap}`, titleWidth && `--tblr-datagrid-title-width: ${titleWidth}`].filter(Boolean).join('; ')
---
-