Just a random developer.
As I mentioned in Beginning of This Blog, I chose to use facebook/stylex
as the styling system for this website. StyleX is not yet production-ready, which makes dealing with its issues challenging, but I still believe it is worth using.
Before starting the implementation, I had to decide on a theme for this project. Since StyleX is still very new, there are no existing component libraries built with it. This meant I had to either create everything from scratch or port an existing component library to StyleX. Despite not being a design expert, I decided to build everything from the ground up.
Choosing colors was particularly challenging. While I could have borrowed colors from tailwindcss
, ensuring good contrast for readability would have been a hassle. Instead, I opted for @radix-ui/colors
, which does an excellent job of maintaining readability.
Even though I enjoy using StyleX, there are some issues worth mentioning.
Configuring a project to work with StyleX is already challenging, especially with SvelteKit. The @stylexjs/rollup-plugin
works with Vite, but @stylexjs/postcss-plugin
does not support Svelte. It scans .{js,ts}
files to generate CSS rules, and while Svelte files can be included, they cause errors because Babel does not understand Svelte.
Fortunately, a third-party package, vite-plugin-stylex
, resolves this issue, making the setup process smoother.
There are two major challenges when using media queries in StyleX. The first is that it defines media queries using string literals as keys:
import stylex from '@stylexjs/stylex';
const styles = stylex.create({
base: {
fontSize: {
default: '1rem',
'@media (max-width: 480px)': '0.8rem'//[!code <<]
}
}
});
However, StyleX does not support dynamic values in stylex.create
, meaning importing constants for breakpoints will fail.
// breakpoints.ts
export const breakpoints = {
sm: '@media (max-width: 480px)',
md: '@media (max-width: 1024px)',
lg: '@media (max-width: 1920px)'
};
// styles.ts
import stylex from '@stylexjs/stylex';
import { breakpoints } from './breakpoints.ts';
const styles = stylex.create({
base: {
fontSize: {
default: '1rem',
[breakpoints.sm]: '0.8rem' // cause error[!code error]
}
}
});
This makes it difficult to maintain predefined breakpoints. There is an RFC addressing this issue, but implementation challenges mean it is unlikely to be resolved soon.
Another problem with StyleX media queries is that the order of generated rules is not guaranteed. If multiple media queries are used, the order of CSS rules might be incorrect.
For example:
import stylex from '@stylexjs/stylex';
const styles = stylex.create({
base: {
fontSize: {
default: '0.8rem',
'@media (min-width: 480px)': '1rem',
'@media (min-width: 1024px)': '1.25rem'
},
},
});
This might generate the following CSS:
.xxxxxx { font-size: 0.8rem; }
@media (min-width: 1024px) { .xxxxxx { font-size: 1.25rem; } }
@media (min-width: 480px) { .xxxxxx { font-size: 1rem; } }
In this case, the 1024px
rule will be overridden by the 480px
rule due to incorrect ordering. A simple fix is to sort media queries properly or structure them hierarchically to prevent conflicts.
import stylex from '@stylexjs/stylex';
const styles = stylex.create({
base: {
fontSize: {
default: '0.8rem',
'@media (min-width: 480px)': {
default: '1rem',
'@media (min-width: 1024px)': '1.25rem'
}
}
}
});
This approach ensures precise control over media queries, though it may be harder to read.
This is a minor issue, but using StyleX in Svelte leads to spread attributes everywhere (<div {...stylex.attrs(styles.base, styles.xxx)}></div>
), making the code look cluttered and harder to read. While this is not a functional problem, I personally dislike it.
To address this, I created a Svelte preprocessor that transforms specific attributes into spread attributes, keeping the source code clean and readable.
Note: I don’t recommend using this preprocessor unless you fully understand its behavior. Debugging preprocessors can be a nightmare if you don’t know what they do.
Although StyleX is not production-ready and I do not currently recommend using it in production, I see its potential. If you have side projects, it might be worth trying out.