ThemeEx
An Elixir package that implements data structures for the Theme UI theme specification and provides utilities for parsing, validation, and CSS variable generation.
Features
- Complete Theme UI Implementation: Full support for Theme UI specification including colors, fonts, typography scales, and design tokens
- Type-Safe Data Structures: Elixir structs with proper type specifications for all theme components
- JSON Parsing & Validation: Parse theme JSON with comprehensive error handling and validation
- Built-in JSON Schema Generation: Generate JSON Schema for theme validation without external dependencies
- CSS Variables Generation: Convert themes to CSS custom properties with consistent naming conventions
- Color Modes Support: Handle multiple color modes (light, dark, custom) with nested color definitions
- Responsive Design Tokens: Support for breakpoints, typography scales, spacing, and size systems
- Comprehensive Validation: Custom validation logic that ensures theme integrity and type safety
Installation
Add theme_ex to your list of dependencies in mix.exs:
def deps do
[
{:theme_ex, "~> 0.1.0"}
]
endData Structures
ThemeEx implements the complete Theme UI specification with the following core structures:
ThemeEx.Theme- Main theme containerThemeEx.Colors- Color palette with semantic colors and modesThemeEx.Fonts- Font family definitionsThemeEx.FontWeights- Font weight specificationsThemeEx.LineHeights- Line height definitions
Usage
Creating Themes
# Create a complete theme
theme = %ThemeEx.Theme{
colors: %ThemeEx.Colors{
text: "#000000",
background: "#ffffff",
primary: "#0066cc",
secondary: "#ff6600",
modes: %{
"dark" => %{
"text" => "#ffffff",
"background" => "#000000",
"primary" => "#66ccff"
}
}
},
fonts: %ThemeEx.Fonts{
body: "system-ui, -apple-system, sans-serif",
heading: "Georgia, serif",
monospace: "Menlo, Monaco, monospace"
},
fontWeights: %ThemeEx.FontWeights{
body: 400,
heading: 700,
bold: 600
},
fontSizes: [12, 14, 16, 20, 24, 32, 48, 64, 96],
space: [0, 4, 8, 16, 32, 64, 128, 256],
breakpoints: ["40em", "52em", "64em"]
}JSON Parsing
# Parse from JSON string
json_theme = """
{
"colors": {
"text": "#000000",
"background": "#ffffff",
"primary": "#0066cc",
"modes": {
"dark": {
"text": "#ffffff",
"background": "#000000"
}
}
},
"fonts": {
"body": ["system-ui", "sans-serif"],
"heading": "Georgia, serif"
},
"fontSizes": [12, 14, 16, 20, 24, 32]
}
"""
{:ok, theme} = ThemeEx.from_json(json_theme)
# Or from a map
theme_map = %{
"colors" => %{"primary" => "#0066cc"},
"fontSizes" => [14, 16, 18, 24]
}
{:ok, theme} = ThemeEx.from_map(theme_map)CSS Variables Generation
theme = %ThemeEx.Theme{
colors: %ThemeEx.Colors{
primary: "#0066cc",
secondary: "#ff6600"
},
fonts: %ThemeEx.Fonts{
body: "system-ui, sans-serif"
},
fontSizes: [12, 14, 16, 20]
}
css = ThemeEx.to_css_variables(theme)
# Output:
# :root {
# --theme-colors-primary: #0066cc;
# --theme-colors-secondary: #ff6600;
# --theme-fonts-body: system-ui, sans-serif;
# --theme-fontSizes-0: 12;
# --theme-fontSizes-1: 14;
# --theme-fontSizes-2: 16;
# --theme-fontSizes-3: 20;
# }Theme Validation
# Validate theme structure and types
case ThemeEx.validate(theme) do
{:ok, validated_theme} ->
IO.puts("Theme is valid!")
validated_theme
{:error, errors} ->
IO.puts("Validation failed: #{inspect(errors)}")
endJSON Schema Generation
# Generate JSON Schema for external validation
schema = ThemeEx.json_schema()
# Returns a complete JSON Schema following draft/2020-12 specification
%{
"$schema" => "https://json-schema.org/draft/2020-12/schema",
"title" => "Theme UI Theme",
"type" => "object",
"properties" => %{
"colors" => %{...},
"fonts" => %{...},
# ... complete schema definition
}
}Advanced Features
Font Arrays and Stacks
# Fonts can be defined as strings or arrays
fonts = %ThemeEx.Fonts{
body: ["Helvetica Neue", "Arial", "sans-serif"],
heading: "Georgia, serif"
}Color Arrays and Scales
# Colors support both single values and scales
colors = %ThemeEx.Colors{
primary: ["#e3f2fd", "#bbdefb", "#90caf9", "#64b5f6", "#42a5f5"],
text: "#000000"
}Responsive Design Tokens
theme = %ThemeEx.Theme{
space: [0, 4, 8, 16, 32, 64, 128], # Spacing scale
sizes: [16, 32, 64, 128, 256, 512, 768], # Size scale
radii: [0, 2, 4, 8, 16], # Border radius scale
shadows: [ # Box shadow definitions
"none",
"0 1px 3px rgba(0,0,0,0.12)",
"0 4px 6px rgba(0,0,0,0.16)"
],
breakpoints: ["40em", "52em", "64em"] # Media query breakpoints
}API Reference
Core Functions
ThemeEx.from_json/1- Parse theme from JSON stringThemeEx.from_map/1- Convert map to theme structThemeEx.to_css_variables/1- Generate CSS custom propertiesThemeEx.json_schema/0- Generate JSON Schema for validationThemeEx.validate/1- Validate theme structure and types
Data Structures
All structs include proper type specifications and support for both single values and arrays where appropriate by the Theme UI specification.
Contributing
- Fork the repository
-
Create your feature branch (
git checkout -b feature/amazing-feature) -
Run tests (
mix test) -
Commit your changes (
git commit -m 'Add amazing feature') -
Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.