---
title: "Customising AG Grid Styles"
framework: javascript
version: "36.1.0"
---

# Customising AG Grid Styles

Learn how to customise the look and feel of AG Grid using the Theming API, custom CSS, and conditional formatting.

## Overview

In this tutorial you will:

1. [Start with a built-in theme](#introduction-to-styling)
2. [Customise theme parameters](#customising-themes)
3. [Add dark mode support](#theme-modes)
4. [Apply custom CSS for fine-tuning](#custom-css-for-fine-tuning)
5. [Conditionally style cells & rows](#conditional-formatting)

By the end, you'll have built a fully styled grid that looks like the example below, complete with both light and dark themes:

#### Complete Example

```ts
import {
  CellClassRules,
  CellStyleModule,
  ClientSideRowModelModule,
  ColDef,
  GridOptions,
  ModuleRegistry,
  NumberFilterModule,
  RowClassRules,
  RowSelectionModule,
  RowStyleModule,
  TextFilterModule,
  ValueFormatterParams,
  createGrid,
  enableDevValidations,
  iconSetMaterial,
  themeQuartz,
} from "ag-grid-community";
import { getData, type IProduct } from "./data";

// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  CellStyleModule,
  ClientSideRowModelModule,
  NumberFilterModule,
  RowSelectionModule,
  TextFilterModule,
  RowStyleModule,
]);

// Create a theme with light and dark modes
const myTheme = themeQuartz
  .withPart(iconSetMaterial)
  .withParams(
    {
      accentColor: "#0e4491",
      backgroundColor: "#ffffff",
      foregroundColor: "#1a1a1a",
      headerBackgroundColor: "#faf8f5",
      selectedRowBackgroundColor: "rgba(14, 68, 145, 0.15)",
      spacing: 10,
      fontSize: 12,
      headerFontSize: 14,
    },
    "light",
  )
  .withParams(
    {
      accentColor: "#6ea8fe",
      backgroundColor: "#1e1e2f",
      foregroundColor: "#e2e8f0",
      headerBackgroundColor: "#2d2d44",
      selectedRowBackgroundColor: "rgba(110, 168, 254, 0.2)",
      spacing: 10,
      fontSize: 12,
      headerFontSize: 14,
    },
    "dark",
  );

// Cell class rules for status column
const statusCellClassRules: CellClassRules = {
  "status-delivered": (params) => params.value === "Delivered",
  "status-pending": (params) => params.value === "Pending",
  "status-cancelled": (params) => params.value === "Cancelled",
};

// Cell class rules for profit margin column
const profitMarginCellClassRules: CellClassRules = {
  "high-margin": (params) => params.value > 0.2,
};

// Row class rules for highlighting sales performance
const salesRowClassRules: RowClassRules<IProduct> = {
  "high-sales": (params) => (params.data?.salesRevenue ?? 0) > 10000,
  "low-sales": (params) => (params.data?.salesRevenue ?? 0) < 1000,
};

const columnDefs: ColDef<IProduct>[] = [
  { field: "productName", headerName: "Product", minWidth: 180 },
  {
    field: "salesRevenue",
    headerName: "Revenue",
    valueFormatter: (params: ValueFormatterParams) =>
      params.value != null ? `$${params.value.toLocaleString()}` : "",
  },
  {
    field: "profitMargin",
    headerName: "Margin",
    valueFormatter: (params: ValueFormatterParams) =>
      params.value != null ? `${(params.value * 100).toFixed(0)}%` : "",
    cellClassRules: profitMarginCellClassRules,
  },
  {
    field: "status",
    cellClassRules: statusCellClassRules,
  },
];

const defaultColDef: ColDef = {
  flex: 1,
  minWidth: 100,
  filter: true,
};

const gridOptions: GridOptions<IProduct> = {
  theme: myTheme,
  columnDefs,
  rowData: getData(),
  defaultColDef,
  rowClassRules: salesRowClassRules,
  rowSelection: {
    mode: "multiRow",
  },
};

// Dark mode toggle logic
const toggleButton = document.querySelector<HTMLElement>("#toggle")!;

function setThemeMode() {
  const isDark: boolean = document.body.dataset.agThemeMode === "dark";
  const nextMode = isDark ? "light" : "dark";

  document.body.dataset.agThemeMode = nextMode;
  toggleButton.innerText =
    nextMode === "dark" ? "Enable Light Mode" : "Enable Dark Mode";
}

// Set initial mode
document.body.dataset.agThemeMode = "light";

createGrid(document.querySelector<HTMLElement>("#myGrid")!, gridOptions);

if (typeof window !== "undefined") {
  // Attach external event handlers to window so they can be called from index.html
  (<any>window).setThemeMode = setThemeMode;
}
```

[Live example: Complete Example](https://www.ag-grid.com/examples/styling-tutorial/complete-example/typescript)

> **Note**
>
> This tutorial assumes basic knowledge of AG Grid. If you haven't already, we recommend reading our [Introductory Tutorial](https://www.ag-grid.com/javascript-data-grid/deep-dive/) first.

## Introduction to Styling

AG Grid provides three main approaches to styling:

- **Theming API:** Fully-typed API for customising default themes.
- **Custom CSS:** Fine-grained control over specific grid elements.
- **Cell/Row Styles:** Data-driven formatting for individual cells and rows.

Each of these approaches can be used to customise our built-in themes: [Quartz](https://www.ag-grid.com/example), [Alpine](https://www.ag-grid.com/example/?theme=alpine), [Balham](https://www.ag-grid.com/example/?theme=balham), and [Material](https://www.ag-grid.com/example/?theme=material).

Our themes are simply JavaScript objects that define colours, spacing, fonts, and other design tokens. By default, the Quartz theme is applied; to use a custom theme, create a reference to the desired theme and pass it to the grid options:

```js
import { themeQuartz } from 'ag-grid-community';

const myTheme = themeQuartz;

const gridOptions = {
    theme: myTheme,
    // ... other options
};
```

The example below demonstrates an AG Grid data grid with an unmodified Quartz theme. Open the example in CodeSandbox or Plunkr to follow this tutorial:

#### Basic Theme

```ts
import {
  ClientSideRowModelModule,
  ColDef,
  GridOptions,
  ModuleRegistry,
  NumberFilterModule,
  RowSelectionModule,
  TextFilterModule,
  ValueFormatterParams,
  createGrid,
  enableDevValidations,
  themeQuartz,
} from "ag-grid-community";
import { getData, type IProduct } from "./data";

// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  RowSelectionModule,
  TextFilterModule,
  NumberFilterModule,
  ClientSideRowModelModule,
]);

const myTheme = themeQuartz;

const columnDefs: ColDef<IProduct>[] = [
  { field: "productName", headerName: "Product", minWidth: 180 },
  {
    field: "salesRevenue",
    headerName: "Revenue",
    valueFormatter: (params: ValueFormatterParams) =>
      params.value != null ? `$${params.value.toLocaleString()}` : "",
  },
  {
    field: "profitMargin",
    headerName: "Margin",
    valueFormatter: (params: ValueFormatterParams) =>
      params.value != null ? `${(params.value * 100).toFixed(0)}%` : "",
  },
  { field: "status" },
];

const defaultColDef: ColDef = {
  flex: 1,
  minWidth: 100,
  filter: true,
};

const gridOptions: GridOptions<IProduct> = {
  theme: myTheme,
  columnDefs,
  defaultColDef,
  rowData: getData(),
  rowSelection: {
    mode: "multiRow",
  },
};

createGrid(document.querySelector<HTMLElement>("#myGrid")!, gridOptions);
```

[Live example: Basic Theme](https://www.ag-grid.com/examples/styling-tutorial/basic-theme/typescript)

> **Note**
>
> Our [Built-in Themes](https://www.ag-grid.com/javascript-data-grid/themes/) docs provide a full overview of the available themes and their features.

## Customising Themes

Themes can be customised directly using the Theming API, which provides three main features:

- **Theme Parameters:** Adjust colours, spacing, fonts, and other variables to create a unique theme.
- **Theme Parts:** Mix-and-match parts from different themes, such as light/dark schemes, inputs or icon sets.
- **Theme Modes:** Define multiple colour schemes (e.g. light and dark) within a single theme.

### Theme Parameters

[Theme Parameters](https://www.ag-grid.com/javascript-data-grid/theming-parameters/) are configuration values that affect the appearance of the grid.

Some parameters, such as `headerTextColor`, affect a single aspect of grid appearance. Others such as `spacing` affect the whole grid.

To set parameters on a theme, call the `theme.withParams(...)` method which returns a new theme with different default values for its parameters.

For example, to customise your themes colours, spacing, and font sizes, add the following params to the `themeQuartz.withParams()` method:

```js
// Custom theme with parameters
const myTheme = themeQuartz
    .withParams({
        backgroundColor: '#ffffff',
        foregroundColor: '#1a1a1a',
        headerBackgroundColor: '#faf8f5',
        spacing: 10,
        fontSize: 12,
        headerFontSize: 14,
    });

const gridOptions = {
    theme: myTheme,
    // ... other options
};
```

When you run your application, you should see the new theme applied to the grid, with slightly smaller text, and an off-white header background colour:

#### Theme Parameters

```ts
import {
  ClientSideRowModelModule,
  ColDef,
  GridOptions,
  ModuleRegistry,
  NumberFilterModule,
  RowSelectionModule,
  TextFilterModule,
  ValueFormatterParams,
  createGrid,
  enableDevValidations,
  themeQuartz,
} from "ag-grid-community";
import { getData, type IProduct } from "./data";

// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  RowSelectionModule,
  TextFilterModule,
  NumberFilterModule,
  ClientSideRowModelModule,
]);

// Customise the theme with parameters
const myTheme = themeQuartz.withParams({
  backgroundColor: "#ffffff",
  foregroundColor: "#1a1a1a",
  headerBackgroundColor: "#faf8f5",
  spacing: 10,
  fontSize: 12,
  headerFontSize: 14,
});

const columnDefs: ColDef<IProduct>[] = [
  { field: "productName", headerName: "Product", minWidth: 180 },
  {
    field: "salesRevenue",
    headerName: "Revenue",
    valueFormatter: (params: ValueFormatterParams) =>
      params.value != null ? `$${params.value.toLocaleString()}` : "",
  },
  {
    field: "profitMargin",
    headerName: "Margin",
    valueFormatter: (params: ValueFormatterParams) =>
      params.value != null ? `${(params.value * 100).toFixed(0)}%` : "",
  },
  { field: "status" },
];

const defaultColDef: ColDef = {
  flex: 1,
  minWidth: 100,
  filter: true,
};

const gridOptions: GridOptions<IProduct> = {
  theme: myTheme,
  columnDefs,
  defaultColDef,
  rowData: getData(),
  rowSelection: {
    mode: "multiRow",
  },
};

createGrid(document.querySelector<HTMLElement>("#myGrid")!, gridOptions);
```

[Live example: Theme Parameters](https://www.ag-grid.com/examples/styling-tutorial/theme-params/typescript)

> **Note**
>
> Our [Theme Parameters](https://www.ag-grid.com/javascript-data-grid/theming-parameters/) docs provide a full list of available parameters and how to use them.

### Theme Parts

[Theme Parts](https://www.ag-grid.com/javascript-data-grid/theming-parts/) contain the CSS styles for a single feature like icons or text inputs. Using parts you can, for example, use the Material icons with the Quartz theme, or use the `colourSchemeDarkBlue` part to use a dark mode colour scheme.

To add a part to a theme, call the `theme.withPart(...)` method which returns a new theme using that part.

For example, to use the Material icon set with the Quartz theme, pass the `iconSetMaterial` part to the `themeQuartz.withPart()` method:

```js
import { themeQuartz, iconSetMaterial } from 'ag-grid-community';

// Custom Quartz theme that uses Material icons
const myTheme = themeQuartz
    .withParams({ /*...*/ })
    .withPart(iconSetMaterial);

const gridOptions = {
    theme: myTheme,
    // ... other options
};
```

When you run your application, you should see the Material icons used for the Filter icon:

#### Theme Parts

```ts
import {
  ClientSideRowModelModule,
  ColDef,
  GridOptions,
  ModuleRegistry,
  NumberFilterModule,
  RowSelectionModule,
  TextFilterModule,
  ValueFormatterParams,
  createGrid,
  enableDevValidations,
  iconSetMaterial,
  themeQuartz,
} from "ag-grid-community";
import { getData, type IProduct } from "./data";

// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  RowSelectionModule,
  TextFilterModule,
  NumberFilterModule,
  ClientSideRowModelModule,
]);

// Apply icon set using withPart()
const myTheme = themeQuartz
  .withParams({
    backgroundColor: "#ffffff",
    foregroundColor: "#1a1a1a",
    headerBackgroundColor: "#faf8f5",
    spacing: 10,
    fontSize: 12,
    headerFontSize: 14,
  })
  .withPart(iconSetMaterial);

const columnDefs: ColDef<IProduct>[] = [
  { field: "productName", headerName: "Product", minWidth: 180 },
  {
    field: "salesRevenue",
    headerName: "Revenue",
    valueFormatter: (params: ValueFormatterParams) =>
      params.value != null ? `$${params.value.toLocaleString()}` : "",
  },
  {
    field: "profitMargin",
    headerName: "Margin",
    valueFormatter: (params: ValueFormatterParams) =>
      params.value != null ? `${(params.value * 100).toFixed(0)}%` : "",
  },
  { field: "status" },
];

const defaultColDef: ColDef = {
  flex: 1,
  minWidth: 100,
  filter: true,
};

const gridOptions: GridOptions<IProduct> = {
  theme: myTheme,
  columnDefs,
  defaultColDef,
  rowData: getData(),
  rowSelection: {
    mode: "multiRow",
  },
};

createGrid(document.querySelector<HTMLElement>("#myGrid")!, gridOptions);
```

[Live example: Theme Parts](https://www.ag-grid.com/examples/styling-tutorial/theme-parts/typescript)

> **Note**
>
> Our [Theming Parts](https://www.ag-grid.com/javascript-data-grid/theming-parts/) docs provide a full overview of the available theme parts and how to use them.

### Theme Modes

[Theme Modes](https://www.ag-grid.com/javascript-data-grid/theming-colors/#theme-modes) allow you to define multiple colour schemes within a single theme, that can be toggled dynamically using a `theme-mode` HTML attribute.

To use Theme Modes, you first need to create light and dark colour schemes by chaining another `withParams` method onto the `themeQuartz` object, passing an additional string parameter to name the scheme:

```js
const myTheme = themeQuartz
    .withPart(iconSetMaterial)
    .withParams(
        {
            // Existing theme params...
        },
        'light' // Light scheme name, used as value of data-ag-theme-mode attribute
    )
    .withParams(
        {
            // Add dark theme params
            backgroundColor: '#1e1e2f',
            foregroundColor: '#e2e8f0',
            headerBackgroundColor: '#2d2d44',
            selectedRowBackgroundColor: 'rgba(110, 168, 254, 0.2)',
            spacing: 10,
            fontSize: 12,
        },
        'dark' // Dark scheme name, used as value of data-ag-theme-mode attribute
    );

const gridOptions = {
    theme: myTheme,
    // ... other options
};
```

The active colour scheme is controlled by setting `data-ag-theme-mode="mode"` on the `<html>` or `<body>` element.

```js
// Index.html - Button to control colour scheme
<button id="toggle" onclick="setThemeMode()">Enable Dark Mode</button>

// Theme mode toggle logic
const toggleButton = document.querySelector<HTMLElement>('#toggle')!;

function setThemeMode() {
    const isDark = document.body.dataset.agThemeMode === 'dark';
    const nextMode = isDark ? 'light' : 'dark';

    document.body.dataset.agThemeMode = nextMode;
    toggleButton.innerText = nextMode === 'dark' ? 'Enable Light Mode' : 'Enable Dark Mode';
}

// Set initial mode
document.body.dataset.agThemeMode = 'light';
```

When you run your application, you should be able to toggle between light and dark modes by clicking the button:

#### Dark Mode

```ts
import {
  ClientSideRowModelModule,
  ColDef,
  GridOptions,
  ModuleRegistry,
  NumberFilterModule,
  RowSelectionModule,
  TextFilterModule,
  ValueFormatterParams,
  createGrid,
  enableDevValidations,
  iconSetMaterial,
  themeQuartz,
} from "ag-grid-community";
import { getData, type IProduct } from "./data";

// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  RowSelectionModule,
  TextFilterModule,
  NumberFilterModule,
  ClientSideRowModelModule,
]);

// Create a theme with light and dark modes
const myTheme = themeQuartz
  .withPart(iconSetMaterial)
  .withParams(
    {
      backgroundColor: "#ffffff",
      foregroundColor: "#1a1a1a",
      headerBackgroundColor: "#faf8f5",
      selectedRowBackgroundColor: "rgba(14, 68, 145, 0.15)",
      spacing: 10,
      fontSize: 12,
      headerFontSize: 14,
    },
    "light",
  )
  .withParams(
    {
      backgroundColor: "#1e1e2f",
      foregroundColor: "#e2e8f0",
      headerBackgroundColor: "#2d2d44",
      selectedRowBackgroundColor: "rgba(110, 168, 254, 0.2)",
      spacing: 10,
      fontSize: 12,
      headerFontSize: 14,
    },
    "dark",
  );

const columnDefs: ColDef<IProduct>[] = [
  { field: "productName", headerName: "Product", minWidth: 180 },
  {
    field: "salesRevenue",
    headerName: "Revenue",
    valueFormatter: (params: ValueFormatterParams) =>
      params.value != null ? `$${params.value.toLocaleString()}` : "",
  },
  {
    field: "profitMargin",
    headerName: "Margin",
    valueFormatter: (params: ValueFormatterParams) =>
      params.value != null ? `${(params.value * 100).toFixed(0)}%` : "",
  },
  { field: "status" },
];

const defaultColDef: ColDef = {
  flex: 1,
  minWidth: 100,
  filter: true,
};

const gridOptions: GridOptions<IProduct> = {
  theme: myTheme,
  columnDefs,
  defaultColDef,
  rowData: getData(),
  rowSelection: {
    mode: "multiRow",
  },
};

// Dark mode toggle logic
const toggleButton = document.querySelector<HTMLElement>("#toggle")!;

function setThemeMode() {
  const isDark = document.body.dataset.agThemeMode === "dark";
  const nextMode = isDark ? "light" : "dark";

  document.body.dataset.agThemeMode = nextMode;
  toggleButton.innerText =
    nextMode === "dark" ? "Enable Light Mode" : "Enable Dark Mode";
}

// Set initial mode
document.body.dataset.agThemeMode = "light";

createGrid(document.querySelector<HTMLElement>("#myGrid")!, gridOptions);

if (typeof window !== "undefined") {
  // Attach external event handlers to window so they can be called from index.html
  (<any>window).setThemeMode = setThemeMode;
}
```

[Live example: Dark Mode](https://www.ag-grid.com/examples/styling-tutorial/dark-mode/typescript)

> **Note**
>
> Our [Theme Modes](https://www.ag-grid.com/javascript-data-grid/theming-colors/#theme-modes) docs provide a full overview of how to use theme modes and best practices for implementing dark mode support.

## Custom CSS for Fine-Tuning

When the Theming API doesn't provide enough control, you can use [custom CSS](https://www.ag-grid.com/javascript-data-grid/theming-css/) to target specific grid elements.

Every DOM element rendered by the grid exposes a class name prefixed with `.ag-`. You can target these class names with your own CSS rules, allowing limitless customisation.

### Finding Class Names

Use your browser's developer tools to inspect grid elements and find the appropriate class names. Right-click on any grid element and select "Inspect" to see its classes.

[Video](https://www.ag-grid.com/_astro/find-css-classes.CqHeT9xz.mp4)

### Overriding CSS Styles

Once you've identified the class names, you can write custom CSS rules to override the default styles.

Override the following CSS to style the header text with the `.ag-header-cell-text` class:

```css
.ag-header-cell-text {
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-weight: 600;
    font-size: 0.85em;
}
```

You can also target specific columns by using the column’s `field` value via the `col-id` [attribute selector](https://www.w3schools.com/css/css_attribute_selectors.asp).

Override the CSS for cells in the product column with the `.ag-cell` class and `[col-id='productName']` selector:

```css
.ag-cell[col-id='productName'] {
    font-weight: 500;
}
```

When you run the example, you should see the styles applied to the header text and product columns:

#### Custom CSS

```ts
import {
  ClientSideRowModelModule,
  ColDef,
  GridOptions,
  ModuleRegistry,
  NumberFilterModule,
  RowSelectionModule,
  TextFilterModule,
  ValueFormatterParams,
  createGrid,
  enableDevValidations,
  iconSetMaterial,
  themeQuartz,
} from "ag-grid-community";
import { getData, type IProduct } from "./data";

// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  RowSelectionModule,
  TextFilterModule,
  NumberFilterModule,
  ClientSideRowModelModule,
]);

// Create a theme with light and dark modes
const myTheme = themeQuartz
  .withPart(iconSetMaterial)
  .withParams(
    {
      backgroundColor: "#ffffff",
      foregroundColor: "#1a1a1a",
      headerBackgroundColor: "#faf8f5",
      selectedRowBackgroundColor: "rgba(14, 68, 145, 0.15)",
      spacing: 10,
      fontSize: 12,
      headerFontSize: 14,
    },
    "light",
  )
  .withParams(
    {
      backgroundColor: "#1e1e2f",
      foregroundColor: "#e2e8f0",
      headerBackgroundColor: "#2d2d44",
      selectedRowBackgroundColor: "rgba(110, 168, 254, 0.2)",
      spacing: 10,
      fontSize: 12,
      headerFontSize: 14,
    },
    "dark",
  );

const columnDefs: ColDef<IProduct>[] = [
  { field: "productName", headerName: "Product", minWidth: 180 },
  {
    field: "salesRevenue",
    headerName: "Revenue",
    valueFormatter: (params: ValueFormatterParams) =>
      params.value != null ? `$${params.value.toLocaleString()}` : "",
  },
  {
    field: "profitMargin",
    headerName: "Margin",
    valueFormatter: (params: ValueFormatterParams) =>
      params.value != null ? `${(params.value * 100).toFixed(0)}%` : "",
  },
  { field: "status" },
];

const defaultColDef: ColDef = {
  flex: 1,
  minWidth: 100,
  filter: true,
};

const gridOptions: GridOptions<IProduct> = {
  theme: myTheme,
  columnDefs,
  defaultColDef,
  rowData: getData(),
  rowSelection: {
    mode: "multiRow",
  },
};

// Dark mode toggle logic
const toggleButton = document.querySelector<HTMLElement>("#toggle")!;

function setThemeMode() {
  const isDark = document.body.dataset.agThemeMode === "dark";
  const nextMode = isDark ? "light" : "dark";

  document.body.dataset.agThemeMode = nextMode;
  toggleButton.innerText =
    nextMode === "dark" ? "Enable Light Mode" : "Enable Dark Mode";
}

// Set initial mode
document.body.dataset.agThemeMode = "light";

createGrid(document.querySelector<HTMLElement>("#myGrid")!, gridOptions);

if (typeof window !== "undefined") {
  // Attach external event handlers to window so they can be called from index.html
  (<any>window).setThemeMode = setThemeMode;
}
```

[Live example: Custom CSS](https://www.ag-grid.com/examples/styling-tutorial/custom-css/typescript)

> **Note**
>
> Our [Extending with CSS](https://www.ag-grid.com/javascript-data-grid/theming-css/) docs provide more information on applying custom CSS.

## Conditional Formatting

Conditional formatting allows you to dynamically apply styles to cells or rows based on their data.

There are two main approaches for applying styles based on data:

- [Cell Class Rules](https://www.ag-grid.com/javascript-data-grid/cell-styles/#cell-class-rules)
- [Row Class Rules](https://www.ag-grid.com/javascript-data-grid/row-styles/#row-class-rules)

Both of these features work in the same way, allowing you to create functions that dynamically apply CSS classes to elements based on arbitrary data.

### Cell Class Rules

Class Rules are defined by passing a JavaScript map to the `cellClassRules` column property where the keys are the class names and the values are expressions that, when returning `true`, the class gets used.

These expressions access the cell's data via the `params` object, which includes information such as the cell value and row node.

For example, to apply different classes to cells in the `status` column based on their value, first define the following `cellClassRules`:

```js
// Column Definition with Cell Class Rules
const columnDefs = [
    {
        field: 'status',
        cellClassRules: {
            'status-delivered': (params) => params.value === 'Delivered',
            'status-pending': (params) => params.value === 'Pending',
            'status-cancelled': (params) => params.value === 'Cancelled',
        },
    },
    // Other columns...
];
```

Each time a cell is rendered within the status column, the `cellClassRules` function will be evaluated, and a CSS class applied to the cell based on its value.

To style these cells, add the corresponding classes to your CSS:

```css
.status-delivered {
    color: #2e7d32;
}

.status-pending {
    color: #ed6c02;
}

.status-cancelled {
    color: #d32f2f;
}
```

When you run your application, you should see the conditional styles applied to cells based on their data:

#### Conditional Styles Cells

```ts
import {
  CellClassRules,
  CellStyleModule,
  ClientSideRowModelModule,
  ColDef,
  GridOptions,
  ModuleRegistry,
  NumberFilterModule,
  RowSelectionModule,
  TextFilterModule,
  ValueFormatterParams,
  createGrid,
  enableDevValidations,
  iconSetMaterial,
  themeQuartz,
} from "ag-grid-community";
import { getData, type IProduct } from "./data";

// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  CellStyleModule,
  RowSelectionModule,
  TextFilterModule,
  NumberFilterModule,
  ClientSideRowModelModule,
]);

// Create a theme with light and dark modes
const myTheme = themeQuartz
  .withPart(iconSetMaterial)
  .withParams(
    {
      backgroundColor: "#ffffff",
      foregroundColor: "#1a1a1a",
      headerBackgroundColor: "#faf8f5",
      selectedRowBackgroundColor: "rgba(14, 68, 145, 0.15)",
      spacing: 10,
      fontSize: 12,
      headerFontSize: 14,
    },
    "light",
  )
  .withParams(
    {
      backgroundColor: "#1e1e2f",
      foregroundColor: "#e2e8f0",
      headerBackgroundColor: "#2d2d44",
      selectedRowBackgroundColor: "rgba(110, 168, 254, 0.2)",
      spacing: 10,
      fontSize: 12,
      headerFontSize: 14,
    },
    "dark",
  );

// Cell class rules for status column
const statusCellClassRules: CellClassRules = {
  "status-delivered": (params) => params.value === "Delivered",
  "status-pending": (params) => params.value === "Pending",
  "status-cancelled": (params) => params.value === "Cancelled",
};

const columnDefs: ColDef<IProduct>[] = [
  { field: "productName", headerName: "Product", minWidth: 180 },
  {
    field: "salesRevenue",
    headerName: "Revenue",
    valueFormatter: (params: ValueFormatterParams) =>
      params.value != null ? `$${params.value.toLocaleString()}` : "",
  },
  {
    field: "profitMargin",
    headerName: "Margin",
    valueFormatter: (params: ValueFormatterParams) =>
      params.value != null ? `${(params.value * 100).toFixed(0)}%` : "",
  },
  {
    field: "status",
    cellClassRules: statusCellClassRules,
  },
];

const defaultColDef: ColDef = {
  flex: 1,
  minWidth: 100,
  filter: true,
};

const gridOptions: GridOptions<IProduct> = {
  theme: myTheme,
  columnDefs,
  defaultColDef,
  rowData: getData(),
  rowSelection: {
    mode: "multiRow",
  },
};

// Dark mode toggle logic
const toggleButton = document.querySelector<HTMLElement>("#toggle")!;

function setThemeMode() {
  const isDark = document.body.dataset.agThemeMode === "dark";
  const nextMode = isDark ? "light" : "dark";

  document.body.dataset.agThemeMode = nextMode;
  toggleButton.innerText =
    nextMode === "dark" ? "Enable Light Mode" : "Enable Dark Mode";
}

// Set initial mode
document.body.dataset.agThemeMode = "light";

createGrid(document.querySelector<HTMLElement>("#myGrid")!, gridOptions);

if (typeof window !== "undefined") {
  // Attach external event handlers to window so they can be called from index.html
  (<any>window).setThemeMode = setThemeMode;
}
```

[Live example: Conditional Styles Cells](https://www.ag-grid.com/examples/styling-tutorial/conditional-styles-cells/typescript)

> **Note**
>
> Our [Cell Styles](https://www.ag-grid.com/javascript-data-grid/cell-styles/) docs provide a full overview of how to use class rules and best practices for conditional formatting.

### Row Class Rules

Row Class Rules work similarly to Cell Class Rules, but use the `rowClassRules` grid option to apply styles to entire rows, rather than cells.

For example, to apply a `high-sales` class to rows where the `salesRevenue` is greater than $10,000, first, define the following rule:

```js
const gridOptions = {
    rowClassRules: {
        'high-sales': params => params.data.salesRevenue > 10000,
    }
};
```

And then add the corresponding class to your CSS:

```css
.high-sales {
    background-color: rgba(76, 175, 80, 0.15);
}
```

When you run your application, you should see the conditional styles applied to rows based on their data:

#### Conditional Styles Rows

```ts
import {
  CellClassRules,
  CellStyleModule,
  ClientSideRowModelModule,
  ColDef,
  GridOptions,
  ModuleRegistry,
  NumberFilterModule,
  RowClassRules,
  RowSelectionModule,
  RowStyleModule,
  TextFilterModule,
  ValueFormatterParams,
  createGrid,
  enableDevValidations,
  iconSetMaterial,
  themeQuartz,
} from "ag-grid-community";
import { getData, type IProduct } from "./data";

// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  CellStyleModule,
  ClientSideRowModelModule,
  NumberFilterModule,
  RowSelectionModule,
  RowStyleModule,
  TextFilterModule,
]);

// Create a theme with light and dark modes
const myTheme = themeQuartz
  .withPart(iconSetMaterial)
  .withParams(
    {
      backgroundColor: "#ffffff",
      foregroundColor: "#1a1a1a",
      headerBackgroundColor: "#faf8f5",
      selectedRowBackgroundColor: "rgba(14, 68, 145, 0.15)",
      spacing: 10,
      fontSize: 12,
      headerFontSize: 14,
    },
    "light",
  )
  .withParams(
    {
      backgroundColor: "#1e1e2f",
      foregroundColor: "#e2e8f0",
      headerBackgroundColor: "#2d2d44",
      selectedRowBackgroundColor: "rgba(110, 168, 254, 0.2)",
      spacing: 10,
      fontSize: 12,
      headerFontSize: 14,
    },
    "dark",
  );

// Cell class rules for status column
const statusCellClassRules: CellClassRules = {
  "status-delivered": (params) => params.value === "Delivered",
  "status-pending": (params) => params.value === "Pending",
  "status-cancelled": (params) => params.value === "Cancelled",
};

// Row class rules for highlighting sales performance
const salesRowClassRules: RowClassRules<IProduct> = {
  "high-sales": (params) => (params.data?.salesRevenue ?? 0) > 10000,
};

const columnDefs: ColDef<IProduct>[] = [
  { field: "productName", headerName: "Product", minWidth: 180 },
  {
    field: "salesRevenue",
    headerName: "Revenue",
    valueFormatter: (params: ValueFormatterParams) =>
      params.value != null ? `$${params.value.toLocaleString()}` : "",
  },
  {
    field: "profitMargin",
    headerName: "Margin",
    valueFormatter: (params: ValueFormatterParams) =>
      params.value != null ? `${(params.value * 100).toFixed(0)}%` : "",
  },
  {
    field: "status",
    cellClassRules: statusCellClassRules,
  },
];

const defaultColDef: ColDef = {
  flex: 1,
  minWidth: 100,
  filter: true,
};

const gridOptions: GridOptions<IProduct> = {
  theme: myTheme,
  columnDefs,
  defaultColDef,
  rowClassRules: salesRowClassRules,
  rowSelection: {
    mode: "multiRow",
  },
  rowData: getData(),
};

// Dark mode toggle logic
const toggleButton = document.querySelector<HTMLElement>("#toggle")!;

function setThemeMode() {
  const isDark = document.body.dataset.agThemeMode === "dark";
  const nextMode = isDark ? "light" : "dark";

  document.body.dataset.agThemeMode = nextMode;
  toggleButton.innerText =
    nextMode === "dark" ? "Enable Light Mode" : "Enable Dark Mode";
}

// Set initial mode
document.body.dataset.agThemeMode = "light";

createGrid(document.querySelector<HTMLElement>("#myGrid")!, gridOptions);

if (typeof window !== "undefined") {
  // Attach external event handlers to window so they can be called from index.html
  (<any>window).setThemeMode = setThemeMode;
}
```

[Live example: Conditional Styles Rows](https://www.ag-grid.com/examples/styling-tutorial/conditional-styles-rows/typescript)

> **Note**
>
> Our [Row Styles](https://www.ag-grid.com/javascript-data-grid/row-styles/) docs provide a full overview of how to use class rules and best practices for conditional formatting.

## Design Tools

Whilst this tutorial has been focused on customising themes from within your application, we also provide tools to help both developers and designers create custom themes.

### Theme Builder

The [Theme Builder](https://www.ag-grid.com/theme-builder/) is a visual tool for creating and customising themes. It allows you to:

- Customise theme parameters via a user-friendly interface.
- Preview theme changes in real-time.
- Enable/disable grid features to accurately represent your configuration.

You can then generate the resulting custom theme as code and copy it directly into your project.

[Video](https://www.ag-grid.com/_astro/theme-builder-demo.DqpCSmGG.mp4)

### Figma Design System

The AG Grid design system replicates the Quartz and Alpine themes within Figma, allowing you to customise them with [Figma variables](https://help.figma.com/hc/en-us/articles/15339657135383-Guide-to-variables-in-Figma) to match your brand and style.

Figma variables can also be used with the [Style Dictionary](https://styledictionary.com/) package to automatically generate an AG Grid compatible theme object. Visit our [Design System](https://www.ag-grid.com/javascript-data-grid/ag-grid-design-system/) docs to learn more, or download the figma file using the button below to get started.

[Introducing the AG Grid Figma Design System](https://www.youtube.com/watch?v=Ymmm7wxLy7Y)

[AG Grid Design System (Figma)](https://www.figma.com/community/file/1360600846643230092/ag-grid-design-system)

## Test Your Knowledge

Try these challenges to reinforce what you've learned:

1. **Change the accent colour to `#0e4491` in light mode, and `#6ea8fe` in dark mode**

   *Hint: Use `withParams({ accentColor: '...' })`*
2. **Add a bottom border to header cells using CSS**

   *Hint: Target `.ag-header-cell` with `border-bottom: 2px solid blue`*
3. **Make cell text bold where profit margin is greater than 20%**

   *Hint: Use `cellClassRules` on the profitMargin column*
4. **Highlight rows with `rgba(244, 67, 54, 0.1)` for products with revenue less than $1,000**

   *Hint: Use `rowClassRules` and check `params.data.salesRevenue`*

Once complete, your grid should look like the example below. If you're stuck, check out the source code to see how it's done:

#### Complete Example

```ts
import {
  CellClassRules,
  CellStyleModule,
  ClientSideRowModelModule,
  ColDef,
  GridOptions,
  ModuleRegistry,
  NumberFilterModule,
  RowClassRules,
  RowSelectionModule,
  RowStyleModule,
  TextFilterModule,
  ValueFormatterParams,
  createGrid,
  enableDevValidations,
  iconSetMaterial,
  themeQuartz,
} from "ag-grid-community";
import { getData, type IProduct } from "./data";

// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([
  CellStyleModule,
  ClientSideRowModelModule,
  NumberFilterModule,
  RowSelectionModule,
  TextFilterModule,
  RowStyleModule,
]);

// Create a theme with light and dark modes
const myTheme = themeQuartz
  .withPart(iconSetMaterial)
  .withParams(
    {
      accentColor: "#0e4491",
      backgroundColor: "#ffffff",
      foregroundColor: "#1a1a1a",
      headerBackgroundColor: "#faf8f5",
      selectedRowBackgroundColor: "rgba(14, 68, 145, 0.15)",
      spacing: 10,
      fontSize: 12,
      headerFontSize: 14,
    },
    "light",
  )
  .withParams(
    {
      accentColor: "#6ea8fe",
      backgroundColor: "#1e1e2f",
      foregroundColor: "#e2e8f0",
      headerBackgroundColor: "#2d2d44",
      selectedRowBackgroundColor: "rgba(110, 168, 254, 0.2)",
      spacing: 10,
      fontSize: 12,
      headerFontSize: 14,
    },
    "dark",
  );

// Cell class rules for status column
const statusCellClassRules: CellClassRules = {
  "status-delivered": (params) => params.value === "Delivered",
  "status-pending": (params) => params.value === "Pending",
  "status-cancelled": (params) => params.value === "Cancelled",
};

// Cell class rules for profit margin column
const profitMarginCellClassRules: CellClassRules = {
  "high-margin": (params) => params.value > 0.2,
};

// Row class rules for highlighting sales performance
const salesRowClassRules: RowClassRules<IProduct> = {
  "high-sales": (params) => (params.data?.salesRevenue ?? 0) > 10000,
  "low-sales": (params) => (params.data?.salesRevenue ?? 0) < 1000,
};

const columnDefs: ColDef<IProduct>[] = [
  { field: "productName", headerName: "Product", minWidth: 180 },
  {
    field: "salesRevenue",
    headerName: "Revenue",
    valueFormatter: (params: ValueFormatterParams) =>
      params.value != null ? `$${params.value.toLocaleString()}` : "",
  },
  {
    field: "profitMargin",
    headerName: "Margin",
    valueFormatter: (params: ValueFormatterParams) =>
      params.value != null ? `${(params.value * 100).toFixed(0)}%` : "",
    cellClassRules: profitMarginCellClassRules,
  },
  {
    field: "status",
    cellClassRules: statusCellClassRules,
  },
];

const defaultColDef: ColDef = {
  flex: 1,
  minWidth: 100,
  filter: true,
};

const gridOptions: GridOptions<IProduct> = {
  theme: myTheme,
  columnDefs,
  rowData: getData(),
  defaultColDef,
  rowClassRules: salesRowClassRules,
  rowSelection: {
    mode: "multiRow",
  },
};

// Dark mode toggle logic
const toggleButton = document.querySelector<HTMLElement>("#toggle")!;

function setThemeMode() {
  const isDark: boolean = document.body.dataset.agThemeMode === "dark";
  const nextMode = isDark ? "light" : "dark";

  document.body.dataset.agThemeMode = nextMode;
  toggleButton.innerText =
    nextMode === "dark" ? "Enable Light Mode" : "Enable Dark Mode";
}

// Set initial mode
document.body.dataset.agThemeMode = "light";

createGrid(document.querySelector<HTMLElement>("#myGrid")!, gridOptions);

if (typeof window !== "undefined") {
  // Attach external event handlers to window so they can be called from index.html
  (<any>window).setThemeMode = setThemeMode;
}
```

[Live example: Complete Example](https://www.ag-grid.com/examples/styling-tutorial/complete-example/typescript)

## Summary

Congratulations! You've completed the tutorial and learned how to style AG Grid using a combination of the Theming API, custom CSS, and conditional formatting.

As recap, here's a simple table summarising the different styling requirements and the recommended approaches:

| Requirement | Recommended Approach |
| --- | --- |
| Global colours, spacing, fonts | Theme with `withParams()` |
| Light/dark mode | Theme modes with `data-ag-theme-mode` |
| Style specific elements | Custom CSS targeting `.ag-*` classes |
| Cells styled based on their value | `cellClassRules` |
| Rows styled based on row data | `rowClassRules` |

## Next Steps

Explore these topics to learn more:

- [Try the Theme Builder](https://www.ag-grid.com/theme-builder/) - Visual tool for creating themes
- [Theming API Docs](https://www.ag-grid.com/javascript-data-grid/theming/) - Complete theming reference
