Skip to Content
DocsRemixFortal

Fortal

Fortal is a prebuilt design system that ships with Remix. It provides a full set of component styles — buttons, inputs, menus, badges, and everything else Remix offers — built on top of the Radix  color scales and spacing system. You can drop it into a Remix app and get a polished, modern look without defining a single token yourself.

Each Fortal widget delegates to a regular Remix fortal*Styler underneath. Those stylers use the same fluent API you’d use to write your own, so you can still extend, override, or fork any piece of the design system.

What Fortal Gives You

  • A complete set of component styles — one or more Fortal styles per Remix component, fully configured for every interaction state (hover, press, focus, disabled)
  • A curated token system — colors, spacing, border radii, shadows, typography, and border widths, all based on Radix’s color science and scale conventions
  • Variants — most components expose multiple Fortal variants (e.g., solid, soft, surface, outline, ghost) so you can match the visual weight to the context
  • Dark mode support — Fortal’s token system resolves automatically in light and dark themes through FortalScope

Installation

Fortal ships with Remix — no extra package to add. Import it alongside Remix:

import 'package:remix/remix.dart';

Then wrap the part of your app that should use Fortal in a FortalScope. The scope provides the token values that every Fortal widget, fortal*Styler, and FortalTokens.* call resolves against:

import 'package:flutter/material.dart'; import 'package:remix/remix.dart'; class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: FortalScope( child: Scaffold( body: Center( child: FortalButton.solid( onPressed: () {}, label: 'Fortal Button', ), ), ), ), ); } }

Without FortalScope in the widget tree above a Fortal-styled component, token lookups have no values to resolve and the component falls back to defaults.

Component Variants

Most Remix components expose generated Fortal* widgets with named constructors for fixed variants:

FortalButton.solid(label: 'Save', onPressed: () {}) FortalButton.soft(label: 'Save', onPressed: () {}) FortalButton.surface(label: 'Save', onPressed: () {}) FortalButton.outline(label: 'Save', onPressed: () {}) FortalButton.ghost(label: 'Save', onPressed: () {})

Use the unnamed constructor when the variant is selected dynamically:

FortalButton( variant: selectedVariant, onPressed: () {}, label: 'Save', );

Call the matching fortal*Styler directly when you need a custom Remix widget composition or want to extend the preset style.

Not every component has every variant — a RemixSpinner doesn’t need a ghost variant. Refer to each component’s reference page for the full list of Fortal variants it ships.

Design Tokens

Fortal is built on a scale-based token system that mirrors Radix’s conventions:

Token groupScale
Colors — accent12 steps (accent1accent12)
Colors — gray12 steps (gray1gray12)
Colors — contrastaccentContrast, grayContrast for text on filled surfaces
Spacing9 steps (space1space9)
Border radius6 steps (radius1radius6)
Shadows6 levels (shadow1shadow6)
Typography9 sizes (text1text9)
Border widthsborderWidth1borderWidth4

Each token is a function on the FortalTokens class. Call it from any style:

final style = ButtonStyler() .color(FortalTokens.accent9()) .paddingAll(FortalTokens.space4()) .borderRadius(.all(FortalTokens.radius3())) .label(.color(FortalTokens.accentContrast()));

The accent9 step is the “solid” color — the strongest fill suitable for primary actions. The 12-step scale maps numbered steps to specific uses:

StepsUse for
1–2App backgrounds
3–5Component backgrounds (hover states on higher steps)
6–8Borders and separators
9–10Solid fills (primary actions)
11–12Text and high-contrast elements

This mapping is consistent across every Radix-based design system — if you know Radix, you know Fortal.

Token functions like FortalTokens.accent9() resolve to the current theme’s value at paint time. That means a single style definition works in both light and dark mode without rewriting.

Customizing Fortal

Because every fortal*Styler returns a regular Remix style, you can chain additional methods to override or extend any piece of it:

final style = fortalButtonStyler(variant: .solid) .borderRadius(.circular(8)) .paddingX(32) .onHovered(.scale(1.05));

The chain above starts from Fortal’s solid button, overrides the border radius and horizontal padding, and adds a scale-on-hover modifier. Every other Fortal property — colors, state styling, typography — passes through untouched.

You can also mix Fortal tokens into a completely custom style:

final style = ButtonStyler() .color(FortalTokens.accent9()) .paddingAll(FortalTokens.space4()) .borderRadius(.all(FortalTokens.radius3())) .onHovered(.color(FortalTokens.accent10()));

This gives you Fortal’s token consistency without starting from its component styles.

When to Use Fortal

Reach for Fortal when:

  • You want a ready-made look that pairs with Radix’s color science
  • You need dark-mode-aware components without wiring up your own theme
  • You’re prototyping and don’t yet have a design system of your own
  • Your design system can be expressed as overrides on top of Fortal’s foundation

Skip Fortal when your design system has strong opinions that clash with Radix’s scale — in that case, build your own styles directly with the Mix API. Remix components work exactly the same either way.

Next Steps

Last updated on