My Blog Theme¶
I use a custom Sphinx theme for this site.
Generally, I try and implement as much as I can using just HTML+CSS however, there are a few occasions where some JavaScript gets involved.
CSS Variables¶
Below are the CSS Variables used throughout the site’s theme.
_static/css/styles.css:root {
--bg-main: white;
--fg-main: black;
--bg-sidebar: #161b22;
--fg-sidebar: white;
--fg-sidebar-dim: #aaa;
--highlight-color: #f0b100;
--border-color: #657b83;
--fg-accent-dim: #064e3b;
--fg-accent: #059669;
--fg-accent-bright: #a7f3d0;
--transition: 300ms;
--sidebar-min-width: 0.5em;
--sidebar-max-width: 300px;
--panel-min-height: 0.5em;
--panel-max-height: 400px;
/* Closed by default */
--left-sidebar-width: var(--sidebar-min-width);
--right-sidebar-width: var(--sidebar-min-width);
--panel-height: var(--sidebar-min-width);
}
Which makes implementing a dark theme straightforward
_static/css/styles.css@media(prefers-color-scheme: dark) {
:root {
--bg-main: #0d1117;
--fg-main: white;
--border-color: #839496;
--fg-accent-dim: #064e3b;
--fg-accent: #059669;
--fg-accent-bright: #a7f3d0;
}
}
As well as respecting the prefers-reduced-motion preference
_static/css/styles.css@media screen and (prefers-reduced-motion: reduce) {
:root {
--transition: 0.001ms;
}
}
A CSS “Reset”¶
You might have heard of a CSS reset, while I haven’t yet felt the need for a full blown reset stylesheet I do include the following
_static/css/styles.css* {
margin: 0;
box-sizing: border-box;
}
Utility Classes¶
.guilabel¶
Used by the :guilabel: role.
_static/css/styles.css.guilabel {
color: var(--fg-accent);
font-style: italic;
font-weight: bold;
}
.highlighted¶
Used by the search system to highlight matched terms.
_static/css/styles.css.highlighted {
background: var(--highlight-color);
color: var(--bg-main);
padding: 0 0.5em;
border: solid 1px var(--fg-main);
border-radius: 3px;
}
.line-through¶
On some pages I define an ad-hoc role to strikethrough some text.
.. role:: strike
:class: line-through
Which requires the CSS for the given class name to be defined.
_static/css/styles.css.line-through { text-decoration: line-through; }
HTML Elements¶
Trying to work with the CSS cascade rather than against it, it’s good to try and define global rules that apply to all HTML elements
Adding styles to the html element applies them to everything on the page.
I also think that by setting the font size here, I can use rem units to set all font sizes relative to this base size.
_static/css/styles.csshtml {
font-size: 13pt;
font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
scroll-behavior: smooth;
}
a¶
It’s nice if links follow the accent color
_static/css/styles.cssa { color: var(--fg-accent);}
Section Links¶
Sphinx automatically inserts links for each section header.
I style the usual character (¶) so that it’s not visible and instead use the link icon from the feather icon set.
_static/css/styles.cssa.headerlink {
float: right;
color: var(--bg-main);
&::before {
content: "";
display: inline-block;
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" /></svg>');
width: 1em;
height: 1em;
}
}
blockquote¶
As generated by the .. pull-quote:: directive.
The technology you use impresses no one.
The experience you create with it is everything. – `Sean Gerety`_
_static/css/styles.cssblockquote.pull-quote {
padding: 0 1em;
font-style: italic;
border-left: solid var(--fg-accent);
}
details¶
Expand...
For additional information.
_static/css/styles.cssdetails > :not(summary) {
border-left: solid 1px var(--border-color);
padding: 0 0 0 1em;
margin: 0 0.2em;
}
dd & dt¶
Used by Sphinx/docutils to represent definition lists
dtRepresents the term to be defined.
ddContains the definition.
_static/css/styles.cssdt { margin-top: 1em; }
dd {
margin-left: 1em;
padding-left: 1em;
border-left: solid 1px var(--fg-accent);
p:first-child {
margin-top: 0;
}
}
figure¶
_static/css/styles.cssfigure {
&.align-center img {
display: block;
margin: auto;
}
figcaption p {
margin: auto;
font-size: 0.8rem;
text-align: center;
}
}
h1-h6¶
_static/css/styles.cssh1, h2, h3, h4, h5, h6 {
font-weight: 500;
}
h1 { font-size: 2rem; }
h2 { font-size: 1.8rem; }
h3 { font-size: 1.7rem; }
h4 { font-size: 1.5rem; }
h5 { font-size: 1.4rem; }
h6 { font-size: 1.2rem; }
img¶
_static/css/styles.cssimg { max-width: 100%; }
kbd¶
e.g. C-x C-f
_static/css/styles.csskbd {
padding: 0 0.15em;
color: var(--fg-accent);
}
table¶
A table is composed of many elements.
Element |
Description |
|---|---|
|
Top-level table element |
|
Contains the table’s header rows |
|
Contains the table’s rows |
|
Defines a row |
|
Defines a header cell |
|
Defines a normal cell |
_static/css/styles.csstable {
width: 100%;
border-collapse: collapse;
thead {
tr {
border-bottom: solid 1px var(--border-color);
}
}
td, th {
padding: 0 0.5em;
border-right: 1px solid var(--border-color);
}
td:last-child, th:last-child {
border-right: none;
}
}
Layout¶
The layout for this site is divided into the following sections
I want a somewhat dynamic layout for this site, meaning that the sidebars and the footer should be open/closable by the user.
Amazingly, it turns out that the grid-template-columns and grid-template-rows CSS properties can be animated!
So as long as you set the transition property and use a few CSS variables
_static/css/styles.cssbody { overflow: hidden; }
.grid {
color: var(--fg-main);
background: var(--bg-main);
display: grid;
grid-template-columns: var(--left-sidebar-width) 1fr var(--right-sidebar-width);
grid-template-rows: 3em calc(100vh - 3em - var(--panel-height)) var(--panel-height);
transition: var(--transition);
}
Then the standard checkbox trick can be used to open/close the various elements! For example, assuming that when checked the sidebar should close, the CSS for the left sidebar might look something like this.
#left-sidebar-checkbox {
&:checked ~ .grid {
--left-sidebar-width: var(--sidebar-min-width);
.sidebar {
opacity: 0;
}
}
}
Which, assumes the following HTML structure.
<body>
<input type="checkbox" id="left-sidebar-checkbox" class="hidden" />
<div class="grid">
...
<aside>
<section class="left-sidebar-toggle">
<label for="left-sidebar-checkbox"></label>
</section>
<div class="sidebar">...</div>
However, I think the only way to make this work across screen sizes, is to change the meaning of the checkbox state depending on the screen size. This is bound to lead to some quirks if you resize the screen across a breakpoint, but hopefully, it’s enough of an edge case to not have to worry too much about it! 😅
Header¶
_static/css/styles.cssheader.site {
color: var(--fg-sidebar-dim);
background: var(--bg-sidebar);
padding: 0.5em;
position: sticky;
top: 0;
display: flex;
align-items: center;
grid-column: span 3;
z-index: 2;
}
Site Title¶
_static/css/styles.csssection.site-title {
display: flex;
align-items: center;
gap: 0.5em;
h1 {
font-size: 1.4rem;
}
a {
color: var(--fg-sidebar-dim);
span {
color: var(--fg-accent);
}
}
img {
border-radius: 100%;
width: 2em;
border: solid 1px var(--fg-accent);
}
}
On narrow screens, omit the site title text.
_static/css/styles.css@media screen and (max-width: 600px) {
section.site-title {
h1 { display: none; }
}
}
Layout Toggles¶
_static/css/styles.csssection.layout-toggles {
color: var(--fg-sidebar-dim);
label[for="left-sidebar-checkbox"] {}
label[for="panel-checkbox"] {
svg { transform: rotate(-90deg); }
}
label[for="right-sidebar-checkbox"] {
svg { transform: rotate(180deg); }
}
}
Main¶
_static/css/styles.cssbody { background: var(--bg-sidebar); }
main {
min-width: 0;
max-height: 100%;
overflow-y: auto;
}
article¶
I use article elements to contain the main content of the page.
_static/css/styles.cssarticle {
padding: 0 2em;
line-height: 1.5;
max-width: 100ch;
footer {
border-top: solid 1px var(--border-color);
}
}
On mobile the padding should be reduced a bit to create more space
_static/css/styles.css@media screen and (max-width: 600px) {
article { padding: 0 0.5em; }
}
Admonitions¶
_static/css/styles.cssdiv.admonition {
margin: 1em 0;
border: solid 1px var(--border-color);
border-radius: 3px;
& > :not(ol,ul) {
padding: 0.5em;
margin: 0;
}
.admonition-title {
margin: -1px -1px 0 -1px;
color: var(--fg-accent);
border: solid 2px var(--fg-accent);
border-left: solid 5px var(--fg-accent);
border-top-left-radius: 3px;
border-top-right-radius: 3px;
}
}
Code¶
Sphinx/docutils render inline code in <code> tags with the text further wrapped in a <span> tag.
_static/css/styles.cssarticle {
code.literal {
&::before, &&::after {
content: "`";
}
span.pre {
font-weight: 600;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}
}
}
While code blocks are in a <div class="highlight"> tag.
_static/css/styles.cssdiv.highlight {
padding: 0.5em;
margin: 0.5em 0;
overflow-x: auto;
border: solid 1px var(--border-color);
border-radius: 5px;
}
I use awdur throughout this site to take the contents of code blocks and export them to separate code files (for example all the CSS on this page!). Awdur provides a code block header, indicating where the code will be exported to. This header also needs to be styled to match the look and feel of this site.
_static/css/styles.cssdiv.awdur-codeblock {
border: none;
div.awdur-codeblock-header {
color: var(--fg-sidebar);
background: var(--bg-sidebar);
padding: 0 0.5em;
border: solid 1px var(--border-color);
border-top-left-radius: 5px;
border-top-right-radius: 5px;
border-bottom: none;
}
div.highlight {
border-top: none;
border-top-left-radius: 0;
border-top-right-radius: 0;
margin-top: 0;
}
}
Typography¶
_static/css/styles.cssarticle {
section {margin-top: 2em;}
h1, h2, h3, h4, h5, h6 { color: var(--fg-accent); }
p { margin: 1em 0; }
table {
line-height: unset;
p { margin: 0;}
}
}
Post Collections¶
I quite liked the idea of placing all of my blog posts on a timeline, but it does involve a fair amount of CSS. I keep telling myself that I’m going to expand the type of “events” in the list… someday! 😅
_static/css/styles.cssarticle.summary {
display: grid;
grid-template-columns: 250px auto;
& > :not(aside) {
padding: 0 1em;
}
header, footer {
grid-column: 2;
}
header {
position: relative;
&::before {
content: '';
width: 0.75em;
height: 0.75em;
background: var(--border-color);
border-radius: 100%;
position: absolute;
top: 1em;
left: -0.4em;
}
}
aside {
grid-row: 1 / span 3;
border-right: solid 1px var(--border-color);
padding: 0 1em;
section.post-metadata {
margin-top: 0.4em;
align-items: flex-start;
h5 { display: none; }
ul.taglist { gap: 0.5em; }
}
}
footer {
border-top: none;
text-align: right;
margin-bottom: 4em;
}
}
Of course on smaller displays, things need to be rearranged a bit.
_static/css/styles.css@media screen and (max-width: 1200px) {
article.summary {
grid-template-columns: auto;
header, footer { grid-column: unset; }
header::before { display: none; }
aside {
grid-row: unset;
border: none;
}
}
}
It would be interesting to see if I can replace the above with a container query at some point.
Search Results¶
The search results page requires its own set of rules.
_static/css/styles.csssection#search-results {
padding: 2em;
h2 { color: var(--fg-accent); }
p.search-summary {
padding: 1em 0;
}
ul.search {
padding: 0;
li {
margin: 1em 0;
padding: 1em;
list-style: none;
border: solid 1px var(--fg-accent-dim);
border-radius: 3px;
}
p.context {
margin: 0.5em 0;
}
}
}
Footer¶
_static/css/styles.cssfooter.site {
color: var(--fg-sidebar);
background: var(--bg-sidebar);
grid-column: span 3;
}
Social Links¶
_static/css/styles.css