Styling and Theme Switching
Relevant source files
The following files were used as context for generating this wiki page:The styling and theming system in railways-docs is a modern, hybrid approach that combines the utility-first power of Tailwind CSS with the component-scoped styling of styled-components. This is made possible by twin.macro, which allows developers to use Tailwind classes within styled-components. The system is fully themeable, supporting both light and dark modes, managed by next-themes.
This architecture provides a flexible and maintainable way to style the application. It leverages CSS variables for theming, which are defined in the Tailwind configuration and applied globally. A ThemeSwitcher component handles the user-facing logic for toggling between themes, which persists across sessions using a cookie.
Core Technologies
The project's styling is built upon a stack of several key libraries. Each library plays a distinct role in the overall architecture.
Sources: package.json:11-44
This diagram illustrates the relationship between the core styling technologies.
| Library | Role | Source |
|---|---|---|
tailwindcss | A utility-first CSS framework for rapid UI development. | package.json:42 |
styled-components | Enables writing CSS-in-JS for component-scoped styles. | package.json:40 |
twin.macro | A bridge that allows using Tailwind CSS classes within styled-components. | package.json:41 |
next-themes | Manages theme switching (light/dark mode) in Next.js applications. | package.json:28 |
Tailwind CSS Configuration
The primary configuration for Tailwind CSS is located in tailwind.config.js. It's set up to enable class-based dark mode, which is essential for the theme switching mechanism provided by next-themes.
// tailwind.config.js:34-35
module.exports = {
// ...
darkMode: "class", // 'media' or 'class'
// ...
};
The configuration also defines a custom color palette that relies on CSS variables. This is the foundation of the theming system.
Sources: tailwind.config.js:1-32
Color System and CSS Variables
Colors are not hardcoded. Instead, the tailwind.config.js file uses a helper function, generateColorShades, to create a palette that maps to CSS variables. This allows the theme to be changed at runtime by simply updating the values of these variables.
The following diagram shows how color variables are generated and consumed.
The core of this system is the generateColorShades function and the customColors object.
// tailwind.config.js:3-12
const prefix = "colors";
const generateColorShades = name =>
Array.from({ length: 9 })
.map((_, i) => (i + 1) * 100)
.reduce(
(acc, k) => ({
...acc,
[k]: `var(--${prefix}-${name}-${k})`,
}),
{},
);
// tailwind.config.js:14-24
const customColors = {
foreground: `var(--${prefix}-foreground)`,
background: `var(--${prefix}-background)`,
secondaryBg: `var(--${prefix}-secondaryBg)`,
gray: generateColorShades("gray"),
pink: generateColorShades("pink"),
blue: generateColorShades("blue"),
yellow: generateColorShades("yellow"),
green: generateColorShades("green"),
red: generateColorShades("red"),
};
These variables are then used throughout the application via Tailwind utility classes, as seen in src/pages/index.tsx.
Sources: tailwind.config.js:1-24, src/pages/index.tsx
Global Styles
Base styles, font settings, and global theme-aware properties like text and background colors are defined in src/styles/GlobalStyles.ts. This file uses createGlobalStyle from styled-components and twin.macro to apply styles to the body and html elements.
Key styles include:
- Setting the default text and background colors using the
foregroundandbackgroundCSS variables. - Defining selection colors for both light and dark themes.
// src/styles/GlobalStyles.ts:5-15
export const GlobalStyles = createGlobalStyle`
body {
${tw`antialiased`}
${tw`text-foreground bg-background leading-relaxed`}
${tw`font-sans`}
}
html {
::selection {
${tw`bg-pink-200 dark:bg-pink-900`}
}
//...
Sources: src/styles/GlobalStyles.ts:1-20
Theme Switching
Theme switching is handled by the next-themes library, orchestrated by the ThemeSwitcher component.
ThemeSwitcher Component
While the source code for ThemeSwitcher.tsx is not fully provided in the context, its usage in src/components/Sidebar.tsx confirms its role. It is a client-side component responsible for toggling between light and dark themes.
// src/components/Sidebar.tsx:10, 24
import { ThemeSwitcher } from "./ThemeSwitcher";
// ...
<ThemeSwitcher />
// ...
Sources: src/components/Sidebar.tsx:10, 24
Theme Switching Flow
The sequence diagram below illustrates the process when a user clicks the theme switcher.
This flow is enabled by the darkMode: "class" setting in the Tailwind configuration and the CSS variables defined for light (:root) and dark (.dark) modes.
Sources: tailwind.config.js:35, package.json:28
Server-Side Rendering (SSR) and Theming
The application ensures a flicker-free theme loading experience by handling themes on the server side. In dynamic pages, getServerSideProps reads the theme cookie and passes it to the page components as the colorModeSSR prop.
// src/pages/dynamic/[...slug].tsx:81-85
const themeCookie = getCookie("theme", { req: context.req }) as
| string
| undefined;
return {
props: {
// ...
colorModeSSR: themeCookie ?? null,
// ...
This colorModeSSR prop is then passed down to components like CodeBlock to ensure they render with the correct theme on the initial server-rendered page load, preventing a flash of unstyled or incorrectly-themed content.
// src/pages/dynamic/[...slug].tsx:36-38
pre: (props: CodeBlockProps) => (
<CodeBlock {...props} colorModeSSR={colorModeSSR} />
),
Sources: src/pages/dynamic/[...slug].tsx:13, 37, 81-88
Summary
The styling and theming system of railways-docs is a well-architected combination of utility-first CSS and component-based styling. By leveraging twin.macro to unite Tailwind CSS and styled-components, it offers a powerful and developer-friendly experience. The theme-switching mechanism, powered by next-themes and a CSS variable-based color system, is robust, supporting both client-side and server-side rendering for a seamless user experience.