Replies: 35 comments 4 replies
-
how can I make this work? |
Beta Was this translation helpful? Give feedback.
-
@alejandroyunes This is an RFC, i.e Request for Comments. It is a proposal that does not work yet. Our plan is to implement this in January. |
Beta Was this translation helpful? Give feedback.
-
that would be nice, because this is not working neither: desktopNav: {
display: {
default: "block",
"@media (max-width: 900px)": "none"
},
},`
but it works on dev |
Beta Was this translation helpful? Give feedback.
-
I was trying something similar in my project (but in a basic form). I created a export const SM = '@media (min-width: 600px)';
export const MD = '@media (min-width: 769px)';
export const LG = '@media (min-width: 992px)';
export const XL = '@media (min-width: 1200px)'; In a import { MD } from './breakpoints';
const styles = stylex.create({
base: {
position: 'fixed',
left: {
default: 10,
[MD]: 20,
}
}
}); However, when this is compiled during development, I receive the following error: When I declare the value of the constant What I don't understand is that the value of Having the |
Beta Was this translation helpful? Give feedback.
-
Yes, values need to defined statically within the same file. Imported values are not "static" as they need to be imported. Why?: With the current architecture every file compilation can be cached. Only if a file changes do we need to re-compile it. If we added support for imports, we would need to invalidate the cache of all dependent files whenever a file changed. @alejandroyunes That is an ESLint error, but I'm not able to reproduce it locally. It works. Maybe the error is on a different line? |
Beta Was this translation helpful? Give feedback.
-
Thank you @nmn for the clarification. I am just curious, are media queries defined statically within every component in large projects that currently use StyleX? Regarding the proposed feature, I suggest using the same terms used in CSS when adding features to StyleX. I mean, using When defining a I also suggest adding a stylex.defineGlobals({
body: {
padding: {
default: 20,
[sm]: 10
}
},
h1: {
fontSize: {
default: fontSizes.lg, // where fontSizes is a StyleX variable
[sm]: fontSizes.md
}
},
small: {
fontSize: fontSizes.xs
}
}); The above can be transformed and added to the top of the generated CSS file instead of having another CSS file to accomplish the same purpose. |
Beta Was this translation helpful? Give feedback.
-
Thanks for your feedback about the Regarding global styles, we are, by design, not going to be adding support for something like that anytime soon. Global styles are, by definition, not encapsulated. Adding a function like that would enable any component, anywhere, to be able to define global styles causing all the problems we're trying to solve. Our official position is that you should use as few global styles as possible, and when you absolutely need them, as is the case with resets, you should use an actual CSS file for that. It's much easier to debug global styles when they live in a single CSS file than if they're all over the codebase. NOTE: We also recommend using StyleX styles to style |
Beta Was this translation helpful? Give feedback.
-
Based on the examples in the proposal, the export const media = stylex.defineConsts({
sm: stylex.types.media('(min-width: 640px) and (max-width: 767px)'),
md: stylex.types.media('(min-width: 768px) and (max-width: 1023px'),
lg: stylex.types.media('(min-width: 1024px) and (max-width: 1279px)'),
xl: stylex.types.media('(min-width: 1280px) and (max-width: 1535px)'),
xxl: stylex.types.media('(min-width: 1536px)'),
landscape: stylex.types.media('screen and (min-width: 30em) and (orientation: landscape)'), // may need a better key name
portrait: stylex.types.media('(min-height: 680px), screen and (orientation: portrait)'), // may need a better key name
}); This will simply output the The export const features = stylex.defineConsts({
flexbox: stylex.types.supports('(display: flex)'),
}); Regarding global styles, I agree with the basic principles. I currently have a dedicated file for resets; but what I proposed is to set those global styles once while being able to consume the shareable values (defined using StyleX) like colors, font sizes, spacings...etc. for consistency. I may not want to create a dedicated component for elements like |
Beta Was this translation helpful? Give feedback.
-
@edamascus The examples make sense. The use-case for Good point regarding the use of constants from StyleX for resets. Will keep thinking about that. |
Beta Was this translation helpful? Give feedback.
-
Regarding global css, you could:
A worse alternative (due to the necessity to give a name) might be to allow setting a constant name to reusable CSS variables. That way, you could reference variables defined with stylex anywhere else (you could also check duplicate variables names during bundling)
Both of the above might be useful to add to stylex for different users. However, I think these two ideas should be moved to separate issues, should I create them? |
Beta Was this translation helpful? Give feedback.
-
We're considering something on these lines anyway, so I'm more in favour of this solution at the moment. It will take a while before we can achieve this though. @o-alexandrov You're welcome to create an issue to discuss this further. |
Beta Was this translation helpful? Give feedback.
-
Are there any way today where i can put my media queries in a seperate file: breakpoints.stylex.ts import { gridTokens } from "./twTokens.stylex";
export const MOBILE = `@media only screen and (max-width: ${gridTokens.mobile})`;
export const TABLET = `@media only screen and (max-width: ${gridTokens.tablet})`;
export const DESKTOP = `@media only screen and (max-width: ${gridTokens.desktop})`; and use without getting compile errors? import stylex from "@stylexjs/stylex";
import { MOBILE } from "../../stylex/tw/breakpoints.stylex";
export const heroNoImageStyle = stylex.create({
hero: {
margin: {
default: "192px 0 96px",
[MOBILE]: "144px 0 48px;"
}
},
heroInner: {
display: "flex",
alignItems: "center",
justifyContent: "center",
},
heroContent: {
width: "100%",
maxWidth: "650px",
padding: "0 20px",
textAlign: "center"
}
}); |
Beta Was this translation helpful? Give feedback.
-
@pksorensen No, which is why this proposal exists. Today, you can ether export styles themselves (the result of calling Also variables can't be used within Media Queries. It's not supported in CSS. Variables can only be used values of style properties. OR You use types to keep media queries consistent across files: breakpoints.stylex.ts export type Sm = "@media (max-width: 768px)";
export type Md = "@media (min-width: 768px) and (max-width: 1260px)";
export type Lg = "@media (min-width: 1260px)"; and use without getting compile errors? import stylex from "@stylexjs/stylex";
import type {Sm, Md, Lg} from "../../stylex/tw/breakpoints.stylex";
export type MOBILE: Sm = "@media (max-width: 768px)";
export type TABLET: Md = "@media (min-width: 768px) and (max-width: 1260px)";
export type DESKTOP: Lg = "@media (min-width: 1260px)";
export const heroNoImageStyle = stylex.create({
hero: {
marginTop: {
default: 192,
[MOBILE]: 144,
},
marginBottom: {
default: 96,
[MOBILE]: 48,
},
},
heroInner: {
display: "flex",
alignItems: "center",
justifyContent: "center",
},
heroContent: {
width: "100%",
maxWidth: "650px",
padding: "0 20px",
textAlign: "center"
}
}); |
Beta Was this translation helpful? Give feedback.
-
Good sugestions, and thanks for pointing out the thing with variables in media queries. new to stylex, is there any historical data to say anything about timeframe that a RFC would be implemented? I took an alternative route for now to keep code clean and not having to write the queries all over and wrote a webpack plugin / loader to find and replace the imports before stylex babel plugin gets the code, so i have a working solution for now that allows me to do what i wrote above. |
Beta Was this translation helpful? Give feedback.
-
This is another approach, but I don't recommend it as it hurts cache-ability. It's the most powerful though. Timeline: A month or two. Depends on how many smaller bugs come up. |
Beta Was this translation helpful? Give feedback.
-
@enmanuel-lab49 This work has a couple of pre-requisites, namely integrating a CSS post-processing step. Once that is done, this work will be relatively trivial to implement. |
Beta Was this translation helpful? Give feedback.
-
Loving this! I also love that you guys are thinking about things like Another note that I probably, because I already know my css, I would use |
Beta Was this translation helpful? Give feedback.
-
@tounsoo We don't intend to choose one. Instead our plan is to create media queries that don't conflict by defining both @media (min-width: 768px) and (max-width: 1368px) { ... } This is why However these little details is why this RFC is still open and not implemented. We value any feedback on this proposal. |
Beta Was this translation helpful? Give feedback.
-
@nmn What’s the cost benefit in the enforcing the entire range? If a developer ships a set of components that are mobile first and another developer ships components that define explicit ranges both will work in any StyleX project, no? It’s also the most common pattern to use One potential issue I see is poorly defined ranges (e.g. |
Beta Was this translation helpful? Give feedback.
-
This is the core design question here. Since we're dealing with variables here, it's impossible to know the actual value of the media query while compiling the Enforcing one of Another easy solution would be to make this configurable, but that breaks our core principle of avoiding global configuration. We want StyleX to work the way for all apps. Media queries both defined locally and from this proposed API still have some potential opportunities of conflict. I'll continue to think about solving these issues, but I also want to ensure this RFC doesn't get stuck in limbo because of these edge-cases. My goal for now is to decide on the best API. If it's the right API design, we can work on the implementation details later. |
Beta Was this translation helpful? Give feedback.
-
I am using a pattern for defining color variables using the const colors = stylex.defineVars({
background: "0 0 0%"
}) Then use this color variable by wrapping it in const styles = stylex.create({
// 50% opacity
backgroundColor: `hsl(${colors.background} / 0.5)`,
// 100% opacity
backgroundColor: `hsl(${colors.foreground})`,
// Mistake!
backgroundColor: colors.foreground,
}) but I often forget to wrap the color variable in im thinking the types feature can include a way to make this pattern safer. the proposed im thinking |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
Could this be implemented as a lint in the eslint plug? |
Beta Was this translation helpful? Give feedback.
-
@aspizu No because the variables are defined in one file but used in another. The Babel plugin or the ESLint plugin can't know what was stored within the variable. |
Beta Was this translation helpful? Give feedback.
-
My exact instinct was to do just this and what @edamascus tried. I, like them also couldn't get it to work which is what led me here :) I'm only able to get it to work by declaring the I saw an option suggested above was to use the stylex.create but that doesn't solve my use case as I only want to re-use the media query as a const. I also couldn't get the
|
Beta Was this translation helpful? Give feedback.
-
No. There is no plan to support this. This decision has been made after a lot of careful consideration and is essential for predictable style composition on both web and compatibility with inline styles on RN.
Yes, as long as the values are wrapped with the valid type. |
Beta Was this translation helpful? Give feedback.
-
Moving to Discussions |
Beta Was this translation helpful? Give feedback.
-
I'm the next person to stumble on the need for this. Building a design framework, and I want to be able to export the breakpoints to people using it so their custom components change at the same places as the provided ones. |
Beta Was this translation helpful? Give feedback.
-
Currently, we're using However, besides media querys there are also design tokens that are not variable, for example spacing units. If I got this correctly, variables are always emitted as a CSS variable, resulting in a CSS variable for every spacing token. For example, Maybe I'm not deep enough into StyleX and there already is a way of doing exactly this. |
Beta Was this translation helpful? Give feedback.
-
before this feature can ship, i think the only workaround is make a babel plugin to transfrom the key into string literal before stylex process it // A very non-efficient workaround
import { declare } from '@babel/helper-plugin-utils';
import * as t from '@babel/types';
import type { NodePath, PluginObj } from '@babel/core';
type Units = 'px' | 'em' | 'rem';
export type PluginOptions = {
sizes?: Record<string, `${number}${Units}`>;
queries?: Record<string, string>;
};
const helpers = {
minw: 'min-width',
maxw: 'max-width',
minh: 'min-height',
maxh: 'max-height'
} as Record<string, string>;
function process_media(
path: NodePath<t.MemberExpression>,
props: string[],
sizes: Record<string, string>,
queries: Record<string, string>
) {
if (props.length === 1) {
if (queries[props[0]]) {
path.replaceWith(t.stringLiteral(queries[props[0]]));
}
}
if (props.length !== 2 || !helpers[props[0]]) return;
const size = sizes[props[1]] ?? props[1];
path.replaceWith(t.stringLiteral(`@media (${helpers[props[0]]}: ${size})`));
}
function process_container(
path: NodePath<t.MemberExpression>,
props: string[],
sizes: Record<string, string>
) {
let container: string | undefined = undefined;
if (props.length === 3) {
container = props.shift();
}
if (props.length !== 2 || !helpers[props[0]]) return;
const size = sizes[props[1]] ?? props[1];
path.replaceWith(
t.stringLiteral(
container
? `@container ${container} (${helpers[props[0]]}: ${size})`
: `@container (${helpers[props[0]]}: ${size})`
)
);
}
function member_to_paths(member: t.MemberExpression): string[] | false {
let paths: string[] = [];
let current: t.Node = member;
while (t.isMemberExpression(current)) {
if (t.isIdentifier(current.property)) {
paths.unshift(current.property.name);
} else if (t.isStringLiteral(current.property)) {
paths.unshift(current.property.value);
} else {
return false;
}
current = current.object;
}
if (t.isIdentifier(current)) {
paths.unshift(current.name);
} else if (t.isStringLiteral(current)) {
paths.unshift(current.value);
} else {
return false;
}
return paths;
}
export default declare<PluginOptions>((api, { sizes = {}, queries = {} }) => {
api.assertVersion(7);
return {
name: 'babel-plugin-replace-stylex-media',
visitor: {
Program(path) {
let import_var = {
media: undefined as string | undefined,
container: undefined as string | undefined
};
path.traverse({
ImportDeclaration(importPath) {
if (importPath.node.source.value === 'stylex-media-helper') {
importPath.node.specifiers.forEach((spec) => {
if (t.isImportSpecifier(spec)) {
if (t.isIdentifier(spec.imported, { name: 'media' })) {
import_var.media = spec.local.name;
}
if (t.isIdentifier(spec.imported, { name: 'container' })) {
import_var.container = spec.local.name;
}
}
});
importPath.remove();
}
},
MemberExpression: {
exit(path) {
const paths = member_to_paths(path.node);
if (!paths) {
return;
}
const root = paths.shift();
if (root === import_var.media) {
process_media(path, paths, sizes, queries);
} else if (root === import_var.container) {
process_container(path, paths, sizes);
}
}
}
});
}
}
} as PluginObj;
}); then import { media } from 'stylex-media-helper';
const styles = create({
title: {
fontSize: {
default: '8rem',
[media.maxw.sm]: '4rem',
[media.maxw.md]: '6rem'
},
fontWeight: 'bold',
}
}); again, this is just a work around, do not expect it to be efficient |
Beta Was this translation helpful? Give feedback.
-
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 asstylex.types.length
andstylex.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
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.
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 markscolors.primary
as a<color>
in CSS itself:This makes the variable itself animateable in CSS using
transition
oranimation
.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
.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
tostylex.types
. I.e a new helper function,stylex.types.atRule
. We can also add convenience functions forstylex.types.media
andstylex.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
andcreateTheme
work as a pair today. And so the proposal also includes a new function calleddefineConsts
. This new function will work exactly likedefineVars
except the variables created with it cannot be overridden withcreateTheme
. Additionally, certain types, like atRule will only be accepted withindefineConsts
and notdefineVars
.Example
Using it would be the same as using variables. You import and use the value.
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:
The main benefit of these convenience functions is reducing boilerplate.
Beta Was this translation helpful? Give feedback.
All reactions