Skip to content

Commit 96e859b

Browse files
committed
fix: use native Swagger UI dark mode support (#440)
1 parent cc0a9b7 commit 96e859b

File tree

4 files changed

+49
-288
lines changed

4 files changed

+49
-288
lines changed

app/globals.css

Lines changed: 5 additions & 270 deletions
Original file line numberDiff line numberDiff line change
@@ -94,278 +94,13 @@ body {
9494
display: none;
9595
}
9696

97-
/* Base Swagger UI styling */
98-
.swagger-ui {
99-
background-color: transparent;
100-
font-family: inherit;
101-
}
102-
103-
/* ============================================
104-
Dark mode styling for Swagger UI
105-
Fixes issue #440: dark mode text was unreadable
106-
Light mode uses Swagger UI defaults (works fine)
107-
============================================ */
108-
109-
/* Dark mode text colors - excluding links (handled separately) */
110-
html.dark .swagger-ui .opblock-summary-path,
111-
html.dark .swagger-ui .opblock-summary-path span,
112-
html.dark .swagger-ui .opblock-tag,
113-
html.dark .swagger-ui .opblock-tag small,
114-
html.dark .swagger-ui .info .title,
115-
html.dark .swagger-ui .info .title small,
116-
html.dark .swagger-ui .info .title small pre,
117-
html.dark .swagger-ui .info li,
118-
html.dark .swagger-ui .info p,
119-
html.dark .swagger-ui .info table,
120-
html.dark .swagger-ui .info h1,
121-
html.dark .swagger-ui .info h2,
122-
html.dark .swagger-ui .info h3,
123-
html.dark .swagger-ui .info h4,
124-
html.dark .swagger-ui .info h5,
125-
html.dark .swagger-ui .tab li,
126-
html.dark .swagger-ui .model-title,
127-
html.dark .swagger-ui .model .property.primitive,
128-
html.dark .swagger-ui .responses-inner h4,
129-
html.dark .swagger-ui .responses-inner h5,
130-
html.dark .swagger-ui .response-col_status,
131-
html.dark .swagger-ui .response-col_links,
132-
html.dark .swagger-ui .opblock .opblock-summary-description,
133-
html.dark .swagger-ui .opblock-description-wrapper p,
134-
html.dark .swagger-ui .opblock-external-docs-wrapper,
135-
html.dark .swagger-ui .opblock-title_normal,
136-
html.dark .swagger-ui .parameter__name,
137-
html.dark .swagger-ui .parameter__type,
138-
html.dark .swagger-ui .parameter__deprecated,
139-
html.dark .swagger-ui .parameter__in,
140-
html.dark .swagger-ui .response-col_description__inner p,
141-
html.dark .swagger-ui .response-col_description__inner span,
142-
html.dark .swagger-ui .prop-type,
143-
html.dark .swagger-ui .prop-format,
144-
html.dark .swagger-ui table thead tr th,
145-
html.dark .swagger-ui table thead tr td,
146-
html.dark .swagger-ui table tbody tr th,
147-
html.dark .swagger-ui table tbody tr td,
148-
html.dark .swagger-ui .renderedMarkdown p,
149-
html.dark .swagger-ui .renderedMarkdown li,
150-
html.dark .swagger-ui .renderedMarkdown code,
151-
html.dark .swagger-ui .markdown p,
152-
html.dark .swagger-ui .markdown li,
153-
html.dark .swagger-ui .markdown code {
154-
color: var(--color-primary-200);
155-
}
156-
157-
/* Dark mode links - green for visibility */
158-
html.dark .swagger-ui a {
159-
color: #6ee7b7;
160-
}
161-
162-
/* Dark mode code blocks */
163-
html.dark .swagger-ui .highlight-code,
164-
html.dark .swagger-ui .microlight {
165-
background-color: var(--color-primary-800) !important;
166-
}
167-
168-
html.dark .swagger-ui pre.microlight {
169-
background-color: var(--color-primary-800) !important;
170-
color: var(--color-primary-200) !important;
171-
}
172-
173-
html.dark .swagger-ui pre.microlight code {
174-
color: var(--color-primary-200) !important;
175-
}
176-
177-
/* Dark mode operation blocks - GET */
178-
html.dark .swagger-ui .opblock.opblock-get {
179-
background: rgba(97, 175, 254, 0.15);
180-
border-color: #61affe;
181-
}
182-
183-
html.dark .swagger-ui .opblock.opblock-get .opblock-summary {
184-
border-color: #61affe;
185-
}
186-
187-
/* Dark mode operation blocks - POST */
188-
html.dark .swagger-ui .opblock.opblock-post {
189-
background: rgba(73, 204, 144, 0.15);
190-
border-color: #49cc90;
191-
}
192-
193-
html.dark .swagger-ui .opblock.opblock-post .opblock-summary {
194-
border-color: #49cc90;
195-
}
196-
197-
/* Dark mode operation blocks - PUT */
198-
html.dark .swagger-ui .opblock.opblock-put {
199-
background: rgba(252, 161, 48, 0.15);
200-
border-color: #fca130;
201-
}
202-
203-
html.dark .swagger-ui .opblock.opblock-put .opblock-summary {
204-
border-color: #fca130;
205-
}
206-
207-
/* Dark mode operation blocks - DELETE */
208-
html.dark .swagger-ui .opblock.opblock-delete {
209-
background: rgba(249, 62, 62, 0.15);
210-
border-color: #f93e3e;
211-
}
212-
213-
html.dark .swagger-ui .opblock.opblock-delete .opblock-summary {
214-
border-color: #f93e3e;
215-
}
216-
217-
/* Dark mode operation blocks - PATCH */
218-
html.dark .swagger-ui .opblock.opblock-patch {
219-
background: rgba(80, 227, 194, 0.15);
220-
border-color: #50e3c2;
221-
}
222-
223-
html.dark .swagger-ui .opblock.opblock-patch .opblock-summary {
224-
border-color: #50e3c2;
225-
}
226-
227-
/* Dark mode expanded operation block body */
228-
html.dark .swagger-ui .opblock-body pre.microlight {
229-
background-color: #1a1a1a !important;
230-
border: 1px solid var(--color-primary-700);
231-
}
232-
233-
/* Dark mode response section */
234-
html.dark .swagger-ui .responses-wrapper,
235-
html.dark .swagger-ui .response {
236-
background-color: transparent;
237-
}
238-
239-
html.dark .swagger-ui .response-col_description__inner {
240-
background-color: transparent;
241-
}
242-
243-
/* Dark mode models section */
244-
html.dark .swagger-ui .models {
245-
border-color: var(--color-primary-700);
246-
}
247-
248-
html.dark .swagger-ui .model-container {
249-
background-color: #1a1a1a;
250-
border-color: var(--color-primary-700);
251-
}
252-
253-
html.dark .swagger-ui .model-box {
254-
background-color: var(--color-primary-800) !important;
97+
/* Prevent Swagger UI's dark-mode from overriding the page background */
98+
html.dark-mode {
99+
background: rgb(var(--nextra-bg));
255100
}
256101

257-
html.dark .swagger-ui .model {
258-
color: var(--color-primary-200);
259-
}
260-
261-
/* Dark mode tables */
262-
html.dark .swagger-ui table {
263-
background-color: transparent;
264-
}
265-
266-
html.dark .swagger-ui table thead tr th,
267-
html.dark .swagger-ui table thead tr td {
268-
border-bottom-color: var(--color-primary-700);
269-
}
270-
271-
html.dark .swagger-ui table tbody tr td {
272-
border-bottom-color: #333;
273-
}
274-
275-
/* Dark mode buttons and interactive elements */
276-
html.dark .swagger-ui .btn {
277-
background-color: var(--color-primary-700);
278-
color: var(--color-primary-200);
279-
border-color: var(--color-primary-600);
280-
}
281-
282-
html.dark .swagger-ui .btn:hover {
283-
background-color: var(--color-primary-600);
284-
}
285-
286-
/* Dark mode tab styling */
287-
html.dark .swagger-ui .tab li {
288-
background-color: transparent;
289-
border-color: var(--color-primary-700);
290-
}
291-
292-
html.dark .swagger-ui .tab li.active {
293-
background-color: var(--color-primary-800);
294-
}
295-
296-
/* Dark mode parameters table */
297-
html.dark .swagger-ui .parameters-col_description {
298-
color: var(--color-primary-400);
299-
}
300-
301-
html.dark .swagger-ui .parameter__name.required::after {
302-
color: #f87171;
303-
}
304-
305-
/* Dark mode copy button - target the SVG inside */
306-
html.dark .swagger-ui .copy-to-clipboard {
307-
background-color: var(--color-primary-700);
308-
}
309-
310-
html.dark .swagger-ui .copy-to-clipboard svg {
311-
fill: var(--color-primary-200);
312-
}
313-
314-
/* Dark mode expand/collapse icons */
315-
html.dark .swagger-ui .expand-operation svg,
316-
html.dark .swagger-ui .expand-methods svg {
317-
fill: var(--color-primary-400);
318-
}
319-
320-
/* Dark mode arrow icons */
321-
html.dark .swagger-ui .arrow {
322-
fill: var(--color-primary-400);
323-
}
324-
325-
/* Dark mode loading spinner */
326-
html.dark .swagger-ui .loading-container .loading::after {
327-
border-color: var(--color-primary-700);
328-
border-top-color: var(--color-primary-200);
329-
}
330-
331-
/* Dark mode section borders */
332-
html.dark .swagger-ui section.models.is-open {
333-
border-color: var(--color-primary-700);
334-
}
335-
336-
html.dark .swagger-ui .opblock-tag-section {
337-
border-bottom-color: var(--color-primary-700);
338-
}
339-
340-
/* Dark mode section headers (Parameters, Request body, Responses) */
341-
html.dark .swagger-ui .opblock-section-header {
342-
background-color: var(--color-primary-800);
343-
border-bottom-color: var(--color-primary-700);
344-
}
345-
346-
html.dark .swagger-ui .opblock-section-header h4,
347-
html.dark .swagger-ui .opblock-section-header label {
348-
color: var(--color-primary-200);
349-
}
350-
351-
/* Dark mode Example Value / Model tabs */
352-
html.dark .swagger-ui .tab li button,
353-
html.dark .swagger-ui .tab li button.tablinks {
354-
color: var(--color-primary-300);
102+
/* Base Swagger UI styling - keep transparent background for integration */
103+
.swagger-ui {
355104
background-color: transparent;
356105
}
357106

358-
html.dark .swagger-ui .tab li button.tablinks.active,
359-
html.dark .swagger-ui .tab li button:focus {
360-
color: var(--color-primary-100);
361-
}
362-
363-
/* Dark mode model/schema display */
364-
html.dark .swagger-ui .model-toggle::after {
365-
background-color: var(--color-primary-400);
366-
}
367-
368-
html.dark .swagger-ui .model span,
369-
html.dark .swagger-ui .model-title__text {
370-
color: var(--color-primary-200);
371-
}

components/swagger.tsx

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use client";
22

33
import dynamic from "next/dynamic";
4+
import { useEffect } from "react";
45
import type { SwaggerUIProps } from "swagger-ui-react";
56

67
const SwaggerUI = dynamic(
@@ -13,7 +14,33 @@ const SwaggerUI = dynamic(
1314

1415
import "swagger-ui-react/swagger-ui.css";
1516

17+
// Sync Nextra's "dark" class with Swagger UI's "dark-mode" class
18+
function useSyncDarkMode() {
19+
useEffect(() => {
20+
const html = document.documentElement;
21+
22+
const syncDarkMode = () => {
23+
html.classList.toggle("dark-mode", html.classList.contains("dark"));
24+
};
25+
26+
syncDarkMode();
27+
28+
const observer = new MutationObserver(() => {
29+
syncDarkMode();
30+
});
31+
32+
observer.observe(html, { attributes: true, attributeFilter: ["class"] });
33+
34+
return () => {
35+
observer.disconnect();
36+
html.classList.remove("dark-mode");
37+
};
38+
}, []);
39+
}
40+
1641
export function Swagger() {
42+
useSyncDarkMode();
43+
1744
return (
1845
<SwaggerUI
1946
url="https://raw.githubusercontent.com/authzed/api/refs/heads/main/docs/apidocs.swagger.json"

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
"react-dom": "^19.2.3",
3939
"react-youtube": "^10.1.0",
4040
"sharp": "^0.34.0",
41-
"swagger-ui-react": "^5.30.2",
41+
"swagger-ui-react": "^5.31.0",
4242
"tailwind-merge": "^3.4.0",
4343
"tailwindcss": "^4.1.17"
4444
},

0 commit comments

Comments
 (0)