Skip to main content

Tufte-style templates and helpers for Plotly charts

Project description

nolegend

Remove the legend to become one.

Tufte-style templates and helpers for Plotly. One import, beautiful charts.

Based on Edward Tufte's The Visual Display of Quantitative Information: maximize data-ink ratio, remove chartjunk, direct-label instead of legends, use range frames, and apply colorblind-safe palettes.

Why this exists

There is no Tufte package for Plotly on PyPI. matplotlib has matplotx, plotnine has theme_tufte(), ggplot2 has ggthemes — Plotly has nothing. The closest built-in template (simple_white) removes gridlines but stops there. The default Plotly output is noisy: legends, gridlines, aggressive colors, titles that describe axes instead of insights.

nolegend fills that gap with opinionated defaults and easy overrides.

What this is NOT

  • Not a general visualization framework. Plotly only.
  • Not a dashboard framework. Use marimo for layout.
  • Not a Tufte orthodoxy enforcer. We take the useful parts (data-ink ratio, range frames, direct labeling, small multiples) and skip the dogma.
  • Not a replacement for domain judgment. The library guides chart selection and color use, but the human picks the story.

Install

uv add nolegend

Claude Code skill

nolegend ships a visualization skill that teaches Claude Code the px → go workflow, Tufte principles, color rules, and marimo patterns.

npx skills add mojzis/nolegend

Once installed, Claude Code will automatically apply nolegend conventions whenever you create Plotly charts.

Quick start

import plotly.express as px
import nolegend

nolegend.activate()  # set as default template for all figures

df = px.data.gapminder().query("continent == 'Europe' and year == 2007")
fig = px.bar(df.nlargest(10, "gdpPercap"), x="country", y="gdpPercap")
fig.show()

The px → go workflow

Start fast with Plotly Express, then refine:

import plotly.express as px
import nolegend

# 1. Quick draft with px
fig = px.line(
    df, x="year", y="revenue", color="product",
    template="tufte",
)

# 2. Refine: axes span only the data
nolegend.range_frame(fig)

# 3. Refine: labels on the lines, not in a legend box
nolegend.direct_label(fig)

# 4. Refine: call out the key moment
nolegend.annotate_point(fig, x=2023, y=peak, text="Record quarter")

fig.show()

Color palettes

Six curated, colorblind-safe palettes:

Palette Use case
qualitative Categorical data (default)
ink Grayscale, maximum data-ink
duo Two series (dark + red accent)
earth Business/financial
slate Technical/scientific
sequential Ordered/continuous data
# Use a specific palette
fig = px.line(df, ..., template=nolegend.with_palette("earth"))

# Spotlight: highlight one series, gray out the rest
colors = nolegend.QUALITATIVE.spotlight(index=2)

Hard ceiling of 5 colors per chart. Gray is a deliberate design tool, not a leftover.

Sparklines

Word-sized graphics, inline with text:

spark = nolegend.sparkline([1, 4, 2, 5, 3, 7], width=150, height=30)
spark.show()

Dark mode

nolegend.activate(dark=True)
# or per-figure:
fig = px.scatter(df, ..., template="tufte_dark")

In marimo notebooks

import marimo as mo
import nolegend

# Auto-detect marimo theme
nolegend.activate(dark=mo.app_meta().theme == "dark")

# Reactive plotly charts
fig = px.scatter(df, x="x", y="y", template="tufte")
chart = mo.ui.plotly(fig)

API

Function What it does
activate(dark=False) Set tufte as default template
with_palette(name, dark=False) Register + return template with specific palette
range_frame(fig, pad=0.02) Trim axes to data range
direct_label(fig, position="right") Label lines directly, remove legend
sparkline(values, ...) Create a word-sized trend graphic
annotate_point(fig, x, y, text) Arrow annotation on a data point
strip_chartjunk(fig) Quick cleanup of any Plotly figure

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nolegend-0.1.2.tar.gz (115.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nolegend-0.1.2-py3-none-any.whl (9.2 kB view details)

Uploaded Python 3

File details

Details for the file nolegend-0.1.2.tar.gz.

File metadata

  • Download URL: nolegend-0.1.2.tar.gz
  • Upload date:
  • Size: 115.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for nolegend-0.1.2.tar.gz
Algorithm Hash digest
SHA256 b08a68724a1c9e49de8ef8fa9a713b0ca032945aef84f9b4918dfe0f2cee1c05
MD5 bba0c162293a5a330ab3039647c33924
BLAKE2b-256 534e36609cf6496ea2efa7105ae814f39ab808d399d674af8709aae015168d4e

See more details on using hashes here.

Provenance

The following attestation bundles were made for nolegend-0.1.2.tar.gz:

Publisher: workflow.yml on mojzis/nolegend

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file nolegend-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: nolegend-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 9.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for nolegend-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 40649f4611e7ce502d554a0bf9e3efd34d537d92bfd27be7a44e86fd6f2d7340
MD5 4c5c4b7d2f854a4ef3407b93479e694a
BLAKE2b-256 1c03c6772f03aa8e713cbd2592bc67efc0b67a179ac8447d23937cca23fde7a5

See more details on using hashes here.

Provenance

The following attestation bundles were made for nolegend-0.1.2-py3-none-any.whl:

Publisher: workflow.yml on mojzis/nolegend

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page