Migrating your app to XCSS
XCSS is a safer, tokens-first approach to CSS-in-JS.Summary of changes
Changes for developers
There are two key changes to be mindful of when migrating to XCSS. The first is updating callsites to remove any nested styles and tokenized values.
- import { css } from '@emotion/react';
+ import { xcss } from '@atlaskit/primitives';
- const someStyles = css({
+ const someStyles = xcss({
// token based properties will no longer need to be wrapped
- padding: token('space.100'),
+ padding: 'space.100'
// no change is required for non-tokenized values
transform: 'scale(2)'
});The second change is that for the xcss function to be applied correctly it must be applied on a
component with an xcss JSXAttribute. This won't work with the css or className JSXAttributes,
be aware if you're not seeing your styles appear.
- <div css={someStyles} />
+ <Box xcss={someStyles} />Changing the way you express styles
Why are nested selectors a problem? A key philosophy of XCSS is encouraging more deterministic style application. Restricting nested styles eliminates side-effects and encourages component encapsulation. Consider the below example:
const myComponentStyles = css({
'> *': {
color: 'color.text.danger',
},
});
const MyComponent = () => (
<div css={myComponentStyles}>
<p>Text here</p>
</div>
);Here the component is applying styles that are implicitly meant for the text wrapped in the p
below. In this simple example, it may seem okay, desirable even, but cases like these often occur
across module or component boundaries.
This makes the visibility of these dependencies harder to capture or reason about. Styles that are inherited or indirectly apply make a UI vulnerable to change and harder to maintain. Instead, if the same styles are applied directly to the affected element this can minimize and in some cases completely eliminate this problem.
const myTextStyles = xcss({
- '> *': {
color: 'color.text.danger',
- }
});
const MyComponent = () => (
- <div xcss={myComponentStyles}>
+ <Box
+ <Text xcss={myTextStyles}>Text here</Text>
</Box>
);There will likely be cases where nesting is the only option. While not desirable, nesting can be used minimally, and when the potential impact is considered.
FAQ
Migration to XCSS is simple for the majority of cases. Here are some common strategies for migrations.
Non-tokenized values
Before migrating to tokens, there are two options. Migrate to tokens first and then on a second pass migrate to XCSS or make the jump directly to use both tokens and XCSS.
const someStyles = css({
color: 'red',
});
// ->>> Optional middle step
const someStyles = css({
color: token('color.text.danger'),
});
// ->>> The final state
const someStyles = xcss({
color: 'color.text.danger',
});Moving from the styled API
If currently using the styled API there are a few steps to migrate. The safest approach is to use
object styles, migrate to tokens (optionally) and then migrate to XCSS.
const MyComponent = styled.div`
color: red;
`;
// ->>> move to object styles
const MyComponent = styled.div({
color: 'red';
});
// ->>> move to tokens
const MyComponent = styled.div({
color: token('color.text.danger'),
});
// ->>> move to Box
const myComponentStyles = xcss({
color: 'color.text.danger',
});
const MyComponent = () => <Box xcss={myComponentStyles} />