Skip to content

Responsive Plugin

The Responsive plugin transforms the grid from a tabular layout to a card/list layout when the grid width falls below a configurable breakpoint. This enables grids to work seamlessly in narrow containers like split-pane UIs, mobile viewports, and dashboard widgets.

import '@toolbox-web/grid/features/responsive';
import { queryGrid } from '@toolbox-web/grid';
import '@toolbox-web/grid/features/responsive';
const grid = queryGrid('tbw-grid');
grid.gridConfig = {
columns: [
{ field: 'id', header: 'ID' },
{ field: 'name', header: 'Name' },
{ field: 'email', header: 'Email' },
],
features: {
responsive: { breakpoint: 500 },
},
};
grid.rows = data;
Breakpoint (px) Width threshold for card layout
Hide header Hide column headers in card layout
Animate Smooth transitions between modes
Hidden columns Columns to hide in card mode
Debounce (ms) Resize debounce delay
↔ Drag the right edge to resize the container

Resize the container to see the grid switch between table and card layouts.

Use the plugin API to manually control responsive mode.

↔ Resize to see custom card layout

Use cardRenderer for complete control over card layout - avatars, badges, custom layouts, etc.

Each card has a fixed 60px height

Use cardRowHeight for consistent card heights, which helps virtualization performance.

↔ Resize to see columns hide progressively

Use multiple breakpoints to progressively hide columns before switching to card layout.

Email shows without label, Start Date is fully hidden

Use enhanced hiddenColumns syntax to show values without labels.

↔ Resize below 500px to see animated transition

Smooth animations when transitioning between modes (enabled by default).

The ResponsivePlugin supports declarative configuration via the <tbw-grid-responsive-card> element. Framework adapters provide wrapper components with idiomatic rendering patterns.

<tbw-grid>
<tbw-grid-responsive-card
breakpoint="500"
card-row-height="80"
hidden-columns="createdAt, updatedAt"
hide-header="true">
<div class="custom-card">
<strong>{{ row.name }}</strong>
<span>{{ row.email }}</span>
</div>
</tbw-grid-responsive-card>
</tbw-grid>

In vanilla JS, the innerHTML of the element is used as a template. Use {{ row.fieldName }} syntax for value interpolation.

AttributeTypeDescription
breakpointnumberWidth threshold in pixels for responsive mode
card-row-heightnumber | 'auto'Card height in pixels or auto
hidden-columnsstringComma-separated field names to hide
hide-header'true' | 'false'Hide header row in responsive mode
debounce-msnumberResize debounce delay in ms

See ResponsivePluginConfig for the full list of options and defaults.

For progressive degradation, use the breakpoints array instead of a single breakpoint. See BreakpointConfig for the full interface.

The enhanced hiddenColumns syntax supports both simple strings and objects. See HiddenColumnConfig for the full type definition.

Example:

hiddenColumns: [
'startDate', // Fully hidden
{ field: 'email', showValue: true }, // Value shown without label
]

The breakpoint should be based on your grid’s column count and content:

Grid SizeSuggested Breakpoint
3-5 columns400-500px
6-10 columns600-800px
10+ columns900-1200px

Resize the browser window below the breakpoint (600 px) to trigger card mode.

Event Log:

Emitted when the grid crosses the breakpoint threshold:

grid.on('responsive-change', ({ isResponsive, width, breakpoint }) => {
console.log(isResponsive ? 'Card mode' : 'Table mode');
console.log(`Width: ${width}px, Breakpoint: ${breakpoint}px`);
});

Control responsive mode programmatically via the plugin instance:

// Get the plugin instance from the grid
const plugin = grid.getPluginByName('responsive');
// Check current mode
const isCardMode = plugin.isResponsive();
// Force responsive mode (regardless of width)
plugin.setResponsive(true);
plugin.setResponsive(false);
// Update breakpoint dynamically
plugin.setBreakpoint(600);
// Get current grid width
const width = plugin.getWidth();
// Get active breakpoint (multi-breakpoint mode)
const activeBreakpoint = plugin.getActiveBreakpoint();
// Returns: { maxWidth: 600, hiddenColumns: [...], cardLayout: false } or null
  1. ResizeObserver monitors the grid element’s width
  2. When width < breakpoint, the plugin adds data-responsive attribute to the grid
  3. CSS transforms cells from horizontal to vertical layout
  4. Each cell displays “Header: Value” using the ::before pseudo-element with data-header attribute

This CSS-only approach means:

  • No DOM replacement or re-rendering needed
  • Smooth transitions between modes
  • Works with all other plugins (selection, editing, etc.)

The responsive plugin uses the grid’s existing CSS custom properties for theming.

The responsive plugin uses the grid’s built-in CSS variables:

PropertyDescription
--tbw-cell-paddingPadding inside card rows
--tbw-color-borderCard separator color
--tbw-color-bgCard background color
--tbw-color-row-altAlternating card background
--tbw-color-row-hoverCard hover background
--tbw-color-selectionSelected card background
--tbw-color-header-fgLabel text color
--tbw-color-accentSelection indicator color
/* Custom card styling */
tbw-grid[data-responsive] .data-grid-row {
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
margin-bottom: 8px;
}
/* Hide specific columns in card mode */
tbw-grid[data-responsive] .cell[data-field="createdAt"],
tbw-grid[data-responsive] .cell[data-field="updatedAt"] {
display: none;
}
AttributeElementDescription
data-responsivetbw-gridPresent when in responsive (card) mode
data-responsive-animatetbw-gridPresent when animations are enabled
data-header.cellColumn header text for CSS ::before
data-responsive-hidden.cellMarks cells hidden via hiddenColumns
data-responsive-value-only.cellMarks cells showing value only (no label)
PropertyDefaultDescription
--tbw-responsive-duration200msAnimation duration for mode transitions

In responsive mode, the visual layout is inverted - cells are stacked vertically within each card. The plugin automatically adjusts keyboard navigation to match this layout:

KeyTable ModeResponsive Mode
Previous rowPrevious field (within card), wraps to previous card
Next rowNext field (within card), wraps to next card
Previous columnPrevious card (same field)
Next columnNext card (same field)
TabNext cell, wraps to next rowSame behavior
EnterStart editingSame behavior
EscapeCancel editingSame behavior

When using cardRenderer (Phase 2), the grid’s built-in keyboard navigation is disabled for arrow keys. Implementors should handle navigation within their custom card content via their own event handlers.

// Grid in a resizable panel
features: {
responsive: {
breakpoint: 400,
hiddenColumns: ['createdAt', 'updatedAt'], // Hide dates in card mode
},
},
// Responsive grid for mobile/tablet
features: {
responsive: {
breakpoint: 768,
hideHeader: true,
},
},
// Small widget in dashboard
features: {
responsive: {
breakpoint: 300,
hiddenColumns: ['email', 'phone', 'address'],
},
},
// Gracefully degrade as container shrinks
features: {
responsive: {
breakpoints: [
{ maxWidth: 900, hiddenColumns: ['startDate'] },
{ maxWidth: 700, hiddenColumns: ['startDate', 'email'] },
{ maxWidth: 500, cardLayout: true },
],
},
},

For advanced card layouts with avatars, badges, or custom grouped fields, use the cardRenderer option:

features: {
responsive: {
breakpoint: 600,
cardRenderer: (row, rowIndex) => {
const card = document.createElement('div');
card.className = 'employee-card';
card.innerHTML = `
<div class="avatar">${row.name[0]}</div>
<div class="info">
<div class="name">${row.name}</div>
<div class="meta">${row.department} · $${row.salary.toLocaleString()}</div>
<div class="email">${row.email}</div>
</div>
`;
return card;
},
},
},
cardRenderer: (row: T, rowIndex: number) => HTMLElement
ParameterTypeDescription
rowTThe row data object
rowIndexnumberIndex of the row in the data array
ReturnsHTMLElementThe element to render as the card content

When using cardRenderer, you can control card height:

// Fixed height (better for virtualization with large datasets)
features: {
responsive: {
breakpoint: 600,
cardRowHeight: 80, // 80px per card
cardRenderer: (row) => { /* ... */ },
},
},
// Auto height (default - cards size to content)
features: {
responsive: {
breakpoint: 600,
cardRowHeight: 'auto',
cardRenderer: (row) => { /* ... */ },
},
},

When using a custom cardRenderer, the grid’s built-in arrow key navigation is disabled. This allows you to implement your own navigation within the card content.

Standard keyboard shortcuts still work:

  • Tab - Move between cards
  • Enter - Triggers cell-activate event
  • Escape - Standard escape handling
  • Selection — Row and cell selection
  • Theming — CSS custom properties for styling
AI assistants: For complete API documentation, implementation guides, and code examples for this library, see https://raw.githubusercontent.com/OysteinAmundsen/toolbox/main/llms-full.txt