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.md,
  ),
  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-md
/* ... and more */

Using CSS Variables in Styles#

Reference these variables in your ArcaneStyleData:

ArcaneBox(
  style: const ArcaneStyleData(
    background: Background.card,
    border: BorderPreset.standard,
    textColor: TextColor.cardForeground,
    borderRadius: Radius.md,
  ),
  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 */
    }
  ''';

}

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

Fonts must be committed with the host site's assets and declared with local @font-face URLs. ArcaneStylesheet intentionally has no remote stylesheet hook, so activating a theme never adds a network font or CSS request.

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.