pytest-dev / pytest-dev/pytest-html
Feature: pluggable HTML themes via Jinja2 template inheritance
@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 suiteduration_display— human-readable total durationtotal_count— total tests excluding reruns
This is ~15 lines in _generate_report().
Benefits
- Backward compatible —
classicis 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
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.