-
Notifications
You must be signed in to change notification settings - Fork 68
Description
Migration 🏎️
We provide a codemod that helps you to convert your existing Griffel code to use the new one that supports CSS shorthand syntax.
Usage
npx griffel-codemod-shorthands [pattern]
npx griffel-codemod-shorthands **/*.styles.tsExample
// Before
const useStyles = makeStyles({
root: {
display: "flex",
...shortcuts.padding("10px"),
},
});⬇️⬇️⬇️
// After
const useStyles = makeStyles({
root: {
display: "flex",
padding: "10px",
},
});Currently, Griffel does not support CSS shorthands, https://griffel.js.org/react/guides/limitations#css-shorthands-are-not-supported 😿 The plan is to bring the support back 🚀
How it works?
This concept was inspired by StyleX and Fela (fela-enforce-longhands plugin).
There are two scenarios where CSS longhands & shorthands could conflict, and the strategies for both are described below ⬇️
Longhand should override
As we depend on JavaScript order of definitions for overrides, we should adhere to it:
const useClasses = makeStyles({
root: {
padding: 0,
paddingLeft: '10px',
},
});In this case, we will generate the following CSS and apply both classes to the element:
<style data-priority="-1">
.padding-0 {
padding: 0;
}
</style>
<style data-priority="0">
.padding-left-10px {
padding-left: 10px;
}
</style>
<div class="padding-0 padding-left-10px"></div>Note: The order of CSS rules ensures that
padding-leftwill override padding, resulting in a deterministic outcome.
Shorthand should override
const useClasses = makeStyles({
root: {
paddingLeft: '10px',
padding: 0,
},
});Unlike the previous example, we will generate the following CSS and apply only a single class to the element:
<style data-priority="-1">
.padding-0 {
padding: 0;
}
</style>
<div class="padding-0"></div>This occurs because padding overrides all its shorthands (padding-left, padding-right, etc.), eliminating the need to apply them individually.
Implementation details
Order adjustments
All CSS shorthands will be defined with a lower priority (-1 or lower) than their corresponding longhands (0). Rules with lower priority appear first in the CSS file (or DOM) and will be overridden by rules with higher priority.
Note: Negative priorities are chosen for backwards compat.
This change has been implemented in #539.
Shorthands & "longhands reset"
Support for rules reset was added in #450.
Whenever a shorthand is defined, its corresponding longhands will be reset (refer to "Shorthand should override"):
const useClasses = makeStyles({
root: {
padding: 0,
// "padding" automatically resets the following longhands
paddingTop: RESET,
paddingRight: RESET,
paddingBottom: RESET,
paddingLeft: RESET,
},
});Limitations
Limitations on border-* shorthands
Unlike other CSS properties, border-* shorthands has an "equality" behavior, for instance, border-left & border-color:
const style = {
borderLeft: '1px solid red',
// is essentially ⬇️
borderLeftWidth: '1px',
borderLeftStyle: 'solid',
borderLeftColor: 'red',
};const style = {
borderColor: 'red',
// is essentially ⬇️
borderTopColor: 'red',
borderRightColor: 'red',
borderBottomColor: 'red',
borderLeftColor: 'red',
};This behavior poses an issue as borderLeft cannot reset borderColor since it's a subset of border*Color, resulting in collision once these rules are applied:
// How to handle this? 🫤
const style = {
borderLeft: '1px solid green',
borderColor: 'red',
}Rejected approach
One of the options considered was to define borderColor, borderStyle & borderWidth with lower priority than borderLeft & etc. to ensure deterministic behavior. However, this approach would be confusing, as illustrated in the example below:
<style data-priority="-2">
.border-color-red {
border-color: red;
}
</style>
<style data-priority="-1">
.border-left-1px-solid-green {
border-left: 1px solid green;
}
</style>Because of CSS order, both sets of rules would visually produce the same result:
const useClasses = makeStyles({
rootA: {
borderLeft: '1px solid green',
borderColor: 'red',
},
rootB: {
borderColor: 'red',
borderLeft: '1px solid green',
},
});While the outcome would be deterministic, it would be confusing for developers, as they would expect borderColor to override borderLeft in the first case, as is the case with other properties.
Solution
Instead, we opted to continue maintain borderColor, borderStyle & borderWidth as unsupported shorthands i.e. they will retain their current limitations. Directional border shorthands will be fully supported (borderLeft, borderTop, etc.), ensuring that styles work as expected:
const useClasses = makeStyles({
rootA: {
borderLeft: '1px solid green',
...shorthands.borderColor('red'),
},
rootB: {
...shorthands.borderColor('red'),
borderLeft: '1px solid green',
},
});