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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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