GODRICH

Skeuomorphism Design Kit: 9 Tactile Landing Parts

Skeuomorphism design brings the feel of real materials — leather, metal, wood, glass — onto the screen.

The early-2010s version of this style wrapped whole apps in stitched leather and linen, and the weight of it is exactly why flat design took over. What returned is narrower and smarter: a bright, flat page with real material in only three places. Scroll the one-pager below and try to find those three spots before reading on.

Open the full page

The nine parts that page is built from are pulled out one by one into the grid below. The order follows how materials earn their weight: leather gets sewn, metal gets machined, paper gets stacked, a knob gets turned by hand, glass gets set, wood gets opened, a sticker gets pressed on, a key gets pressed down — and the ninth part puts all of it on one page without overdoing it.

Auto-plays · click a tile to jump to its section · all nine in one zip

01Leather stitch pricing card

The leather card lands from 20px above, and the instant it touches down, the pale stitch ticks appear inside the double border as a single-frame cut. The stitching is not an image: each of the four edges carries its own repeating-linear-gradient of thread marks.

이중 테두리repeating-linear-gradientsteps(1, end)
.leather__stitch {
  position: absolute;
  top: 8px; left: 8px; right: 8px; bottom: 8px;
  border-radius: $r-control;
  opacity: 0;
  background:
    repeating-linear-gradient(90deg, $stitch 0 8px, transparent 8px 16px) top    / 100% 2px no-repeat,
    repeating-linear-gradient(90deg, $stitch 0 8px, transparent 8px 16px) bottom / 100% 2px no-repeat,
    repeating-linear-gradient(0deg,  $stitch 0 8px, transparent 8px 16px) left   / 2px 100% no-repeat,
    repeating-linear-gradient(0deg,  $stitch 0 8px, transparent 8px 16px) right  / 2px 100% no-repeat;
  animation: stitchCut $duration steps(1, end) infinite;
}
@keyframes stitchCut {
  0%, 36%  { opacity: 0; }
  38%, 86% { opacity: 1; }
  88%, 100% { opacity: 0; }
}

02Metal dial CTA

The dial swings 32 degrees each way, and because the whole disc rotates, the conic-gradient shading streams across its face and reads as machined metal. The element is a real button — the disc itself is the conversion control.

conic-gradient노치 테두리왕복 회전
.metaldial__dial {
  animation: dialSwing $duration $ease-spring infinite;
}
.metaldial__face {
  background:
    conic-gradient(from 210deg,
      $metal-hi 0deg, $metal-lo 70deg, $metal-mid 120deg,
      $metal-hi 180deg, $metal-edge 250deg, $metal-mid 300deg, $metal-hi 360deg);
  box-shadow: inset 0 2px 3px rgba(255, 255, 255, .65), inset 0 -3px 5px rgba(0, 0, 0, .4);
}
@keyframes dialSwing {
  0%, 6%    { transform: rotate(-32deg); }
  48%, 56%  { transform: rotate(32deg); }
  94%, 100% { transform: rotate(-32deg); }
}

03Paper grain feature card

Three grained sheets sit in a stack, and the top sheet slides 48px up to expose the grain of the one beneath. The grain is three 1px repeating-linear-gradient layers set in different directions, so no noise image or base64 blob is ever needed.

1px 결 겹침translateY 시트radius 16
.paperstack__sheet {
  background:
    repeating-linear-gradient(0deg,  $grain-a 0 1px, transparent 1px 3px),
    repeating-linear-gradient(90deg, $grain-b 0 1px, transparent 1px 4px),
    repeating-linear-gradient(48deg, $grain-c 0 1px, transparent 1px 7px),
    $paper-face;
}
@keyframes sheetSlide {
  0%, 10%   { transform: translateY(0); }
  46%, 58%  { transform: translateY(-48px); }
  88%, 100% { transform: translateY(0); }
}

04Volume knob widget

The knob sweeps 120 degrees each way between the low tick and the high one, its pale pointer line traveling across the dial marks. Because the pointer stays inside the knob's circle, the knob never appears to grow as it turns.

rotate 왕복지침 선radius 50%
.volknob__knob {
  background:
    radial-gradient(120% 120% at 34% 26%, rgba(255, 255, 255, .32), transparent 42%),
    conic-gradient(from 250deg, $knob-face, $knob-body 90deg, $knob-face 170deg,
      $knob-body 260deg, $knob-face 340deg);
  box-shadow: 0 6px 10px rgba(23, 20, 26, .35), inset 0 1px 2px rgba(255, 255, 255, .22);
  animation: knobSweep $duration $ease-out infinite;
}
@keyframes knobSweep {
  0%, 8%    { transform: rotate(-120deg); }
  46%, 56%  { transform: rotate(120deg); }
  92%, 100% { transform: rotate(-120deg); }
}

05Glass inlay badge

The badge sits in a recess carved out of a brushed metal plate, and a slanted highlight bar sweeps across it from left to right. As the bar crosses the center, the mint core inside the glass brightens once — light passing through, not a glow effect.

하이라이트 훑기translateX 광선inset 깊이
.glassbadge__sheen {
  position: absolute;
  top: -20%; left: 0;
  width: 44%;
  height: 140%;
  transform: translateX(-140%) rotate(24deg);
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .4) 45%,
    rgba(255, 255, 255, .55) 50%, rgba(255, 255, 255, .4) 55%, transparent);
  animation: sheenSweep $duration $ease-out infinite;
}
@keyframes flareUp {
  0%, 36%   { opacity: 0; }
  44%, 50%  { opacity: 1; }
  58%, 100% { opacity: 0; }
}

06Wood grain sliding panels

Two wood-grain leaves part to reveal a bright slot inside, stay open a beat, then close again. The grain is painted on the panels themselves, so when a leaf slides, the grain travels with it — the same thing a photo of wood would do, at none of the weight.

두 짝 분할나무 결 gradient대칭 이동
.wooddoors__leaf {
  background:
    radial-gradient(ellipse 14px 22px at 62% 30%, rgba(74, 44, 12, .55) 0 38%,
      rgba($wood-c, .5) 42% 52%, transparent 60%),
    repeating-linear-gradient(90deg,
      $wood-a 0 12px, $wood-b 12px 15px, $wood-c 15px 27px, $wood-d 27px 30px,
      $wood-a 30px 38px, $wood-lo 38px 41px);
}
@keyframes doorsOpenL {
  0%, 10%   { transform: translateX(0); }
  42%, 60%  { transform: translateX(-92%); }
  88%, 100% { transform: translateX(0); }
}

07Sticker emboss logo

The embossed sticker drops in tilted from 24px up and seats itself at minus three degrees. The relief is four layers of text-shadow: a bright line below, a dark line above, and two side corrections — all four together make the letters read as carved rather than printed.

text-shadow 4겹스탬프 착지radius 16
.emboss__brand {
  color: $emboss-ink;
  text-shadow:
    0 1px 0 $emboss-light,
    0 -1px 0 $emboss-dark,
    1px 0 0 rgba(255, 255, 255, .55),
    -1px 0 0 rgba(96, 74, 48, .35);
}
@keyframes stickerApply {
  0%        { transform: translateY(-24px) rotate(-8deg) scale(1.12); }
  40%       { transform: translateY(0) rotate(-3deg) scale(1); }
  50%       { transform: translateY(-2px) rotate(-3.6deg) scale(1.01); }
  60%, 88%  { transform: translateY(0) rotate(-3deg) scale(1); }
  100%      { transform: translateY(-24px) rotate(-8deg) scale(1.12); }
}

08Physically pressable button

The keycap sinks 8px while the side wall beneath it shrinks to a quarter via scaleY. The wall is its own element rather than a painted box-shadow, so the press stays a transform — cheap to animate, and it runs on the compositor.

옆벽 scaleY8px 스트로크radius 12
.physkey__wall {
  height: 8px;
  transform-origin: 50% 0;
  animation: wallPress $duration $ease-spring infinite;
}
@keyframes capPress {
  0%, 12%   { transform: translateY(0); }
  32%, 46%  { transform: translateY(8px); }
  62%       { transform: translateY(-3px); }
  76%, 100% { transform: translateY(0); }
}
@keyframes wallPress {
  0%, 12%   { transform: scaleY(1); }
  32%, 46%  { transform: scaleY(.25); }
  62%       { transform: scaleY(1.1); }
  76%, 100% { transform: scaleY(1); }
}

09One page with three texture spots

A miniature landing page scrolls up and down through its own sections. Texture lives in exactly three places — the wood nav, the metal dial, the leather pricing card — and every other band stays bright and flat. The trick behind the preview is a track at exactly 200% of the viewport, so translateY(-50%) moves precisely one screen.

질감 3곳핑퐁 스크롤translateY 프리뷰
.minipage__track {
  display: flex;
  flex-direction: column;
  height: 200%;
  animation: pageScroll $duration $ease-out infinite;
}
@keyframes pageScroll {
  0%, 10%   { transform: translateY(0); }
  44%, 54%  { transform: translateY(-50%); }
  88%, 100% { transform: translateY(0); }
}

Custom tailwind.config

What has to live in one place here is not the palette but the thickness of a press. A physical key rests on an 8px wall and bottoms out at 2px — drift between those two numbers and the button stops feeling seated.

theme: {
  extend: {
    colors: {
      paper: "#fbf6ec", sheet: "#ffffff", ink: "#2a2320", stone: "#5c5048",
      brand: "#2f6df6", branddeep: "#1c48b8", mint: "#2f9e74",
      leather: "#7a4a2b", leatherdeep: "#5e3517", wood: "#a96f38",
    },
    borderRadius: { card: "16px", control: "12px", chip: "4px", pill: "999px" },
    boxShadow: {
      keyrest: "inset 0 2px 0 rgba(255,255,255,.4), inset 0 -2px 0 rgba(0,0,0,.25), 0 8px 0 #1a3fa0",
      keydown: "inset 0 2px 0 rgba(255,255,255,.4), inset 0 -2px 0 rgba(0,0,0,.25), 0 2px 0 #1a3fa0",
    },
  },
},
Role Token When
Key wall at rest shadow-keyrest While the button is idle (8px)
Pressed wall shadow-keydown Only in the active: state (2px)
Texture surface tex-leather and friends Three spots per page, maximum

Vibe-Coding Prompt

With the config planted, the instruction you hand Cursor or Claude Code is one block. The ordering matters: texture placement comes before any color list. The unabridged prompt ships as prompt.md in the zip.

Texture lives in exactly three places: the nav (wood), the CTA (metal dial), and the
recommended pricing plan (leather). Untextured area must be at least twice the textured
area. Wood is a vertical repeating-linear-gradient of grain bands; the metal dial is a
conic-gradient in four stops; leather is two overlapping repeating gradients plus a top
highlight. Stitch ticks wrap each of the four edges as a repeating-linear-gradient of
8px thread and 8px gap. A button rests on an 8px wall and drops to 2px when pressed —
the cap sinks by exactly the wall it loses.

Built from that instruction, page/index.html looks like this at a 1280px viewport.

Result

Where it breaks (the trap)

Text directly on wood was the trap that lasted longest. The nav first carried a white brand name on the raw grain — but the grain repeats through four band colors, and the same white type measured 5.14:1 on the dark band (#96622e) and 3.01:1 on the bright one (#c08a4e). No single color clears 4.5:1 across all four bands, so the type moved onto dark inlay chips and the wood stayed decorative. Measure contrast before seating body text on any texture.

The second trap was travel distance on the paper sheet. Raising the top sheet by 72 percent of its own height looked fine at 480px but clipped the sheet's top edge off the stage at 320px. Switching to a flat 48px — and down to 20px on narrow screens — kept the whole sheet visible at both widths. The finished nine files are all inside the zip, which opens with the password 7dcfwy5m.

Accessibility

Texture is where contrast goes to die. Every text pairing in this kit was run through the relative-luminance formula from the WCAG 2.1 minimum contrast criterion and recorded below.

Text pairing Ratio Verdict
Leather body #f6e7cf / leather #7a4a2b 6.07:1 pass
Leather price #ffffff / leather #7a4a2b 7.39:1 pass
Paper body #574a38 / paper #fff7e6 8.07:1 pass
Paper secondary #7a6c56 / paper #fff7e6 4.80:1 pass, barely
Hero button #ffffff / brand #2f6df6 4.53:1 pass, barely
Leather body on a bright grain band #96613c 4.24:1 fail — no body text on bright bands

The last row is why the leather card keeps its copy on the dark grain side, and why the white icon idea on the mint core (1.86:1) became a deep-teal icon instead. Every one of the nine parts carries the MDN prefers-reduced-motion media query, so visitors who reduce motion get each part seated and still. The materials themselves are conic-gradient and text-shadow values, so an unsupported browser degrades to flat color, never to a broken layout.

Browse the design trends category for other styles making the rounds; what this site hands out is on the about page.

FAQ

How many texture spots can one page carry?

Three. This kit uses exactly that many — wood nav, metal dial, leather pricing card — and keeps everything else bright and flat. Past four spots the eye loses its resting place and the page slides back toward the heavy 2010s look.

Can I wrap the whole page in leather like the old apps?

For a single card, yes; for a whole landing, no. Dark texture forces you to re-measure contrast everywhere, and the brightest grain band cannot carry 4.5:1 body text at all. Texture earns its keep as a point of focus and becomes a cost as a background.

Does this drop straight into a React project?

The zip carries React versions (.tsx + .module.scss) of all nine parts alongside the vanilla files. Duration, easing, and the accent color arrive as props, and both flavors were verified against the same measured values.

Enter the archive password

The password is inside this article. You will find it as you read.