Skip to content
/ twind Public
forked from tw-in-js/twind

The smallest, fastest, most feature complete Tailwind-in-JS solution in existence.

License

Notifications You must be signed in to change notification settings

osdiab/twind

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

The smallest, fastest, most feature complete Tailwind-in-JS solution in existence


MIT License Latest Release Documentation Github Discord CI Coverage Status


READ THIS FIRST!

Twind v1 is still in beta. Expect bugs!


Twind is a small compiler that converts utility-classes into actual CSS rules without any build step right in the browser or any other environment like Node.js, deno, workers, ...

If you have used Tailwind or other CSS-in-JS solutions, then most of the API should feel very familiar.

πŸš€ Features

⚑️ No build step

Get all the benefits of Tailwind without the need for Tailwind, PostCSS, configuration, purging, or autoprefixing.

πŸš€ Framework agnostic

If your app uses HTML and JavaScript, it should work with Twind. This goes for server-rendered apps too.

😎 One low fixed cost

Twind ships the compiler, not the CSS. This means unlimited styles and variants for one low fixed cost.

Other features include:

  • 🌎 No bundler required: Usable via CDN
  • 🎨 Seamless integration with Tailwind
  • 🀝 Feature parity with Tailwind v3
  • ✈️ Tailwind preflight by default
  • 🎯 Extended variants, rules, and syntax
  • πŸš“ Escape hatch for arbitrary CSS
  • πŸ€– Built in support for conditional rule combining
  • 🧐 Improved readability with multiline styles and comments
  • ❄️ Optional hashing of class names ensuring no conflicts
  • πŸ”© Flexible: configurable theme, rules and variants
  • πŸ”Œ Language extension via presets
  • 🎩 No runtime overhead with static extraction
  • πŸš… Faster than most CSS-in-JS libraries
  • ⚑ Fully tree shakeable: Only take what you want
  • 🦾 Type Strong: Written in Typescript
  • and more!

πŸ¦„ Quickstart

twind does not include any core utilities β€” use one of the existing presets:

For the full Tailwind CSS experience try @twind/tailwind or start with Twind CDN a drop-in replacement for Tailwind CSS Play CDN that is almost 6 times smaller (96.4kb vs 16.9kB).

We have created a few examples to get you started:

Example Try it live at Description
Basic Stackblitz β€’ Codesandbox with @twind/tailwind
Playground Stackblitz β€’ Codesandbox all presets enabled
SvelteKit Stackblitz β€’ Codesandbox for SvelteKit with @twind/tailwind
Tailwind Forms Stackblitz β€’ Codesandbox with @twind/tailwind and @twind/preset-tailwind-forms
Twind CDN Stackblitz β€’ Codesandbox with @twind/cdn

Notable Changes

  • twind β€” shim-first implementation without any presets to have a clean start

  • @twind/tailwind β€” shim-first implementation using @twind/preset-tailwind and @twind/preset-autoprefixer

  • API

    • setup can be called as many times as you want.
    • classes are returned in order they are applied by the browser - last one wins
    • tw as known from twind v0.16; additional it can be used to access:
      • the theme: tw.theme(...)
      • the target sheet: tw.target
      • allows to reset twind (start clean): tw.clear()
      • allows to remove twind (remove the associated style element): tw.destroy()
    • shortcut (previously known as apply) and css as known from twind v0.16.
      • with support for creating named shortcuts: shortcut.PrimaryButton\bg-red-500 text-white`->PrimaryButton#`
    • new cx function to create class names
      • grouped rules are ungrouped
    • style β€” stitches like component definitions
      • creates readable class names like
        • style#1hvn013 style--variant-gray#1hvn013 style--size-sm#1hvn013 style--outlined-@sm-true#1hvn013
      • with label: style({ label: 'button', ... })
        • button#p8xtwh button--color-orange#p8xtwh button--size-small#p8xtwh button--color-orange_outlined-true$0#p8xtwh
  • grouping syntax:

    • allow trailing dash before parentheses for utilities -> border-(md:{2 black opacity-50 hover:dashed}}
    • shortcuts: ~ to apply/merge utilities -> ~(text(5xl,red-700),bg-red-100)
      • anonymous shortcuts: ~(!text-(3xl center) !underline italic focus:not-italic)
        • support comma-separated shortcuts β€” this would prevent different classNames errors during hydration:
          • hover:~(!text-(3xl,center),!underline,italic,focus:not-italic)
          • cx() converts these to comma-separated group
      • named shortcuts: PrimaryButton~(bg-red-500 text-white) -> PrimaryButton#<hash>
        • shortcut() is an helper to simplify creation of shortcuts (works like apply() in twind v0.16); it supports creating named shortcuts: shortcut.PrimaryButton\bg-red-500 text-white`->PrimaryButton#`
  • config

    • presets are executed in order they are defined

    • presets can currently not contain other presets β€” a work-around may by to use defineConfig() within the preset (not tested)

    • `defineConfig() helper for typing

    • preset merging:

      • preflight β€” last one wins
      • theme and theme.extend are shallow merged β€” last one wins
      • rules, variants, and ignorelist β€” first one wins
      • hash and stringify are overridden if defined by the preset β€” last one wins
    • user config merging

      • preflight β€” applied last
      • theme and theme.extend are shallow merged β€” applied last
      • rules, variants, and ignorelist β€” applied first
      • hash and stringify are overridden if defined by the preset β€” applied first
    • rules and shortcuts based on ideas from UnoCSS

      // defineConfig is optional but helps with type inference
      defineConfig({
        rules: [
          // Some rules
          ['hidden', { display: 'none' }],
      
          // Table Layout
          // .table-auto { table-layout: auto }
          // .table-fixed { table-layout: fixed }
          ['table-(auto|fixed)', 'tableLayout'],
      
          // Some shortcuts
          {
            // single utility alias
            red: 'text-red-100',
      
            // shortcuts to multiple utilities
            btn: 'py-2 px-4 font-semibold rounded-lg shadow-md',
            'btn-green': 'text-white bg-green-500 hover:bg-green-700',
      
            // dynamic shortcut β€” could be a rule as well
            'btn-': ({ $$ }) => `bg-${$$}-400 text-${$$}-100 py-2 px-4 rounded-lg`,
          },
        ],
      })

      There are lots of things possible. See preset-tailwind/rules and preset-ext/rules for more examples.

    • ignorelist: can be used ignore certain rules

      This following example matches class names from common libraries:

      defineConfig({
        // emotion: `css-`
        // stitches: `c-`
        // styled-components: `sc-`and `-sc-
        // svelte: `svelte-`
        // vanilla-extract: sprinkles_
        // goober: `go1234567890`
        // DO NOT IGNORE rules starting with `^[~#]`, `^css#`, or `^style[~#-]` β€” these may have been generated by `css()` or `style()`, or are hashed
        ignorelist: /^((css|s?c|svelte)-|(sprinkles)?_|go\d)|-sc-/,
      })
    • config theme section function has a changed signature

      theme: {
      -  fill: (theme) => ({
      +  fill: ({ theme }) => ({
          gray: theme('colors.gray')
        })
      }
    • no implicit ordering within preflight

  • comments (single and multiline)

  • no more important suffix: rule! -> !rule

  • styles (the generated CSS rules) are sorted predictably and stable β€” no matter in which order the rules are injected

  • support label for a more readable class names (https://emotion.sh/docs/labels)

  • support theme(...) in property and arbitrary values

  • no more @screen sm -> use the tailwindcss syntax @media screen(sm)

  • @apply finally works as expected

  • full support for color functions: primary: ({ opacityVariable, opacityValue }) => ...

  • strict tailwindcss v3 compatibility

    • no IE 11 fallbacks (color, box-shadow, ...)
    • no more font-* and text-* shortcuts
    • no border-tr but border-[xytrbl]* still exists
    • no bg-origin-*
  • no more @global β€” you must use & for nested selectors (this follows the CSS Nesting Module)

  • new @layer directive following the Cascade Layers (CSS @layer) spec

    The following layer exist in the given order: defaults, base, components, shortcuts, utilities, overrides

    import { css } from 'twind'
    
    element.className = css`
      /* rules with base are not sorted */
      @layer base {
        h1 {
          @apply text-2xl;
        }
        h2 {
          @apply text-xl;
        }
        /* ... */
      }
    
      @layer components {
        .select2-dropdown {
          @apply rounded-b-lg shadow-md;
        }
        .select2-search {
          @apply border border-gray-300 rounded;
        }
        .select2-results__group {
          @apply text-lg font-bold text-gray-900;
        }
        /* ... */
      }
    `
  • drop IE 11 support

TODO

  • const acx = cx'aaa~(text-blue-500)' works, but const bcx = 'bbb~(text-red-500 ${acx})' not

  • support css() in preflight and rules

  • rewrite https://github.com/TanStack/tanstack.com

  • support is(:hover,:focus-visible):underline?

  • style: should it pass class and className through? alternatives: string concat, cx

  • console.warn([twind] unknown rule "${value}")

  • ci: post on discord after release

  • @twind/tailwind: parse style elements like tailwind?

  • zero runtime

  • cdn.twind.dev -> https://cdn.jsdelivr.net/npm/twind@next/cdn.global.js

  • docs: wmr + codehike + cloudflare pages

  • docs: explain and examples of both modes (observe/shim vs library)

  • docs: common patterns

  • docs: debugging the generated CSS in the browser (cssom sheet)

  • auto support dark mode in theme helpers (<section>.dark.<key> or dark.<section>.<key>)

  • @twind/preset-* from tailwind core

  • @twind/react

  • @twind/completions β€” provide autocompletion for classNames

  • a package to make it easy to create lightweight versions of presets (like https://lodash.com/custom-builds)

  • postcss plugin like tailwindcss for SSR

    @twind;

🧱 Contribute

See the Contributing Guide

πŸ’‘ Inspiration

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.

  • Tailwind: created a wonderfully thought out API on which the compiler's grammar was defined.
  • styled-components: implemented and popularized 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 implementation 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.
  • style-vendorizer: essential CSS prefixing helpers in less than 1KB of JavaScript.
  • UnoCSS: for the configuration syntax.
  • CSSType: providing autocompletion and type checking for CSS properties and values.

🌸 Credits

🀝 Contributors

Thank you to all the people who have already contributed to twind!

πŸ™πŸΎ Sponsors

Thank you to all our sponsors! (please ask your company to also support this open source project by becoming a sponsor)

@OnurGvnc @tylerforesthauser @mtsknn [Become a GitHub Sponsor]

βš–οΈ License

The MIT license governs your use of Twind.

About

The smallest, fastest, most feature complete Tailwind-in-JS solution in existence.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 60.0%
  • TypeScript 32.5%
  • Svelte 5.7%
  • Smarty 1.8%