Spreadsheet
Edit submission data with kspreadsheet and a Material enhancement.
kspreadsheet with its original colors by default and a separate, optional Material variant. This standalone example uses fictional submission data, two frozen columns, editable cells, and jSuites editors.
Default — kspreadsheet colors
Double-click text, dropdown, or date cells to edit; click a checkbox to toggle it. Cell dropdowns and right-click context menus share the Material 3 Expressive look in both color variants: rounded surfaces, tertiary selection highlights, and an inset focus indicator for arrow-key navigation. Edits stay in this page until reset or reload. The grid scrolls horizontally on smaller screens.
Double-click a cell to edit. Scroll sideways to see the frozen columns.
<div id="submission-spreadsheet" class="spreadsheet-demo"></div>
<div class="spreadsheet-demo-actions">
<button id="spreadsheet-get" type="button" class="btn filled">Get data</button>
<button id="spreadsheet-update" type="button" class="btn tonal">
Mark first shot ready
</button>
<button id="spreadsheet-reset" type="button" class="btn outlined">
Reset sample
</button>
</div>
<p id="spreadsheet-status" class="spreadsheet-demo-status" role="status">
Double-click a cell to edit. Scroll sideways to see the frozen columns.
</p>
<div id="spreadsheet-data" class="code-card" hidden></div>Material variant
Opt in with spreadsheet-material on the wrapper. Headers and cells use the page’s navigation and surface colors, with fine grid lines, rounded tabs, and soft selection colors in light and dark mode. Both examples remain editable and keep their data separately.
An independent sheet with a softer Material palette.
<!-- Opt in on a wrapper; leave it off to keep kspreadsheet colors. -->
<div class="spreadsheet-material">
<div id="material-spreadsheet" class="spreadsheet-demo"></div>
</div>
<p id="material-spreadsheet-status" class="spreadsheet-demo-status" role="status">
An independent sheet with a softer Material palette.
</p>Install and initialize
Shared spreadsheet styles are loaded once in src/style.scss. Install with pnpm add kspreadsheet@2.10.0 jsuites@5.13.5. The default grid keeps vendor colors; load spreadsheet_editors.scss after them for Material dropdown menus and readable editing text. For the optional Material variant, import the enhancement styles from kmaterialize/sass/enhancement as shown below. Both stylesheets are shipped by the library.
// Add to the main Materialize SCSS entry, once.
@import 'kmaterialize/sass/materialize.scss';
@import 'jsuites/dist/jsuites.css';
@import 'kspreadsheet/dist/jspreadsheet.css';
@import 'kspreadsheet/dist/jspreadsheet.themes.css';
// Optional palette, scoped to .spreadsheet-material.
@import 'kmaterialize/sass/enhancement/jspreadsheet_materializecss.scss';
// Shared dropdown styling and readable cell editors.
@import 'kmaterialize/sass/enhancement/spreadsheet_editors.scss';Complete example — data, columns, events, and buttons
import { CodeCard } from 'kmaterialize';
import jspreadsheet, { type CellValue, type SpreadsheetOptions } from 'kspreadsheet';
// Shared spreadsheet and enhancement styles are loaded by style.scss.
import './spreadsheet-demo.scss';
// Fictional demo data. Column order is the same in data, columns, and getData().
const initialData: CellValue[][] = [
[
'SQ010_SH010',
'Lighting',
'Alex',
'In progress',
'2026-09-15',
false,
'Balance the window light',
],
[
'SQ010_SH020',
'Compositing',
'Sam',
'Ready for review',
'2026-09-16',
true,
'Version 12 is ready',
],
[
'SQ020_SH010',
'Animation',
'Morgan',
'Approved',
'2026-09-17',
true,
'Approved timing',
],
[
'SQ020_SH020',
'Lighting',
'Taylor',
'Not started',
'2026-09-18',
false,
'Awaiting animation',
],
];
const copyRows = () => initialData.map((row) => [...row]);
const host = document.querySelector<HTMLDivElement>('#submission-spreadsheet')!;
const status = document.querySelector<HTMLElement>('#spreadsheet-status')!;
const output = document.querySelector<HTMLElement>('#spreadsheet-data')!;
const outputCard = CodeCard.init(output, { language: 'json' });
const options: SpreadsheetOptions = {
tabs: true,
toolbar: false,
worksheets: [
{
worksheetName: 'Submission review',
data: copyRows(),
columns: [
{ type: 'text', title: 'Shot', width: 150 },
{
type: 'dropdown',
title: 'Department',
width: 140,
source: ['Animation', 'Lighting', 'Compositing'],
},
{ type: 'text', title: 'Artist', width: 130 },
{
type: 'dropdown',
title: 'Status',
width: 170,
source: ['Not started', 'In progress', 'Ready for review', 'Approved'],
},
{
type: 'calendar',
title: 'Due date',
width: 130,
options: { format: 'YYYY-MM-DD' },
},
{ type: 'checkbox', title: 'Ready', width: 80 },
{ type: 'text', title: 'Review notes', width: 260, wordWrap: true },
],
freezeColumns: 2,
tableOverflow: true,
tableWidth: '100%',
tableHeight: '360px',
minDimensions: [7, 8],
allowComments: true,
},
],
onchange: (_worksheet, _cell, x, y) => {
status.textContent = `Updated column ${Number(x) + 1}, row ${Number(y) + 1}. Changes stay in this demo.`;
},
};
// Keep the first worksheet returned by the constructor.
const worksheet = jspreadsheet(host, options)[0];
document.querySelector('#spreadsheet-get')!.addEventListener('click', () => {
void outputCard.update({ code: JSON.stringify(worksheet.getData(), null, 2) });
output.hidden = false;
status.textContent = 'Current worksheet data shown below.';
});
document.querySelector('#spreadsheet-reset')!.addEventListener('click', () => {
worksheet.setData(copyRows());
output.hidden = true;
status.textContent = 'Sample data restored.';
});
document.querySelector('#spreadsheet-update')!.addEventListener('click', () => {
worksheet.setValue('D1', 'Ready for review');
});
// A separate worksheet demonstrates the opt-in Material palette.
const materialHost = document.querySelector<HTMLDivElement>('#material-spreadsheet')!;
const materialWorksheet = jspreadsheet(materialHost, {
...options,
worksheets: options.worksheets!.map((sheet) => ({ ...sheet, data: copyRows() })),
onchange: () => {
document.querySelector('#material-spreadsheet-status')!.textContent =
'Material example updated. Changes stay in this demo.';
},
})[0];
// Release worksheet listeners when Vite replaces this module during development.
if (import.meta.hot)
import.meta.hot.dispose(() => {
jspreadsheet.destroy(worksheet.parent.el);
jspreadsheet.destroy(materialWorksheet.parent.el);
});Data interface and methods
The constructor takes a worksheets array and returns worksheet instances. Use [0] for the first sheet. Each data row follows the column order; blank spare rows also appear in getData(). Calendar values use year-month-day strings, and checkbox values are booleans.
import type { CellValue, SpreadsheetOptions, WorksheetOptions } from 'kspreadsheet';
// Matrix rows match the columns array from left to right.
type SubmissionRow = [
shot: string,
department: string,
artist: string,
status: string,
dueDate: string,
ready: boolean,
notes: string,
];
const rows: SubmissionRow[] = [
[
'SQ010_SH010',
'Lighting',
'Alex',
'In progress',
'2026-09-15',
false,
'Check lighting',
],
];
// WorksheetOptions: data, columns, dimensions, frozen columns, editors.
// SpreadsheetOptions: worksheets, tabs, toolbar, and shared event callbacks.
// CellValue[][] is the library's general matrix type.// Worksheet methods — not the array returned by jspreadsheet().
const rows = worksheet.getData();
worksheet.setData([
['SQ030_SH010', 'Lighting', 'Alex', 'In progress', '2026-09-20', false, 'New review'],
]);
worksheet.setValue('D1', 'Ready for review');
const status = worksheet.getValue('D1');
// Optional local CSV download when the user requests it:
// await worksheet.download(true, true, 'csv');
// When removing the view:
// jspreadsheet.destroy(worksheet.parent.el);onchange(worksheet, cell, column, row, newValue, oldValue) reports edits with zero-based column and row indexes. Connect this event to your application's persistence layer when needed.
Material enhancement
The imported styles preserve editor font size and color, raise active dropdown cells above frozen columns, round popup corners, and style worksheet tabs. The companion jSuites enhancement covers dropdowns, calendars, context menus, and color pickers. All enhancement rules are scoped under spreadsheet-material; default grids retain kspreadsheet’s colors. The Material variant follows the site’s light and dark themes.
Spreadsheet enhancement SCSS
// Optional Material variant. Wrap the spreadsheet host with this class.
// Unwrapped worksheets retain the vendor's colors, including in dark mode.
.spreadsheet-material {
// Inherit the page palette so the grid matches surrounding navigation surfaces.
/// /// ///
/// Enhancement - Crazy Content
///
/// Jspreadsheet ft. Materialize
///
/// @package kzarshenas/crazyphp
/// @author kekefreedog <kevin.zarshenas@gmail.com>
/// @copyright 2022-2023 Kévin Zarshenas
/// /// ///
/// Parameters
///////////////////////////////////////////////////////
///
// Vendor theme CSS is imported by the TypeScript entry before this enhancement.
/* jSuites ft. Materialize - reconcile the floating jSuites widgets (dropdown, calendar,
contextmenu, color picker) used by kSpreadsheet with the Materialize look & feel */
@import './jsuites_materializecss.scss';
/* Variable for repeated values */
$font-size-default: 13px;
$font-color-default: #444;
$border-radius-default: 6px;
$shadow-default:
0 4px 5px 0 rgba(0, 0, 0, 0.14),
0 1px 10px 0 rgba(0, 0, 0, 0.12),
0 2px 4px -1px rgba(0, 0, 0, 0.3);
/// Styles
///////////////////////////////////////////////////////
///
// Assign @material/material-color-utilities color
.jss_worksheet {
// Explicit token mapping for the standalone documentation integration.
--jss_border_color: color-mix(
in srgb,
var(--md-sys-color-outline-variant) 60%,
var(--md-sys-color-surface)
);
--jss_border_color_highlighted: var(--md-sys-color-primary);
--jss_header_background: var(--md-sys-color-surface-variant);
--jss_header_color: var(--md-sys-color-on-surface-variant);
--jss_header_background_highlighted: color-mix(
in srgb,
var(--md-sys-color-surface) 88%,
var(--md-sys-color-primary)
);
--jss_header_color_highlighted: var(--md-sys-color-on-surface);
--jss_content_background: var(--md-sys-color-background);
--jss_content_color: var(--md-sys-color-on-background);
--jss_content_background_highlighted: var(--md-sys-color-surface-variant);
--jss_content_color_highlighted: var(--md-sys-color-on-surface-variant);
--jss_selection: color-mix(
in srgb,
var(--md-sys-color-surface) 90%,
var(--md-sys-color-primary)
);
--jss_cursor: color-mix(
in srgb,
var(--md-sys-color-surface) 84%,
var(--md-sys-color-primary)
);
--jss_menu_background: var(--md-sys-color-surface);
--jss_menu_color: var(--md-sys-color-on-surface);
--jss_menu_box_shadow: 0 2px 6px rgba(0, 0, 0, 0.15); // fallback shadow
--jss_menu_background_highlighted: var(--md-sys-color-surface-variant);
--jss_menu_color_highlighted: var(--md-sys-color-on-surface-variant);
// Font text
--jss_editor_font_size: 13px;
--jss_editor_icon_size: 24px;
--jss_font_size: 13px;
--jss_contextmenu_font_size: 13px;
--jss_about_font_size: 13px;
// Body
> tbody {
// row
> tr {
// Data
> td {
// Drop down
&.jss_dropdown {
// Set background position
background-position: bottom right 5px;
}
}
}
}
// Editor
.editor {
// Rich text
.jss_richtext {
// Break line
white-space: break-spaces;
}
}
}
/**
* Nested Align
*/
.jss_content {
// T head
thead {
// Nested list
tr.jss_nested {
// Item
td {
// Left case
&[align='left'] {
text-align: left;
}
// Left right
&[align='right'] {
text-align: right;
}
// Left center
&[align='center'] {
text-align: center;
}
}
}
}
/* TD */
td {
/**
* Inline editor input - conflict fix
*
* Materialize's global `input { font-family: Roboto; font-size: 100% }` (and the lack of
* any font/colour on the vendor's editor input) made a cell's text jump size and colour
* the moment it entered edit mode. Inherit the cell's own metrics and colour instead of
* hard-coding them, so what you type looks identical to what the cell renders - in every
* theme, whatever the column's font size.
*/
input,
textarea {
/* Font - match the cell exactly (no forced 13px) */
font-family: inherit !important;
font-size: inherit !important;
line-height: inherit !important;
/* Colour - follow the cell / theme (no forced black) */
color: inherit !important;
}
}
}
/**
* Dropdown vs frozen column - stacking fix
*
* A frozen column is `position: sticky` with its own stacking context, so it paints over the
* `.jdropdown-container` popup that jSuites renders inside the cell being edited (the popup sits
* at the grid's base level and gets covered). Lift the edited cell above every frozen cell so its
* dropdown is never hidden: non-frozen cells get a fresh stacking context via `position:relative`,
* while frozen cells keep their `position:sticky` and just take the raised z-index.
*/
.jss_worksheet td.editor {
/* Above frozen cells (which top out around z-index 4) */
z-index: 30 !important;
}
.jss_worksheet td.editor:not(.jss_freezed) {
/* Establish a stacking context so the absolute dropdown popup rises above frozen columns */
position: relative;
}
/**
* In-cell dropdown popup - rounding
*
* kSpreadsheet renders the dropdown list inside the worksheet, where jspreadsheet.css only sets
* `margin-top` (square corners). Round it to 4px like the standalone jSuites dropdown, and clip
* the content so the hovered/selected item highlight follows the rounded corners.
*/
.jss_worksheet .jdropdown-container {
/* Round */
border-radius: 4px;
/* Clip the item highlight to the rounded corners */
overflow: hidden;
}
/* Tabs */
.jtabs {
/* Container */
.jtabs-headers-container {
/* Header */
.jtabs-headers {
// Dimension
overflow-x: auto;
// Selected
.jtabs-selected {
// Color
background-color: color-mix(
in srgb,
transparent,
var(--md-sys-color-primary) 18%
);
color: var(--md-sys-color-primary);
}
// Not selected
div:not(.jtabs-selected) {
// Color
background-color: color-mix(
in srgb,
var(--md-sys-color-surface),
var(--md-sys-color-surface-tint) 17%
);
color: var(--md-sys-color-on-background);
}
/* Div */
div:not(.jtabs-border) {
/* Dimension */
height: 48px;
/* Color */
color: var(--md-sys-color-on-surface-variant);
/* Position */
padding: 12px 20px 12px 20px !important;
}
// Border
.jtabs-border {
// Color
background-color: var(--md-sys-color-primary) !important;
// Dimension
height: 3px;
border-radius: 3px 3px 0 0;
}
}
}
}
// The vendor frozen-cell rule is more specific than its theme stylesheet.
// Keep sticky data cells opaque, but use the current Material surface in dark mode.
.jss_worksheet > tbody > tr > td.jss_freezed:not(:first-child),
.jss_worksheet > tbody > tr > td.jss_row_freezed:not(:first-child) {
background-color: var(--jss_content_background);
color: var(--jss_content_color);
&.highlight {
background-color: var(--jss_content_background_highlighted);
color: var(--jss_content_color_highlighted);
}
}
.jss_worksheet input[type='checkbox'] {
accent-color: var(--md-sys-color-primary);
}
// Quiet headers and tabs; reserve the primary color for active states.
border: 1px solid var(--md-sys-color-outline-variant);
border-radius: 16px;
padding: 8px;
background: var(--md-sys-color-surface);
.jss_worksheet > thead > tr > td {
font-weight: 500;
height: 36px;
}
.jss_worksheet > tbody > tr > td {
height: 34px;
}
.jtabs .jtabs-headers-container .jtabs-headers {
div:not(.jtabs-selected):not(.jtabs-border) {
background: transparent;
color: var(--md-sys-color-on-surface-variant);
}
.jtabs-selected {
background: color-mix(
in srgb,
var(--md-sys-color-primary) 8%,
var(--md-sys-color-surface)
);
color: var(--md-sys-color-primary);
border-radius: 8px 8px 0 0;
}
}
// Paint selection on cells with an opaque, softly tinted surface.
// The overlay draws only the outline, so it never washes out the text.
.jss_worksheet > tbody > tr > td.highlight:not(:first-child) {
background-color: var(--jss_selection);
color: var(--md-sys-color-on-surface);
}
.jss_worksheet > tbody > tr > td.highlight-selected:not(:first-child) {
background-color: var(--jss_cursor);
color: var(--md-sys-color-on-surface);
}
.jss_border_main {
background: transparent;
border-color: var(--md-sys-color-primary);
opacity: 1;
}
}jSuites enhancement SCSS
/// /// ///
/// Enhancement - Crazy Content
///
/// jSuites ft. Materialize
///
/// Reconciles the floating widgets shipped by jSuites (https://github.com/jsuites/jsuites)
/// with the Materialize look & feel (https://github.com/materializecss/materialize) so the
/// dropdowns, calendars, context menus and color pickers rendered by kSpreadsheet
/// (https://github.com/kekefreedog/kspreadsheet) read like native Materialize surfaces.
///
/// Two hard conflicts are resolved here:
/// 1. Materialize styles every raw `<select>` (width:100%, height:3rem, border) which breaks
/// the inline month / year selectors of the jSuites calendar.
/// 2. Materialize colors every `<a>` with its link blue which bleeds into context-menu items.
///
/// Everything below only re-themes colors, typography, radius and elevation. The structural
/// properties kSpreadsheet relies on (position, transform, z-index, dimensions) are left
/// untouched, and the in-cell editors under `.jss_worksheet` keep the overrides already forced
/// by jspreadsheet.css — so kSpreadsheet is never compromised.
///
/// @package kzarshenas/crazyphp
/// @author kekefreedog <kevin.zarshenas@gmail.com>
/// @copyright 2022-2025 Kévin Zarshenas
/// /// ///
/// Parameters
///////////////////////////////////////////////////////
///
/* Variable for repeated values */
$jsuites-font-family: 'Roboto', sans-serif;
$jsuites-font-size: 14px;
$jsuites-radius: 4px;
$jsuites-elevation:
0 2px 2px rgba(0, 0, 0, 0.14),
0 3px 1px -2px rgba(0, 0, 0, 0.12),
0 1px 5px rgba(0, 0, 0, 0.2);
/// Styles
///////////////////////////////////////////////////////
///
/**
* Shared accent
*
* jSuites exposes its accent through CSS variables — remap them onto the Materialize palette so
* the "done" / "close" / "reset" affordances match the rest of the UI.
*/
.jdropdown,
.jcalendar,
.jcontextmenu,
.jcolor,
.jslider,
.jpicker {
// Accent
--jactive-color: var(--md-sys-color-primary);
--jbutton-color: var(--md-sys-color-primary);
// Font
font-family: $jsuites-font-family;
}
/**
* Dropdown
*
* The `.jdropdown-container` popup is appended to the body, outside `.jss_worksheet`, which is
* why it escapes the kSpreadsheet overrides and needs its own Materialize skin here.
*/
.jdropdown-container {
// Surface
background-color: var(--md-sys-color-surface);
color: var(--md-sys-color-on-surface);
// Border
border: none;
border-radius: $jsuites-radius;
// Elevation
box-shadow: $jsuites-elevation;
// Overflow
overflow: hidden;
// Content
.jdropdown-content {
// Surface
background-color: var(--md-sys-color-surface);
}
// Item
.jdropdown-item {
// Font
font-family: $jsuites-font-family;
font-size: $jsuites-font-size;
// Color
color: var(--md-sys-color-on-surface);
// Dimension
padding: 10px 16px;
// Title
.jdropdown-title {
// Color
color: var(--md-sys-color-on-surface-variant);
}
}
// Group name
.jdropdown-group-name {
// Font
font-family: $jsuites-font-family;
font-size: 12px;
font-weight: 500;
// Color
color: var(--md-sys-color-primary);
}
// Search bar
.jdropdown-container-header input {
// Font
font-family: $jsuites-font-family;
font-size: $jsuites-font-size;
// Surface
background-color: var(--md-sys-color-surface);
color: var(--md-sys-color-on-surface);
// Border
border: none;
border-bottom: 1px solid var(--md-sys-color-outline-variant);
border-radius: 0;
}
// Close
.jdropdown-close {
// Color
color: var(--md-sys-color-primary);
}
}
/**
* Dropdown - states
*
* Replace the hard-coded jSuites blue (#1f93ff) hover / selection with the Materialize
* secondary-container / primary tokens.
*/
.jdropdown-default,
.jdropdown-picker,
.jdropdown-searchbar,
.jdropdown-list {
// Hover
.jdropdown-item:hover {
// Surface
background-color: color-mix(in srgb, transparent, var(--md-sys-color-primary) 8%);
color: var(--md-sys-color-on-surface);
}
// Keyboard cursor
.jdropdown-cursor {
// Surface
background-color: color-mix(in srgb, transparent, var(--md-sys-color-primary) 12%);
}
// Selected
.jdropdown-selected {
// Surface
background-color: var(--md-sys-color-secondary-container);
color: var(--md-sys-color-on-secondary-container, var(--md-sys-color-on-surface));
}
}
/**
* Calendar
*/
.jcalendar {
// Content surface
.jcalendar-content {
// Surface
background-color: var(--md-sys-color-surface);
color: var(--md-sys-color-on-surface);
// Border
border-radius: $jsuites-radius;
// Elevation
box-shadow: $jsuites-elevation;
}
// Header
.jcalendar-header {
// Color
color: var(--md-sys-color-on-surface);
}
// Weekday
.jcalendar-weekday {
// Surface
background-color: transparent;
// Color
color: var(--md-sys-color-on-surface-variant);
// Font
font-family: $jsuites-font-family;
font-weight: 500;
}
// Table cells
.jcalendar-table td {
// Font
font-family: $jsuites-font-family;
}
// Selected day
.jcalendar-selected {
// Surface
background-color: var(--md-sys-color-primary) !important;
color: var(--md-sys-color-on-primary) !important;
// Border
border-radius: 50%;
}
// Confirm / reset controls
.jcalendar-confirm,
.jcalendar-reset {
// Color
color: var(--md-sys-color-primary);
}
/**
* Conflict fix - the inline month / year `<select>` gets crushed by Materialize's global
* `select { width:100%; height:3rem; border; }`. Restore the compact jSuites layout.
*/
.jcalendar-container select {
// Reset the Materialize global select
width: auto !important;
height: auto !important;
padding: 4px !important;
border: 0 !important;
border-radius: 0 !important;
// Surface
background-color: transparent !important;
color: var(--md-sys-color-on-surface) !important;
// Font
font-family: $jsuites-font-family;
display: inline-block !important;
}
}
/**
* Context menu
*/
.jcontextmenu {
// Surface
background-color: var(--md-sys-color-surface);
color: var(--md-sys-color-on-surface);
// Border
border: none;
border-radius: $jsuites-radius;
// Elevation
box-shadow: $jsuites-elevation;
// Font
font-family: $jsuites-font-family;
// Item
> div {
// Font
font-size: 13px;
font-family: $jsuites-font-family;
// Icon
&::before {
// Color
color: var(--md-sys-color-on-surface-variant);
}
// Hover / highlight
&:hover {
// Surface - a distinct Material container so the row clearly reads as highlighted,
// paired below with its guaranteed-contrast on-* text token.
background-color: var(--md-sys-color-secondary-container);
// Label + icon stay readable on the highlight (contrast-safe pair)
a {
// Color
color: var(--md-sys-color-on-secondary-container) !important;
}
&::before {
// Color
color: var(--md-sys-color-on-secondary-container);
}
}
/**
* Conflict fix - Materialize colors every `<a>` with its link blue, which on the pale
* highlight left the label almost invisible. Force the menu label onto the surface text
* token so it is dark and readable in every theme (the hover state above swaps in the
* matching on-secondary-container pair).
*/
a {
// Color
color: var(--md-sys-color-on-surface) !important;
}
}
// Disabled
.jcontextmenu-disabled a,
.jcontextmenu-disabled::before {
// Color
color: color-mix(
in srgb,
transparent,
var(--md-sys-color-on-surface) 38%
) !important;
}
// Divisor
hr {
// Color
border-color: var(--md-sys-color-outline-variant);
}
}
/**
* Color picker
*/
.jcolor {
// Content surface
.jcolor-content {
// Surface
background-color: var(--md-sys-color-surface);
color: var(--md-sys-color-on-surface);
// Border
border-radius: $jsuites-radius;
// Elevation
box-shadow: $jsuites-elevation;
}
// Reset / close controls
.jcolor-reset,
.jcolor-close {
// Color
color: var(--md-sys-color-primary);
}
}Shared dropdown and editor SCSS
Import the library stylesheet after the vendor and optional Material styles. It supplies the shared Material 3 Expressive dropdown and context-menu appearance, including selected states and keyboard focus, plus readable cell editor text.
// Use the library's shared menu styles for both worksheet color variants.
@import 'kmaterialize/sass/enhancement/spreadsheet_editors.scss';Example layout SCSS
#spreadsheet-examples {
min-width: 0;
}
.spreadsheet-demo {
width: 100%;
min-width: 0;
margin-block: 24px;
}
.spreadsheet-demo .jss_container {
max-width: 100%;
}
.spreadsheet-demo-actions {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-block: 16px;
}
.spreadsheet-demo-status {
color: var(--md-sys-color-on-surface-variant);
font-size: 14px;
}
#spreadsheet-data {
max-height: 320px;
overflow: auto;
}