Sheetwrite paints cells on a canvas, so styling has two layers: the Theme
(colors, fonts, and geometry the renderer uses) and CellStyle (per-cell and
per-column formatting). A small stylesheet provides the host container chrome and
CSS custom properties.
Theme
The Theme controls the canvas paint. Pass a Partial<Theme> as the theme
option, or call grid.setTheme(partial) at runtime; either is merged over the
defaults.
Note: the shipped styles.css declares different values for the search/highlight
CSS custom properties (#fff47580, #fbbc04, #e8f0fe99); on a host that
imports the stylesheet those win over DEFAULT_THEME per the resolution order
below.
Override the search-match colors at runtime via setTheme:
resolveThemeFromCss(host) reads CSS custom properties from the host's computed
style into a partial theme, and opts.theme wins last.
Two runtime methods with different semantics:
grid.setTheme(partial) — imperative patch: merges into the accumulated
base theme and repaints. Use for incremental tweaks.
grid.replaceTheme(partial | undefined) — option-level replacement:
re-runs the construction-time resolution above with the new value;
undefined restores the CSS-variable/default resolution. The framework
adapters call this for their theme prop, so the prop is authoritative:
removing it (or dropping a field) restores defaults instead of leaving stale
merged values behind.
grid.getEffectiveTheme() returns the effective (post-zoom) theme currently
painting.
grid.getTheme() returns a copy of the base theme, before zoom scaling.
After setTheme or replaceTheme changes the base theme, the Grid emits
theme-change with a copy of the new theme. Use it to keep host controls in
step with the canvas. The built-in toolbar uses it: its text and fill colour
swatches show the theme's fg and bg until the user picks a colour.
Tailwind works through the same seam: set --sheetwrite-* custom properties on
the host with arbitrary-property utilities (e.g.
class="[--sheetwrite-bg:#0b0b0c] [--sheetwrite-fg:#e7e7e7]") or a @layer
rule; construction and replaceTheme(undefined) both pick them up from the
computed style.
Import the stylesheet once. It styles the host container (the grid adds the
.sheetwrite class to your host element), declares the --sheetwrite-* defaults,
and ships a dark variant under [data-theme="dark"] or a .dark ancestor class
(the Tailwind convention):
Partial example
import"@sheetwrite/core/styles.css";
resolveThemeFromCss maps these custom properties onto Theme fields:
CSS custom property
Theme field
--sheetwrite-bg
bg
--sheetwrite-fg
fg
--sheetwrite-grid-line
gridLine
--sheetwrite-header-bg
headerBg
--sheetwrite-header-fg
headerFg
--sheetwrite-selection
selection
--sheetwrite-selection-border
selectionBorder
--sheetwrite-row-height
rowHeight (parsed as a number)
--sheetwrite-search-match
searchMatch
--sheetwrite-search-active
searchActiveMatch
--sheetwrite-highlight
highlight
--sheetwrite-font
font (a / line-height segment is stripped for the canvas)
headerHeight and rowHeaderWidth have no CSS variable — set them through the
theme option. An explicit --sheetwrite-* value set on the .sheetwrite
element itself still wins over either dark selector (same-element beats
ancestor).
Widget and context-menu styling
The stylesheet exposes widget variables in addition to the canvas theme
mapping:
CSS custom property
Widget use
Fallback
--sheetwrite-widget-border
Borders and separators for toolbar/find/menu chrome
--sheetwrite-grid-line
--sheetwrite-menu-hover
Context-menu item hover background
--sheetwrite-grid-line
--sheetwrite-menu-disabled-opacity
Disabled context-menu row opacity
0.5
The bundled menu uses the public .sheetwrite-context-menu container,
.sheetwrite-context-menu-item row, .sheetwrite-context-menu-item-disabled
state, .sheetwrite-context-menu-shortcut hint, and
.sheetwrite-context-menu-sep separator classes. Scope overrides beneath the
grid host rather than replacing positioning, visibility, or disabled behavior:
Context menu theme hooks
.sheetwrite {
--sheetwrite-widget-border: #334155;
--sheetwrite-menu-hover: #1e293b;
--sheetwrite-menu-disabled-opacity: 0.45;
}
.sheetwrite.sheetwrite-context-menu-item {
font-weight: 500;
}
The recommended runtime-theming pattern drives both layers together: toggle
data-theme on a wrapper so the surrounding CSS-variable chrome flips, and
call setTheme so the canvas repaints.
CellStyle formats individual cells. It appears in three places:
as the style on a set patch — formatting for that one specific cell;
as Column.cellStyle — a column-wide default the renderer paints under every
body cell of the column;
as Column.headerStyle — the style applied to that column's header cell.
Column.cellStyle is a default, not a lock: a per-cell set style overrides the
column default for that cell, so a cell paints with its column's cellStyle until
its own patch supplies styling.
A numeric/date column can carry an Excel-style numberFormat code and an explicit
BCP 47 numberLocale. The same UTC serial value and locale are used by the
canvas renderer, auto-fit measurement, and exported formatter:
formatNumber(value, code?, locale?) is exported from @sheetwrite/core. It is
a cached renderer for the supported Excel-style families:
Code
Example output
(none)
1234.5 → locale-default 1,234.5
0
1234.5 → 1235
0.00
1234.5 → 1234.50
#,##0.00
1234.5 → 1,234.50
0%
0.5 → 50%
$#,##0.00
1234.5 → $1,234.50
0.00E+00
12345 → 1.23E+04
yyyy-mm-dd
serial 45351 → 2024-02-29
mmm d, yyyy h:mm:ss AM/PM
UTC serial → Jul 4, 2024 3:06:07 PM
0.00;[Red](0.00);"none";"value: "@
positive / negative / zero / text sections
Numeric formats support fixed decimals, optional grouping, percent scaling,
scientific exponents with explicit width, quoted/escaped literals, and currency
prefixes/suffixes. Up to four semicolon sections select positive, negative, zero,
and text output; color directives such as [Red] are accepted as formatting
metadata but do not change canvas text color.
Date/time formats support numeric and named month/day fields, 12/24-hour time,
minutes, seconds, and AM/PM. Month/minute disambiguation follows neighboring time
tokens. UTC fields make date rendering deterministic across host timezones.
Locale-specific separators come from numberLocale, not ambient browser locale.
Fractions, elapsed-time bracket tokens, and fractional seconds are not supported.
Formula errors are explicit sentinels such as #DIV/0! and #CYCLE!; they are
not non-finite numbers hidden by the formatter. See
Formulas.
Native boolean values use the normal cell style model, render as uppercase
TRUE/FALSE, and default to centered alignment unless CellStyle.align
overrides it.
See also
Configuration for theme, renderers, and the toolbar color controls.
Concepts for the styled row-0 field-header pattern.
interface Theme
interface ThemeinterfaceThemeinterfaceThemeinterfaceThemeinterfaceThemeResolved canvas colors, typography, and geometry used for painting.API reference →Theme.font: stringinterfaceTheme {font:string;}interfaceTheme {font:string;}Canvas font shorthand used for unstyled cells.API reference →Theme.bg: stringinterfaceTheme {bg:string;}interfaceTheme {bg:string;}CSS color painted behind body cells.API reference →Theme.fg: stringinterfaceTheme {fg:string;}interfaceTheme {fg:string;}CSS color used for unstyled cell text.API reference →Theme.gridLine: stringinterfaceTheme {gridLine:string;}interfaceTheme {gridLine:string;}CSS color used for cell grid lines.API reference →Theme.headerBg: stringinterfaceTheme {headerBg:string;}interfaceTheme {headerBg:string;}CSS color painted behind column and row headers.API reference →Theme.headerFg: stringinterfaceTheme {headerFg:string;}interfaceTheme {headerFg:string;}CSS color used for column letters and row numbers.API reference →Theme.selection: stringinterfaceTheme {selection:string;}interfaceTheme {selection:string;}CSS color painted over the selected region.API reference →Theme.selectionBorder: stringinterfaceTheme {selectionBorder:string;}interfaceTheme {selectionBorder:string;}CSS color used for the active selection outline.API reference →Theme.rowHeight: numberinterfaceTheme {rowHeight:number;}interfaceTheme {rowHeight:number;}Default data-row height in unzoomed CSS pixels.API reference →Theme.headerHeight: numberinterfaceTheme {headerHeight:number;}interfaceTheme {headerHeight:number;}Column-header height in unzoomed CSS pixels.API reference →Theme.rowHeaderWidth: numberinterfaceTheme {rowHeaderWidth:number;}interfaceTheme {rowHeaderWidth:number;}Width of the left row-number gutter (0 hides it).API reference →Theme.searchMatch: stringinterfaceTheme {searchMatch:string;}interfaceTheme {searchMatch:string;}Fill behind a search match.API reference →Theme.searchActiveMatch: stringinterfaceTheme {searchActiveMatch:string;}interfaceTheme {searchActiveMatch:string;}Fill/outline for the active (current) search match.API reference →Theme.highlight: stringinterfaceTheme {highlight:string;}interfaceTheme {highlight:string;}Fill for cells highlighted via Grid.highlightCells.API reference →const grid: Gridconstgrid:Gridconstgrid:GridThe imperative core grid this controller owns.API reference →Grid.setTheme(theme: Partial<Theme>): voidGrid.setTheme(theme: Partial<Theme>): voidGrid.setTheme(theme: Partial<Theme>): voidImperative patch: merge theme into the accumulated base theme.API reference →searchMatch?: string | undefinedsearchMatch?: string |undefinedsearchMatch?: string |undefinedFill behind a search match.API reference →searchActiveMatch?: string | undefinedsearchActiveMatch?: string |undefinedsearchActiveMatch?: string |undefinedFill/outline for the active (current) search match.API reference →const swatch: HTMLElementconstswatch:HTMLElementconstswatch:HTMLElementvar document: Documentvar document:Documentvar document:Documentwindow.document returns a reference to the document contained in the window. MDN ReferenceParentNode.querySelector<HTMLElement>(selectors: string): HTMLElement | null (+4 overloads)ParentNode.querySelector<HTMLElement>(selectors: string): HTMLElement |null (+4 overloads)ParentNode.querySelector<HTMLElement>(selectors: string): HTMLElement |null (+4 overloads)Returns the first element that is a descendant of node that matches selectors. MDN Referenceinterface HTMLElementinterfaceHTMLElementinterfaceHTMLElementThe HTMLElement interface represents any HTML element. MDN ReferenceElementCSSInlineStyle.style: CSSStyleDeclarationinterfaceElementCSSInlineStyle {style:CSSStyleDeclaration;}interfaceElementCSSInlineStyle {style:CSSStyleDeclaration;}MDN ReferenceAPI reference →CSSStyleDeclaration.background: stringinterfaceCSSStyleDeclaration {background:string;}interfaceCSSStyleDeclaration {background:string;}MDN ReferenceGrid.getTheme(): ThemeGrid.getTheme(): ThemeGrid.getTheme(): ThemeResolved base theme, before zoom scaling. The returned object is a copy.API reference →Grid.on<"theme-change">(evt: "theme-change", fn: (e: {
theme: Theme;
}) => void): () => voidGrid.on<"theme-change">(evt: "theme-change", fn: (e: {theme:Theme;}) =>void): () =>voidGrid.on<"theme-change">(evt: "theme-change", fn: (e: {theme:Theme;}) =>void): () =>voidtheme: Themelet theme:Themelet theme:ThemeOverrides merged over the default theme and host CSS custom properties.API reference →type Partial<T> = { [P in keyof T]?: T[P] | undefined; }typePartial<T> = { [PinkeyofT]?:T[P] |undefined };typePartial<T> = {[PinkeyofT]?:T[P] |undefined;};Make all properties in T optionalinterface ThemeinterfaceThemeinterfaceThemeResolved canvas colors, typography, and geometry used for painting.API reference →bg?: string | undefinedbg?: string |undefinedbg?: string |undefinedCSS color painted behind body cells.API reference →fg?: string | undefinedfg?: string |undefinedfg?: string |undefinedCSS color used for unstyled cell text.API reference →gridLine?: string | undefinedgridLine?: string |undefinedgridLine?: string |undefinedCSS color used for cell grid lines.API reference →headerBg?: string | undefinedheaderBg?: string |undefinedheaderBg?: string |undefinedCSS color painted behind column and row headers.API reference →headerFg?: string | undefinedheaderFg?: string |undefinedheaderFg?: string |undefinedCSS color used for column letters and row numbers.API reference →selection?: string | undefinedselection?: string |undefinedselection?: string |undefinedCSS color painted over the selected region.API reference →selectionBorder?: string | undefinedselectionBorder?: string |undefinedselectionBorder?: string |undefinedCSS color used for the active selection outline.API reference →const DARK: Partial<Theme>constDARK:Partial<Theme>constDARK:Partial<Theme>API reference →const darkBtn: HTMLButtonElementconstdarkBtn:HTMLButtonElementconstdarkBtn:HTMLButtonElementHTMLButtonElement.addEventListener<"click">(type: "click", listener: (this: HTMLButtonElement, ev: PointerEvent) => any, options?: boolean | AddEventListenerOptions): void (+1 overload)HTMLButtonElement.addEventListener<"click">(type: "click", listener: (this:HTMLButtonElement, ev:PointerEvent) => any, options?: boolean | AddEventListenerOptions): void (+1 overload)HTMLButtonElement.addEventListener<"click">(type: "click", listener: (this:HTMLButtonElement, ev:PointerEvent) => any, options?: boolean | AddEventListenerOptions): void (+1 overload)The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target. MDN Reference The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target. MDN Referenceconst stage: HTMLElementconststage:HTMLElementconststage:HTMLElementElement.setAttribute(qualifiedName: string, value: string): voidElement.setAttribute(qualifiedName: string, value: string): voidElement.setAttribute(qualifiedName: string, value: string): voidThe setAttribute() method of the Element interface sets the value of an attribute on the specified element. MDN Referenceinterface CellStyle
interface CellStyleinterfaceCellStyleinterfaceCellStyleinterfaceCellStyleinterfaceCellStyleSerializable formatting applied to a cell or used as a column default.API reference →CellStyle.bold?: boolean | undefinedinterfaceCellStyle {bold?:boolean|undefined;}interfaceCellStyle {bold?:boolean|undefined;}Uses the bold variant of the theme font.API reference →CellStyle.italic?: boolean | undefinedinterfaceCellStyle {italic?:boolean|undefined;}interfaceCellStyle {italic?:boolean|undefined;}Uses the italic variant of the theme font.API reference →CellStyle.underline?: boolean | undefinedinterfaceCellStyle {underline?:boolean|undefined;}interfaceCellStyle {underline?:boolean|undefined;}Draws a line beneath each rendered text run.API reference →CellStyle.strikethrough?: boolean | undefinedinterfaceCellStyle {strikethrough?:boolean|undefined;}interfaceCellStyle {strikethrough?:boolean|undefined;}Draws a line through each rendered text run.API reference →CellStyle.fontSize?: number | undefinedinterfaceCellStyle {fontSize?:number|undefined;}interfaceCellStyle {fontSize?:number|undefined;}Font size in unzoomed CSS pixels; zoom is applied during painting.API reference →CellStyle.color?: string | undefinedinterfaceCellStyle {color?:string|undefined;}interfaceCellStyle {color?:string|undefined;}hex color, e.g. "#111111"API reference →CellStyle.backgroundColor?: string | undefinedinterfaceCellStyle {backgroundColor?:string|undefined;}interfaceCellStyle {backgroundColor?:string|undefined;}hex color, e.g. "#ffffff"API reference →CellStyle.align?: "left" | "center" | "right" | undefinedinterfaceCellStyle {align?:"left"|"center"|"right"|undefined;}interfaceCellStyle {align?:|"left"|"center"|"right"|undefined;}Horizontal placement of cell text within its column.API reference →CellStyle.wrap?: boolean | undefinedinterfaceCellStyle {wrap?:boolean|undefined;}interfaceCellStyle {wrap?:boolean|undefined;}Wraps text within the cell width; row auto-fit accounts for the resulting line count.API reference →CellStyle.border?: CellBorders | undefinedinterfaceCellStyle {border?:CellBorders|undefined;}interfaceCellStyle {border?:CellBorders|undefined;}Border overrides for the cell's individual sides.API reference →interface CellBordersinterfaceCellBordersinterfaceCellBordersPer-side borders; all applies to any side not given its own border.API reference →Grid.store: StoreinterfaceGrid {store:Store;}interfaceGrid {store:Store;}Store.applyTransaction(tx: Transaction, options?: TransactionApplicationOptions): ApplyTransactionResultStore.applyTransaction(tx: Transaction, options?: TransactionApplicationOptions): ApplyTransactionResultStore.applyTransaction(tx: Transaction, options?: TransactionApplicationOptions): ApplyTransactionResultApply a low-level storage transaction. This bypasses Grid read-only checks and Grid undo/redo history. Use Grid.applyTransaction for normal host-driven edits. Queued and flushed at a barrier — never reentrant.API reference →Transaction.patches: DocumentOp[]interfaceTransaction {patches:DocumentOp[];}interfaceTransaction {patches:DocumentOp[];}Ordered document operations submitted as one store commit.API reference →addr: CellAddresslet addr:CellAddresslet addr:CellAddressAPI reference →CellAddress.sheet: stringinterfaceCellAddress {sheet:string;}interfaceCellAddress {sheet:string;}Sheet identity used by the simple data-first adapter. Defaults to sheet1.API reference →CellAddress.row: numberinterfaceCellAddress {row:number;}interfaceCellAddress {row:number;}API reference →CellAddress.col: numberinterfaceCellAddress {col:number;}interfaceCellAddress {col:number;}API reference →value: CellValuelet value:CellValuelet value:CellValueAPI reference →value: CellScalarlet value:CellScalarlet value:CellScalarAPI reference →style?: CellStyle | undefinedstyle?: CellStyle |undefinedstyle?: CellStyle |undefinedOptional override merged over the deterministic blue/underline link style.API reference →CellStyle.align?: CellAlign | undefinedinterfaceCellStyle {align?:CellAlign|undefined;}interfaceCellStyle {align?:CellAlign|undefined;}Horizontal placement of cell text within its column.API reference →interface CellBorders
interface CellBordersinterfaceCellBordersinterfaceCellBordersinterfaceCellBordersinterfaceCellBordersPer-side borders; all applies to any side not given its own border.API reference →CellBorders.all?: CellBorder | undefinedinterfaceCellBorders {all?:CellBorder|undefined;}interfaceCellBorders {all?:CellBorder|undefined;}interface CellBorderinterfaceCellBorderinterfaceCellBorderAPI reference →CellBorders.top?: CellBorder | undefinedinterfaceCellBorders {top?:CellBorder|undefined;}interfaceCellBorders {top?:CellBorder|undefined;}CellBorders.right?: CellBorder | undefinedinterfaceCellBorders {right?:CellBorder|undefined;}interfaceCellBorders {right?:CellBorder|undefined;}CellBorders.bottom?: CellBorder | undefinedinterfaceCellBorders {bottom?:CellBorder|undefined;}interfaceCellBorders {bottom?:CellBorder|undefined;}CellBorders.left?: CellBorder | undefinedinterfaceCellBorders {left?:CellBorder|undefined;}interfaceCellBorders {left?:CellBorder|undefined;}CellBorder.color?: string | undefinedinterfaceCellBorder {color?:string|undefined;}interfaceCellBorder {color?:string|undefined;}hex color, e.g. "#111111"API reference →CellBorder.width?: number | undefinedinterfaceCellBorder {width?:number|undefined;}interfaceCellBorder {width?:number|undefined;}Unzoomed column width in CSS pixels.API reference →CellBorder.style?: "solid" | "dashed" | "dotted" | undefinedinterfaceCellBorder {style?:"solid"|"dashed"|"dotted"|undefined;}interfaceCellBorder {style?:|"solid"|"dashed"|"dotted"|undefined;}Optional override merged over the deterministic blue/underline link style.API reference →const style: CellStyleconststyle:CellStyleconststyle:CellStyleOptional override merged over the deterministic blue/underline link style.API reference →interface CellStyleinterfaceCellStyleinterfaceCellStyleSerializable formatting applied to a cell or used as a column default.API reference →const columns: Column[]constcolumns:Column[]constcolumns:Column[]Exact column runs represented by every returned row.API reference →interface ColumninterfaceColumninterfaceColumnSchema and default presentation for one workbook column.API reference →Column.key: stringinterfaceColumn {key:string;}interfaceColumn {key:string;}Non-empty key, unique within the sheet, used to map input and datasource values.API reference →Column.header: stringinterfaceColumn {header:string;}interfaceColumn {header:string;}Schema label used by table exports and data-grid presentation headers.API reference →Column.width: numberinterfaceColumn {width:number;}interfaceColumn {width:number;}Unzoomed column width in CSS pixels.API reference →Column.type: CellFormatinterfaceColumn {type:CellFormat;}interfaceColumn {type:CellFormat;}Controls cell input parsing and default value formatting for this column.API reference →Column.numberFormat?: string | undefinedinterfaceColumn {numberFormat?:string|undefined;}interfaceColumn {numberFormat?:string|undefined;}Excel number-format code, e.g. "#,##0.00"API reference →Column.numberLocale?: string | undefinedinterfaceColumn {numberLocale?:string|undefined;}interfaceColumn {numberLocale?:string|undefined;}Explicit BCP 47 locale for separators; omitted keeps the deterministic default.API reference →