💅 Styling

Colour & Subthemes

Abstracting colour in your project

Defining colours is easy enough! In your theme.css.tsx file, you can define a flat set of colours:

theme.css.tsx
    colors: {
      // Brand/literal colors
      russet: "#AF5861",
      gold: "#9B7850",
      buff: "#CFCAC5",
      white: "white",
      black: "black",

      // Semantic colors, which draw from the brand colors
      // If your project requires multiple colour palettes, these should be the defaults
      background: "$white",
      foreground: "$russet",
      highlight: "$gold",
      reverse: "$black",
    },

Abstract Names

We consider it best practice to break your colours out into 'literal' and 'semantic' colours, which you can see in the above example code.

  • Literal colours will be the specific colour names, whether they be black, russet, brand, rose, grey-200 etc.
  • Semantic colours give meaning to your colors, and should map to your literal colours. background, foreground, link, faded etc are all semantic colours.

Using Colours

Similar to any other token, you can use colour tokens directly in your styled components:

You should try your best to use semantic colours, especially on sites which use multiple colour themes.

const MyComponent = styled("div", {
  color: "$foreground",
})

Subthemes

In the default theme.css.tsx, you'll notice a subthemes export. This is a map of subthemes, which you can use to create a new colour theme.

When you create a subtheme with createTheme(), you're actually defining a global class. The class can then be applied to any element, and the colour overrides your define within it will be inherited by all child components.

theme.css.tsx
export const subthemes = {
  // The 'block-editor-page' class is automatically applied
  // to blocks in the Gutenberg editor, and so this theme will be
  // used there automatically!
  editor: createTheme("block-editor-page", {
    colors: {
      background: "white",
      foreground: "black",
      link: "$blue",
    },
  }),
  // We're creating a 'dark-mode' subtheme
  darkMode: createTheme("dark-mode", {
    colors: {
      background: "black",
      foreground: "white",
      link: "$gold",
    },
  }),
}
💡
You don't need to override all colours in a subtheme — just the one's you want to override

In the above example, we're defining two subthemes:

  • An editor theme, which will be used in the Gutenberg editor, because it has the block-editor-page class which is applied to the root div of the Gutenberg UI.
  • A darkMode theme.

With the latter theme, there are two ways in which we can use this:

Subtheme Class names

<Card className="dark-mode">
  This div will have a black background, with white text — from the subtheme
</Card>

<Card>
  This div will have a background + color from the default theme
</Card>

const Card = styled('div', {
  backgroundColor: '$background',
  color: '$foreground'
})

useSubtheme() for global subtheming

The useSubtheme() hook which comes pre-installed, allows you to swap your entire page's theme to a subtheme, when mounting a React component — and then reverts it when the component is unmounted.

The most common usage for this is when you have a whole page or template which uses different colours to the rest of the site:

views/single-project.tsx
import { useSubtheme } from '@hooks/useSubtheme'

export default defineView('single-project', () => {
  // The whole page will use the darkMode colours, until the user navigates to another page
  useSubtheme('darkMode')

  return <div>
    /* ... */
  </div>
})

You can also programatically swap themes, which can be useful in cases where you want to change the theme of a page during specific interactions (eg. scrolling, or clicking). Use the second optional active parameter for this.

function DarkModeToggle() {
  const [darkMode, setDarkMode] = useState(false)
  useSubtheme("darkMode", darkMode)
  return <button onClick={() => setDarkMode(!darkMode)}>Toggle Dark mode</button>
}
⚠️

Note that useSubtheme() takes the object key from the subthemes export in theme.css.tsx, rather than the class name.