Youll
Architecture

Design Token System

How Figma design tokens flow from JSON files through ConfigParser to runtime colors, sizes, and typography in every customer app.

Overview

The design token system is the bridge between design and code. Designers define tokens in Figma (colors, sizes, typography), export them as JSON files, and customer apps bundle those files. At runtime, the platform resolves token references into concrete UIColor and CGFloat values that style every screen.

The system has two sides:

  • Bricks (Core) defines what tokens exist via ThemeTokens, a struct with ~180 static constants representing every color, size, and radius used across the platform. Bricks screens reference tokens by name without knowing the actual values.
  • Customer apps define what tokens mean by providing JSON files with the real hex colors and pixel values, plus a ConfigParser that resolves token references at runtime.

This separation means Bricks can build screens against semantic names like "theme/navigation/bottom/highlight", and each customer app maps that to its own brand color.

Figma Design
     |
     v
JSON Export (tokens.json, colors.json, sizes.json)
     |
     v
App Bundle (Resources/ConfigTokens/)
     |
     v
ConfigParser resolves at runtime
     |
     +---> UIColor (for color tokens)
     +---> CGFloat (for size tokens)
     |
     v
UIConfigurator applies to UIKit components

Dual Theming Approach

The platform uses two parallel systems for resolving visual properties, and both are active simultaneously:

  1. Token-based (dynamic): ConfigParser.colorFromToken(ThemeTokens.someToken) resolves through the JSON pipeline. Changing the JSON files changes the theme without code changes. Used primarily for semantic and contextual colors (navigation backgrounds, card overlays, button states, pill colors).

  2. Hardcoded (static): AppTheming.ColourType.accent.color returns a hardcoded hex value directly. Used for fundamental palette access where the indirection of the JSON pipeline is unnecessary (basic error/success/warning colors, black/white).

Both approaches coexist throughout the codebase. For example, a cell theme config might use a token-resolved background color alongside a hardcoded AppTheming.ColourType for its text color.

Current Limitations

  • No dark mode support. The JSON files contain a single set of values. Supporting dark mode would require either duplicate JSON files or conditional logic in ConfigParser.
  • ConfigParser lives in the customer app, not in Bricks. Each customer app has its own copy of the parser. This is by design (customers may customize resolution logic) but means the parsing algorithm is duplicated.

Token Files

The JSON files follow the W3C Design Token specification, which is the standard export format from Figma Variables. Each entry uses $type and $value fields.

Customer apps include up to four JSON files in the app bundle. Three are required, one is optional.

tokens.json (Semantic Layer)

The routing layer. Maps semantic token paths to references in the color or size files. Each entry has a $type (either "color" or "number") and a $value that points to the actual value.

{
  "theme": {
    "navigation": {
      "bottom": {
        "highlight": {
          "$type": "color",
          "$value": "{Accent.accent-600}"
        }
      }
    }
  },
  "theme-button-height": {
    "$type": "number",
    "$value": "{button-height}"
  }
}

Token values use brace notation for indirection: "{Accent.accent-600}" means "look up Accent > accent-600 in colors.json." Values can also reference other tokens within tokens.json itself, enabling aliases.

colors.json (Color Palette)

A nested dictionary of color families, each containing a scale of shades. Values are hex strings or rgba() strings.

{
  "Accent": {
    "accent-600": {
      "$type": "color",
      "$value": "#017cff"
    },
    "accent-400": {
      "$type": "color",
      "$value": "#77a9f9"
    }
  },
  "Light": {
    "light-50": {
      "$type": "color",
      "$value": "rgba(255, 255, 255, 0.2000)"
    },
    "light-600": {
      "$type": "color",
      "$value": "#ffffff"
    }
  }
}

Color families typically include shades from 50 (lightest) through 900 (darkest). Both #hex and rgba(r, g, b, a) formats are supported.

sizes.json (Size Values)

Numeric values for paddings, border sizes, radii, and component dimensions.

{
  "paddings": {
    "padding-2xl-20": {
      "$type": "number",
      "$value": 20
    },
    "padding-md-8": {
      "$type": "number",
      "$value": 8
    }
  },
  "border-size": {
    "border-size-xs-1": {
      "$type": "number",
      "$value": 1
    }
  },
  "button-height": {
    "$type": "number",
    "$value": 46
  }
}

textStyles.json (Typography, Optional)

Only needed if the customer app uses the JSON-based typography approach. Contains an array of named text styles with font family, weight, size, and case information.

{
  "fileName": "textStyles",
  "textStyles": [
    {
      "name": "headline",
      "fontFamily": "CustomFont",
      "fontWeight": "Bold",
      "fontSize": 16,
      "textCase": "NONE"
    },
    {
      "name": "body",
      "fontFamily": "CustomFont",
      "fontWeight": "Regular",
      "fontSize": 14,
      "textCase": "NONE"
    }
  ]
}

The textCase field supports "UPPER", "TITLE", and "NONE".

ConfigParser

ConfigParser is a customer-app class with static methods that resolve token strings into UIColor and CGFloat values. It loads the three JSON files once (as static properties) and traverses them at runtime.

Color Resolution

Two methods handle color tokens:

colorFromToken(_:) resolves full path tokens like "theme/navigation/bottom/highlight":

  1. Splits the token by "/" to get keys: ["theme", "navigation", "bottom", "highlight"]
  2. Traverses tokens.json using those keys to find the leaf entry
  3. Reads $type (must be "color") and $value
  4. If $value is already a hex or rgba string, returns the color directly
  5. If $value uses brace notation (e.g., "{Accent.accent-600}"), strips braces and splits by "." to get color keys
  6. Traverses colors.json using those keys to find the $value hex/rgba string
  7. Returns the UIColor

colorFromShortToken(_:) resolves brace-wrapped references like "{Accent.accent-400}" directly against colors.json, bypassing tokens.json.

Size Resolution

sizeFromToken(_:) follows the same pattern as colorFromToken but expects $type: "number" and returns a CGFloat.

Recursive Resolution

Token values in tokens.json can reference other tokens within the same file. ConfigParser handles this recursively: if looking up keys in colors.json or sizes.json fails, it falls back to tokens.json to resolve the intermediate reference.

To prevent infinite loops, a depth counter tracks each token's recursion level with a maximum depth of 3. If exceeded, the parser logs an error and returns a fallback value (.clear for colors, 0 for sizes).

Resolution Diagram

colorFromToken("theme/navigation/bottom/highlight")
  |
  |-- Split by "/" --> keys: ["theme", "navigation", "bottom", "highlight"]
  |
  |-- Traverse tokens.json with keys
  |     --> { "$type": "color", "$value": "{Accent.accent-600}" }
  |
  |-- Strip braces, split by "."
  |     --> color keys: ["Accent", "accent-600"]
  |
  |-- Traverse colors.json with color keys
  |     --> { "$type": "color", "$value": "#017cff" }
  |
  |-- UIColor(hexOrRgba: "#017cff")
  |
  --> UIColor (blue)

Typography

Youll supports two approaches to typography. Both must conform to TypographyTypeProtocol, a protocol defined in Core that requires:

public protocol TypographyTypeProtocol {
    var font: UIFont { get }
    var suiFont: Font { get }
    var kern: CGFloat { get }
    var uppercased: Bool? { get }
    var paragraphStyle: NSMutableParagraphStyle { get }
}

The customer app defines an AppTheming.TypographyType enum whose cases directly return UIFont instances through a FontBook helper. This approach keeps typography definitions in Swift alongside other theming code.

// FontBook maps font family names to UIFont
enum FontBook {
    case text

    func font(ofSize size: CGFloat, style: Style) -> UIFont {
        let name = fontName + "-" + style.rawValue
        guard let font = UIFont(name: name, size: size) else {
            return UIFont.systemFont(ofSize: size, weight: style.weight)
        }
        return font
    }

    private var fontName: String {
        switch self {
        case .text: return "YourCustomFont"
        }
    }
}

// TypographyType uses FontBook for each scale step
public enum TypographyType: String, TypographyTypeProtocol {
    case large_title, title_1, title_2, title_3
    case headline, body, callout, subhead
    case footnote, caption_1, caption_2

    public var font: UIFont {
        switch self {
        case .large_title: return FontBook.text.font(ofSize: 27, style: .bold)
        case .headline:    return FontBook.text.font(ofSize: 16, style: .bold)
        case .body:        return FontBook.text.font(ofSize: 14, style: .regular)
        // ... remaining cases
        }
    }
}

FontBook falls back to UIFont.systemFont if the custom font is not found, ensuring the app never crashes on missing fonts.

The suiFont property supports Dynamic Type when Settings.useDynamicFontSizes is enabled, mapping each typography type to an Apple text style for accessibility scaling.

JSON-Based Typography (Alternative)

TypographyParser loads a textStyles.json file and matches style names against TypographyType.rawValue. This approach is useful when designers want to control typography from Figma without code changes.

// TypographyParser resolves fonts from JSON
TypographyParser.getFont(for: .headline) // Returns UIFont based on textStyles.json
TypographyParser.isUppercased(for: .headline) // Returns Bool based on textCase

The parser iterates through text styles looking for a name match, then constructs a UIFont from the fontFamily, fontWeight, and fontSize fields.

AppTheming

AppTheming is a struct defined in the customer app that centralizes all visual constants. It contains four enums:

ColourType

Named color palette with hardcoded values. Used for colors that don't come from tokens (static brand colors, fallback colors, utility colors).

public enum ColourType: String {
    case main, second, third, fourth
    case dark, accent, light
    case error, warning, success
    case black, white

    var color: UIColor {
        switch self {
        case .main:    return UIColor.hex(0x0E0E13)
        case .accent:  return UIColor.hex(0x017CFF)
        case .error:   return UIColor.hex(0xC0373E)
        // ... remaining cases
        }
    }
}

TypographyType

The typography scale, described in the Typography section above.

CornerRadiusType

A scale of corner radius values from 0 to 100 points:

public enum CornerRadiusType: String {
    case radius_1   // 0 (no radius)
    case radius_4   // 8
    case radius_5   // 12
    case radius_6   // 16
    case radius_8   // 24
    case radius_100 // 100 (pill shape)

    var cornerRadius: CGFloat { /* ... */ }
}

ThemeBorderType

A scale of border widths from 0 to 4 points:

public enum ThemeBorderType: String {
    case none       // 0
    case small      // 1
    case medium     // 1.5
    case large      // 2
    case extraLarge // 2.5
    case superLager // 3
    case thickest   // 4

    var borderWidth: CGFloat { /* ... */ }
}

ThemeTokens Registry

ThemeTokens is a struct in Core (the Bricks shared codebase) that holds ~180 static string constants for every token used across the platform. It serves as the single registry of token paths.

public struct ThemeTokens {
    // Palette references (brace notation, resolve directly in colors.json)
    public static let main = "{Main.main-300}"
    public static let accent = "{Accent.accent-400}"

    // View element tokens (path notation, resolve through tokens.json)
    public static let themeNavigationBottomHighlight = "theme/navigation/bottom/highlight"
    public static let themeGeneralOnBackground = "theme/general/on-background"
    public static let themeButtonsPrimaryActiveSurface = "theme/buttons/primary/active/surface"

    // Size tokens (path notation, resolve through tokens.json to sizes.json)
    public static let themeButtonsRadius = "theme/buttons/radius"
    public static let themeCardsRadiusGeneralSmall = "theme/cards/radius/general-small"
}

Tokens are organized by category:

  • Palette colors use brace notation and resolve directly in colors.json
  • View element colors use path notation and resolve through tokens.json to colors.json
  • Sizes use path notation and resolve through tokens.json to sizes.json

Bricks screens call ConfigParser.colorFromToken(ThemeTokens.themeNavigationBottomHighlight) or ConfigParser.colorFromShortToken(ThemeTokens.accent) to get the customer's actual colors without knowing what those colors are.

UIConfig Protocol

UIConfig is the protocol defined in Core that every customer app must implement. It defines how Bricks applies theming to shared UI components.

public protocol UIConfig {
    func applyButtonStyle(_ style: ButtonStyleProtocol, title: String, to button: YoullTextButton)
    func configureAuthButton(_ button: UIButton, title: String?, forOption option: AuthOption, styleType: AuthConnectionWithButtonType)
    func generateGradientLayer() -> CAGradientLayer
    func backgroundColorForToastStyle(_ style: ToastStyle) -> UIColor
    func applyStyle(navigationController: UINavigationController)
    func applyStyle(tabBar: UITabBar)
    func applyPopupBarStyle(viewController: UIViewController)
    var cellsThemingLayoutManager: CellsThemingLayoutManagerProtocol { get }
}

The customer app creates a concrete UIConfigurator class (conforming to UIConfig) that uses ConfigParser and AppTheming to implement each method. For example, applyStyle(tabBar:) uses ConfigParser.colorFromToken(ThemeTokens.themeNavigationBottomHighlight) to set the selected tab color.

Injection

The UIConfigurator is injected at app startup in AppDelegate:

Client.setupUIConfigurator(with: UIConfigurator.shared)

Client.setupUIConfigurator(with:) stores the UIConfig instance in a module-level variable. Bricks modules access it through a computed property called UIConfigurator that fatally errors if setup was not called.

Cell Theming

Collection view cells across the platform are themed through CellsThemingLayoutManager, a class in Core that maps cell styles to themed layout views.

CellsThemingLayoutManagerProtocol

public protocol CellsThemingLayoutManagerProtocol {
    @MainActor
    func getLayout(style: CellStyleType, cellThemeConfig: CellThemeConfig) -> UIView
}

Given a cell style (e.g., .smallVertical, .largeHorizontal, .featured) and a theme config, the manager returns a fully styled UIView.

Cell Theme Configs

Each cell style has a corresponding config type inheriting from CellThemeConfig. There are approximately 15 config types covering all card layouts in the platform:

  • SmallVerticalCellThemeConfig, SmallHorizontalCellThemeConfig, SmallCenteredCellThemeConfig
  • LargeVerticalCellThemeConfig, LargeHorizontalCellThemeConfig
  • FeaturedCellThemeConfig, ClassicCardCellThemeConfig, ProgramCardThemeConfig
  • ProgressCellThemeConfig, TextCellThemeConfig, TagCellThemeConfig
  • BannerCellThemeConfig, DailyQuoteCellThemeConfig
  • TuyaHardwareConfig, TimerSessionCellThemeConfig

The customer app also implements a CollectionViewCellsThemingManager (conforming to CollectionViewCellsThemingManagerProtocol from Core) that maps each CellStyleType to a concrete CellThemeConfig subclass. This is where the customer decides which colors, fonts, and sizes each cell layout uses, calling ConfigParser for token-resolved values and AppTheming for hardcoded palette values.

The customer app creates both the CellsThemingLayoutManager and the CollectionViewCellsThemingManager instances and injects them into UIConfigurator.

Setting Up Theming for a New Customer App

  1. Export tokens from Figma. Use the Figma Tokens plugin (or equivalent) to export tokens.json, colors.json, and sizes.json. Place them in Resources/ConfigTokens/ in the app bundle. See the Design Token Workflow guide for the full export process (coming soon).

  2. Register custom fonts. Add font files (.ttf or .otf) to the app bundle and list them in Info.plist under UIAppFonts. Create a FontBook enum that maps to your font family names.

  3. Implement AppTheming. Create a struct with four enums:

    • ColourType with your brand's named colors
    • TypographyType conforming to TypographyTypeProtocol, using FontBook for font resolution
    • CornerRadiusType with your radius scale
    • ThemeBorderType with your border width scale
  4. Implement ConfigParser. Create a class with static methods colorFromToken(_:), colorFromShortToken(_:), and sizeFromToken(_:) that load your JSON files and resolve token paths. The implementation follows the resolution algorithm described above.

  5. Implement UIConfigurator. Create a class conforming to UIConfig that uses ConfigParser and AppTheming to style buttons, tab bars, navigation bars, toast messages, popup bars, and auth buttons. Include a CellsThemingLayoutManager instance.

  6. Inject at startup. In AppDelegate.didFinishLaunchingWithOptions, call:

    Client.setupUIConfigurator(with: UIConfigurator.shared)

For Agents

Key Files

Bricks (shared core):

  • Bricks/modules/Core/Core/Content/Theming/Tokens/ThemeTokens.swift - All token path constants
  • Bricks/modules/Core/Core/Content/Public Interface/UIConfig.swift - UIConfig protocol
  • Bricks/modules/Core/Core/Content/Public Interface/TextLabelConfig.swift - TypographyTypeProtocol
  • Bricks/modules/Core/Core/Content/Theming/CellsThemingLayoutManager/ - Cell layout theming
  • Bricks/modules/Client/Client/App/UIConfigurator.swift - UIConfigurator injection

Customer app (files to create/modify):

  • UI Customisation/Content/ConfigParser.swift - Token resolution
  • UI Customisation/Content/AppTheming.swift - Color, typography, radius, border enums
  • UI Customisation/Content/UIConfigurator.swift - UIConfig implementation
  • UI Customisation/Content/FontBook.swift - Custom font mapping
  • Resources/ConfigTokens/tokens.json - Semantic token routing
  • Resources/ConfigTokens/colors.json - Color palette
  • Resources/ConfigTokens/sizes.json - Sizes and spacing

Adding a New Token

  1. Add the token constant to ThemeTokens.swift in Core with the appropriate path
  2. Add the corresponding entry in tokens.json with $type and $value
  3. If the value references a new color/size, add it to colors.json or sizes.json
  4. Use ConfigParser.colorFromToken() or ConfigParser.sizeFromToken() in the code that needs the value

Modifying a Token Value

To change a color or size without touching Swift code, edit the JSON files directly:

  • To change a specific color shade, update the $value in colors.json
  • To remap a semantic token to a different shade, update the $value reference in tokens.json
  • To change a spacing or radius value, update the $value in sizes.json

On this page