08 / 20
All 20 in 20 CSS Profile Cards
- 01Bento Grid Profile Card
- 02Glassmorphism 2.0 Profile Card
- 033D Flip Profile Card on Hover
- 04Social Icons Slide-up Overlay Card
- 05Dark Mode Toggle Team Card
- 06Skeleton Loading Shimmer Profile Card
- 07CSS Grid Responsive Team Member Row
- 083D Perspective Tilt Profile Card
- 09Gaming / Discord Style Profile Overlay
- 10Minimalist LinkedIn-style Business Card
- 11Retro / 90s Cyberpunk Profile Card
- 12Neumorphic Soft-Shadow User Profile
- 13Vibrant Gradient Border Profile Card
- 14Material Design 3 Avatar Card with Ripples
- 15Tabbed Content Profile Component
- 16Flexbox E-commerce Hire-Me Badge Card
- 17Testimonial Customer Review Profile Card
- 18Creator Link-in-Bio Mobile Profile Card
- 19Morphing Profile Card / Expanding Bio
- 20SaaS User Dropdown Profile Card
3D Perspective Tilt Profile Card
A card that tilts in 3D toward the cursor with a moving specular sheen, driven by a small pointer handler that batches updates through requestAnimationFrame.
Build interactive UI like this from scratch: 20 HTML, CSS & JavaScript projects
ZTM · JavaScript Web Projects · 30‑day money-back guarantee · Affiliate link
The code
<div class="pc-08">
<div class="pc-08__card" id="pc-08-tilt">
<div class="pc-08__shine"></div>
<div class="pc-08__content">
<div class="pc-08__pfp">TN</div>
<div class="pc-08__info">
<h3>Theo Nakamura</h3>
<p>3D / WebGL Developer</p>
</div>
<div class="pc-08__foot">
<span class="pc-08__chip">Three.js</span>
<span class="pc-08__chip">GLSL</span>
<span class="pc-08__chip">Blender</span>
</div>
</div>
</div>
</div><div class="pc-08">
<div class="pc-08__card" id="pc-08-tilt">
<div class="pc-08__shine"></div>
<div class="pc-08__content">
<div class="pc-08__pfp">TN</div>
<div class="pc-08__info">
<h3>Theo Nakamura</h3>
<p>3D / WebGL Developer</p>
</div>
<div class="pc-08__foot">
<span class="pc-08__chip">Three.js</span>
<span class="pc-08__chip">GLSL</span>
<span class="pc-08__chip">Blender</span>
</div>
</div>
</div>
</div>.pc-08,
.pc-08 *,
.pc-08 *::before,
.pc-08 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.pc-08 ::selection {
background: #ff4d6d;
color: #fff;
}
.pc-08 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 48px 20px;
background: #0a0710;
perspective: 900px;
color: #fff;
}
.pc-08__card {
position: relative;
width: 320px;
border-radius: 24px;
padding: 30px;
overflow: hidden;
background: linear-gradient(150deg,#241033,#3a0f2e);
border: 1px solid rgba(255,255,255,.1);
box-shadow: 0 30px 70px -28px rgba(0,0,0,.85);
transform-style: preserve-3d;
transition: transform .12s ease-out;
will-change: transform;
}
.pc-08__shine {
position: absolute;
inset: 0;
pointer-events: none;
background: radial-gradient(300px 300px at var(--mx,50%) var(--my,0%),rgba(255,255,255,.22),transparent 60%);
transition: background .1s;
}
.pc-08__content {
transform: translateZ(40px);
}
.pc-08__pfp {
width: 84px;
height: 84px;
border-radius: 22px;
display: grid;
place-items: center;
font-weight: 800;
font-size: 1.6rem;
background: linear-gradient(145deg,#ff4d6d,#c44dff);
box-shadow: 0 14px 30px -10px #ff4d6d;
}
.pc-08__info {
margin-top: 20px;
}
.pc-08__info h3 {
font-size: 1.35rem;
font-weight: 800;
}
.pc-08__info p {
color: rgba(255,255,255,.6);
font-size: .86rem;
margin-top: 3px;
}
.pc-08__foot {
display: flex;
gap: 8px;
margin-top: 22px;
}
.pc-08__chip {
font-size: .72rem;
padding: 6px 11px;
border-radius: 9px;
background: rgba(255,255,255,.1);
border: 1px solid rgba(255,255,255,.14);
}
.pc-08__hint {
position: absolute;
bottom: 12px;
right: 16px;
font-size: .66rem;
color: rgba(255,255,255,.3);
letter-spacing: .1em;
}
@media (prefers-reduced-motion: reduce) {
.pc-08__card {
transition: none;
}
}.pc-08,
.pc-08 *,
.pc-08 *::before,
.pc-08 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.pc-08 ::selection {
background: #ff4d6d;
color: #fff;
}
.pc-08 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 48px 20px;
background: #0a0710;
perspective: 900px;
color: #fff;
}
.pc-08__card {
position: relative;
width: 320px;
border-radius: 24px;
padding: 30px;
overflow: hidden;
background: linear-gradient(150deg,#241033,#3a0f2e);
border: 1px solid rgba(255,255,255,.1);
box-shadow: 0 30px 70px -28px rgba(0,0,0,.85);
transform-style: preserve-3d;
transition: transform .12s ease-out;
will-change: transform;
}
.pc-08__shine {
position: absolute;
inset: 0;
pointer-events: none;
background: radial-gradient(300px 300px at var(--mx,50%) var(--my,0%),rgba(255,255,255,.22),transparent 60%);
transition: background .1s;
}
.pc-08__content {
transform: translateZ(40px);
}
.pc-08__pfp {
width: 84px;
height: 84px;
border-radius: 22px;
display: grid;
place-items: center;
font-weight: 800;
font-size: 1.6rem;
background: linear-gradient(145deg,#ff4d6d,#c44dff);
box-shadow: 0 14px 30px -10px #ff4d6d;
}
.pc-08__info {
margin-top: 20px;
}
.pc-08__info h3 {
font-size: 1.35rem;
font-weight: 800;
}
.pc-08__info p {
color: rgba(255,255,255,.6);
font-size: .86rem;
margin-top: 3px;
}
.pc-08__foot {
display: flex;
gap: 8px;
margin-top: 22px;
}
.pc-08__chip {
font-size: .72rem;
padding: 6px 11px;
border-radius: 9px;
background: rgba(255,255,255,.1);
border: 1px solid rgba(255,255,255,.14);
}
.pc-08__hint {
position: absolute;
bottom: 12px;
right: 16px;
font-size: .66rem;
color: rgba(255,255,255,.3);
letter-spacing: .1em;
}
@media (prefers-reduced-motion: reduce) {
.pc-08__card {
transition: none;
}
}(function(){
var card = document.getElementById('pc-08-tilt');
if(!card) return;
var raf = null, tx=0, ty=0, mx=50, my=0;
function onMove(e){
var r = card.getBoundingClientRect();
var px = (e.clientX - r.left) / r.width;
var py = (e.clientY - r.top) / r.height;
tx = (py - .5) * -16;
ty = (px - .5) * 16;
mx = px * 100; my = py * 100;
if(!raf) raf = requestAnimationFrame(apply);
}
function apply(){
raf = null;
card.style.transform = 'rotateX(' + tx + 'deg) rotateY(' + ty + 'deg)';
card.style.setProperty('--mx', mx + '%');
card.style.setProperty('--my', my + '%');
}
function reset(){
if(raf) cancelAnimationFrame(raf), raf=null;
card.style.transform = 'rotateX(0) rotateY(0)';
card.style.setProperty('--mx','50%');
card.style.setProperty('--my','0%');
}
card.addEventListener('pointermove', onMove);
card.addEventListener('pointerleave', reset);
})();(function(){
var card = document.getElementById('pc-08-tilt');
if(!card) return;
var raf = null, tx=0, ty=0, mx=50, my=0;
function onMove(e){
var r = card.getBoundingClientRect();
var px = (e.clientX - r.left) / r.width;
var py = (e.clientY - r.top) / r.height;
tx = (py - .5) * -16;
ty = (px - .5) * 16;
mx = px * 100; my = py * 100;
if(!raf) raf = requestAnimationFrame(apply);
}
function apply(){
raf = null;
card.style.transform = 'rotateX(' + tx + 'deg) rotateY(' + ty + 'deg)';
card.style.setProperty('--mx', mx + '%');
card.style.setProperty('--my', my + '%');
}
function reset(){
if(raf) cancelAnimationFrame(raf), raf=null;
card.style.transform = 'rotateX(0) rotateY(0)';
card.style.setProperty('--mx','50%');
card.style.setProperty('--my','0%');
}
card.addEventListener('pointermove', onMove);
card.addEventListener('pointerleave', reset);
})();Here's a working CSS Profile Card from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: 3D Perspective Tilt Profile Card
Source: https://codefronts.com/components/css-profile-cards/3d-perspective-tilt-profile-card/
A card that tilts in 3D toward the cursor with a moving specular sheen, driven by a small pointer handler that batches updates through requestAnimationFrame.
## HTML
```html
<div class="pc-08">
<div class="pc-08__card" id="pc-08-tilt">
<div class="pc-08__shine"></div>
<div class="pc-08__content">
<div class="pc-08__pfp">TN</div>
<div class="pc-08__info">
<h3>Theo Nakamura</h3>
<p>3D / WebGL Developer</p>
</div>
<div class="pc-08__foot">
<span class="pc-08__chip">Three.js</span>
<span class="pc-08__chip">GLSL</span>
<span class="pc-08__chip">Blender</span>
</div>
</div>
</div>
</div>
```
## CSS
```css
.pc-08,
.pc-08 *,
.pc-08 *::before,
.pc-08 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.pc-08 ::selection {
background: #ff4d6d;
color: #fff;
}
.pc-08 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 48px 20px;
background: #0a0710;
perspective: 900px;
color: #fff;
}
.pc-08__card {
position: relative;
width: 320px;
border-radius: 24px;
padding: 30px;
overflow: hidden;
background: linear-gradient(150deg,#241033,#3a0f2e);
border: 1px solid rgba(255,255,255,.1);
box-shadow: 0 30px 70px -28px rgba(0,0,0,.85);
transform-style: preserve-3d;
transition: transform .12s ease-out;
will-change: transform;
}
.pc-08__shine {
position: absolute;
inset: 0;
pointer-events: none;
background: radial-gradient(300px 300px at var(--mx,50%) var(--my,0%),rgba(255,255,255,.22),transparent 60%);
transition: background .1s;
}
.pc-08__content {
transform: translateZ(40px);
}
.pc-08__pfp {
width: 84px;
height: 84px;
border-radius: 22px;
display: grid;
place-items: center;
font-weight: 800;
font-size: 1.6rem;
background: linear-gradient(145deg,#ff4d6d,#c44dff);
box-shadow: 0 14px 30px -10px #ff4d6d;
}
.pc-08__info {
margin-top: 20px;
}
.pc-08__info h3 {
font-size: 1.35rem;
font-weight: 800;
}
.pc-08__info p {
color: rgba(255,255,255,.6);
font-size: .86rem;
margin-top: 3px;
}
.pc-08__foot {
display: flex;
gap: 8px;
margin-top: 22px;
}
.pc-08__chip {
font-size: .72rem;
padding: 6px 11px;
border-radius: 9px;
background: rgba(255,255,255,.1);
border: 1px solid rgba(255,255,255,.14);
}
.pc-08__hint {
position: absolute;
bottom: 12px;
right: 16px;
font-size: .66rem;
color: rgba(255,255,255,.3);
letter-spacing: .1em;
}
@media (prefers-reduced-motion: reduce) {
.pc-08__card {
transition: none;
}
}
```
## JavaScript
```js
(function(){
var card = document.getElementById('pc-08-tilt');
if(!card) return;
var raf = null, tx=0, ty=0, mx=50, my=0;
function onMove(e){
var r = card.getBoundingClientRect();
var px = (e.clientX - r.left) / r.width;
var py = (e.clientY - r.top) / r.height;
tx = (py - .5) * -16;
ty = (px - .5) * 16;
mx = px * 100; my = py * 100;
if(!raf) raf = requestAnimationFrame(apply);
}
function apply(){
raf = null;
card.style.transform = 'rotateX(' + tx + 'deg) rotateY(' + ty + 'deg)';
card.style.setProperty('--mx', mx + '%');
card.style.setProperty('--my', my + '%');
}
function reset(){
if(raf) cancelAnimationFrame(raf), raf=null;
card.style.transform = 'rotateX(0) rotateY(0)';
card.style.setProperty('--mx','50%');
card.style.setProperty('--my','0%');
}
card.addEventListener('pointermove', onMove);
card.addEventListener('pointerleave', reset);
})();
```Here's a working CSS Profile Card from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: 3D Perspective Tilt Profile Card
Source: https://codefronts.com/components/css-profile-cards/3d-perspective-tilt-profile-card/
A card that tilts in 3D toward the cursor with a moving specular sheen, driven by a small pointer handler that batches updates through requestAnimationFrame.
## HTML
```html
<div class="pc-08">
<div class="pc-08__card" id="pc-08-tilt">
<div class="pc-08__shine"></div>
<div class="pc-08__content">
<div class="pc-08__pfp">TN</div>
<div class="pc-08__info">
<h3>Theo Nakamura</h3>
<p>3D / WebGL Developer</p>
</div>
<div class="pc-08__foot">
<span class="pc-08__chip">Three.js</span>
<span class="pc-08__chip">GLSL</span>
<span class="pc-08__chip">Blender</span>
</div>
</div>
</div>
</div>
```
## CSS
```css
.pc-08,
.pc-08 *,
.pc-08 *::before,
.pc-08 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.pc-08 ::selection {
background: #ff4d6d;
color: #fff;
}
.pc-08 {
font-family: 'Segoe UI',system-ui,sans-serif;
width: 100%;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 48px 20px;
background: #0a0710;
perspective: 900px;
color: #fff;
}
.pc-08__card {
position: relative;
width: 320px;
border-radius: 24px;
padding: 30px;
overflow: hidden;
background: linear-gradient(150deg,#241033,#3a0f2e);
border: 1px solid rgba(255,255,255,.1);
box-shadow: 0 30px 70px -28px rgba(0,0,0,.85);
transform-style: preserve-3d;
transition: transform .12s ease-out;
will-change: transform;
}
.pc-08__shine {
position: absolute;
inset: 0;
pointer-events: none;
background: radial-gradient(300px 300px at var(--mx,50%) var(--my,0%),rgba(255,255,255,.22),transparent 60%);
transition: background .1s;
}
.pc-08__content {
transform: translateZ(40px);
}
.pc-08__pfp {
width: 84px;
height: 84px;
border-radius: 22px;
display: grid;
place-items: center;
font-weight: 800;
font-size: 1.6rem;
background: linear-gradient(145deg,#ff4d6d,#c44dff);
box-shadow: 0 14px 30px -10px #ff4d6d;
}
.pc-08__info {
margin-top: 20px;
}
.pc-08__info h3 {
font-size: 1.35rem;
font-weight: 800;
}
.pc-08__info p {
color: rgba(255,255,255,.6);
font-size: .86rem;
margin-top: 3px;
}
.pc-08__foot {
display: flex;
gap: 8px;
margin-top: 22px;
}
.pc-08__chip {
font-size: .72rem;
padding: 6px 11px;
border-radius: 9px;
background: rgba(255,255,255,.1);
border: 1px solid rgba(255,255,255,.14);
}
.pc-08__hint {
position: absolute;
bottom: 12px;
right: 16px;
font-size: .66rem;
color: rgba(255,255,255,.3);
letter-spacing: .1em;
}
@media (prefers-reduced-motion: reduce) {
.pc-08__card {
transition: none;
}
}
```
## JavaScript
```js
(function(){
var card = document.getElementById('pc-08-tilt');
if(!card) return;
var raf = null, tx=0, ty=0, mx=50, my=0;
function onMove(e){
var r = card.getBoundingClientRect();
var px = (e.clientX - r.left) / r.width;
var py = (e.clientY - r.top) / r.height;
tx = (py - .5) * -16;
ty = (px - .5) * 16;
mx = px * 100; my = py * 100;
if(!raf) raf = requestAnimationFrame(apply);
}
function apply(){
raf = null;
card.style.transform = 'rotateX(' + tx + 'deg) rotateY(' + ty + 'deg)';
card.style.setProperty('--mx', mx + '%');
card.style.setProperty('--my', my + '%');
}
function reset(){
if(raf) cancelAnimationFrame(raf), raf=null;
card.style.transform = 'rotateX(0) rotateY(0)';
card.style.setProperty('--mx','50%');
card.style.setProperty('--my','0%');
}
card.addEventListener('pointermove', onMove);
card.addEventListener('pointerleave', reset);
})();
```How this works
The card sits in a perspective context; a pointer handler maps the cursor's position within the card to rotateX/rotateY values and writes them to the element's transform. Updates are coalesced inside requestAnimationFrame so the DOM is touched at most once per frame, keeping input latency (INP) low.
The same handler updates two custom properties feeding a radial --mx/--my highlight, so a specular sheen tracks the pointer for a glossy, tactile feel. On pointerleave the transform resets to flat; with JS disabled the card simply renders static and fully readable.
Make it yours
- Change tilt intensity by scaling the
(py-.5) * -16multipliers in the handler. - Soften or sharpen the settle with the
transition:transform .12son the card. - Recolour or resize the sheen via the
radial-gradienton.pc-08__shine. - Increase the parallax pop by raising the
translateZ(40px)on the content layer.
Gotchas — read before shipping
- Read layout with
getBoundingClientRect()once per move and never inside the rAF write step, to avoid layout thrash. pointermovefires rapidly — always batch throughrequestAnimationFramerather than styling on every event.- Provide a flat static state for no-JS and reduced-motion users; the tilt is an enhancement, not the content.
- Set
will-change:transform(already applied) sparingly — over-promoting layers hurts memory on mobile.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 55+ | 13+ | 59+ | 55+ |
Pointer Events plus 3D transforms; degrades to a static card without JS.