GODRICH

ペーパーカット デザインのLPパーツ9選、コピペOK

ペーパーカット デザインは、はさみで切って貼ったような面をぼかし0の影だけで立ち上げ、色は決められた5色の中から選ぶスタイルです。紙のテクスチャ画像を1枚も敷かず、影の値ひとつで紙らしさを出します。

Pinterestで「paper cutout」として保存されている作例の多くはPhotoshopで作った画像なので、そのままWebに持ち込むと重くなります。ここでは画像を1枚も使わず、CSSの値だけで同じ印象を出しました。まずは下のワンページをスクロールして、紙に見える理由がどこにあるのか探してみてください。

ページを新しいタブで開く

そのページを組み立てている9個のパーツを、下のグリッドに1つずつ取り出して並べました。順番は人気順ではなく、1枚の紙が画面の上でたどる順序です。切って置く、位置を移す、重ねる、スタンプする、角を折る、テープで留める、ちぎる、流す、最後に押し込む、という流れになっています。

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

01切り貼りのヒーロー

白い紙が浮き上がるあいだ、背面の色紙3枚は逆に沈みます。動く向きが逆になって初めて、重なりが層として見えます。影は紙が上がった分だけ広がって薄くなります。

겹친 층반대 위상radius 16
.heropaper__sheet {
  box-shadow: 0 10px 18px rgba(42, 35, 32, .14);
  animation: sheetLift 2s ease-out infinite;
}
.heropaper__scrap { animation: scrapDrift 2s ease-out infinite; }
.heropaper__scrap--butter { animation-delay: -.66s; }
.heropaper__scrap--leaf   { animation-delay: -1.33s; }

@keyframes sheetLift {
  0%, 100% { transform: translateY(0);      box-shadow: 0 10px 18px rgba(42, 35, 32, .14); }
  50%      { transform: translateY(-12px);  box-shadow: 0 22px 28px rgba(42, 35, 32, .1); }
}
@keyframes scrapDrift {
  0%, 100% { transform: translateY(0) rotate(-2deg); }
  50%      { transform: translateY(8px) rotate(3deg); }
}

02持ち上がる紙タブナビ

下線が滑る代わりに、黄色い紙のチップがタブからタブへ跳んで移ります。移動のたびに5px持ち上がるので、滑っているのではなく、つまんで置き直しているように見えます。

고정 폭 탭왕복 이동radius 12
.navpaper__tabs { --step: 76px; }        /* タブ幅72 + 間隔4 */
.navpaper__tab  { flex: 0 0 72px; }      /* 固定幅なので移動量と実際の幅がずれない */
.navpaper__chip { animation: chipHop 2s cubic-bezier(.2, .8, .2, 1) infinite; }

@keyframes chipHop {
  0%   { transform: translateX(0) translateY(0); }
  12%  { transform: translateX(0) translateY(-5px); }
  25%  { transform: translateX(var(--step)) translateY(0); }
  50%  { transform: translateX(calc(var(--step) * 2)) translateY(0); }
  75%  { transform: translateX(var(--step)) translateY(0); }
  100% { transform: translateX(0) translateY(0); }
}

03色紙の帯カードグリッド

白いカード3枚の上端に、それぞれ違う色紙の帯を載せました。カード本体はすべて白のままで、色が入るのは帯とアイコンだけです。3枚は0.22秒ずつずれて順番に持ち上がります。

시차 0.22s머리띠 색종이radius 16
.cardpaper { display: flex; gap: 12px; align-items: stretch; }
.cardpaper__card {
  box-shadow: 0 6px 0 rgba(42, 35, 32, .16);   /* ぼかし0 = 紙の厚み */
  animation: cardPeel 2s cubic-bezier(.2, .8, .2, 1) infinite;
}
.cardpaper__card::before {                      /* 上端に載せた色紙の帯 */
  content: ""; position: absolute; left: 12px; right: 12px; top: -8px;
  height: 16px; border-radius: 4px;
}
.cardpaper__card--sky  { animation-delay: -.22s; }
.cardpaper__card--leaf { animation-delay: -.44s; }

@keyframes cardPeel {
  0%, 100% { transform: translateY(0);     box-shadow: 0 6px 0 rgba(42, 35, 32, .16); }
  45%      { transform: translateY(-14px) rotate(-2deg); box-shadow: 0 18px 8px rgba(42, 35, 32, .12); }
}

04シールのチェックリスト

丸いシール4枚が順番に貼り付きます。0.45倍まで潰れ、1.18倍に跳ね返り、元に戻るという3段階が、判子を押したような手応えを作ります。シールは4枚で色も4色なので、1枚が1色ずつ受け持ちます。

scale 0.45→1.18네 색 스티커radius 50%
.stickerlist__dot {
  box-shadow: 0 3px 0 rgba(42, 35, 32, .2);
  animation: stickerStamp 2s cubic-bezier(.2, 1.4, .4, 1) infinite;
}
.stickerlist__row:nth-child(2) .stickerlist__dot { animation-delay: -1.75s; }
.stickerlist__row:nth-child(3) .stickerlist__dot { animation-delay: -1.5s; }
.stickerlist__row:nth-child(4) .stickerlist__dot { animation-delay: -1.25s; }

@keyframes stickerStamp {
  0%, 60%, 100% { transform: scale(1) rotate(0deg); }
  10%           { transform: scale(.45) rotate(-12deg); }
  22%           { transform: scale(1.18) rotate(6deg); }
  34%           { transform: scale(1) rotate(0deg); }
}

05角折れ料金プラン

おすすめプランの右上の角が、紙の裏面の色を見せながら折れて、また開きます。三角形はclip-pathで一度切り抜いておき、折れる動きは角の頂点に軸を置いたscaleひとつで済ませます。

clip-path 삼각형transform-origin 100% 0radius 16
.dogear__plan { position: relative; overflow: hidden; }
.dogear__fold {
  position: absolute; top: 0; right: 0; width: 40px; height: 40px;
  background: #f6c453;                          /* 紙の裏面の色 */
  clip-path: polygon(100% 0, 0 0, 100% 100%);
  transform-origin: 100% 0;                     /* 角の頂点を軸に折る */
  animation: cornerFold 2s ease-out infinite;
}
@keyframes cornerFold {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(.28); }
}

06テープ留めのレビュー

メモの上辺の両端をマスキングテープで押さえています。回転の軸を紙の外、12px上に置くと、同じ角度でも下端の振れ幅がずっと大きくなり、テープにぶら下がった重さが出ます。

transform-origin 50% -12px테이프 두 장radius 8
.tapenote {
  transform-origin: 50% -12px;                  /* 軸は紙の外 — 下端が大きく動く */
  animation: noteSway 2s cubic-bezier(.2, .8, .2, 1) infinite;
}
.tapenote__tape    { position: absolute; top: -10px; width: 56px; height: 20px; opacity: .92; }
.tapenote__tape--l { left: 16px;  transform: rotate(-6deg); }
.tapenote__tape--r { right: 16px; transform: rotate(5deg); }

@keyframes noteSway {
  0%, 100% { transform: rotate(-1.6deg); }
  50%      { transform: rotate(1.6deg); }
}

07ちぎれ端のCTA帯

上下をギザギザにちぎった帯です。ギザギザは画像ではなくマスク3枚で、上下を削るconic-gradientが2枚と本体を残すlinear-gradientが1枚という構成になっていて、動いているのはボタンだけです。ボタンが沈んだ分だけ影の厚みも減ります。

conic-gradient 마스크그림자=두께radius 12
.tornband {
  mask:
    conic-gradient(from -45deg at bottom, #0000, #000 1deg 89deg, #0000 90deg) bottom / 14px 7px repeat-x,
    conic-gradient(from 135deg at top,    #0000, #000 1deg 89deg, #0000 90deg) top    / 14px 7px repeat-x,
    linear-gradient(#000 0 0) center / 100% calc(100% - 14px) no-repeat;
}
.tornband__btn { animation: paperPress 2s cubic-bezier(.2, .8, .2, 1) infinite; }

@keyframes paperPress {
  0%, 100% { transform: translateY(-8px); box-shadow: 0 14px 0 rgba(42, 35, 32, .3); }
  50%      { transform: translateY(0);    box-shadow: 0 2px 0 rgba(42, 35, 32, .3); }
}

08色紙ストリップのフッター

一番下の帯には4色の紙片を2組並べ、1組分だけ流します。一周した瞬間が最初のフレームとピクセル単位で同じになるので、継ぎ目が見えません。間隔をgapで与えると、この計算が崩れます。

두 벌 복제margin-right 간격translateX(-50%)
.pfoot__track {
  display: flex;
  width: max-content;
  animation: stripFlow 2s linear infinite;
}
.pfoot__bit {
  width: 36px; height: 20px;
  margin-right: 12px;        /* 2N枚の間のgapは2N-1個しかないので-50%がずれる */
  border-radius: 4px;
}
@keyframes stripFlow {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

09紙のボタン・入力欄

2つのボタンが半周期ずれて6px沈みます。影の6pxがそもそも紙の厚みなので、押し込むと厚みが消えるのが物理的に正しい見え方です。入力欄は逆に、内側の影で彫った溝にします。

그림자=두께inset 홈radius 12
.paperui__field {
  box-shadow: inset 0 3px 0 #e7dccb;            /* 彫った溝 — 外側の影を一緒に付けない */
}
.paperui__btn--solid { box-shadow: 0 6px 0 rgba(42, 35, 32, .28); }
.paperui__btn--ghost {
  box-shadow: 0 6px 0 #e7dccb, inset 0 0 0 2px #e7dccb;
  animation-delay: -1s;                          /* 半周期あと — 2つが交互に沈む */
}
@keyframes btnPress {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(6px); }
}

tailwind.config カスタム

このスタイルで1か所にまとめるべきなのは、色ではなくです。床に付いた紙の厚み(shadow-paper)と、持ち上げたときのぼけた影(shadow-lift)を混ぜて使うと紙に見えなくなるので、名前をつける段階で分けておきます。

theme: {
  extend: {
    colors: {
      paper: "#fbf6ec", sheet: "#ffffff", ink: "#2a2320", stone: "#5c5048",
      coral: "#cc3d26", sky: "#2f6fb8", leaf: "#2b7d5a",
      butter: "#f6c453", blush: "#f6b3ab", edge: "#e7dccb",
    },
    borderRadius: { card: "16px", control: "12px", chip: "4px", pill: "999px" },
    boxShadow: {
      paper: "0 6px 0 rgba(42,35,32,.16)",      // 床に付いた紙 = 厚み
      "paper-edge": "0 6px 0 #e7dccb",
      lift: "0 18px 8px rgba(42,35,32,.12)",    // 持ち上げたときだけ
      "lift-high": "0 22px 28px rgba(42,35,32,.1)",
      groove: "inset 0 3px 0 #e7dccb",          // 彫った溝
    },
  },
},
役割 トークン いつ
紙の厚み shadow-paper カードやボタンが床に付いているとき
持ち上げ shadow-lift ホバー・アクティブのときだけ
彫った溝 shadow-groove 入力欄の内側

バイブコーディング プロンプト

設定を入れたあと、Claude CodeやCursorに渡す指示は次のひとかたまりで足ります。色のリストより影のルールを先に置く、という順番が大事です。省略していない原文はzipの中のprompt.mdに入っています。

床に付いた紙の影はぼかしを0にする: 0 6px 0 rgba(42,35,32,.16)。この6pxが紙の厚みで、
画面全体で同じ値にする。持ち上げるときだけ 0 18px 8px のぼけた影に変える。背景は
paper(#fbf6ec)の1トーン、面は sheet(#ffffff)の1トーン。色紙は5色で、1画面に3色まで。
白い文字は coral・sky・leaf の上にだけ置く。ちぎれた端は画像ではなく conic-gradient の
マスクで作る。

この指示どおりにpage/index.htmlを作り、1280pxのビューポートでページ全体を撮ったものが下の画像です。

結果

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

一番長く気づけなかったのは文字の大きさでした。9個のパーツのスタイルにfont: 800 24px/1.1;のように大きさと太さを1行にまとめ、すぐ下にfont-family: inherit;を置いていたのですが、レンダリングした画面では料金の数字が見出しより小さく見えました。Playwrightで算出値を直接取ってみると、その数字は24pxではなく16px/400、ヒーローの見出しは狙った22pxではなくブラウザ既定の32px/700でした。fontのショートハンドは最後の位置にfont-familyが必ず必要で、そこが空だと宣言そのものが捨てられます。9つのファイルに散らばった21か所をfont-weightfont-sizeline-heightの個別プロパティに分解して、ようやく測った値と書いた値が一致しました。

2つ目はワンページ側でした。Tailwindのplay CDNを使いながら、普通の<style>の中にbody { background: theme('colors.paper') }と書いたところ、画面はただの白で、料金カードの折れた角はまったく見えませんでした。算出値の背景色を取るとどちらもrgba(0, 0, 0, 0)——play CDNはtype="text/tailwindcss"が付いたstyleだけをコンパイルし、それ以外の<style>の中のtheme()は無効として捨てます。この2つを直した完成版は、解凍パスワードrysqfgwrで開くzipにそのまま入っています。

アクセシビリティ

明るい紙のトーンはコントラストを確保しやすい方ですが、色紙の上に白い文字を載せた瞬間に基準線ぎりぎりになります。WCAG 2.1 最小コントラスト基準の相対輝度の式で実際に計算してみると、インク(#2a2320)は紙の地(#fbf6ec)に対して14.34:1、補助文字のストーン(#5c5048)は7.23:1で、本文の基準4.5:1を十分に超えます。問題はアクセント色でした。最初に選んだコーラル#d6452fは白文字に対して4.42:1と基準を下回り、#cc3d26まで暗くしてやっと4.93:1になりました。同じやり方でスカイ(#2f6fb8)は5.14:1、リーフ(#2b7d5a)は5.02:1を確保し、バター(#f6c453)とブラッシュ(#f6b3ab)は白文字ではどうしても基準に届かないため、インク文字だけを載せる(それぞれ9.52:1、8.79:1)ようにコード側で制限しました。

動きについては、MDN prefers-reduced-motion のページのメディアクエリを9個すべてに入れ、動きを減らす設定にしているユーザーには紙が浮いたり帯が流れたりせず、静止した状態だけが見えるようにしました。そのときもMDN box-shadow のページどおりに描いた厚みの影は残るので、層の構造は読み取れます。ちぎれ端に使ったMDN mask のページのマスクは装飾なので、対応していないブラウザでは四角い帯に見えるだけで、内容は失われません。

ほかのWebデザインの流行が気になる方はデザイントレンドのカテゴリをご覧ください。このブログが何を配っているのかは紹介ページにまとめてあります。

FAQ

紙のテクスチャ画像を敷いた方がそれらしくなりませんか?

テクスチャ写真を敷くとファイルが重くなり、背景色を変えるたびに画像を作り直すことになります。このキットが紙に見える理由は質感ではなく、ぼかし0の影と一方向にそろえた厚みです。写真なしで層が読み取れるようになれば、そこから先の質感は好みで足せる選択肢のひとつになります。

影の厚みを要素ごとに変えてもいいですか?

1つの画面では同じ値にそろえる方が安全です。厚みがばらばらだと、見る人はそれを「紙の厚さの違い」ではなく「そろえ忘れ」として受け取ります。厚みを変えたいときは要素ごとに散らさず、6pxから3pxあるいは10pxへ、画面全体を一度に動かしてください。

暗い背景でもこのスタイルは成立しますか?

厚みの影は背景より暗くないと層が見えませんが、暗い背景ではその余地がなくなります。どうしても使うなら、影を濃くするよりも、各紙の上端に明るい線を1本(inset 0 1px 0)入れて光の当たる面を作る方が現実的です。ただしその時点で、このスタイルの明るく軽い印象は消えます。

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

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