Light, dark and auto — and how to make the picker follow your app's colour scheme instead of the OS.

Theming

Light, dark and auto — and how to make the picker follow your app's colour scheme instead of the OS.


Overview

theme takes three values:

ValueBehaviour
"auto" (default)Follows the OS/browser prefers-color-scheme setting.
"light"Always light, whatever the OS says.
"dark"Always dark, whatever the OS says.

"auto" is the right default for a standalone picker, and the wrong one for any app with its own theme toggle: the OS preference and the app's toggle disagree the moment a user flips the toggle. Drive theme from your own state instead.

Live demo

Following a Mantine colour scheme

The cheapest way to keep the picker in step with a dmc.MantineProvider is a clientside callback that maps the scheme onto theme — no server round-trip, so the picker flips in the same frame as the rest of the page:

from dash import Input, Output, clientside_callback

clientside_callback(
    "(scheme) => scheme || 'light'",
    Output("picker", "theme"),
    Input("color-scheme-storage", "data"),
)

This documentation site does exactly that — the sun/moon toggle in the header drives every picker on every page.

Unlike set and locale, theme is read on every render. Updating it on a mounted picker works, and keeps the user's search text and scroll position intact.

Styling the picker itself

emoji-mart exposes its own CSS custom properties, which you can set on any ancestor — including through style on the component:

DashEmojiMart(
    id="picker",
    style={
        "--em-rgb-accent": "250, 176, 5",
        "--em-rgb-background": "255, 255, 255",
        "--em-rgb-input": "245, 245, 245",
        "--em-rgb-color": "34, 36, 39",
    },
)

emojiButtonColors cycles hover backgrounds through a list instead:

DashEmojiMart(
    id="picker",
    emojiButtonColors=[
        "rgba(155,223,88,.7)",
        "rgba(149,211,254,.7)",
        "rgba(247,233,34,.7)",
    ],
)

Source

# File: docs/theming/example.py

"""Three theme modes side by side, plus a picker bound to this site's own toggle.

The right-hand picker has no `theme` control of its own: a clientside callback maps the
site's stored colour scheme onto its `theme` prop, so the sun/moon button in the header
drives it. That is the pattern to copy for an app with its own theme switch — `theme` is
re-read on every render, so no remount is needed.
"""

import dash_mantine_components as dmc
from dash import Input, Output, callback, clientside_callback, html

from dash_emoji_mart import DashEmojiMart

component = dmc.Stack(
    [
        dmc.SegmentedControl(
            id="theme-mode",
            data=[
                {"value": "light", "label": "light"},
                {"value": "dark", "label": "dark"},
                {"value": "auto", "label": "auto"},
            ],
            value="light",
        ),
        dmc.SimpleGrid(
            [
                dmc.Stack(
                    [
                        dmc.Text("Explicit theme", fw=600, size="sm"),
                        DashEmojiMart(
                            id="theme-explicit-picker",
                            theme="light",
                            perLine=8,
                            emojiSize=22,
                            maxFrequentRows=1,
                            previewPosition="none",
                        ),
                        dmc.Text(id="theme-explicit-out", size="xl"),
                    ],
                    gap="xs",
                ),
                dmc.Stack(
                    [
                        dmc.Text("Follows this site's toggle", fw=600, size="sm"),
                        DashEmojiMart(
                            id="theme-synced-picker",
                            perLine=8,
                            emojiSize=22,
                            maxFrequentRows=1,
                            previewPosition="none",
                        ),
                        dmc.Text(
                            "Flip the sun/moon in the header.",
                            size="xs",
                            c="dimmed",
                        ),
                    ],
                    gap="xs",
                ),
            ],
            cols={"base": 1, "md": 2},
            spacing="xl",
        ),
    ],
    gap="md",
)


@callback(Output("theme-explicit-picker", "theme"), Input("theme-mode", "value"))
def set_theme(value):
    return value or "auto"


@callback(Output("theme-explicit-out", "children"), Input("theme-explicit-picker", "value"))
def show(value):
    return value or ""


# The site header stores the active colour scheme in `color-scheme-storage`.
# Clientside so the picker repaints in the same frame as the rest of the page.
clientside_callback(
    "(scheme) => scheme || 'light'",
    Output("theme-synced-picker", "theme"),
    Input("color-scheme-storage", "data"),
)

Source: /theming

Note for AI agents: This is the static, prerendered view of an interactive Dash application served because we detected a non-JS user agent. Full prose docs: