LiveStyle (LiveStyle v0.12.0)
View SourceLiveStyle - Compile-time CSS-in-Elixir for Phoenix LiveView.
All style definitions compile away to string constants. At runtime, only class name strings exist - no function calls or manifest lookups.
Basic Usage
defmodule MyAppWeb.Button do
use Phoenix.Component
use LiveStyle
# Define CSS variables
vars primary: "#3b82f6",
white: "#ffffff"
# Define a theme that overrides variables
theme :dark,
primary: "#60a5fa",
white: "#1f2937"
# Define keyframes
keyframes :spin,
from: [transform: "rotate(0deg)"],
to: [transform: "rotate(360deg)"]
# Define classes
class :base,
display: "inline-flex",
padding: "0.5rem 1rem"
class :styled,
background_color: var(:primary),
color: var(:white)
def render(assigns) do
~H"""
<button {css([:base, :styled])}>
<%= render_slot(@inner_block) %>
</button>
"""
end
endReference Syntax
Cross-module references:
var({Module, :name})- Reference a CSS variableconst({Module, :name})- Reference a compile-time constantkeyframes({Module, :name})- Reference a keyframes animationtheme_class({Module, :name})- Reference a theme classposition_try({Module, :name})- Reference a position-try ruleview_transition({Module, :name})- Reference a view transition
Local references (within the same module):
var(:name)keyframes(:name)theme_class(:name)
Public API Functions
LiveStyle.default_marker/0- Get the default marker class for contextual selectorsLiveStyle.marker/1- Get a custom marker class
See the README for comprehensive documentation and examples.
Summary
Functions
Defines a style class with CSS declarations.
References a constant, returning its raw value.
Defines compile-time constants (no CSS output).
Returns CSS attributes for spreading in HEEx templates.
Returns CSS attributes with additional inline styles merged in.
Returns the default marker class name for use with LiveStyle.When selectors.
Creates fallback values for CSS properties (StyleX firstThatWorks equivalent).
Includes styles from another class.
Runs LiveStyle CSS generation, installing dependencies if needed.
Defines a keyframes animation (2-arg form) or references one (1-arg form).
Returns a marker class name for use with LiveStyle.When selectors.
Defines or references a @position-try rule for anchor positioning.
Runs LiveStyle CSS generation.
Defines a theme (variable overrides).
References a theme, returning the class name.
References a CSS variable, returning var(--vhash).
Defines CSS custom properties (variables).
Defines a view transition.
References a view transition, returning the view-transition-class value.
Functions
Defines a style class with CSS declarations.
Static classes
class :button,
display: "flex",
padding: "8px 16px"With variable references
class :themed,
color: var({MyAppWeb.Tokens, :white})Conditional styles (pseudo-classes, media queries)
class :interactive,
color: [
default: "blue",
":hover": "darkblue",
"@media (prefers-color-scheme: dark)": "lightblue"
]Conditional syntax (StyleX-style)
LiveStyle follows modern StyleX conditional syntax: conditions live inside each property's value (keyword list), rather than using top-level at-rule keys.
class :responsive_card,
padding: [
default: "1rem",
"@container (min-width: 400px)": "2rem",
"@media (min-width: 768px)": "3rem"
]Dynamic classes (StyleX-style with CSS variables)
Dynamic classes use a function that declares which properties can be set at runtime.
The CSS is generated with var(--x-property) references, and at runtime only
the CSS variable values are set via inline style.
# Single parameter
class :dynamic_opacity, fn opacity -> [opacity: opacity] end
# Multiple parameters
class :dynamic_size, fn width, height -> [width: width, height: height] endUsage:
<div {css([:base, {:dynamic_opacity, "0.5"}])}>
<div {css([:base, {:dynamic_size, ["100px", "200px"]}])}>
References a constant, returning its raw value.
Local reference
const(:breakpoint_lg)Cross-module reference
const({MyAppWeb.Tokens, :breakpoint_lg})
Defines compile-time constants (no CSS output).
Examples
consts breakpoint_sm: "@media (max-width: 640px)",
breakpoint_lg: "@media (min-width: 1025px)",
z_modal: "50",
z_tooltip: "100"
Returns CSS attributes for spreading in HEEx templates.
Returns %LiveStyle.Attrs{} for use with the spread syntax {css(...)}
in templates. This handles both static and dynamic styles that set CSS
variables via inline style.
Examples
# Single ref
<div {css(:button)}>
# List of refs with conditionals
<div {css([:base, @active && :active])}>
# Dynamic styles
<div {css([{:dynamic_color, @color}])}>
# With additional inline styles
<div {css([:card], style: [view_transition_name: "card-1"])}>
# With view transitions
<div {css([:card], style: [
view_transition_class: view_transition_class(:card),
view_transition_name: "card-#{@id}"
])}>
Returns CSS attributes with additional inline styles merged in.
The second argument is a keyword list with a :style key containing
additional CSS properties to merge into the inline style.
Options
:style- A keyword list of CSS properties to merge. Property names should be atoms (snake_case).
Examples
# With view transition styles
<div {css([:card], style: [
view_transition_class: view_transition_class(:card),
view_transition_name: "card-#{@id}"
])}>
# With arbitrary inline styles
<div {css([:base], style: [opacity: "0.5", transform: "scale(1.1)"])}>
Returns the default marker class name for use with LiveStyle.When selectors.
Example
<div class={default_marker()}>
<div {css(:card)}>Hover parent to move me</div>
</div>
Creates fallback values for CSS properties (StyleX firstThatWorks equivalent).
This function handles two cases:
Regular fallbacks - Multiple declarations for browser compatibility:
class :sticky, position: fallback(["sticky", "fixed"]) # Generates: .class{position:fixed;position:sticky}CSS variable fallbacks - Nested var() with fallback values:
class :themed, background_color: fallback(["var(--bg-color)", "#808080"]) # Generates: .class{background-color:var(--bg-color, #808080)}
Values are tried in order - first value has highest priority.
For CSS variables, they are nested: var(--a, var(--b, fallback)).
Examples
# Browser fallbacks (position: sticky not supported everywhere)
class :sticky,
position: fallback(["sticky", "fixed"])
# CSS variable with fallback
class :themed,
color: fallback(["var(--theme-color)", "blue"])
# Multiple CSS variables with final fallback
class :multi_theme,
color: fallback(["var(--primary)", "var(--fallback)", "black"])
Includes styles from another class.
Used inside class/2 definitions for style composition. Included styles
are merged with the current class using last-wins semantics - properties
defined after include() override properties from included classes.
Examples
# Include a local class
class :primary, [
include(:base),
background_color: "blue"
]
# Include from another module
class :themed, [
include({OtherModule, :base}),
color: "white"
]
# Multiple includes
class :fancy, [
include(:base),
include(:rounded),
include({SharedStyles, :animated}),
border_radius: "12px"
]
@spec install_and_run(atom(), [String.t()]) :: non_neg_integer()
Runs LiveStyle CSS generation, installing dependencies if needed.
This follows the same pattern as Tailwind.install_and_run/2 and
Esbuild.install_and_run/2, making it suitable for use as a Phoenix
endpoint watcher.
Setup
Add to your config/dev.exs:
config :my_app, MyAppWeb.Endpoint,
watchers: [
esbuild: {Esbuild, :install_and_run, [:my_app, ~w(--sourcemap=inline --watch)]},
live_style: {LiveStyle, :install_and_run, [:default, ~w(--watch)]}
]Examples
LiveStyle.install_and_run(:default, ~w(--watch))
Defines a keyframes animation (2-arg form) or references one (1-arg form).
Definition (2 args)
keyframes :spin,
from: [transform: "rotate(0deg)"],
to: [transform: "rotate(360deg)"]
keyframes :fade_in,
"0%": [opacity: "0"],
"100%": [opacity: "1"]Local reference (1 arg)
keyframes(:spin)Cross-module reference
keyframes({MyAppWeb.Tokens, :spin})
Returns a marker class name for use with LiveStyle.When selectors.
Custom markers allow you to have multiple independent sets of contextual selectors in the same component tree.
Examples
# Local marker (same module)
marker(:row)
# Cross-module marker
marker({OtherModule, :row})Usage
<tr class={marker(:row)}>
<td {css(:cell)}>...</td>
</tr>
Defines or references a @position-try rule for anchor positioning.
Definition (2 args)
position_try :bottom_fallback,
top: "anchor(bottom)",
left: "anchor(left)"Local reference (1 arg atom)
position_try(:bottom_fallback)Cross-module reference (1 arg tuple)
position_try({MyAppWeb.Tokens, :bottom_fallback})
@spec run(atom(), [String.t()]) :: non_neg_integer()
Runs LiveStyle CSS generation.
This is typically called by the mix task or the watcher. Returns 0 on success.
Options
--watch- Watch for manifest changes and regenerate CSS automatically
Examples
LiveStyle.run(:default, [])
LiveStyle.run(:default, ~w(--watch))
Defines a theme (variable overrides).
Similar to StyleX's createTheme, this creates a class that overrides
CSS variables defined with vars.
Examples
# First define your variables
vars white: "#ffffff",
primary: "#3b82f6"
# Then create a theme that overrides those variables
theme :dark,
white: "#000000",
primary: "#8ab4f8"
References a theme, returning the class name.
Local reference
theme_class(:dark)Cross-module reference
theme_class({MyAppWeb.Tokens, :dark})
References a CSS variable, returning var(--vhash).
When used as a value, returns var(--vhash) for CSS variable references.
When used as a map key in keyframes, the var() wrapper is automatically
stripped to produce valid CSS (matching StyleX behavior).
Local reference (same module)
var(:white)Cross-module reference
var({MyAppWeb.Tokens, :white})Using in keyframes (animating typed variables)
keyframes :rotate,
from: [{var({Tokens, :angle}), "0deg"}],
to: [{var({Tokens, :angle}), "360deg"}]
Defines CSS custom properties (variables).
Examples
vars white: "#ffffff",
primary: "#3b82f6",
spacing_sm: "0.5rem",
spacing_lg: "2rem"For typed variables that can be animated, use LiveStyle.Types:
import LiveStyle.Types
vars angle: angle("0deg"),
hue: percentage("0%")
Defines a view transition.
Examples
view_transition :card_transition,
old: [animation_name: keyframes(:fade_out), animation_duration: "250ms"],
new: [animation_name: keyframes(:fade_in), animation_duration: "250ms"]
References a view transition, returning the view-transition-class value.
Returns the hashed class name that should be used with the CSS view-transition-class
property. You control when and where to apply view-transition-name via inline styles.
Local reference
view_transition_class(:card)
# => "x9fx6z8"Cross-module reference
view_transition_class({Tokens, :card})
# => "x9fx6z8"Usage in templates
Use with inline styles to control view transitions:
<div style={"view-transition-class: #{view_transition_class(:card)}; view-transition-name: card-#{@id}"}>Or use css/2 with the style option for merging with other styles:
<div {css([:card_styles], style: [view_transition_class: view_transition_class(:card), view_transition_name: "card-#{@id}"])}>