From 19a67179130cdf403f1678f218e84ee69d7a4c57 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Isabella=20Sko=C5=99epov=C3=A1?= <isabella@skorepova.info>
Date: Sat, 11 Jan 2020 15:20:15 +0100
Subject: [PATCH] Split html from content

---
 package-lock.json                             |  37 ++
 package.json                                  |   3 +
 theme-source/partials/base-body.tsx           | 345 +++++++++++++++++
 theme-source/partials/base.js                 | 363 ------------------
 theme-source/render-to-string.js              |  11 +
 .../templates/{article.js => article.tsx}     |  81 ++--
 theme-source/templates/tag.js                 |  15 -
 theme-source/templates/tag.tsx                |  17 +
 tsconfig.json                                 |  66 ++++
 9 files changed, 522 insertions(+), 416 deletions(-)
 create mode 100644 theme-source/partials/base-body.tsx
 delete mode 100644 theme-source/partials/base.js
 create mode 100644 theme-source/render-to-string.js
 rename theme-source/templates/{article.js => article.tsx} (70%)
 delete mode 100644 theme-source/templates/tag.js
 create mode 100644 theme-source/templates/tag.tsx
 create mode 100644 tsconfig.json

diff --git a/package-lock.json b/package-lock.json
index f26d5dfd..c53b5aef 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2349,6 +2349,31 @@
       "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==",
       "dev": true
     },
+    "@types/prop-types": {
+      "version": "15.7.3",
+      "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
+      "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==",
+      "dev": true
+    },
+    "@types/react": {
+      "version": "16.9.17",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.17.tgz",
+      "integrity": "sha512-UP27In4fp4sWF5JgyV6pwVPAQM83Fj76JOcg02X5BZcpSu5Wx+fP9RMqc2v0ssBoQIFvD5JdKY41gjJJKmw6Bg==",
+      "dev": true,
+      "requires": {
+        "@types/prop-types": "*",
+        "csstype": "^2.2.0"
+      }
+    },
+    "@types/react-dom": {
+      "version": "16.9.4",
+      "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.4.tgz",
+      "integrity": "sha512-fya9xteU/n90tda0s+FtN5Ym4tbgxpq/hb/Af24dvs6uYnYn+fspaxw5USlw0R8apDNwxsqumdRoCoKitckQqw==",
+      "dev": true,
+      "requires": {
+        "@types/react": "*"
+      }
+    },
     "a-sync-waterfall": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz",
@@ -3512,6 +3537,12 @@
       "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
       "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4="
     },
+    "csstype": {
+      "version": "2.6.8",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.8.tgz",
+      "integrity": "sha512-msVS9qTuMT5zwAGCVm4mxfrZ18BNc6Csd0oJAtiFMZ1FAx1CCvy2+5MDmYoix63LM/6NDbNtodCiGYGmFgO0dA==",
+      "dev": true
+    },
     "currently-unhandled": {
       "version": "0.4.1",
       "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
@@ -9629,6 +9660,12 @@
       "integrity": "sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw==",
       "dev": true
     },
+    "typescript": {
+      "version": "3.7.4",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.4.tgz",
+      "integrity": "sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw==",
+      "dev": true
+    },
     "ua-parser-js": {
       "version": "0.7.17",
       "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz",
diff --git a/package.json b/package.json
index 63fc4d83..6ecf9022 100644
--- a/package.json
+++ b/package.json
@@ -39,6 +39,8 @@
     "@babel/preset-env": "^7.7.7",
     "@babel/preset-react": "^7.7.4",
     "@babel/preset-typescript": "^7.7.7",
+    "@types/react": "^16.9.17",
+    "@types/react-dom": "^16.9.4",
     "babel-eslint": "^10.0.3",
     "eslint": "^6.6.0",
     "eslint-config-airbnb-base": "^14.0.0",
@@ -48,6 +50,7 @@
     "istanbul": "^0.4.5",
     "mocha": "^6.2.2",
     "prettier": "^1.18.2",
+    "typescript": "^3.7.4",
     "unit.js": "^2.1.1"
   },
   "optionalDependencies": {}
diff --git a/theme-source/partials/base-body.tsx b/theme-source/partials/base-body.tsx
new file mode 100644
index 00000000..7d553933
--- /dev/null
+++ b/theme-source/partials/base-body.tsx
@@ -0,0 +1,345 @@
+import React, { PropsWithChildren } from 'react'
+import { SymbolSearch, SymbolMenu, SymbolFacebook } from './svgs'
+import { Search } from './search'
+import { Html } from './html'
+
+export function BaseBody({
+  config,
+  children,
+}: PropsWithChildren<{ config: any }>) {
+  return (
+    <div id="all">
+      {/* Top nav */}
+      <nav
+        className="supernav"
+        role="navigation"
+        style={{ zIndex: 5, position: 'absolute' }}
+      >
+        <div className="nav-wrapper container">
+          <a
+            data-activates="nav-mobile"
+            className="menu-icon hide-on-med-and-up"
+            style={{ float: 'left' }}
+          >
+            <SymbolMenu color="#fff" size={false} />
+          </a>
+          <div style={{ zIndex: 2 }} className="brand-logo">
+            <a id="logo-container" href={`${config.baseurl}/tag/vse/1/`}>
+              <img
+                className="hide-on-small-and-up"
+                src={`${config.baseurl}/theme/icon56.png`}
+              />
+              <img
+                className="hide-on-med-and-up hide-on-ultra-small"
+                src={`${config.baseurl}/theme/logo56.png`}
+              />
+              <img
+                className="hide-on-small-only"
+                src={`${config.baseurl}/theme/logo64.png`}
+              />
+            </a>
+          </div>
+          <div className="toplinks">
+            <a className="hide-on-small-only left" id="aktuality">
+              Aktuality
+            </a>
+            <a className="hide-on-small-only left" id="clanky">
+              Články
+            </a>
+            <a className="hide-on-small-only left" id="o-nas">
+              O nás
+            </a>
+            <a className="search-icon left">
+              <SymbolSearch color="#fff" size={false} />
+            </a>
+          </div>
+        </div>
+      </nav>
+      <script
+        dangerouslySetInnerHTML={{
+          __html: `
+      $(document).ready(function() {
+        $(".menu-icon").sideNav();
+        $("#o-nas").click(function() {
+          var body = $("body");
+          body.removeClass("show-subnav2");
+          body.removeClass("show-subnav3");
+          if(body.hasClass("show-subnav1"))
+            body.removeClass("show-subnav1");
+          else
+            body.addClass("show-subnav1");
+        });
+        $("#aktuality").click(function() {
+          var body = $("body");
+          body.removeClass("show-subnav1");
+          body.removeClass("show-subnav3");
+          if(body.hasClass("show-subnav2"))
+            body.removeClass("show-subnav2");
+          else
+            body.addClass("show-subnav2");
+        });
+        $("#clanky").click(function() {
+          var body = $("body");
+          body.removeClass("show-subnav1");
+          body.removeClass("show-subnav2");
+          if(body.hasClass("show-subnav3"))
+            body.removeClass("show-subnav3");
+          else
+            body.addClass("show-subnav3");
+        })
+        $("body").click(function(e) {
+          if(!($(e.target).parents("a").length || $(e.target).is("a"))) {
+            $("body").removeClass("show-subnav2").removeClass("show-subnav1");
+          }
+        });
+      });
+    `,
+        }}
+      />
+      {/* Side nav */}
+      <ul id="nav-mobile" className="side-nav collapsible">
+        <li className="no-padding">
+          <a className="collapsible-header waves-effect waves-teal">
+            Aktuality
+          </a>
+          <div className="collapsible-body">
+            <ul>
+              <li>
+                <a href={`${config.baseurl}/tag/aktuality`}>
+                  Všechny aktuality
+                </a>
+              </li>
+              <li>
+                <a href={`${config.baseurl}/tag/pozvanky`}>Pozvánky</a>
+              </li>
+              <li>
+                <a href={`${config.baseurl}/tag/stalo-se`}>Stalo se</a>
+              </li>
+              <li>
+                <a href={`${config.baseurl}/tag/zavody`}>Závody</a>
+              </li>
+            </ul>
+          </div>
+        </li>
+        <li className="no-padding">
+          <a className="collapsible-header waves-effect waves-teal">Články</a>
+          <div className="collapsible-body">
+            <ul>
+              <li>
+                <a href={`${config.baseurl}/tag/clanky`}>Všechny články</a>
+              </li>
+              <li>
+                <a href={`${config.baseurl}/tag/programovani`}>Programování</a>
+              </li>
+              <li>
+                <a href={`${config.baseurl}/tag/mikroprocesory`}>
+                  Mikroprocesory
+                </a>
+              </li>
+              <li>
+                <a href={`${config.baseurl}/tag/konstrukce`}>Konstrukce</a>
+              </li>
+              <li>
+                <a href={`${config.baseurl}/tag/technicke-clanky`}>
+                  Technické články
+                </a>
+              </li>
+              <li>
+                <a href={`${config.baseurl}/tag/zajimavosti`}>ZajĂ­mavosti</a>
+              </li>
+            </ul>
+          </div>
+        </li>
+        <li className="no-padding">
+          <a className="collapsible-header waves-effect waves-teal">O nás</a>
+          <div className="collapsible-body">
+            <ul>
+              <li>
+                <a href={`${config.baseurl}/clanek/o-radioklubu`}>
+                  O Radioklubu
+                </a>
+              </li>
+              <li>
+                <a href={`${config.baseurl}/clanek/kontakt`}>Kontakt</a>
+              </li>
+              <li>
+                <a href={`${config.baseurl}/clanek/krouzek-mladeze`}>
+                  Kroužek mládeže
+                </a>
+              </li>
+              <li>
+                <a href={`${config.baseurl}/clanek/pro-cleny-rk`}>
+                  Pro ÄŤleny RK
+                </a>
+              </li>
+            </ul>
+          </div>
+        </li>
+      </ul>
+
+      {/* Lower nav */}
+      <nav className="subnav subnav1 hide-on-small-and-down" role="navigation">
+        <div className="nav-wrapper container">
+          <ul className="right">
+            <li>
+              <a href={`${config.baseurl}/clanek/o-radioklubu`}>O Radioklubu</a>
+            </li>
+            <li>
+              <a href={`${config.baseurl}/clanek/kontakt`}>Kontakt</a>
+            </li>
+            <li>
+              <a href={`${config.baseurl}/clanek/krouzek-mladeze`}>
+                Kroužek mládeže
+              </a>
+            </li>
+            <li>
+              <a href={`${config.baseurl}/clanek/pro-cleny-rk`}>Pro ÄŤleny RK</a>
+            </li>
+          </ul>
+        </div>
+      </nav>
+      {/* Lower nav 2 - Aktuality */}
+      <nav className="subnav subnav2 hide-on-small-and-down" role="navigation">
+        <div className="nav-wrapper container">
+          <ul className="right">
+            <li>
+              <a href={`${config.baseurl}/tag/aktuality`}>Všechny aktuality</a>
+            </li>
+            <li>
+              <a href={`${config.baseurl}/tag/pozvanky`}>Pozvánky</a>
+            </li>
+            <li>
+              <a href={`${config.baseurl}/tag/stalo-se`}>Stalo se</a>
+            </li>
+            <li>
+              <a href={`${config.baseurl}/tag/zavody`}>Závody</a>
+            </li>
+          </ul>
+        </div>
+      </nav>
+      {/* Lower nav 3 - Články */}
+      <nav className="subnav subnav3 hide-on-small-and-down" role="navigation">
+        <div className="nav-wrapper container">
+          <ul className="right">
+            <li>
+              <a href={`${config.baseurl}/tag/clanky`}>Všechny články</a>
+            </li>
+            <li>
+              <a href={`${config.baseurl}/tag/programovani`}>Programování</a>
+            </li>
+            <li>
+              <a href={`${config.baseurl}/tag/mikroprocesory`}>
+                Mikroprocesory
+              </a>
+            </li>
+            <li>
+              <a href={`${config.baseurl}/tag/konstrukce`}>Konstrukce</a>
+            </li>
+            <li>
+              <a href={`${config.baseurl}/tag/technicke-clanky`}>
+                Technické články
+              </a>
+            </li>
+            <li>
+              <a href={`${config.baseurl}/tag/zajimavosti`}>ZajĂ­mavosti</a>
+            </li>
+          </ul>
+        </div>
+      </nav>
+      <Search />
+
+      {children}
+
+      <footer className="page-footer blue">
+        <div className="container s6">
+          <a
+            className="orange-text text-lighten-3"
+            target="_blank"
+            href="https://git.ok1kvk.cz/"
+          >
+            Gitlab
+          </a>{' '}
+          |{' '}
+          <a
+            className="orange-text text-lighten-3"
+            target="_blank"
+            href="http://forum.ok1kvk.cz/"
+          >
+            FĂłrum
+          </a>{' '}
+          |{' '}
+          <a className="orange-text text-lighten-3" href="/clanek/webkamera/">
+            Webkamera
+          </a>{' '}
+          |{' '}
+          <a
+            className="orange-text text-lighten-3"
+            href="/clanek/jak-nahlasit-chybu/"
+          >
+            Jak nahlásit chybu
+          </a>{' '}
+          <div style={{ display: 'inline-block', height: '1em' }}>
+            <a
+              style={{
+                display: 'block',
+                float: 'left',
+                marginTop: '-.5em',
+              }}
+              target="_blank"
+              href="https://www.facebook.com/ok1kvk"
+            >
+              <SymbolFacebook color="white" size="2em" />
+            </a>
+          </div>
+        </div>
+        <div className="container s6">
+          Stránky pro OK1KVK vytvořila{' '}
+          <a
+            className="orange-text text-lighten-3"
+            target="_blank"
+            href="https://codewitchbella.com"
+          >
+            Isabella Skořepová
+          </a>{' '}
+          2015-2020
+        </div>
+      </footer>
+
+      <script src={`${config.baseurl}/theme/js/materialize.js`}></script>
+      <script src={`${config.baseurl}/theme/js/init.js`}></script>
+
+      <script
+        dangerouslySetInnerHTML={{
+          __html: `
+      (function($) {
+      var onresize = function(){
+        $("footer").height("auto");
+        $("#content").css("margin-bottom",$("footer").height());
+      };
+      $(window).resize(onresize);
+      $(document).load(onresize);
+      $(document).ready(onresize);
+    })(jQuery);`,
+        }}
+      />
+      {config.debug ? null : (
+        <script
+          dangerouslySetInnerHTML={{
+            __html: `
+        (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+        (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+        m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+        })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+
+        ga('create', 'UA-74646565-1', 'auto');
+        ga('send', 'pageview');
+      `,
+          }}
+        />
+      )}
+      <script
+        src={`${config.baseurl}/theme/lightbox2/dist/js/lightbox.min.js`}
+      ></script>
+    </div>
+  )
+}
diff --git a/theme-source/partials/base.js b/theme-source/partials/base.js
deleted file mode 100644
index f9cc8f62..00000000
--- a/theme-source/partials/base.js
+++ /dev/null
@@ -1,363 +0,0 @@
-import React from 'react'
-import { SymbolSearch, SymbolMenu, SymbolFacebook } from './svgs'
-import { Search } from './search'
-
-export function Base(props) {
-  const { config, children, footerlink = null, ...htmlProps } = props
-  return (
-    <Html {...htmlProps} config={config}>
-      {/* % block bodyattr %}{% endblock % */}
-      <div id="all">
-        {/* Top nav */}
-        <nav
-          className="supernav"
-          role="navigation"
-          style={{ zIndex: 5, position: 'absolute' }}
-        >
-          <div className="nav-wrapper container">
-            <a
-              data-activates="nav-mobile"
-              className="menu-icon hide-on-med-and-up"
-              style={{ float: 'left' }}
-            >
-              <SymbolMenu color="#fff" size={false} />
-            </a>
-            <div style={{ zIndex: 2 }} className="brand-logo">
-              <a id="logo-container" href={`${config.baseurl}/tag/vse/1/`}>
-                <img
-                  className="hide-on-small-and-up"
-                  src={`${config.baseurl}/theme/icon56.png`}
-                />
-                <img
-                  className="hide-on-med-and-up hide-on-ultra-small"
-                  src={`${config.baseurl}/theme/logo56.png`}
-                />
-                <img
-                  className="hide-on-small-only"
-                  src={`${config.baseurl}/theme/logo64.png`}
-                />
-              </a>
-            </div>
-            <div className="toplinks">
-              <a className="hide-on-small-only left" id="aktuality">
-                Aktuality
-              </a>
-              <a className="hide-on-small-only left" id="clanky">
-                Články
-              </a>
-              <a className="hide-on-small-only left" id="o-nas">
-                O nás
-              </a>
-              <a className="search-icon left">
-                <SymbolSearch color="#fff" size={false} />
-              </a>
-            </div>
-          </div>
-        </nav>
-        <script
-          dangerouslySetInnerHTML={{
-            __html: `
-      $(document).ready(function() {
-        $(".menu-icon").sideNav();
-        $("#o-nas").click(function() {
-          var body = $("body");
-          body.removeClass("show-subnav2");
-          body.removeClass("show-subnav3");
-          if(body.hasClass("show-subnav1"))
-            body.removeClass("show-subnav1");
-          else
-            body.addClass("show-subnav1");
-        });
-        $("#aktuality").click(function() {
-          var body = $("body");
-          body.removeClass("show-subnav1");
-          body.removeClass("show-subnav3");
-          if(body.hasClass("show-subnav2"))
-            body.removeClass("show-subnav2");
-          else
-            body.addClass("show-subnav2");
-        });
-        $("#clanky").click(function() {
-          var body = $("body");
-          body.removeClass("show-subnav1");
-          body.removeClass("show-subnav2");
-          if(body.hasClass("show-subnav3"))
-            body.removeClass("show-subnav3");
-          else
-            body.addClass("show-subnav3");
-        })
-        $("body").click(function(e) {
-          if(!($(e.target).parents("a").length || $(e.target).is("a"))) {
-            $("body").removeClass("show-subnav2").removeClass("show-subnav1");
-          }
-        });
-      });
-    `,
-          }}
-        />
-        {/* Side nav */}
-        <ul id="nav-mobile" className="side-nav collapsible">
-          <li className="no-padding">
-            <a className="collapsible-header waves-effect waves-teal">
-              Aktuality
-            </a>
-            <div className="collapsible-body">
-              <ul>
-                <li>
-                  <a href={`${config.baseurl}/tag/aktuality`}>
-                    Všechny aktuality
-                  </a>
-                </li>
-                <li>
-                  <a href={`${config.baseurl}/tag/pozvanky`}>Pozvánky</a>
-                </li>
-                <li>
-                  <a href={`${config.baseurl}/tag/stalo-se`}>Stalo se</a>
-                </li>
-                <li>
-                  <a href={`${config.baseurl}/tag/zavody`}>Závody</a>
-                </li>
-              </ul>
-            </div>
-          </li>
-          <li className="no-padding">
-            <a className="collapsible-header waves-effect waves-teal">Články</a>
-            <div className="collapsible-body">
-              <ul>
-                <li>
-                  <a href={`${config.baseurl}/tag/clanky`}>Všechny články</a>
-                </li>
-                <li>
-                  <a href={`${config.baseurl}/tag/programovani`}>
-                    Programování
-                  </a>
-                </li>
-                <li>
-                  <a href={`${config.baseurl}/tag/mikroprocesory`}>
-                    Mikroprocesory
-                  </a>
-                </li>
-                <li>
-                  <a href={`${config.baseurl}/tag/konstrukce`}>Konstrukce</a>
-                </li>
-                <li>
-                  <a href={`${config.baseurl}/tag/technicke-clanky`}>
-                    Technické články
-                  </a>
-                </li>
-                <li>
-                  <a href={`${config.baseurl}/tag/zajimavosti`}>ZajĂ­mavosti</a>
-                </li>
-              </ul>
-            </div>
-          </li>
-          <li className="no-padding">
-            <a className="collapsible-header waves-effect waves-teal">O nás</a>
-            <div className="collapsible-body">
-              <ul>
-                <li>
-                  <a href={`${config.baseurl}/clanek/o-radioklubu`}>
-                    O Radioklubu
-                  </a>
-                </li>
-                <li>
-                  <a href={`${config.baseurl}/clanek/kontakt`}>Kontakt</a>
-                </li>
-                <li>
-                  <a href={`${config.baseurl}/clanek/krouzek-mladeze`}>
-                    Kroužek mládeže
-                  </a>
-                </li>
-                <li>
-                  <a href={`${config.baseurl}/clanek/pro-cleny-rk`}>
-                    Pro ÄŤleny RK
-                  </a>
-                </li>
-              </ul>
-            </div>
-          </li>
-        </ul>
-
-        {/* Lower nav */}
-        <nav
-          className="subnav subnav1 hide-on-small-and-down"
-          role="navigation"
-        >
-          <div className="nav-wrapper container">
-            <ul className="right">
-              <li>
-                <a href={`${config.baseurl}/clanek/o-radioklubu`}>
-                  O Radioklubu
-                </a>
-              </li>
-              <li>
-                <a href={`${config.baseurl}/clanek/kontakt`}>Kontakt</a>
-              </li>
-              <li>
-                <a href={`${config.baseurl}/clanek/krouzek-mladeze`}>
-                  Kroužek mládeže
-                </a>
-              </li>
-              <li>
-                <a href={`${config.baseurl}/clanek/pro-cleny-rk`}>
-                  Pro ÄŤleny RK
-                </a>
-              </li>
-            </ul>
-          </div>
-        </nav>
-        {/* Lower nav 2 - Aktuality */}
-        <nav
-          className="subnav subnav2 hide-on-small-and-down"
-          role="navigation"
-        >
-          <div className="nav-wrapper container">
-            <ul className="right">
-              <li>
-                <a href={`${config.baseurl}/tag/aktuality`}>
-                  Všechny aktuality
-                </a>
-              </li>
-              <li>
-                <a href={`${config.baseurl}/tag/pozvanky`}>Pozvánky</a>
-              </li>
-              <li>
-                <a href={`${config.baseurl}/tag/stalo-se`}>Stalo se</a>
-              </li>
-              <li>
-                <a href={`${config.baseurl}/tag/zavody`}>Závody</a>
-              </li>
-            </ul>
-          </div>
-        </nav>
-        {/* Lower nav 3 - Články */}
-        <nav
-          className="subnav subnav3 hide-on-small-and-down"
-          role="navigation"
-        >
-          <div className="nav-wrapper container">
-            <ul className="right">
-              <li>
-                <a href={`${config.baseurl}/tag/clanky`}>Všechny články</a>
-              </li>
-              <li>
-                <a href={`${config.baseurl}/tag/programovani`}>Programování</a>
-              </li>
-              <li>
-                <a href={`${config.baseurl}/tag/mikroprocesory`}>
-                  Mikroprocesory
-                </a>
-              </li>
-              <li>
-                <a href={`${config.baseurl}/tag/konstrukce`}>Konstrukce</a>
-              </li>
-              <li>
-                <a href={`${config.baseurl}/tag/technicke-clanky`}>
-                  Technické články
-                </a>
-              </li>
-              <li>
-                <a href={`${config.baseurl}/tag/zajimavosti`}>ZajĂ­mavosti</a>
-              </li>
-            </ul>
-          </div>
-        </nav>
-        <Search />
-
-        {children}
-
-        <footer className="page-footer blue">
-          <div className="container s6">
-            <a
-              className="orange-text text-lighten-3"
-              target="_blank"
-              href="https://git.ok1kvk.cz/"
-            >
-              Gitlab
-            </a>{' '}
-            |{' '}
-            <a
-              className="orange-text text-lighten-3"
-              target="_blank"
-              href="http://forum.ok1kvk.cz/"
-            >
-              FĂłrum
-            </a>{' '}
-            |{' '}
-            <a className="orange-text text-lighten-3" href="/clanek/webkamera/">
-              Webkamera
-            </a>{' '}
-            |{' '}
-            <a
-              className="orange-text text-lighten-3"
-              href="/clanek/jak-nahlasit-chybu/"
-            >
-              Jak nahlásit chybu
-            </a>{' '}
-            {footerlink}{' '}
-            <div style={{ display: 'inline-block', height: '1em' }}>
-              <a
-                style={{
-                  display: 'block',
-                  float: 'left',
-                  marginTop: '-.5em',
-                }}
-                target="_blank"
-                href="https://www.facebook.com/ok1kvk"
-              >
-                <SymbolFacebook color="white" size="2em" />
-              </a>
-            </div>
-          </div>
-          <div className="container s6">
-            Stránky pro OK1KVK vytvořila{' '}
-            <a
-              className="orange-text text-lighten-3"
-              target="_blank"
-              href="https://codewitchbella.com"
-            >
-              Isabella Skořepová
-            </a>{' '}
-            2015-2020
-          </div>
-        </footer>
-
-        <script src={`${config.baseurl}/theme/js/materialize.js`}></script>
-        <script src={`${config.baseurl}/theme/js/init.js`}></script>
-
-        <script
-          dangerouslySetInnerHTML={{
-            __html: `
-      (function($) {
-      var onresize = function(){
-        $("footer").height("auto");
-        $("#content").css("margin-bottom",$("footer").height());
-      };
-      $(window).resize(onresize);
-      $(document).load(onresize);
-      $(document).ready(onresize);
-    })(jQuery);`,
-          }}
-        />
-        {config.debug ? null : (
-          <script
-            dangerouslySetInnerHTML={{
-              __html: `
-        (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
-        (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
-        m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
-        })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
-
-        ga('create', 'UA-74646565-1', 'auto');
-        ga('send', 'pageview');
-      `,
-            }}
-          />
-        )}
-        <script
-          src={`${config.baseurl}/theme/lightbox2/dist/js/lightbox.min.js`}
-        ></script>
-      </div>
-    </Html>
-  )
-}
diff --git a/theme-source/render-to-string.js b/theme-source/render-to-string.js
new file mode 100644
index 00000000..2d93c66d
--- /dev/null
+++ b/theme-source/render-to-string.js
@@ -0,0 +1,11 @@
+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/templates/article.js b/theme-source/templates/article.tsx
similarity index 70%
rename from theme-source/templates/article.js
rename to theme-source/templates/article.tsx
index bc08af60..4be9ce3d 100644
--- a/theme-source/templates/article.js
+++ b/theme-source/templates/article.tsx
@@ -1,21 +1,21 @@
+// @ts-check
+
 import React from 'react'
 import url from 'url'
-import { Base } from '../partials/base'
+import ReactDOM from 'react-dom/server'
+import { BaseBody } from '../partials/base-body'
 import { dateFilter, truncate, striptags } from '../utils'
+import { renderToString } from '../render-to-string'
 
-export default function Article({
-  metadata,
-  content,
-  config,
-  file,
-  canonicalURL,
-}) {
-  return (
-    <Base
-      metadata={metadata}
-      config={config}
-      canonicalURL={canonicalURL}
-      prehead={
+export default {
+  type: 'template',
+  ext: 'html',
+  value: props => {
+    const { metadata, canonicalURL, content } = props
+    return renderToString(<Article {...props} />, {
+      ...props,
+      bodyClassName: 'type-article',
+      prehead: (
         <>
           <meta property="og:type" content="article" />
           <meta
@@ -41,31 +41,31 @@ export default function Article({
             }
           />
         </>
-      }
-      head={
+      ),
+      head: (
         <>
           <script
             type="text/x-mathjax-config"
             dangerouslySetInnerHTML={{
               __html: `
-              MathJax.Hub.Config({
-                extensions: ["tex2jax.js"],
-                jax: ["input/TeX", "output/HTML-CSS"],
-                tex2jax: {
-                  inlineMath: [ ['$','$'], ["\\\\(","\\\\)"] ],
-                  displayMath: [ ['$$','$$'], ["\\\\[","\\\\]"] ],
-                  processEscapes: true
-                },
-                elements: $("#content article"),
-                "HTML-CSS": { availableFonts: ["TeX"] },
-                locale: "cs",
-                menuSettings: {
-                  zoom: "Hover"
-                },
-                MathEvents: {
-                  hover: 0
-                }
-              });`,
+            MathJax.Hub.Config({
+              extensions: ["tex2jax.js"],
+              jax: ["input/TeX", "output/HTML-CSS"],
+              tex2jax: {
+                inlineMath: [ ['$','$'], ["\\\\(","\\\\)"] ],
+                displayMath: [ ['$$','$$'], ["\\\\[","\\\\]"] ],
+                processEscapes: true
+              },
+              elements: $("#content article"),
+              "HTML-CSS": { availableFonts: ["TeX"] },
+              locale: "cs",
+              menuSettings: {
+                zoom: "Hover"
+              },
+              MathEvents: {
+                hover: 0
+              }
+            });`,
             }}
           />
           <script
@@ -74,9 +74,14 @@ export default function Article({
             src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_CHTML&"
           ></script>
         </>
-      }
-      bodyClassName="type-article"
-    >
+      ),
+    })
+  },
+}
+
+function Article({ metadata, content, config, file }) {
+  return (
+    <BaseBody config={config}>
       <div className="section container" id="content">
         <div className="row">&nbsp;</div>
         <div className="metadata">
@@ -121,6 +126,6 @@ export default function Article({
           <p dangerouslySetInnerHTML={{ __html: content }} />
         </article>
       </div>
-    </Base>
+    </BaseBody>
   )
 }
diff --git a/theme-source/templates/tag.js b/theme-source/templates/tag.js
deleted file mode 100644
index 0ab71959..00000000
--- a/theme-source/templates/tag.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import React from 'react'
-import { Base } from '../partials/base'
-import { List as ListPartial } from '../partials/list'
-
-export default function List(props) {
-  return (
-    <Base
-      canonicalURL={props.canonicalURL}
-      head={<meta property="og:type" content="website" />}
-      config={props.config}
-    >
-      <ListPartial {...props} />
-    </Base>
-  )
-}
diff --git a/theme-source/templates/tag.tsx b/theme-source/templates/tag.tsx
new file mode 100644
index 00000000..6ffe7f03
--- /dev/null
+++ b/theme-source/templates/tag.tsx
@@ -0,0 +1,17 @@
+import React from 'react'
+import { BaseBody } from '../partials/base-body'
+import { List as ListPartial } from '../partials/list'
+import { renderToString } from '../render-to-string'
+
+export default {
+  type: 'template',
+  ext: 'html',
+  value: props => {
+    return renderToString(
+      <BaseBody config={props.config}>
+        <ListPartial {...props} />
+      </BaseBody>,
+      { ...props, head: <meta property="og:type" content="website" /> },
+    )
+  },
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 00000000..90bb945e
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,66 @@
+{
+  "compilerOptions": {
+    /* Basic Options */
+    // "incremental": true,                   /* Enable incremental compilation */
+    "target": "esnext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */,
+    "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
+    // "lib": [],                             /* Specify library files to be included in the compilation. */
+    "allowJs": true /* Allow javascript files to be compiled. */,
+    // "checkJs": true,                       /* Report errors in .js files. */
+    "jsx": "preserve" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */,
+    // "declaration": true,                   /* Generates corresponding '.d.ts' file. */
+    // "declarationMap": true,                /* Generates a sourcemap for each corresponding '.d.ts' file. */
+    // "sourceMap": true,                     /* Generates corresponding '.map' file. */
+    // "outFile": "./",                       /* Concatenate and emit output to single file. */
+    // "outDir": "./",                        /* Redirect output structure to the directory. */
+    // "rootDir": "./",                       /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
+    // "composite": true,                     /* Enable project compilation */
+    // "tsBuildInfoFile": "./",               /* Specify file to store incremental compilation information */
+    // "removeComments": true,                /* Do not emit comments to output. */
+    "noEmit": true /* Do not emit outputs. */,
+    // "importHelpers": true,                 /* Import emit helpers from 'tslib'. */
+    // "downlevelIteration": true,            /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
+    // "isolatedModules": true,               /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
+
+    /* Strict Type-Checking Options */
+    "strict": true /* Enable all strict type-checking options. */,
+    // "noImplicitAny": true,                 /* Raise error on expressions and declarations with an implied 'any' type. */
+    // "strictNullChecks": true,              /* Enable strict null checks. */
+    // "strictFunctionTypes": true,           /* Enable strict checking of function types. */
+    // "strictBindCallApply": true,           /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
+    // "strictPropertyInitialization": true,  /* Enable strict checking of property initialization in classes. */
+    // "noImplicitThis": true,                /* Raise error on 'this' expressions with an implied 'any' type. */
+    // "alwaysStrict": true,                  /* Parse in strict mode and emit "use strict" for each source file. */
+
+    /* Additional Checks */
+    // "noUnusedLocals": true,                /* Report errors on unused locals. */
+    // "noUnusedParameters": true,            /* Report errors on unused parameters. */
+    // "noImplicitReturns": true,             /* Report error when not all code paths in function return a value. */
+    // "noFallthroughCasesInSwitch": true,    /* Report errors for fallthrough cases in switch statement. */
+
+    /* Module Resolution Options */
+    // "moduleResolution": "node",            /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
+    // "baseUrl": "./",                       /* Base directory to resolve non-absolute module names. */
+    // "paths": {},                           /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
+    // "rootDirs": [],                        /* List of root folders whose combined content represents the structure of the project at runtime. */
+    // "typeRoots": [],                       /* List of folders to include type definitions from. */
+    // "types": [],                           /* Type declaration files to be included in compilation. */
+    // "allowSyntheticDefaultImports": true,  /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
+    "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
+    // "preserveSymlinks": true,              /* Do not resolve the real path of symlinks. */
+    // "allowUmdGlobalAccess": true,          /* Allow accessing UMD globals from modules. */
+
+    /* Source Map Options */
+    // "sourceRoot": "",                      /* Specify the location where debugger should locate TypeScript files instead of source locations. */
+    // "mapRoot": "",                         /* Specify the location where debugger should locate map files instead of generated locations. */
+    // "inlineSourceMap": true,               /* Emit a single file with source maps instead of having a separate file. */
+    // "inlineSources": true,                 /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
+
+    /* Experimental Options */
+    // "experimentalDecorators": true,        /* Enables experimental support for ES7 decorators. */
+    // "emitDecoratorMetadata": true,         /* Enables experimental support for emitting type metadata for decorators. */
+
+    /* Advanced Options */
+    "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
+  }
+}
-- 
GitLab