philiprehberger_design_tokens 0.4.0 copy "philiprehberger_design_tokens: ^0.4.0" to clipboard
philiprehberger_design_tokens: ^0.4.0 copied to clipboard

Token-based design system with themes, JSON import/export, and validation

philiprehberger_design_tokens #

Tests pub package Last updated

Token-based design system with themes, JSON import/export, and validation. Zero dependencies. Pure Dart.

Requirements #

  • Dart >= 3.6

Installation #

Add to your pubspec.yaml:

dependencies:
  philiprehberger_design_tokens: ^0.4.0

Then run:

dart pub get

Usage #

import 'package:philiprehberger_design_tokens/design_tokens.dart';

final theme = Theme(
  name: 'light',
  colors: {
    'primary': ColorToken.fromHex('#3366FF'),
    'background': ColorToken.fromHex('#FFFFFF'),
  },
  spacings: {
    'sm': SpacingToken(value: 8.0),
    'md': SpacingToken(value: 16.0),
  },
  typographies: {
    'body': TypographyToken(fontSize: 16.0, fontWeight: FontWeight.regular),
  },
);

final color = theme.color('primary');
print(color!.toHex()); // #3366ff

Theme Switching #

final manager = ThemeManager();
manager.register(lightTheme);
manager.register(darkTheme);

manager.onChange((theme) => print('Now using: ${theme.name}'));
manager.switchTo('dark');

Token Types #

// Colors with hex parsing
final color = ColorToken.fromHex('#FF6633');
final rgba = ColorToken(red: 1.0, green: 0.4, blue: 0.2);

// Spacing
const spacing = SpacingToken(value: 16.0);

// Typography with font weight enum
const heading = TypographyToken(
  fontSize: 32.0,
  fontWeight: FontWeight.bold,
  lineHeight: 1.2,
  letterSpacing: -0.5,
);

// Shadows
final shadow = ShadowToken(
  color: ColorToken.fromHex('#000000'),
  radius: 8.0,
  xOffset: 0.0,
  yOffset: 4.0,
  opacity: 0.15,
);

// Borders
final border = BorderToken(
  width: 1.0,
  color: ColorToken.fromHex('#E0E0E0'),
  style: BorderStyle.dashed,
);

Theme Merging and Extending #

// Merge: other theme's tokens override matching keys
final merged = baseTheme.merging(overrideTheme);

// Extend: add tokens while keeping the original name
final extended = baseTheme.extending(
  colors: {'accent': ColorToken.fromHex('#FF6633')},
);

Token Aliases #

// Register semantic aliases for existing token keys
final themed = theme.withAliases({
  'brand': 'primary',
  'small': 'sm',
  'paragraph': 'body',
});

// Resolve by alias or direct name
final color = themed.resolveColor('brand');    // same as theme.color('primary')
final spacing = themed.resolveSpacing('small'); // same as theme.spacing('sm')
final typo = themed.resolveTypography('paragraph');

Responsive Tokens #

// Define breakpoint-dependent values
final spacing = ResponsiveToken<double>({
  'mobile': 8.0,
  'tablet': 16.0,
  'desktop': 24.0,
});

print(spacing.resolve('tablet'));          // 16.0
print(spacing.containsBreakpoint('mobile')); // true
print(spacing.breakpointNames);            // [mobile, tablet, desktop]

// JSON round-trip
final json = spacing.toJson((v) => v);
final restored = ResponsiveToken.fromJson<double>(json, (v) => (v as num).toDouble());

JSON Export and Import #

final exporter = TokenExporter();

// Export to JSON map
final json = exporter.exportJson(theme);

// Import from JSON map
final restored = exporter.importJson(json);

// Serialize to bytes
final bytes = exporter.serialize(theme);
final fromBytes = exporter.deserialize(bytes);

Input Validation #

All token constructors validate their inputs at construction time:

  • ColorToken -- RGBA channels must be between 0.0 and 1.0
  • SpacingToken -- value must be non-negative
  • TypographyToken -- fontSize must be positive, lineHeight (if set) must be positive
  • BorderToken -- width must be non-negative
  • ShadowToken -- radius must be non-negative, opacity must be between 0.0 and 1.0

Invalid values throw RangeError.

Accessibility Validation #

ColorToken includes WCAG 2.1 contrast helpers for verifying color pairs:

final foreground = ColorToken.fromHex('#000000');
final background = ColorToken.fromHex('#FFFFFF');

// Relative luminance per WCAG 2.1
print(foreground.relativeLuminance); // 0.0
print(background.relativeLuminance); // 1.0

// Contrast ratio (1.0 .. 21.0)
print(foreground.contrastRatio(background)); // ~21.0

// AA: 4.5:1 normal text, 3:1 large text
foreground.meetsWcagAA(background);                   // true
foreground.meetsWcagAA(background, largeText: true);  // true

// AAA: 7:1 normal text, 4.5:1 large text
foreground.meetsWcagAAA(background);                  // true
foreground.meetsWcagAAA(background, largeText: true); // true

Validation #

final validator = TokenValidator();
final issues = validator.validate(
  theme,
  requiredColors: ['primary', 'background', 'error'],
  requiredSpacing: ['sm', 'md', 'lg'],
  requiredTypography: ['body', 'heading'],
  requiredShadows: ['card'],
  requiredBorders: ['default'],
);

for (final issue in issues) {
  print('${issue.severity.name}: ${issue.message}');
}

API #

Token Types #

Class Description
ColorToken RGBA color with hex parsing (fromHex, toHex)
SpacingToken Numeric spacing value
TypographyToken Font size, weight, line height, letter spacing
ShadowToken Shadow with color, radius, offsets, opacity
BorderToken Border with width, color, and style
TokenAlias Maps an alias name to an existing token key
ResponsiveToken<T> Breakpoint-dependent token values

ColorToken #

Member Description
relativeLuminance WCAG 2.1 relative luminance in [0.0, 1.0]
contrastRatio(other) WCAG 2.1 contrast ratio against another color (1.0..21.0)
meetsWcagAA(other, {largeText}) true if contrast meets WCAG AA (4.5 normal, 3.0 large)
meetsWcagAAA(other, {largeText}) true if contrast meets WCAG AAA (7.0 normal, 4.5 large)

Theme #

Method Description
color(key) Look up a color token
spacing(key) Look up a spacing token
typography(key) Look up a typography token
shadow(key) Look up a shadow token
border(key) Look up a border token
merging(other) Merge another theme (other overrides)
extending(...) Extend with additional tokens
withAliases(aliases) Return new theme with semantic aliases
resolveColor(nameOrAlias) Look up color by name or alias
resolveSpacing(nameOrAlias) Look up spacing by name or alias
resolveTypography(nameOrAlias) Look up typography by name or alias
aliases Registered aliases (unmodifiable map)

ResponsiveToken #

Method Description
resolve(breakpoint) Get value for a breakpoint
breakpointNames List of all breakpoint keys
containsBreakpoint(breakpoint) Check if breakpoint exists
toJson(serializer) Serialize to JSON map
fromJson(json, deserializer) Deserialize from JSON map

ThemeManager #

Method Description
register(theme) Register a theme
switchTo(name) Switch active theme
activeTheme Current active theme
availableThemes List of registered theme names
onChange(callback) Listen for theme changes

TokenExporter #

Method Description
exportJson(theme) Export theme to JSON map
importJson(json) Import theme from JSON map
serialize(theme) Export to UTF-8 bytes
deserialize(bytes) Import from UTF-8 bytes

TokenValidator #

Method Description
validate(theme, ...) Validate against required token names (colors, spacing, typography, shadows, borders)

Development #

dart pub get
dart analyze --fatal-infos
dart test

Support #

If you find this project useful:

⭐ Star the repo

πŸ› Report issues

πŸ’‘ Suggest features

❀️ Sponsor development

🌐 All Open Source Projects

πŸ’» GitHub Profile

πŸ”— LinkedIn Profile

License #

MIT

2
likes
160
points
7
downloads

Documentation

API reference

Publisher

verified publisherphiliprehberger.com

Weekly Downloads

Token-based design system with themes, JSON import/export, and validation

Homepage
Repository (GitHub)
View/report issues

Topics

#design-system #themes #tokens

License

MIT (license)

More

Packages that depend on philiprehberger_design_tokens