맥시멀리즘 디자인 랜딩 키트, 겹쳐 붙인 콜라주 9종
맥시멀리즘 디자인은 여백을 비우는 대신 색면·큰 활자·스티커를 겹쳐 붙여 화면을 꽉 채우는 스타일입니다. 잡지에서 오려 붙인 콜라주가 그 대표 얼굴이고, 이 키트는 사진 파일 없이 CSS 값만으로 그 인상을 냅니다.
핀터레스트에서 이런 화면을 저장해 놓고 막상 만들려고 하면 어디부터 손대야 할지 막막합니다. 요소가 많아서가 아니라 겹치는 규칙이 없어서입니다. 아래 원페이지를 먼저 훑어보면서, 이 화면에서 마음대로 정해진 것이 몇 개나 되는지 세어 보세요. 셋입니다.
저 페이지를 이루는 부품 아홉 개를 하나씩 떼어 아래 격자에 늘어놓았습니다. 순서는 인기순이 아니라 콜라주 한 장을 실제로 만드는 순서입니다. 바탕에 사진 프레임을 겹치고, 목차를 얹고, 조각을 배열하고, 도장을 찍고, 띠를 두르고, 밑줄을 긋고, 카드를 포개고, 가장자리를 파내고, 마지막으로 서명을 남깁니다.
자동 재생 · 눌러서 해당 항목으로 · 전부 한 zip
- 01 겹친 프레임 히어로
- 02 잡지 목차형 내비
- 03 오려낸 프레임 카드 격자
- 04 회전 스탬프 배지
- 05 마키 텍스트 띠
- 06 손글씨 밑줄 후기
- 07 겹침 레이어 요금제
- 08 스탬프 CTA 밴드
- 09 콜라주 푸터
01겹친 프레임 히어로
색면 그라디언트로 채운 사진 프레임 세 장이 각자 다른 위상으로 흔들리고, 그 위에 얹힌 제목 카드만 제자리에서 떠오릅니다. 뒤와 앞이 같은 방향으로 움직이면 겹침이 한 장으로 뭉쳐 보입니다.
.hcol__frame {
border: 6px solid #fff; // 흰 여백이 사진 테두리 노릇을 한다
animation-name: frameSway;
animation-duration: 2s;
animation-iteration-count: infinite;
}
.hcol__frame--sky { --rot: 8deg; animation-delay: -.7s; }
.hcol__frame--lime { --rot: -5deg; animation-delay: -1.4s; }
@keyframes frameSway {
0%, 100% { transform: translateY(0) rotate(var(--rot)); }
50% { transform: translateY(8px) rotate(calc(var(--rot) + 5deg)); }
}
02잡지 목차형 내비
메뉴마다 두 자리 쪽번호를 붙이면 그것만으로 화면이 잡지 목차가 됩니다. 레몬색 칩은 칸마다 잠깐 앉았다가 다음 칸으로 뛰는데, 앉아 있는 구간이 있어야 어느 칸이 켜져 있는지 읽힙니다.
.ncol { --tab: 68px; --step: 72px; } // 실측: 탭 68px + 간격 4px
.ncol__tab { flex: 0 0 var(--tab); } // 균등분배로 두면 이동량과 렌더 폭이 어긋난다
@keyframes chipHop {
0%, 10% { transform: translateX(0) rotate(-3deg); }
13.3% { transform: translateX(calc(var(--step) * .5)) translateY(-11px) rotate(5deg); }
16.6%, 26.6% { transform: translateX(var(--step)) rotate(-3deg); }
50%, 60% { transform: translateX(calc(var(--step) * 3)) rotate(-3deg); }
100% { transform: translateX(0) rotate(-3deg); }
}
03오려낸 프레임 카드 격자
세 장이 각자 붙인 각도에서 0도까지 펴졌다 되돌아갑니다. 0.24초씩 어긋나게 두면 세 장이 한 덩어리가 아니라 따로 붙인 종이로 보입니다.
.ccol__card--coral { --rot: -7deg; }
.ccol__card--sky { --rot: 5deg; animation-delay: -.24s; }
.ccol__card--grape { --rot: -4deg; animation-delay: -.48s; }
@keyframes cardStraighten {
0%, 100% { transform: rotate(var(--rot)); box-shadow: 0 5px 0 rgba(36, 29, 46, .16); }
45% { transform: rotate(0deg) translateY(-8px); box-shadow: 0 13px 0 rgba(36, 29, 46, .12); }
}
04회전 스탬프 배지
점선 테두리 하나로 원이 도장이 됩니다. 0.55배까지 눌렸다가 1.16배로 튀어 오르는데, 기울기를 유지하려면 transform 값 하나 안에 rotate 를 매번 같이 써 줘야 합니다.
.scol__badge--lemon { --rot: -12deg; background: #ffd93d; }
.scol__badge--lime { --rot: 8deg; animation-delay: -.5s; }
@keyframes stampPress {
0%, 55%, 100% { transform: rotate(var(--rot)) scale(1); }
8% { transform: rotate(var(--rot)) scale(.55); }
20% { transform: rotate(var(--rot)) scale(1.16); }
32% { transform: rotate(var(--rot)) scale(1); }
}
05마키 텍스트 띠
같은 낱말 묶음을 두 벌 깔고 한 벌 폭만큼만 밀면, 한 바퀴가 끝난 순간이 첫 프레임과 픽셀 단위로 같아집니다. 위아래 띠를 반대로 흘려보내면 두 줄이 서로 밀어내는 것처럼 읽힙니다.
.mcol__track {
display: flex;
width: max-content;
animation-name: stripFlow;
animation-duration: 2s;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
.mcol__track--back { animation-name: stripFlowBack; }
.mcol__bit { margin-right: 24px; } // gap 으로 주면 -50% 가 어긋난다
@keyframes stripFlow { from { transform: translateX(0); } to { transform: translateX(-50%); } }
06손글씨 밑줄 후기
SVG 선 하나에 stroke-dashoffset 을 양수에서 0, 다시 음수로 보내면 왼쪽부터 그어졌다가 왼쪽부터 지워집니다. 되돌아가는 구간이 없으니 루프 이음매가 눈에 안 띕니다.
.tcol__line {
fill: none;
stroke: #d93a1e;
stroke-width: 4;
stroke-linecap: round;
stroke-dasharray: 230; // path 실측 길이보다 넉넉히
animation-name: inkDraw;
animation-duration: 2s;
animation-iteration-count: infinite;
}
@keyframes inkDraw {
0% { stroke-dashoffset: 230; }
45% { stroke-dashoffset: 0; }
70% { stroke-dashoffset: 0; }
100% { stroke-dashoffset: -230; }
}
07겹침 레이어 요금제
카드 두 장을 음수 margin 으로 포개고 z-index 로 순서를 정합니다. 앞 카드가 떠오를 때 그림자 두께가 5px에서 17px로 늘어나야 층이 층으로 읽힙니다.
.pcol__plan--base { z-index: 1; transform: rotate(-3deg); }
.pcol__plan--pick {
z-index: 2;
margin-left: -20px; // 앞 카드가 뒤 카드 모서리를 덮는다
margin-bottom: 16px;
}
@keyframes planLift {
0%, 100% { transform: rotate(2deg) translateY(0); box-shadow: 0 5px 0 rgba(36, 29, 46, .16); }
50% { transform: rotate(2deg) translateY(-12px); box-shadow: 0 17px 0 rgba(36, 29, 46, .12); }
}
08스탬프 CTA 밴드
띠 아래쪽을 반원으로 파냅니다. 마스크 두 겹이면 되는데, 원의 어느 쪽을 남기느냐가 전부입니다. 버튼은 도장처럼 기울며 눌리고, 내려가는 동안 그림자 두께도 14px에서 2px로 얇아집니다.
.bcol {
mask:
radial-gradient(circle 8px at 50% 0, #000 98%, #0000 100%) 0 100% / 16px 8px repeat-x,
linear-gradient(#000 0 0) 0 0 / 100% calc(100% - 8px) no-repeat;
}
@keyframes stampPush {
0%, 100% { transform: translateY(-6px) rotate(-2deg); box-shadow: 0 14px 0 rgba(36, 29, 46, .35); }
50% { transform: translateY(0) rotate(1.5deg); box-shadow: 0 2px 0 rgba(36, 29, 46, .35); }
}
09콜라주 푸터
큰 세리프 낱말을 일부러 잘리게 깔면 글자가 아니라 면으로 읽힙니다. 그 앞의 조각 넷은 0.5초씩 어긋나 떠다니는데, 올라갈 때 기울기 부호가 뒤집히면 종이가 뒤집히는 것처럼 보입니다.
.fcol__word {
position: absolute;
left: -12px; bottom: -16px;
color: rgba(36, 29, 46, .1); // 면으로만 읽히게 10%
font-size: 62px;
white-space: nowrap;
}
@keyframes chipBob {
0%, 100% { transform: translateY(0) rotate(var(--rot)); }
50% { transform: translateY(-9px) rotate(calc(var(--rot) * -1)); }
}
tailwind.config 커스텀
이 스타일에서 한 곳에 모아야 하는 값은 색이 아니라 각도입니다. 요소마다 기분대로 각도를 주면 겹침이 의도가 아니라 사고로 보입니다. 그래서 각도를 유틸리티 이름으로 바꿔 넷만 남깁니다.
theme: {
extend: {
colors: {
cream: "#fbf3e4", sheet: "#ffffff", ink: "#241d2e", slate: "#57506a",
coral: "#d93a1e", sky: "#2d6fb5", grape: "#6b3fa0",
lemon: "#ffd93d", lime: "#b6e34f", blush: "#ffb3c7",
},
borderRadius: { chip: "4px", control: "12px", card: "16px", pill: "999px" },
boxShadow: {
paste: "0 5px 0 rgba(36,29,46,.16)", // 붙어 있는 상태 = 두께
stamp: "0 4px 0 rgba(36,29,46,.2)",
lift: "0 16px 0 rgba(36,29,46,.12)", // 들어올렸을 때만
},
rotate: { a: "-3deg", b: "2deg", c: "-1.5deg", d: "5deg" },
},
},
| 역할 | 토큰 | 언제 |
|---|---|---|
| 오려 붙인 각도 | rotate-a ~ rotate-d |
프레임·카드·배지·띠 전부 |
| 붙어 있는 두께 | shadow-paste |
바닥에 놓인 상태 |
| 들어올림 | shadow-lift |
떠오르는 순간에만 |
바이브코딩 프롬프트
설정 파일을 넣었다면 AI 편집기에 건네는 지시는 아래 한 덩어리로 끝납니다. 색 목록보다 각도 목록이 먼저 오는 순서가 중요합니다. 자르지 않은 원문은 zip 안의 prompt.md 에 있습니다.
각도는 네 개만 쓴다: -3deg, 2deg, -1.5deg, 5deg. 이 목록 밖의 각도를 만들지 않는다.
바닥에 붙은 요소의 그림자는 흐림 0 의 0 5px 0 rgba(36,29,46,.16), 들어올릴 때만
0 16px 0 으로 두껍게 바꾼다. 배경은 cream(#fbf3e4) 한 톤, 면은 sheet(#ffffff) 한 톤.
흰 글자는 coral·sky·grape 위에만 얹고, lemon·lime·blush 위에는 ink 글자만 얹는다.
사진은 쓰지 않는다 — 사진 자리는 두 색 linear-gradient 와 흰 테두리 6px 으로 대신한다.
위 지시로 나온 page/index.html 을 1280px 뷰포트에 띄워 페이지 전체를 한 장으로 찍었습니다.

어디서 깨지는가 — 함정
제일 오래 헤맨 것은 CTA 띠의 가장자리였습니다. 아래쪽을 물결처럼 파내려고 radial-gradient 마스크를 깔았는데, 처음 쓴 circle 8px at 50% 0, #0000 98%, #000 100% 는 원의 바깥을 남기는 식이라 화면에는 아래로 뾰족한 삼각 톱니가 줄줄이 달렸습니다. 포스터를 열어 보고서야 알았습니다. 파도가 아니라 이빨이었습니다. 색 순서를 뒤집어 #000 98%, #0000 100% 로 원 안쪽을 남기자 그제야 반원이 아래로 매달렸습니다. 마스크는 "무엇을 지울까"가 아니라 "무엇을 남길까"로 읽어야 합니다.
두 번째는 마키 띠였습니다. 위아래 두 띠를 각각 -2.5도와 2도로 기울이고 음수 margin-top 으로 겹쳤더니, 렌더된 화면에서 아래 띠가 위 띠의 글자 밑동을 그대로 덮었습니다. 기울인 요소의 실제 점유 높이는 회전 전 높이보다 크기 때문에, 회전 전 기준으로 계산한 겹침 값이 그대로 통하지 않습니다. 아래 띠를 88% 폭으로 줄이고 오른쪽으로 붙여 띠 가장자리끼리만 겹치게 바꾸자 두 줄이 다 읽혔습니다. 같은 김에 트랙 폭도 Playwright 의 offsetWidth 로 다시 쟀습니다. 회전한 요소는 getBoundingClientRect 가 회전 후 경계 상자를 주므로 이 계산에는 쓸 수 없습니다. 위 띠 796px에 한 벌 398px, 아래 띠 524px에 한 벌 262px — 둘 다 정확히 절반이라 translateX(-50%) 한 바퀴의 이음매가 0px입니다. 두 함정을 다 손본 파일은 압축 비밀번호 s3yjtjja 으로 열리는 zip 안에 그대로 들어 있습니다.
접근성
색이 많을수록 대비를 눈으로 판정하면 안 됩니다. WCAG 2.1 최소 대비 기준의 상대 휘도 공식으로 열 가지 색을 전부 계산해 표로 정리했습니다. 본문 기준은 4.5:1입니다.
| 색 | 값 | 크림 바탕 위 | 흰 면 위 | 잉크 글자를 얹으면 |
|---|---|---|---|---|
| 잉크 | #241d2e |
14.76:1 | 16.28:1 | — |
| 슬레이트 | #57506a |
6.90:1 | 7.61:1 | — |
| 코랄 | #d93a1e |
4.16:1 | 4.59:1 | 3.55:1 |
| 스카이 | #2d6fb5 |
4.70:1 | 5.18:1 | 3.14:1 |
| 그레이프 | #6b3fa0 |
6.70:1 | 7.38:1 | 2.20:1 |
| 레몬 | #ffd93d |
1.25:1 | 1.38:1 | 11.82:1 |
| 라임 | #b6e34f |
1.35:1 | 1.49:1 | 10.92:1 |
| 블러시 | #ffb3c7 |
1.52:1 | 1.68:1 | 9.71:1 |
여기서 쓰임이 두 갈래로 갈립니다. 코랄 글자는 흰 면 위에서 4.59:1로 통과하지만 크림 바탕에서는 4.16:1이라 본문으로 쓰면 안 됩니다. 그래서 부품 아홉 개의 코랄 글자는 전부 흰 카드 안에만 있습니다. 반대로 레몬·라임·블러시는 어느 바탕에서도 글자로 못 쓰는 대신 잉크 글자를 얹으면 9:1을 넘으므로, 배지와 띠의 면으로만 씁니다.
움직임 쪽은 MDN prefers-reduced-motion 문서의 미디어 쿼리를 아홉 개 전부에 넣었습니다. 동작 줄이기를 켜면 흔들림과 흐름은 멈추지만 붙인 각도는 그대로 남습니다. MDN transform 문서의 rotate 로 만든 기울기가 이 스타일의 정체성이라, 각도까지 0으로 되돌리면 다른 디자인이 되기 때문입니다.
다른 밝은 톤 스타일이 궁금하면 종이 질감 디자인 랜딩 키트를 같이 보시고, 이 블로그가 뭘 주는 곳인지는 소개 페이지에 적어 뒀습니다.
FAQ
색을 여섯 개나 쓰면 촌스러워지지 않나요?
색 개수가 아니라 역할이 겹칠 때 촌스러워집니다. 이 키트에서 코랄·스카이·그레이프는 글자를 얹는 면이고 레몬·라임·블러시는 글자를 안 얹는 장식 면입니다. 역할이 갈려 있으면 한 화면에 여섯 색이 있어도 눈은 두 무리로 읽습니다.
각도를 왜 네 개로 묶나요?
기울기는 값이 아니라 목록이기 때문입니다. 요소마다 -2도, -7도, 3도처럼 제각각 주면 보는 사람은 그것을 "일부러 기울였다"가 아니라 "정렬이 틀렸다"로 읽습니다. 네 개를 돌려 쓰면 같은 손이 붙인 것처럼 보입니다.
사진 없이 만든 프레임이 어색하지 않나요?
사진 자리를 비워 두면 어색하지만, 두 색 그라디언트로 채우고 흰 테두리를 6px 두르면 액자 안에 무언가 들어 있는 것으로 읽힙니다. 실제 사진을 넣을 때도 이 구조 그대로 background 만 이미지로 바꾸면 되므로, 시안 단계에서 파일을 기다릴 필요가 없습니다.