Skip to content

✨ Add sd_custom_roles: config-defined badge roles with baked tooltips#295

Open
chrisjsewell wants to merge 2 commits into
mainfrom
brief/26-custom-roles
Open

✨ Add sd_custom_roles: config-defined badge roles with baked tooltips#295
chrisjsewell wants to merge 2 commits into
mainfrom
brief/26-custom-roles

Conversation

@chrisjsewell

@chrisjsewell chrisjsewell commented Jul 16, 2026

Copy link
Copy Markdown
Member

Adds sd_custom_roles — the role counterpart to sd_custom_directives (#194), and the follow-up to the badge tooltips of #286. Repeated status badges get defined once in conf.py, fully TOML-representable:

sd_custom_roles = {
    "bdg-stable": {
        "inherit": "bdg-success",
        "tooltip": "A released, supported version",
    },
    "bdg-beta": {"inherit": "bdg-warning", "tooltip": "Interface may change"},
}

Then :bdg-stable:`v1.2` everywhere — no retyped ; tooltip suffixes. The docs present this as the primary pattern for repeated semantics, with the per-instance suffix as the one-off escape hatch (which still overrides a baked tooltip; precedence documented and tested).

Behaviour

  • v1 scope: the badge family (bdg, bdg-<color>, line/link/ref variants; extension point for icon/button roles noted in-code). A baked tooltip also applies to bare-form link/ref badges — where the suffix grammar deliberately refuses to split — and overrides a ref's automatic title.
  • Validation at config time: dict-of-dicts of primitives, unknown keys rejected naming the offender, unknown inherit warns and skips, unparseable role names rejected.
  • Registry safety (both review-caught): the clash guard now also covers docutils' canonical roles — a custom role named code, math or strong is warn+skipped instead of silently hijacking core rendering; and registrations are reconciled every build with ownership markers, so under in-process rebuilds (sphinx-autobuild) tooltip edits take effect, removed roles stop resolving, and nothing leaks between apps — matching sd_custom_directives behaviour. Both locked by regression tests (including a three-apps-in-one-process test that bypasses the fixture cleanup which had masked the leak).
  • A no-tooltip custom role is byte-identical in output to the role it inherits (tested), and all 69 built-in badge roles are byte-identical to main (the shared-constructor refactor is behaviour-preserving — review diffed an exhaustive page of every family).

Verification

  • Full test/lint/type matrix green (+13 dedicated tests incl. the meta-test syncs every SdConfig field requires); docs -nW with live examples rendering in the docs' own conf.py
  • Reviewer probe scripts (canonical-role hijack, in-process staleness/leak, role-name edge cases, grammar edges) all rerun clean post-fix

Add an ``sd_custom_roles`` config option (parallel to ``sd_custom_directives``)
to register badge roles that inherit a built-in badge role (``bdg``,
``bdg-<color>``, ``bdg-*-line``, ``bdg-link-*``, ``bdg-ref-*``) and optionally
bake in a default ``tooltip``. This is the recommended way to declare repeated
status badges (stable/beta/deprecated) once, instead of retyping the ``;
tooltip`` suffix at every use site.

- A baked tooltip is only the default: a per-instance ``; tooltip`` suffix
  overrides it. The shared badge-role constructor gains a ``tooltip`` keyword,
  used as the fallback when no suffix is present; a tooltip-less custom role is
  byte-identical to the role it inherits. A baked tooltip also applies to the
  bare form of link/ref badges (where the suffix grammar refuses to split) and
  overrides a reference's automatic title.
- Config validation: dict-of-dicts, unknown keys rejected, ``inherit`` a
  string, and the role name must be non-empty and whitespace-free (unicode,
  case and colons are accepted; docutils looks roles up case-insensitively).
- Registration reconciles on every ``config-inited`` against docutils'
  process-global role registry: our own names are refreshed (picking up
  tooltip/inherit edits in-process), names dropped from the config are
  unregistered (no stale leak into a later build or sibling app), and a name
  clashing with a foreign role - another extension's, or a docutils canonical
  role such as ``code``/``math``/``strong`` - is skipped with a warning.
- The field is fully TOML-representable and picked up by the config table.
- Docs frame the ``; tooltip`` suffix as the one-off escape hatch, with custom
  roles as the primary pattern (rst + myst examples, cross-linked with the
  custom directives docs, and a note recommending a ``bdg-`` name prefix).
@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.06173% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.64%. Comparing base (4f66f32) to head (e826af9).

Files with missing lines Patch % Lines
sphinx_design/config.py 84.00% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #295      +/-   ##
==========================================
+ Coverage   92.63%   92.64%   +0.01%     
==========================================
  Files          13       13              
  Lines        1371     1428      +57     
==========================================
+ Hits         1270     1323      +53     
- Misses        101      105       +4     
Flag Coverage Δ
pytests 92.64% <95.06%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant