Docs
v1.4.1
GitHubSite
React / Theming

Theming

Customise colours, typography, spacing, and shape with theme objects, CSS custom properties, and auto-inherited host page styles.

How theming works

FieldCraft's renderer uses CSS custom properties (--background, --foreground, --primary, etc.) to control visual appearance. There are three ways to theme a form, from simplest to most flexible:

  1. Auto-inherit — if your host page defines shadcn-style CSS variables at :root, the renderer picks them up automatically. No code needed.
  2. Theme object — pass a FormEngineTheme to the theme prop for explicit control.
  3. CSS overrides — target --fc-* variables in your own stylesheets.

Auto-inherit from host page

If your page already defines CSS custom properties like --background, --foreground, --primary, --border, etc. (the standard shadcn/ui pattern), FormEngineRenderer inherits them automatically:

import { FormEngineRenderer } from '@squaredr/fieldcraft-react'

// No theme prop needed — inherits from your page's CSS variables
<FormEngineRenderer schema={schema} onSubmit={handleSubmit} />

This also applies to FieldCraft Pro components (FormBuilder, ResponseViewer, ThemeEditor) — they all auto-inherit from the host page's CSS variables and follow dark/light mode changes.

Custom theme object

Build a FormEngineTheme to control every visual aspect. All properties are optional — undefined values fall back to the host page's CSS variables or built-in defaults.

import type { FormEngineTheme } from '@squaredr/fieldcraft-react'

const myTheme: FormEngineTheme = {
  colors: {
    primary: '#0066cc',
    primaryForeground: '#ffffff',
    secondary: '#f0f0f0',
    secondaryForeground: '#333333',
    error: '#dc2626',
    errorForeground: '#ffffff',
    warning: '#f59e0b',
    success: '#22c55e',
    surface: '#ffffff',
    background: '#fafafa',
    text: '#111111',
    textMuted: '#666666',
    textDisabled: '#aaaaaa',
    border: '#e0e0e0',
    borderFocus: '#0066cc',
    inputBackground: '#ffffff',
  },
  typography: {
    fontFamily: 'Inter, system-ui, sans-serif',
    scale: 'comfortable',            // 'compact' | 'comfortable' | 'spacious'
    questionSize: '1rem',
    labelSize: '0.875rem',
    helpTextSize: '0.75rem',
    bodySize: '0.875rem',
  },
  shape: {
    radius: 'md',                    // 'none' | 'sm' | 'md' | 'lg' | 'full'
    inputRadius: '6px',
    buttonRadius: '6px',
    cardRadius: '8px',
  },
  spacing: {
    base: 16,
    sectionGap: '2rem',
    fieldGap: '1.5rem',
    inputPaddingX: '12px',
    inputPaddingY: '8px',
  },
  layout: {
    maxWidth: '640px',
    alignment: 'left',               // 'left' | 'center'
    progressPosition: 'top',         // 'top' | 'bottom' | 'none'
    sectionLayout: 'card',           // 'card' | 'flat' | 'bordered'
  },
}

Pass it to the renderer:

<FormEngineRenderer schema={schema} theme={myTheme} onSubmit={handleSubmit} />

Presets (Pro)

Five ready-made preset families with light and dark variants are available in FieldCraft Pro:

Preset familyDescription
CleanMinimal, neutral colours, comfortable spacing.
ModernContemporary styling with rounded corners.
High ContrastAccessibility-focused. Strong borders, large text.
ClinicalProfessional/medical. Clean lines, clinical feel.
PlayfulFriendly and colourful. Larger radii, warmer tones.
import { PRESET_FAMILIES } from '@squaredr/fieldcraft-pro'

// Each family has .light and .dark variants
const theme = PRESET_FAMILIES.modern.light
const darkTheme = PRESET_FAMILIES.modern.dark

CSS custom properties

The theme is converted to CSS custom properties (--fc-*) on the form root element. You can use these variables in your own CSS:

.my-form-wrapper {
  border: 1px solid var(--fc-border);
  background: var(--fc-surface);
  font-family: var(--fc-font-family);
}

How the CSS variable chain works

Host page :root CSS variables (--background, --primary, etc.)
    → Inherited by FormEngineRenderer automatically
    → theme prop overrides specific values via inline styles
    → themeToCssVars() converts to --fc-* variables
    → shadcn/Radix components read these variables
    → Your custom CSS can also read them

ThemeProvider

For advanced use cases, wrap your app in FormEngineThemeProvider:

import { FormEngineThemeProvider } from '@squaredr/fieldcraft-react'

function App() {
  return (
    <FormEngineThemeProvider theme={myTheme}>
      {/* All FormEngineRenderer instances inside inherit this theme */}
      <FormEngineRenderer schema={form1} onSubmit={handle1} />
      <FormEngineRenderer schema={form2} onSubmit={handle2} />
    </FormEngineThemeProvider>
  )
}

The useTheme() hook accesses the current theme from context:

import { useTheme } from '@squaredr/fieldcraft-react'

function CustomComponent() {
  const theme = useTheme()
  return <div style={{ color: theme.colors?.primary }}>Themed content</div>
}

Next steps

On this page