Skip to content

This guide shows application styling recipes. The stable selector inventory lives in the Grid DOM state contract.

Import the package stylesheet once:

import "@sapporta/grid/index.css";

Then put the grid inside a container with stable dimensions:

<div className="task-grid-shell">
<GridRuntimeProvider runtime={runtime}>
<GridLevel path={rootPath("tasks")} />
</GridRuntimeProvider>
</div>
.task-grid-shell {
block-size: min(42rem, 80vh);
border: 1px solid var(--rule);
min-inline-size: 0;
}

Stable dimensions matter because focus rings, editors, hover state, and loading state should not resize the page around the user.

Styling follows the interaction config. If the grid does not enable active rows, row selection, or cell range selection, GridCore will not render those states.

createGridRuntime({
schema,
dataSource,
interaction: CELL_GRID_WITH_ACTIVE_ROW,
});

Common choices:

GoalInteraction shape
Spreadsheet focus and editingActive cell
Spreadsheet range selectionActive cell + cell selection
Highlight the row under the active cellActive cell + active row
Bulk actions on checked rowsActive cell or row cursor + row selection
Master-detail panel that follows navigationActive row + selected rows that follow it

See Interaction configuration and presets for the available interaction models and keyboard behavior.

For a direct GridCore composition, put the class on a wrapper or supply it through your grid chrome:

<div className="projectGrid">
<GridRuntimeProvider runtime={runtime}>
<GridLevel path={rootPath("projects")} />
</GridRuntimeProvider>
</div>

For Sapporta framework table-grid root styling, see the framework table-grid reference.

GridCore marks the current row with data-row-active="true":

.projectGrid [data-grid-part="row"][data-row-active="true"] {
background: #eef4ff;
}
.projectGrid [data-grid-part="row"][data-row-active="true"]::before {
background: #2563eb;
content: "";
inset-block: 0;
inset-inline-start: 0;
pointer-events: none;
position: absolute;
width: 2px;
}

GridCore marks selected rows with data-row-selected="true":

.projectGrid [data-grid-part="row"][data-row-selected="true"] {
background: #eef4ff;
}
.projectGrid
[data-grid-part="row"][data-row-active="true"][data-row-selected="true"] {
background: #dbeafe;
}

Use aria-selected="true" for accessibility-aware selectors when that is more appropriate, but prefer data-row-selected for visual chrome.

GridCore marks the active cell with data-cell-status="focus":

.projectGrid [data-grid-part="cell"][data-cell-status="focus"] {
background: white;
box-shadow: inset 0 0 0 2px #2563eb;
z-index: 3;
}

Cells in the selected range are marked with data-cell-status="in-selection":

.projectGrid [data-grid-part="cell"][data-cell-status="in-selection"] {
background: #eaf1ff;
}

When focus moves to another nested level, the inactive level root has data-active="false". Use that root state when inactive cell chrome should look different:

.projectGrid
[data-grid-part="root"][data-active="false"]
[data-grid-part="cell"][data-cell-status="focus"] {
background: #f3f4f6;
box-shadow: inset 0 0 0 1px #9ca3af;
}

When an editor is open, the cell is marked with data-cell-status="editing":

.projectGrid [data-grid-part="cell"][data-cell-status="editing"] {
background: white;
box-shadow: inset 0 0 0 2px #16a34a;
z-index: 3;
}

Editing state should usually take precedence over range selection for the same cell.

Hover is for discoverability. Keep it lower priority than active or selected row states:

.projectGrid
[data-grid-part="row"]:not([data-row-selected="true"]):not(
[data-row-active="true"]
):hover {
background: #f7f7f7;
}

Phantom rows are rows created by insertion flows before they are saved:

.projectGrid [data-grid-part="row"][data-row-kind="phantom"] {
background: #fff7ed;
}

Do not recreate focus, selection, or active-row state in CSS classes managed outside the runtime. The DOM data attributes already follow the configured interaction preset.

Typecheck the example and exercise its visible loading, ready, interaction, and failure states. Use only public @sapporta/grid export paths. Continue with the Grid DOM state contract.