Workspace
The reusable record-workspace system: toolbar, filters, grid, selection, details and states. Every list screen in the template is assembled from the components on this page — copy the markup as-is, no extra CSS required.
Page Toolbar
.page-toolbarTop control strip of a workspace: search, actions, density and view controls on one wrapping row. Put it inside a .card-header or directly above the grid. Bootstrap dependencies: none — it is a flex row, so gap-*, ms-auto and order-* utilities work on its children.
Compact — add .page-toolbar-compact on an ancestor to shrink every control in the strip.
Disabled & long content — buttons take the native disabled attribute; long labels wrap to the next row rather than overflowing.
Quick Filters
.quick-filtersInline filter pills with a result count. Use for the four or five filters people reach for most; anything longer belongs in a filter drawer. Set aria-pressed so screen readers announce the active state.
Filter Chips
.filter-chipsThe applied-filter summary. Each chip carries its own dismiss button; the container collapses to nothing when empty, so it can stay in the markup permanently.
Empty — an empty container renders nothing, no conditional markup needed.
Summary Strip & Summary Card
.summary-strip · .summary-cardHeadline figures above a grid. The strip is an eight-column grid that steps down to four, two and one across breakpoints. Tone comes from the global accent modifiers — .accent-primary, .accent-success, .accent-warning, .accent-danger, .accent-info, .accent-purple, .accent-teal — never from a page-level colour.
Record Grid
.record-grid-wrapper · .record-grid-table · .record-grid-rowThe workspace table. .record-grid-wrapper owns the horizontal scroll, .record-grid-table the layout, and each <tr> takes .record-grid-row. Sorting uses .record-sortable plus .is-sorted-asc / .is-sorted-desc; the first column can stick with .is-pinned; a chosen row takes .is-selected. Identity cells combine .record-identity, .record-avatar and .record-tag / .record-pill.
| Student | Class | Status | Attendance | Actions | |
|---|---|---|---|---|---|
Janet DanielAD1256589
|
III-A | Active | |||
Joann MichaelAD1256590
|
IV-B | Pending | |||
Kathleen Moreno — transferred from St. Andrews Senior SecondaryLong content truncates rather than widening the column
|
V-A | Suspended |
Loading — swap the grid for the global skeleton loader. There is one skeleton system template-wide; never write a per-page variant.
Empty — .workspace-empty-state is hidden until it gets .show, so it can sit in the markup permanently.
No records match these filters
Try widening the date range or clearing a filter chip.
Record Cards
.record-cards · .record-cardThe responsive counterpart of the grid — the same record shown as a card once the table is too wide for the viewport. Pair it with the view switcher, or let it take over below the grid's breakpoint.
Selection Toolbar
.selection-toolbarAppears when rows are selected — add .show to reveal it. Keep destructive actions last and announce the count with aria-live="polite".
Quick Menu .quick-menu
The dropdown panel behind a workspace action button.
Details Panel
.details-panel · .details-panel-block · .details-panel-rowThe expanded view of a record, either inline under its row (toggled by .details-toggle) or in an offcanvas. The panel is a grid of blocks; a page may override the column count, nothing else.
Personal
Academic
Contact
Workspace Footer & Pagination
.workspace-footer · .workspace-paginationCloses the workspace: result count and page-size selector on the left, pagination on the right. Wrap the control in <nav aria-label="Pagination"> and mark the current page with aria-current="page".
Upload Dropzone
.upload-dropzoneImport panel for bulk record uploads. Add .dragover while a file is over the target. Always pair it with a real <input type="file"> so keyboard users can browse.
Live Content
.live-contentThe counterpart to the skeleton loader: content inside .live-content stays hidden until the workspace root gains .is-loaded, so the two never flash at once.
<div class="students-workspace">
<div class="skeleton-loader">…</div>
<div class="live-content">…</div>
</div>
// when data arrives
root.classList.add("is-loaded");
Workspace Actions
.workspace-actionsA trailing group of related buttons — inside a card header, a row, or the details panel.
Component reference
| Component | Purpose | Modifiers & states | Notes |
|---|---|---|---|
.page-toolbar | Workspace control strip | .page-toolbar-compact | Flex row — Bootstrap spacing and order utilities apply |
.search-toolbar | Search field with icon and shortcut hint | .search-toolbar-icon, .search-toolbar-kbd | Wraps a Bootstrap .form-control |
.summary-strip / .summary-card | Headline figures above a grid | Any .accent-* | 8 → 4 → 2 → 1 columns by breakpoint |
.selection-toolbar | Bulk actions for selected rows | .show | Count needs aria-live="polite" |
.record-grid-wrapper | Horizontal scroll container | — | Owns the scroll; never set overflow on the table |
.record-grid-table | Workspace table | .record-sortable, .is-sorted-asc, .is-sorted-desc, .is-pinned | Column widths are page overrides |
.record-grid-row | Record row | .is-selected | Pair with .record-check |
.record-cards / .record-card | Card view of the same record | .record-card-head, -body, -actions | Responsive fallback for the grid |
.record-identity | Avatar plus name and sub-label | .record-identity-name, -sub | Truncates instead of widening the column |
.record-avatar | Record thumbnail | .record-avatar-presence | Accepts .accent-* for the presence tone |
.record-tag / .record-pill | Inline status marker | .record-pill-dot, any .accent-* | Use Bootstrap .badge for non-record contexts |
.filter-chips / .filter-chip-item | Applied-filter summary | .filter-chip-clear | Collapses when empty |
.quick-filters / .quick-filter | Inline filter pills | .active, .quick-filter-count, disabled | Set aria-pressed |
.quick-menu | Dropdown action panel | -head, -head-title, -head-sub, -icon, -list | Drop inside a Bootstrap .dropdown-menu |
.details-panel | Expanded record view | -block, -row, -photo, .details-toggle | Column count may be overridden per page |
.workspace-footer | Result count and page size | -info, -size | Sits directly under the grid |
.workspace-pagination | Page navigation | .workspace-page-button, -numbers, -gap, .active | Wrap in <nav aria-label>, mark aria-current="page" |
.workspace-button / .workspace-icon-button | Toolbar controls | .active, disabled, .workspace-button-icon | Icon-only buttons need aria-label |
.workspace-actions | Trailing button group | — | Use gap-* to adjust rhythm |
.density-toggle / .view-switcher | Row density and list/grid switch | .active | Use role="group" with a label |
.workspace-empty-state | No-results state | .show, .workspace-empty-art | Hidden until .show is added |
.skeleton-loader / .skeleton | Loading placeholder | -line, -thin, -avatar, -circle, -pill, -card, -btn, -stack | The template's only skeleton system |
.live-content | Real content, revealed on load | Root .is-loaded | Pairs with the skeleton loader |
.upload-dropzone | Bulk import target | .dragover, -title, -hint | Always back it with a real file input |
.id-card-preview | Printable ID card preview | .id-card-head, -identity, -info, -row, -qr, -barcode, -foot | Fixed aspect; scales with its container |
Every component above inherits colour, radius, shadow and motion from the global design tokens — --bs-* for the brand palette, --radius*, --shadow*, --transition, --accent* and --tint-*. None of them declares a colour of its own, so they follow the theme and dark mode automatically. Do not add page-level colour, spacing or typography overrides; restrict page SCSS to genuine layout differences.





