Skip to content
dysonsphere

Theming

ds.theme() registers a global Altair theme, so every chart you build afterwards inherits perceptually uniform palettes and publication-ready styling. Call it once; override per chart with ordinary Altair encodings. Calling it again replaces the theme entirely.

The same Altair code, with exactly one line added - ds.theme():

import altair as alt
import dysonsphere as ds
from vega_datasets import data
ds.theme()
cars = ds.ensure_polars(data.cars()).drop_nulls(["Miles_per_Gallon", "Horsepower"])
chart = (
alt.Chart(cars)
.mark_point()
.encode(
x=alt.X("Horsepower:Q"),
y=alt.Y("Miles_per_Gallon:Q", title="Miles per gallon"),
color=alt.Color("Origin:N"),
)
)
Altair’s default theme
after ds.theme()

Dark-background figures keep getting more common: notebooks render inline charts against the dark editor themes most IDEs default to, and dark or black slide backgrounds are increasingly the norm in scientific and industrial presentations alike. ds.theme(darkmode=True) inverts the axis, label, and text ink while keeping palette colors intact - one flag, same chart code:

import altair as alt
import dysonsphere as ds
from vega_datasets import data
# darkmode=True inverts the ink; chartFill auto-resolves to black.
ds.theme(darkmode=True, transparent=False)
cars = ds.ensure_polars(data.cars()).drop_nulls(["Miles_per_Gallon", "Horsepower"])
chart = (
alt.Chart(cars)
.mark_point()
.encode(
x=alt.X("Horsepower:Q"),
y=alt.Y("Miles_per_Gallon:Q", title="Miles per gallon"),
color=alt.Color("Origin:N"),
)
)
default ink, white chartFill
after darkmode=True - black chartFill

These two pin transparent=False, so each bakes its own contrasting chartFill background (auto: white, black in darkmode). Ordinarily dysonsphere draws transparent instead, letting the page or slide provide the ground - every chart on this site swaps ink with the site’s light/dark toggle, which is this same darkmode flag at work.

Pass palette= to set the categorical color scheme - any palette name from ds.colors (browse them all in the Palettes gallery):

import altair as alt
import dysonsphere as ds
from vega_datasets import data
ds.theme(palette="blues", xLabelAngle=-45)
barley = data.barley()
chart = (
alt.Chart(barley)
.mark_bar()
.encode(
x=alt.X("variety:N", sort="-y", title="Variety"),
y=alt.Y("mean(yield):Q", title="Mean yield (bu/acre)"),
color=alt.Color("variety:N", legend=None),
)
)

palette sets the master categorical scheme. To recolor just one channel, use the per-type keys: categoryPalette, divergingPalette, heatmapPalette, ordinalPalette, rampPalette. Here only the continuous heatmap is restyled:

import altair as alt
import dysonsphere as ds
from vega_datasets import data
# Per-type palettes: heatmapPalette styles only continuous heatmaps, leaving the
# categorical palette untouched. Any name from ds.colors works.
ds.theme(heatmapPalette="lagoon")
cars = ds.ensure_polars(data.cars()).drop_nulls(["Miles_per_Gallon", "Horsepower"])
chart = (
alt.Chart(cars)
.mark_rect()
.encode(
x=alt.X("Horsepower:Q", bin=alt.Bin(maxbins=12)),
y=alt.Y("Miles_per_Gallon:Q", bin=alt.Bin(maxbins=12), title="Miles per gallon"),
color=alt.Color("count():Q", title="Cars"),
)
)

Named presets are selected with style=. One ships built in - "notebook", a big-screen exploration look (900 x 900 px, 18 pt type, dark, transparent) for working inline in a notebook before you produce the print-scale figure:

ds.theme(style="notebook") # explore at screen scale
ds.theme() # back to publication defaults

A dysonsphere.toml config file can customise the preset or add your own named styles - ds.theme(style="poster") picks up a [poster] section from your config. See the configuration guide for the full file format, search paths, and merge order; ds.create_config() scaffolds a starter file.

closed=True draws a full frame around the plot (all four spines):

import altair as alt
import dysonsphere as ds
from vega_datasets import data
# closed=True draws a full frame around the plot (all four spines).
ds.theme(closed=True)
cars = ds.ensure_polars(data.cars()).drop_nulls(["Miles_per_Gallon", "Horsepower"])
chart = (
alt.Chart(cars)
.mark_point()
.encode(
x=alt.X("Horsepower:Q"),
y=alt.Y("Miles_per_Gallon:Q", title="Miles per gallon"),
)
)

grid=True adds grid lines - solid by default; dashedGrid=True dashes them:

import altair as alt
import dysonsphere as ds
from vega_datasets import data
# grid=True draws grid lines; dashedGrid=False makes them solid instead of dashed.
ds.theme(grid=True)
stocks = data.stocks()
chart = (
alt.Chart(stocks)
.mark_line()
.encode(
x=alt.X("date:T", title=None),
y=alt.Y("price:Q", title="Price (USD)"),
color=alt.Color("symbol:N", title="Symbol"),
)
)

cornerRadius=True rounds bar tips (and rects and arcs) by a size derived from the chart dimensions; pass a float for explicit pixels.

import altair as alt
import dysonsphere as ds
from vega_datasets import data
# cornerRadius=True rounds bar tips (and rects/arcs) by a size derived from the
# chart dimensions; pass a float for explicit pixels.
ds.theme(palette="blues", xLabelAngle=-45, cornerRadius=True)
barley = data.barley()
chart = (
alt.Chart(barley)
.mark_bar()
.encode(
x=alt.X("variety:N", sort="-y", title="Variety"),
y=alt.Y("mean(yield):Q", title="Mean yield (bu/acre)"),
color=alt.Color("variety:N", legend=None),
)
)

xLabelAngle / yLabelAngle rotate axis labels (in degrees); alignment is derived from the sign, so crowded categories stay legible.

import altair as alt
import dysonsphere as ds
from vega_datasets import data
# Angle crowded categorical labels; alignment follows the sign automatically.
ds.theme(palette="blues", xLabelAngle=-45)
barley = data.barley()
chart = (
alt.Chart(barley)
.mark_bar()
.encode(
x=alt.X("variety:N", sort="-y", title=None),
y=alt.Y("mean(yield):Q", title="Mean yield (bu/acre)"),
color=alt.Color("variety:N", legend=None),
)
)

inwardTicks=True points tick marks into the plot area (physics-journal style) and defaults the frame to closed:

import altair as alt
import dysonsphere as ds
from vega_datasets import data
# inwardTicks=True points tick marks into the plot (physics-journal style);
# it also defaults the frame to closed.
ds.theme(inwardTicks=True)
cars = ds.ensure_polars(data.cars()).drop_nulls(["Miles_per_Gallon", "Horsepower"])
chart = (
alt.Chart(cars)
.mark_point()
.encode(
x=alt.X("Horsepower:Q"),
y=alt.Y("Miles_per_Gallon:Q", title="Miles per gallon"),
)
)

The theme exposes dozens of parameters - axisOffset, tickSize, inwardTicks, fontSize / secondaryFontSize / smallestFontSize, markSize, markStrokeWidth, bandPadding, sigFigs, and the axis-element toggles (xAxis, xTicks, xLabels, and their y counterparts). The configuration guide’s key reference documents every one of them, with defaults - and each is equally valid as a theme() keyword or a dysonsphere.toml entry.