Patterns Easy
Progressive Blur
A gradient blur effect where content gets progressively more blurry at the edges, like a fade-to-blur overlay.
Open in Lab
MCP
css javascript vue svelte
Targets: TS JS HTML React Vue Svelte
Code
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
:root {
--blur-intensity: 12px;
--blur-height: 200px;
--accent: #818cf8;
--accent-dim: rgba(99, 102, 241, 0.15);
}
body {
font-family: system-ui, -apple-system, sans-serif;
background: #0a0a0a;
color: #e2e8f0;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 2rem;
gap: 4rem;
}
h1 {
font-size: clamp(2rem, 5vw, 3.5rem);
font-weight: 800;
letter-spacing: -0.03em;
background: linear-gradient(135deg, #e0e7ff 0%, #818cf8 50%, #6366f1 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-align: center;
padding-top: 3rem;
}
.subtitle {
text-align: center;
color: rgba(148, 163, 184, 0.8);
font-size: 1.125rem;
margin-top: -2rem;
}
/* Progressive Blur container */
.progressive-blur-container {
position: relative;
width: 100%;
max-width: 700px;
overflow: hidden;
}
/* The blur overlay — stacks multiple layers for smooth progressive effect */
.progressive-blur {
position: absolute;
left: 0;
right: 0;
height: var(--blur-height);
z-index: 10;
pointer-events: none;
display: flex;
flex-direction: column;
}
.progressive-blur.bottom {
bottom: 0;
}
.progressive-blur.top {
top: 0;
}
.progressive-blur.left,
.progressive-blur.right {
top: 0;
bottom: 0;
height: 100%;
width: var(--blur-height);
flex-direction: row;
}
.progressive-blur.left {
left: 0;
right: auto;
}
.progressive-blur.right {
right: 0;
left: auto;
}
/* Multiple blur layers for smooth gradient effect */
.progressive-blur-layer {
flex: 1;
backdrop-filter: blur(0px);
}
.progressive-blur.bottom .progressive-blur-layer:nth-child(1) {
backdrop-filter: blur(1px);
}
.progressive-blur.bottom .progressive-blur-layer:nth-child(2) {
backdrop-filter: blur(2px);
}
.progressive-blur.bottom .progressive-blur-layer:nth-child(3) {
backdrop-filter: blur(4px);
}
.progressive-blur.bottom .progressive-blur-layer:nth-child(4) {
backdrop-filter: blur(8px);
}
.progressive-blur.bottom .progressive-blur-layer:nth-child(5) {
backdrop-filter: blur(12px);
}
.progressive-blur.bottom .progressive-blur-layer:nth-child(6) {
backdrop-filter: blur(20px);
}
.progressive-blur.top .progressive-blur-layer:nth-child(6) {
backdrop-filter: blur(1px);
}
.progressive-blur.top .progressive-blur-layer:nth-child(5) {
backdrop-filter: blur(2px);
}
.progressive-blur.top .progressive-blur-layer:nth-child(4) {
backdrop-filter: blur(4px);
}
.progressive-blur.top .progressive-blur-layer:nth-child(3) {
backdrop-filter: blur(8px);
}
.progressive-blur.top .progressive-blur-layer:nth-child(2) {
backdrop-filter: blur(12px);
}
.progressive-blur.top .progressive-blur-layer:nth-child(1) {
backdrop-filter: blur(20px);
}
/* Demo: Scrollable content */
.scroll-demo {
height: 400px;
overflow-y: auto;
padding: 2rem;
scrollbar-width: thin;
scrollbar-color: rgba(255, 255, 255, 0.1) transparent;
}
.scroll-demo::-webkit-scrollbar {
width: 6px;
}
.scroll-demo::-webkit-scrollbar-track {
background: transparent;
}
.scroll-demo::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
}
/* Demo cards */
.demo-card {
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 12px;
padding: 1.5rem;
margin-bottom: 1rem;
}
.demo-card h3 {
font-size: 1rem;
font-weight: 600;
color: #f1f5f9;
margin-bottom: 0.5rem;
}
.demo-card p {
color: rgba(148, 163, 184, 0.7);
font-size: 0.9rem;
line-height: 1.6;
}
.demo-card .tag {
display: inline-block;
background: var(--accent-dim);
color: var(--accent);
font-size: 0.75rem;
font-weight: 600;
padding: 0.25rem 0.75rem;
border-radius: 999px;
margin-top: 0.75rem;
}
/* Section labels */
.section-label {
text-align: center;
font-size: 0.875rem;
font-weight: 600;
color: rgba(148, 163, 184, 0.5);
text-transform: uppercase;
letter-spacing: 0.1em;
margin-bottom: 1rem;
}
/* Image demo */
.image-demo {
height: 300px;
border-radius: 16px;
overflow: hidden;
background: linear-gradient(135deg, #1e1b4b 0%, #312e81 30%, #4338ca 60%, #6366f1 100%);
display: grid;
place-items: center;
}
.image-demo-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 1rem;
}
.image-demo-content .orb {
width: 80px;
height: 80px;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, #a78bfa, #4338ca);
box-shadow: 0 0 60px rgba(129, 140, 248, 0.4);
}
.image-demo-content p {
color: rgba(255, 255, 255, 0.7);
font-size: 0.9rem;
}
.examples-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 2rem;
width: 100%;
max-width: 900px;
}
.example-wrapper {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.outer-border {
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 16px;
overflow: hidden;
}// Progressive Blur — minimal JS for setup (effect is primarily CSS)
(function () {
"use strict";
// Auto-create blur layers for each .progressive-blur element
function initProgressiveBlur() {
const blurElements = document.querySelectorAll(".progressive-blur");
const LAYER_COUNT = 6;
blurElements.forEach((el) => {
// Skip if layers already exist
if (el.querySelector(".progressive-blur-layer")) return;
for (let i = 0; i < LAYER_COUNT; i++) {
const layer = document.createElement("div");
layer.className = "progressive-blur-layer";
el.appendChild(layer);
}
});
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", initProgressiveBlur);
} else {
initProgressiveBlur();
}
})();<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Progressive Blur</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h1>Progressive Blur</h1>
<p class="subtitle">Content fades into a smooth blur at the edges</p>
<div class="examples-grid">
<!-- Bottom blur on scrollable list -->
<div class="example-wrapper">
<span class="section-label">Bottom Blur</span>
<div class="outer-border">
<div class="progressive-blur-container">
<div class="scroll-demo">
<div class="demo-card">
<h3>Design System v4.2</h3>
<p>Updated component tokens and added new color primitives for dark mode theming.</p>
<span class="tag">Design</span>
</div>
<div class="demo-card">
<h3>API Rate Limiting</h3>
<p>Implemented sliding window rate limiter with Redis backing store.</p>
<span class="tag">Backend</span>
</div>
<div class="demo-card">
<h3>Motion Library</h3>
<p>Spring-based animation primitives with configurable stiffness and damping.</p>
<span class="tag">Animation</span>
</div>
<div class="demo-card">
<h3>Edge Caching</h3>
<p>Cloudflare Workers KV integration for sub-50ms response times globally.</p>
<span class="tag">Infra</span>
</div>
<div class="demo-card">
<h3>Accessibility Audit</h3>
<p>Full WCAG 2.1 AA compliance pass with automated testing pipeline.</p>
<span class="tag">A11y</span>
</div>
<div class="demo-card">
<h3>Real-time Sync</h3>
<p>WebSocket-based collaborative editing with conflict resolution via CRDTs.</p>
<span class="tag">Feature</span>
</div>
</div>
<div class="progressive-blur bottom">
<div class="progressive-blur-layer"></div>
<div class="progressive-blur-layer"></div>
<div class="progressive-blur-layer"></div>
<div class="progressive-blur-layer"></div>
<div class="progressive-blur-layer"></div>
<div class="progressive-blur-layer"></div>
</div>
</div>
</div>
</div>
<!-- Bottom blur on image/visual -->
<div class="example-wrapper">
<span class="section-label">Image Blur Edge</span>
<div class="outer-border">
<div class="progressive-blur-container">
<div class="image-demo">
<div class="image-demo-content">
<div class="orb"></div>
<p>Visual content fades smoothly</p>
</div>
</div>
<div class="progressive-blur bottom" style="--blur-height: 150px;">
<div class="progressive-blur-layer"></div>
<div class="progressive-blur-layer"></div>
<div class="progressive-blur-layer"></div>
<div class="progressive-blur-layer"></div>
<div class="progressive-blur-layer"></div>
<div class="progressive-blur-layer"></div>
</div>
</div>
</div>
</div>
<!-- Top blur -->
<div class="example-wrapper">
<span class="section-label">Top Blur</span>
<div class="outer-border">
<div class="progressive-blur-container">
<div class="scroll-demo">
<div class="demo-card">
<h3>GraphQL Federation</h3>
<p>Unified schema gateway stitching multiple microservice subgraphs.</p>
<span class="tag">API</span>
</div>
<div class="demo-card">
<h3>Container Queries</h3>
<p>Responsive components that adapt to their container, not the viewport.</p>
<span class="tag">CSS</span>
</div>
<div class="demo-card">
<h3>AI Code Review</h3>
<p>Automated pull request analysis with context-aware suggestions.</p>
<span class="tag">AI</span>
</div>
<div class="demo-card">
<h3>Zero-downtime Deploy</h3>
<p>Blue-green deployment strategy with automatic rollback on health check failure.</p>
<span class="tag">DevOps</span>
</div>
</div>
<div class="progressive-blur top">
<div class="progressive-blur-layer"></div>
<div class="progressive-blur-layer"></div>
<div class="progressive-blur-layer"></div>
<div class="progressive-blur-layer"></div>
<div class="progressive-blur-layer"></div>
<div class="progressive-blur-layer"></div>
</div>
</div>
</div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>import { type CSSProperties, type ReactNode } from "react";
interface ProgressiveBlurProps {
children: ReactNode;
direction?: "top" | "bottom" | "left" | "right";
blurHeight?: number;
layers?: number;
maxBlur?: number;
className?: string;
style?: CSSProperties;
}
export function ProgressiveBlur({
children,
direction = "bottom",
blurHeight = 200,
layers = 6,
maxBlur = 20,
className = "",
style = {},
}: ProgressiveBlurProps) {
const isVertical = direction === "top" || direction === "bottom";
const containerStyle: CSSProperties = {
position: "relative",
overflow: "hidden",
...style,
};
const overlayStyle: CSSProperties = {
position: "absolute",
zIndex: 10,
pointerEvents: "none",
display: "flex",
...(isVertical
? {
left: 0,
right: 0,
height: blurHeight,
flexDirection: direction === "bottom" ? "column" : "column-reverse",
...(direction === "bottom" ? { bottom: 0 } : { top: 0 }),
}
: {
top: 0,
bottom: 0,
width: blurHeight,
flexDirection: direction === "right" ? "row" : "row-reverse",
...(direction === "right" ? { right: 0 } : { left: 0 }),
}),
};
// Exponential blur progression for smooth gradient
const blurValues = Array.from({ length: layers }, (_, i) => {
const t = i / (layers - 1);
return Math.round(maxBlur * Math.pow(t, 2));
});
return (
<div className={className} style={containerStyle}>
{children}
<div style={overlayStyle} aria-hidden="true">
{blurValues.map((blur, i) => (
<div
key={i}
style={{
flex: 1,
backdropFilter: `blur(${blur}px)`,
WebkitBackdropFilter: `blur(${blur}px)`,
}}
/>
))}
</div>
</div>
);
}
// Demo usage
export default function ProgressiveBlurDemo() {
const items = [
{
title: "Design System v4.2",
desc: "Updated component tokens and added new color primitives for dark mode theming.",
tag: "Design",
},
{
title: "API Rate Limiting",
desc: "Implemented sliding window rate limiter with Redis backing store.",
tag: "Backend",
},
{
title: "Motion Library",
desc: "Spring-based animation primitives with configurable stiffness and damping.",
tag: "Animation",
},
{
title: "Edge Caching",
desc: "Cloudflare Workers KV integration for sub-50ms response times globally.",
tag: "Infra",
},
{
title: "Accessibility Audit",
desc: "Full WCAG 2.1 AA compliance pass with automated testing pipeline.",
tag: "A11y",
},
{
title: "Real-time Sync",
desc: "WebSocket-based collaborative editing with conflict resolution via CRDTs.",
tag: "Feature",
},
];
const cardStyle: CSSProperties = {
background: "rgba(255,255,255,0.04)",
border: "1px solid rgba(255,255,255,0.08)",
borderRadius: 12,
padding: "1.5rem",
marginBottom: "1rem",
};
const tagStyle: CSSProperties = {
display: "inline-block",
background: "rgba(99,102,241,0.15)",
color: "#818cf8",
fontSize: "0.75rem",
fontWeight: 600,
padding: "0.25rem 0.75rem",
borderRadius: 999,
marginTop: "0.75rem",
};
return (
<div
style={{
background: "#0a0a0a",
minHeight: "100vh",
fontFamily: "system-ui, -apple-system, sans-serif",
color: "#e2e8f0",
display: "flex",
flexDirection: "column",
alignItems: "center",
padding: "2rem",
gap: "4rem",
}}
>
<h1
style={{
fontSize: "clamp(2rem, 5vw, 3.5rem)",
fontWeight: 800,
letterSpacing: "-0.03em",
background: "linear-gradient(135deg, #e0e7ff 0%, #818cf8 50%, #6366f1 100%)",
WebkitBackgroundClip: "text",
WebkitTextFillColor: "transparent",
backgroundClip: "text",
textAlign: "center",
paddingTop: "3rem",
}}
>
Progressive Blur
</h1>
<p
style={{
textAlign: "center",
color: "rgba(148,163,184,0.8)",
fontSize: "1.125rem",
marginTop: "-2rem",
}}
>
Content fades into a smooth blur at the edges
</p>
<div
style={{
display: "grid",
gridTemplateColumns: "repeat(auto-fit, minmax(320px, 1fr))",
gap: "2rem",
width: "100%",
maxWidth: 900,
}}
>
{/* Bottom blur */}
<div style={{ display: "flex", flexDirection: "column", gap: "0.75rem" }}>
<span
style={{
textAlign: "center",
fontSize: "0.875rem",
fontWeight: 600,
color: "rgba(148,163,184,0.5)",
textTransform: "uppercase" as const,
letterSpacing: "0.1em",
}}
>
Bottom Blur
</span>
<div
style={{
border: "1px solid rgba(255,255,255,0.06)",
borderRadius: 16,
overflow: "hidden",
}}
>
<ProgressiveBlur direction="bottom" style={{ maxWidth: 700 }}>
<div
style={{
height: 400,
overflowY: "auto",
padding: "2rem",
scrollbarWidth: "thin" as const,
scrollbarColor: "rgba(255,255,255,0.1) transparent",
}}
>
{items.map((item) => (
<div key={item.title} style={cardStyle}>
<h3
style={{
fontSize: "1rem",
fontWeight: 600,
color: "#f1f5f9",
marginBottom: "0.5rem",
}}
>
{item.title}
</h3>
<p
style={{
color: "rgba(148,163,184,0.7)",
fontSize: "0.9rem",
lineHeight: 1.6,
}}
>
{item.desc}
</p>
<span style={tagStyle}>{item.tag}</span>
</div>
))}
</div>
</ProgressiveBlur>
</div>
</div>
{/* Image with bottom blur */}
<div style={{ display: "flex", flexDirection: "column", gap: "0.75rem" }}>
<span
style={{
textAlign: "center",
fontSize: "0.875rem",
fontWeight: 600,
color: "rgba(148,163,184,0.5)",
textTransform: "uppercase" as const,
letterSpacing: "0.1em",
}}
>
Image Blur Edge
</span>
<div
style={{
border: "1px solid rgba(255,255,255,0.06)",
borderRadius: 16,
overflow: "hidden",
}}
>
<ProgressiveBlur direction="bottom" blurHeight={150}>
<div
style={{
height: 300,
background:
"linear-gradient(135deg, #1e1b4b 0%, #312e81 30%, #4338ca 60%, #6366f1 100%)",
display: "grid",
placeItems: "center",
}}
>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "1rem",
}}
>
<div
style={{
width: 80,
height: 80,
borderRadius: "50%",
background: "radial-gradient(circle at 30% 30%, #a78bfa, #4338ca)",
boxShadow: "0 0 60px rgba(129,140,248,0.4)",
}}
/>
<p style={{ color: "rgba(255,255,255,0.7)", fontSize: "0.9rem" }}>
Visual content fades smoothly
</p>
</div>
</div>
</ProgressiveBlur>
</div>
</div>
{/* Top blur */}
<div style={{ display: "flex", flexDirection: "column", gap: "0.75rem" }}>
<span
style={{
textAlign: "center",
fontSize: "0.875rem",
fontWeight: 600,
color: "rgba(148,163,184,0.5)",
textTransform: "uppercase" as const,
letterSpacing: "0.1em",
}}
>
Top Blur
</span>
<div
style={{
border: "1px solid rgba(255,255,255,0.06)",
borderRadius: 16,
overflow: "hidden",
}}
>
<ProgressiveBlur direction="top" style={{ maxWidth: 700 }}>
<div
style={{
height: 400,
overflowY: "auto",
padding: "2rem",
scrollbarWidth: "thin" as const,
scrollbarColor: "rgba(255,255,255,0.1) transparent",
}}
>
{items.slice(0, 4).map((item) => (
<div key={item.title} style={cardStyle}>
<h3
style={{
fontSize: "1rem",
fontWeight: 600,
color: "#f1f5f9",
marginBottom: "0.5rem",
}}
>
{item.title}
</h3>
<p
style={{
color: "rgba(148,163,184,0.7)",
fontSize: "0.9rem",
lineHeight: 1.6,
}}
>
{item.desc}
</p>
<span style={tagStyle}>{item.tag}</span>
</div>
))}
</div>
</ProgressiveBlur>
</div>
</div>
</div>
</div>
);
}<script setup>
import { computed } from "vue";
const items = [
{
title: "Design System v4.2",
desc: "Updated component tokens and added new color primitives for dark mode theming.",
tag: "Design",
},
{
title: "API Rate Limiting",
desc: "Implemented sliding window rate limiter with Redis backing store.",
tag: "Backend",
},
{
title: "Motion Library",
desc: "Spring-based animation primitives with configurable stiffness and damping.",
tag: "Animation",
},
{
title: "Edge Caching",
desc: "Cloudflare Workers KV integration for sub-50ms response times globally.",
tag: "Infra",
},
{
title: "Accessibility Audit",
desc: "Full WCAG 2.1 AA compliance pass with automated testing pipeline.",
tag: "A11y",
},
{
title: "Real-time Sync",
desc: "WebSocket-based collaborative editing with conflict resolution via CRDTs.",
tag: "Feature",
},
];
function makeOverlay(direction, blurHeight = 200, layers = 6, maxBlur = 20) {
const isVertical = direction === "top" || direction === "bottom";
const blurValues = Array.from({ length: layers }, (_, i) => {
const t = i / (layers - 1);
return Math.round(maxBlur * Math.pow(t, 2));
});
const style = {
position: "absolute",
zIndex: 10,
pointerEvents: "none",
display: "flex",
};
if (isVertical) {
style.left = "0";
style.right = "0";
style.height = blurHeight + "px";
style.flexDirection = direction === "bottom" ? "column" : "column-reverse";
if (direction === "bottom") style.bottom = "0";
else style.top = "0";
} else {
style.top = "0";
style.bottom = "0";
style.width = blurHeight + "px";
style.flexDirection = direction === "right" ? "row" : "row-reverse";
if (direction === "right") style.right = "0";
else style.left = "0";
}
return { style, blurValues };
}
const bottomOverlay = computed(() => makeOverlay("bottom", 200));
const imageOverlay = computed(() => makeOverlay("bottom", 150));
const topOverlay = computed(() => makeOverlay("top", 200));
</script>
<template>
<div class="page">
<h1 class="title">Progressive Blur</h1>
<p class="subtitle">Content fades into a smooth blur at the edges</p>
<div class="grid">
<!-- Bottom Blur -->
<div class="demo-col">
<span class="label">Bottom Blur</span>
<div class="card-wrap">
<div style="position: relative; overflow: hidden;">
<div class="scroll-area">
<div v-for="item in items" :key="item.title" class="card">
<h3 class="card-title">{{ item.title }}</h3>
<p class="card-desc">{{ item.desc }}</p>
<span class="tag">{{ item.tag }}</span>
</div>
</div>
<div :style="bottomOverlay.style" aria-hidden="true">
<div
v-for="(blur, i) in bottomOverlay.blurValues"
:key="i"
:style="{ flex: '1', backdropFilter: `blur(${blur}px)`, WebkitBackdropFilter: `blur(${blur}px)` }"
/>
</div>
</div>
</div>
</div>
<!-- Image Edge Blur -->
<div class="demo-col">
<span class="label">Image Blur Edge</span>
<div class="card-wrap">
<div style="position: relative; overflow: hidden;">
<div class="gradient-box">
<div class="orb" />
<p class="orb-label">Visual content fades smoothly</p>
</div>
<div :style="imageOverlay.style" aria-hidden="true">
<div
v-for="(blur, i) in imageOverlay.blurValues"
:key="i"
:style="{ flex: '1', backdropFilter: `blur(${blur}px)`, WebkitBackdropFilter: `blur(${blur}px)` }"
/>
</div>
</div>
</div>
</div>
<!-- Top Blur -->
<div class="demo-col">
<span class="label">Top Blur</span>
<div class="card-wrap">
<div style="position: relative; overflow: hidden;">
<div class="scroll-area">
<div v-for="item in items.slice(0, 4)" :key="item.title" class="card">
<h3 class="card-title">{{ item.title }}</h3>
<p class="card-desc">{{ item.desc }}</p>
<span class="tag">{{ item.tag }}</span>
</div>
</div>
<div :style="topOverlay.style" aria-hidden="true">
<div
v-for="(blur, i) in topOverlay.blurValues"
:key="i"
:style="{ flex: '1', backdropFilter: `blur(${blur}px)`, WebkitBackdropFilter: `blur(${blur}px)` }"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
.page {
background: #0a0a0a;
min-height: 100vh;
font-family: system-ui, -apple-system, sans-serif;
color: #e2e8f0;
display: flex;
flex-direction: column;
align-items: center;
padding: 2rem;
gap: 4rem;
}
.title {
font-size: clamp(2rem, 5vw, 3.5rem);
font-weight: 800;
letter-spacing: -0.03em;
background: linear-gradient(135deg, #e0e7ff 0%, #818cf8 50%, #6366f1 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-align: center;
padding-top: 3rem;
}
.subtitle {
text-align: center;
color: rgba(148, 163, 184, 0.8);
font-size: 1.125rem;
margin-top: -2rem;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 2rem;
width: 100%;
max-width: 900px;
}
.demo-col {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.label {
text-align: center;
font-size: 0.875rem;
font-weight: 600;
color: rgba(148, 163, 184, 0.5);
text-transform: uppercase;
letter-spacing: 0.1em;
}
.card-wrap {
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 16px;
overflow: hidden;
}
.scroll-area {
height: 400px;
overflow-y: auto;
padding: 2rem;
scrollbar-width: thin;
scrollbar-color: rgba(255, 255, 255, 0.1) transparent;
}
.card {
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 12px;
padding: 1.5rem;
margin-bottom: 1rem;
}
.card-title {
font-size: 1rem;
font-weight: 600;
color: #f1f5f9;
margin-bottom: 0.5rem;
}
.card-desc {
color: rgba(148, 163, 184, 0.7);
font-size: 0.9rem;
line-height: 1.6;
}
.tag {
display: inline-block;
background: rgba(99, 102, 241, 0.15);
color: #818cf8;
font-size: 0.75rem;
font-weight: 600;
padding: 0.25rem 0.75rem;
border-radius: 999px;
margin-top: 0.75rem;
}
.gradient-box {
height: 300px;
background: linear-gradient(135deg, #1e1b4b 0%, #312e81 30%, #4338ca 60%, #6366f1 100%);
display: grid;
place-items: center;
}
.orb {
width: 80px;
height: 80px;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, #a78bfa, #4338ca);
box-shadow: 0 0 60px rgba(129, 140, 248, 0.4);
margin-bottom: 1rem;
}
.orb-label {
color: rgba(255, 255, 255, 0.7);
font-size: 0.9rem;
}
</style><script>
// ProgressiveBlur component logic
let direction = "bottom";
let blurHeight = 200;
let layers = 6;
let maxBlur = 20;
$: isVertical = direction === "top" || direction === "bottom";
$: overlayStyle = (() => {
let s = "position: absolute; z-index: 10; pointer-events: none; display: flex;";
if (isVertical) {
s += ` left: 0; right: 0; height: ${blurHeight}px;`;
s += ` flex-direction: ${direction === "bottom" ? "column" : "column-reverse"};`;
s += direction === "bottom" ? " bottom: 0;" : " top: 0;";
} else {
s += ` top: 0; bottom: 0; width: ${blurHeight}px;`;
s += ` flex-direction: ${direction === "right" ? "row" : "row-reverse"};`;
s += direction === "right" ? " right: 0;" : " left: 0;";
}
return s;
})();
$: blurValues = Array.from({ length: layers }, (_, i) => {
const t = i / (layers - 1);
return Math.round(maxBlur * Math.pow(t, 2));
});
const items = [
{
title: "Design System v4.2",
desc: "Updated component tokens and added new color primitives for dark mode theming.",
tag: "Design",
},
{
title: "API Rate Limiting",
desc: "Implemented sliding window rate limiter with Redis backing store.",
tag: "Backend",
},
{
title: "Motion Library",
desc: "Spring-based animation primitives with configurable stiffness and damping.",
tag: "Animation",
},
{
title: "Edge Caching",
desc: "Cloudflare Workers KV integration for sub-50ms response times globally.",
tag: "Infra",
},
{
title: "Accessibility Audit",
desc: "Full WCAG 2.1 AA compliance pass with automated testing pipeline.",
tag: "A11y",
},
{
title: "Real-time Sync",
desc: "WebSocket-based collaborative editing with conflict resolution via CRDTs.",
tag: "Feature",
},
];
</script>
<div class="page">
<h1 class="title">Progressive Blur</h1>
<p class="subtitle">Content fades into a smooth blur at the edges</p>
<div class="grid">
<!-- Bottom Blur -->
<div class="demo-col">
<span class="label">Bottom Blur</span>
<div class="card-wrap">
<div style="position: relative; overflow: hidden;">
<div class="scroll-area">
{#each items as item}
<div class="card">
<h3 class="card-title">{item.title}</h3>
<p class="card-desc">{item.desc}</p>
<span class="tag">{item.tag}</span>
</div>
{/each}
</div>
<div style={overlayStyle} aria-hidden="true">
{#each blurValues as blur}
<div style="flex: 1; backdrop-filter: blur({blur}px); -webkit-backdrop-filter: blur({blur}px);" />
{/each}
</div>
</div>
</div>
</div>
<!-- Image Edge Blur -->
<div class="demo-col">
<span class="label">Image Blur Edge</span>
<div class="card-wrap">
<div style="position: relative; overflow: hidden;">
<div class="gradient-box">
<div class="orb" />
<p class="orb-label">Visual content fades smoothly</p>
</div>
<!-- overlay with smaller blurHeight=150 -->
<div
style="position: absolute; z-index: 10; pointer-events: none; display: flex; left: 0; right: 0; height: 150px; flex-direction: column; bottom: 0;"
aria-hidden="true"
>
{#each blurValues as blur}
<div style="flex: 1; backdrop-filter: blur({blur}px); -webkit-backdrop-filter: blur({blur}px);" />
{/each}
</div>
</div>
</div>
</div>
<!-- Top Blur -->
<div class="demo-col">
<span class="label">Top Blur</span>
<div class="card-wrap">
<div style="position: relative; overflow: hidden;">
<div class="scroll-area">
{#each items.slice(0, 4) as item}
<div class="card">
<h3 class="card-title">{item.title}</h3>
<p class="card-desc">{item.desc}</p>
<span class="tag">{item.tag}</span>
</div>
{/each}
</div>
<!-- overlay top direction -->
<div
style="position: absolute; z-index: 10; pointer-events: none; display: flex; left: 0; right: 0; height: 200px; flex-direction: column-reverse; top: 0;"
aria-hidden="true"
>
{#each blurValues as blur}
<div style="flex: 1; backdrop-filter: blur({blur}px); -webkit-backdrop-filter: blur({blur}px);" />
{/each}
</div>
</div>
</div>
</div>
</div>
</div>
<style>
:global(*, *::before, *::after) { box-sizing: border-box; margin: 0; padding: 0; }
:global(body) { background: #0a0a0a; }
.page {
background: #0a0a0a;
min-height: 100vh;
font-family: system-ui, -apple-system, sans-serif;
color: #e2e8f0;
display: flex;
flex-direction: column;
align-items: center;
padding: 2rem;
gap: 4rem;
}
.title {
font-size: clamp(2rem, 5vw, 3.5rem);
font-weight: 800;
letter-spacing: -0.03em;
background: linear-gradient(135deg, #e0e7ff 0%, #818cf8 50%, #6366f1 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-align: center;
padding-top: 3rem;
}
.subtitle {
text-align: center;
color: rgba(148, 163, 184, 0.8);
font-size: 1.125rem;
margin-top: -2rem;
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 2rem;
width: 100%;
max-width: 900px;
}
.demo-col {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.label {
text-align: center;
font-size: 0.875rem;
font-weight: 600;
color: rgba(148, 163, 184, 0.5);
text-transform: uppercase;
letter-spacing: 0.1em;
}
.card-wrap {
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 16px;
overflow: hidden;
}
.scroll-area {
height: 400px;
overflow-y: auto;
padding: 2rem;
scrollbar-width: thin;
scrollbar-color: rgba(255, 255, 255, 0.1) transparent;
}
.card {
background: rgba(255, 255, 255, 0.04);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 12px;
padding: 1.5rem;
margin-bottom: 1rem;
}
.card-title {
font-size: 1rem;
font-weight: 600;
color: #f1f5f9;
margin-bottom: 0.5rem;
}
.card-desc {
color: rgba(148, 163, 184, 0.7);
font-size: 0.9rem;
line-height: 1.6;
}
.tag {
display: inline-block;
background: rgba(99, 102, 241, 0.15);
color: #818cf8;
font-size: 0.75rem;
font-weight: 600;
padding: 0.25rem 0.75rem;
border-radius: 999px;
margin-top: 0.75rem;
}
.gradient-box {
height: 300px;
background: linear-gradient(135deg, #1e1b4b 0%, #312e81 30%, #4338ca 60%, #6366f1 100%);
display: grid;
place-items: center;
}
.orb {
width: 80px;
height: 80px;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, #a78bfa, #4338ca);
box-shadow: 0 0 60px rgba(129, 140, 248, 0.4);
margin-bottom: 1rem;
}
.orb-label {
color: rgba(255, 255, 255, 0.7);
font-size: 0.9rem;
}
</style>Progressive Blur
A gradient blur overlay that progressively blurs content at an edge, creating an elegant fade-to-blur effect. Perfect for truncating content, creating depth, or masking scroll areas.
How it works
- A pseudo-element or overlay div is placed over the content edge
- CSS
backdrop-filter: blur()applies the blur effect - A CSS
mask-imagelinear gradient controls where the blur is visible - The gradient goes from transparent (no blur) to opaque (full blur)
Customization
- Change
directionto blur from top, bottom, left, or right - Adjust
--blur-intensityto control the maximum blur amount - Modify
--blur-heightto control how much area the blur covers - Works on any scrollable or overflowing content area
When to use it
- Bottom of scrollable content areas (read-more pattern)
- Image edges that fade into the background
- Long lists or feeds with a soft content boundary
- Hero sections with content fading into a solid background