The 8 principles behind great UI layout
Alignment, hierarchy, and whitespace aren't just buzzwords — they're the load-bearing beams of everything you build.
Eight working card patterns every site needs — pricing, product, team, blog, stats, profile, feature, and testimonials.
For growing teams
<div class="cards"> <div class="card"> <h3>Starter</h3> <div class="price">$9<span>/mo</span></div> <ul> <li><i class="ph-fill ph-check-circle"></i> 1 user</li> </ul> <a class="cta">Get Starter</a> </div> <!-- featured middle card --> <div class="card featured"> <div class="badge">Most Popular</div> <!-- ... --> </div> </div>
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 20px; } .card { padding: 32px 28px; border: 1px solid #e5e5e5; border-radius: 16px; transition: all .2s; } /* Featured card inverts colors + scales up */ .card.featured { background: #111; color: #fff; transform: scale(1.03); position: relative; } .badge { position: absolute; top: -12px; left: 50%; transform: translateX(-50%); } .price { font-size: 2.75rem; font-weight: 800; letter-spacing: -0.03em; }
<div class="card"> <div class="thumb"> <span class="fav"><i class="ph ph-heart"></i></span> <!-- product image here --> </div> <div class="body"> <div class="brand">Sonos</div> <h3>Wave Bluetooth Headphones</h3> <div class="row"> <div class="price">$249</div> <button class="add-btn">Add to Cart</button> </div> </div> </div>
.thumb { height: 200px; position: relative; background: linear-gradient(135deg, #fbbf24, #f97316); } /* Favorite button floats on top of thumb */ .fav { position: absolute; top: 14px; right: 14px; width: 36px; height: 36px; border-radius: 50%; background: rgba(255,255,255,.85); } .row { display: flex; justify-content: space-between; align-items: center; }
<div class="card"> <div class="avatar-wrap"> <div class="avatar">JL</div> </div> <h3>Julia Lang</h3> <div class="role">CEO & Founder</div> <div class="socials"> <a href="#"><i class="ph ph-linkedin-logo"></i></a> <a href="#"><i class="ph ph-twitter-logo"></i></a> </div> </div>
/* Gradient banner across the top */ .avatar-wrap { height: 120px; background: linear-gradient(135deg, #ec4899, #3b82f6); position: relative; margin-bottom: 50px; } /* Avatar half-overlaps the banner */ .avatar { width: 90px; height: 90px; border-radius: 50%; border: 5px solid #fff; position: absolute; bottom: -45px; left: 50%; transform: translateX(-50%); } .socials a { width: 32px; height: 32px; border-radius: 50%; background: #f4f4f5; } .socials a:hover { background: #111; color: #fff; }
Alignment, hierarchy, and whitespace aren't just buzzwords — they're the load-bearing beams of everything you build.
Grid does 80% of what teams use Flexbox for, only cleaner. Here's when each actually wins.
OKLCH, color-mix, @color-profile — the color system finally caught up with the web in 2025.
<article class="card"> <div class="thumb"> <span class="category">Design</span> </div> <div class="body"> <h3>Post title goes here</h3> <p class="excerpt">Post excerpt here...</p> <div class="meta"> <div class="author-dot">JL</div> <span>Julia Lang · 8 min read</span> </div> </div> </article>
.thumb { height: 180px; background: linear-gradient(135deg, #6366f1, #8b5cf6); display: flex; align-items: flex-end; padding: 16px; } /* Pill category tag with inverted background */ .category { background: rgba(255,255,255,.95); color: #111; padding: 4px 10px; border-radius: 999px; font-size: 11px; font-weight: 700; } .author-dot { width: 32px; height: 32px; border-radius: 50%; background: linear-gradient(135deg, #ec4899, #3b82f6); color: #fff; }
<div class="card"> <div class="top-row"> <div class="stat-icon"><i class="ph-duotone ph-users"></i></div> <div class="delta up">+ 12.4%</div> </div> <div class="label">Active Users</div> <div class="value">8,247</div> <div class="sub">vs last month</div> </div>
.top-row { display: flex; justify-content: space-between; align-items: flex-start; } /* Tinted icon tile — subtle brand-colored bg */ .stat-icon { width: 44px; height: 44px; border-radius: 12px; background: rgba(236, 72, 153, .12); color: #ec4899; } /* Delta chip — green up, red down */ .delta.up { color: #10b981; background: rgba(16,185,129,.1); } .delta.down { color: #ef4444; background: rgba(239,68,68,.1); } .value { font-size: 2rem; font-weight: 800; letter-spacing: -0.02em; }
Mathematician, writer, and the world's first programmer. Currently building steam-powered algorithms.
Rear Admiral, compiler inventor, and enemy of the phrase "we've always done it that way."
<div class="card"> <div class="header"> <div class="avatar">AL</div> <div> <div class="name">Ada Lovelace</div> <div class="handle">@adalove</div> </div> </div> <p class="bio">Short bio here...</p> <div class="stats"> <div class="stat"><strong>1.2k</strong><span>Posts</span></div> </div> <button class="follow">Follow</button> </div>
.header { display: flex; align-items: center; gap: 16px; } .avatar { width: 58px; height: 58px; border-radius: 50%; background: linear-gradient(135deg, #ec4899, #f43f5e); display: flex; align-items: center; justify-content: center; color: #fff; font-weight: 700; } /* Stat row divided by top/bottom borders */ .stats { display: flex; justify-content: space-between; padding: 16px 0; border-top: 1px solid #eee; border-bottom: 1px solid #eee; }
Ship static pages in milliseconds. Edge-cached. No cold starts.
HTTPS, CSP headers, and rate limiting enabled out of the box.
50+ tools ready to go: Slack, GitHub, Stripe, Notion, and more.
Track every request with live dashboards and anomaly detection.
<div class="card"> <div class="icon"><i class="ph-duotone ph-lightning"></i></div> <h3>Blazing fast</h3> <p>Ship static pages in milliseconds.</p> </div>
/* Subtle card on dark background */ .card { padding: 28px; border-radius: 14px; background: rgba(255,255,255,.04); border: 1px solid rgba(255,255,255,.08); transition: all .2s; } .card:hover { border-color: rgba(236, 72, 153, .4); transform: translateY(-3px); } /* Gradient tinted icon tile */ .icon { width: 48px; height: 48px; border-radius: 12px; background: linear-gradient(135deg, rgba(236,72,153,.2), rgba(59,130,246,.15)); color: #ec4899; }
This completely changed how our team ships. What used to take a sprint now takes an afternoon.
The documentation alone is worth the price. Every question I had was already answered with a live example.
Finally something that treats students like designers instead of button-pushers. My class loves it.
<div class="card"> <div class="rating"> <i class="ph-fill ph-star"></i> <!-- 5 stars --> </div> <p class="quote">This changed how our team ships.</p> <div class="person"> <div class="avatar">RN</div> <div> <div class="name">Renée Nakamura</div> <div class="title">Head of Product · Vellum</div> </div> </div> </div>
/* Decorative open-quote behind the text */ .card::before { content: "\201C"; position: absolute; top: 8px; left: 22px; font-family: Georgia, serif; font-size: 5rem; color: rgba(236, 72, 153, .15); line-height: 1; } .quote { font-style: italic; position: relative; line-height: 1.65; } .rating { color: #f59e0b; }
<article class="card"> <img src="photo.jpg" alt=""> <div class="overlay"> <span class="pill">Travel</span> <h3>Post title</h3> <div class="meta"> <span>8 min read</span> <span class="dot"></span> <span>Mar 2026</span> </div> </div> </article>
.card { position: relative; aspect-ratio: 4 / 5; border-radius: 18px; overflow: hidden; } /* Image fills the card, scales on hover */ .card img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform .6s; } .card:hover img { transform: scale(1.08); } /* Dark scrim anchors text to the bottom */ .overlay { position: absolute; inset: 0; background: linear-gradient(180deg, transparent 35%, rgba(0,0,0,.82)); padding: 26px; display: flex; flex-direction: column; justify-content: flex-end; color: #fff; } .pill { align-self: flex-start; background: rgba(255,255,255,.92); color: #111; padding: 5px 12px; border-radius: 999px; font-size: 10px; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; }
A practical guide to starting with color, type, and tone — before touching a single component.
Three rules that keep critique sharp without turning into a personality contest. Works for remote teams too.
Skip the "so what do you think?" and get to the behaviors that actually reveal the problem you're solving.
Sketch six ideas, vote, and ship the best one. The format has worked at three companies and counting.
<article class="card"> <!-- Use background-image for flexible ratio --> <div class="img" style="background-image:url('photo.jpg')"></div> <div class="body"> <div class="tag">Design</div> <h3>Post title</h3> <p>Short excerpt...</p> <div class="row"> <i class="ph ph-clock"></i> <span>9 min read</span> </div> </div> </article>
/* Horizontal split: fixed image column + flexible body */ .card { display: grid; grid-template-columns: 160px 1fr; border-radius: 16px; overflow: hidden; border: 1px solid #eee; } .card .img { background-size: cover; background-position: center; } .card .body { padding: 20px 22px; display: flex; flex-direction: column; justify-content: center; } /* Accent-colored category tag */ .tag { color: #ec4899; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .12em; }
Editorial layout principles — hierarchy, whitespace, captions — translate surprisingly well to the web.
Typography decisions that happen before anyone touches a font. Leading, tracking, and trust.
Not every decision needs Figma. Sometimes paper, walks, and a phone camera is the right stack.
<article class="card"> <div class="thumb"> <img src="photo.jpg" alt=""> <span class="ribbon new">New</span> </div> <div class="body"> <h3>Editorial title</h3> <p>Magazine-style description.</p> <div class="bottom"> <div class="author"> <img src="avatar.jpg" alt=""> <span>Julia Lang</span> </div> <span class="date">Apr 14</span> </div> </div> </article>
/* Serif headline for magazine feel */ .body h3 { font-family: Georgia, serif; font-size: 1.4rem; font-weight: 700; line-height: 1.25; } /* Image zooms while card stays put */ .thumb { position: relative; aspect-ratio: 16 / 10; overflow: hidden; } .thumb img { width: 100%; height: 100%; object-fit: cover; transition: transform .6s; } .card:hover .thumb img { transform: scale(1.06); } /* Ribbon sits on top of the image */ .ribbon { position: absolute; top: 14px; left: 14px; background: #111; color: #fff; padding: 6px 12px; border-radius: 6px; font-size: 11px; font-weight: 700; text-transform: uppercase; } .ribbon.new { background: linear-gradient(135deg, #ec4899, #f43f5e); } .ribbon.sale { background: linear-gradient(135deg, #10b981, #22c55e); } /* Author + date row separated by a divider */ .bottom { display: flex; justify-content: space-between; padding-top: 14px; border-top: 1px solid #f0f0f0; }
12 images · Mar 2026
18 images · Apr 2026
24 images · Feb 2026
<article class="card"> <div class="img-wrap"> <img src="photo.jpg" alt=""> <span class="heart"><i class="ph-fill ph-heart"></i></span> </div> <!-- The floating panel overlaps the image's bottom --> <div class="content-float"> <div class="tag">Studio</div> <h3>Collection title</h3> <p>12 images · Mar 2026</p> </div> </article>
/* Card is tall so the floating panel has room to overlap */ .card { position: relative; padding-bottom: 50px; } .img-wrap { position: relative; aspect-ratio: 4 / 5; border-radius: 16px; overflow: hidden; box-shadow: 0 20px 50px rgba(191, 90, 242, .18); } /* Absolute positioning + negative translate = the overlap */ .content-float { position: absolute; bottom: 0; left: 16px; right: 16px; background: #fff; border-radius: 14px; padding: 18px 20px; box-shadow: 0 10px 30px rgba(0,0,0,.08); transform: translateY(30%); } /* Floating heart button on the image corner */ .heart { position: absolute; top: 14px; right: 14px; width: 38px; height: 38px; border-radius: 50%; background: rgba(255,255,255,.92); color: #ec4899; }
<div class="flip"> <div class="face front"> <img src="photo.jpg" alt=""> <div class="name"><h3>Maya Chen</h3></div> </div> <div class="face back"> <div class="tagline">"Your quote here."</div> <a class="cta" href="#">View portfolio →</a> </div> </div>
/* Parent needs perspective for the 3D effect */ .cards { perspective: 1200px; } .flip { position: relative; transform-style: preserve-3d; transition: transform .8s cubic-bezier(.2,.7,.3,1); } .flip:hover { transform: rotateY(180deg); } /* Both faces stack — hide the back side of each */ .face { position: absolute; inset: 0; backface-visibility: hidden; border-radius: 18px; overflow: hidden; } /* Pre-rotate the back so it faces you after the flip */ .back { transform: rotateY(180deg); background: linear-gradient(135deg, #ec4899, #8b5cf6, #3b82f6); color: #fff; }
Works beautifully on photography or gradient backgrounds — the blur picks up the colors behind.
Matches the Liquid Glass aesthetic Apple uses in Control Center and notifications.
Glass is beautiful against visuals. Avoid stacking too many — the effect loses its magic.
<!-- Parent needs a colorful / photo background --> <div class="v14-wrap"> <div class="glass"> <div class="icon"><i class="ph-duotone ph-sparkle"></i></div> <h3>Modern feel</h3> <p>Description here.</p> </div> </div>
/* Background must have something to blur */ .v14-wrap { background: radial-gradient(circle at 20% 30%, #ec4899, transparent 45%), radial-gradient(circle at 80% 60%, #3b82f6, transparent 45%), linear-gradient(135deg, #6366f1, #a855f7); } /* The magic trio: translucent bg + backdrop-filter + thin border */ .glass { background: rgba(255, 255, 255, .12); backdrop-filter: blur(20px) saturate(1.4); -webkit-backdrop-filter: blur(20px) saturate(1.4); border: 1px solid rgba(255, 255, 255, .25); border-radius: 20px; padding: 26px; color: #fff; }
Everything you need to ship your first product to the world.
Unlimited projects, priority support, and analytics.
For growing teams — collaboration, roles, and custom branding.
<!-- Outer = animated gradient, inner = actual content --> <div class="grad-card"> <div class="inner"> <div class="icon"><i class="ph-duotone ph-rocket-launch"></i></div> <h3>Launch Plan</h3> <p>Description here.</p> <div class="price">$19/mo</div> </div> </div>
/* Outer box = colorful gradient, small padding = border width */ .grad-card { padding: 3px; border-radius: 18px; background: linear-gradient(135deg, #ec4899, #8b5cf6, #3b82f6, #06b6d4, #ec4899); background-size: 300% 300%; animation: borderFlow 6s linear infinite; } /* Inner card covers most of the box, leaving a slim gradient ring */ .inner { background: #fff; border-radius: 16px; padding: 28px; } /* Animate the gradient's position for a flowing effect */ @keyframes borderFlow { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } } /* Bonus: gradient-filled text for the price */ .price { background: linear-gradient(135deg, #ec4899, #8b5cf6); -webkit-background-clip: text; background-clip: text; color: transparent; }
<div class="deck"> <!-- Back two layers are decorative --> <div class="layer behind-2"></div> <div class="layer behind-1"></div> <!-- Front card has the actual content --> <div class="layer front-card"> <img src="photo.jpg" alt=""> <div class="body"> <h3>Collection title</h3> </div> </div> </div>
.deck { position: relative; aspect-ratio: 4 / 5; } /* All 3 layers absolute-stacked in the same spot */ .deck .layer { position: absolute; inset: 0; border-radius: 18px; transition: transform .4s cubic-bezier(.2,.8,.3,1); } /* Offset + rotate the decorative layers */ .layer.behind-2 { transform: translate(16px, 16px) rotate(4deg) scale(.96); } .layer.behind-1 { transform: translate(8px, 8px) rotate(-2deg) scale(.98); } /* On hover, fan them out in opposite directions */ .deck:hover .layer.behind-2 { transform: translate(26px, 22px) rotate(7deg); } .deck:hover .layer.behind-1 { transform: translate(-14px, 14px) rotate(-6deg); }
Show the thing you build on nights and weekends — not the client work that pays the bills.
Minimal JS, aggressive caching, and edge delivery — your site should feel instant.
The little details — easter eggs, micro-interactions, and inside jokes — are what people remember.
Portfolios evolve. Revisit yours every six months and prune the pieces that no longer represent you.
<!-- Each card gets a unique color via --neon custom property --> <div class="neon"> <div class="icon"><i class="ph-duotone ph-heart"></i></div> <h3><span>Passion</span> project</h3> <p>Description...</p> </div>
/* Each card gets its own --neon color */ .neon:nth-child(1) { --neon: #ec4899; animation-delay: 0s; } .neon:nth-child(2) { --neon: #3b82f6; animation-delay: 0.9s; } .neon:nth-child(3) { --neon: #22d3ee; animation-delay: 1.8s; } .neon { background: #14141f; color: #f5f5f7; padding: 28px; border-radius: 16px; animation: neonPulse 3.6s ease-in-out infinite; } /* Pulse between dim and bright glow using the color variable */ @keyframes neonPulse { 0%, 100% { box-shadow: 0 0 20px color-mix(in srgb, var(--neon) 25%, transparent); } 50% { box-shadow: 0 0 38px color-mix(in srgb, var(--neon) 55%, transparent); } } /* The accent word gets the neon color too */ h3 span { color: var(--neon); }
<div class="tilt"> <img src="photo.jpg" alt=""> <span class="badge">Nature</span> <div class="content"> <h3>Mountain Series</h3> </div> </div>
/* Parent sets the 3D stage */ .cards { perspective: 1000px; } .tilt { position: relative; border-radius: 18px; overflow: hidden; transform-style: preserve-3d; transition: transform .4s, box-shadow .4s; } /* The whole card tilts on hover */ .tilt:hover { transform: rotateY(-14deg) rotateX(8deg) scale(1.03); box-shadow: -20px 30px 60px rgba(0,0,0,.3); } /* Content + badge lift forward on the Z-axis */ .tilt .content { transform: translateZ(40px); } .tilt .badge { transform: translateZ(50px); }