Files
supabase/apps/www/components/Solutions/PricingComparisonSection.tsx
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

122 lines
3.7 KiB
TypeScript

import { Check, Minus } from 'lucide-react'
import React, { type FC, type ReactNode } from 'react'
import { cn } from 'ui'
import { TextLink } from 'ui-patterns/TextLink'
import SectionContainer from '@/components/Layouts/SectionContainer'
export interface PricingComparisonPlan {
name: string
/** Highlights the column visually (eg. the recommended plan). */
highlight?: boolean
}
export interface PricingComparisonRow {
feature: ReactNode
/** One value per plan, in the same order as `plans`. Booleans render as a check or dash. */
values: (ReactNode | boolean)[]
}
export interface PricingComparisonSectionProps {
id?: string
heading: ReactNode
subheading?: ReactNode
plans: PricingComparisonPlan[]
rows: PricingComparisonRow[]
cta?: {
label: string
url: string
}
className?: string
}
const PlanValue = ({ value }: { value: ReactNode | boolean }) => {
if (value === true) {
return <Check className="w-4 h-4 text-primary" strokeWidth={2} aria-label="Included" />
}
if (value === false) {
return (
<Minus className="w-4 h-4 text-foreground-muted" strokeWidth={2} aria-label="Not included" />
)
}
return <span className="text-sm text-foreground">{value}</span>
}
const PricingComparisonSection: FC<PricingComparisonSectionProps> = ({
id,
heading,
subheading,
plans,
rows,
cta,
className,
}) => {
return (
<SectionContainer id={id} className={cn('flex flex-col gap-8 py-16 md:py-24', className)}>
<div className="flex flex-col gap-2 max-w-xl">
<h2 className="h2 text-foreground-lighter m-0!">{heading}</h2>
{subheading && <p className="text-foreground-lighter">{subheading}</p>}
</div>
<div className="w-full overflow-x-auto border border-default rounded-lg bg-surface-75">
<table className="w-full min-w-[480px] border-collapse text-left">
<thead>
<tr className="border-b border-default">
<th
scope="col"
className="py-4 px-4 md:px-6 text-sm font-normal text-foreground-light"
>
What you get
</th>
{plans.map((plan) => (
<th
key={plan.name}
scope="col"
className={cn(
'py-4 px-4 md:px-6 text-sm font-medium text-center',
plan.highlight ? 'text-foreground bg-surface-100' : 'text-foreground'
)}
>
{plan.name}
</th>
))}
</tr>
</thead>
<tbody>
{rows.map((row, rowIndex) => (
<tr
key={rowIndex}
className="border-b border-default last:border-b-0 hover:bg-surface-100/50 transition-colors"
>
<th
scope="row"
className="py-3 px-4 md:px-6 text-sm font-normal text-foreground-light"
>
{row.feature}
</th>
{row.values.map((value, valueIndex) => (
<td
key={valueIndex}
className={cn(
'py-3 px-4 md:px-6 text-center',
plans[valueIndex]?.highlight && 'bg-surface-100'
)}
>
<span className="flex items-center justify-center">
<PlanValue value={value} />
</span>
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
{cta && <TextLink hasChevron label={cta.label} url={cta.url} className="mt-2" />}
</SectionContainer>
)
}
export default PricingComparisonSection