Implement `stylex.defineConsts`
Depends on #517
Implement a new stylex.defineConsts API that works exactly like stylex.defineVars with two differences:
- It is impossible to create themes for these sets of constants.
- It is possible to define types of values that are not supported within
defineVars.- Initially, we would want to support shared media queries.
How this will enable sharable media queries:
export const viewports = stylex.defineConsts({
sm: stylex.types.media('(min-width: 768px)'),
md: stylex.types.media('(min-width: 1024px)'),
lg: stylex.types.media('(min-width: 1600px)'),
});
These can then be used like this:
const styles = stylex.create({
base: {
fontSize: {
default: 16,
[`@media ${viewports.sm}`]: 18,
[`@media ${viewports.md}`]: 20,
[`@media ${viewports.lg}`]: 24,
'@media (min-width: 1024px)': 20,
},
},
Behind the scenes, shared media queries should compile to the @custom-media proposal which
can then be compiled away in the CSS post-processing step. (probably by lightningcss)
There are two major challenges to consider with this feature:
Avoiding conflicts
Once we introduce shared media queries, we will no longer be able to detect overlapping media queries within a value statically. This is a solvable problem with combination of compilation-time and post-processing transformations.
Give the examples above, assume the initial constants are compiled to the following CSS:
@custom-media --vw-sm (min-width: 768px);
@custom-media --vw-md (min-width: 1024px);
@custom-media --vw-lg (min-width: 1600px);
However, in order to resolve any conflicts, the styles above would be transformed into:
const styles = stylex.create({
base: {
fontSize: {
default: 16,
'@media (--vw-sm) and not (--vw-md) and not (--vw-lg) and not (min-width: 1024px)': 18,
'@media (--vw-md) and not (--vw-lg) and not (min-width: 1024px)': 20,
'@media (--vw-lg) and not (min-width: 1024px)': 24,
'@media (min-width: 1024px)': 20,
},
},
These styles would then be simplified after the final CSS file is generated.
Dealing with duplicate CSS rules
Since StyleX cannot know the value of a shared media query at compile time, the generated className will be different from the hash had the literal value been used instead.
Therefore, there is no way to support shareable constants (media queries or otherwise) without duplicating some CSS rules.
However, our CSS post-processing should be able to find and combine duplicate rules and minimize the performance impact of this duplication.
.hash1, .hash2 { <rule generated twice with different classNames> }
The assumption here is that if you define shared constants, you should be expected to use them consistently across the codebase. We could also implement a lint rule to enforce this?