Skip to main content

Overview

GitFolio templates support both light and dark themes, giving your portfolio a modern look that adapts to user preferences. Theme switching is powered by next-themes and provides seamless transitions between modes.

Dark and light mode

All GitFolio templates include built-in support for light and dark themes:
GitFolio uses the next-themes library to manage theme state:
import { useTheme } from "next-themes";

const { theme, setTheme, resolvedTheme } = useTheme();

// Toggle between themes
const toggleTheme = () => {
  setTheme(theme === "dark" ? "light" : "dark");
};
The theme state is persisted in localStorage and applied on page load.

Template-specific themes

Some templates enforce a specific default theme:

Dark-first templates

Templates like Persona and Pixel Perfect default to dark mode:
// From Persona/template.tsx
const template = ({ data = DummyData }: { data?: DATA }) => {
  const { setTheme } = useTheme();
  
  useEffect(() => {
    setTheme("dark"); // Force dark theme
  }, []);
  
  return (
    <main className="antialiased bg-[#0a0a0a] text-stone-200 min-h-screen">
      {/* Template content */}
    </main>
  );
};
This ensures the template displays with its intended aesthetic.

Flexible themes

Other templates support both light and dark modes with adaptive styling:
// Tailwind classes adapt to theme
<div className="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
  <p className="text-muted-foreground">This text adapts to the current theme</p>
</div>

Theme configuration

Themes are configured in your app’s theme provider:
1

ThemeProvider setup

Wrap your app with the ThemeProvider:
import { ThemeProvider } from "next-themes";

export default function App({ Component, pageProps }) {
  return (
    <ThemeProvider
      attribute="class"
      defaultTheme="system"
      enableSystem
      disableTransitionOnChange
    >
      <Component {...pageProps} />
    </ThemeProvider>
  );
}
2

CSS variables

Define theme colors using CSS variables:
:root {
  --background: 0 0% 100%;
  --foreground: 222.2 84% 4.9%;
  --primary: 222.2 47.4% 11.2%;
  --muted: 210 40% 96.1%;
  --border: 214.3 31.8% 91.4%;
}

.dark {
  --background: 222.2 84% 4.9%;
  --foreground: 210 40% 98%;
  --primary: 210 40% 98%;
  --muted: 217.2 32.6% 17.5%;
  --border: 217.2 32.6% 17.5%;
}
3

Apply to components

Use the CSS variables in your Tailwind config:
// tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        background: "hsl(var(--background))",
        foreground: "hsl(var(--foreground))",
        primary: {
          DEFAULT: "hsl(var(--primary))",
          foreground: "hsl(var(--primary-foreground))",
        },
        muted: {
          DEFAULT: "hsl(var(--muted))",
          foreground: "hsl(var(--muted-foreground))",
        },
      },
    },
  },
};

System preference detection

GitFolio can automatically detect the user’s system theme preference:
const { systemTheme, theme } = useTheme();

// Use system theme if no preference is set
const currentTheme = theme === "system" ? systemTheme : theme;
This provides a native experience that matches the user’s OS settings.

Theme toggle button

Add a theme toggle to your portfolio’s navigation:
import { ModeToggle } from "@workspace/templates/components/mode-toggle";

export function Navbar() {
  return (
    <nav className="flex items-center justify-between p-4">
      <div className="flex items-center gap-4">
        <Logo />
        <NavLinks />
      </div>
      <ModeToggle variant="ghost" />
    </nav>
  );
}
The toggle button shows a sun icon in dark mode and a moon icon in light mode.

Template theme examples

Here’s how different templates implement theming:

Pixel Perfect

GitHub-inspired design with:
  • Dark mode by default
  • Light borders and separators
  • Contribution graph in theme colors
  • Subtle gradients

Persona

Dark-themed template with:
  • Black background (#0a0a0a)
  • Stone-200 text color
  • Animated gradient effects
  • High contrast elements

White Space

Flexible theme with:
  • Clean white/dark backgrounds
  • Adaptive text colors
  • Spacious layout
  • Theme-aware shadows

Clean Slate

Minimalist theming:
  • Neutral color palette
  • Subtle theme transitions
  • Focus on readability
  • System theme support

Custom color schemes

While templates come with predefined themes, you can customize colors:

Modifying theme colors

Advanced customization requires editing the template’s CSS variables or Tailwind configuration. This is recommended for users comfortable with CSS and Tailwind.
/* Override theme colors in your global CSS */
.dark {
  --background: 224 71% 4%;      /* Custom dark background */
  --primary: 210 100% 50%;       /* Custom primary color */
  --accent: 45 93% 47%;          /* Custom accent color */
}

Per-template customization

Each template can have unique theme variables:
// Obsidian template with gradient accents
<main className="bg-gradient-to-br from-gray-900 via-purple-900 to-gray-900">
  <div className="text-white">
    {/* Dark theme with purple gradients */}
  </div>
</main>

Accessibility considerations

GitFolio themes are designed with accessibility in mind:

Contrast ratios

All text maintains WCAG AA contrast ratios:
  • Normal text: 4.5:1 minimum
  • Large text: 3:1 minimum
  • UI components: 3:1 minimum

Reduced motion

Respects user’s motion preferences:
@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

Focus indicators

Clear focus states for keyboard navigation:
.focus-visible:focus {
  outline: 2px solid hsl(var(--ring));
  outline-offset: 2px;
}

High contrast mode

Themes work with OS high contrast modes:
@media (prefers-contrast: high) {
  :root {
    --border: 0 0% 0%;
    --foreground: 0 0% 0%;
  }
}

Theme persistence

Theme preferences are saved automatically:
// next-themes handles persistence via localStorage
localStorage.setItem("theme", "dark");

// On page load, the theme is restored
const savedTheme = localStorage.getItem("theme") || "system";
setTheme(savedTheme);
This ensures visitors see their preferred theme on repeat visits.

Troubleshooting themes

If the theme toggle doesn’t work:
  1. Ensure ThemeProvider wraps your app
  2. Check that attribute="class" is set on ThemeProvider
  3. Verify dark mode classes exist in your CSS
  4. Clear localStorage and refresh the page
To prevent theme flashing on page load:
<ThemeProvider
  attribute="class"
  defaultTheme="system"
  enableSystem
  disableTransitionOnChange  // Prevents flash
>
  <YourApp />
</ThemeProvider>
Some templates enforce a specific theme:
useEffect(() => {
  setTheme("dark"); // Template forces dark mode
}, []);
This is intentional for templates designed for a specific aesthetic.

Next steps

Choose a template

Explore templates with different theme aesthetics

Customize content

Personalize your portfolio content

Build docs developers (and LLMs) love