Feature: Dark Mode
Relevant source files
The following files were used as context for generating this wiki page:
The Heyme application includes a dark mode feature to enhance user experience by allowing users to switch between a light and a dark color scheme. This functionality is implemented using the next-themes library in conjunction with Tailwind CSS's dark mode variant. The theme is toggled by adding or removing a dark class to the root <html> element, which conditionally applies dark-themed styles defined throughout the application's components.
The implementation is primarily managed by a global ThemeProvider, a dedicated ThemeToggle component for user interaction, and a class-based styling strategy configured in Tailwind CSS. This allows for a consistent and maintainable approach to theming across the entire application.
Sources: README.md:16, package.json:49, pages/_app.tsx:29-31
Architecture and Core Implementation
The dark mode architecture is centered around three key parts: the next-themes library for state management, Tailwind CSS for styling, and the Next.js application structure for integration.
1. Theme Provider Setup
The root of the application in pages/_app.tsx is wrapped with the ThemeProvider from next-themes. This provider is responsible for managing the theme state and applying the theme class to the DOM.
attribute="class": This prop tellsnext-themesto toggle themes by changing a class on the<html>element.defaultTheme="system": The application will initially respect the user's operating system preference for light or dark mode.enableSystem: Enables the automatic detection and application of the system's theme.
// pages/_app.tsx:29-31
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<Component {...pageProps} />
</ThemeProvider>
Sources: pages/_app.tsx:3, pages/_app.tsx:29-31
2. Tailwind CSS Configuration
The dark mode strategy is enabled in tailwind.config.js by setting the darkMode option to "class". This instructs Tailwind to generate utility classes with a dark: variant (e.g., dark:bg-slate-900), which are applied only when the dark class is present on an ancestor element (in this case, the <html> tag).
// tailwind.config.js:3-4
module.exports = {
darkMode: ["class"],
// ...
}
Sources: tailwind.config.js:3-4
3. Document Structure
The main document file, pages/_document.tsx, sets up the initial HTML structure. The <html> element has a dark class and a color-scheme: dark style applied by default, ensuring the dark theme is correctly rendered on initial load if it's the active theme. The <body> tag also contains default light and dark theme classes for background and text colors.
// pages/_document.tsx:4
<Html lang="en" className="dark" style={{ colorScheme: 'dark' }}>
// ...
<body className="min-h-screen bg-slate-100 font-sans text-slate-900 antialiased dark:bg-slate-900 dark:text-slate-50 ">
// ...
</body>
</Html>
Sources: pages/_document.tsx:4, pages/_document.tsx:12
Component Integration and Theme Toggling
The user-facing part of the feature is a toggle button that allows switching between light and dark modes.
ThemeToggle Component
This React component is the user interface for changing the theme.
- It uses the
useThemehook fromnext-themesto access the currentthemeand thesetThemefunction. - An
onClickevent handler toggles the theme: if the current theme is 'light', it sets it to 'dark', and vice versa.
The following diagram shows the flow when a user toggles the theme.
This diagram illustrates the sequence of events from user click to UI update for the theme change.
Sources: components/theme-toggle.tsx:9-12
SiteHeader Integration
The ThemeToggle component is integrated into the application's main header, making it accessible on all pages.
// components/site-header.tsx:49
<ThemeToggle />
The following diagram shows how the components are structured.
This diagram shows the component hierarchy for the theme feature.
Sources: components/site-header.tsx:7, components/site-header.tsx:49
Dark Mode Styling Strategy
Styling for dark mode is handled on a per-component basis using Tailwind's dark: variant. This allows for granular control over the appearance of each element in both light and dark modes.
Styling Examples
Below are examples of how dark: utility classes are used across different components to adapt their styles.
| Component | Light Mode Classes | Dark Mode Classes | Source File |
|---|---|---|---|
SiteHeader | border-b-slate-200 bg-white | dark:border-b-slate-700 dark:bg-slate-900 | components/site-header.tsx:11 |
Testimonial | bg-white | dark:bg-slate-800 | components/testimonials.tsx:20 |
Testimonial | text-slate-700 | dark:text-slate-100 | components/testimonials.tsx:22 |
DialogContent | bg-white | dark:bg-slate-900 | components/ui/dialog.tsx:43 |
DialogTitle | text-slate-900 | dark:text-slate-50 | components/ui/dialog.tsx:93 |
body | bg-slate-100 text-slate-900 | dark:bg-slate-900 dark:text-slate-50 | pages/_document.tsx:12 |
This utility-first approach ensures that all styling logic is co-located with the component's markup, making it easy to understand and maintain.
Sources: components/site-header.tsx:11, components/testimonials.tsx:20-22, components/ui/dialog.tsx:43-93, pages/_document.tsx:12
Summary
The dark mode feature in Heyme is a robust and well-integrated system built on modern frontend technologies. By leveraging next-themes for state management and Tailwind CSS's class-based variant system, the application provides a seamless and maintainable way to offer users a choice in their preferred viewing mode. The implementation is clean, with a clear separation of concerns between the theme management logic, the UI toggle component, and the component-level styling.