stylex
stylex copied to clipboard
RFC: Shared Constants (Media Queries)
Motivation
The immediate motivation is to enable defining and exporting Media Queries the same way that variables can be shared using defineVars
. In the future, this feature could be expanded to support additional share-able values.
Inspiration
From StyleX itself, the idea is to expand defineVars
API to able to share more kinds of values. Further, we’re adding helper functions such as stylex.types.length
and stylex.types.color
which will be used to mark values of variables with a particular type. This will not only add additional type-safety to the variable types, but also insert @property
CSS rules which gives those variables types in CSS itself, which enables certain unique features, such as being able to animate variables.
From CSS, there is an old proposal for @custom-media
that hasn’t been implemented in any browser.
stylex.types
explainer
stylex.types
explainerWe are already working on utility functions to lock down the types of particular variable values. So, when using defineVars you can assign a type to a particular variable.
import * as stylex from '@stylexjs/stylex';
export const colors = stylex.defineVars({
primary: stylex.types.color('black'),
// ...
});
Here, the value ’black’
will be validated by the compiler and a compiler error will be thrown if a non-valid color is passed in. It also changes the behaviour of the variable generated. An @property CSS rule will be generated that marks colors.primary
as a <color>
in CSS itself:
@propert --colors-primary {
syntax: '<color>';
inherits: true;
initial-value: black;
}
This makes the variable itself animateable in CSS using transition
or animation
.
The static types of the variable would be affected as well, and you’d be forced to use the same function to define values within createTheme
.
const dracula = stylex.createTheme(colors, {
// ERROR: Expected a type.Color, got a string.
primary: 'purple',
// OK!
primary: stylex.types.color('purple'),
});
We can also consider adding utility functions like stylex.types.rgb(0, 0, 0)
in the future. As all of these functions are compiled away, we can ensure that tree-shaking removes all these functions from the JS bundle.
Proposal
The core proposal is to add special type for atRules
to stylex.types
. I.e a new helper function, stylex.types.atRule
. We can also add convenience functions for stylex.types.media
and stylex.types.supports
if it makes sense.
However, unlike CSS variables, the value of a custom at-rule needs to remain constant and cannot be overridden in a theme. This conflicts with the way defineVars
and createTheme
work as a pair today. And so the proposal also includes a new function called defineConsts
. This new function will work exactly like defineVars
except the variables created with it cannot be overridden with createTheme
. Additionally, certain types, like atRule will only be accepted within defineConsts
and not defineVars
.
Example
// globalTokens.stylex.js
import * as stylex from '@stylexjs/stylex';
export const media = stylex.defineConsts({
sm: stylex.types.atRule('@media (min-width: 640px) and (max-width: 767px)'),
md: stylex.types.atRule('@media (min-width: 768px) and (max-width: 1023px'),
lg: stylex.types.atRule('@media (min-width: 1024px) and (max-width: 1279px)'),
xl: stylex.types.atRule('@media (min-width: 1280px) and (max-width: 1535px)'),
xxl: stylex.types.atRule('@media (min-width: 1536px)'),
});
export const colors = stylex.defineVars({
primary: stylex.types.color('black'),
// ...
});
Using it would be the same as using variables. You import and use the value.
import * as stylex from '@stylexjs/stylex';
import {media, colors} from './globalTokens.stylex'
const styles = stylex.create({
base: {
width: {
default: '100%',
[media.sm]: 500,
[media.md]: 800,
[media.lg]: 960,
},
color: colors.primary,
},
});
Implementation Details
The implementation would be almost identical to how variables already work. The defineConsts
call would output a variable with a media query value to the generated styles, and while generating the CSS file by combining all the collected styles, the media queries variables would be inlined with the actual value.
This same process can be generalized to variables in general where any variable that is never overridden can be inlined directly and any unused variable can be removed.
Optional Extras
As mentioned earlier, we can add some additional utilities to make things easier, namely:
-
stylex.types.media
-
stylex.types.mediaWidth
-
stylex.types.mediaHeight
-
stylex.types.supports
Here’s what the example above could look like:
import * as stylex from '@stylexjs/stylex';
export const media = stylex.defineConsts({
sm: stylex.types.mediaWidth(640, 768),
md: stylex.types.mediaWidth(768, 1024),
lg: stylex.types.mediaWidth(1024, 1280),
xl: stylex.types.mediaWidth(1280, 1536),
xxl: stylex.types.media('(min-width: 1536px)'),
});
The main benefit of these convenience functions is reducing boilerplate.