Styling
Admin-ui handles component stylization with the className prop, like native components. Components with their own
styling merge the inputed className with the internal one. We provide a function that generates classNames based
on a StyleObject
. It lets you style elements while consuming values from the theme.
Styling helper functionsβ
csxβ
Function that transforms a valid StyleObject into a className
. It is used to style native JSX elements and support integrations with other libraries while being consistent.
Exampleβ
Recomendationsβ
- β Use it with native elements:
import { csx } from '@vtex/admin-ui'
function Example() {
return (
<nav
className={csx({
bg: 'muted',
'button + button': { marginLeft: 2 },
})}
/>
)
}
- β Use it with custom libraries that accept a className like admin-ui:
import { csx, Button } from '@vtex/admin-ui'
function Example() {
return (
<Button
className={csx({
margin: '$m',
})}
/>
)
}
- β
Prefer to execute
csx
calls outside the component function to get better rendering performance:
// β
Preferred
// warning.css.ts
export const warningComponentTheme = csx({
backgroundColor: '$primary',
paddingLeft: '$xs',
paddingRight: '$xs',
height: '20rem',
width: '20rem',
})
// mainFile.ts
import { warningComponentTheme } from './warning.css.ts'
function WarningComponent() {
return <div className={warningComponentTheme} />
}
// π¨ Not wrong, but less performatic
// mainFile.ts
function WarningComponent() {
return (
<div
className={csx({
backgroundColor: '$primary',
paddingLeft: '$xs',
paddingRight: '$xs',
height: '20rem',
width: '20rem',
})}
/>
)
}
cxβ
A function that combines two or more class names generated by the csx
function or even native ones.
Combining class namesβ
dataAttrβ
Function that generates a data attribute selector based on a property and value.
It's used internally to generate selectors when data attributes are used for styling.
// dataAttr('variant', 'primary') => "[data-variant='primary']"
// dataAttr({ size: 'large', 'compact': true }) => "[data-size='large'][data-compact=true]"
// with dataAttr
const style = csx({
[dataAttr('variant', 'primary')]: {
margin: '1px',
},
[dataAttr({ size: 'large', 'compact': true })]: action({
height: '$m'
}),
})
// equivalent with plain values
const style2 = csx({
"[data-variant='primary']": {
margin: '1px',
},
"[data-size='large'][data-compact=true]": action({
height: '$m'
}),
})
Design tokensβ
Design tokens can be consumed from specific properties within the StyleObject
. They are the recommended values for those props if you desire to reach consistency between apps. These are defined within our default theme
, and you can check all the available design tokens on the link.
Aliasesβ
It works as a shorthand to apply a value to one or more CSS properties, and you can use it within the StyleObject
. In addition to the following aliases, you can also use the Responsive Aliases explained further on this page.
Alias | Description |
---|---|
bg | A shorthand for background CSS property |
fg | A shorthand for color CSS property |
colorTheme | A shorthand for applying color and background properties from the same palette while mantaining design consistency |
marginX | A shorthand for margin-left and margin-right CSS properties |
marginY | A shorthand for margin-top and margin-bottom CSS properties |
paddingX | A shorthand for padding-left and padding-right CSS properties |
paddingY | A shorthand for padding-top and padding-bottom CSS properties |
size | A shorthand for width and height CSS properties |
minSize | A shorthand for min-width and min-height CSS properties |
maxSize | A shorthand for max-width and max-height CSS properties |
absoluteSize | A shorthand for min-width, min-height, max-Width, and max-Height CSS properties |
Exampleβ
Nestingβ
Sometimes itβs useful to nest selectors to target elements inside the current class or React component. An example with an element selector is shown below.
classNameβ
Scoped classNames can also be created and reused multiple times.
Data attributesβ
Some admin-ui components use data attributes to style different variations of the same component. For example components that take props that affect it's color, size or layout.
Since these components use data attribute selectors in their css objects, you must also use the same data-attribute selector if you want to overwrite a style that was set inside a selector.
- Simplified example
// simplified internal button css
export const buttonTheme = csx({
//...
[dataAttr('variant', 'primary')]: {
color: `action.main.primary`,
bg: `action.main.primary`,
},
[dataAttr('variant', 'secondary')]: action({
color: `action.main.secondary`,
bg: `action.main.secondary`,
}),
})
// π¨ Doesn't work, this bg style won't have an effect
import { Button } from '@vtex/admin-ui'
function WarningComponent() {
return (
<>
<Button className={csx({ bg: 'black' })}>Primary</Button>
<Button variant="secondary" className={csx({ bg: 'black' })}>
Secondary
</Button>
</>
)
}
// β
Works in overwriting the bg style
import { Button } from '@vtex/admin-ui'
function WarningComponent() {
return (
<>
<Button
className={csx({
[dataAttr('variant', 'primary')]: {
bg: 'black',
},
})}
>
Primary
</Button>
<Button
variant="secondary"
className={csx({
[dataAttr('variant', 'secondary')]: {
bg: 'black',
},
})}
>
Secondary
</Button>
</>
)
}
Pseudo-classesβ
You can use all CSS Pseudo-classes such as :hover
, :active
, :focus
and more. For example:
Pseudo-elementsβ
You can use all CSS Pseudo-elements such as :before
and :after
. For example:
Animationsβ
Keyframesβ
CSS animations are supported. You can use the keyframes
function to configure the sequence timing.
Responsive designβ
Responsive aliasesβ
Admin UI has five responsive aliases that guide you while authoring responsive layouts.
alias | min-width em | min-width px | max-width em | max-width px |
---|---|---|---|---|
@tablet | 48em | 768px | - | - |
@tabletOnly | 48em | 768px | 64em | 1024px |
@desktop | 64em | 1024px | - | - |
@desktopOnly | 64em | 1024px | 75em | 1200px |
@widescreen | 75em | 1200px | - | - |
In the example - the text content changes, representing the current screen size. You can resize your browser window to see the result.
π‘ What happens under the hood?
The responsive alias string will be replaced by a media query.
It replaces the pattern @[alias] by @media (min-width: theme.breakpoints[alias]).
For exemple, @tablet will be replaced by @media (min-width: 48em).
The same idea applies to the only aliases! The diference is that it set the next breakpoint's min-width as its max-width, creating a interval.
For exemple, @tabletOnly will be replaced by @media (min-width: 48em) and (max-width: 64em).
Mental modelβ
Our styles have a mobile-first mindset. It means that every style that you write targets mobile by default. Each responsive alias targets all others above it so that you can work in optimizing your layout for larger screens. In other words, always work optimizing space.
The div
in the example has the $s
padding for all screens:
<div
className={csx({
padding: '$s',
})}
/>
If we add the @tablet
rule, the padding will be $m
for tablets and above - which means that it will remain $s
for screens smaller than @tablet
. The logic is still the same for other breakpoints.
<div
className={csx({
padding: '$s',
'@tablet': {
padding: '$m',
},
})}
/>
Custom media queriesβ
The responsive aliases can cover most of the use cases, but custom media queries are allowed if you want to target specific devices on your design. For example:
// declaration
const iPhoneXRLandscape = `
@media only screen
and (device-width : 414px)
and (device-height : 896px)
and (-webkit-device-pixel-ratio : 2)
and (orientation : landscape)
`
// usage
const layout = style({
// ... component styles,
[iPhoneXRLandscape]: {
// ... device specyfic styles
},
})
Responsive Propβ
Properties of components with the type ResponsiveProp<T>
indicate that you can use them to behave similarly to responsive aliases by passing different values ββdepending on the screen size
You can check the mental model section to see how the responsiveness works in these cases.
interface ResponsiveProp<T> = T | {
mobile: T
tablet?: T
desktop?: T
widescreen?: T
}