├── .gitignore
├── .prettierrc
├── LICENSE
├── README.md
├── package.json
├── src
├── index.d.ts
└── index.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Optional stylelint cache
58 | .stylelintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variable files
76 | .env
77 | .env.development.local
78 | .env.test.local
79 | .env.production.local
80 | .env.local
81 |
82 | # parcel-bundler cache (https://parceljs.org/)
83 | .cache
84 | .parcel-cache
85 |
86 | # Next.js build output
87 | .next
88 | out
89 |
90 | # Nuxt.js build / generate output
91 | .nuxt
92 | dist
93 |
94 | # Gatsby files
95 | .cache/
96 | # Comment in the public line in if your project uses Gatsby and not Next.js
97 | # https://nextjs.org/blog/next-9-1#public-directory-support
98 | # public
99 |
100 | # vuepress build output
101 | .vuepress/dist
102 |
103 | # vuepress v2.x temp and cache directory
104 | .temp
105 | .cache
106 |
107 | # Docusaurus cache and generated files
108 | .docusaurus
109 |
110 | # Serverless directories
111 | .serverless/
112 |
113 | # FuseBox cache
114 | .fusebox/
115 |
116 | # DynamoDB Local files
117 | .dynamodb/
118 |
119 | # TernJS port file
120 | .tern-port
121 |
122 | # Stores VSCode versions used for testing VSCode extensions
123 | .vscode-test
124 |
125 | # yarn v2
126 | .yarn/cache
127 | .yarn/unplugged
128 | .yarn/build-state.yml
129 | .yarn/install-state.gz
130 | .pnp.*
131 |
132 | *.ignored
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "tabWidth": 2,
4 | "printWidth": 100,
5 | "singleQuote": true,
6 | "trailingComma": "es5",
7 | "arrowParens": "avoid"
8 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Soorria Saruva (https://soorria.com)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # tailwindcss-hocus
2 |
3 | Adds `hocus:`, `hocus-within:`, `group-hocus:`, `group-hocus-within:`, `peer-hocus:`, `peer-hocus-within:` for
4 | lazy people like me who use similar styles for `:hover` and `:focus` states.
5 |
6 | Works with Tailwind v4!!
7 |
8 | ## Installation
9 |
10 | > [!Note] If you're using Tailwind v3, you should install `tailwindcss-hocus@0.0.7`
11 |
12 | ```sh
13 | npm i -D tailwindcss-hocus
14 | ```
15 |
16 | ```sh
17 | yarn add -D tailwindcss-hocus
18 | ```
19 |
20 | Add the plugin to your `tailwind.config.js`
21 |
22 | ```js
23 | const hocusPlugin = require('tailwindcss-hocus')
24 |
25 | /**
26 | * @type {import('tailwindcss').Config}
27 | */
28 | module.exports = {
29 | // ...the rest of your config
30 | plugins: [hocusPlugin],
31 | }
32 | ```
33 |
34 | ## Usage
35 |
36 | You can use the variant added by this plugin just like you would `hover:` or `focus:`:
37 |
38 | ```html
39 |
40 | ```
41 |
42 | ```css
43 | .hocus\:text-red-700:hover,
44 | .hocus\:text-red-700:focus {
45 | --tw-text-opacity: 1;
46 | color: rgb(185 28 28 / var(--tw-text-opacity));
47 | }
48 | ```
49 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "tailwindcss-hocus",
3 | "version": "1.0.0",
4 | "main": "src/index.js",
5 | "types": "src/index.d.ts",
6 | "author": "Soorria Saruva (http://soorria.com/)",
7 | "repository": "https://github.com/soorria/tailwindcss-hocus",
8 | "license": "MIT",
9 | "peerDependencies": {
10 | "tailwindcss": "^4.0.0"
11 | }
12 | }
--------------------------------------------------------------------------------
/src/index.d.ts:
--------------------------------------------------------------------------------
1 | import type plugin from 'tailwindcss/plugin'
2 |
3 | declare const hocusPlugin: ReturnType
4 | export = hocusPlugin
5 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const plugin = require('tailwindcss/plugin')
2 |
3 | /**
4 | * Adapted from https://github.com/tailwindlabs/tailwindcss/blob/master/src/featureFlags.js
5 | *
6 | * @param {import('tailwindcss').Config} config
7 | * @param {string} flag
8 | * @returns {boolean}
9 | */
10 | const futureFlagEnabled = (config, flag) => {
11 | return config.future === 'all' || (config?.future?.[flag] ?? false)
12 | }
13 |
14 | /**
15 | * @param {string} modifier
16 | * @param {string} prefix
17 | * @returns {string}
18 | */
19 | const addModifierIfNeeded = (modifier, prefix) => {
20 | return modifier ? `${prefix}\\/${modifier}` : prefix
21 | }
22 |
23 | const hocusPlugin = plugin(({ addVariant, config }) => {
24 | const hoverOnlyWhenSupported = futureFlagEnabled(config(), 'hoverOnlyWhenSupported')
25 | const wrapSelectorForHoverIfNeeded = (/** @type {string} */ selector) =>
26 | hoverOnlyWhenSupported ? `@media (hover: hover) and (pointer: fine) { ${selector} }` : selector
27 |
28 | const hoverSelector = wrapSelectorForHoverIfNeeded('&:hover')
29 |
30 | addVariant('hocus', [hoverSelector, '&:focus'])
31 | addVariant('hocus-within', [hoverSelector, '&:focus-within'])
32 | addVariant('hocus-visible', [hoverSelector, '&:focus-visible'])
33 | })
34 |
35 | module.exports = hocusPlugin
36 |
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 |
--------------------------------------------------------------------------------