diff --git a/index.js b/index.js
index 8078644acd74477f03f4c4a6f158eb5292343705..21cd9a1b0d816fa90bf0f535ed275bf7f453342e 100755
--- a/index.js
+++ b/index.js
@@ -5,7 +5,9 @@ const config = require('./sitegin/config')
 function runBabel({ onExit, opts = [] }) {
   const babel = spawn(
     './node_modules/.bin/babel',
-    ['theme-source', '-d', 'theme', ...opts].filter(a => a),
+    ['theme-source', '-d', 'theme', '-x', '.js,.ts,.tsx', ...opts].filter(
+      a => a,
+    ),
     { stdio: 'inherit', cwd: __dirname },
   )
   if (onExit) babel.on('exit', onExit)
@@ -19,12 +21,12 @@ function runBabelWatch() {
 }
 
 function runNodemon({ onExit }) {
-  const babel = spawn(
+  const nodemon = spawn(
     './node_modules/.bin/nodemon',
-    ['--ignore', 'build-debug', 'sitegin/index.js'],
+    ['--ignore', 'build-debug', '--ignore', 'theme-source', 'sitegin/index.js'],
     { stdio: 'inherit', cwd: __dirname },
   )
-  if (onExit) babel.on('exit', onExit)
+  if (onExit) nodemon.on('exit', onExit)
 }
 function runNodemonWatch() {
   runNodemon({ onExit: () => setTimeout(runNodemonWatch, 500) })
diff --git a/package-lock.json b/package-lock.json
index c53b5aefc37a210ed686392db6baf9dddbc1a8d9..7a5243d235afa0a03ba2e9a8086ef1d8fa0ba56f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1995,6 +1995,14 @@
         "@babel/plugin-transform-typescript": "^7.7.4"
       }
     },
+    "@babel/runtime": {
+      "version": "7.7.7",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.7.tgz",
+      "integrity": "sha512-uCnC2JEVAu8AKB5do1WRIsvrdJ0flYx/A/9f/6chdacnEZ7LmavjdsDXr5ksYBegxtuTPR5Va9/+13QF/kFkCA==",
+      "requires": {
+        "regenerator-runtime": "^0.13.2"
+      }
+    },
     "@babel/template": {
       "version": "7.7.0",
       "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.0.tgz",
@@ -2034,6 +2042,89 @@
         "to-fast-properties": "^2.0.0"
       }
     },
+    "@emotion/cache": {
+      "version": "11.0.0-next.10",
+      "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.0.0-next.10.tgz",
+      "integrity": "sha512-FywMhiO4CVken54RMpBLiXSQ0QICs5lQ7EWTbwxkr1Mlm/K8Ztx/6lMK2amT3IAUWsI4jxY8Kbd7PxF427VY1g==",
+      "requires": {
+        "@emotion/sheet": "0.10.0-next.1",
+        "@emotion/stylis": "0.8.4",
+        "@emotion/utils": "0.11.2",
+        "@emotion/weak-memoize": "0.2.4"
+      }
+    },
+    "@emotion/hash": {
+      "version": "0.7.3",
+      "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.7.3.tgz",
+      "integrity": "sha512-14ZVlsB9akwvydAdaEnVnvqu6J2P6ySv39hYyl/aoB6w/V+bXX0tay8cF6paqbgZsN2n5Xh15uF4pE+GvE+itw=="
+    },
+    "@emotion/memoize": {
+      "version": "0.7.3",
+      "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.3.tgz",
+      "integrity": "sha512-2Md9mH6mvo+ygq1trTeVp2uzAKwE2P7In0cRpD/M9Q70aH8L+rxMLbb3JCN2JoSWsV2O+DdFjfbbXoMoLBczow=="
+    },
+    "@emotion/react": {
+      "version": "11.0.0-next.10",
+      "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.0.0-next.10.tgz",
+      "integrity": "sha512-OgsMiAKDQgugzOkV33+3jPICPEJ7FdyDcf/IJgup3TKxHrVlvT+S7WdxHcStcrq/xcwDx8Qf3EUmx1D+pGz9Kw==",
+      "requires": {
+        "@babel/runtime": "^7.7.2",
+        "@emotion/cache": "^11.0.0-next.10",
+        "@emotion/serialize": "^0.12.0-next.3",
+        "@emotion/sheet": "0.10.0-next.1",
+        "@emotion/utils": "0.11.2",
+        "@emotion/weak-memoize": "0.2.4",
+        "hoist-non-react-statics": "^3.3.1"
+      }
+    },
+    "@emotion/serialize": {
+      "version": "0.12.0-next.3",
+      "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.12.0-next.3.tgz",
+      "integrity": "sha512-/bznLGIry2OZ5hcbWaV79DcpBko3z27ifRU1cQNf5WClBvNg9ps/HTobAIlxiVfE/AFMumtGP+fW8RFwnW7zvg==",
+      "requires": {
+        "@emotion/hash": "0.7.3",
+        "@emotion/memoize": "0.7.3",
+        "@emotion/unitless": "0.7.4",
+        "@emotion/utils": "0.11.2",
+        "csstype": "^2.6.7"
+      }
+    },
+    "@emotion/server": {
+      "version": "11.0.0-next.10",
+      "resolved": "https://registry.npmjs.org/@emotion/server/-/server-11.0.0-next.10.tgz",
+      "integrity": "sha512-0ONYqFkSScg6r3zlNncOhJ/ZMdecSeO+Kza9lKI99KLKBY0YaHnWJ45ZM36Jau6Jsbbpl1OjpPejdIEkfJlR5g==",
+      "requires": {
+        "@emotion/utils": "0.11.2",
+        "html-tokenize": "^2.0.0",
+        "multipipe": "^1.0.2",
+        "through": "^2.3.8"
+      }
+    },
+    "@emotion/sheet": {
+      "version": "0.10.0-next.1",
+      "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.10.0-next.1.tgz",
+      "integrity": "sha512-dKzGmnML1bDTvKDZ3joyiNghIMCKgCwjUQZjqwFvPRN1n/M9HFZR+Iq3/I8xnfUhUDmFGIsL2A2ER4Dol75hyw=="
+    },
+    "@emotion/stylis": {
+      "version": "0.8.4",
+      "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.4.tgz",
+      "integrity": "sha512-TLmkCVm8f8gH0oLv+HWKiu7e8xmBIaokhxcEKPh1m8pXiV/akCiq50FvYgOwY42rjejck8nsdQxZlXZ7pmyBUQ=="
+    },
+    "@emotion/unitless": {
+      "version": "0.7.4",
+      "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.4.tgz",
+      "integrity": "sha512-kBa+cDHOR9jpRJ+kcGMsysrls0leukrm68DmFQoMIWQcXdr2cZvyvypWuGYT7U+9kAExUE7+T7r6G3C3A6L8MQ=="
+    },
+    "@emotion/utils": {
+      "version": "0.11.2",
+      "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.2.tgz",
+      "integrity": "sha512-UHX2XklLl3sIaP6oiMmlVzT0J+2ATTVpf0dHQVyPJHTkOITvXfaSqnRk6mdDhV9pR8T/tHc3cex78IKXssmzrA=="
+    },
+    "@emotion/weak-memoize": {
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.4.tgz",
+      "integrity": "sha512-6PYY5DVdAY1ifaQW6XYTnOMihmBVT27elqSjEoodchsGjzYlEsTQMcEhSud99kVawatyTZRTiVkJ/c6lwbQ7nA=="
+    },
     "@jimp/bmp": {
       "version": "0.8.5",
       "resolved": "https://registry.npmjs.org/@jimp/bmp/-/bmp-0.8.5.tgz",
@@ -3094,6 +3185,11 @@
       "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
       "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw="
     },
+    "buffer-from": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-0.1.2.tgz",
+      "integrity": "sha512-RiWIenusJsmI2KcvqQABB83tLxCByE3upSP8QU3rJDMVFGPWLvPQJt/O1Su9moRWeH7d+Q2HYb68f6+v+tw2vg=="
+    },
     "bytes": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
@@ -3540,8 +3636,7 @@
     "csstype": {
       "version": "2.6.8",
       "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.8.tgz",
-      "integrity": "sha512-msVS9qTuMT5zwAGCVm4mxfrZ18BNc6Csd0oJAtiFMZ1FAx1CCvy2+5MDmYoix63LM/6NDbNtodCiGYGmFgO0dA==",
-      "dev": true
+      "integrity": "sha512-msVS9qTuMT5zwAGCVm4mxfrZ18BNc6Csd0oJAtiFMZ1FAx1CCvy2+5MDmYoix63LM/6NDbNtodCiGYGmFgO0dA=="
     },
     "currently-unhandled": {
       "version": "0.4.1",
@@ -3691,6 +3786,14 @@
         "is-obj": "^1.0.0"
       }
     },
+    "duplexer2": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
+      "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=",
+      "requires": {
+        "readable-stream": "^2.0.2"
+      }
+    },
     "duplexer3": {
       "version": "0.1.4",
       "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
@@ -5541,11 +5644,54 @@
       "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.16.2.tgz",
       "integrity": "sha512-feMUrVLZvjy0oC7FVJQcSQRqbBq9kwqnYE4+Kj9ZjbHh3g+BisiPgF49NyQbVLNdrL/qqZr3Ca9yOKwgn2i/tw=="
     },
+    "hoist-non-react-statics": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
+      "integrity": "sha512-wbg3bpgA/ZqWrZuMOeJi8+SKMhr7X9TesL/rXMjTzh0p0JUBo3II8DHboYbuIXWRlttrUFxwcu/5kygrCw8fJw==",
+      "requires": {
+        "react-is": "^16.7.0"
+      }
+    },
     "hosted-git-info": {
       "version": "2.8.5",
       "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz",
       "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg=="
     },
+    "html-tokenize": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/html-tokenize/-/html-tokenize-2.0.0.tgz",
+      "integrity": "sha1-izqaXetHXK5qb5ZxYA0sIKspglE=",
+      "requires": {
+        "buffer-from": "~0.1.1",
+        "inherits": "~2.0.1",
+        "minimist": "~0.0.8",
+        "readable-stream": "~1.0.27-1",
+        "through2": "~0.4.1"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+        },
+        "readable-stream": {
+          "version": "1.0.34",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+          "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.1",
+            "isarray": "0.0.1",
+            "string_decoder": "~0.10.x"
+          }
+        },
+        "string_decoder": {
+          "version": "0.10.31",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+        }
+      }
+    },
     "http-errors": {
       "version": "1.7.3",
       "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz",
@@ -6917,6 +7063,15 @@
       "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
       "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
     },
+    "multipipe": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-1.0.2.tgz",
+      "integrity": "sha1-zBPv2DPJzamfIk+GhGG44aP9k50=",
+      "requires": {
+        "duplexer2": "^0.1.2",
+        "object-assign": "^4.1.0"
+      }
+    },
     "must": {
       "version": "0.13.4",
       "resolved": "https://registry.npmjs.org/must/-/must-0.13.4.tgz",
@@ -9477,8 +9632,52 @@
     "through": {
       "version": "2.3.8",
       "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
-      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
-      "dev": true
+      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
+    },
+    "through2": {
+      "version": "0.4.2",
+      "resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz",
+      "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=",
+      "requires": {
+        "readable-stream": "~1.0.17",
+        "xtend": "~2.1.1"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "0.0.1",
+          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+        },
+        "object-keys": {
+          "version": "0.4.0",
+          "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz",
+          "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY="
+        },
+        "readable-stream": {
+          "version": "1.0.34",
+          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+          "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.1",
+            "isarray": "0.0.1",
+            "string_decoder": "~0.10.x"
+          }
+        },
+        "string_decoder": {
+          "version": "0.10.31",
+          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+        },
+        "xtend": {
+          "version": "2.1.2",
+          "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz",
+          "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=",
+          "requires": {
+            "object-keys": "~0.4.0"
+          }
+        }
+      }
     },
     "timed-out": {
       "version": "4.0.1",
diff --git a/package.json b/package.json
index 6ecf902278c1280cc1827ac1de5e5eeb25cf8518..c52892a847694056bb08a1551d7855d299deea8c 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,9 @@
   "author": "",
   "license": "UNSPECIFIED",
   "dependencies": {
+    "@emotion/cache": "^11.0.0-next.10",
+    "@emotion/react": "^11.0.0-next.10",
+    "@emotion/server": "^11.0.0-next.10",
     "browser-sync": "^2.26.7",
     "chokidar": "^3.3.0",
     "cli": "^1.0.1",
diff --git a/theme-source/partials/html.js b/theme-source/partials/html.js
index 79424286edce0df4b1eb8a7fb7b5a1b953126b69..0bd882a8d265349d6191113977de1c9d0e04a48d 100644
--- a/theme-source/partials/html.js
+++ b/theme-source/partials/html.js
@@ -10,6 +10,7 @@ export function Html({
   config,
   children,
   bodyClassName,
+  css,
 }) {
   return (
     <html lang="en">
@@ -157,6 +158,7 @@ export function Html({
             src={`${config.baseurl}/theme/js/socket.io-1.4.5.js`}
           ></script>
         ) : null}
+        {css}
         {head}
       </head>
       <body className={bodyClassName}>{children}</body>
diff --git a/theme-source/partials/list.js b/theme-source/partials/list.tsx
similarity index 95%
rename from theme-source/partials/list.js
rename to theme-source/partials/list.tsx
index 6be8b0dba41413552228cf59e7f72e9cfa56eb57..6f0eea6337ebdea404f7afd30d9bf789bc040c0e 100644
--- a/theme-source/partials/list.js
+++ b/theme-source/partials/list.tsx
@@ -1,5 +1,7 @@
+/* @jsx jsx */
 import React from 'react'
 import moment from 'moment'
+import { jsx } from '@emotion/react'
 import { Paginator } from './paginator'
 import { SymbolBackward, SymbolForward } from './svgs'
 import { dateFilter, truncate, striptags } from '../utils'
@@ -37,16 +39,19 @@ export function List({ subtags, tag, content, metadata, file, config }) {
               <div className="block article">
                 <a href={`${config.baseurl}/${page.file}`}>
                   <div
-                    style={{
+                    css={{
                       height: 120,
+                      backgroundColor: '#0288d1',
                       backgroundImage: page.metadata.image
                         ? `url('${config.baseurl}${relURL(
                             page.metadata.image,
                             page.file,
                           )}')`
                         : undefined,
+                      backgroundSize: 'cover',
+                      backgroundRepeat: 'no-repeat',
+                      backgroundPosition: '50% 50%',
                     }}
-                    className="leadimage light-blue darken-2"
                   ></div>
                 </a>
                 <div className="head-title">
diff --git a/theme-source/render-to-string.js b/theme-source/render-to-string.js
deleted file mode 100644
index 2d93c66d05f46f174a775945e34bf16bf7658165..0000000000000000000000000000000000000000
--- a/theme-source/render-to-string.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import React from 'react'
-import ReactDOM from 'react-dom/server'
-import { Html } from './partials/html'
-
-export function renderToString(Component, htmlProps) {
-  const delimiter = '---delimiter---'
-  const html = ReactDOM.renderToStaticMarkup(
-    <Html {...htmlProps}>{delimiter}</Html>,
-  ).split(delimiter)
-  return html[0] + ReactDOM.renderToStaticMarkup(Component) + html[1]
-}
diff --git a/theme-source/render-to-string.tsx b/theme-source/render-to-string.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..1003d618a7a82b5ef964d50f9508833fca3a8fda
--- /dev/null
+++ b/theme-source/render-to-string.tsx
@@ -0,0 +1,27 @@
+import React from 'react'
+import ReactDOM from 'react-dom/server'
+import { CacheProvider } from '@emotion/react'
+import createCache from '@emotion/cache'
+import createEmotionServer from '@emotion/server/create-instance'
+import { Html } from './partials/html'
+
+export function renderToString(Component: JSX.Element, htmlProps: any) {
+  const emotionCache = createCache()
+  const emotionServer = createEmotionServer(emotionCache)
+  const { html: content, css } = emotionServer.extractCritical(
+    ReactDOM.renderToStaticMarkup(
+      <CacheProvider value={emotionCache}>{Component}</CacheProvider>,
+    ),
+  )
+
+  const delimiter = '---delimiter---'
+  const html = ReactDOM.renderToStaticMarkup(
+    <Html
+      {...htmlProps}
+      css={<style dangerouslySetInnerHTML={{ __html: css }} />}
+    >
+      {delimiter}
+    </Html>,
+  ).split(delimiter)
+  return html[0] + content + html[1]
+}