Skip to content

feat: add openapi shortcode rendered with Swagger UI - #1046

Open
davlgd wants to merge 3 commits into
imfing:mainfrom
davlgd:feat/openapi-shortcode
Open

davlgd wants to merge 3 commits into
imfing:mainfrom
davlgd:feat/openapi-shortcode

Conversation

@davlgd

@davlgd davlgd commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

Supersedes #833.

Summary

Adds an openapi shortcode that renders an OpenAPI description (JSON or YAML) as an interactive API reference with Swagger UI:

{{< openapi "openapi/example.yaml" >}}
{{< openapi src="https://petstore3.swagger.io/api/v3/openapi.json" docExpansion="none" filter=true >}}

It builds on the implementation we use for our own API reference. Unlike the page layout of #833, it keeps the Hextra navigation and can sit next to other content. The description can be a local file (page resource, asset or static/) or a URL, which covers the request for local files in #833. Six Swagger UI options are exposed and validated.

Swagger UI only loads on pages using the shortcode. Like asciinema and gallery, it is fetched from jsDelivr (swagger-ui-dist@5) at build time and published with SRI, and params.openapi accepts a mirror or local assets.

Dark mode

Swagger UI 5.31 added a native dark theme, enabled by a dark-mode class on the root element. The loader keeps it in step with the Hextra theme, including later toggles, so no dark stylesheet has to be maintained here. assets/css/components/openapi.css only adjusts it: the page background shows through instead of the Swagger UI blue-gray, and the fixes below.

Accessibility

  • Visible focus with the primary color on selects and on operation and schema toggles: Swagger UI removes the outline of selects, and its dark theme removes the others
  • Deprecated operations keep full opacity: the 60% dimming takes their text and badge below 3:1, and the struck-through path and gray badge already mark them
  • The PUT section header gets the same dark tone as the other methods in dark mode, since its text stayed at 4.25:1
  • The servers selects, global and per operation, get an accessible name from the title of their section, as Swagger UI wraps them in empty labels (axe select-name, critical). Per-operation selects only appear after "Try it out", so they are named as they appear

The request body textarea shown after "Try it out" has no label either. It only appears after an interaction, so it is left to Swagger UI.

Test plan

  • Local asset, static/ file and remote URL descriptions resolve, and local files are published
  • Options reach Swagger UI, and invalid values warn at build time instead of failing it
  • A mirror that returns no file fails the build
  • Pages without the shortcode do not load Swagger UI assets
  • Light and dark themes, including switching theme after load
  • axe with the settings of tests/accessibility.spec.ts on the 49 English pages: no violations
  • npm run test:build, with a new tests/openapi.spec.ts that uses local assets and needs no network
  • Docs translated for fa, ja and zh-cn

@davlgd
davlgd deployed to accessibility September 30, 2026 14:39 — with GitHub Actions Active
@netlify

netlify Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for hugo-hextra ready!

Name Link
🔨 Latest commit ea025f9
🔍 Latest deploy log https://app.netlify.com/projects/hugo-hextra/deploys/6ac560961e9240000829867f
😎 Deploy Preview https://deploy-preview-1046--hugo-hextra.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@imfing imfing added this to the v0.14.0 milestone Oct 1, 2026
@imfing
imfing deployed to accessibility October 6, 2026 20:47 — with GitHub Actions Active
- Extract the CDN/mirror/local asset loader into utils/vendor-assets.html
  and use it for Swagger UI and the asciinema player
- Fail the build when the OpenAPI description cannot be resolved or when
  a non-remote base leaves no script to load, warn when no stylesheet
- Validate filter and tryItOutEnabled as booleans
- Label the Swagger UI container as a region and demote the nested
  <main> rendered by Swagger UI so the page keeps one main landmark
- Disconnect MutationObservers on pagehide
- Pin the asciinema player CDN to its current major version
@imfing
imfing deployed to accessibility October 6, 2026 20:56 — with GitHub Actions Active

This branch was successfully deployed

1 active deployment
accessibility — ea025f98 Deployed Oct 6, 2026 by imfing via a11y #136
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.

2 participants