
Theming
Learn how to configure stylesheets and theming in Arcane Jaspr
Live Demo
Component: theming
Preview + Code

Theme-owned controls

Buttons, badges, and surfaces inherit the active renderer.

Code
ArcaneBox(
  style: const ArcaneStyleData(
    padding: PaddingPreset.lg,
    background: Background.card,
    border: BorderPreset.accent,
    borderRadius: Radius.lg,
  ),
  children: [
    Button.primary(label: 'Primary', onPressed: () {}),
    Button.secondary(label: 'Secondary', onPressed: () {}),
  ],
)

Theming#

Arcane Jaspr uses a stylesheet-based theming system built on CSS custom properties. Stylesheets provide complete design systems including colors, typography, spacing, and component renderers.

ArcaneApp#

Every Arcane application must be wrapped in an ArcaneApp component which provides the theme context and injects the stylesheet CSS.

import 'package:arcane_jaspr/arcane_jaspr.dart';
import 'package:arcane_jaspr_neon/arcane_jaspr_neon.dart';
import 'package:arcane_jaspr_neubrutalism/arcane_jaspr_neubrutalism.dart';
import 'package:arcane_jaspr_shadcn/arcane_jaspr_shadcn.dart';

ArcaneApp(
  stylesheet: const ShadcnStylesheet(),
  brightness: Brightness.dark,
  home: YourApp(),
)

Available Stylesheets#

Arcane Jaspr keeps the Flutter-like authoring surface in package:arcane_jaspr and ships concrete stylesheets as renderer packages:

StylesheetDescription
arcane_jaspr_shadcn / ShadcnStylesheet Quiet document/app chrome with compact spacing, subtle borders, and shadcn-style controls
arcane_jaspr_neon / NeonStylesheet Command/workbench chrome with stronger contrast, denser shell treatment, larger controls, and bolder radius
arcane_jaspr_neubrutalism / NeubrutalismStylesheet Comic-book pop-art chrome with thick black borders, hard-offset shadows, flat saturated colors, and press-down click feedback
import 'package:arcane_jaspr_shadcn/arcane_jaspr_shadcn.dart';

ArcaneApp(
  stylesheet: const ShadcnStylesheet(),
  home: MyApp(),
)
import 'package:arcane_jaspr_neon/arcane_jaspr_neon.dart';

ArcaneApp(
  stylesheet: const NeonStylesheet(),
  home: MyApp(),
)
import 'package:arcane_jaspr_neubrutalism/arcane_jaspr_neubrutalism.dart';

ArcaneApp(
  stylesheet: const NeubrutalismStylesheet(theme: NeubrutalismTheme.yellow),
  home: MyApp(),
)

Brightness Mode#

Control light/dark mode with the brightness parameter:

ArcaneApp(
  stylesheet: const ShadcnStylesheet(),
  brightness: Brightness.dark,
  home: MyApp(),
)
ValueDescription
Brightness.darkDark mode (default)
Brightness.lightLight mode

CSS Variables#

Stylesheets define CSS custom properties that all components use. These are automatically injected into the document.

Core Colors#

--background          /* Page background */
--foreground          /* Default text color */
--card                /* Card background */
--card-foreground     /* Text on cards */
--popover             /* Popover background */
--popover-foreground  /* Text in popovers */
--primary             /* Primary brand color */
--primary-foreground  /* Text on primary */
--secondary           /* Secondary color */
--secondary-foreground
--muted               /* Muted backgrounds */
--muted-foreground    /* Muted text */
--accent              /* Accent color */
--accent-foreground
--destructive         /* Error/danger color */
--destructive-foreground
--border              /* Border color */
--input               /* Input border color */
--ring                /* Focus ring color */

Status Colors#

--success             /* Success state */
--success-foreground
--warning             /* Warning state */
--warning-foreground
--info                /* Info state */
--info-foreground

Structure Tokens#

--radius              /* Base border radius (e.g., 0.5rem) */
--font-sans           /* Sans-serif font stack */
--font-mono           /* Monospace font stack */

Arcane Aliases#

Stylesheets also define --arcane-* prefixed variables for package-level components:

--arcane-background
--arcane-foreground
--arcane-primary
--arcane-primary-foreground
--arcane-secondary
--arcane-accent
--arcane-muted
--arcane-border
--arcane-radius
--arcane-radius-sm
--arcane-radius-md
--arcane-radius-lg
/* ... and more */

Using CSS Variables in Styles#

Reference these variables in your ArcaneStyleData:

ArcaneBox(
  style: const ArcaneStyleData(
    raw: {
      'background': 'var(--card)',
      'border': '1px solid var(--border)',
      'color': 'var(--card-foreground)',
      'border-radius': 'var(--radius)',
    },
  ),
  children: [...],
)

Or use the type-safe presets which map to these variables:

ArcaneBox(
  style: const ArcaneStyleData(
    background: Background.card,
    textColor: TextColor.cardForeground,
    border: BorderPreset.standard,
    borderRadius: Radius.md,
  ),
  children: [...],
)

Component Renderers#

Each stylesheet provides its own implementation of all components via the renderer system. Access renderers through context:

@override
Widget build(BuildContext context) {
  return Button(
    label: 'Click me',
    onPressed: () {},
  );
}

Creating Custom Stylesheets#

To create a custom stylesheet, extend ArcaneStylesheet:

class MyStylesheet extends ArcaneStylesheet {
  const MyStylesheet();

  @override
  String get id => 'my-style';

  @override
  String get name => 'My Style';

  @override
  ComponentRenderers get renderers => const MyRenderers();

  @override
  LayoutRenderers get layouts => const MyLayouts();

  @override
  String get baseCss => '''
    :root {
      --background: #ffffff;
      --foreground: #000000;
      --primary: #3b82f6;
      --primary-foreground: #ffffff;
      /* ... define all required variables */
    }

    .dark {
      --background: #0a0a0a;
      --foreground: #fafafa;
      /* ... dark mode overrides */
    }
  ''';

  @override
  get externalCssUrls => const [
    'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap',
  ];
}

Your custom MyRenderers class must implement all methods in ComponentRenderers, and MyLayouts must implement adaptive layout slots such as ArcaneScaffold.

Theme Toggle#

Use ArcaneThemeToggle to let users switch between light and dark modes:

ArcaneThemeToggle(
  isDark: true,
  onChanged: (isDark) {
    // Handle theme change
  },
)

Static Site Support#

When building static sites with jaspr build, include fallback scripts for interactivity:

ArcaneApp(
  stylesheet: const ShadcnStylesheet(),
  includeFallbackScripts: true,
  home: MyApp(),
)

This injects JavaScript that provides interactivity when Jaspr client hydration is unavailable.