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

Add theme-aware Global component #2385

Merged
merged 10 commits into from
Jan 22, 2023
6 changes: 3 additions & 3 deletions examples/next/lib/theme.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { makeTheme } from '@theme-ui/css/utils'
// import { makeTheme } from '@theme-ui/css/utils'
Copy link
Member

Choose a reason for hiding this comment

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

After running pnpm build Next.js dev server doesn't catch that the libraries are built instantly, so I removed .next dir in the example, restarted pnpm dev and the error on this import went away.

The error was basically saying we can't import TypeScript files directly, and that's totally fine, because we want to develop examples against prebuilt libraries, but even after rebuilding you gotta clean the cache if you started dev server previously.


export const theme = makeTheme({
export const theme = {
config: {
initialColorModeName: 'light',
useColorSchemeMediaQuery: true,
Expand Down Expand Up @@ -49,4 +49,4 @@ export const theme = makeTheme({
cursor: 'pointer',
},
},
})
}
1 change: 1 addition & 0 deletions examples/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@mdx-js/loader": "^2.1.2",
"@mdx-js/react": "^2.1.2",
"@next/mdx": "^12.0.7",
"@theme-ui/css": "workspace:^",
"next": "^12.1.0",
"react": "^18",
"react-dom": "^18",
Expand Down
2 changes: 2 additions & 0 deletions examples/next/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import Head from 'next/head'
import About from '../components/about.mdx'
import { Global } from 'theme-ui'

export default function Page() {
return (
<>
<Head>
<title>Next.js Theme UI</title>
</Head>
<Global sx={{ h1: { color: 'salmon !important' } }} />
<About />
</>
)
Expand Down
1 change: 1 addition & 0 deletions packages/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@theme-ui/core": "workspace:^",
"@theme-ui/components": "workspace:^",
"@theme-ui/css": "workspace:^",
"@theme-ui/global": "workspace:^",
"@theme-ui/match-media": "workspace:^",
"@theme-ui/mdx": "workspace:^",
"@theme-ui/presets": "workspace:^",
Expand Down
32 changes: 16 additions & 16 deletions packages/docs/src/pages/guides/global-styles.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,34 @@ title: 'Global Styles'

# Global Styles

Use the Emotion `Global` component to add global CSS with theme-based values.
Theme UI offers a `Global` component (that wraps Emotion’s) for adding global
CSS with theme-based values.

By default, the `ThemeProvider` component will apply styles in `theme.styles.root` to the `<html>` element.
It will also apply `color` and `background-color` styles based on `theme.colors.text` and `theme.colors.background` respectively.
By default (or, unless the
[`useRootStyles` configuration option](/theming#configuration-flags)is
disabled), the `ThemeProvider` component will apply styles in
`theme.styles.root` to the `<html>` element. It will also apply `color` and
`background-color` styles based on `theme.colors.text` and
`theme.colors.background` respectively.

<Note>
Generally, you should try to avoid adding CSS to global scope. Many styles can
be safely encapsulated into a component without the need for global styles.
</Note>

```jsx
import { Global } from '@emotion/react'
import { Global } from 'theme-ui'

export default (props) => (
<Global
styles={(theme) => ({
'*': {
boxSizing: 'border-box',
sx={{
button: {
m: 0,
bg: 'primary',
color: 'background',
border: 0,
},
})}
}}
/>
)
```

<Note>

If you are upgrading from a version of theme-ui older that v0.6.0, be aware the import
package has changed from `@emotion/core` to `@emotion/react`. For more information see
the [Migration Notes for 0.6](https://theme-ui.com/migrating/#breaking-changes).

</Note>
7 changes: 7 additions & 0 deletions packages/docs/src/pages/packages/global.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: '@theme-ui/global'
---

import Readme from '@theme-ui/global/README.md'

<Readme />
1 change: 1 addition & 0 deletions packages/docs/src/sidebar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
- [@theme-ui/core](/packages/core)
- [@theme-ui/components](/packages/components)
- [@theme-ui/mdx](/packages/mdx)
- [@theme-ui/global](/packages/global)
- [@theme-ui/presets](/packages/presets)
- [@theme-ui/prism](/packages/prism)
- [@theme-ui/color](/packages/color)
Expand Down
2 changes: 1 addition & 1 deletion packages/docs/static/card.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/docs/static/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 27 additions & 0 deletions packages/global/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# @theme-ui/global

Wrapper around the Emotion `Global` component, made Theme UI theme-aware.

**Note:** _This package is included in the main `theme-ui` package and a
separate installation is not required unless you’re using `@theme-ui/core`._

```sh
npm i @theme-ui/global @theme-ui/core @emotion/react
```

```jsx
import Global from '@theme-ui/global'

export default (props) => (
<Global
sx={{
button: {
m: 0,
bg: 'primary',
color: 'background',
border: 0,
},
}}
/>
)
```
27 changes: 27 additions & 0 deletions packages/global/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "@theme-ui/global",
"version": "0.15.4",
"repository": "system-ui/theme-ui",
"main": "dist/theme-ui-global.cjs.js",
"module": "dist/theme-ui-global.esm.js",
"source": "src/index.tsx",
"author": "Lachlan Campbell",
"license": "MIT",
"sideEffects": false,
"publishConfig": {
"access": "public"
},
"dependencies": {
"@theme-ui/core": "workspace:^",
"@theme-ui/css": "workspace:^"
},
"peerDependencies": {
"@emotion/react": "^11",
"react": ">=18"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"@types/react": "^18",
"@theme-ui/test-utils": "workspace:^"
}
}
14 changes: 14 additions & 0 deletions packages/global/src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react'
import { jsx, type ThemeUIStyleObject } from '@theme-ui/core'
import { css, type Theme } from '@theme-ui/css'
import { Global as EmotionGlobal } from '@emotion/react'

const Global = ({ sx }: { sx: ThemeUIStyleObject }) =>
jsx(EmotionGlobal, {
styles: (emotionTheme: unknown) => {
const theme = emotionTheme as Theme
return css(sx)(theme)
Copy link
Member

Choose a reason for hiding this comment

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

I'm kinda wondering if

useMemo(() => css(sx)(theme), [sx, theme])

here would be an overkill.
We can always add it later after we know more I suppose.

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh good call. I wish evaluating perf with hooks like that with were easier…conceptually makes sense to use it at least

},
})

export default Global
84 changes: 84 additions & 0 deletions packages/global/test/__snapshots__/index.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders global and component styles 1`] = `
.emotion-0 {
color: pink;
}

<html>
<head>
<style
data-emotion="css"
data-s=""
>

.css-2cx43c{}
</style>
<style
data-emotion="css"
data-s=""
>

.css-2cx43c html{background-color:hotpink;}
</style>
<style
data-emotion="css"
data-s=""
>

.emotion-0{color:pink;}
</style>
</head>
<body>
<div>
<header>
<div
class="emotion-0"
/>
</header>
</div>
</body>
</html>
`;

exports[`renders global styles 1`] = `
<html>
<head>
<style
data-emotion="css"
data-s=""
>

.css-ck0s41{}
</style>
<style
data-emotion="css"
data-s=""
>

@font-face{font-family:some-name;}
</style>
<style
data-emotion="css"
data-s=""
>

.css-ck0s41 body{font-family:Georgia,serif;margin:0;}
</style>
<style
data-emotion="css"
data-s=""
>

.css-ck0s41 h1{color:salmon;}
</style>
</head>
<body>
<div>
<h1>
Hello
</h1>
</div>
</body>
</html>
`;
84 changes: 84 additions & 0 deletions packages/global/test/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/**
* @jest-environment jsdom
* @jsx jsx
*/

import { jsx } from '@theme-ui/core'
import { cleanup } from '@testing-library/react'
import { render } from '@theme-ui/test-utils'
import { matchers } from '@emotion/jest'

import { ThemeProvider } from '@theme-ui/core'
import Global from '../src'

expect.extend(matchers)

beforeEach(() => {
document.head.innerHTML = ''
jest.resetAllMocks()
})

afterEach(cleanup)

test('renders global styles', async () => {
const root = (
<ThemeProvider
theme={{
config: {
useRootStyles: false,
},
fonts: {
body: 'Georgia,serif',
},
colors: {
primary: 'salmon',
},
}}
>
<Global
sx={{
'@font-face': {
fontFamily: 'some-name',
},
body: {
fontFamily: 'body',
margin: 0,
},
h1: {
color: 'primary',
},
}}
/>
<h1>Hello</h1>
</ThemeProvider>
)

const document = render(root)
expect(document.baseElement.parentElement).toMatchSnapshot()

// const style = window.getComputedStyle(document.baseElement.parentElement!)
// expect(style.fontFamily).toBe('Georgia,serif')
// expect(style.margin).toBe('0px')

// const h1 = document.baseElement.querySelector('h1')!
// const h1Style = global.getComputedStyle(h1)
// expect(h1Style.fontFamily).toBe('Georgia,serif')
// expect(h1Style.color).toBe('salmon')
})

test('renders global and component styles', () => {
const root = (
<header>
<Global
sx={{
html: {
backgroundColor: 'hotpink',
},
}}
/>
<div sx={{ color: 'pink' }} />
</header>
)
const { baseElement } = render(root)
expect(baseElement.parentElement).toMatchSnapshot()
})
5 changes: 2 additions & 3 deletions packages/sidenav/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/** @jsx jsx */
import { jsx, ThemeProvider, Theme, ThemeStyles } from 'theme-ui'
import { jsx, ThemeProvider, Theme, ThemeStyles, Global } from 'theme-ui'
import { MDXProvider, MDXProviderComponents } from '@mdx-js/react'
import React, {
useState,
Expand All @@ -11,7 +11,6 @@ import React, {
ReactComponentElement,
ReactElement,
} from 'react'
import { Global } from '@emotion/react'
import merge from 'deepmerge'

const createNestedLinks = (
Expand Down Expand Up @@ -66,7 +65,7 @@ const Overlay = ({ onClick }: OverlayProps) => (
}}
/>
<Global
styles={{
sx={{
body: {
overflow: ['hidden', 'auto'],
},
Expand Down
1 change: 1 addition & 0 deletions packages/theme-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@theme-ui/components": "workspace:^",
"@theme-ui/core": "workspace:^",
"@theme-ui/css": "workspace:^",
"@theme-ui/global": "workspace:^",
"@theme-ui/theme-provider": "workspace:^"
},
"peerDependencies": {
Expand Down
Loading