Twigs Logo

Theming

Twigs ships with maximum flexibility to customize the look and feel of your application.

Theme Setup

Twigs uses Stitches as its styling engine. The theme system consists of:

  1. Default Theme (stitches.config.ts) - Contains all default theme values and configuration functions for initial setup.
  2. ThemeProvider - Applies and merges custom themes with defaults.

ThemeProvider Architecture

The ThemeProvider automatically:

  • Runs global styles on mount - Applies CSS reset and base styles (box-sizing, font-family, etc.)
  • Merges themes - Combines your custom theme with defaults
  • Applies theme - Adds merged theme as CSS class to document root
// Simplified ThemeProvider behavior
const mergeThemes = (outerTheme: any, theme: any) => {
  return { ...outerTheme, ...theme };
};

useEffect(() => {
  globalStyles(); // Runs once on mount - applies CSS reset & base styles
}, []);

Key behaviors:

  • Global styles applied automatically (CSS reset, box-sizing, font-family)
  • Custom values override defaults at the same key level
  • Nested objects are merged (not replaced)
  • Theme is applied as a CSS class to document root
  • Changes update all components automatically

Custom themes merge with defaults - only specified keys override:

// Default theme
{ colors: { primary: "#2E666D", secondary: "#363A43" } }

// Custom theme
{ colors: { primary: "#FF5733" } }

// Result: primary overridden, secondary preserved
{ colors: { primary: "#FF5733", secondary: "#363A43" } }

Basic Setup

Wrap your application with ThemeProvider:

import { ThemeProvider, defaultTheme } from '@sparrowengg/twigs-react'

function App() {
  return (
    <ThemeProvider theme={defaultTheme}>
      <YourApp />
    </ThemeProvider>
  )
}

For the best experience, wrap ThemeProvider in your main.jsx or main.tsx file at the root of your application. This ensures all components have access to the theme.

Customizing the Theme

Create a twigs.config.js or twigs.config.ts file in your project root:

export default {
  theme: {
    extends: {
      // overridding the default colors
      colors: {
        primary500: "blue",
        secondary500: "green",
      },
      // overridding the default font
      fonts: {
        body: "DM Sans, sans-serif",
        heading: "DM Sans, sans-serif", 
      },
    }
  }
};

Make sure to import your custom fonts (e.g., DM Sans) in your project via Google Fonts, a CDN, or local files. The font will not apply unless it's properly loaded in your application.

Import and use in ThemeProvider:

import { ThemeProvider } from '@sparrowengg/twigs-react';
import twigsConfig from '../twigs.config.js'; 

function App() {
  return (
    <ThemeProvider theme={twigsConfig.theme.extends}>
      <YourApp />
    </ThemeProvider>
  );
}

Benefits: Better organization, IntelliSense support, version control friendly.

Direct Theme Prop

Pass theme object directly:

import { ThemeProvider } from '@sparrowengg/twigs-react';

const customTheme = {
  colors: {
    primary: "#2E666D",
  },
  fontSizes: {
    md: "1.125rem",
  },
};

function App() {
  return (
    <ThemeProvider theme={customTheme}>
      <YourApp />
    </ThemeProvider>
  );
}

Dark Mode Configuration

Add dark mode support using the createTheme function, which generates a theme class that can be applied to override specific tokens while inheriting others from your default theme.

Add Dark Theme

Extend your twigs.config by altering the default export to include dark theme configuration

import { createTheme } from '@sparrowengg/twigs-react';

const config = {
  theme: {
    extends: {
      colors: {
        primary500: "blue",
        secondary500: "green",
      },
      fonts: {
        body: "DM Sans, sans-serif",
      },
    },
    dark: {
      colors: {
        primary500: "#5CB5BD",
        secondary500: "#A3AEBD",
        // Override colors for dark mode
      },
    }
  }
};

export default config;

export const darkTheme = createTheme('dark-theme', config.theme.dark);

Apply Theme class

Use ThemeProvider for your default theme and apply the darkTheme class conditionally when needed.

import config, { darkTheme } from '../twigs.config.js'; 
import { useState } from "react";

function App() {
  const [isDark,setIsDark] = useState(false);
  return (
    <div className={isDark ? darkTheme : ''}>
      <yourComponents />
    </div>
  );
}

Apply the dark theme class in your App.tsx or App.jsx file to ensure consistent theming across your entire application.

How it works:

  • ThemeProvider applies your custom theme via the theme prop.
  • Dark theme class is applied to the wrapper div when isDark is true.
  • Both work together: custom theme + dark mode overrides.
  • Only tokens defined in dark theme override; others use your custom/default theme.

See the Stitches Custom Theming documentation for complete details regarding the dark mode configuration.

Theme Variables

Reference theme variables using the $ prefix in the css prop:

<Text
  css={{
    fontSize:"$lg",
    lineHeight:"$lg",
    fontWeight:"$5",
    padding:"$5",
    borderWidth:"$xs",
    borderStyle:"solid",
    borderRadius:"$md"
  }}
>
  Twigs 
</Text>

Reference

CategoryExampleKeys
Colors$primary500, $white900Named
Spacing$4, $10, $24Numeric (1-50)
Font Sizes$sm, $md, $xlNamed
Font Weights$4, $6, $7Numeric (1-9)
Border Radius$sm, $md, $pillNamed
Sizes$10, $20, $25Numeric (1-34)

Breakpoints

Twigs includes pre-configured breakpoints for responsive design. Use them with the @ prefix in your css prop:

<Box
  css={{
    padding: '$4',
    '@screen-md': {
      padding: '$8',
    },
    '@screen-lg': {
      padding: '$12',
    },
  }}
>
  Responsive Content
</Box>

Available breakpoints:

  • @screen-xxs - 320px
  • @screen-xs - 480px
  • @screen-sm - 640px
  • @screen-md - 768px
  • @screen-lg - 1024px
  • @screen-xl - 1280px
  • @screen-2xl - 1536px

Breakpoints are pre-configured in the Twigs setup and cannot be overridden through ThemeProvider. For custom breakpoints, use standard CSS media queries directly in your css prop.

Scale-Prefixed Tokens

By default, theme tokens are automatically mapped to their corresponding CSS properties. For example, $primary500 works in color-related properties, and $6 works in spacing properties.

When you need to explicitly target a token from a specific scale (e.g., using a color token in a non-color property), use scale-prefixed syntax:

<Box
  css={{
    marginTop: '$sizes$10',        // Use size token for margin
    boxShadow: '0 0 0 2px $colors$primary500',  // Use color token in shadow
    border: '1px solid $colors$secondary500',    // Explicit color scale
  }}
>
  Content
</Box>

Scale-prefixed syntax: $scaleName$tokenName

  • $colors$primary500 - Explicitly use color token
  • $space$6 - Explicitly use spacing token
  • $sizes$10 - Explicitly use size token

For complete property mapping details, see the Stitches Property Mapping documentation.

Color Opacity Utils

Twigs provides utility functions to apply opacity to theme colors. These utils convert theme colors to RGBA format with your specified opacity:

<Box
  css={{
    backgroundColorOpacity: ['$primary500', 0.1],  // 10% opacity background
    colorOpacity: ['$primary800', 0.8],             // 80% opacity text
    borderColorOpacity: ['$secondary500', 0.5],    // 50% opacity border
  }}
>
  Content with opacity
</Box>

Available utils:

  • backgroundColorOpacity: [colorToken, opacity] - Apply opacity to background color
  • colorOpacity: [colorToken, opacity] - Apply opacity to text color
  • borderColorOpacity: [colorToken, opacity] - Apply opacity to border color

Opacity value: Number between 0 (transparent) and 1 (opaque).

Advanced Styling

When modifying default colors or styles of Twigs components, you may need to use !important to override component defaults that have higher CSS specificity:

<Button
  css={{
    backgroundColor: '$primary500',
    '&:hover': { 
      backgroundColor: '$primary600 !important',
    },
    '&:focus': {
      outline: '2px solid $primary300',
    },
    '@screen-md': {
      padding: '$8 $12',
    },
  }}
>
  Submit
</Button>

Best practice: Try without !important first. Only add it when the style isn't being applied due to specificity conflicts.

Practical Example

Server-Side Rendering

Twigs supports server-side rendering (SSR) through the getCssText function, which extracts all CSS needed for hydration.

Basic Setup

Here's an example of SSR with Next.js

pages/_document.tsx
import { Html, Head, Main, NextScript } from "next/document";
import { getCssText } from "@sparrowengg/twigs-react"; 

export default function Document() {
  return (
    <Html lang="en">
      <Head>
        <style
          id="stitches"
          dangerouslySetInnerHTML={{ __html: getCssText() }}
        />
      </Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}

Important: Always include id="stitches" on the style tag for optimal hydration.

How it works:

  • getCssText() returns all CSS generated by Stitches
  • Styles are injected server-side to prevent FOUC (Flash of Unstyled Content)

Complete Theme Reference

Colors

Click any color swatch to copy its token name, Use these values in the css prop to style your components.

    primary

    50
    #E6F5F6
    100
    #B8E1E5
    200
    #8ACCD2
    300
    #5CB5BD
    400
    #2E9CA6
    500
    #00828D
    600
    #006B74
    700
    #00555C
    800
    #003E43
    900
    #00272A

    secondary

    50
    #F4F6F7
    100
    #E2E6EB
    200
    #C9CFD8
    300
    #A3AEBD
    400
    #76859A
    500
    #64748B
    600
    #4E596C
    700
    #444B5A
    800
    #3D424D
    900
    #363A43

    accent

    50
    #F3F3FF
    100
    #EAE9FE
    200
    #D7D6FE
    300
    #B9B5FD
    400
    #978CF9
    500
    #7158F5
    600
    #623BEC
    700
    #5329D8
    800
    #4622B5
    900
    #3B1E94

    warning

    50
    #FFF6EF
    100
    #FEEAC7
    200
    #FDD28A
    300
    #FCBD4F
    400
    #FBAB24
    500
    #F59E0B
    600
    #DB8D06
    700
    #B47409
    800
    #92610E
    900
    #78510F

    highlight

    50
    #FFFCDA
    100
    #FFF7AD
    200
    #FFF27D
    300
    #FFED4B
    400
    #FFE81A
    500
    #E6CF00
    600
    #B3A100
    700
    #807300
    800
    #786B03
    900
    #6A5F00

    positive

    50
    #F4FAF1
    100
    #E8F4E3
    200
    #D4E8CA
    300
    #A8D291
    400
    #67B034
    500
    #5EA130
    600
    #55932A
    700
    #4C8425
    800
    #437720
    900
    #3C691C

    negative

    50
    #FFF6F3
    100
    #FDEDE8
    200
    #FFDAD0
    300
    #FFB4A1
    400
    #FA7659
    500
    #F65633
    600
    #E75030
    700
    #D14729
    800
    #BC4024
    900
    #A9371E

    neutral

    50
    #F8F8F8
    100
    #F1F1F1
    200
    #E2E2E2
    300
    #C6C6C6
    400
    #9E9E9E
    500
    #919191
    600
    #848484
    700
    #757575
    800
    #575757
    900
    #111111

    black

    50
    #0000000A
    100
    #00000014
    200
    #0000001A
    300
    #00000026
    400
    #00000033
    500
    #0000004D
    600
    #00000080
    700
    #000000B2
    800
    #000000CC
    900
    #000000

    white

    50
    #FFFFFF0D
    100
    #FFFFFF14
    200
    #FFFFFF1A
    300
    #FFFFFF26
    400
    #FFFFFF33
    500
    #FFFFFF4D
    600
    #FFFFFF80
    700
    #FFFFFFB2
    800
    #FFFFFFCC
    900
    #FFFFFF

    Font Sizes

      The quick brown fox jumps over the lazy dog

      The quick brown fox jumps over the lazy dog

      The quick brown fox jumps over the lazy dog

      The quick brown fox jumps over the lazy dog

      The quick brown fox jumps over the lazy dog

      The quick brown fox jumps over the lazy dog

      The quick brown fox jumps over the lazy dog

      The quick brown fox jumps over the lazy dog

      The quick brown fox jumps over the lazy dog

      The quick brown fox jumps over the lazy dog

      Font Weights

        The quick brown fox jumps over the lazy dog

        The quick brown fox jumps over the lazy dog

        The quick brown fox jumps over the lazy dog

        The quick brown fox jumps over the lazy dog

        The quick brown fox jumps over the lazy dog

        The quick brown fox jumps over the lazy dog

        The quick brown fox jumps over the lazy dog

        The quick brown fox jumps over the lazy dog

        The quick brown fox jumps over the lazy dog

        Line Heights

          The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.

          The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.

          The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.

          The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.

          The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.

          The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.

          The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.

          The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.

          The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.

          Border Widths and Radius

            Border Widths

            Border Radius

            Spacing

            Space values for padding, margin, gap (numeric keys 1-50):

              KeyValuepx ValueExample
              10.125rem2px
              20.25rem4px
              30.375rem6px
              40.5rem8px
              50.625rem10px
              60.75rem12px
              70.875rem14px
              81rem16px
              91.125rem18px
              101.25rem20px
              111.375rem22px
              121.5rem24px
              131.625rem26px
              141.75rem28px
              151.875rem30px
              162rem32px
              172.125rem34px
              182.25rem36px
              192.375rem38px
              202.5rem40px
              212.625rem42px
              222.75rem44px
              232.875rem46px
              243rem48px
              253.125rem50px
              263.25rem52px
              273.375rem54px
              283.5rem56px
              293.625rem58px
              303.75rem60px
              313.875rem62px
              324rem64px
              334.125rem66px
              344.25rem68px
              354.375rem70px
              364.5rem72px
              374.625rem74px
              384.75rem76px
              394.875rem78px
              405rem80px
              415.125rem82px
              425.25rem84px
              435.375rem86px
              445.5rem88px
              455.625rem90px
              465.75rem92px
              475.875rem94px
              486rem96px
              496.125rem98px
              506.25rem100px

              Sizing

              Size tokens for height, width, dimensions (numeric keys 1-34):

                KeyValueExample
                14px
                28px
                312px
                416px
                520px
                624px
                728px
                832px
                936px
                1040px
                1144px
                1248px
                1352px
                1456px
                1560px
                1664px
                1768px
                1872px
                1976px
                2080px
                2184px
                2288px
                2392px
                2496px
                25100px
                26104px
                27108px
                28112px
                29116px
                30120px
                31124px
                32128px
                33132px
                34136px

                Default Theme Object

                "colors": {
                  "primary": "#2E666D",
                  "secondary": "#363A43",
                  "accent50": "#F3F3FF",
                  "accent100": "#EAE9FE",
                  "accent200": "#D7D6FE",
                  "accent300": "#B9B5FD",
                  "accent400": "#978CF9",
                  "accent500": "#7158F5",
                  "accent600": "#623BEC",
                  "accent700": "#5329D8",
                  "accent800": "#4622B5",
                  "accent900": "#3B1E94",
                  "primary50": "#E6F5F6",
                  "primary100": "#B8E1E5",
                  "primary200": "#8ACCD2",
                  "primary300": "#5CB5BD",
                  "primary400": "#2E9CA6",
                  "primary500": "#00828D",
                  "primary600": "#006B74",
                  "primary700": "#00555C",
                  "primary800": "#003E43",
                  "primary900": "#00272A",
                  "warning50": "#FFF6EF",
                  "warning100": "#FEEAC7",
                  "warning200": "#FDD28A",
                  "warning300": "#FCBD4F",
                  "warning400": "#FBAB24",
                  "warning500": "#F59E0B",
                  "warning600": "#DB8D06",
                  "warning700": "#B47409",
                  "warning800": "#92610E",
                  "warning900": "#78510F",
                  "highlight50": "#FFFCDA",
                  "highlight100": "#FFF7AD",
                  "highlight200": "#FFF27D",
                  "highlight300": "#FFED4B",
                  "highlight400": "#FFE81A",
                  "highlight500": "#E6CF00",
                  "highlight600": "#B3A100",
                  "highlight700": "#807300",
                  "highlight800": "#786B03",
                  "highlight900": "#6A5F00",
                  "positive50": "#F4FAF1",
                  "positive100": "#E8F4E3",
                  "positive200": "#D4E8CA",
                  "positive300": "#A8D291",
                  "positive400": "#67B034",
                  "positive500": "#5EA130",
                  "positive600": "#55932A",
                  "positive700": "#4C8425",
                  "positive800": "#437720",
                  "positive900": "#3C691C",
                  "secondary50": "#F4F6F7",
                  "secondary100": "#E2E6EB",
                  "secondary200": "#C9CFD8",
                  "secondary300": "#A3AEBD",
                  "secondary400": "#76859A",
                  "secondary500": "#64748B",
                  "secondary600": "#4E596C",
                  "secondary700": "#444B5A",
                  "secondary800": "#3D424D",
                  "secondary900": "#363A43",
                  "negative50": "#FFF6F3",
                  "negative100": "#FDEDE8",
                  "negative200": "#FFDAD0",
                  "negative300": "#FFB4A1",
                  "negative400": "#FA7659",
                  "negative500": "#F65633",
                  "negative600": "#E75030",
                  "negative700": "#D14729",
                  "negative800": "#BC4024",
                  "negative900": "#A9371E",
                  "neutral50": "#F8F8F8",
                  "neutral100": "#F1F1F1",
                  "neutral200": "#E2E2E2",
                  "neutral300": "#C6C6C6",
                  "neutral400": "#9E9E9E",
                  "neutral500": "#919191",
                  "neutral600": "#848484",
                  "neutral700": "#757575",
                  "neutral800": "#575757",
                  "neutral900": "#111111",
                  "black50": "#0000000A",
                  "black100": "#00000014",
                  "black200": "#0000001A",
                  "black300": "#00000026",
                  "black400": "#00000033",
                  "black500": "#0000004D",
                  "black600": "#00000080",
                  "black700": "#000000B2",
                  "black800": "#000000CC",
                  "black900": "#000000",
                  "white50": "#FFFFFF0D",
                  "white100": "#FFFFFF14",
                  "white200": "#FFFFFF1A",
                  "white300": "#FFFFFF26",
                  "white400": "#FFFFFF33",
                  "white500": "#FFFFFF4D",
                  "white600": "#FFFFFF80",
                  "white700": "#FFFFFFB2",
                  "white800": "#FFFFFFCC",
                  "white900": "#FFFFFF"
                },
                "space": {
                  1: "0.125rem",
                  2: "0.25rem",
                  3: "0.375rem",
                  4: "0.5rem",
                  5: "0.625rem",
                  6: "0.75rem",
                  7: "0.875rem",
                  8: "1rem",
                  9: "1.125rem",
                  10: "1.25rem",
                  11: "1.375rem",
                  12: "1.5rem",
                  13: "1.625rem",
                  14: "1.75rem",
                  15: "1.875rem",
                  16: "2rem",
                  17: "2.125rem",
                  18: "2.25rem",
                  19: "2.375rem",
                  20: "2.5rem",
                  21: "2.625rem",
                  22: "2.75rem",
                  23: "2.875rem",
                  24: "3rem",
                  25: "3.125rem",
                  26: "3.25rem",
                  27: "3.375rem",
                  28: "3.5rem",
                  29: "3.625rem",
                  30: "3.75rem",
                  31: "3.875rem",
                  32: "4rem",
                  33: "4.125rem",
                  34: "4.25rem",
                  35: "4.375rem",
                  36: "4.5rem",
                  37: "4.625rem",
                  38: "4.75rem",
                  39: "4.875rem",
                  40: "5rem",
                  41: "5.125rem",
                  42: "5.25rem",
                  43: "5.375rem",
                  44: "5.5rem",
                  45: "5.625rem",
                  46: "5.75rem",
                  47: "5.875rem",
                  48: "6rem",
                  49: "6.125rem",
                  50: "6.25rem"
                },
                "fontSizes": {
                  "xxs": "0.625rem",
                  "xs": "0.75rem",
                  "sm": "0.875rem",
                  "md": "1rem",
                  "lg": "1.2rem",
                  "xl": "1.44rem",
                  "2xl": "1.728rem",
                  "3xl": "2.074rem",
                  "4xl": "2.488rem",
                  "5xl": "2.986rem"
                },
                "fonts": {
                  "body": "sytem-ui",
                  "heading": "sans-serif"
                },
                "fontWeights": {
                   1: "100",
                   2: "200",
                   3: "300",
                   4: "400",
                   5: "500",
                   6: "600",
                   7: "700",
                   8: "800",
                   9: "900"
                },
                "lineHeights": {
                  "xxs": "0.75rem",
                  "xs": "1rem",
                  "sm": "1.25rem",
                  "md": "1.5rem",
                  "lg": "1.75rem",
                  "xl": "2rem",
                  "2xl": "2.5rem",
                  "3xl": "3rem",
                  "4xl": "4rem"
                },
                "letterSpacings": {},
                "sizes": {
                  1: "4px",
                  2: "8px",
                  3: "12px",
                  4: "16px",
                  5: "20px",
                  6: "24px",
                  7: "28px",
                  8: "32px",
                  9: "36px",
                  10: "40px",
                  11: "44px",
                  12: "48px",
                  13: "52px",
                  14: "56px",
                  15: "60px",
                  16: "64px",
                  17: "68px",
                  18: "72px",
                  19: "76px",
                  20: "80px",
                  21: "84px",
                  22: "88px",
                  23: "92px",
                  24: "96px",
                  25: "100px",
                  26: "104px",
                  27: "108px",
                  28: "112px",
                  29: "116px",
                  30: "120px",
                  31: "124px",
                  32: "128px",
                  33: "132px",
                  34: "136px"
                },
                "borderWidths": {
                  "xs": "1px",
                  "sm": "2px",
                  "md": "3px",
                  "lg": "4px",
                  "xl": "5px"
                },
                "borderStyles": {},
                "radii": {
                  "none": "0px",
                  "xs": "0.125rem",
                  "sm": "0.25rem",
                  "md": "0.375rem",
                  "lg": "0.5rem",
                  "xl": "0.75rem",
                  "2xl": "1rem",
                  "3xl": "1.25rem",
                  "4xl": "1.5rem",
                  "round": "50%",
                  "pill": "9999px"
                },
                "shadows": {
                  "sm": "0px 5px 15px rgba(0, 0, 0, 0.04)"
                },
                "zIndices": {},
                "transitions": {
                   1: "0.1s",
                   2: "0.2s",
                   3: "0.3s"
                }
                Theming | Twigs