diff --git a/.vscode/settings.json b/.vscode/settings.json index 4e21739..f626077 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { "javascript.format.enable": false, - "typescript.format.enable": false + "typescript.format.enable": false, + "deno.enable": false } \ No newline at end of file diff --git a/lib/m.ts b/lib/m.ts new file mode 100644 index 0000000..082c450 --- /dev/null +++ b/lib/m.ts @@ -0,0 +1,4 @@ +import M from 'mithril' +import CSS from './index' +export const { css, m } = CSS(M, { server: !('document' in globalThis) }) +export default m \ No newline at end of file diff --git a/package.json b/package.json index d218112..87c39f6 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,9 @@ "scripts": { "test": "node --import tsx --test test/*.ts", "dev": "node --watch --import tsx --test test/*.ts", - "build:bundle": "esbuild lib/index.ts --bundle --format=esm --sourcemap --allow-overwrite --outfile=./dist/super-mithril-css.esm.js", + "build:bundle:index": "esbuild lib/index.ts --bundle --format=esm --sourcemap --allow-overwrite --outfile=./dist/super-mithril-css.esm.js", + "build:bundle:m": "esbuild lib/index.ts --bundle --format=esm --sourcemap --allow-overwrite --outfile=./dist/mithril.esm.js", + "build:bundle": "npm run build:bundle:index && npm run build:bundle:m", "build:types": "npx tsc -p tsconfig.test.json && npx tsc", "build:test": "npm run test", "build:clear": "rm -fr ./dist", diff --git a/readme.md b/readme.md index 2873cbe..1473835 100644 --- a/readme.md +++ b/readme.md @@ -1,17 +1,15 @@ # super-mithril-css -🎨 A simple reactive util for inline styles in mithril +🎨 A simple css-in-js solution for mithril.js ## Quick Start ```typescript -import CSS from 'super-mithril-css' -import m from 'mithril' -import {Stream} from 'super-mithril-stream' +import m, { css } from 'super-mithril-css/m' +import * as c from 'chifley' -const { css } = CSS(m) -const color = Stream('red') -const opacity = Stream(1) +const color = c('red') +const opacity = c(1) const desktop = css('(min-width: 1000px)') @@ -38,7 +36,7 @@ It has a few more features, but not much more than that. ## Why -We've had this built in to our internal fork of Mithril for a long time, but we're trying to extract a lot of that code into tiny libraries to share with the community. +We've had this built in to our internal fork of Mithril at [harth](harth.io) for a long time, but we're trying to extract a lot of that code into tiny libraries to share with the community. ## Tell me more @@ -47,7 +45,7 @@ We've had this built in to our internal fork of Mithril for a long time, but we' This is an exhaustive list of what this library does: - Takes any interpolated values you set and makes them css variables -- If your intepolated value adheres to the (sin.js inspired) observable spec, we will subscribe to them and patch the dom without redraws +- If your intepolated value adheres to the observable spec, we will subscribe to them and patch the dom without redraws - If its not an observable, the value is injected literally with no processing and updates whenever there is a redraw - We automatically wrap your original CSS definition in a block with a hash selector to isolate your styles to the current scope - We identify any `@keyframes` definitions and move them to the top level (because they don't work with the nested css spec) @@ -57,7 +55,7 @@ This is an exhaustive list of what this library does: - If you use `// comments` we'll fix it for you - You can directly inject literal css text via `${css('your text')}` - We also pretty print the styles while we parse them and compute the hash, all in one loop. -- It works in the browser and the server +- It works in the browser and on the server - We overload the hyperscript function to allow us to inject the hash selector onto the parent and to re-order elements so attrs always comes first - We support nesting css expressions which helps with writing handy utils @@ -104,6 +102,8 @@ type Stream = { Anything not treated as a stream will be bound to the DOM on init and every redraw. +> 🤓 We will soon release our stream API _chifley_, some of the mithril community are already playing with it. If you'd like to request early access, leave a comment [here](https://mithril.zulipchat.com/#narrow/channel/324076-general/topic/New.20stream.20library!) + ## Literals By default all interpolated values in the CSS are treated as css variables. If you would like to inject literal CSS text you can call `css('your string')`. @@ -142,9 +142,7 @@ Note: in order to attach the the selector the parent element on the server we ne ```typescript import M from 'mithril' -import CSS from 'super-mithril-css' - -const { m, css } = CSS(m, { server: true }) +import m, { css } from 'super-mithril-css/m' ``` In the browser we also override the hyperscript function to ensure attrs written after css nodes are moved to the start of the child list. @@ -200,23 +198,47 @@ One issue you may run into is using `#` as a property name, as typescript will r ## Browser support -Evergreen only. We do not make (web)apps that target older browsers and we aren't realistically able to support them without running up against their quirks day to day. We also rely and plan to rely on pretty new features to keep the codebase simple. +Evergreen only. We do not actively work on (web)apps that target older browsers and we aren't realistically able to support them without running up against their quirks day to day. We also rely and plan to rely on pretty new features to keep the codebase simple. ## Pretty printing -This library pretty prints while it is pseudo-parsing your css. At time of writing this cannot be disabled, but it wouldn't be a lot of work to change that -If it bothers you let us know. +This library pretty prints while it is pseudo-parsing your css. At time of writing this cannot be disabled, but it wouldn't be a lot of work to change that. If it bothers you let us know. ## FAQ -### What about nested css, auto units, etc +### Why does super-mithril-css export its own mithril? + +This library has to inject some extra functionality into mithril's hyperscript functionality + +- On the server we need to attach a generated classname to the parent element +- On the client and server we reorder args so `css` always comes after `attrs` + +To make the library super simple to use and adhere to mithril's tradition of only 1 import. + +### Why not use peerDeps? + +We feel peer deps are bit a convoluted, if you would like to manually parameterize `m` you can do so: + +```js +import M from 'mithril' +import CSS from 'super-mithril-css' + +const { m, css } = CSS(M) +``` + +### I tried some of my css and it didn't render? + +This library is fairly new, and while the parser has lots of tests and is being used in production, its definitely possible to confuse it if you are doing complex expressions across multiple lines. If you come across a situation where the parser gets confused please let us know and we'll patch it quickly. + +We also welcome contributions! -This library deliberately just does the bare minimum. Native CSS really isn't that bad and we feel to deviate from the syntax even a little bit you really need to be getting a lot of bang for your buck. You should go all in or not at all. +### What do you mean by 'pseudo-parsing'? -We do automatically inject semicolons and transform comments, but that is just after reviewing many PRs seeing devs assume that will work, its not worth the friction not to support that when its so simple to implement. +css is a constantly evolving language, and we don't want to bake in any assumptions that could lead to this library not supporting future features. We also want to parse and render the css as quickly as possible. This library therefore trusts you to write correctly formatted css. -If you would like us to add some other feature ask away, but its pretty likely we'll want to just keep things vanilla for this library. +The parser detects groupings. It detects the start and end of rules. It detects when you've entered or exited a comment. It detects nested rules/blocks. -### Why do we pass in the hyperscript function as any argument +It also detects the usage of `:root` and moves in any `:root` block outside of the scoped context. +But largely, the css you write, is the css we render. This library doesn't build an AST and then print CSS from the AST, it collects groups of rules and blocks and prints them again in order. -A few reasons. The first is it is much simpler than dealing with `peerDeps`, it makes this library easier to maintain and it gives you the power to swap out mithril for your own hyperscript wrapper (something we do at [Harth](https://harth.io) to add reactivity to hyperscript directly)/ +We do pretty print your css but only because this allows us to deterministically hash your styles to support client side hand off of server rendered styles. \ No newline at end of file diff --git a/test/browser.ts b/test/browser.ts index 4de67b1..10cffa0 100644 --- a/test/browser.ts +++ b/test/browser.ts @@ -1,5 +1,5 @@ import test from 'node:test' -import CSS, { pretty, sheets } from '../lib' +import { pretty, sheets } from '../lib' import { JSDOM, VirtualConsole } from 'jsdom' import assert from 'node:assert' @@ -22,9 +22,7 @@ test('browser', async () => { // just sync render, easier tests ;(globalThis as any).requestAnimationFrame = (f:any) => f() - const { default: M } = await import('mithril') - - const { css, m } = CSS(M, { server: false }) + const { css, default:m } = await import('../lib/m') const desktop = css('@media(min-width: 1000px)') const className = `css-x3m6yr` diff --git a/test/server.ts b/test/server.ts index 7305236..a769e14 100644 --- a/test/server.ts +++ b/test/server.ts @@ -1,9 +1,9 @@ import test from 'node:test' import assert from 'node:assert' -import hyperscript from 'mithril' // @ts-ignore import render from 'mithril-node-render' -import CSS, { sheets, pretty } from '../lib' +import { sheets, pretty } from '../lib' + import { JSDOM } from 'jsdom' const assertStringEq = (a:string,b: string) => { @@ -14,7 +14,7 @@ const assertStringEq = (a:string,b: string) => { test('server', async () => { - const { css, m } = CSS(hyperscript, { server: true}) + const { css, default: m } = await import('../lib/m') const desktop = css('@media(min-width: 1000px)') const rendered = await render( m(