UI Styling Standard
The eslint plugin to enforce and educate on Atlassian's UI Styling Standardno-container-queries
Blocks @container at-rules
(container queries, (opens new window)) by default. The
rule allows a narrowly-scoped exception when the named container is declared in the same file and
the query is dimension-based (width/height or a range). Style queries, unnamed queries, and IDs
shared across files remain blocked.
Container queries are unsafe because they:
- break the rules of scope
- aren't deterministic
- aren't type-safe
- aren't fully supported across our frontend tech stacks
Use typed media queries, (opens new window), a WidthObserver, (opens new window), or a custom ResizeObserver, (opens new window) instead.
Approved queries must use satisfies with the default type import from
@atlaskit/css/at-rules/container, and the name after @container must match the local
containerName declaration:
import { cssMap } from '@atlaskit/css';
import type ContainerQuery from '@atlaskit/css/at-rules/container';
const styles = cssMap({
root: { containerName: 'sidebar', containerType: 'inline-size' },
content: {
['@container sidebar (width > 300px)' satisfies ContainerQuery]: {},
},
});
export const Card = () => (
<div css={styles.root}>
<div css={styles.content}>Card content</div>
</div>
);Test approved responsive states at 400% zoom and 270–320px widths. Do not use style queries such as
@container sidebar style(--density: compact).
Examples
Incorrect
import { css } from '@compiled/react';
import { token } from '@atlaskit/tokens';
const headingStyles = css({
'@container (width > 400px)': {
font: token('font.heading.large'),
},
});Correct
Media queries
import { css } from '@atlaskit/css';
import type MediaAboveSm from '@atlaskit/css/at-rules/media-above-sm';
import { token } from '@atlaskit/tokens';
const headingStyles = css({
['@media (min-width: 48rem)' satisfies MediaAboveSm]: {
font: token('font.heading.large'),
},
});WidthObserver
import { useState } from 'react';
import { css, cssMap } from '@compiled/react';
import throttle from 'lodash/throttle';
import { WidthObserver } from '@atlaskit/width-detector';
import { token } from '@atlaskit/tokens';
const containerStyles = css({
// Required for the `WidthObserver`, which is absolutely positioned
position: 'relative',
});
const headingStyles = cssMap({
small: {
font: token('font.heading.small'),
},
large: {
font: token('font.heading.large'),
},
});
const ResponsiveH2 = () => {
const [width = 0, setWidth] = useState<number | undefined>(undefined);
const throttledSetWidth = throttle(setWidth, 50);
return (
<div css={containerStyles}>
<WidthObserver setWidth={throttledSetWidth} />
<h2 css={width < 400 ? headingStyles.small : headingStyles.large}>Title</h2>
</div>
);
};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.