Excel Export Theming Implementation

How exportToExcel() produces a spreadsheet that matches the grid's active theme, and how custom themes declare their own export palette. This builds on the Export to Excel Implementation and the Themes Implementation; this document is the authoritative design intent.

Summary

The grid owns its export palette. A host no longer needs to re-type each theme's colours to style an export — exportToExcel() themes the sheet to match the active theme by default:

grid.exportToExcel();                          // colours match the active theme (default)
grid.exportToExcel({ themeStyle: 'auto' });   // same, explicit
grid.exportToExcel({ themeStyle: false });    // opt out: unstyled

Theming is on by default. Pass themeStyle: false to get the legacy unstyled sheet.

The palette descriptor

A VanillaGridExportPalette is a small object of bare Excel hex strings (RRGGBB / AARRGGBB, no leading #):

interface VanillaGridExportPalette {
  headerBg: string; headerFg: string; headerBorder: string;
  cellFg: string;   cellBg: string;   cellBorder: string;
  altBg?: string | null;   // alternating-row fill; null/absent ⇒ no banding
}

The grid converts a palette into the three static style objects the export understands (headerCellStyle, dataCellStyle, dataCellStyleAlt, in the format described in Export to Excel § Cell Styling) — a bold, vertically-centred header, solid fills, and uniform thin borders. Because they are static objects (not per-cell callbacks) they are resolved once per export: each is merged with every column's number format and alignment, giving a handful of entries in the workbook's cell-format table (one per column for the header, even rows and odd rows), and each cell simply references its column's entry.

CSS tokens (primary source)

Each src/vanilla-grid/themes/vn-grid-<name>.css declares the palette on .vn-grid-table-container, mirroring how --vn-grid-row-height is declared:

Token Palette field
--vn-grid-export-header-bg headerBg
--vn-grid-export-header-fg headerFg
--vn-grid-export-header-border headerBorder
--vn-grid-export-cell-fg cellFg
--vn-grid-export-cell-bg cellBg
--vn-grid-export-cell-border cellBorder
--vn-grid-export-alt-bg altBg (none/omit ⇒ no banding)

_readThemeExportPalette() (on VanillaGrid.prototype) reads all six mandatory tokens in one getComputedStyle(this.container) call — mirroring _readThemeRowHeight() — and returns null when any mandatory token is missing (so resolution falls through to the JS fallback). The shared themes/TEMPLATE-vn-grid-theme.css carries the same token block so new and custom themes inherit the contract.

Resolution precedence

exportToExcel() resolves the effective styling in resolveExportStyles() (exposed on window.VanillaGridExcelExportInternals for testing). Precedence is resolved per style slotheaderCellStyle, dataCellStyle, and dataCellStyleAlt each independently pick their own winner, so an explicit value for one slot does not suppress theming for the others:

  1. An explicit headerCellStyle / dataCellStyle / dataCellStyleAlt (whichever is present) → used as-is for that slot (a raw style object; existing behaviour).
  2. Otherwise, if themeStyle resolves to a palette, that slot falls back to the palette's built style for it:
    • themeStyle: 'auto' (the default when themeStyle is omitted) → resolve a palette:
      1. CSS tokens via _readThemeExportPalette(), else
      2. JS registry VanillaGridElement.getThemeExportStyle(themeName), else
      3. the grid's built-in fallback table for themeName, else
      4. the built-in default palette.
    • themeStyle is a palette object → build styles from it.
  3. Otherwise (themeStyle === false, or no palette resolved) → that slot stays unstyled.

This means { headerCellStyle: myStyle } (with themeStyle left at its 'auto' default) keeps your explicit header style and themes the data rows — it does not disable theming for the whole export the way an all-or-nothing "first hit wins" reading might suggest.

The active themeName is read from the host element via grid.container.closest('vn-grid')?.dataset.theme (set by the element's attributeChangedCallback), falling back to 'default'. The CSS-token path (2.i) works for both built-in and custom CSS themes without ever needing the theme name, so it covers most cases on its own.

headersOnly

exportToExcel({ headersOnly: true }) keeps the resolved header style but forces dataCellStyle/dataCellStyleAlt to null, so data cells carry no fill, border or font colour. Column-level numFmt/alignment still apply. The modifier is applied after a palette is chosen, so it composes with any themeStyle.

Custom-theme story

Three ways to give a custom theme an export palette, all parallel to how custom themes are registered:

API surface (additive)

Member Where Description
exportToExcel({ themeStyle?, headersOnly? }) VanillaGrid / <vn-grid> themeStyle: 'auto' (default) | VanillaGridExportPalette | false. headersOnly: boolean.
_readThemeExportPalette() VanillaGrid.prototype Reads --vn-grid-export-*; mirrors _readThemeRowHeight().
static registerThemeExportStyle(name, palette) VanillaGridElement Register/override a named theme's export palette.
static getThemeExportStyle(name) VanillaGridElement Lookup (returns null when unknown).

Migrating from the opt-in default

themeStyle used to default to unstyled (false); it now defaults to 'auto'. This is a deliberate breaking change to the default — the option itself, and its precedence rules, are unchanged: