johanholmerin / style9 Goto Github PK
View Code? Open in Web Editor NEWCSS-in-JS compiler inspired by Meta's stylex
License: MIT License
CSS-in-JS compiler inspired by Meta's stylex
License: MIT License
I have to use _doucment.tsx in Next.js to add the lang attribute to the html element. And it I keep seeing this error on a static export
ModuleNotFoundError: Module not found: Error: Can't resolve '/Users/eric/projects/airtable-code-interview/pages/index.xsrcSRK.css' in '***/***/airtable-code-interview/pages'
Here's my _document.tsx
import Document, { DocumentContext,Html,Head,Main,NextScript } from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
const initialProps = await Document.getInitialProps(ctx)
return initialProps
}
render() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
And my index.tsx
import Head from 'next/head'
import style9 from 'style9';
const styles = style9.create({
container: {
minHeight: '100vh',
paddingLeft: '.5rem',
paddingRight: '.5rem',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center'
}
});
export default function Home() {
return (
<div className={styles('container')}>
<Head>
<title>Create Next App</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main>
<h1 className="title">
Welcome to <a href="https://nextjs.org">Next.js!</a>
</h1>
<p className="description">
Get started by editing <code>pages/index.js</code>
</p>
<div className="grid">
<a href="https://nextjs.org/docs" className="card">
<h3>Documentation →</h3>
<p>Find in-depth information about Next.js features and API.</p>
</a>
<a href="https://nextjs.org/learn" className="card">
<h3>Learn →</h3>
<p>Learn about Next.js in an interactive course with quizzes!</p>
</a>
<a
href="https://github.com/zeit/next.js/tree/master/examples"
className="card"
>
<h3>Examples →</h3>
<p>Discover and deploy boilerplate example Next.js projects.</p>
</a>
<a
href="https://vercel.com/import?filter=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
className="card"
>
<h3>Deploy →</h3>
<p>
Instantly deploy your Next.js site to a public URL with Vercel.
</p>
</a>
</div>
</main>
<footer>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by{' '}
<img src="/vercel.svg" alt="Vercel Logo" className="logo" />
</a>
</footer>
</div>
)
}
export const config = { unstable_runtimeJS: false }
(unrelated this is a very cool project)
When using SSR, export key in index.js file cause error because deps are externalized in webpack.
A build step using rollup(to provide cjs build) or a different file to commonjs envs should fix the error.
https://github.com/johanholmerin/style9/blob/master/index.js#L28
You wrote:
Some may complain about this, that the generated class names are not semantic, that they are opaque and are ignoring the cascade. This is true.
In: https://css-tricks.com/style9-build-time-css-in-js/
I just thought I'd mention the idea I came across in the video from Stitches, that unreadable class names could potentially be overcome in the build output.
Inspired by Stitches: https://youtu.be/Gw28VgyKGkw?t=2061
Facebook's stylex has evolved a lot since it is being first introduced in React Conf 2019. At the time, stylex uses a syntax like this:
const styles = stylex.create({ red });
<div className={styles('red')} />
Nowadays, stylex seems to prefer the syntax with directly stylex()
call, see React Finland 2021 - Introducing StyleX:
const styles = stylex.create({ red });
<div className={stylesx(styles.red)} />
And according to React Finland 2021, they are able to strip most of stylex
runtime call through static analysis.
Currently, style9 doesn't support stripping away the style9()
runtime at all. I am about to experiment with it a little bit to see if it is doable.
When using the latest version of next, the following error is thrown:
./pages/index.22C1ZZc.css:4:0
Module not found:
Can't resolve '[...]/node_modules/next/dist/compiled/mini-css-extract-plugin/hmr/hotModuleReplacement.js'
null
Here's a basic reproduction reproduction repository: TxHawks/style9-latest-next-repro
The 1st commit is a working example using Next v10.0.9
The 2nd commit breaks after an upgrade to latest Next (v10.2.3
)
The 3rd commit remains broken after upgrading next-transpile-modules to its latest version (v7.2.0
)
I see that you have an experimental branch for Vite. Any plans to officially productionize it?
I just found this repo and I'm excited about it. But I can't quite tell from README if it quite meets my needs. If it does I want to use it / contribute. I'm wondering:
I guess I must be the only person writing code on windows :)
modules by path ./Application/ 2.22 KiB
no extension
Field 'browser' doesn't contain a valid alias configuration
C:\Source\_\Application\Root.KBftMHg.css doesn't exist
.tsx
Field 'browser' doesn't contain a valid alias configuration
C:\Source\_\Application\Root.KBftMHg.css.tsx doesn't exist
.ts
Field 'browser' doesn't contain a valid alias configuration
C:\Source\_\Application\Root.KBftMHg.css.ts doesn't exist
.js
Field 'browser' doesn't contain a valid alias configuration
C:\Source\_\Application\Root.KBftMHg.css.js doesn't exist
as directory
C:\Source\_\Application\Root.KBftMHg.css doesn't exist
@ ./Application/app.web.tsx 4:0-26 5:162-166
I have traced the issue down to loader.js:43 where the value of css path is 'C:/Source/_/Application/Root.KBftMHg.css', that is with forward slashes instead of backslashes
I'm not really sure the best fix otherwise I'd have sent through a PR.. it seems deliberate in https://github.com/webpack/loader-utils/blob/master/lib/interpolateName.js#L44
For now, I'm just writing both paths:
virtualModules.writeModule(cssPath.replace(/\//g, "\\"), metadata.style9);
virtualModules.writeModule(cssPath, metadata.style9);
👋 Hey, is it possible to add a way to write css as template literals?
Simple example:
import stylesheet from 'style9'
const styles = stylesheet`
.one {
color: red;
}
`
console.log('my class', styles('one'))
Hi Johan,
thank you so much for open-sourcing this library!
I'm using typescript and it complains about my CSS variables not existing on type "Style". At runtime it works fine though.
What's the recommended way of defining these variables?
const styles = style9.create({
body: {
margin: 0
},
lightTheme: {
"--bg-color": "#CCC"
}
});
Hey @johanholmerin, pretty interesting package, but I've tried to run it in Nextjs applications and I wasn't able, I didn't get which are the exact steps to go with it.
So my suggestion is to improve the documentation and perhaps add some use cases, or even an example, using NextJS, CRA, or whatever.
em outras bibliotecas eu posso usar estilos globais... como eu devo fazer nessa biblioteca para fazer um reset css por exemplo?
Possible for an easier implementation for named grid areas? I get that it's a shorthand property for grid-column-end
,
grid-column-start
, grid-row-end
, grid-row-start
, and it would be hard to support the non-named version (i.e. something like grid-area: 2 / 1 / 2 / 4;
) but expanding named grid-areas would be a boon.
I'd like to have a way to specify rules for nested selectors, like:
style9.create({
text: {
color: '#333',
'&>a': {
color: 'blue',
}
},
});
Admittedly, this doesn't fit very well with the concept of atomic CSS. However, it is often a critical escape hatch in cases where we do not have control over the html, e.g., when dealing with text generated from markdown or wyiwg editors, and all other atomic-styling solutions I'm aware of support this feature to some degree.
Seems like this is a prime candidate for auto-expanding. It was the second opened issue on inline-style-expand-shorthand (robinweser/inline-style-expand-shorthand#2) but that project seems dormant. What are your thoughts on adding it here?
Using imported values within the styles object passed to Style9.create()
seems to break the Webpack build when using the Style9 Webpack loader.
yarn install
yarn build
The color value imported from colorValue.js
("blue
") is applied to the <body>
.
The Webpack build fails with an error:
SyntaxError: unknown: Could not evaluate value
4 | const styles = style9.create({
5 | body: {
> 6 | color: colorValue,
| ^^^^^^^^^^
7 | },
8 | });
I found that style9 will try to parse selector name from each decl's parent
https://github.com/johanholmerin/style9/blob/master/src/process-css.js#L94
const selectors = parseSelector(rule.parent);
but there are some exception, like this one
@-ms-viewport { width: device-width; }
PostCSS recognizes width: device-width
as a declaration, but the type of this declaration’s parent is 'atrule'. There is no property 'selector' in type 'atrule', and this will make postcss-selector-parser generate exception.
As far as I know, @-ms-viewport is a deprecated API but some UI component repository still use it for some reasons.
[webpack-cli] TypeError: Cannot read property 'valueOf' of undefined at tokenize (/Users/cosimo/code/style9/node_modules/postcss-selector-parser/dist/tokenize.js:93:23) at new Parser (/Users/cosimo/code/style9/node_modules/postcss-selector-parser/dist/parser.js:130:41) at Processor._root (/Users/cosimo/code/style9/node_modules/postcss-selector-parser/dist/processor.js:55:18) at Processor._runSync (/Users/cosimo/code/style9/node_modules/postcss-selector-parser/dist/processor.js:102:21) at Processor.transformSync (/Users/cosimo/code/style9/node_modules/postcss-selector-parser/dist/processor.js:173:17) at parseSelector (/Users/cosimo/code/style9/src/process-css.js:41:47) at /Users/cosimo/code/style9/src/process-css.js:94:23 at Array.forEach (<anonymous>) at extractDecls (/Users/cosimo/code/style9/src/process-css.js:93:9) at sortPseudo (/Users/cosimo/code/style9/src/process-css.js:149:27) at LazyResult.run (/Users/cosimo/code/style9/node_modules/postcss/lib/lazy-result.js:288:14) at LazyResult.sync (/Users/cosimo/code/style9/node_modules/postcss/lib/lazy-result.js:274:26) at LazyResult.stringify (/Users/cosimo/code/style9/node_modules/postcss/lib/lazy-result.js:298:10) at LazyResult.get (/Users/cosimo/code/style9/node_modules/postcss/lib/lazy-result.js:379:19) at Style9Plugin._processFiles (/Users/cosimo/code/style9/webpack/index.js:52:18) at /Users/cosimo/code/style9/webpack/index.js:33:16
Hey, this looks great! I wanted to build something like this myself, but this looks like just what I need. However, I use Vue. Any chance you are going to support it? Seems like it should be doable since Vue let's you modify the webpack config, but Im not sure exactly what I need to modify.
Vite does not process files during development. It serves application source using browser built-in support for JavaScript modules.
This leads to style9
failing in runtime:
Uncaught Error: style9.create calls should be compiled away
After upgrading next from v12.0.2
to v12.0.10
, I am getting the following error when building:
TypeError: rawOptions.postcss is not a function
I did not have time to investigate, but my guess is that this is the same issue as vanilla-extract-css/vanilla-extract#522, which they seem to have been fixed in vanilla-extract-css/vanilla-extract#537
There seems to be some weird behavior going on when using conditionals.
Given these styles
const styles = style9.create({
primaryButton: {
":hover": {
boxShadow: "var(--box-shadow-hover)"
}
},
secondaryButton: {
":hover": {
boxShadow: "var(--box-shadow-hover)"
}
},
});
I would expect these two invocations to return the same string
const st1 = styles(
true && 'primaryButton',
false && 'secondaryButton'
);
const st2 = styles(
true && 'primaryButton'
);
// st1 === st2 => false
// st1 === "" => true
// st2 === "c17j9v3m " => true
It's a amazing project, I using it in my project. But when i migrate to vite. I found style9 currently don't support it. So i want a vite plugin :)
I'll do it.
I know that this is not the ideal place for this... I'm having trouble running a webpack with babel-loader. Would you have a more complete example of a configured webpack? I was not successful, following the documentation.
My webpack:
module.exports = (env = { NODE_ENV: 'development' }) => {
const prod = env.NODE_ENV === 'production';
// eslint-disable-next-line no-console
console.log(`MODE: ${prod ? 'PRODUCTION' : 'DEVELOPMENT'}`);
return {
entry: './src/index.tsx',
output: {
filename: '[name].[chunkhash].js',
path: path.resolve(__dirname, 'dist'),
},
mode: prod ? 'production' : 'development',
devtool: 'source-map',
...(prod
? {}
: { devServer: { historyApiFallback: true, hot: true, hotOnly: true } }),
plugins: [
// HTML Template
new HtmlWebpackPlugin({ template: './index.html' }),
new Style9Plugin(),
new MiniCssExtractPlugin(),
].filter(Boolean),
resolve: {
extensions: ['.tsx', '.ts', '.js'],
},
module: {
rules: [
{
test: /\.([tj])sx?$/,
use: [
Style9Plugin.loader,
{
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', { targets: 'last 1 chrome version' }],
[
'@babel/preset-react',
{
// Removes the need to import React into JSX files
runtime: 'automatic',
},
],
'@babel/preset-typescript',
],
plugins: [
// Fast Refresh
!prod && require.resolve('react-refresh/babel'),
].filter(Boolean),
cacheDirectory: true,
},
},
],
},
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, 'css-loader'],
},
{
test: /\.(png|jpe?g|gif|woff2?|eot|otf|webp)$/i,
use: 'file-loader',
},
],
},
};
};
Error:
Per your suggestion in #2 (comment) , I tried adding style9 to CRA via CRACO (my craco config is included below). The only changes I made are adding the Style9Plugin.loader and new Style9Plugin().
This works entirely fine in development. However, there's an interesting glitch where the order of CSS rules changes in production leading to glitches like the one below (a minimal button displays as a regular button in light mode, but correctly as a minimal button in dark mode).
This demo app does not use style9 calls anywhere. I've only simply added the config in craco. The issue goes away when I remove new Style9Plugin()
from the plugins list. So I assume the Style9Plugin (or perhaps the postcss loader it calls) are modifying CSS incorrectly somehow.
Part of the issue here could well be my very limited understanding of webpack, their plugins, etc. It would be a great help to have a clean CRA plugin supported by the library.
I'm happy to share my repro if it would be helpful.
When using webpack plugin, throw an error due to incorrect path.
See:
https://github.com/johanholmerin/style9/blob/master/webpack/index.js#L4
Looks like a1317e1 added them, but they've since been removed. Can we bring them back?
If You have a component something like this:
import style9 from 'style9';
const styles = style9.create({
blue: {
color: 'blue',
},
red: {
color: 'red'
}
});
function LazyComponent() {
return (
<h1 className={styles('blue', true && 'red')}>I'm Lazy Component</h1>
);
}
export default LazyComponent;
And before that you created this style in another component:
const styles = style9.create({
blue: {
color: 'blue',
},
red: {
color: 'red'
}
});
New CSS file generated as duplicate
Hi, is there any reason not to combine your efforts with compiled from Atlassian? They support shorthands properties, styled-components and emotion-like API, and much more. CSS extraction is currently being worked on.
Hi @johanholmerin!
Very promising lib! I was eager to pull it in my preact project, however I am having some hard time figuring out how to wire it into preact-cli
.
I tried to follow your guides in the Readme's webpack section, and even the vue-related example that you prepared, without much luck.
The conversion to atomic classnames seemed to be working, as they were written onto the target element in the DOM, but I was missing the actual related CSS output. I am not sure whether it is related to how preact-cli
assembles the final loaders for webpack, where it seemes to be using style-loader
instead of css-loader
and maybe even MiniCssExtractPlugin.
I tried with something like this:
// preact.config.js
import Style9Plugin from 'style9/webpack';
export default {
webpack(config, env, helpers, options) {
// ...
config.module.rules.splice(4, 0, {
test: /\.(tsx|ts|js|mjs|jsx)$/,
use: Style9Plugin.loader
});
config.plugins.splice(8, 0, new Style9Plugin());
}
};
Could you give some hints on pulling style9
into a preact project?
Is it currently possible specify multiple transition effects? From what I can tell the transition sub-properties only accept a string, not an array.
I noticed that the cursor
property (https://developer.mozilla.org/en-US/docs/Web/CSS/cursor) is not supported anywhere here. Is this something you would accept a PR for?
Currently the transitionProperty
doesn't work when providing an array of properties.
e.g.
let styles = style9.create({
default: {
transitionProperty: ["backgroundColor", "color"]
}
});
the resulting class will end up with this css:
.random-name {
transition-property: backgroundColor color;
}
where it should be a comma separated hyphen-case string:
.random-name {
transition-property: background-color, color;
}
For now a workaround would be to provide the values as a string:
let styles = style9.create({
default: {
transitionProperty: "backgroundColor, color"
}
});
I tried to use style9 in ejected CRA but it not works.
How reproduce:
npx create-react-app s9 --use-npm
npm run eject
./config/webpack.config.js
App.js
ex:import style9 from 'style9';
const styles = style9.create({
blue: {
color: 'blue',
},
red: {
color: 'red'
}
});
function App() {
return (
<h1 className={styles('blue', true && 'red')>Hi Style9</h1>
);
}
export default App;
npm run start
Example: blog/wip
Description: Cannot navigate via onClick
event when there is Style9-related code in the component.
Example from Facebook Workplace:
{
focused: {
outline: "2px solid Highlight",
"@media (-webkit-min-device-pixel-ratio: 0)": {
outline: "5px auto -webkit-focus-ring-color"
}
},
unfocused: {
outline: "none"
}
}
{
typeaheadLayoutOpened: {
borderBottomEndRadius: "8px",
borderBottomStartRadius: "8px",
"::before": {
borderBottomEndRadius: "8px",
borderBottomStartRadius: "8px"
}
}
}
I'd like to ask are you can to add support for advanced pseudos like :not, :nth-child etc?
Using the withStyle9
plugin, hot-reload only sort of works: When authoring styles, classes will correctly be attached to elements on hot-reload, but a new css file isn't generated, so if the class did not previously exist already, no style is associated with the attached class. A hard reload fixes this.
So, for example, if we have:
const styles = style9.create({
foo: {
color: '#333',
display: 'flex',
paddingLeft: '2em',
paddingRight: '2em',
},
bar: {
marginTop: '2em',
},
});
And we than add paddingLeft: 2em
to bar
, that will work fine, because there is already a class associated with paddingLeft: 2em
in the css file. However, if we were to add, for instance, color: 'green'
, to either foo
or bar
, a new class would be attached to the relevant element(s), but it would have no effect, because there is no class associated with that style already in the css file, and no new css file is being generated.
style9/next does not seem to work nice with next-contentlayer. When the two plugins are used together, an error is thrown:
// next.config.js
const { withContentlayer } = require("next-contentlayer");
const withTM = require("next-transpile-modules")(["style9"]);
const withStyle9 = require("style9/next");
const nextConfig = withContentlayer({
...withStyle9()(withTM()),
});
module.exports = nextConfig;
# or
module.exports = withTM(withStyle9({})(withContentlayer({})));
# or
module.exports = {
...withContentlayer(),
...withStyle9()(withTM()),
};
# or
function compose(...plugins) {
return (options) => {
return plugins.reduce((acc, next) => {
return next(acc);
}, options);
};
}
module.exports = compose(
withContentlayer,
withStyle9(),
withTM
)()
# or
module.exports = withStyle9()(compose(
withContentlayer,
withTM
)())
I have tried many solutions, all to no avail.
Compressing the generated class names into the shortest possible strings could be a nice optimization.
In jest, my suggestion is to find a way to use incremental strings instead of hashes, e.g., .a
, .b
, etc., instead of c1r9f2e5
, cu2kwdz
, etc.
This is the class name strategy used by Fela and currently considered by compiled (relevant RFC).
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.