Explore interactive endpoints for both GraphQL and REST APIs. All requests route through the dev proxy which injects authentication headers securely.
diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx
index 75f634e..fd38f5a 100644
--- a/src/components/Sidebar.jsx
+++ b/src/components/Sidebar.jsx
@@ -1,8 +1,8 @@
import { useState } from 'react'
-import { Search, ChevronRight, ChevronDown, Layers, Terminal } from 'lucide-react'
+import { Search, ChevronRight, ChevronDown, Layers, Terminal, X } from 'lucide-react'
import { getTopicIcon } from '../lib/icons'
-export default function Sidebar({ legacyTopics, restTopics, activeTopic, setActiveTopic, searchQuery, setSearchQuery }) {
+export default function Sidebar({ legacyTopics, restTopics, activeTopic, setActiveTopic, searchQuery, setSearchQuery, isOpen = false, onClose }) {
const [open, setOpen] = useState({ general: true, legacy: true, rest: true })
const toggle = (k) => setOpen((s) => ({ ...s, [k]: !s[k] }))
@@ -10,12 +10,12 @@ export default function Sidebar({ legacyTopics, restTopics, activeTopic, setActi
{topics.map((t) => {
@@ -27,13 +27,13 @@ export default function Sidebar({ legacyTopics, restTopics, activeTopic, setActi
onClick={() => setActiveTopic(t)}
className={`w-full flex items-center gap-2.5 px-3 py-2 rounded-lg text-sm transition-all duration-200 group ${
isActive
- ? 'text-brand-700 bg-brand-50 shadow-sm font-medium'
- : 'text-slate-600 hover:text-slate-900 hover:bg-slate-100/50'
+ ? 'text-brand-700 dark:text-brand-300 bg-brand-50 dark:bg-brand-500/10 shadow-sm font-medium'
+ : 'text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-slate-100 hover:bg-slate-100/50 dark:hover:bg-white/5'
}`}
>
{t.name}
@@ -44,21 +44,35 @@ export default function Sidebar({ legacyTopics, restTopics, activeTopic, setActi
)
return (
-
+
{/* Brand Header */}
-
-
-
-
+
+
+
-
NearleXpress
+ {/* Close button — mobile only */}
+
setSearchQuery(e.target.value)}
@@ -73,21 +87,21 @@ export default function Sidebar({ legacyTopics, restTopics, activeTopic, setActi
diff --git a/src/components/ThemeToggle.jsx b/src/components/ThemeToggle.jsx
new file mode 100644
index 0000000..a254d9d
--- /dev/null
+++ b/src/components/ThemeToggle.jsx
@@ -0,0 +1,40 @@
+import { useEffect, useState } from 'react'
+import { Sun, Moon } from 'lucide-react'
+
+function getInitialTheme() {
+ if (typeof window === 'undefined') return false
+ const stored = localStorage.getItem('theme')
+ if (stored) return stored === 'dark'
+ return window.matchMedia('(prefers-color-scheme: dark)').matches
+}
+
+export default function ThemeToggle() {
+ const [dark, setDark] = useState(getInitialTheme)
+
+ useEffect(() => {
+ const root = document.documentElement
+ if (dark) root.classList.add('dark')
+ else root.classList.remove('dark')
+ try { localStorage.setItem('theme', dark ? 'dark' : 'light') } catch {}
+ }, [dark])
+
+ return (
+
+ )
+}
diff --git a/src/components/TopicView.jsx b/src/components/TopicView.jsx
index 81aab5d..2595512 100644
--- a/src/components/TopicView.jsx
+++ b/src/components/TopicView.jsx
@@ -108,24 +108,24 @@ export default function TopicView({ topic, searchQuery }) {
return (
-
{topic.name}
-
{topic.description}
+
{topic.name}
+
{topic.description}
{topic.type === 'legacy' ? 'GraphQL API' : 'REST API'}
- {filtered.length} endpoint{filtered.length !== 1 ? 's' : ''}
+ {filtered.length} endpoint{filtered.length !== 1 ? 's' : ''}
{filtered.length === 0 ? (
-
+
No endpoints match "{searchQuery}".
) : (
diff --git a/src/index.css b/src/index.css
index f16be74..365b95b 100644
--- a/src/index.css
+++ b/src/index.css
@@ -8,6 +8,18 @@
body {
@apply bg-slate-50 text-slate-800 font-sans antialiased selection:bg-brand-500/30 selection:text-brand-900;
}
+
+ .dark body {
+ @apply bg-dark-900 text-slate-200;
+ }
+
+ html {
+ color-scheme: light;
+ }
+
+ html.dark {
+ color-scheme: dark;
+ }
}
@layer utilities {
@@ -15,6 +27,10 @@
@apply bg-white/70 backdrop-blur-md border border-white/40 shadow-glass;
}
+ .dark .glass {
+ @apply bg-dark-800/80 border-white/5 shadow-none;
+ }
+
.dark-glass {
@apply bg-dark-900/90 backdrop-blur-xl border border-white/5;
}
@@ -40,12 +56,22 @@
::-webkit-scrollbar-thumb:hover {
@apply bg-slate-300;
}
+.dark ::-webkit-scrollbar-thumb {
+ @apply bg-dark-600 rounded-full;
+}
+.dark ::-webkit-scrollbar-thumb:hover {
+ @apply bg-dark-700;
+}
.bg-grid-pattern {
background-image: linear-gradient(to right, #f1f5f9 1px, transparent 1px),
linear-gradient(to bottom, #f1f5f9 1px, transparent 1px);
background-size: 40px 40px;
}
+.dark .bg-grid-pattern {
+ background-image: linear-gradient(to right, rgba(255, 255, 255, 0.03) 1px, transparent 1px),
+ linear-gradient(to bottom, rgba(255, 255, 255, 0.03) 1px, transparent 1px);
+}
.animation-delay-2000 { animation-delay: 2s; }
.animation-delay-4000 { animation-delay: 4s; }
diff --git a/tailwind.config.js b/tailwind.config.js
index 00dfa31..6b22766 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -1,5 +1,6 @@
/** @type {import('tailwindcss').Config} */
export default {
+ darkMode: 'class',
content: ['./index.html', './src/**/*.{js,jsx}'],
theme: {
extend: {