09One page with three texture spots
A miniature landing page scrolls up and down through its own sections; texture lives only in the wood nav, the metal dial, and the leather pricing card, while everything else stays bright and flat.
Best forWhole-landing previews and section order reviews
질감 3곳핑퐁 스크롤translateY 프리뷰

<main class="stage">
<section class="minipage" aria-label="Mini landing with three texture spots">
<div class="minipage__frame">
<div class="minipage__track">
<header class="minipage__nav">
<span class="minipage__navdot"></span>
<span class="minipage__navlink"></span>
<span class="minipage__navlink"></span>
</header>
<section class="minipage__hero">
<p class="minipage__hero-eyebrow">Texture landing</p>
<span class="minipage__hero-bar"></span>
<span class="minipage__hero-bar minipage__hero-bar--short"></span>
<span class="minipage__hero-btn">Get started</span>
</section>
<section class="minipage__features">
<article class="minipage__fcard"><span class="minipage__fdot"></span><span class="minipage__fline"></span></article>
<article class="minipage__fcard"><span class="minipage__fdot minipage__fdot--b"></span><span class="minipage__fline"></span></article>
<article class="minipage__fcard"><span class="minipage__fdot minipage__fdot--c"></span><span class="minipage__fline"></span></article>
</section>
<section class="minipage__cta">
<span class="minipage__dial" aria-hidden="true"><span class="minipage__dial-cap"></span></span>
<span class="minipage__cta-btn">Start free</span>
</section>
<section class="minipage__pricing">
<p class="minipage__plan">Pro membership</p>
<p class="minipage__price">₩24,000 / month</p>
<span class="minipage__stitch" aria-hidden="true"></span>
</section>
<section class="minipage__quote">
<span class="minipage__avatar"></span>
<span class="minipage__qline"></span>
<span class="minipage__qline minipage__qline--short"></span>
</section>
<section class="minipage__faq">
<span class="minipage__qrow"></span>
<span class="minipage__qrow minipage__qrow--open">
<span class="minipage__qbody"></span>
</span>
<span class="minipage__qrow"></span>
</section>
<footer class="minipage__foot">
<span class="minipage__footdot"></span>
<span class="minipage__footdot"></span>
<span class="minipage__footdot"></span>
</footer>
</div>
</div>
<div class="minipage__notes">
<p class="minipage__note minipage__note--tex">3 texture spots — wood nav, metal dial, leather card</p>
<p class="minipage__note">Every other section stays bright and flat</p>
<p class="minipage__note">Scrolling shows where the textures sit</p>
</div>
</section>
</main>@use "../../../../site/static/demo-tokens" as *;
// 유형 F 마무리 부품 — 원페이지 미리보기. 질감은 세 곳(나무 내비·금속 다이얼·가죽 카드)에만,
// 나머지 구간은 밝은 평면(230 신뢰 톤 계열)으로 대비시킨다 — 9곳 전부 질감이면 촌스러워진다
$mp-flat: #f7f5f0; // 평면 구간 바탕
$mp-flat-2: #eceae3; // 평면 구간 보조
$mp-ink: #2a2732; // 평면 위 글자
$mp-line: #d8d4c9; // 평면 선·막대
$duration: $dur-loop; // 위 → 아래 → 위 한 번 스크롤
$easing: $ease-out; // 스크롤은 관성을 두고 감속
$color: $mp-flat;
@include stage($stage-ink);
.minipage {
display: flex;
align-items: center;
gap: $sp-4;
}
// 뷰포트 — 안에서 트랙이 스크롤된다
.minipage__frame {
position: relative;
width: clamp(184px, 58vw, 224px);
height: clamp(208px, 64vh, 252px);
border-radius: $r-card;
overflow: hidden;
box-shadow: 0 12px 20px rgba(0, 0, 0, .5), inset 0 0 0 1px rgba(255, 255, 255, .08);
}
// 트랙 — 높이가 뷰포트의 정확히 2배. translateY(-50%) 가 한 화면만큼 = 아래 절반
.minipage__track {
display: flex;
flex-direction: column;
height: 200%;
animation: pageScroll $duration $easing infinite;
}
// ① 나무 내비 (질감 1)
.minipage__nav {
display: flex;
align-items: center;
gap: $sp-2;
box-sizing: border-box;
height: 12.5%;
padding: 0 $sp-3;
background: repeating-linear-gradient(90deg, #b57e46 0 10px, #a96f38 10px 13px, #c08a4e 13px 24px, #96622e 24px 27px);
border-bottom: 2px solid rgba(45, 25, 6, .55);
}
.minipage__navdot {
width: 10px;
height: 10px;
border-radius: 50%;
background: #2d1c0d;
}
.minipage__navlink {
width: 26px;
height: 4px;
border-radius: $r-pill;
background: rgba(45, 25, 6, .5);
}
// ② 히어로 — 평면. 질감 없음
.minipage__hero {
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: $sp-2;
height: 17%;
padding: $sp-3 $sp-4 0;
background: $mp-flat;
}
.minipage__hero-eyebrow {
margin: 0;
color: $mp-ink;
font-weight: 800; font-size: clamp(13px, 3.4vw, 16px); line-height: 1;
font-family: inherit;
}
.minipage__hero-bar {
width: 76%;
height: 7px;
border-radius: $r-pill;
background: $mp-line;
}
.minipage__hero-bar--short { width: 48%; }
.minipage__hero-btn {
margin-top: $sp-1;
border-radius: $r-pill;
padding: $sp-1 $sp-3;
background: $subject-blue;
color: #ffffff;
font-weight: 700; font-size: clamp(9px, 2.4vw, 11px); line-height: 1;
font-family: inherit;
}
// ③ 기능 카드 — 종이 결 (미세 repeating-gradient)
.minipage__features {
box-sizing: border-box;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: $sp-2;
height: 14.5%;
padding: $sp-2 $sp-4;
background: $mp-flat;
}
.minipage__fcard {
display: flex;
flex-direction: column;
gap: $sp-1;
padding: $sp-2;
border-radius: $r-sm;
background:
repeating-linear-gradient(0deg, rgba(122, 105, 78, .07) 0 1px, transparent 1px 3px),
repeating-linear-gradient(90deg, rgba(122, 105, 78, .05) 0 1px, transparent 1px 4px),
$subject-cream;
box-shadow: 0 2px 4px rgba(23, 20, 26, .22);
}
.minipage__fdot {
width: 10px;
height: 10px;
border-radius: 50%;
background: $subject-blue;
}
.minipage__fdot--b { background: $subject-mint; }
.minipage__fdot--c { background: $subject-pink; }
.minipage__fline {
width: 80%;
height: 5px;
border-radius: $r-pill;
background: rgba(87, 74, 56, .22);
}
// ④ 다이얼 CTA (질감 2) — 평면 띠 위에 금속 다이얼
.minipage__cta {
box-sizing: border-box;
display: flex;
align-items: center;
gap: $sp-3;
height: 14.5%;
padding: $sp-2 $sp-4;
background: $mp-flat-2;
}
.minipage__dial {
position: relative;
width: clamp(40px, 12vw, 48px);
aspect-ratio: 1;
border-radius: 50%;
background:
conic-gradient(from 210deg, #f4f5f8 0deg, #6f7481 70deg, #aab0bc 120deg, #f4f5f8 180deg, #4b4f59 250deg, #aab0bc 300deg, #f4f5f8 360deg);
box-shadow: 0 3px 5px rgba(23, 20, 26, .4), inset 0 1px 1px rgba(255, 255, 255, .6);
flex: 0 0 auto;
}
.minipage__dial::after {
content: "";
position: absolute;
top: 30%; left: 30%; right: 30%; bottom: 30%;
border-radius: 50%;
background: radial-gradient(120% 120% at 32% 28%, #c4cde2, $subject-blue 55%, #5d74af);
}
.minipage__cta-btn {
border-radius: $r-pill;
padding: $sp-1 $sp-3;
background: $subject-ink;
color: #ffffff;
font-weight: 700; font-size: clamp(9px, 2.4vw, 11px); line-height: 1;
font-family: inherit;
}
// ⑤ 가죽 요금 카드 (질감 3)
.minipage__pricing {
position: relative;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: center;
gap: $sp-1;
height: 14.5%;
padding: $sp-2 $sp-4;
background:
repeating-linear-gradient(34deg, rgba(94, 53, 23, .4) 0 2px, transparent 2px 5px),
#7a4a2b;
}
.minipage__plan {
margin: 0;
color: #f6e7cf;
font-weight: 700; font-size: clamp(8px, 2.2vw, 10px); line-height: 1;
font-family: inherit;
letter-spacing: .12em;
}
.minipage__price {
margin: 0;
color: #ffffff;
font-weight: 800; font-size: clamp(12px, 3.2vw, 15px); line-height: 1;
font-family: inherit;
}
.minipage__stitch {
position: absolute;
top: 4px; left: 8px; right: 8px; bottom: 4px;
border-radius: $r-sm;
background:
repeating-linear-gradient(90deg, #f3e2c8 0 6px, transparent 6px 12px) top / 100% 2px no-repeat,
repeating-linear-gradient(90deg, #f3e2c8 0 6px, transparent 6px 12px) bottom / 100% 2px no-repeat,
repeating-linear-gradient(0deg, #f3e2c8 0 6px, transparent 6px 12px) left / 2px 100% no-repeat,
repeating-linear-gradient(0deg, #f3e2c8 0 6px, transparent 6px 12px) right / 2px 100% no-repeat;
}
// ⑥ 후기 — 평면
.minipage__quote {
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: $sp-1;
height: 11.5%;
padding: $sp-2 $sp-4;
background: $mp-flat;
}
.minipage__avatar {
order: -1;
width: 14px;
height: 14px;
border-radius: 50%;
background: $subject-mint;
}
.minipage__qline {
width: 88%;
height: 5px;
border-radius: $r-pill;
background: $mp-line;
}
.minipage__qline--short { width: 60%; }
// ⑦ FAQ — 평면 아코디언 (하나 열린 모습)
.minipage__faq {
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: $sp-2;
height: 10%;
padding: $sp-2 $sp-4;
background: $mp-flat-2;
}
.minipage__qrow {
display: flex;
align-items: center;
box-sizing: border-box;
height: 14px;
border-radius: $r-xs;
background: #ffffff;
box-shadow: 0 1px 2px rgba(23, 20, 26, .16);
padding: 0 $sp-2;
}
.minipage__qrow::before {
content: "";
width: 44%;
height: 4px;
border-radius: $r-pill;
background: $mp-line;
}
.minipage__qrow--open {
height: 26px;
flex-direction: column;
align-items: flex-start;
justify-content: center;
gap: $sp-1;
border: 2px solid $subject-blue;
}
.minipage__qbody {
width: 78%;
height: 4px;
border-radius: $r-pill;
background: $mp-line;
}
// ⑧ 푸터
.minipage__foot {
box-sizing: border-box;
display: flex;
align-items: center;
gap: $sp-1;
height: 5.5%;
padding: 0 $sp-4;
background: $subject-ink;
}
.minipage__footdot {
width: 6px;
height: 6px;
border-radius: 50%;
background: rgba(255, 255, 255, .4);
}
// 옆 주석 — 480px 에서만. 320px 에서는 프레임이 전부
.minipage__notes {
display: flex;
flex-direction: column;
gap: $sp-2;
max-width: 152px;
}
.minipage__note {
margin: 0;
color: rgba(255, 255, 255, .85);
font-weight: 500; font-size: 11px; line-height: 1.5;
font-family: inherit;
}
.minipage__note--tex {
color: $stage-yellow;
font-weight: 700;
}
// 핑퐁 스크롤 — 트랙이 정확히 200% 라 -50% 가 한 화면. 텔레포트 없이 위로 돌아온다
@keyframes pageScroll {
0%, 10% { transform: translateY(0); }
44%, 54% { transform: translateY(-50%); }
88%, 100% { transform: translateY(0); }
}
@media (max-width: 340px), (max-height: 220px) {
.minipage__notes { display: none; }
.minipage__frame { width: 188px; height: 164px; }
.minipage__hero { padding-top: $sp-2; }
}
@media (prefers-reduced-motion: reduce) {
.minipage__track { animation: none !important; transform: none; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #17141a;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.minipage {
display: flex;
align-items: center;
gap: 16px;
}
.minipage__frame {
position: relative;
width: clamp(184px, 58vw, 224px);
height: clamp(208px, 64vh, 252px);
border-radius: 16px;
overflow: hidden;
box-shadow: 0 12px 20px rgba(0, 0, 0, 0.5), inset 0 0 0 1px rgba(255, 255, 255, 0.08);
}
.minipage__track {
display: flex;
flex-direction: column;
height: 200%;
animation: pageScroll 2s ease-out infinite;
}
.minipage__nav {
display: flex;
align-items: center;
gap: 8px;
box-sizing: border-box;
height: 12.5%;
padding: 0 12px;
background: repeating-linear-gradient(90deg, #b57e46 0 10px, #a96f38 10px 13px, #c08a4e 13px 24px, #96622e 24px 27px);
border-bottom: 2px solid rgba(45, 25, 6, 0.55);
}
.minipage__navdot {
width: 10px;
height: 10px;
border-radius: 50%;
background: #2d1c0d;
}
.minipage__navlink {
width: 26px;
height: 4px;
border-radius: 999px;
background: rgba(45, 25, 6, 0.5);
}
.minipage__hero {
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
height: 17%;
padding: 12px 16px 0;
background: #f7f5f0;
}
.minipage__hero-eyebrow {
margin: 0;
color: #2a2732;
font-weight: 800;
font-size: clamp(13px, 3.4vw, 16px);
line-height: 1;
font-family: inherit;
}
.minipage__hero-bar {
width: 76%;
height: 7px;
border-radius: 999px;
background: #d8d4c9;
}
.minipage__hero-bar--short {
width: 48%;
}
.minipage__hero-btn {
margin-top: 4px;
border-radius: 999px;
padding: 4px 12px;
background: #2f6df6;
color: #ffffff;
font-weight: 700;
font-size: clamp(9px, 2.4vw, 11px);
line-height: 1;
font-family: inherit;
}
.minipage__features {
box-sizing: border-box;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
height: 14.5%;
padding: 8px 16px;
background: #f7f5f0;
}
.minipage__fcard {
display: flex;
flex-direction: column;
gap: 4px;
padding: 8px;
border-radius: 8px;
background: repeating-linear-gradient(0deg, rgba(122, 105, 78, 0.07) 0 1px, transparent 1px 3px), repeating-linear-gradient(90deg, rgba(122, 105, 78, 0.05) 0 1px, transparent 1px 4px), #fff7e6;
box-shadow: 0 2px 4px rgba(23, 20, 26, 0.22);
}
.minipage__fdot {
width: 10px;
height: 10px;
border-radius: 50%;
background: #2f6df6;
}
.minipage__fdot--b {
background: #4cd4a6;
}
.minipage__fdot--c {
background: #ff9ebb;
}
.minipage__fline {
width: 80%;
height: 5px;
border-radius: 999px;
background: rgba(87, 74, 56, 0.22);
}
.minipage__cta {
box-sizing: border-box;
display: flex;
align-items: center;
gap: 12px;
height: 14.5%;
padding: 8px 16px;
background: #eceae3;
}
.minipage__dial {
position: relative;
width: clamp(40px, 12vw, 48px);
aspect-ratio: 1;
border-radius: 50%;
background: conic-gradient(from 210deg, #f4f5f8 0deg, #6f7481 70deg, #aab0bc 120deg, #f4f5f8 180deg, #4b4f59 250deg, #aab0bc 300deg, #f4f5f8 360deg);
box-shadow: 0 3px 5px rgba(23, 20, 26, 0.4), inset 0 1px 1px rgba(255, 255, 255, 0.6);
flex: 0 0 auto;
}
.minipage__dial::after {
content: "";
position: absolute;
top: 30%;
left: 30%;
right: 30%;
bottom: 30%;
border-radius: 50%;
background: radial-gradient(120% 120% at 32% 28%, #c4cde2, #2f6df6 55%, #5d74af);
}
.minipage__cta-btn {
border-radius: 999px;
padding: 4px 12px;
background: #17141a;
color: #ffffff;
font-weight: 700;
font-size: clamp(9px, 2.4vw, 11px);
line-height: 1;
font-family: inherit;
}
.minipage__pricing {
position: relative;
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: center;
gap: 4px;
height: 14.5%;
padding: 8px 16px;
background: repeating-linear-gradient(34deg, rgba(94, 53, 23, 0.4) 0 2px, transparent 2px 5px), #7a4a2b;
}
.minipage__plan {
margin: 0;
color: #f6e7cf;
font-weight: 700;
font-size: clamp(8px, 2.2vw, 10px);
line-height: 1;
font-family: inherit;
letter-spacing: 0.12em;
}
.minipage__price {
margin: 0;
color: #ffffff;
font-weight: 800;
font-size: clamp(12px, 3.2vw, 15px);
line-height: 1;
font-family: inherit;
}
.minipage__stitch {
position: absolute;
top: 4px;
left: 8px;
right: 8px;
bottom: 4px;
border-radius: 8px;
background: repeating-linear-gradient(90deg, #f3e2c8 0 6px, transparent 6px 12px) top/100% 2px no-repeat, repeating-linear-gradient(90deg, #f3e2c8 0 6px, transparent 6px 12px) bottom/100% 2px no-repeat, repeating-linear-gradient(0deg, #f3e2c8 0 6px, transparent 6px 12px) left/2px 100% no-repeat, repeating-linear-gradient(0deg, #f3e2c8 0 6px, transparent 6px 12px) right/2px 100% no-repeat;
}
.minipage__quote {
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 4px;
height: 11.5%;
padding: 8px 16px;
background: #f7f5f0;
}
.minipage__avatar {
order: -1;
width: 14px;
height: 14px;
border-radius: 50%;
background: #4cd4a6;
}
.minipage__qline {
width: 88%;
height: 5px;
border-radius: 999px;
background: #d8d4c9;
}
.minipage__qline--short {
width: 60%;
}
.minipage__faq {
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 8px;
height: 10%;
padding: 8px 16px;
background: #eceae3;
}
.minipage__qrow {
display: flex;
align-items: center;
box-sizing: border-box;
height: 14px;
border-radius: 4px;
background: #ffffff;
box-shadow: 0 1px 2px rgba(23, 20, 26, 0.16);
padding: 0 8px;
}
.minipage__qrow::before {
content: "";
width: 44%;
height: 4px;
border-radius: 999px;
background: #d8d4c9;
}
.minipage__qrow--open {
height: 26px;
flex-direction: column;
align-items: flex-start;
justify-content: center;
gap: 4px;
border: 2px solid #2f6df6;
}
.minipage__qbody {
width: 78%;
height: 4px;
border-radius: 999px;
background: #d8d4c9;
}
.minipage__foot {
box-sizing: border-box;
display: flex;
align-items: center;
gap: 4px;
height: 5.5%;
padding: 0 16px;
background: #17141a;
}
.minipage__footdot {
width: 6px;
height: 6px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.4);
}
.minipage__notes {
display: flex;
flex-direction: column;
gap: 8px;
max-width: 152px;
}
.minipage__note {
margin: 0;
color: rgba(255, 255, 255, 0.85);
font-weight: 500;
font-size: 11px;
line-height: 1.5;
font-family: inherit;
}
.minipage__note--tex {
color: #ffd23f;
font-weight: 700;
}
@keyframes pageScroll {
0%, 10% {
transform: translateY(0);
}
44%, 54% {
transform: translateY(-50%);
}
88%, 100% {
transform: translateY(0);
}
}
@media (max-width: 340px), (max-height: 220px) {
.minipage__notes {
display: none;
}
.minipage__frame {
width: 188px;
height: 164px;
}
.minipage__hero {
padding-top: 8px;
}
}
@media (prefers-reduced-motion: reduce) {
.minipage__track {
animation: none !important;
transform: none;
}
}import type { CSSProperties } from "react";
import styles from "./SkeuoLandingAssembly.module.scss";
export type SkeuoLandingAssemblyProps = {
eyebrow?: string;
heroBtn?: string;
ctaBtn?: string;
plan?: string;
price?: string;
noteTex?: string;
noteFlat?: string;
noteScroll?: string;
/** 한 주기(기본 2000ms) */
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function SkeuoLandingAssembly({
eyebrow = "질감 랜딩",
heroBtn = "시작하기",
ctaBtn = "무료로 시작",
plan = "Pro 멤버십",
price = "월 24,000원",
noteTex = "질감 3곳 — 나무 내비 · 금속 다이얼 · 가죽 카드",
noteFlat = "나머지 구간은 밝은 평면으로 대비",
noteScroll = "스크롤이 질감의 자리를 알려준다",
duration = 2000,
easing = "ease-out",
color = "#f7f5f0",
}: SkeuoLandingAssemblyProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<section className={styles.minipage} style={vars} aria-label="질감 세 곳만 쓴 미니 랜딩">
<div className={styles.frame}>
<div className={styles.track}>
<header className={styles.nav}>
<span className={styles.navdot} />
<span className={styles.navlink} />
<span className={styles.navlink} />
</header>
<section className={styles.hero}>
<p className={styles.heroEyebrow}>{eyebrow}</p>
<span className={styles.heroBar} />
<span className={`${styles.heroBar} ${styles.heroBarShort}`} />
<span className={styles.heroBtn}>{heroBtn}</span>
</section>
<section className={styles.features}>
<article className={styles.fcard}><span className={styles.fdot} /><span className={styles.fline} /></article>
<article className={styles.fcard}><span className={`${styles.fdot} ${styles.fdotB}`} /><span className={styles.fline} /></article>
<article className={styles.fcard}><span className={`${styles.fdot} ${styles.fdotC}`} /><span className={styles.fline} /></article>
</section>
<section className={styles.cta}>
<span className={styles.dial} aria-hidden="true"><span /></span>
<span className={styles.ctaBtn}>{ctaBtn}</span>
</section>
<section className={styles.pricing}>
<p className={styles.plan}>{plan}</p>
<p className={styles.price}>{price}</p>
<span className={styles.stitch} aria-hidden="true" />
</section>
<section className={styles.quote}>
<span className={styles.avatar} />
<span className={styles.qline} />
<span className={`${styles.qline} ${styles.qlineShort}`} />
</section>
<section className={styles.faq}>
<span className={styles.qrow} />
<span className={`${styles.qrow} ${styles.qrowOpen}`}>
<span className={styles.qbody} />
</span>
<span className={styles.qrow} />
</section>
<footer className={styles.foot}>
<span className={styles.footdot} />
<span className={styles.footdot} />
<span className={styles.footdot} />
</footer>
</div>
</div>
<div className={styles.notes}>
<p className={`${styles.note} ${styles.noteTex}`}>{noteTex}</p>
<p className={styles.note}>{noteFlat}</p>
<p className={styles.note}>{noteScroll}</p>
</div>
</section>
);
}09 One page with three texture spots — Measured area 32.228% · moving frames 19/23Original by GODRICH · SCSS and React ship in the zip · Skeuomorphism Design Kit: 9 Tactile Landing Parts
The post this demo lives in
Skeuomorphism Design Kit: 9 Tactile Landing PartsSkeuomorphism design brings the feel of real materials — leather, metal, wood, glass — onto the screen. This kit rebuild See all demos in this post

