When you are working on a project that produces many plots (a data analysis pipeline, a research codebase, a dashboard toolkit), it’s tempting to copy-paste your styling code into every script. Axis label sizes, font weights, line widths, legend formatting: they all accumulate into a sizeable block that quietly duplicates itself across your project. The problem surfaces the moment you decide to change something. Adjust a font size once, and you now have twelve scripts that disagree with each other, and you will not notice until you are assembling figures side by side at 11 pm before a deadline.
The fix is to extract the style definition into a single shared file and have every script load it. This is a straightforward application of the same principle behind shared configuration files, CSS stylesheets, or design tokens: one authoritative source, many consumers. Each script remains unaware of the details and simply inherits the style, so changing the theme in one place propagates everywhere automatically. It also makes the style testable and reviewable in isolation, which matters if you are tweaking things iteratively or collaborating with others.
In Julia with CairoMakie, the shared file defines a Theme object and activates it with set_theme!. Each script loads it via include using @__DIR__ so the path is always relative to the script’s own location rather than wherever you launched Julia from. The pattern looks like this:
# styles/plot_theme.jl ← lives in a shared utils or styles directory
publication_theme = Theme(
fontsize = 16,
linewidth = 2.0,
Axis = (
xlabelfont = :bold,
ylabelfont = :bold,
xgridvisible = false,
ygridvisible = false,
),
Legend = (
labelsize = 14,
framevisible = false,
),
)
set_theme!(publication_theme)
# any_script.jl ← one line replaces the entire inline block
include(joinpath(@__DIR__, "..", "styles", "plot_theme.jl"))
# ... all your plotting code here ...
set_theme!() # reset to default when done
The same pattern works in Python with Matplotlib (rcParams in a shared module), R with ggplot2 (a shared theme() object), or JavaScript with charting libraries that accept a config object. The language details differ; the principle does not.
What about scripts that need slightly different styling? A shared theme does not mean every plot must look identical. It means every plot starts from the same baseline. Makie provides three tools for overriding it, each suited to a different scope.
For a script that consistently needs a small variation from the base (say, all its figures are small inset panels and need a reduced font size), call update_theme! immediately after loading the shared file. It merges the new values on top without replacing the rest:
include(joinpath(@__DIR__, "..", "styles", "plot_theme.jl"))
update_theme!(fontsize = 14, Axis = (xticklabelsize = 12,))
For a single figure within a script that needs different styling, use with_theme. It applies a temporary overlay for the duration of the block and restores the global theme automatically when the block exits:
with_theme(Theme(markersize = 6, Axis = (xticklabelsize = 14,))) do
fig = Figure()
# ... build this one figure ...
save("special_figure.pdf", fig)
end
For a single element that is just a one-off exception, pass keyword arguments directly to the plot call. Explicit kwargs always win over any theme, no wrapping needed:
ax = Axis(fig[1, 1], xlabel = "Time", xlabelsize = 20)
The priority order is: explicit kwargs beat with_theme beats the global theme. This layering means the shared file can stay focused on the non-negotiables (font weights, grid visibility, spine widths, legend style) and leave room for legitimate per-script variation without anyone reaching for copy-paste.
The same layered override idea exists in most styling systems: CSS specificity rules, Matplotlib’s rcParams plus per-call kwargs, ggplot2’s theme() plus element_* overrides. The shared file is the stylesheet; the scripts are the components that can deviate where they have good reason to.
If Claude Code is part of your workflow, this is different from a Claude Code custom command. A custom slash command (/explain-method, /docstring, etc.) is a prompt template you invoke deliberately when you want Claude to perform a specific, bounded transformation: writing a docstring, explaining a method, scaffolding a note. A shared theme file is just ordinary project architecture: it exists in your repository, runs without Claude, and benefits every collaborator and every CI job that ever touches your plots. The right way to use Claude Code here is to document the convention in your CLAUDE.md so that whenever Claude helps you write a new script, it knows to include the shared file rather than reinlining the block. The theme file solves the duplication problem in the code; the CLAUDE.md entry solves it in the AI-assisted workflow.


Leave a Reply