Recap
For a recap what we went through today:
Background
What's there about styling a Gatsby site?
- Gatsby gives us access to content development fast
-
React changes the way we think about styling, specifically:
- we're writing way more components than pages, creating a greater need for namespaced CSS
- parent - child > sibling relationship
- components need to live in different contexts, and contexts need to preserve possibilities for unknowns
The nature of the problem
Looking at the nature of this problem in a spectrum of globalness v.s. component:
Tasks
- spacing between site's H2 tags and the paragraphs next to it: global
- styling a header: component
- layout that adapts to viewports: rely on global information, but exports a styling to a specific component (layout is just another component)
- dark toggle: by itself a component, but creates styling information that affects a layer very high up on the global scale
Tools
- vanilla CSS: global – putting down styling information on the floor
- CSS modules: towards component, but may put down styling that affects global scope as well
- CSS-in-JS: it's a way to write CSS, there are libraries (typographyJS) that writes global styles, but more CSS-in-JS libraries are designed for component styling, and it may go towards the very end allowing us to write styling + DOM + logic in one file
This article A Conceptual Look at Theming shares a similar mind model.
Some history
- roots in typography and printing
- presenting information logically and aesthetically
A lot of the things we do on the web has roots in typography and printing. If there's only one thing you want to check out, absolutely the historical Byrn's Euclid and its faithful reproduction on the web.
The root cause also stays the same: we need to present information logically and aesthetically.
A practice + demo
Finally, sharing my practice on how I think about Gatsby site:
- typography – maintains all typesetting information with TypographyJS, handy util funciton
rhythm
that gives you one line of height - layout – keeps all page structure information, creates contexts for components, handles responsivity
- component – very scoped, many CSS-in-JS libraries give quick access from logic -> style (sometimes don't even need to create classes)
It's not three layer, gula melaka tea, it's 2.5 layers, because layout is in-between.
Demo
Once again ran over time. Apparently I have no respect to time :(((( I'll do better next time
The goal was to live code this no style starter to have some basic styles with the mindset above and using
Finally (for real)
- Is this the only way? No. The key is to find the solution that fits your needs.
- Universal CSS is not a joke
- Use a style guide
- We're still writing CSS
- It's your Gatsby site, come up with your own styles :)))))
Slides
Styling Gatsby Site
- WomenWhoCode Connect 2019
I'm Wei,
- @wgao19 everywhere
- React developer at Shopee
- maintains Docusaurus
- creates many websites
Agenda
this workshop is not about
- designs of Gatsby sites
- which css framework is better
past talks / workshops on Gatsby
this workshop is about
- a mind model to think about styling a Gatsby site
- one practical method that works for me
Gatsby
And here's Gatsby's slogan:
Fast in every way that matters
Gatsby is a free and open source framework based on React that helps developers build blazing fast websites and apps
To me that every way includes one aspect – it gets us to content development really fast.
#30DaysOfCSSGirls
Gatsby brings us quick access to content development, your site is literally an npm install away
- very simple abstraction of APIs
- great docs and tutorials
- generate site from starters
- features as plugins with unified API
By building a Gatsby site, we are very much free from all the messiness setting a site up and running. However, the work does not go away. Things like setting up the site, code splitting, fetching and loading logic, service workers, even image optimizations, all of those work did not go away. But rather, it is well encapsulated in Gatsby's implementation including its core packages and the plugins system. Thanks to all of those encapsulation well taken care of, we can put most of our time into working on the content of our sites.
Practically every Gatsby site is a starter no? 🤯
Powered by React
React is changing the way we think about styles
We moved from page- and template-based to component-based mindset
🧩 We're writing many more components than pages
namespaced CSS
First of all, because we're making each of our components responsible for a very atomic functionality, we're writing way more components than we used to write pages. It's not on the same scale. And so there comes a strong need for namespaced CSS.
👩👧👦 Parent – child relationships > sibling relationships
- learn modern CSS layout techniques
- Rachel Andrew – Refactoring (the way we talk about) CSS
We're also letting parent – child relationships dominate over sibling relationships. In our React world, siblings don't communicate very well. Much information is passed down via parent to children.
🗺 We need to think in terms of context – component
Also, since we may easily reuse components, a component must handle styles that fit different contexts, and the contexts must preserve possibilities for unknowns such as third party components.
🤔 Global = hacky?
- Night toggle at aworkinprogress.dev
Global things feel "hacky". This tiny night toggle, albeit very simple by itself, assumes certain specific global DOM structure to work. So it's very fragile if we have to think in terms of components.
Would like to point out again that global styling is not a problem. It only starts to feel hacky since we're now thinking in terms of components.
All in all, it can be quite deceptive that we're still writing CSS and something that feels like HTML, but we're not thinking the same way anymore.
So how 😳 to also style our Gatsby site fast?
Spectrum of styling
Today in this workshop I invite you to think about styling in a spectrum.
Brent Jackson, creator of MDX Deck and many more cool things, happens to share a similar mind and has written a brief article that discusses this.
global 🐶🐱🐭🐹🐰🦊🐮🐵 component
- tasks
- tools
Tasks
- styling of headers?
- dark toggle?
- spacing between an h2 and the paragraph next to it?
- layout that adapts to different viewports?
global 🐶🐱🐭🐹🐰🦊🐮🐵 component
Tools
- vanilla css?
- css modules?
- css-in-js?
global 🐶🐱🐭🐹🐰🦊🐮🐵 component
Layers in a Gatsby site's styling
To me, styling a Gatsby site boils down to the following three aspects:
- Typography
- Layout
- Components
Typography
I think of creating websites just another way of presenting information, in addition to putting things on newspaper, books, and magazines. The origin goes very easily in to printing.
Historically, typesetting is a printing process where printers set the content of a page when printing a book.
These are movable types. Each letters are called a "type" – that's where the word typography came from. And when you print a book, you pick the types you need and set them in words, add spaces, then into sentences, paragraphs, until you have a page. Then you brush some ink and press your paper against this thing.
When you type set a book, which types you pick is only part of the problem. You also need to decide the horizontal and vertical spacing, maximum widths, and their relations to each other.
Typography is the subject about this whole process.
And there is a faithful reproduction of the original book on the web:
Likewise, typesetting is in essence a transported concept. We're doing the same thing – picking a font, deciding the vertical and horizontal spacing, and their relations with each other – to help present our information well.
Web typography
font faces + sizes + scales
Once again let's look at one example of typography.
... I'd like to think of typography as font faces + sizes + scales. Some would add vertical spacing...
... All in all, typography is in the fundation section in almost all design systems. There's one more reason why we both talk about and work on typography first – the font sizes we set will we set the HTML's root font-size that will be used as the base font size for rem
values.
global 🐶🐱🐭...
Layout
Layout, once again, has a root in printing.
Many books or magazines follow a columnar layout.
- References: A Type Primer by John Kane
I think the root cause stays the same – the need to present information both logically and aesthetically.
in our terms...
On the other hand, layout is also just another component
Gatsby after v2 also no longer gives Layout component the magical auto wrapping behavior.
...🐭🐹🐰...
Components
It's a CS thing
...🦊🐮🐵 component
Components mostly stay towards the right end of our spectrum. Some components can be slightly towards the center, such as the <Layout />
components, or page components.
Ready to get hands dirty? 🙌🏻
$ gatsby new my-blog https://github.com/wgao19/gatsby-starter-no-style
Typography
- Typography
- Layout
- Components
TypographyJS
We are going to use TypographyJS. This is a CSS-in-JS library that focuses on dealing with the site's typography. And it is created by Kyle Matthews, also the creator of Gatsby.
Installation
To use TypographyJS in Gatsby, first we need to add the library and its corresponding Gatsby plugin to our package:
$ yarn add gatsby-plugin-typography react-typography typography
# or
$ npm install --save gatsby-plugin-typography react-typography typography
Then, we'll need to specify in gatsby-config.js
that we are using this library and the plugin, and we'll specify a file in which we'll write the typography related CSS in a JS object.
// gatsby-config.js
module.exports = {
plugins: [
{
resolve: `gatsby-plugin-typography`,
options: {
pathToConfigModule: `src/utils/typography`
}
}
]
};
Basic usage
We'll also need to create the typography.js
file. Then, we'll need to restart the dev server. Nothing will change yet. Now let's work on the typography.js
file.
// src/utils/typography.js
import T from 'typography';
export default = new T({
baseFontSize: '18px',
baseLineHeight: 1.7
});
Google fonts
Typography provides a quick API to load google fonts. Let's pick a Google font.
// src/utils/typogrpahy.js
import T from 'typography';
export default new T({
// ...
googleFonts: [
{
name: 'Nunito',
styles: ['800']
},
{
name: 'IBM Plex Sans',
styles: ['400', '400i']
}
],
headerFontFamily: ['Nunito', 'sans-serif'],
bodyFontFamily: ['IBM Plex Sans', 'monospace'],
});
"Typesetting"
overrideStyles
- vertical rhythm
Next, we want to specify some more detailed adjustments to the typography, we're "typesetting" the page!!
The TypographyJS API has a special field called overrideStyles
which is supposed to be a function. If you initialize the typography object with this function provided, it will be called with a few util functions for you to use, and the objects you return in this function will be injected to your site.
The util function we are going to use today is rhythm
.
rhythm
stands for vertical rhythm. This is another library created (transported) by Kyle. You can read more about it in its separate repo.
What it does intuitively is that gives you 1 line of height. But why can't you just use, say, pixel numbers? This is because, the font sizes are different in headings and body texts. And sometimes you may adjust the line height as well, resulting yet another different final line height. So what rhythm gives you is the actual one line of height in the text's context. And we can use the number at margins, paddings, etc.
// src/utils/typography.js
import T from 'typography';
export default new T({
// ...
overrideStyles: ({ rhythm }) => {
return {
h1: {
paddingTop: rhythm(1.2),
paddingBottom: rhythm(1.2),
lineHeight: 1.8,
fontStyle: 'italic'
},
h2: {
paddingTop: rhythm(1),
paddingBottom: rhythm(0.8)
},
};
}
});
The return object keys are simply CSS selectors, and you can put media queries here too.
// src/utils/typography.js
import T from 'typography';
const typography = new T({
// ...
overrideStyles: ({ rhythm }) => {
return {
// ...
a: {
color: '#222',
textDecoration: 'none',
borderBottom: '3px solid gold',
paddingBottom: '2px',
transition: 'all .2s ease'
},
'a:hover, a:active, a:focus': {
borderBottom: '1.5px solid gold'
}
};
}
});
Wrapping up
- typography.js theme picker
- vertical rhythm
- object keys are css selectors
- all typography related information will be kept here
The TypographyJS library contains a list of styles where you may use directly to your Gatsby site. You can even play around with it, tweak the numbers of the prepackaged styles to fit your own needs. Demo and full API docs at https://kyleamathews.github.io/typography.js/.
Before we move on to the next section, I'd like to point out that with TypographyJS you can actually write media queries exactly like how we put the relatively more complex selectors in the object key. And as a matter of fact, TypographyJS has a package that includes all the common media query strings. But we're not going to use that part of the library because we'll move on to consider our site's layout separately.
We keep all typography related styling information here. Later on we will work on layout, and then components. But we will see that they won't interfere with what we do here.
Layout
- Typography
- Layout
- Components
emotion
My own words: composable css + modules in one step.
Base layout
// src/components/Layout/index.jsx
import * as React from 'react';
import { Link } from 'gatsby';
export default ({ children, currentPage, className }) => (
<div>
<nav>
{[
{
path: '/',
title: 'Home'
},
{
path: '/blog',
title: 'Blog'
}
].map(({ path, title }) => (
<Link
to={path}
key={path}
className={currentPage === path ? 'active' : 'normal'}
>
{title}
</Link>
))}
</nav>
<main className={className}>{children}</main>
<footer>
Built with{' '}
<span role="img" aria-label="heart">
💛
</span>
</footer>
</div>
);
-
// layout styles
<div
className={css`
background: #efefef;
`}
>
{/**
*layout component
*/}
</div>
-
// nav styles
<nav className={css`
height: var(--navbar-height, 96px);
line-height: var(--navbar-height, 96px);
background: white;
padding: 0 var(--spacing, 2rem);
`} />
-
// Put variables to typography.js
// src/utils/typography.js
import T from 'typography';
export default new T({
// ...
overrideStyles: ({ rhythm }) => {
return {
//...
':root': {
'--spacing': '2rem',
'--round-corner': '2px',
'--footer-height': '72px',
'--navbar-height': '96px'
},
};
}
});
-
// footer styles
<footer
className={css`
height: var(--footer-height);
line-height: var(--footer-height);
background: #1e1e1e;
color: #777;
text-align: center;
`} />
-
// main chunk styles
<main
className={cx(
css`
min-height: calc(100vh - var(--footer-height) - var(--navbar-height));
padding: 0 var(--spacing, 2rem);
`,
className
)}
>
Because we use css variables such as --spacing
, personally prefer to add the spacing to each individual blocks instead of giving the spacing to the outer playout.
Adapting to viewports:
// layout component
<div
className={css`
background: #efefef;
@media (max-width: 979px) {
--spacing: 1rem;
}
`}
>
Page (very in-between layout and component)
Post listing *css variables will work just fine (since its global mah)**
<section
className={css`
margin: var(--spacing) auto;
border-radius: 2px;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-column-gap: calc(100% / 15);
grid-auto-flow: dense;
@media (max-width: 979px) {
display: block;
}
`}
/>
Post briefing you can nest class name and use it in its nesting component
<article
className={css`
&:first-child {
grid-column: 1 / -1;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 100px calc(100% / 15);
grid-auto-flow: dense;
margin-bottom: calc(2 * var(--spacing));
.image {
grid-column: span 2;
}
}
`}
/>
Post briefing image you can combine class names
<Image
fluid={fluid}
className={cx(
'image',
css`
background: white;
border-radius: var(--round-corner);
box-shadow: #efefef 2px 2px 1px 0;
`
)}
/>
Component
apply styles without having to go through separating them by class names – it's in build step
// src/components/Navbar.js
import * as React from 'react';
import { css, cx } from 'emotion';
// ...
export default ({ currentPage }) => {
return (
<nav
className={css`
height: var(--navbar-height);
padding: 0 var(--spacing);
background: white;
display: flex;
align-items: center;
`}
>
{items.map(({ path, title }) => (
<Link
to={path}
key={path}
className={css`
border-radius: var(--round-corner);
margin-right: calc(var(--spacing) / 4);
border-bottom: 3px solid
${path === currentPage ? 'gold' : 'transparent'};
&:hover {
border-bottom: 3px solid lightgoldenrodyellow;
}
`}
>
{title}
</Link>
))}
</nav>
);
};
Had fun?
🤞 this is not the only way
Universal CSS 😅
is there a real world example using this?
yes there is lol
Theme UI
map theme info to styles
the key is to fit your very needs
for example, emotion may no longer be preferrable to myself if my components are getting bigger, because in that case I'll prefer to have a cleaner file for rendering logic, and styles in their own files.
☝️ Use a style guide
mainly to save the time wasted in constantly tweaking styles
✌️ We're still writing CSS
And I think it implies a greater challenge on our understanding of CSS because we now need to incorporate where and how we write CSS, and how it is built, together with CSS's intrinsic logic.