Back to Reflex

Themes

docs/enterprise/ag_grid/theme.md

0.9.2a28.1 KB
Original Source
python
import reflex as rx
import reflex_enterprise as rxe

Themes

md
# Only the old theme API of AG Grid is currently supported. The new theme API is not supported yet.

You can style your grid with a theme. AG Grid includes the following themes:

  1. quartz
  2. alpine
  3. balham
  4. material

The grid uses quartz by default. To use any other theme, set it using the theme prop, i.e. theme="alpine".

python
import reflex as rx
import reflex_enterprise as rxe
import pandas as pd


class AGGridThemeState(rx.State):
    """The app state."""

    theme: str = "quartz"
    themes: list[str] = ["quartz", "balham", "alpine", "material"]


df = pd.read_csv("data/gapminder2007.csv")

column_defs = [
    {"field": "country"},
    {"field": "pop", "headerName": "Population"},
    {"field": "lifeExp", "headerName": "Life Expectancy"},
]


def ag_grid_simple_themes():
    return rx.vstack(
        rx.hstack(
            rx.text("Theme:"),
            rx.select(
                AGGridThemeState.themes,
                value=AGGridThemeState.theme,
                on_change=AGGridThemeState.set_theme,
            ),
        ),
        rxe.ag_grid(
            id="ag_grid_basic_themes",
            row_data=df.to_dict("records"),
            column_defs=column_defs,
            theme=AGGridThemeState.theme,
            width="100%",
            height="40vh",
        ),
        width="100%",
    )

šŸ“Š Dataset source: gapminder2007.csv

Customizing a Theme with CSS

The theme prop maps directly to one of AG Grid's built-in theme classes, which reflex-enterprise applies on the grid root element:

  • theme="quartz" → .ag-theme-quartz
  • theme="balham" → .ag-theme-balham
  • theme="alpine" → .ag-theme-alpine
  • theme="material" → .ag-theme-material

Each theme also has a dark variant (.ag-theme-quartz-dark, .ag-theme-balham-dark, etc.). The dark variant is only applied when the corresponding theme name is selected; listing both light and dark class selectors in your CSS lets the same overrides apply in either mode.

You can customize any theme by overriding the AG Grid CSS variables (the --ag-* custom properties) and by writing rules that target AG Grid's built-in classes. The full list of available variables is documented in the AG Grid theming reference.

CSS Variables

AG Grid exposes most visual properties as CSS variables. Commonly customized ones:

  • Typography: --ag-font-family, --ag-font-size
  • Density and shape: --ag-grid-size (base unit), --ag-border-radius, --ag-wrapper-border-radius, --ag-cell-horizontal-padding
  • Borders: --ag-border-color, --ag-row-border-color
  • Backgrounds: --ag-background-color, --ag-odd-row-background-color, --ag-row-hover-color
  • Headers: --ag-header-background-color, --ag-header-foreground-color, --ag-header-column-separator-color
  • Accents (selection / focus): --ag-accent-color, --ag-selected-row-background-color, --ag-range-selection-background-color, --ag-input-focus-border-color, --ag-checkbox-checked-color

Scoping Overrides with a Wrapper Class

If you have multiple grids on a page, scope your overrides to a parent class so they don't leak to other grids. Wrap the grid in an rx.box (or any element) with a class such as custom-ag-grid, then target the theme class inside that wrapper in your CSS:

python
rx.box(
    rxe.ag_grid(
        id="my_themed_grid",
        row_data=df.to_dict("records"),
        column_defs=column_defs,
        theme="quartz",
        width="100%",
        height="40vh",
    ),
    class_name="custom-ag-grid",
)
css
/* assets/ag_grid_theme.css */

/*
 * Custom ag-grid theme overrides.
 *
 * reflex-enterprise applies the built-in theme as a class on the grid root.
 * Scope overrides to a parent `.custom-ag-grid` container so they don't leak
 * to other grids on the page.
 */

.custom-ag-grid .ag-theme-quartz,
.custom-ag-grid .ag-theme-quartz-dark,
.custom-ag-grid .ag-theme-balham,
.custom-ag-grid .ag-theme-balham-dark,
.custom-ag-grid .ag-theme-alpine,
.custom-ag-grid .ag-theme-alpine-dark,
.custom-ag-grid .ag-theme-material,
.custom-ag-grid .ag-theme-material-dark {
    --ag-font-family: "Inter", ui-sans-serif, system-ui, sans-serif;
    --ag-font-size: 13px;

    --ag-grid-size: 6px;
    --ag-border-radius: 12px;
    --ag-wrapper-border-radius: 12px;

    --ag-background-color: #ffffff;
    --ag-odd-row-background-color: #fafbfd;
    --ag-row-hover-color: #f1f5f9;

    --ag-header-background-color: #eef2ff;
    --ag-header-foreground-color: #1e293b;

    --ag-accent-color: #3b82f6;
    --ag-selected-row-background-color: #dbeafe;
    --ag-input-focus-border-color: #3b82f6;
    --ag-checkbox-checked-color: #3b82f6;

    --ag-cell-horizontal-padding: 16px;
}

/* Stronger header typography. */
.custom-ag-grid .ag-header-cell-text {
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    font-size: 11px;
}

Listing every theme variant in the selector keeps the overrides applied no matter which built-in theme is selected at runtime, including its dark variant.

Loading the Stylesheet

Place your CSS file under assets/ and add it to the stylesheets list on your rxe.App (or rx.App):

python
app = rxe.App(
    stylesheets=["/ag_grid_theme.css"],
)

The path is relative to assets/, so assets/ag_grid_theme.css is referenced as /ag_grid_theme.css.

Overriding CSS Variables Inline via style

For one-off tweaks you don't need a separate stylesheet — you can set --ag-* variables directly in the style dict on the grid:

python
rxe.ag_grid(
    id="comic_grid",
    row_data=df.to_dict("records"),
    column_defs=column_defs,
    theme="quartz",
    style={
        "--ag-font-family": "Comic Sans MS !important",
        "--ag-font-size": "14px !important",
    },
    width="100%",
    height="40vh",
)
md
# CSS variable inheritance and `!important`
Inline `style` sets the variable on the grid root element only. The built-in themes bind their styling to multiple CSS classes (`.ag-theme-quartz`, `.ag-theme-quartz .ag-cell`, etc.), and those rules often have higher specificity than a custom property defined inline. As a result, a value set via `style={"--ag-font-family": "..."}` may be ignored unless you append `!important`. If your inline override doesn't take effect, add `!important` or move the override into a stylesheet that targets the theme class with comparable specificity.

Inline `style` overrides only apply to the grid they are set on. To customize multiple grids consistently, prefer a stylesheet scoped to a wrapper class (see above).

Per-Cell Custom Classes

Built-in themes style the grid as a whole. To highlight individual cells based on their value, attach custom CSS classes via the cellClass and cellClassRules column-def keys, then style those classes in your stylesheet.

  • cellClass always applies the listed class(es) to every cell in the column.
  • cellClassRules is a mapping of class name -> JS expression; the class is applied when the expression evaluates truthy. The cell's value is available as params.value.
python
column_defs = [
    {"field": "project"},
    {
        "field": "status",
        "cellClass": "status-pill",
        "cellClassRules": {
            "status-active": "params.value === 'Active'",
            "status-blocked": "params.value === 'Blocked'",
            "status-complete": "params.value === 'Complete'",
            "status-planning": "params.value === 'Planning'",
        },
    },
]
css
/* assets/ag_grid_theme.css */

.custom-ag-grid .status-pill {
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.custom-ag-grid .status-active   { color: #047857; }
.custom-ag-grid .status-blocked  { color: #b91c1c; }
.custom-ag-grid .status-complete { color: #1d4ed8; }
.custom-ag-grid .status-planning { color: #a16207; }

The same approach works for header cells (headerClass) and rows (rowClass / rowClassRules on the grid itself).