Files
Danny White ec53175b8a refactor(ui): rename text-brand to text-primary (#50564)
## What kind of change does this PR introduce?

Refactor. Follow-up to #49871.

## What is the current behavior?

Branded (green) text still uses the `text-brand` classname while the
colour comes from `--primary`.

## What is the new behavior?

**Rename-only:** `text-brand` → `text-primary` across callsites and
docs. Leftover `bg-brand` / `border-brand` alias to `brand-default`. No
intentional colour changes in this PR.

This better matches how we treat our green in other components and
props, like `variant="primary"` for green buttons.

## To test

On light mode: smoke-test that branded text still looks like #49871
(readable green, not the bright fill).

-
[Homepage](https://zone-www-dot-com-git-dnywh-depr-316-text-brand-de2380-supabase.vercel.app/):
“Scale to millions” uses `text-primary`
- [Docs
homepage](https://docs-git-dnywh-depr-316-text-brand-to-primary-supabase.vercel.app/docs):
branded links like “More on self-hosting” are still readable
- [Typography
docs](https://design-system-git-dnywh-depr-316-text-brand-to-primary-supabase.vercel.app/design-system/docs/typography):
documents `text-primary`
- [Colour
usage](https://design-system-git-dnywh-depr-316-text-brand-to-primary-supabase.vercel.app/design-system/docs/color-usage):
`text-primary` is visibly darker than `bg-brand-default` _on light mode_
- [Studio auth
providers](https://studio-staging-git-dnywh-depr-316-text-brand-to-60fa6c-supabase.vercel.app/dashboard/project/_/auth/providers):
enabled provider badge text readable; status dot stays bright green

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

* **Style**
* Updated branded text, link hover states, icons, badges, indicators,
and highlighted content across the Design System, Docs, Studio, Learn,
UI Library, and marketing experiences to use the primary theme color.
* Updated syntax highlighting and table-of-contents styling for
consistent primary-color presentation.
  * Refined brand color fallback behavior for bright fills and borders.

* **Documentation**
* Updated color-usage and typography guidance to recommend the primary
text utility.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-22 16:19:03 +10:00

206 lines
6.1 KiB
TypeScript

'use client'
import { AnimatedCounter } from '../AnimatedCounter'
import { AnimatedGridBackground } from '../AnimatedGridBackground'
type HeroStat = {
headline: string
number: number
increment: number
intervalMs: number
}
const heroStats: HeroStat[] = [
{
headline: 'More databases created in 2025 than in all previous years combined',
number: 15_106_212,
increment: 1,
intervalMs: 1000,
},
{
headline: 'Projects created',
number: 11_808_815,
increment: 1,
intervalMs: 500,
},
{
headline: 'Realtime messages delivered',
number: 280_355_288_012,
increment: 1931,
intervalMs: 200,
},
]
type GridStat = {
headline: string
number: number
increment: number
suffix?: string
prefix?: string
}
const gridStats: GridStat[] = [
{
headline: 'Peak concurrent Realtime connections',
number: 193_942,
increment: 0,
},
{
headline: 'Edge Functions invoked',
number: 49_632_206_530,
increment: 1_709,
},
{
headline: 'Images transformed via Storage',
number: 64_454_990,
increment: 2,
},
{
headline: 'Petabytes stored',
number: 64.5,
increment: 0,
suffix: 'PB',
},
{
headline: 'Petabytes served',
number: 19.3,
increment: 0,
suffix: 'PB',
},
{
headline: 'One of the top 100 repos on Github',
number: 94_500,
increment: 0,
suffix: 'stars',
},
{
headline: 'paid to fund open source',
number: 662_357.71,
increment: 0,
prefix: '$',
suffix: 'USD',
},
{
headline: 'Commits to our repos by community members',
number: 1_421,
increment: 0,
},
]
export const Devs = () => {
return (
<>
<section className="relative max-w-240 h-[240px] md:h-[360px] mx-auto border-x border-b w-[95%] md:w-full">
{/* Grid background */}
<AnimatedGridBackground
cols={5}
rows={{ mobile: 2, desktop: 3 }}
tiles={[
{ cell: 0, type: 'dots' },
{ cell: 2, type: 'stripes' },
{ cell: 3, type: 'stripes' },
{ cell: 4, type: 'stripes' },
{ cell: 5, type: 'stripes' },
{ cell: 7, type: 'dots' },
]}
initialDelay={0.35}
/>
{/* Content */}
<div className="flex flex-col justify-end h-full px-4 py-0 relative">
<div className="flex justify-between items-center">
<h2 className="font-medium tracking-tighter text-6xl md:text-7xl lg:text-[5.6rem] translate-y-2 lg:translate-y-[10px]">
Devs <span className="text-primary">love</span> Supabase
</h2>
</div>
</div>
</section>
<div className="relative max-w-240 mx-auto border-x border-b px-4 lg:px-8 py-12 w-[95%] md:w-full">
<p className="text-lg text-foreground-light max-w-2xl">
In 2025, developers around the world shipped faster, scaled further, and built things we
never imagined. Here is what you accomplished on Supabase.
</p>
</div>
{/* Hero stats */}
{heroStats.map((stat) => (
<div
key={stat.headline}
className="relative max-w-240 mx-auto border-x border-b p-8 bg-surface-75 w-[95%] md:w-full"
>
<div className="flex flex-col gap-2">
<p className="text-4xl md:text-5xl font-mono font-medium text-primary tracking-tighter">
<AnimatedCounter
value={stat.number}
increment={stat.increment}
intervalMs={stat.intervalMs}
/>
</p>
<p className="text-base text-foreground-light">{stat.headline}</p>
</div>
</div>
))}
{/* Stats grid */}
<div className="relative max-w-240 mx-auto border-x w-[95%] md:w-full">
<div className="grid grid-cols-2 lg:grid-cols-4">
{(() => {
const cols = 4
const remainder = gridStats.length % cols
const emptyCells = remainder === 0 ? 0 : cols - remainder
return (
<>
{gridStats.map((stat) => (
<div
key={stat.headline}
className="p-8 border-r border-b border-muted nth-[2n]:border-r-0 lg:nth-[2n]:border-r nth-[4n]:border-r-0"
>
<div className="flex flex-col gap-2">
<div className="flex flex-col">
<p
className={`text-2xl md:text-3xl font-mono font-medium tracking-tighter ${stat.increment > 0 ? 'text-primary' : 'text-foreground'}`}
>
{stat.suffix ? (
<>
{stat.prefix}
{new Intl.NumberFormat('en-US', {
notation: 'compact',
maximumFractionDigits: stat.number % 1 !== 0 ? 2 : 0,
minimumFractionDigits: stat.number % 1 !== 0 ? 1 : 0,
})
.format(stat.number)
.toLowerCase()}
<span className="text-lg ml-1.5 font-medium tracking-tight">
{stat.suffix}
</span>
</>
) : (
<AnimatedCounter
value={stat.number}
increment={stat.increment}
compact
/>
)}
</p>
</div>
<p className="text-sm text-foreground-lighter">{stat.headline}</p>
</div>
</div>
))}
{Array.from({ length: emptyCells }).map((_, i) => (
<div
key={`empty-${i}`}
className="px-6 py-8 border-r border-b border-muted last:border-r-0"
/>
))}
</>
)
})()}
</div>
</div>
</>
)
}