summaryrefslogtreecommitdiffstats
path: root/site/posts/cleopatra/theme.org
diff options
context:
space:
mode:
Diffstat (limited to 'site/posts/cleopatra/theme.org')
-rw-r--r--site/posts/cleopatra/theme.org573
1 files changed, 573 insertions, 0 deletions
diff --git a/site/posts/cleopatra/theme.org b/site/posts/cleopatra/theme.org
new file mode 100644
index 0000000..9b1a129
--- /dev/null
+++ b/site/posts/cleopatra/theme.org
@@ -0,0 +1,573 @@
+#+TITLE: Layout and Style
+
+#+SERIES: ../cleopatra.html
+#+SERIES_PREV: ./literate-programming.html
+#+SERIES_NEXT: ./soupault.html
+
+#+BEGIN_EXPORT html
+<nav id="generate-toc"></nav>
+<div id="history">site/cleopatra/theme.org</div>
+#+END_EXPORT
+
+* Setup
+
+ As often when it comes to frontend development, we will use several
+ tools hosted in the ~npm~ packages repository. ~npm~ is infamous
+ for downloading lots of files and to store it in the ~node_modules/~
+ directory. We configure *~cleopatra~* accordingly.
+
+ #+begin_src makefile :tangle theme.mk
+CONFIGURE += package.json package-lock.json node_modules
+ #+end_src
+
+* Base CSS
+
+ We know construct piece by piece the “base” CSS layout which we will
+ inject inside a ~<style>~ tag in each web page.
+
+** Layout
+
+ Our goal is to have a three columns layout: one aside menu, with
+ the top-level navigation links (technical articles, news, etc.) and
+ the table of contents of the current pages if relevant, one main
+ area for the webpage content, and a margin column with side notes
+ and margin notes.
+
+ #+caption: Widths of page components (in ~rem~)
+ #+name: widths
+ | Content | 35 |
+ | Gutter | 3 |
+ | Margin | 13 |
+
+ #+name: main-width
+ #+begin_src emacs-lisp :exports none :noweb yes :var widths=widths[,1]
+(nth 0 widths)
+ #+end_src
+
+ #+name: gutter-width
+ #+begin_src emacs-lisp :exports none :noweb yes :var widths=widths[,1]
+(nth 1 widths)
+ #+end_src
+
+ #+name: margin-width
+ #+begin_src emacs-lisp :exports none :noweb yes :var widths=widths[,1]
+(nth 2 widths)
+ #+end_src
+
+ #+begin_src css :tangle style.css :noweb yes
+:root {
+ --main-width: <<main-width()>>rem;
+ --gutter-width: <<gutter-width()>>rem;
+ --margin-width: <<margin-width()>>rem;
+ --code-width: calc(var(--main-width) + var(--gutter-width) + var(--margin-width));
+ --body-width: calc(var(--main-width) + 2 * (var(--gutter-width) + var(--margin-width)));
+}
+ #+end_src
+
+ According to CSS’ own [[https://www.w3.org/TR/css-variables-1/#using-variables][specification]], you cannot use ~var()~ inside
+ media queries. As a consequnece, for our theme to be responsive,
+ the full width of the page content (\im 2 \times (that is,
+ \mathrm{margin\_width} + \mathrm{gutter\_width}) +
+ \mathrm{content\_width} \mi or call_body-width[:results raw]()rem)
+ has to be hard-coded[fn::Fortunately, this is a literate
+ program. This value is actually programmatically computed, so that
+ we do not have to worry about forgetting to update it].
+
+ #+name: body-width
+ #+begin_src bash :exports none :noweb yes
+echo $((2 * (<<margin-width()>> + <<gutter-width()>>) + <<main-width()>>))
+ #+end_src
+
+ #+begin_src css :tangle style.css :noweb yes
+@media (max-width: <<body-width()>>rem) {
+ :root {
+ --body-width: var(--main-width);
+ --code-width: var(--main-width);
+ }
+}
+ #+end_src
+
+ And now, we are free to actually implement the layout.
+
+ #+begin_src css :tangle style.css :noweb yes
+,* {
+ box-sizing: border-box;
+}
+
+.fullwidth {
+ width: var(--body-width);
+}
+
+@media (min-width: <<body-width()>>rem) {
+ .fullwidth {
+ margin-left: calc(-1 * (var(--margin-width) + var(--gutter-width)));
+ }
+}
+
+html {
+ font-size: 1rem;
+}
+
+body {
+ line-height: 1.4;
+ max-width: var(--body-width);
+ margin-left: auto;
+ margin-right: auto;
+}
+
+aside {
+ background: var(--bg);
+ z-index: 9999;
+ width: var(--body-width);
+ align-self: flex-start;
+ position: sticky;
+ top: 0;
+}
+
+aside nav {
+ text-align: center;
+ border-bottom: 1px solid var(--fade);
+}
+
+aside nav ul {
+ list-style: none;
+ padding: 1rem 0;
+ margin: 0;
+}
+
+aside nav li {
+ display: inline;
+}
+
+aside nav li:not(:first-of-type)::before {
+ content: " · ";
+}
+
+main {
+ counter-reset: sidenote-counter;
+ max-width: var(--main-width);
+ margin: auto;
+}
+
+main nav {
+ font-style: italic;
+ color: var(--fg-plus);
+ background: var(--current-line);
+ padding: .5rem 1rem;
+}
+
+main nav .series-next {
+ text-align: right;
+}
+
+main nav p.series-next::after {
+ content: " →";
+}
+
+main nav p.series-prev::before {
+ content: "← ";
+}
+
+img {
+ max-width: 100%;
+}
+
+#whoami.marginnote {
+ color: var(--fg);
+ margin-bottom: 2em;
+}
+
+img.avatar {
+ border-radius: 20px;
+ display: block;
+ max-width: 90%;
+ margin: auto;
+}
+
+dd {
+ margin-left: 0;
+ margin-bottom: 0.5rem;
+}
+
+.sidenote,
+.marginnote {
+ font-size: smaller;
+ position: relative;
+ width: var(--margin-width);
+}
+
+.sidenote {
+ margin-right: calc(-1 * (var(--margin-width) + var(--gutter-width)));
+ float: right;
+ clear: right;
+}
+
+.marginnote {
+ float: left;
+ clear: left;
+ margin-left: calc(-1 * (var(--margin-width) + var(--gutter-width)));
+}
+
+input.margin-toggle {
+ display: none;
+}
+
+label.sidenote-number {
+ display: inline;
+}
+
+label.margin-toggle:not(.sidenote-number) {
+ display: none;
+}
+
+.sidenote-number:after,
+.sidenote:before {
+ position: relative;
+ vertical-align: baseline;
+}
+
+.sidenote-number {
+ counter-increment: sidenote-counter;
+}
+
+.sidenote-number::after {
+ content: "(" counter(sidenote-counter, lower-greek) ")";
+ font-size: 60%;
+ top: -0.4rem;
+ left: 0.1rem;
+}
+
+.sidenote::before {
+ content: "(" counter(sidenote-counter, lower-greek) ")";
+ font-size: 70%;
+ top: -0.5rem;
+ right: 0.1rem;
+}
+
+div.code,
+pre {
+ width: var(--code-width);
+ overflow-x: auto;
+ overflow-y: hidden;
+ padding: 1rem 2rem;
+}
+
+main {
+ padding-top: 4.2rem;
+ padding-bottom: 4.2rem;
+}
+
+h1 {
+ text-align: center;
+}
+
+h2, h3, h4 {
+ font-style: italic;
+}
+
+h1, h2, h3, h4 {
+ color: var(--doc);
+ font-family: serif;
+ font-weight: normal;
+}
+
+dt {
+ font-weight: bold;
+}
+
+div.code,
+span.inlinecode,
+pre,
+tt,
+.dmath,
+.imath {
+ font-family: monospace;
+ font-size: 85%;
+}
+
+details {
+ margin: 1.5rem 0;
+}
+
+table {
+ border-top: 2px solid var(--fg);
+ border-bottom: 2px solid var(--fg);
+ border-collapse: collapse;
+ width: 100%;
+ margin: 1.5rem 0;
+}
+
+th {
+ font-weight: normal;
+ text-transform: uppercase;
+}
+
+td,
+th {
+ border-top: 1px solid var(--fade);
+ height: 2em;
+ padding: 0 1em;
+}
+
+td.date,
+td.commit {
+ text-align: center;
+ font-size: 0.75em;
+ font-family: monospace;
+}
+
+/* max-width has to be equal to --body-width */
+@media (max-width: <<body-width()>>rem) {
+ body {
+ padding: 2rem;
+ margin: auto;
+ display: block;
+ }
+
+ aside {
+ width: var(--main-width);
+ margin: auto;
+ }
+
+ label.margin-toggle:not(.sidenote-number) {
+ display: inline;
+ }
+
+ .sidenote,
+ .marginnote {
+ display: none;
+ }
+
+ .margin-toggle:checked + .sidenote,
+ .margin-toggle:checked + .marginnote {
+ display: block;
+ float: left;
+ left: 1rem;
+ clear: both;
+ width: 95%;
+ margin: 1rem 2.5%;
+ vertical-align: baseline;
+ position: relative;
+ }
+
+ label {
+ cursor: pointer;
+ }
+
+ pre, aside, div.code {
+ width: 100%;
+ }
+}
+ #+end_src
+
+** Colors
+
+ #+begin_src css :tangle style.css
+:root {
+ --bg: white;
+ --bg-plus: #f7f7f7;
+ --current-line: #fbfbfb;
+ --fade: #cfcecb;
+ --fg: #3c3c3c;
+ --fg-plus: #575757;
+ --doc: black;
+ --warning: #bd745e;
+ --red: #b3534b;
+ --green: #6d9319;
+ --yellow: #d4b100;
+}
+ #+end_src
+
+ #+begin_src css :tangle style.css
+body {
+ font-family: sans-serif;
+ color: var(--fg);
+ background: var(--bg);
+}
+
+h2 a.anchor-link,
+h3 a.anchor-link,
+h4 a.anchor-link {
+ display: none;
+ font-style: normal;
+ text-decoration: none;
+ font-family: monospace;
+ font-size: smaller;
+ color: var(--doc);
+}
+
+[id] {
+ scroll-margin-top: 4rem;
+}
+
+h2:hover a.anchor-link,
+h3:hover a.anchor-link,
+h4:hover a.anchor-link {
+ display: inline;
+}
+
+.sidenote,
+.marginnote {
+ color: var(--fg-plus);
+}
+
+pre,
+code,
+div.code,
+span.inlinecode,
+tt {
+ color: var(--doc);
+}
+ #+end_src
+
+** Coq
+
+ #+begin_src css :tangle style.css
+div.code {
+ white-space: nowrap;
+}
+
+div.code,
+span.inlinecode {
+ font-family : monospace;
+}
+
+.paragraph {
+ margin-bottom : .8em;
+}
+
+.code a[href] {
+ color : inherit;
+ text-decoration : none;
+ background : var(--bg-plus);
+ padding : .1rem .15rem .1rem .15rem;
+ border-radius : 15%;
+}
+
+.code .icon {
+ display: none;
+}
+#+END_SRC
+
+** Icons
+
+ #+begin_src css :tangle style.css
+.icon svg {
+ display: inline;
+ width: 1em;
+ height: .9em;
+ vertical-align: text-top;
+}
+
+a[href], .margin-toggle {
+ color: #0000ee;
+}
+
+a[href] .icon svg {
+ fill: #0000ee;
+}
+
+a[href]:visited {
+ color: #25008b;
+}
+
+a[href]:visited .icon svg {
+ fill: #25008b;
+}
+
+.url-mark.fa {
+ display: inline;
+ font-size: 90%;
+ width: 1em;
+}
+
+.url-mark.fa-github::before {
+ content: "\00a0\f09b";
+}
+
+.url-mark.fa-external-link::before {
+ content: "\00a0\f08e";
+}
+ #+end_src
+
+** Minify CSS
+
+ #+begin_src shell :shebang #!/bin/bash :tangle scripts/css.sh
+minify="$(npm bin)/minify"
+normalize="$(npm root)/normalize.css/normalize.css"
+style="style.css"
+
+# minify add a newline character at the end of its input
+# we remove it using `head'
+echo "
+@charset \"UTF-8\";
+$(cat ${normalize})
+$(cat ${style})
+" | ${minify} --css | head -c -1 > style.min.css
+ #+end_src
+
+ #+begin_src makefile :tangle theme.mk
+style.min.css : style.css dependencies-prebuild
+ @cleopatra echo "Minifying" "CSS"
+ @scripts/css.sh
+
+ARTIFACTS += style.min.css
+
+theme-build : style.min.css
+ #+end_src
+
+* HTML Templates
+
+ It would be best if we had a preprocessing step to inject the
+ minified style, rather than using ~soupault~ to do the work once per
+ page.
+
+ #+begin_src html :tangle templates/main.html :noweb yes
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <style></style>
+ <link href="https://soap.coffee/+vendors/katex.0.11.1+swap/katex.css" rel="stylesheet" media="none" onload="if(media!='all')media='all'">
+ <title></title>
+ </head>
+ <body>
+ <aside>
+ <nav>
+ <ul>
+ <li>
+ <a href="/">Technical Posts</a>
+ </li>
+ <li>
+ <a href="/opinions">Opinions</a>
+ </li>
+ <li>
+ <a href="/news">News</a>
+ </li>
+ </ul>
+ </nav>
+ </aside>
+ <main>
+ <span id="whoami" class="marginnote">
+ <img class="avatar" src="/img/vampy.jpg" />
+
+ <p>
+ Hi, I’m <strong>lthms</strong>.
+ </p>
+
+ <p>
+ I don’t like syntax highlighting, but I like
+ types and functional programming languages.
+ He/him.
+ </p>
+
+ <p>
+ Interested in starting a discussion? Don’t hesitate to <a
+ href="mailto:~lthms/public-inbox@lists.sr.ht">shoot me an
+ email</a>.
+ </p>
+ </span>
+ </main>
+ </body>
+</html>
+ #+end_src