How To Build Gatsby Theme With Theme UI?
Tags: gatsby, webdev
Published: 2020-02-17 10:10:41

<h2><b>What is Theme UI?</b></h2>
<p>As <a href="https://theme-ui.com/">the official site</a> says, Theme UI is a library to building consistent, themeable React apps based on constraint-based design principles. </p>
<p>It allows you to style any component in your application with typographic, color, and layout values defined in a shared theme object. </p>
<p>Theme UI includes Emotion (CSS-in-JS library) with additional utilities for simple styling MDX documents, works with Styled System and Typography.js, supports mobile-first responsive approach.</p>
<p>In this post I will show you how to create some basic components (like Text and Button) using Theme UI in Gatsby.</p>
<h2><b>Use Theme UI in Gatsby</b></h2>
<p>Install Gatsby plugin for adding Theme UI context:</p>
```javascript
yarn theme-ui gatsby-plugin-theme-ui @emotion/core @mdx-js/react
```
<p>Add the following package to <i>gatsby-config.js</i></p>
```javascript
module.exports = {
 plugins: ['gatsby-plugin-theme-ui'],
}
```
<p>Theme UI uses a theme configuration object to provide color, typography, layout, and other shared stylistic values through the React context. </p>
<p>This allows components within your site to add styles based on a predefined set of values. </p>
<p>To customize the theme used in your Gatsby site, create <em>src/gatsby-plugin-theme-ui</em> directory in your project, and add an index.js file to export a theme object (it’s called <a href="https://www.gatsbyjs.org/docs/themes/api-reference#component-shadowing">shadowing)</a></p>
<img src="https://lh3.googleusercontent.com/KBGoCMJEuy16-DvMi6a6EPM-VUmeJg0yCfUluuiRi3wU9MVRJTHBRh_4Zs8LPJGj8QvSdqK2D65uFCioUvP75nAj7GdIX3bBcvLyy9O0-tK9VfGCpBvep7HiAWsGksYaUqo3hff7"/>
<h2><b>Create your theme object</b></h2>
<p>Add colors object to the file:</p>
```javascript
export default {
  colors: {
    text: "#333",
    background: "#fff",
    primary: "#639",
    secondary: "#ff6347",
    white: "#fff",
  },
}
```
<p>Add some base typography.</p>
<p>Core typographic values can be stored in the following theme keys:</p>
<ul><li>fonts (font families)</li><li>fontSizes</li><li>fontWeights</li><li>lineHeights</li><li>letterSpacings</li></ul>
```javascript
export default {
  colors,
  fonts: {
    base: "system-ui, sans-serif",
    secondary: "Menlo, monospace",
  },
  fontWeights: {
    normal: 400,
    medium: 500,
    bold: 700,
  },
  lineHeights: {
    normal: 1.5,
    medium: 1.4,
  },
  fontSizes: {
    xs: 12,
    sm: 16,
    md: 32,
    lg: 48,
    xl: 64,
  },
  letterSpacings: {
    normal: "0",
    wide: "0.25em",
  },
}
```
<p>Add values for use in margin and padding.</p>
```javascript
export default {
  ...,
  space: [0,  8,  16,  32,  64],
}
</code></pre>
```
<p>Add an array of breakpoints. Each breakpoint should be a string with a CSS length unit included. </p>
<p>These values will be used to generate mobile-first (i.e. min-width) media queries, which can then be used to apply responsive styles.</p>
```javascript
breakpoints: ["768px", "1025px", "1290px"],
```
<p>To define the styles used across multiple buttons, typographic elements, or any elements, we can use <a href="https://theme-ui.com/guides/variants/">Variants </a>feature. </p>
<p>Let's define primary and secondary variants for buttons and use colors from the theme.</p>
```javascript
buttons: {
  primary: {
    color: "white",
    bg: "primary",
    "&:hover": {
    color: "primary",
    bg: "white",
    },
  },
  secondary: {
    color: "text",
    bg: "secondary",
    "&:hover": {
    color: "secondary",
    bg: "text",
    },
  },
},
```
<h2><b>Create UI components<b></h2>
<p>To style elements by referencing values from the theme object, use the sx prop in JSX. This custom function is enabled by including a comment on the top of the file.</p>
<p>Let’s create a Button component.</p>
<p>button.js:</p>
```javascript
/** @jsx jsx */
import { jsx } from "theme-ui"

const Button = ({ variant = "primary", ...props }) => (
  <button
    {...props}
    sx={{
      appearance: "none",
      display: "inline-block",
      textAlign: "center",
      fontSize: ["xs", "sm"],
      m: 0,
      px: 3,
      py: 2,
      border: 0,
      borderRadius: 4,
      variant: `buttons.${variant}`,
    }}
  />
)

export default Button
```
<p>The fontSize property is using array as a value which is a shorthand syntax for writing mobile-first responsive styles. ‘m’, ‘px’, ‘py’ properties are shorthands for applying margin and padding defined in theme.space. </p>
<p>To read more about how the values are converted and for a reference of supported CSS properties, see the <a href="https://styled-system.com/css/">@styled-system/css</a>. Variant is a prop passed to sx (button variants are defined in theme.buttons).</p>
<p>Render variants of buttons in pages/index.js. First of them is primary (as a default value for a variant prop), and in the second one, we passed ‘secondary’ to variant prop. </p>
```javascript
import Button from "../components/button"

const Index = () => (
  <>
    <Button>Primary</Button>
    <Button variant="secondary">Secondary</Button>
  </>
 )

export default Index
```
<p>The result is:</p>
<img src="https://pagepro.co/blog/wp-content/uploads/2020/01/gifiks.gif"/>
<p>Variants can also be used to create type styles. </p>
<p>Let’s create groups of texts using reference to other values defined in the theme or adding new styles.</p>
```javascript
{
  ...,
  text: {
    heading: {
      fontFamily: "base",
      fontWeight: "bold",
      lineHeight: "medium",
      fontSize: ["lg", "xl"],
    },
    title: {
      fontFamily: "base",
      fontWeight: "medium",
      lineHeight: "medium",
      fontSize: ["md", "lg"],
    },
    body: {
      fontFamily: "base",
      fontWeight: "normal",
      lineHeight: "normal",
      fontSize: "sm",
    },
    label: {
      fontFamily: "secondary",
      fontWeight: "normal",
      lineHeight: "normal",
      fontSize: "xs",
      letterSpacing: "wide",
      textTransform: "uppercase",
    },
  },
}
```
<p>Create Text component in text.js:</p>
```javascript
/** @jsx jsx */
import { jsx } from "theme-ui"

const Text = ({ variant = "heading", ...props }) => (
  <p
    {...props}
    sx={{
      m: 0,
      variant: `text.${variant}`,
    }}
  />
)

export default Text
```
<p>And render variants of them in pages/index.js:</p>
<p>The result is:</p>
<img src="https://lh6.googleusercontent.com/gWe2gmdaJB1euwr_SYgyi0MXrxfBCakh2mQVtCe6Nof3952dzMveybiCey6uVHAFYZ1ZrbjjQAtDJxuzc1rBDKvVQVwIVhPf82Bvx4LPpxQuaJtRiOQQbZ2VWxyjEzjb8VxHEJc2"/>
<h2><b>Styled component</b></h2>
<p>The typography looks pretty well but it’s not SEO-friendly. All the Text components are paragraphs and sometimes we would like to have the possibility to transform them into headings. </p>
<p>Let's make our typography more flexible.</p>
<p>There are two ways to render UI with any tag and both of them are really similar.</p>
<p>The second way is using <a href="https://theme-ui.com/styled">Styled component</a>. Import Styled and change static ‘p’ tag to Styled.p component in your text.js:</p>
```javascript
/** @jsx jsx */
import { jsx, Styled } from "theme-ui"

const Text = ({ variant = "heading", ...props }) => (
  <Styled.p
    {...props}
    sx={{
      m: 0,
      variant: `text.${variant}`,
    }}
  />
)

export default Text
```
<p>The following examples in pages/index.js render <code>&lt;p&gt;</code> that are styled like headings elements.</p>
```javascript
<p>The following examples in pages/index.js render <code>&lt;p&gt;</code> that are styled like headings elements.</p>

```
<img src="https://pagepro.co/blog/wp-content/uploads/2020/01/gifiks2.gif"/>
<p>It’s a great solution in which we can also use from styles defined in theme.styles object (in this example it could be in <a href="https://theme-ui.com/styled">theme.styles.p</a>.</p>
<p>The minus is that not all HTML elements are available with the dot notation syntax. For a full list of keys available, see the <a href="https://theme-ui.com/theme-spec#styles">Theme Specification docs</a>.</p>
<p>The second option is styled. </p>
<p>styled is a way to create React components that have styles attached to them. It’s available from @emotion/styled.</p>
<p>Install @emotion/styled package to enable creating Styled components.</p>
```javascript
yarn add @emotion/styled
```
<p>Import styled and create your styled component. You can write some <a href="https://emotion.sh/docs/styled">CSS styles inside</a>:</p>
```javascript
/** @jsx jsx */
import { jsx } from "theme-ui"

import styled from "@emotion/styled"

const StyledText = styled.p`
  margin: 0;
`

const Text = ({ variant = "heading", ...props }) => (
  <StyledText
    {...props}
    sx={{
      variant: `text.${variant}`,
    }}
  />
)

export default Text
```
<p>As in ‘Styled’ way, the Text components will be rendered with the appropriate tags defined in ‘as’ prop.</p>
<p>You can change an HTML component as you want. This can also be used to style components like Link which can be internal or external links. </p>
<p>For example, you can create a simple ‘anchor’ styled component to external links and change them to the Gatsby Link component (to match internal links) when you will need it.</p>
```javascript
<Link as={Link} to="about" variant="label">
  heading / hello world
</Link>
```
<h2><b>Conclusion</b></h2>
<p>Theme UI includes a lot of features that can help you build your site. It can be used to extend existing Gatsby themes or styling MDX content without the need to add global CSS to your site. </p>
<p>It supports to create a user-configurable dark mode or any number of other color modes. You can style your page without creating components or use pre-build UI components (like layouts, grid, buttons, form, etc.) from optional package @theme-ui/components.  </p>
<p>I really recommend looking into <a href="https://theme-ui.com/components/">Theme UI documentation</a> to get to know more about this wonderful tool.</p>