Pure CSS diagonal optical illusion with parallel lines distorted on a yellow background, revealed on hover.

Diagonal Illusion Pattern in Pure CSS

A pure CSS diagonal optical illusion that distorts parallel lines without JavaScript. Combines repeating-linear-gradient grid stripes with rotated conic-gradient elements on body::before and body::after pseudo-elements. Hovering transitions the @property --color variable to transparent, removing the visual interference to demonstrate that all diagonal lines are strictly parallel.

Technologies:
CSS
Difficulty: Intermediate
Browser Support (as of Jul 2026):
Chrome Chrome Edge Edge Safari Safari Firefox Firefox
Features:
Optical Illusion Pure CSS Animated Property Hover Reveal
License: MIT
Loading...
/*
optical effect
The diagonal black lines are parallel, but they look like they are not. Mouse over to reveal
*/
@property --color {
  syntax: '<color>';
  initial-value: #f00;
  inherits: true;
}

body {
  --color: #000;
  margin: 0;
  font-size: 1vmax;
  height: 100vh;
  overflow: hidden;
  transition: --color 1s;
  background: #fd5;
}

body:hover {
  --color: #0000;
}

body::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  width: 300em;
  height: 300em;
  background:
    repeating-linear-gradient(#0000 0 18em, #000 0 20em),
    conic-gradient(at 1em 8em, #0000 75%, var(--color) 0) 0 -5em / 2em 20em;
  transform: skewY(-45deg);
}

body::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  width: 300em;
  height: 300em;
  background:
    repeating-linear-gradient(#0000 0 18em, #000 0 20em),
    conic-gradient(at 1em 8em, #0000 75%, var(--color) 0) 0 -5em / 2em 20em;
  transform: rotate(90deg) skewY(45deg) translate(0.25em, 12em);
}
  • Colors / Themes: Change canvas color via background: #fd5 on body. Adjust illusion tile color --color: #000 and initial value initial-value: #f00 inside @property --color. Tweak hover reveal state via --color: #0000.
  • Gradients & Visual Effects: Modify grid line spacing by editing repeating-linear-gradient(#0000 0 18em, #000 0 20em) stop offsets. Alter distortion block positioning through conic-gradient(at 1em 8em, #0000 75%, var(--color) 0) 0 -5em / 2em 20em inside pseudo-elements.
  • Sizing, Layout & Scales: Change viewport scale density via font-size: 1vmax on body. Adjust tilt severity by modifying transform: skewY(-45deg) on body::before and transform: rotate(90deg) skewY(45deg) on body::after.
Interactive CSS grid accordion gallery featuring notched card corners and hover column expansion.

Expanding CSS Grid Notched Accordion Gallery

An interactive image gallery accordion leveraging CSS Grid and :has() selectors. Column ratios expand dynamically on hover via custom property --grid-cols. Cards use overlapping grid coordinates and corner-shape: notch with border-radius to create angled panels. Hovering removes the notch on active and adjacent cards while displaying CSS counter() labels.

Technologies:
HTML CSS
Difficulty: Intermediate
Browser Support (as of Jul 2026):
Chrome Chrome Edge Edge Firefox Firefox (partial) Safari Safari (partial)
Features:
Grid Accordion Notched Corners Hover Expansion Pure CSS State
Code by: Chris Bolson Chris Bolson
License: MIT
Cyberpunk theme switcher using CSS corner-shape notches, light-dark colors, and pure CSS state handling.

Futuristic CSS Theme Switcher with Corner Shaping

A pure CSS theme switcher showcasing bleeding-edge spec features. Uses :has() on :root to toggle color schemes without JavaScript. Leverages light-dark(), color-mix(), and native inline if() logic for dynamic styling. Corner shapes like notch and superellipse() provide a pixel-art sci-fi aesthetic. Built on an accessible <fieldset> radio control structure with full keyboard focus support.

Technologies:
HTML CSS
Difficulty: Advanced
Browser Support (as of Jul 2026):
Chrome Chrome Edge Edge
Features:
Pure CSS State Corner Shaping Color Mix Engine Inline CSS if()
Code by: Mandy Nicole Mandy Nicole
License: MIT
Loading...
<fieldset id="color-scheme">
  <legend>Theme</legend>
  <label for="system" tabindex="0">
    <input type="radio" id="system" name="theme" value="system" tabindex="-1"/><span>Sys</span>
  </label>
  <label for="light" tabindex="0">
    <input type="radio" id="light" name="theme" value="light" tabindex="-1" checked="checked"/><span>Light</span>
  </label>
  <label for="dark" tabindex="0">
    <input type="radio" id="dark" name="theme" value="dark" tabindex="-1"/><span>Dark</span>
  </label>
</fieldset>
fieldset {
	border: 2px solid 
		if(
			style(--color-scheme: light):
				color-mix(
					in oklab, 
					var(--theme-accent), 
					transparent 80%
				)
			;
			
			/* uses NOT over ELSE to respect `prefers-color-scheme` */
			not style(--color-scheme: light): 
				color-mix(
					in oklab, 
					var(--theme-accent), 
					transparent 70%
				)
			;
		)
	;	
	border-radius: 5px;
	corner-shape: notch;
	color: var(--theme-text);
	outline: 
		7px ridge 		
		if(
			style(--color-scheme: light):
				color-mix(
					in oklab, 
					var(--theme-accent), 
					transparent 25%
				)
			;
			
			/* uses NOT over ELSE to respect `prefers-color-scheme` */
			not style(--color-scheme: light): 
				color-mix(
					in oklab, 
					var(--theme-accent), 
					transparent 25%
				)
			;
		)
	;	
	outline-offset: 2px;
	
	filter: 
		drop-shadow(
			0 0 1dvw 
			color-mix(
				in oklab, 
				light-dark(	transparent, var(--theme-accent) ), 
				transparent 5%
			)
		)
		drop-shadow(
			0 0 5dvw 
			color-mix(
				in oklab, 
				light-dark(	transparent, var(--theme-accent) ), 
				transparent 25%
			)
		)
	;
	
	font-family: "Silkscreen", monospace;	
	font-size: 1.75rex;
	letter-spacing: -.075ch;
	text-rendering: geometricprecision;
	
	display: grid;
	gap: 0;
	
	margin: 0;
	padding: 0;
	
	max-width: 30px;
	padding: 6px 12px;
	
	position: relative;
	z-index: 3;
	
	legend {
		color: color-mix(in oklab, var(--theme-accent), transparent 25%);
		text-align: center;
		text-box: trim-both cap alphabetic;
		top: -.5ex;
		position: relative;
		padding-top: .84ex;
	}
}

input {
	--d: 10px;
	--rotation: 225deg;
	
	appearance: none;
	
	background: currentColor;
	color: inherit;
	
	border-radius: 6px;
	corner-shape: notch;
	rotate: var(--rotation);
	
	margin-top: -1px;
	
	outline: 2px solid color-mix(in oklab, var(--theme-text), transparent 50%);
	outline-offset: 2px;
	
	height: var(--d);
	width: var(--d);

	label:hover &:not(:checked) {
		--rotation: 90deg;
		corner-shape: superellipse(-.25);
		outline-color: color-mix(in oklab, var(--theme-text), transparent 75%);
	}
	
	&:checked {		
		--rotation: unset;
		color: inherit;
		outline-color: color-mix(in oklab, var(--theme-accent), transparent 25%);
		outline-offset: 3px;
		corner-shape: superellipse(-.5);
		
		label:hover & {
			outline-color: color-mix(in oklab, var(--theme-accent), transparent 0%);
		}
	}
}

label {
	border-radius: 6px;
	corner-shape: superellipse(-1);
	
	display: flex;
	align-items: center;
	gap: 8px;	
	
	user-select: none;
	padding: 19px 24px 18px;
	
	span {
		text-box: trim-both cap alphabetic;
		display: inline-block;
	}
	
	&:has(:checked) {		
		background: var(--theme-text);		
		color: var(--theme-canvas);
		
		input { color: var(--theme-accent);	}
	}
	
	&:is(:focus-visible),
	&:has(:focus-within) {
		--rotation: 225deg;
		outline-width: 2px;
		outline-style: solid;
		outline-offset: -4px;
	}
	
	&:is(:focus-visible) {
		outline-color: color-mix(in oklab, var(--theme-accent), transparent 50%);
		&:not(:has(:checked)):hover { background: color-mix(in oklab, var(--theme-accent), transparent 84%); }
	}
	
	&:has(:focus-within) {
		outline-color: color-mix(in oklab, var(--theme-accent), transparent 50%);
	}
	
	&:has(:focus) {
		outline-color: color-mix(in oklab, var(--theme-accent), transparent 50%);
	}
}

@media (prefers-reduced-motion: no-preference) {		
	html,
	body,
	fieldset, 
	label, 
	input { 
		transition-property: background, border-color, box-shadow, color, filter, rotate;
		transition-duration: .2s;
		transition-timing-function: ease-out;
	}
	
	label,
	input {
		&:focus-within,
		&:focus,
		&:hover,
		&:focus-visible {
			transition-property: background, border-color, rotate;
			transition-duration: .2s;
			transition-timing-function: ease-in;
		}
	}
}

:root {
	--white: 0 0 90%;
	--black: 0 0 10%;
	--gray: 0 0 45%;
	
	--gold: 36deg 75% 33%;
	--turq: 150deg 55% 35%;
	
	--theme-canvas: light-dark(
		hsl(var(--white)),
		hsl(var(--black))
	);
	
	--theme-text: light-dark(
		hsl(var(--black)),
		hsl(var(--white))
	);
	
	--theme-text-2: ;
	
	--theme-accent: light-dark(
		hsl(var(--gold)),
		hsl(var(--turq))
	);
	
	--color-scheme: light dark;
	color-scheme: var(--color-scheme);
	&:has(#color-scheme input[value="light"]:checked) {	--color-scheme: light; }
	&:has(#color-scheme input[value="dark"]:checked) { --color-scheme: dark; }
	
	container: html / inline-size;
}

@font-face {
	src: url("https://assets.codepen.io/2392/Silkscreen-Regular.woff2");
	font-family: "Silkscreen";
}
	
body {
	accent-color: var(--theme-accent);
	background: var(--theme-canvas);	
	color: var(--theme-text);
	
	box-sizing: border-box;
	display: grid;
	place-content: center;	
	
	height: 100cqh;	
	margin: 0;
	padding: 3rem 3cqw;	
}
  • Colors & Palette: Redefine --gold (36deg 75% 33%), --turq (150deg 55% 35%), or override --theme-accent and --theme-canvas inside :root using custom hsl() coordinates.
  • Corner Geometry: Replace corner-shape: notch or corner-shape: superellipse(-.5) on fieldset and input selectors to alter edge geometry.
  • Switch Sizing: Adjust --d: 10px on input to rescale input indicators, or tweak padding: 19px 24px 18px on label for custom panel height.
  • Typography: Swap font-family: "Silkscreen", monospace and font-size: 1.75rex on fieldset to change retro text rendering.
  • Glow & Shadow Effects: Modify drop-shadow() parameters and color-mix() opacity percentages on fieldset to control light-dark background halos.
  • Rotation Angles: Alter --rotation: 225deg on input to change indicator hover transformation direction.
Responsive portfolio project grid featuring holographic background overlays, container query cards, and stacked image previews.

Holographic CSS Project Cards Grid

An advanced portfolio project card layout leveraging CSS Container Queries and modern color functions. Features dynamic light/dark/holo theme switching using :has() selectors and @property animated radial gradients. Staggered card preview stacks expand on hover with pure CSS if() media logic and mix-blend-mode effects. Semantic layout powered by CSS Grid.

Technologies:
HTML CSS
Difficulty: Advanced
Browser Support (as of Jul 2026):
Chrome Chrome Edge Edge
Features:
Holographic Overlay Container Queries Pure CSS Theme Stacked Previews
Code by: Mandy Nicole Mandy Nicole
License: MIT
Loading...
<header class="settings">
  <fieldset id="color-scheme">
    <label for="system" tabindex="0">
      <input id="system" type="radio" name="theme" value="system" checked="checked"/>				<span>Sys</span>
    </label>
    <label for="light" tabindex="0">
      <input id="light" type="radio" name="theme" value="light"/>				<span>Light</span>
    </label>
    <label for="dark" tabindex="0">
      <input id="dark" type="radio" name="theme" value="dark"/>				<span>Dark</span>
    </label>
    <label for="holo" tabindex="0">
      <input id="holo" type="checkbox" name="theme" value="holo" checked="checked"/>				<span>Holo</span>
    </label>
  </fieldset>
</header>
<div class="project-container">
  <div class="project project--3 full">
    <div class="preview-container">
      <picture tabindex="0">
        <div class="bg red" alt="fakeCap"></div>
      </picture>
      <picture tabindex="0">
        <div class="bg blue" alt="fakeCap"></div>
      </picture>
      <picture tabindex="0">
        <div class="bg green" alt="fakeCap"></div>
      </picture>
    </div>
    <div class="content-container">
      <hgroup>
        <h1>Project / Client Name</h1>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
      </hgroup>
      <ul class="tag-container">
        <li class="tag design">ui/ux</li>
        <li class="tag design">component library</li>
        <li class="tag design">product design</li>
        <li class="tag dev">interactive prototypes</li>
        <li class="tag dev">html</li>
        <li class="tag dev">css</li>
        <li class="tag dev">documentation</li>
      </ul>
    </div>
  </div>
  <div class="project project--1">
    <div class="preview-container">
      <picture tabindex="0">
        <div class="bg green" alt="fakeCap"></div>
      </picture>
      <picture tabindex="0">
        <div class="bg red" alt="fakeCap"></div>
      </picture>
      <picture tabindex="0">
        <div class="bg blue" alt="fakeCap"></div>
      </picture>
    </div>
    <div class="content-container">
      <hgroup>
        <h1>Project / Client Name</h1>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
      </hgroup>
      <ul class="tag-container">
        <li class="tag design">ui/ux</li>
        <li class="tag design">component library</li>
        <li class="tag design">a11y auditing</li>
        <li class="tag design">wcag conformance</li>
        <li class="tag dev">interactive prototyping</li>
        <li class="tag dev">html</li>
        <li class="tag dev">css</li>
        <li class="tag dev">documentation</li>
      </ul>
    </div>
  </div>
  <div class="project project--2">
    <div class="preview-container">
      <picture tabindex="0">
        <div class="bg blue" alt="fakeCap"></div>
      </picture>
      <picture tabindex="0">
        <div class="bg green" alt="fakeCap"></div>
      </picture>
      <picture tabindex="0">
        <div class="bg red" alt="fakeCap"></div>
      </picture>
    </div>
    <div class="content-container">
      <hgroup>
        <h1>Project / Client Name</h1>
        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
      </hgroup>
      <ul class="tag-container">
        <li class="tag design">ui/ux</li>
        <li class="tag design">component library</li>
        <li class="tag design">wcag conformance</li>
        <li class="tag dev">interactive prototyping</li>
        <li class="tag dev">html</li>
        <li class="tag dev">css</li>
        <li class="tag dev">documentation</li>
      </ul>
    </div>
  </div>
</div>
@property --1-holo-pos {
  syntax: "<percentage>+";
  inherits: true;
  initial-value: 100% 0%;
}

@property --2-holo-pos {
  syntax: "<percentage>+";
  inherits: true;
  initial-value: 0% 100%;
}

:root {
	--scheme-light: light;
	--scheme-dark: dark;
	--color-scheme: var(--scheme-light) var(--scheme-dark);	
	color-scheme: var(--color-scheme);
	
	--primary--h: 25;
	--primary--s: 30%;
	--primary--l: 50%;
	
	--primary-000: var(--primary--h) var(--primary--s) 2.5%;	
	--primary-005: var(--primary--h) var(--primary--s) 6%;
	--primary-010: var(--primary--h) var(--primary--s) 12%;
	--primary-020: var(--primary--h) var(--primary--s) 20%;
	--primary-030: var(--primary--h) var(--primary--s) 30%;
	--primary-040: var(--primary--h) var(--primary--s) 40%;
	--primary-050: var(--primary--h) var(--primary--s) 50%;
	--primary-060: var(--primary--h) var(--primary--s) 60%;
	--primary-080: var(--primary--h) var(--primary--s) 80%;
	--primary-070: var(--primary--h) var(--primary--s) 70%;
	--primary-090: var(--primary--h) var(--primary--s) 90%;
	
	--secondary--h: 206;
	--secondary--s: 12%;
	--secondary--l: 50%;
	
	--secondary-000: var(--secondary--h) var(--secondary--s) 2.5%;	
	--secondary-005: var(--secondary--h) var(--secondary--s) 6%;
	--secondary-010: var(--secondary--h) var(--secondary--s) 12%;
	--secondary-020: var(--secondary--h) var(--secondary--s) 20%;
	--secondary-030: var(--secondary--h) var(--secondary--s) 30%;
	--secondary-040: var(--secondary--h) var(--secondary--s) 40%;
	--secondary-050: var(--secondary--h) var(--secondary--s) 50%;
	--secondary-060: var(--secondary--h) var(--secondary--s) 60%;
	--secondary-080: var(--secondary--h) var(--secondary--s) 80%;
	--secondary-070: var(--secondary--h) var(--secondary--s) 70%;
	--secondary-090: var(--secondary--h) var(--secondary--s) 90%;
	
	--bg: light-dark( hsl(var(--primary-080)), hsl(var(--secondary-010)) );
	
	--spacer-x: 1.75dvw;
	--spacer-y: 18px;
	
	--spacer: min(var(--spacer-x), var(--spacer-y));
	
	--radius-min: 3cqi;
	--radius-001: min(var(--radius-min), 18px);
	--radius-002: calc( var(--radius-001) / 2 );	
}

.project-container {
	--col-w--min: calc(max(390px, 50cqw) - var(--spacer-x));
	
	container: project-container / inline-size;	
	display: grid;
	grid-template-columns: 
		repeat(
			auto-fit, 
			minmax( min( var(--col-w--min), 100% ), 1fr )
		)
	;
	gap: calc(var(--spacer-y) * 2) var(--spacer-x);
	margin: 0 auto;
}

.project {	
	--preview-x: var(--x-init);
	
	background: 
		linear-gradient(
			light-dark( hsl(var(--primary-090) / .5), hsl(var(--secondary-020) / 1) ),
			light-dark( hsl(var(--primary-090) / 1), hsl(var(--secondary-040) / 1) )
		)
	;
	border-radius: var(--radius-001);
	
	container: project / inline-size;	
	
	display: grid;
	grid-template-rows: auto 1fr;
	align-content: start;
	min-height: 50cqh;
	
	&:not(.full) {
		--x-init: 6cqw;
		--x-hover: -1cqw;
		
		.preview-container {			
			picture {
				aspect-ratio: 9/16;
				height: 100cqh;
				
				&:nth-of-type(2) {
					height: 110cqh;
				}
			}
		}
	}
	
	&.full { 
		--x-init: 2cqw;
		--x-hover: -4cqw;
		grid-column: 1 / -1;
		
		.preview-container {			
			picture {
				aspect-ratio: 16/9;
				height: 100cqh;
				
				&:nth-of-type(2) {
					height: 110cqh;
					width: 75%;
					position: absolute;
				}
			}
		}
	}	
}

.preview-container {	
	border-radius: var(--radius-002);
	
	container: preview-container / size;	
	
	display: flex;
	align-items: center;
	justify-content: center;
	
	margin: var(--spacer) var(--spacer) 0 var(--spacer);
	padding: calc(var(--spacer) / 3);	
	
	min-height: 150px;
	height: 60cqw;
	max-height: 60dvh;
	
	transition-property: height;
	transition-duration: .3s;
	transition-timing-function: ease-out;

	will-change: height;
	
	position: relative;

	&:before {		
		background: linear-gradient(
			light-dark( hsl(var(--primary-070)), hsl(var(--secondary-010) / .75) ),
			light-dark( hsl(var(--primary-080) / .5), hsl(var(--secondary-020) / .35) )
		);
		
		border-bottom: 
			1px 
			solid 
			light-dark( 
				hsl(var(--primary--h) var(--primary--s) 95% / 1),  
				hsl(var(--secondary-060) / .45) 
			)
		;	
		border-radius: inherit;		
		opacity: 1;
		
		content: "";
		inset: 0;
		position: absolute;
	}
	
	picture {
		--height-init: 100cqh;
		--height: var(--height-init);
		--shadow-blur: 4cqw;
		--shadow-spread: 0;
		--shadow-y: .25cqh;
		--translateY: 1.25cqh;
		
		border-radius: var(--radius-002);			
		box-shadow: 
			0 
			var(--shadow-y)
			var(--shadow-blur) 
			var(--shadow-spread)
			
			if(
				style(--color-scheme: var(--scheme-light)): hsl( var(--primary-020) / var(--shadow-opacity, 60%) );
				not style(--color-scheme: var(--scheme-light)): hsl( var(--secondary-010) / var(--shadow-opacity, 75%) );
			)
		;
		
		container: picture / size;
		display: grid;
		place-content: start;	
		min-height: 0;
		
		overflow: hidden;
		
		translate: var(--translateX, 0) var(--translateY);
		
		transition-property: box-shadow, filter, opacity, rotate, translate, width;
		transition-duration: .3s;
		transition-timing-function: ease-in-out;
		
		position: relative;		
		
		/* stack <picture> contents */
		* { grid-area: 1 / 1;	}
		
		img {			
			border-radius: inherit;
			pointer-events: none;
			user-select: none;
			width: 100cqw;
			
			&.fill {
				all: initial;
				object-fit: cover;
			}
		}
		
		.project:has(:hover, :focus) & {
			--preview-x: var(--x-hover);
			
			--shadow-blur: 25cqh;
			--shadow-spread: 3cqh;
			--shadow-opacity: if(
					style(--color-scheme: var(--scheme-light)): 70%;
					not style(--color-scheme: var(--scheme-light)): 100%;
				)
			;
			filter: unset;
			mix-blend-mode: unset;
		}
		
		/* inactive state */
		.preview-container:has(:hover) &:not(:hover),
		&:first-of-type,
		&:last-of-type {
			mix-blend-mode: luminosity;
			filter: 
				grayscale(100%)
				if(
					style(--color-scheme: var(--scheme-light)): opacity(50%);
					not style(--color-scheme: var(--scheme-light)): brightness(60%) opacity(80%);
				)
			;
			
			&:focus,
			&:hover {
				filter: unset;
				mix-blend-mode: unset;
				opacity: 1;
				rotate: 0deg;
				z-index: 600;
			}
		}
		
		&:nth-of-type(1) {
			--translateX: var(--preview-x);
			rotate: if(
				media(width > 750px): -3deg; 
				media(width > 480px): -2deg; 
				else: 0deg
			);			
		}
		
		&:nth-of-type(2) {
			--translateY: -1.25cqh;
			scale: 1;
			z-index: 500;
			
			&:hover {
				--translateY: -2cqh;
			}
		}
		
		&:nth-of-type(3) {
			--translateX: calc(var(--preview-x) * -1);
			rotate: if(
				media(width > 750px): 3deg; 
				media(width > 480px): 2deg; 
				else: 0deg
			);
		}
		
		&:focus { 
			outline: 6px solid #ffc32c; 
			outline-offset: 3px; 
		}
		
		&.scrollable {
			cursor: ns-resize;			
			overflow-y: auto;
			scrollbar-width: none;
		}
		
		&:focus,
		&:hover {			
			--shadow-blur: 15cqh;
			--shadow-spread: 1cqh;
			--shadow-opacity: 50%;
			--shadow-y: 3cqh;
			--translateY: 0;
			mix-blend-mode: initial;
		}
	}
}

@media (orientation: portrait) {
	.preview-container {
		max-height: 360px;
	}
}

@media (max-width: 880px) {
	.preview-container {
		max-height: 720px;
	}
}

.content-container {
	display: grid;
	gap: 1lh;
	color: light-dark( hsl(var(--primary-030)), hsl(var(--secondary-080) / 1) );
	padding: 3ex min(6%, calc(var(--spacer) * 3));
	
	hgroup {
		display: grid;
		gap: .5ex;
		margin-block: .5ex;
		
		h1 {
			color: light-dark( hsl(var(--primary-010)), hsl(var(--secondary--h) var(--secondary--s) 95% / 1) );
			font-size: clamp(1.1rem, 5cqw, 1.5rem);
			font-weight: 550;
		}
		
		p { 
			font-size: clamp(0.85rem, 5cqw, 1rem);
			font-weight: 460;
			line-height: 2.4ex;
		}
	}
}

.tag-container {
	display: flex;
	flex-wrap: wrap;
	gap: .5ch;
	list-style: none;
	
	li.tag {
		border-radius: 60px;
		background: linear-gradient(
			light-dark( hsl(var(--primary-030) / .2), hsl(var(--secondary-020) / .81) ),
			light-dark( hsl(var(--primary-040) / .05), hsl(var(--secondary-020) / .36) )
		);
		
		border-bottom: 
			1px 
			solid 
			light-dark( 
				hsl(var(--primary--h) var(--primary--s) 95% / 1),  
				hsl(var(--secondary-060) / .65) 
			)
		;	
		
		display: flex;
		align-items: center;
		text-box: trim-both ex alphabetic;
		font-size: 84%;
		font-weight: 500;
		height: 29px;
		padding-inline: 1.45ch;
		padding-bottom: .18ex;
		user-select: none;
		white-space: nowrap;
		overflow-x: hidden;
		text-overflow: ellipsis;
	}
}

/* Demo Resets */
* { font-family: inherit; margin: 0; padding: 0; }

html { 
	background: var(--bg); 
	height: 100%;
	transition: all .45s ease-out;
	
	&:has(#color-scheme input[value="dark"]:checked) {
		--color-scheme: var(--scheme-dark);
	}

	&:has(#color-scheme input[value="light"]:checked) {
		--color-scheme: var(--scheme-light);
	}
	
	&:has(#color-scheme input[value="holo"]:checked) {
		body {
			&:before {
				background: 
					radial-gradient(
						circle at var(--1-holo-pos) in hsl longer hue,
						oklch(100% 0 80 / .75),
						oklch(90% 0.1 300 / .75)
					),
					radial-gradient(
						circle at var(--2-holo-pos) in hsl longer hue,
						oklch(90% 0.1 70 / .75),
						oklch(90% 0.1 370 / .75)
					),
					radial-gradient(
						rgba(168, 169, 174), 
						rgba(165, 174, 216)
					)		
				;
				background-blend-mode: plus-lighter, plus-darker, overlay;
				animation-name: holo-anim;
				animation-duration: 3s;
				animation-iteration-count: infinite;
				animation-timing-function: ease-in-out;
				animation-direction: alternate;
				mix-blend-mode: color;
				opacity: .5;
				user-select: none;
				pointer-events: none;
				position: absolute;
				inset: 0;
				content: "";
				z-index: 10;
			}
		}
	}
}

body {
	container: body / inline-size;
	font-family: "Libre Franklin";
	padding: 0 calc(var(--spacer-x) * 4) calc(var(--spacer-y) * 4);
	overflow-x: hidden;
	position: relative;
}

@keyframes holo-anim {
	to { 
		--1-holo-pos: -50% 30%;
		--2-holo-pos: 60% 150%;
	}
}

header.settings {
	--header-border--w: 1px;
	border-radius: var(--radius-002);
	corner-shape: square square round round;
	overflow: clip;
	display: grid;
	justify-content: end;
	margin-bottom: calc(var(--spacer-y) * 3);	
	background: light-dark(
		hsl(var(--primary-070) / .5),
		hsl(var(--secondary-005) / .5)
	);
	color: light-dark(
		hsl(var(--primary-090)), 
		hsl(var(--secondary-010))
	);
	
	fieldset {
		border: 0;
		margin: 0;
		padding: 0;
	}

	label {
		cursor: pointer;
		display: flex;
		align-items: center;
		font-weight: 720;
		user-select: none;
	}
}

fieldset[id="color-scheme"] {
	display: flex;
	flex-wrap: wrap;

	label {
		--gap: 1.25ch;
		color: light-dark(
			hsl(var(--primary-030) / 0.75),
			hsl(var(--secondary-060) / 0.75)
		);			
		flex: 1;
		font-size: 1.25ex;
		margin: 0;
		padding: 3ex 1.5rem;
		text-transform: uppercase;

		input {
			color: inherit;
			appearance: none;
			border-radius: 2px;
			outline: 2px solid currentColor;		
			outline-offset: 2px;
			height: 0.5rem;
			width: 0.5rem;
			transition-property: outline, background;
			transition-duration: .15s;
			transition-timing-function: ease-out;

			&:checked {
				background: light-dark(
					hsl(var(--primary-060)),
					hsl(var(--secondary-010))
				);
				outline-color: light-dark(
					hsl(var(--primary-060) / .5),
					hsl(var(--secondary-010) / .5)
				);
			}
		}
		
		span {
			margin-left: 0.75rem;
		}

		&:hover {
			background: light-dark(
				hsl(var(--primary-090)),
				hsl(var(--secondary-010))
			);
			color: light-dark(
				hsl(var(--primary-040)),
				hsl(var(--secondary-090))
			);

			input {
				outline-color: currentColor;
				&:checked {
					background: currentColor;
				}
			}
		}

		&:has(input:checked) {
			background: light-dark(
				hsl(var(--primary-090)),
				hsl(var(--secondary-050))
			);
			color: light-dark(
				hsl(var(--primary-030)),
				hsl(var(--secondary-010))
			);
		}
		
		&[for="holo"] {
			--icon-unchecked: "✧";
			--icon-checked: "✦";
			--icon-opacity: .5;
			position: relative;
			
			input {				
				outline: 0;
				border: 0;
				width: 0;
			}

			span {
				margin-left: 1.15rem;
				&:before { 
					content: var(--icon, var(--icon-unchecked)); 
					font-size: 1.2rem;
					opacity: var(--icon-opacity);
					position: absolute;
					left: 1.5ch;
					top: .75ex;
				}					
			}

			&:has(:checked) {
				--icon: var(--icon-checked);
				--icon-opacity: 100%;
				input { background: none; }
			}
		}
	}
}

.bg {
	height: 100cqh;
	width: 100cqw;
}

.red { background: oklch(60% 50% 10deg); }
.blue { background: oklch(50% 60% 220deg); }
.green { background: oklch(70% 70% 110deg); }

@font-face {
	font-family: "Libre Franklin";
	src: url("https://assets.codepen.io/2392/LibreFranklin.ttf");
	font-style: normal;
	font-weight: 100 900;
}
  • Colors / Themes: Tweak base HSL color channels --primary--h: 25 and --secondary--h: 206 on :root. Customize preview card background blocks using .red, .blue, and .green selectors with custom oklch() values.
  • Gradients & Visual Effects: Edit the animated holographic foil overlay inside html:has(#color-scheme input[value="holo"]:checked) by altering radial-gradient stops and @keyframes holo-anim targets --1-holo-pos: -50% 30%. Adjust image card stack blending via mix-blend-mode: luminosity.
  • Sizing, Layout & Scales: Modify responsive grid column width --col-w--min: calc(max(390px, 50cqw) - var(--spacer-x)) on .project-container. Customize image stack fan-out distance by changing --x-init: 6cqw and --x-hover: -1cqw on .project.
Glassmorphism capsule card featuring realistic liquid glass refraction and chromatic aberration SVG filters.

Liquid Glass Distortion Card in CSS and SVG

A ultra-realistic liquid glass frosted capsule element combining CSS backdrop-filter with SVG displacement shaders. Employs embedded feImage displacement mapping and split-channel feDisplacementMap RGB filters to reproduce physical optical refraction and chromatic aberration over scrolling background content. Styled with layered inset lab() shadows.

Technologies:
SVG HTML CSS
Difficulty: Advanced
Browser Support (as of Jul 2026):
Chrome Chrome Edge Edge Safari Safari
Features:
Liquid Glass Effect Chromatic Aberration SVG Backdrop Filter Glassmorphism Capsule
License: MIT
Loading...
<div class="liquidglass">
  <svg class="glass-surface__filter" xmlns="http://www.w3.org/2000/svg">
    <defs>
      <filter id="glass-filter-_r_b_" color-interpolation-filters="sRGB" x="0%" y="0%" width="100%" height="100%">
       <feImage
  x="0"
  y="0"
  width="100%"
  height="100%"
  preserveAspectRatio="none"
  result="map"
  href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAApQAAAIdCAYAAACDcO0sAAAQAElEQVR4Aey9iZrcOo+k7bdnX3u23u7/Qj0OyUhCEEhRSmVVVhXOc2ACEQGQDLsy+ft8/c8//Pr167cC+G3xD//wD78t/t2/+3e/ffz7f//vf1v8h//wH35b/Mf/+B9/W/yn//Sfflv85//8n3/7+C//5b/8tviv//W//rb4b//tv/22+O///b//9vE//sf/+G3xP//n//zt4x//8R9//6OL//W//tdvi//9v//3bx//5//8n98+/u///b+/Y/y///f/fsf4p3/6p98x/vmf//l3Fv/yL//yuxf/+q//+nsm/u3f/u33Z8TM2aTp3c/wzBdh0UPV0WvV8fdEtf99U+5/Xy2333db/Z8L5f7PjXL7M2Wr/zOn3P482mp/Vv1qf5Zt9X/WldvPgV/t58Sv9nPkV/s5i6v/eYy5/dz2VvsZP7Pq86Fi/ZwsH8qH+jNQfwbqz0D+Z0APyj/e/Mx/f//WW/r97v7R59J+ihknjnQ9PsOvYjN9UfOK+uxM+Zv1RCzTGZZpZ7ler/otpIlhXK2f7UDtXw6UA+XA+zrwox+UH/3boi/q2T3PaGdnRp32UES8V4+04hRZb4Y/g8U94qyPrnWemT2PNDYn00XMazPO88p7oV4fPV3h5UA58PUd+PNfJn5V8DIPvv6fkOdu8JYPSn3BPXettTvOifWqar+eyXqzeviZ2abVLIXVd62aqZidJ62ipz/LZfqrWOz7jPrsnvIx64lYphOmyLSGi1OozkKcRcYXVg6UA6934DMedq+/1c/eYeb39Ds79JYPyu9suL7Iz97vSk+2h+YoMq6HHelHfMbdicVZ71DPnOFIo98LaRTKfQhTeEy5MIXyLMRZZHxh5cAXc+DtjjvzmPCat7tAHehDHPB/Biz/kI0/YJMv96DUl+IrfXn1fJ39yh7qsdCMmTC9rTM9XqM+X8d8xGfcnVic9Y710ZnkZ9SMsJ42w22OOIXqinKgHHjeAXsExPX5yd9zQvTpO9bP/s5FT56d91n9X+5B+RlGnf1CPqs/eyfNn4mzc02/zD74P1iSxvRxzbgMi32qM13Evlqd3Ut3UIizUK2wWqtqhXIfwhQes1y4wupay4Fy4JoD8Yte9bVJ79Gl8390vMfNX3uKWU9nTxHnzfZ9tq4elE/8Djzzpf1M7xNHPmydOddIk3EZpoNEPNYzmtjzjvXRmbJ7jrA4T1qFcIXyinKgHLjugH2hX5/w2k47n62z62tPVdOPHOj9Pp3pO9J+Jl8Pyhe5P/PFPqN50fF2Y3UWxY4IwEiTcRmmkRGP9Ywm61GfReQ/u9a5sjNcxTRPoX6F8opyoBy45oD/sr824Z4uf45efs9OX2tKz4vPwO92Lt5hNN9rR7rP4H78g/Kzv4g/e3/9oZs5gzQK6WMIV2R4xFRnWuE+oibWXmv5SBO5c/WvX3foj2boHlHTwwzP9OIqyoFyYN4BfUnPq+9Ras8s7pl+75TsnB+N3Xuj56ZdufuZHf38UZ/pRpqP5A4flPWFtf529Hzo4eoaceItZnWmv2vVvoqjeSNNjzuDR+1RrfMeaTzv87O9r9JnZ5rB7DxRK7yiHCgHzjugL+XzXec7tI+P8xOe7/D7n8mf3/kDJ7zpVj2/j47r+3raGU2v90788EH57Gav/uJ7xfxXzDzy8SP31F6KozOJH+l63Bk8amOtM8SImqPa9x9pP5rX2eKeIyzTSl9RDpQD5x3QF/H5rrkOzfYx13Vd5ffq5denV+erHIi/V6N9vLanM02PfyX+8gfl7OE/6ovyo/axe5/Z74zW5p9ZNV8x2zPS9rgzeNTGWueM2DP1M73xLJqlEK5QrlCuUK5QbpHVVzGb+QlrbVkOfAsH9MV790U00+Lu2TbP5sfV+M9a43mqbv8v8pz5PYm+9Xq9LtMYn3Gvwt7mQTm6YPzSPaM90zuaK643q4er52xoluJs30iveYqRxnPSKjzm8x53Bo/aWGu/iD1TP9vr+30+e86ZnqjJZgurKAfKgecc0JftcxO23Zqn2KLPV5oZ4/mpxxPinjP18dSfq+j5N+OI7+3pTbPnf/0acb9u/id9UGZfbDfv+1bjXn3fK/PVo7hqlHotzsxQz0jf48/gURtr7R+xZ+rP6u3d4+g81hd1wivKgXLgfRy4+8va5tn6ipva7NH6in1r5t6B7Pdgr2qI1ze0ZcY3pGUjrqmey9IH5XMj9/8Xsc/O+4z+s1/mR/ojvndH9V2J3rwebnv0eOHSaI1xBo/aWGt2xJ6tNdPizKwzWs0/q+/1xDnSVbynA3Wqr+eAvlifPbVmKO6ao1mKZ+dZv2b1wjQfvfbO853xKx57P0b9I90MN5p9lXs8KD/jS+wz9rxqlPU9c+Znem3/V6xH5xKvyPY+g0dtrDU/YnfWZ2ad0V4999EemltRDpQD7+WAvqyfOZH6LZ6ZY702y6/GvWL1+5zJX3GWd5858mfm7LG/12O6jO9xwjP9M9jjQfnMEPXGL0dhz0c+4dm9nu3PTzX3N7Ov2rt3phGusyiOND2+15vhGRbnRs2d9ZlZZ7S6wx36OENzK8qBcuB9HNAXsOLqidSruNpvfZrhw/A7Vj+3l9+xT8349fjfNnqffx38c6Q1PhuTcRmW9c5itz0oZzec1Z35gj3SHvE6U6bJsJ5WuKLXI85iRmPaV6zaX3E0e6TpcRk+g0XNnfWZWWe1Xq9cYb4qV1it9aiWpqIc+JIOfOND64v36vXUq3i2XzMUV+f4Ps3Jwms+Ms/O8tWxK/7FO49mmDbTHHGxZ6SP2lH91IMyfjmONjLuSo/1zq4fscfsWXo6nVHR41+Baz/F0WxpFD1dxglTxJ4ZLGrurM/MepVWnmi2QrlFrA2vtRwoB76+A898SVuv1med0IwYz87M+uMeZ+ps3lfHRvefvZuf0esZaYyLvWfx2N+rn3pQ9oa+Ar/7y3d2Xk/Xw3X3ESfexwmtbzuVaw/FTNNIJ04R52SYNBkesVfWZ2aPtOIUupPC51frOENzKsqBcuD9HNCX79lTXenRHupTKL8S6o1xZU7siTOzOvZU3Xfgin++pzfZNJG/gscZM/XpB+WVL8LZnlndzMWuaO7c/8wsaRVXztzr0TyLnsbjR1rxXm/5LC6dwvq0vrI+M3ukHXF33EEzKsqBcuA9HdAX8ZmTSa/49etM16/H/57u14V/tJ/FhfZNi82J60b0CUU8zzvXV+2JdxrN8dpMZ3zkzuA9bZzp66kHZfxS9QM+Kz97prN6f69ebw+33iPedLZK78PwmdX3KZ/pMc2RvsdnuDCFzdYa6wyLmmfqM70j7YiLd5BWIVyhXKFcoVyhvKIcKAe+nwP6Aj57K/UorvZd6bW91BvDuDvXuMeV+s7zvHrWzP1mzhDn9HpMl/E9boTHOdJGrFdPPSh7zSN89stzpBtxce+ojXXUq840GSZtL470R3xvrnD1zob0Z8Nm9/pGvLjYdxWLfTO139vrfS7NqL7KxbmjOVGruqIcKAfe34EzX6RntLq59ArlsyG9xWyP6azPr8Y9s/p5vfyZ+d+1N/Pq6K6+J9OOeONiX4bPYnGW6u6DMn5JSvyT4xk/nul9hec6j2I0e8Rn3FUs9j1Tn+kdaY84z/tcfh7V0lSUA+XA93FAX8BnbnNFf7ZH51GPheqrYTOydXLmh8iy830UdscF41lHM7020xkfuTN4ps0wv8fmQRm/DL1wJp/tn9XFPa/22ZysfxazGdmazYg6aRQR/8ha+ytGe4pXZBrhishdxWLfM/WZ3pH2KidPRr3iK8qBcuDnOnD0ZRydOatXv/VoVX021Bfj7IxZfdzn2Xp231foZs9+Zm8/c9RnukzT40Z4nCPtDCbN5kEp4GzEL9Gz/VF/Zl7UHtVxr7N1nO/7R1zUzWp939Vce1kczZCup+lxGT6DRc0z9ZnekfYZzvcqV/S8vB2vgeVAOXCrA9mXaLbB3TrtoZkK5TMhrcWM3musz1bPXc1t1tF6df5X7ut5cnQn39fTmibje1yGP4NdelBe+bIc9VxlMuOOsGyvWUyzM61wxYgT70NahcfuzDVbMTNTOkVP2+MyfAaLmmfqM70jreeUK8wPnwvztc8jp7qiHCgHvqcD+uKdudkZ3axW+0qrUD4b0vuY7Ys6PyPmUVv11oGsOuOh1x7Nirz1zuCZtofZvOkHZfzitAGj9UrPaJ64szPP6rXHUYxmilMczTBeWgvDrqw2w9aZGUfaHj/C477SeuzO+swsr1WusHP1cvGRi7U0Fp4zrNZyoBz4uQ7oC3jm9rM6zZJWoXwmpLWY0UeN9fo1au6q/R7fLT/jUbx7r9frMo3xkRvhmfYIs3nDB+WVL8jZnpFuxMWLRW2so151ppnF1K/I9MItjnjT+VU9V8PPOcptj5FOmozPcGEKr1etiNhd9ZnZXutzncXXPj/DRa3qis9yoPYtB8YOxJ/zsTpn9QWaMw2d0Uh9Rjertbln9OpRqMeHsGfDzxvlz+7zzv29e8+c2ff29KbJ+B6X4c9guwfllR+22Z5ZXTTkap/NeaZ/1DvitLd4hfJ3CJ1FMTqLeEWmyfCrWOw7U4+04hR2/l4u/i7Oz9HcinKgHCgHZhzQl/eRThrFkU68dBaqZ8L0ts70ZBrrz9ZMX9jqwFm/vH6dsP11xBu37fiV/v9YP9P2sF9//9k9KP/i3cWIu79Ez8yL2qPazuzX2CMuw0b4ESde0Zsr7iNC+ytGe4lXZBrhishdxWLfmXqk/Wwu+lN1OVAO/EwH9MV7dPO7NNpHsxTKZ0Jaixl91FivX6Pm7trv9ZXyKz74+436j3TGxxkjPNMeYTbv1IMyfmHHTbJ61HOVy/Y5wkZ7+d6eroerd8SJV0hjofrVYXtpPdprpOlxGR4x1Qq//zP1qPfVnOYr7C4+N6zWcuDNHKjjfIID+nI92vZOzcwsO4+0CqtnV/X4mO0b6fy8mXw065250d1mzh37ez2my/gel+HPYIcPyitfnFd6MhM8Fmeerf0sy+OMq7j6erPExZDWInJXa5tn68yckbbHjXC/p3S+Vh6xM/VIewenGQqdU9HLI6e6ohwoB76WA/7n++zJ9YV7tsfrj/rFK3xPls9o1CedheqZML2tMz2ZxvqzNdP/NOyKL74n88v4M1zWcwazvdIH5ZUfttmekW7E2YGfWbP5GTbaY6MPQnGKAA9L6WMMG/6QUa/6Dzz1r7QWvQbxGZfhwhReH2txETtTj7R3cFdn6F4V5UA5UA7MOqAv6ZH2iFevNArlo5BGMdJ4TloLj8/k1hfXmd67NHHvz66v3iueezTHtJnGOK2RF6bI8CuYZinSB2Uc2KvjF/EV3WhG5J6tdb444wwmrSKbIdxCvMLqs6t6R3F2nvQ2T3kvepoRHmdJ6zHVioj1amkVxvtcmK99PuKkU0ij6OUjTj0KaSrKgXLg6znwESfWl+rVfWZ6ZzUzOp1TOgvVs2E9ts72jQV1sMzb/X4kEbhsaty+Sp6ftIoMl2FR+2ozvpaes/53Hqz0+uqPOvrYZVXhff8erz37WkXvzawNrA/kGYO6xB3P6fOp9KHz2/e775J/v/HxQfv5V1w0/fQMzD4AZbfa5R/pHNN67p0///S5eS97X/8P9F8K0Nf7f9X7OnpE+W1es6n7XvBnv9uIUbv5unvV6tHof66Y9Gz+i+Z8X7e9Snt7X4vWglFAclAPlQDlQDpQDJxzIPuhGv4Z+/Z7F9Nn8EXuM6OnV4qM+GfX6vP9S9PnV/bN3+L3+p6fG3xH27shZf9U3q6vOeh9yX9gZ9g/36kEpMlLK9+l8PVDgOVAOfFYHcujD+RpeX0f39XfMvGeWf4vXj8T8rM8M8zPhXn/UqE+8RuzV0rQ4K9+KnfGmv8s3sD6erx6UPWcKLwfKgXKgHCgHfsEBe7D1/kZ8b89or9F+Prc+/0zv3R9/t79/y0u8YuxL1GfWb+mP6v38WnzE+bHOfbXfV96yD9j+reT1oPyWb0BtWQ6UA+VAOVAPnOfAnzxcPHN7vPfO9PZ6u/uT9p7uK0f/r97nOf38p/uY4fHReS2vXhNreYn/JeXz9+Y8KOfcrFmHDoD2GPCgdW1gbeDnbuArH6y9Dzo3GjOayEevWToL1Ufz9zKszuh9RuxrRms+Z+b3+BlMf6SReZ+NuVdLezXywt7XU+WteT0orzhYPeVAOVAOlAPlwK84YI+Xz8+X6OnzE76F/R2YmUf7qFec7U3zI/r0MGPWv7Onm9FmHsh7g+0fI9vVvE4PrI7v0X3y3Z56UFaX039KB4DNZ8Eez6bA7p7A42cAew2Mf9K0WfA+mNNZ6P7Ies3Y38O2D8YwjX9Fhvf0MGL66FmY93oRP/N7es5jI06YIuPX+vT3qAclUPlf08C3bODZhzvE16v9r/yB/WfG6rE76vE9D6v/AunO8D59Gf3p/jP9M/NRPfLpYQonPeIpnWJEex7GvT9zXvFh5yN8b8z79Xf+7O9gLff3U/WgvOJi9ZQD5UA5UA6UA5/kgD1eOj0i/iAn/C0unR6vMzzb90Q+es+Xj849Yp8T/4u9H0S7bZ6F+2dY9K6ofjOfpXUWe49e3vIqXJzpz2rU2uYpfXUez9XbeYdYv63MfyoLXRv4eRswenSdwcIUR73W9+rpRzyPzWjpWlzG3f3Z+97pGekjToxqTsujWv6pD8ofv38VvzbwpQ0AD//S89n/Kef+q3bA9p9vV04n3nLY6mGPe2M8Y7I0Pka9bY491ss94R6vUfN4f0XEz+jP3Bv2pT69Z5v5Ue/+rR6U3uGqf8YGoP9ogcYDKrcBLL9pAMtz8InRymcY9X91K670+OjxGvXe6SreKzO9vGf56H3O0GfeAcaPsK3VM9rsc8X6YfWgvOpi6csB4PFoAmP8KIFV+8D0Y0Z8R7W+P7Mv4Z6re3tGzXnN9On94/F+uD83M3+H5mZzzT3D488C9v4r3+2pB+XgVnXlQDlQDpQD5cC/cgPY9wZg/bbyb93An7Yg+H9vA1c8f+R8G/PThB/+H/fVf0m8v+C/xH6rV6/VnZ/+L9R5rZ26t7eU+Zf0mWe1ZqbeW0X+XgP9rN9O97X8vD3++7hUD0r9E+XlQDlQDpQD5cC9DhRP7T/p9Gf7L7EvV8vbeE+/j6iN++Xv6m9xrZfH3pXby+LkfdbL9Fev7mU+37PzF1gUf9bLfbZ76/P/mHclV6vP1s98Zq/nfbZnvZ0N+F072D0or9pefeVAOVAOfFcHeoxE9H9O9P/R1f+TPhv1R67K8YyO9mP9bO9oZp7ofZ+969X8M72z82fna8/V971T/V4vT4X/pZz/qI66K4t6/v4D+v3XyEdfV/ovOf/+H/8bC/+r6nOfE79H/T+K99rC+hWw2B/5T2FffgKofYDN94bN8F4/Z8byR/K1ZzUrf/O/+rXf67/+D/wQ6pA3/uL8jC3O7/tX/f7f/gX9vI/pZ/sD087sT8U+s/oYf/+ZtWfrb8D/3D09u7N+6pWfP6XzX/F6zL6m/6D0p8vKgXKgHCgHyoFy4L9gAxH6XfO5b4b9bK/fPZ//r/Y39f/bAetgO99f5X9D+XUerz7D3f7L/D0925m/+L/gP60OfvH9V/v9F73O/L+iM/+7+q+xL74v/k/R/L0BWB/vWf/K++L/g/+E+n/+93/4H/D/b/+E+X+g+R9Y/h8AAAAASUVORK5CYII="
/>

        <feDisplacementMap in="SourceGraphic" in2="map" id="redchannel" result="dispRed" scale="-20" xChannelSelector="R" yChannelSelector="G"></feDisplacementMap>
        <feColorMatrix in="dispRed" type="matrix" values="1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0" result="red"></feColorMatrix>
        <feDisplacementMap in="SourceGraphic" in2="map" id="greenchannel" result="dispGreen" scale="-24" xChannelSelector="R" yChannelSelector="G"></feDisplacementMap>
        <feColorMatrix in="dispGreen" type="matrix" values="0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0" result="green"></feColorMatrix>
        <feDisplacementMap in="SourceGraphic" in2="map" id="bluechannel" result="dispBlue" scale="-28" xChannelSelector="R" yChannelSelector="G"></feDisplacementMap>
        <feColorMatrix in="dispBlue" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0" result="blue"></feColorMatrix>
        <feBlend in="red" in2="green" mode="screen" result="rg"></feBlend>
        <feBlend in="rg" in2="blue" mode="screen" result="output"></feBlend>
        <feGaussianBlur in="output" stdDeviation="3"></feGaussianBlur>
      </filter>
    </defs>
  </svg>
</div>
<h1>This is a HTML text</h1>
:root,:host{ 
    --glass-frost: 0.1;
    --glass-saturation: 1.2;
    --filter-id: url(#glass-filter-_r_b_);
}

body {
    margin: 0;
    padding: 0;
    min-height: 200vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    font-family: sans-serif;
    background: url(https://images.unsplash.com/photo-1551384963-cccb0b7ed94b?q=80&w=3247&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D) center / cover;
  overflow:auto;
  color:white;
}

.liquidglass{
    position: fixed;
    left:50%;
    top:50%;
    transform:translate(-50%);
    z-index: 1;
    width: 340px;
    background: hsl(0 0% 0% / var(--glass-frost, 0));
    -webkit-backdrop-filter: var(--filter-id,url(#glass-filter))saturate(var(--glass-saturation,1));
    backdrop-filter: var(--filter-id, url(#glass-filter)) saturate(var(--glass-saturation, 1));
    box-shadow: inset 0 0 2px 1px #ffffff59, inset 0 0 10px 4px #ffffff26, inset 0 4px 16px #11111a0d, inset 0 8px 24px #11111a0d, inset 0 6px 56px #11111a0d;
    box-shadow: inset 0 0 2px 1px lab(100% 0 0 / .35), inset 0 0 10px 4px lab(100% 0 0 / .15), inset 0 4px 16px lab(5.32203% 1.61424 -5.88284 / .0509804), inset 0 8px 24px lab(5.32203% 1.61424 -5.88284 / .0509804), inset 0 6px 56px lab(5.32203% 1.61424 -5.88284 / .0509804);
    border-radius: 9999px;
    height: 140px;
    pointer-events: none;
}
.glass-surface__filter{
    pointer-events: none;
    opacity: 0;
    z-index: -1;
    width: 100%;
    height: 100%;
    position: absolute;
    inset: 0;
}
  • Colors / Themes: Tweak glass opacity via --glass-frost: 0.1 and saturation via --glass-saturation: 1.2 in :root. Adjust capsule inner reflections on .liquidglass via box-shadow inset lab() color stops.
  • Gradients & Visual Effects: Modify chromatic dispersion scales in SVG by changing scale="-20" (red), scale="-24" (green), and scale="-28" (blue) on feDisplacementMap filters. Adjust edge softening using stdDeviation="3" on feGaussianBlur.
  • Sizing, Layout & Scales: Resize glass capsule container via width: 340px and height: 140px on .liquidglass. Alter corner curvature using border-radius: 9999px to convert from pill capsule to rounded box.
Pure CSS Cafe Wall optical illusion with parallel lines distorted by conic gradients, revealed on hover.

Optical Illusion Pattern in Pure CSS

An optical illusion rendered entirely in CSS using zero HTML elements. Employs pseudo-elements with skewed repeating-linear-gradient and conic-gradient layers to create the perception of non-parallel lines. Hovering over the viewport transitions the @property --color variable to transparent, instantly disabling the visual distortion and revealing parallel horizontal lines.

Technologies:
CSS
Difficulty: Intermediate
Browser Support (as of Jul 2026):
Chrome Chrome Edge Edge Safari Safari Firefox Firefox
Features:
Optical Illusion Pure CSS Animated Property Hover Reveal
License: MIT
Loading...
/*
optical effect
The black lines are parallel, but they look like they are not. Mouse over to reveal
*/
@property --color {
  syntax: '<color>';
  initial-value: #f00;
  inherits: true;
}

body {
  --color: #f55;
  margin: 0;
  font-size: 1vmax;
  height: 100vh;
  overflow: hidden;
  transition: --color 1s;
}

body:hover {
  --color: #0000;
}

body::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  width: 300em;
  height: 300em;
  background:
    repeating-linear-gradient(#0000 0 18em, #000 0 20em),
    conic-gradient(at 1.25em 10em, #0000 75%, var(--color) 0) 0 -6em / 2.5em 20em;
  transform: skewY(-45deg);
}

body::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  translate: -50% -50%;
  width: 300em;
  height: 300em;
  background:
    repeating-linear-gradient(#0000 0 18em, #000 0 20em),
    conic-gradient(at 1.25em 10em, #0000 75%, var(--color) 0) 0 -6em / 2.5em 20em;
  transform: rotate(90deg) skewY(45deg) translate(0.25em, 12em);
}
  • Colors / Themes: Change distortion tile colors by modifying --color: #f55 on body and initial-value #f00 inside @property --color. Update hover transparency target --color: #0000 to alter reveal contrast.
  • Gradients & Visual Effects: Adjust line frequency by tweaking repeating-linear-gradient(#0000 0 18em, #000 0 20em) offsets inside body::before and body::after. Modify pattern angles via conic-gradient(at 1.25em 10em, ...) tile dimensions / 2.5em 20em.
  • Sizing, Layout & Scales: Rescale pattern density by altering font-size: 1vmax on body. Tweak illusion distortion strength by adjusting skew angles transform: skewY(-45deg) and skewY(45deg).
Scroll-driven text assembly animation using CSS sibling-index, math functions, and scroll-timeline.

Scroll Driven Text Unscramble in CSS

A scroll-driven text assembly effect built without JavaScript. Uses CSS animation-timeline: scroll() paired with @property --page-offset to transition letter positions on scroll. Random dispersion is computed natively using sibling-index(), sin(), and mod(). Fully accessible structure with aria-label readable words and aria-hidden individual letter spans.

Technologies:
HTML CSS
Difficulty: Advanced
Browser Support (as of Jul 2026):
Chrome Chrome Edge Edge Safari Safari
Features:
Scroll Driven Pure CSS Math 3D Text Assembly Accessibility Ready
Code by: Chris Bolson Chris Bolson
License: MIT
Loading...
<section class="scramble">
  <p aria-label="Meaning">
    <span aria-hidden="true">M</span>
    <span aria-hidden="true">e</span>
    <span aria-hidden="true">a</span>
    <span aria-hidden="true">n</span>
    <span aria-hidden="true">i</span>
    <span aria-hidden="true">n</span>
    <span aria-hidden="true">g</span>
  </p>

  <p aria-label="emerges,">
    <span aria-hidden="true">e</span>
    <span aria-hidden="true">m</span>
    <span aria-hidden="true">e</span>
    <span aria-hidden="true">r</span>
    <span aria-hidden="true">g</span>
    <span aria-hidden="true">e</span>
    <span aria-hidden="true">s</span>
    <span aria-hidden="true">,</span>
  </p>

  <p aria-label="one">
    <span aria-hidden="true">o</span>
    <span aria-hidden="true">n</span>
    <span aria-hidden="true">o</span>
  </p>

  <p aria-label="letter">
    <span aria-hidden="true">l</span>
    <span aria-hidden="true">e</span>
    <span aria-hidden="true">t</span>
    <span aria-hidden="true">t</span>
    <span aria-hidden="true">e</span>
    <span aria-hidden="true">r</span>
  </p>

  <p aria-label="at">
    <span aria-hidden="true">a</span>
    <span aria-hidden="true">t</span>
  </p>

  <p aria-label="a">
    <span aria-hidden="true">a</span>
  </p>

  <p aria-label="time.">
    <span aria-hidden="true">t</span>
    <span aria-hidden="true">i</span>
    <span aria-hidden="true">m</span>
    <span aria-hidden="true">e</span>
    <span aria-hidden="true">.</span>
  </p>
</section>

<div class="icon">
		<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 40" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mouse">
			<path stroke="none" d="M0 0h24v24H0z" fill="none" />
			<path d="M6 3m0 4a4 4 0 0 1 4 -4h4a4 4 0 0 1 4 4v10a4 4 0 0 1 -4 4h-4a4 4 0 0 1 -4 -4z" />
			<path d="M12 7l0 4" />
			<path d="M8 26l4 4l4 -4">
				<animateTransform attributeType="XML" attributeName="transform" type="translate" values="0 0; 0 4; 0 0" dur="1s" repeatCount="indefinite" />
			</path>
		</svg>
	</div>

<footer>
  <a href="https://codepen.io/cbolson/pen/NPrrpzJ" target="_blank">See Circular version</a>
</footer>
@import url(https://fonts.bunny.net/css?family=noto-sans-mono:400);
@layer base, mouse, demo;

@layer demo{
  @property --page-offset {
    syntax: "<number>";
    inherits: true;
    initial-value: 75;
  }
  :root {
    --cta: rgb(94, 165, 0);
    --mouse-color: var(--cta);
		--first-word-color: var(--cta);
    --letter-spacing: 1ch;
    --f-size: clamp(1rem, 2.5vw + .045rem,  1.5rem);
  }

  body {
    height: 800svh;
    font-family: 'Noto Sans Mono', monospace;    
  }
  
  .scramble {
    --count: sibling-count();
    position: fixed;
    left: 50%;
    top: 50%;
    translate: -50% -50%;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 1ch;
		
		animation: --page-scroll 1s linear;
    animation-timeline: scroll(root block);
    animation-timing-function: steps(100);
  }
  
  .scramble > * {
    perspective: 300px;
    transform-style: preserve-3d;
    margin: 0;
    display: flex;
    align-items: center;
    gap: 0;
  }

  .scramble > *:nth-child(2){
    color: var(--first-word-color);
  }

  .scramble > * > span {
    --i: sibling-index();
    --dir: calc(1 - 2 * mod(var(--i), 2));
    --rand: calc(mod((var(--i) * 7 + 20), 4) - 4);
    --wave: sin(calc(var(--i) * 17deg));
    
    /* y position */
    --start-y: calc(
      (var(--rand) + var(--wave) )
      * var(--dir)
      * var(--page-offset)
      * 1px
    );
    
    /* depth */
    --z: calc(abs(var(--rand) * 2 + var(--wave)));
    --depth-factor: calc(var(--page-offset) * 0.02);
    --start-z: calc(var(--z) * var(--depth-factor) * 13px);
  
    transform:
    translate3d(
      0,
      var(--start-y),
      var(--start-z)
    );
    
    display: inline-block;
    position: relative;
    font-size: var(--f-size);
  }
	
	@keyframes --page-scroll {
    to {
      --page-offset: 0;
    }
  }
}

/* the mouse icon is just to indicate scroll, it is not relevant to the demo */
@layer mouse{
  .mouse {
    pointer-events: none;
		position: fixed;
		bottom:10px;
		left: 50%;
    translate: -50% 0;
		display: block;
		width: 50px;
		height: 50px;
		opacity: 1;
    color:var(--mouse-color);
    display: none;
		animation-name: mouse;
		animation-duration: 1s;
		animation-timing-function: linear;
		animation-fill-mode: forwards;
		animation-timeline: scroll(root block);
	}
  @supports  (animation-timeline: scroll()) {
    .mouse {
      display: block;
    }
  }
	@keyframes mouse {
		95% {
			opacity: 1;
		}
		100% {
			opacity: 0;
		}
	}
}

 /* general styling not relevant for this demo */
@layer base {
	* {
		box-sizing: border-box;
	}
	:root {
		color-scheme: light dark;
		--bg-dark: rgb(12, 10, 9);
		--bg-light: rgb(248, 244, 238);
		--txt-light: rgb(10, 10, 10);
		--txt-dark: rgb(245, 245, 245);
		--line-light: rgba(0 0 0 / .25);
		--line-dark: rgba(255 255 255 / .25);
    
    --clr-bg: light-dark(var(--bg-light), var(--bg-dark));
    --clr-txt: light-dark(var(--txt-light), var(--txt-dark));
    --clr-lines: light-dark(var(--line-light), var(--line-dark));
	}
 
	body {
		background-color: var(--clr-bg);
		color: var(--clr-txt);
		min-height: 100svh;
		margin: 0;
		padding: 1rem;
		font-family: system-ui, sans-serif;
		font-size: 1rem;
		line-height: 1.5;
    display: grid;
    place-items: center;
    grid-template-rows: 1fr auto;
	}
	@supports not (animation-timeline: scroll()) {
	 body::before {
			content:"Sorry, your browser doesn't support animation-timeline";
			position: fixed;
			top: 2rem;
			left: 50%;
			translate: -50% 0;
			font-size: 0.8rem;
		}
	}
  footer a {
    font-size: .8rem;
    color: var(--cta);
    z-index: 2;
    text-decoration: none;
    display: block;
    transition: transform 150ms ease-in-out;
  }
  footer a:hover {
    transform: scale(1.12);
  }
}
  • Colors / Themes: Modify key accent variable --cta: rgb(94, 165, 0) in :root. Tweak target element highlight on .scramble > *:nth-child(2) using --first-word-color: var(--cta).
  • Gradients & Visual Effects: Adjust 3D depth strength inside .scramble > * > span by changing --start-z: calc(var(--z) * var(--depth-factor) * 13px). Modify scattering amplitude using initial-value: 75 on @property --page-offset.
  • Sizing, Layout & Scales: Change typography responsiveness via --f-size: clamp(1rem, 2.5vw + .045rem, 1.5rem). Adjust scroll viewport height on body via height: 800svh to slow down or speed up scroll duration.
Advanced interactive segment control with CSS Anchor-based sliding pill highlights and SVG knockout mask filters.

Advanced Sliding Radio Pill Selector with CSS Anchor

This highly advanced segment controller showcases state-of-the-art interactive transitions. It demonstrates two distinct animations: a directional elastic hop and an SVG-masked CSS Anchor Positioning slide. When anchoring is active, a custom feColorMatrix filter punches out the text, leaving an inverted, color-shifting cutout inside the sliding pill without duplicating HTML nodes. (Requires: gsap.js, gsap-draggable.js, tweakpane.js)

Technologies:
SVG HTML CSS JavaScript
Difficulty: Advanced
Browser Support (as of Jul 2026):
Chrome Chrome Edge Edge Firefox Firefox Safari Safari
Features:
CSS Anchor Positioning Sliding Pill Animations SVG Knockout Masking Tweakpane Control
Code by: Jhey Jhey
License: MIT
Loading...
<svg class="sr-only">
      <filter id="knockout-black" colorInterpolationFilters="sRGB">
        <feColorMatrix
          type="matrix"
          values="1 0 0 0 0
                  0 1 0 0 0
                  0 0 1 0 0
                  -1 -1 -1 0 1"
        />
        <feComposite in="SourceGraphic" operator="out" />
      </filter>
      <filter id="knockout-white" colorInterpolationFilters="sRGB">
        <feColorMatrix
          type="matrix"
          values="1 0 0 0 0
                  0 1 0 0 0
                  0 0 1 0 0
                  1 1 1 0 0"
        />
        <feComposite in="SourceGraphic" operator="out" />
      </filter>
    </svg>
    <div class="resize-container">
    </div>
    <svg class="svg-mask" xmlns="http://www.w3.org/2000/svg"></svg>
    <a
      aria-label="Follow Jhey"
      class="bear-link"
      href="https://twitter.com/intent/follow?screen_name=jh3yy"
      target="_blank"
      rel="noreferrer noopener"
    >
      <svg
        class="w-9"
        viewBox="0 0 969 955"
        fill="none"
        xmlns="http://www.w3.org/2000/svg"
      >
        <circle
          cx="161.191"
          cy="320.191"
          r="133.191"
          stroke="currentColor"
          stroke-width="20"
        ></circle>
        <circle
          cx="806.809"
          cy="320.191"
          r="133.191"
          stroke="currentColor"
          stroke-width="20"
        ></circle>
        <circle
          cx="695.019"
          cy="587.733"
          r="31.4016"
          fill="currentColor"
        ></circle>
        <circle
          cx="272.981"
          cy="587.733"
          r="31.4016"
          fill="currentColor"
        ></circle>
        <path
          d="M564.388 712.083C564.388 743.994 526.035 779.911 483.372 779.911C440.709 779.911 402.356 743.994 402.356 712.083C402.356 680.173 440.709 664.353 483.372 664.353C526.035 664.353 564.388 680.173 564.388 712.083Z"
          fill="currentColor"
        ></path>
        <rect
          x="310.42"
          y="448.31"
          width="343.468"
          height="51.4986"
          fill="#FF1E1E"
        ></rect>
        <path
          fill-rule="evenodd"
          clip-rule="evenodd"
          d="M745.643 288.24C815.368 344.185 854.539 432.623 854.539 511.741H614.938V454.652C614.938 433.113 597.477 415.652 575.938 415.652H388.37C366.831 415.652 349.37 433.113 349.37 454.652V511.741L110.949 511.741C110.949 432.623 150.12 344.185 219.845 288.24C289.57 232.295 384.138 200.865 482.744 200.865C581.35 200.865 675.918 232.295 745.643 288.24Z"
          fill="currentColor"
        ></path>
      </svg>
    </a>
@import url('https://unpkg.com/normalize.css') layer(normalize);
@import url('https://fonts.googleapis.com/css2?family=Gloria+Hallelujah&display=swap');

@layer normalize, base, prototype, anchor, hop, knockout;

@layer knockout {
  .svg-mask {
    outline-offset: -2px;
    position: absolute;
    inset: 0;
    height: 100%;
    width: 100%;
    pointer-events: none;
    z-index: 2;
    translate: 0 100%;
  }

  [data-knockout='mask'][data-style='anchor'] fieldset:first-of-type > div {
    -webkit-mask: var(--svg-mask, initial);
            mask: var(--svg-mask, initial);
  }

  [data-knockout='none'] .mask-layer,
  [data-knockout='mask'] .mask-layer {
    display: none;
  }
  [data-knockout='filter'][data-style='anchor'] {
    .mask-layer {
      filter: url(#knockout-black);
    }
    @media (prefers-color-scheme: dark) {
      .mask-layer {
        filter: url(#knockout-white);
      }
    }
  }
}

@layer hop {
  [data-match='true'][data-style='hop'] {
    label::before {
      inset: unset;
      top: 0;
      left: 0;
      bottom: 0;
      width: max(var(--pill-width-current, 100%), var(--pill-width-previous, 100%));
    }
    label:has(+ :not(:checked) ~ :checked)::before,
    label:has(+ [data-current-checked='true']):has(~ [data-previous-checked='true'])::before {
      right: 0;
      left: unset;
    }
    label:has(+ [data-previous-checked='true'])
      ~ label:has(+ [data-current-checked='true'])::before,
    label:has(+ [data-current-checked='true']) ~ label::before {
      left: 0;
      right: unset;
    }
  }
  [data-style='hop'] {
    label {
      position: relative;
      background: #0000;
      overflow: hidden;
    }
    label::before {
      content: '';
      position: absolute;
      inset: 0;
      border-radius: 100px;
      z-index: -1;
      width: 100%;
    }

    label:hover {
      background: light-dark(
        color-mix(in srgb, canvasText, #0000 95%),
        color-mix(in srgb, canvasText, #fff0 75%)
      );
    }

    /* the labels that precede a checked option */
    label:has(+ :not(:checked) ~ :checked)::before {
      transform: translateX(100%);
    }

    /* the last checked that comes after the current checked */
    label:has(+ [data-current-checked='true']) ~ label::before {
      transform: translateX(-100%);
    }

    
    label:has(+ [data-previous-checked='true'])::before,
    label:has(+ [data-current-checked='true'])::before {
      background: light-dark(#000, #fff);
      transition: transform calc(var(--duration, 0.2) * 1s) ease-out;
    }
  }
  [data-style='hop'][data-wrap='true'] {
    fieldset:first-of-type:has(:nth-of-type(3)) {
      label:has(+ [data-current-checked='true']):first-of-type ~ label:last-of-type::before {
        transform: translateX(100%);
      }
      label:not(:has(+ [data-current-checked='true'])):first-of-type:has(
          ~ input:last-of-type:checked
        )::before {
        transform: translateX(-100%);
      }
    }
  }
}

@layer anchor {
  @supports (not (anchor-name: --active)) {
    .fields {
      position: relative;
    }
    .pill {
      top: calc(var(--top, 0) * 1px);
      left: calc(var(--left, 0) * 1px);
      height: calc(var(--height, 0) * 1px);
      width: calc(var(--width, 0) * 1px);
    }
    .fields[data-initiated='true'] .pill {
      transition-property: left, top, width;
      transition-duration: calc(var(--duration, 0.2) * 1s);
      transition-timing-function: ease-out;
    }
  }
  
  @supports (anchor-name: --active) {
    .pill {
      top: anchor(top);
      left: anchor(left);
      width: anchor-size(width);
      height: anchor-size(height);
      position-anchor: --active;
      transition-property: left, top, width;
      transition-duration: calc(var(--duration, 0.2) * 1s);
      transition-timing-function: ease-out;
    }
    label:has(+ :checked) {
      anchor-name: --active;
    }
  }
  [data-style='anchor'] {
    .pill {
      display: inline-block;
      background: light-dark(#000, #fff);
      position: absolute;
      border-radius: 100px;
    }
    fieldset {
      &:first-of-type {
        position: relative;
      }

      &:last-of-type {
        position: absolute;
        top: 0;
        left: 0;
        pointer-events: none;
        z-index: 2;
        /* makes room for the legend */
        -webkit-clip-path: inset(1lh 0 0 0);
                clip-path: inset(1lh 0 0 0);
      }
    }
    label:has(+ :checked) {
      background: #0000;
    }
    .mask-layer {
      display: inline-flex;
      background: light-dark(#fff, #000);

      legend {
        opacity: 0;
      }

      label {
        color: light-dark(#000, #fff);
        background: light-dark(#000, #fff);
        border: 1px solid light-dark(#000, #fff);
      }
    }
  }
}

@layer prototype {
  .resize-container {
    resize: both;
    overflow: auto;
    position: relative;
    width: 260px;
    height: 195px;
    font-family: monospace;
    padding: 2rem;
  }


  .fields {
    position: relative;
  }
  fieldset {
    border: 0;
  }

  fieldset, fieldset > div {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 0.5rem;
  }

  legend {
    font-size: 0.875rem;
    font-weight: 300;
    letter-spacing: 0.1em;
    margin-bottom: 0.5rem;
    font-variant: small-caps;
    translate: 2ch 0;
  }
  fieldset label {
    padding: 0.5rem 1rem;
    border-radius: 100px;
    background: #0000;
    border: 1px solid color-mix(in srgb, canvasText, canvas);
    cursor: pointer;
    white-space: nowrap;
    transition-property: color, background;
    transition-duration: calc(var(--duration, 0.2) * 1s), 0.2s;
    transition-timing-function: ease-out;
    -webkit-user-select: none;
       -moz-user-select: none;
        -ms-user-select: none;
            user-select: none;

    &:hover {
      background: light-dark(
        color-mix(in srgb, canvasText, #0000 95%),
        color-mix(in srgb, canvasText, #fff0 75%)
      );
    }
  }
  label:has(+ :checked) {
    background: light-dark(#000, #fff);
    color: light-dark(#fff, #000);
  }
  .pill {
    z-index: -1;
  }
  .mask-layer,
  .pill {
    display: none;
  }
}

@layer base {
  :root {
    --font-size-min: 16;
    --font-size-max: 20;
    --font-ratio-min: 1.2;
    --font-ratio-max: 1.33;
    --font-width-min: 375;
    --font-width-max: 1500;
  }

  html {
    color-scheme: light dark;
  }

  [data-theme='light'] {
    color-scheme: light only;
  }

  [data-theme='dark'] {
    color-scheme: dark only;
  }

  [data-raw='true'] div.tp-dfwv {
    display: none;
  }

  [data-raw='true'] {
    scrollbar-color: #0000 #0000;
  }

  :where(.fluid) {
    --fluid-min: calc(
      var(--font-size-min) * pow(var(--font-ratio-min), var(--font-level, 0))
    );
    --fluid-max: calc(
      var(--font-size-max) * pow(var(--font-ratio-max), var(--font-level, 0))
    );
    --fluid-preferred: calc(
      (var(--fluid-max) - var(--fluid-min)) /
        (var(--font-width-max) - var(--font-width-min))
    );
    --fluid-type: clamp(
      (var(--fluid-min) / 16) * 1rem,
      ((var(--fluid-min) / 16) * 1rem) -
        (((var(--fluid-preferred) * var(--font-width-min)) / 16) * 1rem) +
        (var(--fluid-preferred) * var(--variable-unit, 100vi)),
      (var(--fluid-max) / 16) * 1rem
    );
    font-size: var(--fluid-type);
  }

  *,
  *:after,
  *:before {
    box-sizing: border-box;
  }

  body {
    background: light-dark(#fff, #000);
    display: grid;
    place-items: center;
    min-height: 100vh;
    font-family:
      'SF Pro Text', 'SF Pro Icons', 'AOS Icons', 'Helvetica Neue', Helvetica,
      Arial, sans-serif, system-ui;
  }

  body::before {
    --size: 45px;
    --line: color-mix(in hsl, canvasText, transparent 80%);
    content: '';
    height: 100vh;
    width: 100vw;
    position: fixed;
    background:
      linear-gradient(90deg, var(--line) 1px, transparent 1px var(--size))
        calc(var(--size) * 0.36) 50% / var(--size) var(--size),
      linear-gradient(var(--line) 1px, transparent 1px var(--size)) 0%
        calc(var(--size) * 0.32) / var(--size) var(--size);
    -webkit-mask: linear-gradient(140deg, #0000 65%, #fff);
            mask: linear-gradient(140deg, #0000 65%, #fff);
    top: 0;
    transform-style: flat;
    pointer-events: none;
    z-index: -1;
  }

  .bear-link {
    color: canvasText;
    position: fixed;
    top: 1rem;
    left: 1rem;
    width: 48px;
    aspect-ratio: 1;
    display: grid;
    place-items: center;
    opacity: 0.8;
  }

  :where(.x-link, .bear-link):is(:hover, :focus-visible) {
    opacity: 1;
  }

  .bear-link svg {
    width: 75%;
  }

  /* Utilities */
  .sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border-width: 0;
  }
}

div.tp-dfwv {
  width: 256px;
  view-transition-name: prototype-config;
}
import gsap from 'https://esm.sh/[email protected]';
import Draggable from 'https://esm.sh/[email protected]/Draggable';
import { Pane } from 'https://esm.sh/[email protected]';
gsap.registerPlugin(Draggable);

const resizeContainer = document.querySelector('.resize-container');
let fieldset;
let fields;
let radios;
let labels;
let initialChecked;
let initialIndex = 0;
let maskContainer;
let checkedIndices;

const svgMask = document.querySelector('.svg-mask');

const config = {
  theme: 'system',
  style: 'hop',
  match: true,
  wrap: true,
  knockout: 'filter',
  duration: 0.2,
  variants: 5,
  options: ['128GB', '256GB', '512GB', '1TB', '2TB', '4TB', '8TB']
};

const ctrl = new Pane({
  title: 'config'
});

const buildFieldsets = () => {
  resizeContainer.innerHTML = `
  <div class="fields">
  <div class="pill"></div>
    <fieldset>
      <legend>capacity</legend>
      <div class="content">
        ${new Array(config.variants).fill().map((_, i) => `
          <label for="${config.options[i]}">
            <span class="variant-option__button-label__text">${config.options[i]}</span>
          </label>
          <input class="sr-only" ${i === initialIndex ? 'checked data-current-checked="true"' : ''} data-input-index="${i}" aria-label="${config.options[i]}" type="radio" name="capacity" value="${config.options[i]}" id="${config.options[i]}">
        `).join('')}
      </div>
    </fieldset>
    <fieldset aria-hidden="true" class="mask-layer">
      <legend>capacity</legend>
      ${new Array(config.variants).fill().map((_, i) => `
        <label>
          <span class="variant-option__button-label__text">${config.options[i]}</span>
        </label>
      `).join('')}
    </fieldset>
  </div>
  `;
  fields = resizeContainer.querySelector('.fields');
  fieldset = resizeContainer.querySelector('fieldset');
  radios = [...fieldset.querySelectorAll('input')];
  labels = [...fieldset.querySelectorAll('label')];
  maskContainer = resizeContainer.querySelector('fieldset:first-of-type > div');

  initialChecked = fieldset.querySelector('input:checked');
  initialIndex = radios.indexOf(initialChecked);
  checkedIndices = [initialIndex];
  fieldset.style.setProperty('--pill-width', `${labels[initialIndex].offsetWidth}px`);

  initialChecked.dataset.currentChecked = true;
};
buildFieldsets();

ctrl.addBinding(config, 'variants', {
  min: 2,
  max: config.options.length,
  step: 1
}).on('change', buildFieldsets);

ctrl.addBinding(config, 'duration', {
  min: 0,
  max: 2,
  step: 0.01
});

ctrl.addBinding(config, 'style', {
  options: {
    anchor: 'anchor',
    hop: 'hop'
  }
});

const wrap = ctrl.addBinding(config, 'wrap', {
  hidden: config.style !== 'hop'
});

const match = ctrl.addBinding(config, 'match', {
  hidden: config.style !== 'hop'
});

const knockout = ctrl.addBinding(config, 'knockout', {
  options: {
    filter: 'filter',
    mask: 'mask',
    none: 'none'
  },
  hidden: config.style === 'hop'
});

ctrl.addBinding(config, 'theme', {
  label: 'theme',
  options: {
    system: 'system',
    light: 'light',
    dark: 'dark'
  }
});

const update = () => {
  document.documentElement.dataset.theme = config.theme;
  document.documentElement.dataset.style = config.style;
  document.documentElement.dataset.match = config.match;
  document.documentElement.dataset.knockout = config.knockout;
  document.documentElement.dataset.wrap = config.wrap;
  document.documentElement.style.setProperty('--duration', config.duration);
  knockout.hidden = config.style === 'hop';
  match.hidden = wrap.hidden = config.style !== 'hop';
  ctrl.refresh();
};

const sync = event => {
  if (!document.startViewTransition || event.target.controller.view.labelElement.innerText !== 'theme') {
    return update();
  }
  document.startViewTransition(() => update());
};

ctrl.on('change', sync);

const isRaw = new URLSearchParams(window.location.search).get('raw') === 'true';
if (isRaw) document.documentElement.dataset.raw = 'true';

const tweakClass = 'div.tp-dfwv';
const d = Draggable.create(tweakClass, {
  type: 'x,y',
  allowEventDefault: true,
  trigger: tweakClass + ' button.tp-rotv_b'
});

document.querySelector(tweakClass).addEventListener('dblclick', () => {
  gsap.to(tweakClass, {
    x: `+=${d[0].x * -1}`,
    y: `+=${d[0].y * -1}`,
    onComplete: () => {
      gsap.set(tweakClass, { clearProps: 'all' });
    }
  });
});
update();

const updateMask = () => {
  const { left, top } = maskContainer.getBoundingClientRect();
  svgMask.setAttribute('viewBox', `0 0 ${maskContainer.offsetWidth} ${maskContainer.offsetHeight}`);

  const maskString = new Array(radios.length).fill().map((_, i) => {
    const { x, y, width, height } = labels[i].getBoundingClientRect();
    return `<rect x="${x - left}" y="${y - top}" width="${width}" height="${height}" rx="${height * 0.5}" ry="${height * 0.5}" fill="#000"></rect>`;
  }).join('');
  
  svgMask.innerHTML = maskString;
  const encoded = encodeURIComponent(svgMask.outerHTML).replace(/'/g, '%27').replace(/"/g, '%22');
  const dataUri = `data:image/svg+xml;utf8,${encoded}`;
  document.documentElement.style.setProperty('--svg-mask', `url(${dataUri})`);
};

const resizeHandler = new ResizeObserver(() => {
  if (config.style === 'anchor') {
    updateMask();
    if (!CSS.supports('anchor-name: --active')) {
      cacheBase();
      syncChecked();
    }
  }
});
resizeHandler.observe(resizeContainer);

const positions = [];
const syncChecked = () => {
  const checked = fieldset.querySelector(':checked');
  const index = radios.indexOf(checked);
  fields.style.setProperty('--top', positions[index].top);
  fields.style.setProperty('--left', positions[index].left);
  fields.style.setProperty('--width', positions[index].width);
  fields.style.setProperty('--height', positions[index].height);
  fields.style.setProperty('--marker-top', positions[index].top);
  fields.style.setProperty('--marker-left', positions[index].left);
};

const cacheBase = () => {
  const { x: fieldX, y: fieldY } = fields.getBoundingClientRect();
  const { x, y } = fieldset.querySelector('label').getBoundingClientRect();
  fields.style.setProperty('--left', x - fieldX);
  fields.style.setProperty('--top', y - fieldY);

  document.documentElement.style.setProperty('--marker-top', y - fieldY);
  document.documentElement.style.setProperty('--marker-left', x - fieldX);
  positions.length = 0;
  for (const label of labels) {
    const { height, width, left, top } = label.getBoundingClientRect();
    positions.push({
      width,
      height,
      left: left - fieldX,
      top: top - fieldY
    });
  }
};

resizeContainer.addEventListener('input', event => {
  if (config.style === 'hop') {
    const inputIndex = Number.parseInt(event.target.dataset.inputIndex || '');
    const [currentIndex, previousIndex] = checkedIndices;
    if (currentIndex !== undefined && radios[currentIndex]) {
      radios[currentIndex].dataset.previousChecked = 'false';
    }
    if (previousIndex !== undefined && radios[previousIndex]) {
      radios[previousIndex].dataset.previousChecked = 'false';
    }

    checkedIndices.unshift(inputIndex);
    checkedIndices.length = Math.min(checkedIndices.length, 2);

    const newCurrentIndex = checkedIndices[0];
    const newPreviousIndex = checkedIndices[1];

    if (newCurrentIndex !== undefined && radios[newCurrentIndex]) {
      var _radios$newCurrentInd;
      radios[newCurrentIndex].dataset.currentChecked = 'true';
      fieldset.style.setProperty(
        '--pill-width-current',
        `${((_radios$newCurrentInd = radios[newCurrentIndex].previousElementSibling) === null || _radios$newCurrentInd === void 0 ? void 0 : _radios$newCurrentInd.offsetWidth) || 0}px`
      );
    }

    if (newPreviousIndex !== undefined && radios[newPreviousIndex]) {
      var _radios$newPreviousIn;
      radios[newPreviousIndex].dataset.previousChecked = 'true';
      radios[newPreviousIndex].dataset.currentChecked = 'false';
      fieldset.style.setProperty(
        '--pill-width-previous',
        `${((_radios$newPreviousIn = radios[newPreviousIndex].previousElementSibling) === null || _radios$newPreviousIn === void 0 ? void 0 : _radios$newPreviousIn.offsetWidth) || 0}px`
      );
    }
  } else if (config.style === 'anchor') {
    if (!CSS.supports('anchor-name: --active')) {
      syncChecked();
    }
  }
});

if (!CSS.supports('anchor-name: --active')) {
  requestAnimationFrame(() => {
    cacheBase();
    syncChecked();
    fields.dataset.initiated = true;
  });
}
CSS-only hyper-realistic glowing volumetric bell with rotating rays and a realistic notification button.

CSS Hyper-Realistic Animated Glowing Bell

This showcase demonstrates the extreme rendering potential of pure CSS. By layer-masking realistic gradients, intricate custom clip-path shapes, and dynamic box-shadow depth, it renders a hyper-realistic glowing bell. Toggling the active state switches the glowing volumetric shafts, rotating light rays, and ambient button backlights using only sibling selectors.

Technologies:
HTML CSS
Difficulty: Advanced
Browser Support (as of Jul 2026):
Chrome Chrome Edge Edge Firefox Firefox Safari Safari
Features:
Volumetric Shading Interactive Toggle Ray Traced Conics Grain Overlay
Code by: Rafa Rafa
License: MIT
Loading...
<div class="bell-container off" onclick="this.classList.toggle('off')">
	<div class="rope"></div>
	<div class="bell-top"></div>

	<div class="bell-base"></div>
	<div class="bell-base"></div>
	<div class="shadow-l1"></div>
	<div class="shadow-l2"></div>
	<div class="left-glow"></div>
	<div class="left-glow2"></div>
	<div class="r-glow"></div>
	<div class="r-glow2"></div>
	<div class="mid-ring"></div>
	<div class="mid-ring small"></div>

	<div class="glow"></div>
	<div class="glow2"></div>

	<div class="bell-buff-t"></div>
	<div class="bell-buff"></div>

	<div class="bell-btm"></div>
	<div class="bell-btm2"></div>

	<div class="bell-ring-container">
		<div class="bell-ring"></div>
		<div class="bell-rays"></div>
	</div>

	<div class="volumetric">
		<div class="vl"></div>
		<div class="vr"></div>
	</div>

</div>
<div class="button">Notify Me</div>
<div class="grain"></div>
* {
	box-sizing: border-box;
}
html,
body {
	height: 100%;
	overflow: hidden;
}
body {
	display: flex;
	flex-direction: row;
	flex-wrap: wrap;
	justify-content: center;
	align-items: center;
	margin: 0;
	background: #000;

	font-size: calc(var(--_size) * 0.01);
	--_size: min(min(600px, 50vh), 50vw);
	--base-clr: #b7b5b4;
	--degofrot: 0.8;
}

.bell-container {
	width: 80em;
	height: 80em;
	opacity: 1;
	cursor: pointer;

	transform-origin: 50% -50vh;
	animation: 5s ease-in-out infinite bell;
}
@keyframes bell {
	0% {
		rotate: calc(1deg * var(--degofrot));
	}
	50% {
		rotate: calc(-1deg * var(--degofrot));
	}
	100% {
		rotate: calc(1deg * var(--degofrot));
	}
}

* {
	transition: filter 0.4s ease-in-out, box-shadow 0.4s ease-in-out,
		opacity 0.4s ease-in-out, color 0.4s ease-in-out, background 0.4s ease-in-out,
		text-shadow 0.4s ease-in-out;
}

*::before,
*::after {
	transition: filter 0.4s ease-in-out, box-shadow 0.4s ease-in-out,
		opacity 0.4s ease-in-out, color 0.4s ease-in-out, background 0.4s ease-in-out,
		text-shadow 0.4s ease-in-out;
}

.bell-container,
.bell-container * {
	position: absolute;
	left: 0;
	right: 0;
	top: 0;
	bottom: 0;
	margin: auto;
}

.rope {
	height: 50vh;
	width: 2em;
	translate: 0 -52%;
	background: linear-gradient(90deg, #2d54744d 0%, #000b 30%, transparent 100%),
		repeating-linear-gradient(-70deg, #252525, #888 2%, #3a3a3a 3%);
}

.bell-top {
	width: 14%;
	height: 14%;
	border-radius: 50%;
	translate: 0 -28em;
	background: var(--base-clr);
	box-shadow: inset -1em -0.5em 2em 0.5em #fff, inset 1em -1em 2em 3em #000,
		0 -0.1em 0.4em 0.3em #c6eaffa8;
}

.bell-base {
	width: 50%;
	height: 50%;
	border-radius: 50%;
	translate: 0 -24%;
	background: var(--base-clr);
	box-shadow: 0 -0.1em 0.4em 0.2em #c6eaffa8;
}
.bell-base:before {
	content: "";
	background-image: radial-gradient(
		circle at -80% -12%,
		transparent 50em,
		var(--base-clr) 50em
	);
	position: absolute;
	translate: -18em 20em;
	width: 100%;
	height: 80%;
}
.bell-base:after {
	content: "";
	background-image: radial-gradient(
		circle at -80% -12%,
		transparent 50.1em,
		#cacaca 50.3em,
		var(--base-clr) 50.5em
	);
	position: absolute;
	translate: 18em 20em;
	width: 100%;
	height: 80%;
	transform: rotateY(180deg);
}
.bell-base:nth-child(2) {
	filter: brightness(3) blur(1em);
	scale: 0.74 0.84;
	translate: 0em -11em;
}
.shadow-l1 {
	width: 30em;
	height: 42em;
	border-radius: 50%;
	rotate: 12deg;
	translate: -3em -6em;
	filter: blur(2em);
	background: #797a80;
}
.shadow-l2 {
	width: 130%;
	height: 90%;
	filter: blur(5em);
	translate: -6em 9em;
}
.shadow-l2::before {
	display: block;
	content: "";
	width: 68%;
	height: 64%;
	border-radius: 50%;
	rotate: -54deg;
	translate: -8em 2em;
	scale: 1;
	background: red;
	background: #000000;
}
.glow {
	width: 100%;
	height: 100%;
	filter: brightness(2) blur(2em);
}
.glow::before {
	clip-path: polygon(
		9% 83%,
		12% 79%,
		15% 74%,
		18% 68%,
		20% 62%,
		22% 56%,
		23% 50%,
		24% 43%,
		25% 38%,
		25% 34%,
		26% 29%,
		26% 26%,
		27% 22%,
		29% 19%,
		30% 15%,
		32% 13%,
		34% 10%,
		37% 7%,
		41% 5%,
		44% 4%,
		47% 3%,
		51% 3%,
		55% 3%,
		58% 5%,
		62% 6%,
		73% 29%,
		72% 25%,
		70% 20%,
		67% 16%,
		63% 12%,
		60% 9%,
		58% 8%,
		55% 7%,
		52% 6%,
		48% 6%,
		44% 8%,
		41% 9%,
		37% 12%,
		36% 14%,
		33% 16%,
		31% 20%,
		30% 23%,
		29% 26%,
		28% 31%,
		27% 36%,
		27% 39%,
		26% 44%,
		26% 48%,
		26% 52%,
		25% 56%,
		23% 61%,
		22% 65%,
		21% 69%,
		19% 72%,
		17% 75%,
		15% 78%,
		13% 81%
	);
	width: 100%;
	height: 80%;
	translate: 0 6em;
	scale: 0.94 0.94;
	background: #fff3;
	display: block;
	content: "";
}
.glow2 {
	width: 100%;
	height: 100%;
	filter: brightness(1) blur(0.3em);
	opacity: 0.1;
}
.glow2::before {
	clip-path: polygon(
		9.21% 83%,
		12.28% 79%,
		15.35% 74%,
		18.41% 68%,
		20.46% 62%,
		22.51% 56%,
		23.53% 50%,
		24.55% 43%,
		25.58% 34%,
		26.6% 29%,
		27.62% 22%,
		29.16% 18.5%,
		30.95% 15.75%,
		32.74% 13%,
		34.78% 10%,
		37.85% 7%,
		41.94% 5%,
		45.01% 4%,
		48.08% 3%,
		52.17% 3%,
		56.27% 3%,
		64.01% 6.36%,
		55.75% 4.5%,
		47.83% 4.75%,
		42.84% 5.88%,
		39.51% 7.88%,
		36.45% 10.38%,
		33.38% 14.88%,
		30.69% 19%,
		29.67% 22.5%,
		28.8% 26.72%,
		28.21% 31.36%,
		26.92% 38.44%,
		26.57% 43.67%,
		25.51% 48.34%,
		25% 54.34%,
		23.4% 60.69%,
		21.23% 65.38%,
		18.41% 71.5%,
		16.88% 74.75%,
		12.28% 80.5%
	);
	width: 100%;
	height: 84%;
	translate: -1em 8.4em;
	scale: 1;
	background: #fff3;
	display: block;
	content: "";
}
.left-glow {
	--lgc: #5d819666;
	width: 50%;
	height: 50%;
	border-radius: 50%;
	translate: 0 -24%;
	background: transparent;
	box-shadow: inset 1em 0em 1em 0.2em var(--lgc);
	clip-path: polygon(0 0, 100% 0, 100% 50%, 0 50%);
}
.left-glow2 {
	--lgc2: #5d819666;
	width: 49%;
	height: 50%;
	background-image: radial-gradient(
		circle at -80% -12%,
		transparent 50em,
		var(--lgc2) 50.3em,
		transparent 52em
	);
	position: absolute;
	translate: -19em 10.35em;
	clip-path: polygon(0 0, 100% 0, 100% 78%, 0 78%);
}
.r-glow {
	--lgc: #fffaf680;
	width: 50%;
	height: 50%;
	border-radius: 50%;
	translate: 0 -24%;
	background: transparent;
	box-shadow: inset 1em 0em 1em 0.2em var(--lgc);
	clip-path: polygon(0 0, 100% 0, 100% 50%, 0 50%);
	transform: rotateY(180deg);
}
.r-glow2 {
	--lgc2: #fffaf680;
	width: 49%;
	height: 50%;
	background-image: radial-gradient(
		circle at -80% -12%,
		transparent 50em,
		var(--lgc2) 50.3em,
		transparent 52em
	);
	position: absolute;
	translate: 18.2em 10.35em;
	clip-path: polygon(0 0, 100% 0, 100% 78%, 0 78%);
	transform: rotateY(180deg) rotateZ(-2deg);
}
.mid-ring.small {
	translate: 0.04em -8em;
	scale: 0.8 0.5;
}
.mid-ring {
	width: 64%;
	height: 10%;
	border-radius: 50%;
	translate: -0.1em 10em;
	box-shadow: inset -0.3em 1.3em 0.4em -1em #fff5,
		-0.2em -1.2em 0.4em -0.4em #505050, -0.1em -1.8em 0.4em -0.4em #fff5,
		0 -2.5em 0.4em -1em #000000;
	mix-blend-mode: hard-light;
	filter: brightness(0.8);
}
.mid-ring::before,
.mid-ring::after {
	content: "";
	display: block;
	background: #000;
	width: 2em;
	height: 2em;
	top: 10%;
	border-radius: 50%;
	position: absolute;
}
.mid-ring::after {
	right: -2%;
}
.mid-ring::before {
	left: -2%;
}
.bell-buff-t {
	background: #fff2;
	width: 72%;
	height: 12%;
	border-radius: 50%;
	translate: 0 16em;
	filter: blur(1em);
}
.bell-buff {
	background: linear-gradient(90deg, black 40%, var(--base-clr) 90%);
	width: 88%;
	height: 20%;
	border-radius: 50% 50% 50% 50% / 50% 50% 30% 30%;
	translate: 0 20em;
	box-shadow: inset 1em 0 2em -1em #5d819666, inset -1em 0 2em -1em #fff;
}
.bell-btm {
	width: 88%;
	height: 18%;
	border-radius: 50%;
	translate: 0 23em;
	background: linear-gradient(90deg, black 40%, var(--base-clr) 90%);
}
.bell-btm2 {
	width: 74%;
	height: 12%;
	border-radius: 50%;
	translate: 0 24em;
	background: #fffff6;
	box-shadow: 0 0 1em 0.6em #ffe9d4, -0.8em 0.2em 2em 1em #cca37f,
		-5.4em -0.6em 3em -1em #ce6e1abb, 6em -0.6em 3em -1em #ce6e1abb,
		inset 0em 30.3em 0.3em -30em #c7962d, inset 0 -2em 2em -2em #ffe9d4,
		inset 0em -1em 2em 1em #ce6e1a66;
	filter: brightness(1);
}
.off .bell-btm2 {
	filter: brightness(0.02);
}
.bell-ring-container {
	width: 74%;
	height: 24%;
	border-radius: 50% 50% 50% 50% / 25% 25% 0% 0%;
	translate: 0 29.2em;
	overflow: hidden;
}
.bell-ring {
	width: 12em;
	height: 12em;
	background: #fff;
	border-radius: 50%;
	translate: 0 -6em;
	box-shadow: 0 0.8em 1em -0.3em #f8e1d0, inset 0 -6em 4em -4em #e3b695,
		inset 0 1em 3em 1em #fff4, inset 0 2em 3em 1em #fff,
		inset 0 100em 0 100em #2c2c2c;
}
.off .bell-ring {
	background: #000;
	box-shadow: 0 0.8em 1em -0.3em #f8e1d000, inset 0 -6em 4em -4em #e3b69500,
		inset 0 1em 3em 1em #fff0, inset 0 -2em 3em 1em #fff2,
		inset 0 100em 0 100em #000;
}

.bell-rays {
	mix-blend-mode: soft-light;
	box-shadow: inset 0 -21em 4em -20em #000;
	width: 100%;
	height: 140%;
	translate: 0 -4em;
	border-radius: 50%;
}
.bell-rays::before {
	content: "";
	display: block;
	width: 100em;
	height: 100em;
	transform-origin: 50% 50%;
	position: absolute;
	left: -21em;
	top: -77em;
	border-radius: 100%;
	filter: blur(0.6em);
	background: repeating-conic-gradient(
		at 50% 50%,
		#fff2 0%,
		transparent 0.6%,
		#fff2 0.8%
	);
	animation: radiate 1s linear infinite;
}
.off .bell-rays {
	opacity: 0;
}
@keyframes radiate {
	0% {
		rotate: 0deg;
	}
	100% {
		rotate: 6deg;
	}
}

.volumetric {
	width: 98%;
	height: 224%;
	translate: 0 124em;
	opacity: 0.2;
}
.volumetric .vl {
	width: 100%;
	height: 100%;
	transform-origin: 50% 20em;
	rotate: 22deg;
	box-shadow: inset 40em 0 20em -20em #fff1;
}
.volumetric .vr {
	width: 100%;
	height: 100%;
	transform-origin: 50% 20em;
	rotate: -22deg;
	box-shadow: inset -40em 0 20em -20em #fff1;
}
.off .volumetric {
	opacity: 0;
}

.grain {
	z-index: 10;
	position: absolute;
	pointer-events: none;
	width: 100%;
	height: 100%;
	top: 0;
	bottom: 0;
	margin: auto;
	background: radial-gradient(circle at 50% 50%, #000, #0000),
		url("data:image/svg+xml,%3Csvg viewBox='0 0 600 600' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='2' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E");
	filter: contrast(100%) brightness(200%) grayscale(1) opacity(0.168);
	mix-blend-mode: screen;
}

.button {
	position: relative;
	font-size: 6em;
	font-family: monospace;
	background: #000;
	top: 8em;
	width: fit-content;
	height: fit-content;
	color: #000;
	cursor: pointer;
	padding: 0.4em 1.2em;
	border-radius: 0.4em;
	text-shadow: 0 -1px 3px #fff0;
	box-shadow: inset 0 0.04em 0.06em 0 #fff, inset 0 1em 1em 0 #fff5,
		inset 0 0.2em 0.2em 0 #e3b695;
	animation: 5s ease-in-out infinite lumenbtn;
}
.button:hover {
	color: #fff;
	text-shadow: 0 -1px 3px #fff;
	transition: all 0.16s ease-in-out;
}
.button::before,
.button::after {
	content: "";
	display: block;
	width: 100%;
	height: 54%;
	position: absolute;
	top: 6em;
	left: 0;
	right: 0;
}
.button::before {
	background: #e3b695;
	scale: 2;
	z-index: -2;
	filter: blur(12px);
	border-radius: 100%;
	animation: 5s ease-in-out infinite lumen;
}
.button::after {
	background: #000c;
	z-index: -1;
	filter: blur(0.3em);
	border-radius: 30%;
}

@keyframes lumenbtn {
	0% {
		box-shadow: inset 0 0.04em 0.06em 0 #fff,
			inset calc(-0.2em * var(--degofrot)) 1em 1em 0 #fff5,
			inset calc(-0.2em * var(--degofrot)) 0.2em 0.4em 0 #e3b695;
	}
	50% {
		box-shadow: inset 0 0.04em 0.06em 0 #fff,
			inset calc(0.2em * var(--degofrot)) 1em 1em 0 #fff5,
			inset calc(0.2em * var(--degofrot)) 0.2em 0.4em 0 #e3b695;
	}
	100% {
		box-shadow: inset 0 0.04em 0.06em 0 #fff,
			inset calc(-0.2em * var(--degofrot)) 1em 1em 0 #fff5,
			inset calc(-0.2em * var(--degofrot)) 0.2em 0.4em 0 #e3b695;
	}
}
@keyframes lumen {
	0% {
		translate: calc(-0.8em * var(--degofrot));
	}
	50% {
		translate: calc(0.8em * var(--degofrot));
	}
	100% {
		translate: calc(-0.8em * var(--degofrot));
	}
}

.off + .button {
	opacity: 0;
	pointer-events: none;
}
const bell = document.querySelector('.bell-container');
const button = document.querySelector('.button');

if (bell && button) {
  button.addEventListener('click', () => {
    bell.classList.toggle('off');
  });
}
Custom curved scrollbars generated with dynamic SVG path tracing to match container border radius using JavaScript.

Custom Curved SVG Border Radius Scrollbar

This highly innovative scrollbar solution bypasses the strict styling limitations of standard native scrollbars by rendering them as dynamic SVG overlays. By calculating container border-radius, sizing ratios, and scroll progression heights, it dynamically draws an SVG path to follow the container’s exact curvature. Grab states are handled seamlessly via the Pointer Events API and the SVG geometry method getPointAtLength.

Technologies:
SVG HTML CSS JavaScript
Difficulty: Advanced
Browser Support (as of Jul 2026):
Chrome Chrome Edge Edge Firefox Firefox Safari Safari
Features:
SVG Path Tracing Scrollbar Customization Pointer Grab Fluid Calculations
Code by: Chris Bolson Chris Bolson
License: MIT
Loading...
<header>
  <h1>Custom curved scrollbars</h1>
  <p>The scrollbar follows the border-radius of the container, it's length being calculated by the amount of content</p>
</header>


<div class="wrapper">
  <section class="scroll-container " data-scrollbar>
    <div class="scroll-content">
      
      <h2>Grab the scrollbar thumb and scroll up or down.</h2>
      <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Modi placeat rem at esse reiciendis ullam veritatis sapiente sit ducimus ea magnam nihil vero corrupti quo quae, tempore dolor, reprehenderit debitis?
      </p>
      <img src='https://images.unsplash.com/photo-1759392658577-4324fb89b991?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3wzMjM4NDZ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3NjEzMzUxNTl8&ixlib=rb-4.1.0&q=80&w=400' alt='random' width="300" height="150">
      <p>Veritatis et repellendus, consequatur dicta saepe vel natus aliquam reprehenderit accusamus voluptatem commodi, minus eos nihil quae. Odit eveniet quia a minus natus qui reiciendis unde! Quam, odit temporibus? Illo.
      </p>
      <p>
        Sunt consectetur asperiores fuga quasi nobis vitae et commodi error deserunt perspiciatis, modi eaque iste eligendi quod esse aliquam. Vitae debitis minima, laborum incidunt ex corporis odit eaque consequatur beatae.
      </p>
      <p>Suscipit possimus mollitia ut minus commodi facilis distinctio assumenda corrupti voluptate, ipsa voluptates minima ex nam exercitationem? Laborum, sequi ducimus! Sunt repellendus dolorum suscipit similique accusamus eius ad laborum sed.</p>
      <p>
      Laboriosam beatae consequatur omnis repellat consequuntur obcaecati! Voluptatibus sunt quibusdam optio nihil delectus cumque unde omnis voluptatum laborum temporibus, labore perspiciatis quis impedit sapiente animi adipisci, explicabo velit nesciunt similique?</p>
      
    
      <p>In id itaque ullam totam, dolor blanditiis praesentium, at laborum esse molestias fugit dolorum sapiente dolores quo animi ducimus provident deserunt tempora, voluptatem recusandae aliquid. Tenetur harum doloremque provident excepturi!</p>
      <p>
      Et suscipit dolores, non ullam consequuntur illo cumque perspiciatis quos voluptate impedit quas? Similique omnis magnam iste quasi aspernatur neque. Quis porro consequuntur facere odio sapiente praesentium sed harum dignissimos.
      </p>
      <p>Quisquam porro esse fuga ducimus inventore, repellendus ad omnis perferendis velit atque commodi natus fugit, aliquid incidunt, eveniet odit. Sunt blanditiis expedita, laudantium fugit dignissimos eius provident aperiam cum deleniti!</p>
    </div>
  </section>

  <section class="scroll-container box-2 " data-scrollbar>
    <div class="scroll-content">
      <h2>Less border radius on this one</h2>
      <p>
      Ut quasi magni rerum magnam, earum officiis asperiores placeat maiores ullam sint debitis culpa! Nemo est, modi dolor eos, blanditiis, neque ullam facere voluptates accusamus quasi nostrum similique. Quod, itaque.
      </p>
      <p>Est rem voluptates nihil culpa fugiat possimus provident a adipisci facilis, molestias quos sed pariatur cum aperiam vitae. Repellat natus iure omnis, sed nisi laboriosam ullam pariatur aliquam dolore! Hic.
      </p>
      <p>In id itaque ullam totam, dolor blanditiis praesentium, at laborum esse molestias fugit dolorum sapiente dolores quo animi ducimus provident deserunt tempora, voluptatem recusandae aliquid. Tenetur harum doloremque provident excepturi!</p>
      <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Modi placeat rem at esse reiciendis ullam veritatis sapiente sit ducimus ea magnam nihil vero corrupti quo quae, tempore dolor, reprehenderit debitis?
      </p>
      <p>Veritatis et repellendus, consequatur dicta saepe vel natus aliquam reprehenderit accusamus voluptatem commodi, minus eos nihil quae. Odit eveniet quia a minus natus qui reiciendis unde! Quam, odit temporibus? Illo.
      </p>
      <p>
        Sunt consectetur asperiores fuga quasi nobis vitae et commodi error deserunt perspiciatis, modi eaque iste eligendi quod esse aliquam. Vitae debitis minima, laborum incidunt ex corporis odit eaque consequatur beatae.
      </p>
      <p>Suscipit possimus mollitia ut minus commodi facilis distinctio assumenda corrupti voluptate, ipsa voluptates minima ex nam exercitationem? Laborum, sequi ducimus! Sunt repellendus dolorum suscipit similique accusamus eius ad laborum sed.</p>
      <p>
      Laboriosam beatae consequatur omnis repellat consequuntur obcaecati! Voluptatibus sunt quibusdam optio nihil delectus cumque unde omnis voluptatum laborum temporibus, labore perspiciatis quis impedit sapiente animi adipisci, explicabo velit nesciunt similique?</p>
      <p>
      Ut quasi magni rerum magnam, earum officiis asperiores placeat maiores ullam sint debitis culpa! Nemo est, modi dolor eos, blanditiis, neque ullam facere voluptates accusamus quasi nostrum similique. Quod, itaque.
      </p>
      <p>Est rem voluptates nihil culpa fugiat possimus provident a adipisci facilis, molestias quos sed pariatur cum aperiam vitae. Repellat natus iure omnis, sed nisi laboriosam ullam pariatur aliquam dolore! Hic.
      </p>
      <p>In id itaque ullam totam, dolor blanditiis praesentium, at laborum esse molestias fugit dolorum sapiente dolores quo animi ducimus provident deserunt tempora, voluptatem recusandae aliquid. Tenetur harum doloremque provident excepturi!</p>
      <p>
      Et suscipit dolores, non ullam consequuntur illo cumque perspiciatis quos voluptate impedit quas? Similique omnis magnam iste quasi aspernatur neque. Quis porro consequuntur facere odio sapiente praesentium sed harum dignissimos.
      </p>
      <p>Quisquam porro esse fuga ducimus inventore, repellendus ad omnis perferendis velit atque commodi natus fugit, aliquid incidunt, eveniet odit. Sunt blanditiis expedita, laudantium fugit dignissimos eius provident aperiam cum deleniti!</p>
      <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Modi placeat rem at esse reiciendis ullam veritatis sapiente sit ducimus ea magnam nihil vero corrupti quo quae, tempore dolor, reprehenderit debitis?
      </p>
      <p>Veritatis et repellendus, consequatur dicta saepe vel natus aliquam reprehenderit accusamus voluptatem commodi, minus eos nihil quae. Odit eveniet quia a minus natus qui reiciendis unde! Quam, odit temporibus? Illo.
      </p>
      <p>
        Sunt consectetur asperiores fuga quasi nobis vitae et commodi error deserunt perspiciatis, modi eaque iste eligendi quod esse aliquam. Vitae debitis minima, laborum incidunt ex corporis odit eaque consequatur beatae.
      </p>
      <p>Suscipit possimus mollitia ut minus commodi facilis distinctio assumenda corrupti voluptate, ipsa voluptates minima ex nam exercitationem? Laborum, sequi ducimus! Sunt repellendus dolorum suscipit similique accusamus eius ad laborum sed.</p>
      <p>
      Laboriosam beatae consequatur omnis repellat consequuntur obcaecati! Voluptatibus sunt quibusdam optio nihil delectus cumque unde omnis voluptatum laborum temporibus, labore perspiciatis quis impedit sapiente animi adipisci, explicabo velit nesciunt similique?</p>
      
      Et suscipit dolores, non ullam consequuntur illo cumque perspiciatis quos voluptate impedit quas? Similique omnis magnam iste quasi aspernatur neque. Quis porro consequuntur facere odio sapiente praesentium sed harum dignissimos.
      </p>
      <p>Quisquam porro esse fuga ducimus inventore, repellendus ad omnis perferendis velit atque commodi natus fugit, aliquid incidunt, eveniet odit. Sunt blanditiis expedita, laudantium fugit dignissimos eius provident aperiam cum deleniti!</p>
    </div>
  </section>
</div>
@import url(https://fonts.bunny.net/css?family=krub:200i,300);
@layers demo,base;


@layer demo{
  
  .wrapper{
    display: grid;
    grid-template-columns: repeat(auto-fit,minmax(250px, 1fr));
    gap: 1rem;
    height: 500px;
    width: min(100%, 600px);
    margin: 0 auto;
  }
  .scroll-container {
		--card-border-radius: 50px;
		
    --thumb-color: rgb(0, 188, 255);
    --thumb-color-active: dodgerblue;
    --thumb-width: 4;
    --thumb-width-active: 6;
    
    --track-color: transparent;
    --track-color-active: light-dark(#EEE,#222);
    --track-width: 4;
    --track-width-active: 6;

		/* styling for the second card */
		&.box-2{
      --card-border-radius: 20px;
    	--thumb-color: rgb(154, 230, 0);
    	--thumb-color-active: rgb(124, 207, 0);
    }
		
		
    position: relative;
    width: 100%;
    contain:size;
    margin: 3rem auto;
    border: 1px solid var(--clr-lines);
    border-radius: var(--card-border-radius);
    overflow: hidden;  
    & h2{
      text-wrap: balance;
      font-size: 1.1rem;
    }
    & img{
      max-width: 100%;
      display: block;
    }
  }
	
  
  .scroll-content {
    height: 100%;
    overflow-y: auto;
    padding:1rem 1.5rem 1rem 1rem; /* space on right for scrollbar */
	
    /* keep scrolling functional */
    overflow-y: scroll;
  
    /* hide scrollbars */
	 	scrollbar-width: none; /* Firefox */
   	-ms-overflow-style: none; /* IE and Edge Legacy */
		&::-webkit-scrollbar {
			display: none; /* Chrome, Safari, and Opera */
		}
	}
  .scrollbar-svg {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    &:has(:active) {
      --thumb-width: var(--thumb-width-active);
      --track-width: var(--track-width-active);
      --track-color: var(--track-color-active);
    }
  }
  
  .scrollbar-track {
    fill: none;
    stroke: var(--track-color);
    stroke-width: var(--track-width);
    stroke-linecap: round;
    transition: stroke-width,stroke;
    transiton-duration: 150ms;
    transition-timing-function:ease-in-out;
  }
  .scrollbar-thumb {
    fill: none;
    stroke: var(--thumb-color);
    stroke-width: var(--thumb-width);
    stroke-linecap: round;
    pointer-events: auto;
    cursor: grab;
    transition: stroke-width,stroke;
    transiton-duration: 150ms;
    transition-timing-function:ease-in-out;
    &:active {
      stroke: var(--thumb-color-active);
      cursor: grabbing;
    }
  }
  
}


@layer base{
  *{
  	box-sizing: border-box;
  }
  :root {
    color-scheme: light dark;
  
    --bg-dark: rgb(11, 11, 11);
    --bg-light: rgb(248, 244, 238);
    --txt-light: rgb(10, 10, 10);
    --txt-dark: rgb(245, 245, 245);
    --line-light: rgba(0 0 0 / .15);
    --line-dark: rgba(255 255 255 / .25);
  
    --clr-bg: light-dark(var(--bg-light), var(--bg-dark));
    --clr-txt: light-dark(var(--txt-light), var(--txt-dark));
    --clr-lines: light-dark(var(--line-light), var(--line-dark));
    --clr-accent: dodgerblue;
  
  }
  
  html, body {
    background-color: var(--clr-bg);
    color: var(--clr-txt);
    font-family: 'Krub', sans-serif;
    margin: 0;
    padding: 0;
    min-height: 100svh;
  }
  
  body {
    display: grid;
    place-content: center;
  	gap: 0;
    padding: 1rem;
    line-height: 1.4;
  }
  header{
    text-align: center;
    text-wrap: balance;
    width: min(100%, 60ch);
    margin: 0 auto;
  }
  h1{
    font-size: 1.2rem;
    margin: 0;
    text-align: center;
		text-transform: capitalize;
  }
  p{
    font-size: 0.8rem;
    opacity: .9;
  }
}
console.clear();

// config
const OFFSET = 7; // distance from edge of container
const EXTRA_INSET = 2;
const MIN_START_RATIO = 0.8;
const MIN_THUMB = 20;
const SEGMENTS = 50;

// init scrollbars - each has their own scoped functions and settings
document.querySelectorAll('[data-scrollbar]').forEach(container => {
  initCurvedScrollbar(container);
});

// function - init scrolled container
function initCurvedScrollbar(container) {
  const content = container.querySelector('.scroll-content');
  
  // create and add SVG
  svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
  svg.classList.add('scrollbar-svg');
  svg.setAttribute('aria-hidden', 'true');

  const trackPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
  trackPath.classList.add('scrollbar-track');

  const thumbPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
  thumbPath.classList.add('scrollbar-thumb');

  svg.appendChild(trackPath);
  svg.appendChild(thumbPath);
  container.appendChild(svg);
  
  // state
  let pathLength = 0;
  let thumbLength = 50;
  let dragging = false;
  let pointerId = null;

  // function - update
  function updatePath() {
    const w = container.clientWidth;
    const h = container.clientHeight;
    const r = parseFloat(getComputedStyle(container).borderRadius) || 0;

    const effectiveRadius = Math.max(r - OFFSET, 0);
    const trackX = w - OFFSET;
    const topY = OFFSET;
    const bottomY = h - OFFSET;
    const cornerX = trackX - effectiveRadius;

    // calculate x start point - ensure that it is never higher than the corner radius start point
    const minStartX = w * MIN_START_RATIO;
    let startX = trackX - effectiveRadius * EXTRA_INSET;
    if (startX < minStartX) startX = minStartX;
    if (startX > cornerX) startX = cornerX;

    // create the dynamic path
    const d = `
      M ${startX} ${topY}
      L ${cornerX} ${topY}                     
      A ${effectiveRadius} ${effectiveRadius} 0 0 1 ${trackX} ${topY + effectiveRadius} 
      L ${trackX} ${bottomY - effectiveRadius} 
      A ${effectiveRadius} ${effectiveRadius} 0 0 1 ${cornerX} ${bottomY} 
      L ${startX} ${bottomY}
    `;
    trackPath.setAttribute('d', d);

    // calculate length of thumb based on content height
    pathLength = trackPath.getTotalLength();
    const ratio = content.clientHeight / content.scrollHeight;
    thumbLength = Math.max(MIN_THUMB, pathLength * ratio);

    updateThumb();
  }

  // function - thumb update
  function updateThumb() {
    const scrollableHeight = content.scrollHeight - content.clientHeight || 1;
    const scrollRatio = content.scrollTop / scrollableHeight;
    const startOffset = (pathLength - thumbLength) * scrollRatio;
    const endOffset = startOffset + thumbLength;

    // the thumb needs to follow the path so we get the generated path points
    // NOTE - the high number of segments help follow the curves
    const points = [];
    for (let i = 0; i <= SEGMENTS; i++) {
      const t = startOffset + ((endOffset - startOffset) / SEGMENTS) * i;
      const p = trackPath.getPointAtLength(t);
      points.push(`${p.x} ${p.y}`);
    }
  
    const segmentD = `M ${points[0]} ${points.slice(1).map(pt => `L ${pt}`).join(' ')}`;
    thumbPath.setAttribute('d', segmentD);
  }

  //  function - grab thumb
  thumbPath.addEventListener('pointerdown', e => {
    e.preventDefault();
    dragging = true;
    pointerId = e.pointerId;
    thumbPath.setPointerCapture(pointerId);
  });
  
  // function - drag thumb
  window.addEventListener('pointermove', e => {
    if (!dragging || e.pointerId !== pointerId) return;
    const rect = container.getBoundingClientRect();
    let ratio = (e.clientY - rect.top) / rect.height;
    ratio = Math.max(0, Math.min(1, ratio));
    content.scrollTop = ratio * (content.scrollHeight - content.clientHeight);
    updateThumb();
  });
	
  // function - release thumb
  window.addEventListener('pointerup', e => {
    if (!dragging || e.pointerId !== pointerId) return;
    dragging = false;
    try { thumbPath.releasePointerCapture(pointerId); } catch {}
    pointerId = null;
  });

  // events
  content.addEventListener('scroll', updateThumb);
  window.addEventListener('resize', updatePath);

  updatePath();
}