Skip to content

[Feature Request] Add a SketchThemeProvider with CSS custom property tokens to enable global theming across all components #266

Description

@prince-pokharna

Summary

Sketchbook UI's theming system currently requires passing colors and typography props to every individual component. If a user wants to change the primary stroke colour of their entire UI, they must update each component usage site manually — there is no global theme that propagates to all components at once. A SketchThemeProvider using CSS custom properties (CSS variables) would allow one-time theme definition that cascades to all child components automatically, without any JavaScript prop drilling.

Problem

  • Theme customisation is per-component and per-instance: <Button colors={{ stroke: "#000" }}> must be duplicated across every Button, Input, Card, etc. in the consuming application.
  • There is no documented way to set a single primaryColor or fontFamily that applies consistently across the entire library.
  • The existing SketchProvider (mentioned in the Quick Start) may handle some context, but there is no CSS variable system documented or enforced.
  • If a user switches from one hand-drawn color palette to another (e.g. pastel to dark mode), they must find and update every individual prop across their codebase.
  • Competing libraries (shadcn/ui, Chakra, Radix) all offer a token-based theming system. The absence of one in sketchbook-ui is a meaningful adoption barrier for larger projects.

Impact

  • Sketchbook-UI's adoption in real projects is limited to small or one-off uses where per-component props are acceptable.
  • Dark mode support is impossible to implement without a global CSS variable layer.
  • The library cannot be themed once and reused consistently in a design system.

Proposed Solution

Extend the existing SketchProvider (or create SketchThemeProvider) to inject CSS custom property tokens that all components read from, with per-component props as overrides.

Theme token definition (src/theme/tokens.ts):

export interface SketchTheme {
  colors: {
    primary: string;
    stroke: string;
    background: string;
    text: string;
    muted: string;
    danger: string;
  };
  typography: {
    fontFamily: string;
    fontSize: string;
    fontWeight: string;
  };
  sketch: {
    borderWidth: string;
    wobbleIntensity: string; // controls SVG path jitter
  };
}

export const defaultTheme: SketchTheme = {
  colors: {
    primary: "#fff9c4",
    stroke: "#1a1a1a",
    background: "#fffef7",
    text: "#1a1a1a",
    muted: "#9e9e9e",
    danger: "#ef4444",
  },
  typography: {
    fontFamily: "'Caveat', cursive",
    fontSize: "1rem",
    fontWeight: "600",
  },
  sketch: {
    borderWidth: "2px",
    wobbleIntensity: "1",
  },
};

Provider implementation (src/theme/SketchThemeProvider.tsx):

import { createContext, useContext, useEffect } from "react";
import { SketchTheme, defaultTheme } from "./tokens";

const ThemeContext = createContext<SketchTheme>(defaultTheme);

export function SketchThemeProvider({
  theme = defaultTheme,
  children,
}: {
  theme?: Partial<SketchTheme>;
  children: React.ReactNode;
}) {
  const merged = { ...defaultTheme, ...theme };

  useEffect(() => {
    const root = document.documentElement;
    Object.entries(merged.colors).forEach(([key, val]) =>
      root.style.setProperty(`--sketch-color-${key}`, val)
    );
    Object.entries(merged.typography).forEach(([key, val]) =>
      root.style.setProperty(`--sketch-typography-${key}`, val)
    );
  }, [merged]);

  return <ThemeContext.Provider value={merged}>{children}</ThemeContext.Provider>;
}

export const useSketchTheme = () => useContext(ThemeContext);

Component usage (example in Button.tsx):

// Before: hardcoded default inline style
style={{ backgroundColor: colors?.bg ?? "#fff9c4" }}

// After: reads from CSS variable, overrideable by prop
style={{ backgroundColor: colors?.bg ?? "var(--sketch-color-primary)" }}

Scope of this PR:

  • src/theme/tokens.ts with SketchTheme interface and defaultTheme.
  • src/theme/SketchThemeProvider.tsx injecting CSS custom properties.
  • Update src/style.css to use CSS variable fallbacks throughout.
  • Update Button, Input, Card, Modal, Toast to read from CSS variables with prop overrides.
  • Storybook story demonstrating a full dark-mode theme switch via SketchThemeProvider.
  • README section documenting the global theming API.

Could you please assign this issue to me?

Labels: enhancement, feature, theming, design-system, GSSoC 2026

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions