@alaarab/ogrid-mcp 2.5.4 → 2.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +6 -51
  2. package/bundled-docs/api/README.md +11 -11
  3. package/bundled-docs/api/components-column-chooser.mdx +21 -21
  4. package/bundled-docs/api/components-column-header-filter.mdx +23 -23
  5. package/bundled-docs/api/components-datagrid-table.mdx +27 -27
  6. package/bundled-docs/api/components-pagination-controls.mdx +21 -21
  7. package/bundled-docs/api/components-sidebar.mdx +27 -27
  8. package/bundled-docs/api/components-status-bar.mdx +12 -12
  9. package/bundled-docs/api/js-api.mdx +13 -13
  10. package/bundled-docs/api/types.mdx +4 -4
  11. package/bundled-docs/features/column-chooser.mdx +4 -4
  12. package/bundled-docs/features/column-groups.mdx +4 -4
  13. package/bundled-docs/features/column-pinning.mdx +5 -5
  14. package/bundled-docs/features/column-reordering.mdx +5 -5
  15. package/bundled-docs/features/column-types.mdx +7 -7
  16. package/bundled-docs/features/context-menu.mdx +4 -4
  17. package/bundled-docs/features/csv-export.mdx +4 -4
  18. package/bundled-docs/features/editing.mdx +78 -68
  19. package/bundled-docs/features/filtering.mdx +83 -67
  20. package/bundled-docs/features/formulas.mdx +135 -139
  21. package/bundled-docs/features/grid-api.mdx +4 -4
  22. package/bundled-docs/features/keyboard-navigation.mdx +6 -6
  23. package/bundled-docs/features/mobile-touch.mdx +9 -9
  24. package/bundled-docs/features/pagination.mdx +4 -4
  25. package/bundled-docs/features/performance.mdx +97 -100
  26. package/bundled-docs/features/premium-inputs.mdx +579 -0
  27. package/bundled-docs/features/responsive-columns.mdx +1 -1
  28. package/bundled-docs/features/row-selection.mdx +8 -8
  29. package/bundled-docs/features/server-side-data.mdx +4 -4
  30. package/bundled-docs/features/sidebar.mdx +6 -6
  31. package/bundled-docs/features/sorting.mdx +87 -44
  32. package/bundled-docs/features/spreadsheet-selection.mdx +9 -9
  33. package/bundled-docs/features/status-bar.mdx +4 -4
  34. package/bundled-docs/features/toolbar.mdx +9 -9
  35. package/bundled-docs/features/virtual-scrolling.mdx +13 -13
  36. package/bundled-docs/getting-started/overview.mdx +9 -9
  37. package/bundled-docs/getting-started/quick-start.mdx +47 -47
  38. package/bundled-docs/getting-started/vanilla-js.mdx +98 -74
  39. package/bundled-docs/guides/accessibility.mdx +113 -421
  40. package/bundled-docs/guides/framework-showcase.mdx +98 -52
  41. package/bundled-docs/guides/mcp-live-testing.mdx +12 -12
  42. package/bundled-docs/guides/mcp.mdx +47 -46
  43. package/dist/esm/bridge-client.d.ts +3 -3
  44. package/dist/esm/index.js +9 -9
  45. package/package.json +3 -2
@@ -1,16 +1,18 @@
1
1
  ---
2
2
  sidebar_position: 3
3
3
  title: Quick Start
4
- description: Build a fully functional data grid in minutes
4
+ description: A sortable, filterable, editable data grid in under 50 lines
5
5
  ---
6
6
 
7
7
 
8
8
  # Quick Start
9
9
 
10
- Build a sortable, filterable, editable data grid in under 50 lines.
10
+ Here's a grid in 60 seconds — sorting, filtering, editing, pagination, and keyboard navigation, all working out of the box.
11
11
 
12
12
  ## Install
13
13
 
14
+ Pick your framework and UI library:
15
+
14
16
  <Tabs groupId="framework">
15
17
  <TabItem value="react" label="React" default>
16
18
 
@@ -88,7 +90,9 @@ npm install @alaarab/ogrid-js
88
90
  </TabItem>
89
91
  </Tabs>
90
92
 
91
- ## Full Example
93
+ ## Your first grid
94
+
95
+ This example renders an employee directory with sortable columns, a department filter, and an editable salary column. Copy it and it'll just work.
92
96
 
93
97
  <Tabs groupId="framework">
94
98
  <TabItem value="react" label="React" default>
@@ -145,11 +149,11 @@ export default function App() {
145
149
  ```
146
150
 
147
151
  :::tip Switching UI libraries
148
- The `OGrid` component has the same props across all React UI packages. To switch, just change the import:
152
+ The `OGrid` component has identical props across all React UI packages. To switch, change one import:
149
153
 
150
- - **Radix** (lightweight, default): `from '@alaarab/ogrid-react-radix'`
151
- - **Fluent UI** (Microsoft 365 / SPFx): `from '@alaarab/ogrid-react-fluent'`wrap in `<FluentProvider>`
152
- - **Material UI** (MUI v7): `from '@alaarab/ogrid-react-material'`wrap in `<ThemeProvider>`
154
+ - **Radix** (lightweight, no peer deps): `from '@alaarab/ogrid-react-radix'`
155
+ - **Fluent UI** (Microsoft 365 / SPFx): `from '@alaarab/ogrid-react-fluent'` - wrap in `<FluentProvider>`
156
+ - **Material UI** (MUI v7): `from '@alaarab/ogrid-react-material'` - wrap in `<ThemeProvider>`
153
157
  :::
154
158
 
155
159
  </TabItem>
@@ -203,7 +207,7 @@ export class EmployeeGridComponent {
203
207
  ```
204
208
 
205
209
  :::tip Switching UI libraries
206
- Same component API across Angular packages. To switch, just change the import:
210
+ All Angular packages share the same component API. Change one import:
207
211
 
208
212
  - **Radix (CDK)** (lightweight): `from '@alaarab/ogrid-angular-radix'`
209
213
  - **Angular Material**: `from '@alaarab/ogrid-angular-material'`
@@ -262,10 +266,10 @@ const gridProps = {
262
266
  ```
263
267
 
264
268
  :::tip Switching UI libraries
265
- Same component API across Vue packages. To switch, just change the import:
269
+ All Vue packages share the same API. Change one import:
266
270
 
267
271
  - **Radix (Headless UI)** (lightweight): `from '@alaarab/ogrid-vue-radix'`
268
- - **Vuetify**: `from '@alaarab/ogrid-vue-vuetify'`wrap in `<v-app>` for theming
272
+ - **Vuetify**: `from '@alaarab/ogrid-vue-vuetify'` - wrap in `<v-app>` for theming
269
273
  - **PrimeVue**: `from '@alaarab/ogrid-vue-primevue'`
270
274
  :::
271
275
 
@@ -307,31 +311,33 @@ const grid = new OGrid(document.getElementById('grid'), {
307
311
  ```
308
312
 
309
313
  :::tip
310
- The JS package uses a class-based imperative API. See the [Vanilla JS guide](./vanilla-js) for the full API including events, grid methods, and theming.
314
+ The JS package has a class-based imperative API. See the [Vanilla JS guide](./vanilla-js) for events, grid methods, and theming.
311
315
  :::
312
316
 
313
317
  </TabItem>
314
318
  </Tabs>
315
319
 
316
- ## What You Get
320
+ ## What you get for free
321
+
322
+ Paste those ~40 lines and you have:
317
323
 
318
- With just this code:
324
+ - **Sorting** click any column header, ascending/descending, shift-click for multi-sort
325
+ - **Filtering** — the Department column gets a multi-select dropdown filter
326
+ - **Inline editing** — double-click any salary cell to edit in place
327
+ - **Currency formatting** — `valueFormatter` formats numbers on display without affecting stored values
328
+ - **Pagination, keyboard nav, cell selection, status bar** — all there, no extra config
319
329
 
320
- - **Sorting** -- click column headers to sort ascending/descending
321
- - **Filtering** -- click the filter icon on Department to filter by value
322
- - **Inline editing** -- double-click any salary cell to edit
323
- - **Numeric formatting** -- `type: 'numeric'` right-aligns; `valueFormatter` displays currency
324
- - **Pagination, keyboard nav, cell selection, status bar** -- all built in
330
+ ## Three concepts worth knowing
325
331
 
326
- ## Key Concepts
332
+ | | Description |
333
+ |--|--|
334
+ | `IColumnDef<T>` | Describes a column. `columnId` must match a key on your row type (or use `valueGetter` for computed values). |
335
+ | `getRowId` | Returns a stable unique identifier per row. Used for selection, editing, and efficient re-renders. |
336
+ | `layoutMode` | `'fill'` (default) stretches the grid to fill its container. `'content'` sizes to the data. |
327
337
 
328
- | Concept | Description |
329
- |---------|-------------|
330
- | `IColumnDef<T>` | Column config. `columnId` matches a key on `T` (or use `valueGetter`). |
331
- | `getRowId` | Returns a unique `string \| number` per row for selection, editing, and re-rendering. |
332
- | `layoutMode` | `'fill'` (default) fills container; `'content'` sizes to content. |
338
+ ## Programmatic control
333
339
 
334
- ## Using the Grid API
340
+ Need to interact with the grid from outside? Use the API:
335
341
 
336
342
  <Tabs groupId="framework">
337
343
  <TabItem value="react" label="React" default>
@@ -343,8 +349,8 @@ function App() {
343
349
 
344
350
  return (
345
351
  <>
346
- <button onClick={() => console.log(gridRef.current?.getColumnState())}>
347
- Log State
352
+ <button onClick={() => gridRef.current?.exportToCsv('employees.csv')}>
353
+ Export CSV
348
354
  </button>
349
355
  <OGrid<Employee> ref={gridRef} columns={columns} data={data} getRowId={(row) => row.id} />
350
356
  </>
@@ -361,15 +367,15 @@ function App() {
361
367
  standalone: true,
362
368
  imports: [OGridComponent],
363
369
  template: `
364
- <button (click)="logState()">Log State</button>
370
+ <button (click)="exportCsv()">Export CSV</button>
365
371
  <ogrid [props]="gridProps" />
366
372
  `
367
373
  })
368
374
  export class AppComponent {
369
375
  constructor(private gridService: OGridService) {}
370
376
 
371
- logState() {
372
- console.log(this.gridService.getColumnState());
377
+ exportCsv() {
378
+ this.gridService.exportToCsv('employees.csv');
373
379
  }
374
380
 
375
381
  gridProps = { columns, data, getRowId: (row: any) => row.id };
@@ -382,15 +388,11 @@ export class AppComponent {
382
388
  ```vue
383
389
  <script setup lang="ts">
384
390
 
385
- const { getColumnState } = useOGrid({ columns, data, getRowId: (row) => row.id });
386
-
387
- function logState() {
388
- console.log(getColumnState());
389
- }
391
+ const { exportToCsv } = useOGrid({ columns, data, getRowId: (row) => row.id });
390
392
  </script>
391
393
 
392
394
  <template>
393
- <button @click="logState">Log State</button>
395
+ <button @click="exportToCsv('employees.csv')">Export CSV</button>
394
396
  <OGrid :gridProps="{ columns, data, getRowId: (row) => row.id }" />
395
397
  </template>
396
398
  ```
@@ -404,22 +406,20 @@ const grid = new OGrid(document.getElementById('grid'), {
404
406
  columns, data, getRowId: (row) => row.id,
405
407
  });
406
408
 
407
- // Programmatic control
408
- const api = grid.getApi();
409
- console.log(api.getColumnState());
410
- api.setRowData(newData);
409
+ document.getElementById('export-btn').addEventListener('click', () => {
410
+ grid.api.exportToCsv('employees.csv');
411
+ });
411
412
 
412
- // Cleanup
413
+ // Cleanup when done
413
414
  grid.destroy();
414
415
  ```
415
416
 
416
417
  </TabItem>
417
418
  </Tabs>
418
419
 
419
- ## Next Steps
420
+ ## What's next?
420
421
 
421
- - [Sorting](../features/sorting), [Filtering](../features/filtering), [Editing](../features/editing) -- core features
422
- - [Grid API](../api/grid-api) -- imperative methods
423
- - [Column Definitions](../api/column-def) -- all column options
424
- - [Controlled vs Uncontrolled](../guides/controlled-vs-uncontrolled) -- managing state externally
425
- - [Server-Side Data](../features/server-side-data) -- connect to REST APIs or GraphQL
422
+ - [Sorting](../features/sorting), [Filtering](../features/filtering), [Editing](../features/editing) dig into individual features
423
+ - [Server-Side Data](../features/server-side-data) connect to a REST API or GraphQL endpoint
424
+ - [Grid API](../api/js-api) full reference for programmatic control
425
+ - [Column Definitions](../api/types) every column option explained
@@ -1,13 +1,15 @@
1
1
  ---
2
2
  sidebar_position: 4
3
3
  title: Vanilla JS
4
- description: Use OGrid without React — pure JavaScript, class-based API
4
+ description: OGrid without a framework — pure JavaScript, class-based API
5
5
  ---
6
6
 
7
7
 
8
- # Vanilla JS Quick Start
8
+ # Vanilla JS
9
9
 
10
- OGrid's vanilla JS package provides the same features as the React packages with a class-based, imperative API. No React, no virtual DOM — just a container element and an options object.
10
+ You don't need React, Angular, or Vue to use OGrid. The `@alaarab/ogrid-js` package gives you a full-featured, sortable, filterable, editable data grid using a simple class-based API — just a container element and an options object.
11
+
12
+ It's a good fit if you're working on an internal tool, a dashboard that can't take on a framework, or you're embedding a grid inside an existing non-framework app.
11
13
 
12
14
  ## Install
13
15
 
@@ -15,57 +17,83 @@ OGrid's vanilla JS package provides the same features as the React packages with
15
17
  npm install @alaarab/ogrid-js
16
18
  ```
17
19
 
18
- ## Basic Example
20
+ Or drop it into an HTML file directly using a CDN (no build tools needed):
21
+
22
+ ```html
23
+ <script type="module">
24
+ import { OGrid } from 'https://cdn.jsdelivr.net/npm/@alaarab/ogrid-js/dist/esm/index.js';
25
+ </script>
26
+ ```
27
+
28
+ ## Build a real example
29
+
30
+ Let's make an order management dashboard. It tracks orders by status, amount, and customer — sortable, filterable, and editable inline.
19
31
 
20
32
  ```html
21
- <div id="grid" style="height: 400px;"></div>
33
+ <div id="orders-grid" style="height: 500px;"></div>
22
34
 
23
35
  <script type="module">
24
36
  import { OGrid } from '@alaarab/ogrid-js';
25
- import '@alaarab/ogrid-js/styles'; // default theme (light + dark mode)
37
+ import '@alaarab/ogrid-js/styles';
26
38
 
27
- const grid = new OGrid(document.getElementById('grid'), {
39
+ const grid = new OGrid(document.getElementById('orders-grid'), {
28
40
  columns: [
29
- { columnId: 'name', name: 'Name', sortable: true },
30
- { columnId: 'department', name: 'Department', sortable: true },
41
+ { columnId: 'orderId', name: 'Order #', sortable: true },
42
+ { columnId: 'customer', name: 'Customer', sortable: true },
43
+ {
44
+ columnId: 'status',
45
+ name: 'Status',
46
+ sortable: true,
47
+ filterable: { type: 'multiSelect', options: ['Pending', 'Shipped', 'Delivered', 'Cancelled'] },
48
+ },
31
49
  {
32
- columnId: 'salary',
33
- name: 'Salary',
50
+ columnId: 'amount',
51
+ name: 'Amount',
34
52
  type: 'numeric',
35
53
  sortable: true,
36
54
  editable: true,
37
55
  valueFormatter: (v) => `$${Number(v).toLocaleString()}`,
38
56
  },
57
+ {
58
+ columnId: 'date',
59
+ name: 'Order Date',
60
+ type: 'date',
61
+ sortable: true,
62
+ },
39
63
  ],
40
64
  data: [
41
- { id: 1, name: 'Alice Johnson', department: 'Engineering', salary: 95000 },
42
- { id: 2, name: 'Bob Smith', department: 'Marketing', salary: 72000 },
43
- { id: 3, name: 'Carol Williams', department: 'Sales', salary: 110000 },
44
- { id: 4, name: 'David Brown', department: 'Engineering', salary: 68000 },
45
- { id: 5, name: 'Eva Martinez', department: 'Marketing', salary: 78000 },
65
+ { orderId: 'ORD-1001', customer: 'Acme Corp', status: 'Shipped', amount: 4200, date: '2024-03-01' },
66
+ { orderId: 'ORD-1002', customer: 'Globex Inc', status: 'Pending', amount: 1850, date: '2024-03-03' },
67
+ { orderId: 'ORD-1003', customer: 'Initech', status: 'Delivered', amount: 9300, date: '2024-02-28' },
68
+ { orderId: 'ORD-1004', customer: 'Umbrella LLC', status: 'Cancelled', amount: 620, date: '2024-03-05' },
69
+ { orderId: 'ORD-1005', customer: 'Acme Corp', status: 'Delivered', amount: 7100, date: '2024-02-20' },
46
70
  ],
47
- getRowId: (row) => row.id,
48
- pageSize: 10,
49
- editable: true,
71
+ getRowId: (row) => row.orderId,
72
+ pageSize: 20,
50
73
  cellSelection: true,
51
74
  statusBar: true,
52
75
  });
76
+
77
+ // React to edits in real time
78
+ grid.on('cellValueChanged', ({ item, columnId, newValue }) => {
79
+ console.log(`Order ${item.orderId}: ${columnId} updated to ${newValue}`);
80
+ // sync to your backend here
81
+ });
53
82
  </script>
54
83
  ```
55
84
 
56
- ## Live Demo
85
+ ## Live demo
57
86
 
58
87
  <VanillaJSDemo />
59
88
 
60
- ## What You Get
61
-
62
- With this code you have:
89
+ ## What you get out of the box
63
90
 
64
91
  - **Sorting** — click column headers
65
- - **Inline editing** — double-click any salary cell
92
+ - **Filtering** — multi-select dropdown on the Status column
93
+ - **Inline editing** — double-click any Amount cell to edit
66
94
  - **Cell selection** — click and drag to select ranges
67
95
  - **Keyboard navigation** — arrow keys, Tab, Home/End, Ctrl+Arrow
68
- - **Clipboard** — Ctrl+C/V/X
96
+ - **Clipboard** — Ctrl+C/V/X works like a spreadsheet
69
97
  - **Undo/redo** — Ctrl+Z/Y
70
98
  - **Status bar** — row count and selection aggregations
71
99
  - **Pagination** — page controls at the bottom
@@ -76,78 +104,77 @@ With this code you have:
76
104
  const grid = new OGrid(containerElement, options);
77
105
  ```
78
106
 
79
- | Parameter | Type | Description |
80
- |-----------|------|-------------|
81
- | `container` | `HTMLElement` | The DOM element to render into. OGrid fills this element. |
82
- | `options` | `OGridOptions<T>` | Grid configuration (see below). |
107
+ The container element defines where the grid renders. OGrid fills it completely — give it an explicit height (CSS or inline).
83
108
 
84
- ## Key Options
109
+ ## Key options
85
110
 
86
111
  | Option | Type | Default | Description |
87
112
  |--------|------|---------|-------------|
88
113
  | `columns` | `IColumnDef<T>[]` | **required** | Column definitions |
89
- | `data` | `T[]` | — | Client-side data array |
90
- | `dataSource` | `IDataSource<T>` | — | Server-side data source (mutually exclusive with `data`) |
91
- | `getRowId` | `(item: T) => RowId` | **required** | Unique row identifier |
114
+ | `data` | `T[]` | — | Client-side row data |
115
+ | `dataSource` | `IDataSource<T>` | — | Server-side data source (use instead of `data`) |
116
+ | `getRowId` | `(item: T) => RowId` | **required** | Returns a unique identifier per row |
92
117
  | `pageSize` | `number` | `20` | Rows per page |
93
- | `sort` | `{ field, direction }` | — | Initial sort |
118
+ | `sort` | `{ field, direction }` | — | Initial sort state |
94
119
  | `filters` | `IFilters` | — | Initial filters |
95
120
  | `editable` | `boolean` | `false` | Enable cell editing |
96
- | `cellSelection` | `boolean` | `false` | Enable spreadsheet-style selection |
121
+ | `cellSelection` | `boolean` | `false` | Spreadsheet-style range selection |
97
122
  | `rowSelection` | `'single' \| 'multiple'` | — | Row selection mode |
98
- | `sideBar` | `boolean \| ISideBarDef` | — | Show sidebar (columns + filters panels) |
99
- | `layoutMode` | `'fill' \| 'content'` | `'fill'` | `'fill'` stretches to container; `'content'` sizes to data |
100
- | `pinnedColumns` | `Record<string, 'left' \| 'right'>` | — | Pin columns to edges |
123
+ | `sideBar` | `boolean \| ISideBarDef` | — | Column visibility + filter panel |
124
+ | `layoutMode` | `'fill' \| 'content'` | `'fill'` | `'fill'` fills the container; `'content'` sizes to data |
125
+ | `pinnedColumns` | `Record<string, 'left' \| 'right'>` | — | Pin columns to left or right edge |
101
126
 
102
- ## Events
127
+ ## Listening to events
103
128
 
104
- Listen for events with `.on()`:
129
+ Use `.on()` to subscribe to grid events:
105
130
 
106
131
  ```js
107
- grid.on('cellValueChanged', (event) => {
108
- console.log(`Cell ${event.columnId} changed from ${event.oldValue} to ${event.newValue}`);
109
- });
110
-
111
- grid.on('sortChange', ({ field, direction }) => {
112
- console.log(`Sorted by ${field} ${direction}`);
132
+ grid.on('cellValueChanged', ({ item, columnId, oldValue, newValue }) => {
133
+ console.log(`${columnId} changed: ${oldValue} ${newValue}`);
134
+ // save to backend
113
135
  });
114
136
 
115
137
  grid.on('selectionChange', ({ selectedRows }) => {
116
138
  console.log(`${selectedRows.length} rows selected`);
139
+ updateActionBar(selectedRows);
140
+ });
141
+
142
+ grid.on('sortChange', ({ field, direction }) => {
143
+ console.log(`Sorted by ${field} ${direction}`);
117
144
  });
118
145
  ```
119
146
 
120
- | Event | Payload | Fired when |
121
- |-------|---------|------------|
147
+ | Event | Payload | When it fires |
148
+ |-------|---------|---------------|
122
149
  | `cellValueChanged` | `{ item, columnId, oldValue, newValue }` | A cell edit is committed |
123
- | `sortChange` | `{ field, direction }` | Sort changes |
124
- | `filterChange` | `{ filters }` | Filters change |
125
- | `pageChange` | `{ page }` | Page changes |
150
+ | `sortChange` | `{ field, direction }` | User changes sort |
151
+ | `filterChange` | `{ filters }` | User changes filters |
152
+ | `pageChange` | `{ page }` | User navigates pages |
126
153
  | `selectionChange` | `{ selectedRows, selectedRowIds }` | Row selection changes |
127
154
 
128
- ## Grid API
155
+ ## Programmatic control
129
156
 
130
- Access the imperative API via `grid.api`:
157
+ The grid API lives on `grid.api`:
131
158
 
132
159
  ```js
133
- // Update data
134
- grid.api.setRowData(newData);
160
+ // Swap in new data (re-renders efficiently)
161
+ grid.api.setRowData(freshData);
135
162
 
136
- // Get current state
137
- const state = grid.api.getColumnState();
163
+ // Export what's currently displayed (respects active filters/sort)
164
+ grid.api.exportToCsv('orders.csv');
138
165
 
139
- // Export to CSV
140
- grid.api.exportToCsv('my-data.csv');
166
+ // Set sort programmatically
167
+ grid.api.setSort('amount', 'desc');
141
168
 
142
- // Programmatic sort
143
- grid.api.setSort('salary', 'desc');
169
+ // Get current column state (widths, visibility, order)
170
+ const state = grid.api.getColumnState();
144
171
  ```
145
172
 
146
- See the full [JS API Reference](../api/js-api) for all methods.
173
+ See the full [JS API Reference](../api/js-api) for all available methods.
147
174
 
148
175
  ## Theming
149
176
 
150
- The default theme uses CSS custom properties. Override them to match your design:
177
+ The default theme uses CSS custom properties. Override any of them to match your design system:
151
178
 
152
179
  ```css
153
180
  :root {
@@ -155,37 +182,34 @@ The default theme uses CSS custom properties. Override them to match your design
155
182
  --ogrid-selection: #0078d4; /* active cell outline */
156
183
  --ogrid-bg: #ffffff; /* background */
157
184
  --ogrid-fg: #242424; /* text color */
158
- --ogrid-border: #e0e0e0; /* borders */
159
- --ogrid-bg-subtle: #f3f2f1; /* header background */
160
- --ogrid-bg-hover: #f5f5f5; /* row hover */
185
+ --ogrid-border: #e0e0e0; /* borders */
186
+ --ogrid-bg-subtle: #f3f2f1; /* header background */
187
+ --ogrid-bg-hover: #f5f5f5; /* row hover */
161
188
  }
162
189
  ```
163
190
 
164
- For dark mode, set `data-theme="dark"` on a parent element or rely on `prefers-color-scheme`:
191
+ Dark mode works automatically if you rely on `prefers-color-scheme`, or you can opt into it explicitly:
165
192
 
166
193
  ```css
167
194
  [data-theme='dark'] {
168
195
  --ogrid-bg: #1a1a24;
169
196
  --ogrid-fg: #e0e0e0;
170
197
  --ogrid-border: #333340;
171
- /* ... */
172
198
  }
173
199
  ```
174
200
 
175
- Or skip the default theme entirely and write your own CSS targeting the `ogrid-*` class names.
201
+ Or skip the built-in theme entirely and write your own CSS targeting `ogrid-*` class names.
176
202
 
177
203
  ## Cleanup
178
204
 
179
- Always destroy the grid when you're done:
205
+ When your grid is no longer needed (navigating away, SPA route change, etc.), call `destroy()` to remove all DOM elements and event listeners:
180
206
 
181
207
  ```js
182
208
  grid.destroy();
183
209
  ```
184
210
 
185
- This removes all DOM elements, event listeners, and ResizeObservers.
186
-
187
- ## Next Steps
211
+ ## What's next?
188
212
 
189
213
  - [JS API Reference](../api/js-api) — full method and options reference
190
- - [Features](../features/sorting) — all grid features work identically in JS
214
+ - [Features](../features/sorting) — all features work identically in the JS package
191
215
  - [Theming Guide](../guides/theming) — deep dive into CSS customization