feat: add separator component

Horizontal and vertical separator (divider) with CSS classes, ARIA
role="none", and data-orientation attribute. Includes dark mode support,
unit tests, and demos in all three dev targets.
This commit is contained in:
Florian Schroedl
2026-03-11 18:38:20 +01:00
parent 660723179c
commit e9e0b15e16
7 changed files with 190 additions and 3 deletions

53
src/ui/separator.cljc Normal file
View File

@@ -0,0 +1,53 @@
(ns ui.separator
(:require [clojure.string :as str]))
#?(:squint (defn- kw-name [s] s)
:cljs (defn- kw-name [s] (name s))
:clj (defn- kw-name [s] (name s)))
(defn separator-class-list
"Generate a vector of CSS class strings for a separator.
Orientation: :horizontal (default), :vertical."
[{:keys [orientation]}]
(let [o (or (some-> orientation kw-name) "horizontal")]
["separator" (str "separator-" o)]))
(defn separator-classes
"Generate CSS class string for a separator."
[opts]
(str/join " " (separator-class-list opts)))
(defn separator
"Render a separator element. Works across all targets via reader conditionals.
Props:
:orientation - :horizontal (default), :vertical
:class - additional CSS classes
:attrs - additional HTML attributes map"
[{:keys [orientation class attrs] :as _props}]
#?(:squint
(let [classes (cond-> (separator-classes {:orientation orientation})
class (str " " class))
base-attrs (merge {:class classes
:role "none"
:data-orientation (or (some-> orientation kw-name) "horizontal")}
attrs)]
[:div base-attrs])
:cljs
(let [cls (separator-class-list {:orientation orientation})
classes (cond-> cls class (conj class))
base-attrs (merge {:class classes
:role "none"
:data-orientation (or (some-> orientation kw-name) "horizontal")}
attrs)]
[:div base-attrs])
:clj
(let [classes (cond-> (separator-classes {:orientation orientation})
class (str " " class))
base-attrs (merge {:class classes
:role "none"
:data-orientation (or (some-> orientation kw-name) "horizontal")}
attrs)]
[:div base-attrs])))

27
src/ui/separator.css Normal file
View File

@@ -0,0 +1,27 @@
/* Separator — visually or semantically separates content */
.separator {
flex-shrink: 0;
background: var(--gray-200);
}
.separator-horizontal {
height: 1px;
width: 100%;
}
.separator-vertical {
width: 1px;
align-self: stretch;
}
/* Dark mode: use a lighter border shade */
[data-theme="dark"] .separator {
background: var(--gray-800);
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) .separator {
background: var(--gray-800);
}
}