pytest-dev / pytest-dev/pytest-html

Feature: pluggable HTML themes via Jinja2 template inheritance

Open
#1,029 0 comments 1 reaction 1 assignee View on GitHub

@RonnyPfannschmidt is already working on this.

Since May 24, 2026.

Dominant language
Python
Stars
779
Forks
260
PR merge metrics
No merged PRs in 30d

Description

Motivation

There is clear user demand for a modernised report UI (see #913, #904, #884). Currently, anyone who wants a different visual style has to override CSS after the fact or maintain a separate fork. pytest-html could instead offer first-class theme support, making it trivial to ship alternative report styles and for the community to contribute new ones.

Proposed design

1. Jinja2 template inheritance with {% extends %}

Factor the current index.jinja2 into a base template and per-theme overrides:

resources/
  base.jinja2              # shared HTML skeleton with {% block %} placeholders
  classic/
    layout.jinja2          # {% extends "base.jinja2" %} — current look
    style.css
  modern/
    layout.jinja2          # {% extends "base.jinja2" %} — card-based dashboard
    style.css

Only 4 blocks actually differ between visual styles:

  • {% block header %} — page title / navbar
  • {% block summary %} — run stats presentation (flat text vs stat cards with percentages)
  • {% block filters %} — checkboxes vs toggle switches
  • {% block footer %} — generation info placement

Everything else is shared in base.jinja2: the results table, the <template> for result rows, the media viewer, the JS data container, and the script include.

2. Config option
# pytest.ini / pyproject.toml
[tool.pytest.ini_options]
html_theme = "modern"   # default: "classic"
# plugin.py
parser.addini(
    "html_theme",
    type="string",
    default="classic",
    help="HTML report theme ('classic' or 'modern').",
)
3. Template loader change

Pass [theme_path, resources_path] to Jinja2's FileSystemLoader so each theme's layout.jinja2 can do {% extends "base.jinja2" %} and the loader resolves base.jinja2 from the parent resources directory. Minimal change to _read_template.

4. Extra template variables

A modern card-based theme benefits from a few additional pre-computed template variables. These are cheap to compute and can be passed unconditionally so all themes have them available:

  • start_time / end_time — formatted timestamps for the suite
  • duration_display — human-readable total duration
  • total_count — total tests excluding reruns

This is ~15 lines in _generate_report().

Benefits

  • Backward compatibleclassic is the default; no one's custom CSS breaks
  • Extensible — new themes are just a directory with layout.jinja2 + style.css
  • Low risk — the JS logic, data model, hooks, and Python backend are untouched
  • Community-friendly — lowers the bar for contributing visual improvements

What this does NOT change

  • No new features or hooks — purely a presentation-layer refactor
  • No change to the default appearance (classic remains default)
  • No JS modifications required

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.