Skip to main content

Overview

The GitHub profile README project supports automatic light/dark theme switching based on the user’s system preferences. Themes are implemented using CSS custom properties (CSS variables) within the SVG rendering system.

How Theme Switching Works

Themes are controlled via a data-theme attribute on the SVG wrapper element. The theme is determined by URL parameters:
const theme = (searchParams.get("theme") ?? "light") as "light" | "dark";
Reference: src/worker.ts:18

Theme Attribute

The SVG root element receives a data-theme attribute that triggers CSS variable changes:
<svg data-theme="light">...</svg>
<svg data-theme="dark">...</svg>

Color Variables

The project uses a comprehensive color system defined in CSS custom properties. Here are the color variables from the source code:

Root Color Definitions

:root {
  --color-black: #202020;
  --color-white: #FFFFFF;
  
  --color-text-light: var(--color-black);
  --color-text-dark: var(--color-white);
  
  /* Light theme dot backgrounds */
  --color-dot-bg-0-light: var(--color-white);
  --color-dot-bg-1-light: #e0e0e0;
  --color-dot-bg-2-light: #b0b0b0;
  --color-dot-bg-3-light: #808080;
  --color-dot-bg-4-light: var(--color-black);
  
  /* Dark theme dot backgrounds */
  --color-dot-bg-0-dark: #262626;
  --color-dot-bg-1-dark: #404040;
  --color-dot-bg-2-dark: #686868;
  --color-dot-bg-3-dark: #a0a0a0;
  --color-dot-bg-4-dark: var(--color-white);
  
  /* Dot borders */
  --color-dot-border-light: rgba(32, 32, 32, 0.06);
  --color-dot-border-dark: rgba(255, 255, 255, 0.06);
}
Reference: src/render.ts:140-161

Theme-Specific Mappings

The active theme determines which color set is used:
[data-theme="dark"] {
  --color-text: var(--color-text-dark);
  --color-dot-bg-0: var(--color-dot-bg-0-dark);
  --color-dot-bg-1: var(--color-dot-bg-1-dark);
  --color-dot-bg-2: var(--color-dot-bg-2-dark);
  --color-dot-bg-3: var(--color-dot-bg-3-dark);
  --color-dot-bg-4: var(--color-dot-bg-4-dark);
  --color-dot-border: var(--color-dot-border-dark);
}

[data-theme="light"] {
  --color-text: var(--color-text-light);
  --color-dot-bg-0: var(--color-dot-bg-0-light);
  --color-dot-bg-1: var(--color-dot-bg-1-light);
  --color-dot-bg-2: var(--color-dot-bg-2-light);
  --color-dot-bg-3: var(--color-dot-bg-3-light);
  --color-dot-bg-4: var(--color-dot-bg-4-light);
  --color-dot-border: var(--color-dot-border-light);
}
Reference: src/render.ts:178-196

Contribution Dot Colors

The contribution graph uses a 5-level color system (0-4) representing activity intensity:
.dot--0 { background-color: var(--color-dot-bg-0); }
.dot--1 { background-color: var(--color-dot-bg-1); }
.dot--2 { background-color: var(--color-dot-bg-2); }
.dot--3 { background-color: var(--color-dot-bg-3); }
.dot--4 { background-color: var(--color-dot-bg-4); }
Reference: src/render.ts:423-427 Light Theme:
  • Level 0: White (#FFFFFF) - No activity
  • Level 1: Light gray (#e0e0e0)
  • Level 2: Medium gray (#b0b0b0)
  • Level 3: Dark gray (#808080)
  • Level 4: Black (#202020) - Highest activity
Dark Theme:
  • Level 0: Dark gray (#262626) - No activity
  • Level 1: Lighter dark gray (#404040)
  • Level 2: Medium gray (#686868)
  • Level 3: Light gray (#a0a0a0)
  • Level 4: White (#FFFFFF) - Highest activity

Font Configuration

The project uses the Geist font family with embedded base64-encoded font files for optimal loading:
--font-primary: 'Geist', -apple-system, BlinkMacSystemFont, "Segoe UI", 
                "Noto Sans", Helvetica, Arial, sans-serif, 
                "Apple Color Emoji", "Segoe UI Emoji";
Reference: src/render.ts:162

Font Weights

Three font weights are embedded:
  1. Light (300) - Used for body copy and introductory text
    • Reference: src/render.ts:104-111
  2. Normal (400) - Default text weight
    • Reference: src/render.ts:113-120
  3. Bold (700) - Used for labels and emphasis
    • Reference: src/render.ts:122-129
  4. Semibold (600) - Used for labels
    • Reference: src/render.ts:131-138

Font Display Strategy

font-display: swap;
This ensures text remains visible during font loading, preventing invisible text (FOIT).

Customizing Your Theme

Changing Colors

To customize the color palette, modify the CSS variables in your local version:
  1. Text Colors: Edit --color-text-light and --color-text-dark
  2. Dot Colors: Modify the --color-dot-bg-*-light and --color-dot-bg-*-dark variables
  3. Borders: Adjust --color-dot-border-light and --color-dot-border-dark

Creating Custom Themes

You can create additional theme variants by:
  1. Adding new color variable sets
  2. Creating new [data-theme="your-theme"] selectors
  3. Mapping the theme-specific variables to your custom colors

Using Different Fonts

To use a different font family:
  1. Replace the @font-face declarations with your font
  2. Update the --font-primary variable
  3. Ensure proper font-weight support for 300, 400, 600, and 700

Browser Compatibility

The theme system uses CSS custom properties, which are supported in all modern browsers. Note that Firefox has specific handling for certain features (see @-moz-document rules in the source). Reference: src/render.ts:221-225

Build docs developers (and LLMs) love