diff --git a/CHANGELOG.md b/CHANGELOG.md index a62ef33..5141e79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 [bdsg 0.2.0]: https://github.com/CarlosEduJs/bdsg/releases/tag/bdsg@v0.2.0 +## [bdsg-cli 0.2.0] - 2026-01-14 + +### Added + +- **Gradient Command**: New `bdsg generate gradient` command + - Generate color gradients with OKLCH interpolation + - Easing functions: `linear`, `easeIn`, `easeOut`, `easeInOut` + - Hue direction control: `shorter`, `longer`, `increasing`, `decreasing` + - Output CSS variables and gradient strings + - JSON export with gradient metadata + +### Changed + +- **Dependencies**: Updated `bdsg` dependency to `^0.2.0` + +[bdsg-cli 0.2.0]: https://github.com/CarlosEduJs/bdsg/releases/tag/bdsg-cli@v0.2.0 + ## [bdsg-cli 0.1.2] - 2026-01-12 ### Fixed diff --git a/packages/bdsg-cli/README.md b/packages/bdsg-cli/README.md index 6f051bb..a2cea83 100644 --- a/packages/bdsg-cli/README.md +++ b/packages/bdsg-cli/README.md @@ -36,6 +36,7 @@ bdsg generate palette "#3B82F6" -n primary bdsg generate typography -r golden-ratio bdsg generate spacing -m fibonacci bdsg generate shadows -s material +bdsg generate gradient "#FF0000" "#0000FF" -s 5 # Validate WCAG contrast bdsg validate "#3B82F6" "#FFFFFF" @@ -159,6 +160,51 @@ Options: Available styles: `material`, `soft`, `hard` +#### generate gradient + +*New in v0.2.0* + +```bash +bdsg generate gradient [options] + +Arguments: + startColor Start color in hex format (e.g., #FF0000) + endColor End color in hex format (e.g., #0000FF) + +Options: + -n, --name Gradient name (default: "gradient") + -s, --steps Number of color steps (default: "5") + -e, --easing Easing function (default: "linear") + -d, --direction Hue direction (default: "shorter") + -o, --output Output directory (default: "./tokens") + -f, --format Output format: css, json (default: "css") +``` + +Available easing: `linear`, `easeIn`, `easeOut`, `easeInOut` + +Available hue directions: `shorter`, `longer`, `increasing`, `decreasing` + +**Example:** + +``` +$ bdsg generate gradient "#FF0000" "#0000FF" -s 5 -e easeInOut + +✔ Gradient generated! + +File: ./tokens/gradient.css + +Gradient colors: + 1: #ff0000 + 2: #e8007b + 3: #ba00c2 + 4: #7a00f4 + 5: #0000ff + +CSS Gradients: + linear: linear-gradient(90deg, #ff0000, #e8007b, #ba00c2, #7a00f4, #0000ff) + radial: radial-gradient(circle, #ff0000, #e8007b, #ba00c2, #7a00f4, #0000ff) +``` + ### validate Validate WCAG contrast between two colors with suggestions for accessibility compliance. diff --git a/packages/bdsg-cli/package.json b/packages/bdsg-cli/package.json index 6c28018..89dc28a 100644 --- a/packages/bdsg-cli/package.json +++ b/packages/bdsg-cli/package.json @@ -1,6 +1,6 @@ { "name": "bdsg-cli", - "version": "0.1.2", + "version": "0.2.0", "description": "CLI for design system generation", "type": "module", "bin": { @@ -11,7 +11,7 @@ "dev": "tsc --watch" }, "dependencies": { - "bdsg": "^0.1.3", + "bdsg": "^0.2.0", "commander": "^12.0.0", "inquirer": "^9.2.0", "chalk": "^5.3.0", diff --git a/packages/bdsg-cli/src/commands/generate.ts b/packages/bdsg-cli/src/commands/generate.ts index 77022f1..328c512 100644 --- a/packages/bdsg-cli/src/commands/generate.ts +++ b/packages/bdsg-cli/src/commands/generate.ts @@ -1,10 +1,13 @@ import { mkdir, writeFile } from "node:fs/promises"; import { join } from "node:path"; import { + EASING, + generateGradient, generatePalette, generateShadows, generateSpacingScale, generateTypographyScale, + toCssGradient, } from "bdsg"; import chalk from "chalk"; import { Command } from "commander"; @@ -233,4 +236,118 @@ export const generateCommand = new Command("generate") process.exit(1); } }), + ) + .addCommand( + new Command("gradient") + .description("Generate a color gradient") + .argument("", "Start color in hex format (e.g., #FF0000)") + .argument("", "End color in hex format (e.g., #0000FF)") + .option("-n, --name ", "Gradient name", "gradient") + .option("-s, --steps ", "Number of color steps", "5") + .option( + "-e, --easing ", + "Easing function (linear, easeIn, easeOut, easeInOut)", + "linear", + ) + .option( + "-d, --direction ", + "Hue direction (shorter, longer, increasing, decreasing)", + "shorter", + ) + .option("-o, --output ", "Output directory", "./tokens") + .option("-f, --format ", "Output format (css, json)", "css") + .action(async (startColor, endColor, options) => { + const spinner = ora("Generating gradient...").start(); + + try { + const steps = Number.parseInt(options.steps, 10); + if (Number.isNaN(steps)) { + spinner.fail(chalk.red("Invalid steps value")); + console.error( + chalk.yellow(` Steps must be a number, got: "${options.steps}"`), + ); + process.exit(1); + } + + const validEasings = ["linear", "easeIn", "easeOut", "easeInOut"]; + if (!validEasings.includes(options.easing)) { + spinner.fail(chalk.red("Invalid easing function")); + console.error( + chalk.yellow( + ` Valid options: ${validEasings.join(", ")}\n Got: "${options.easing}"`, + ), + ); + process.exit(1); + } + const easingFn = EASING[options.easing as keyof typeof EASING]; + + const validFormats = ["css", "json"]; + if (!validFormats.includes(options.format)) { + spinner.fail(chalk.red("Invalid output format")); + console.error( + chalk.yellow( + ` Valid options: ${validFormats.join(", ")}\n Got: "${options.format}"`, + ), + ); + process.exit(1); + } + + const colors = generateGradient(startColor, endColor, steps, { + easing: easingFn, + hueDirection: options.direction, + }); + + await mkdir(options.output, { recursive: true }); + + if (options.format === "json") { + const gradient: Record = { + name: options.name, + start: startColor, + end: endColor, + steps, + colors, + css: { + linear: toCssGradient("linear", colors, 90), + radial: toCssGradient("radial", colors), + }, + }; + await writeFile( + join(options.output, `${options.name}.json`), + JSON.stringify({ gradient }, null, 2), + ); + } else { + let css = ":root {\n"; + for (let i = 0; i < colors.length; i++) { + css += ` --${options.name}-${i + 1}: ${colors[i]};\n`; + } + css += ` --${options.name}-linear: ${toCssGradient("linear", colors, 90)};\n`; + css += ` --${options.name}-radial: ${toCssGradient("radial", colors)};\n`; + css += "}\n"; + await writeFile(join(options.output, `${options.name}.css`), css); + } + + spinner.succeed(chalk.green("Gradient generated!")); + console.log( + chalk.dim( + `\nFile: ${options.output}/${options.name}.${options.format}`, + ), + ); + console.log(); + + // Show preview + console.log(chalk.bold("Gradient colors:")); + for (let i = 0; i < colors.length; i++) { + console.log(` ${i + 1}: ${colors[i]}`); + } + console.log(); + console.log(chalk.bold("CSS Gradients:")); + console.log(` linear: ${toCssGradient("linear", colors, 90)}`); + console.log(` radial: ${toCssGradient("radial", colors)}`); + console.log(); + } catch (error) { + spinner.fail(chalk.red("Failed to generate gradient")); + console.error(error); + process.exit(1); + } + }), );