diff --git a/public/vendor/alpine-ext/persist@3.13.8.js b/public/vendor/alpine-ext/persist@3.13.8.js
new file mode 100644
index 0000000..5da9cb7
--- /dev/null
+++ b/public/vendor/alpine-ext/persist@3.13.8.js
@@ -0,0 +1 @@
+(()=>{function d(t){let n=()=>{let r,a;try{a=localStorage}catch(i){console.error(i),console.warn("Alpine: $persist is using temporary storage since localStorage is unavailable.");let e=new Map;a={getItem:e.get.bind(e),setItem:e.set.bind(e)}}return t.interceptor((i,e,l,s,f)=>{let o=r||`_x_${s}`,u=g(o,a)?p(o,a):i;return l(u),t.effect(()=>{let c=e();m(o,c,a),l(c)}),u},i=>{i.as=e=>(r=e,i),i.using=e=>(a=e,i)})};Object.defineProperty(t,"$persist",{get:()=>n()}),t.magic("persist",n),t.persist=(r,{get:a,set:i},e=localStorage)=>{let l=g(r,e)?p(r,e):a();i(l),t.effect(()=>{let s=a();m(r,s,e),i(s)})}}function g(t,n){return n.getItem(t)!==null}function p(t,n){let r=n.getItem(t,n);if(r!==void 0)return JSON.parse(r)}function m(t,n,r){r.setItem(t,JSON.stringify(n))}document.addEventListener("alpine:init",()=>{window.Alpine.plugin(d)});})();
diff --git a/src/routes/index.lisp b/src/routes/index.lisp
index 6c25d42..5311ce1 100644
--- a/src/routes/index.lisp
+++ b/src/routes/index.lisp
@@ -11,7 +11,9 @@
       (h1
         "Hello, World!")
       (a :href "/about"
-        "About"))))
+        "About")
+      (button :x-data t :@click "$store.darkMode.toggle()"
+        "Toggle theme"))))
 
 (defun handle-get (params)
   (declare (ignore params))
diff --git a/src/scripts/global.js b/src/scripts/global.js
new file mode 100644
index 0000000..d11bab5
--- /dev/null
+++ b/src/scripts/global.js
@@ -0,0 +1,9 @@
+document.addEventListener('alpine:init', () => {
+  Alpine.store('darkMode', {
+    on: Alpine.$persist(false).as('darkMode'),
+
+    toggle() {
+      this.on = ! this.on
+    }
+  })
+})
diff --git a/src/styles/global.css b/src/styles/global.css
index ad42820..a7c7c9b 100644
--- a/src/styles/global.css
+++ b/src/styles/global.css
@@ -5,4 +5,8 @@ body {
   font-optical-sizing: auto;
   font-weight: 400;
   font-style: normal;
-}
\ No newline at end of file
+
+  &[data-dark] {
+    background-color: slategrey; 
+  }
+}
diff --git a/src/styles/pages/about.css b/src/styles/pages/about.css
index 5b4c823..1d792a6 100644
--- a/src/styles/pages/about.css
+++ b/src/styles/pages/about.css
@@ -1,7 +1,6 @@
 @scope ([data-css='pages/about']) {
   :scope {
     height: 100svh;
-    background-color: thistle;
     display: grid;
     place-content: center;
   }
diff --git a/src/styles/pages/index.css b/src/styles/pages/index.css
index 780db37..463170b 100644
--- a/src/styles/pages/index.css
+++ b/src/styles/pages/index.css
@@ -1,7 +1,6 @@
 @scope ([data-css='pages/index']) {
   :scope {
     height: 100svh;
-    background-color: aliceblue;
     display: grid;
     place-content: center;
   }
diff --git a/src/view/components/document.lisp b/src/view/components/document.lisp
index afdaed7..0707ef0 100644
--- a/src/view/components/document.lisp
+++ b/src/view/components/document.lisp
@@ -31,7 +31,7 @@
   (pi:h
     (<>
       (mapcar (lambda (src)
-                (script :src src))
+                (script :src src :defer t))
               srcs))))
 
 (pi:define-element stylesheets (hrefs)
@@ -63,8 +63,10 @@
         (link 
           :rel "stylesheet"
           :href "https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;700&display=swap")
-        (on-demand-assets :component pi:children)
         (script :src "/vendor/htmx@1.9.12.js")
+        (script :src "/vendor/alpine-ext/persist@3.13.8.js" :defer t)
+        (script :src "/scripts/global.js" :defer t)
+        (on-demand-assets :component pi:children)
         (script :src "/vendor/alpine@3.13.8.js" :defer t)
         (title (format nil "~@[~a - ~]skyizwhite.dev" title))
         (meta
diff --git a/src/view/components/layout.lisp b/src/view/components/layout.lisp
index 2802401..3a0e3e3 100644
--- a/src/view/components/layout.lisp
+++ b/src/view/components/layout.lisp
@@ -7,6 +7,8 @@
 (pi:define-element layout ()
   (pi:h
     (body
+      :x-data t
+      :|:data-dark| "$store.darkMode.on"
       ; header
       (main pi:children)
       ; footer