Create Stitches RSC Theme
Guide creation of properly structured and typed theme configurations for Stitches RSC.
Theme Structure Overview
A Stitches theme consists of design tokens organized by scale:
typescript
1import { createStitches } from '@stitches-rsc/react';
2
3const { styled, css, theme, createTheme } = createStitches({
4 theme: {
5 colors: { /* color tokens */ },
6 space: { /* spacing tokens */ },
7 fontSizes: { /* typography scale */ },
8 fonts: { /* font families */ },
9 fontWeights: { /* font weights */ },
10 lineHeights: { /* line heights */ },
11 letterSpacings: { /* letter spacing */ },
12 sizes: { /* width/height tokens */ },
13 borderWidths: { /* border widths */ },
14 borderStyles: { /* border styles */ },
15 radii: { /* border radius */ },
16 shadows: { /* box shadows */ },
17 zIndices: { /* z-index scale */ },
18 transitions: { /* transition values */ },
19 },
20});
Creating a Theme Step-by-Step
Step 1: Define Color Palette
Start with semantic color tokens:
typescript
1const theme = {
2 colors: {
3 // Brand colors
4 primary: '#0070f3',
5 primaryLight: '#3291ff',
6 primaryDark: '#0051a8',
7
8 // Semantic colors
9 success: '#0070f3',
10 warning: '#f5a623',
11 error: '#e00',
12 info: '#0070f3',
13
14 // Neutrals
15 background: '#ffffff',
16 foreground: '#000000',
17 border: '#eaeaea',
18
19 // Text
20 text: '#171717',
21 textMuted: '#666666',
22 textInverse: '#ffffff',
23
24 // Interactive states
25 hover: 'rgba(0, 0, 0, 0.05)',
26 active: 'rgba(0, 0, 0, 0.1)',
27 focus: 'rgba(0, 112, 243, 0.4)',
28 },
29};
Step 2: Define Spacing Scale
Use a consistent spacing scale (4px base recommended):
typescript
1const theme = {
2 space: {
3 0: '0',
4 1: '4px',
5 2: '8px',
6 3: '12px',
7 4: '16px',
8 5: '20px',
9 6: '24px',
10 7: '28px',
11 8: '32px',
12 9: '36px',
13 10: '40px',
14 12: '48px',
15 14: '56px',
16 16: '64px',
17 20: '80px',
18 24: '96px',
19 // Semantic aliases
20 xs: '4px',
21 sm: '8px',
22 md: '16px',
23 lg: '24px',
24 xl: '32px',
25 },
26};
Step 3: Define Typography
typescript
1const theme = {
2 fonts: {
3 sans: 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
4 serif: 'Georgia, "Times New Roman", serif',
5 mono: 'Menlo, Monaco, Consolas, "Liberation Mono", monospace',
6 },
7
8 fontSizes: {
9 xs: '0.75rem', // 12px
10 sm: '0.875rem', // 14px
11 base: '1rem', // 16px
12 lg: '1.125rem', // 18px
13 xl: '1.25rem', // 20px
14 '2xl': '1.5rem', // 24px
15 '3xl': '1.875rem',// 30px
16 '4xl': '2.25rem', // 36px
17 '5xl': '3rem', // 48px
18 },
19
20 fontWeights: {
21 thin: '100',
22 light: '300',
23 normal: '400',
24 medium: '500',
25 semibold: '600',
26 bold: '700',
27 extrabold: '800',
28 },
29
30 lineHeights: {
31 none: '1',
32 tight: '1.25',
33 snug: '1.375',
34 normal: '1.5',
35 relaxed: '1.625',
36 loose: '2',
37 },
38
39 letterSpacings: {
40 tighter: '-0.05em',
41 tight: '-0.025em',
42 normal: '0',
43 wide: '0.025em',
44 wider: '0.05em',
45 widest: '0.1em',
46 },
47};
Step 4: Define Visual Properties
typescript
1const theme = {
2 radii: {
3 none: '0',
4 sm: '2px',
5 base: '4px',
6 md: '6px',
7 lg: '8px',
8 xl: '12px',
9 '2xl': '16px',
10 full: '9999px',
11 },
12
13 shadows: {
14 none: 'none',
15 sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
16 base: '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)',
17 md: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',
18 lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
19 xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
20 inner: 'inset 0 2px 4px 0 rgba(0, 0, 0, 0.06)',
21 },
22
23 borderWidths: {
24 0: '0',
25 1: '1px',
26 2: '2px',
27 4: '4px',
28 8: '8px',
29 },
30
31 zIndices: {
32 hide: '-1',
33 auto: 'auto',
34 base: '0',
35 docked: '10',
36 dropdown: '1000',
37 sticky: '1100',
38 banner: '1200',
39 overlay: '1300',
40 modal: '1400',
41 popover: '1500',
42 toast: '1700',
43 tooltip: '1800',
44 },
45
46 transitions: {
47 none: 'none',
48 fast: '150ms ease',
49 base: '200ms ease',
50 slow: '300ms ease',
51 slower: '400ms ease',
52 },
53};
Using Theme Tokens
Reference tokens with $scale$token syntax:
typescript
1const Button = styled('button', {
2 backgroundColor: '$colors$primary',
3 padding: '$space$2 $space$4',
4 fontSize: '$fontSizes$base',
5 fontFamily: '$fonts$sans',
6 borderRadius: '$radii$md',
7 boxShadow: '$shadows$sm',
8 transition: 'all $transitions$fast',
9
10 '&:hover': {
11 backgroundColor: '$colors$primaryDark',
12 },
13});
Shorthand for same-name scale (using themeMap):
typescript
1const Button = styled('button', {
2 // These are equivalent due to defaultThemeMap
3 color: '$primary', // maps to colors.primary
4 padding: '$2', // maps to space.2
5 fontSize: '$base', // maps to fontSizes.base
6});
Creating Theme Variants
Create alternate themes with createTheme:
typescript
1const darkTheme = createTheme('dark', {
2 colors: {
3 background: '#0a0a0a',
4 foreground: '#ffffff',
5 text: '#ededed',
6 textMuted: '#a1a1a1',
7 border: '#333333',
8 primary: '#3291ff',
9 hover: 'rgba(255, 255, 255, 0.05)',
10 },
11});
12
13// Apply theme with className
14<div className={darkTheme}>
15 <App />
16</div>
Complete Example
typescript
1// stitches.config.ts
2import { createStitches } from '@stitches-rsc/react';
3
4export const {
5 styled,
6 css,
7 globalCss,
8 keyframes,
9 theme,
10 createTheme,
11 getCssText,
12} = createStitches({
13 prefix: 'app',
14
15 theme: {
16 colors: {
17 primary: '#0070f3',
18 primaryLight: '#3291ff',
19 primaryDark: '#0051a8',
20 secondary: '#7928ca',
21 success: '#17c964',
22 warning: '#f5a623',
23 error: '#f21361',
24 background: '#ffffff',
25 foreground: '#000000',
26 text: '#171717',
27 textMuted: '#666666',
28 border: '#eaeaea',
29 },
30 space: {
31 1: '4px', 2: '8px', 3: '12px', 4: '16px',
32 5: '20px', 6: '24px', 8: '32px', 10: '40px',
33 },
34 fontSizes: {
35 xs: '0.75rem', sm: '0.875rem', base: '1rem',
36 lg: '1.125rem', xl: '1.25rem', '2xl': '1.5rem',
37 },
38 fonts: {
39 sans: 'Inter, -apple-system, sans-serif',
40 mono: 'Menlo, monospace',
41 },
42 fontWeights: { normal: '400', medium: '500', bold: '700' },
43 lineHeights: { tight: '1.25', normal: '1.5', relaxed: '1.75' },
44 radii: { sm: '4px', md: '8px', lg: '12px', full: '9999px' },
45 shadows: {
46 sm: '0 1px 2px rgba(0,0,0,0.05)',
47 md: '0 4px 6px rgba(0,0,0,0.1)',
48 },
49 zIndices: { modal: '1000', tooltip: '1500' },
50 transitions: { fast: '150ms ease', base: '200ms ease' },
51 },
52
53 media: {
54 sm: '(min-width: 640px)',
55 md: '(min-width: 768px)',
56 lg: '(min-width: 1024px)',
57 xl: '(min-width: 1280px)',
58 },
59
60 utils: {
61 mx: (value: string) => ({ marginLeft: value, marginRight: value }),
62 my: (value: string) => ({ marginTop: value, marginBottom: value }),
63 px: (value: string) => ({ paddingLeft: value, paddingRight: value }),
64 py: (value: string) => ({ paddingTop: value, paddingBottom: value }),
65 size: (value: string) => ({ width: value, height: value }),
66 },
67});
68
69export const darkTheme = createTheme('dark', {
70 colors: {
71 background: '#0a0a0a',
72 foreground: '#ffffff',
73 text: '#ededed',
74 textMuted: '#a1a1a1',
75 border: '#333333',
76 },
77});
Additional Resources
See references/theme-scales.md for the complete list of theme scales and their CSS property mappings.