09コラージュのフッター
リンク2行の背面に大きなセリフの語が切れたまま敷かれ、その手前の色紙4片が半拍ずつずれて上下に漂います。
使いどころエディトリアルサイトの最後の領域
배경 워드마크시차 0.5sradius 4

<main class="stage">
<footer class="fcol">
<span class="fcol__word" aria-hidden="true">COLLAGE</span>
<p class="fcol__brand">切り貼りキット</p>
<span class="fcol__rule" aria-hidden="true"></span>
<div class="fcol__cols">
<ul class="fcol__list">
<li>パーツ</li>
<li>ワンページ</li>
</ul>
<ul class="fcol__list">
<li>トークン</li>
<li>プロンプト</li>
</ul>
<ul class="fcol__list">
<li>zip</li>
<li>お問い合わせ</li>
</ul>
</div>
<div class="fcol__chips" aria-hidden="true">
<span class="fcol__chip fcol__chip--coral"></span>
<span class="fcol__chip fcol__chip--lemon"></span>
<span class="fcol__chip fcol__chip--lime"></span>
<span class="fcol__chip fcol__chip--sky">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256" fill="currentColor"><path d="M223.69,42.18l-58.22,192a8,8,0,0,1-14.92,1.25L108,148,20.58,105.45a8,8,0,0,1,1.25-14.92l192-58.22A8,8,0,0,1,223.69,42.18Z" opacity="0.2"/><path d="M227.32,28.68a16,16,0,0,0-15.66-4.08l-.15,0L19.57,82.84a16,16,0,0,0-2.49,29.8L102,154l41.3,84.87A15.86,15.86,0,0,0,157.74,248q.69,0,1.38-.06a15.88,15.88,0,0,0,14-11.51l58.2-191.94c0-.05,0-.1,0-.15A16,16,0,0,0,227.32,28.68ZM157.83,231.85l-.05.14,0-.07-40.06-82.3,48-48a8,8,0,0,0-11.31-11.31l-48,48L24.08,98.25l-.07,0,.14,0L216,40Z"/></svg>
</span>
</div>
</footer>
</main>@use "../../../../site/static/demo-tokens" as *;
// 콜라주·맥시멀 팔레트 (01 과 같은 값)
$cg-cream: #fbf3e4;
$cg-sheet: #ffffff;
$cg-ink: #241d2e;
$cg-slate: #57506a;
$cg-coral: #bb7f74;
$cg-sky: #668cb4;
$cg-lemon: #d1c8a3;
$cg-lime: #bfce9c;
$serif: Georgia, "Nanum Myeongjo", "Times New Roman", serif;
$duration: $dur-loop; // 조각 하나가 떴다 내려오는 한 주기
$easing: $ease-spring;
$color: $cg-coral; // 첫 조각 색
@include stage($cg-cream);
.fcol {
position: relative;
box-sizing: border-box;
width: clamp(232px, 80vw, 312px);
overflow: hidden;
padding: $sp-5 $sp-4 $sp-4;
border-radius: $r-xs;
background: $cg-sheet;
box-shadow: 0 5px 0 rgba($cg-ink, .16);
isolation: isolate;
}
// 배경 워드마크 — 일부러 잘리게 둔다. 글자가 아니라 면으로 읽힌다
.fcol__word {
position: absolute;
z-index: 0;
left: -$sp-3;
bottom: -$sp-4;
color: rgba($cg-ink, .1);
font-family: $serif;
font-weight: 700;
font-size: 62px;
line-height: 1;
letter-spacing: -.02em;
white-space: nowrap;
}
.fcol__brand {
position: relative;
z-index: 1;
margin: 0;
color: $cg-ink;
font-family: $serif;
font-weight: 700;
font-size: 15px;
line-height: 1;
transform: rotate(-1.5deg);
}
.fcol__rule {
position: relative;
z-index: 1;
display: block;
height: 3px;
margin: $sp-3 0;
border-radius: $r-xs;
background: repeating-linear-gradient(90deg, $cg-coral 0 10px, transparent 10px 18px);
}
.fcol__cols {
position: relative;
z-index: 1;
display: flex;
gap: $sp-6;
}
.fcol__list {
margin: 0;
padding: 0;
list-style: none;
color: $cg-slate;
font-weight: 600;
font-size: 12px;
line-height: 1.8;
}
.fcol__chips {
position: relative;
z-index: 2;
display: flex;
gap: $sp-2;
margin-top: $sp-4;
}
.fcol__chip {
width: 30px;
height: 22px;
display: grid;
place-items: center;
border-radius: $r-xs;
color: $cg-sheet;
font-size: 13px;
box-shadow: 0 3px 0 rgba($cg-ink, .18);
animation-name: chipBob;
animation-duration: $duration;
animation-timing-function: $easing;
animation-iteration-count: infinite;
}
.fcol__chip--coral { --rot: -8deg; background: $cg-coral; }
.fcol__chip--lemon { --rot: 6deg; background: $cg-lemon; animation-delay: -.5s; }
.fcol__chip--lime { --rot: -4deg; background: $cg-lime; animation-delay: -1s; }
.fcol__chip--sky { --rot: 9deg; background: $cg-sky; animation-delay: -1.5s; }
// 데모: 네 조각이 반 박자씩 어긋나 떠다닌다 — 붙인 각도는 그대로
@keyframes chipBob {
0%, 100% { transform: translateY(0) rotate(var(--rot)); }
50% { transform: translateY(-9px) rotate(calc(var(--rot) * -1)); }
}
@media (max-width: 360px), (max-height: 230px) {
.fcol { width: 236px; padding: $sp-3; }
.fcol__word { font-size: 44px; }
.fcol__brand { font-size: 12px; }
.fcol__rule { margin: $sp-2 0; }
.fcol__cols { gap: $sp-4; }
.fcol__list { font-size: 11px; line-height: 1.6; }
.fcol__chips { margin-top: $sp-2; }
.fcol__chip { width: 24px; height: 18px; font-size: 11px; }
}
@media (prefers-reduced-motion: reduce) {
.fcol__chip { animation: none !important; transform: rotate(var(--rot)); }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #fbf3e4;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.fcol {
position: relative;
box-sizing: border-box;
width: clamp(232px, 80vw, 312px);
overflow: hidden;
padding: 20px 16px 16px;
border-radius: 4px;
background: #ffffff;
box-shadow: 0 5px 0 rgba(36, 29, 46, 0.16);
isolation: isolate;
}
.fcol__word {
position: absolute;
z-index: 0;
left: -12px;
bottom: -16px;
color: rgba(36, 29, 46, 0.1);
font-family: Georgia, "Nanum Myeongjo", "Times New Roman", serif;
font-weight: 700;
font-size: 62px;
line-height: 1;
letter-spacing: -0.02em;
white-space: nowrap;
}
.fcol__brand {
position: relative;
z-index: 1;
margin: 0;
color: #241d2e;
font-family: Georgia, "Nanum Myeongjo", "Times New Roman", serif;
font-weight: 700;
font-size: 15px;
line-height: 1;
transform: rotate(-1.5deg);
}
.fcol__rule {
position: relative;
z-index: 1;
display: block;
height: 3px;
margin: 12px 0;
border-radius: 4px;
background: repeating-linear-gradient(90deg, #bb7f74 0 10px, transparent 10px 18px);
}
.fcol__cols {
position: relative;
z-index: 1;
display: flex;
gap: 24px;
}
.fcol__list {
margin: 0;
padding: 0;
list-style: none;
color: #57506a;
font-weight: 600;
font-size: 12px;
line-height: 1.8;
}
.fcol__chips {
position: relative;
z-index: 2;
display: flex;
gap: 8px;
margin-top: 16px;
}
.fcol__chip {
width: 30px;
height: 22px;
display: grid;
place-items: center;
border-radius: 4px;
color: #ffffff;
font-size: 13px;
box-shadow: 0 3px 0 rgba(36, 29, 46, 0.18);
animation-name: chipBob;
animation-duration: 2s;
animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
animation-iteration-count: infinite;
}
.fcol__chip--coral {
--rot: -8deg;
background: #bb7f74;
}
.fcol__chip--lemon {
--rot: 6deg;
background: #d1c8a3;
animation-delay: -0.5s;
}
.fcol__chip--lime {
--rot: -4deg;
background: #bfce9c;
animation-delay: -1s;
}
.fcol__chip--sky {
--rot: 9deg;
background: #668cb4;
animation-delay: -1.5s;
}
@keyframes chipBob {
0%, 100% {
transform: translateY(0) rotate(var(--rot));
}
50% {
transform: translateY(-9px) rotate(calc(var(--rot) * -1));
}
}
@media (max-width: 360px), (max-height: 230px) {
.fcol {
width: 236px;
padding: 12px;
}
.fcol__word {
font-size: 44px;
}
.fcol__brand {
font-size: 12px;
}
.fcol__rule {
margin: 8px 0;
}
.fcol__cols {
gap: 16px;
}
.fcol__list {
font-size: 11px;
line-height: 1.6;
}
.fcol__chips {
margin-top: 8px;
}
.fcol__chip {
width: 24px;
height: 18px;
font-size: 11px;
}
}
@media (prefers-reduced-motion: reduce) {
.fcol__chip {
animation: none !important;
transform: rotate(var(--rot));
}
}import type { CSSProperties, ReactNode } from "react";
import styles from "./FooterCollage.module.scss";
export type FooterCollageProps = {
word?: string;
brand?: string;
columns?: string[][];
/** 조각끼리 어긋나는 간격(ms) */
stagger?: number;
planeIcon?: ReactNode;
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
type ChipVars = CSSProperties & { "--rot"?: string; background?: string; animationDelay?: string };
const defaultColumns: string[][] = [
["부품", "원페이지"],
["토큰", "프롬프트"],
["zip", "문의"],
];
const pieces = [
{ bg: "#d93a1e", rot: "-8deg" },
{ bg: "#ffd93d", rot: "6deg" },
{ bg: "#b6e34f", rot: "-4deg" },
{ bg: "#2d6fb5", rot: "9deg" },
];
export default function FooterCollage({
word = "COLLAGE",
brand = "오려 붙이는 랜딩 키트",
columns = defaultColumns,
stagger = 500,
planeIcon,
duration = 2000,
easing = "cubic-bezier(.2, .8, .2, 1)",
color = "#d93a1e",
}: FooterCollageProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<footer className={styles.fcol} style={vars}>
<span className={styles.word} aria-hidden="true">{word}</span>
<p className={styles.brand}>{brand}</p>
<span className={styles.rule} aria-hidden="true" />
<div className={styles.cols}>
{columns.map((col, i) => (
<ul key={i} className={styles.list}>
{col.map((x) => <li key={x}>{x}</li>)}
</ul>
))}
</div>
<div className={styles.chips} aria-hidden="true">
{pieces.map((c, i) => {
const cv: ChipVars = { "--rot": c.rot, background: c.bg, animationDelay: `${-stagger * i}ms` };
return (
<span key={c.bg} className={styles.chip} style={cv}>
{i === pieces.length - 1 ? planeIcon : null}
</span>
);
})}
</div>
</footer>
);
}09 コラージュのフッター — 計測 累積面積 2.096% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · コラージュ デザインのLPパーツ9選、コピペOK
このデモが属する記事
コラージュ デザインのLPパーツ9選、コピペOKコラージュ デザインは、余白を取るのではなく色面・大きな活字・シールを重ねて貼り、画面を埋めていくスタイルです。このキットは写真ファイルを1枚も使わず、CSSの値だけで雑誌から切り抜いたような画面を作ります。 この記事のデモをすべて見る


