Font stacks
A few native font stacks are set in the root-vars.css
file.
:root {
--ff-sans: ui-sans-serif, system-ui, Arial, sans-serif;
--ff-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
--ff-mono: ui-monospace, Menlo, Consolas, "Courier New", monospace;
--ff-base: var(--t-sans);
--ff-prose: var(--t-serif);
}
Everything here is an example, a place to start. Your own --base
and --prose
don’t need to be both sans-serif and serif. See setting font stacks for more information.
The Baselayer docs make use of JavaScript and some extra CSS to enable a demo toggle, so that you can switch between --base
and --prose
in this main content area — see main menu.
Font stack usage in Baselayer:
- The base font stack
--ff-base
is set in the<body>
tag and via thet-base
utility class. It is also set on form inputs and on heading tags (using--h-ff: inherit;
so that you can override and set a different heading typeface). - The prose is only available via the
t-prose
utility class. Employ it on your<article>
tag, or wherever you need. - The monotype is available via the
<code>
,<kbd>
, and<samp>
HTML tags, and in thet-mono
utility class.
Setting font stacks
In choosing your own font stacks, you will probably need two fonts that have similar x-height, so that you can set them using the same font size and line-height. Some other factors to compare are hights for lowercase ascenders and descenders, letter width, and stroke thickness.
If you want to stick with using native font stacks (i.e. those that come with computers, tablets, and phones), then the Modern Font Stacks website has several examples that you can easily copy.
In choosing your own typefaces from elsewhere, a good place to start is by pairing serif and sans-serif (or slab serif) fonts of the same typeface, if both are available. Google Fonts has several such typefaces to choose from, such as:
- Alegreya and Alegreya Sans
- IBM Plex and IBM Plex Sans
- Inria Serif and Inria Sans
- Noto Serif and Noto Sans
- PT Serif and PT Sans
- Roboto and Roboto Slab
- Source Sans 3 and Source Serif 4
In Baselayer, sizes and weights for the base and prose typefaces can optionally be set independently — but you will still want them to share the same line height. For using another set of sizes and weights for prose, you will need to do some un-commenting in two files: variables.css
and typography.css
.
So, for example:
- If your chosen base typeface has an x-height much larger than your chosen prose typeface, then you can set the prose font-size slightly larger, or the base slightly smaller.
- If your base typeface has thicker strokes so that it “looks darker” than your prose typeface, then you may want to set your base font-weight slightly lighter, to
300
(if this is available) while retaining your prose font-weight at the normal400
.
For inspiration:
- Pairing Typefaces (Google Fonts article)
- fontpairings.com
- fontpair.co
- fontjoy.com
- Pair & Compare
- Top 50 Google Font Pairings [Handpicked by Pro Designers] (Pagecloud)
Base font size
The default font size in Baselayer is 100% for small viewports, and bumped up to 112.5% from width 640px up (equivalent to Baselayer width variable sm
).
/* in variables.css */
:root {
--fs-base: 100%;
--fs-bump: 112.5%;
}
/* in base.css */
body{
container-type: inline-size;
font-family: var(--ff-base);
font-size: var(--fs-base);
}
@container (min-width: 640px) {
body > * {
font-size: var(--fs-bump);
}
}
In addition to this base font size bump to 1.125em (18px), Baselayer also has a CSS clamp()
controllled t-long-read
utility class to further increase the font size on larger devices, for even more comfortable reading of longform articles (see making text bigger).
Typographic block elements
Most typographic blocks have zero top margin, and bottom margin set to var(--s-2)
(1rem).
Headings
Example heading sizes:
Heading h1
Heading h2
Heading h3
Heading h4
Heading h5
Heading h6
All headings <h1>
to <h6>
and matching utility classes h1
to h6
have:
- Headings font sizes set in the variables file.
- Headings also have their font-family set using
--h-ff: inherit
. This has been done so that you can use the variable to override it. So, your headings don’t need to be the same typeface as your paragraphs. - Headings font weight is set using
--h-fw: var(--fwsemibold)
— which you can also override. - Headings line heights set using the formula
1em + 0.5rem
. - Headings have their bottom margin set the same as for paragraphs,
var(t--mb)
. The top margin for<h2>
thorugh<h6>
is double that — except then used as immediate child items of the content-grid where the top margin is reduced tovar(t--mb)
. This is because margin collapse is prevented by CSS grid. (And the top margin of<h2>
is totally removed when it’s the first immediate child of a.content-grid
.) - The matching utility classes
h2
toh6
only affect font-size. These utilities do not include margin or font-weight styling.
:root {
--h1: 2.25em;
--h2: 1.875em;
--h3: 1.5em;
--h4: 1.25em;
--h5: 1.125em;
--h6: 1em;
--h-ff: inherit; /* headings font-family */
--h-fw: var(--fw-semibold);
--h-lh: calc(1em + 0.5rem);
--h-mt: calc(var(--t-mb) * 2);
}
Tips:
- In some contexts (e.g. in card components) you may not want any built-in spacing for typographic block elements. Then, you can remove margins by using the
m-0
utility class. - You can also remove top margin indirectly: e.g. you can target the first item inside its wrapper using
.your-wrapper:first-child { margin-top: 0; }
, or the first sibling after the<header>
or<h1>
. Same as I have done in Baselayer at.content-grid > h2:first-child { margin-top: 0; }
.
Block quotes
Baselayer styles <blockquote>
tags with some inline (x-axis) padding, to give the effect of indentation. This inline padding is set using the container-responsive spacing variable --s-4
so that it increases if there is more available width.
Otherwise, blockquotes have the same as paragraph styling.
Lorem ipsum dolor sit amet, adipiscing honestatis ius ut, nisl consulatu pro in. Imperdiet evertitur no usu, his te suavitate salutatus. Nullam ridens deterruisset an duo. Cum harum insolens ei, cum probo placerat praesent et.
Lists
In Baselayer ordered <ol>
and unordered <ul>
have a small amount of left padding. But Baselayer separates list items <li>
to make them more obvious by setting a small top margin between list items (smaller than the top margin between paragraphs), and the same amount above nested <ol>
and <ul>
. This makes ordered and unordered lists have an even spacing like this:
- Ordered item one
- Ordered item two
- Ordered item two child one
- Ordered item two child two
- Ordered item three
<ol>
<li>Ordered item one</li>
<li>Ordered item two
<ol>
<li>Ordered item two child one</li>
<li>Ordered item two child two</li>
</ol>
</li>
<li>Ordered item three</li>
</ol>
- Unordered item
- Unordered item
- Unordered item child
- Unordered item child
- Unordered item
<ul>
<li>Unordered item</li>
<li>Unordered item
<ul>
<li>Unordered item child</li>
<li>Unordered item child</li>
</ul>
</li>
<li>Unordered item</li>
</ul>
For definition lists, the title is bold and the definition data item is indented with the same left padding as for the lists (see above).
- Definition list title
- Definition list data
- Definition list title
- Definition list data
<dl>
<dt>Definition list title</dt>
<dd>Definition list data</dd>
<dt>Definition list title</dt>
<dd>Definition list data</dd>
</dl>
The link tag
The default underline for links has been moved downwards slightly to improve legibility. The link decoration (underline) thickness has been set at 1px, so that it doesn’t become thicker when used on larger text (e.g. in headings) where link underline can be too bulky).
On :hover
, the link text color becomes slightly darker and underline becomes thicker, increasing to 3px. This thickening of the line helps people who don't see the slight color change.
All these styles are controlled by CSS variables so that you can change them.
Then there are the following two classes that apply to links, that may be handy in some situations:
- This link has no underline —
t-no-underline
- This link has no underline unless hovered —
t-underline-hover-only
The browser default, and the best practice for accessability, is to have links indicated by an underline (and the browser default color of links is blue). But in the context of menus it is permissible to deviate from the best practice, provided there are other visual and non-visual indicators. This is the reason why we should use semantic HTML tags on menus, and and why we should place navigation menus in their expected locations (in sitewide menubar ribbons, sidebars, and footers.
Menus
nav
/<menu>
/<menuitem>
The semantic <nav>
tag has no styling of its own, other than its being a block element (similar to a div
). Use it to organize your major navigation blocks of links (whether to other pages in your website, or “on this page”).
Semantic <menu>
tags are treated by the browser as no different to an <ul>
, but they are announced as a menu by screen readers. Baselayer gives <menu>
, <ul>
, and <ol>
the same styling: some left padding and bottom margin (and you can remove these you don't require them, using spacing utility classes).
Using an <li>
within a <menu>
will give you bulletpoint block items. This will be OK for sidebars and dropdown menus, for example, but there’s also the <menuitem>
tag. This is an inline tag (like a <span>
but semantic for menu items), and it doesn’t come with a bulletpoint.
This is what you get if you add no styling, other than the Baselayer defaults (but of course you will want to add more styling):
<nav>
<menu>
<menuitem><a href="">Menu item 1</a></menuitem>
<menuitem><a href="">Menu item 2</a></menuitem>
<menuitem><a href="">Menu item 3</a></menuitem>
</menu>
</nav>
Notes:
- Use
<nav>
to encapsulate your main navigation and “on this page” navigation. Navs can contain more than one menu, e.g. in dropdowns. The website or brand logo (with a link to the homepage) can be situated within the top<nav>
but outside of the menu. - The
<menu>
tag can be used in other contexts, e.g. for a list of related links to other websites, or for a set of user interface controls in a web application. - Inside a
<menuitem>
can be a link, a button, or other interactive element.
If you want the group of links to not have underlines, or to have underlines only when hovered (because you intend to style them differently in your menu):
links-not-underlined
links-underlined-hover-only
And with a few other Baselayer utilities, you have a menubar:
<nav>
<menu class="p-3 flex flex-wrap gap-4 bg-gray bg-100 bg-dark-invert links-not-underlined">
<menuitem><a href="">Menu item 1</a></menuitem>
<menuitem><a href="">Menu item 2</a></menuitem>
<menuitem><a href="">Menu item 3</a></menuitem>
</menu>
</nav>
Or a menu for a sidebar or footer buffet:
<menu class="pl-0 flex flex-column gap-3 links-underlined-hover-only">
<menuitem><a href="">Menu item 1</a></menuitem>
<menuitem><a href="">Menu item 2</a></menuitem>
<menuitem><a href="">Menu item 3</a></menuitem>
</menu>
Text alignment
t-right
,t-center
, andt-left
In addition to the three simple text alignment classes above, Baselayer also has several container-query responsive variants, for xs:
, sm:
, md:
and lg:
container breakpoints widths.
Example: The hearing in the card below is left aligned by default, but becomes center aligned when its container is sm
(640px) wide and above, using sm:t-center
:
A title
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Magni rem animi quaerat accusantium illum architecto, nemo, ex harum voluptatum adipisci eum blanditiis dolorum. Natus debitis quisquam, expedita accusantium quos cumque?
For more information on Baselayer container queries, see contaner query contexts and container query powered layouts.
Tables
Baselayer tables are set using the .table
class.
- Table headers
<th>
are bold. - Table cells
<th>
and<td>
have a border set byvar(--b-1)
– the same detail as<hr>
and border utility classes. - Table cell paddings are set by
--p-cell
(see decoration spacing extras) - All cell content is left-aligned. You can change that on the whole
<table>
or on a per-cell basis using the text alignment classes. - Optional modifier
table-grid
will add the outline to allth
andtd
cells.
Table Header 1 | Table Header 2 | Table Header 3 |
---|---|---|
Table content 1.1 | Table content 2.1 | Table content 3.1 |
Table content 1.2 | Table content 2.2 | Table content 3.2 |
Table content 1.3 | Table content 2.3 | Table content 3.3 |
Table content 1.4 | Table content 2.4 | Table content 3.4 |
<table class="table">
<caption>This is a Table Caption</caption>
<thead>
<tr>
<th>Table Header 1</th>
<th>Table Header 2</th>
<th>Table Header 3</th>
</tr>
</thead>
<tbody>
<tr>
<td>Table content 1.1</td>
<td>Table content 2.1</td>
<td>Table content 3.1</td>
</tr>
<tr>
<td>Table content 1.2</td>
<td>Table content 2.2</td>
<td>Table content 3.2</td>
</tr>
...
</tbody>
</table>
With table table-grid
:
Table Header 1 | Table Header 2 | Table Header 3 |
---|---|---|
Table content 1.1 | Table content 2.1 | Table content 3.1 |
Table content 1.2 | Table content 2.2 | Table content 3.2 |
Table content 1.3 | Table content 2.3 | Table content 3.3 |
Table content 1.4 | Table content 2.4 | Table content 3.4 |
Making wide tables responsive
If you have a lot of content in your table, it will probably break your page layout on small viewports (e.g. phones). The simplest way to make a table “responsive” is to wrap your table in a DIV with the overflow-x
class to make it horizontally scrollable.
Column title | Column title | Column title | Column title | Column title | Column title | Column title | Column title | Column title | Column title |
---|---|---|---|---|---|---|---|---|---|
Tabel cell content | Tabel cell content | Tabel cell content | Tabel cell content | Tabel cell content | Tabel cell content | Tabel cell content | Tabel cell content | Tabel cell content | Tabel cell content |
<div class="overflow-x">
<table class="table">
...
</table>
</div>
Making text bigger
The base font size is 100% (usually 16px), bumping up to 112.5% (usually 18px) for more comfortable reading on tablet devices up.
Additionally:
- There’s a
t-lg
class that can be used to make text 1.325em — good for a lead paragraph or important messaging. - Classes
.h1
through.h6
will resize text the same amount as for heading tag sizes — use when you want to make text larger (or large text smaller) without adversly affecting accessibility/ SEO heading hierarchy. - The
t-display
wrapping class uses aclamp()
to raise text from starting size 1.25em up to 1.625rem depending on container size — e.g. used for responsively increasing text size in hero components. An<h1>
inside at-display
will have maximum font size 72px.
The t-display
text size clamp have been calculated using Petter Walbø Johnsgård’s Font-size Clamp Generator and changed to a container query width controlled cqi
unit instead of viewport-width controlled vm
.
Code
<code>
tags have monospaced text (set by --ff-mono
) over a white or black background (in light or dark modes), with a thin border and a little padding to improve readability.
If the <code>
tag is wrapped in a <pre>
tag, then it becomes a block level element with more padding, a max-width of 100%, and y-axis overflow scrolling.
Other typographic utility classes
Besides those already introduced, Baselayer also has utility classes for:
t-lg
— increase font size by 1.5em. Use it directly on a<p>
to enlarge the font (e.g. for a lead paragraph).t-sm
(or use the<small>
HTML tag) — decrease font-size to 0.8emt-highlight
(or use the<mark>
HTML tag) — text highlightert-thin
,t-normal
,t-semibold
,t-bold
,t-heavy
— font weightst-italic
— font style italict-balance
— balances word-wrap, so that e.g. long headings don't have orphanst-uppercase
— text transform to capitalst-nowrap
— prevents text wrapping (spaces behave as non-breaking spaces)
Note: links can also be styled as though they are buttons using the btn
utility class. See buttons.