CSS Styling

CSS isn’t my strong suit. I generally find myself having to re-learn it every time I want to make a substantial change to this website. In an effort to leave notes to my future self, I’ve decided to use literate programming to annotate my CSS file.

This page is tangled during the build process of my site by a custom pandoc compiler. Thanks to the power of Hakyll, this takes only 4 lines of code. Due to the ‘C’ in CSS, the order of code blocks is significant in this file. Since the pandoc compiler simply strips anything that isn’t a code block, and doesn’t perform proper tangling, I’m required to introduce code blocks in the order they must appear in the output. One day I hope to replace this with a full tangling system.

Colours

I find it’s easiest to specify the site’s colours globally, in a set of variables. We can attach them to the root node, and place this definition first such that it is inherited by the other rules:

:root {
  --light: #f5fffA;
  --dark: #001604;
  --hl1: #00a871;
  --hl2: #335839;

  --fg: var(--dark);
  --bg: var(--light);
}

While I don’t use dark mode myself, it’s certainly a nice thing to provide for others. My light and dark colours have a high relative contrast, so I’ve decided I can get away with just swapping them:

@media (prefers-color-scheme: dark) {
  :root {
    --fg: var(--light);
    --bg: var(--dark);
  }
} 

Now let’s set the foreground and background colours in the document body. While we’re at it, we’ll set some font defaults, and ensure there’s no default margin on the body:

body {
  margin: 0;
  background-color: var(--bg);
  color: var(--fg);
  font-size: 1.4em;
  font: -apple-system-body;
  font-family: serif;
  line-height: 1.3em;
}

The font: -apple-system-body tells Apple devices to use the default system font. This is useful if the user has changed the size of their font to something larger, as it overrides font-size. Naturally, it also overrides font-family, so we make sure to set our preferred font immediately afterwards.

Now to style the header at the top of the page.

We give it a contrasting colour, add some chunky horizontal padding, and give everything a nice sans serif:

header {
  overflow: hidden;
  background-color: var(--fg);
  left: 0;
  padding: 4px 30px;
  font-family: sans-serif;
}

The “logo,” being my URL and acting as a home button, goes on the left in bold.

.logo {
  float: left;
  font-weight: bold;
}

The other navigation links go on the right.

.header-nav {
  float: right;
}

When on small screens, we progressively move the navigation links below the home link:

@media screen and (max-width: 550px) {
  header .logo {
    float: none;
    display: block;
    text-align: left;
  }

  .header-nav {
    float: none;
  }
}

Front Page Style

The front page features a large, bright green box with my name in it:

.hero {
  margin: auto;
  font-size: 2em;
  line-height: 1em;
  padding: 1em;
  border: 1px none var(--fg);
  border-bottom-style: solid;
  background-color: var(--hl1);
}

I’m the most important person on this site so my name gets to be in bold. On mobile screens, we want to wrap my name so it doesn’t go past the edge of the screen:

.heroText {
  font-weight: bold;
  overflow-wrap: anywhere;
}

And when you hover on it, it switches into the IPA pronunciation of my name:

.heroText span {
  display: table;
  margin: 0 auto;
  line-height: 1em;
}

h1.heroText:hover span {
  display: none;
}
h1.heroText:hover:before {
  content:'/ˈʤeɪkəb ˈwɔːltəz/';
  display:table;
  margin:0 auto;
  line-height: 1em;
}

Main Content Style

I’ve defined the main body in a mobile-first manner. All we enforce here is 1em padding, and a sans serif font for the links in the footer. To separate the footer from the page content, we want a horizonal line above and below it:

.main {
  margin: auto;
  padding: 1em;
}
footer {
  margin: auto;
  padding: 1em;
  margin-bottom: 1em;
  font-family: sans-serif;
  border: 1px solid var(--fg);
  border-style: solid none;
}

When the screen width is large enough, we introduce vertical rules to keep the content at a fixed, readable width:

@media only screen and (min-width: 768px) {
  .main {
    width: 50%;
    min-width: calc(768px - 2em);
    border: 1px solid var(--fg);
    border-style: none solid solid solid;
  }
  footer {
    width: 50%;
    min-width: calc(768px - 2em);
    border: 1px solid var(--fg);
    border-style: none solid solid solid;
  }
}

I don’t want the skip-to-content button to be visible by default. (You can still access it by pressing tab):

.skip {
  left: 50%;
  position: absolute;
  transform: translateY(-200%);
}

Let’s set images to fit within the borders:

img {
  max-width: 100%;
  margin-left: auto;
  margin-right: auto;
  display: block;
}

And let’s extend horizontal rules to touch both sides, and be 1px wide:

hr {
  width: calc(100% + 2em);
  margin-left: -1em;
  border: 0px solid var(--fg);
  border-top: 1px solid var(--fg);
}

The horizontal rules are mainly as a hack around the fact that pandoc inserts one before footnotes. I couldn’t be bothered to find out why, and the resulting look works well enough for me.

For contrast, I’ve set headings to sans serif:

h1, h2, h3, h4, h5, h6 {
  font-family: sans-serif;
}

And links are also green:

a:link {
  text-decoration: none;
  position: relative;
  font-family: sans-serif;
  color: var(--hl1);
}
a:visited {
  text-decoration:none;
  position:relative;
  font-family:sans-serif;
  color: var(--hl1);
}

Code Blocks

First, we want to set the text size of code snippets to match the body:

code, .verbatim {
  font-size: 1.4em;
}

Let’s also extend code blocks to the full width of the body, and give them some nice colours:

pre {
  width: calc(100% + 1.4em);
  margin: 0 auto;
  margin-left: -1.7em;
  overflow: auto;
  color: var(--bg);
  background-color: var(--fg);
  padding: 1em;
  font-size: 0.8em;
}

This does most of the job, but there’s no right padding on the code content. This is a general “feature” with scrolling overflow, and we can fix it by manually adding a margin to the child elements:

pre > code {
  margin-right:1em;
  display: inline-block;
}

At some point I’ll get around to adding syntax highlighting. This will get put here.

The cookie banner is a rounded bar at the bottom of the page, in inverted colours to make it (vaguely) stand out.

#cookie_bar {
  width: calc(100% - 10px);
  z-index: 999999999;
  position: fixed;
  left: 0;
  bottom: 0;
  background: var(--fg);
  color: var(--bg);
  font-size: 14px;
  margin: 5px;
  padding: 0px;
  font-family: sans-serif;
  line-height: 20px;
  box-sizing: border-box;
  border-radius: 16px;
  display: flex;
  flex-flow: row wrap;
  align-items: center;
}
#cookie_bar_text {
  margin: 0px 15px;
  padding: 5px 0px;
}
#cookie_bar_buttons {
  padding: 5px 5px;
  flex-grow: 1;
}

The “manage cookies” page appears as a side panel, with a slight shadow to distinguish it from the main content. Obviously, it should be hidden by default:

#cookie_settings {
  width: 100%;
  height: 100vh;
  max-width: 360px;
  z-index: 999999999;
  position: fixed;
  left: 0;
  top: 0;
  background: inherit;
  box-shadow: 5px 0px 10px grey;
  font-size: 14px;
  padding: 5px 2% 10px 2%;
  font-family: sans-serif;
  line-height: 24px;
  box-sizing: border-box;
  overflow-y: auto;
  display: inline-block;
  visibility: hidden;
}

We want some nice styling for the buttons. This gives a nice pill shape, with a border that expands slightly when clicked:

button {
  color: var(--fg);
  background: var(--bg);
  border: 2px solid var(--fg);
  border-radius: 45px;
  padding: 0px 8px;
  margin: 1px 1px;
  outline: none;
}
button:hover {
  border: 3px solid var(--fg);
  margin: 0px;
}
button:active {
  background: var(--fg);
  color: var(--bg);
}

As with all proper cookie banners, we want to use dark patterns to discourage the user from managing their preferences. We’ll make a button with inverse colours that blends in more:

.inverseButton {
  background: var(--fg);
  border: 2px solid var(--bg);
  color: var(--bg);
}
.inverseButton:hover {
  border: 3px solid var(--bg);
  margin: 0px;
}
.inverseButton:active {
  background: var(--bg);
  color: var(--fg);
}

Lastly, some helper rules for formatting entries, and fading out the settings page:

.rightAlign {
  left: auto;
  right: 0;
  float: right;
}

.fadeOut {
  visibility: hidden;
  opacity: 0;
  transition: visibility 0s linear 300ms, opacity 300ms;
}

Printing

You can specify a separate set of rules used when printing the page. Generally, the purpose of these rules is to undo all of the fancy styling you’ve already done to your page, so things appear presentable on paper. You can specify print rules in a separate stylesheet, but in the interest of keeping things in one place, I’m specifying it in the same file as the rest of the CSS. Since we’re overriding every other rule, it’s important that these rules occur last.

We use a media selector, much like the ones we used earlier for screen size and dark mode, to enable our printing rules:

@media print {

Most of the page’s interactive elements don’t have any purpose on paper, so we’ll make sure they stay hidden:

header,
footer,
.skip,
.footnote-back,
[aria-hidden="true"],
#cookie_bar,
nav {
  display: none !important;
}

To save on ink, we want to ensure that our text is printed black on white. We also reduce our font size back down to 1em, which is usually fully readable on paper.

body {
  font-size: 1em;
  color: #000;
  background-color: #fff;
}

We also want to remove the colour for links, and add an underline to highlight that they were interactive elements:

a:link {
  color:#000;
  text-decoration: underline;
}
a:visited {
  color:#000;
}

However, it’s no use knowing that there was a link if you don’t know where it went! This little snippet includes the URL in the text body, just after the link’s name.

a:after {
  content: " (" attr(href) ")";
  font-family: monospace;
}

Since this is a technical blog, it’s important to pay close attention to the rendering of code blocks. The first two lines here reset the size and position of the code block. The next three will force long lines to wrap. Without these, the text will be cut off, or worse, fit in by shrinking the rest of the document! The last three lines add a swanky black bar to the edge of the code block, to visually distinguish code from prose without using excess ink.

pre {
  width: 100%;
  margin-left: 0;

  word-wrap: break-word;
  white-space: pre-wrap;
  overflow:visible;

  border-left: 4px solid #000;
  padding: 0;
  padding-left: 1.4em;
}

Finally, let’s close our @media print block:

}