Skip to content

Design Tokens

Kenny Mochizuki Escalona edited this page Aug 16, 2026 · 11 revisions

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.


Color Tokens

All colors are defined for light mode only. See decision D7 for details.

Access via context.theme.tokens.colors or context.tokenizer.colors.

Brand Color

Token Value Hex Usage
primary Deep navy blue (customizable) #001E60 Interactive elements, prominent actions, primary brand

Default: customizable via LayrzThemeData.light(primaryColor: ...).

Surface Colors

Token Value Hex Usage
background Light gray #FCFCFC Canvas/scaffold background
surface White #FFFFFF Cards, dialogs, elevated containers
surface2 Light gray 2 #F7F7F7 Nested containers, popovers
surface3 Light gray 3 #F0F0F0 Deepest nesting surface color

Foreground / Text Colors

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

Semantic Colors

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

Structural Colors

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

Tonal Opacity

Token Value Usage
tonalOpacity 0.2 (20%) Alpha value applied to tonal/filledTonal fills to create visual distinction

Typography Tokens

Access via context.theme.tokens.typography or context.theme.textTheme.

All text styles use Open Sans by default (customizable via font parameters in LayrzThemeData.light()). Font color is always fg1 (highest-contrast text).

Display Styles

Style Size Weight Line Height Letter Spacing Usage
displayLarge 57 normal (400) 1.17 −0.25 Large headlines (h1)
displayMedium 45 normal 1.16 0.0 Major section headers
displaySmall 36 normal 1.22 0.0 Secondary headlines

Headline Styles

Style Size Weight Line Height Letter Spacing Usage
headlineLarge 32 normal 1.25 0.0 Section headers (h2)
headlineMedium 28 normal 1.29 0.0 Subsection headers (h3)
headlineSmall 24 normal 1.33 0.0 Card titles, list headers

Title Styles

Style Size Weight Line Height Letter Spacing Usage
titleLarge 22 bold (600) 1.27 0.0 Prominent titles, dialog headers
titleMedium 16 bold 1.5 0.15 Item titles, field labels
titleSmall 14 bold 1.43 0.1 Small titles, badges

Body Styles

Style Size Weight Line Height Letter Spacing Usage
bodyLarge 16 normal 1.5 0.15 Paragraph text, UI copy
bodyMedium 14 normal 1.43 0.25 Default root text style
bodySmall 12 normal 1.33 0.4 Secondary UI text, captions

Label Styles

Style Size Weight Line Height Letter Spacing Usage
labelLarge 14 bold 1.43 0.1 Button labels, form labels
labelMedium 12 bold 1.33 0.5 Small labels, tags
labelSmall 11 bold 1.45 0.5 Tiny labels, badges

Spacing Tokens

Access via context.theme.tokens.spacing or context.tokenizer.spacingTokens.

All values are in logical pixels. The base unit is 8.0 pixels.

Token Value Usage
base 8.0 Base spacing unit, used by convenience methods
sp4 4.0 Extra-small gaps
sp6 6.0 Small gaps
sp8 8.0 Small gaps (matches base)
sp10 10.0 Small-medium gaps
sp12 12.0 Medium gaps
sp14 14.0 Medium gaps
sp16 16.0 Standard padding / margin
sp20 20.0 Large padding
sp24 24.0 Large padding
sp28 28.0 Large padding
sp32 32.0 Large padding
sp36 36.0 Extra-large padding
sp40 40.0 Extra-large padding
sp44 44.0 Extra-large padding (touch target size)
sp48 48.0 Extra-large padding

Convenience Accessors

// These use the base unit (8.0)
context.tokens.spacing.margin        // EdgeInsets.all(8)
context.tokens.spacing.reducedMargin // EdgeInsets.all(4)
context.tokens.spacing.padding       // EdgeInsets.all(8)
context.tokens.spacing.sizedBox      // SizedBox(8x8)
context.tokens.spacing.spacingSize   // Size(8, 8)

Breakpoint Tokens

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)

Themeable Breakpoints

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.

Breakpoint Resolution

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
}

Radius Tokens

Access via context.theme.tokens.radius or context.tokenizer.radiusTokens.

All values are in logical pixels. The base unit is 8.0 pixels.

Token Value Usage
base 8.0 Base radius, used by convenience methods and most containers
r8 8.0 Standard corner rounding
r10 10.0 Slightly more rounded
r12 12.0 Medium rounding
r14 14.0 Medium-large rounding
r16 16.0 Large rounding
r20 20.0 Extra-large rounding
r24 24.0 Extra-large rounding
full 999.0 Pill shape (fully rounded)

Convenience Accessors

// These use the base unit (8.0)
context.tokens.radius.borderRadius // BorderRadius.circular(8)

Inner Radius Calculation

For nested container borders, compute the inner radius using:

context.tokenizer.innerRadius(
  outerRadius: 12.0,
  spacer: 4.0,
)

This returns BorderRadius.circular(8.0) because inner = outer − spacer = 12 − 4 = 8.

Rule: 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.


Shadow Tokens

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.

Elevation Algorithm

  • Opacity: Linear interpolation from 0.06 (elevation 0) to 0.12 (elevation 5)
  • Blur Radius: 3 × elevation + 2
  • Vertical Offset: elevation − 1 pixels downward

Elevation Getters

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))]

Elevation Method

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 effect

Border Tokens

Access 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

Pre-built BorderSides

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),
  ),
)

Motion Tokens

Access via context.theme.tokens.motion or context.tokenizer.motion.

Motion tokens define animation durations and easing curves.

Durations

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

Easing Curves

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(),
)

Accessing Tokens

Pattern 1: BuildContext Extensions (Recommended)

context.theme.tokens.colors.primary
context.tokens.colors.primary
context.tokenizer.primary

All three patterns are equivalent but offer different levels of brevity.

Pattern 2: Direct Access via LayrzTheme

final theme = LayrzTheme.of(context);
final primary = theme.tokens.colors.primary;

Pattern 3: Direct Access via LayrzTokens

final tokens = context.tokens;
final primary = tokens.colors.primary;

Customizing Tokens

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(
      sp16: 18.0, // Increase standard padding
    ),
  ),
);

Then pass the custom theme to LayrzApp:

LayrzApp(
  theme: customTheme,
  // ...
)

Related Decisions


Last updated: 2026-08-13
Related pages: Theming, LayrzTokenizer, Getting-Started, Architecture

Clone this wiki locally