Files
Danny White ed8ac5bb90 chore(design-system): preserve icon fill or stroke (#43417)
## What kind of change does this PR introduce?

Bug fix: custom icons now respect their source SVG’s stroke/fill and
stroke-width instead of always getting a global stroke.

## What is the current behavior?

Every custom icon gets `stroke="currentColor"` and `strokeWidth={2}`
from `createSupabaseIcon`, so fill-only logos get an extra stroke and
icons designed at `stroke-width="1"` (e.g. postgres, auth) render too
thick. Call sites and the design-system grid needed workarounds
(`strokeWidth={0}`, `FILL_ONLY_ICONS`, `STROKE_WIDTH_FROM_SOURCE`).

## What is the new behavior?

The icon build reads root SVG attributes (`fill`, `stroke`,
`stroke-width`, etc.) and passes them as per-icon defaults. Fill-only
icons (chatgpt, claude, axiom, last9) have `stroke="none"` in source and
render with no stroke; stroke icons keep their source stroke-width (e.g.
postgres at 1). No `strokeWidth={0}` or allowlists needed at call sites
or in the icon grid.

| Before | After |
| --- | --- |
| <img width="1826" height="552" alt="CleanShot 2026-03-05 at 10 53
31@2x-3D63CEF0-1132-44F5-A382-730346432F1E"
src="https://github.com/user-attachments/assets/9caf73fa-351b-4ea2-a420-b3339f934742"
/> | <img width="1814" height="552" alt="CleanShot 2026-03-05 at 10 53
40@2x-514FC8BD-D30F-4D23-B209-0ECD6D13A184"
src="https://github.com/user-attachments/assets/936ea5a5-dae0-413f-8545-b90a502cea69"
/> |

## Additional context

- Added `stroke="none"` to the four fill-only source SVGs;
sentry/grafana/otlp/datadog already had `stroke-width="0"`.
- `createSupabaseIcon` only applies `color`/`strokeWidth` when the
consumer passes them, so `svgDefaults` from the build are used
otherwise.
- Removed workarounds from `icons.tsx` and GuidesSidebar; updated
icons.mdx (defaults + fill-only guidance).
2026-03-09 10:26:59 +11:00

122 lines
3.1 KiB
TypeScript

import { useState, useEffect } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import { cn } from 'ui'
import { Datadog, Grafana, Sentry } from 'icons'
import { BracesIcon } from 'lucide-react'
export const AnimatedLogos = () => {
const [currIndex, setCurrIndex] = useState(0)
const timer = 2500
const iconSize = 36
const logos = [
{
id: 'datadog',
name: 'Datadog',
icon: <Datadog fill="currentColor" size={iconSize} />,
},
{
id: 'loki',
name: 'Loki',
icon: <Grafana fill="currentColor" size={iconSize} />,
},
{ id: 'https', name: 'HTTPS', icon: <BracesIcon size={iconSize} /> },
{
id: 'sentry',
name: 'Sentry',
icon: <Sentry fill="currentColor" size={iconSize} />,
},
]
useEffect(() => {
const interval = setInterval(() => {
setCurrIndex((prev) => (prev + 1) % logos.length)
}, timer)
return () => clearInterval(interval)
}, [logos.length])
const getPreviousIndex = () => (currIndex - 1 + logos.length) % logos.length
const getNextIndex = () => (currIndex + 1) % logos.length
const getPosition = (index: number) => {
if (index === currIndex) return 'center'
if (index === getPreviousIndex()) return 'left'
if (index === getNextIndex()) return 'right'
return 'hidden'
}
const logoVariants = {
hidden: {
x: 'calc(-50% + 120px)',
y: '-50%',
scale: 0.6,
opacity: 0,
filter: 'blur(1px)',
},
right: {
x: 'calc(-50% + 80px)',
y: '-50%',
scale: 0.8,
opacity: 0.5,
zIndex: 2,
filter: 'blur(1px)',
},
center: {
x: '-50%',
y: '-50%',
scale: 1,
opacity: 1,
zIndex: 3,
filter: 'blur(0px)',
},
left: {
x: 'calc(-50% - 80px)',
y: '-50%',
scale: 0.8,
opacity: 0.5,
zIndex: 2,
filter: 'blur(1px)',
},
exit: {
x: 'calc(-50% - 120px)',
y: '-50%',
scale: 0.6,
opacity: 0,
filter: 'blur(1px)',
},
}
const visibleIndices = [getPreviousIndex(), currIndex, getNextIndex()]
return (
<div className="relative w-48 h-32 mx-auto mb-8 overflow-hidden">
<AnimatePresence initial={false}>
{logos.map((logo, index) => {
if (!visibleIndices.includes(index)) return null
const position = getPosition(index)
const isCenter = index === currIndex
return (
<motion.div
key={logo.id}
className={cn(
'absolute top-1/2 left-1/2 flex items-center justify-center rounded-lg',
isCenter ? 'w-24 h-24' : 'w-20 h-20'
)}
variants={logoVariants}
initial="hidden"
animate={position}
exit="exit"
transition={{ duration: 0.5, ease: 'easeInOut' }}
>
<span>{logo.icon}</span>
</motion.div>
)
})}
</AnimatePresence>
<div className="absolute -inset-4 bg-gradient-to-r from-background-surface-75 via-transparent to-background-surface-75 z-40" />
</div>
)
}