diff --git a/.gitignore b/.gitignore
index 8a2a767..0ffe02c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-.qlot
\ No newline at end of file
+.qlot
+public/dist.css
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..7a1aae6
--- /dev/null
+++ b/Makefile
@@ -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}'
diff --git a/README.md b/README.md
index 693c937..66efaf1 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1 @@
 # homepage (WIP)
-
-My homepage made with Common Lisp, HTMX and Alpine.js.
diff --git a/assets/css/components/layout/footer.css b/assets/css/components/layout/footer.css
deleted file mode 100644
index 6a0d903..0000000
--- a/assets/css/components/layout/footer.css
+++ /dev/null
@@ -1,6 +0,0 @@
-@scope ([data-css='components/layout/footer.css']) {
-  :scope {
-    height: 40px;
-    background-color: gray;
-  }
-}
diff --git a/assets/css/components/layout/header.css b/assets/css/components/layout/header.css
deleted file mode 100644
index 3b0754e..0000000
--- a/assets/css/components/layout/header.css
+++ /dev/null
@@ -1,7 +0,0 @@
-@scope ([data-css='components/layout/header.css']) {
-  :scope {
-    height: 80px;
-    display: flex;
-    background-color: gray;
-  }
-}
diff --git a/assets/css/components/layout/main.css b/assets/css/components/layout/main.css
deleted file mode 100644
index fe2004e..0000000
--- a/assets/css/components/layout/main.css
+++ /dev/null
@@ -1,11 +0,0 @@
-@scope ([data-css='components/layout/main.css']) {
-  :scope {
-    height: 100svh;
-    display: flex;
-    flex-direction: column;
-  }
-  
-  .main {
-    flex: 1;
-  }
-}
diff --git a/assets/css/pages/index.css b/assets/css/pages/index.css
deleted file mode 100644
index 109f9ac..0000000
--- a/assets/css/pages/index.css
+++ /dev/null
@@ -1,7 +0,0 @@
-@scope ([data-css='pages/index.css']) {
-  :scope {
-    height: 100%;
-    display: grid;
-    place-content: center;
-  }
-}
\ No newline at end of file
diff --git a/assets/img/me.jpg b/assets/img/me.jpg
deleted file mode 100644
index d5e9362..0000000
Binary files a/assets/img/me.jpg and /dev/null differ
diff --git a/assets/js/pages/.keep b/assets/js/pages/.keep
deleted file mode 100644
index e69de29..0000000
diff --git a/assets/vendor/alpine-ext/async-alpine@1.2.2.js b/assets/vendor/alpine-ext/async-alpine@1.2.2.js
deleted file mode 100644
index 43a2b91..0000000
--- a/assets/vendor/alpine-ext/async-alpine@1.2.2.js
+++ /dev/null
@@ -1 +0,0 @@
-(()=>{var d=Object.defineProperty;var w=e=>d(e,"__esModule",{value:!0});var A=(e,t)=>{w(e);for(var i in t)d(e,i,{get:t[i],enumerable:!0})};var o={};A(o,{eager:()=>h,event:()=>f,idle:()=>c,media:()=>_,visible:()=>m});var v=()=>!0,h=v;var b=({component:e,argument:t})=>new Promise(i=>{if(t)window.addEventListener(t,()=>i(),{once:!0});else{let s=n=>{n.detail.id===e.id&&(window.removeEventListener("async-alpine:load",s),i())};window.addEventListener("async-alpine:load",s)}}),f=b;var $=()=>new Promise(e=>{"requestIdleCallback"in window?window.requestIdleCallback(e):setTimeout(e,200)}),c=$;var E=({argument:e})=>new Promise(t=>{if(!e)return console.log("Async Alpine: media strategy requires a media query. Treating as 'eager'"),t();let i=window.matchMedia(`(${e})`);i.matches?t():i.addEventListener("change",t,{once:!0})}),_=E;var q=({component:e,argument:t})=>new Promise(i=>{let s=t||"0px 0px 0px 0px",n=new IntersectionObserver(a=>{a[0].isIntersecting&&(n.disconnect(),i())},{rootMargin:s});n.observe(e.el)}),m=q;function p(e){let t=P(e),i=x(t);return i.type==="method"?{type:"expression",operator:"&&",parameters:[i]}:i}function P(e){let t=/\s*([()])\s*|\s*(\|\||&&|\|)\s*|\s*((?:[^()&|]+\([^()]+\))|[^()&|]+)\s*/g,i=[],s;for(;(s=t.exec(e))!==null;){let[,n,a,r]=s;if(n!==void 0)i.push({type:"parenthesis",value:n});else if(a!==void 0)i.push({type:"operator",value:a==="|"?"&&":a});else{let u={type:"method",method:r.trim()};r.includes("(")&&(u.method=r.substring(0,r.indexOf("(")).trim(),u.argument=r.substring(r.indexOf("(")+1,r.indexOf(")"))),r.method==="immediate"&&(r.method="eager"),i.push(u)}}return i}function x(e){let t=g(e);for(;e.length>0&&(e[0].value==="&&"||e[0].value==="|"||e[0].value==="||");){let i=e.shift().value,s=g(e);t.type==="expression"&&t.operator===i?t.parameters.push(s):t={type:"expression",operator:i,parameters:[t,s]}}return t}function g(e){if(e[0].value==="("){e.shift();let t=x(e);return e[0].value===")"&&e.shift(),t}else return e.shift()}var y="__internal_",l={Alpine:null,_options:{prefix:"ax-",alpinePrefix:"x-",root:"load",inline:"load-src",defaultStrategy:"eager"},_alias:!1,_data:{},_realIndex:0,get _index(){return this._realIndex++},init(e,t={}){return this.Alpine=e,this._options={...this._options,...t},this},start(){return this._processInline(),this._setupComponents(),this._mutations(),this},data(e,t=!1){return this._data[e]={loaded:!1,download:t},this},url(e,t){!e||!t||(this._data[e]||this.data(e),this._data[e].download=()=>import(this._parseUrl(t)))},alias(e){this._alias=e},_processInline(){let e=document.querySelectorAll(`[${this._options.prefix}${this._options.inline}]`);for(let t of e)this._inlineElement(t)},_inlineElement(e){let t=e.getAttribute(`${this._options.alpinePrefix}data`),i=e.getAttribute(`${this._options.prefix}${this._options.inline}`);if(!t||!i)return;let s=this._parseName(t);this.url(s,i)},_setupComponents(){let e=document.querySelectorAll(`[${this._options.prefix}${this._options.root}]`);for(let t of e)this._setupComponent(t)},_setupComponent(e){let t=e.getAttribute(`${this._options.alpinePrefix}data`);e.setAttribute(`${this._options.alpinePrefix}ignore`,"");let i=this._parseName(t),s=e.getAttribute(`${this._options.prefix}${this._options.root}`)||this._options.defaultStrategy;this._componentStrategy({name:i,strategy:s,el:e,id:e.id||this._index})},async _componentStrategy(e){let t=p(e.strategy);await this._generateRequirements(e,t),await this._download(e.name),this._activate(e)},_generateRequirements(e,t){if(t.type==="expression"){if(t.operator==="&&")return Promise.all(t.parameters.map(i=>this._generateRequirements(e,i)));if(t.operator==="||")return Promise.any(t.parameters.map(i=>this._generateRequirements(e,i)))}return o[t.method]?o[t.method]({component:e,argument:t.argument}):!1},async _download(e){if(e.startsWith(y)||(this._handleAlias(e),!this._data[e]||this._data[e].loaded))return;let t=await this._getModule(e);this.Alpine.data(e,t),this._data[e].loaded=!0},async _getModule(e){if(!this._data[e])return;let t=await this._data[e].download(e);return typeof t=="function"?t:t[e]||t.default||Object.values(t)[0]||!1},_activate(e){this.Alpine.destroyTree(e.el);let t=`${this._options.alpinePrefix}ignore`;e.el.removeAttribute(t),e.el._x_ignore=!1,!this._anyParent(e.el,i=>i.hasAttribute(t))&&this.Alpine.initTree(e.el)},_mutations(){new MutationObserver(t=>{for(let i of t)if(!!i.addedNodes)for(let s of i.addedNodes){if(s.nodeType!==1)continue;s.hasAttribute(`${this._options.prefix}${this._options.root}`)&&this._mutationEl(s),s.querySelectorAll(`[${this._options.prefix}${this._options.root}]`).forEach(a=>this._mutationEl(a))}}).observe(document,{attributes:!0,childList:!0,subtree:!0})},_mutationEl(e){e.hasAttribute(`${this._options.prefix}${this._options.inline}`)&&this._inlineElement(e),this._setupComponent(e)},_handleAlias(e){if(!(!this._alias||this._data[e])){if(typeof this._alias=="function"){this.data(e,this._alias);return}this.url(e,this._alias.replaceAll("[name]",e))}},_parseName(e){return(e||"").split(/[({]/g)[0]||`${y}${this._index}`},_parseUrl(e){return new RegExp("^(?:[a-z+]+:)?//","i").test(e)?e:new URL(e,document.baseURI).href},_anyParent(e,t){return!e||e.nodeName==="HTML"?!1:t(e)?e:this._anyParent(e.parentElement,t)}};document.addEventListener("alpine:init",()=>{window.AsyncAlpine=l,l.init(Alpine,window.AsyncAlpineOptions||{}),document.dispatchEvent(new CustomEvent("async-alpine:init")),l.start()});})();
diff --git a/assets/vendor/ress@5.0.2.css b/assets/vendor/ress@5.0.2.css
deleted file mode 100644
index 3a886b9..0000000
--- a/assets/vendor/ress@5.0.2.css
+++ /dev/null
@@ -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}
\ No newline at end of file
diff --git a/assets/css/global.css b/public/global.css
similarity index 57%
rename from assets/css/global.css
rename to public/global.css
index 9f4385b..a7b476f 100644
--- a/assets/css/global.css
+++ b/public/global.css
@@ -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;
 }
diff --git a/assets/js/components/.keep b/public/img/.keep
similarity index 100%
rename from assets/js/components/.keep
rename to public/img/.keep
diff --git a/assets/js/global.js b/public/store.js
similarity index 100%
rename from assets/js/global.js
rename to public/store.js
diff --git a/assets/vendor/alpine-ext/morph@3.13.8.js b/public/vendor/alpine-ext/morph@3.13.8.js
similarity index 100%
rename from assets/vendor/alpine-ext/morph@3.13.8.js
rename to public/vendor/alpine-ext/morph@3.13.8.js
diff --git a/assets/vendor/alpine-ext/persist@3.13.8.js b/public/vendor/alpine-ext/persist@3.13.8.js
similarity index 100%
rename from assets/vendor/alpine-ext/persist@3.13.8.js
rename to public/vendor/alpine-ext/persist@3.13.8.js
diff --git a/assets/vendor/alpine@3.13.8.js b/public/vendor/alpine@3.13.8.js
similarity index 100%
rename from assets/vendor/alpine@3.13.8.js
rename to public/vendor/alpine@3.13.8.js
diff --git a/assets/vendor/htmx-ext/alpine-morph@1.9.12.js b/public/vendor/htmx-ext/alpine-morph@1.9.12.js
similarity index 100%
rename from assets/vendor/htmx-ext/alpine-morph@1.9.12.js
rename to public/vendor/htmx-ext/alpine-morph@1.9.12.js
diff --git a/assets/vendor/htmx-ext/head-support@1.9.12.js b/public/vendor/htmx-ext/head-support@1.9.12.js
similarity index 100%
rename from assets/vendor/htmx-ext/head-support@1.9.12.js
rename to public/vendor/htmx-ext/head-support@1.9.12.js
diff --git a/assets/vendor/htmx@1.9.12.js b/public/vendor/htmx@1.9.12.js
similarity index 100%
rename from assets/vendor/htmx@1.9.12.js
rename to public/vendor/htmx@1.9.12.js
diff --git a/src/app.lisp b/src/app.lisp
index ba379df..3b03e78 100644
--- a/src/app.lisp
+++ b/src/app.lisp
@@ -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 ()
diff --git a/src/components/document.lisp b/src/components/document.lisp
new file mode 100644
index 0000000..f6d6281
--- /dev/null
+++ b/src/components/document.lisp
@@ -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)))
diff --git a/src/components/document/main.lisp b/src/components/document/main.lisp
deleted file mode 100644
index 780f854..0000000
--- a/src/components/document/main.lisp
+++ /dev/null
@@ -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)))
diff --git a/src/components/document/scripts.lisp b/src/components/document/scripts.lisp
deleted file mode 100644
index 53c4bf4..0000000
--- a/src/components/document/scripts.lisp
+++ /dev/null
@@ -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))))
diff --git a/src/components/document/seo.lisp b/src/components/document/seo.lisp
deleted file mode 100644
index 056289c..0000000
--- a/src/components/document/seo.lisp
+++ /dev/null
@@ -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の個人サイト")))))
diff --git a/src/components/document/styles.lisp b/src/components/document/styles.lisp
deleted file mode 100644
index 7ea7303..0000000
--- a/src/components/document/styles.lisp
+++ /dev/null
@@ -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"))))
diff --git a/src/components/layout.lisp b/src/components/layout.lisp
new file mode 100644
index 0000000..d9854d1
--- /dev/null
+++ b/src/components/layout.lisp
@@ -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))))
diff --git a/src/components/layout/footer.lisp b/src/components/layout/footer.lisp
deleted file mode 100644
index da0091f..0000000
--- a/src/components/layout/footer.lisp
+++ /dev/null
@@ -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")))
diff --git a/src/components/layout/header.lisp b/src/components/layout/header.lisp
deleted file mode 100644
index a185db8..0000000
--- a/src/components/layout/header.lisp
+++ /dev/null
@@ -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"))))
diff --git a/src/components/layout/main.lisp b/src/components/layout/main.lisp
deleted file mode 100644
index db768d3..0000000
--- a/src/components/layout/main.lisp
+++ /dev/null
@@ -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)
-      )))
diff --git a/src/config/asset.lisp b/src/config/asset.lisp
index 3a14077..f39ef81 100644
--- a/src/config/asset.lisp
+++ b/src/config/asset.lisp
@@ -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/"))
diff --git a/src/middlewares/access-blocker.lisp b/src/middlewares/access-blocker.lisp
deleted file mode 100644
index 6a20d72..0000000
--- a/src/middlewares/access-blocker.lisp
+++ /dev/null
@@ -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))))))
diff --git a/src/middlewares/assets-server.lisp b/src/middlewares/public-server.lisp
similarity index 56%
rename from src/middlewares/assets-server.lisp
rename to src/middlewares/public-server.lisp
index fe90fe2..34c2a60 100644
--- a/src/middlewares/assets-server.lisp
+++ b/src/middlewares/public-server.lisp
@@ -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/"))))
diff --git a/src/routes/index.lisp b/src/routes/index.lisp
index a09e4f0..c147282 100644
--- a/src/routes/index.lisp
+++ b/src/routes/index.lisp
@@ -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))
diff --git a/src/view/asset.lisp b/src/view/asset.lisp
index 8361445..45f5b62 100644
--- a/src/view/asset.lisp
+++ b/src/view/asset.lisp
@@ -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")))
diff --git a/src/view/renderer.lisp b/src/view/renderer.lisp
index c6ece3b..342f03e 100644
--- a/src/view/renderer.lisp
+++ b/src/view/renderer.lisp
@@ -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)))
diff --git a/tailwind.config.js b/tailwind.config.js
new file mode 100644
index 0000000..fc5032f
--- /dev/null
+++ b/tailwind.config.js
@@ -0,0 +1,11 @@
+/** @type {import('tailwindcss').Config} */
+module.exports = {
+  content: [
+    "./src/routes/**/*.lisp",
+    "./src/components/**/*.lisp"
+  ],
+  theme: {
+    extend: {},
+  },
+  plugins: [],
+}