diff --git a/AGENTS.md b/AGENTS.md index a7be8cff..2b2e050c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,9 +17,9 @@ gp-sphinx (`gp_sphinx`) is a shared Sphinx documentation platform for Python pro Key features: - `merge_sphinx_config()` API for shared defaults with per-project overrides -- Shared extension list (autodoc, intersphinx, myst_parser, sphinx_design, etc.) +- Shared extension list (autodoc, intersphinx, myst_parser, sphinx_ux_*, etc.) - Shared Furo theme configuration (CSS variables, fonts, sidebar, footer) -- Bundled workarounds (tabs.js removal, spa-nav.js injection) +- Bundled workarounds (spa-nav.js injection) - Shared font configuration (IBM Plex via Fontsource) ## Development Environment diff --git a/docs/_ext/package_reference.py b/docs/_ext/package_reference.py index 16bfe3ca..017c5ff7 100644 --- a/docs/_ext/package_reference.py +++ b/docs/_ext/package_reference.py @@ -200,6 +200,9 @@ class PackageDocsRecord: "@gp-sphinx/serene-tokens": "tokens", "sphinx-fonts": "tokens", "sphinx-ux-badges": "ux", + "sphinx-ux-octicons": "ux", + "sphinx-ux-grid": "ux", + "sphinx-ux-tabs": "ux", "sphinx-ux-autodoc-layout": "ux", "sphinx-vite-builder": "build-seo", "sphinx-gp-opengraph": "build-seo", @@ -902,7 +905,7 @@ def package_reference_markdown(package_name: str) -> str: def maturity_badge(maturity: str) -> str: - """Return a sphinx-design badge role for use in grid markdown output. + """Return a badge role (MyST ``{bdg-*}`` syntax) for use in grid markdown output. Used only in :func:`workspace_package_grid_markdown` which produces raw MyST markdown strings. Per-page package headers use the ``gp-sphinx-package-meta`` diff --git a/docs/_static/css/custom.css b/docs/_static/css/custom.css index 5a30f20e..ec28567f 100644 --- a/docs/_static/css/custom.css +++ b/docs/_static/css/custom.css @@ -286,251 +286,6 @@ img[src*="codecov.io"] { line-height: 1.5; } -/* ── Package page metadata strip ──────────────────────────── - * Selects the first

inside the top-level section that - * contains ONLY sphinx-design badges (no non-badge children). - * - * MyST renders {bdg-*} roles as inside - * a bare

that is a direct child of

, which is a - * direct child of
. - * - * :has() requires CSS Level 4 (all evergreen browsers ≥ 2024). - * Falls back gracefully: badges render inline without the flex strip. - * - * Overrides `article h1 { margin-bottom: 0.75rem }` (line 38) - * via equal specificity (0,0,2) and later source order. - * ────────────────────────────────────────────────────────── */ - -/* Tighten h1 → badge strip gap so they read as a unit */ -article > section > h1 { - margin-bottom: 0.2rem; -} - -/* Convert badge-only paragraph into a flex metadata strip */ -article > section > p:first-of-type:has(> .sd-badge:first-child):not(:has(*:not(.sd-badge))) { - display: flex; - align-items: center; - gap: 0.5rem; - margin-top: 0; - margin-bottom: 1.5rem; - padding: 0; - line-height: 1; -} - -/* ── Base badge reset ────────────────────────────────────── */ -.sd-badge { - display: inline-flex !important; - align-items: center; - vertical-align: middle; - font-size: 0.67rem; - font-weight: 600; - line-height: 1; - letter-spacing: 0.02em; - padding: 0.16rem 0.4rem; - border-radius: 0.22rem; - user-select: none; - -webkit-user-select: none; -} - -/* ── Maturity palette ──────────────────────────────────────── - * Subtle fill: Radix steps 12 (text), 11 (border), 3 (bg). - * Step-12 text passes WCAG AAA on step-3 tinted backgrounds. - * Step-11 border passes WCAG 1.4.11 (non-text contrast ≥ 3:1). - * - * !important matches sphinx-design's own !important on - * .sd-text-warning / .sd-text-success and .sd-outline-*. - * Without it, color and border-color are silently overridden. - * background-color does not need !important — sphinx-design - * does not set background on these badge variants. - * ─────────────────────────────────────────────────────────── */ -:root { - --badge-alpha-color: #4e2009; /* Radix amber-12 — 11.62:1 on amber-3 (AAA) */ - --badge-alpha-border: #ab6400; /* Radix amber-11 — 4.11:1 on card footer (WCAG 1.4.11 ✓) */ - --badge-alpha-bg: #ffedc6; /* Radix amber-3 — opaque tint, no color-mix */ - - --badge-beta-color: #193b2d; /* Radix green-12 — 10.55:1 on green-3 (AAA) */ - --badge-beta-border: #218358; /* Radix green-11 — 4.22:1 on card footer (WCAG 1.4.11 ✓) */ - --badge-beta-bg: #ddf3e4; /* Radix green-3 */ -} - -/* Furo explicit dark theme */ -body[data-theme="dark"] { - --badge-alpha-color: #ffca16; /* Radix amber-11 dark — 9.13:1 on #3f2700 (AAA) */ - --badge-alpha-border: #8f6424; /* Radix amber-8 dark */ - --badge-alpha-bg: #3f2700; /* Radix amber-3 dark */ - - --badge-beta-color: #3dd68c; /* Radix green-11 dark — 6.66:1 on #113b29 (AA) */ - --badge-beta-border: #2f7c57; /* Radix green-8 dark */ - --badge-beta-bg: #113b29; /* Radix green-3 dark */ -} - -/* Furo auto mode: system dark when not explicitly set to light */ -@media (prefers-color-scheme: dark) { - body:not([data-theme="light"]) { - --badge-alpha-color: #ffca16; - --badge-alpha-border: #8f6424; - --badge-alpha-bg: #3f2700; - --badge-beta-color: #3dd68c; - --badge-beta-border: #2f7c57; - --badge-beta-bg: #113b29; - } -} - -/* {bdg-warning-line} → .sd-badge.sd-outline-warning.sd-text-warning */ -.sd-badge.sd-outline-warning { - color: var(--badge-alpha-color) !important; - border-color: var(--badge-alpha-border) !important; - background-color: var(--badge-alpha-bg); -} - -/* {bdg-success-line} → .sd-badge.sd-outline-success.sd-text-success */ -.sd-badge.sd-outline-success { - color: var(--badge-beta-color) !important; - border-color: var(--badge-beta-border) !important; - background-color: var(--badge-beta-bg); -} - -/* ── Safety badge compatibility ───────────────────────────── - * Downstream projects like libtmux-mcp emit safety badges via - * a custom extension. Keep the docs-site copy aligned with the - * packaged theme CSS so previews match downstream builds. - * ────────────────────────────────────────────────────────── */ -:root { - --gp-sphinx-fastmcp-safety-readonly-bg: #1f7a3f; - --gp-sphinx-fastmcp-safety-readonly-border: #2a8d4d; - --gp-sphinx-fastmcp-safety-readonly-text: #f3fff7; - --gp-sphinx-fastmcp-safety-mutating-bg: #b96a1a; - --gp-sphinx-fastmcp-safety-mutating-border: #cf7a23; - --gp-sphinx-fastmcp-safety-mutating-text: #fff8ef; - --gp-sphinx-fastmcp-safety-destructive-bg: #b4232c; - --gp-sphinx-fastmcp-safety-destructive-border: #cb3640; - --gp-sphinx-fastmcp-safety-destructive-text: #fff5f5; -} - -h2:has(> .sd-badge[role="note"][aria-label^="Safety tier:"]), -h3:has(> .sd-badge[role="note"][aria-label^="Safety tier:"]), -h4:has(> .sd-badge[role="note"][aria-label^="Safety tier:"]) { - display: inline-flex; - align-items: center; - gap: 0.45rem; -} - -.sd-badge[role="note"][aria-label^="Safety tier:"] { - gap: 0.28rem; - font-weight: 700; - letter-spacing: 0.01em; - border: 1px solid transparent; -} - -.sd-badge[role="note"][aria-label^="Safety tier:"]::before { - font-style: normal; - font-weight: normal; - font-size: 1em; - line-height: 1; - flex-shrink: 0; -} - -.sd-badge.sd-bg-success[role="note"][aria-label^="Safety tier:"] { - background-color: var(--gp-sphinx-fastmcp-safety-readonly-bg) !important; - color: var(--gp-sphinx-fastmcp-safety-readonly-text) !important; - border-color: var(--gp-sphinx-fastmcp-safety-readonly-border); -} - -.sd-badge.sd-bg-warning[role="note"][aria-label^="Safety tier:"] { - background-color: var(--gp-sphinx-fastmcp-safety-mutating-bg) !important; - color: var(--gp-sphinx-fastmcp-safety-mutating-text) !important; - border-color: var(--gp-sphinx-fastmcp-safety-mutating-border); -} - -.sd-badge.sd-bg-danger[role="note"][aria-label^="Safety tier:"] { - background-color: var(--gp-sphinx-fastmcp-safety-destructive-bg) !important; - color: var(--gp-sphinx-fastmcp-safety-destructive-text) !important; - border-color: var(--gp-sphinx-fastmcp-safety-destructive-border); -} - -.sd-badge.sd-bg-success[role="note"][aria-label^="Safety tier:"]::before { - content: "🔍"; -} - -.sd-badge.sd-bg-warning[role="note"][aria-label^="Safety tier:"]::before { - content: "✏️"; -} - -.sd-badge.sd-bg-danger[role="note"][aria-label^="Safety tier:"]::before { - content: "💣"; -} - -h2 .sd-badge[role="note"][aria-label^="Safety tier:"], -h3 .sd-badge[role="note"][aria-label^="Safety tier:"] { - font-size: 0.68rem; - padding: 0.17rem 0.4rem; -} - -p .sd-badge[role="note"][aria-label^="Safety tier:"], -li .sd-badge[role="note"][aria-label^="Safety tier:"], -td .sd-badge[role="note"][aria-label^="Safety tier:"], -a .sd-badge[role="note"][aria-label^="Safety tier:"] { - font-size: 0.62rem; - padding: 0.12rem 0.32rem; -} - -code.docutils + .sd-badge[role="note"][aria-label^="Safety tier:"], -.sd-badge[role="note"][aria-label^="Safety tier:"] + code.docutils { - margin-left: 0.4em; -} - -/* ── Safety badge link behavior ──────────────────────────── - * Keep docs-site previews aligned with the packaged theme: - * tool links underline only the code token, not the gap or - * the attached safety badge. - * ────────────────────────────────────────────────────────── */ -a.reference:has(.sd-badge[role="note"][aria-label^="Safety tier:"]) { - text-decoration: none; -} - -a.reference:has(.sd-badge[role="note"][aria-label^="Safety tier:"]) - .sd-badge[role="note"][aria-label^="Safety tier:"] { - text-decoration: none; - vertical-align: middle; -} - -a.reference:has(.sd-badge[role="note"][aria-label^="Safety tier:"]) code { - text-decoration: none; - vertical-align: middle; -} - -a.reference:has(.sd-badge[role="note"][aria-label^="Safety tier:"]):hover code { - text-decoration: underline; -} - -/* Type badges (solid fills) — muted gray, scoped to metadata strip only. - * extension: {bdg-primary} → .sd-badge.sd-bg-primary - * coordinator: {bdg-success} → .sd-badge.sd-bg-success (solid fill) - * theme: {bdg-info} → .sd-badge.sd-bg-info - * .sd-bg-success and .sd-outline-success are mutually exclusive in sphinx-design. */ -article > section > p:first-of-type > .sd-badge.sd-bg-primary, -article > section > p:first-of-type > .sd-badge.sd-bg-success, -article > section > p:first-of-type > .sd-badge.sd-bg-info { - font-size: 0.6rem; - font-weight: 500; - letter-spacing: 0.04em; - text-transform: uppercase; - opacity: 0.65; - background-color: var(--color-background-border) !important; - color: var(--color-foreground-secondary) !important; - border: 1px solid var(--color-foreground-border); -} - -/* Card footer badges — compact and scannable in the grid index */ -.sd-card-footer .sd-badge { - font-size: 0.6rem; - font-weight: 500; - padding: 0.13rem 0.35rem; - letter-spacing: 0.03em; - text-transform: uppercase; - vertical-align: middle; -} - /* Per-package landing layout — rendered by PackageLandingDirective. * * The directive emits :class-container: gp-sphinx-package__landing-grid diff --git a/docs/conf.py b/docs/conf.py index 833a1b3b..79530502 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -46,6 +46,18 @@ 0, str(project_root / "packages" / "sphinx-ux-badges" / "src"), ) +sys.path.insert( + 0, + str(project_root / "packages" / "sphinx-ux-octicons" / "src"), +) +sys.path.insert( + 0, + str(project_root / "packages" / "sphinx-ux-grid" / "src"), +) +sys.path.insert( + 0, + str(project_root / "packages" / "sphinx-ux-tabs" / "src"), +) sys.path.insert( 0, str(project_root / "packages" / "sphinx-autodoc-fastmcp" / "src"), @@ -92,7 +104,6 @@ "package_reference", "sab_demo", "sab_meta", - "sphinx_ux_badges", "sphinx_autodoc_api_style", "sphinx_autodoc_pytest_fixtures", "sphinx_autodoc_docutils", diff --git a/docs/configuration.md b/docs/configuration.md index 697b901c..cc8d1175 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -83,12 +83,15 @@ already present. ## Injected `setup(app)` The returned config includes a `setup(app)` function from -{py:func}`gp_sphinx.config.setup`. It does two things: +{py:func}`gp_sphinx.config.setup`. It wires the runtime hooks that the +shared config depends on: | Action | Effect | | --- | --- | | `app.add_js_file("js/spa-nav.js", loading_method="defer")` | Registers the bundled SPA navigation script from `sphinx-gp-theme` | -| `app.connect("build-finished", remove_tabs_js)` | Removes `_static/tabs.js` after HTML builds as a `sphinx-inline-tabs` workaround | +| `app.connect("html-page-context", _inject_copybutton_bridge)` | Exposes `copybutton_selector` as a window global for `spa-nav.js` | +| `app.connect("html-page-context", _inject_fowt_prevention)` | Injects the FOWT-prevention head snippet to suppress flash-of-wrong-theme | +| `app.add_lexer("myst", MystLexer)` / `("myst-md", MystLexer)` | Registers the MyST source-block lexer aliases | ## Always-set coordinator values @@ -111,7 +114,7 @@ These are injected even though they are not exposed as `DEFAULT_*` constants: | Constant | Value | | --- | --- | -| `DEFAULT_EXTENSIONS` | `["sphinx.ext.autodoc", "sphinx_fonts", "sphinx.ext.intersphinx", "sphinx_autodoc_typehints_gp", "sphinx.ext.todo", "sphinx_inline_tabs", "sphinx_copybutton", "sphinx_gp_opengraph", "sphinx_gp_sitemap", "sphinxext.rediraffe", "sphinx_design", "myst_parser", "linkify_issues"]` | +| `DEFAULT_EXTENSIONS` | `["sphinx.ext.autodoc", "sphinx_fonts", "sphinx.ext.intersphinx", "sphinx_autodoc_typehints_gp", "sphinx.ext.todo", "sphinx_ux_tabs", "sphinx_copybutton", "sphinx_gp_opengraph", "sphinx_gp_sitemap", "sphinxext.rediraffe", "sphinx_ux_grid", "sphinx_ux_octicons", "sphinx_ux_badges", "myst_parser", "linkify_issues"]` | | `DEFAULT_SOURCE_SUFFIX` | `{".rst": "restructuredtext", ".md": "markdown"}` | | `DEFAULT_MYST_EXTENSIONS` | `["colon_fence", "substitution", "replacements", "strikethrough", "linkify"]` | | `DEFAULT_MYST_HEADING_ANCHORS` | `4` | @@ -138,7 +141,7 @@ These are injected even though they are not exposed as `DEFAULT_*` constants: | Constant | Value | | --- | --- | -| `DEFAULT_PYGMENTS_STYLE` | `"monokai"` | +| `DEFAULT_PYGMENTS_STYLE` | `"gp-sphinx-light"` | | `DEFAULT_PYGMENTS_DARK_STYLE` | `"monokai"` | | `DEFAULT_COPYBUTTON_PROMPT_TEXT` | regex matching Python, shell, and IPython prompts | | `DEFAULT_COPYBUTTON_PROMPT_IS_REGEXP` | `True` | diff --git a/docs/packages/index.md b/docs/packages/index.md index 170e8631..6865cea2 100644 --- a/docs/packages/index.md +++ b/docs/packages/index.md @@ -15,6 +15,9 @@ and independently installable. The rendering pipeline every autodoc extension consumes: - [`sphinx-ux-badges`](sphinx-ux-badges/index.md) — badge primitives and colour palette +- [`sphinx-ux-octicons`](sphinx-ux-octicons/index.md) — curated GitHub Octicons as a Sphinx `{octicon}` role +- [`sphinx-ux-grid`](sphinx-ux-grid/index.md) — CSS-Grid `{grid}` and `{grid-item-card}` directives +- [`sphinx-ux-tabs`](sphinx-ux-tabs/index.md) — drop-in tabs replacement for sphinx-inline-tabs and sphinx-design - [`sphinx-ux-autodoc-layout`](sphinx-ux-autodoc-layout/index.md) — structural presenter for `api-*` entry components - [`sphinx-autodoc-typehints-gp`](sphinx-autodoc-typehints-gp/index.md) — annotation normalization and type rendering - [`sphinx-fonts`](sphinx-fonts/index.md) — IBM Plex font preloading diff --git a/docs/packages/sphinx-ux-badges/reference.md b/docs/packages/sphinx-ux-badges/reference.md index 9cf3f9f6..9f286a59 100644 --- a/docs/packages/sphinx-ux-badges/reference.md +++ b/docs/packages/sphinx-ux-badges/reference.md @@ -2,6 +2,72 @@ # API Reference +## `{bdg-*}` MyST roles + +The extension registers a family of 22 MyST roles for inline badges +using semantic colour names. Each colour comes in a filled variant +(`{bdg-}`) and an outline variant (`{bdg--line}`). + +```{list-table} +:header-rows: 1 +:widths: 22 14 32 32 + +* - Colour + - Filled + - Filled example + - Outline example +* - `primary` + - `` {bdg-primary}`text` `` + - {bdg-primary}`primary` + - {bdg-primary-line}`primary` +* - `secondary` + - `` {bdg-secondary}`text` `` + - {bdg-secondary}`secondary` + - {bdg-secondary-line}`secondary` +* - `success` + - `` {bdg-success}`text` `` + - {bdg-success}`success` + - {bdg-success-line}`success` +* - `info` + - `` {bdg-info}`text` `` + - {bdg-info}`info` + - {bdg-info-line}`info` +* - `warning` + - `` {bdg-warning}`text` `` + - {bdg-warning}`warning` + - {bdg-warning-line}`warning` +* - `danger` + - `` {bdg-danger}`text` `` + - {bdg-danger}`danger` + - {bdg-danger-line}`danger` +* - `light` + - `` {bdg-light}`text` `` + - {bdg-light}`light` + - {bdg-light-line}`light` +* - `muted` + - `` {bdg-muted}`text` `` + - {bdg-muted}`muted` + - {bdg-muted-line}`muted` +* - `dark` + - `` {bdg-dark}`text` `` + - {bdg-dark}`dark` + - {bdg-dark-line}`dark` +* - `white` + - `` {bdg-white}`text` `` + - {bdg-white}`white` + - {bdg-white-line}`white` +* - `black` + - `` {bdg-black}`text` `` + - {bdg-black}`black` + - {bdg-black-line}`black` +``` + +The roles emit a `BadgeNode` carrying `gp-sphinx-badge`, +`gp-sphinx-badge--color-`, and either `gp-sphinx-badge--filled` +or `gp-sphinx-badge--outline`. Colour values are defined as CSS +custom-property triples (`-bg`, `-fg`, `-border`) in +`sab_palettes.css` and have dedicated dark-mode overrides. + ## Colour palette All semantic badge colours live in `sab_palettes.css` (registered by diff --git a/docs/packages/sphinx-ux-grid/dependents.md b/docs/packages/sphinx-ux-grid/dependents.md new file mode 100644 index 00000000..19d03865 --- /dev/null +++ b/docs/packages/sphinx-ux-grid/dependents.md @@ -0,0 +1,6 @@ +(sphinx-ux-grid-dependents)= + +# Dependents + +```{package-dependents} sphinx-ux-grid +``` diff --git a/docs/packages/sphinx-ux-grid/examples.md b/docs/packages/sphinx-ux-grid/examples.md new file mode 100644 index 00000000..f6de0ac5 --- /dev/null +++ b/docs/packages/sphinx-ux-grid/examples.md @@ -0,0 +1,128 @@ +(sphinx-ux-grid-examples)= + +# Examples + +## Four-breakpoint responsive grid + +Resize the browser to see the column count change. + +::::{grid} 1 2 3 4 +:gutter: 3 + +:::{grid-item-card} {octicon}`rocket` Tutorial +:link: tutorial +:link-type: doc +Working usage examples for `{grid}` and `{grid-item-card}`. +::: + +:::{grid-item-card} {octicon}`tools` How to +:link: how-to +:link-type: doc +Recipes for sync groups, links, images, and overrides. +::: + +:::{grid-item-card} {octicon}`book` Reference +:link: reference +:link-type: doc +Option reference for every directive. +::: + +:::{grid-item-card} {octicon}`light-bulb` Explanation +:link: explanation +:link-type: doc +Why CSS Grid, why custom properties, why one node. +::: +:::: + +## Cards with header / body / footer + +Markers `^^^` and `+++` split the card body into three regions. + +::::{grid} 1 1 2 2 +:gutter: 3 + +:::{grid-item-card} Three regions +{bdg-primary}`Header` +^^^ +Body text describing the card's main content. Renders inside +`.gp-sphinx-grid-card__body`. ++++ +{octicon}`info` Footer with a different background tint. +::: + +:::{grid-item-card} Two regions (body + footer) +Body-only opening — no `^^^` marker, so the card has no header. ++++ +{bdg-success}`Stable` · {bdg-info-line}`v0.0.1` +::: +:::: + +## Mixed-span items inside one grid + +`{grid-item}` accepts its own `:columns:` to override the parent +grid's breakpoint defaults. + +::::{grid} 1 2 4 4 +:gutter: 3 + +:::{grid-item} +:columns: 4 4 4 4 +:class: gp-sphinx-grid-card gp-sphinx-grid-card--outline +Full-width across mobile, then quarter-width on tablet and desktop. +::: + +:::{grid-item-card} +Standard card. +::: + +:::{grid-item-card} +Standard card. +::: + +:::{grid-item-card} +Standard card. +::: +:::: + +## Outline cards (no shadow) + +::::{grid} 1 2 2 2 +:gutter: 2 + +:::{grid-item-card} Subtle framing +:outline: +Low-density framing without a drop shadow. Useful for content +indexes. +::: + +:::{grid-item-card} {octicon}`star` Highlight +:outline: +Compose with `{octicon}` for an icon next to the title. +::: +:::: + +## RST authoring + +The directives also work in reStructuredText. The rendered HTML is +identical to the MyST forms above. + +```{eval-rst} +.. grid:: 1 2 3 3 + :gutter: 3 + + .. grid-item-card:: First + :shadow: sm + + Authored in reStructuredText. + + .. grid-item-card:: Second + :shadow: sm + + Same ``gp-sphinx-grid-card`` HTML as MyST source. + + .. grid-item-card:: Third + :shadow: sm + + Mix RST and MyST in the same project — both authoring + surfaces produce the same DOM. +``` diff --git a/docs/packages/sphinx-ux-grid/explanation.md b/docs/packages/sphinx-ux-grid/explanation.md new file mode 100644 index 00000000..6b6b73c8 --- /dev/null +++ b/docs/packages/sphinx-ux-grid/explanation.md @@ -0,0 +1,93 @@ +(sphinx-ux-grid-explanation)= + +# Explanation + +## CSS Grid, not Bootstrap floats + +Older grid systems (Bootstrap 4 and earlier) built columns out of +`float: left` with negative margins. `sphinx-ux-grid` uses CSS Grid +directly: every grid is `display: grid` with +`grid-template-columns: repeat(N, minmax(0, 1fr))`. The breakpoint +column count is set per-grid via inline custom properties, and the +static CSS reads the property at each media query. + +The upshot: + +- Items size proportionally without `width: percentage` arithmetic. +- Gutter is a single `gap` declaration, not nested padding. +- A single rule set in the CSS file handles every grid invocation, + regardless of how many appear on a page. + +## Plain `nodes.container`, not custom nodes + +The directives emit +{py:class}`docutils.nodes.container` with class names. There is no +custom node subclass for the grid itself. + +This is deliberate. The grid is presentation-only — every behaviour +worth describing (column count, gutter, alignment, links) is encoded +in a CSS class or an inline custom property. Non-HTML builders +(LaTeX, text, man) descend into the container's children and render +the content; the layout disappears without breaking the doctree. + +The exception is the card's link target. To preserve a Sphinx +cross-reference resolution path for `:link-type: doc` and `:ref`, the +directive emits a thin `LinkPassthrough(nodes.TextElement)` that +hosts an `addnodes.pending_xref` or `nodes.reference`. This sidesteps +Sphinx's `HTML5Translator.visit_reference` assertion that a reference +node containing more than one child must wrap a single image — a +constraint that prevents wrapping the entire card container in a +reference. + +## Breakpoint values as inline custom properties + +The straightforward way to make a grid responsive is to write one CSS +rule per (grid × breakpoint) combination — but that produces an +unbounded number of selectors as the docs grow. Instead, the +directive emits one `style="..."` declaration per grid: + +```html +
+``` + +The static CSS file has a finite set of rules consuming these +properties via `var()`: + +```css +.gp-sphinx-grid { + grid-template-columns: repeat(var(--gp-sphinx-grid-cols-xs, 1), minmax(0, 1fr)); +} +@media (min-width: 576px) { + .gp-sphinx-grid { + grid-template-columns: repeat(var(--gp-sphinx-grid-cols-sm, 1), minmax(0, 1fr)); + } +} +``` + +This pattern is repeated for margin and padding. The rendered CSS +stays a constant size; per-grid variation lives entirely in inline +styles. + +## Comparison to sphinx-design + +`sphinx-design` ships a much larger surface: dropdowns, plain cards, +buttons, article-info, Material and FontAwesome icons, +card-carousels, tab-set-code. `sphinx-ux-grid` is intentionally +narrow — just the grid and card directives gp-sphinx actually uses. + +The MyST authoring surface is unchanged: `{grid}`, `{grid-item}`, +`{grid-item-card}`, and their option names, all match sphinx-design. +Source written against sphinx-design's grid works against this +package without edits. + +## Comparison to Furo's `sd-card` + +Furo's bundled CSS includes overrides for sphinx-design's +`.sd-card`/`.sd-row` classes. This package uses its own +`gp-sphinx-grid*` namespace under `@layer gp-sphinx`, which sits +between Furo's `components` and `utilities` layers. Project-level +overrides win because they target the package's namespaced classes +directly. diff --git a/docs/packages/sphinx-ux-grid/how-to.md b/docs/packages/sphinx-ux-grid/how-to.md new file mode 100644 index 00000000..dc116e82 --- /dev/null +++ b/docs/packages/sphinx-ux-grid/how-to.md @@ -0,0 +1,120 @@ +(sphinx-ux-grid-how-to)= + +# How to + +## Mix columns within a grid + +`{grid-item}` accepts its own `:columns:` argument that overrides the +parent grid's breakpoint defaults for that one item: + +````markdown +:::{grid} 1 2 3 4 + +:::{grid-item} +:columns: 12 12 12 12 +Full-width hero spanning every breakpoint. +::: + +:::{grid-item-card} +Normal-width sibling. +::: + +::: +```` + +`:columns:` takes the same single-int or four-int values as the +parent's column count. + +## Compose with icons and badges + +Cards compose with [`sphinx-ux-octicons`](../sphinx-ux-octicons/index.md) +and [`sphinx-ux-badges`](../sphinx-ux-badges/index.md): + +````markdown +:::{grid-item-card} {octicon}`rocket` Quickstart +:link: quickstart +:link-type: doc +{bdg-success}`Stable` +^^^ +Install and get started in minutes. +::: +```` + +## Outline cards + +Use `:outline:` to swap the shadow for a border. Common for cards in +documentation indexes where you want low-density framing: + +```markdown +:::{grid-item-card} Reference +:outline: +:link: reference +:link-type: doc +API reference for every directive and option. +::: +``` + +## Image-only cards + +`:img-background:` puts an image behind the card body. `:img-top:` / +`:img-bottom:` places an image above or below the body: + +```markdown +:::{grid-item-card} Gallery +:img-top: _static/gallery-hero.png +:img-alt: A wide shot of the gallery +A walkthrough of every component. +::: +``` + +## Reverse a grid's visual order + +`:reverse:` flips the grid items' visual order without changing +source order. Useful when you want the most recent item to appear +first visually but you author it last: + +```markdown +:::{grid} 1 1 2 2 +:reverse: +:gutter: 3 + +:::{grid-item-card} Older item +::: +:::{grid-item-card} Newer item +::: + +::: +``` + +## Apply custom margins via the spacing scale + +The `:margin:` and `:padding:` options accept the integer scale +`0..5` or the keyword `auto`. Each integer maps to a CSS length +(`0` → `0`, `1` → `0.25rem`, ..., `5` → `3rem`). Pass one value to +apply to every breakpoint, or four values for `xs sm md lg`: + +```markdown +:::{grid} 1 2 3 4 +:gutter: 3 +:margin: 0 0 4 4 + +Spacious only on tablet and desktop. +::: +``` + +## Override classes from MyST source + +Every card section accepts a class override via the relevant +`:class-*:` option (`:class-container:`, `:class-row:`, +`:class-item:`, `:class-card:`, `:class-body:`, `:class-title:`, +`:class-header:`, `:class-footer:`): + +```markdown +:::{grid-item-card} Custom card +:class-card: my-project-callout +Body text. +::: +``` + +The class names are appended to the rendered element. The package's +own `gp-sphinx-grid*` classes are always present. diff --git a/docs/packages/sphinx-ux-grid/index.md b/docs/packages/sphinx-ux-grid/index.md new file mode 100644 index 00000000..c8ef0a5d --- /dev/null +++ b/docs/packages/sphinx-ux-grid/index.md @@ -0,0 +1,6 @@ +(sphinx-ux-grid)= + +# sphinx-ux-grid + +```{package-landing} sphinx-ux-grid +``` diff --git a/docs/packages/sphinx-ux-grid/reference.md b/docs/packages/sphinx-ux-grid/reference.md new file mode 100644 index 00000000..7b80dfb5 --- /dev/null +++ b/docs/packages/sphinx-ux-grid/reference.md @@ -0,0 +1,236 @@ +(sphinx-ux-grid-reference)= + +# API Reference + +## Directives + +### `{grid}` + +Container for grid items. Lays out children in a CSS-Grid template +whose column count varies by breakpoint. + +```{list-table} +:header-rows: 1 +:widths: 20 20 60 + +* - Option + - Value + - Description +* - argument + - `` or ` ` + - Column counts. Single integer applies to every breakpoint; four + integers map to `xs sm md lg` (defaults: 1, 1, 2, 2). +* - `:gutter:` + - `0`–`5` or four such values + - Spacing scale. `0` → `0`, `1` → `0.25rem`, ..., `5` → `3rem`. + Defaults to `3`. +* - `:margin:` + - `0`–`5`, `auto`, or four such values + - Margin scale, per breakpoint. +* - `:padding:` + - `0`–`5` or four such values + - Padding scale, per breakpoint. +* - `:outline:` + - flag + - Render a faint outline around the grid container (debugging + helper). +* - `:reverse:` + - flag + - Reverse the visual order of grid items without changing source + order. +* - `:class-container:` + - string + - Append classes to the grid container. +* - `:class-row:` + - string + - Append classes to the inner row wrapper. +``` + +### `{grid-item}` + +Child of `{grid}`. Carries column-span overrides for itself. + +```{list-table} +:header-rows: 1 +:widths: 20 20 60 + +* - Option + - Value + - Description +* - `:columns:` + - `` or four ints (1..12) + - Column span, per breakpoint. Default inherits the parent grid's + breakpoint columns. +* - `:child-direction:` + - `column` or `row` + - Direction the item's children flow. +* - `:child-align:` + - `start`, `end`, `center`, `justify`, `spaced` + - Alignment of the item's children. +* - `:margin:` + - `0`–`5`, `auto`, or four such values + - Margin scale, per breakpoint. +* - `:padding:` + - `0`–`5` or four such values + - Padding scale, per breakpoint. +* - `:outline:` + - flag + - Debug outline. +* - `:class:` + - string + - Append classes to the item. +``` + +### `{grid-item-card}` + +Composite: a `{grid-item}` wrapping a card. Accepts every `{grid-item}` +option plus card-specific options below. + +```{list-table} +:header-rows: 1 +:widths: 20 20 60 + +* - Option + - Value + - Description +* - argument + - inline text + - Card title. +* - `:link:` + - target string + - Make the card clickable. Combined with `:link-type:`. +* - `:link-type:` + - `url`, `any`, `ref`, `doc` + - How `:link:` is resolved. Default `any` (tries `doc` then `ref`). +* - `:link-alt:` + - string + - Accessible label for the link. +* - `:shadow:` + - `none`, `sm`, `md`, `lg` + - Card shadow strength. +* - `:width:` + - `25%`, `50%`, `75%`, `100%`, `auto` + - Card width override. +* - `:text-align:` + - `left`, `center`, `right`, `justify` + - Body text alignment. +* - `:img-top:` + - image path + - Image rendered above the body. +* - `:img-bottom:` + - image path + - Image rendered below the body. +* - `:img-background:` + - image path + - Image rendered behind the body. +* - `:img-alt:` + - string + - Alt text for the image. +* - `:class-item:` + - string + - Classes appended to the outer grid-item wrapper. +* - `:class-card:` + - string + - Classes appended to the card container. +* - `:class-body:` + - string + - Classes appended to the card body. +* - `:class-title:` + - string + - Classes appended to the card title. +* - `:class-header:` + - string + - Classes appended to the card header. +* - `:class-footer:` + - string + - Classes appended to the card footer. +``` + +#### Card content splitters + +Inside a `{grid-item-card}`, two markers split the body: + +- `^^^` — separator between header and body. +- `+++` — separator between body and footer. + +The header, body, and footer each receive their own +`.gp-sphinx-grid-card__header` / `__body` / `__footer` container. + +## CSS classes + +```{list-table} +:header-rows: 1 +:widths: 35 65 + +* - Class + - Applied to +* - `gp-sphinx-grid` + - The grid container. +* - `gp-sphinx-grid__item` + - Each grid item wrapper. +* - `gp-sphinx-grid-card` + - The card element inside `{grid-item-card}`. +* - `gp-sphinx-grid-card__body` + - Card body region. +* - `gp-sphinx-grid-card__title` + - Card title. +* - `gp-sphinx-grid-card__header` + - Header region (above `^^^`). +* - `gp-sphinx-grid-card__footer` + - Footer region (below `+++`). +* - `gp-sphinx-grid-card__img-top` + - Top-positioned image. +* - `gp-sphinx-grid-card__img-bottom` + - Bottom-positioned image. +* - `gp-sphinx-grid-card__link` + - Stretched-link anchor that makes the whole card clickable. +* - `gp-sphinx-grid-card--shadow-sm` + - Modifier — small shadow. +* - `gp-sphinx-grid-card--shadow-md` + - Modifier — medium shadow. +* - `gp-sphinx-grid-card--shadow-lg` + - Modifier — large shadow. +* - `gp-sphinx-grid-card--outline` + - Modifier — render with an outline instead of a shadow. +* - `gp-sphinx-grid--reverse` + - Modifier — reverse visual order. +``` + +## CSS custom properties + +The grid container reads breakpoint values from inline custom +properties; the package's CSS file declares the rules that consume +them. Override defaults via your project's `custom.css`. + +```{list-table} +:header-rows: 1 +:widths: 40 60 + +* - Property + - Purpose +* - `--gp-sphinx-grid-cols-xs` + - Column count at extra-small width. +* - `--gp-sphinx-grid-cols-sm` + - Column count at small width (≥ 576px). +* - `--gp-sphinx-grid-cols-md` + - Column count at medium width (≥ 768px). +* - `--gp-sphinx-grid-cols-lg` + - Column count at large width (≥ 992px). +* - `--gp-sphinx-grid-gutter` + - Gap between grid items. +* - `--gp-sphinx-grid-margin-{xs,sm,md,lg}` + - Margin per breakpoint. +* - `--gp-sphinx-grid-padding-{xs,sm,md,lg}` + - Padding per breakpoint. +``` + +The directive emits these as inline `style="..."` overrides on the +grid container. The static CSS rules consume them with the +established cascade (each breakpoint's rule reads the matching +property and falls back to the previous breakpoint). + +## Python API + +```{eval-rst} +.. autofunction:: sphinx_ux_grid.setup +``` diff --git a/docs/packages/sphinx-ux-grid/tutorial.md b/docs/packages/sphinx-ux-grid/tutorial.md new file mode 100644 index 00000000..aba9be72 --- /dev/null +++ b/docs/packages/sphinx-ux-grid/tutorial.md @@ -0,0 +1,84 @@ +(sphinx-ux-grid-tutorial)= + +# Tutorial + +## Add the extension + +`sphinx-ux-grid` is loaded automatically by +{py:func}`~gp_sphinx.config.merge_sphinx_config`. To use it in a +standalone Sphinx project: + +```python +extensions = ["sphinx_ux_grid"] +``` + +## A responsive grid + +The `{grid}` directive accepts a breakpoint argument of either a +single integer or four space-separated integers (`xs sm md lg`): + +````markdown +:::{grid} 1 2 3 4 +:gutter: 3 + +:::{grid-item-card} Quickstart +Install and get started in minutes. +::: + +:::{grid-item-card} Reference +Full API documentation. +::: + +:::{grid-item-card} Examples +Live demos. +::: + +:::{grid-item-card} Source +GitHub source for every package. +::: + +::: +```` + +The grid renders 1 column on phones, 2 on small tablets, 3 on +tablets, and 4 on desktops. + +## A card with a link + +Cards become clickable when `:link:` is set. `:link-type:` controls +how the link is resolved: + +- `url` — bare HTML link +- `doc` — Sphinx docname (e.g. `packages/sphinx-ux-tabs/index`) +- `ref` — `:ref:` label +- `any` — try `doc` first, then `ref` (the default) + +```markdown +:::{grid-item-card} Quickstart +:link: quickstart +:link-type: doc +:shadow: md +Install and get started in minutes. +::: +``` + +The whole card is clickable. The internal `.gp-sphinx-grid-card__link` +covers the card via `position: absolute; inset: 0`. + +## Header and footer markers + +Inside `{grid-item-card}`, two markers split the card body into +sections: + +- `^^^` — everything above is the header. +- `+++` — everything below is the footer. + +````markdown +:::{grid-item-card} Card with sections +header text +^^^ +body text ++++ +footer text +::: +```` diff --git a/docs/packages/sphinx-ux-octicons/dependents.md b/docs/packages/sphinx-ux-octicons/dependents.md new file mode 100644 index 00000000..a01b093c --- /dev/null +++ b/docs/packages/sphinx-ux-octicons/dependents.md @@ -0,0 +1,6 @@ +(sphinx-ux-octicons-dependents)= + +# Dependents + +```{package-dependents} sphinx-ux-octicons +``` diff --git a/docs/packages/sphinx-ux-octicons/examples.md b/docs/packages/sphinx-ux-octicons/examples.md new file mode 100644 index 00000000..2adf39f2 --- /dev/null +++ b/docs/packages/sphinx-ux-octicons/examples.md @@ -0,0 +1,90 @@ +(sphinx-ux-octicons-examples)= + +# Examples + +## Every bundled icon + +Each icon below is rendered by the real `{octicon}` role at `1.5rem`: + +```{list-table} +:header-rows: 1 +:widths: 25 25 50 + +* - Name + - Rendered + - Role +* - `rocket` + - {octicon}`rocket;1.5rem` + - `` {octicon}`rocket;1.5rem` `` +* - `tools` + - {octicon}`tools;1.5rem` + - `` {octicon}`tools;1.5rem` `` +* - `book` + - {octicon}`book;1.5rem` + - `` {octicon}`book;1.5rem` `` +* - `light-bulb` + - {octicon}`light-bulb;1.5rem` + - `` {octicon}`light-bulb;1.5rem` `` +* - `star` + - {octicon}`star;1.5rem` + - `` {octicon}`star;1.5rem` `` +* - `alert` + - {octicon}`alert;1.5rem` + - `` {octicon}`alert;1.5rem` `` +* - `terminal` + - {octicon}`terminal;1.5rem` + - `` {octicon}`terminal;1.5rem` `` +* - `paintbrush` + - {octicon}`paintbrush;1.5rem` + - `` {octicon}`paintbrush;1.5rem` `` +* - `code` + - {octicon}`code;1.5rem` + - `` {octicon}`code;1.5rem` `` +* - `device-camera` + - {octicon}`device-camera;1.5rem` + - `` {octicon}`device-camera;1.5rem` `` +* - `diff` + - {octicon}`diff;1.5rem` + - `` {octicon}`diff;1.5rem` `` +* - `link` + - {octicon}`link;1.5rem` + - `` {octicon}`link;1.5rem` `` +* - `home` + - {octicon}`home;1.5rem` + - `` {octicon}`home;1.5rem` `` +* - `gear` + - {octicon}`gear;1.5rem` + - `` {octicon}`gear;1.5rem` `` +* - `package` + - {octicon}`package;1.5rem` + - `` {octicon}`package;1.5rem` `` +* - `info` + - {octicon}`info;1.5rem` + - `` {octicon}`info;1.5rem` `` +* - `check-circle` + - {octicon}`check-circle;1.5rem` + - `` {octicon}`check-circle;1.5rem` `` +* - `x-circle` + - {octicon}`x-circle;1.5rem` + - `` {octicon}`x-circle;1.5rem` `` +``` + +## Icons inherit text colour + +Wrap the role in a span with a colour class to tint the icon: + +{octicon}`rocket` Ship it + +{octicon}`alert` Heads up + +{octicon}`check-circle` Looks good + +## RST authoring + +The role is also available as a reStructuredText role. Both syntaxes +emit the same SVG markup. + +```{eval-rst} +Build it with :octicon:`rocket;1.5rem`, document it with :octicon:`book;1.5rem`, +ship it with :octicon:`check-circle;1.5rem`. +``` diff --git a/docs/packages/sphinx-ux-octicons/explanation.md b/docs/packages/sphinx-ux-octicons/explanation.md new file mode 100644 index 00000000..719a035d --- /dev/null +++ b/docs/packages/sphinx-ux-octicons/explanation.md @@ -0,0 +1,47 @@ +(sphinx-ux-octicons-explanation)= + +# Explanation + +## Curated bundle, not the full set + +Upstream Octicons ships ~200 icons across 16px and 24px variants. The +full JSON is roughly 1 MB. The gp-sphinx docs use about a dozen icons +in practice, so the bundle ships only what is used — under 15 KB — +plus a handful of common headroom names (`home`, `gear`, `info`). + +The bundle is regenerated by `scripts/sync_octicons.py` from the +upstream `@primer/octicons` npm package. The script is a maintainer +one-shot; consumers never run it. + +## Inline SVG, not icon fonts + +`{octicon}` emits inline SVG markup with `fill: currentColor`. This +trades one network request (an icon font) for slightly larger HTML, +and in return: icons inherit text colour without per-icon CSS, scale +crisply at any zoom, and remain accessible (the SVG carries +`aria-hidden="true"` so screen readers skip decorative icons). + +## Two nodes, not one + +The directive emits an `OcticonNode` subclass of +{py:class}`docutils.nodes.inline` carrying: + +- `svg_markup` — the pre-rendered SVG string consumed by the HTML + visitor. +- A child {py:class}`docutils.nodes.Text` carrying the icon name, + reached only by non-HTML builders via MRO fallback. + +This two-payload shape means HTML emits SVG and text emits the icon +name, with no per-builder branching at directive-run time. + +## Comparison to sphinx-design + +`sphinx-design` shipped a similar `{octicon}` role with the full +upstream icon set plus support for FontAwesome and Material variants. +This package keeps just the Octicons role and just the icons gp-sphinx +docs use, so the rendered HTML is smaller and consumers get a single +icon library with a consistent visual style. + +The role syntax is intentionally identical (`name`, `name;height`, +`name;height;classes`), so MyST source written against sphinx-design's +`{octicon}` works unchanged. diff --git a/docs/packages/sphinx-ux-octicons/how-to.md b/docs/packages/sphinx-ux-octicons/how-to.md new file mode 100644 index 00000000..858fa13a --- /dev/null +++ b/docs/packages/sphinx-ux-octicons/how-to.md @@ -0,0 +1,59 @@ +(sphinx-ux-octicons-how-to)= + +# How to + +## Icon in a heading + +Inline `{octicon}` works anywhere a role is allowed, including +headings: + +```markdown +## {octicon}`book` Reference +``` + +The icon inherits the heading's colour and scales with the heading's +font size when the role's height argument is `1em` (the default). + +## Icon inside a grid card title + +`{octicon}` composes with [`sphinx-ux-grid`](../sphinx-ux-grid/index.md) +to put an icon next to a card title: + +```markdown +:::{grid-item-card} {octicon}`rocket` Quickstart +:link: quickstart +:link-type: doc +Install and get started in minutes. +::: +``` + +## Match the rendered SVG to a colour + +`gp-sphinx-octicon` uses `fill: currentColor`. Override the colour by +wrapping the role in a span carrying a colour class, or by setting +`color` on the parent element via your project's `custom.css`: + +```css +.my-warning { + color: var(--color-attention-foreground, #b08800); +} +``` + +```markdown +{octicon}`alert` Heads up +``` + +## Add a new icon to the bundle + +The bundle is hand-curated to keep the wheel small. Add a name to +`_data/octicons_curated.txt` and regenerate `_data/octicons.json` from +upstream `@primer/octicons`: + +```console +$ cd packages/sphinx-ux-octicons +$ pnpm install @primer/octicons +$ python scripts/sync_octicons.py node_modules/@primer/octicons/build/svg +``` + +Commit both files together so the JSON and the audit-source-of-truth +text file stay in sync. diff --git a/docs/packages/sphinx-ux-octicons/index.md b/docs/packages/sphinx-ux-octicons/index.md new file mode 100644 index 00000000..a8c2207b --- /dev/null +++ b/docs/packages/sphinx-ux-octicons/index.md @@ -0,0 +1,6 @@ +(sphinx-ux-octicons)= + +# sphinx-ux-octicons + +```{package-landing} sphinx-ux-octicons +``` diff --git a/docs/packages/sphinx-ux-octicons/reference.md b/docs/packages/sphinx-ux-octicons/reference.md new file mode 100644 index 00000000..5b56302f --- /dev/null +++ b/docs/packages/sphinx-ux-octicons/reference.md @@ -0,0 +1,110 @@ +(sphinx-ux-octicons-reference)= + +# API Reference + +## Role syntax + +The `{octicon}` role accepts up to three `;`-separated arguments: + +| Form | Example | +|---|---| +| `name` | `` {octicon}`rocket` `` | +| `name;height` | `` {octicon}`rocket;1.5rem` `` | +| `name;height;classes` | `` {octicon}`rocket;1.5rem;text-success` `` | + +- `name` — bundled icon name (see {ref}`bundled-icons` below). +- `height` — CSS length (`em`, `rem`, `px`). Default `1em`. Width + scales to preserve the icon's 1:1 aspect ratio. +- `classes` — space-separated extra classes appended to the SVG. + +Unknown icon names emit a docutils error pointing at the source line. + +(bundled-icons)= +## Bundled icons + +```{list-table} +:header-rows: 1 +:widths: 30 70 + +* - Name + - Used for +* - `rocket` + - Tutorials and getting-started entry points +* - `tools` + - How-to guides and configuration recipes +* - `book` + - Reference / API pages +* - `light-bulb` + - Explanation and design rationale +* - `star` + - Example showcases and highlights +* - `alert` + - Errors, warnings, breaking-change callouts +* - `terminal` + - CLI documentation and command reference +* - `paintbrush` + - Theme tokens and styling content +* - `code` + - Signature and code-example pages +* - `device-camera` + - Gallery / kitchen-sink content +* - `diff` + - Surface-diff and migration content +* - `link` + - Dependents and cross-package references +* - `home` + - Landing pages +* - `gear` + - Settings and configuration +* - `package` + - Package-level content +* - `info` + - Informational notes +* - `check-circle` + - Validation passes and success states +* - `x-circle` + - Validation failures and error states +``` + +Need an icon that isn't in the bundle? Add it via the recipe in +{doc}`how-to`. + +## CSS class + +`gp-sphinx-octicon` is the only class shipped by this extension. The +rendered SVG receives both `gp-sphinx-octicon` and a per-icon modifier +`gp-sphinx-octicon--`: + +```html + +``` + +The CSS rule lives in `_static/css/sphinx_ux_octicons.css`: + +```css +@layer gp-sphinx { + .gp-sphinx-octicon { + display: inline-block; + vertical-align: text-top; + fill: currentColor; + } +} +``` + +`fill: currentColor` lets the icon inherit its colour from the +surrounding text — no per-role styling required. + +## Non-HTML builders + +`OcticonNode` subclasses {py:class}`docutils.nodes.inline`, so non-HTML +builders (text, man, LaTeX) fall back via Sphinx MRO dispatch to +`visit_inline` and render the icon name as visible text. Documents +build cleanly across every Sphinx builder. + +## Python API + +```{eval-rst} +.. autofunction:: sphinx_ux_octicons.setup +``` diff --git a/docs/packages/sphinx-ux-octicons/tutorial.md b/docs/packages/sphinx-ux-octicons/tutorial.md new file mode 100644 index 00000000..08bf69c4 --- /dev/null +++ b/docs/packages/sphinx-ux-octicons/tutorial.md @@ -0,0 +1,47 @@ +(sphinx-ux-octicons-tutorial)= + +# Tutorial + +## Add the extension + +`sphinx-ux-octicons` is loaded automatically by +{py:func}`~gp_sphinx.config.merge_sphinx_config`. To use it in a +standalone Sphinx project, list it in `conf.py`: + +```python +extensions = ["sphinx_ux_octicons"] +``` + +## Use the role + +The `{octicon}` role accepts an icon name and emits an inline SVG. + +```markdown +Welcome {octicon}`rocket`! +``` + +Welcome {octicon}`rocket`! + +## Size an icon + +Pass a CSS length as the second argument, separated by `;`. + +```markdown +{octicon}`book;1.5rem` Documentation +``` + +{octicon}`book;1.5rem` Documentation + +## Add extra classes + +Pass a space-separated list of class names as the third argument. + +```markdown +{octicon}`alert;1em;text-warning` heads up +``` + +{octicon}`alert;1em;text-warning` heads up + +The rendered SVG inherits its colour from `currentColor`, so wrapping +the role in a coloured container (a heading, an admonition, a span +with a colour utility) tints the icon without per-role styling. diff --git a/docs/packages/sphinx-ux-tabs/dependents.md b/docs/packages/sphinx-ux-tabs/dependents.md new file mode 100644 index 00000000..36c86fb1 --- /dev/null +++ b/docs/packages/sphinx-ux-tabs/dependents.md @@ -0,0 +1,6 @@ +(sphinx-ux-tabs-dependents)= + +# Dependents + +```{package-dependents} sphinx-ux-tabs +``` diff --git a/docs/packages/sphinx-ux-tabs/examples.md b/docs/packages/sphinx-ux-tabs/examples.md new file mode 100644 index 00000000..58e86be4 --- /dev/null +++ b/docs/packages/sphinx-ux-tabs/examples.md @@ -0,0 +1,242 @@ +(sphinx-ux-tabs-examples)= + +# Examples + +## Inline-tabs style (consecutive `.. tab::`) + +```{eval-rst} +.. tab:: Python + + .. code-block:: python + + def greet(name: str) -> str: + return f"Hello, {name}!" + +.. tab:: Rust + + .. code-block:: rust + + fn greet(name: &str) -> String { + format!("Hello, {}!", name) + } + +.. tab:: Go + + .. code-block:: go + + func Greet(name string) string { + return fmt.Sprintf("Hello, %s!", name) + } +``` + +## Tab-set style ({tab-set} / {tab-item}) + +::::{tab-set} + +:::{tab-item} pip +```console +$ pip install gp-sphinx +``` +::: + +:::{tab-item} uv +```console +$ uv add gp-sphinx +``` +::: + +:::{tab-item} pipx +```console +$ pipx install gp-sphinx +``` +::: + +:::: + +## Pre-selected tab + +::::{tab-set} + +:::{tab-item} Linux +Linux setup instructions. +::: + +:::{tab-item} macOS +:selected: +macOS setup instructions — selected on page load. +::: + +:::{tab-item} Windows +Windows setup instructions. +::: + +:::: + +## Synchronized tab-sets + +Pick a shell here: + +::::{tab-set} +:sync-group: shell + +:::{tab-item} bash +:sync: bash +```bash +export PATH="$HOME/.local/bin:$PATH" +``` +::: + +:::{tab-item} zsh +:sync: zsh +```zsh +typeset -U path PATH +path=("$HOME/.local/bin" $path) +``` +::: + +:::{tab-item} fish +:sync: fish +```fish +fish_add_path "$HOME/.local/bin" +``` +::: + +:::: + +The same shell stays selected here: + +::::{tab-set} +:sync-group: shell + +:::{tab-item} bash +:sync: bash +```bash +source ~/.bashrc +``` +::: + +:::{tab-item} zsh +:sync: zsh +```zsh +source ~/.zshrc +``` +::: + +:::{tab-item} fish +:sync: fish +```fish +source ~/.config/fish/config.fish +``` +::: + +:::: + +Click a tab in either tab-set; the other follows. + +## Size variants + +Default size — compact labels at `0.95em`: + +::::{tab-set} + +:::{tab-item} pip +```console +$ pip install gp-sphinx +``` +::: + +:::{tab-item} uv +```console +$ uv add gp-sphinx +``` +::: + +:::{tab-item} pipx +```console +$ pipx install gp-sphinx +``` +::: + +:::: + +`:class: gp-sphinx-tabs--large` — body-size labels with roomier +padding: + +::::{tab-set} +:class: gp-sphinx-tabs--large + +:::{tab-item} pip +```console +$ pip install gp-sphinx +``` +::: + +:::{tab-item} uv +```console +$ uv add gp-sphinx +``` +::: + +:::{tab-item} pipx +```console +$ pipx install gp-sphinx +``` +::: + +:::: + +## Deep-link to a tab + +::::{tab-set} +:sync-group: example + +:::{tab-item} Python +:sync: python +```python +print("hello world") +``` +::: + +:::{tab-item} Rust +:sync: rust +```rust +println!("hello world"); +``` +::: + +:::{tab-item} Go +:sync: go +```go +fmt.Println("hello world") +``` +::: + +:::: + +Open this page with ?example=python +to pre-select the Python tab via the sphinx-design URL form. The +legacy sphinx-inline-tabs form is supported too: +?tabs=Python. Either form +writes through to `localStorage`, so the choice persists on subsequent +visits. + +## `:new-set:` breaks a consecutive run + +```{eval-rst} +.. tab:: Set A, tab 1 + + First tab of the first set. + +.. tab:: Set A, tab 2 + + Second tab of the first set — auto-grouped with the previous. + +.. tab:: Set B, tab 1 + :new-set: + + ``:new-set:`` forces a fresh tab-set break. + +.. tab:: Set B, tab 2 + + Second tab of the second set. +``` diff --git a/docs/packages/sphinx-ux-tabs/explanation.md b/docs/packages/sphinx-ux-tabs/explanation.md new file mode 100644 index 00000000..e09122ee --- /dev/null +++ b/docs/packages/sphinx-ux-tabs/explanation.md @@ -0,0 +1,97 @@ +(sphinx-ux-tabs-explanation)= + +# Explanation + +## Radio inputs and CSS-only switching + +Tabs are rendered as a group of `` elements +sharing a `name` attribute, each followed by a `