Skip to main content
stevenson.space offers extensive theme customization options, allowing you to personalize colors, backgrounds, header images, and even add particle effects to your interface.

Theme Structure

Themes in stevenson.space are defined using a comprehensive JSON structure:
type Theme = {
  metadata: ThemeMetadata;      // Name and author
  visibility: "show" | "draft" | "hide";
  styling: ThemeStyling;        // All visual customization
};

type ThemeStyling = {
  base?: "light" | "dark";                // Base theme to extend
  background?: string;                    // Main background color
  secondaryBackground?: string;           // Secondary areas background
  accent?: string;                        // Primary accent color
  text?: ThemeTextColors;                 // Text color overrides
  header?: ThemeHeaderConfig;             // Header customization
  iconCards?: ThemeIconCards;             // Icon card colors
  particles?: ThemeParticles;             // Particle effects
};

Getting Started with Themes

Theme customization in stevenson.space is currently controlled programmatically through the themes store. A UI for theme customization may be available in future releases.
Theme styling is stored in localStorage.themeStyling and loaded on app initialization.

Customizing Colors

Base Colors

The most fundamental customization options:
Control the main visual appearance:
{
  "background": "#ffdcdc",              // Main page background
  "secondaryBackground": "white"        // Cards and panels
}
Use complementary colors for background and secondaryBackground to create visual hierarchy.
The accent color affects buttons, icons, highlights, and interactive elements:
{
  "accent": "#1b5e20"  // Green accent for Light theme
}
Example accent colors:
  • Light theme: #1b5e20 (green)
  • Sakura theme: #f25477 (pink)
  • Midnight theme: #4fc3f7 (cyan)
Override text colors for different hierarchy levels:
type ThemeTextColors = {
  primary: string;      // Main headings and important text
  secondary: string;    // Body text
  tertiary: string;     // Subtle text and labels
};
Example:
{
  "text": {
    "primary": "#000000",
    "secondary": "#333333",
    "tertiary": "#666666"
  }
}

Base Theme Selection

Start with a light or dark foundation:
{
  "base": "light"  // or "dark"
}
The light base theme provides:
  • Light background colors
  • Dark text for readability
  • Green accent (#1b5e20)
Perfect for daytime use or bright environments.

Header Customization

The header is one of the most prominent visual elements:
type ThemeHeaderConfig = {
  background: string;           // Header background color
  image?: {                     // Optional background image
    full: string;               // Desktop image path
    mobile?: string;            // Mobile-specific image
  };
  scheduleBar: string;          // Color of bar below header
};

Adding Header Images

1

Choose Your Images

Prepare two versions of your header image:
  • Full: For desktop/tablet (recommended: 1920x400px)
  • Mobile: For phones (recommended: 800x300px)
Mobile image is optional - the full image will be used if not specified.
2

Use Asset Protocol

Reference images using the assets:// protocol:
{
  "header": {
    "background": "#f25477",
    "image": {
      "full": "assets://header-images/sakura-full.png",
      "mobile": "assets://header-images/sakura-mobile.png"
    },
    "scheduleBar": "white"
  }
}
3

Set Schedule Bar Color

Choose a color for the thin bar below the header that complements your image:
{
  "scheduleBar": "white"  // or any CSS color
}

Example: Sakura Theme Header

The Sakura theme demonstrates a complete header setup:
{
  "header": {
    "background": "var(--accent)",
    "image": {
      "full": "assets://header-images/sakura-full.png",
      "mobile": "assets://header-images/sakura-mobile.png"
    },
    "scheduleBar": "white"
  }
}

Particle Effects

Add dynamic, animated particles to your theme for visual interest:
type ThemeParticles = {
  images: string[];         // Array of particle image paths
  speed?: number;           // Animation speed (default varies)
  count?: number;           // Number of particles
  size?: number;            // Particle size in pixels
  opacity?: number;         // Particle opacity (0-1)
  windPower?: number;       // Horizontal drift (-10 to 10)
};

Configuring Particles

Provide an array of image paths. The system will randomly select from these:
{
  "particles": {
    "images": [
      "assets://particles/sakura/petal_1.png",
      "assets://particles/sakura/petal_2.png",
      "assets://particles/sakura/petal_3.png",
      "assets://particles/sakura/petal_4.png",
      "assets://particles/sakura/petal_5.png",
      "assets://particles/sakura/bee.png"
    ]
  }
}
Use multiple variations of the same particle type for more natural-looking animations.
Fine-tune the particle behavior:
{
  "particles": {
    "images": [...],
    "speed": 3,          // Higher = faster fall
    "count": 50,         // More = denser effect
    "size": 12,          // Particle size in pixels
    "opacity": 0.8,      // 0 = invisible, 1 = opaque
    "windPower": -1      // Negative = left drift, Positive = right drift
  }
}
High particle counts (>100) may impact performance on slower devices.

Example: Sakura Theme Particles

The Sakura theme includes cherry blossom petals with subtle wind effect:
{
  "particles": {
    "images": [
      "assets://particles/sakura/petal_1.png",
      "assets://particles/sakura/petal_2.png",
      "assets://particles/sakura/petal_3.png",
      "assets://particles/sakura/petal_4.png",
      "assets://particles/sakura/petal_5.png",
      "assets://particles/sakura/bee.png"
    ],
    "speed": 3,
    "count": 50,
    "size": 12,
    "opacity": 0.8,
    "windPower": -1
  }
}

Icon Card Colors

Customize the appearance of icon cards on the homepage:
type ThemeIconCards = {
  regular: string;      // Text color for regular cards
  invert: string;       // Text color for inverted cards
};
Example:
{
  "iconCards": {
    "regular": "#1b5e20",
    "invert": "#ffffff"
  }
}

Complete Theme Examples

{
  "metadata": {
    "name": "Light",
    "author": "Abhinav P. ('19)"
  },
  "visibility": "show",
  "recommended": {
    "timing": "always"
  },
  "styling": {
    "base": "light",
    "accent": "#1b5e20"
  }
}

Applying Custom Themes

To apply theme customization programmatically:
import useThemeStore from '@/stores/themes';

const themeStore = useThemeStore();

// Set custom styling
themeStore.setStyling({
  base: 'light',
  background: '#ffdcdc',
  accent: '#f25477',
  header: {
    background: '#f25477',
    scheduleBar: 'white'
  }
});
The setStyling function accepts either a complete Theme object or just the ThemeStyling portion. It automatically saves to localStorage.themeStyling.

Theme Persistence

Theme customizations are automatically persisted:
1

Automatic Saving

When you call setStyling(), the theme is immediately saved to localStorage.themeStyling.
2

Initialization

On app startup, initializeTheme() is called, which:
  1. Checks for localStorage.themeStyling
  2. Parses and applies the saved theme
  3. Falls back to the default Light theme if parsing fails
3

Cross-Session Persistence

Themes persist across browser sessions and page refreshes automatically.

Best Practices

Color Contrast

Ensure sufficient contrast between text and background colors for readability. Aim for WCAG AA compliance (4.5:1 ratio).

Performance

Limit particle counts and sizes to maintain smooth performance, especially on mobile devices.

Asset Paths

Always use the assets:// protocol for images to ensure proper resolution by the app’s asset management system.

Theme Consistency

Choose accent colors that complement your background and header images for a cohesive visual experience.

Technical Reference

For developers working with the theme system:
// From src/stores/themes.ts

// Get current theme (readonly)
const theme = themeStore.theme;

// Get styling (readonly)
const styling = themeStore.styling;

// Initialize theme from localStorage
themeStore.initializeTheme();

// Set custom styling
themeStore.setStyling({
  base: 'dark',
  accent: '#4fc3f7'
});
See src/stores/themes.ts and src/themes/*.json for complete theme implementations and examples.

Build docs developers (and LLMs) love