Skip to content

Changing the look

Each section below answers one question — « how do I make the rows taller? », « how do I change the font? » — with the code to copy. Every name used here is in the table of variables, with its default.

  • In your stylesheet, with CSS variables. This styles every grid of the page, or one grid when you put its rules under the host’s class or id (#orders .dzg { … }). Use it for the look of your whole application.
  • From code, with the theme setting and the theming() feature. This styles one grid only, wins over your stylesheet, and changes live with grid.update({ theme }). Use it when the look depends on data or on the user.

Heights are not CSS: they are settings (density, rowHeight, headerHeight), because the grid needs them to place rows it has not drawn yet.

import '@dzgrid/theme/dzgrid.css'; // required: colours, lines, header, hover, focus
import '@dzgrid/theme/variables.css'; // the presentation knobs marked « variables » in the table

In Angular, put them in angular.json ("styles": ["node_modules/@dzgrid/theme/dzgrid.css", "node_modules/@dzgrid/theme/variables.css", "src/styles.css"]) and write your rules in a global stylesheet: a component’s own styles (emulated encapsulation) never reach the grid’s cells.

The theme’s rules sit in the cascade layer dzgrid, so yours win without !important. With an unlayered reset (Tailwind 3’s preflight, Bootstrap’s Reboot), load the *.unlayered.css copies instead (more).

You want Write
Tighter or looser rows, from a list density: 'compact' (28 px), 'comfortable' (32 px, the default), 'spacious' (40 px)
An exact height rowHeight: 44 (pixels, 24 to 1 000) — wins over the density
Back to the density’s height grid.update({ rowHeight: undefined })
Rows as tall as their text the column’s wrap: true and the autoHeight() feature, without rowHeight
The user chooses columnMenu({ density: true }): the column menu offers the three densities

The heights are for the browser’s default text size (16 px). A reader who makes the text bigger gets taller rows by themselves. The font you set on the grid does not change the height: with bigger text, set rowHeight too.

The density also sets the cells’ side padding (× 0.75, 1, 1.25). To keep the padding fixed whatever the density: .dzg { --dzg-density: 1 }; to change it: --dzg-cell-padding-inline.

You want Write
Taller column titles headerHeight: 56 (pixels, 24 at least)
Column titles and the group rows above them apart headerHeight: { columns: 56, groups: 32 }
A taller filter row filters({ floating: true, rowHeight: 44 })
Long titles on several lines the column’s headerWrap: true (the row grows to the tallest title)

Without headerHeight, the header rows take the row height.

The demo below sets rowHeight: 44 and headerHeight: 56, with a serif font from the theme setting.

import { createGrid } from '@dzgrid/dom';
import { theming } from '@dzgrid/dom/theme';
import { EN } from '@dzgrid/dom/locales/en';
import '@dzgrid/theme/dzgrid.css';
import '@dzgrid/theme/variables.css';
type Light = { id: string; name: string; range: number };
const grid = createGrid<Light>(document.getElementById('lights')!, {
rows: [{ id: 'l1', name: 'Gull Point', range: 18 }],
columns: [{ id: 'name', header: 'Lighthouse' }, { id: 'range', header: 'Range (nm)', type: 'number' }],
getRowId: (l) => l.id,
messages: EN,
rowHeight: 44,
headerHeight: 56,
theme: { font: '400 0.9375rem/1.4 Georgia, "Times New Roman", serif', headerWeight: 700 },
features: [theming()],
});
grid.update({ rowHeight: 36 }); // live

The grid takes the height of the element you give it — never the height of its rows. Give that element a height and the rows scroll inside:

#lights { block-size: 480px; } /* fixed */
#lights { block-size: calc(100dvh - 4rem); } /* the screen, minus a bar */
.page { display: flex; flex-direction: column; block-size: 100dvh; }
.page > #lights { flex: 1; min-block-size: 0; } /* what is left in a column layout */

An element with no height gives a grid with no room: give the height to the element itself or to its flex or grid parent. There is no « as tall as its rows » mode: the page would jump each time a filter changes the count.

The whole grid. In your stylesheet, set the font on the grid’s root; the header, cells, menus and popups follow:

.dzg { font-family: "Atkinson Hyperlegible", system-ui, sans-serif; font-size: 0.875rem; }

Or with the variable --dzg-font, which is the CSS font shorthand: it needs at least a size and a family, in that order. A family alone is not a valid font and the grid keeps the page’s font:

.dzg { --dzg-font: 400 0.875rem/1.45 "Atkinson Hyperlegible", system-ui, sans-serif; } /* works */
.dzg { --dzg-font: "Atkinson Hyperlegible", sans-serif; } /* ignored: no size */

From code, for one grid: theme: { font: '400 0.875rem/1.45 Inter, sans-serif' }, with theming() (the demo above).

Only the cells, only the header. These need variables.css, except --dzg-header-size:

.dzg {
--dzg-cell-font-size: 0.8125rem; /* the cells' text */
--dzg-header-size: 0.8125rem; /* the titles' text */
--dzg-header-weight: 500; /* the titles' weight (600 by default) */
--dzg-header-font-family: "Inter", sans-serif;
--dzg-header-case: uppercase; /* titles in capitals (none in Arabic) */
--dzg-header-tracking: 0.04em; /* letter spacing */
}

The same from code: theme: { cellFontSize: '0.8125rem', headerSize: '0.8125rem', headerWeight: 500, headerCase: 'uppercase' }. Numbers keep digits of equal width so columns line up (--dzg-num-figures: normal turns it off).

The palette is ten variables shared by every part of the grid. Change them and everything follows: header, hover, selection, focus, filters, menus.

:root {
--tz-accent: light-dark(#0f766e, #5eead4); /* focus, selection, active filters, buttons */
--tz-bg: light-dark(#ffffff, #111827); /* the grid's surface */
--tz-fg: light-dark(#1f2937, #f3f4f6); /* text */
}
.dzg {
--dzg-header-bg: light-dark(#f1f5f9, #1e293b);
--dzg-row-hover-bg: color-mix(in srgb, var(--tz-accent) 6%, var(--tz-bg));
--dzg-row-selected-bg: color-mix(in srgb, var(--tz-accent) 14%, var(--tz-bg));
}
  • light-dark(light, dark) gives each colour for both modes. The grid follows the system’s mode; data-theme="light" or data-theme="dark" on any ancestor forces one.
  • The other surfaces (--tz-bg-raised, --tz-fg-muted, --tz-border) and the tones (--tz-danger, --tz-warning, --tz-success) are in the table.
  • Contrast is yours to keep: 4.5:1 for text. npm run check:contrast -- --palette palettes.json, in DzGrid’s repository, checks yours.
  • With Tailwind or Bootstrap, load @dzgrid/theme/tailwind.css (Tailwind 4), tailwind3.css or bootstrap.css: the grid then takes your framework’s colours, font and corners.
.dzg {
--dzg-row-odd-bg: color-mix(in srgb, var(--tz-accent) 5%, var(--tz-bg)); /* striped rows */
--dzg-column-line: var(--dzg-line, 1px) solid var(--tz-border); /* lines between columns */
--dzg-row-line: none; /* no line between rows */
--dzg-frame: none; /* no outer border */
--dzg-radius: 0; /* square corners */
--dzg-line: 1px; /* every line's width */
}

--dzg-row-odd-bg and --dzg-radius are in dzgrid.css; the others need variables.css. To reuse a grid variable in a value, give its fallback — var(--dzg-line, 1px): the theme declares none, so without the , 1px the value is empty and the whole line is dropped. The demo below sets the same from code, on this grid only:

Short dividers, the last row and column, hovered text (1.12.0)

Section titled “Short dividers, the last row and column, hovered text (1.12.0)”
.dzg {
--dzg-column-separator: var(--tz-border); /* a mark between columns… */
--dzg-column-separator-height: 60%; /* …60 % of the row's height, centred */
--dzg-column-separator-size: 1px;
--dzg-last-column-line: var(--dzg-line, 1px) solid var(--tz-border); /* close the table after its last column */
--dzg-last-row-line: none; /* no line under the last row */
--dzg-row-hover-bg: light-dark(#eef2ff, #1e1b4b);
--dzg-row-hover-fg: light-dark(#1e1b4b, #e0e7ff); /* the hovered row's text */
--dzg-input-border: none; /* filter boxes without an edge */
--dzg-tooltip-arrow-size: 0; /* no arrow on the tooltip */
}

All need variables.css. Content that sets its own colour (a coloured mark in a part) keeps it under --dzg-row-hover-fg; content that should follow the row’s text uses currentColor or color: inherit in its own CSS.

Widths are column options, not CSS: width (pixels), flex (a share of the room left), minWidth, maxWidth, resizable. The columns guide shows them, with sizing to the content.

A column’s cells and title: the column option ui takes classes.

import type { ColumnDef } from '@dzgrid/core';
type Order = { id: string; client: string; total: number };
export const columns: ColumnDef<Order>[] = [
{ id: 'client', header: 'Client', ui: { headerClass: 'col-client', cellClass: () => 'col-client' } },
{ id: 'total', header: 'Total', type: 'number', ui: { cellClass: (c) => (c.row.total > 1000 ? 'big' : null) } },
];
.dzg .col-client { font-weight: 600; }
.dzg .big { color: var(--tz-success); }

A row: the rowClass setting, from the row’s data: rowClass: ({ row }) => (row.daysLate > 0 ? 'late' : null), then .dzg .late { color: var(--tz-danger); }.

Content drawn your way (a badge, a bar, a link): a cell part, see ui and parts. Classes name a look; keep the colours as variables so the dark mode and forced colours still apply.

The sort arrows, the funnel, the menu dots and the chevrons can be your own SVG, with the icons setting and theming(). Their size: --dzg-icon-size (variables.css).

@dzgrid/theme/midnight.css and clinic.css, each light and dark, are complete themes to start from; data-dzg-skin="midnight" on a container applies one to that area only. Reading their source is the quickest way to see many variables at work.