08Wave-edge footer
The footer's top edge is a wave row drawn by a single repeating background — no element per wave. Inside, columns sit in pale lime, grapefruit, and lemon while tiny droplets twinkle.
Best forMulti-column footers, site endings
repeating 웨이브 가장자리3열→1열반짝임 점

<main class="stage">
<footer class="footercitrus">
<span class="footercitrus__wave" aria-hidden="true"></span>
<div class="footercitrus__cols">
<div class="footercitrus__col footercitrus__col--org">
<p class="footercitrus__head">Product</p>
<p class="footercitrus__link">Features</p>
</div>
<div class="footercitrus__col footercitrus__col--grp">
<p class="footercitrus__head">Company</p>
<p class="footercitrus__link">About</p>
</div>
<div class="footercitrus__col footercitrus__col--lem">
<p class="footercitrus__head">Help</p>
<p class="footercitrus__link">Guides</p>
</div>
</div>
<p class="footercitrus__copy">
<span class="footercitrus__spark" aria-hidden="true">✦</span>
Squeeze · A fresh day
<span class="footercitrus__spark footercitrus__spark--b" aria-hidden="true">✦</span>
</p>
</footer>
</main>@use "../../../../site/static/demo-tokens" as *;
$duration: $dur-loop; // 방울이 한 번 반짝이는 주기
$easing: $ease-out;
$color: $stage-ink;
$citrus-cream: #fffaf0;
$citrus-orange-deep: #bd967a;
$citrus-lime: #c4ce9c;
$citrus-grapefruit: #ebd6da;
$citrus-lemon: #dfd8be;
$citrus-ink: #204a17;
// 푸터 패널 색 — 크림보다 살짝 진한 라임 우유크림
$panel: mix($citrus-lime, $stage-paper, 18%);
@include stage($citrus-cream);
.footercitrus {
position: relative;
box-sizing: border-box;
width: clamp(300px, 94vw, 460px);
padding: $sp-4 $sp-3 $sp-3;
border-radius: $r-card;
background: $panel;
box-shadow: 0 -6px 20px rgba($citrus-lime, .4);
}
// 웨이브 가장자리 — 물결 하나하나를 요소로 그리지 않고 배경 반복 하나로 그린다.
// 각 타일(30×15px) 아래변 중심에 반원을 그리면 윗반원만 남아 물결치는 가장자리가 된다
.footercitrus__wave {
position: absolute;
top: calc($sp-3 * -1);
left: 0;
width: 100%;
height: $sp-3;
background-image: radial-gradient(circle at 50% 100%, $panel 0 64%, transparent 66%);
background-size: 30px 15px;
background-repeat: repeat-x;
background-position: bottom center;
}
.footercitrus__cols {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(clamp(84px, 26vw, 120px), 1fr));
gap: $sp-2;
}
.footercitrus__col {
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: $sp-1;
padding: $sp-3;
border-radius: $r-control;
}
.footercitrus__col--org { background: mix($citrus-orange-deep, $stage-paper, 30%); animation: colRipple $duration $easing infinite; }
.footercitrus__col--grp { background: mix($citrus-grapefruit, $stage-paper, 45%); animation: colRipple $duration $easing infinite; animation-delay: calc($duration / -3); }
.footercitrus__col--lem { background: mix($citrus-lemon, $stage-paper, 45%); animation: colRipple $duration $easing infinite; animation-delay: calc($duration / -3 * 2); }
.footercitrus__head {
margin: 0;
color: $citrus-ink;
font-weight: 700; font-size: clamp(10px, 2.6vw, 12px); line-height: 1;
font-family: inherit;
letter-spacing: .06em;
}
.footercitrus__link {
margin: 0;
color: $citrus-ink;
font-weight: 500; font-size: clamp(11px, 2.8vw, 13px); line-height: 1.5;
font-family: inherit;
}
.footercitrus__copy {
display: flex;
align-items: center;
justify-content: center;
gap: $sp-2;
margin: $sp-3 0 0;
color: rgba($citrus-ink, .78);
font-weight: 500; font-size: clamp(10px, 2.6vw, 12px); line-height: 1;
font-family: inherit;
}
.footercitrus__spark {
color: $citrus-orange-deep;
animation: dropTwinkle $duration $easing infinite;
}
.footercitrus__spark--b {
animation-delay: calc($duration / -2);
color: mix($citrus-lime, $citrus-ink, 70%);
}
// 데모: 세 칸이 순서대로 살짝 들썩이는 물결을 만들고, 방울 두 개는 어긋나게 반짝인다
@keyframes colRipple {
0%, 40%, 100% { transform: translateY(0); }
20% { transform: translateY(-6px); }
}
@keyframes dropTwinkle {
0%, 100% { opacity: .35; transform: scale(.8) rotate(0deg); }
50% { opacity: 1; transform: scale(1.3) rotate(25deg); }
}
// 반응형: 320×200 처럼 세로가 짧은 화면에선 칸 속과 웨이브 번짐을 줄인다(BD9)
@media (max-width: 340px), (max-height: 220px) {
.footercitrus { padding: $sp-3 $sp-2 $sp-2; }
.footercitrus__wave { top: calc($sp-2 * -1); height: $sp-2; background-size: 22px 11px; }
.footercitrus__cols { gap: $sp-1; }
.footercitrus__col { padding: $sp-2; }
.footercitrus__link { display: none; }
.footercitrus__copy { margin-top: $sp-2; font-size: 9px; }
}
@media (prefers-reduced-motion: reduce) {
.footercitrus__col, .footercitrus__spark { animation: none !important; transform: none; }
.footercitrus__spark { opacity: .8; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #fffaf0;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.footercitrus {
position: relative;
box-sizing: border-box;
width: clamp(300px, 94vw, 460px);
padding: 16px 12px 12px;
border-radius: 16px;
background: rgb(95.8352941176%, 96.5411764706%, 93.0117647059%);
box-shadow: 0 -6px 20px rgba(196, 206, 156, 0.4);
}
.footercitrus__wave {
position: absolute;
top: -12px;
left: 0;
width: 100%;
height: 12px;
background-image: radial-gradient(circle at 50% 100%, rgb(95.8352941176%, 96.5411764706%, 93.0117647059%) 0 64%, transparent 66%);
background-size: 30px 15px;
background-repeat: repeat-x;
background-position: bottom center;
}
.footercitrus__cols {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(clamp(84px, 26vw, 120px), 1fr));
gap: 8px;
}
.footercitrus__col {
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 4px;
padding: 12px;
border-radius: 12px;
}
.footercitrus__col--org {
background: rgb(92.2352941176%, 87.6470588235%, 84.3529411765%);
animation: colRipple 2s ease-out infinite;
}
.footercitrus__col--grp {
background: rgb(96.4705882353%, 92.7647058824%, 93.4705882353%);
animation: colRipple 2s ease-out infinite;
animation-delay: -0.6666666667s;
}
.footercitrus__col--lem {
background: rgb(94.3529411765%, 93.1176470588%, 88.5294117647%);
animation: colRipple 2s ease-out infinite;
animation-delay: -1.3333333333s;
}
.footercitrus__head {
margin: 0;
color: #204a17;
font-weight: 700;
font-size: clamp(10px, 2.6vw, 12px);
line-height: 1;
font-family: inherit;
letter-spacing: 0.06em;
}
.footercitrus__link {
margin: 0;
color: #204a17;
font-weight: 500;
font-size: clamp(11px, 2.8vw, 13px);
line-height: 1.5;
font-family: inherit;
}
.footercitrus__copy {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
margin: 12px 0 0;
color: rgba(32, 74, 23, 0.78);
font-weight: 500;
font-size: clamp(10px, 2.6vw, 12px);
line-height: 1;
font-family: inherit;
}
.footercitrus__spark {
color: #bd967a;
animation: dropTwinkle 2s ease-out infinite;
}
.footercitrus__spark--b {
animation-delay: -1s;
color: rgb(57.568627451%, 65.2549019608%, 45.5294117647%);
}
@keyframes colRipple {
0%, 40%, 100% {
transform: translateY(0);
}
20% {
transform: translateY(-6px);
}
}
@keyframes dropTwinkle {
0%, 100% {
opacity: 0.35;
transform: scale(0.8) rotate(0deg);
}
50% {
opacity: 1;
transform: scale(1.3) rotate(25deg);
}
}
@media (max-width: 340px), (max-height: 220px) {
.footercitrus {
padding: 12px 8px 8px;
}
.footercitrus__wave {
top: -8px;
height: 8px;
background-size: 22px 11px;
}
.footercitrus__cols {
gap: 4px;
}
.footercitrus__col {
padding: 8px;
}
.footercitrus__link {
display: none;
}
.footercitrus__copy {
margin-top: 8px;
font-size: 9px;
}
}
@media (prefers-reduced-motion: reduce) {
.footercitrus__col, .footercitrus__spark {
animation: none !important;
transform: none;
}
.footercitrus__spark {
opacity: 0.8;
}
}import type { CSSProperties } from "react";
import styles from "./FooterCitrus.module.scss";
export type FooterCitrusProps = {
brand?: string;
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function FooterCitrus({
brand = "스퀴즈 · 상큼한 하루",
duration = 2000,
easing = "ease-out",
color = "#ff6a00",
}: FooterCitrusProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<footer className={styles.footercitrus} style={vars}>
<span className={styles.wave} aria-hidden="true" />
<div className={styles.cols}>
<div className={`${styles.col} ${styles.colOrg}`}>
<p className={styles.head}>제품</p>
<p className={styles.link}>기능</p>
</div>
<div className={`${styles.col} ${styles.colGrp}`}>
<p className={styles.head}>회사</p>
<p className={styles.link}>소개</p>
</div>
<div className={`${styles.col} ${styles.colLem}`}>
<p className={styles.head}>도움말</p>
<p className={styles.link}>가이드</p>
</div>
</div>
<p className={styles.copy}>
<span className={styles.spark} aria-hidden="true">✦</span>
{brand}
<span className={`${styles.spark} ${styles.sparkB}`} aria-hidden="true">✦</span>
</p>
</footer>
);
}08 Wave-edge footer — Measured area 2.668% · moving frames 23/23Original by GODRICH · SCSS and React ship in the zip · Citrus Web Design Kit: 9 Fruit-Color Sections
The post this demo lives in
Citrus Web Design Kit: 9 Fruit-Color SectionsCitrus web design layers saturated fruit colors — orange, lime, grapefruit — on cream, with each card's shadow tinted to See all demos in this post


