Class Variance Authority for Elixir - easily construct classes with variant definitions.
Building out core HEEx function components like button
, heading
, link
in general requires
some way to distinguish between different appearances of the component. This is generally achieved
by concatenating class strings or by extracting them into separate functions. In addition, maintaining proper definitions of supported attr
:value
options is required.
ex_cva
aims to make this process convenient by providing a clean and maintainable way to define component variants.
Add the cva
dependency to your mix.exs
file.
def deps do
[
{:cva, "~> 0.2"}
]
end
Configure a few variants with defaults and optionally add compound variants. CVA will take care of creating the proper class names.
One more goodie: it even creates compile-time checks for your variants to make sure all required
variants are applied and contain correct values (thanks to Phoenix.Component.attr/3
).
defmodule MyWeb.Components do
use Phoenix.Component
use CVA.Component
variant :intent, [
primary: "bg-cyan-600",
secondary: "bg-zinc-700",
destructive: "bg-red-500"
],
default: :secondary
variant :size, [
xs: "rounded px-2.5 py-1.5 text-xs",
sm: "rounded-md px-3 py-2 text-sm",
md: "rounded-md px-4 py-2 text-sm",
lg: "rounded-md px-4 py-2 text-base",
xl: "rounded-md px-6 py-3 text-base"
],
default: :md
compound_variant "uppercase", intent: :primary, size: :xl
attr :rest, :global
slot :inner_block
def button(assigns) do
~H"""
<button class={@cva_class} {@rest}><%= render_slot(@inner_block) %></button>
"""
end
end
defmodule MyWeb.SomeLive do
import MyWeb.Components
def render(assigns) do
~H"""
<.button intent="primary">Click me</.button>
"""
end
end
Even though ex_cva
shines when working with function components, you can still use the raw cva
function to generate classes.
defmodule MyCVA do
import CVA
def button(props) do
config = [
variants: [
intent: [
primary: "bg-cyan-600",
secondary: "bg-zinc-700",
destructive: "bg-red-500"
],
size: [
xs: "rounded px-2.5 py-1.5 text-xs",
sm: "rounded-md px-3 py-2 text-sm",
md: "rounded-md px-4 py-2 text-sm",
lg: "rounded-md px-4 py-2 text-base",
xl: "rounded-md px-6 py-3 text-base"
]
]
]
cva(config, props)
end
end
button(intent: :primary, size: :md) # -> "bg-cyan-600 rounded-md px-4 py-2 text-sm"
- cva (Joe Bell) Thank you for providing the JavaScript implementation. It inspired me to port this over to Elixir. I hope you don't mind that I hijacked your logo. If you are not feeling well about that, feel free to shoot me a message.
Contributions are very welcome. To get it up on your local machine, just check out the repo and run
mix deps.get
mix test