Getting Started
View SourceThis guide walks you through setting up LiveStyle in a Phoenix application.
Installation
1. Add Dependencies
Add live_style to your dependencies in mix.exs:
def deps do
[
{:live_style, "~> 0.12.0"},
# Optional: for automatic vendor prefixing
{:autoprefixer_ex, "~> 0.1.0"},
# Optional: for deprecation warnings
{:css_compat_data_ex, "~> 0.1.0"}
]
end2. Add the LiveStyle Compiler
In mix.exs, add :live_style to your compilers:
def project do
[
# ...
compilers: [:phoenix_live_view] ++ Mix.compilers() ++ [:live_style]
]
end3. Configure Esbuild for CSS
Phoenix uses esbuild for JavaScript. Add a separate profile for CSS bundling in config/config.exs:
config :esbuild,
version: "0.25.4",
my_app: [
args:
~w(js/app.js --bundle --target=es2022 --outdir=../priv/static/assets/js --external:/fonts/* --external:/images/*),
cd: Path.expand("../assets", __DIR__),
env: %{"NODE_PATH" => Path.expand("../deps", __DIR__)}
],
css: [
args: ~w(css/app.css --bundle --outdir=../priv/static/assets/css),
cd: Path.expand("../assets", __DIR__)
]4. Configure LiveStyle
Add to config/config.exs:
# Configure LiveStyle
config :live_style,
# Optional: automatic vendor prefixing
prefix_css: &AutoprefixerEx.prefix_css/2,
# Optional: deprecation warnings
deprecated?: &CSSCompatDataEx.deprecated?/1,
default: [
output: "priv/static/assets/css/live.css",
cd: Path.expand("..", __DIR__)
]
# Optional: configure browser targets for autoprefixing
config :autoprefixer_ex,
browserslist: ["defaults"]5. Add LiveStyle Watcher for Hot Reload
Add the LiveStyle watcher to your config/dev.exs. This follows the same pattern as esbuild and Tailwind:
config :my_app, MyAppWeb.Endpoint,
watchers: [
esbuild: {Esbuild, :install_and_run, [:my_app, ~w(--sourcemap=inline --watch)]},
esbuild_css: {Esbuild, :install_and_run, [:css, ~w(--watch)]},
live_style: {LiveStyle, :install_and_run, [:default, ~w(--watch)]}
]The watcher monitors the manifest file for changes. When you save a file:
- Phoenix triggers recompilation
- The
@before_compilehook updates the manifest - The watcher detects the change and regenerates CSS
- Phoenix live_reload refreshes your browser
6. Update Build Aliases
In mix.exs, update your aliases:
defp aliases do
[
setup: ["deps.get", "assets.setup", "assets.build"],
"assets.setup": ["esbuild.install --if-missing"],
"assets.build": ["compile", "esbuild my_app", "esbuild css", "live_style default"],
"assets.deploy": [
"live_style default",
"esbuild my_app --minify",
"esbuild css --minify",
"phx.digest"
]
]
end7. Include CSS in Layout
Add the stylesheets to your root layout (lib/my_app_web/components/layouts/root.html.heex):
<link phx-track-static rel="stylesheet" href={~p"/assets/css/app.css"} />
<link phx-track-static rel="stylesheet" href={~p"/assets/css/live.css"} />
<script defer phx-track-static type="text/javascript" src={~p"/assets/js/app.js"}></script>8. Add CSS Reset (Optional but Recommended)
Create a base CSS reset in assets/css/app.css:
/*
* CSS Reset and base styles.
* Wrapped in @layer reset so LiveStyle rules take precedence.
*/
@layer reset {
*,
*::before,
*::after {
box-sizing: border-box;
}
* {
margin: 0;
padding: 0;
}
body {
min-height: 100vh;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
img, picture, video, canvas, svg {
display: block;
max-width: 100%;
}
input, button, textarea, select {
font: inherit;
}
/* Phoenix LiveView compatibility */
[data-phx-main],
[data-phx-session] {
display: contents;
}
}9. Test Setup (If Needed)
If your tests define LiveStyle modules (e.g., test fixtures with use LiveStyle),
add the test setup task to your aliases:
defp aliases do
[
# ...
test: ["live_style.setup_tests", "test"]
]
endQuick Start
Here's a complete example of a styled button component:
defmodule MyAppWeb.Components.Button do
use Phoenix.Component
use LiveStyle
class :base,
display: "flex",
align_items: "center",
padding: "8px 16px",
border_radius: "8px",
border: "none",
cursor: "pointer"
class :primary,
background_color: "#4f46e5",
color: "white"
class :secondary,
background_color: "#e5e7eb",
color: "#1f2937"
def button(assigns) do
assigns = assign_new(assigns, :variant, fn -> :primary end)
~H"""
<button {css([:base, @variant])}>
<%= render_slot(@inner_block) %>
</button>
"""
end
endUse it in your templates:
<.button>Primary Button</.button>
<.button variant={:secondary}>Secondary Button</.button>Module Organization
LiveStyle uses a module-as-namespace pattern. Each module defines its own tokens or styles.
Design Tokens
For centralized design tokens, create separate modules for each token type. Use vars for values that might be themed (colors) and consts for static values:
defmodule MyAppWeb.Colors do
use LiveStyle
vars [
white: "#ffffff",
black: "#000000",
gray_900: "#111827",
indigo_600: "#4f46e5"
]
end
defmodule MyAppWeb.Spacing do
use LiveStyle
consts [
sm: "8px",
md: "16px",
lg: "24px"
]
end
defmodule MyAppWeb.Radius do
use LiveStyle
consts [
sm: "4px",
md: "8px",
lg: "12px"
]
end
defmodule MyAppWeb.Animations do
use LiveStyle
keyframes :spin,
from: [transform: "rotate(0deg)"],
to: [transform: "rotate(360deg)"]
endComponent Styles
For component-specific styles, use var for colors/themed values and const for static values:
defmodule MyAppWeb.Button do
use Phoenix.Component
use LiveStyle
class :base,
display: "inline-flex",
padding: const({MyAppWeb.Spacing, :md}),
border_radius: const({MyAppWeb.Radius, :md})
class :primary,
background_color: var({MyAppWeb.Colors, :indigo_600}),
color: var({MyAppWeb.Colors, :white})
def button(assigns) do
~H"""
<button {css([:base, :primary])}>
<%= render_slot(@inner_block) %>
</button>
"""
end
endNext Steps
- Design Tokens - Learn about CSS variables, constants, and keyframes
- Styling Components - Deep dive into
classand composition - Theming - Create and apply themes
- Configuration - Configure shorthand behaviors and other options