Skip to content

DOM and styling contract

View as Markdown

@sapporta/grid/index.css and public DOM state attributes. BaseGrid renders grid mechanics and stable DOM state. It does not own product visuals. Style rows, cells, nested levels, and editing state with data-grid-part, data-row-*, and data-cell-* selectors.

The Sapporta column preset supplies default admin chrome for grids that opt into columnPreset.chrome(). That preset may use CSS variables internally, but application overrides should target the DOM state attributes directly.

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

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

For React-owned BaseGrid runtimes, create the runtime with useGridRuntimeEffect as shown in the BaseGrid reference.

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 the Interactions reference for the interaction presets and keyboard behavior.

For a direct BaseGrid 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.

BaseGrid 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;
}

BaseGrid 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.

BaseGrid 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;
}
<div
data-grid-part="row"
data-row-kind="data"
data-row-active="true"
data-row-selected="true"
data-row-interaction-status="cursor-selected"
data-row-selectable="true"
aria-selected="true"
/>
AttributeMeaning
data-grid-part="row"Identifies a body row.
data-row-kindRow kind, such as data or phantom.
data-row-active="true"This row is the current row.
data-row-selected="true"This row is in the effective row selection.
data-row-interaction-statusCombined row cursor and row selection status.
data-row-selectableWhether row-operation selection can target this row.
aria-selected="true"Present when the row is selected.

Prefer data-row-active and data-row-selected for styling. Use data-row-interaction-status only when one combined status value is genuinely more convenient.

<div
role="gridcell"
data-grid-part="cell"
data-cell-status="focus"
data-col-id="name"
/>
AttributeMeaning
data-grid-part="cell"Identifies a body cell.
data-cell-status="focus"The active cell.
data-cell-status="in-selection"A cell in the selected range.
data-cell-status="editing"A cell with an open editor.
data-col-idColumn id.
<div
data-grid-part="root"
data-grid-path="projects"
data-grid-depth="0"
data-active="true"
/>
AttributeMeaning
data-grid-part="root"Identifies a grid level root.
data-grid-pathStable logical path for this level.
data-grid-depthNesting depth, with root at 0.
data-activeWhether this level owns the active cursor.

When multiple rules can apply, order application CSS from broad to specific:

  1. base row and phantom row
  2. hover row
  3. selected row
  4. active row
  5. active + selected row
  6. cell range
  7. active cell
  8. editing cell

The Sapporta preset follows the same visual order with low-specificity selectors, so normal application selectors such as .projectGrid [data-grid-part="row"][data-row-active="true"] override it.

Grid reference overview