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:
| Value | Behaviour |
|---|---|
"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:
- /theming/llms.txt — LLM-friendly documentation
- /sitemap.xml
- /robots.txt