-
Notifications
You must be signed in to change notification settings - Fork 0
Design Tokens
A complete reference of all design tokens in layrz_ui. This is the source of truth for semantic color, typography, spacing, radius, shadow, border, and motion values.
All colors are defined for light mode only. See decision D7 for details.
Access via context.theme.tokens.colors or context.tokenizer.colors.
| Token | Value | Hex | Usage |
|---|---|---|---|
primary |
Deep navy blue (customizable) | #001E60 |
Interactive elements, prominent actions, primary brand |
Default: customizable via LayrzThemeData.light(primaryColor: ...).
A numbered ramp of four surface steps for layered composition, from lightest (canvas) to darkest (deepest nesting).
| Token | Value | Hex | Usage |
|---|---|---|---|
sf1 |
Lightest gray | #FCFCFC |
Canvas/scaffold background, cards, dialogs, panels, overlays (elevation shadow provides separation) |
sf2 |
Light gray 2 | #F7F7F7 |
Nested containers, secondary elevations, popovers |
sf3 |
Light gray 3 | #F0F0F0 |
Deeper nesting, tertiary elevations |
sf4 |
Darkest gray | #E8E8E8 |
Deepest nesting surface color, maximum contrast (currently unused) |
| Token | Value | Hex | Usage |
|---|---|---|---|
fg1 |
Very dark navy | #1A1A2E |
Highest-contrast text (labels, body) |
fg2 |
Dark gray | #4A4A5A |
Secondary text, borders |
fg3 |
Medium gray | #9E9E9E |
Placeholders, disabled text, hints |
fg4 |
Light gray | #C4C4C4 |
Very subtle text, lowest contrast |
| Token | Value | Hex | Usage |
|---|---|---|---|
danger |
Red | #E53935 |
Errors, destructive actions, critical alerts |
success |
Green | #43A047 |
Positive confirmations, valid input, good status |
warning |
Orange | #FB8C00 |
Cautions, non-critical alerts, warnings |
info |
Blue | #1E88E5 |
Informational and neutral alerts |
| Token | Value | Hex | Usage |
|---|---|---|---|
contextual |
Medium gray | #9E9E9E |
Neutral status, informational elements |
divider |
Light gray | #E0E0E0 |
Borders, dividers, separator lines |
overlay |
Black @ 50% | #00000080 |
Modal scrims, overlay backgrounds |
| Token | Value | Usage |
|---|---|---|
tonalOpacity |
0.2 (20%) | Alpha value applied to tonal/filledTonal fills to create visual distinction |
Access via context.theme.tokens.typography or context.theme.textTheme.
layrz_ui defines five text styles — one per semantic role. All styles use Open Sans by default (customizable via font parameters in LayrzThemeData.light()). Font color is always fg1 (highest-contrast text). See decision D23 for the rationale on the five-style scale.
| Style | Size | Weight | Usage |
|---|---|---|---|
display |
40 | 700 | Hero text, splash screens, major section headers |
| Style | Size | Weight | Usage |
|---|---|---|---|
headline |
24 | 600 | Section headers (h2), subsection headers (h3) |
| Style | Size | Weight | Usage |
|---|---|---|---|
title |
20 | 600 | Card titles, dialog headers, item titles, field labels |
| Style | Size | Weight | Usage |
|---|---|---|---|
body |
16 | 400 | Default root text style. Paragraph text, UI copy, secondary text, captions |
| Style | Size | Weight | Usage |
|---|---|---|---|
label |
14 | 400 | Button labels, form labels, tags, badges |
By default, text in layrz_ui wraps freely when space is available. Truncation (the three-dot … ellipsis) is an explicit choice made by individual components when their geometry is fixed.
What this means for you:
- Text in unlimited space wraps naturally — no special handling required
- Fixed-height containers (badges, button labels, input fields) may truncate at the component level
- If you want to force truncation, wrap the component or pass a
maxLinesparameter if the component exposes one - Page layout may change if text previously truncated silently — verify your layouts still fit after upgrade
See decision D51 for the full design rationale.
Access via context.theme.tokens.spacing or context.tokenizer.spacingTokens.
All values are in logical pixels. Spacing uses five semantic levels with the value scale 4, 8, 16, 24, 32 — mirroring the radius and shadow elevation patterns for design system consistency.
| Token | Value | Usage |
|---|---|---|
sp1 |
4.0 | Extra-small gaps, reduced spacing |
sp2 |
8.0 | Small gaps, standard spacing |
sp3 |
16.0 | Standard padding / margin |
sp4 |
24.0 | Large padding |
sp5 |
32.0 | Extra-large padding |
// Padding clarity — both are EdgeInsets.all(spN), identical in value
context.tokens.spacing.pd1 // EdgeInsets.all(4)
context.tokens.spacing.pd2 // EdgeInsets.all(8)
context.tokens.spacing.pd3 // EdgeInsets.all(16)
context.tokens.spacing.pd4 // EdgeInsets.all(24)
context.tokens.spacing.pd5 // EdgeInsets.all(32)
// Margin clarity — same values as pd*, different name for intent
context.tokens.spacing.mg1 // EdgeInsets.all(4)
context.tokens.spacing.mg2 // EdgeInsets.all(8)
context.tokens.spacing.mg3 // EdgeInsets.all(16)
context.tokens.spacing.mg4 // EdgeInsets.all(24)
context.tokens.spacing.mg5 // EdgeInsets.all(32)All other convenience methods (base, margin, reducedMargin, padding, spacingSize, sizedBox) were removed in the refactor to enforce the five-level model.
Access via context.theme.tokens.breakpoints or context.tokens.breakpoints.
Breakpoint tokens define the viewport width thresholds that determine which responsive band a layout is in. All values are in logical pixels. Each field represents the upper bound (exclusive) of the band below it:
| Token | Value (u) | Band Range | Usage |
|---|---|---|---|
xs |
600 | xs: < 600 | Mobile: phones, small tablets |
sm |
960 | sm: 600–959 | Tablets (portrait) |
md |
1264 | md: 960–1263 | Tablets (landscape), small desktops |
lg |
1904 | lg: 1264–1903 | Large desktops |
| (xl) | — | xl: ≥ 1904 | Extra-large displays (no token; defined by remaining space) |
Apps can override breakpoint thresholds by passing a custom theme to LayrzApp:
final customTheme = LayrzThemeData.light().copyWith(
tokens: tokens.copyWith(
breakpoints: LayrzBreakpointTokens(
xs: 480.0, // Custom mobile threshold
sm: 900.0, // Custom tablet threshold
md: 1200.0, // Custom desktop threshold
lg: 1800.0, // Custom large-desktop threshold
),
),
);
LayrzApp(
theme: customTheme,
// ...
)All components that read breakpoints (LayrzRow, LayrzCol, responsive layouts) will follow the custom thresholds automatically.
Use the bandAt(double width) method to resolve a viewport width to its breakpoint band:
final band = context.tokens.breakpoints.bandAt(500);
// Returns LayrzBreakpoint.xs (because 500 < 600)
final band = context.tokens.breakpoints.bandAt(1500);
// Returns LayrzBreakpoint.lg (because 1500 >= 1264 and < 1904)Convenience getter: Use context.breakpoint to get the current breakpoint band based on the viewport width:
if (context.breakpoint == LayrzBreakpoint.xs) {
// Mobile layout
} else if (context.breakpoint == LayrzBreakpoint.md) {
// Tablet or desktop layout
}Access via context.theme.tokens.radius or context.tokenizer.radiusTokens.
All values are in logical pixels. Radius uses five semantic levels with the value scale 4, 8, 16, 24, 32 — mirroring the spacing and shadow elevation patterns for design system consistency.
| Token | Value | Usage |
|---|---|---|
r1 |
4.0 | Subtle rounding |
r2 |
8.0 | Standard corner rounding |
r3 |
16.0 | Medium rounding |
r4 |
24.0 | Large rounding |
r5 |
32.0 | Extra-large rounding |
full |
999.0 | Pill shape (fully rounded) |
// BorderRadius at semantic levels — computed on-demand, not in copyWith/==/hashCode
context.tokens.radius.br1 // BorderRadius.circular(4)
context.tokens.radius.br2 // BorderRadius.circular(8)
context.tokens.radius.br3 // BorderRadius.circular(16)
context.tokens.radius.br4 // BorderRadius.circular(24)
context.tokens.radius.br5 // BorderRadius.circular(32)For nested container borders, compute the inner radius using:
context.tokens.radius.innerRadius(
outerRadius: context.tokens.radius.r4, // Outer: 24 logical pixels
spacer: context.tokens.spacing.sp2, // Spacing: 8 logical pixels
)
// Returns BorderRadius.circular(16) because inner = 24 − 8 = 16Rule: The inner corner arc must share the outer arc's centre. The spacer represents the distance between the two arcs' centres; subtracting it from the outer radius gives the inner radius. If the result is negative (e.g., outerRadius: 4, spacer: 10), it clamps to 0, producing a pill shape.
All other convenience methods (base, borderRadius getter) were removed in the refactor to enforce the five-level model. Use br2 for the common case where you previously used borderRadius.
Access via context.theme.tokens.shadow or context.tokenizer.shadowTokens.
Shadow tokens map elevation levels (0–5) to BoxShadow lists. The algorithm computes blur radius, opacity, and offset based on elevation to create visual hierarchy.
- Opacity: Linear interpolation from 0.06 (elevation 0) to 0.12 (elevation 5)
-
Blur Radius:
3 × elevation + 2 -
Vertical Offset:
elevation − 1pixels downward
| Token | Elevation | Usage |
|---|---|---|
elevation1 |
1 | Subtle elevation, cards |
elevation2 |
2 | Standard elevation, popovers |
elevation3 |
3 | Medium elevation, overlays |
elevation4 |
4 | High elevation, modal dialogs |
elevation5 |
5 | Highest elevation, top-most overlays |
Example:
context.tokens.shadow.elevation2
// Returns: [BoxShadow(color: black@9%, blur: 8, offset: Offset(0, 1))]For custom elevation settings:
context.tokenizer.shadow(
elevation: 2, // 0–5 (required)
radius: 12.0, // Custom border radius (optional, defaults to base)
color: Colors.white, // Surface color (optional, defaults to surface)
shadowColor: Colors.black, // Shadow color (optional)
reverse: false, // Flip offset for pressed state (optional)
hideOnElevationZero: false, // Hide shadow at elevation 0 (optional)
)Returns a BoxDecoration with:
- The specified surface color and border radius
- Computed box shadow for the elevation
- At elevation 0: a 1-pixel outline border (unless
hideOnElevationZero: true)
Example: Pressed Button State:
context.tokenizer.shadow(elevation: 2, reverse: true)
// Reverses the shadow offset for a "pressed" visual effectAccess via context.theme.tokens.border.
Border tokens define stroke widths and pre-built BorderSide objects.
| Token | Value | Usage |
|---|---|---|
base |
1.5 | Default border width |
stroke1 |
1.0 | Thin stroke |
stroke2 |
2.0 | Medium stroke |
stroke3 |
3.0 | Thick stroke |
| Property | Value | Usage |
|---|---|---|
light |
BorderSide(color: divider, width: 1.0) |
Thin divider lines |
normal |
BorderSide(color: divider, width: 2.0) |
Standard borders |
thick |
BorderSide(color: divider, width: 3.0) |
Prominent borders |
Example:
Container(
decoration: BoxDecoration(
border: Border(bottom: context.tokens.border.normal),
),
)Access via context.theme.tokens.motion or context.tokenizer.motion.
Motion tokens define animation durations and easing curves.
| Token | Value | Usage |
|---|---|---|
dHover |
100ms | Hover state transitions |
dPress |
80ms | Press/tap feedback |
dTransition |
200ms | Component state changes |
dPageTransition |
250ms | Page navigation |
dDialog |
300ms | Dialog entrance/exit |
| Token | Value | Usage |
|---|---|---|
easing |
Curves.easeInOut |
Standard animations |
easingEnter |
Curves.easeOut |
Entrance animations (fast start) |
easingExit |
Curves.easeIn |
Exit animations (slow start) |
Example:
AnimatedOpacity(
duration: context.tokens.motion.dTransition,
curve: context.tokens.motion.easing,
opacity: isHovered ? 1.0 : 0.5,
child: MyWidget(),
)The following constants are exported from package:layrz_ui/layrz_ui.dart as default values for the token system:
| Constant | Value | Hex | Description |
|---|---|---|---|
kPrimaryColor |
Deep navy blue | #001E60 |
Layrz primary brand color (default tokens.colors.primary) |
kHoverDuration |
— | — | Micro-interaction animation duration (100ms, default tokens.motion.dHover) |
kPageTransitionDuration |
— | — | Page transition animation duration (250ms, default tokens.motion.dPageTransition) |
These are convenience re-exports of the most-used defaults. For customization, pass overrides to LayrzThemeData.light() or LayrzApp:
LayrzApp(
theme: LayrzThemeData.light(
primaryColor: const Color(0xFF0077BE), // Override default brand color
),
// ...
)context.theme.tokens.colors.primary
context.tokens.colors.primary
context.tokenizer.primaryAll three patterns are equivalent but offer different levels of brevity.
final theme = LayrzTheme.of(context);
final primary = theme.tokens.colors.primary;final tokens = context.tokens;
final primary = tokens.colors.primary;Modify tokens via LayrzThemeData.copyWith() and LayrzTokens.copyWith():
final customTheme = theme.copyWith(
tokens: theme.tokens.copyWith(
colors: theme.tokens.colors.copyWith(
primary: const Color(0xFF0077BE),
danger: const Color(0xFFD32F2F),
),
spacing: theme.tokens.spacing.copyWith(
sp3: 18.0, // Increase standard padding
),
),
);Then pass the custom theme to LayrzApp:
LayrzApp(
theme: customTheme,
// ...
)- D7: Light Mode Only — Explains why dark mode is not supported.
- D15: Interaction States Never Change Geometry — States that hover/press/focus do not change size, padding, or layout; only visual properties like color and opacity.
Last updated: 2026-08-13
Related pages: Theming, LayrzTokenizer, Getting-Started, Architecture
Made with ❤️ by Golden M, Inc.