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.ts
Example
// 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-left will 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',
},
});
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
Example
⬇️⬇️⬇️
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:
In this case, we will generate the following CSS and apply both classes to the element:
Shorthand should override
Unlike the previous example, we will generate the following CSS and apply only a single class to the element:
This occurs because
paddingoverrides 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 (
-1or 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.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"):
Limitations
Limitations on
border-*shorthandsUnlike other CSS properties,
border-*shorthands has an "equality" behavior, for instance,border-left&border-color:This behavior poses an issue as
borderLeftcannot resetborderColorsince it's a subset ofborder*Color, resulting in collision once these rules are applied:Rejected approach
One of the options considered was to define
borderColor,borderStyle&borderWidthwith lower priority thanborderLeft& etc. to ensure deterministic behavior. However, this approach would be confusing, as illustrated in the example below:Because of CSS order, both sets of rules would visually produce the same result:
While the outcome would be deterministic, it would be confusing for developers, as they would expect
borderColorto overrideborderLeftin the first case, as is the case with other properties.Solution
Instead, we opted to continue maintain
borderColor,borderStyle&borderWidthas 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: