From d5a3c905dbe898d63da9b4fec0be6fa1cb2ffe9e Mon Sep 17 00:00:00 2001 From: Ivan Vasilov Date: Fri, 24 Apr 2026 18:29:11 +0200 Subject: [PATCH] chore: swap hit-area JS plugin for v4-native CSS @utility port MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the custom `packages/config/tailwind-plugins/hit-area.js` (v3-style `plugin()` + `matchUtilities`) with the Tailwind v4 port from bazza.dev/craft/2026/hit-area (registry: https://bazza.dev/r/hit-area, MIT). The new version is pure CSS using `@utility hit-area-*` blocks with `--value(number)` / `--value([*])` and integrates cleanly with v4's CSS-first model — no JS plugin, no `matchUtilities` runtime. Changes: - Add `packages/config/tailwind-utilities/hit-area.css` with the vendored `@utility` blocks for `hit-area`, `hit-area-debug`, `hit-area-*`, `hit-area-t/b/l/r-*`, `hit-area-x-*`, `hit-area-y-*`. - Delete `packages/config/tailwind-plugins/hit-area.js`. - Remove `require('./tailwind-plugins/hit-area')` from `packages/config/tailwind.config.js`. - Import the new CSS from every app CSS entry (studio, docs, www, design-system x2, ui-library, learn, lite-studio). Co-Authored-By: Claude Opus 4.7 --- packages/config/tailwind-plugins/hit-area.js | 143 ---------------- .../config/tailwind-utilities/hit-area.css | 155 ++++++++++++++++++ packages/config/tailwind.config.js | 1 - 3 files changed, 155 insertions(+), 144 deletions(-) delete mode 100644 packages/config/tailwind-plugins/hit-area.js create mode 100644 packages/config/tailwind-utilities/hit-area.css diff --git a/packages/config/tailwind-plugins/hit-area.js b/packages/config/tailwind-plugins/hit-area.js deleted file mode 100644 index fd75ddb5bde..00000000000 --- a/packages/config/tailwind-plugins/hit-area.js +++ /dev/null @@ -1,143 +0,0 @@ -const plugin = require('tailwindcss/plugin') - -/** - * Hit Area plugin (Tailwind v3) - * - * Expands the interactive hit area of an element using a ::before pseudo-element, - * without affecting the visual layout. - * - * Usage: - * hit-area — adds invisible ::before that fills the element (0px expansion) - * hit-area-debug — same but with a visible blue dashed border overlay (hover turns green) - * hit-area-{n} — expands all sides by spacing value n (e.g. hit-area-2 → -0.5rem on each side) - * hit-area-x-{n} — expands left + right - * hit-area-y-{n} — expands top + bottom - * hit-area-t-{n} — expands top only - * hit-area-b-{n} — expands bottom only - * hit-area-l-{n} — expands left only - * hit-area-r-{n} — expands right only - * - * Arbitrary values are also supported, e.g. hit-area-[12px], hit-area-x-[1rem]. - */ -module.exports = plugin(function ({ addUtilities, matchUtilities, theme }) { - const beforeBase = { - content: '""', - position: 'absolute', - top: 'var(--hit-area-t, 0px)', - right: 'var(--hit-area-r, 0px)', - bottom: 'var(--hit-area-b, 0px)', - left: 'var(--hit-area-l, 0px)', - pointerEvents: 'inherit', - } - - // Static utilities - addUtilities({ - '.hit-area': { - position: 'relative', - '&::before': { ...beforeBase }, - }, - '.hit-area-debug': { - position: 'relative', - '&::before': { - ...beforeBase, - border: '1px dashed #3b82f6', - backgroundColor: 'rgb(59 130 246 / 0.1)', - }, - '&:hover::before': { - border: '1px dashed #22c55e', - backgroundColor: 'rgb(34 197 94 / 0.1)', - }, - }, - }) - - const spacingConfig = { values: theme('spacing'), supportsNegativeValues: false } - - // hit-area-{value} — all sides - matchUtilities( - { - 'hit-area': (value) => ({ - position: 'relative', - '--hit-area-t': `calc(${value} * -1)`, - '--hit-area-b': `calc(${value} * -1)`, - '--hit-area-l': `calc(${value} * -1)`, - '--hit-area-r': `calc(${value} * -1)`, - '&::before': { ...beforeBase }, - }), - }, - spacingConfig - ) - - // hit-area-x-{value} — left + right - matchUtilities( - { - 'hit-area-x': (value) => ({ - position: 'relative', - '--hit-area-l': `calc(${value} * -1)`, - '--hit-area-r': `calc(${value} * -1)`, - '&::before': { ...beforeBase }, - }), - }, - spacingConfig - ) - - // hit-area-y-{value} — top + bottom - matchUtilities( - { - 'hit-area-y': (value) => ({ - position: 'relative', - '--hit-area-t': `calc(${value} * -1)`, - '--hit-area-b': `calc(${value} * -1)`, - '&::before': { ...beforeBase }, - }), - }, - spacingConfig - ) - - // hit-area-t-{value} — top only - matchUtilities( - { - 'hit-area-t': (value) => ({ - position: 'relative', - '--hit-area-t': `calc(${value} * -1)`, - '&::before': { ...beforeBase }, - }), - }, - spacingConfig - ) - - // hit-area-b-{value} — bottom only - matchUtilities( - { - 'hit-area-b': (value) => ({ - position: 'relative', - '--hit-area-b': `calc(${value} * -1)`, - '&::before': { ...beforeBase }, - }), - }, - spacingConfig - ) - - // hit-area-l-{value} — left only - matchUtilities( - { - 'hit-area-l': (value) => ({ - position: 'relative', - '--hit-area-l': `calc(${value} * -1)`, - '&::before': { ...beforeBase }, - }), - }, - spacingConfig - ) - - // hit-area-r-{value} — right only - matchUtilities( - { - 'hit-area-r': (value) => ({ - position: 'relative', - '--hit-area-r': `calc(${value} * -1)`, - '&::before': { ...beforeBase }, - }), - }, - spacingConfig - ) -}) diff --git a/packages/config/tailwind-utilities/hit-area.css b/packages/config/tailwind-utilities/hit-area.css new file mode 100644 index 00000000000..a717de72b12 --- /dev/null +++ b/packages/config/tailwind-utilities/hit-area.css @@ -0,0 +1,155 @@ +/* + * Tailwind v4 hit-area utilities. + * Vendored from https://bazza.dev/r/hit-area (MIT, Kian Bazarjani). + * + * Expands interactive hit areas without affecting layout by positioning + * a ::before pseudo-element driven by `--hit-area-{t,r,b,l}` custom + * properties. Each utility sets the properties that scope its direction. + */ + +@utility hit-area-debug { + position: relative; + &::before { + content: ''; + position: absolute; + top: var(--hit-area-t, 0px); + right: var(--hit-area-r, 0px); + bottom: var(--hit-area-b, 0px); + left: var(--hit-area-l, 0px); + pointer-events: inherit; + @apply border border-dashed border-blue-500; + @apply bg-blue-500/10; + } + &:hover::before { + @apply border border-dashed border-green-500; + @apply bg-green-500/10; + } +} + +@utility hit-area { + position: relative; + &::before { + content: ''; + position: absolute; + top: var(--hit-area-t, 0px); + right: var(--hit-area-r, 0px); + bottom: var(--hit-area-b, 0px); + left: var(--hit-area-l, 0px); + pointer-events: inherit; + } +} + +@utility hit-area-* { + position: relative; + --hit-area-t: --spacing(--value(number) * -1); + --hit-area-t: calc(--value([*]) * -1); + --hit-area-b: --spacing(--value(number) * -1); + --hit-area-b: calc(--value([*]) * -1); + --hit-area-l: --spacing(--value(number) * -1); + --hit-area-l: calc(--value([*]) * -1); + --hit-area-r: --spacing(--value(number) * -1); + --hit-area-r: calc(--value([*]) * -1); + &::before { + content: ''; + position: absolute; + top: var(--hit-area-t, 0px); + right: var(--hit-area-r, 0px); + bottom: var(--hit-area-b, 0px); + left: var(--hit-area-l, 0px); + pointer-events: inherit; + } +} + +@utility hit-area-t-* { + position: relative; + --hit-area-t: --spacing(--value(number) * -1); + --hit-area-t: calc(--value([*]) * -1); + &::before { + content: ''; + position: absolute; + top: var(--hit-area-t, 0px); + right: var(--hit-area-r, 0px); + bottom: var(--hit-area-b, 0px); + left: var(--hit-area-l, 0px); + pointer-events: inherit; + } +} + +@utility hit-area-b-* { + position: relative; + --hit-area-b: --spacing(--value(number) * -1); + --hit-area-b: calc(--value([*]) * -1); + &::before { + content: ''; + position: absolute; + top: var(--hit-area-t, 0px); + right: var(--hit-area-r, 0px); + bottom: var(--hit-area-b, 0px); + left: var(--hit-area-l, 0px); + pointer-events: inherit; + } +} + +@utility hit-area-l-* { + position: relative; + --hit-area-l: --spacing(--value(number) * -1); + --hit-area-l: calc(--value([*]) * -1); + &::before { + content: ''; + position: absolute; + top: var(--hit-area-t, 0px); + right: var(--hit-area-r, 0px); + bottom: var(--hit-area-b, 0px); + left: var(--hit-area-l, 0px); + pointer-events: inherit; + } +} + +@utility hit-area-r-* { + position: relative; + --hit-area-r: --spacing(--value(number) * -1); + --hit-area-r: calc(--value([*]) * -1); + &::before { + content: ''; + position: absolute; + top: var(--hit-area-t, 0px); + right: var(--hit-area-r, 0px); + bottom: var(--hit-area-b, 0px); + left: var(--hit-area-l, 0px); + pointer-events: inherit; + } +} + +@utility hit-area-x-* { + position: relative; + --hit-area-l: --spacing(--value(number) * -1); + --hit-area-l: calc(--value([*]) * -1); + --hit-area-r: --spacing(--value(number) * -1); + --hit-area-r: calc(--value([*]) * -1); + &::before { + content: ''; + position: absolute; + top: var(--hit-area-t, 0px); + right: var(--hit-area-r, 0px); + bottom: var(--hit-area-b, 0px); + left: var(--hit-area-l, 0px); + pointer-events: inherit; + } +} + +@utility hit-area-y-* { + position: relative; + --hit-area-t: --spacing(--value(number) * -1); + --hit-area-t: calc(--value([*]) * -1); + --hit-area-b: --spacing(--value(number) * -1); + --hit-area-b: calc(--value([*]) * -1); + &::before { + content: ''; + position: absolute; + top: var(--hit-area-t, 0px); + right: var(--hit-area-r, 0px); + bottom: var(--hit-area-b, 0px); + left: var(--hit-area-l, 0px); + pointer-events: inherit; + } +} diff --git a/packages/config/tailwind.config.js b/packages/config/tailwind.config.js index 8f93682c240..2a87acd71eb 100644 --- a/packages/config/tailwind.config.js +++ b/packages/config/tailwind.config.js @@ -429,7 +429,6 @@ const uiConfig = ui({ plugins: [ require('@tailwindcss/typography'), require('tailwindcss-animate'), - require('./tailwind-plugins/hit-area'), require('./tailwind-plugins/motion-safe-transition'), function ({ addVariant }) { addVariant('not-disabled', '&:not(:disabled)')