Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Theming in the future #973

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 77 additions & 12 deletions docs/theming.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,28 @@
title: 'Theming'
---

Theming is provided by the library [`emotion-theming`](https://emotion.sh/docs/emotion-theming).
Emotion 10 and above doesn’t have any explicit theming API, instead, we recommend using React’s Context and Hooks which work well together with Emotion. This has many benefits over having a built in theming API which are explained below.

```bash
npm install -S emotion-theming
We recommend creating a file which exports a ThemeProvider component and a useTheme hook.

```jsx
import { createContext, useContext } from 'react'

let defaultTheme = {
colors: {
primary: 'hotpink',
secondary: 'green'
}
}

let ThemeContext = createContext(defaultTheme)

export let ThemeProvider = ThemeContext.Provider

export let useTheme = () => useContext(ThemeContext)
```

Add `ThemeProvider` to the top level of your app and access the theme with `props.theme` in a styled component or provide a function that accepts the theme as the css prop. The api is laid out in detail [in the documentation](https://emotion.sh/docs/emotion-theming).
The theme can be used with css prop like this.

## Examples

Expand Down Expand Up @@ -38,21 +53,71 @@ render(
### styled

```jsx
// @live
/** @jsx jsx */
import { jsx } from '@emotion/core'
import { useTheme } from './theme'

let Button = props => {
let theme = useTheme()
return (
<button
css={{
color: theme.colors.primary
}}
{...props}
/>
)
}

let SecondaryButton = props => {
let theme = useTheme()
return (
<button
css={{
color: theme.colors.secondary
}}
{...props}
/>
)
}
```

Or with the styled api like this

```jsx
import styled from '@emotion/styled'
import { ThemeProvider } from 'emotion-theming'
import { useTheme } from './theme'

const theme = {
colors: {
primary: 'hotpink'
let Button = styled.button(() => {
let theme = useTheme()
Copy link

@stramel stramel Dec 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This usage and the below usage trigger errors in the latest React.

Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.

This is the line it errors on

const Card = styled.div`
	background-color: ${() => useTheme().palette.white}; /* Error */
`

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stramel have you tried to use this with emotion 10 or 11? It can only work with the latter.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh let me try! Thank you for the quick reply!

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Andarist I'm running it inside Docz v2 using @emotion/styled and @emotion/core on version 11.0.0-next.9.

When starting the project it complains because it can't find @emotion/styled-base.

ERROR #98123  WEBPACK

Generating SSR bundle failed

Can't resolve '@emotion/styled-base' in 'Projects/libs/atoms/src/lib/Button'

so I installed @emotion/styled-base version v11.0.0-next.3 and it is telling me i need to use @emotion/styled?

When I tried the v10 version of @emotion/styled-base it obviously failed... 😞

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like maybe you have 2 versioms of emotion in your node modules for some reason or that you are using v10 of our babel plugin. @emotion/styled-base has been removed in v11 in favor of @emotion/styled/base.

If you prepare a runnable repro case then I could take a look at it.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Andarist Looks like you were correct. Just tried it in my Next.js app with v11 and no errors. Doing some debugging...

yarn why @emotion/core and yarn why @emotion/styled both result in v10 and v11 because Docz and the gatsby docz theme both use v10

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any suggestions how to fix it?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I filed an issue on docz asking for a bumping to v11 but I'm not sure what the workaround would be. doczjs/docz#1335

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @stramel! Anyone know the expected release date of v11?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't promise anything, but hopefully within a month. We have most of the things done, some final touches left, but this is a work we are doing in our free time so there is also a possibility that this will take some more time.


return {
color: theme.colors.primary
}
}
})

const SomeText = styled.div`
color: ${props => props.theme.colors.primary};
let SecondaryButton = styled.button`
color: ${() => useTheme().colors.secondary};
`
```

## Why don’t we have a built in theming API?

Theming in emotion v9 and earlier was inspired by theming in styled-components which was created before React’s new Context and Hooks existed. This meant having access to a value through context was novel but with React's new features, problems with that API can be solved and having a theming API built in is unnecessary

### Type Safety

The previous theming API was extremely hard to statically type. By using context and hooks directly, a default value can be provided so type systems can guarantee that there's always a theme that can be accessed.

### Global Namespace

Since there was a single object which contained the theme, if a component from npm or somewhere wanted to use theming, it had to choose a unique name store it’s theme in which meant name conflicts could occur. By directly using the Context API, this problem doesn't exist.

### Testing

To test components that used the previous theming API, either a default theme has to be provided with defaultProps or a ThemeProvider has to be wrapped around every component which could get really inconvenient. By providing a default value when the theme is created, components can just be rendered.

### Over-subscription

render(
<ThemeProvider theme={theme}>
Expand Down