HTMX + Alpine.js + TailwindCSS

This commit is contained in:
Akira Tempaku 2024-04-25 21:08:56 +09:00
parent b62a2dd8c4
commit d614f039d3
37 changed files with 114 additions and 233 deletions

3
.gitignore vendored
View file

@ -1 +1,2 @@
.qlot
.qlot
public/dist.css

15
Makefile Normal file
View file

@ -0,0 +1,15 @@
install: ## Install dependencies
@qlot install
dev: ## Run dev mode
@tailwindcss -i ./public/global.css -o ./public/dist.css --watch=always < /dev/null &
stop: ## Stop dev mode
@pkill -f tailwind
build: ## Build
@tailwindcss -i ./public/global.css -o ./public/dist.css
help: ## Show options
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'

View file

@ -1,3 +1 @@
# homepage (WIP)
My homepage made with Common Lisp, HTMX and Alpine.js.

View file

@ -1,6 +0,0 @@
@scope ([data-css='components/layout/footer.css']) {
:scope {
height: 40px;
background-color: gray;
}
}

View file

@ -1,7 +0,0 @@
@scope ([data-css='components/layout/header.css']) {
:scope {
height: 80px;
display: flex;
background-color: gray;
}
}

View file

@ -1,11 +0,0 @@
@scope ([data-css='components/layout/main.css']) {
:scope {
height: 100svh;
display: flex;
flex-direction: column;
}
.main {
flex: 1;
}
}

View file

@ -1,7 +0,0 @@
@scope ([data-css='pages/index.css']) {
:scope {
height: 100%;
display: grid;
place-content: center;
}
}

Binary file not shown.

Before

(image error) Size: 211 KiB

View file

File diff suppressed because one or more lines are too long

View file

@ -1 +0,0 @@
html{-webkit-text-size-adjust:100%;box-sizing:border-box;-moz-tab-size:4;tab-size:4;word-break:normal}*,:after,:before{background-repeat:no-repeat;box-sizing:inherit}:after,:before{text-decoration:inherit;vertical-align:inherit}*{margin:0;padding:0}hr{color:inherit;height:0;overflow:visible}details,main{display:block}summary{display:list-item}small{font-size:80%}[hidden]{display:none}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}a{background-color:transparent}a:active,a:hover{outline-width:0}code,kbd,pre,samp{font-family:monospace,monospace}pre{font-size:1em}b,strong{font-weight:bolder}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-color:inherit;text-indent:0}iframe{border-style:none}input{border-radius:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}textarea{overflow:auto;resize:vertical}button,input,optgroup,select,textarea{font:inherit}optgroup{font-weight:700}button{overflow:visible}button,select{text-transform:none}[role=button],[type=button],[type=reset],[type=submit],button{cursor:pointer}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button:-moz-focusring{outline:1px dotted ButtonText}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}button,input,select,textarea{background-color:transparent;border-style:none}a:focus,button:focus,input:focus,select:focus,textarea:focus{outline-width:0}select{-moz-appearance:none;-webkit-appearance:none}select::-ms-expand{display:none}select::-ms-value{color:currentColor}legend{border:0;color:inherit;display:table;max-width:100%;white-space:normal}::-webkit-file-upload-button{-webkit-appearance:button;color:inherit;font:inherit}[disabled]{cursor:default}img{border-style:none}progress{vertical-align:baseline}[aria-busy=true]{cursor:progress}[aria-controls]{cursor:pointer}[aria-disabled=true]{cursor:default}

View file

@ -1,8 +1,8 @@
@charset "utf-8";
@tailwind base;
@tailwind components;
@tailwind utilities;
html {
font-family: "Noto Sans JP", sans-serif;
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
}

View file

@ -14,9 +14,13 @@
:port cfg:*port*))
(defun start ()
(if (cfg:dev-mode-p)
(uiop:run-program "make dev"))
(jg:start *app*))
(defun stop ()
(if (cfg:dev-mode-p)
(uiop:run-program "make stop"))
(jg:stop *app*))
(defun setup ()
@ -24,9 +28,8 @@
(jg:clear-routing-rules *app*)
(fbr:assign-routes *app* :system "hp" :directory "src/routes")
(jg:install-middleware *app* mw:*path-normalizer*)
(jg:install-middleware *app* mw:*assets-server*)
(jg:install-middleware *app* mw:*public-server*)
(jg:install-middleware *app* mw:*access-logger*)
(jg:install-middleware *app* mw:*access-blocker*)
(jg:install-middleware *app* mw:*recoverer*))
(defun update ()

View file

@ -0,0 +1,43 @@
(defpackage #:hp/components/document
(:use #:cl)
(:local-nicknames (#:pi #:piccolo))
(:import-from #:hp/view/asset
#:defasset)
(:export #:document))
(in-package #:hp/components/document)
(defasset *htmx* :vendor "htmx@1.9.12.js")
(defasset *htmx-exts* :htmx-ext
("alpine-morph@1.9.12.js"
"head-support@1.9.12.js"))
(defasset *alpine* :vendor "alpine@3.13.8.js")
(defasset *alpine-exts* :alpine-ext
("morph@3.13.8.js"
"persist@3.13.8.js"))
(defasset *alpine-store* :root "store.js")
(defasset *global-css* :root "global.css")
(defasset *dist-css* :root "dist.css")
(pi:define-element document (title description)
(pi:h
(html :lang "ja"
(head
(meta :charset "UTF-8")
(script :src *htmx*)
(mapcar (lambda (path) (script :src path))
*htmx-exts*)
(mapcar (lambda (path) (script :src path :defer t))
*alpine-exts*)
(script :src *alpine-store* :defer t)
(script :src *alpine* :defer t)
(style
"@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;700&display=swap');")
(link :rel "stylesheet" :type "text/css" :href *global-css*)
(link :rel "stylesheet" :type "text/css" :href *dist-css*)
(title (format nil "~@[~a - ~]skyizwhite.dev" title))
(meta
:name "description"
:content (or description "pakuの個人サイト")))
pi:children)))

View file

@ -1,30 +0,0 @@
(defpackage #:hp/components/document/main
(:use #:cl)
(:local-nicknames (#:pi #:piccolo))
(:import-from #:hp/components/document/styles
#:styles
#:on-demand-styles)
(:import-from #:hp/components/document/scripts
#:scripts)
(:import-from #:hp/components/document/seo
#:seo)
(:export #:document
#:partial-document))
(in-package #:hp/components/document/main)
(pi:define-element document (metadata)
(pi:h
(html :lang "ja"
(head
(meta :charset "UTF-8")
(styles pi:children)
(scripts)
(seo metadata))
pi:children)))
(pi:define-element partial-document ()
(pi:h
(<>
(head :hx-head "append"
(on-demand-styles pi:children))
pi:children)))

View file

@ -1,36 +0,0 @@
(defpackage #:hp/components/document/scripts
(:use #:cl)
(:local-nicknames (#:pi #:piccolo))
(:import-from #:hp/view/asset
#:defasset)
(:export #:scripts))
(in-package #:hp/components/document/scripts)
(defasset *htmx* :vendor "htmx@1.9.12.js")
(defasset *htmx-exts* :htmx-ext
("alpine-morph@1.9.12.js"
"head-support@1.9.12.js"))
(defasset *alpine* :vendor "alpine@3.13.8.js")
(defasset *alpine-exts* :alpine-ext
("async-alpine@1.2.2.js"
"persist@3.13.8.js"
"morph@3.13.8.js"))
(defasset *global* :js "global.js")
(pi:define-element extentions (paths defer)
(pi:h
(<>
(mapcar (lambda (path)
(script :src path :defer defer))
paths))))
(pi:define-element scripts ()
(pi:h
(<>
(script :src *htmx*)
(extentions :paths *htmx-exts*)
(extentions :paths *alpine-exts* :defer t)
(script :src *global* :defer t)
(script :src *alpine* :defer t))))

View file

@ -1,13 +0,0 @@
(defpackage #:hp/components/document/seo
(:use #:cl)
(:local-nicknames (#:pi #:piccolo))
(:export #:seo))
(in-package #:hp/components/document/seo)
(pi:define-element seo (title description)
(pi:h
(<>
(title (format nil "~@[~a - ~]skyizwhite.dev" title))
(meta
:name "description"
:content (or description "pakuの個人サイト")))))

View file

@ -1,34 +0,0 @@
(defpackage #:hp/components/document/styles
(:use #:cl)
(:local-nicknames (#:pi #:piccolo))
(:import-from #:hp/view/asset
#:defasset
#:get-css-paths)
(:export #:on-demand-styles
#:styles))
(in-package #:hp/components/document/styles)
(defasset *ress* :vendor "ress@5.0.2.css")
(defasset *global* :css "global.css")
(pi:define-element on-demand-styles ()
(let* ((html-str (let ((pi:*escape-html* nil))
(pi:elem-str pi:children)))
(css-paths (get-css-paths html-str)))
(pi:h
(<>
(mapcar (lambda (path)
(link :rel "stylesheet" :type "text/css" :href path))
css-paths)))))
(pi:define-element styles ()
(pi:h
(<>
(link :rel "stylesheet" :type "text/css" :href *ress*)
(link :rel "stylesheet" :type "text/css" :href *global*)
(on-demand-styles pi:children)
(link :rel "preconnect" :href "https://fonts.googleapis.com")
(link :rel "preconnect" :href "https://fonts.gstatic.com" :crossorigin t)
(link
:rel "stylesheet"
:href "https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;700&display=swap"))))

View file

@ -0,0 +1,17 @@
(defpackage #:hp/components/layout
(:use #:cl)
(:local-nicknames (#:pi #:piccolo))
(:local-nicknames (#:cfg #:hp/config/*))
(:export #:layout))
(in-package #:hp/components/layout)
(pi:define-element layout ()
(pi:h
(body
:hx-ext cfg:*hx-ext*
:class "h-[100svh] flex flex-col"
(header)
(main :class "flex-1"
pi:children)
; footer
(footer))))

View file

@ -1,9 +0,0 @@
(defpackage #:hp/components/layout/footer
(:use #:cl)
(:local-nicknames (#:pi #:piccolo))
(:export #:layout-footer))
(in-package #:hp/components/layout/footer)
(pi:define-element layout-footer ()
(pi:h
(footer :data-css "components/layout/footer.css")))

View file

@ -1,10 +0,0 @@
(defpackage #:hp/components/layout/header
(:use #:cl)
(:local-nicknames (#:pi #:piccolo))
(:export #:layout-header))
(in-package #:hp/components/layout/header)
(pi:define-element layout-header ()
(pi:h
(header :data-css "components/layout/header.css"
(p "skyizwhite.dev"))))

View file

@ -1,21 +0,0 @@
(defpackage #:hp/components/layout/main
(:use #:cl)
(:local-nicknames (#:pi #:piccolo))
(:local-nicknames (#:cfg #:hp/config/asset))
(:import-from #:hp/components/layout/header
#:layout-header)
(:import-from #:hp/components/layout/footer
#:layout-footer)
(:export #:layout))
(in-package #:hp/components/layout/main)
(pi:define-element layout ()
(pi:h
(body
:hx-ext cfg:*hx-ext*
:data-css "components/layout/main.css"
(layout-header)
(main :class "main"
pi:children)
(layout-footer)
)))

View file

@ -5,9 +5,8 @@
(in-package #:hp/config/asset)
(defparameter *asset-roots*
'(:img "/img/"
:css "/css/"
:js "/js/"
'(:root "/"
:img "/img/"
:vendor "/vendor/"
:htmx-ext "/vendor/htmx-ext/"
:alpine-ext "/vendor/alpine-ext/"))

View file

@ -1,15 +0,0 @@
(defpackage #:hp/middlewares/access-blocker
(:use #:cl)
(:local-nicknames (#:re #:cl-ppcre))
(:export #:*access-blocker*))
(in-package #:hp/middlewares/access-blocker)
(defparameter *access-blocker*
(lambda (app)
(lambda (env)
(let ((user-agent (gethash "user-agent" (getf env :headers))))
(if (re:scan "(Firefox|SamsungBrowser)" user-agent)
`(:400
(:content-type "text/plain")
("This site is not compatible with your browser. Please use Chrome, Edge, Safari, or another compatible browser."))
(funcall app env))))))

View file

@ -1,18 +1,18 @@
(defpackage #:hp/middlewares/assets-server
(defpackage #:hp/middlewares/public-server
(:use #:cl)
(:import-from #:lack.middleware.static
#:*lack-middleware-static*)
(:export #:*assets-server*))
(in-package #:hp/middlewares/assets-server)
(:export #:*public-server*))
(in-package #:hp/middlewares/public-server)
(defun exist-asset-file-p (path)
(let ((pathname (probe-file (concatenate 'string "assets" path))))
(let ((pathname (probe-file (concatenate 'string "public" path))))
(and pathname (pathname-name pathname))))
(defparameter *assets-server*
(defparameter *public-server*
(lambda (app)
(funcall *lack-middleware-static*
app
:path (lambda (path)
(and (exist-asset-file-p path) path))
:root (asdf:system-relative-pathname :hp "assets/"))))
:root (asdf:system-relative-pathname :hp "public/"))))

View file

@ -7,8 +7,13 @@
(pi:define-element page ()
(pi:h
(div :data-css "pages/index.css"
(h1 "Hello, World!"))))
(div :class "h-full place-content-center"
(h1
:x-data "{flag: true}"
:@click "flag = ! flag"
:class "text-4xl text-center"
:|:class| "flag ? 'text-red-400' : 'text-blue-400'"
"Hello, world!"))))
(defun handle-get (params)
(declare (ignore params))

View file

@ -3,8 +3,7 @@
(:local-nicknames (#:re #:cl-ppcre))
(:local-nicknames (#:cfg #:hp/config/asset))
(:export #:asset-root
#:defasset
#:get-css-paths))
#:defasset))
(in-package #:hp/view/asset)
(defun asset-root (kind)
@ -21,12 +20,3 @@
`(defparameter ,name
(,(if (listp files) 'mapcar 'funcall)
(asset-path-under ,kind) ',files)))
(defun detect-attr-vals (html attr)
(let* ((regex (format nil "(?<=~a=\")[^\"]*(?=\")" attr))
(vals (re:all-matches-as-strings regex html)))
(remove-duplicates vals :test #'string=)))
(defun get-css-paths (html)
(mapcar (asset-path-under :css)
(detect-attr-vals html "data-css")))

View file

@ -3,7 +3,7 @@
(:local-nicknames (#:jg #:jingle))
(:local-nicknames (#:pi #:piccolo))
(:local-nicknames (#:cfg #:hp/config/env))
(:local-nicknames (#:cmp #:hp/components/**/*))
(:local-nicknames (#:cmp #:hp/components/*))
(:export #:render
#:partial-render))
(in-package #:hp/view/renderer)
@ -22,4 +22,4 @@
(defun partial-render (component &key status)
(jg:with-html-response
(if status (jg:set-response-status status))
(funcall (renderer) (cmp:partial-document component))))
(funcall (renderer) component)))

11
tailwind.config.js Normal file
View file

@ -0,0 +1,11 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/routes/**/*.lisp",
"./src/components/**/*.lisp"
],
theme: {
extend: {},
},
plugins: [],
}