本文へ移動
aapp-hacks
← 記事一覧へ
著者
app-hacks
公開日
更新日
確認日

CSS / グラデーション / JavaScript

CSSグラデーション生成ツールの設計と実装

線形・放射状グラデーション、色停止点、CSS出力、コピー処理を備えるCSSグラデーション生成ツールの設計を解説します。

CSSグラデーション生成ツールの設計と実装

CSSグラデーションのコードを手で組み立てると、色を追加したときに位置がずれたり、画面のプレビューとコピーしたCSSが一致しなかったりします。生成ツールでは、入力を検証してから一つの状態にまとめ、プレビューとコード出力の両方を同じ処理から作ることが重要です。

この記事では、linear・radial・conicの3種類を扱う場合の設計を説明します。実際に設定と出力を試す場合は、CSSグラデーションジェネレーターを利用できます。

色、位置、種類の入力を検証し、共通のCSS生成処理でプレビューとコード出力を作り、コピー結果を通知する流れ

入力を一つの状態として持つ

種類、色停止点、角度、形状、中心位置を別々の値として場当たり的に扱うと、種類を切り替えたときに古い値が残りやすくなります。グラデーション全体を表す状態を用意し、色と位置は必ず同じ停止点オブジェクトに入れます。

type ColorStop = { color: string; position: number };

type GradientOptions = {
  type: 'linear' | 'radial' | 'conic';
  stops: ColorStop[];
  angle: number;
  radialShape: 'circle' | 'ellipse';
  radialPosition: string;
  conicAngle: number;
  conicPosition: string;
};

色と位置を別配列にすると、行の追加・削除・並べ替えで対応関係を失いやすくなります。停止点は2〜5個のように範囲を決めておくと、UIと検証の条件を必要以上に増やさずに済みます。

プレビューとコード出力を同じ関数から作る

プレビュー用とコピー用で別々にCSSを生成すると、片方だけ修正されて表示と出力が食い違います。グラデーション値を組み立てる関数を一つにし、コピーする場合だけbackground:を付けます。

function gradientValue(options: GradientOptions) {
  const stops = options.stops
    .map(({ color, position }) => `${color} ${position}%`)
    .join(', ');

  if (options.type === 'radial') {
    return `radial-gradient(${options.radialShape} at ${options.radialPosition}, ${stops})`;
  }
  if (options.type === 'conic') {
    return `conic-gradient(from ${options.conicAngle}deg at ${options.conicPosition}, ${stops})`;
  }
  return `linear-gradient(${options.angle}deg, ${stops})`;
}

function backgroundDeclaration(options: GradientOptions) {
  return `background: ${gradientValue(options)};`;
}

プレビューにはgradientValue()の戻り値を、コード欄にはbackgroundDeclaration()の戻り値を使います。この形なら、種類を変更しても色停止点の扱いは共通です。

入力を正規化してから検証する

HEXカラーは#の有無や英字の大文字・小文字をそろえ、位置と角度は数値に変換してから使います。不正な入力の間は最後に有効だったプレビューを残すと、入力途中に画面全体が崩れることを防げます。

確認する項目は、停止点が2〜5個であること、色が許可したHEX形式であること、位置が0〜100の範囲で重複していないこと、角度が0〜360の範囲であることです。停止点は位置順に並べてから出力すれば、入力行を操作した順番に左右されないCSSになります。同じ位置を許可すると意図的な色の切り替えは可能ですが、基本機能では誤操作との区別が難しいため、エラーにする方が扱いやすくなります。

コピーの成功・失敗を利用者に伝える

コピーにはnavigator.clipboard.writeText()を使えますが、権限や実行環境によって失敗する可能性があります。コピーできた場合とできなかった場合の両方を、画面上のテキストで伝えます。

async function copyCss(css: string) {
  try {
    await navigator.clipboard.writeText(css);
    return { ok: true, message: 'CSSをコピーしました。' };
  } catch {
    return { ok: false, message: 'コピーできませんでした。コードを選択してコピーしてください。' };
  }
}

失敗時には次に取る操作を示すことが大切です。また、生成されたCSSや色は利用者が作成した内容です。利用状況を計測する場合も、コードそのものを送らず、コピー操作が行われたかどうかなど必要最小限の情報にとどめます。

出力をテストして変更に強くする

文字列を返す関数は、入力と期待するCSSを対応させてテストできます。linear・radial・conicの各形式、停止点の並び替え、境界値、不正なHEXを確認しておけば、画面の実装を変更しても出力形式を守れます。

グラデーションは背景に使われることが多いため、実装後は文字を重ねたときの読みやすさも確認してください。CSSが正しく生成されることと、配色が読みやすいことは別の問題です。生成、検証、コピーの責務を分けることで、後から機能を追加しても壊れにくいツールになります。