{"_id":"@basilafro/fluid-clamp","_rev":"3-de58c514b929545a11a50c047283b3c9","name":"@basilafro/fluid-clamp","dist-tags":{"latest":"2.2.0"},"versions":{"1.0.0":{"name":"@basilafro/fluid-clamp","version":"1.0.0","keywords":["tailwindcss","tailwind-plugin","fluid","clamp","container-queries","cqw","responsive"],"license":"MIT","_id":"@basilafro/fluid-clamp@1.0.0","maintainers":[{"name":"basilafro","email":"danielmarin.dev@gmail.com"}],"dist":{"shasum":"34919146718935646cc02d3ddb309d7972eaa7c7","tarball":"https://registry.npmjs.org/@basilafro/fluid-clamp/-/fluid-clamp-1.0.0.tgz","fileCount":20,"integrity":"sha512-9s4Ke+1XjzCvbrzfEjXqCgtbPj3mPgpgonjg2ss8PnROxqXBD7cUeBl9+I+mch42q+htRMSaBOoQv67Fxh7ztA==","signatures":[{"sig":"MEUCIGEcd3fJAWOoVEAYX32OneaREg8SAa06KE+LE3vPsdG0AiEA2X27uqrw7fA8taqZqG6poddzT/um1jMw4mNOmCWlKho=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":42232},"main":"dist/index.js","types":"dist/index.d.ts","scripts":{"dev":"tsc --watch","build":"tsc"},"_npmUser":{"name":"basilafro","email":"danielmarin.dev@gmail.com"},"description":"Tailwind CSS plugin for fluid clamp utilities using cqw, cqh, and vw","directories":{},"_nodeVersion":"26.0.0","dependencies":{"@basilafro/fluid-clamp":"link:"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.0.0","tailwindcss":"^3.4.0"},"peerDependencies":{"tailwindcss":">=3.0.0"},"_npmOperationalInternal":{"tmp":"tmp/fluid-clamp_1.0.0_1778736957548_0.2712809645819221","host":"s3://npm-registry-packages-npm-production"}},"2.1.0":{"name":"@basilafro/fluid-clamp","version":"2.1.0","keywords":["tailwindcss","tailwind-plugin","fluid","clamp","container-queries","cqw","responsive"],"license":"MIT","_id":"@basilafro/fluid-clamp@2.1.0","maintainers":[{"name":"basilafro","email":"danielmarin.dev@gmail.com"}],"homepage":"https://github.com/BasilAfro/fluid-clamp#readme","bugs":{"url":"https://github.com/BasilAfro/fluid-clamp/issues"},"dist":{"shasum":"d789f541f811572d26fcba4f6b3056c371f37450","tarball":"https://registry.npmjs.org/@basilafro/fluid-clamp/-/fluid-clamp-2.1.0.tgz","fileCount":24,"integrity":"sha512-U7lbJzh5hEL9b5cpHXN1nGqmXislFWGQPJXJEEg7wVWuDshBu7ytvZeJCZ3rWS+CPtwBSktipbjlEXHHNnUxkw==","signatures":[{"sig":"MEYCIQCH2X8RA7GqGHTv/q9odvBeFDqGryIVLIC69sPkGSRi8QIhAM1cKEYwQ5LHUSYlrBJA5DSVlg9eGtscVVmUEDLMbyXY","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":86258},"main":"dist/index.js","_from":"file:basilafro-fluid-clamp-2.1.0.tgz","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./package.json":"./package.json"},"scripts":{"dev":"tsc --watch","test":"vitest run","build":"tsc","test:watch":"vitest"},"_npmUser":{"name":"basilafro","email":"danielmarin.dev@gmail.com"},"_resolved":"/private/var/folders/8s/5tc3tbgd415c11m3qb5lwxsx34q9hg/T/fd7f97bc2f6ad5278515eae991594683/basilafro-fluid-clamp-2.1.0.tgz","_integrity":"sha512-U7lbJzh5hEL9b5cpHXN1nGqmXislFWGQPJXJEEg7wVWuDshBu7ytvZeJCZ3rWS+CPtwBSktipbjlEXHHNnUxkw==","repository":{"url":"git+https://github.com/BasilAfro/fluid-clamp.git","type":"git"},"_npmVersion":"10.9.2","description":"Tailwind CSS plugin for fluid clamp utilities using cqw, cqh, and vw","directories":{},"_nodeVersion":"22.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.0.0","postcss":"^8.4.0","typescript":"^5.0.0","tailwindcss":"^3.4.0","tailwindcss4":"npm:tailwindcss@^4.3.2","@tailwindcss/oxide":"^4.3.2"},"peerDependencies":{"tailwindcss":">=3.0.0"},"_npmOperationalInternal":{"tmp":"tmp/fluid-clamp_2.1.0_1785211108240_0.7229096774755546","host":"s3://npm-registry-packages-npm-production"}},"2.2.0":{"name":"@basilafro/fluid-clamp","version":"2.2.0","description":"Tailwind CSS plugin for fluid clamp utilities using cqw, cqh, and vw","author":"BasilAfro","repository":{"type":"git","url":"git+https://github.com/BasilAfro/fluid-clamp.git"},"homepage":"https://github.com/BasilAfro/fluid-clamp#readme","bugs":{"url":"https://github.com/BasilAfro/fluid-clamp/issues"},"main":"dist/cjs/index.js","types":"dist/types/index.d.ts","sideEffects":false,"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"},"./tw-merge":{"types":"./dist/types/tw-merge.d.ts","import":"./dist/esm/tw-merge.js","require":"./dist/cjs/tw-merge.js"},"./package.json":"./package.json"},"engines":{"node":">=18"},"peerDependencies":{"clsx":"^2.0.0","tailwind-merge":"^2.0.0 || ^3.0.0","tailwindcss":">=3.4.0 <5.0.0"},"peerDependenciesMeta":{"clsx":{"optional":true},"tailwind-merge":{"optional":true}},"devDependencies":{"@tailwindcss/oxide":"^4.3.2","clsx":"^2.1.1","postcss":"^8.4.0","tailwind-merge":"^3.6.0","tailwindcss":"^3.4.0","tailwindcss4":"npm:tailwindcss@^4.3.2","typescript":"^5.0.0","vitest":"^2.0.0"},"keywords":["tailwindcss","tailwind-plugin","fluid","clamp","container-queries","cqw","responsive"],"license":"MIT","publishConfig":{"access":"public"},"scripts":{"build":"tsc -p tsconfig.cjs.json && tsc -p tsconfig.esm.json && node scripts/stamp-dist-package-json.mjs","dev":"tsc -p tsconfig.esm.json --watch","test":"vitest run","test:watch":"vitest"},"_nodeVersion":"26.7.0","_id":"@basilafro/fluid-clamp@2.2.0","dist":{"integrity":"sha512-rbftaf5EQ7pNy8NqUe9AHOUXbhEAfZIrJlI5mWTc/yBhvR8NVVc/+SPshsxe8kYilVugFe3Tcb7Y9qRMJobaQg==","shasum":"1a94b3a48305157b63443d8af5b89cd6507ef76f","tarball":"https://registry.npmjs.org/@basilafro/fluid-clamp/-/fluid-clamp-2.2.0.tgz","fileCount":54,"unpackedSize":269182,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDF2bDoZJ4+mVUvKlb6Y8EMyT65ypj2ygkBW3299kmvuAIhAP8y5g/2zMK8HKvzY/8rmAaWh141vzZrH2ULLqxHFyOb"}]},"_npmUser":{"name":"basilafro","email":"danielmarin.dev@gmail.com"},"directories":{},"maintainers":[{"name":"basilafro","email":"danielmarin.dev@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/fluid-clamp_2.2.0_1787635449450_0.07080135297491541"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-14T05:35:57.392Z","modified":"2026-08-25T05:24:09.739Z","1.0.0":"2026-05-14T05:35:57.682Z","2.1.0":"2026-07-28T03:58:28.383Z","2.2.0":"2026-08-25T05:24:09.585Z"},"bugs":{"url":"https://github.com/BasilAfro/fluid-clamp/issues"},"license":"MIT","homepage":"https://github.com/BasilAfro/fluid-clamp#readme","keywords":["tailwindcss","tailwind-plugin","fluid","clamp","container-queries","cqw","responsive"],"repository":{"type":"git","url":"git+https://github.com/BasilAfro/fluid-clamp.git"},"description":"Tailwind CSS plugin for fluid clamp utilities using cqw, cqh, and vw","maintainers":[{"name":"basilafro","email":"danielmarin.dev@gmail.com"}],"readme":"# @basilafro/fluid-clamp\n\nTailwind CSS plugin for fluid `clamp()` utilities using `cqw`, `cqh`, and `vw`.\nWorks with Tailwind CSS **v3** (JS config) and **v4** (CSS-first `@plugin`).\n\nGenerates fluid type and spacing classes that scale smoothly between a minimum\nand maximum size across a container or viewport range.\n\n---\n\n## Install\n\n```bash\npnpm add @basilafro/fluid-clamp\n```\n\n---\n\n## Setup — Tailwind v4 (CSS-first)\n\nRegister the plugin in your CSS with `@plugin`:\n\n```css\n/* app.css */\n@import \"tailwindcss\";\n@plugin \"@basilafro/fluid-clamp\";\n```\n\nOptions go in a block. `@plugin` blocks only carry flat key/value pairs, so the\nbreakpoint ranges are spelled out as two keys (this is the flat form of\n`breakpointRange` below):\n\n```css\n@plugin \"@basilafro/fluid-clamp\" {\n  minBreakpoint: 320; /* px number or breakpoint name, e.g. sm */\n  maxBreakpoint: 1280;\n  unit: vw; /* default — see \"Fluid unit selection\" below */\n}\n```\n\nFlat option keys: `minBreakpoint`, `maxBreakpoint`, `unit`, `lengthUnit`,\n`rootFontSize`, `cssApi`, plus the per-target overrides `textMinBreakpoint`,\n`textMaxBreakpoint`, `spaceMinBreakpoint`, `spaceMaxBreakpoint`, `textUnit`,\n`spaceUnit`. They map 1:1 onto the config options table below. (`breakpoints`\nand `fluidVars` are absent by necessity — they need nested values, which\n`@plugin` blocks can't carry.)\n\nNamed breakpoints come straight from your `@theme` — every `--breakpoint-*`\nvariable is usable in anchors and options, no plugin config needed:\n\n```css\n@theme {\n  --breakpoint-xs: 30rem; /* usable as text-fluid-[15@xs,32@lg] */\n}\n```\n\n> Need the nested config or the `breakpoints` override map? Load a JS config\n> with `@config \"./tailwind.config.ts\"` and register `createFluidPlugin({ … })`\n> there — same as the v3 setup below.\n\nThe default export also works from a JS config (v3 or v4), taking the same flat\nkeys — plus the nested ones the CSS form can't express (`breakpoints`,\n`fluidVars`, `breakpointRange`, …):\n\n```ts\nplugins: [fluidClampPlugin({ minBreakpoint: 320, maxBreakpoint: 1280 })];\n```\n\n> **Using it from a Tailwind v3 config?** Pass `cssApi: \"v3\"`. The default\n> export assumes `\"v4\"` (it's the CSS-first entry point), and on v3 that makes\n> the [composite utilities](#config-options) emit v4's internal variables, so\n> they silently stop composing with native `rotate-*`/`ring-*`. Everything else\n> is unaffected. `createFluidPlugin` already defaults to `\"v3\"`, so on a v3\n> project it's the simpler choice.\n\n---\n\n## Setup — Tailwind v3 (JS config)\n\n### 1. Register the plugin\n\n```ts\n// tailwind.config.ts\nimport { createFluidPlugin } from \"@basilafro/fluid-clamp\";\n\nexport default {\n  plugins: [\n    createFluidPlugin({\n      breakpointRange: { minBreakpoint: 320, maxBreakpoint: 1280 }, // viewport range to scale across\n      unit: \"vw\", // default — see \"Fluid unit selection\" below\n    }),\n  ],\n};\n```\n\n> `breakpointRange` and `unit` apply to both text and spacing. Override just one\n> with `textBreakpointRange`/`spaceBreakpointRange` or `textUnit`/`spaceUnit`\n> (see Config options).\n\n### 2. (Only for `cqw`/`cqh`) set container-type\n\nThe default unit is `vw`, which is relative to the viewport and needs **no\nsetup**. You only need `container-type` if you opt into container units\n(`cqw`/`cqh`) — either as the config default or per-class with a unit token.\n\n```css\n/* required only when using cqw */\n.page-container {\n  max-width: 1280px;\n  margin-inline: auto;\n  container-type: inline-size;\n}\n```\n\n---\n\n## Config options\n\n| Option                 | Type                                   | Default                | Description                                                  |\n| ---------------------- | -------------------------------------- | ---------------------- | ------------------------------------------------------------ |\n| `breakpointRange`      | `{ minBreakpoint, maxBreakpoint }`     | `{ 320, 1280 }`        | Breakpoint range for **all** fluid utilities                 |\n| `unit`                 | `\"vw\" \\| \"cqw\" \\| \"cqh\"`               | `\"vw\"`                 | Default fluid unit for **all** utilities (overridable per-class) |\n| `breakpoints`          | `Record<string, number>`               | `{}`                   | Extra/override named breakpoints for arbitrary values (px)   |\n| `lengthUnit`           | `\"rem\" \\| \"px\"`                        | `\"rem\"`                | Unit for the generated min/max/intercept lengths (not the fluid unit) |\n| `rootFontSize`         | `number`                               | `16`                   | Root font size (px) for px→rem conversion; only affects `rem` output |\n| `textBreakpointRange`  | `{ minBreakpoint, maxBreakpoint }`     | `breakpointRange`      | Override the breakpoint range for `text-fluid-*` only        |\n| `spaceBreakpointRange` | `{ minBreakpoint, maxBreakpoint }`     | `breakpointRange`      | Override the breakpoint range for spacing utilities only     |\n| `textUnit`             | `\"vw\" \\| \"cqw\" \\| \"cqh\"`               | `unit`                 | Override fluid unit for text only                            |\n| `spaceUnit`            | `\"vw\" \\| \"cqw\" \\| \"cqh\"`               | `unit`                 | Override fluid unit for spacing only                         |\n| `cssApi`               | `\"v3\" \\| \"v4\"`                         | see below              | Which Tailwind major version's formula to use for composite utilities |\n| `fluidVars`            | `Record<string, string>`               | `{}`                   | Fluid CSS custom properties (`:root` overrides) — see below   |\n\nMost projects only need `breakpointRange` and `unit`. The four `text*`/`space*` keys are\nescape hatches for the rarer case where text and spacing scale differently\n(e.g. text against the viewport, spacing against a component container).\n\n`cssApi` only affects the [composite utilities](#arbitrary-only-utilities-no-static-scale-yet)\n(`translate-x/y`, `blur`, `backdrop-blur`, `ring`, `ring-offset`, `space-x/y`,\n`divide-x/y`) — it defaults to `\"v3\"` from `createFluidPlugin` and `\"v4\"` from\nthe default `@plugin`/CSS-first export, matching each entry point's usual\nTailwind version. Override it explicitly if that doesn't hold for your setup —\nfor example, a Tailwind v4 project that still runs plugins through v4's legacy\nJS-config compat mode, where other utilities on the page may still compose the\nv3 way even though npm has v4 installed:\n\n```ts\n// Tailwind v4 project using the legacy JS-config compat path\ncreateFluidPlugin({ cssApi: \"v3\" }); // instead of the \"v4\" you might expect\n```\n\n```css\n/* Tailwind v3 project loading the CSS-first entry via a compat shim */\n@plugin \"@basilafro/fluid-clamp\" {\n  cssApi: v3;\n}\n```\n\nPicking the wrong `cssApi` doesn't break the plain-property utilities (`p-fluid-*`,\n`border-fluid-*`, etc.) — only the composite ones, which would then use the\nwrong internal variable names and stop composing with Tailwind's own\n`rotate-*`/`scale-*`, other filter utilities, `ring-color`, etc. on the same\nelement.\n\n`minBreakpoint`/`maxBreakpoint` (in `breakpointRange`, `textBreakpointRange`,\n`spaceBreakpointRange`) accept either a px number or a **breakpoint name** — a\nTailwind `theme.screens` entry or a name from the `breakpoints` option:\n\n```ts\ncreateFluidPlugin({\n  breakpoints: { xs: 480 }, // adds a name not in theme.screens\n  breakpointRange: { minBreakpoint: \"xs\", maxBreakpoint: \"lg\" }, // scale across xs(480) → lg(1024)\n});\n```\n\nAn unknown name throws a clear config error at build time.\n\n### `fluidVars` — fluid CSS custom properties\n\n`fluidVars` emits `:root` overrides instead of utility classes — handy for\noverriding Tailwind's own scale variables (`--text-xs`, `--text-sm`, …) or any\nglobal design token across breakpoints, without needing an element to carry a\nclass:\n\n```ts\ncreateFluidPlugin({\n  fluidVars: {\n    \"text-xs\": \"10@390,11@768,12@1280\",\n    \"text-sm\": \"11@390,12@768,14@1280\",\n  },\n});\n```\n\n```css\n/* generated */\n:root {\n  --text-xs: clamp(0.625rem, 0.26455vw + 0.560516rem, 0.6875rem);\n}\n@media (min-width: 768px) {\n  :root {\n    --text-xs: clamp(0.6875rem, 0.195313vw + 0.59375rem, 0.75rem);\n  }\n}\n```\n\nEach key becomes `--${key}`; each value is parsed with the **exact same\narbitrary-value grammar** as `text-fluid-[...]` (minus the brackets) —\nshorthand, anchors, named breakpoints, insets, the unit token, and bound\nmarkers all work identically, including [piecewise ramps](#piecewise-ramps--3-anchors)\nfor 3+ anchors (shown above: a base declaration plus one `@media` override per\nextra anchor). Values are resolved against the same `textUnit`/\n`textBreakpointRange` as `text-fluid-*`, so a shorthand value like `\"10,12\"`\nscales across `textBreakpointRange`.\n\nAn unparsable value throws a clear config error at build time, the same way\nan unknown breakpoint name does.\n\n> `@plugin` blocks only carry flat key/value pairs, so `fluidVars` (like\n> `breakpoints`) is JS-config-only — load one via `@config \"./tailwind.config.ts\"`\n> and register `createFluidPlugin({ fluidVars: { ... } })` there.\n\n---\n\n## Classes\n\n### Type scale\n\n| Class             | Min  | Max  |\n| ----------------- | ---- | ---- |\n| `text-fluid-2xs`  | 10px | 12px |\n| `text-fluid-xs`   | 12px | 14px |\n| `text-fluid-sm`   | 14px | 16px |\n| `text-fluid-base` | 16px | 18px |\n| `text-fluid-md`   | 18px | 22px |\n| `text-fluid-lg`   | 20px | 28px |\n| `text-fluid-xl`   | 24px | 36px |\n| `text-fluid-2xl`  | 32px | 48px |\n| `text-fluid-3xl`  | 40px | 64px |\n| `text-fluid-4xl`  | 48px | 80px |\n\n### Space scale\n\nPrefixes: `p`, `px`, `py`, `pt`, `pr`, `pb`, `pl`, `m`, `mx`, `my`, `mt`, `mr`, `mb`, `ml`,\n`gap`, `gap-x`, `gap-y`, `w`, `h`, `min-w`, `max-w`, `min-h`, `max-h`, `size`,\n`top`, `right`, `bottom`, `left`, `inset`, `inset-x`, `inset-y`, `start`, `end`,\n`basis`, `scroll-m`, `scroll-mx`, `scroll-my`, `scroll-mt`, `scroll-mr`, `scroll-mb`,\n`scroll-ml`, `scroll-p`, `scroll-px`, `scroll-py`, `scroll-pt`, `scroll-pr`,\n`scroll-pb`, `scroll-pl`\n\nSteps: `1 2 3 4 6 8 10 12 16 20 24`\n\nExample: `p-fluid-4`, `gap-fluid-6`, `w-fluid-12`, `inset-fluid-4`, `size-fluid-8`\n\n`start`/`end` are the logical (RTL-aware) equivalents of `left`/`right` —\n`inset-inline-start`/`inset-inline-end`. `size` sets `width` and `height`\ntogether from the same clamp value.\n\n### Arbitrary-only utilities (no static scale yet)\n\nThese utilities only support the [arbitrary-value syntax](#arbitrary-values)\nbelow (e.g. `border-fluid-[1,4]`) — their px ranges vary too much from the\nspace scale above to reuse it, so v1 ships arbitrary values only and leaves a\ncurated default scale for a future release.\n\n`perspective-fluid-*` is only registered under Tailwind v4 (i.e. `cssApi: \"v4\"`,\nthe default for the CSS-first `@plugin` entry point) — Tailwind v3 never\nshipped a native `perspective` utility, so this plugin doesn't invent one for it.\n\n| Category                | Prefixes                                                                                          | CSS property                                                    |\n| ------------------------ | -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |\n| Typography               | `leading`, `tracking`, `indent`                                                                    | `line-height`, `letter-spacing`, `text-indent`                    |\n| Borders / outline        | `border`, `border-t`, `border-r`, `border-b`, `border-l`, `outline`, `outline-offset`               | `border(-*)-width`, `outline-width`, `outline-offset`             |\n| Border radius             | `rounded`, `rounded-t/r/b/l`, `rounded-tl/tr/br/bl`                                                 | `border-radius` (whole or per-corner)                             |\n| Perspective (v4 only)     | `perspective`                                                                                       | `perspective`                                                     |\n| Transform (composite)    | `translate-x`, `translate-y`                                                                        | `translate` (v4) / `transform` (v3), via `--tw-translate-x/y`     |\n| Filters (composite)      | `blur`, `backdrop-blur`                                                                             | `filter` / `backdrop-filter`, via `--tw-blur`/`--tw-backdrop-blur` |\n| Ring (composite)         | `ring`, `ring-offset`                                                                                | `box-shadow`, via the same `--tw-ring-*` variables Tailwind uses  |\n| Spacing between children (composite) | `space-x`, `space-y`, `divide-x`, `divide-y`                                              | margin/border-width on a child selector — `:where(& > :not(:last-child))` (v4) / `> :not([hidden]) ~ :not([hidden])` (v3) |\n\nExample: `border-fluid-[1,4]`, `rounded-tl-fluid-[4,12]`, `leading-fluid-[16,24]`,\n`translate-x-fluid-[8,24]`, `space-x-fluid-[8,16]`.\n\nThe **composite** utilities above don't set a plain CSS property — they write\nto the same internal CSS variables Tailwind's own `translate-*`/`rotate-*`/\n`scale-*`, `blur-*`/other filter utilities, `ring-*`, and `space-x/y`/\n`divide-x/y` utilities use, so they compose correctly with those native\nutilities on the same element (e.g. `translate-x-fluid-[8,24]` and `rotate-45`\nboth apply). The exact formula is resolved automatically for Tailwind v3 vs\nv4, since the two versions compose these differently under the hood.\n\n---\n\n## Arbitrary values\n\nFor one-off values outside the scale, use bracket syntax directly in your JSX.\nAll numbers are in `px` (the `px` suffix is optional). The same syntax works on\nevery utility: `text-fluid-[...]`, `p-fluid-[...]`, `w-fluid-[...]`, etc.\n\nTokens are separated by a **comma**. (An underscore — Tailwind's space escape —\nalso works, so `text-fluid-[16@320_24@1280]` is accepted as well.)\n\n### Shorthand — two sizes\n\nScales between two sizes across the **configured** breakpoints. The first number\nis the size at the min breakpoint, the second at the max:\n\n```tsx\n<p className=\"text-fluid-[13,19]\" />; // 13px → 19px (grows)\n<p className=\"text-fluid-[19,13]\" />; // 19px → 13px (shrinks as the viewport grows)\n```\n\nPut the larger size first to **shrink** as the breakpoint grows; two equal sizes\njust emit that constant value (`text-fluid-[16,16]` → `1rem`). The same holds for\nanchors below.\n\n### Negative values\n\nMargins, insets, scroll-margins and translates come in negative form on the\nstatic scale, exactly as they do in Tailwind:\n\n```tsx\n<div className=\"-mt-fluid-4\" />;  {/* margin-top: clamp(-1.5rem, -0.833333vw - 0.833333rem, -1rem) */}\n```\n\nFor arbitrary values, put the signs **inside** the bracket:\n\n```tsx\n<div className=\"mt-fluid-[-8,-16]\" />;   {/* ✅ */}\n<div className=\"-mt-fluid-[8,16]\" />;    {/* ❌ produces nothing */}\n```\n\nThe `-` prefix doesn't work on arbitrary values: Tailwind rejects a negative\ncandidate whose value contains a comma before this plugin's matcher ever runs,\nand the bracket grammar is comma-based. Both forms are equally expressive —\n`mt-fluid-[-8,-16]` is simply where the sign has to go.\n\nOnly the prefixes Tailwind itself makes negatable get a negative form\n(`m*`, `inset*`/`top`/`right`/`bottom`/`left`/`start`/`end`, `translate-x/y`,\n`scroll-m*`). `-p-fluid-4` doesn't exist, the same way `-p-4` doesn't.\n\n### Anchors — `size@breakpoint`\n\nPin a size to an explicit breakpoint with `size@breakpoint`. Order doesn't matter.\n\n```tsx\n{\n  /* 16px at 320, 24px at 1280 */\n}\n<p className=\"text-fluid-[16@320,24@1280]\" />;\n\n{\n  /* Spacing works the same way */\n}\n<div className=\"p-fluid-[8@320,16@1280]\" />;\n```\n\nThe breakpoint can be a **name** — a Tailwind screen (`sm`, `md`, `lg`, `xl`,\n`2xl`, plus custom ones — `theme.screens` in v3, `--breakpoint-*` theme\nvariables in v4), or a name from the `breakpoints` config.\nNames may contain hyphens (e.g. `tablet-portrait`); a registered name is matched\nin full before any trailing `-N` is read as an inset:\n\n```tsx\n<p className=\"text-fluid-[16@sm,24@lg]\" />;\n```\n\n```ts\ncreateFluidPlugin({\n  breakpoints: { xs: 480 }, // now usable as text-fluid-[16@xs,24@lg]\n});\n```\n\n#### Inset\n\nAppend `-N` to a breakpoint to subtract `N` px from it — handy for accounting\nfor container padding or fixed sibling elements. It's subtracted **directly**\n(no doubling):\n\n```tsx\n{\n  /* effective range 304 → 1256 (320−16, 1280−24) */\n}\n<p className=\"text-fluid-[16@320-16,24@1280-24]\" />;\n```\n\n### Piecewise ramps — 3+ anchors\n\nAdd more anchors to ramp through multiple slopes instead of a single clamp —\nhandy for a type/spacing scale that should grow faster after a given\nbreakpoint (e.g. a headline that barely grows on mobile, then accelerates from\ntablet up). Order doesn't matter; anchors are sorted by breakpoint internally:\n\n```tsx\n<h1 className=\"text-fluid-[24@390,28@640,42@768,48@1024]\" />\n```\n\nThis compiles to **one class** with a base `clamp()` plus a stacked\n`@media (min-width: …)` override per extra anchor — each pair of consecutive\nanchors gets its own two-point clamp, valid from its lower anchor's breakpoint\nup:\n\n```css\n.text-fluid-\\[24\\@390\\2c 28\\@640\\2c 42\\@768\\2c 48\\@1024\\] {\n  font-size: clamp(1.5rem, 1.6vw + 1.11rem, 1.75rem); /* 24→28px, 390 → 640 */\n}\n@media (min-width: 640px) {\n  .text-fluid-\\[24\\@390\\2c 28\\@640\\2c 42\\@768\\2c 48\\@1024\\] {\n    font-size: clamp(1.75rem, 10.9375vw - 2.625rem, 2.625rem); /* 28→42px, 640 → 768 */\n  }\n}\n@media (min-width: 768px) {\n  .text-fluid-\\[24\\@390\\2c 28\\@640\\2c 42\\@768\\2c 48\\@1024\\] {\n    font-size: clamp(2.625rem, 2.34375vw + 1.5rem, 3rem); /* 42→48px, 768 → 1024 */\n  }\n}\n```\n\nSizes are written in px but emitted in `rem` (the `lengthUnit` default, which\nrespects the reader's browser font-size preference) — 24px → `1.5rem` at the\ndefault `rootFontSize` of 16. Pass `lengthUnit: \"px\"` to get px out.\n\nWorks with named breakpoints, insets, the unit token, and every other\n`*-fluid-[...]` prefix (spacing, typography, border, composite) — it's the\nsame anchor syntax, just with more than two anchors. Bound markers (`<`/`>`)\nstill apply to the true outer ends only — `<` opens the floor of the first\nsegment, `>` the ceiling of the last one; interior segments stay fully clamped\nsince they're bounded by real anchors on both sides:\n\n```tsx\n<h1 className=\"text-fluid-[<24@390,28@640,42@768,48@1024>]\" />\n```\n\n> Want a reusable named utility (`text-h1`, `text-display-1`, …) instead of\n> repeating the bracket value? Define it as your own `@utility` (v4) or\n> `@layer components` (v3) rule composed with `@apply`:\n> `@utility text-h1 { @apply text-fluid-[24@390,28@640,42@768,48@1024]; }`\n\n### Breaking the bounds\n\nBy default the value is clamped at both ends. To let it keep scaling along the\n**same slope** past a breakpoint, open that bound with an edge marker. The markers\nare **positional** — they open the breakpoint end they sit next to: a leading `<`\nopens the **min-breakpoint** end, a trailing `>` opens the **max-breakpoint** end.\nWorks on both shorthand and anchors, and composes with the unit token and insets.\n\n```tsx\n<p className=\"text-fluid-[16@320,24@1280]\" />;   {/* clamp() — bounded both ends (default) */}\n<p className=\"text-fluid-[16@320,24@1280>]\" />;  {/* opens the 1280 end — keeps growing past 24px */}\n<p className=\"text-fluid-[<16@320,24@1280]\" />;  {/* opens the 320 end — keeps shrinking below 16px */}\n<p className=\"text-fluid-[<16@320,24@1280>]\" />; {/* calc() — fully linear, unbounded */}\n```\n\nFor a **growing** scale (the common case) opening the max-breakpoint end emits\n`max(floor, …)` and opening the min-breakpoint end emits `min(ceiling, …)`.\nBecause the markers track the breakpoint end — not a fixed size bound — a\n**shrinking** scale (larger size first) flips which CSS function you get: there\nthe smaller size sits at the max breakpoint, so `>` opens the floor (`min(…)`) and\n`<` opens the ceiling (`max(…)`). Either way, the end you mark keeps extrapolating\nand the other end stays clamped; opening both yields a bare `calc(…)`.\n\n### Fluid unit selection\n\nThe unit (`vw`, `cqw`, or `cqh`) is chosen automatically, with this precedence:\n\n1. **Inline unit token** — a leading `vw`/`cqw`/`cqh` always wins (explicit opt-in).\n2. **Named breakpoint → `vw`** — a named breakpoint is a viewport screen, so it\n   selects `vw` automatically (no token needed).\n3. **Config default** — `textUnit` / `spaceUnit` (default `vw`).\n\n```tsx\n<p className=\"text-fluid-[16,24]\" />;          {/* default → vw */}\n<p className=\"text-fluid-[16@sm,24@lg]\" />;    {/* named breakpoint → vw */}\n<p className=\"text-fluid-[cqw,16,24]\" />;      {/* inline token → cqw */}\n<p className=\"text-fluid-[cqw,16@sm,24@lg]\" />;{/* token wins over the auto rule */}\n```\n\n> `cqw`/`cqh` are container-relative and require `container-type` on an ancestor;\n> `vw` is viewport-relative and needs no setup. An unknown breakpoint name (or any\n> malformed value) produces no class, the same way an invalid number does.\n\n> **Note:** Tailwind scans files statically. If you build a class name dynamically\n> at runtime, use the `fluidClamp()` function in an inline style instead.\n\n---\n\n## Direct function usage\n\n```ts\nimport { fluidClamp } from \"@basilafro/fluid-clamp\";\n\n// anywhere you need a clamp string\nconst fontSize = fluidClamp({\n  minSize: 14,\n  maxSize: 22,\n  minBreakpoint: 304,\n  maxBreakpoint: 1074,\n  fluidUnit: \"cqw\",\n});\n// → \"clamp(0.875rem, 1.038961cqw + 0.677597rem, 1.375rem)\"\n\n// Open a bound to extrapolate past it along the same slope:\nfluidClamp({ minSize: 14, maxSize: 22, minBreakpoint: 304, maxBreakpoint: 1074, fluidUnit: \"cqw\", clampMax: false });\n// → \"max(0.875rem, 1.038961cqw + 0.677597rem)\"  (grows past the max breakpoint)\n```\n\n`clampMin`/`clampMax` default to `true`. Set either to `false` to drop that bound\n(`min()`/`max()`); drop both for a bare `calc()`.\n\n### Other exports\n\n| Export | Type | Use |\n| ------ | ---- | --- |\n| `DEFAULT_TYPE_SCALE`, `DEFAULT_SPACE_SCALE` | `Record<string, { minSize, maxSize }>` | The px tables backing `text-fluid-lg`, `p-fluid-4`, … — read them to mirror the scale elsewhere, or feed entries to `fluidClamp()` directly |\n| `isFluidUnit` | `(value: string) => value is FluidUnit` | Narrowing guard for `vw`/`cqw`/`cqh`, e.g. when validating your own config input |\n| `normalizeOptions` | `(options: FluidPluginOptions) => FluidPluginConfig` | Turns the flat CSS-first keys into the nested config shape; exported mainly for wrapping the plugin in your own preset |\n| `FluidUnit`, `LengthUnit`, `CssApi`, `ScaleEntry`, `FluidClampOptions`, `FluidPluginConfig`, `FluidPluginOptions`, `FluidPluginCssOptions`, `BreakpointConfig` | types | For typing your own config objects and wrappers |\n\n---\n\n## Zero-config\n\nIf you don't need to configure anything, in v4 the bare `@plugin` line is all\nthere is:\n\n```css\n@plugin \"@basilafro/fluid-clamp\";\n```\n\nIn a JS config, use the pre-built plugin:\n\n```ts\nimport { fluidPlugin } from \"@basilafro/fluid-clamp\";\nplugins: [fluidPlugin];\n```\n\n---\n\n## `tailwind-merge` / `cn()` integration\n\n`tailwind-merge` only knows Tailwind's built-in scales, so `p-fluid-4`,\n`w-fluid-[16,24]`, `text-fluid-lg`, etc. either land in the wrong class group\n(silently overwritten by an unrelated native utility) or form a lone group\nthat never dedupes against a repeat of itself. The `@basilafro/fluid-clamp/tw-merge`\nsubpath fixes that — it's a separate entry point so importing the main\npackage never pulls in `clsx`/`tailwind-merge` for projects that don't use them.\n\n```\npnpm add clsx tailwind-merge\n```\n\nDrop-in `cn()`:\n\n```ts\nimport { cn } from \"@basilafro/fluid-clamp/tw-merge\";\n\ncn(\"p-4\", \"p-fluid-4\"); // → \"p-fluid-4\"\ncn(\"ring-fluid-4\", \"ring-2\"); // → \"ring-2\"\ncn(\"text-fluid-lg\", \"text-red-500\"); // → \"text-fluid-lg text-red-500\"\n```\n\nComposing with your own `extendTailwindMerge` config (e.g. a custom\n`font-size` scale) — your `classGroups` entries are added to fluid-clamp's,\nnot replaced by them:\n\n```ts\nimport { createFluidTwMerge } from \"@basilafro/fluid-clamp/tw-merge\";\n\nexport const cn = createFluidTwMerge({\n  extend: {\n    classGroups: {\n      \"font-size\": [{ text: [\"heading-lg\", \"heading-sm\"] }],\n    },\n  },\n});\n```\n\nThe raw `fluidClassGroups` fragment is also exported for consumers who want\nto wire it into their own `extendTailwindMerge` call directly.\n\n---\n\n## Changelog\n\nSee [CHANGELOG.md](./CHANGELOG.md).\n","readmeFilename":"","author":"BasilAfro"}