# Jetons
A compile-time design token library for Elixir.
Jetons generates fast, type-safe accessor functions from design token JSON files at compile time. Instead of runtime map lookups, tokens become individual function clauses that use pattern matching for maximum performance.
## Features
- **Compile-time generation** - Tokens become functions, not data
- **Multi-theme support** - Light/dark/custom themes with zero runtime overhead
- **DTCG format** - Follows Design Tokens Community Group specification
- **Fast lookups** - O(1) access via pattern matching, not map traversal
- **Type-safe** - Use with Dialyzer for compile-time guarantees
## Installation
Add `jetons` to your dependencies in `mix.exs`:
```elixir
def deps do
[
{:jetons, "~> 0.1.0"},
{:jason, "~> 1.4"} # Required for JSON parsing
]
end
```
## Quick Start
### Single Theme
```elixir
defmodule MyApp.Tokens do
use Jetons,
main: File.read!("tokens.json") |> Jason.decode!()
end
# Usage
MyApp.Tokens.token("color.primary") # => "#1B66B3"
MyApp.Tokens.token!("color.primary") # => "#1B66B3" (raises if not found)
MyApp.Tokens.list_color() # => [{"color.primary", "#1B66B3"}, ...]
```
### Multiple Themes
```elixir
defmodule MyApp.Tokens do
use Jetons,
light: File.read!("tokens/light.json") |> Jason.decode!(),
dark: File.read!("tokens/dark.json") |> Jason.decode!()
end
# Usage
MyApp.Tokens.token("color.background") # Uses first theme (light)
MyApp.Tokens.token("color.background", :dark) # Explicit theme
MyApp.Tokens.themes() # => [:light, :dark]
```
## Token Format
Jetons expects tokens in DTCG (Design Tokens Community Group) format:
```json
{
"colors": {
"brand": {
"primary": {
"$value": "#1B66B3"
}
},
"grey": {
"500": {
"$value": "#676767"
}
}
},
"spacing": {
"small": {
"$value": "8px"
}
}
}
```
Tokens are flattened to dot-notation paths:
- `"colors.brand.primary"` → `"#1B66B3"`
- `"colors.grey.500"` → `"#676767"`
- `"spacing.small"` → `"8px"`
## API Reference
See [HexDocs](https://hexdocs.pm/jetons) for complete API documentation.
### Core Functions
**Single Theme:**
- `token/1` - Get token value (returns `nil` if not found)
- `token!/1` - Get token value (raises if not found)
- `list_<category>/0` - List all tokens in a category
**Multi-Theme:**
- `token/2` - Get token value with theme parameter
- `token!/2` - Get token value with theme (raises if not found)
- `list_<category>/1` - List tokens in category for specific theme
- `themes/0` - List available themes
### Helper Functions
- `group_by_path/2` or `group_by_path/3` - Group tokens by path depth
- `get_groups/1` or `get_groups/2` - Get top-level groups for a category
- `get_in_group/2` or `get_in_group/3` - Get all tokens in a specific group
## Performance
Jetons generates individual function clauses for each token:
```elixir
def token("color.primary"), do: "#1B66B3"
def token("color.secondary"), do: "#FCE531"
# ... one clause per token
def token(_), do: nil
```
This means:
- **Token access is O(1)** - Direct function call with pattern matching
- **No runtime overhead** - All lookups happen at compile time
- **Memory efficient** - Functions are code, not data
Typical impact: ~50KB binary size for 300 tokens, <2ms compilation time.
## Use Cases
### Multi-Brand Applications
```elixir
defmodule Tokens.BrandA do
use Jetons,
light: File.read!("brands/a/light.json") |> Jason.decode!(),
dark: File.read!("brands/a/dark.json") |> Jason.decode!()
end
defmodule Tokens.BrandB do
use Jetons,
light: File.read!("brands/b/light.json") |> Jason.decode!(),
dark: File.read!("brands/b/dark.json") |> Jason.decode!()
end
# Dynamic brand selection
def get_brand_module(:brand_a), do: Tokens.BrandA
def get_brand_module(:brand_b), do: Tokens.BrandB
```
### Phoenix LiveView
```elixir
def render_button(assigns) do
~H"""
<button style={"background: #{MyTokens.token("button.bg", @theme)};
color: #{MyTokens.token("button.text", @theme)}"}>
<%= @label %>
</button>
"""
end
```
## License
[Add your license here]
## Links
- [DTCG Specification](https://tr.designtokens.org/format/)
- [HexDocs](https://hexdocs.pm/jetons)