Skip to content

hoangnhan2ka3/twg

Repository files navigation

twg thumbnail

twg

Tests Status Repository stars Bundle Size Gzip Size Total Downloads Latest Release License

Dependency Count Tree Shakable Supports

πŸͺ₯ A utility function for grouping TailwindCSS variants.

A more elegant way of writing Tailwind classes. Never need to repeating the same variants over and over again 🀯.


πŸ“ Entry point πŸ“¦ Bundle πŸ“¦ Gzip
twg 2721B 1442B
twg/lite πŸͺΆ 2274B 1220B
twg/extend 2756B 1272B
twg/extend/lite πŸͺΆ 2138B 976B

From v6, each entry already contains both twg() and transformer()[1] function


[1]: Previously called replacer() function.

πŸ—οΈ Features

  • βœ… Elegant.
  • βœ… Easy setup.
  • βœ… Support for multiple objects parsing.
  • βœ… Support for nesting multiple objects, arrays, and itself functions.
  • βœ… Support for (multiple) conditional classes, objects, and arrays.
  • βœ… Customizable callee name and separator.
  • βœ… Compatible with wrappers like twMerge.
  • βœ… "Base" support for Tailwind CSS IntelliSense (IDEs extension), as well as Hover Preview.
  • βœ… 0 dependencies on default version.
  • βœ… Tree-shaking friendly.
  • βœ… Lite version.

Version comparison

Features default default/lite extend extend/lite
Accept string, number, array, object βœ“ βœ“ βœ“ βœ“
Accept template literal partial partial βœ“ βœ“
Accept multiple outermost objects βœ“ βœ“ βœ“ βœ“
Accept nesting objects, arrays βœ“ βœ“ βœ“ βœ“
Accept nesting chosen callee inside main object(s) βœ“ βœ“
Accept nesting custom callee functions inside main object(s) βœ“
Accept logical conditionals βœ“ βœ“ βœ“ βœ“
Accept ternary conditionals partial partial βœ“ βœ“
Accept native object behavior (key as classes and value as conditionals) partial partial βœ“ βœ“
Compatible with wrappers βœ“ βœ“ βœ“ βœ“
Fully customizable βœ“ partial βœ“ partial
Options ↗️ 3 1 4 1

Which version should I use?

  • default version seems to be suitable for almost base cases, promise that you don't need to handle very complex conditions like ternary in template literal, or nesting itself/custom functions inside main object(s).
  • In the other hand, extend version is the most stable and powerful, it can handle almost any cases you can imagine (if not, please open an issue), your only trade-off is that it's a very little bit slower than default version (because of AST parsing).
  • I think you should try with default, default/lite version first. BTW, I'm currently using extend/lite version.

Tested conditions

No. Framework/Lib Version Additional info Tester Status
1. next 15.0.0-canary.156 With --turbo flag and babel-plugin-react-compiler enabled. author βœ…
2. react 19.0.0-rc-206df66e-20240912 author βœ…
3. tailwindcss 3.4.11 author βœ…
4. tailwind-merge 2.5.2 author βœ…

πŸ“Œ Table of contents

πŸ“° News

Click to expand/collapse this section
  • πŸ”₯ From v6.0.2:

    • Type TransformerOptions (previously ReplacerOptions) can now be imported from each entry point.
  • πŸ”₯ From v6.0.0:

    • Rename replacer() function to transformer().
    • Remove */replacer entry point, transformer() now is in the same entry point as twg(), also with createTwg() function. That means transformer() function will be combined/located in the same file with twg(), to take advantage of its API without rewrite it in another entry.
    • ~25% lighter bundle size in overall.
  • πŸ”₯ From v5.0.3:

    • Enable tree-shaking for the package.
  • πŸ”₯ From v5.0.0:

    • From now if you want to use custom separator option, you need to define the separator option in new createTwg() function (previously in the last Object of twg() function) and also in transformer() function like previous version.
  • πŸ”₯ From v4.0.0:

    • Make extend version (which previously called AST version) as optional entry point. From now if you want to use extend version, you need to install 4 more @babel dependencies, refer to docs.
    • Default version now using combiner() which written in native JS to parse conditionals (use with limitations).
  • πŸ”₯ From v3.1.0:

    • Supports nesting custom callee functions through nestingCallee option (default version only).

      transform: {
        DEFAULT: transformer({
          // Define options here, eg.:
          callee: "twg",
          nestingCallee: ["cn", "twg"]
        })
      }

      Usage: see custom nestingCallee.

  • πŸ”₯ From v2.0.0:

    • Supports native objects behavior like clsx (Key as classes and value as conditionals)

      twg({ foo: true, bar: false, baz: isTrue() });
      //=> "foo baz"
      
      twg({ "you are": true, not: false, m_y: 1, "destiny": isTrue() });
      //=> "you are m_y destiny"
    • Use @babel AST to parse all conditional classes, objects, arrays or even both string and array.

      • Pros:
        • More accurate, more trust in processing.
        • Works perfectly even with complex conditionals like nested ternary and inside template literal.
        • Reduce several complex regex use to parse condition.
        • Resolve all outstanding issues related to any kind of conditionals, nested conditionals.
      • Cons:
        • Currently work on (.js, .ts, .jsx, .tsx) file only.
        • A bit slower, especially on the first time, when nothing is cached.
        • 4 more dependencies 😒
  • πŸ”₯ Lite version:

    Same as default, but:

    • Without any options API except for custom callee in transformer() options.
    • No debug messages (no console messages).
    • No JSDoc comments for each function.
    • 20 ~ 30% lighter.
    • Compile 200 ~ 300ms faster (extend version).

[!TIP] When you tested using with default version, and everything's OK. So you could want to use lite version, for better performance.

🚨 Quick intro

Tip

Simply open an Object, put the variant as key, and classes you want to map to that variant as each value.
And if you want to nest other variants inside a variant, you can open an Array and start use it like you've just call a callee function. See usage / use cases for more details.

Example:

<div className={twg(
  "size-92 relative grid place-items-center",
  {
    before: "absolute inset-0 bg-red-500",
    "aria-expanded": "bg-red-500 text-yellow-500",
  }
)}>
  Hello, World!
</div>

Output (html):

<div class="size-92 relative grid place-items-center before:absolute before:inset-0 before:bg-red-500 aria-expanded:bg-red-500 aria-expanded:text-yellow-500">
  Hello, World!
</div>

πŸš€ Getting started

⏩ default version

1. Install the package

pnpm add twg

or

npm install twg

2. Setup

// tailwind.config.ts

import { type Config } from "tailwindcss"
import { transformer } from "twg"

export default {
  content: {
    files: [
      "./src/app/**/*.{ts,tsx}",
      "./src/components/**/*.{ts,tsx}",
    ], // Move your old `content` to `content.files` like this
    transform: {
      DEFAULT: transformer() // Put `transformer()` here
    }
  },
  // Other configurations...
} satisfies Config
  • Lite version:

    // tailwind.config.ts
    
    import { transformer } from "twg/lite"
    
    // Rest like above
  • If you need to override default transformer() options:

    transform: {
      DEFAULT: transformer({
        // Define options here, eg.:
        callee: "cn"
      })
    }

    See all options and how to custom options.

3. Use

import { twg } from "twg"
  • Lite version:

    import { twg } from "twg/lite"
  • If you need to override default twg() options, you need to use createTwg() function (not for lite version):

    import { createTwg } from "twg"
    
    createTwg({ separator: "_" })(
      //...
    )

    See custom separator.

DONE!

⏩ extend version

Note

You need to install 4 more @babel dependencies in order to use extend version. Besides that, you just need to change the import statement from twg/* to twg/extend/*.

1. Install the package

pnpm add twg @babel/generator @babel/parser @babel/traverse @babel/types

or

npm install twg @babel/generator @babel/parser @babel/traverse @babel/types

2. Setup

// tailwind.config.ts

import { type Config } from "tailwindcss"
import { transformer } from "twg/extend"

export default {
  content: {
    files: [
      "./src/app/**/*.{ts,tsx}",
      "./src/components/**/*.{ts,tsx}",
    ], // Move your old `content` to `content.files` like this
    transform: {
      DEFAULT: transformer() // Put `transformer()` here
    }
  },
  // Other configurations...
} satisfies Config
  • Lite version:

    // tailwind.config.ts
    
    import { transformer } from "twg/extend/lite"
    
    // Rest like above
  • If you need to override default transformer() options:

    transform: {
      DEFAULT: transformer({
        // Define options here, eg.:
        callee: "cn"
      })
    }

    See all options and how to custom options.

3. Use

import { twg } from "twg/extend"
  • Lite version:

    import { twg } from "twg/extend/lite"
  • If you need to override default twg() options, you need to use createTwg() function (not for lite version):

    import { createTwg } from "twg/extend"
    
    createTwg({ separator: "_" })(
      //...
    )

    See custom separator.

DONE!

For more information, consider reading custom options ↗️ and best practice ↗️.

See how to use on docs πŸ‘‡.

πŸ“š Docs

πŸŽ‰ Changelog

For full & latest update changelog, please refer to CHANGELOG.md.

πŸ’Ž Contributing

Bugs

twg now work for me but maybe not for you in some edges. Consider opening an issue if you have any problem with it that I can fix it ASAP. Or a pull request is welcome too.

Features

If you have any ideas, feel free to open a feature request template or make a pull request to share your ideas.

For Development and more information on contributing please read CONTRIBUTING.md.

πŸͺͺ Credits

References

Project starts on

  • August 15, 2024

πŸ’ͺ Work with me

https://www.linkedin.com/in/hoangnhan2ka3/

Funding

Donate me