twind
the smallest, fastest, most feature complete Tailwind-in-JS solution in existence
![PRs Welcome](https://flat.badgen.net/badge/PRs/welcome/purple)
If you are here then the likelihood is that you using in Tailwind or CSS-in-JS libraries such as styled components, emotion or goober before. These packages have proven overwhelmingly popular and revolutionized web development as we know it.
The purpose of this project is unify these two philosophies and create the smallest, fastest, most feature complete Tailwind-in-JS solution in existence. Embracing the flexibility of CSS-in-JS whilst conforming to the natural constraints of the Tailwind API.
More importantly though, we hope to create a place for likeminded people to discuss issues and share ideas.
Quickstart
If you would like to get started with twind right away then copy paste this code into your favorite sandbox.
⚡️ Alternatively try the live and interactive demo
import { tw } from 'https://cdn.skypack.dev/twind'
document.body.innerHTML = `
<main class="${tw`bg-black text-white`}">
<h1 class="${tw`text-xl`}">This is Tailwind in JS!</h1>
</main>
`
📚 For further instruction on usage please read the documentation!
Rational
This project was started by the authors of two similar libraries – oceanwind and beamwind – who chose to collaborate rather than compete with each other in this space. The open source community is full of fragmentation but we wanted to see cohesion here.
Combining efforts has saved us time and resulted in a much more complete and production ready offering. Furthermore we were able to agree on and coin some standards for certain aspects of the implementation, based on all of our learnings; things like parsing input, grouping syntax, prescedence calculation and plugin API.
Why twind?
A lot of developers ask "Why not just use Tailwind?" and our answer is always that you should use Tailwind, it is an absolutely incredible invention! However, if like us you are already building your app in JS using a framework like react, preact, vue or svelte, rather than just static HTML, then compiling Tailwind shorthand just in time (like twind does) rather than ahead of time like with Tailwind and PostCSS, comes with a lot of advantages.
I've wanted to do a CSS-in-JS flavor of Tailwind for over 2 years because of all the neat benefits you get there so it's cool to see projects like this! – @adamwathan
Take the following snippet for example:
import { tw, setup, strict } from 'https://cdn.skypack.dev/twind'
setup({
hash: true,
mode: strict,
theme: {
fontFamily: {
sans: ['Helvetica', 'sans-serif'],
serif: ['Times', 'serif'],
display: ['Baloo', 'sans-serif'],
},
extend: {
colors: { hotpink: '#FF00FF' },
rotate: { 5: '5deg' },
},
},
plugins: {
'scroll-snap': (parts) => ({
'scroll-snap-type': parts.join(' '),
}),
},
})
const app = () => `
<div class='${style.container}'>
<h1 class='${tw`
text(white 4xl)
font(bold sans)
transition-transform
hover:(
rotate-5
scale-150
cursor-pointer
)
`}'>Hello World</h1>
</div>
`
const style = {
container: tw`
h-full
bg-hotpink
md:(bg-purple-500)
lg:(bg-white text-hotpink)
flex
items-center
justify-center
`,
}
document.body.innerHTML = app()
- All setup is done at runtime, no build step required! This makes that is possible to configure and reconfigure the compiler on the fly.
- By shipping the compiler (rather than the resultant output) there is a known and fixed cost associated with styling. No matter how many styles you write or how many variants you use, all your users will ever have to download is approximately 10Kb of code (which is less than styled-components or your average Tailwind build).
- By default the base reset provided by tailwind is instantiated with your theme values (like fonts and colors) and injected in the document during setup. Guaranteeing more consistent cross browser results out of the box.
- It is possible to configure twind to hash class names before injecting them into the DOM. This can reduce the overall down the wire size of pages and eliminate any chance of class name conflicts.
- Theming is done exactly as documented by the Tailwind meaning that you can copy paste in your project theme from existing projects. The only different here is that there is no need to rebuild after changing you theme. Just refresh the page!
- Input to the compiler is not limited to just a string like HTML classes are. Twind accept arrays, objects, template literals, functions, almost everything! The interpretter spec is inspired by and very similar to clsx and offers a much more developer friendly API that handles null values gracefully.
- Using template literals as input (the recommended method) allows you to break rules over multiple lines, drastically improving readability and maintainability.
- Control over the interpreter affords us the possibility of defining syntax for grouping responsive and pseudo variants as well as directives with common prefixes. This massively reduces repetition and improves comprehension.
- The fact that the compiler accepts functions that return arbritary CSS-in-JS provides an escape hatch for all those one off rules which aren't supported by tailwind. The
&
keyword allows you to write complex rules (like pseudo elements &::before
and &::after
) that are beyond the scope of inline styles. - Given the finite grammars that the compiler has to support, we are able to specialize it to compile and inject CSS faster than all the popular CSS-in-JS solutions.
- Extending the grammar is trivial and can be achieved by providing a function inline or generalizing inline rules and defining them during setup under the plugins key.
- The compiler itself is not reliant on the DOM at all, which makes it an ideal candidate for static extraction which would remove all runtime overhead. This is possible during SSR or build time prepass.
Prior Art
It would be untrue to suggest that the design here is totally original, other than the founders initial attempts at implementing such a module (oceanwind and beamwind) we are truly standing on the shoulders of giants. Prior art includes but is not limited to:
- tailwind: created a wonderfully thought out API on which the compiler's grammar was defined.
- styled-components: implemented and popularised the advantages of doing CSS-in-JS.
- htm: a JSX compiler that proved there is merit in doing runtime compilation of DSLs like JSX.
- goober: an impossibly small yet efficient CSS-in-JS implemetation that defines critical module features.
- otion: the first CSS-in-JS solution specifically oriented around handling CSS in an atomic fashion.
- clsx: a tiny utility for constructing class name strings conditionally.
- tiny-css-prefixer: essentials CSS prefixing helpers in less than 1KB of JavaScript.
- csstype: providing autocompletion and type checking for CSS properties and values.
License
MIT