Skip to content

Instantly share code, notes, and snippets.

@ccjoel
Created June 13, 2024 01:59
Show Gist options
  • Save ccjoel/6be6aa6a757785bbad874b01a7e9beb6 to your computer and use it in GitHub Desktop.
Save ccjoel/6be6aa6a757785bbad874b01a7e9beb6 to your computer and use it in GitHub Desktop.
clojure htmx + hiccup + picoCSS
;; deps.edn
{:paths ["src"]
:deps {org.clojure/clojure {:mvn/version "1.11.2"}
ring/ring-core {:mvn/version "1.12.1"}
ring/ring-devel {:mvn/version "1.12.1"}
http-kit/http-kit {:mvn/version "2.8.0"}
compojure/compojure {:mvn/version "1.7.1"}
rum/rum {:mvn/version "0.12.11"
:exclusions [cljsjs/react cljsjs/react-dom]}}
:aliases {:run {:ns-default clj32.server
:exec-fn run}}}
;; src/clj32/server ns file:
(ns clj32.server
(:require [org.httpkit.server :as hk-server]
[ring.middleware.reload :as reload]
[compojure.handler :refer [site]]
[compojure.core :refer [defroutes GET POST ANY PUT]]
[compojure.route :as route]
[rum.core :as rum]))
(defonce my-server (atom nil))
;; server state, usually a cache/DB in practice
;; but we can also have in-memory aspects of server
(defonce app-counter (atom 0))
(defn stop-server []
(when-not (nil? @my-server)
(@my-server :timeout 100)
(reset! my-server nil)))
(defn hello-handler [req]
{:status 200
:headers {"Content-Type" "text/html"}
:body "hello HTTP (KIT)!"})
;; counter "component", reusable widget
(rum/defc counter []
[:<>
[:p#counter [:span (str "Count is " @app-counter)]]
[:form
[:input {:name "test-value" :placeholder "Enter here"}]
[:button
{:hx-post "/count"
:hx-trigger "click"
:hx-target "#counter"
:hx-swap "innerHTML"}
"Click Me"]]])
(rum/defc body-contents []
[:<>
[:header.container
[:nav
[:ul
[:li [:a {:href "/"} "HOME"]]
[:details.dropdown
[:summary.secondary {:role "button"} "Theme"]
[:ul
[:li [:a {:href "#" :data-theme-switcher "light"} "Light"]]
[:li [:a {:href "#" :data-theme-switcher "dark"} "dark"]]]]]]]
[:main.container
[:hgroup
[:h1 "Hello, World."]
[:p "Trying this out."]]
[:article [:h2 "Article"]
[:p "Nullam dui arcu, malesuada et sodales eu, efficitur vitae dolor. Sed ultricies dolor non
ante vulputate hendrerit. Vivamus sit amet suscipit sapien. Nulla iaculis eros a elit
pharetra egestas. Nunc placerat facilisis cursus. Sed vestibulum metus eget dolor pharetra
rutrum."]
[:footer
[:small "Duis nec elit placerat, suscipit nibh quis, finibus neque."]]]
(counter)]
[:footer.container
[:button {:aria-busy "true"}
"Please wait…"]]])
(defn index-handler [req]
{:status 200
:headers {"Content-Type" "text/html"}
:body
;; TODO move hardcoded fixed html tags to a different file
(rum/render-static-markup
[:html {:lang "en"}
[:head
[:meta {:charset "utf-8"}]
[:meta {:name "viewport" :content "width=device-width, initial-scale=1"}]
[:meta {:name "color-scheme" :content "light dark"}]
[:script {:src "https://unpkg.com/htmx.org@1.9.12" :crossorigin "anonymous"}]
[:link
{:rel "stylesheet"
:href "https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css"}]
[:title "hcljx"]]
[:body
(body-contents)
[:script {:src "https://4mrnhq.csb.app/js/minimal-theme-switcher.js"}]]])})
(defroutes app-routes
(GET "/" [] index-handler)
(POST "/count" []
(fn [req]
(swap! app-counter inc)
{:status 200
:headers {}
;; TODO this span code is duplicated here from counter above
;; should just use the same "template"
:body (rum/render-static-markup [:span (str "Count is " @app-counter)])}))
(GET "/hello" [] hello-handler)
(route/not-found "You Must Be New Here"))
;; start server in code reload mode
(defn run [opts]
(reset! my-server
(hk-server/run-server
(reload/wrap-reload (site #'app-routes))
{:port 8083}))
(println "Server started on port 8083"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment