mirror of
https://github.com/hanzoai/billing.git
synced 2026-10-30 18:46:54 +00:00
The Tailwind strip left the colour *values* behind: markup still named literal whites and dark-theme hexes, so `html.light` moved the tokens and nothing followed. Light mode rendered near-black text on a near-black page. Same four facts, each with one home now: ground bg-[#09090b] · bg-[#141419] · bg-[#0d0d10] · bg-[#16161c] -> bg-bg · bg-bg-card · bg-bg-elevated ink the whole `*-white/α` family is really "foreground at α%", so it says so: `-ink/α`, mixed from var(--color-text) brand bg-white + text-black pills -> bg-brand + text-brand-foreground (+ .bg-brand-foreground, which the switch knob needed — it was painting white on the white ON-track and vanishing) selection one rule for both themes; the light override painted white text on a pale tint /commerce styled itself entirely with inline dark literals and ignored the flip wholesale — 82 unreadable elements. Its hexes and rgba(255,255,255,α) now read the same tokens, and the H-mark inherits currentColor instead of being permanently white. Also: every self-navigation goes through BASE_PATH. /plans, /transactions, /payment-methods and the auth callback jumped to a bare '/', which walks the browser out of the app whenever the SPA is mounted at /admin/billing inside the commerce binary. billing-shell already did this for /topup — now it is the only way. Verified: next build green; Playwright over the static export in both themes — light /commerce 82 unreadable -> 0, /plans -> /admin/billing/#pricing (was the server root); dark render byte-identical in intent (same alphas, now expressed in oklab). Co-authored-by: Hanzo Dev <[email protected]>
77 lines
2.5 KiB
TypeScript
77 lines
2.5 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import { PlanCard, type PricingPlan } from './plan-card'
|
|
|
|
interface BillingToggleProps {
|
|
plans: PricingPlan[]
|
|
}
|
|
|
|
export function BillingToggle({ plans }: BillingToggleProps) {
|
|
const [interval, setInterval] = useState<'monthly' | 'annual'>('monthly')
|
|
|
|
return (
|
|
<>
|
|
{/* Toggle */}
|
|
<div style={{ display: 'flex', justifyContent: 'center', marginBottom: '40px' }}>
|
|
<div style={{
|
|
display: 'inline-flex',
|
|
background: 'color-mix(in oklab, var(--color-text) 4%, transparent)',
|
|
border: '1px solid color-mix(in oklab, var(--color-text) 10%, transparent)',
|
|
borderRadius: '10px',
|
|
padding: '4px',
|
|
gap: '4px',
|
|
}}>
|
|
{(['monthly', 'annual'] as const).map((iv) => (
|
|
<button
|
|
key={iv}
|
|
type="button"
|
|
onClick={() => setInterval(iv)}
|
|
style={{
|
|
background: interval === iv ? 'color-mix(in oklab, var(--color-text) 10%, transparent)' : 'transparent',
|
|
border: interval === iv ? '1px solid color-mix(in oklab, var(--color-text) 15%, transparent)' : '1px solid transparent',
|
|
borderRadius: '7px',
|
|
padding: '6px 16px',
|
|
color: interval === iv ? 'var(--color-text)' : 'color-mix(in oklab, var(--color-text) 40%, transparent)',
|
|
fontSize: '13px',
|
|
fontWeight: 500,
|
|
cursor: 'pointer',
|
|
transition: 'all 0.15s',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '6px',
|
|
}}
|
|
>
|
|
{iv === 'monthly' ? 'Monthly' : 'Annual'}
|
|
{iv === 'annual' && (
|
|
<span style={{
|
|
background: 'rgba(74,222,128,0.15)',
|
|
color: '#4ade80',
|
|
fontSize: '10px',
|
|
fontWeight: 700,
|
|
padding: '1px 5px',
|
|
borderRadius: '4px',
|
|
letterSpacing: '0.03em',
|
|
}}>
|
|
Save 20%
|
|
</span>
|
|
)}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Plans grid */}
|
|
<div style={{
|
|
display: 'grid',
|
|
gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))',
|
|
gap: '20px',
|
|
}}>
|
|
{plans.map((plan) => (
|
|
<PlanCard key={plan.id} plan={plan} billingInterval={interval} />
|
|
))}
|
|
</div>
|
|
</>
|
|
)
|
|
}
|