30 CSS Hover Effects22 / 30
CSS Curtain Reveal Image Hover Effect
Five theatrical curtain-style hover reveals — split curtain open, top-to-bottom drop, blind slats slide, venetian blind open, and radial iris expand — all using clip-path polygon and inset transitions to theatrically reveal hidden image content on hover.
Published
This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.
The code
<div class="hv-22">
<div class="hv-22__grid">
<div class="hv-22__cell">
<div class="hv-22__wrap hv-22__wrap--split">
<div class="hv-22__reveal hv-22__swatch--a">
<span class="hv-22__tag">OPEN</span>
</div>
</div>
<span class="hv-22__label">split curtain</span>
</div>
<div class="hv-22__cell">
<div class="hv-22__wrap hv-22__wrap--drop">
<div class="hv-22__reveal hv-22__swatch--b">
<span class="hv-22__tag">DROP</span>
</div>
<div class="hv-22__curtain-drop"></div>
</div>
<span class="hv-22__label">curtain drop</span>
</div>
<div class="hv-22__cell">
<div class="hv-22__wrap hv-22__wrap--iris">
<div class="hv-22__reveal hv-22__swatch--c">
<span class="hv-22__tag">IRIS</span>
</div>
<div class="hv-22__iris-mask"></div>
</div>
<span class="hv-22__label">iris expand</span>
</div>
<div class="hv-22__cell">
<div class="hv-22__wrap hv-22__wrap--slat">
<div class="hv-22__reveal hv-22__swatch--d">
<span class="hv-22__tag">BLIND</span>
</div>
<div class="hv-22__slat hv-22__slat--1"></div>
<div class="hv-22__slat hv-22__slat--2"></div>
<div class="hv-22__slat hv-22__slat--3"></div>
<div class="hv-22__slat hv-22__slat--4"></div>
<div class="hv-22__slat hv-22__slat--5"></div>
</div>
<span class="hv-22__label">venetian blind</span>
</div>
</div>
</div><div class="hv-22">
<div class="hv-22__grid">
<div class="hv-22__cell">
<div class="hv-22__wrap hv-22__wrap--split">
<div class="hv-22__reveal hv-22__swatch--a">
<span class="hv-22__tag">OPEN</span>
</div>
</div>
<span class="hv-22__label">split curtain</span>
</div>
<div class="hv-22__cell">
<div class="hv-22__wrap hv-22__wrap--drop">
<div class="hv-22__reveal hv-22__swatch--b">
<span class="hv-22__tag">DROP</span>
</div>
<div class="hv-22__curtain-drop"></div>
</div>
<span class="hv-22__label">curtain drop</span>
</div>
<div class="hv-22__cell">
<div class="hv-22__wrap hv-22__wrap--iris">
<div class="hv-22__reveal hv-22__swatch--c">
<span class="hv-22__tag">IRIS</span>
</div>
<div class="hv-22__iris-mask"></div>
</div>
<span class="hv-22__label">iris expand</span>
</div>
<div class="hv-22__cell">
<div class="hv-22__wrap hv-22__wrap--slat">
<div class="hv-22__reveal hv-22__swatch--d">
<span class="hv-22__tag">BLIND</span>
</div>
<div class="hv-22__slat hv-22__slat--1"></div>
<div class="hv-22__slat hv-22__slat--2"></div>
<div class="hv-22__slat hv-22__slat--3"></div>
<div class="hv-22__slat hv-22__slat--4"></div>
<div class="hv-22__slat hv-22__slat--5"></div>
</div>
<span class="hv-22__label">venetian blind</span>
</div>
</div>
</div>.hv-22,
.hv-22 *,
.hv-22 *::before,
.hv-22 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.hv-22 ::selection {
background: #be185d;
color: #fff;
}
.hv-22 {
--bg: #08040c;
--dim: #6b7280;
font-family: 'Segoe UI',system-ui,sans-serif;
background: var(--bg);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 60px 24px;
}
.hv-22__grid {
display: grid;
grid-template-columns: repeat(2,1fr);
gap: 32px;
max-width: 720px;
width: 100%;
}
.hv-22__cell {
display: flex;
flex-direction: column;
gap: 12px;
}
.hv-22__label {
font-size: 11px;
letter-spacing: .12em;
text-transform: uppercase;
color: var(--dim);
}
.hv-22__wrap {
aspect-ratio: 16/9;
border-radius: 12px;
overflow: hidden;
position: relative;
cursor: pointer;
}
.hv-22__reveal {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.hv-22__tag {
font-size: 1.4rem;
font-weight: 900;
letter-spacing: .15em;
color: #fff;
opacity: .8;
}
/* swatches */
.hv-22__swatch--a {
background: linear-gradient(135deg,#0f172a,#1e40af,#3b82f6);
}
.hv-22__swatch--b {
background: linear-gradient(135deg,#0f172a,#7c3aed,#c084fc);
}
.hv-22__swatch--c {
background: linear-gradient(135deg,#0f172a,#0f766e,#2dd4bf);
}
.hv-22__swatch--d {
background: linear-gradient(135deg,#0f172a,#be185d,#f9a8d4);
}
/* 1 — split curtain */
.hv-22__wrap--split::before,
.hv-22__wrap--split::after {
content: '';
position: absolute;
top: 0;
width: 50%;
height: 100%;
background: #1a0a2e;
z-index: 2;
transition: transform .5s cubic-bezier(.4,0,.2,1);
}
.hv-22__wrap--split::before {
left: 0;
}
.hv-22__wrap--split::after {
right: 0;
}
.hv-22__wrap--split:hover::before {
transform: translateX(-100%);
}
.hv-22__wrap--split:hover::after {
transform: translateX(100%);
}
/* 2 — curtain drop */
.hv-22__curtain-drop {
position: absolute;
inset: 0;
z-index: 2;
background: #1a002a;
transform: scaleY(1);
transform-origin: top;
transition: transform .5s cubic-bezier(.4,0,.2,1);
}
.hv-22__wrap--drop:hover .hv-22__curtain-drop {
transform: scaleY(0);
}
/* 3 — iris expand */
.hv-22__iris-mask {
position: absolute;
inset: 0;
z-index: 2;
background: #0a001a;
clip-path: polygon(0 0,100% 0,100% 100%,0 100%,0 0,50% 50%);
transition: clip-path .5s cubic-bezier(.4,0,.2,1);
}
.hv-22__wrap--iris .hv-22__iris-mask {
clip-path: circle(75% at 50% 50%);
background: transparent;
border: none;
}
/* re-approach: solid overlay with clip-path shrinking to reveal */
.hv-22__wrap--iris::before {
content: '';
position: absolute;
inset: 0;
z-index: 2;
background: #0a001a;
clip-path: polygon(
0 0,50% 0,50% 0,50% 0,50% 100%,50% 100%,50% 100%,0 100%,
0 0,100% 0,100% 50%,100% 50%,100% 50%,100% 100%,50% 100%
);
transition: clip-path .5s cubic-bezier(.4,0,.2,1),opacity .5s;
opacity: 1;
}
.hv-22__iris-mask {
display: none;
}
.hv-22__wrap--iris::after {
content: '';
position: absolute;
inset: 0;
z-index: 1;
}
/* simpler iris via circle clip-path on a dark overlay */
.hv-22__wrap--iris::before {
clip-path: circle(76% at 50% 50%);
background: transparent;
box-shadow: 0 0 0 200px #0a001a;
border-radius: 50%;
top: 50%;
left: 50%;
width: 0;
height: 0;
transform: translate(-50%,-50%);
clip-path: none;
transition: box-shadow .5s cubic-bezier(.4,0,.2,1);
}
.hv-22__wrap--iris:hover::before {
box-shadow: 0 0 0 0 #0a001a;
}
/* 4 — venetian blind slats */
.hv-22__slat {
position: absolute;
left: 0;
width: 100%;
height: 20%;
background: #1a0014;
z-index: 2;
transform: scaleY(1);
transform-origin: top;
transition: transform .3s cubic-bezier(.4,0,.2,1);
}
.hv-22__slat--1 {
top: 0%;
transition-delay: 0s;
}
.hv-22__slat--2 {
top: 20%;
transition-delay: .05s;
}
.hv-22__slat--3 {
top: 40%;
transition-delay: .1s;
}
.hv-22__slat--4 {
top: 60%;
transition-delay: .15s;
}
.hv-22__slat--5 {
top: 80%;
transition-delay: .2s;
}
.hv-22__wrap--slat:hover .hv-22__slat {
transform: scaleY(0);
}
@media (max-width: 520px) {
.hv-22__grid {
grid-template-columns: 1fr;
}
}
@media (prefers-reduced-motion: reduce) {
.hv-22__wrap::before,
.hv-22__wrap::after,
.hv-22__curtain-drop,
.hv-22__slat {
transition: none!important;
}
}.hv-22,
.hv-22 *,
.hv-22 *::before,
.hv-22 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.hv-22 ::selection {
background: #be185d;
color: #fff;
}
.hv-22 {
--bg: #08040c;
--dim: #6b7280;
font-family: 'Segoe UI',system-ui,sans-serif;
background: var(--bg);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 60px 24px;
}
.hv-22__grid {
display: grid;
grid-template-columns: repeat(2,1fr);
gap: 32px;
max-width: 720px;
width: 100%;
}
.hv-22__cell {
display: flex;
flex-direction: column;
gap: 12px;
}
.hv-22__label {
font-size: 11px;
letter-spacing: .12em;
text-transform: uppercase;
color: var(--dim);
}
.hv-22__wrap {
aspect-ratio: 16/9;
border-radius: 12px;
overflow: hidden;
position: relative;
cursor: pointer;
}
.hv-22__reveal {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.hv-22__tag {
font-size: 1.4rem;
font-weight: 900;
letter-spacing: .15em;
color: #fff;
opacity: .8;
}
/* swatches */
.hv-22__swatch--a {
background: linear-gradient(135deg,#0f172a,#1e40af,#3b82f6);
}
.hv-22__swatch--b {
background: linear-gradient(135deg,#0f172a,#7c3aed,#c084fc);
}
.hv-22__swatch--c {
background: linear-gradient(135deg,#0f172a,#0f766e,#2dd4bf);
}
.hv-22__swatch--d {
background: linear-gradient(135deg,#0f172a,#be185d,#f9a8d4);
}
/* 1 — split curtain */
.hv-22__wrap--split::before,
.hv-22__wrap--split::after {
content: '';
position: absolute;
top: 0;
width: 50%;
height: 100%;
background: #1a0a2e;
z-index: 2;
transition: transform .5s cubic-bezier(.4,0,.2,1);
}
.hv-22__wrap--split::before {
left: 0;
}
.hv-22__wrap--split::after {
right: 0;
}
.hv-22__wrap--split:hover::before {
transform: translateX(-100%);
}
.hv-22__wrap--split:hover::after {
transform: translateX(100%);
}
/* 2 — curtain drop */
.hv-22__curtain-drop {
position: absolute;
inset: 0;
z-index: 2;
background: #1a002a;
transform: scaleY(1);
transform-origin: top;
transition: transform .5s cubic-bezier(.4,0,.2,1);
}
.hv-22__wrap--drop:hover .hv-22__curtain-drop {
transform: scaleY(0);
}
/* 3 — iris expand */
.hv-22__iris-mask {
position: absolute;
inset: 0;
z-index: 2;
background: #0a001a;
clip-path: polygon(0 0,100% 0,100% 100%,0 100%,0 0,50% 50%);
transition: clip-path .5s cubic-bezier(.4,0,.2,1);
}
.hv-22__wrap--iris .hv-22__iris-mask {
clip-path: circle(75% at 50% 50%);
background: transparent;
border: none;
}
/* re-approach: solid overlay with clip-path shrinking to reveal */
.hv-22__wrap--iris::before {
content: '';
position: absolute;
inset: 0;
z-index: 2;
background: #0a001a;
clip-path: polygon(
0 0,50% 0,50% 0,50% 0,50% 100%,50% 100%,50% 100%,0 100%,
0 0,100% 0,100% 50%,100% 50%,100% 50%,100% 100%,50% 100%
);
transition: clip-path .5s cubic-bezier(.4,0,.2,1),opacity .5s;
opacity: 1;
}
.hv-22__iris-mask {
display: none;
}
.hv-22__wrap--iris::after {
content: '';
position: absolute;
inset: 0;
z-index: 1;
}
/* simpler iris via circle clip-path on a dark overlay */
.hv-22__wrap--iris::before {
clip-path: circle(76% at 50% 50%);
background: transparent;
box-shadow: 0 0 0 200px #0a001a;
border-radius: 50%;
top: 50%;
left: 50%;
width: 0;
height: 0;
transform: translate(-50%,-50%);
clip-path: none;
transition: box-shadow .5s cubic-bezier(.4,0,.2,1);
}
.hv-22__wrap--iris:hover::before {
box-shadow: 0 0 0 0 #0a001a;
}
/* 4 — venetian blind slats */
.hv-22__slat {
position: absolute;
left: 0;
width: 100%;
height: 20%;
background: #1a0014;
z-index: 2;
transform: scaleY(1);
transform-origin: top;
transition: transform .3s cubic-bezier(.4,0,.2,1);
}
.hv-22__slat--1 {
top: 0%;
transition-delay: 0s;
}
.hv-22__slat--2 {
top: 20%;
transition-delay: .05s;
}
.hv-22__slat--3 {
top: 40%;
transition-delay: .1s;
}
.hv-22__slat--4 {
top: 60%;
transition-delay: .15s;
}
.hv-22__slat--5 {
top: 80%;
transition-delay: .2s;
}
.hv-22__wrap--slat:hover .hv-22__slat {
transform: scaleY(0);
}
@media (max-width: 520px) {
.hv-22__grid {
grid-template-columns: 1fr;
}
}
@media (prefers-reduced-motion: reduce) {
.hv-22__wrap::before,
.hv-22__wrap::after,
.hv-22__curtain-drop,
.hv-22__slat {
transition: none!important;
}
}Here's a working CSS Hover Effect 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: CSS Curtain Reveal Image Hover Effect
Source: https://codefronts.com/motion/css-hover-effects/css-curtain-reveal-image-hover-effect/
Five theatrical curtain-style hover reveals — split curtain open, top-to-bottom drop, blind slats slide, venetian blind open, and radial iris expand — all using clip-path polygon and inset transitions to theatrically reveal hidden image content on hover.
## HTML
```html
<div class="hv-22">
<div class="hv-22__grid">
<div class="hv-22__cell">
<div class="hv-22__wrap hv-22__wrap--split">
<div class="hv-22__reveal hv-22__swatch--a">
<span class="hv-22__tag">OPEN</span>
</div>
</div>
<span class="hv-22__label">split curtain</span>
</div>
<div class="hv-22__cell">
<div class="hv-22__wrap hv-22__wrap--drop">
<div class="hv-22__reveal hv-22__swatch--b">
<span class="hv-22__tag">DROP</span>
</div>
<div class="hv-22__curtain-drop"></div>
</div>
<span class="hv-22__label">curtain drop</span>
</div>
<div class="hv-22__cell">
<div class="hv-22__wrap hv-22__wrap--iris">
<div class="hv-22__reveal hv-22__swatch--c">
<span class="hv-22__tag">IRIS</span>
</div>
<div class="hv-22__iris-mask"></div>
</div>
<span class="hv-22__label">iris expand</span>
</div>
<div class="hv-22__cell">
<div class="hv-22__wrap hv-22__wrap--slat">
<div class="hv-22__reveal hv-22__swatch--d">
<span class="hv-22__tag">BLIND</span>
</div>
<div class="hv-22__slat hv-22__slat--1"></div>
<div class="hv-22__slat hv-22__slat--2"></div>
<div class="hv-22__slat hv-22__slat--3"></div>
<div class="hv-22__slat hv-22__slat--4"></div>
<div class="hv-22__slat hv-22__slat--5"></div>
</div>
<span class="hv-22__label">venetian blind</span>
</div>
</div>
</div>
```
## CSS
```css
.hv-22,
.hv-22 *,
.hv-22 *::before,
.hv-22 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.hv-22 ::selection {
background: #be185d;
color: #fff;
}
.hv-22 {
--bg: #08040c;
--dim: #6b7280;
font-family: 'Segoe UI',system-ui,sans-serif;
background: var(--bg);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 60px 24px;
}
.hv-22__grid {
display: grid;
grid-template-columns: repeat(2,1fr);
gap: 32px;
max-width: 720px;
width: 100%;
}
.hv-22__cell {
display: flex;
flex-direction: column;
gap: 12px;
}
.hv-22__label {
font-size: 11px;
letter-spacing: .12em;
text-transform: uppercase;
color: var(--dim);
}
.hv-22__wrap {
aspect-ratio: 16/9;
border-radius: 12px;
overflow: hidden;
position: relative;
cursor: pointer;
}
.hv-22__reveal {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.hv-22__tag {
font-size: 1.4rem;
font-weight: 900;
letter-spacing: .15em;
color: #fff;
opacity: .8;
}
/* swatches */
.hv-22__swatch--a {
background: linear-gradient(135deg,#0f172a,#1e40af,#3b82f6);
}
.hv-22__swatch--b {
background: linear-gradient(135deg,#0f172a,#7c3aed,#c084fc);
}
.hv-22__swatch--c {
background: linear-gradient(135deg,#0f172a,#0f766e,#2dd4bf);
}
.hv-22__swatch--d {
background: linear-gradient(135deg,#0f172a,#be185d,#f9a8d4);
}
/* 1 — split curtain */
.hv-22__wrap--split::before,
.hv-22__wrap--split::after {
content: '';
position: absolute;
top: 0;
width: 50%;
height: 100%;
background: #1a0a2e;
z-index: 2;
transition: transform .5s cubic-bezier(.4,0,.2,1);
}
.hv-22__wrap--split::before {
left: 0;
}
.hv-22__wrap--split::after {
right: 0;
}
.hv-22__wrap--split:hover::before {
transform: translateX(-100%);
}
.hv-22__wrap--split:hover::after {
transform: translateX(100%);
}
/* 2 — curtain drop */
.hv-22__curtain-drop {
position: absolute;
inset: 0;
z-index: 2;
background: #1a002a;
transform: scaleY(1);
transform-origin: top;
transition: transform .5s cubic-bezier(.4,0,.2,1);
}
.hv-22__wrap--drop:hover .hv-22__curtain-drop {
transform: scaleY(0);
}
/* 3 — iris expand */
.hv-22__iris-mask {
position: absolute;
inset: 0;
z-index: 2;
background: #0a001a;
clip-path: polygon(0 0,100% 0,100% 100%,0 100%,0 0,50% 50%);
transition: clip-path .5s cubic-bezier(.4,0,.2,1);
}
.hv-22__wrap--iris .hv-22__iris-mask {
clip-path: circle(75% at 50% 50%);
background: transparent;
border: none;
}
/* re-approach: solid overlay with clip-path shrinking to reveal */
.hv-22__wrap--iris::before {
content: '';
position: absolute;
inset: 0;
z-index: 2;
background: #0a001a;
clip-path: polygon(
0 0,50% 0,50% 0,50% 0,50% 100%,50% 100%,50% 100%,0 100%,
0 0,100% 0,100% 50%,100% 50%,100% 50%,100% 100%,50% 100%
);
transition: clip-path .5s cubic-bezier(.4,0,.2,1),opacity .5s;
opacity: 1;
}
.hv-22__iris-mask {
display: none;
}
.hv-22__wrap--iris::after {
content: '';
position: absolute;
inset: 0;
z-index: 1;
}
/* simpler iris via circle clip-path on a dark overlay */
.hv-22__wrap--iris::before {
clip-path: circle(76% at 50% 50%);
background: transparent;
box-shadow: 0 0 0 200px #0a001a;
border-radius: 50%;
top: 50%;
left: 50%;
width: 0;
height: 0;
transform: translate(-50%,-50%);
clip-path: none;
transition: box-shadow .5s cubic-bezier(.4,0,.2,1);
}
.hv-22__wrap--iris:hover::before {
box-shadow: 0 0 0 0 #0a001a;
}
/* 4 — venetian blind slats */
.hv-22__slat {
position: absolute;
left: 0;
width: 100%;
height: 20%;
background: #1a0014;
z-index: 2;
transform: scaleY(1);
transform-origin: top;
transition: transform .3s cubic-bezier(.4,0,.2,1);
}
.hv-22__slat--1 {
top: 0%;
transition-delay: 0s;
}
.hv-22__slat--2 {
top: 20%;
transition-delay: .05s;
}
.hv-22__slat--3 {
top: 40%;
transition-delay: .1s;
}
.hv-22__slat--4 {
top: 60%;
transition-delay: .15s;
}
.hv-22__slat--5 {
top: 80%;
transition-delay: .2s;
}
.hv-22__wrap--slat:hover .hv-22__slat {
transform: scaleY(0);
}
@media (max-width: 520px) {
.hv-22__grid {
grid-template-columns: 1fr;
}
}
@media (prefers-reduced-motion: reduce) {
.hv-22__wrap::before,
.hv-22__wrap::after,
.hv-22__curtain-drop,
.hv-22__slat {
transition: none!important;
}
}
```Here's a working CSS Hover Effect 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: CSS Curtain Reveal Image Hover Effect
Source: https://codefronts.com/motion/css-hover-effects/css-curtain-reveal-image-hover-effect/
Five theatrical curtain-style hover reveals — split curtain open, top-to-bottom drop, blind slats slide, venetian blind open, and radial iris expand — all using clip-path polygon and inset transitions to theatrically reveal hidden image content on hover.
## HTML
```html
<div class="hv-22">
<div class="hv-22__grid">
<div class="hv-22__cell">
<div class="hv-22__wrap hv-22__wrap--split">
<div class="hv-22__reveal hv-22__swatch--a">
<span class="hv-22__tag">OPEN</span>
</div>
</div>
<span class="hv-22__label">split curtain</span>
</div>
<div class="hv-22__cell">
<div class="hv-22__wrap hv-22__wrap--drop">
<div class="hv-22__reveal hv-22__swatch--b">
<span class="hv-22__tag">DROP</span>
</div>
<div class="hv-22__curtain-drop"></div>
</div>
<span class="hv-22__label">curtain drop</span>
</div>
<div class="hv-22__cell">
<div class="hv-22__wrap hv-22__wrap--iris">
<div class="hv-22__reveal hv-22__swatch--c">
<span class="hv-22__tag">IRIS</span>
</div>
<div class="hv-22__iris-mask"></div>
</div>
<span class="hv-22__label">iris expand</span>
</div>
<div class="hv-22__cell">
<div class="hv-22__wrap hv-22__wrap--slat">
<div class="hv-22__reveal hv-22__swatch--d">
<span class="hv-22__tag">BLIND</span>
</div>
<div class="hv-22__slat hv-22__slat--1"></div>
<div class="hv-22__slat hv-22__slat--2"></div>
<div class="hv-22__slat hv-22__slat--3"></div>
<div class="hv-22__slat hv-22__slat--4"></div>
<div class="hv-22__slat hv-22__slat--5"></div>
</div>
<span class="hv-22__label">venetian blind</span>
</div>
</div>
</div>
```
## CSS
```css
.hv-22,
.hv-22 *,
.hv-22 *::before,
.hv-22 *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.hv-22 ::selection {
background: #be185d;
color: #fff;
}
.hv-22 {
--bg: #08040c;
--dim: #6b7280;
font-family: 'Segoe UI',system-ui,sans-serif;
background: var(--bg);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 60px 24px;
}
.hv-22__grid {
display: grid;
grid-template-columns: repeat(2,1fr);
gap: 32px;
max-width: 720px;
width: 100%;
}
.hv-22__cell {
display: flex;
flex-direction: column;
gap: 12px;
}
.hv-22__label {
font-size: 11px;
letter-spacing: .12em;
text-transform: uppercase;
color: var(--dim);
}
.hv-22__wrap {
aspect-ratio: 16/9;
border-radius: 12px;
overflow: hidden;
position: relative;
cursor: pointer;
}
.hv-22__reveal {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.hv-22__tag {
font-size: 1.4rem;
font-weight: 900;
letter-spacing: .15em;
color: #fff;
opacity: .8;
}
/* swatches */
.hv-22__swatch--a {
background: linear-gradient(135deg,#0f172a,#1e40af,#3b82f6);
}
.hv-22__swatch--b {
background: linear-gradient(135deg,#0f172a,#7c3aed,#c084fc);
}
.hv-22__swatch--c {
background: linear-gradient(135deg,#0f172a,#0f766e,#2dd4bf);
}
.hv-22__swatch--d {
background: linear-gradient(135deg,#0f172a,#be185d,#f9a8d4);
}
/* 1 — split curtain */
.hv-22__wrap--split::before,
.hv-22__wrap--split::after {
content: '';
position: absolute;
top: 0;
width: 50%;
height: 100%;
background: #1a0a2e;
z-index: 2;
transition: transform .5s cubic-bezier(.4,0,.2,1);
}
.hv-22__wrap--split::before {
left: 0;
}
.hv-22__wrap--split::after {
right: 0;
}
.hv-22__wrap--split:hover::before {
transform: translateX(-100%);
}
.hv-22__wrap--split:hover::after {
transform: translateX(100%);
}
/* 2 — curtain drop */
.hv-22__curtain-drop {
position: absolute;
inset: 0;
z-index: 2;
background: #1a002a;
transform: scaleY(1);
transform-origin: top;
transition: transform .5s cubic-bezier(.4,0,.2,1);
}
.hv-22__wrap--drop:hover .hv-22__curtain-drop {
transform: scaleY(0);
}
/* 3 — iris expand */
.hv-22__iris-mask {
position: absolute;
inset: 0;
z-index: 2;
background: #0a001a;
clip-path: polygon(0 0,100% 0,100% 100%,0 100%,0 0,50% 50%);
transition: clip-path .5s cubic-bezier(.4,0,.2,1);
}
.hv-22__wrap--iris .hv-22__iris-mask {
clip-path: circle(75% at 50% 50%);
background: transparent;
border: none;
}
/* re-approach: solid overlay with clip-path shrinking to reveal */
.hv-22__wrap--iris::before {
content: '';
position: absolute;
inset: 0;
z-index: 2;
background: #0a001a;
clip-path: polygon(
0 0,50% 0,50% 0,50% 0,50% 100%,50% 100%,50% 100%,0 100%,
0 0,100% 0,100% 50%,100% 50%,100% 50%,100% 100%,50% 100%
);
transition: clip-path .5s cubic-bezier(.4,0,.2,1),opacity .5s;
opacity: 1;
}
.hv-22__iris-mask {
display: none;
}
.hv-22__wrap--iris::after {
content: '';
position: absolute;
inset: 0;
z-index: 1;
}
/* simpler iris via circle clip-path on a dark overlay */
.hv-22__wrap--iris::before {
clip-path: circle(76% at 50% 50%);
background: transparent;
box-shadow: 0 0 0 200px #0a001a;
border-radius: 50%;
top: 50%;
left: 50%;
width: 0;
height: 0;
transform: translate(-50%,-50%);
clip-path: none;
transition: box-shadow .5s cubic-bezier(.4,0,.2,1);
}
.hv-22__wrap--iris:hover::before {
box-shadow: 0 0 0 0 #0a001a;
}
/* 4 — venetian blind slats */
.hv-22__slat {
position: absolute;
left: 0;
width: 100%;
height: 20%;
background: #1a0014;
z-index: 2;
transform: scaleY(1);
transform-origin: top;
transition: transform .3s cubic-bezier(.4,0,.2,1);
}
.hv-22__slat--1 {
top: 0%;
transition-delay: 0s;
}
.hv-22__slat--2 {
top: 20%;
transition-delay: .05s;
}
.hv-22__slat--3 {
top: 40%;
transition-delay: .1s;
}
.hv-22__slat--4 {
top: 60%;
transition-delay: .15s;
}
.hv-22__slat--5 {
top: 80%;
transition-delay: .2s;
}
.hv-22__wrap--slat:hover .hv-22__slat {
transform: scaleY(0);
}
@media (max-width: 520px) {
.hv-22__grid {
grid-template-columns: 1fr;
}
}
@media (prefers-reduced-motion: reduce) {
.hv-22__wrap::before,
.hv-22__wrap::after,
.hv-22__curtain-drop,
.hv-22__slat {
transition: none!important;
}
}
```How this works
The split curtain uses two absolutely positioned ::before / ::after pseudo-elements each covering half the card. On hover, the left half transitions transform: translateX(-100%) and the right half translateX(100%) — they slide apart like stage curtains revealing the content below. The parent has overflow: hidden so the curtains disappear off the sides cleanly.
The venetian blind uses 5 overlapping div strips, each animated with a staggered animation-delay so they open top-to-bottom in sequence. The iris expand uses clip-path: circle(0% at 50% 50%) transitioning to circle(75% at 50% 50%) on the overlay layer — the circle grows outward from the center revealing the underneath layer, simulating a camera iris opening.
Make it yours
- Change curtain color to match the brand by editing the
backgroundon the::before/::aftercurtain panels — use a gradient for a more theatrical fabric look. - Speed up the reveal by reducing transition-duration to
.3sor slow to.8sfor a ceremonial theatrical pace. - Add a soft shadow to the curtain edges with
box-shadow: -8px 0 16px rgba(0,0,0,.3)on the left panel and its mirror on the right for a fabric-drape depth feel. - Replace the solid curtain with a
repeating-linear-gradientof thin vertical stripes to simulate fabric weave texture on the curtain panels. - Combine the iris clip-path reveal with a
scale(1.1)on the revealed image so it zooms in simultaneously as the iris opens.
Gotchas — read before shipping
- clip-path transitions require the start and end shapes to have the same number of polygon vertices — you cannot transition between a
circle()and apolygon(). - Staggered animation delays on venetian slats fire on every hover — if the user hovers and un-hovers rapidly, the animation resets mid-way; add a minimum hover dwell with JavaScript for polished production use.
clip-path: circle()with percentage values calculates against the element's reference box —75%on a non-square element won't form a perfect circle.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 60+ | 13.1+ | 54+ | 60+ |
clip-path polygon/circle are fully supported. Percentage circle values may behave differently on non-square elements in older Safari.