Have an ergonomic way of styling components where the styles are:
A quick overview of existing styling solutions and their pros-cons.
Inlinable | Scopes Style | Composable | Type Safe | Needs Name | Scope DOM | |
---|---|---|---|---|---|---|
Pure CSS | ✅ | ❌ | ||||
Emotion | ✅ | ✅ | ✅ | |||
Vanila Extract | ✅ | ✅ | ✅ | |||
CSS Modules | ✅ | ✅ | ✅ | ❌ | ||
useStyleScope$() | ✅ | ✅ | ✅ | ❌ | ||
Tailwind | ✅ | ✅ | ||||
StyleX | ✅ | ✅ | ❌ |
Legend:
There is an interesting article on Atomic CSS called StyleX This points out that as applications get very large the need for additional CSS reaches zero growth. This is because the styling is broken up into primitives which are then reused. We think this is a good approach for Qwik as well.
The basic idea is to create a CSS$
tagged string literal which can be used as:
Both of the above examples are equally supported and are identical. Advantages between the two approaches are:
redBorderFromString
: devs can cut&paste from the dev-tools. Downside is that to get code completion they have to install an editor plugin.redBorderFromObjLiteral
: TypeScript can verify types, but cut&paste would not work from dev-tools/existing CSS.The return value of CSS$
is an opaque object which can contain 1 or more classes along with the associated QRLs. The returned values can be composed together in markup.
As you can see the CSS$
ends with $
which means it is subject to optimizer and lazy-loading.
Will be transformed to:
file: HASH_OF_JS
NOTE: exact implementation to be determined and may be different.
The Qwik runtime will be able to recognize the strings which are QRLs and will know to load a specific JS files and create <style>
tags from those JS files.
The SSR will insert the <style>
tags into the corresponding SSR output.
Because the styles are just JS loaded through QRLs, existing prefetching and bundling system will be able to optimize the loading of the styles.
The runtime can easily see which styles have already been loaded and which still need to be loaded.
useStyle$()
With the CSS$
approach there is no need for useStyle$()
to load the styles. The renderer is now intelligent enough to recognize when a QRL is being passed into the class
and if it needs to be loaded. Because the rendering can delay flushing of the UI to DOM, the renderer can load the CSS without causing a flash of unstyled content.
The CSS$
will be able to refer to static content only. So things like this will not be supported and will be a compiled error.:
If the CSS needs to have variable, than CSS variables should be used.
CSS selectors can have complex rules such as body>ul>li
. We think such rules are very hard to reason about and make the CSS append only as devs are worried that changing them will break something. Such rules are also hard to tree-shake for.
We think for styling components such rules are an anti-pattern and will not be supported by the CSS$
which has one-to-one connection.
Instead if you want to use such complex rules, global.css
is a good place to put them, but you lose the ability to lazy load such rules.
CSS$
can be composed together or grouped into arrays and referred to by other JSX.