You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is a tracking issue consolidating a full triage of the open backlog (56 issues, 14 PRs as of 2026-07), a code-level review of the extension, and a forward plan β to agree direction before implementation starts.
Implementation briefs: each work item below has a detailed, self-contained brief (one PR each, with file:line references, steps, tests, and acceptance criteria) on the claude/sphinx-design-modernize-orr9g0 branch under briefs/, including an execution plan with parallelization tracks and a file-conflict matrix.
Verified: OCTICON_CSS in icons.py was dead code. Original fork branch couldn't re-trigger required checks (2024 runs too old to re-run), so re-created with authorship preserved
Superseded by the merged #267 (pins refreshed + dependabot groups)
2. Open issue triage (by cluster)
i18n β β buttons fixed
#44, #96, #263 closed by #270. Remaining: #234 (table content inside tabs missing from .pot) β separate extraction bug to investigate. Known gap documented in #270: button-ref text is absent from .pot extraction (std-domain xref resolution flattens explicit-title content before the gettext builder extracts); translation itself works.
Static files / builder lifecycle β β fixed
#200, #235 closed by #276: static assets are now served via Sphinx's standard html_static_path mechanism (HTML-format builders only), nothing is written into outdir outside Sphinx's own copying, and the Sphinx floor moved to β₯7.2.
LaTeX output
#107 (dropdown β \subsubsection*), #218 (same for tabs), #179 (grids with images), #242 (fontawesome5 package clash β fix in progress via the FontAwesome modernization). The extension has essentially no LaTeX visitors; everything degrades via sphinxuseclass. Plan: a dedicated LaTeX pass β dropdown/tab-item β boxed environments (e.g. sphinxShadowBox-style), grids β sequential blocks, and conditional fontawesome5 loading.
#6, #77, #80, #177, #165, #182 all closed with explanations; #260 β done in #277 (public sphinx_design.testing module).
3. Modernization plan
Rework static-asset handling β done in β»οΈ Serve static assets via html_static_pathΒ #276 β update_css_js/update_css_links replaced by a single builder-inited hook registering sphinx_design/static/ on html_static_path + add_css_file/add_js_file (HTML-format builders only, served filenames unchanged); Sphinx floor bumped to β₯7.2 (native ?v= checksums), md5 branch and test-suite compat shims deleted.
CI coverage is silently dead β fixed in π§ͺ Fix dead codecov upload, modernize CI actionsΒ #272 (condition, codecov-action v5, action bumps, concurrency group). Follow-up idea from Phase 1 friction: run the no-myst tox env in CI and widen tox -e mypy to cover tests/, so all checker environments agree (several Phase 1 failures were only visible in envs CI doesn't run). Phase 2 addendum: the ~=7.0 CI rail resolves to 7.4.x, so the declared 7.2 floor is never CI-tested β three floor-only bugs were caught in local pinned-version runs; consider adding a sphinx==7.2.* matrix entry.
PyPI Trusted Publishing (OIDC) β replace the long-lived PYPI_KEY secret + flit publish with pypa/gh-action-pypi-publish under a pypi environment; attestations for free. Deferred for now β needs a one-time publisher registration on PyPI; revisit before the next release.
Declarative configuration β landed in β»οΈ Central declarative SdConfig dataclassΒ #274: all sd_* options on a typed dataclass (modelled on myst-parser's MdParserConfig + validators), every field TOML-representable. New options (tabs storage prefix, FontAwesome source/LaTeX) are being added as fields.
4. Card directive: replace bespoke header/footer syntax with a pure parse
Today card (and grid-item-card via CardDirective.create_card) pre-processes its raw source lines: split_content regex-scans for the first ^{3,} line (header separator) and the last \+{3,} line (footer separator), slices the StringList into three chunks, and nested-parses each separately.
Problems with this:
False positives in nested content β a +++ or ^^^ line inside a nested code-block/literal block (e.g. Jupytext cell markers, rST section underlines in examples) is treated as a separator. The "last occurrence" heuristic for footers mitigates but doesn't solve.
Approximate source mapping β there's a literal TODO set proper lines in _create_component; error locations inside cards are off.
Invisible to tooling β the separators aren't part of any parse; formatters, MyST linters/LSPs, and other renderers (including any future non-Python MyST implementation) can't understand card structure.
Adopt the same structural pattern as tab-set/tab-item:
Register card-header and card-footer directives. Content of card is parsed with one ordinary nested_parse (pure rST/MyST β no raw-text surgery).
After parsing, the card directive hoists any card-header/card-footer component children into their slots and wraps the remaining siblings in card-body. Pure doctree manipulation, correct line attribution for free.
card-header/card-footer used outside a card warn, mirroring tab-item.
.. card:: Card title
.. card-header::
Header content
Body content
.. card-footer::
Footer content
Ergonomics notes:
Slightly more verbose than ^^^/+++, but explicit, unambiguous, and identical in rST and MyST (MyST users already nest fences for tab-set/tab-item).
Optionally also add :header:/:footer:options accepting inline markup for the very common one-liner case β to decide.
Alternative considered (rejected): transition-based splitting
Parse the content and split on nodes.transition (the old sphinx-panels approach). Rejected because the two parsers disagree: ^^^^ (4+) parses as a transition in rST but +++ does not, while MyST tokenizes +++ as a block-break β so no single separator has consistent semantics across parsers, and transitions inside genuine card body content would be misinterpreted.
Deprecation pathway (not back-breaking)
vNext (0.8): both syntaxes work; docs/examples switch to the new sub-directives; ^^^/+++ remain the default-on legacy path.
Add config sd_card_legacy_separators (default True) and emit a once-per-document deprecation notice (subtype design.card_legacy, suppressible) when separators are used.
Publish a short migration guide (precedent: the sphinx-panels migration docs); the rewrite is mechanical enough for a python -m sphinx_design.migrate helper if wanted.
1.0/2.0: flip the default off; remove one major later.
5. New components & directive improvements
New directives that fit the repo's CSS-first / minimal-JS philosophy:
accordion β a group of dropdowns where opening one closes the others. Dropdowns already render as <details>/<summary>, and native HTML <details name="..."> gives exclusive-open behaviour with zero JS (Baseline 2024). Implementation is essentially a container directive assigning a shared name + CSS. Bootstrap-parity component that gets asked for regularly.
steps / step β numbered procedure steps (install guides, tutorials), CSS counters only. One of the most-copied custom CSS snippets across Sphinx sites.
aside (Aside boxesΒ #97) β floated call-out box with text wrap-around; CSS-only, long-standing request.
tooltip β either a role or a :tooltip: option on badges/buttons (Add tooltip for badgesΒ #81) in progress: ; tooltip suffix syntax on badge roles.
Improvements to existing components (no new directives): vertical tab orientation (#219), carousel controls via CSS scroll-snap (#181, #133), card img-left/right (#211), dropdown FA icon option (#233).
Considered and decided out of scope: menu-style dropdowns / nav widgets (#165, closed) β interactive navigation rather than content components; better served by themes.
Context
This is a tracking issue consolidating a full triage of the open backlog (56 issues, 14 PRs as of 2026-07), a code-level review of the extension, and a forward plan β to agree direction before implementation starts.
Groundwork already landed: v0.7.0 (Python 3.11β3.14, Sphinx 7β9) via #250/#255/#256,
AGENTS.md(#251, #254), Sass@importβ@usemigration (#259), tab-set crash fix (#258).Implementation briefs: each work item below has a detailed, self-contained brief (one PR each, with file:line references, steps, tests, and acceptance criteria) on the
claude/sphinx-design-modernize-orr9g0branch underbriefs/, including an execution plan with parallelization tracks and a file-conflict matrix.1. Open PR triage
OCTICON_CSSinicons.pywas dead code. Original fork branch couldn't re-trigger required checks (2024 runs too old to re-run), so re-created with authorship preserved[dependency-groups]pins synced in #267@useSCSS with author credit, as part of the a11y pass (brief 10, in progress).readthedocs.ymlon main already hassphinx.configurationmyst-parser>=4,<6groups)2. Open issue triage (by cluster)
i18n β β buttons fixed
#44, #96, #263closed by #270. Remaining: #234 (table content inside tabs missing from.pot) β separate extraction bug to investigate. Known gap documented in #270:button-reftext is absent from.potextraction (std-domain xref resolution flattens explicit-title content before the gettext builder extracts); translation itself works.Static files / builder lifecycle β β fixed
#200, #235closed by #276: static assets are now served via Sphinx's standardhtml_static_pathmechanism (HTML-format builders only), nothing is written intooutdiroutside Sphinx's own copying, and the Sphinx floor moved to β₯7.2.LaTeX output
#107 (dropdown β
\subsubsection*), #218 (same for tabs), #179 (grids with images), #242 (fontawesome5package clash β fix in progress via the FontAwesome modernization). The extension has essentially no LaTeX visitors; everything degrades viasphinxuseclass. Plan: a dedicated LaTeX pass β dropdown/tab-item β boxed environments (e.g.sphinxShadowBox-style), grids β sequential blocks, and conditionalfontawesome5loading.Tabs
sessionStorage(despite the JS header comment claiming localStorage) β so selection does not survive a new browsing session. Fix in progress: switch tolocalStorage+ a configurable storage-key prefix:name:is used in URLΒ #239 open tab when its anchor is in the URL fragment (in progress, same JS pass)aria-controlswiring in progress, folded into the a11y pass with Restore focus ringsΒ #230label.onclickinstead of the radio'schangeevent; fix in progress), Scope of tabs is visually unclearΒ #173 (tab scope CSS), Vertically-oriented tabsΒ #219 (vertical orientation option), Tab-item is modifying .html filesΒ #238 (raw HTML inside tab-item)Cards / grids
comments inside a grid generate confusing warningsΒ #86β closed by π Ignore comments/targets in component child validationΒ #271 (+ anchor follow-up π Keep target anchors rendered in tab-set and tab-set-codeΒ #275)Multi-line content in dropdown has extra spacing on first lineΒ #40β fixed in π Stamp sd-card-text on direct paragraphs only, never replace classesΒ #278: the dropdown transform no longer wipes user paragraph classes (inverted conditional), andsd-card-textis stamped on direct child paragraphs only (a CSS-child-selector alternative is noted as a follow-up for the card redesign)grid-item-cardapplies:padding:to the outersd-coldiv, and the plaincarddirective has nopaddingoption at all), docutils hyperlink targets don't work for clickable cardsΒ #164 (docutils hyperlink targets as card links). Also: the card:link:option shares :ref: does not resolve autosectionlabelΒ #110's whitespace-stripping bug (directives.uriin the option spec) β targeted follow-up in progress:link-newtab:), [Feature request] Add class-link option for cardsΒ #261 (:class-link:), CSS Class for Thumbnails/Images with fixed HeightΒ #152 (fixed-height image class), Add img-left and img-right to grid-item-cardΒ #211 (img-left/right), After screen resize or on mobile card-carousel looks badΒ #133/Card Carousel with Controls/IndicatorsΒ #181 (carousel options/controls), ENH: Adddownloadas clickable ref type for CardsΒ #56, Changing colors on cardsΒ #157Icons
β fixed/documented in π Stop icon roles leaking SVG into toc labels and searchΒ #279: icon roles no longer leak SVG into toctree labels or the search index (newtoctreeskips an entry if its title begins with an inline icon directiveΒ #99/icons not shown correctly in rst toctreeΒ #117sd_iconnode with emptyastext()); icons not shown correctly in rst toctreeΒ #117 documented as a core-Sphinx limitation (toctree entry titles are plain text). Bonus:article-infoicons regained their silently-droppedsd-pr-2spacing classRefs / links
:ref: does not resolve autosectionlabelΒ #110, Nested parse broken for button-ref but not button-linkΒ #228β fixed in π Fix button-ref target whitespace and nested contentΒ #281:button-refno longer strips whitespace from targets (multi-word autosectionlabel labels resolve;ref-type: reflowercases for:ref:parity), and rich/nested button content survives resolution instead of being flattened. (Card:link:follow-up for the same root cause in progress β see cards cluster)button-linkURL other than a string?Β #222 (URL from substitution)Docs-only quick wins β β done
#172, #122, #184, #247closed by #273. Remaining: #52 (indent MyST examples).Close / answer β β done
#6, #77, #80, #177, #165, #182 all closed with explanations;
#260β done in #277 (publicsphinx_design.testingmodule).3. Modernization plan
Rework static-asset handlingβ done in β»οΈ Serve static assets via html_static_pathΒ #276 βupdate_css_js/update_css_linksreplaced by a singlebuilder-initedhook registeringsphinx_design/static/onhtml_static_path+add_css_file/add_js_file(HTML-format builders only, served filenames unchanged); Sphinx floor bumped to β₯7.2 (native?v=checksums), md5 branch and test-suite compat shims deleted.CI coverage is silently deadβ fixed in π§ͺ Fix dead codecov upload, modernize CI actionsΒ #272 (condition, codecov-action v5, action bumps, concurrency group). Follow-up idea from Phase 1 friction: run theno-mysttox env in CI and widentox -e mypyto covertests/, so all checker environments agree (several Phase 1 failures were only visible in envs CI doesn't run). Phase 2 addendum: the~=7.0CI rail resolves to 7.4.x, so the declared 7.2 floor is never CI-tested β three floor-only bugs were caught in local pinned-version runs; consider adding asphinx==7.2.*matrix entry.PYPI_KEYsecret +flit publishwithpypa/gh-action-pypi-publishunder apypienvironment; attestations for free. Deferred for now β needs a one-time publisher registration on PyPI; revisit before the next release.Drop theβ done in β»οΈ Serve static assets via html_static_pathΒ #276 β all call sites now use_compat.findallshimnode.findall(...)directly (note for downstream: the private helper is gone).Development Status :: 4 - Betaβ5 - Production/Stable; adopt PEP 639license = "MIT";refresh theme extras pins(done in β¬οΈ Refresh dev/docs pins, group dependabot updatesΒ #267); plan a 1.0 once i18n + static-asset fixes land.a11y pass(in progress: Restore focus ringsΒ #230 + Include radio/label ID as part of tab-content divΒ #30); FA import opt-out (in progress: FontAwesome import interfering with Pro LicenseΒ #174).no i18n/gettext tests(β added in π Keep buttons intact under gettext translationΒ #270); JS behaviour (tab sync) still entirely untested β doctree regressions can't catch Click+drag on tab causes synced tabs to go out of syncΒ #46/Tab-item is modifying .html filesΒ #238-class bugs, and the π Ignore comments/targets in component child validationΒ #271 anchor bugs (fixed in π Keep target anchors rendered in tab-set and tab-set-codeΒ #275) were likewise invisible to doctree assertions. Add a minimal Playwright smoke test.Declarative configurationβ landed in β»οΈ Central declarative SdConfig dataclassΒ #274: allsd_*options on a typed dataclass (modelled on myst-parser'sMdParserConfig+ validators), every field TOML-representable. New options (tabs storage prefix, FontAwesome source/LaTeX) are being added as fields.4. Card directive: replace bespoke header/footer syntax with a pure parse
Today
card(andgrid-item-cardviaCardDirective.create_card) pre-processes its raw source lines:split_contentregex-scans for the first^{3,}line (header separator) and the last\+{3,}line (footer separator), slices theStringListinto three chunks, and nested-parses each separately.Problems with this:
+++or^^^line inside a nested code-block/literal block (e.g. Jupytext cell markers, rST section underlines in examples) is treated as a separator. The "last occurrence" heuristic for footers mitigates but doesn't solve.TODO set proper linesin_create_component; error locations inside cards are off.add_card_child_classesthen stamps(β fixed in π Stamp sd-card-text on direct paragraphs only, never replace classesΒ #278 β now direct children only).sd-card-textonto all descendant paragraphsProposal:
card-header/card-footersub-directives + doctree hoistingAdopt the same structural pattern as
tab-set/tab-item:card-headerandcard-footerdirectives. Content ofcardis parsed with one ordinarynested_parse(pure rST/MyST β no raw-text surgery).card-header/card-footercomponent children into their slots and wraps the remaining siblings incard-body. Pure doctree manipulation, correct line attribution for free.card-header/card-footerused outside a card warn, mirroringtab-item.Ergonomics notes:
^^^/+++, but explicit, unambiguous, and identical in rST and MyST (MyST users already nest fences for tab-set/tab-item).:header:/:footer:options accepting inline markup for the very common one-liner case β to decide.Alternative considered (rejected): transition-based splitting
Parse the content and split on
nodes.transition(the old sphinx-panels approach). Rejected because the two parsers disagree:^^^^(4+) parses as a transition in rST but+++does not, while MyST tokenizes+++as a block-break β so no single separator has consistent semantics across parsers, and transitions inside genuine card body content would be misinterpreted.Deprecation pathway (not back-breaking)
^^^/+++remain the default-on legacy path.sd_card_legacy_separators(defaultTrue) and emit a once-per-document deprecation notice (subtypedesign.card_legacy, suppressible) when separators are used.python -m sphinx_design.migratehelper if wanted.5. New components & directive improvements
New directives that fit the repo's CSS-first / minimal-JS philosophy:
accordionβ a group of dropdowns where opening one closes the others. Dropdowns already render as<details>/<summary>, and native HTML<details name="...">gives exclusive-open behaviour with zero JS (Baseline 2024). Implementation is essentially a container directive assigning a sharedname+ CSS. Bootstrap-parity component that gets asked for regularly.steps/stepβ numbered procedure steps (install guides, tutorials), CSS counters only. One of the most-copied custom CSS snippets across Sphinx sites.aside(Aside boxesΒ #97) β floated call-out box with text wrap-around; CSS-only, long-standing request.tooltipβeither a role or ain progress::tooltip:option on badges/buttons (Add tooltip for badgesΒ #81); tooltipsuffix syntax on badge roles.Improvements to existing components (no new directives): vertical tab orientation (#219), carousel controls via CSS scroll-snap (#181, #133), card img-left/right (#211), dropdown FA icon option (#233).
Considered and decided out of scope: menu-style dropdowns / nav widgets (#165, closed) β interactive navigation rather than content components; better served by themes.
6. Suggested sequencing
_sphinx_design_staticdirectory in_build/latexΒ #200/Missing build directory leads tobuilder-initederrorΒ #235, superseded Limit copying _sphinx_design_static to HTML based buildersΒ #241; Sphinx floor now β₯7.2normalize_doctree_xmlviasphinx_design.testingΒ #260sd-card-textfixes (π Stamp sd-card-text on direct paragraphs only, never replace classesΒ #278) β closed Multi-line content in dropdown has extra spacing on first lineΒ #40toctreeskips an entry if its title begins with an inline icon directiveΒ #99, icons not shown correctly in rst toctreeΒ #117:name:is used in URLΒ #239/Support sticky tab selectionΒ #103), FontAwesome modernization (FontAwesome import interfering with Pro LicenseΒ #174/Latex build broken in Sphinx 8Β #242), badge tooltips (Add tooltip for badgesΒ #81), card:link:whitespace follow-upaccordion;steps;aside; card options (Option to open clickable cards in new tabΒ #170/[Feature request] Add class-link option for cardsΒ #261/CSS Class for Thumbnails/Images with fixed HeightΒ #152/Adding padding to a grid-card-item adds it to the sd-col div and not to sd-card divΒ #26)Feedback welcome on any of the verdicts above β in particular Β§4 (card syntax) and which of Β§5 to green-light.