Hi,
Thank you for your question. Regarding whether to use theme switching or defaultStyle (backColor and border) for dark mode — these two approaches operate at different layers of SpreadJS and are not interchangeable. Here is a breakdown to help you decide.
WHAT EACH APPROACH CONTROLS
Theme switching (excel2016black.css + designer.dark.min.css) styles the entire UI: ribbon, toolbar, formula bar, row/column headers, sheet tabs, grid lines, scrollbars, and cell backgrounds. It covers the complete application.
defaultStyle (backColor, border) only controls the default cell background and borders. It has no effect on the Designer chrome — ribbon, toolbar, headers, tabs, or scrollbars.
If you rely solely on defaultStyle, your ribbon and toolbar will remain light while cells go dark, resulting in a broken half-and-half appearance. The theme CSS files are what SpreadJS uses to define dark mode end-to-end.
WHEN defaultStyle IS USEFUL
There are cases where you want cells to differ from the theme’s default — for example, a custom brand background color or specific border colors. In that scenario, you apply it on top of the theme swap, not instead of it. Below is an example:
async function setThemeMode(newMode) {
const config = THEMES[newMode];
// Step 1: Swap both CSS layers (designer chrome + grid runtime)
await Promise.all([
swapStylesheet("runtime-theme", config.runtime),
swapStylesheet("designer-theme", config.designer),
]);
// Step 2: Optionally fine-tune default cell appearance
if (newMode === "dark") {
applyDarkDefaultStyle();
} else {
clearDefaultStyle();
}
spread.refresh();
designer.refresh();
currentMode = newMode;
}
function applyDarkDefaultStyle() {
const darkStyle = new GC.Spread.Sheets.Style();
darkStyle.backColor = "#1a1a2e";
darkStyle.foreColor = "#e0e0e0";
darkStyle.borderLeft = new GC.Spread.Sheets.LineBorder("#3a3a5c", GC.Spread.Sheets.LineStyle.thin);
darkStyle.borderRight = new GC.Spread.Sheets.LineBorder("#3a3a5c", GC.Spread.Sheets.LineStyle.thin);
darkStyle.borderTop = new GC.Spread.Sheets.LineBorder("#3a3a5c", GC.Spread.Sheets.LineStyle.thin);
darkStyle.borderBottom = new GC.Spread.Sheets.LineBorder("#3a3a5c", GC.Spread.Sheets.LineStyle.thin);
const sheetCount = spread.getSheetCount();
for (let i = 0; i < sheetCount; i++) {
spread.getSheet(i).setDefaultStyle(darkStyle);
}
}
function clearDefaultStyle() {
const sheetCount = spread.getSheetCount();
for (let i = 0; i < sheetCount; i++) {
spread.getSheet(i).setDefaultStyle(null);
}
}
Refer to the attached sample: https://jscodemine.mescius.io/share/FryVemor1USsjkhK1eozqg/?defaultOpen={"OpenedFileName"%3A["%2Findex.html"%2C"%2Fsrc%2Fapp.js"]%2C"ActiveFile"%3A"%2Fsrc%2Fapp.js"}
IMPORTANT CAVEATS ON defaultStyle
It only sets the floor. Any cell that already has an explicit backColor or border applied will not be affected. defaultStyle is the default, not an override. If your workbook contains cells with hardcoded white backgrounds (common in Excel-imported data), those cells will remain white even after setDefaultStyle is called. Walking the used range to clear per-cell styles is possible but expensive on large sheets and is not recommended as a dark mode mechanism.
SUMMARY
Always start with theme CSS swapping. This gives you the complete dark UI — Designer chrome and grid — in one step.
Add setDefaultStyle only if your design requires cell colors or borders that differ from the theme’s built-in dark defaults.
Do not use defaultStyle alone as a substitute for theme switching. The Designer chrome will remain unstyled.
Regards,
Priyam