This commit is contained in:
Florian Schroedl
2026-03-03 10:38:02 +01:00
commit 42ddb56d65
25 changed files with 3912 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
(ns dev.hiccup
(:require [org.httpkit.server :as http]
[hiccup2.core :as h]
[ui.button :as button]))
(def variants [:primary :secondary :ghost :danger])
(def sizes [:sm :md :lg])
(defn button-grid []
[:div {:style "display: grid; grid-template-columns: repeat(4, auto); gap: 1rem; align-items: center;"}
;; Header row
[:div]
(for [size sizes]
[:div {:style "font-weight: 600; text-align: center; color: var(--fg-1); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em;"}
(name size)])
;; Variant rows
(for [variant variants]
(list
[:div {:style "font-weight: 600; color: var(--fg-1); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em;"}
(name variant)]
(for [size sizes]
[:div {:style "text-align: center;"}
(button/button {:variant variant :size size}
(str (name variant) " " (name size)))])))])
(defn disabled-row []
[:div {:style "display: flex; gap: 0.75rem; flex-wrap: wrap;"}
(for [variant variants]
(button/button {:variant variant :disabled true}
(str (name variant) " disabled")))])
(defn page []
(str
(h/html
[:html
[:head
[:meta {:charset "utf-8"}]
[:meta {:name "viewport" :content "width=device-width, initial-scale=1"}]
[:link {:rel "stylesheet" :href "/theme.css"}]
[:style "body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; padding: 2rem; background: var(--bg-0); color: var(--fg-0); margin: 0; transition: background-color 0.2s, color 0.2s; }"]]
[:body
[:div {:style "max-width: 800px; margin: 0 auto;"}
[:div {:style "display: flex; justify-content: space-between; align-items: center; margin-bottom: 2rem;"}
[:h2 {:style "margin: 0; color: var(--fg-0);"} "Hiccup (Backend)"]
[:button {:onclick "document.documentElement.dataset.theme = document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark'"
:style "padding: 0.5rem 1rem; cursor: pointer; border-radius: var(--radius-md); border: var(--border-0); background: var(--bg-1); color: var(--fg-0);"}
"Toggle Dark Mode"]]
[:h3 {:style "color: var(--fg-1); margin-bottom: 1rem;"} "Button Grid"]
(button-grid)
[:h3 {:style "color: var(--fg-1); margin: 2rem 0 1rem;"} "Disabled States"]
(disabled-row)]]])))
(defn handler [{:keys [uri]}]
(case uri
"/" {:status 200
:headers {"Content-Type" "text/html; charset=utf-8"}
:body (page)}
"/theme.css" {:status 200
:headers {"Content-Type" "text/css"}
:body (slurp "dist/theme.css")}
{:status 404
:headers {"Content-Type" "text/plain"}
:body "Not found"}))
(defn start! [{:keys [port] :or {port 3003}}]
(println (str "Hiccup server running at http://localhost:" port))
(http/run-server handler {:port port}))

84
dev/index.html Normal file
View File

@@ -0,0 +1,84 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>UI Framework — Test Page</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: #f0f0f0;
color: #1a1a1a;
}
.tabs {
display: flex;
gap: 0;
background: #fff;
border-bottom: 2px solid #e0e0e0;
padding: 0 1rem;
}
.tab {
padding: 0.75rem 1.5rem;
cursor: pointer;
border: none;
background: none;
font-size: 0.875rem;
font-weight: 500;
color: #666;
border-bottom: 2px solid transparent;
margin-bottom: -2px;
transition: color 0.15s, border-color 0.15s;
}
.tab:hover { color: #1a1a1a; }
.tab.active {
color: #2563eb;
border-bottom-color: #2563eb;
}
.tab.disabled {
color: #ccc;
cursor: not-allowed;
}
.frame-container {
height: calc(100vh - 45px);
}
iframe {
width: 100%;
height: 100%;
border: none;
}
.placeholder {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: #999;
font-size: 1.1rem;
}
</style>
</head>
<body>
<div class="tabs">
<button class="tab active" data-target="hiccup" data-url="http://localhost:3003">Hiccup</button>
<button class="tab" data-target="replicant" data-url="http://localhost:3001">Replicant</button>
<button class="tab" data-target="squint" data-url="http://localhost:3002">Squint</button>
</div>
<div class="frame-container" id="frame-container">
<iframe id="target-frame" src="http://localhost:3003"></iframe>
</div>
<script>
const tabs = document.querySelectorAll('.tab');
const frame = document.getElementById('target-frame');
tabs.forEach(tab => {
tab.addEventListener('click', () => {
if (tab.classList.contains('disabled')) return;
tabs.forEach(t => t.classList.remove('active'));
tab.classList.add('active');
frame.src = tab.dataset.url;
});
});
</script>
</body>
</html>

3
dev/replicant/deps.edn Normal file
View File

@@ -0,0 +1,3 @@
{:paths ["src" "../../src"]
:deps {no.cjohansen/replicant {:mvn/version "2025.12.1"}
thheller/shadow-cljs {:mvn/version "2.28.23"}}}

1565
dev/replicant/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
{
"name": "ui-framework-dev-replicant",
"private": true,
"devDependencies": {
"shadow-cljs": "^2.28.0"
}
}

View File

@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/theme.css" />
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
padding: 2rem;
background: var(--bg-0);
color: var(--fg-0);
margin: 0;
transition: background-color 0.2s, color 0.2s;
}
</style>
</head>
<body>
<div id="app"></div>
<script src="/js/main.js"></script>
</body>
</html>

View File

@@ -0,0 +1,9 @@
{:deps true
:dev-http {3001 "public"}
:builds
{:app
{:target :browser
:output-dir "public/js"
:asset-path "/js"
:modules {:main {:init-fn dev.replicant/init!}}
:devtools {:after-load dev.replicant/reload!}}}}

View File

@@ -0,0 +1,59 @@
(ns dev.replicant
(:require [replicant.dom :as d]
[ui.button :as button]))
(def variants [:primary :secondary :ghost :danger])
(def sizes [:sm :md :lg])
(defn button-grid []
[:div {:style {:display "grid"
:grid-template-columns "repeat(4, auto)"
:gap "1rem"
:align-items "center"}}
[:div]
(for [size sizes]
[:div {:style {:font-weight "600" :text-align "center" :color "var(--fg-1)"
:font-size "0.75rem" :text-transform "uppercase" :letter-spacing "0.05em"}}
(name size)])
(for [variant variants]
(list
[:div {:style {:font-weight "600" :color "var(--fg-1)"
:font-size "0.75rem" :text-transform "uppercase" :letter-spacing "0.05em"}}
(name variant)]
(for [size sizes]
[:div {:style {:text-align "center"}}
(button/button {:variant variant :size size
:on-click (fn [_] (js/console.log (str "Clicked: " (name variant) " " (name size))))}
(str (name variant) " " (name size)))])))])
(defn disabled-row []
[:div {:style {:display "flex" :gap "0.75rem" :flex-wrap "wrap"}}
(for [variant variants]
(button/button {:variant variant :disabled true}
(str (name variant) " disabled")))])
(defn toggle-theme! [_e]
(let [el (.-documentElement js/document)
current (.. el -dataset -theme)]
(set! (.. el -dataset -theme)
(if (= current "dark") "light" "dark"))))
(defn app []
[:div {:style {:max-width "800px" :margin "0 auto"}}
[:div {:style {:display "flex" :justify-content "space-between" :align-items "center" :margin-bottom "2rem"}}
[:h2 {:style {:margin "0" :color "var(--fg-0)"}} "Replicant (CLJS)"]
[:button {:on {:click toggle-theme!}
:style {:padding "0.5rem 1rem" :cursor "pointer" :border-radius "var(--radius-md)"
:border "var(--border-0)" :background "var(--bg-1)" :color "var(--fg-0)"}}
"Toggle Dark Mode"]]
[:h3 {:style {:color "var(--fg-1)" :margin-bottom "1rem"}} "Button Grid"]
(button-grid)
[:h3 {:style {:color "var(--fg-1)" :margin "2rem 0 1rem"}} "Disabled States"]
(disabled-row)])
(defn ^:export init! []
(d/set-dispatch! (fn [_ _]))
(d/render (.getElementById js/document "app") (app)))
(defn ^:export reload! []
(d/render (.getElementById js/document "app") (app)))

22
dev/squint/index.html Normal file
View File

@@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="/theme.css" />
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
padding: 2rem;
background: var(--bg-0);
color: var(--fg-0);
margin: 0;
transition: background-color 0.2s, color 0.2s;
}
</style>
</head>
<body>
<div id="app"></div>
<script type="module" src=".compiled/dev/squint.mjs"></script>
</body>
</html>

1160
dev/squint/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

16
dev/squint/package.json Normal file
View File

@@ -0,0 +1,16 @@
{
"name": "ui-framework-dev-squint",
"private": true,
"type": "module",
"scripts": {
"dev": "npx squint watch & npx vite --port 3002",
"build": "npx squint compile && npx vite build"
},
"devDependencies": {
"squint-cljs": "latest",
"vite": "^6.0.0"
},
"dependencies": {
"eucalypt": "latest"
}
}

2
dev/squint/squint.edn Normal file
View File

@@ -0,0 +1,2 @@
{:paths ["src" "../../src"]
:output-dir ".compiled"}

View File

@@ -0,0 +1,73 @@
(ns dev.squint
(:require ["eucalypt" :as eu]
[ui.button :as button]))
(def variants ["primary" "secondary" "ghost" "danger"])
(def sizes ["sm" "md" "lg"])
(defn toggle-theme! [_e]
(let [el (.-documentElement js/document)
current (.. el -dataset -theme)]
(set! (.. el -dataset -theme)
(if (= current "dark") "light" "dark"))))
(def label-style {"font-weight" "600"
"color" "var(--fg-1)"
"font-size" "0.75rem"
"text-transform" "uppercase"
"letter-spacing" "0.05em"})
(defn button-grid []
(into
[:div {:style {"display" "grid"
"grid-template-columns" "repeat(4, auto)"
"gap" "1rem"
"align-items" "center"}}
[:div]
[:div {:style (merge label-style {"text-align" "center"})} "sm"]
[:div {:style (merge label-style {"text-align" "center"})} "md"]
[:div {:style (merge label-style {"text-align" "center"})} "lg"]]
(mapcat (fn [variant]
[[:div {:style label-style} variant]
[:div {:style {"text-align" "center"}}
(button/button {:variant variant :size "sm"
:on-click (fn [_] (js/console.log (str "Clicked: " variant " sm")))}
(str variant " sm"))]
[:div {:style {"text-align" "center"}}
(button/button {:variant variant :size "md"
:on-click (fn [_] (js/console.log (str "Clicked: " variant " md")))}
(str variant " md"))]
[:div {:style {"text-align" "center"}}
(button/button {:variant variant :size "lg"
:on-click (fn [_] (js/console.log (str "Clicked: " variant " lg")))}
(str variant " lg"))]])
variants)))
(defn disabled-row []
(into
[:div {:style {"display" "flex" "gap" "0.75rem" "flex-wrap" "wrap"}}]
(map (fn [variant]
(button/button {:variant variant :disabled true}
(str variant " disabled")))
variants)))
(defn app []
[:div {:style {"max-width" "800px" "margin" "0 auto"}}
[:div {:style {"display" "flex" "justify-content" "space-between" "align-items" "center" "margin-bottom" "2rem"}}
[:h2 {:style {"margin" "0" "color" "var(--fg-0)"}} "Squint (Eucalypt)"]
[:button {:on-click toggle-theme!
:style {"padding" "0.5rem 1rem" "cursor" "pointer" "border-radius" "var(--radius-md)"
"border" "var(--border-0)" "background" "var(--bg-1)" "color" "var(--fg-0)"}}
"Toggle Dark Mode"]]
[:h3 {:style {"color" "var(--fg-1)" "margin-bottom" "1rem"}} "Button Grid"]
(button-grid)
[:h3 {:style {"color" "var(--fg-1)" "margin" "2rem 0 1rem"}} "Disabled States"]
(disabled-row)])
(defn init! []
(eu/render (app) (js/document.getElementById "app")))
(defn reload! []
(eu/render (app) (js/document.getElementById "app")))
(init!)

View File

@@ -0,0 +1,9 @@
import { defineConfig } from "vite";
export default defineConfig({
root: ".",
publicDir: "public",
server: {
port: 3002,
},
});