Theming
One theme of colours, corners, density, text and motion, in light and dark. Every component draws from it.
The theme lives in lib/ui/theme.dart in your app. Change the values there and every component follows, or pass your own to
AshlarApp.
Bases, accents and styles
Seven bases set the greys, seventeen accents the colour of the main actions and the charts, and six styles the corners, the size of controls, the room in panels and shadows. See them on the
themes page, or choose them at ashlar init.
AshlarApp(
theme: AshlarThemeData.from(base: AshlarBase.zinc, accent: AshlarAccent.blue, style: AshlarStyle.compact),
darkTheme: AshlarThemeData.from(
brightness: Brightness.dark,
base: AshlarBase.zinc,
accent: AshlarAccent.blue,
style: AshlarStyle.compact,
),
)
In every base and accent, body text, muted text and button labels reach 4.5 to 1 against what they sit on. A style's
density makes controls 4 taller or shorter a step (t.control(36)), and its
spacing scales the room in panels (t.space(24)): use them in your own widgets to follow the style.
Phones and desktops
Every component stays what it is on every screen: a dialog is a dialog on a phone too. What changes is how it meets the device. On iOS a control's press area is at least 44 square, and on Android 48, as each platform asks for a finger; it looks the same, and the room it takes grows. Dialogs, sheets and drawers rise above the on-screen keyboard. A hover card opens on a long press where there is no hover. Set
tapTarget on the theme to choose the press area yourself, or 0 for none.
Theme mode
AshlarApp(
theme: const AshlarThemeData.light(),
darkTheme: const AshlarThemeData.dark(),
themeMode: AshlarThemeMode.system, // or .light, .dark
home: const HomePage(),
)
Colours
Colours are named by what they are for, not by hue. Each ...Foreground is for text and icons on the colour it is named after.
| Role | For |
|---|---|
background, foreground | The page and its text. |
card | Cards and panels on the page. |
popover | Menus, popovers and dialogs, which float above the page. |
primary | The main action: a primary button, a checked box. |
secondary | A quieter action. |
muted | What recedes: hints, descriptions, placeholders. |
accent | What is under the pointer or chosen in a list. |
destructive | Actions that delete or warn. |
border, input, ring |
Lines between things, the edge of a field, and the focus ring. |
Radius, font and animation
final theme = const AshlarThemeData.light().copyWith(
radius: 6, // controls use radius - 2, cards radius + 4
text: const AshlarText(fontFamily: 'Inter'),
duration: const Duration(milliseconds: 120),
);
Without a fontFamily the platform's own font is used. To use your own, add it to pubspec.yaml
and name it here. fontFamilyFallback names fonts for what yours has no glyph for, such as symbols.
Using the theme
final t = AshlarTheme.of(context);
Text('Due today', style: t.text.sm.copyWith(color: t.colors.mutedForeground))