diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml
index 0e22849..3c65b4d 100644
--- a/.github/workflows/pre-release.yml
+++ b/.github/workflows/pre-release.yml
@@ -86,6 +86,7 @@ jobs:
run: |
echo "🔨 Generating UI assets..."
npm install
+ npm run lint
npm run build
cp ./build/injectNav.iife.js ../quantinuum_sphinx/static/injectNav.iife.js
cp ./build/syncTheme.iife.js ../quantinuum_sphinx/static/syncTheme.iife.js
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 300c0a4..b60423d 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -147,6 +147,7 @@ jobs:
run: |
echo "🔨 Generating UI assets..."
npm install
+ npm run lint
npm run build
cp ./build/injectNav.iife.js ../quantinuum_sphinx/static/injectNav.iife.js
cp ./build/syncTheme.iife.js ../quantinuum_sphinx/static/syncTheme.iife.js
diff --git a/sphinx-ui/react/package-lock.json b/sphinx-ui/react/package-lock.json
index 9de780d..4674423 100644
--- a/sphinx-ui/react/package-lock.json
+++ b/sphinx-ui/react/package-lock.json
@@ -30,7 +30,6 @@
"prettier-plugin-tailwindcss": "^0.6.6",
"serve": "^14.2.3",
"tailwindcss": "^3.4.4",
- "tsc": "^2.0.4",
"tsdown": "^0.21.7",
"typescript": "^6.0.3"
},
@@ -9909,15 +9908,6 @@
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
},
- "node_modules/tsc": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/tsc/-/tsc-2.0.4.tgz",
- "integrity": "sha512-fzoSieZI5KKJVBYGvwbVZs/J5za84f2lSTLPYf6AGiIf43tZ3GNrI1QzTLcjtyDDP4aLxd46RTZq1nQxe7+k5Q==",
- "dev": true,
- "bin": {
- "tsc": "bin/tsc"
- }
- },
"node_modules/tsdown": {
"version": "0.21.10",
"resolved": "https://registry.npmjs.org/tsdown/-/tsdown-0.21.10.tgz",
diff --git a/sphinx-ui/react/package.json b/sphinx-ui/react/package.json
index 27bb73a..0e4921e 100644
--- a/sphinx-ui/react/package.json
+++ b/sphinx-ui/react/package.json
@@ -5,6 +5,7 @@
"main": "index.js",
"scripts": {
"build": "npx tsdown",
+ "lint": "tsc --noEmit",
"format": "prettier --write --config='../../libraries/ui/.prettierrc' --ignore-path='.prettierignore' ."
},
"author": "",
@@ -23,7 +24,6 @@
"prettier-plugin-tailwindcss": "^0.6.6",
"serve": "^14.2.3",
"tailwindcss": "^3.4.4",
- "tsc": "^2.0.4",
"tsdown": "^0.21.7",
"typescript": "^6.0.3"
},
diff --git a/sphinx-ui/react/postcss.config.mjs b/sphinx-ui/react/postcss.config.mjs
index 45cb478..d54a93b 100644
--- a/sphinx-ui/react/postcss.config.mjs
+++ b/sphinx-ui/react/postcss.config.mjs
@@ -5,13 +5,34 @@ import autoprefixer from 'autoprefixer'
import prefixSelector from 'postcss-prefix-selector'
import failOnWarn from 'postcss-fail-on-warn'
+const tailwindScopeSelector = '.use-tailwind'
+
+function scopeTailwindSelector(prefix, selector, prefixedSelector) {
+ if (selector === '*') {
+ return `${prefix}, ${prefixedSelector}`
+ }
+
+ if (selector.startsWith('::')) {
+ return `${prefix}${selector}, ${prefixedSelector}`
+ }
+
+ if (selector.startsWith('.')) {
+ return `${prefixedSelector}, ${prefix}${selector}`
+ }
+
+ return prefixedSelector
+}
+
export default {
plugins: [
postcssImport,
tailwindcssNesting,
tailwindcss,
autoprefixer,
- prefixSelector({ prefix: '.use-tailwind' }),
+ prefixSelector({
+ prefix: tailwindScopeSelector,
+ transform: scopeTailwindSelector,
+ }),
failOnWarn,
],
}
diff --git a/sphinx-ui/react/src/injectNav.tsx b/sphinx-ui/react/src/injectNav.tsx
index 1c73bf8..71a0fed 100644
--- a/sphinx-ui/react/src/injectNav.tsx
+++ b/sphinx-ui/react/src/injectNav.tsx
@@ -8,12 +8,53 @@ const GA_ID = __NEXT_PUBLIC_GA_ID__;
const tailwindScopeClassName = 'use-tailwind';
-(() => {
- document.body.classList.add(tailwindScopeClassName)
+const isTailwindDialogPortalElement = (element: Element): element is HTMLElement => {
+ if (!(element instanceof HTMLElement)) return false
+
+ const { classList } = element
+ return (
+ classList.contains('fixed') &&
+ classList.contains('z-50') &&
+ (classList.contains('inset-0') || element.getAttribute('role') === 'dialog')
+ )
+}
+
+const scopeTailwindDialogPortalElements = (root: ParentNode = document.body) => {
+ const scopeElement = (element: Element) => {
+ if (isTailwindDialogPortalElement(element)) {
+ element.classList.add(tailwindScopeClassName)
+ }
+ }
+
+ if (root instanceof Element) {
+ scopeElement(root)
+ }
+
+ root.querySelectorAll('[class][data-state], [class][role="dialog"]').forEach(scopeElement)
+}
+const observeTailwindDialogPortalElements = () => {
+ scopeTailwindDialogPortalElements()
+
+ const observer = new MutationObserver((records) => {
+ records.forEach((record) => {
+ record.addedNodes.forEach((node) => {
+ if (node instanceof Element) {
+ scopeTailwindDialogPortalElements(node)
+ }
+ })
+ })
+ })
+
+ observer.observe(document.body, { childList: true, subtree: true })
+}
+
+(() => {
const mountElement = document.querySelector('.nexus-nav')
if (!mountElement) return
+ observeTailwindDialogPortalElements()
+
const renderIn = document.createElement('div')
mountElement.appendChild(renderIn)
diff --git a/sphinx-ui/react/tailwind.config.ts b/sphinx-ui/react/tailwind.config.ts
index ec3aedb..1b41934 100644
--- a/sphinx-ui/react/tailwind.config.ts
+++ b/sphinx-ui/react/tailwind.config.ts
@@ -1,3 +1,5 @@
+///
+
import path from 'path'
import { Config } from 'tailwindcss'
import plugin from 'tailwindcss/plugin'
diff --git a/sphinx-ui/react/tsdown.config.ts b/sphinx-ui/react/tsdown.config.ts
index 5422f5b..dd89be8 100644
--- a/sphinx-ui/react/tsdown.config.ts
+++ b/sphinx-ui/react/tsdown.config.ts
@@ -1,6 +1,8 @@
+///
+
import { defineConfig } from 'tsdown'
-const NEXT_PUBLIC_GA_ID = JSON.stringify(process.env.NEXT_PUBLIC_GA_ID || '')
+const NEXT_PUBLIC_GA_ID = JSON.stringify(process.env['NEXT_PUBLIC_GA_ID'] || '')
export default defineConfig([
// Script for injecting nav into sphinx build.
diff --git a/sphinx-ui/react/tsup.config.ts b/sphinx-ui/react/tsup.config.ts
deleted file mode 100644
index 3dd401e..0000000
--- a/sphinx-ui/react/tsup.config.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { defineConfig } from 'tsup'
-
-export default defineConfig([
- // Script for injecting nav into sphinx build.
- {
- entry: ['./src/injectNav.tsx'],
- outDir: 'build',
- minify: true,
- skipNodeModulesBundle: false,
- target: "es2015",
- platform: "browser",
- format: ["iife"],
- clean: true,
-},
-// Script for syncing dark mode preference
-{
- entry: ['./src/syncTheme.ts'],
- outDir: 'build',
- minify: true,
- skipNodeModulesBundle: false,
- target: "es2015",
- platform: "browser",
- format: ["iife"],
- clean: true,
-}])
diff --git a/sphinx-ui/uv.lock b/sphinx-ui/uv.lock
index 5a811ac..f1238b6 100644
--- a/sphinx-ui/uv.lock
+++ b/sphinx-ui/uv.lock
@@ -38,24 +38,24 @@ wheels = [
[[package]]
name = "beautifulsoup4"
-version = "4.14.3"
+version = "4.15.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "soupsieve" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", hash = "sha256:6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86", size = 627737, upload-time = "2025-11-30T15:08:26.084Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/43/65/318323f98dbee45d42dff61d8f047181bc6f2268a9068cfad035a46be5af/beautifulsoup4-4.15.0.tar.gz", hash = "sha256:288e3ca7d54b06f2ac191970bc275c1939cb46d450b255bf6718b04aa37ab4f7", size = 632571, upload-time = "2026-06-07T16:44:20.453Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", hash = "sha256:0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb", size = 107721, upload-time = "2025-11-30T15:08:24.087Z" },
+ { url = "https://files.pythonhosted.org/packages/88/c6/92fcd42f1ba33e1184263f25bfabf3d27c383410470f169e4b8163bf9c17/beautifulsoup4-4.15.0-py3-none-any.whl", hash = "sha256:d6f88de62e1d4e38ecb1077eb9724cd0eff29d2a08ca16a401e9b9e93f117cf9", size = 109924, upload-time = "2026-06-07T16:44:21.566Z" },
]
[[package]]
name = "certifi"
-version = "2026.4.22"
+version = "2026.5.20"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" },
+ { url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" },
]
[[package]]
@@ -184,11 +184,11 @@ wheels = [
[[package]]
name = "idna"
-version = "3.15"
+version = "3.18"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/82/77/7b3966d0b9d1d31a36ddf1746926a11dface89a83409bf1483f0237aa758/idna-3.15.tar.gz", hash = "sha256:ca962446ea538f7092a95e057da437618e886f4d349216d2b1e294abfdb65fdc", size = 199245, upload-time = "2026-05-12T22:45:57.011Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/cd/63/9496c57188a2ee585e0f1db071d75089a11e98aa86eb99d9d7618fc1edce/idna-3.18.tar.gz", hash = "sha256:ffb385a7e039654cef1ab9ef32c6fafe283c0c0467bba1d9029738ce4a14a848", size = 196711, upload-time = "2026-06-02T14:34:07.794Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/d2/23/408243171aa9aaba178d3e2559159c24c1171a641aa83b67bdd3394ead8e/idna-3.15-py3-none-any.whl", hash = "sha256:048adeaf8c2d788c40fee287673ccaa74c24ffd8dcf09ffa555a2fbb59f10ac8", size = 72340, upload-time = "2026-05-12T22:45:55.733Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/5e/d4e9f1a599fb8e573b7b87160658329fbf28d19eac2718f51fc3def3aa5a/idna-3.18-py3-none-any.whl", hash = "sha256:7f952cbe720b688055e3f87de14f5c3e5fdaa8bc3928985c4077ca689de849a2", size = 65455, upload-time = "2026-06-02T14:34:06.319Z" },
]
[[package]]
@@ -340,20 +340,20 @@ wheels = [
[[package]]
name = "snowballstemmer"
-version = "3.0.1"
+version = "3.1.1"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575, upload-time = "2025-05-09T16:34:51.843Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/43/f8/0a71edf031f03c40db17503cb8ca78a69a171254e568e7db241b0ab57ea1/snowballstemmer-3.1.1.tar.gz", hash = "sha256:e07bbc54a0d798fe6010a12398422e62a8bfbba95c394fd0956ef58cb4d3e260", size = 123314, upload-time = "2026-06-03T00:56:40.194Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274, upload-time = "2025-05-09T16:34:50.371Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/07/2ebca9b11fb9be7340a818d8d6f63feaebb146be2c4afbd6061701d6df6e/snowballstemmer-3.1.1-py3-none-any.whl", hash = "sha256:7e207fa178741da09cdee59d3ecec3827ad5f92b1fc5c9ff3755b639f71f5752", size = 104164, upload-time = "2026-06-03T00:56:38.614Z" },
]
[[package]]
name = "soupsieve"
-version = "2.8.3"
+version = "2.8.4"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/7b/ae/2d9c981590ed9999a0d91755b47fc74f74de286b0f5cee14c9269041e6c4/soupsieve-2.8.3.tar.gz", hash = "sha256:3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349", size = 118627, upload-time = "2026-01-20T04:27:02.457Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/47/2c/0a5f6f8ee0d5589e48c7640213ed5175d52cf540a06725b628cc1a45d6ce/soupsieve-2.8.4.tar.gz", hash = "sha256:e121fd02e975c695e4e9e8774a5ee35d74714b59307868dcc5319ad2d9e3328e", size = 121110, upload-time = "2026-05-24T13:55:57.154Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95", size = 37016, upload-time = "2026-01-20T04:27:01.012Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/f5/0c41cb68dcae6b7de4fac4188a3a9589e21fb31df21ea3a2e888db95e6c9/soupsieve-2.8.4-py3-none-any.whl", hash = "sha256:e7e6b0769c8f51ed59acab6e994b00621096cfb1c640a7509295987388fbaf65", size = 37304, upload-time = "2026-05-24T13:55:55.406Z" },
]
[[package]]