{"_id":"grfti","_rev":"4-6d74f1e282f4b5fdc3e36911b6f7902a","name":"grfti","dist-tags":{"latest":"0.6.0"},"versions":{"0.2.0":{"name":"grfti","version":"0.2.0","keywords":["gradient","gradient-string","color","colors","terminal","ansi","truecolor","oklab","cli","chalk","typescript"],"author":{"name":"Tal Hayut"},"license":"MIT","_id":"grfti@0.2.0","maintainers":[{"name":"tool3","email":"talhayut.dev@gmail.com"}],"homepage":"https://github.com/tool3/badabing#readme","bugs":{"url":"https://github.com/tool3/badabing/issues"},"dist":{"shasum":"46d564e1966bf167b6e2210e3f33bca986d6d8f7","tarball":"https://registry.npmjs.org/grfti/-/grfti-0.2.0.tgz","fileCount":9,"integrity":"sha512-fbXDxh0HKWHbAMCmkk+JZkmahDJXNySYEnjeeU5oqgiLjuLMGvpETGX2K5a6VJ/Znbpso7emIGGEbpca8eBWig==","signatures":[{"sig":"MEYCIQC1GV/mSj/2JObJap7dkEgwuC0Xzd+TUEtwX/sAAhyF/QIhAIMhbCHvtYovkZeiBXFVRmRkNLdgqYEJULR1qhFkN2Gd","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":317561},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"6bc4364b8bf2971cf3df00282ee3b3a96bb849ce","scripts":{"test":"vitest run","build":"tsup","typecheck":"tsc","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm test && npm run build"},"_npmUser":{"name":"tool3","email":"talhayut.dev@gmail.com"},"repository":{"url":"git+https://github.com/tool3/badabing.git","type":"git"},"_npmVersion":"11.17.0","description":"Pretty terminal color and gradients. Type-safe, zero-dependency, oklab-smooth.","directories":{},"sideEffects":false,"_nodeVersion":"26.4.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","vitest":"^4.0.18","typescript":"^5.6.2","@types/node":"^25.6.0"},"_npmOperationalInternal":{"tmp":"tmp/grfti_0.2.0_1789136509370_0.1130815985348621","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"grfti","version":"0.4.0","keywords":["gradient","gradient-string","color","colors","terminal","ansi","truecolor","oklab","cli","chalk","typescript"],"author":{"name":"Tal Hayut"},"license":"MIT","_id":"grfti@0.4.0","maintainers":[{"name":"tool3","email":"talhayut.dev@gmail.com"}],"homepage":"https://github.com/tool3/grfti#readme","bugs":{"url":"https://github.com/tool3/grfti/issues"},"dist":{"shasum":"9d687ad3b9bbd697011c6c6489eab73484b8eec2","tarball":"https://registry.npmjs.org/grfti/-/grfti-0.4.0.tgz","fileCount":9,"integrity":"sha512-jShFcECzv9cSMZLG7B7aTrUsm99883GRi0632ORsz95uw0sebhOCtynorFQIuEXEAhQcOsbakaP4Q7W+4xm6YA==","signatures":[{"sig":"MEUCIQDYFwvQGh2CerBGhn0/Aq/fK9iIvQl4PVzAaMEYRiR4pQIgCuG0NJHnFxoVtM0jMGIpw5a/CumJ1CihQFgfG41NH3c=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQCddEvO9NMX/nF9gU0INFKUjLZfM54lJIdQVyGCc6H39AIgT+kcaXXcJjsZKm8Qe7+d6Z9/+9jPoZv/5P58Qi7tebQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/grfti@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":318622},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"bf34d1179504357f2d460bda2acf379ab162f035","scripts":{"test":"vitest run","build":"tsup","typecheck":"tsc","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm test && npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:24de7090-187a-479e-862b-5f44a474759c"}},"repository":{"url":"git+https://github.com/tool3/grfti.git","type":"git"},"_npmVersion":"11.19.0","description":"Spray color and gradients across your terminal. Type-safe, zero-dependency, oklab-smooth.","directories":{},"sideEffects":false,"_nodeVersion":"24.20.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","vitest":"^4.0.18","typescript":"^5.6.2","@types/node":"^25.6.0"},"_npmOperationalInternal":{"tmp":"tmp/grfti_0.4.0_1789412161283_0.9856504719527721","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"grfti","version":"0.5.0","keywords":["gradient","gradient-string","color","colors","terminal","ansi","truecolor","oklab","cli","chalk","typescript"],"author":{"name":"Tal Hayut"},"license":"MIT","_id":"grfti@0.5.0","maintainers":[{"name":"tool3","email":"talhayut.dev@gmail.com"}],"homepage":"https://github.com/tool3/grfti#readme","bugs":{"url":"https://github.com/tool3/grfti/issues"},"dist":{"shasum":"5f3ba0e9f2a981a7faec5ae930005a0f0a3fc8f5","tarball":"https://registry.npmjs.org/grfti/-/grfti-0.5.0.tgz","fileCount":9,"integrity":"sha512-vhQcIW4YqDb/yiQETEcEjMKtC9kAV+KD4i5tR73RBy9Ve8jLQX2cTllFdua4XCYpDZH52inQIevgdlfA9JlLTA==","signatures":[{"sig":"MEUCIQCDPDKEB/RUnj3U2aO7puCK4vmqUzvV8SMtzgeCXIFFkAIgWpRJjKAU96g8jo0dtYYbC6PBF9ctbwbQ/Uyu2EuVzKs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIEgwwCtMSYEwq2aMW25UtYFUTYo8Qtjwwk+HB4z9Ai8SAiEAwrihEkEUq8UYxk0jXKU5+VXoVc4qLbVWInegoQgAy1o=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/grfti@0.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":318622},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"4acad23e22938fb08698960a4059645d3e57e6c4","scripts":{"test":"vitest run","build":"tsup","typecheck":"tsc","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm test && npm run build"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:24de7090-187a-479e-862b-5f44a474759c"}},"repository":{"url":"git+https://github.com/tool3/grfti.git","type":"git"},"_npmVersion":"11.19.0","description":"Spray color and gradients across your terminal. Type-safe, zero-dependency, oklab-smooth.","directories":{},"sideEffects":false,"_nodeVersion":"24.20.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","vitest":"^4.0.18","typescript":"^5.6.2","@types/node":"^25.6.0"},"_npmOperationalInternal":{"tmp":"tmp/grfti_0.5.0_1789412341462_0.26145375512692115","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"_id":"grfti@0.6.0","bugs":{"url":"https://github.com/tool3/grfti/issues"},"dist":{"shasum":"fe28a5ca04af295ce91c44de71194f23a03daf2f","tarball":"https://registry.npmjs.org/grfti/-/grfti-0.6.0.tgz","fileCount":9,"integrity":"sha512-1IcEnTtUvBzP2QNmBrhSXN480Cm2Y0Olb5r9yfKtJWo80tqWbjUXkmyDm+xA12ixxA47+0eG7TOmcl90NW3LOg==","signatures":[{"sig":"MEUCIHo50TcHe/rV+N1arROOtQgJ9weOndJrNXmlmIw6R5zGAiEAvY0gWvfiy7a9BAEW1pmkIbbphW8xCLe/yAkV0d7CpQk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDCLFzYrWdTIv3G1TKAEXtgkvgr/8Lmt26J4uAPPAchYAIgLhlNNw/ektX9Z9wSLIZIbcOxTXLQmmaVE6IAeY7fpMc="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/grfti@0.6.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":310810},"main":"./dist/index.cjs","name":"grfti","type":"module","types":"./dist/index.d.ts","author":{"name":"Tal Hayut"},"module":"./dist/index.js","engines":{"node":">=18.0.0"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"4063a8f6d9ee5de0446a2ea1bdb9e8793d638f39","license":"MIT","scripts":{"test":"vitest run","build":"tsdown","example":"tsx","examples":"tsx examples/run-all.ts","typecheck":"tsc","test:watch":"vitest","prepublishOnly":"npm run typecheck && npm test && npm run build"},"version":"0.6.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:24de7090-187a-479e-862b-5f44a474759c"}},"homepage":"https://github.com/tool3/grfti#readme","keywords":["gradient","gradient-string","color","colors","terminal","ansi","truecolor","oklab","cli","chalk","typescript"],"repository":{"url":"git+https://github.com/tool3/grfti.git","type":"git"},"_npmVersion":"11.19.0","description":"Spray color and gradients across your terminal. Type-safe, zero-dependency, oklab-smooth.","directories":{},"maintainers":[{"name":"tool3","email":"talhayut.dev@gmail.com"}],"sideEffects":false,"_nodeVersion":"24.20.0","_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.23.13","tsdown":"^0.23.0","vitest":"^4.0.18","shellfie":"^2.4.4","typescript":"^7.0.2","@types/node":"^25.6.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/grfti_0.6.0_1789491317802_0.3819251488912332"}}},"time":{"created":"2026-09-11T14:21:49.296Z","modified":"2026-09-15T16:55:18.214Z","0.2.0":"2026-09-11T14:21:49.535Z","0.4.0":"2026-09-14T18:56:01.373Z","0.5.0":"2026-09-14T18:59:01.564Z","0.6.0":"2026-09-15T16:55:17.893Z"},"bugs":{"url":"https://github.com/tool3/grfti/issues"},"author":{"name":"Tal Hayut"},"license":"MIT","homepage":"https://github.com/tool3/grfti#readme","keywords":["gradient","gradient-string","color","colors","terminal","ansi","truecolor","oklab","cli","chalk","typescript"],"repository":{"url":"git+https://github.com/tool3/grfti.git","type":"git"},"description":"Spray color and gradients across your terminal. Type-safe, zero-dependency, oklab-smooth.","maintainers":[{"name":"tool3","email":"talhayut.dev@gmail.com"}],"readme":"# grfti\n\nSpray color and gradients across your terminal. TypeScript-native, zero dependencies, perceptually smooth color engine.\n\n<p align=\"center\">\n  <a href=\"./examples/gradients/intro.ts\">\n    <img src=\"https://raw.githubusercontent.com/tool3/grfti/master/examples/svgs/intro.svg\" width=\"620\" alt=\"grfti intro svg\">\n  </a>\n</p>\n\n```ts\nimport { gradient, color } from 'grfti';\n\ngradient('pink', 'cyan')('hello world');\ngradient('sunset')(banner);\ngradient('gradient(green, yellow):reverse:vertical')(banner);\n\ncolor('pink')('just this color');\ncolor('#3d7fb3').lighten(0.2).hex;\n```\n\nEverything below is a picture of a real terminal buffer — grfti painted it, and\n[shellfie](https://github.com/tool3/shellfie) wrote it out as SVG. Each one links to the\nscript that made it.\n\n## Install\n\n```sh\nnpm install grfti\n```\n\n## Why grfti\n\n| | grfti | gradient-string |\n| --- | --- | --- |\n| Dependencies | none | chalk, tinygradient, tinycolor2 |\n| Written in | TypeScript | TypeScript |\n| Multi-line strings | just work | need `.multiline()` |\n| Direction | horizontal, vertical, diagonal | horizontal only |\n| Interpolation | oklab (default), oklch, hsl, hsv, rgb | rgb, hsv |\n| Out-of-gamut colors | mapped by chroma reduction | channel-clipped |\n| Color input | names, hex, `rgb()`, `hsl()`, `hsv()`, `oklab()`, `oklch()`, ANSI escapes, 256-index | whatever tinycolor2 takes |\n| Terminal depth | truecolor / 256 / 16 / none, auto-detected | truecolor only |\n| Also renders | CSS, SVG, sampled color arrays | terminal text |\n| Autocomplete | every color and preset name | none |\n| Presets | 52 | 13 (all included here) |\n\nDrop-in for the common cases — same `gradient(...)` call, same preset names on the factory:\n\n```ts\nimport gradient from 'grfti';\n\ngradient(['red', 'blue'])('hello');\ngradient(['red', 'blue'], { interpolation: 'hsv', hsvSpin: 'long' })('hello');\ngradient.atlas('hello');\ngradient.atlas.multiline(banner);\n```\n\n## The string DSL\n\nEvery gradient is expressible as one string, so a terminal flag, a config file and a\nTypeScript call all use the same syntax. Colors first, then `:flags` in any order.\n\n```ts\ngradient('pink, cyan');                            // bare list\ngradient('gradient(pink, cyan)');                  // wrapped\ngradient('gradient(pink, cyan:vertical)');         // flags inside\ngradient('gradient(pink, cyan):vertical:reverse'); // flags outside\ngradient('pink', 'cyan:vertical');                 // flags on any argument\ngradient('sunset:reverse');                        // a preset, reversed\ngradient('red 0%, yellow 25%, green 100%');        // explicit stops\ngradient('linear-gradient(135deg, #ff7a59, #7c2bff)'); // pasted from CSS\n```\n\n<a href=\"./examples/gradients/dsl.ts\"><img src=\"https://raw.githubusercontent.com/tool3/grfti/master/examples/svgs/dsl.svg\" width=\"720\" alt=\"dsl\"></a>\n\n| Flags | |\n| --- | --- |\n| Direction | `horizontal` `h` `row`, `vertical` `v` `column`, `diagonal` `diag` `d` |\n| Reverse | `reverse` `reversed` `rev` `r` |\n| Space | `oklab` `oklch` `hsl` `hsv` `hsb` `rgb` |\n| Hue spin | `short` `long` |\n| Depth | `truecolor` `24bit`, `ansi256` `256`, `ansi16` `16`, `none` `plain` |\n| Background | `bg` `background` `on` |\n\nColors can be a CSS name, `#rgb` / `#rgba` / `#rrggbb` / `#rrggbbaa`, `rgb()` / `rgba()`,\n`hsl()` / `hsla()`, `hsv()`, `oklab()`, `oklch()`, `ansi256(196)`, or a raw ANSI escape\nsequence. An unknown color or flag throws a `GrftiError` that suggests the nearest match.\n\n<a href=\"./examples/gradients/stops.ts\"><img src=\"https://raw.githubusercontent.com/tool3/grfti/master/examples/svgs/stops.svg\" width=\"380\" alt=\"stops\"></a> <a href=\"./examples/gradients/css-gradient.ts\"><img src=\"https://raw.githubusercontent.com/tool3/grfti/master/examples/svgs/css-gradient.svg\" width=\"380\" alt=\"css-gradient\"></a>\n\n<a href=\"./examples/colors/errors.ts\"><img src=\"https://raw.githubusercontent.com/tool3/grfti/master/examples/svgs/errors.svg\" width=\"720\" alt=\"errors\"></a>\n\n## Gradients\n\n`gradient(...)` returns a callable, immutable gradient. Every modifier returns a new one.\n\n```ts\nconst brand = gradient('#ff7a59', '#7c2bff');\n\nbrand('hello');                   // paint, horizontally\nbrand.vertical(banner);           // paint, down the lines\nbrand.diagonal(banner);           // paint, across both axes\nbrand.reverse.vertical(banner);   // chain as far as you like\nbrand.bg('  padded  ');           // paint the background instead\nbrand.space('oklch')(banner);     // pick the interpolation space\nbrand.depth('ansi256')(banner);   // pin the color depth\n\ngradient.vertical('#ff7a59', '#7c2bff')(banner); // or set direction up front\n```\n\n<a href=\"./examples/gradients/directions.ts\"><img src=\"https://raw.githubusercontent.com/tool3/grfti/master/examples/svgs/directions.svg\" width=\"380\" alt=\"directions\"></a> <a href=\"./examples/gradients/chaining.ts\"><img src=\"https://raw.githubusercontent.com/tool3/grfti/master/examples/svgs/chaining.svg\" width=\"380\" alt=\"chaining\"></a>\n\n<a href=\"./examples/gradients/background.ts\"><img src=\"https://raw.githubusercontent.com/tool3/grfti/master/examples/svgs/background.svg\" width=\"720\" alt=\"background\"></a>\n\nMulti-line input is handled without asking. Each line is painted across the width of the\nwidest line, so columns line up — ASCII art and figlet banners come out straight.\n\n```ts\ngradient.atlas(`\n ___  ___  ___\n|   ||   ||   |\n|___||___||___|\n`);\n```\n\nThe banner at the top of this page is exactly that, painted four ways —\n[`examples/gradients/multiline.ts`](./examples/gradients/multiline.ts).\n\nGrapheme clusters stay whole (emoji and combining marks are never split), existing ANSI\nescapes in the input pass through untouched, and runs of identical color share one escape.\n\n### Reading the colors out\n\nA gradient is a color ramp, not just a text painter:\n\n```ts\nbrand.at(0.5).hex;                      // '#c860a8'\nbrand.sample(5).map((c) => c.hex);       // five evenly spaced stops\nbrand.stops;                             // [{ color, position }, ...]\nbrand.colors;                            // just the colors\n```\n\n<a href=\"./examples/gradients/sampling.ts\"><img src=\"https://raw.githubusercontent.com/tool3/grfti/master/examples/svgs/sampling.svg\" width=\"720\" alt=\"sampling\"></a>\n\nThat is what makes it shareable across renderers — a chart colors its bars with\n`sample(bars.length)`, an SVG writer asks for `svg()`, a web preview asks for `css()`.\n\n```ts\nbrand.css();                    // 'linear-gradient(90deg, #ff7a59 0%, #7c2bff 100%)'\nbrand.vertical.css();           // 'linear-gradient(180deg, ...)'\nbrand.css({ angle: 45 });\nbrand.svg({ id: 'bg' });        // '<linearGradient id=\"bg\" x1=\"0%\" ...>'\nString(brand);                  // 'gradient(#ff7a59 0%, #7c2bff 100%):horizontal:oklab'\n```\n\n`String(gradient)` round-trips: the output parses back into an equivalent gradient.\n\n<a href=\"./examples/gradients/handoff.ts\"><img src=\"https://raw.githubusercontent.com/tool3/grfti/master/examples/svgs/handoff.svg\" width=\"720\" alt=\"handoff\"></a>\n\n## Colors\n\n`color(...)` returns a callable, immutable color with the same shape.\n\n```ts\nconst accent = color('#3d7fb3');\n\naccent('hello');          // paint the text\naccent.bg('hello');       // paint the background\naccent.ansi;              // the raw escape sequence\n\naccent.hex;               // '#3d7fb3'\naccent.rgb;               // { r, g, b, alpha }\naccent.hsl;               // { h, s, l, alpha }\naccent.hsv;\naccent.oklab;\naccent.oklch;\naccent.name;              // the CSS color name, when it has one\naccent.luminance;\naccent.isDark;\n\naccent.lighten(0.1);      // perceptual, in oklch\naccent.darken(0.1);\naccent.saturate(0.3);\naccent.desaturate(0.3);\naccent.rotate(180);\naccent.alpha(0.5);\naccent.mix('pink');               // oklab midpoint\naccent.mix('pink', 0.25, 'hsl');\naccent.contrast('white');         // WCAG ratio\n```\n\n<a href=\"./examples/colors/formats.ts\"><img src=\"https://raw.githubusercontent.com/tool3/grfti/master/examples/svgs/formats.svg\" width=\"380\" alt=\"formats\"></a> <a href=\"./examples/colors/inspect.ts\"><img src=\"https://raw.githubusercontent.com/tool3/grfti/master/examples/svgs/inspect.svg\" width=\"380\" alt=\"inspect\"></a>\n\n<a href=\"./examples/colors/adjust.ts\"><img src=\"https://raw.githubusercontent.com/tool3/grfti/master/examples/svgs/adjust.svg\" width=\"380\" alt=\"adjust\"></a> <a href=\"./examples/colors/mix.ts\"><img src=\"https://raw.githubusercontent.com/tool3/grfti/master/examples/svgs/mix.svg\" width=\"380\" alt=\"mix\"></a>\n\n<a href=\"./examples/colors/contrast.ts\"><img src=\"https://raw.githubusercontent.com/tool3/grfti/master/examples/svgs/contrast.svg\" width=\"720\" alt=\"contrast\"></a>\n\nEvery name it answers to:\n\n<a href=\"./examples/colors/named.ts\"><img src=\"https://raw.githubusercontent.com/tool3/grfti/master/examples/svgs/named.svg\" width=\"720\" alt=\"named\"></a>\n\nStandalone helpers are exported too: `rgb()`, `hsl()`, `oklch()`, `parseRgb()`,\n`tryParseRgb()`, `mixRgb()`, `rgbToOklab()`, `rgbToAnsi256()`, `ansi256ToRgb()`, and the\nrest of the conversion set.\n\n## Color depth\n\nDepth is detected once per paint from `FORCE_COLOR`, `NO_COLOR`, `GRFTI_DEPTH`, `COLORTERM`,\n`TERM`, `TERM_PROGRAM`, `CI` and whether stdout is a TTY. Truecolor terminals get 24-bit\nescapes; 256-color terminals get the nearest palette entry, chosen by comparing the color\ncube against the grayscale ramp; 16-color terminals get the nearest base color; everything\nelse gets plain text. Pin it explicitly with `.depth('truecolor')` — worth doing in tests.\n\n<a href=\"./examples/gradients/depth.ts\"><img src=\"https://raw.githubusercontent.com/tool3/grfti/master/examples/svgs/depth.svg\" width=\"720\" alt=\"depth\"></a>\n\n## Presets\n\nReach for one by property, by name, or from the DSL:\n\n```ts\ngradient.vaporwave('hello');\ngradient('vaporwave')('hello');\ngradient('vaporwave:vertical:reverse')(banner);\ngradient('sunset', 'ice');            // presets concatenate\n```\n\nNo preset name collides with a CSS color name, so the DSL is never ambiguous.\n\n<a href=\"./examples/gradients/presets.ts\"><img src=\"https://raw.githubusercontent.com/tool3/grfti/master/examples/svgs/presets.svg\" width=\"720\" alt=\"presets\"></a>\n\n<a href=\"./examples/gradients/concat.ts\"><img src=\"https://raw.githubusercontent.com/tool3/grfti/master/examples/svgs/concat.svg\" width=\"720\" alt=\"concat\"></a>\n\n| Preset | Colors |\n| --- | --- |\n| `atlas` | #feac5e → #c779d0 → #4bc0c8 |\n| `cristal` | #bdfff3 → #4ac29a |\n| `teen` | #77a1d3 → #79cbca → #e684ae |\n| `mind` | #473b7b → #3584a7 → #30d2be |\n| `morning` | #ff5f6d → #ffc371 |\n| `vice` | #5ee7df → #b490ca |\n| `passion` | #f43b47 → #453a94 |\n| `fruit` | #ff4e50 → #f9d423 |\n| `instagram` | #833ab4 → #fd1d1d → #fcb045 |\n| `retro` | #3f51b1 → … → #f7c978 |\n| `summer` | #fdbb2d → #22c1c3 |\n| `rainbow` | full hue sweep |\n| `pastel` | full pastel sweep |\n| `aurora` | #00c9a7 → #4aa3df → #845ec2 |\n| `sunset` | #ff7a59 → #c860a8 → #7c2bff |\n| `synthwave` | #ff2e97 → #7b2ff7 → #2de2e6 |\n| `vaporwave` | #ff71ce → #01cdfe → #05ffa1 |\n| `matrix` | #00ff41 → #008f11 |\n| `amber` | #ffd166 → #ffb000 → #ff6a00 |\n| `phosphor` | #00ff9f → #00d4ff |\n| `midnight` | #a5b4fc → #6366f1 → #312e81 |\n| `paper` | #f6f1e5 → #c9b99b |\n| `hotdog` | #ff0000 → #ffff00 |\n| `breeze` | #cf2f98 → #6a3dec |\n| `candy` | #a58efb → #e9bff8 |\n| `ember` | #ff6363 → #733434 |\n| `falcon` | #bde3ec → #363654 |\n| `meadow` | #59d499 → #a0872d |\n| `raindrop` | #8ec7fb → #1c55aa |\n| `noir` | #e6e6e6 → #4a4a4a |\n| `ice` | #ffffff → #80deea |\n| `sand` | #eed5b6 → #af8856 |\n| `forest` | #83c98d → #2f4f38 |\n| `mono` | #e0e0e0 → #3a3a3a |\n| `fire` | #ffe259 → #ff6b35 → #c1121f |\n| `neon` | #00f5d4 → #f15bb5 |\n| `toxic` | #d8ff3e → #56ab2f |\n| `royal` | #8e2de2 → #4a00e0 |\n| `peach` | #ffecd2 → #fcb69f |\n| `abyss` | #00c6ff → #0072ff |\n| `bubblegum` | #ffafbd → #ffc3a0 |\n| `cyberpunk` | #fcee0a → #00f0ff → #ff003c |\n| `vercel` | #ffffff → #7a7a7a |\n| `supabase` | #6ee7b7 → #3ecf8e → #12805c |\n| `openai` | #74aa9c → #10a37f |\n| `prisma` | #71e8df → #5a67d8 |\n| `stripe` | #00d4ff → #635bff |\n| `cloudflare` | #faad3f → #f6821f |\n| `nuxt` | #36e4da → #00dc82 |\n| `gemini` | #4285f4 → #9b72cb → #d96570 |\n| `tailwind` | #38bdf8 → #818cf8 |\n| `firecrawl` | #fbbf24 → #f97316 → #dc2626 |\n\n## Types\n\nEverything is typed, and the names autocomplete. `NamedColor` is the union of all 148 CSS\ncolor names plus `marine` and `transparent`; `PresetName` is the union of all 52 presets. Both flow into\n`gradient()` and `color()` through a literal union that still accepts arbitrary DSL\nstrings, so you get suggestions without losing expressiveness.\n\n```ts\nimport type { Color, ColorDepth, ColorSpace, Direction, Gradient, NamedColor, PresetName, Rgb, Stop } from 'grfti';\n```\n\n## Interpolation\n\nDefault is **oklab** — perceptually uniform, so pink → cyan does not sag through grey and\nblue → yellow does not dip through mud. Where a straight oklab line leaves the sRGB gamut,\ngrfti reduces chroma at constant lightness and hue until the color fits, instead of clipping\neach channel (which shifts hue).\n\n`oklch` interpolates hue directly, for deliberate hue sweeps. `hsl` and `hsv` are there for\ncompatibility and for vivid wheel spins — pair them with `long` to sweep the whole wheel.\n`rgb` matches what gradient-string and most other tools do.\n\n```ts\ngradient('pink', 'cyan').at(0.5).hex;                  // '#bbe2e5'  oklab\ngradient('pink', 'cyan').space('rgb').at(0.5).hex;     // '#80e0e5'  rgb\ngradient('pink', 'cyan').space('oklch').at(0.5).hex;   // '#ccd1ff'  oklch\n```\n\n<a href=\"./examples/gradients/spaces.ts\"><img src=\"https://raw.githubusercontent.com/tool3/grfti/master/examples/svgs/spaces.svg\" width=\"720\" alt=\"spaces\"></a>\n\n<a href=\"./examples/gradients/hue-spin.ts\"><img src=\"https://raw.githubusercontent.com/tool3/grfti/master/examples/svgs/hue-spin.svg\" width=\"720\" alt=\"hue-spin\"></a>\n\n## Examples\n\nEvery picture on this page is a script in [`examples/`](./examples), and every one of them\nruns:\n\n```sh\nnpm run examples                              # render all 21\nnpm run example examples/gradients/spaces.ts  # or just one\n```\n\nThey run through `tsx`, which is a devDependency — `node examples/…​.ts` on its own fails\nwith `ERR_MODULE_NOT_FOUND … /src/index`, because grfti's sources carry no file extensions\nin their internal imports and native type stripping insists on them.\n\n`█` is the widest thing a terminal can paint, so a run of them is the closest a text grid\ngets to a swatch — every ramp above is one call, `gradient('sunset')('█'.repeat(40))`, with\nthe terminal filling each cell with a slightly different 24-bit color. Multi-line blocks are\nhow `vertical` and `diagonal` become visible at all.\n\n[`gradients/`](./examples/gradients) holds 14 scripts and [`colors/`](./examples/colors)\nseven; [examples/README.md](./examples/README.md) has the full table.\n\n## CLI\n\n```sh\nnpm install -g grfti-cli\ngrfti sunset \"hello world\"\n```\n\nSee [grfti-cli](https://github.com/tool3/grfti-cli).\n\n## Development\n\nSources are pure TypeScript: no `.js` anywhere outside `dist`, and no file extensions in\ninternal imports. `tsup` resolves them at build time and emits ESM, CJS and declarations.\n\n```sh\nnpm run typecheck   # tsc, noEmit — it can never scatter .js next to sources\nnpm test            # vitest, runs the .ts directly\nnpm run build       # dist/index.js (esm), dist/index.cjs, dist/index.d.ts, dist/index.d.cts\nnpm run examples    # tsx, renders examples/svgs\n```\n\n### Running the sources\n\nAnything that executes `src/` needs a loader that resolves extensionless imports. `tsup`\ndoes it at build time, `vitest` does it in tests, and `tsx` does it everywhere else:\n\n```sh\nnpx tsx examples/gradients/spaces.ts\nnpx tsx -e \"import { gradient } from './src/index'; console.log(gradient('sunset')('hi'))\"\n```\n\n`node` and `ts-node` both fail, with the same error and for the same reason:\n\n```\nError [ERR_MODULE_NOT_FOUND]: Cannot find module '…/grfti/src/index'\n```\n\nThe package is `\"type\": \"module\"`, so a relative import is resolved as a URL and must name\na real file. `src/index.ts` imports `'./gradient/create'`, not `'./gradient/create.ts'` —\nNode's native type stripping requires the exact specifier, and `ts-node`'s ESM loader does\nno extension resolution either. Neither has a flag for it; the old\n`--experimental-specifier-resolution=node` was removed in Node 20. Reach for `tsx`.\n\nThe CLI in `grfti-cli` is CommonJS, so it does run under `ts-node` — that is what\n`npm start` there uses.\n\n## License\n\nMIT\n","readmeFilename":"README.md"}