GODRICH
#cssアニメーション#ボタン#ローディング#トランジション#スクロール#ダッシュボード#実用ui#デザイントレンド

css ジェネレーター 9種 — スライダーで作る css 影のつけ方

css ジェネレーター とは、CSS の値を画面の上でつまんで決める道具のことです — スライダーを動かすとコードが変わります。影・グラデーション・角丸・タイミングを目で合わせ、下のコード欄からそのまま貼って使えます。9つを自作し、グリッドで今動いています。

自動再生 · タイルを押すとその項目へ · すべて1つのzip

並びは画面を組み立てていく順です。まずカードを浮かせる影を敷き(01)、背景に色の流れを置き(02)、角をやわらかく整え(03)、写真を形に切り抜き(04)、トーンを揃え(05)、枠を列に割り(06)、文字に縁を付け(07)、文字サイズの段を決め(08)、最後に動きの速さを合わせます(09)。9つとも値はカスタムプロパティの一か所に留まり、プレビューとつまみとコード欄が同じ値を読む点が骨格です。同じ creative 柱では、種から模様を生やすcss 背景 パターン ジェネレーター 自作9種と、手で物理を操る物理シミュレーション 作り方 9選が隣に並んでいます。

01影の重ね積み

横・縦・ぼかし・広がりの四軸をスライダーで持ち、同じ影を二枚重ねて深さを作る、カードとボタンの影の段階を決めるための道具です。

影の重ね積み
新しいタブで開く

01 · 影の重ね積み — 計測 累積面積 10.931% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

box-shadow@property--shadow-y

四つの値がひとつの場所に留まる仕組み

// 四軸を整数として登録します。登録するとキーフレームで補間され、counter() でも読めます
@property --shadow-x { syntax: "<integer>"; inherits: true; initial-value: 0; }
@property --shadow-y { syntax: "<integer>"; inherits: true; initial-value: 10; }
@property --shadow-blur { syntax: "<integer>"; inherits: true; initial-value: 20; }
@property --shadow-spread { syntax: "<integer>"; inherits: true; initial-value: -4; }

// コード欄の数字も同じプロパティをカウンターとして読みます — 画面とコードが一つの値を見ます
.gen__v--x::after { counter-reset: v var(--shadow-x); content: counter(v); }

値が登録済みの四つのプロパティにだけ留まるので、プレビューカードの box-shadow とつまみの位置とコード欄の数字が全部同じ場所を見ており、グリッドのプレビューはこのプロパティをそのままキーフレームで動かして累積面積 10.93%・強度 110.3・動いたフレーム 23/23 が測定されました。

二枚重ねで深さを作る書き方

box-shadow:
  calc(var(--shadow-x) * 1px) calc(var(--shadow-y) * 1px) calc(var(--shadow-blur) * 1px) calc(var(--shadow-spread) * 1px) rgba(23, 20, 26, .34),
  calc(var(--shadow-x) * .5px) calc(var(--shadow-y) * .5px) calc(var(--shadow-blur) * .5px) 0 rgba(23, 20, 26, .22);

下の一枚は距離とぼかしを半分にした同じ影なので、一枚のときより値を大きくしても輪郭がにじまず、デザインシステムの影の段階を決めるときの基準に置けます。

02角度と停止点のグラデーション

角度スライダーが linear-gradient の方向を回転させ、停止点スライダー二つが色の境目の位置を動かす、ヒーロー背景やボタンの塗りに使う css グラデーション ジェネレーター です。

角度と停止点のグラデーション
新しいタブで開く

02 · 角度と停止点のグラデーション — 計測 累積面積 8.818% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

linear-gradient--grad-angle--grad-stop-b

角度ひとつが向きを決める

// 角度は整数ひとつ。calc が度の単位を付け、停止点二つが色の境目を押します
.gen__preview {
  background: linear-gradient(
    calc(var(--grad-angle) * 1deg),
    $subject-blue calc(var(--grad-stop-a) * 1%),
    $subject-pink calc(var(--grad-stop-b) * 1%)
  );
}

角度は --grad-angle という整数ひとつで calc が度の単位を付けており、45 度から 210 度を通って 315 度まで回る間の測定は累積面積 8.82%・強度 70.1・動いたフレーム 23/23 で、面全体の色が一度に押し流されるぶん明るさの変化が大きく出ます。

つまみの位置も同じ値から出る

.gen__row:nth-child(1) .gen__thumb { --pct: calc(var(--grad-angle) / 360 * 100); }
.gen__row:nth-child(2) .gen__thumb { --pct: var(--grad-stop-a); }
.gen__row:nth-child(3) .gen__thumb { --pct: var(--grad-stop-b); }

つまみの位置も同じプロパティから計算していて、停止点 A を 0 寄りに、B を 100 寄りに離すと二色が広く混ざるので、css グラデーション おしゃれ に見せたいときは境目を離し、線のような区切りが欲しいときは二点を近づけます。

03八つのつまみの角丸

四隅の横半径と共通の縦半径を別々に持ち、スラッシュ構文で書き出す、やわらかい水滴形を作るための道具です。

八つのつまみの角丸
新しいタブで開く

03 · 八つのつまみの角丸 — 計測 累積面積 5.821% · 動いたフレーム 22/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

border-radius--r-tl--r-vy

角ごとの longhand がスラッシュ構文と同じ意味になる

// 角ごとの longhand 二値(横 縦)はスラッシュ構文と同じ意味です — 値がプロパティごとに付くので
// どの角が動いているのかも読み取りやすくなります
border-top-left-radius: calc(var(--r-tl) * 1px) calc(var(--r-vy) * 1px);
border-top-right-radius: calc(var(--r-tr) * 1px) calc(var(--r-vy) * 1px);
border-bottom-right-radius: calc(var(--r-br) * 1px) calc(var(--r-vy) * 1px);
border-bottom-left-radius: calc(var(--r-bl) * 1px) calc(var(--r-vy) * 1px);

右上と左下を大きく、左上と右下を小さくすると水滴のように傾いた形になり、コード欄は border-radius: 12px 48px 12px 48px / 28px という短い構文で出しつつ、プレビュー側は上のように角ごとに展開した形で持っています。

角の点が値に付いて動く

.gen__dot--tl { left: calc(var(--r-tl) * 1px); top: calc(var(--r-vy) * 1px); }
.gen__dot--tr { right: calc(var(--r-tr) * 1px); top: calc(var(--r-vy) * 1px); }
.gen__dot--br { right: calc(var(--r-br) * 1px); bottom: calc(var(--r-vy) * 1px); }
.gen__dot--bl { left: calc(var(--r-bl) * 1px); bottom: calc(var(--r-vy) * 1px); }

黒い点が四つ、半径のぶんだけ内側へ寄ってどの角が動いたかを示し、測定は累積面積 5.82%・強度 133.6・動いたフレーム 22/23 でした。

04多角形の頂点クリップ

六つの頂点の半径をスライダーで動かすとその場で画像が切り抜かれ、座標の並びがそのまま clip-path のコードになる道具です。

多角形の頂点クリップ
新しいタブで開く

04 · 多角形の頂点クリップ — 計測 累積面積 6.233% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

clip-pathpolygonpointerdown

CSS と JS が同じ係数表を共有する

// 六つの頂点は 60 度間隔で固定。各頂点の (dx, dy) 係数表 — CSS と JS が同じ表を共有します
// (60 度間隔の六方向の単位円座標、小数第三位で四捨五入)。中心 50% から係数のぶんだけ伸びます
var DIRS = [
  [0, -1], [0.87, -0.5], [0.87, 0.5], [0, 1], [-0.87, 0.5], [-0.87, -0.5]
];

プレビューの切り抜きは scss の calc が描き、コピーされる文字列は JS が組み立てますが、二つが同じ係数表を読むので画面とコピーしたコードが食い違わず、頂点の半径を互い違いに動かすと六角形が星のように呼吸して測定は累積面積 6.23%・強度 127.8・動いたフレーム 23/23 でした。

縦の係数で平たく押さえる

.gen__preview {
  box-sizing: border-box; width: 116px; height: 72px;
  background: $subject-blue;
  clip-path: polygon(
    calc(50% + 0 * var(--v0) * 1%) calc(50% + -1 * var(--v0) * 0.6 * 1%),
    calc(50% + 0.87 * var(--v1) * 1%) calc(50% + -0.5 * var(--v1) * 0.6 * 1%),
    calc(50% + 0.87 * var(--v2) * 1%) calc(50% + 0.5 * var(--v2) * 0.6 * 1%)
  );
}

縦の座標に 0.6 を掛けてプレビュー枠の比率に合わせ(六頂点とも同じ係数です)、破線の枠が切り抜かれる前の大きさを残すので、どこが切り落とされたのかを目で追えます。

05フィルターチェーンのプリセット

明るさ・コントラスト・彩度・ぼかしを一行につなぎ、プリセットボタンが四つの値を一度に動かす、写真ギャラリーのトーンを決める道具です。

フィルターチェーンのプリセット
新しいタブで開く

05 · フィルターチェーンのプリセット — 計測 累積面積 9.446% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

filtersaturatedata-preset

順番が変わると結果も変わる

filter:
  brightness(calc(var(--f-bri) * 1%))
  contrast(calc(var(--f-con) * 1%))
  saturate(calc(var(--f-sat) * 1%))
  blur(calc(var(--f-blur) * 1px));

フィルターは左から順に掛かるので blur を先頭へ置けばぼけた上にコントラストが乗って結果が変わりますが、brightness から blur までがよく使われる並びで、彩度が 0 まで落ちる瞬間を含む測定は累積面積 9.45%・強度 96.3・動いたフレーム 23/23 でした。

プリセットは四つの値を一度に入れる

// プリセットボタン — 四つの値を一度にプロパティへ入れます。値の出どころがボタンだと data-preset で示します
Array.prototype.forEach.call(gen.querySelectorAll('.gen__preset'), function (btn) {
  btn.addEventListener('click', function () {
    gen.classList.remove('is-demo');
    btn.dataset.preset.split(',').forEach(function (val, i) {
      gen.style.setProperty(VARS[i], val);
      ranges[i].value = val;
    });
  });
});

data-preset 属性に四つの値がカンマ区切りで入っているのでボタン一つがそのまま保存済みの値一式になり、操作実測ではボタンを押した瞬間にスライダー四本とコード欄が一緒に変わることを確かめました。

06グリッドの列と間隔

最小幅と間隔をスライダーで持つと repeat() 構文がそのまま書き出され、auto-fit が列の数を画面幅に任せるレスポンシブなグリッドの道具です。

グリッドの列と間隔
新しいタブで開く

06 · グリッドの列と間隔 — 計測 累積面積 10.212% · 動いたフレーム 22/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

grid-template-columnsrepeatminmax

列数のスライダーを置かない理由

.gen__preview {
  display: grid; align-content: start;
  grid-template-columns: repeat(auto-fit, minmax(calc(var(--g-min) * 0.32px), 1fr));
  gap: 0;
  overflow: hidden;
}

auto-fit と minmax は列の数を数えずに最小幅に任せる書き方なので、スライダーは最小幅と間隔だけを持ち、最小幅が大きくなると列が何行かに折り返し、小さくすると一行に伸びて測定は累積面積 10.21%・強度 169.7・動いたフレーム 22/23 でした。

列の間隔は列自身の枠で作る

// 桁の間隔は gap ではなく透明な border で与えます(297 の先例) — 桁自身の一部なので
// 値が小数 px になってもグリッドの目盛り規則を破りません。左右と上下へ半分ずつ分けて付けます
.gen__cell {
  height: 20px; border-radius: $r-xs; box-sizing: border-box;
  border: calc(var(--g-gap) * 0.32px / 2) solid transparent;
  background-clip: padding-box;
}

間隔の値を列ごとの透明な枠に分けて付けると小数ピクセルになっても並びが均一に保たれ、プレビューの係数 0.32 は枠が実寸より小さいための表示倍率であって、列の数の変化はそのまま見えます。

07文字の縁取りの太さ

縁の太さと塗りの不透明度をスライダーで持ち、中を抜くとステッカーのような文字になる、写真の上の大見出し向けの道具です。

文字の縁取りの太さ
新しいタブで開く

07 · 文字の縁取りの太さ — 計測 累積面積 2.958% · 動いたフレーム 22/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

-webkit-text-stroketext-shadowpaint-order

縁を内側に描く paint-order

.gen__word {
  color: rgba(255, 247, 230, calc(var(--t-fill) * 1%));
  -webkit-text-stroke: calc(var(--t-sw) * 1px) $subject-ink;
  paint-order: stroke fill;
  font-family: $mono; font-weight: 900; font-size: 44px; line-height: 1;
}

paint-order: stroke fill は SVG の作法を CSS が受け継いだプロパティで、縁を文字の内側へ描いて細い字が潰れないようにし、塗りを 0 まで下げると中抜きの文字になって測定は累積面積 2.96%・強度 123.8・動いたフレーム 22/23 でした。

古いブラウザ向けの影による代替

// 四方向(斜めを含む八方向)へ同じオフセットの影を置いて縁取りを真似ます
var shades = [];
for (var dx = -1; dx <= 1; dx++) {
  for (var dy = -1; dy <= 1; dy++) {
    if (dx === 0 && dy === 0) continue;
    shades.push((dx * sw) + 'px ' + (dy * sw) + 'px 0 #17141a');
  }
}
return 'text-shadow: ' + shades.join(', ') + ';';

-webkit-text-stroke が効かない環境では八方向へ同じオフセットの text-shadow を置いて同じ太さを真似ており、コピーボタンを押すと二つのコードが一緒に入るので貼った先で選んで使えます。

08タイポ倍率のはしご

基準サイズと倍率ひとつで六段階を計算してはしごで見せ、その値がカスタムプロパティの一覧として出てくる、文字サイズを決めるための道具です。

タイポ倍率のはしご
新しいタブで開く

08 · タイポ倍率のはしご — 計測 累積面積 42.674% · 動いたフレーム 21/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

clampMath.pow--step-3

掛ける回数だけが段ごとに違う

.gen__step--1 { font-size: calc(var(--s-base) * 0.6px); }
.gen__step--2 { font-size: calc(var(--s-base) * 0.6px * var(--s-ratio) * 0.01); }
.gen__step--3 { font-size: calc(var(--s-base) * 0.6px * var(--s-ratio) * var(--s-ratio) * 0.0001); }
.gen__step--4 { font-size: calc(var(--s-base) * 0.6px * var(--s-ratio) * var(--s-ratio) * var(--s-ratio) * 0.000001); }

CSS の calc には累乗がないので倍率を掛ける回数だけ書き並べますが、式は一つで掛ける回数だけが段ごとに違うため、倍率を一つ動かすとはしご全体が一緒に呼吸し、六行すべてがサイズを変えるので測定は累積面積 42.67%・強度 153.5・動いたフレーム 21/23 でした。

コピーされるのは rem の一覧

// 基準サイズと倍率 — 六段のはしごが calc の掛け算で自然に追従します(scss の calc と同じ式)。
// プロパティは --s-base(整数 px) と --s-ratio(百分率の整数) の二つだけです
var base = readVar('--s-base');
var ratio = readVar('--s-ratio') / 100;
var lines = [];
for (var i = 1; i <= 6; i++) {
  lines.push('--step-' + i + ': ' + (Math.round(base * Math.pow(ratio, i - 1) * 100) / 100 / 16).toFixed(3) + 'rem;');
}

コピーボタンを押すと --step-1 から --step-6 までの rem 値が :root のブロックとして出てきて、プレビューの 0.6 という係数は六段を枠内へ収めるための表示倍率にすぎず、段と段の比率はそのまま保たれます。

09タイミング値のプレビュー

長さ・遅延・繰り返しをスライダーで持つと、横のボールがその値ですぐに跳ね直る、アニメーションの速さを決めるための道具です。

タイミング値のプレビュー
新しいタブで開く

09 · タイミング値のプレビュー — 計測 累積面積 3.245% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · パスワードを見る

animation-durationanimation-delaygetAnimations

一括指定ではなく個別プロパティを使う理由

// 個別プロパティ — 一括指定が delay をリセットする落とし穴を避けます
animation-name: ballRun;
animation-duration: calc(var(--a-dur) * 1ms);
animation-delay: calc(var(--a-delay) * 1ms);
animation-iteration-count: var(--a-iter);
animation-timing-function: $easing;
animation-direction: alternate;
animation-fill-mode: both;

animation: の一括指定は書かれていない個別プロパティを初期値へ戻すため、遅延を別に持たせた瞬間にそれを消してしまうので、この道具は最初から個別プロパティで書いており、値が行き来する間もボールはその値のまま往復して測定は累積面積 3.25%・強度 146.0・動いたフレーム 23/23 でした。

値を変えたらボールを立て直す

// 値が変わったらボールのアニメーションを作り直します — getAnimations で掴んで時計を 0 に戻します
function restartBall() {
  ball.getAnimations().forEach(function (a) {
    a.cancel();
  });
  // cancel のあと同じ名前のアニメーションが作り直されます — CSS シートがそのまま残っているからです
}

スライダーを動かすと getAnimations() で掴んだアニメーションを取り消し、CSS 側の定義は残っているので同じ名前で作り直され、新しい duration で 0 から始まります。

css 影 ジェネレーター はスライダー何本で作れるか?

影を決める値は横・縦・ぼかし・広がり・色の五つですが、色はデザインシステムが先に決めている場合が多いため、この道具は四本のスライダーから始めます。感覚で 16px と打つより、スライダーを 2px ずつ往復させてカードが浮いて見える位置を探すほうが早いです。二枚重ねの下の一枚は上の半分の値で付いてくるので、実際に触る軸は四本で足ります。

九つの調整値と出てくるコード

項目 調整値 出てくるコード
01 影 横・縦・ぼかし・広がり box-shadow 二枚
02 グラデーション 角度・停止点 A・停止点 B linear-gradient
03 角丸 四隅の横半径・共通の縦半径 border-radius スラッシュ
04 多角形 六頂点の半径 clip-path: polygon
05 フィルター 明るさ・コントラスト・彩度・ぼかし filter チェーン
06 グリッド 最小幅・間隔 grid-template-columns
07 文字の縁 太さ・塗り -webkit-text-stroke と color
08 タイポ 基準・倍率 --step-1 から --step-6
09 タイミング 長さ・遅延・繰り返し animation- の個別プロパティ

値はすべてカスタムプロパティを通じて流れるので、一つの項目のプレビューとつまみとコード欄がずれることはありません。操作実測でも9項目ともスライダーを動かすと値・プレビュー・コードが一緒に動きました(run/073/_조작실측.json)。

どこで壊れるか — 落とし穴

最初に壊れたのは 320px 幅でした。input[type=range] は自前の最小幅を持っていて、その幅がグリッドのトラックごとにカードを押し広げ、狭い画面で本文の枠からはみ出しました。直し方は三段階です。トラックを repeat(2, minmax(0, 1fr)) にして最小値を 0 に落とし、スライダーの列と入力に min-width: 0 を与え、カード自身もステージのグリッド項目なので自動最小幅を打ち消しました。直したあとは9つとも本文の 320×200 に収まり、モバイル幅のはみ出しは9件とも 0 です。

二つ目はコード欄の数字でした。値が CSS カウンターで描かれる場所は textContent に出てこず、計算値を取得しても counter(v) という文字列がそのまま返ってきます。操作実測のプローブは結局、コード欄を撮ったピクセルのハッシュで数字が変わったことを示しました。目に見えるものと機械が読むものが違う典型です。三つ目はクリップボードでした。iframe の中では navigator.clipboard.writeText が拒否されたプロミスを返し、try/catch だけでは捕まらないので、.catch で選択によるコピー(画面外の textarea を選んでコピー)へ切り替えています。9ファイルとも同じ形です。この9ファイルをまとめた zip のパスワードは ycejvu79 で、開くと vanilla 版と React 版が一緒に入っています。

アクセシビリティ

prefers-reduced-motion が有効な環境では、グリッドのプレビューの値の巡回が止まり、09 のボールも開始位置で止まります。要素そのものが消えることはありません。スライダーはネイティブの input[type=range] なので矢印キーで値が動き(操作実測もキーボードで行いました)、ラベルは aria-label で付いており、フォーカスリングは focus-visible ではっきり出ます。コピーボタンを押したあとは文字が短く変わるので、結果を目で確かめられます。

FAQ

css 影のつけ方 として二行そのままコピーしてもよいか

大丈夫です。一行目が主の影、二行目が半分の値で支える影です。カード一枚なら一行目だけで足りますし、浮いている感じを強めたいときに二行を一緒に使ってください。

コード欄の数字が変な単位に見えるのは直すべきか

いいえ。コード欄の数字は画面と同じカスタムプロパティを読むカウンターなので、出力される文字列と同じ値です。コピーボタンを押せば JS が組み立てた完全なコードが入るので、そのまま貼って使えます。

linear-gradient を radial や conic に変えたいとき

02 のコードの linear-gradient(角度, 色 停止点) を radial-gradient(原点, 色 停止点) や conic-gradient(開始角, 色 停止点) へ名前だけ変えれば動きます。停止点の構文は三つとも同じなので、境目の値はそのまま移せます。

参考

解凍パスワードを入力してください

パスワードはこの記事の本文の中にあります。読み進めると出てきます。