Extend
Some components are another component plus a little more: an icon button is a button with a fixed width, a danger card is a card with one more tone. extend builds one ev() result on top of another, so the shared styles and variants are declared once.
Build on another function
Pass an ev() result as extend. The new function inherits everything the parent declared.
import { ev } from 'emotion-variants';
const button = ev({
base: {
border: '1px solid transparent',
borderRadius: 8,
cursor: 'pointer',
fontWeight: 600,
transition: 'all 0.2s ease',
},
variants: {
variant: {
filled: {
background: 'var(--button-color)',
color: 'white',
},
outlined: {
background: 'transparent',
borderColor: 'var(--button-color)',
color: 'var(--button-color)',
},
text: {
background: 'transparent',
color: 'var(--button-color)',
},
},
size: {
small: {
padding: '8px 16px',
fontSize: 12,
},
medium: {
padding: '10px 20px',
fontSize: 14,
},
large: {
padding: '12px 24px',
fontSize: 16,
},
},
color: {
primary: {
'--button-color': '#d36ac2',
},
secondary: {
'--button-color': '#475569',
},
},
},
defaultVariants: {
variant: 'filled',
size: 'medium',
color: 'primary',
},
});
const iconButton = ev({
extend: button,
base: {
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
width: 44,
height: 44,
},
});
<button css={button()}>Button</button>
<button aria-label="Close" css={iconButton()}>
✕
</button>iconButton accepts variant, size, and color, applies the parent's defaults, and adds its own base after the parent's. button itself is not changed.
Add values and axes
A child can add values to an inherited axis, add styles to an inherited value, and declare new axes.
const iconButton = ev({
extend: button,
variants: {
size: {
large: {
width: 48,
},
huge: {
fontSize: 24,
},
},
round: {
true: {
borderRadius: '50%',
},
},
},
});
iconButton.variants(); // ['variant', 'size', 'color', 'round']size: 'small'is inherited as it is.size: 'large'gets the parent's font size, then the child's width.size: 'huge'and theroundaxis exist only oniconButton.
Inherited axes keep their position, and new axes come after them.
Defaults and compound variants
defaultVariants are merged per axis, and the child's value wins. The child's compoundVariants and defaultVariants can refer to inherited axes and values.
const iconButton = ev({
extend: button,
variants: {
round: {
true: {
borderRadius: '50%',
},
},
},
compoundVariants: [
{
variants: {
variant: 'outlined',
round: true,
},
style: {
borderWidth: 2,
},
},
],
defaultVariants: {
size: 'large',
},
});The parent's compound variants are inherited too, and they are matched against the values the child resolved.
Order
At every step, the parent's styles come before the child's. Later entries win on CSS conflicts.
base, parent then child- Matching
variantsentries, axis by axis; for each value, parent then child - Matching
compoundVariantsentries, parent then child
Every compound variant comes after every variant style. So a parent's compound variant still wins over a style the child declares for the same variant value. To override it, add a compound variant with the same condition to the child.
The chain can be as long as you like. Extending iconButton again puts button first, iconButton second, and the new config last.
With slots
A slots function extends another slots function. The child can add slots, and add styles to inherited slots.
const card = ev({
slots: {
root: {
padding: 16,
},
title: {
fontWeight: 600,
},
},
});
const mediaCard = ev({
extend: card,
slots: {
root: {
overflow: 'hidden',
},
footer: {
display: 'flex',
},
},
});
const { root, title, footer } = mediaCard();A child that declares no slots keeps the parent's slots, and its variant values are still keyed by slot name.
Class names
className() includes the inherited styles. In classNames, each entry merges the parent's and the child's styles for that key, and a value the child did not touch keeps the parent's class name.
Rules
extendtakes a singleev()result. Anything else throws whenev()runs. If the value did come fromev(), two copies ofemotion-variantsare probably installed.- A config with
slotscan only extend one withslots. Extending a plain function throws, and is a type error. - When the parent has
slots, the child cannot usebase. Put the styles in a slot. This is a type error.
Props
VariantProps on an extended function gives you the merged variants.
import type { VariantProps } from 'emotion-variants';
type IconButtonVariants = VariantProps<typeof iconButton>;
// {
// variant?: 'filled' | 'outlined' | 'text';
// size?: 'small' | 'medium' | 'large';
// color?: 'primary' | 'secondary';
// round?: true;
// }See also
Last updated on