Skip to content

Instantly share code, notes, and snippets.

@knowler
Last active January 2, 2024 17:46
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save knowler/af029df4a6568d6ea8467c7b0fb023c4 to your computer and use it in GitHub Desktop.
Save knowler/af029df4a6568d6ea8467c7b0fb023c4 to your computer and use it in GitHub Desktop.
/*
* Modern CSS crash course:
*
* - Leverage flexibility for everything. The web is magic paper that adapts to whatever viewport it’s in.
* - This means using `display` values like `flex` and `grid`.
* - Avoid using `@media` for setting sizes: use fluid type and spacing (i.e. `clamp()`, `min()`, `max()` math functions).
* - Use logical properties for sizing and spacing. This helps with internationalization.
* - Think in axes: block (vertical: top is start, bottom is end), inline (horizontal: left is start, right is end).
* - Those are the left-to-right/top-to-bottom equivalances, but for other writing modes/directions you’ll be setting the correct direction (e.g. in RTL, inline-start is right and inline-end is left).
* - For flexbox and grid: “align” properties apply to the block axis and “justify” properties apply to the inline axis.
* - Use custom properties to avoid repetition. They are like variables (but a bit different).
* - Use variable fonts if you can. Some system fonts are variable fonts.
* - Almost never use `px`. Prefer `rem` and `em` instead (rem is based on the root font size which is set by a user preference — respects accessibility).
* - Don’t be afraid of targeting “semantic” selectors.
*/
/* border-box is a more intuitive box-sizing */
*, ::before, ::after { box-sizing: border-box; }
:root {
/* Dark mode by default, but respect user preference */
color-scheme: dark light;
/* Using custom properties for our colour variables, but setting them to the browser defaults since I’m lazy. */
--color-canvas: Canvas;
--color-canvas-text: CanvasText;
/* Dark mode + Safari link colours don’t play nice, so we’ll set our own link colour */
--color-accent: hsl(210deg 80% 60%); /* h(ue) s(aturation) l(ightness) */
/* Font variation settings we’ll use to make the system font look unique. */
--font-settings-wide: "wdth" 130;
--font-settings-regular: "wdth" 110;
--font-settings-condensed: "wdth" 90;
}
/* Light colour scheme settings */
@media (prefers-color-scheme: light) {
:root {
/* Dim the accent colour in light mode */
--color-accent: hsl(210deg 80% 40%);
}
}
/* Default focus visible styles (i.e. when focused with keyboard, not mouse) */
:focus-visible {
outline: 0.125em dotted var(--color-accent);
outline-offset: 0.25em;
border-radius: 0.25em;
}
html {
block-size: 100%;
/* Use the system font (macOS: San Francisco / Windows: Segoe UI / Android: Roboto / Linux: Ubuntu maybe) and fallback to the default serif font */
font-family: system-ui, sans-serif;
/* Don’t use px for font-size on html element — it’ll ignore user preferences. */
font-size: clamp(1rem, 0.8rem + 1vw, 1.5rem);
font-variation-settings: var(--font-settings-condensed);
line-height: 1.5; /* Minimum accessible line-height */
}
body {
margin: 0;
padding-block: 2rem;
padding-inline: 1rem;
min-block-size: 100%;
display: grid;
gap: 2rem;
/* Centre the column and add an upper and lower bound for it’s max width, otherwise set it to 80% of the viewport width */
grid-template-columns: min(clamp(20rem, 80vw, 40rem), 100%);
justify-content: center;
/* header main footer (skip link is excluded since it’s position: fixed) */
grid-template-rows: max-content auto max-content;
}
/* Visually hidden elements (i.e. stuff you just want to expose to screen readers) */
.visuallyhidden:not(:focus) {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border-width: 0;
}
/* Default link colour */
a {
color: var(--color-accent);
text-underline-offset: 0.1em;
}
/* Skip to content link (when focused) */
a[href="#content"]:focus {
position: fixed;
display: inline-block;
inset-inline-start: 50%;
inset-block-start: 1rem;
padding: 0.5rem;
background-color: var(--color-canvas);
font-variation-settings: var(--font-settings-regular);
outline-offset: 0;
}
/* The current page */
a[aria-current="page"] {
text-decoration: none;
background-color: var(--color-accent);
color: var(--color-canvas);
}
/* Header & footer styles */
header {
font-size: 1.25rem;
}
footer p {
margin-block: 0;
}
header, footer {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
column-gap: 1rem;
row-gap: 0.5rem;
font-variation-settings: var(--font-settings-wide);
}
:is(header, footer) > nav > ul {
list-style: none;
display: flex;
flex-wrap: wrap;
column-gap: 1rem;
row-gap: 0.5rem;
padding-inline-start: 0;
margin-block: 0;
}
/* Prose styles */
h1, h2, h3, h4, h5, h6 {
font-variation-settings: var(--font-settings-wide);
font-weight: 500;
line-height: 1.2;
letter-spacing: 0.01em;
}
/* Heading with sub-title */
hgroup {
display: grid;
justify-items: start;
gap: 0.5rem;
}
hgroup > * {
margin-block: 0;
}
hgroup > p {
font-size: 1.125rem;
font-variation-settings: var(--font-settings-regular);
letter-spacing: 0.01em;
}
<!doctype html>
<html lang="en">
<head>
<!-- Generally, include these two meta tags on every document -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Content Title – Website</title>
<meta name="description" content="Description of the webpage">
<link rel="stylesheet" href="/main.css">
</head>
<body>
<!-- Allow keyboard navigators to skip the header/banner -->
<a class="visuallyhidden" href="#content">Skip to content</a>
<!-- This is the site banner — you only need one of these and it’s top-level -->
<header>
<!-- Set aria-current=page on current page for nav links – works as a nice hook in CSS -->
<a href="/" aria-current="page">Name of Website</a>
<nav aria-label="primary">
<ul>
<li><a href="/about">About</a></li>
<li><a href="/blog">Blog</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
</header>
<!-- Only ever use one <main> and make sure it’s not nested in another semantic element (div is fine) -->
<!-- This is the main content of the page -->
<main id="content">
<!-- Since you might want a sidebar or something, stuff the content of the page into an article element -->
<article>
<hgroup>
<h1>Title of the content</h1>
<p>Do this if you need a subtitle</p>
</hgroup>
<p>Don’t repeat h1 ever</p>
<h2>Section heading</h2>
<p>Some content for the section</p>
<h3>Sub-section heading</h3>
<p>This is a sub-section within the previous section</p>
<h2>Next section heading</h2>
<p>Next section content</p>
</article>
</main>
<!-- Info about the content, maybe a license, secondary nav (make sure it’s labelled). You only need one footer at the top-level -->
<footer>
<p>&copy;2024 Author of Website</p>
<nav aria-label="secondary">
<ul>
<li><a href="/accessibility">Accessibility</a></li>
<li><a href="/privacy">Privacy</a></li>
</ul>
</nav>
</footer>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment