style: replace dotted chart background with grid line pattern
Swap the old dot grid overlay inside the Recharts SVG for a GridPattern component rendered behind the chart card. Uses a vertical mask gradient to fade edges for a cleaner look.
This commit is contained in:
@@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
|
||||
### Improved
|
||||
|
||||
- **Refreshed chart background.** The dashboard chart now has a clean grid line pattern instead of the old dotted background, with a soft fade at the top and bottom edges for a more polished look.
|
||||
- **Smoother loading transitions.** When your data finishes loading, the page now fades in smoothly instead of appearing all at once. This applies across Dashboard, Journeys, Funnels, Uptime, Settings, Notifications, and shared dashboards. If your data was already cached from a previous visit, it still loads instantly with no animation — the fade only kicks in when you're actually waiting for fresh data.
|
||||
- **Faster tab switching across the board.** Switching between Settings, Funnels, Uptime, and other tabs now shows your data instantly instead of flashing a loading skeleton every time. Previously visited tabs remember their data and show it right away, while quietly refreshing in the background so you always see the latest numbers without the wait.
|
||||
- **Smoother loading on the Journeys page.** The Journeys tab now shows a polished skeleton placeholder while data loads, matching the loading experience on other tabs.
|
||||
|
||||
@@ -11,6 +11,7 @@ import { Checkbox } from '@ciphera-net/ui'
|
||||
import { ArrowUpRight, ArrowDownRight } from '@phosphor-icons/react'
|
||||
import { motion } from 'framer-motion'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { GridPattern } from '@/components/ui/grid-pattern'
|
||||
|
||||
const ANNOTATION_COLORS: Record<string, string> = {
|
||||
deploy: '#3b82f6',
|
||||
@@ -338,7 +339,14 @@ export default function Chart({
|
||||
|
||||
return (
|
||||
<div ref={chartContainerRef} className="relative">
|
||||
<Card className="w-full overflow-hidden rounded-2xl">
|
||||
<Card className="w-full overflow-hidden rounded-2xl relative">
|
||||
<GridPattern
|
||||
width={30}
|
||||
height={30}
|
||||
x={-1}
|
||||
y={-1}
|
||||
className="fill-neutral-400/5 stroke-neutral-400/10 dark:fill-neutral-500/5 dark:stroke-neutral-500/10 [mask-image:linear-gradient(to_bottom,transparent,white_30%,white_70%,transparent)]"
|
||||
/>
|
||||
<CardHeader className="p-0 mb-0">
|
||||
{/* Metrics Grid - 21st.dev style */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 grow w-full">
|
||||
@@ -462,9 +470,6 @@ export default function Chart({
|
||||
style={{ overflow: 'visible' }}
|
||||
>
|
||||
<defs>
|
||||
<pattern id="dotGrid" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
|
||||
<circle cx="10" cy="10" r="1" fill="var(--chart-grid)" fillOpacity="1" />
|
||||
</pattern>
|
||||
<filter id="lineShadow" x="-100%" y="-100%" width="300%" height="300%">
|
||||
<feDropShadow
|
||||
dx="4"
|
||||
@@ -501,15 +506,6 @@ export default function Chart({
|
||||
|
||||
<ChartTooltip content={<CustomTooltip metric={metric} />} cursor={{ strokeDasharray: '3 3', stroke: '#9ca3af' }} />
|
||||
|
||||
{/* Background dot grid pattern */}
|
||||
<rect
|
||||
x="60px"
|
||||
y="-20px"
|
||||
width="calc(100% - 75px)"
|
||||
height="calc(100% - 10px)"
|
||||
fill="url(#dotGrid)"
|
||||
style={{ pointerEvents: 'none' }}
|
||||
/>
|
||||
|
||||
{/* Annotation reference lines */}
|
||||
{visibleAnnotationMarkers.map((marker) => {
|
||||
|
||||
72
components/ui/grid-pattern.tsx
Normal file
72
components/ui/grid-pattern.tsx
Normal file
@@ -0,0 +1,72 @@
|
||||
import { useId } from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
interface GridPatternProps {
|
||||
width?: number;
|
||||
height?: number;
|
||||
x?: number;
|
||||
y?: number;
|
||||
squares?: Array<[x: number, y: number]>;
|
||||
strokeDasharray?: string;
|
||||
className?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
function GridPattern({
|
||||
width = 40,
|
||||
height = 40,
|
||||
x = -1,
|
||||
y = -1,
|
||||
strokeDasharray = "0",
|
||||
squares,
|
||||
className,
|
||||
...props
|
||||
}: GridPatternProps) {
|
||||
const id = useId();
|
||||
|
||||
return (
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className={cn(
|
||||
"pointer-events-none absolute inset-0 h-full w-full fill-gray-400/30 stroke-gray-400/30",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<defs>
|
||||
<pattern
|
||||
id={id}
|
||||
width={width}
|
||||
height={height}
|
||||
patternUnits="userSpaceOnUse"
|
||||
x={x}
|
||||
y={y}
|
||||
>
|
||||
<path
|
||||
d={`M.5 ${height}V.5H${width}`}
|
||||
fill="none"
|
||||
strokeDasharray={strokeDasharray}
|
||||
/>
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" strokeWidth={0} fill={`url(#${id})`} />
|
||||
{squares && (
|
||||
<svg x={x} y={y} className="overflow-visible">
|
||||
{squares.map(([x, y]) => (
|
||||
<rect
|
||||
strokeWidth="0"
|
||||
key={`${x}-${y}`}
|
||||
width={width - 1}
|
||||
height={height - 1}
|
||||
x={x * width + 1}
|
||||
y={y * height + 1}
|
||||
/>
|
||||
))}
|
||||
</svg>
|
||||
)}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export { GridPattern };
|
||||
Reference in New Issue
Block a user