Pricing

TableHead handbook

v2.4.1 · @snowui-design-system/components

License: Before reading component documentation, source, or npm usage instructions for the first time, read the full LICENSE. Reuse that review for the same task and version. Public API configuration and internal implementation changes have different license implications. All components use the unified runtime package; source rights remain governed by the license.

Overview

Define Table column headers, with optional sorting and column resizing to help users understand and organize data.

When to use it

Use it for header cells in Table or DataTable. DataTable coordinates data sorting; resizing and sorting are separate capabilities, so resizable does not imply sortable.

  • columnId identifies the column; resizable controls width adjustment. Independent sortDirection and onSortChange express sorting state.

  • The selection role uses DataTable’s select-all control. parts configures generated Indicator and Checkbox; configure an explicit title directly.

Quick start

This example shows the recommended basic composition. See the sections below for more cases and composition limits.

<script lang="ts">	import { TableHead, Table, TableHeader, TableRow, TableBody, TableCell, Button } from '@snowui-design-system/components';</script> <Table ariaLabel="Recently updated component documentation" minWidth={560}>	<TableHeader><TableRow>		<TableHead columnId="component">Component</TableHead>		<TableHead columnId="updated" defaultSort="descending" defaultSortActive>Updated</TableHead>		<TableHead columnId="coverage" align="end" resizable={false}>API coverage</TableHead>	</TableRow></TableHeader>	<TableBody>		<TableRow rowId="dialog"><TableCell columnId="component">Dialog</TableCell><TableCell columnId="updated">2026-08-28</TableCell><TableCell columnId="coverage" align="end">100%</TableCell></TableRow>		<TableRow rowId="table"><TableCell columnId="component">Table</TableCell><TableCell columnId="updated">2026-08-27</TableCell><TableCell columnId="coverage" align="end">96%</TableCell></TableRow>	</TableBody></Table>

API

Keyword Type Default Description
responsiveHide boolean true When horizontal scrolling is off, allow ordinary columns to hide automatically; false keeps them visible. The first ordinary column and role columns never hide.
columnId string — Stable column ID. Role columns use built-in IDs; ordinary columns must declare one.
label string — Readable column name for the TableToolbar sorting list and the column-width handle. Without it, the sorting list falls back to columnId, while the handle uses a localized generic name and never announces the internal ID.
sortDirection "ascending" | "descending" — Independently controlled sorting direction; otherwise derived from Table context.
defaultSort "ascending" | "descending" — Declare this column as the table’s default ascending or descending sort, while keeping it sortable.
defaultSortActive boolean false Show active styling and an icon even in the defaultSort direction, adding an unsorted state to the interaction cycle.
roleLayout TableRoleLayout — Override role-column layout for this role header. Ordinary headers ignore it; default comes from Table context.
onSortChange (direction: "ascending" | "descending" | undefined) => void — Three-state sort callback for standalone use.
resizable boolean — Show a resize handle; an explicit value overrides Table context. Defaults: Table false, DataTable true.
minWidth number 64 Minimum resized width in CSS px; applies only when resizing is enabled.
maxWidth number Infinity Maximum resized width in CSS px; Infinity adds no upper limit.
align "start" | "center" | "end" "start" Align header content at start, center, or end without changing sorting.
children Snippet — Parameterless header content: text, icons, resource icons, or other components. Sortable headers add a built-in sort button; the selection role uses its built-in select-all control.
parts ComponentPartsOf<{ Indicator: typeof Indicator; Checkbox: typeof Checkbox; }> — Configure generated resize Indicator and select-all Checkbox; explicit children are configured directly.

See the base component documentation for inherited capabilities not redeclared here. This table lists only named public Props declared by this component.

Usage boundaries and details

Icon and component title

Table and DataTable share TableHead. children is the title slot; no extra icon or header Prop is needed. Resource icons, Icons, Chips, or custom display components participate in native width measurement, alignment, and responsive column collection. Static titles use inline boxes; block-level content and built-in sort buttons follow the column alignment too.

<script lang="ts">    import { TableHead, Chip } from '@snowui-design-system/components';    import { CurrencyDollar } from '@snowui-design-system/resource-svelte';</script> <TableHead columnId="amount" label="Amount in USD" align="end" title="Amount in USD">    <CurrencyDollar size={16} role="img" aria-label="Amount in USD" /></TableHead><TableHead columnId="status" label="Payment status"><Chip>Payment status</Chip></TableHead>

An icon-only title needs an accessible name. label names the column in the TableToolbar sorting list and its column-width handle; it does not automatically name the header or sort button. Give the icon role="img" and aria-label so its title and enclosing sort button are meaningful. Sortable content is placed inside the built-in button, so use only noninteractive children; do not nest links, buttons, or inputs. Selection headers use the built-in select-all Checkbox.

Generated component configuration

  • parts.Indicator configures the generated resize Indicator.

  • parts.Checkbox configures the selection header’s select-all Checkbox.

  • Sorting, selection, and resize events remain owned by TableHead and cannot be overridden through parts.

Attribute Type Default Description
as selection | actions - Set the role column. Role columns do not sort or resize.
minWidth / maxWidth number 64 / Infinity Resize constraints.

Sort state maps to aria-sort. Only the default direction of the DataTable defaultSort column omits the active state and icon, unless defaultSortActive is enabled. Other sorted states show an icon. Following numeric direction, ascending (A→Z, 0→9, early→late) shows ArrowsUp; descending shows ArrowsDown. The icon is nominally 16px and stays at least 4 CSS px from title content as root font or gap scale shrinks. The icon does not shrink, and the title retains its one-line truncation.

Interactive cycle:

  • defaultSort hit column: default direction (no icon) → reverse (with icon) → default direction (no icon).

  • Other columns: no sorting → ascending (ArrowsUp) → descending (ArrowsDown) → no sorting.

Sortable headers follow the shared colorless text/Icon hover rule: keep Black/100% as the source and raise opacity from 0.4 to 1. Hover, keyboard focus, and active sort use opacity 1, matching the icon and text. Unsortable columns render no button or hover/click state.

No sortable Prop is needed. DataTable infers sortability from the matching raw row field or sorters[columnId]. Standalone TableHead enables sorting when sortDirection or onSortChange is declared. Columns without sorting rules render no button.

Ordinary Table columns do not resize by default; pass resizable to TableHead to enable it. DataTable ordinary columns with columnId resize by default; pass resizable={false} to disable. as="selection" and as="actions" role columns never resize. Use resizable={false} for a fixed-width column. Headers and TableCells share responsive horizontal padding from 4px to 12px; a narrow screen commonly uses 4px per side for at least 8px between adjacent columns, with 8px vertical padding. First and last visible cells and role overlays retain at least 8px toward the outer edge.

Without a fixed column width, the header’s unwrapped min-content participates in Table auto sizing alongside same-column TableCells. A narrow host therefore does not break titles letter by letter. Explicit columnWidths, drag widths, and other fixed widths take precedence; overflow titles still use the header truncation rule.

Without defaultSort, TableHead does not treat the first column as implicitly sorted. When no column has active sorting, DataTable preserves the order of rows or filteredRows supplied by the parent.

Double-click the resize handle

  • Double-clicking Indicator on a default single-line column fits the widest currently rendered cell and header content, including icons, gaps, padding, and sort-icon space. It does not scan unloaded server data.

  • If any same-column cell has explicit nowrap={false}, double-click restores the width from before the first drag. Prefer external columnWidths for that baseline; otherwise use the measured initial layout width to avoid unbounded growth from long text.

  • Clamp the result to minWidth/maxWidth and preserve other columns. Update columnWidths binding and callback; Table may scroll horizontally if needed.

  • Double-click does not sort and is unavailable with resizable={false}. Repeat double-click to measure current-page content again. Row changes retain the same initial baseline until Table remounts.

Role column

  • as="selection" renders the built-in select-all Checkbox. Its resident role-column layout always shows it; in overlay mode it appears on first-header hover, direct hover, or keyboard focus. Partial selection uses indeterminate. Table responsive rules set horizontal padding.

  • as="actions" has no title or business button by default. Pass children for a custom title. Table responsive rules set horizontal padding.

  • roleLayout="overlay" gives role headers zero width and sticks them to the left or right edge. Selection Checkbox and any actions title use responsive horizontal padding. Real first and last header cells keep square corners; floating controls may use a 12px radius. The control overlay defaults to one-third of its original width. Its blur layer starts hidden and becomes visible on header hover, using shared Progressive Blur from components/src/lib/progressive-blur.ts and a segmented mask. Selection fades from strong left to clear right; actions reverse that direction.

  • The first real title behind a selection overlay starts its transparent region at the Checkbox’s actual right edge, then fades over --size-5 (20px nominally) to the right, matching row Checkbox/button columns. Override with --snowui-table-head-fade-start-override and --snowui-table-head-fade-end-override.

  • The overlay select-all Checkbox appears progressively. When pointer leaves the header and control with no keyboard focus inside, remove it in the same frame as the title mask to avoid an afterglow. Preserve focusable DOM without visibility hiding so keyboard access remains possible.

  • Both roles use explicit combination contracts and do not guess the column identity through DOM scanning.

Each TableHead registers its public column structure (columnId, selection/actions role, and alignment) with the nearest Table Context. TableBody uses this order to generate Skeleton rows on first load. Registration does not expose or scan TableHead’s private DOM.

Only data headers adjacent to selection/actions role columns include fade sampling layout boxes. Other columns omit them so incremental loading does not lock width or create horizontal scroll from invisible masks. Sampling width comes from the role overlay width, not ordinary data-column width.

Sort value type inference

TableHead does not declare a content type or expose sortType="text | number | date". DataTable compares raw row fields:

  • Date values compare by timestamp.

  • number values compare numerically, so Unix timestamps sort in time order.

  • ISO 8601 date-time strings with time zones are converted to timestamps before comparison.

  • Other strings use localized, case-insensitive natural order with numeric segments.

  • For derived display values, DataTable sorters[columnId] returns a comparable raw value.

This prevents formatted text such as Just now or $1,299 from driving sort order and avoids redeclaring types already known to the data layer.

Default sorting declaration

The default sorting is directly declared by the corresponding column header:

<TableHead columnId="createdAt" defaultSort="descending">Date</TableHead>

One DataTable may have only one TableHead declaring defaultSort. It takes precedence over the legacy DataTable-level defaultSort={{ columnId, direction }} object, retained for compatibility.

In the default direction, the column normally has no active styling and cycles “default → reverse → default.” To show that state as active:

<TableHead    columnId="createdAt"    defaultSort="descending"    defaultSortActive>    Date</TableHead>

defaultSortActive changes behavior as well as appearance: “default (active) → reverse (active) → unsorted → default (active).”

Keyword

Keyword Description
component Current public component contract
  • Table context coordinates sorting and column width; the component does not store application state.

  • In DataTable, Context isSelectionEnabled/isActionsEnabled controls whether as="selection"/as="actions" columns render at all.

  • Default font weight is 400; font size and line height use --text-12 and --leading-12.

  • All header cells and data rows use the same responsive left and right padding, ranging from 4px to 12px.

  • resizable uses shared Indicator. Its hit strip spans the full header-cell right edge; the visible Indicator is 2px × 16px normally and 4px thick on hover. The entire edge can start a drag.

TableHead style overlay

Read the full LICENSE first; reuse that review for the same task, version, and scope.

First use public component Props to match the desired appearance, preserving the existing look where possible. If changes are necessary, follow the style selection and override order: use your own class (including Tailwind already in the project) or an instance style. The names below are public overrides actually consumed by source; check their scope and do not modify private DOM. Choose the correct SnowUI token family. External apps need not install SnowUI website utilities.

Variable Purpose and default reference
--snowui-checkbox-size-override Overall size (size px, see the component source code for the default value)
--snowui-table-head-actions-padding-override default: --padding-2 --padding-3
--snowui-table-head-padding-override default: --padding-2 --padding-3
--snowui-table-head-selection-padding-override default: --padding-2 --padding-3
--snowui-table-head-width-override default: runtime column width
--snowui-table-role-overlay-width-override default: calc((--size-12 + --size-14) / 3)
--snowui-table-row-radius-override default: --radius-3
--snowui-table-selection-checkbox-size-override Default: calc(var(--size-4) + 0.0625rem) = 17px; StyleSpec §2.2.2 registration special case, only works for selection full selection / line Checkbox

Design Guide · Token · Combination boundary

TableHead Type and Status

Required Props: no named required Props.

Bindable Props: this component declares no named two-way binding; handle state updates through public callbacks or its host.

Inheritance: Omit<FrameProps, "children">. Consult the base class public documentation; fields excluded by Omit cannot be passed through unchanged.

Share Frame API · Return API

The following type declarations are actually referenced by public Props and come from source. A Svelte Snippet denotes a content fragment; ComponentProps denotes the Props of the corresponding component.

TableRoleLayout

Source: components/src/lib/table-context.svelte.ts

export type TableRoleLayout = "auto" | "column" | "overlay";

ComponentPartsOf

Source: components/src/logic/component-parts.svelte.ts

export type ComponentPartsOf<    T extends Record<string, Component<any>>,> = {    [K in keyof T]?: ConfigurableComponentPartProps<T[K]>;};

ConfigurableComponentPartProps

Source: components/src/logic/component-parts.svelte.ts

export type ConfigurableComponentPartProps<C extends Component<any>> = Partial<    Omit<ComponentProps<C>, ComponentPartControlledKey>>;

ComponentPartControlledKey

Source: components/src/logic/component-parts.svelte.ts

export type ComponentPartControlledKey =    | "children"    | "as"    | "value"    | "defaultValue"    | "open"    | "selected"    | "checked"    | "disabled"    | "readonly"    | "id"    | `on${string}`    | `aria-${string}`;