LiveStyle
View SourceAtomic CSS-in-Elixir for Phoenix LiveView, inspired by Meta's StyleX.
LiveStyle provides a type-safe, composable styling system with:
- Atomic CSS: Each property-value pair becomes a single class
- Deterministic hashing: Same styles always produce same class names
- CSS Variables: Type-safe design tokens with
vars/1 - Constants: Static values inlined at compile time with
consts/1 - Theming: Override variables with
theme/2 - @layer support: CSS cascade layers for predictable specificity
- Last-wins merging: Like StyleX, later styles override earlier ones
Installation
Add live_style to your dependencies in mix.exs:
def deps do
[
{:live_style, "~> 0.12.0"}
]
endSee the Getting Started guide for complete setup instructions.
Quick Example
defmodule MyAppWeb.Components.Button do
use Phoenix.Component
use LiveStyle
class :base,
display: "inline-flex",
align_items: "center",
padding: "8px 16px",
border_radius: "6px"
class :primary,
background_color: "#4f46e5",
color: "white",
":hover": [background_color: "#4338ca"]
def button(assigns) do
~H"""
<button {css([:base, :primary])}>
<%= render_slot(@inner_block) %>
</button>
"""
end
endGuides
- Getting Started - Installation and setup
- Design Tokens - CSS variables, constants, and keyframes
- Styling Components - Defining and composing styles
- Theming - Creating and applying themes
- Advanced Features - Contextual selectors, view transitions, anchor positioning
- Configuration - Shorthand behaviors and options
Key Concepts
Design Tokens
Use vars for values that change (colors, themed tokens) and consts for static values:
defmodule MyAppWeb.Colors do
use LiveStyle
vars primary: "#4f46e5",
gray_900: "#111827"
end
defmodule MyAppWeb.Spacing do
use LiveStyle
consts sm: "8px",
md: "16px"
end
defmodule MyAppWeb.Animations do
use LiveStyle
keyframes :fade_in,
from: [opacity: "0"],
to: [opacity: "1"]
endComponent Styles
Reference tokens with var for colors and const for static values:
defmodule MyAppWeb.Card do
use LiveStyle
class :card,
padding: const({MyAppWeb.Spacing, :md}),
border_radius: "12px",
color: var({MyAppWeb.Colors, :gray_900})
endTheming
Create theme variations (only works with vars):
defmodule MyAppWeb.Semantic do
use LiveStyle
vars text_primary: var({MyAppWeb.Colors, :gray_900}),
fill_page: "#ffffff"
theme :dark,
text_primary: "#ffffff",
fill_page: var({MyAppWeb.Colors, :gray_900})
end
# In your template
<html {css(@theme == :dark && theme({MyAppWeb.Semantic, :dark}))}>Pseudo-classes & Media Queries
class :link,
color: [
default: "blue",
":hover": "darkblue"
]
class :container,
padding: [
default: const({MyAppWeb.Spacing, :md}),
"@media (min-width: 768px)": "24px"
]Why LiveStyle?
vs Tailwind CSS
- Type-safe tokens: Design tokens are Elixir values, not magic strings
- No purging complexity: Only styles you use are generated
- Elixir-native: Conditional logic uses
&&and||, not string concatenation - Scoped theming: Override tokens for subtrees without global CSS
vs Inline Styles
- Pseudo-classes:
:hover,:focus, etc. work naturally - Media queries: Responsive design without JavaScript
- Performance: Atomic classes are cached and deduplicated
- DevTools: Inspect class names instead of inline style blobs
Inspired by StyleX
LiveStyle brings Meta's StyleX philosophy to Phoenix LiveView:
- Atomic CSS for minimal bundle size
- Last-wins merging for predictable composition
- Deterministic class names for caching
- CSS variables for theming
Optional Integrations
def deps do
[
{:live_style, "~> 0.12.0"},
# Automatic vendor prefixing
{:autoprefixer_ex, "~> 0.1.0"},
# Deprecation warnings for CSS properties
{:css_compat_data_ex, "~> 0.1.0"}
]
endLicense
MIT License - see LICENSE for details.