SpreadsheetGrid
ガイド

テーマとスタイリング

ダークテーマ(light / dark / auto)、密度(density)、CSS トークンによるカスタマイズ

見た目のカスタマイズは 3 段階あります: ① theme / density prop によるプリセット切替、② CSS カスタムプロパティ(--ssg-*)によるトークン上書き、③ className / classNames / cellClassName などのクラスフックです。③ はすべて string | { className, style } を受けるため、StyleX の stylex.props() の戻り値をそのまま渡せます(後述)。

デモ

ToolbarRows: 5 / 5Columns: 3 / 3Sort: なし
#
1
2
3
4
5
Rows: 5 / 5Columns: 3 / 3
Active: なしSelection: なしCells: 0 / Rows: 0Cols: 0

テーマ(ダークモード)

<SpreadsheetGrid theme="auto" ... />  // 'light'(既定) | 'dark' | 'auto'
  • 'auto' は OS / ブラウザの prefers-color-scheme に追従します。SSR ではいったん light で描画され、ハイドレーション後に追従します。
  • Tailwind / Mantine / HeroUI などのクラスベース dark 運用では、ページの実テーマと prefers-color-scheme が一致しないことがあります。その場合は利用側カラースキームフックの解決値を 'light' | 'dark' で渡してください。
  • テーマはグリッド本体だけでなく、ポップオーバー / メニュー / パネル / ツールチップ / ドラッグゴーストにも一括適用されます。

密度(density)

<SpreadsheetGrid density="compact" ... />  // 'standard'(既定) | 'compact' | 'comfortable'

行高(40 / 32 / 48px)・セル padding・アイコン寸法・文字の相対サイズが一括で切り替わります。rowHeight / headerHeight の明示指定はプリセットより優先されます。

CSS トークンによるカスタマイズ

デザイントークンは特異度ゼロ(:where(.ssg-root))で定義されているため、通常のクラス 1 つで確実に上書きできます。

.my-grid {
  --ssg-accent: #7c3aed;          /* アクセント(選択枠など) */
  --ssg-header-bg: #f8fafc;       /* ヘッダー背景 */
  --ssg-invalid: #dc2626;         /* バリデーション NG の色 */
  --ssg-note-indicator: #f59e0b;  /* セルのメモ(cellNote)の印 */
  --ssg-cf-bar: rgba(52, 97, 201, 0.26); /* 条件付き書式のデータバー(--ssg-cf-seq-* / --ssg-cf-div-* / --ssg-cf-good 等も) */
  --ssg-tooltip-bg: #1e293b;      /* ツールチップ背景 */
}
<SpreadsheetGrid className="my-grid" ... />

テーマビルダー

テーマビルダーで、アクセント / ヘッダー背景 / セル背景 / 文字色 / 罫線 / 外枠 / 角丸 / 密度 / 文字サイズを実物のグリッドで動かしながら調整できます。派生色(hover / 選択 / 読み取り専用 / パネル)は自動で埋まり、生成された CSS 変数をコピーして貼るだけです。プリセット(既定 / Mantine 風 / HeroUI 風 / 業務グレー / Excel 風 / ダーク)から始められ、状態は URL に載るのでチームで共有できます。

条件付きセルスタイル

{
  key: 'qty', title: '数量', width: 110,
  cellClassName: ({ value }) =>
    typeof value === 'number' && value < 10 ? 'my-warn-cell' : undefined,
}
/* 基底クラスにチェーンして特異度で勝たせる(import 順に依存しない) */
.ssg-body-cell.my-warn-cell {
  background-color: #fff7ed;
}

行ホバーや readonly などの状態と組み合わせるときは、公開契約の状態クラス(.ssg-body-cell--row-hovered / .ssg-body-cell--readonly 等)と連結できます。一覧は SpreadsheetGrid props の「スタイリング用の状態クラス」を参照してください。

パーツ別スロット(classNames)と { className, style } 形

classNames はパーツごとに class を差し込むスロット表です(root / toolbar / statusBar / headerRow / headerCell / rowHeaderCell / cornerCell / bodyRow / bodyCell / groupRow / groupCell / detailBand / detailCard / iconButton / popover / menuItem / tooltip / dragGhost / checkbox / cellEditor / emptyState / filterChipBar / errorBar / scrollHint / activeCellOverlay / selectionOverlay。付与先の一覧は props リファレンス)。

classNames.* / cellClassName / getRowClassName / detailRow.className の値は GridSlotProps = string | { className?: string; style?: CSSProperties } です。文字列は従来どおり class、オブジェクト形は class に加えて style をその要素へインラインで付与します。ルート要素には style prop も使えます。

<SpreadsheetGrid
  style={{ maxWidth: 960 }}
  classNames={{
    root: 'my-grid',                                       // 文字列
    bodyCell: { className: 'tabular', style: { fontVariantNumeric: 'tabular-nums' } },
    popover: { style: { '--ssg-accent': '#7c3aed' } },     // ポータル(body 直下)にもトークンを渡せる
  }}
  getRowClassName={(row) => (row.archived ? { style: { opacity: 0.5 } } : undefined)}
/>
  • グリッドが位置決めに使う座標 / 寸法(left / top / width / height / transform 等)はグリッド側が後勝ちで上書きするため、スロットの style でレイアウトは壊せません。
  • グリッドの高さは style ではなく height prop で指定します(ルートの style={{ height }} だけではスクロール領域の高さが決まらず、バーが切れることがあります)。詳しくは サイズ(高さ) を参照してください。
  • インライン style は状態クラス(選択 / ホバー等の背景)にも勝ちます。状態で切り替えたい装飾は className 側で行ってください。
  • classNames はレンダー毎に新しいオブジェクトを渡して構いません(内容の署名で memo され、行 / ヘッダーの再レンダーは増えません)。
  • tooltip / dragGhost は命令的 DOM のため、style の数値に単位は付きません('12px' のように文字列で)。

StyleX との併用

StyleX(@stylexjs/stylex)はそのまま併用できます。reset を持たず、生成クラスは接頭辞 x なので基底 CSS と衝突しません。stylex.props(...) は { className, style } を返すため、上記のスロットへそのまま渡せます(StyleX の動的スタイルは style 側の CSS 変数で届くので、この形が必要です)。

import * as stylex from '@stylexjs/stylex';
import { vars } from './tokens.stylex';   // stylex.defineVars({ accent: '#7c3aed', danger: '#dc2626' })

const s = stylex.create({
  grid: { '--ssg-accent': vars.accent, borderRadius: 8 },   // グリッドのトークンへ橋渡し
  cell: { fontVariantNumeric: 'tabular-nums' },
  negative: { color: vars.danger },
  width: (w: number) => ({ maxWidth: w }),                  // 動的スタイル
});

<SpreadsheetGrid
  classNames={{ root: stylex.props(s.grid), bodyCell: stylex.props(s.cell) }}
  columns={[
    {
      key: 'amount',
      title: '金額',
      cellClassName: (ctx) => stylex.props(typeof ctx.value === 'number' && ctx.value < 0 && s.negative, s.width(120)),
    },
  ]}
/>
  • StyleX は子孫セレクタを書けない(要素自身のクラスでしか装飾できない)ため、スロットに無い内部要素(ポップオーバー内のボタン / 入力欄等)は CSS トークン(--ssg-*)で調整してください。
  • StyleX の atomic クラスとグリッドの基底クラスは同じ特異度 (0,1,0) です。同じプロパティを指定した場合は後に読み込まれた方が勝つため、グリッド CSS を先に・StyleX の出力 CSS を後に読み込むか、style.layer.css(グリッド全体を @layer ssg-base に入れる)を使ってください。StyleX 側で useLayers を有効にしている場合は、未レイヤーのグリッド CSS が常に勝ってしまうため style.layer.css が必須です。
  • Solid など class 属性のフレームワーク向けには StyleX に stylex.attrs() があります(将来のアダプタで同じスロット規則を使う予定です)。

Tailwind との共存(レイヤー運用、style.layer.css)はインストールを参照してください。

On this page