{"id":14962185,"url":"https://github.com/noriller/easy-tailwind","last_synced_at":"2025-09-04T06:38:32.721Z","repository":{"id":64748713,"uuid":"576636721","full_name":"Noriller/easy-tailwind","owner":"Noriller","description":"An easier way of writing Tailwind classes.","archived":false,"fork":false,"pushed_at":"2022-12-23T15:46:05.000Z","size":173,"stargazers_count":171,"open_issues_count":3,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-08-27T06:47:25.678Z","etag":null,"topics":["javascript","nextjs","react","tailwind","tailwind-css","tailwindcss","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/easy-tailwind","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Noriller.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-12-10T13:41:47.000Z","updated_at":"2025-03-20T17:55:33.000Z","dependencies_parsed_at":"2023-01-30T19:01:08.757Z","dependency_job_id":null,"html_url":"https://github.com/Noriller/easy-tailwind","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/Noriller/easy-tailwind","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Noriller%2Feasy-tailwind","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Noriller%2Feasy-tailwind/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Noriller%2Feasy-tailwind/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Noriller%2Feasy-tailwind/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Noriller","download_url":"https://codeload.github.com/Noriller/easy-tailwind/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Noriller%2Feasy-tailwind/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273567090,"owners_count":25128630,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-04T02:00:08.968Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","nextjs","react","tailwind","tailwind-css","tailwindcss","typescript"],"created_at":"2024-09-24T13:29:38.373Z","updated_at":"2025-09-04T06:38:32.696Z","avatar_url":"https://github.com/Noriller.png","language":"TypeScript","funding_links":["https://www.paypal.com/donate/?business=VWNG7KZD9SS4S\u0026no_recurring=0\u0026currency_code=USD\u0026amount=5","https://www.paypal.com/donate/?business=VWNG7KZD9SS4S\u0026no_recurring=0\u0026currency_code=USD\u0026amount=10","https://www.paypal.com/donate/?business=VWNG7KZD9SS4S\u0026no_recurring=0\u0026currency_code=USD\u0026amount=42","https://www.paypal.com/donate/?business=VWNG7KZD9SS4S\u0026no_recurring=0\u0026currency_code=USD\u0026amount=1000","https://www.paypal.com/donate/?business=VWNG7KZD9SS4S\u0026no_recurring=0\u0026currency_code=USD\u0026amount=5000","https://www.paypal.com/donate/?business=VWNG7KZD9SS4S\u0026no_recurring=0\u0026currency_code=USD\u0026amount=10000","https://www.paypal.com/donate/?business=VWNG7KZD9SS4S\u0026no_recurring=0\u0026currency_code=USD"],"categories":[],"sub_categories":[],"readme":"# Easy Tailwind\n\nAn easier way of writing Tailwind classes.\n\n[Example to play around](https://stackblitz.com/edit/easy-tailwind?file=src/App.jsx)\n\n## Table of Contents\n\n- [What this is and what this isn't](#what-this-is-and-what-this-isnt)\n- [Installation](#installation)\n- [Setup](#setup)\n- [How to Use](#how-to-use)\n  - [Break Lines](#break-lines)\n  - [Use Objects](#use-objects)\n  - [Conditional Classes](#conditional-classes)\n- [Rules for it to Work](#rules-for-it-to-work)\n- [Does it Support XYZ?](#does-it-support-xyz)\n- [Final Considerations](#final-considerations)\n- [Why \"Easy\" Tailwind?](#why-easy-tailwind)\n\n## What this is and what this isn't\n\n### What this isn't\n\nThis is not [`WindiCSS`](https://github.com/windicss/windicss), [`UnoCSS`](https://github.com/unocss/unocss) or any other CSS lib or framework. This isn't meant to replace them.\n\nThis is meant to be used with Tailwind. So, if you're not using Tailwind, you don't want this.\n\n### What this might be\n\nIf you use [`classnames`](https://github.com/JedWatson/classnames), [`clsx`](https://github.com/lukeed/clsx/) and other utilities to have conditional classes, then this might be a replacement for them.\n\nThis doesn't cover all cases they do, and you could use all of them in conjunction if you want (they would wrap `e`/`etw` functions).\n\nBut if you just use them for class toggling and use Tailwind, then you might want to consider replacing them with this.\n\n### What this is\n\nThis is a utility to be used with Tailwind. If you're using Tailwind, you want to consider using this.\n\nThis is a tool to increase Developer Experience. The Tailwind world-class extension still works, even while writing with EasyTailwind. (It doesn't show the whole CSS class generated when using the modifiers, but it shows the important part.)\n\nThis is a tool for cleaner code. You might not agree, but I developed that in mind.\n\nThis is like \"table salt\", salt is good but you don't want to cover everything in it.\n\nIf you have just a couple of classes then there's no need to call it. Call it when you have multiple classes, especially with modifiers or when you need to toggle classes.\n\nGo to: [Table of Contents](#table-of-contents)\n\n## Installation\n\nInstall with your preferred manager:\n\n```bash\nnpm i easy-tailwind\n```\n\n```bash\nyarn add easy-tailwind\n```\n\n```bash\npnpm add easy-tailwind\n```\n\nGo to: [Table of Contents](#table-of-contents)\n\n## Setup\n\nThe configuration file is usually located in the `root` of the application and looks something like `tailwind.config.cjs`. The basic configuration you need is:\n\n```js\n// tailwind.config.cjs\nconst { content } = require('easy-tailwind/transform');\n/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  content,\n  // ...\n  theme: {\n    // ...\n  },\n  plugins: [\n    // ...\n  ],\n};\n```\n\nIf you need to override something in the `content` config:\n\n```js\n// tailwind.config.cjs\nconst { replacer } = require('easy-tailwind/transform');\n/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  content: {\n    files: [\n      '!node_modules',\n      './**/*.{js,ts,jsx,tsx,html,vue,svelte,astro}' // here you can specify your own files and directories\n    ],\n    transform: {\n      DEFAULT: replacer, // default is applied to all files types\n    },\n  }\n  // ...\n  theme: {\n    // ...\n  },\n  plugins: [\n    // ...\n  ],\n};\n```\n\n### Specific configuration for frameworks\n\nRight now, the only specific one is `React`, instead of importing from `easy-tailwind/transform`, import from `easy-tailwind/transform/react`\n\nIn the example for `React`:\n\n```js\n// tailwind.config.cjs\nconst { content } = require('easy-tailwind/transform/react');\n/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  content,\n  // ...\n  theme: {\n    // ...\n  },\n  plugins: [\n    // ...\n  ],\n};\n```\n\nWhere `content` is equivalent to:\n\n```js\n// tailwind.config.cjs\nconst { replacer } = require('easy-tailwind/transform');\n/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  content: {\n    files: ['!node_modules', './**/*.{js,ts,jsx,tsx,html}'],\n    transform: {\n      DEFAULT: replacer,\n    },\n  }\n  // ...\n  theme: {\n    // ...\n  },\n  plugins: [\n    // ...\n  ],\n};\n```\n\nGo to: [Table of Contents](#table-of-contents)\n\n### Renaming the exports\n\nIf you rename the exports to something other than `e` or `etw`, or maybe you want to use only one because you're already using another function with the same name, then you need to change the `replacer` with the `customNameReplacer` (exported from `easy-tailwind/transform`).\n\nExample:\n\n```js\n// tailwind.config.cjs\nconst { customNameReplacer } = require('easy-tailwind/transform');\n/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  content: {\n    files: [\n      '!node_modules',\n      './**/*.{js,ts,jsx,tsx,html,vue,svelte,astro}' // here you can specify your own files and directories\n    ],\n    transform: {\n      DEFAULT: customNameReplacer('newFuncName1', 'newFuncName2'), // default is applied to all files types\n      'some-file-extension': customNameReplacer('etw'), // this one you know you're only using `etw`\n      'some-other-file-extension': customNameReplacer('newFuncName'), // this one you know you're only using `newFuncName'\n    },\n  }\n  // ...\n  theme: {\n    // ...\n  },\n  plugins: [\n    // ...\n  ],\n};\n```\n\nGo to: [Table of Contents](#table-of-contents)\n\n## How to use\n\nFirst, import `e` or `etw`:\n\n```js\nimport { e } from 'easy-tailwind';\n// e and etw resolve to the same function\n```\n\nThis is a pure function, so you can be sure that it will always return the same values as you pass the same values:\n\n```js\ne(\n  'some base classes here',\n  'breaking the line because',\n  'it was getting too long',\n  {\n    mod1: 'classes with mod1',\n    mod2: [\n      'classes with only mod2',\n      {\n        subMod1: 'nested classes with both',\n      },\n    ],\n  },\n);\n// this will return:\n'some base classes here breaking the line because it was getting too long mod1:classes mod1:with mod1:mod1 mod2:classes mod2:with mod2:only mod2:mod2 mod2:subMod1:nested mod2:subMod1:classes mod2:subMod1:with mod2:subMod1:both';\n```\n\nNow use it where you would use the Tailwind classes.\n\nExample below will use the React syntax, but as long as you can call it, it will probably work:\n\n```js\n\u003cdiv\n  className={e(\n    'text-lg font-medium text-black',\n    {\n      hover: 'underline decoration-black',\n      sm: [\n        'text-base text-blue-500',\n        {\n          hover: 'decoration-cyan-500',\n        },\n      ],\n      lg: [\n        'text-2xl text-green-500',\n        {\n          hover: 'decoration-amber-500',\n        },\n      ],\n    }\n  )}\n\u003e\n  EasyTailwind!!!\n\u003c/div\u003e\n```\n\nWhich is way faster and easier to understand, maintain and debug than:\n\n```js\n\"text-lg font-medium text-black hover:underline hover:decoration-black sm:text-base sm:text-blue-500 sm:hover:decoration-cyan-500 lg:text-2xl lg:text-green-500 lg:hover:decoration-amber-500\"\n```\n\n\u003e ℹ️ Sense of style not included. 🤣\n\nGo to: [Table of Contents](#table-of-contents)\n\n### Break lines\n\nOne of the uses is to \"break lines\" of the styles.\n\nFor this, just split the classes into multiple strings and put each one in a single line.\n\nExample:\n\n```js\n\u003cdiv\n  className={e(\n    'text-lg',\n    'font-medium',\n    'text-black',\n    'underline',\n    'decoration-cyan-500'\n  )}\n\u003e\n  Multiple lines!\n\u003c/div\u003e\n```\n\nGo to: [Table of Contents](#table-of-contents)\n\n### Use Objects\n\nThis is what you were waiting for, objects where the keys are applied to whatever the value is, even nested structures.\n\nExample:\n\n```js\n\u003cdiv\n  className={e({\n    sm: \"text-sm text-blue-500\",\n    md: \"text-base text-green-500\",\n    lg: [\"text-lg text-black\", {\n      hover: \"text-red-500\"\n    }],\n  })}\n\u003e\n  Objects!\n\u003c/div\u003e\n```\n\nThis will create exactly what you would expect:\n\n```js\n\"sm:text-sm sm:text-blue-500 md:text-base md:text-green-500 lg:text-lg lg:text-black lg:hover:text-red-500\"\n```\n\nEach key (sm, md, lg, [\u0026_li], group-hover, ...) will apply to everything inside the value.\n\nEach value can be a string, another object, or an array with strings and/or objects.\n\n\u003e ⚠️ The ordering is applied in the order of the object.\n\u003e\n\u003e Some Tailwind classes don't work properly depending on the order,\n\u003e\n\u003e so always check if what you will be building is valid.\n\nGo to: [Table of Contents](#table-of-contents)\n\n### Conditional classes\n\nOne thing we usually need is conditional classes, we got you covered!\n\nAs long as you follow [the rules](#rules-for-it-to-work):\n\n- Use boolean values for conditional expressions (ternary, \u0026\u0026, ||, ??, etc...)\n- Don't add variables other than the boolean for the conditional expressions\n\nExample:\n\n```js\nconst boolean = Math.random() \u003e 0.5;\n// ...\n\u003cdiv\n  className={e(\n    boolean ? 'text-lg' : 'text-base',\n    boolean \u0026\u0026 'font-medium',\n    !boolean \u0026\u0026 'text-black',\n  )}\n\u003e\n  Conditional classes!\n\u003c/div\u003e\n```\n\nGo to: [Table of Contents](#table-of-contents)\n\n## Rules for it to work\n\n1. Use boolean values for conditional expressions (ternary, \u0026\u0026, ||, ??, etc...)\n2. Don't add variables other than the boolean for the conditional expressions\n\nGo to: [Table of Contents](#table-of-contents)\n\n### Known limitations\n\nThose limitations are about the `replacer` in the transform.\n\nRight now, depending on what you try to use in the `e` function, it will not work and you will be given a warning (check the terminal where you're running your `dev` or `build` script).\n\nWhen you're inspecting it in the browser, it will show the classes as normal, but it won't work (the function works as normal, but the transformer won't be able to parse it and the classes won't be added and sent to the browser).\n\nIn those cases, you can append them separately from those you use with `EasyTailwind`.\n\nExamples:\n\n```js\nclassName={`${variable} ${Math.random() \u003e 0.5 ? \"more\" : \"less\"} ${e(\"here goes the safe to parse classes\")}`}\n```\n\n### Why is this necessary?\n\nTailwind works with the JIT compiler that can create new classes on the fly and inject them.\n\nFor it to work, they need to scan all the files looking for the classes, but when you use `EasyTailwind`,\nyou're basically compressing many of the classes you're trying to use. So we need to add an extra step to Tailwind.\n\nIn the Tailwind config (`tailwind.config.cjs`) you add the files it will scan for tailwind classes and a transform that uses a function that will resolve ahead of time what `EasyTailwind` can produce, so Tailwind can inject ahead of time all possible classes.\n\nHowever, the more complicated and inclusive you want it to scan for, the more you lose performance (for running in dev mode and for build).\n\nThe best balance to be able to accept having conditional classes while minimizing the impact on performance is to simplify this, looking for only a boolean variable and not something that can be as simple as a variable or as complex as complex can be.\n\nSee more at [Tailwind \"Transforming source files\"](https://tailwindcss.com/docs/content-configuration#transforming-source-files).\n\nGo to: [Table of Contents](#table-of-contents)\n\n## Does it Support XYZ?\n\nProbably.\n\nIf you can use `e('tw classes')` and it generates the classes (even if they don't actually work), then just follow the setup part.\n\nIf you want me to add a custom `content` for the framework you're using, feel free to open a PR. =D\n\nGo to: [Table of Contents](#table-of-contents)\n\n## Final Considerations\n\nThese are mostly 'pure' functions, so we don't need to worry about getting \"stale\".\n\nMore functionalities are welcome, but ultimately this package can have months between any updates.\nThis doesn't mean that it's \"dead\", just that it's doing what it needs.\n\nToday it works with Tailwind v3, I'm not sure if with lower versions or for higher versions.\nAs long as the `content` part doesn't change, then you can just import and use it.\nIf it changes, you have the `replacer` for the transformations (as long as it supports it) but expect updates as soon as possible.\n\nGo to: [Table of Contents](#table-of-contents)\n\n### Why \"Easy\" Tailwind?\n\nI'm lazy.\n\nAnd while I'm already productive using Tailwind, I don't like to keep repeating the same modifiers over and over again.\n\nSo, it's \"easy\" to type.\n\nAnother thing is about reading the classes. It's easy to get a long string with all classes jumbled together, even with extensions sorting and linting them it's hard to keep in mind everything that's happening at once.\n\nSo, it's \"easy\" to read.\n\nAnd well, naming is hard and I went with the first thing I thought about. =p\n\nGo to: [Table of Contents](#table-of-contents)\n\n## Work with me\n\n\u003chttps://www.linkedin.com/in/noriller/\u003e\n\n### Hit me up at Discord\n\n\u003chttps://discord.gg/XtNPk7HeCa\u003e\n\n### Or Donate\n\n- [$5 Nice job! Keep it up.](https://www.paypal.com/donate/?business=VWNG7KZD9SS4S\u0026no_recurring=0\u0026currency_code=USD\u0026amount=5)\n- [$10 I really liked that, thank you!](https://www.paypal.com/donate/?business=VWNG7KZD9SS4S\u0026no_recurring=0\u0026currency_code=USD\u0026amount=10)\n- [$42 This is exactly what I was looking for.](https://www.paypal.com/donate/?business=VWNG7KZD9SS4S\u0026no_recurring=0\u0026currency_code=USD\u0026amount=42)\n- [$1K WOW. Did not know javascript could do that!](https://www.paypal.com/donate/?business=VWNG7KZD9SS4S\u0026no_recurring=0\u0026currency_code=USD\u0026amount=1000)\n- [$5K I need something done ASAP! Can you do it for yesterday?](https://www.paypal.com/donate/?business=VWNG7KZD9SS4S\u0026no_recurring=0\u0026currency_code=USD\u0026amount=5000)\n- [$10K Please consider this: quit your job and work with me!](https://www.paypal.com/donate/?business=VWNG7KZD9SS4S\u0026no_recurring=0\u0026currency_code=USD\u0026amount=10000)\n- [$??? Shut up and take my money!](https://www.paypal.com/donate/?business=VWNG7KZD9SS4S\u0026no_recurring=0\u0026currency_code=USD)\n\n## That’s it! 👏\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoriller%2Feasy-tailwind","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnoriller%2Feasy-tailwind","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoriller%2Feasy-tailwind/lists"}