feat(canvas): replace sidebar fade with progressive blur

This commit is contained in:
2026-04-11 07:25:27 +02:00
parent 9732022461
commit f3dcaf89f2
3 changed files with 123 additions and 10 deletions

View File

@@ -22,6 +22,7 @@
"hooks": "@/hooks" "hooks": "@/hooks"
}, },
"registries": { "registries": {
"@tool-ui": "https://www.tool-ui.com/r/{name}.json" "@tool-ui": "https://www.tool-ui.com/r/{name}.json",
"@magicui": "https://magicui.design/r/{name}"
} }
} }

View File

@@ -31,6 +31,7 @@ import {
} from "lucide-react"; } from "lucide-react";
import { CanvasUserMenu } from "@/components/canvas/canvas-user-menu"; import { CanvasUserMenu } from "@/components/canvas/canvas-user-menu";
import { ProgressiveBlur } from "@/components/ui/progressive-blur";
import { useAuthQuery } from "@/hooks/use-auth-query"; import { useAuthQuery } from "@/hooks/use-auth-query";
import { api } from "@/convex/_generated/api"; import { api } from "@/convex/_generated/api";
import type { Id } from "@/convex/_generated/dataModel"; import type { Id } from "@/convex/_generated/dataModel";
@@ -240,15 +241,13 @@ export default function CanvasSidebar({
</> </>
)} )}
</div> </div>
<div <div aria-hidden="true">
aria-hidden="true" <ProgressiveBlur
className={cn( position="bottom"
"pointer-events-none absolute inset-x-0 bottom-0 z-10", height={railMode ? "4rem" : "6rem"}
railMode blurLevels={[0.5, 1, 2, 4, 8, 12, 16, 24]}
? "h-16 bg-gradient-to-t from-black via-black/80 to-transparent" />
: "h-24 bg-gradient-to-t from-black via-black/80 to-transparent", </div>
)}
/>
</div> </div>
<div className="relative z-20 bg-background"> <div className="relative z-20 bg-background">

View File

@@ -0,0 +1,113 @@
"use client"
import React from "react"
import { cn } from "@/lib/utils"
export interface ProgressiveBlurProps {
className?: string
height?: string
position?: "top" | "bottom" | "both"
blurLevels?: number[]
children?: React.ReactNode
}
export function ProgressiveBlur({
className,
height = "30%",
position = "bottom",
blurLevels = [0.5, 1, 2, 4, 8, 16, 32, 64],
}: ProgressiveBlurProps) {
// Create array with length equal to blurLevels.length - 2 (for before/after pseudo elements)
const divElements = Array(blurLevels.length - 2).fill(null)
return (
<div
className={cn(
"gradient-blur pointer-events-none absolute inset-x-0 z-10",
className,
position === "top"
? "top-0"
: position === "bottom"
? "bottom-0"
: "inset-y-0"
)}
style={{
height: position === "both" ? "100%" : height,
}}
>
{/* First blur layer (pseudo element) */}
<div
className="absolute inset-0"
style={{
zIndex: 1,
backdropFilter: `blur(${blurLevels[0]}px)`,
WebkitBackdropFilter: `blur(${blurLevels[0]}px)`,
maskImage:
position === "bottom"
? `linear-gradient(to bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 12.5%, rgba(0,0,0,1) 25%, rgba(0,0,0,0) 37.5%)`
: position === "top"
? `linear-gradient(to top, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 12.5%, rgba(0,0,0,1) 25%, rgba(0,0,0,0) 37.5%)`
: `linear-gradient(rgba(0,0,0,0) 0%, rgba(0,0,0,1) 5%, rgba(0,0,0,1) 95%, rgba(0,0,0,0) 100%)`,
WebkitMaskImage:
position === "bottom"
? `linear-gradient(to bottom, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 12.5%, rgba(0,0,0,1) 25%, rgba(0,0,0,0) 37.5%)`
: position === "top"
? `linear-gradient(to top, rgba(0,0,0,0) 0%, rgba(0,0,0,1) 12.5%, rgba(0,0,0,1) 25%, rgba(0,0,0,0) 37.5%)`
: `linear-gradient(rgba(0,0,0,0) 0%, rgba(0,0,0,1) 5%, rgba(0,0,0,1) 95%, rgba(0,0,0,0) 100%)`,
}}
/>
{/* Middle blur layers */}
{divElements.map((_, index) => {
const blurIndex = index + 1
const startPercent = blurIndex * 12.5
const midPercent = (blurIndex + 1) * 12.5
const endPercent = (blurIndex + 2) * 12.5
const maskGradient =
position === "bottom"
? `linear-gradient(to bottom, rgba(0,0,0,0) ${startPercent}%, rgba(0,0,0,1) ${midPercent}%, rgba(0,0,0,1) ${endPercent}%, rgba(0,0,0,0) ${endPercent + 12.5}%)`
: position === "top"
? `linear-gradient(to top, rgba(0,0,0,0) ${startPercent}%, rgba(0,0,0,1) ${midPercent}%, rgba(0,0,0,1) ${endPercent}%, rgba(0,0,0,0) ${endPercent + 12.5}%)`
: `linear-gradient(rgba(0,0,0,0) 0%, rgba(0,0,0,1) 5%, rgba(0,0,0,1) 95%, rgba(0,0,0,0) 100%)`
return (
<div
key={`blur-${index}`}
className="absolute inset-0"
style={{
zIndex: index + 2,
backdropFilter: `blur(${blurLevels[blurIndex]}px)`,
WebkitBackdropFilter: `blur(${blurLevels[blurIndex]}px)`,
maskImage: maskGradient,
WebkitMaskImage: maskGradient,
}}
/>
)
})}
{/* Last blur layer (pseudo element) */}
<div
className="absolute inset-0"
style={{
zIndex: blurLevels.length,
backdropFilter: `blur(${blurLevels[blurLevels.length - 1]}px)`,
WebkitBackdropFilter: `blur(${blurLevels[blurLevels.length - 1]}px)`,
maskImage:
position === "bottom"
? `linear-gradient(to bottom, rgba(0,0,0,0) 87.5%, rgba(0,0,0,1) 100%)`
: position === "top"
? `linear-gradient(to top, rgba(0,0,0,0) 87.5%, rgba(0,0,0,1) 100%)`
: `linear-gradient(rgba(0,0,0,0) 0%, rgba(0,0,0,1) 5%, rgba(0,0,0,1) 95%, rgba(0,0,0,0) 100%)`,
WebkitMaskImage:
position === "bottom"
? `linear-gradient(to bottom, rgba(0,0,0,0) 87.5%, rgba(0,0,0,1) 100%)`
: position === "top"
? `linear-gradient(to top, rgba(0,0,0,0) 87.5%, rgba(0,0,0,1) 100%)`
: `linear-gradient(rgba(0,0,0,0) 0%, rgba(0,0,0,1) 5%, rgba(0,0,0,1) 95%, rgba(0,0,0,0) 100%)`,
}}
/>
</div>
)
}