Skip to content

Snapp-Mobile/SnappDesignTokens

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

SnappDesignTokens logo

Platform Swift License

SnappDesignTokens

A Swift library for parsing and processing DTCG-compliant design tokens into type-safe Swift structures.

Features

  • โœ… Full DTCG format specification compliance
  • ๐ŸŽจ Support for all primitive token types (Color, Dimension, FontFamily, FontWeight, Duration, CubicBezier, Number)
  • ๐Ÿงฉ Support for composite token types (StrokeStyle, Border, Transition, Shadow, Gradient, Typography)
  • ๐Ÿ”— Token alias resolution with {group.token} syntax
  • ๐Ÿงฎ Dimension expression evaluation with arithmetic operations
  • ๐Ÿ“ Unit conversion (px โ†” rem)
  • ๐Ÿ”„ Flexible token processing pipeline
  • ๐Ÿ’พ Built-in file asset caching
  • โšก๏ธ Zero external dependencies
  • ๐Ÿ“ฑ Cross-platform support (iOS, macOS, tvOS, watchOS, visionOS)

Requirements

  • iOS 16.0+ / macOS 13.0+
  • Swift 6.1+
  • Xcode 16.0+

Installation

Swift Package Manager

Add SnappDesignTokens to your Package.swift file:

dependencies: [
    .package(url: "https://github.com/Snapp-Mobile/SnappDesignTokens.git", from: "1.0.0")
]

Or add it through Xcode:

  1. File > Add Package Dependencies...
  2. Enter package URL: https://github.com/Snapp-Mobile/SnappDesignTokens.git
  3. Select version and add to your target

Quick Start

import SnappDesignTokens
import Foundation

// Load and parse DTCG-compliant JSON file
let url = Bundle.main.url(forResource: "tokens", withExtension: "json")!
let data = try Data(contentsOf: url)

let decoder = JSONDecoder()
let token = try decoder.decode(Token.self, from: data)

// Access token values
if case .group(let group) = token,
   case .value(.color(let colorValue)) = group["brand.primary"] {
    print("Brand color: \(colorValue.hex)")
}

Usage

Loading Design Tokens

let jsonData = """
{
  "color": {
    "base": {
      "red": { "$value": "#FF0000", "$type": "color" },
      "blue": { "$value": "#0000FF", "$type": "color" }
    }
  }
}
""".data(using: .utf8)!

let decoder = JSONDecoder()
let token = try decoder.decode(Token.self, from: jsonData)

Processing Pipelines

// Resolve aliases and flatten hierarchy
let processedToken = try await CombineProcessor.combine(
    .resolveAliases,
    .flatten()
).process(token)

// Alternative syntax
let processedToken = try await ResolveAliasesTokenProcessor
    .resolveAliases
    .combine(.flatten())
    .process(token)

Working with Token Values

// Color tokens
if case .color(let color) = token.value {
    print("Hex: \(color.hex)")
    print("RGB: \(color.components)")
    print("Alpha: \(color.alpha)")
}

// Dimension tokens
if case .dimension(let dimension) = token.value {
    print("Value: \(dimension.value)")
    print("Unit: \(dimension.unit)") // .px, .rem, etc.
}

// Typography tokens
if case .typography(let typography) = token.value {
    print("Font: \(typography.fontFamily)")
    print("Size: \(typography.fontSize)")
    print("Weight: \(typography.fontWeight)")
}

Alias Resolution

let jsonData = """
{
  "base": {
    "color1": { "$value": "#FF0000", "$type": "color" },
    "color2": { "$value": "{base.color1}" }
  }
}
""".data(using: .utf8)!

let token: Token = try jsonData.decode()
let resolved = try await ResolveAliasesTokenProcessor
    .resolveAliases
    .process(token)

// base.color2 now contains #FF0000

Expression Evaluation

let jsonData = """
{
  "space1": { "$value": "2*2", "$type": "dimension" },
  "space2": { "$value": "2", "$type": "dimension" }
}
""".data(using: .utf8)!

let token: Token = try jsonData.decode()

// Using arithmetical evaluation
let evaluated = try await DimensionValueEvaluationProcessor
    .arithmeticalEvaluation
    .process(token)

// space1 is now 4

// Or using NSExpression-based evaluation
let evaluated = try await DimensionValueEvaluationProcessor
    .expressionsEvaluation
    .process(token)

Unit Conversion

let token: Token = .group([
    "dimension1": .value(.dimension(.constant(.init(value: 160, unit: .px)))),
    "dimension2": .value(.dimension(.constant(.init(value: 1, unit: .rem))))
])

let processor: DimensionValueConversionProcessor = .dimensionValueConversion(
    using: .converter(with: 16), // 1rem = 16px
    targetUnit: .rem
)

let converted = try await processor.process(token)
// dimension1 is now 10rem, dimension2 remains 1rem

Documentation

For more information about the DTCG specification, visit Design Tokens Community Group Format Specification.

Contributing

Contributions are welcome! See CONTRIBUTING.md for guidelines.

License

SnappDesignTokens is available under the MIT license. See the LICENSE file for more info.

About

A package to process DTCG-compliant design tokens

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •  

Languages