mirror of
https://github.com/supabase/supabase.git
synced 2026-09-22 13:37:53 +08:00
a31ca2bad0
## What kind of change does this PR introduce? Bug fix and design-system update. Resolves DEPR-316. Follow-up rename (`text-brand` → `text-primary`) is in a dedicated PR (https://github.com/supabase/supabase/pull/50564) stacked on this one. ## What is the current behavior? `text-brand` resolves to the canonical bright brand green in places that need readable text, which fails WCAG AA on light surfaces. A separate `text-brand-display` token adds another green for large type. ## What is the new behavior? - `text-brand` maps to accessible `--primary` (light mode darkened to meet ~4.5:1 AA) - `--hue` / `--primary-hue` aligned to 157.5 - `text-brand-display` removed; former display callsites use `text-brand` - Bright fills/borders stay on `brand-default` - Design-system colour and typography docs updated | Before | After | | --- | --- | | <img width="514" height="112" alt="CleanShot 2026-09-02 at 11 13 09@2x" src="https://github.com/user-attachments/assets/4e0138a9-a32d-4e4c-a426-90736706e1e7" /> | <img width="512" height="138" alt="CleanShot 2026-09-21 at 11 42 05@2x" src="https://github.com/user-attachments/assets/164cc5b1-a0c5-4e93-95f1-80016641a114" /> | | <img width="864" height="266" alt="CleanShot 2026-09-02 at 11 13 53@2x" src="https://github.com/user-attachments/assets/3c1ca53f-bf9e-431e-bc15-816b4a275b8e" /> | <img width="882" height="248" alt="CleanShot 2026-09-21 at 11 41 37@2x" src="https://github.com/user-attachments/assets/24828e7b-ed6b-44cb-b9dc-becc3398bdfc" /> | | <img width="782" height="692" alt="CleanShot 2026-09-02 at 11 16 30@2x" src="https://github.com/user-attachments/assets/fc871977-77bc-47fb-9e0e-9284e0ecd5cc" /> | <img width="730" height="690" alt="CleanShot 2026-09-21 at 11 42 52@2x" src="https://github.com/user-attachments/assets/bf479515-d5f9-471e-b82d-f097c0f4b56c" /> | | <img width="480" height="306" alt="CleanShot 2026-09-02 at 11 18 53@2x" src="https://github.com/user-attachments/assets/03f341f4-f02e-44f8-a2b2-8c31670d0427" /> | <img width="470" height="300" alt="CleanShot 2026-09-21 at 11 43 19@2x" src="https://github.com/user-attachments/assets/9df18217-d5e6-48b8-ba0b-579d2664b94b" /> | | <img width="960" height="300" alt="CleanShot 2026-09-02 at 11 32 04@2x" src="https://github.com/user-attachments/assets/6b1d9373-7a71-4247-81ff-26441604b09d" /> | <img width="980" height="306" alt="CleanShot 2026-09-21 at 11 44 13@2x" src="https://github.com/user-attachments/assets/41ad4784-02ec-4b29-b860-32af9fa79aa8" /> | | <img width="924" height="214" alt="CleanShot 2026-09-02 at 11 34 44@2x" src="https://github.com/user-attachments/assets/1de661fe-c7b6-499b-a94f-e4737436ec79" /> | <img width="752" height="162" alt="CleanShot 2026-09-21 at 11 44 56@2x" src="https://github.com/user-attachments/assets/1811890f-0660-4445-84e9-447720954fa1" /> | ## To test Test each callsite **in light mode** (dark mode is largely unchanged). ### WWW - [Homepage](https://zone-www-dot-com-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/): “Scale to millions” uses readable brand text (display token is gone) - [Careers](https://zone-www-dot-com-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/careers): small “Careers” eyebrow readable; green dividers stay bright `brand-default` - [Contact](https://zone-www-dot-com-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/contact-us): email / policy links use readable brand text - [Regions](https://zone-www-dot-com-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/regions): “Ask about early access to BYOC” readable ### Docs - [Docs homepage](https://docs-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/docs): “DOCS” wordmark and resource links readable - [Database overview](https://docs-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/docs/guides/database/overview): nav / footer brand links readable - [JavaScript reference](https://docs-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/docs/reference/javascript/introduction): active sidebar treatment readable ### Design system - [Typography](https://design-system-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/docs/typography): documents `text-brand` only (no display) - [Colour usage](https://design-system-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/docs/color-usage): `text-brand` vs `bg-brand-default` - [Design-system homepage](https://design-system-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/): brand text examples across themes ### Studio - [Auth providers](https://studio-staging-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/dashboard/project/_/auth/providers): enabled provider badge text readable; status dot stays bright - [Database policies](https://studio-staging-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/dashboard/project/_/database/policies?new=true): template hover text more legible - [Database connections](https://studio-staging-git-dnywh-depr-316-brand-text-tokens-supabase.vercel.app/dashboard/project/_/observability/connections): “Live” status readable; animated dot stays bright green --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
199 lines
7.3 KiB
TypeScript
199 lines
7.3 KiB
TypeScript
import { getCustomContent } from '~/lib/custom-content/getCustomContent'
|
|
import { useIsLoggedIn, useIsUserLoading } from 'common'
|
|
import { AnimatePresence, domAnimation, LazyMotion, m } from 'framer-motion'
|
|
import { X } from 'lucide-react'
|
|
import Image from 'next/image'
|
|
import Link from 'next/link'
|
|
import { usePathname } from 'next/navigation'
|
|
import { Dispatch, Fragment, SetStateAction, useEffect } from 'react'
|
|
import { useKey } from 'react-use'
|
|
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Button, cn } from 'ui'
|
|
import { ThemeToggle } from 'ui-patterns/ThemeToggle'
|
|
|
|
import type { DropdownMenuItem } from '../Navigation.types'
|
|
import { MenuItem, useActiveMenuLabel } from './GlobalNavigationMenu'
|
|
import { GLOBAL_MENU_ITEMS } from './NavigationMenu.constants'
|
|
|
|
const DEFAULT_EASE = [0.24, 0.25, 0.05, 1]
|
|
|
|
const container = {
|
|
hidden: { opacity: 0 },
|
|
show: { opacity: 1, transition: { duration: 0.15, staggerChildren: 0.05, ease: DEFAULT_EASE } },
|
|
exit: { opacity: 0, transition: { duration: 0.15 } },
|
|
}
|
|
|
|
const listItem = {
|
|
hidden: { opacity: 0, y: 10 },
|
|
show: { opacity: 1, y: 0, transition: { duration: 0.25, ease: DEFAULT_EASE } },
|
|
exit: { opacity: 0, transition: { duration: 0.05 } },
|
|
}
|
|
|
|
const itemClassName =
|
|
'block py-2 pl-2 pr-3.5 text-sm text-foreground-light hover:bg-surface-200 focus-ring rounded-sm'
|
|
|
|
const AccordionMenuItem = ({ section }: { section: DropdownMenuItem[] }) => {
|
|
const activeLabel = useActiveMenuLabel(GLOBAL_MENU_ITEMS)
|
|
|
|
return section[0].menuItems ? (
|
|
<AccordionItem
|
|
value={section[0].label}
|
|
className={cn(
|
|
'relative',
|
|
activeLabel === section[0].label && 'text-foreground!',
|
|
itemClassName
|
|
)}
|
|
>
|
|
<AccordionTrigger className="py-1">{section[0].label}</AccordionTrigger>
|
|
<AccordionContent>
|
|
{section[0].menuItems?.map((menuItem, menuItemIndex) => (
|
|
<Fragment key={`desktop-docs-menu-section-${menuItemIndex}`}>
|
|
{menuItem
|
|
.filter((item) => item.enabled !== false)
|
|
.map((item) =>
|
|
!item.href ? (
|
|
<div
|
|
key={item.label}
|
|
className="font-mono tracking-wider flex items-center text-foreground-muted text-xs uppercase rounded-md p-2 leading-none"
|
|
>
|
|
{item.label}
|
|
</div>
|
|
) : (
|
|
<MenuItem
|
|
key={item.label}
|
|
href={item.href}
|
|
title={item.label}
|
|
community={item.community}
|
|
new={item.new}
|
|
icon={item.icon}
|
|
/>
|
|
)
|
|
)}
|
|
</Fragment>
|
|
))}
|
|
</AccordionContent>
|
|
</AccordionItem>
|
|
) : (
|
|
<Link
|
|
href={section[0].href || '#'}
|
|
className={cn(activeLabel === section[0].label && 'text-foreground!', itemClassName)}
|
|
>
|
|
{section[0].label}
|
|
</Link>
|
|
)
|
|
}
|
|
|
|
const Menu = () => (
|
|
<Accordion type="multiple" className="space-y-1 mt-2.5">
|
|
{GLOBAL_MENU_ITEMS.filter((section) => section[0].enabled !== false).map((section, index) => (
|
|
<AccordionMenuItem key={index} section={section} />
|
|
))}
|
|
</Accordion>
|
|
)
|
|
interface Props {
|
|
open: boolean
|
|
setOpen: Dispatch<SetStateAction<boolean>>
|
|
}
|
|
|
|
const GlobalMobileMenu = ({ open, setOpen }: Props) => {
|
|
const isLoggedIn = useIsLoggedIn()
|
|
const isUserLoading = useIsUserLoading()
|
|
const pathname = usePathname()
|
|
|
|
const { navigationLogo } = getCustomContent(['navigation:logo'])
|
|
|
|
// Close mobile menu on route change
|
|
useEffect(() => {
|
|
setOpen(false)
|
|
}, [pathname])
|
|
|
|
useKey('Escape', () => setOpen(false))
|
|
|
|
return (
|
|
<LazyMotion features={domAnimation}>
|
|
<AnimatePresence mode="wait">
|
|
{open && (
|
|
<m.div
|
|
variants={container}
|
|
initial="hidden"
|
|
animate="show"
|
|
exit="exit"
|
|
className="bg-overlay fixed overflow-hidden inset-0 z-50 h-screen max-h-screen w-screen supports-[height:100cqh]:h-[100cqh] supports-[height:100svh]:h-svh transform"
|
|
>
|
|
<div className="absolute px-5 h-(--header-height) flex items-center justify-between w-screen left-0 top-0 z-50 bg-overlay before:content[''] before:absolute before:w-full before:h-3 before:inset-0 before:top-full before:bg-linear-to-b before:from-background-overlay before:to-transparent">
|
|
<Link href="/" className="flex items-center gap-2">
|
|
<Image
|
|
className="cursor-pointer hidden dark:block"
|
|
src={navigationLogo?.dark ?? '/docs/supabase-dark.svg'}
|
|
priority
|
|
width={96}
|
|
height={24}
|
|
alt="Supabase Logo"
|
|
/>
|
|
<Image
|
|
className="cursor-pointer block dark:hidden"
|
|
src={navigationLogo?.light ?? '/docs/supabase-light.svg'}
|
|
priority
|
|
width={96}
|
|
height={24}
|
|
alt="Supabase Logo"
|
|
/>
|
|
<span className="font-mono text-sm font-medium text-brand mb-px">DOCS</span>
|
|
</Link>
|
|
<div className="flex gap-4 items-center">
|
|
<ThemeToggle contentClassName="bg-surface-200" />
|
|
<button
|
|
tabIndex={0}
|
|
onClick={() => setOpen(false)}
|
|
type="button"
|
|
className="inline-flex items-center justify-center bg-surface-100 hover:bg-surface-200 border border-default bg-surface-100/75 text-foreground-light rounded-sm min-w-[30px] w-[30px] h-[30px] focus-ring"
|
|
>
|
|
<span className="sr-only">Close menu</span>
|
|
<X />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="max-h-screen supports-[height:100cqh]:h-[100cqh] supports-[height:100svh]:h-svh overflow-y-auto pt-12 pb-32 px-3">
|
|
<Menu />
|
|
</div>
|
|
<div className="absolute bottom-0 left-0 right-0 top-auto w-full bg-alternative flex items-stretch p-4 gap-4">
|
|
{!isUserLoading && (
|
|
<>
|
|
{isLoggedIn ? (
|
|
<Button block size="medium" asChild>
|
|
<Link href="https://supabase.com/dashboard/projects">Dashboard</Link>
|
|
</Button>
|
|
) : (
|
|
<>
|
|
<Button block size="medium" asChild>
|
|
<Link href="https://supabase.com/dashboard/sign-in">Sign in</Link>
|
|
</Button>
|
|
<Button variant="primary" block size="medium" asChild>
|
|
<Link href="https://supabase.com/dashboard/sign-up">
|
|
Start your project
|
|
</Link>
|
|
</Button>
|
|
</>
|
|
)}
|
|
</>
|
|
)}
|
|
</div>
|
|
</m.div>
|
|
)}
|
|
</AnimatePresence>
|
|
<AnimatePresence mode="wait">
|
|
{open && (
|
|
<m.div
|
|
variants={container}
|
|
initial="hidden"
|
|
animate="show"
|
|
exit="exit"
|
|
className="bg-alternative fixed overflow-hidden inset-0 z-40 h-screen w-screen transform"
|
|
/>
|
|
)}
|
|
</AnimatePresence>
|
|
</LazyMotion>
|
|
)
|
|
}
|
|
|
|
export default GlobalMobileMenu
|