<.icon :if={@kind == :info} name="hero-information-circle" class="size-5 shrink-0" />
<.icon :if={@kind == :error} name="hero-exclamation-circle" class="size-5 shrink-0" />
@@ -90,33 +90,71 @@ defmodule MvWeb.CoreComponents do
@doc """
Renders a button with navigation support.
+ ## Variants (Design Guidelines §5.2)
+ - primary (main CTA)
+ - secondary (supporting)
+ - neutral (cancel/back)
+ - ghost (low emphasis; table/toolbars)
+ - outline (alternative CTA)
+ - danger (destructive)
+ - link (inline; rare)
+ - icon (icon-only)
+
+ ## Sizes
+ - sm, md (default), lg
+
## Examples
<.button>Send!
<.button phx-click="go" variant="primary">Send!
- <.button navigate={~p"/"}>Home
+ <.button navigate={~p"/"} variant="secondary">Home
+ <.button variant="ghost" size="sm">Edit
<.button disabled={true}>Disabled
"""
attr :rest, :global, include: ~w(href navigate patch method data-testid)
- attr :variant, :string, values: ~w(primary)
+
+ attr :variant, :string,
+ values: ~w(primary secondary neutral ghost outline danger link icon),
+ default: "primary"
+
+ attr :size, :string, values: ~w(sm md lg), default: "md"
attr :disabled, :boolean, default: false, doc: "Whether the button is disabled"
slot :inner_block, required: true
def button(assigns) do
rest = assigns.rest
- variants = %{"primary" => "btn-primary", nil => "btn-primary btn-soft"}
- assigns = assign(assigns, :class, Map.fetch!(variants, assigns[:variant]))
+ variant = assigns[:variant] || "primary"
+ size = assigns[:size] || "md"
+
+ variant_classes = %{
+ "primary" => "btn-primary",
+ "secondary" => "btn-secondary",
+ "neutral" => "btn-neutral",
+ "ghost" => "btn-ghost",
+ "outline" => "btn-outline",
+ "danger" => "btn-error",
+ "link" => "btn-link",
+ "icon" => "btn-ghost btn-square"
+ }
+
+ size_classes = %{
+ "sm" => "btn-sm",
+ "md" => "",
+ "lg" => "btn-lg"
+ }
+
+ base_class = Map.fetch!(variant_classes, variant)
+ size_class = size_classes[size]
+ btn_class = [base_class, size_class] |> Enum.reject(&(&1 == "")) |> Enum.join(" ")
+
+ assigns = assign(assigns, :btn_class, btn_class)
if rest[:href] || rest[:navigate] || rest[:patch] do
- # For links, we can't use disabled attribute, so we use btn-disabled class
- # DaisyUI's btn-disabled provides the same styling as :disabled on buttons
link_class =
if assigns[:disabled],
- do: ["btn", assigns.class, "btn-disabled"],
- else: ["btn", assigns.class]
+ do: ["btn", btn_class, "btn-disabled"],
+ else: ["btn", btn_class]
- # Prevent interaction when disabled
- # Remove navigation attributes to prevent "Open in new tab", "Copy link" etc.
link_attrs =
if assigns[:disabled] do
rest
@@ -138,13 +176,49 @@ defmodule MvWeb.CoreComponents do
"""
else
~H"""
-