Files
supabase/apps/docs/features/ui/IconLink.tsx
T
Danny White 4bda3bfe72 refactor(docs): unify homepage icon link tiles (#48317)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Docs UI refactor.

## What is the current behavior?

Framework quickstarts and several other homepage grids used `IconPanel`
/ `IconPanelWithIconPicker`, which brought redundant tooltips
(duplicating the visible title), inconsistent hover treatment, and
circular icon tiles that didn't match the intended squarish look.

| Before |
| --- |
| <img width="1744" height="626" alt="CleanShot 2026-07-24 at 14 29
44@2x"
src="https://github.com/user-attachments/assets/1e145be2-19c9-4bd0-bf06-17516acbc774"
/> |

## What is the new behavior?

Introduces a shared `IconLink` used across the docs homepage (and
reference index) so icon+label tiles share the same composition and
hover: icon tile fill matches the surrounding surface (`surface-100`),
with a stronger border on hover, and the row uses `hover:bg-accent`.
Framework quickstarts keep the larger tile size; other sections keep the
smaller size. Also aligns Explore more GlassPanels with Build your
backend by using the same bordered background treatment.

| After |
| --- |
| <img width="2478" height="906" alt="CleanShot 2026-07-24 at 15 09
48@2x"
src="https://github.com/user-attachments/assets/dd516845-c162-47e4-8b86-9a7fa0e73290"
/> |

## Additional context

Removes the unused `IconPanelWithIconPicker` wrapper now that
homepage/reference consumers go through `IconLink`.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added reusable `IconLink` UI for consistent docs tiles, including
`IconLinkList`, menu icon, and light/dark icon rendering.
* Introduced `FrameworkQuickstarts` to generate quickstart links based
on feature-flag-enabled SDKs.
* **UI / Improvements**
* Updated the docs home and API reference pages to use the new
list-based icon-link layout.
* Refreshed migration guides and “Explore more”/self-hosting sections
(including accessibility and updated CTA text).
* **Bug Fixes**
  * Migration guide items now omit entries missing required details.
* **Chores / Cleanup**
* Removed the older icon panel picker-based UI component and its usage.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-07-28 15:12:13 +10:00

112 lines
2.4 KiB
TypeScript

import MenuIconPicker from '~/components/Navigation/NavigationMenu/MenuIconPicker'
import Link from 'next/link'
import { type ReactNode } from 'react'
import { cn } from 'ui'
const sizeStyles = {
sm: {
tile: 'h-10 w-10',
image: 'w-5',
},
lg: {
tile: 'h-10 w-10 sm:h-16 sm:w-16',
image: 'w-5 sm:w-8',
},
} as const
export type IconLinkSize = keyof typeof sizeStyles
export type IconLinkItem = {
title: string
href: string
icon: ReactNode
className?: string
}
export function IconLink({
href,
title,
icon,
size = 'sm',
className,
}: {
href: string
title: string
icon: ReactNode
size?: IconLinkSize
className?: string
}) {
return (
<Link
href={href}
className={cn(
'group relative -m-3 flex items-center gap-3 rounded-xl p-3 no-underline transition-colors hover:bg-accent',
className
)}
>
<div
className={cn(
'flex shrink-0 items-center justify-center overflow-hidden rounded-lg border bg-surface-100 transition-colors group-hover:border-strong',
sizeStyles[size].tile
)}
>
{icon}
</div>
<span className="text-base text-foreground">{title}</span>
</Link>
)
}
export function IconLinkList({
items,
labelledBy,
className,
itemClassName = 'col-span-6 md:col-span-4',
size = 'sm',
}: {
items: IconLinkItem[]
labelledBy?: string
className?: string
itemClassName?: string
size?: IconLinkSize
}) {
return (
<ul className={cn('grid grid-cols-12 gap-6', className)} aria-labelledby={labelledBy}>
{items.map((item) => (
<li key={item.href} className={cn(itemClassName, item.className)}>
<IconLink href={item.href} title={item.title} icon={item.icon} size={size} />
</li>
))}
</ul>
)
}
export function IconLinkImage({
path,
hasLightIcon,
size = 'sm',
}: {
path: string
hasLightIcon?: boolean
size?: IconLinkSize
}) {
const imgClassName = sizeStyles[size].image
return (
<>
{hasLightIcon ? (
<img src={`${path}-light.svg`} alt="" className={cn(imgClassName, 'dark:hidden')} />
) : null}
<img
src={`${path}.svg`}
alt=""
className={cn(imgClassName, hasLightIcon && 'hidden dark:block')}
/>
</>
)
}
export function IconLinkMenuIcon({ icon }: { icon: string }) {
return <MenuIconPicker icon={icon} width={18} height={18} />
}