Skip to main content

Overview

GitScope includes a full-featured dark mode that adapts the entire interface for comfortable viewing in any lighting condition. Theme preferences are automatically saved and persist across sessions.
Your theme choice is saved to localStorage and restored automatically when you return to GitScope.

Quick Toggle

Switch themes instantly using the theme button in the header:
Header.jsx
<button className="theme-btn" onClick={toggleTheme} aria-label="Toggle theme">
  {theme === 'dark' ? <Sun size={18} /> : <Moon size={18} />}
</button>

Light Mode

Shows a Moon icon—click to switch to dark mode

Dark Mode

Shows a Sun icon—click to switch to light mode

Implementation

The theme system uses React state and HTML data attributes:

State Management

App.jsx
const [theme, setTheme] = useState(
  () => localStorage.getItem('theme') || 'light'
)

const toggleTheme = () => setTheme(t => t === 'dark' ? 'light' : 'dark')
The initial state reads from localStorage, defaulting to light mode for first-time users.

DOM Synchronization

App.jsx
useEffect(() => {
  document.documentElement.setAttribute('data-theme', theme)
  localStorage.setItem('theme', theme)
}, [theme])
1

Update Data Attribute

Sets data-theme="dark" or data-theme="light" on the <html> element
2

Save to Storage

Persists theme choice to localStorage as ‘theme’
3

Trigger CSS Variables

CSS custom properties update based on data-theme value

CSS Architecture

Themes use CSS custom properties for instant switching:
:root {
  --bg: #ffffff;
  --text: #1a1a1a;
  --border: #e5e7eb;
  --card-bg: #ffffff;
  --accent: #3b82f6;
  /* ... more variables */
}

[data-theme="dark"] {
  --bg: #0a0a0a;
  --text: #e5e5e5;
  --border: #2a2a2a;
  --card-bg: #141414;
  --accent: #60a5fa;
  /* ... more variables */
}

Key Color Variables

  • --bg - Main page background
  • --card-bg - Card and panel backgrounds
  • --card-hover - Hover state for interactive elements
  • --text - Primary text color
  • --text-dim - Secondary/muted text
  • --text-on-accent - Text displayed on accent backgrounds
  • --border - Default border color
  • --input-bg - Input field backgrounds
  • --shadow - Box shadow colors
  • --accent - Primary brand/action color
  • --green, --yellow, --red - Status indicators
  • --gradient-* - Gradient overlays and effects

Component Integration

All components automatically respond to theme changes:

Header Component

Header.jsx
export function Header({ theme, toggleTheme, rateLimit, onTokenClick }) {
  return (
    <header className="header">
      {/* ... other content ... */}
      
      <button className="theme-btn" onClick={toggleTheme} aria-label="Toggle theme">
        {theme === 'dark' ? <Sun size={18} /> : <Moon size={18} />}
      </button>
    </header>
  )
}
The theme prop is passed from App.jsx to control the icon display, while CSS variables handle visual changes.

Accessibility Features

ARIA Label

Button includes aria-label="Toggle theme" for screen readers

Visual Feedback

Icon changes immediately to reflect current theme state

No Flash

Theme loads from localStorage before render to prevent white flash

High Contrast

Dark mode uses carefully chosen colors for WCAG compliance

Persistent Storage

Theme preferences survive page reloads and browser sessions:
// On theme change
localStorage.setItem('theme', theme)

// On app initialization
const [theme, setTheme] = useState(
  () => localStorage.getItem('theme') || 'light'
)
Clear your browser’s localStorage to reset the theme to the default light mode.

Smooth Transitions

CSS transitions provide smooth color changes:
* {
  transition: background-color 0.2s ease,
              border-color 0.2s ease,
              color 0.2s ease;
}
Avoid adding transitions to layout properties (width, height, transform) to prevent performance issues during theme switches.

Dark Mode Best Practices

1

Reduce Pure Black

GitScope uses #0a0a0a instead of #000000 to reduce eye strain
2

Maintain Contrast

All text meets WCAG AA standards for contrast against backgrounds
3

Subtle Borders

Dark mode borders use #2a2a2a to maintain separation without harshness
4

Adjusted Accents

Accent colors are lighter in dark mode (#60a5fa) for better visibility

Color Palette

Light Mode

--bg: #ffffff
--text: #1a1a1a
--border: #e5e7eb
--card-bg: #ffffff
--accent: #3b82f6

Dark Mode

--bg: #0a0a0a
--text: #e5e5e5
--border: #2a2a2a
--card-bg: #141414
--accent: #60a5fa
These colors are carefully balanced to provide excellent readability and aesthetic appeal in both modes.

Icon Usage

Theme toggle uses Lucide React icons:
import { Moon, Sun } from 'lucide-react'

{theme === 'dark' ? <Sun size={18} /> : <Moon size={18} />}

Moon Icon

Displayed in light mode to suggest switching to dark

Sun Icon

Displayed in dark mode to suggest switching to light

User Experience Flow

1

First Visit

User sees GitScope in light mode (default)
2

Toggle Dark Mode

User clicks moon icon to switch to dark theme
3

Preference Saved

Choice is saved to localStorage
4

Return Visit

GitScope loads in dark mode automatically

Testing Different Themes

Easily test both themes during development:
// Force light mode
localStorage.setItem('theme', 'light')
window.location.reload()

// Force dark mode
localStorage.setItem('theme', 'dark')
window.location.reload()

// Clear preference (reverts to default)
localStorage.removeItem('theme')
window.location.reload()

System Preference Detection

Current implementation defaults to light mode. To detect system preference, you could enhance the initial state:
const [theme, setTheme] = useState(() => {
  const saved = localStorage.getItem('theme')
  if (saved) return saved
  
  const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
  return prefersDark ? 'dark' : 'light'
})

Performance Considerations

No Repaints

CSS variables change colors without triggering layout recalculation

Instant Toggle

Theme switches happen in under 50ms for responsive UX

Minimal Storage

Only stores 5-10 bytes (‘light’ or ‘dark’) in localStorage

No Network Calls

Theme changes are purely client-side

Common Issues

If you see a white flash in dark mode, ensure localStorage is read before React renders:
const [theme, setTheme] = useState(
  () => localStorage.getItem('theme') || 'light'
)
The function form of useState ensures synchronous localStorage read before first render.
Check that localStorage is enabled in your browser. Some privacy modes block localStorage.
Verify the data-theme attribute is set on <html> and CSS custom properties are defined in :root.

User Search

Search interface adapts to dark mode automatically

Repository Explorer

Repository cards use theme-aware colors

Build docs developers (and LLMs) love