Converting from sass to nested vanilla css with layers (#147)

This commit is contained in:
Fredrik Jonsson 2026-03-10 14:14:55 +01:00 • committed by GitHub
commit 57244c3f33
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
101 changed files with 2687 additions and 2751 deletions

View file

@ -3,14 +3,14 @@ repos:
rev: "0.0.2"
hooks:
- id: stylelint
files: assets/sass/.*\.scss$
files: assets/css/.*\.css$
additional_dependencies:
- "stylelint@16.25.0"
- "stylelint-config-standard-scss@16.0.0"
- "stylelint@17.4.0"
- "stylelint-config-standard@40.0.0"
- repo: https://github.com/biomejs/pre-commit
rev: "v2.3.5"
rev: "v2.4.6"
hooks:
- id: biome-check
files: assets/js/.*\.js$
additional_dependencies:
- "@biomejs/biome@1.9.4"
- "@biomejs/biome@2.4.6"

View file

@ -1,5 +1,5 @@
extends:
- stylelint-config-standard-scss
- stylelint-config-standard
rules:
alpha-value-notation: number
block-no-empty: null
@ -19,17 +19,5 @@ rules:
media-feature-range-notation: prefix
no-descending-specificity: null
selector-class-pattern:
- "^[a-z0-9\\-]+$"
- message: Selector should be written in lowercase with hyphens (selector-class-pattern)
scss/at-mixin-argumentless-call-parentheses: null
scss/comment-no-empty: null
scss/double-slash-comment-empty-line-before:
- always
- except:
- first-nested
ignore:
- between-comments
- stylelint-commands
- inside-block
scss/no-global-function-names: null
scss/percent-placeholder-pattern: null
- "^[a-z0-9\\-\\_]+$"
- message: Selector should be written in lowercase with hyphens or underscore (selector-class-pattern)

View file

@ -18,6 +18,7 @@ Quickstart a new site with the Zen theme by using the [Zen demo repo as a templa
## Table of contents
* [Version 6.x](#version-6x)
* [Version 5.x](#version-5x)
* [Version 4.x](#version-4x)
* [Version 3.x](#version-3x)
@ -41,14 +42,19 @@ Quickstart a new site with the Zen theme by using the [Zen demo repo as a templa
* [Podcast](#podcast)
* [Shortcodes](#shortcodes)
* [Content security policy headers](#Content-security-policy-headers)
* [Use npm to lint Sass and JavaScript](#use-npm-to-lint-sass-and-javascript)
* [Use npm to lint CSS and JavaScript](#use-npm-to-lint-css-and-javascript)
* [Repository links](#repository-links)
* [Math typesetting with KaTeX](#math-typesetting-with-katex)
* [Getting help](#getting-help)
* [Credits](#credits)
## Version 5.x
## Version 6.x 2026-03-10
* Converted from sass to modern vanilla css with nesting and layers. Please check [CSS Nesting | Can I use](https://caniuse.com/css-nesting) to see if you are comfortable with css nesting support in browsers before updating to v6.
* No changes to styles or templates so sites should look identical, if the browser support nesting.
## Version 5.x 2025-03-26
* Minimum Hugo version is now v0.146.0. This plus the rearranged variables are the reasons for making this v5.
* Rearranged a lot of variables. This will require some work to upgrade if you have customisations. I'm working towards using vanilla css only.
@ -69,14 +75,15 @@ Quickstart a new site with the Zen theme by using the [Zen demo repo as a templa
4. Update/remove/convert variables you overridden in projects `_extra.scss` so they match changes in themes `_variables.scss` and `_sass_variables.scss`.
5. If you have overridden templates you will need to use the new template structure in your root layout directory.
## Version 4.x
## Version 4.x 2024-08-14
* Minimum Hugo version is now v0.124.0. This is the reason for making this v4.
* Updated a number of things to remove all "WARN deprecated".
* No other significant changes.
## Version 3.x
## Version 3.x 2023-04-04
* Implement modern CSS like flex, grid and variables throughout.
* All sass variables now have a css variable version. All styles use the css variable version.
@ -113,7 +120,7 @@ Quickstart a new site with the Zen theme by using the [Zen demo repo as a templa
5. Replace sass variables with css variables, e.g. `$zen-gutters` with `var(--gutters)`.
## Version 2.x
## Version 2.x 2022-03-11
* Replaced "normalize" with slimmer and updated "reset". Removed everything for old IE versions.
* Use of css4 variables. Colours are now used like this `var(--color-brand)`.
@ -136,7 +143,7 @@ Quickstart a new site with the Zen theme by using the [Zen demo repo as a templa
* Contact form (PHP)
* CSS grid and flex throughout
* HTML5
* Hugo Pipes for images, js and sass
* Hugo Pipes for images, js and css
* Math typesetting with KaTeX
* Micro.blog
* Meta tags and JSON-LD
@ -150,7 +157,7 @@ Quickstart a new site with the Zen theme by using the [Zen demo repo as a templa
## Minimum Hugo version
Hugo Extended version 0.146.0 or higher is required.
Hugo version 0.146.0 or higher is required.
## Installation
@ -311,7 +318,6 @@ params:
poweredby: true # Show powered by hugo in footer
privacyPolicyUrl: "" # If set will add link to cookie consent form.
relatedposts: true # Show related posts under a "See also" section, default false.
sassTranspiler: "dartsass"# The Sass transpiler to use, default "libsass".
searchLimit: 20 # Max number of search hits, default 20.
sidebar: true # Show a sidebar to the right, default false.
siteName: false # Hide the site name (visually-hidden), default true.
@ -358,13 +364,11 @@ If you do not specify any sections in the "mainSections" param (see configuratio
This is another part that almost everyone will like to customise.
They are found in the theme `assets/sass/_colors.scss`, `assets/sass/_variables.scss` and `assets/sass/_sass_variables.scss` files. You can copy them to the root `assets/sass/` directory to set your own values.
They are found in the theme `assets/css/_colors.css` and `assets/css/_variables.css` files. You can copy them to the root `assets/css/` directory to set your own values.
In most cases you likely only need to override a few values and then it is easier to set these in the extra and custom sass files.
In most cases you likely only need to override a few values and then it is easier to set these in the custom css files.
Root `assets/sass/_extra.scss`: Loaded first so use to override sass variables only.
Root `assets/sass/_custom.scss`: Loaded last so use to override css variables and add any custom styles you need.
Root `assets/css/_custom.css`: Loaded last so use to override css variables and add any custom styles you need.
### Logo
@ -409,20 +413,20 @@ They are set up in `layouts/partials/menu.html`, `layouts/partials/mobilmenu.htm
Modern CSS grid is the easiest and cleanest way to layout your pages.
The CSS grid layout are in `assets/sass/layouts/_layouts.scss`. A lot can be done by just reordering "grid-template-rows".
The CSS grid layout are in `assets/css/layouts/_layouts.css`. A lot can be done by just reordering "grid-template-rows".
### Other styles and scripts
Styles and scripts are processed with Hugo pipes that was added in Hugo 0.46.
To customise a js or sass file, copy it to the root assets directory and edit it there. Make sure to maintain the directory structure inside the assets/sass directory.
To customise a js or css file, copy it to the root assets directory and edit it there. Make sure to maintain the directory structure inside the assets/css directory.
There is an `assets/sass/_custom.scss` file meant for your custom styles. Copy it to the root `assets/sass/_custom.scss` to use it.
There is an `assets/css/_custom.css` file meant for your custom styles. Copy it to the root `assets/css/_custom.css` to use it.
The default styles in `assets/sass/_zen.scss` are boring but functional. You can easily override them completely by placing an empty file named "_zen.scss" in root assets/sass directory.
The default styles in `assets/css/_zen.css` are boring but functional. You can easily override them completely by placing an empty file named "_zen.css" in root assets/css directory.
The sass files are by default built for production, compressed with fingerprint.
The css files are by default built for production, compressed with fingerprint.
By setting the Hugo environment variable to "development" (default when running `hugo server`) they will instead be nested with sourcemaps.
@ -825,9 +829,9 @@ The **content** that should be wrapped. Some more content.
Includes tracking code for Matomo or Google in a way that supports Content security policy headers. Read more in my blog post [Content security policy headers when using Matomo or Google analytics](https://xdeb.org/post/2020/01/14/content-security-policy-headers-when-using-matomo-or-google-analytics/).
## Use npm to lint Sass and JavaScript
## Use npm to lint CSS and JavaScript
* Lint your Sass using stylelint.
* Lint your CSS using stylelint.
* Lint your JavaScript using biome.
* Can lint files in the theme as well as the project assets directory.
@ -839,7 +843,7 @@ Node.js software you need. To install them run:
npm install
3. Run the following commands to lint your theme and projects Sass and JavaScript code.
3. Run the following commands to lint your theme and projects CSS and JavaScript code.
npm run lint-theme
npm run lint-project

99
assets/css/_colors.css Normal file
View file

@ -0,0 +1,99 @@
/* Theme colours. */
/* Override css variables in "assets/css/_custom.css" */
@layer vars {
:root {
/* Base colours. */
--color-black: oklch(1% 0.01 0);
--color-nearblack: oklch(25% 0.01 0);
--color-grey-700: oklch(38% 0 0);
--color-grey-600: oklch(49% 0 0);
--color-grey-500: oklch(60% 0 0);
--color-grey-400: oklch(71% 0 0);
--color-grey-300: oklch(80% 0 0);
--color-grey-200: oklch(89% 0 0);
--color-grey-100: oklch(95% 0 0);
--color-nearwhite: oklch(98% 0 0);
--color-white: oklch(100% 0 0);
--color-red: oklch(65% 0.2 24);
--color-yellow: oklch(65% 0.12 80);
--color-green: oklch(65% 0.19 138);
--color-blue: oklch(65% 0.16 247);
/* Transparent colours. */
--color-black-60: oklch(0% 0 0 / 0.6);
--color-black-40: oklch(0% 0 0 / 0.4);
--color-black-20: oklch(0% 0 0 / 0.2);
--color-black-10: oklch(0% 0 0 / 0.1);
--color-white-20: oklch(100% 0 0 / 0.2);
--color-white-10: oklch(100% 0 0 / 0.1);
/* Brand colours. */
--color-brand: var(--color-nearblack);
--color-brand-alt: var(--color-grey-500);
--color-brand-light: var(--color-grey-300);
--color-brand-dark: var(--color-grey-700);
/* Text colours. */
--color-text: var(--color-black);
--color-text-bg: var(--color-white);
--color-text-meta: var(--color-grey-600);
--color-headings: var(--color-nearblack);
--color-code: var(--color-nearblack);
/* Link colours. */
--color-link: var(--color-brand);
--color-link-visited: var(--color-brand);
--color-link-hover: var(--color-brand-alt);
--color-link-active: var(--color-red);
--color-link-inverted: var(--color-white);
--color-link-visited-inverted: var(--color-white);
--color-link-hover-inverted: var(--color-white);
--color-link-active-inverted: var(--color-red);
/* Button colours. */
--color-button: var(--color-brand);
--color-button-hover: var(--color-brand-alt);
--color-button-text: var(--color-white);
--color-button-text-hover: var(--color-white);
--color-button-disabled: var(--color-grey-400);
/* Border colours. */
--color-border: var(--color-brand);
--color-border-light: var(--color-brand-light);
--color-border-dark: var(--color-brand-dark);
/* Background colours. */
--color-header-bg: var(--color-white);
--color-body-bg: var(--color-white);
--color-footer-bg: var(--color-white);
--color-backdrop: var(--color-grey-100);
/* Mobil menu colours. */
--color-mobile-menu: var(--color-brand);
--color-mobile-menu-cover: var(--color-black-20);
/* Form colours. */
--color-accent: var(--color-brand);
/* Row (e.g. in table) colours. */
--color-row-header: transparent;
--color-row-odd: var(--color-nearwhite);
--color-row-even: transparent;
/* Status colours. */
--color-info: var(--color-blue);
--color-info-bg: oklch(from var(--color-blue) 100% 0.03 h);
--color-success: var(--color-green);
--color-success-bg: oklch(from var(--color-green) 100% 0.03 h);
--color-warning: var(--color-yellow);
--color-warning-bg: oklch(from var(--color-yellow) 100% 0.03 h);
--color-error: var(--color-red);
--color-error-bg: oklch(from var(--color-red) 100% 0.03 h);
/* Highlight colours. */
--color-highlight: var(--color-brand-alt);
--color-mark-highlight: var(--color-red);
}
}

9
assets/css/_custom.css Normal file
View file

@ -0,0 +1,9 @@
/* Site custom styles and colours. This file loads last. */
/* Override colours like this
:root {
--color-brand: red;
--color-brand-alt: blue;
--color-brand-dark: green;
}
*/

34
assets/css/_fonts.css Normal file
View file

@ -0,0 +1,34 @@
/* Font faces
Instead of relying on the fonts that are available on a user's computer, you
can use web fonts which, like images, are resources downloaded to the user's
browser. Because of the bandwidth and rendering resources required, web fonts
should be used with care.
Numerous resources for web fonts can be found on the web. Here are a few
websites where you can find Open Source fonts to download:
- https://www.fontsquirrel.com/
- https://www.theleagueofmoveabletype.com
- https://fonts.google.com
In order to use fonts on a web site you will need to convert them into woff2 format.
Font Squirrel's Font-Face Generator is a good option:
https://www.fontsquirrel.com/tools/webfont-generator
The following is an example @font-face declaration. This font can then be
used in any ruleset using a property like this: font-family: Example, serif;
@font-face {
font-family: 'Example';
src: url('../fonts/example.woff2') format('woff2');
font-weight: normal;
font-style: normal;
font-display: swap;
}
The fonts layer is loaded early to minimize front-end performance impact.
*/
@layer fonts {
/* Add your fonts here. */
}

125
assets/css/_variables.css Normal file
View file

@ -0,0 +1,125 @@
@layer vars {
:root {
/* Set theme css variables.
Override css variables in "assets/css/_custom.css" */
/* The max content width. (Consider using one of the breakpoints.) */
--max-content-width: 1111px;
/* The max page width. Defaults to max-content-width.
Setting this to a larger value than max-content-width allows
content with class "stretch" to stretch out to the page edge. */
--max-page-width: var(--max-content-width);
/* Base line height, unitless. */
--base-line-height: 1.5;
/* Heading line height, unitless. */
--heading-line-height: 1.3;
/* The font size set on the root html element.
Specify unitless, it will be recalculated as %. */
--base-font-size: 18;
/* Font sizes. */
--fs-xxxxl: clamp(2.4881rem, calc(1.8896rem + 2.9924vw), 4.2088rem);
--fs-xxxl: clamp(2.0738rem, calc(1.6968rem + 1.8848vw), 3.1575rem);
--fs-xxl: clamp(1.7281rem, calc(1.5053rem + 1.1141vw), 2.3688rem);
--fs-xl: clamp(1.44rem, calc(1.3228rem + 0.5859vw), 1.7769rem);
--fs-l: clamp(1.2rem, calc(1.1537rem + 0.2315vw), 1.3331rem);
--fs-m: 1rem;
--fs-s: 0.889rem;
--fs-xs: 0.778rem;
/* Modern font stacks. See how they look at https://modernfontstacks.com/.
The following font family declarations use widely available fonts.
A user's web browser will look at the comma-separated list and will
attempt to use each font in turn until it finds one that is available
on the user's computer. The final "generic" font (sans-serif, serif or
monospace) hints at what type of font to use if the web browser doesn't
find any of the fonts in the list. */
/* stylelint-disable value-keyword-case */
/* Emojis font stack. Add to end of other stacks when needed. */
--emojis: 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
/* System-ui font stack. */
--system-ui: system-ui, sans-serif;
/* Serif font stacks. */
--ui-serif: ui-serif, Charter, 'Bitstream Charter', 'Sitka Text', Cambria, serif;
--transitional: Charter, 'Bitstream Charter', 'Sitka Text', Cambria, serif;
--old: 'Iowan Old Style', 'Palatino Linotype', 'URW Palladio L', P052, serif;
--slab: Rockwell, 'Rockwell Nova', 'Roboto Slab', 'DejaVu Serif', 'Sitka Small', serif;
--antique: Superclarendon, 'Bookman Old Style', 'URW Bookman', 'URW Bookman L', 'Georgia Pro', Georgia, serif;
--didone: Didot, 'Bodoni MT', 'Noto Serif Display', 'URW Palladio L', P052, Sylfaen, serif;
/* Sans-serif font stacks. */
--ui-sans-serif: ui-sans-serif, system-ui, sans-serif;
--humanist: Seravek, 'Gill Sans Nova', Ubuntu, Calibri, 'DejaVu Sans', source-sans-pro, sans-serif;
--geometric: Avenir, Montserrat, Corbel, 'URW Gothic', source-sans-pro, sans-serif;
--classical: Optima, Candara, 'Noto Sans', source-sans-pro, sans-serif;
--neo: Inter, Roboto, 'Helvetica Neue', 'Arial Nova', 'Nimbus Sans', Arial, sans-serif;
--industrial: Bahnschrift, 'DIN Alternate', 'Franklin Gothic Medium', 'Nimbus Sans Narrow', sans-serif-condensed, sans-serif;
--futura: Futura, Montserrat, Corbel, 'URW Gothic', source-sans-pro, sans-serif;
/* Rounded Sans-serif font stacks. */
--ui-rounded: ui-rounded, 'Hiragino Maru Gothic ProN', Quicksand, Comfortaa, Manjari, 'Arial Rounded MT', 'Arial Rounded MT Bold', Calibri, source-sans-pro, sans-serif;
/* Monospace font stacks. */
--ui-monospace: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace;
/* stylelint-enable value-keyword-case */
/* Font family. */
--ff-body: var(--ui-serif);
--ff-headings: var(--ui-sans-serif);
--ff-alt: var(--system-ui);
--ff-monospace: var(--ui-monospace);
/* Font weights. */
--fw-headings: 700;
--fw-bolder: 900;
--fw-bold: 700;
--fw-medium: 500;
--fw-normal: 400;
--fw-light: 300;
--fw-lighter: 200;
/* Spacing.
Generates mt--m (mt = margin-top), mb--m (mb = margin-bottom) and
mx--m (mx = margin-block) classes. */
--spacing-xxl: calc(3rem * var(--base-line-height));
--spacing-xl: calc(2rem * var(--base-line-height));
--spacing-l: calc(1.5rem * var(--base-line-height));
--spacing-m: calc(1rem * var(--base-line-height));
--spacing-s: calc(0.75rem * var(--base-line-height));
--spacing-xs: calc(0.5rem * var(--base-line-height));
--spacing-xxs: calc(0.3rem * var(--base-line-height));
/* Border radius. */
--radius-xl: 1rem;
--radius-l: 0.75rem;
--radius-m: 0.5rem;
--radius-s: 0.3rem;
--radius-xs: 0.125rem;
/* Breakout amount.
How much should the "breakout" be. Defaults to same as gutters value. */
--breakout: calc(5px + 1.5625vw);
/* The left/right gutter/padding. */
--gutters: calc(5px + 1.5625vw);
--gutters-reverse: calc(0px - (5px + 1.5625vw));
/* The amount lists, blockquotes and comments are indented. */
--indent-amount: 2rem;
/* The max line width for readability. */
--max-line-width: 70ch;
/* The pt font-size to be used for printing. */
--print-font-size: 12pt;
}
}

207
assets/css/_zen.css Normal file
View file

@ -0,0 +1,207 @@
/* Zen custom styles */
@layer zen {
/* Page */
body {
background-color: var(--color-backdrop);
}
.page {
background-color: var(--color-body-bg);
}
.footer {
border-block-start: 2px solid var(--color-border);
}
/* Apply smaller text to blocks, footer etc. */
th,
label,
legend,
figcaption {
font-size: var(--fs-s);
}
.language-selector,
.footer {
font-size: var(--fs-xs);
}
/* Apply heading font stack and colour to labels etc. */
th,
label,
legend,
.main-menu,
.header,
.footer {
color: var(--color-headings);
font-family: var(--ff-headings);
}
th,
label {
font-weight: var(--fw-headings);
}
/* Article and content. */
article,
aside {
& > ul {
display: table;
list-style: none;
padding: 0;
& > li {
display: table-row;
&::before {
padding-inline-end: var(--spacing-xs);
display: table-cell;
content: '\2981';
font-size: var(--fs-s);
}
}
}
& > ol {
display: table;
list-style: none;
padding: 0;
& > li {
display: table-row;
counter-increment: table-ol;
&::before {
padding-inline-end: var(--spacing-xs);
display: table-cell;
content: counter(table-ol) '.';
font-size: var(--fs-s);
text-align: end;
}
}
}
}
article {
.submitted,
.tags {
@media (min-width: 666px) {
display: inline-block;
padding-inline-end: 1rem;
}
}
code {
font-size: var(--fs-xs);
overflow-wrap: break-word;
}
pre {
border-radius: var(--radius-xs);
padding: 5px;
border: 1px solid var(--color-border-light);
background-color: var(--color-text-bg);
overflow: auto;
max-height: 300px;
&.chroma {
max-height: initial;
overflow-x: scroll;
}
& > code {
display: inline-block;
white-space: pre;
}
}
}
.content-dates {
text-align: end;
}
.repository-links {
justify-content: end;
align-items: center;
svg {
order: 1;
}
}
/* Navigation */
.main-menu {
a {
display: block;
background-color: var(--color-button);
color: var(--color-button-text);
padding: 2px 8px;
text-decoration: none;
&:hover,
&:focus {
background-color: var(--color-button-hover);
}
&:active,
&[aria-current] {
text-decoration: underline;
}
}
}
/* Search */
.search-text {
font-size: var(--fs-l);
}
/* Copy code */
[data-codeblock] {
position: relative;
& > pre {
padding-block-start: 1lh;
}
}
.codebutton {
position: absolute;
inset-inline-end: 1rem;
background: unset;
border: unset;
text-shadow: unset;
font-size: var(--fs-l);
color: var(--color-border);
rotate: 90deg;
cursor: pointer;
& > span {
pointer-events: none;
}
&:hover,
&:focus-visible,
&:active {
scale: 1.15;
}
}
.flash-item {
animation: --flash 500ms ease-in-out 1ms;
}
@keyframes --flash {
50% {
border-color: var(--color-warning);
background-color: var(--color-warning-bg);
}
}
}

View file

@ -0,0 +1,22 @@
/* Document */
@layer base {
html {
/* Change the default font family in all browsers. */
font-family: var(--ff-body);
/* Set base font size in percent. */
font-size: calc(var(--base-font-size) / 16 * 100%);
/* Set text colour. */
color: var(--color-text);
}
body {
/* Correct the line height in all browsers. */
line-height: var(--base-line-height);
/* Accent colour for forms. */
accent-color: var(--color-accent);
}
}

View file

@ -0,0 +1,89 @@
/* Form defaults. */
@layer base {
form {
/* Add vertical rhythm margins. */
margin-block: 0 var(--spacing-m);
}
input,
textarea,
select {
margin-block: 0 var(--spacing-m);
}
label {
font-weight: var(--fw-bold);
/* Display as block if before input. */
&:has(+ input),
&:has(+ textarea){
display: block;
}
/* Mark field as required if needed. */
&:has(+ :required) {
&::after {
content: '*';
color: var(--color-mark-highlight);
}
}
}
/* Valid and invalid state. */
input,
textarea {
&:user-valid {
border-color: var(--color-success);
border-style: solid;
background-color: var(--color-success-bg);
}
&:focus:user-valid {
border-color: transparent;
}
&:user-invalid {
border-color: var(--color-error);
border-style: solid;
background-color: var(--color-error-bg);
}
&:focus:user-invalid {
border-color: transparent;
background-color: var(--color-warning-bg);
}
}
/* Set max text line width for readability. */
input,
label,
textarea,
select {
max-width: var(--max-line-width);
}
fieldset {
border: 1px solid var(--color-border);
}
/* Make field where you enter more info a bit wider. */
[type='email'],
[type='password'],
[type='range'],
[type='search'],
[type='url'] {
width: min(20em, 100%);
}
/* Make field where you enter more info a bit wider. */
[type='text'],
textarea {
width: min(30em, 100%);
}
/* Make sure textareas without a rows attribute are not tiny. */
textarea:not([rows]) {
min-height: 7lh;
}
}

View file

@ -0,0 +1,107 @@
/* Grouping content */
@layer base {
/* Block quotes */
blockquote {
/* Add vertical rhythm margins. */
margin-block: 0 var(--spacing-m);
/* Nicer text wrap, e.g. no lone word on last line. */
text-wrap: pretty;
/* Also indent the quote on both sides. */
margin-inline: var(--indent-amount);
/* Set max text line width in main for readability. */
max-width: var(--max-line-width);
}
/* Lists */
dl,
ol,
ul,
menu {
/* Add vertical rhythm margins. */
margin-block: 0 var(--spacing-m);
}
dt {
font-weight: var(--fw-bold);
}
dd {
margin: 0;
margin-inline-start: var(--indent-amount);
/* Nicer text wrap, e.g. no lone word on last line. */
text-wrap: pretty;
}
ol,
ul,
menu {
padding: 0;
padding-inline-start: var(--indent-amount);
/* Turn off margins on nested lists. */
ol,
ul,
menu {
margin: 0;
}
}
li {
/* Nicer text wrap, e.g. no lone word on last line. */
text-wrap: pretty;
/* Set max text line width in main for readability. */
.main & {
max-width: var(--max-line-width);
}
}
/* Horizontal rule */
hr {
/* Add vertical rhythm margins. */
margin-block: var(--spacing-m);
border: 0;
border-block-start: 1px solid var(--color-border);
height: 0;
}
/* Paragraphs */
p {
/* Add vertical rhythm margins. */
margin-block: 0 var(--spacing-m);
/* Nicer text wrap, e.g. no lone word on last line. */
text-wrap: pretty;
/* Set max text line width in main for readability. */
.main & {
max-width: var(--max-line-width);
}
}
/* Preformatted text */
pre {
/* Add vertical rhythm margins. */
margin-block: 0 var(--spacing-m);
font-family: var(--ff-monospace);
}
/* Code */
/* Do not set colour for code with syntax highlighting. */
code:not([data-lang]),
kbd,
samp {
color: var(--color-code);
}
}

View file

@ -0,0 +1,59 @@
/* Headings
All HTML headings, `<h1>` through `<h6>`, are available. */
@layer base {
h1,
h2,
h3,
h4,
h5,
h6 {
margin-block: 1.5em .5em;
color: var(--color-headings);
font-family: var(--ff-headings);
font-weight: var(--fw-headings);
line-height: var(--heading-line-height);
text-wrap: balance;
/* Smaller margin when a headeing is right after another heading. */
& + & {
margin-block-start: .75em;
}
/* Set max text line width in main for readability. */
.main & {
max-width: var(--max-line-width);
}
}
h1 {
/* h1 only on top of page so different margins. */
margin-block-start: var(--spacing-m);
margin-block-end: var(--spacing-xs);
font-size: var(--fs-xxl);
letter-spacing: -.01em;
}
h2 {
font-size: var(--fs-xl);
letter-spacing: -.01em;
}
h3 {
font-size: var(--fs-l);
letter-spacing: -.01em;
}
h4 {
font-size: var(--fs-m);
}
h5 {
font-size: var(--fs-s);
}
h6 {
font-size: var(--fs-xs);
}
}

View file

@ -0,0 +1,89 @@
/* Figure and image. */
@layer base {
figure,
img {
&.image {
--image-padding: .75rem;
@media (min-width: 666px) {
&.center {
margin-inline: auto;
}
&.left {
padding-inline-end: var(--image-padding);
padding-block-end: var(--image-padding);
margin: 0;
float: left;
clear: both;
}
&.right {
padding-inline-start: var(--image-padding);
padding-block-end: var(--image-padding);
margin: 0;
float: right;
clear: both;
}
}
}
}
figure {
/* Add vertical rhythm margins. */
margin-block: 0 var(--spacing-m);
&.image {
figcaption {
padding-block-start: var(--image-padding);
contain: inline-size;
/* Set max text line width in main for readability. */
max-width: var(--max-line-width);
/* Nicer text wrap, e.g. no lone word on last line. */
text-wrap: pretty;
@media (min-width: 666px) {
padding-block-start: 0;
}
p {
margin: 0;
}
}
@media (min-width: 666px) {
inline-size: fit-content;
&.left {
figcaption {
padding-inline-end: var(--image-padding);
padding-block-end: var(--image-padding);
}
}
&.right {
figcaption {
padding-inline-start: var(--image-padding);
padding-block-end: var(--image-padding);
}
}
}
}
&.podcast {
audio {
width: 95%;
}
}
}
img {
&.image {
/* Add vertical rhythm margins. */
margin-block: 0 var(--spacing-m);
}
}
}

View file

@ -0,0 +1,81 @@
/* Links
Hyperlinks are used to allow the user to navigate to other resources or to
download a resource.
The order of link states are based on Eric Meyer's article:
http://meyerweb.com/eric/thoughts/2007/06/11/who-ordered-the-link-states
:visited - A link that has already been visited.
:hover - A link that is being hovered over.
:focus - A link that has been given keyboard focus.
:active - A link that is being actively clicked. */
@layer base {
a {
color: var(--color-link);
}
/* Any element can receive focus, but only links can be visited, so we can
safely lower this selector's specificity. This also ensures visited links
with additional styles won't have their styles overridden. */
:visited {
color: var(--color-link-visited);
}
a:hover,
a:focus {
color: var(--color-link-hover);
}
a:active {
color: var(--color-link-active);
}
/* Remove underline from links to current page to */
/* indicate that there is no need to click on it. */
[aria-current] {
text-decoration: none;
}
/* Use smooth scrolling unless user prefer less motion. */
@media (prefers-reduced-motion: no-preference) {
:has(:target) {
scroll-behavior: smooth;
scroll-padding-block-start: var(--gutters);
}
}
.link-nav {
a {
text-decoration: none;
&:hover,
&:focus {
text-decoration: underline;
}
}
}
/* Inverted links, for dark backgrounds. */
.link-inverted {
a {
&:link {
color: var(--color-link-inverted);
}
&:visited {
color: var(--color-link-visited-inverted);
}
&:hover,
&:focus {
color: var(--color-link-hover-inverted);
}
&:active {
color: var(--color-link-active-inverted);
}
}
}
}

View file

@ -0,0 +1,34 @@
/* Tables */
@layer base {
table {
/* Add vertical rhythm margins. */
margin-block: 0 var(--spacing-m);
/* Remove default spacing between table cells. */
border-collapse: collapse;
/* Prevent cramped-looking tables */
width: 100%;
}
td,
th {
/* Remove most spacing between table cells. */
padding: 0;
/* Align content to top of cells. */
vertical-align: text-top;
}
th {
/* Align content to bottom of headers. */
vertical-align: bottom;
}
caption,
th {
/* Align all text the same. */
text-align: start;
}
}

View file

@ -0,0 +1,36 @@
/* Anchor */
@layer components {
.anchor {
--anchor-gap: .5rem;
--anchor-opacity: .4;
display: grid;
grid-template-columns: auto auto;
justify-content: start;
align-items: baseline;
gap: var(--anchor-gap);
a {
opacity: 0;
text-decoration: none;
width: fit-content;
}
&:has(:focus-visible),
&:hover {
a {
opacity: var(--anchor-opacity);
&:focus-visible,
&:hover {
opacity: 1;
}
}
}
}
p:has(+ .anchor) {
margin-block-end: 0;
}
}

View file

@ -0,0 +1,66 @@
/* Box */
@layer components {
.box {
/* Add vertical rhythm margins. */
margin-block: var(--spacing-m);
padding: var(--spacing-xs);
border: 5px solid var(--color-border);
.box__title,
.title {
margin-block-start: 0;
}
& > * {
&:first-child {
margin-block-start: 0;
}
&:last-child {
margin-block-end: 0;
}
}
}
.box--highlight {
border-color: var(--color-highlight);
}
.box--fit {
width: fit-content;
}
.box--gutter {
padding-inline: var(--gutters);
}
.box--inverted {
background: var(--color-border);
color: var(--color-text-bg);
/* Make sure headings etc. also gets inverted. */
* {
color: var(--color-text-bg);
}
a {
&:link {
color: var(--color-link-inverted);
}
&:visited {
color: var(--color-link-visited-inverted);
}
&:hover,
&:focus {
color: var(--color-link-hover-inverted);
}
&:active {
color: var(--color-link-active-inverted);
}
}
}
}

View file

@ -0,0 +1,109 @@
/* stylelint-disable no-duplicate-selectors */
/* Button */
@layer components {
.button,
[type='button'],
[type='submit'] {
/* Some styles don't apply to <a> links since they are inline elements by default. */
display: inline-block;
font-family: var(--ff-headings);
text-decoration: none;
text-align: center;
width: fit-content;
padding: .2rem 1rem;
/* Improve usability and consistency of cursor style between image-type `input` and others. */
cursor: pointer;
color: var(--color-button-text);
text-shadow: var(--color-button) 0 1px 0;
background: linear-gradient(var(--color-button), var(--color-button-hover));
border: 1px solid var(--color-button-hover);
border-radius: var(--radius-s);
&:hover,
&:focus-visible,
&:active {
/* Override any link underlines and color changes. */
text-decoration: none;
color: var(--color-button-text-hover);
text-shadow: var(--color-button-hover) 0 1px 0;
background: linear-gradient(var(--color-button-hover), var(--color-button));
}
&:active {
box-shadow: inset 2px 2px 5px var(--color-black-40);
}
}
.button--small {
font-size: var(--fs-xs);
padding: .2rem .75rem;
}
.button--alt {
color: var(--color-button-text-hover);
background: var(--color-button-hover);
&:hover,
&:focus-visible,
&:active {
color: var(--color-button-text);
background: var(--color-button);
}
}
.button--outline {
color: var(--color-button);
background: var(--color-button-text);
}
.button--accept {
border-color: var(--color-success);
}
.button--decline {
border-color: var(--color-warning);
}
/* The disabled variation should always go last, so that it overrides any
other variations. */
.button,
[type='button'],
[type='submit'] {
&:disabled {
background-color: var(--color-grey-200);
border: 1px solid var(--color-button-disabled);
background-image: none;
text-shadow: none;
cursor: default;
color: var(--color-button-disabled);
&:hover,
&:focus,
&:active {
/* Override any link underlines and color changes. */
color: var(--color-button-disabled);
text-decoration: none;
}
}
}
/* For forms with multiple fields show submit button below the form. */
input:not(:only-of-type),
textarea {
& + [type='submit'] {
display: block;
}
}
/* For forms on mobile with single field add space between it and the submit button. */
@media (min-width: 666px) {
input:only-of-type {
&:has(+ [type='submit']) {
margin-inline-end: .5rem;
}
}
}
}

View file

@ -0,0 +1,70 @@
/* Cards
A simple cards style. */
@layer components {
.cards {
--grid-group-column-min: 200px;
--grid-group-gap: 1.5rem;
display: grid;
gap: var(--grid-group-gap);
grid-template-columns: repeat(auto-fit, minmax(min(var(--grid-group-column-min), 100%), 1fr));
/* Add vertical rhythm margins. */
margin-block: var(--spacing-m);
}
.card {
padding: var(--spacing-xs);
border: 1px solid var(--color-border);
& > * {
&:first-child {
margin-block-start: 0;
}
&:last-child {
margin-block-end: 0;
}
}
}
.card--highlight {
border-color: var(--color-highlight);
}
.card--featured {
grid-row: span 2;
grid-column: span 2;
}
.card--gutter {
padding-inline: var(--gutters);
}
.card--inverted {
background: var(--color-border);
color: var(--color-text-bg);
a {
&:link {
color: var(--color-link-inverted);
}
&:visited {
color: var(--color-link-visited-inverted);
}
&:hover,
&:focus {
color: var(--color-link-hover-inverted);
}
&:active {
color: var(--color-link-active-inverted);
}
}
}
}

View file

@ -0,0 +1,14 @@
@layer components {
.grid-center {
display: grid;
place-items: center;
}
.text-center {
text-align: center;
}
.margin-center {
margin-inline: auto;
}
}

Some files were not shown because too many files have changed in this diff Show more