Typography

Consistent styles for standard HTML elements covering typography, lists, inline and block elements, plus optional styles for smooth-scroll and view-transition.

Examples

Headings and text

<h1>

Heading 1

<h2>

Heading 2

<h3>

Heading 3

<h4>

Heading 4

<h5>

Heading 5

<h6>

Heading 6
<p>

The quick brown fox jumps over the lazy dog.
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
123456789

<small>

The quick brown fox jumps over the lazy dog.
ABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
123456789

Lists

<ol>

  1. Item
  2. Item
  3. Item
  4. Item

<ul>

  • Item 1
  • Item 2
  • Item 3
  • Item 4

<dl>

Term 1
Description
Term 2
Description

Inline elements

Bold/strong
Italic/emphasis
Superscript(1)
Subscript(2)
Delete

Mark
Small
Keyboard
Code
Abbr

Block elements

<details>

Summary

Details body content

<blockquote>

Blockquote with author and citation.

— Editor Daily Blog

<hr>


<pre>

:where(html) {
  color-scheme: light dark;
  color: CanvasText;
  background-color: Canvas;
}

<figure> with caption

Forest covered in snow with sunset filtering between tall trees.
Winter forest

Smooth-scroll and view-transition

Optional Smooth scroll and view-transition styles are included with the _typography.scss document but provided as individual modules to allow using them independently.

Using the modules

Add the sassmods.scss to your custom styles as below, use the Sass variables from the module's source code (see below) to customize the default property values if required, then include the Sass mixin(s) anywhere below.

// custom.scss
@use "sassmods/scss/sassmods" as *;
$font-family:  "OpenSans", system-ui, -apple-system, sans-serif; // example
$h1-font-size: 2.2rem; // example

@include typography-css;
@media (prefers-reduced-motion: no-preference) { 
  @include smooth-scroll-css;
  @include view-transition-css;
}

Using the framework

Enable the module(s) in the sassmods-system.scss document as below, if required customize the styles or the Sass variables for the default property values directly in the source code (see below).

$enable-typography:      true;
$enable-smooth-scroll:   true;
$enable-view-transition: true;

Source

The source code is a self-contained Sass document that compiles the style modules as Sass mixins using Sass variables provided for the core property values that can be customized as described above.

_typography.scss

Use the property specific CSS variables to create root level variables for global styles and/or editing inline, or change all the values to suit your own parameters.

// ---------------------------------------------------------- 
// Typography
// ----------------------------------------------------------
$text-color:                  var(--text, CanvasText) !default;
$background-color:            var(--background, Canvas) !default;
$font-family:                 system-ui, -apple-system, Arial, Helvetica, sans-serif !default;
$html-font-size:              var(--html-fs, clamp(100%, 40% + 0.666vw, 140%)) !default;
$body-font-size:              var(--body-fs, 1rem) !default;
$body-font-weight:            var(--body-fw, normal) !default;
$body-line-height:            var(--body-lh, 1.5) !default;

$link-color:                  var(--link, LinkText) !default;
$link-decoration-color:       color-mix(in srgb, var(--link, LinkText) 75%, Canvas) !default;
$link-underline-offset:       0.1175em !default;
$visited-color:               color-mix(in srgb, var(--visited, LinkText) 75%, white) !default;
$visited-decoration-color:    color-mix(in srgb, var(--visited, LinkText) 50%, Canvas);
$hover-color:                 color-mix(in srgb, var(--hover, LinkText) 75%, white) !default;

$heading-font-weight:         var(--heading-fw, 700) !default;
$heading-line-height:         var(--heading-lh, 1.2) !default;
$heading-text-wrap:           var(--heading-tw, pretty) !default;
$heading-margin-bottom:       var(--heading-mb, 0.75rem) !default;
$h1-font-size:                var(--h1-fs, 2rem) !default;
$h2-font-size:                var(--h2-fs, 1.725rem) !default;
$h3-font-size:                var(--h3-fs, 1.5rem) !default;
$h4-font-size:                var(--h4-fs, 1.35rem) !default;
$h5-font-size:                var(--h5-fs, 1.2rem) !default;
$h6-font-size:                var(--h6-fs, 1.063rem) !default;

$para-margin-bottom:          var(--para-mb, 1rem) !default;
$para-text-wrap:              var(--para-tw, pretty) !default;
$list-margin-bottom:          var(--list-mb, 1rem) !default;
$list-text-wrap:              var(--list-tw, pretty) !default;
$list-indent:                 var(--li-ps, 2rem) !default;
$dl-list-indent:              var(--dd-ms, 1rem) !default;

$small-font-size:             0.906em !default;
$small-font-size-heading:     0.75em !default;
$code-color:                  color-mix(in srgb, CanvasText 60%, Canvas) !default;
$kbd-padding-block:           0.1em !default;
$kbd-padding-inline:          0.35em !default;
$kbd-background-color:        color-mix(in srgb, CanvasText 5%, Canvas) !default;
$kbd-radius:                  0.125em !default;

$pre-padding:                 1rem !default;
$pre-margin-bottom:           1rem !default;
$pre-border-color:            color-mix(in srgb, CanvasText 12%, Canvas) !default;;
$pre-background-color:        color-mix(in srgb, CanvasText 1%, Canvas) !default;;
$blockquote-padding-block:    0.75rem !default;
$blockquote-padding-inline:   1rem !default;
$blockquote-margin-bottom:    1rem !default;
$blockquote-border-color:     color-mix(in srgb, CanvasText 12%, Canvas) !default;
$blockquote-border:           1px !default;
$blockquote-border-left:      10px !default;

@mixin typography-css {

:where(:not([popover], dialog)) {
  margin: 0;
}

*, *::before, *::after {
  box-sizing: border-box;
}

:where(html) {
  color-scheme: light dark;
  color: #{$text-color};
  font-size: #{$html-font-size};
  font-family: #{$font-family};  
  background-color: #{$background-color};
  block-size: 100%;
}

:where(body) {
  font-size: #{$body-font-size};
  font-weight: #{$body-font-weight};
  line-height: #{$body-line-height};
}

:where(a) {
  color: #{$link-color};
  overflow-wrap: break-word;
  text-decoration-color: #{$link-decoration-color};
  text-decoration-thickness: 1px;
  text-underline-offset: #{$link-underline-offset};
}

:where(a:visited) {
  color: #{$visited-color};
  text-decoration-color: #{$visited-decoration-color};
}

:where(a:hover) {
  color: #{$hover-color};
  text-decoration: none;
}

:where(h1, h2, h3, h4, h5, h6) a {
  text-decoration: none;
}

:where(h1, h2, h3, h4, h5, h6) {
  font-weight: #{$heading-font-weight};
  line-height: #{$heading-line-height};
  text-wrap: #{$heading-text-wrap};
  margin-block-end: #{$heading-margin-bottom};
}

:where(h1) {
  font-size: #{$h1-font-size};
}

:where(h2) {
  font-size: #{$h2-font-size};
}

:where(h3) {
  font-size: #{$h3-font-size};
}

:where(h4) {
  font-size: #{$h4-font-size};
}

:where(h5) {
  font-size: #{$h5-font-size};
}

:where(h6) {
  --heading-lh: 1.25;
  font-size: #{$h6-font-size};
}

:where(p) {
  text-wrap: #{$para-text-wrap};
  margin-block-end: #{$para-margin-bottom};
}

:where(ol, ul, dl) {
  text-wrap: #{$list-text-wrap};
  margin-block-end: #{$list-margin-bottom};
}

:where(ol, ul) {
  padding-inline-start: #{$list-indent};
}

:where(ol) {
  list-style-type: var(--marker, decimal);
}

:where(ul) {
  list-style-type: var(--marker, disc);
}

:where(ol ol, ul ul, ol ul, ul ol, dl dl) {
  --mb: 0;
}

:where(dt) {
  font-weight: var(--fw);
}

:where(dt):not(:first-child) {
  margin-block-start: var(--dt-mt);
}

:where(dd) {
  margin-inline-start: #{$dl-list-indent};
}

:where(small) {
  font-size: #{$small-font-size};
}

:where(h1, h2, h3, h4, h5, h6) small {
  font-size: #{$small-font-size-heading};
}

:where(bold, strong) {
  font-weight: var(--fw, bold);
}

:where(abbr[title]) {
  cursor: help;
}

:where(code) {
  color: #{$code-color};
  word-wrap: break-word;
}

a > :where(code) {
  color: inherit;
}

:where(kbd) {
  padding-block: #{$kbd-padding-block};
  padding-inline: #{$kbd-padding-inline};
  background-color: #{$kbd-background-color};
  border-radius: #{$kbd-radius};
}

:where(pre) {
  overflow: auto;
  padding: #{$pre-padding};
  margin-block-end: #{$pre-margin-bottom};
  border: 1px solid #{$pre-border-color};
  background-color: #{$pre-background-color};
}

:where(pre code) {
  color: revert;
  word-break: normal;
}

:where(address) {
  font-style: normal;
  margin-block-end: var(--mb, 1rem);
}

:where(blockquote) {
  padding-block: #{$blockquote-padding-block};
  padding-inline: #{$blockquote-padding-inline};
  margin-block-end: #{$blockquote-margin-bottom};
  border: #{$blockquote-border} solid #{$blockquote-border-color};
  border-inline-start-width: #{$blockquote-border-left};
}

:where(blockquote p) {
  --mb: 0;
}

:where(blockquote p:first-of-type) {
  --mb: 0.25rem;
}

:where(details) {
  margin-block-end: 1rem;
}

:where(summary) {
  cursor: pointer;
}

:where(details[open] summary) {
  margin-block-end: var(--mb-open, 0.75rem);
}

:where(hr) {
  box-sizing: content-box;
  block-size: 0;
  overflow: visible;
  border: none;
  border-block-start: 1px solid color-mix(in srgb, CanvasText 12%, Canvas);
  margin-block-end: 1rem;
}

:where(figure) {
  margin-block-end: 1rem;
}

:where(img, svg, video, audio, iframe, embed, object) {
  display: block;
}

:where(img, svg) {
  max-inline-size: 100%;
}

:where(iframe) {
  border: 0;
}

} // End typography mixin

// Motion-animation
@mixin smooth-scroll-css {
  :where(html) {
    scroll-behavior: smooth;
  }
}

@mixin view-transition-css {
  @view-transition {
    navigation: auto;
  }
}