feat(button): add link variant and polymorphic <a> rendering

When :href is provided, the button renders as <a> instead of <button>,
enabling navigation links with full button styling.

New :link variant renders as a minimal text link (underlined, accent
color, no background/padding) — works as both <button> and <a>.

CSS includes reset styles for a.btn (removes default link decoration)
and preserves underline for a.btn-link.
This commit is contained in:
Florian Schroedl
2026-03-03 17:04:41 +01:00
parent 6a3fee34c4
commit e4ee7b750e
10 changed files with 1118 additions and 43 deletions

View File

@@ -25,40 +25,45 @@
(defn button
"Render a button element. Works across all targets via reader conditionals.
When :href is provided, renders as <a> instead of <button>.
Props:
:variant - :primary, :secondary, :ghost, :danger
:variant - :primary, :secondary, :ghost, :danger, :link
:size - :sm, :md, :lg
:href - URL string; when set, renders as <a> tag
:on-click - click handler (ignored in :clj target)
:disabled - boolean
:class - additional CSS classes (string or vector)
:attrs - additional HTML attributes map"
[{:keys [variant size on-click disabled class attrs] :as _props} & children]
[{:keys [variant size href on-click disabled class attrs] :as _props} & children]
#?(:squint
(let [classes (cond-> (button-classes {:variant variant :size size})
(let [tag (if href :a :button)
classes (cond-> (button-classes {:variant variant :size size})
class (str " " class))
base-attrs (merge {:class classes}
(when disabled {:disabled true})
attrs)]
(into [:button (cond-> base-attrs
on-click (assoc :on-click on-click))]
base-attrs (cond-> (merge {:class classes} attrs)
href (assoc :href href)
disabled (assoc :disabled true))]
(into [tag (cond-> base-attrs
on-click (assoc :on-click on-click))]
children))
:cljs
(let [cls (button-class-list {:variant variant :size size})
(let [tag (if href :a :button)
cls (button-class-list {:variant variant :size size})
classes (cond-> cls
class (conj class))
base-attrs (merge {:class classes}
(when disabled {:disabled true})
attrs)]
(into [:button (cond-> base-attrs
on-click (assoc-in [:on :click] on-click))]
base-attrs (cond-> (merge {:class classes} attrs)
href (assoc :href href)
disabled (assoc :disabled true))]
(into [tag (cond-> base-attrs
on-click (assoc-in [:on :click] on-click))]
children))
:clj
(let [classes (cond-> (button-classes {:variant variant :size size})
(let [tag (if href :a :button)
classes (cond-> (button-classes {:variant variant :size size})
class (str " " class))
base-attrs (merge {:class classes}
(when disabled {:disabled true})
attrs)]
(into [:button base-attrs] children))))
base-attrs (cond-> (merge {:class classes} attrs)
href (assoc :href href)
disabled (assoc :disabled true))]
(into [tag base-attrs] children))))