### Install DittoTones via npm Source: https://github.com/meodai/dittotones/blob/main/README.md Install the dittoTones library using npm. This is the first step before using the library in your project. ```bash npm install dittotones ``` -------------------------------- ### Development Commands for DittoTones Source: https://github.com/meodai/dittotones/blob/main/README.md Common development commands for the dittoTones project, including installing dependencies, starting the dev server, building the library, and previewing the build. ```bash npm install npm run dev # Start dev server with demo npm run build # Build library npm run preview # Preview the demo build ``` -------------------------------- ### Generate Colors Using Built-in Ramps in TypeScript Source: https://context7.com/meodai/dittotones/llms.txt Demonstrates how to import and use various pre-built design system ramps (e.g., Tailwind, Radix, Flexoki) with the DittoTones constructor. Shows how to swap ramp sets at runtime and generate colors based on an input color. ```typescript import { DittoTones } from 'dittotones'; import { formatHex } from 'culori'; // Available built-in ramps: import { tailwindRamps } from 'dittotones/src/ramps/tailwind'; // Tailwind v4 — shades 50–950 import { tailwindV3Ramps } from 'dittotones/src/ramps/tailwind-v3'; // Tailwind v3 — shades 50–950 import { radixRamps } from 'dittotones/src/ramps/radix'; // Radix UI light — steps 1–12 import { shoelaceRamps } from 'dittotones/src/ramps/shoelace'; // Shoelace — steps 05–95 import { waDefaultRamps } from 'dittotones/src/ramps/wa-default'; // Web Awesome default — steps 05–95 import { waBrightRamps } from 'dittotones/src/ramps/wa-bright'; // Web Awesome bright — steps 05–95 import { flexokiRamps } from 'dittotones/src/ramps/flexoki'; // Flexoki — steps 50–950 // Swap ramp sets at runtime const generators = { tailwind: new DittoTones({ ramps: tailwindRamps }), radix: new DittoTones({ ramps: radixRamps }), flexoki: new DittoTones({ ramps: flexokiRamps }), }; const inputColor = '#e11d48'; // rose-600 in Tailwind for (const [system, gen] of Object.entries(generators)) { const result = gen.generate(inputColor); const shades = Object.keys(result.scale); console.log(`[${system}] ${shades.length} shades, matched at ${result.matchedShade}, method: ${result.method}`); // [tailwind] 11 shades, matched at 600, method: exact // [radix] 12 shades, matched at 9, method: blend // [flexoki] 13 shades, matched at 600, method: single } ``` -------------------------------- ### Configure DittoTones with Custom Ramps Source: https://context7.com/meodai/dittotones/llms.txt Build a custom color ramp from scratch using `oklch` color space. Ensure all ramps share the same shade keys. The `DittoTonesOptions` allow specifying custom ramps, preserving hue offsets, and enabling gamut mapping for displayable sRGB output. ```typescript import { DittoTones, type DittoTonesOptions } from 'dittotones'; import { oklch, parse, type Oklch } from 'culori'; // Build a custom ramp from scratch (all ramps must share the same shade keys) const myBrandRamp: Record = { '50': oklch(parse('oklch(98% 0.01 250)')) as Oklch, '100': oklch(parse('oklch(95% 0.02 250)')) as Oklch, '200': oklch(parse('oklch(88% 0.05 250)')) as Oklch, '300': oklch(parse('oklch(78% 0.09 250)')) as Oklch, '400': oklch(parse('oklch(65% 0.14 250)')) as Oklch, '500': oklch(parse('#3B82F6')) as Oklch, // anchor shade '600': oklch(parse('oklch(48% 0.16 250)')) as Oklch, '700': oklch(parse('oklch(37% 0.13 250)')) as Oklch, '800': oklch(parse('oklch(28% 0.09 250)')) as Oklch, '900': oklch(parse('oklch(20% 0.06 250)')) as Oklch, '950': oklch(parse('oklch(14% 0.04 250)')) as Oklch, }; const neutralRamp: Record = { '50': { mode: 'oklch', l: 0.985, c: 0.003, h: 0 }, '100': { mode: 'oklch', l: 0.96, c: 0.005, h: 0 }, '500': { mode: 'oklch', l: 0.50, c: 0.005, h: 0 }, '900': { mode: 'oklch', l: 0.14, c: 0.003, h: 0 }, '950': { mode: 'oklch', l: 0.09, c: 0.002, h: 0 }, }; const options: DittoTonesOptions = { ramps: new Map([ ['brand', myBrandRamp], ['neutral', neutralRamp], ]), preserveHueOffsets: true, // Keep reference ramp's hue drift across shades gamutMap: true, // Ensure all output is displayable in sRGB }; const ditto = new DittoTones(options); const result = ditto.generate('oklch(0.6 0.18 180)'); // teal-ish console.log(result.method); // 'single' or 'blend' console.log(result.sources); // [{ name: 'brand', diff: 0.12, weight: 1 }] ``` -------------------------------- ### Generate Color Palette with DittoTones Source: https://github.com/meodai/dittotones/blob/main/README.md Import DittoTones and culori, instantiate DittoTones with optional custom ramps, generate a palette from a target color, and format the output using culori. ```typescript import { DittoTones } from 'dittotones'; import { formatCss, formatHex } from 'culori'; // Bring your own ramps (see "Custom ramps" below) const ditto = new DittoTones({ ramps: myRamps }); const result = ditto.generate('#F97316'); // result.scale contains Oklch color objects // Use culori's formatCss or formatHex to convert: for (const [shade, color] of Object.entries(result.scale)) { console.log(`${shade}: ${formatCss(color)}`); // 50: oklch(0.98 0.016 49) // 100: oklch(0.954 0.038 49) // ... } // Or as hex: for (const [shade, color] of Object.entries(result.scale)) { console.log(`${shade}: ${formatHex(color)}`); } ``` -------------------------------- ### new DittoTones(options) Source: https://context7.com/meodai/dittotones/llms.txt Constructs a DittoTones instance around a set of reference color ramps. Throws if no ramps are provided or if ramps have inconsistent shade keys. ```APIDOC ## `new DittoTones(options)` — Create a palette generator Constructs a `DittoTones` instance around a set of reference color ramps. Throws if no ramps are provided or if ramps have inconsistent shade keys. The constructor automatically detects the most neutral (lowest average chroma) ramp, which is used as the fallback for near-achromatic input colors. ```typescript import { DittoTones } from 'dittotones'; import { formatCss, formatHex } from 'culori'; // Use the built-in Tailwind ramps import tailwindRamps from 'dittotones/ramps/tailwind'; const ditto = new DittoTones({ ramps: tailwindRamps, // Map> — required preserveHueOffsets: false, // Keep hue shifts from reference ramp (e.g. Tailwind blues shift purple in dark shades). Default: false gamutMap: true, // Clamp output to sRGB by reducing chroma. Prevents out-of-gamut hex values. Default: false }); // Throws: "At least one ramp is required" // new DittoTones({ ramps: new Map() }); // Throws: "Ramp red has inconsistent keys" // new DittoTones({ ramps: new Map([['blue', {'50': ...}], ['red', {'100': ...}]]) }); ``` ``` -------------------------------- ### Create DittoTones Instance with Tailwind Ramps Source: https://context7.com/meodai/dittotones/llms.txt Instantiate DittoTones using built-in Tailwind CSS ramps. Ensure ramps are provided and have consistent shade keys. The `gamutMap` option clamps output to sRGB. ```typescript import { DittoTones } from 'dittotones'; import { formatCss, formatHex } from 'culori'; // Use the built-in Tailwind ramps import tailwindRamps from 'dittotones/ramps/tailwind'; const ditto = new DittoTones({ ramps: tailwindRamps, // Map> — required preserveHueOffsets: false, // Keep hue shifts from reference ramp (e.g. Tailwind blues shift purple in dark shades). Default: false gamutMap: true, // Clamp output to sRGB by reducing chroma. Prevents out-of-gamut hex values. Default: false }); // Throws: "At least one ramp is required" // new DittoTones({ ramps: new Map() }); // Throws: "Ramp red has inconsistent keys" // new DittoTones({ ramps: new Map([['blue', {'50': ...}], ['red', {'100': ...}]]) }); ``` -------------------------------- ### Inspect DittoTones Ramps and Shades in TypeScript Source: https://context7.com/meodai/dittotones/llms.txt Shows how to use the `rampNames` and `shades` read-only getters on a DittoTones instance to inspect the currently loaded configuration. Useful for understanding available color scales and iterating through output in a defined order. ```typescript import { DittoTones } from 'dittotones'; import { tailwindRamps } from 'dittotones/src/ramps/tailwind'; const ditto = new DittoTones({ ramps: tailwindRamps }); console.log(ditto.rampNames); // ['slate', 'gray', 'zinc', 'neutral', 'stone', 'red', 'orange', 'amber', ...] console.log(ditto.shades); // ['50', '100', '200', '300', '400', '500', '600', '700', '800', '900', '950'] // Use shades to iterate output in correct order const result = ditto.generate('#06b6d4'); // cyan for (const shade of ditto.shades) { const color = result.scale[shade]; console.log(`${shade}: l=${color.l.toFixed(3)} c=${color.c?.toFixed(3)} h=${color.h?.toFixed(1)}`); } // 50: l=0.985 c=0.014 h=198.2 // 100: l=0.956 c=0.035 h=198.2 // ... // 950: l=0.166 c=0.038 h=198.2 ``` -------------------------------- ### DittoTones Configuration Options Source: https://github.com/meodai/dittotones/blob/main/README.md Configure DittoTones with options such as preserving hue offsets from reference ramps and mapping output colors to the sRGB gamut to prevent out-of-gamut issues. ```typescript const ditto = new DittoTones({ ramps: myRamps, // Preserve hue shifts from reference ramps across the scale. // e.g. Tailwind blues shift toward purple in dark shades. // Default: false (flat hue across all shades) preserveHueOffsets: true, // Map output colors to sRGB gamut by reducing chroma. // Prevents out-of-gamut colors when converting to hex. // Default: false gamutMap: true, }); ``` -------------------------------- ### ditto.generate(color) Source: https://context7.com/meodai/dittotones/llms.txt Generate a full palette from any color. Accepts any CSS color string and returns a GenerateResult object containing the full generated palette as OKLCH color objects, plus metadata about which ramps were used and the matching strategy applied. ```APIDOC ## `ditto.generate(color)` — Generate a full palette from any color The primary API. Accepts any CSS color string (`#hex`, `rgb()`, `hsl()`, `oklch()`, named colors) and returns a `GenerateResult` object containing the full generated palette as OKLCH color objects, plus metadata about which ramps were used and the matching strategy applied. ```typescript import { DittoTones, type GenerateResult } from 'dittotones'; import { formatCss, formatHex } from 'culori'; import tailwindRamps from 'dittotones/ramps/tailwind'; const ditto = new DittoTones({ ramps: tailwindRamps, gamutMap: true }); // Input: any valid CSS color const result: GenerateResult = ditto.generate('#F97316'); // Tailwind orange-500 // ── result shape ────────────────────────────────────────── // result.inputColor → { mode: 'oklch', l: 0.702, c: 0.191, h: 55.3 } // result.matchedShade → '500' // result.method → 'exact' | 'single' | 'blend' // result.sources → [{ name: 'orange', diff: 0.003, weight: 1 }] // result.scale → { '50': Oklch, '100': Oklch, ..., '950': Oklch } // Convert to CSS custom properties const cssVars = Object.entries(result.scale) .sort(([a], [b]) => Number(a) - Number(b)) .map(([shade, color]) => ` --brand-${shade}: ${formatCss(color)};`) .join('\n'); console.log(`:root {\n${cssVars}\n}`); // :root { // --brand-50: oklch(0.985 0.012 56); // --brand-100: oklch(0.969 0.024 56); // --brand-200: oklch(0.937 0.05 56); // ... // --brand-950: oklch(0.274 0.075 56); // } // Convert to hex for (const [shade, color] of Object.entries(result.scale)) { console.log(`${shade}: ${formatHex(color)}`); } // 50: #fff7ed // 100: #ffedd5 // ... // 950: #431407 // Throws: "Invalid color: not-a-color" // ditto.generate('not-a-color'); ``` ``` -------------------------------- ### Generate Full Color Palette from Input Color Source: https://context7.com/meodai/dittotones/llms.txt Generate a complete color palette from any CSS color string using a configured DittoTones instance. The result includes the generated palette, input color details, and generation strategy. Output can be formatted as CSS custom properties or hex values. ```typescript import { DittoTones, type GenerateResult } from 'dittotones'; import { formatCss, formatHex } from 'culori'; import tailwindRamps from 'dittotones/ramps/tailwind'; const ditto = new DittoTones({ ramps: tailwindRamps, gamutMap: true }); // Input: any valid CSS color const result: GenerateResult = ditto.generate('#F97316'); // Tailwind orange-500 // ── result shape ────────────────────────────────────────── // result.inputColor → { mode: 'oklch', l: 0.702, c: 0.191, h: 55.3 } // result.matchedShade → '500' // result.method → 'exact' | 'single' | 'blend' // result.sources → [{ name: 'orange', diff: 0.003, weight: 1 }] // result.scale → { '50': Oklch, '100': Oklch, ..., '950': Oklch } // Convert to CSS custom properties const cssVars = Object.entries(result.scale) .sort(([a], [b]) => Number(a) - Number(b)) .map(([shade, color]) => ` --brand-${shade}: ${formatCss(color)};`) .join('\n'); console.log(`:root {\n${cssVars}\n}`); // :root { // --brand-50: oklch(0.985 0.012 56); // --brand-100: oklch(0.969 0.024 56); // --brand-200: oklch(0.937 0.05 56); // ... // --brand-950: oklch(0.274 0.075 56); // } // Convert to hex for (const [shade, color] of Object.entries(result.scale)) { console.log(`${shade}: ${formatHex(color)}`); } // 50: #fff7ed // 100: #ffedd5 // ... // 950: #431407 // Throws: "Invalid color: not-a-color" // ditto.generate('not-a-color'); ``` -------------------------------- ### Define Custom Color Ramps Source: https://github.com/meodai/dittotones/blob/main/README.md Create and use custom color ramps by defining a Map of ramp names to color objects. This allows for fine-grained control over the generated palette's base colors. ```typescript import { DittoTones } from 'dittotones'; import { parse, oklch, type Oklch } from 'culori'; const customRamps = new Map([ [ 'brand', { '50': oklch(parse('oklch(98% 0.01 250)')) as Oklch, '500': oklch(parse('#3B82F6')) as Oklch, '950': oklch(parse('oklch(25% 0.05 250)')) as Oklch, }, ], ]); const ditto = new DittoTones({ ramps: customRamps }); ``` -------------------------------- ### Inspect DittoTones Generation Results Source: https://context7.com/meodai/dittotones/llms.txt The `GenerateResult` type provides the generated color palette and diagnostic metadata. Inspect the input color, matched shade, generation method, and sources used for blending. Iterate over the `scale` property to access individual shades in various formats. ```typescript import { DittoTones, type GenerateResult, type Oklch } from 'dittotones'; import { formatHex, formatCss } from 'culori'; import radixRamps from 'dittotones/ramps/radix'; const ditto = new DittoTones({ ramps: radixRamps }); const result: GenerateResult = ditto.generate('hsl(271, 81%, 56%)'); // purple // Inspect the result structure console.log(result.inputColor); // { mode: 'oklch', l: 0.515, c: 0.234, h: 301 } console.log(result.matchedShade); // '9' (Radix uses 1–12 scale) console.log(result.method); // 'blend' console.log(result.sources); // [ // { name: 'violet', diff: 0.041, weight: 0.62 }, // { name: 'purple', diff: 0.067, weight: 0.38 } // ] // Iterate over the generated scale for (const [shade, color] of Object.entries(result.scale)) { const hex = formatHex(color) ?? '#000000'; const css = formatCss(color); console.log(`shade ${shade}: ${hex} (${css})`); } // shade 1: #fdfaff (oklch(0.991 0.007 301)) // shade 2: #f8f3ff (oklch(0.974 0.019 301)) // ... // shade 12: #1a0033 (oklch(0.155 0.078 301)) // Check if method was a blend and inspect weights if (result.method === 'blend') { const [s1, s2] = result.sources; console.log(`Blended ${s1.name} (${(s1.weight * 100).toFixed(0)}%) + ${s2.name} (${(s2.weight * 100).toFixed(0)}%)`); // Blended violet (62%) + purple (38%) } ``` -------------------------------- ### Generate CSS Custom Properties from Color Palette Source: https://context7.com/meodai/dittotones/llms.txt Converts a generated color palette into CSS custom properties. It takes an input color, an optional variable name, and a format ('oklch' or 'hex'). The output includes comments detailing the color generation process. ```typescript import { DittoTones } from 'dittotones'; import { tailwindRamps } from 'dittotones/src/ramps/tailwind'; import { formatCss, formatHex, converter } from 'culori'; import type { Oklch } from 'culori'; const ditto = new DittoTones({ ramps: tailwindRamps, gamutMap: true }); function generateCSSVars( inputColor: string, variableName = 'brand', format: 'oklch' | 'hex' = 'oklch' ): string { const result = ditto.generate(inputColor); const info = result.sources.map(s => `${s.name} (${(s.weight * 100).toFixed(0)}%)`).join(' + '); const header = ` /* ${variableName}: ${result.method} from ${info} @ shade ${result.matchedShade} */`; const lines = Object.entries(result.scale) .sort(([a], [b]) => Number(a) - Number(b)) .map(([shade, color]) => { const value = format === 'hex' ? (formatHex(color) ?? '#000000') : (formatCss(color) ?? 'oklch(0 0 0)'); return ` --${variableName}-${shade}: ${value};`; }); return `:root { ${header} ${lines.join(' ')} }`; } console.log(generateCSSVars('#8b5cf6', 'purple', 'oklch')); // :root { // /* purple: exact from violet (100%) @ shade 500 */ // --purple-50: oklch(0.969 0.016 293); // --purple-100: oklch(0.943 0.029 294); // --purple-200: oklch(0.897 0.055 294); // --purple-300: oklch(0.827 0.107 294); // --purple-400: oklch(0.74 0.16 293); // --purple-500: oklch(0.627 0.265 293); // --purple-600: oklch(0.558 0.288 293); // --purple-700: oklch(0.491 0.27 293); // --purple-800: oklch(0.381 0.189 293); // --purple-900: oklch(0.291 0.149 293); // --purple-950: oklch(0.208 0.104 293); // } ``` -------------------------------- ### DittoTones GenerateResult Interface Source: https://github.com/meodai/dittotones/blob/main/README.md Defines the structure of the result object returned by the generate method, including input color details, matching strategy, sources, and the generated color scale. ```typescript interface GenerateResult { inputColor: Oklch; // Parsed input color matchedShade: string; // e.g. "500" method: 'exact' | 'single' | 'blend'; sources: { // Which ramps were used name: string; diff: number; weight: number; }[]; scale: Record; // The generated palette } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.