Authoring a Pack
A theme pack is a Python module that exports three objects: a PRESET
(colors, light + dark), a DESIGN_SYSTEM (typography / layout /
animation / interaction), and a PACK (the cohesive bundle). Register
all three and your pack is selectable from settings, the theme panel,
and the dev panel.
Minimum viable example
# my_app/themes/sunset.py
from djust.theming import (
AnimationStyle, ColorScale, DesignSystem, IconStyle, InteractionStyle,
LayoutStyle, SurfaceStyle, ThemePack, ThemePreset, ThemeTokens,
TypographyStyle, PATTERN_MINIMAL, ILLUST_LINE,
)
# ─── colors ──────────────────────────────────────────────────────────
DARK = ThemeTokens(
background=ColorScale(20, 80, 8), # warm near-black
foreground=ColorScale(30, 30, 95),
card=ColorScale(20, 80, 11),
card_foreground=ColorScale(30, 30, 95),
popover=ColorScale(20, 80, 11),
popover_foreground=ColorScale(30, 30, 95),
primary=ColorScale(15, 90, 60), # sunset orange
primary_foreground=ColorScale(20, 80, 8),
secondary=ColorScale(20, 80, 14),
secondary_foreground=ColorScale(30, 30, 95),
muted=ColorScale(20, 50, 15),
muted_foreground=ColorScale(20, 30, 60),
accent=ColorScale(15, 90, 60),
accent_foreground=ColorScale(20, 80, 8),
destructive=ColorScale(355, 75, 55),
destructive_foreground=ColorScale(0, 0, 100),
success=ColorScale(160, 60, 45),
success_foreground=ColorScale(0, 0, 100),
warning=ColorScale(40, 90, 55),
warning_foreground=ColorScale(0, 0, 8),
info=ColorScale(210, 60, 55),
info_foreground=ColorScale(0, 0, 100),
link=ColorScale(15, 90, 60),
link_hover=ColorScale(15, 90, 70),
code=ColorScale(20, 80, 14),
code_foreground=ColorScale(30, 30, 95),
selection=ColorScale(15, 80, 55),
selection_foreground=ColorScale(20, 80, 8),
brand=ColorScale(15, 90, 60),
brand_foreground=ColorScale(20, 80, 8),
border=ColorScale(0, 0, 18),
input=ColorScale(0, 0, 18),
ring=ColorScale(15, 90, 60),
surface_1=ColorScale(20, 80, 8),
surface_2=ColorScale(20, 80, 11),
surface_3=ColorScale(20, 80, 14),
)
LIGHT = ThemeTokens(
background=ColorScale(30, 100, 98), # warm cream paper
foreground=ColorScale(20, 80, 8),
# …same shape, light values
)
PRESET = ThemePreset(
name='sunset',
display_name='Sunset',
description='Warm dusk gradients on a near-black base',
light=LIGHT,
dark=DARK,
default_mode='dark',
radius=0.5,
)
# ─── design system (typography / layout / etc.) ──────────────────────
DESIGN_SYSTEM = DesignSystem(
name='sunset',
display_name='Sunset',
description='Wide-tracking display headings, monospace labels',
category='editorial',
typography=TypographyStyle(
name='sunset',
font_family_sans="'Inter', system-ui, sans-serif",
font_family_mono="'JetBrains Mono', monospace",
scale_ratio=1.25,
),
layout=LayoutStyle(name='sunset', density='comfortable', spacing_unit=4),
surface=SurfaceStyle(name='sunset', shadow_intensity='subtle', border_radius='medium'),
icons=IconStyle(name='sunset', style='outlined', stroke_width='1.5'),
animation=AnimationStyle(name='sunset', transition_style='smooth', duration_normal='0.25s'),
interaction=InteractionStyle(name='sunset', button_hover='color', focus_style='ring'),
)
# ─── pack (the cohesive bundle) ──────────────────────────────────────
PACK = ThemePack(
name='sunset',
display_name='Sunset',
description='Warm dusk preset on an editorial design system',
category='editorial',
design_theme='sunset',
color_preset='sunset',
icon_style=DESIGN_SYSTEM.icons,
animation_style=DESIGN_SYSTEM.animation,
pattern_style=PATTERN_MINIMAL,
interaction_style=DESIGN_SYSTEM.interaction,
illustration_style=ILLUST_LINE,
)
Registration
Register all three during app startup:
# my_app/apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = 'my_app'
def ready(self):
from djust.theming import register_pack, register_preset, register_design_system
from .themes.sunset import PRESET, DESIGN_SYSTEM, PACK
register_preset(PRESET)
register_design_system(DESIGN_SYSTEM)
register_pack(PACK)
Then point your settings at it:
LIVEVIEW_CONFIG = {
'theme': {'pack': 'sunset', 'default_mode': 'dark'},
}
Mixing and matching
You don't need a custom design system to ship a custom pack. You can build on a shipped one:
from djust.theming import get_registry
# Pair your custom preset with the shipped 'editorial' design system.
editorial_ds = get_registry().get_design_system('editorial')
PACK = ThemePack(
name='sunset-editorial',
display_name='Sunset Editorial',
description='Sunset colors with the editorial design system',
category='editorial',
design_theme='editorial',
color_preset='sunset',
icon_style=editorial_ds.icons,
animation_style=editorial_ds.animation,
pattern_style=PATTERN_MINIMAL,
interaction_style=editorial_ds.interaction,
illustration_style=ILLUST_LINE,
)
This is how the shipped packs are structured — many of them share design
systems and only differ in their PRESET.
Field reference
ThemePreset
The full set is in
_types.py,
but the load-bearing fields:
| Field | Required | What it does |
|---|---|---|
name | yes | Unique identifier used in settings and the API. |
display_name | yes | Human-readable name shown in the theme panel. |
light / dark | yes | ThemeTokens for each mode. Both required. |
default_mode | no | 'light' or 'dark'. Determines which set goes into :root. Default: 'light'. |
radius | no | Border-radius multiplier in rem. Default: 0.5. |
extra_css_vars | no | Project-specific knobs. See extra_css_vars. |
extra_css_vars_light / _dark | no | Mode-specific overrides. |
surface | no | SurfaceTreatment for glass / gradient / noise effects. |
ThemeTokens
22 color slots — see Tokens for the full table and
what each one paints. Every slot is a ColorScale(hue, saturation, lightness) with HSL values 0-360 / 0-100% / 0-100%.
DesignSystem
| Field | What it does |
|---|---|
typography | Font families, scale ratio, weight scale. |
layout | Density (compact / comfortable / spacious), spacing unit. |
surface | Shadow intensity, border-radius preset, surface treatment. |
icons | Style (outlined / filled / rounded), stroke width, corner rounding. |
animation | Entrance / hover / loading effects, durations, easing. |
interaction | Button-hover, link-hover, card-hover, focus-style behaviors. |
ThemePack
The cohesive bundle — references a preset name and a design-system name, plus pattern + illustration choices and copies of icon / animation / interaction styles for cross-pack querying.
Testing your pack
Local dev:
- Add the pack module + register it in
apps.py:ready(). - Restart the dev server.
- Open
/_djust/panel/(DEBUG=True) → Theming tab → swap to your pack. - Click through every page that matters; look for color leaks (see Troubleshooting).
- Toggle light / dark mode; verify both sets work.
See also
- Tokens — full reference for every color slot.
- Structure vs. style — what does NOT belong in a pack.
- Source: djust.theming.themes — every shipped pack as a reference.