diff --git a/CHANGELOG.md b/CHANGELOG.md
index 424ff81b5..5b492b18f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
- Adjust media query sort logic #600
- Fixed link to Gatsby Plugin page in `getting-started` page. Issue #602
+- Adds support for combining multiple variants (pass `variants` an array in `sx` or as a prop on components)
## v0.3.0 2019-01-22
diff --git a/packages/components/package.json b/packages/components/package.json
index 849616e59..171457a8a 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -14,7 +14,8 @@
"@styled-system/color": "^5.1.2",
"@styled-system/should-forward-prop": "^5.1.2",
"@styled-system/space": "^5.1.2",
- "@theme-ui/css": "^0.3.1"
+ "@theme-ui/css": "^0.3.1",
+ "deepmerge": "^4.2.2"
},
"peerDependencies": {
"react": "^16.8.0"
diff --git a/packages/components/src/Box.js b/packages/components/src/Box.js
index c4c0c0bfa..e75a0b1ad 100644
--- a/packages/components/src/Box.js
+++ b/packages/components/src/Box.js
@@ -3,6 +3,7 @@ import { css, get } from '@theme-ui/css'
import { createShouldForwardProp } from '@styled-system/should-forward-prop'
import space from '@styled-system/space'
import color from '@styled-system/color'
+import deepmerge from 'deepmerge'
const shouldForwardProp = createShouldForwardProp([
...space.propNames,
@@ -11,8 +12,17 @@ const shouldForwardProp = createShouldForwardProp([
const sx = props => css(props.sx)(props.theme)
const base = props => css(props.__css)(props.theme)
+
const variant = ({ theme, variant, __themeKey = 'variants' }) =>
css(get(theme, __themeKey + '.' + variant, get(theme, variant)))
+const variants = ({ theme, variants = [], __themeKey = 'variants' }) => {
+ if (!Array.isArray(variants)) return {}
+ return css(
+ deepmerge.all(
+ variants.map(v => get(theme, __themeKey + '.' + v, get(theme, v)))
+ )
+ )
+}
export const Box = styled('div', {
shouldForwardProp,
@@ -23,6 +33,7 @@ export const Box = styled('div', {
minWidth: 0,
},
base,
+ variants,
variant,
space,
color,
diff --git a/packages/components/test/__snapshots__/index.js.snap b/packages/components/test/__snapshots__/index.js.snap
index 203351cc5..d8463d31e 100644
--- a/packages/components/test/__snapshots__/index.js.snap
+++ b/packages/components/test/__snapshots__/index.js.snap
@@ -180,6 +180,18 @@ exports[`Box renders 1`] = `
`;
+exports[`Box renders with incorrect type of variants prop 1`] = `
+.emotion-0 {
+ box-sizing: border-box;
+ margin: 0;
+ min-width: 0;
+}
+
+
+`;
+
exports[`Button renders 1`] = `
.emotion-0 {
box-sizing: border-box;
diff --git a/packages/components/test/index.js b/packages/components/test/index.js
index 3d7a9d279..4a86d1607 100644
--- a/packages/components/test/index.js
+++ b/packages/components/test/index.js
@@ -48,6 +48,10 @@ const theme = {
p: 4,
bg: 'highlight',
},
+ boop: {
+ bg: 'tomato',
+ color: 'white',
+ },
},
cards: {
primary: {
@@ -132,6 +136,38 @@ describe('Box', () => {
expect(json).toHaveStyleRule('padding', '32px')
})
+ test('renders with variants prop', () => {
+ const json = renderJSON(
+
+
+
+ )
+ expect(json).toHaveStyleRule('padding', '32px')
+ expect(json).toHaveStyleRule('background-color', 'tomato')
+ expect(json).toHaveStyleRule('color', 'white')
+ })
+
+ test('renders with variant and variants prop', () => {
+ const json = renderJSON(
+
+
+
+ )
+ expect(json).toHaveStyleRule('font-size', '32px')
+ expect(json).toHaveStyleRule('padding', '32px')
+ expect(json).toHaveStyleRule('background-color', 'tomato')
+ expect(json).toHaveStyleRule('color', 'white')
+ })
+
+ test('renders with incorrect type of variants prop', () => {
+ const json = renderJSON(
+
+
+
+ )
+ expect(json).toMatchSnapshot()
+ })
+
test('renders with base styles', () => {
const json = renderJSON(
{
key = key && key.split ? key.split('.') : [key]
for (p = 0; p < key.length; p++) {
@@ -191,6 +193,12 @@ export const css = args => (props = {}) => {
continue
}
+ if (key === 'variants') {
+ const variants = css(get(theme, val))(theme)
+ result = { ...result, ...variants }
+ continue
+ }
+
if (val && typeof val === 'object') {
result[key] = css(val)(theme)
continue
diff --git a/packages/css/test/index.js b/packages/css/test/index.js
index e1b7fcf04..b6e106f85 100644
--- a/packages/css/test/index.js
+++ b/packages/css/test/index.js
@@ -230,6 +230,21 @@ test('handles responsive variants', () => {
})
})
+test('returns multiple variants from theme', () => {
+ const result = css({
+ variants: ['text.caps', 'text.title'],
+ })(theme)
+ expect(result).toEqual({
+ fontSize: 16,
+ letterSpacing: '-0.01em',
+ textTransform: 'uppercase',
+ '@media screen and (min-width: 40em)': {
+ fontSize: 16,
+ letterSpacing: '-0.02em',
+ },
+ })
+})
+
test('handles negative margins from scale', () => {
const result = css({
mt: -3,
diff --git a/packages/docs/src/pages/components/variants.mdx b/packages/docs/src/pages/components/variants.mdx
index 1a7c0828b..6a3aee37b 100644
--- a/packages/docs/src/pages/components/variants.mdx
+++ b/packages/docs/src/pages/components/variants.mdx
@@ -64,6 +64,17 @@ For example, a secondary button style can be added to `theme.buttons.secondary`
```
+## Combining Multiple Variants
+
+If you want to combine multiple variants on the same element, pass an array of variants with the `variants` prop.
+If styles conflict, those of the variant passed later will render.
+
+```jsx live=true
+
+ Title text
+
+```
+
## Example Theme
```js
diff --git a/packages/docs/src/pages/guides/variants.mdx b/packages/docs/src/pages/guides/variants.mdx
index 3ca36662f..3bd07a615 100644
--- a/packages/docs/src/pages/guides/variants.mdx
+++ b/packages/docs/src/pages/guides/variants.mdx
@@ -29,6 +29,9 @@ For example, you can define `primary` and `secondary` variants for buttons and u
color: 'white',
bg: 'secondary',
},
+ small: {
+ fontSize: 1
+ }
},
}
```
@@ -51,6 +54,15 @@ Variants can use any name you choose, and deeply nested objects can be accessed
+## Combining Multiple Variants
+
+If you want to combine multiple variants on the same element, pass an array to `variants` in the `sx` prop.
+If styles conflict, those of the variant passed later will render.
+
+```jsx
+
+```
+
## Color Modes
Variants will also work with [color modes](/color-modes).