UI Styling Standard
The eslint plugin to enforce and educate on Atlassian's UI Styling Standardno-unsafe-selectors
Blocks unsafe CSS selectors.
Use alongside no-nested-selectors, (opens new window) which blocks nested CSS selectors.
Blocking unsafe selectors enables static analysis and also prevents regressions and incidents when migrating styles at scale.
Examples
Keyframes
Incorrect
Don't use @keyframes at-rules in styles.
import { css } from '@compiled/react';
css({
'@keyframes fadeIn': {
from: {
opacity: 0,
},
to: {
opacity: 1,
},
},
animation: '1s ease-in fadeIn',
});Correct
Use the keyframes API.
import { css, keyframes } from '@compiled/react';
const fadeIn = keyframes({
from: {
opacity: 0,
},
to: {
opacity: 1,
},
});
css({
animation: `1s ease-in ${fadeIn}`,
});Legacy pseudo-element syntax
Incorrect
Don't use the legacy single colon syntax for pseudo-elements.
This issue is autofixable.
import { css } from '@compiled/react';
css({
'&:after': {
content: '""',
},
});Correct
Use the double colon syntax for pseudo-elements.
import { css } from '@compiled/react';
css({
'&::after': {
content: '""',
},
});Increased specificity selectors
Incorrect
Don't use the nesting selector to increase specificity of styles.
import { css } from '@compiled/react';
import { token } from '@atlaskit/tokens';
css({
'&&': {
color: token('color.text'),
},
});Correct
Use styles that do not require increased specificity.
import { css } from '@compiled/react';
import { token } from '@atlaskit/tokens';
css({
color: token('color.text'),
});At-rules
Incorrect
Don't use at-rules in styles (see Correct below for exceptions).
import { css } from '@compiled/react';
css({
'@scope (.article-body) to (figure)': {
img: {
display: 'block',
},
},
});Correct
Use supported at-rules:
@media, including canonical typed queries from granular@atlaskit/css/at-rules/*, (opens new window)@property@supports
import { css } from '@atlaskit/css';
import type MediaAboveSm from '@atlaskit/css/at-rules/media-above-sm';
css({
['@media (min-width: 48rem)' satisfies MediaAboveSm]: {
display: 'block',
},
});Restricted pseudo-selectors
Incorrect
Don't use pseudo-classes which take arguments or implicitly rely on external context.
import { css } from '@compiled/react';
import { token } from '@atlaskit/tokens';
css({
'&:hover': {
backgroundColor: token('color.background.neutral.hovered'),
},
'&:not(:hover)': {
backgroundColor: token('color.background.neutral'),
}
'&:first-child': {
fontWeight: token('font.weight.bold'),
},
});Correct
import { css } from '@compiled/react';
import { token } from '@atlaskit/tokens';
type Props = { isFirstChild: boolean };
const baseStyles = css({
backgroundColor: token('color.background.neutral'),
'&:hover': {
backgroundColor: token('color.background.neutral.hovered'),
},
});
const firstChildStyles = css({
fontWeight: token('font.weight.bold'),
});
const Component = ({ isFirstChild }: Props) => (
<div css={[baseStyles, isFirstChild && firstChildStyles]} />
);Ambiguous pseudo-selectors
Incorrect
Don't use pseudo-selectors without a leading selector, as they are ambiguous and interpreted differently between contexts.
For example some contexts will treat :hover as &:hover while others will treat it as & :hover
(which targets descendants).
This issue is autofixable. By default a nesting selector & is added unless the
shouldAlwaysInsertNestingSelectorForAmbiguousPseudos option is disabled.
import { css } from '@compiled/react';
css({
/**
* This pseudo-class has no leading selector and is ambiguous.
*/
':hover': {},
/**
* In some contexts the above is treated as implicitly containing a nesting selector.
*/
'&:hover': {},
/**
* In other contexts it is treated as a descendant selector.
*
* (The selectors below are equivalent.)
*/
'*:hover': {},
'& :hover': {},
'& *:hover': {},
});Correct
Use explicit selectors. Do not rely on implicit or undefined behavior for pseudo-selectors.
Grouped at-rules
Incorrect
Don't group at-rules with the cssMap API.
import { cssMap } from '@compiled/react';
```tsx
cssMap({
success: {
'@media': {
'(min-width: 900px)': {},
'(min-width: 1200px)': {},
},
},
});Correct
Write flattened at-rules instead.
cssMap({
success: {
selectors: {
'&:not(:active)': {
backgroundColor: 'white',
},
},
},
});Correct
const fadeIn = keyframes({
from: {
opacity: 0,
},
to: {
opacity: 1,
},
});
css({
animationName: fadeIn,
});css({
'::after': {
content: '""',
width: 100,
height: 100,
backgroundColor: 'red',
},
});css({
'&:first-of-type': {
width: 100,
},
});css({
'&:hover': {},
});cssMap({
success: {
'@media (min-width: 900px)': {},
'@media (min-width: 1200px)': {},
},
});cssMap({
success: {
backgroundColor: 'white',
'&:active': {
backgroundColor: 'green',
},
},
});Options
importSources: string[]
By default, this rule will check styles using:
@atlaskit/css@atlaskit/primitives@compiled/react@emotion/react@emotion/core@emotion/styledstyled-components
Override this list with the importSources option, which accepts an array of package names.
shouldAlwaysInsertNestingSelectorForAmbiguousPseudos: boolean
By default this rule will always fix ambiguous pseudo-selectors by inserting a leading & (nesting
selector).
Set shouldAlwaysInsertNestingSelectorForAmbiguousPseudos to false to disable this auto-fix. When
disabled, a suggestion will be provided instead of an auto-fix.