Update UI design across multiple pages and components

Several changes have been made to improve the user interface and enhance the user experience. This includes redesigning Auth buttons, modifying website layouts and routing, tweaking heading and text sizes for clarity, and revamping the marketing, documentation, and pricing pages. These changes collectively contribute to a cleaner, more concise and navigable interface.
This commit is contained in:
giancarlo
2024-04-09 13:35:12 +08:00
parent 80952dc445
commit e7f2660032
21 changed files with 179 additions and 166 deletions

View File

@@ -3,7 +3,7 @@ import { withI18n } from '~/lib/i18n/with-i18n';
function CookiePolicyPage() { function CookiePolicyPage() {
return ( return (
<div className={'mt-8'}> <div>
<div className={'container mx-auto'}> <div className={'container mx-auto'}>
<SitePageHeader <SitePageHeader
title={`Cookie Policy`} title={`Cookie Policy`}

View File

@@ -9,7 +9,7 @@ const YEAR = new Date().getFullYear();
export function SiteFooter() { export function SiteFooter() {
return ( return (
<footer className={'py-8 lg:py-24'}> <footer className={'border-t py-8 xl:py-12 2xl:py-14'}>
<div className={'container mx-auto'}> <div className={'container mx-auto'}>
<div className={'flex flex-col space-y-8 lg:flex-row lg:space-y-0'}> <div className={'flex flex-col space-y-8 lg:flex-row lg:space-y-0'}>
<div <div

View File

@@ -10,6 +10,7 @@ import { PersonalAccountDropdown } from '@kit/accounts/personal-account-dropdown
import { useSignOut } from '@kit/supabase/hooks/use-sign-out'; import { useSignOut } from '@kit/supabase/hooks/use-sign-out';
import { useUser } from '@kit/supabase/hooks/use-user'; import { useUser } from '@kit/supabase/hooks/use-user';
import { Button } from '@kit/ui/button'; import { Button } from '@kit/ui/button';
import { ModeToggle } from '@kit/ui/mode-toggle';
import { Trans } from '@kit/ui/trans'; import { Trans } from '@kit/ui/trans';
import featuresFlagConfig from '~/config/feature-flags.config'; import featuresFlagConfig from '~/config/feature-flags.config';
@@ -55,10 +56,18 @@ function SuspendedPersonalAccountDropdown(props: { user: User | null }) {
function AuthButtons() { function AuthButtons() {
return ( return (
<div className={'hidden space-x-2 lg:flex'}> <div className={'hidden space-x-0.5 lg:flex'}>
<ModeToggle />
<Link href={pathsConfig.auth.signIn}>
<Button variant={'link'}>
<Trans i18nKey={'auth:signIn'} />
</Button>
</Link>
<Link href={pathsConfig.auth.signUp}> <Link href={pathsConfig.auth.signUp}>
<Button variant={'outline'}> <Button className={'rounded-full'}>
<Trans i18nKey={'auth:signUp'} /> <Trans i18nKey={'auth:getStarted'} />
<ChevronRight className={'h-4'} /> <ChevronRight className={'h-4'} />
</Button> </Button>
</Link> </Link>

View File

@@ -1,27 +1,23 @@
import type { User } from '@supabase/supabase-js'; import type { User } from '@supabase/supabase-js';
import { ModeToggle } from '@kit/ui/mode-toggle';
import { SiteHeaderAccountSection } from '~/(marketing)/_components/site-header-account-section'; import { SiteHeaderAccountSection } from '~/(marketing)/_components/site-header-account-section';
import { SiteNavigation } from '~/(marketing)/_components/site-navigation'; import { SiteNavigation } from '~/(marketing)/_components/site-navigation';
import { AppLogo } from '~/components/app-logo'; import { AppLogo } from '~/components/app-logo';
export function SiteHeader(props: { user?: User | null }) { export function SiteHeader(props: { user?: User | null }) {
return ( return (
<div className={'container mx-auto'}> <div className={'border-b'}>
<div className="flex h-16 items-center justify-between"> <div className={'container mx-auto'}>
<div className={'w-4/12'}> <div className="flex h-16 items-center justify-between">
<AppLogo /> <div className={'flex w-6/12 items-center space-x-8'}>
</div> <AppLogo />
<div className={'hidden w-4/12 justify-center lg:flex'}> <SiteNavigation />
<SiteNavigation /> </div>
</div>
<div className={'flex flex-1 items-center justify-end space-x-4'}> <div className={'flex flex-1 items-center justify-end space-x-1'}>
<ModeToggle /> <SiteHeaderAccountSection user={props.user ?? null} />
</div>
<SiteHeaderAccountSection user={props.user ?? null} />
</div> </div>
</div> </div>
</div> </div>

View File

@@ -9,9 +9,8 @@ import { cn, isRouteActive } from '@kit/ui/utils';
const getClassName = (path: string, currentPathName: string) => { const getClassName = (path: string, currentPathName: string) => {
const isActive = isRouteActive(path, currentPathName); const isActive = isRouteActive(path, currentPathName);
return cn(`text-sm transition-all px-4 py-2 rounded-full font-medium`, { return cn(`text-sm font-medium text-primary`, {
'bg-muted': isActive, 'hover:underline': !isActive,
'hover:bg-muted active:bg-muted/50': !isActive,
}); });
}; };

View File

@@ -45,11 +45,7 @@ export function SiteNavigation() {
<> <>
<div className={'hidden items-center lg:flex'}> <div className={'hidden items-center lg:flex'}>
<NavigationMenu> <NavigationMenu>
<NavigationMenuList <NavigationMenuList className={'space-x-6'}>
className={
'space-x-1.5 rounded-full px-1 py-2 shadow-sm dark:shadow-primary/10'
}
>
{NavItems} {NavItems}
</NavigationMenuList> </NavigationMenuList>
</NavigationMenu> </NavigationMenu>

View File

@@ -6,16 +6,18 @@ export function SitePageHeader(props: {
className?: string; className?: string;
}) { }) {
return ( return (
<div <div className={cn('border-b py-8 xl:py-12 2xl:py-14', props.className)}>
className={cn('flex flex-col items-center space-y-4', props.className)} <div className={'container flex flex-col space-y-4'}>
> <h1 className={'text-3xl font-semibold xl:text-5xl'}>{props.title}</h1>
<h1 className={'text-center text-3xl font-semibold xl:text-4xl'}>
{props.title}
</h1>
<h2 className={'text-center text-xl text-muted-foreground xl:text-2xl'}> <h2
<span className={'font-normal'}>{props.subtitle}</span> className={
</h2> 'text-base text-secondary-foreground xl:text-lg 2xl:text-xl'
}
>
<span className={'font-normal'}>{props.subtitle}</span>
</h2>
</div>
</div> </div>
); );
} }

View File

@@ -4,7 +4,7 @@ import { notFound } from 'next/navigation';
import { createCmsClient } from '@kit/cms'; import { createCmsClient } from '@kit/cms';
import Post from '~/(marketing)/blog/_components/post'; import { Post } from '~/(marketing)/blog/_components/post';
import { withI18n } from '~/lib/i18n/with-i18n'; import { withI18n } from '~/lib/i18n/with-i18n';
export async function generateMetadata({ export async function generateMetadata({
@@ -55,11 +55,7 @@ async function BlogPost({ params }: { params: { slug: string } }) {
notFound(); notFound();
} }
return ( return <Post post={post} content={post.content} />;
<div className={'container mx-auto'}>
<Post post={post} content={post.content} />
</div>
);
} }
export default withI18n(BlogPost); export default withI18n(BlogPost);

View File

@@ -1,6 +1,12 @@
import Link from 'next/link';
import { ArrowLeft } from 'lucide-react';
import { Cms } from '@kit/cms'; import { Cms } from '@kit/cms';
import { Heading } from '@kit/ui/heading'; import { Button } from '@kit/ui/button';
import { If } from '@kit/ui/if'; import { If } from '@kit/ui/if';
import { Trans } from '@kit/ui/trans';
import { cn } from '@kit/ui/utils';
import { CoverImage } from '~/(marketing)/blog/_components/cover-image'; import { CoverImage } from '~/(marketing)/blog/_components/cover-image';
import { DateFormatter } from '~/(marketing)/blog/_components/date-formatter'; import { DateFormatter } from '~/(marketing)/blog/_components/date-formatter';
@@ -11,27 +17,37 @@ export const PostHeader: React.FC<{
const { title, publishedAt, description, image } = post; const { title, publishedAt, description, image } = post;
return ( return (
<div className={'flex flex-col space-y-8'}> <div className={'flex flex-col'}>
<div className={'flex flex-col space-y-2'}> <div className={'container'}>
<Heading level={1}>{title}</Heading> <Link href={'/blog'}>
<Button variant={'link'}>
<ArrowLeft className={'h-4'} />
<Heading level={3}> <span>
<p <Trans i18nKey={'marketing:backToBlog'} />
className={'font-normal text-muted-foreground'} </span>
dangerouslySetInnerHTML={{ __html: description ?? '' }} </Button>
/> </Link>
</Heading> </div>
<div className="flex flex-row items-center space-x-2 text-muted-foreground"> <div className={cn('border-b py-8')}>
<div className={'text-sm'}> <div className={'container flex flex-col space-y-4'}>
<DateFormatter dateString={publishedAt.toISOString()} /> <h1 className={'text-3xl font-semibold xl:text-5xl'}>{title}</h1>
</div>
<h2 className={'text-base text-secondary-foreground xl:text-lg'}>
<span
className={'font-normal'}
dangerouslySetInnerHTML={{ __html: description ?? '' }}
/>
</h2>
<DateFormatter dateString={publishedAt.toISOString()} />
</div> </div>
</div> </div>
<If condition={image}> <If condition={image}>
{(imageUrl) => ( {(imageUrl) => (
<div className="relative mx-auto h-[378px] w-full justify-center"> <div className="relative mx-auto mt-8 flex h-[378px] w-full max-w-2xl justify-center">
<CoverImage <CoverImage
preloadImage preloadImage
className="rounded-md" className="rounded-md"

View File

@@ -24,7 +24,7 @@ export function PostPreview({
const slug = `/blog/${post.slug}`; const slug = `/blog/${post.slug}`;
return ( return (
<div className="rounded-xl transition-shadow duration-500"> <div className="flex flex-col space-y-4 rounded-lg transition-shadow duration-500">
<If condition={image}> <If condition={image}>
{(imageUrl) => ( {(imageUrl) => (
<div className="relative mb-2 w-full" style={{ height }}> <div className="relative mb-2 w-full" style={{ height }}>
@@ -39,23 +39,23 @@ export function PostPreview({
)} )}
</If> </If>
<div className={'px-1'}> <div className={'flex flex-col space-y-2 px-1'}>
<div className="flex flex-col space-y-1 py-2"> <div className={'flex flex-col space-y-1'}>
<h3 className="text-2xl font-bold leading-snug dark:text-white"> <h3 className="text-2xl font-semibold leading-snug">
<Link href={slug} className="hover:underline"> <Link href={slug} className="hover:underline">
{title} {title}
</Link> </Link>
</h3> </h3>
</div>
<div className="mb-2 flex flex-row items-center space-x-2 text-sm"> <div className="flex flex-row items-center space-x-2 text-sm">
<div className="text-muted-foreground"> <div className="text-muted-foreground">
<DateFormatter dateString={publishedAt.toISOString()} /> <DateFormatter dateString={publishedAt.toISOString()} />
</div>
</div> </div>
</div> </div>
<p <p
className="mb-4 text-sm leading-relaxed text-muted-foreground" className="mb-4 text-base leading-relaxed text-secondary-foreground"
dangerouslySetInnerHTML={{ __html: description ?? '' }} dangerouslySetInnerHTML={{ __html: description ?? '' }}
/> />
</div> </div>

View File

@@ -9,14 +9,14 @@ export const Post: React.FC<{
content: string; content: string;
}> = ({ post, content }) => { }> = ({ post, content }) => {
return ( return (
<div className={'mx-auto flex max-w-2xl flex-col space-y-6'}> <div>
<PostHeader post={post} /> <PostHeader post={post} />
<article className={styles.HTML}> <div className={'mx-auto flex max-w-2xl flex-col space-y-6'}>
<ContentRenderer content={content} /> <article className={styles.HTML}>
</article> <ContentRenderer content={content} />
</article>
</div>
</div> </div>
); );
}; };
export default Post;

View File

@@ -24,13 +24,13 @@ async function BlogPage() {
}); });
return ( return (
<div className={'container mx-auto'}> <div className={'flex flex-col space-y-12'}>
<div className={'flex flex-col space-y-12 xl:space-y-24'}> <SitePageHeader
<SitePageHeader title={t('marketing:blog')}
title={t('marketing:blog')} subtitle={t('marketing:blogSubtitle')}
subtitle={t('marketing:blogSubtitle')} />
/>
<div className={'container mx-auto'}>
<GridList> <GridList>
{posts.map((post, idx) => { {posts.map((post, idx) => {
return <PostPreview key={idx} post={post} />; return <PostPreview key={idx} post={post} />;

View File

@@ -48,18 +48,18 @@ async function DocumentationPage({ params }: PageParams) {
const description = page?.description ?? ''; const description = page?.description ?? '';
return ( return (
<div className={'container mx-auto'}> <div>
<SitePageHeader
title={page.title}
subtitle={description}
className={'items-start'}
/>
<div <div
className={ className={
'relative mx-auto flex max-w-4xl grow flex-col space-y-4 px-8' 'container relative mx-auto flex max-w-4xl grow flex-col space-y-4 py-6'
} }
> >
<SitePageHeader
title={page.title}
subtitle={description}
className={'items-start'}
/>
<article className={styles.HTML}> <article className={styles.HTML}>
<ContentRenderer content={page.content} /> <ContentRenderer content={page.content} />
</article> </article>

View File

@@ -27,13 +27,15 @@ async function DocsPage() {
return ( return (
<PageBody> <PageBody>
<div className={'flex flex-col items-center space-y-12 xl:space-y-24'}> <div className={'flex flex-col space-y-12 xl:space-y-24'}>
<SitePageHeader <SitePageHeader
title={t('marketing:documentation')} title={t('marketing:documentation')}
subtitle={t('marketing:documentationSubtitle')} subtitle={t('marketing:documentationSubtitle')}
/> />
<DocsCards cards={cards} /> <div className={'flex flex-col items-center'}>
<DocsCards cards={cards} />
</div>
</div> </div>
</PageBody> </PageBody>
); );

View File

@@ -1,4 +1,9 @@
import { ChevronDown } from 'lucide-react'; import Link from 'next/link';
import { ArrowRight, ChevronDown } from 'lucide-react';
import { Button } from '@kit/ui/button';
import { Trans } from '@kit/ui/trans';
import { SitePageHeader } from '~/(marketing)/_components/site-page-header'; import { SitePageHeader } from '~/(marketing)/_components/site-page-header';
import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
@@ -60,34 +65,40 @@ async function FAQPage() {
}; };
return ( return (
<div> <>
<script <script
key={'ld:json'} key={'ld:json'}
type="application/ld+json" type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }} dangerouslySetInnerHTML={{ __html: JSON.stringify(structuredData) }}
/> />
<div className={'container mx-auto'}> <div className={'flex flex-col space-y-4 xl:space-y-8'}>
<div className={'flex flex-col space-y-12 xl:space-y-24'}> <SitePageHeader
<SitePageHeader title={t('marketing:faq')}
title={t('marketing:faq')} subtitle={t('marketing:faqSubtitle')}
subtitle={t('marketing:faqSubtitle')} />
/>
<div <div className={'container flex flex-col space-y-8 pb-16'}>
className={ <div className="flex w-full max-w-xl flex-col">
'm-auto flex w-full max-w-xl items-center justify-center' {faqItems.map((item, index) => {
} return <FaqItem key={index} item={item} />;
> })}
<div className="flex w-full flex-col"> </div>
{faqItems.map((item, index) => {
return <FaqItem key={index} item={item} />; <div>
})} <Link href={'/contact'}>
</div> <Button variant={'outline'}>
<span>
<Trans i18nKey={'marketing:contactFaq'} />
</span>
<ArrowRight className={'ml-2 w-4'} />
</Button>
</Link>
</div> </div>
</div> </div>
</div> </div>
</div> </>
); );
} }
@@ -102,7 +113,7 @@ function FaqItem({
}; };
}>) { }>) {
return ( return (
<details className={'group border-b px-2 py-4'}> <details className={'group border-b px-2 py-4 last:border-b-transparent'}>
<summary <summary
className={ className={
'flex items-center justify-between hover:cursor-pointer hover:underline' 'flex items-center justify-between hover:cursor-pointer hover:underline'

View File

@@ -1,5 +1,4 @@
import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client'; import { getSupabaseServerComponentClient } from '@kit/supabase/server-component-client';
import { Separator } from '@kit/ui/separator';
import { SiteFooter } from '~/(marketing)/_components/site-footer'; import { SiteFooter } from '~/(marketing)/_components/site-footer';
import { SiteHeader } from '~/(marketing)/_components/site-header'; import { SiteHeader } from '~/(marketing)/_components/site-header';
@@ -9,13 +8,11 @@ async function SiteLayout(props: React.PropsWithChildren) {
const user = await getUser(); const user = await getUser();
return ( return (
<div className={'flex flex-col space-y-6 xl:space-y-10 2xl:space-y-12'}> <div className={'flex flex-col'}>
<SiteHeader user={user} /> <SiteHeader user={user} />
{props.children} {props.children}
<Separator />
<SiteFooter /> <SiteFooter />
</div> </div>
); );

View File

@@ -13,7 +13,7 @@ import { withI18n } from '~/lib/i18n/with-i18n';
function Home() { function Home() {
return ( return (
<div className={'flex flex-col space-y-24'}> <div className={'flex flex-col space-y-24 py-16'}>
<div className={'container mx-auto flex flex-col space-y-24'}> <div className={'container mx-auto flex flex-col space-y-24'}>
<div <div
className={ className={
@@ -22,7 +22,11 @@ function Home() {
' duration-1000 slide-in-from-top-12' ' duration-1000 slide-in-from-top-12'
} }
> >
<div className={'flex w-full flex-1 flex-col items-center space-y-8'}> <div
className={
'flex w-full flex-1 flex-col items-center space-y-8 xl:space-y-12 2xl:space-y-14'
}
>
<Pill> <Pill>
<span>The leading SaaS Starter Kit for ambitious developers</span> <span>The leading SaaS Starter Kit for ambitious developers</span>
</Pill> </Pill>
@@ -53,9 +57,7 @@ function Home() {
</span> </span>
</Heading> </Heading>
</div> </div>
</div>
<div>
<MainCallToActionButton /> <MainCallToActionButton />
</div> </div>
</div> </div>
@@ -70,9 +72,7 @@ function Home() {
<Image <Image
priority priority
className={ className={
'rounded-2xl' + 'rounded-lg border delay-500 duration-1000 ease-out animate-in fade-in zoom-in-50 fill-mode-both'
' border animate-in fade-in' +
' delay-300 duration-1000 ease-out zoom-in-50 fill-mode-both'
} }
width={3069} width={3069}
height={1916} height={1916}
@@ -115,20 +115,20 @@ function Home() {
<div className={'flex flex-col space-y-4'}> <div className={'flex flex-col space-y-4'}>
<FeatureShowcaseContainer> <FeatureShowcaseContainer>
<LeftFeatureContainer> <LeftFeatureContainer>
<div className={'flex flex-col space-y-2'}> <div className={'flex flex-col space-y-1'}>
<Heading level={2}>Authentication</Heading> <Heading level={2}>Authentication</Heading>
<Heading level={3} className={'text-muted-foreground'}> <Heading level={3} className={'text-muted-foreground'}>
Secure and Easy-to-Use Authentication for Your SaaS Website Secure and Easy-to-Use Authentication for Your SaaS Website
and API and API
</Heading> </Heading>
</div>
<div> <div>
Our authentication system is built on top of the Our authentication system is built on top of the
industry-leading PaaS such as Supabase and Firebase. It is industry-leading PaaS such as Supabase and Firebase. It is
secure, easy-to-use, and fully customizable. It supports secure, easy-to-use, and fully customizable. It supports
email/password, social logins, and more. email/password, social logins, and more.
</div>
</div> </div>
</LeftFeatureContainer> </LeftFeatureContainer>
@@ -155,18 +155,18 @@ function Home() {
</LeftFeatureContainer> </LeftFeatureContainer>
<RightFeatureContainer> <RightFeatureContainer>
<div className={'flex flex-col space-y-2'}> <div className={'flex flex-col space-y-1'}>
<Heading level={2}>Dashboard</Heading> <Heading level={2}>Dashboard</Heading>
<Heading level={3} className={'text-muted-foreground'}> <Heading level={3} className={'text-muted-foreground'}>
A fantastic dashboard to manage your SaaS business A fantastic dashboard to manage your SaaS business
</Heading> </Heading>
</div>
<div> <div>
Our dashboard offers an overview of your SaaS business. It Our dashboard offers an overview of your SaaS business. It shows
shows at a glance all you need to know about your business. It at a glance all you need to know about your business. It is
is fully customizable and extendable. fully customizable and extendable.
</div>
</div> </div>
</RightFeatureContainer> </RightFeatureContainer>
</FeatureShowcaseContainer> </FeatureShowcaseContainer>
@@ -247,14 +247,18 @@ function FeatureShowcaseContainer(props: React.PropsWithChildren) {
function LeftFeatureContainer(props: React.PropsWithChildren) { function LeftFeatureContainer(props: React.PropsWithChildren) {
return ( return (
<div className={'flex w-full flex-col space-y-8 lg:w-6/12'}> <div className={'flex w-full flex-col space-y-6 lg:w-6/12'}>
{props.children} {props.children}
</div> </div>
); );
} }
function RightFeatureContainer(props: React.PropsWithChildren) { function RightFeatureContainer(props: React.PropsWithChildren) {
return <div className={'flex w-full lg:w-6/12'}>{props.children}</div>; return (
<div className={'flex w-full flex-col space-y-6 lg:w-6/12'}>
{props.children}
</div>
);
} }
function MainCallToActionButton() { function MainCallToActionButton() {

View File

@@ -18,13 +18,15 @@ async function PricingPage() {
const { t } = await createI18nServerInstance(); const { t } = await createI18nServerInstance();
return ( return (
<div className={'container mx-auto mt-8 flex flex-col space-y-24'}> <div className={'flex flex-col space-y-12'}>
<SitePageHeader <SitePageHeader
title={t('marketing:pricing')} title={t('marketing:pricing')}
subtitle={t('marketing:pricingSubtitle')} subtitle={t('marketing:pricingSubtitle')}
/> />
<PricingTable paths={pathsConfig.auth} config={billingConfig} /> <div className={'container mx-auto pb-8 xl:pb-16'}>
<PricingTable paths={pathsConfig.auth} config={billingConfig} />
</div>
</div> </div>
); );
} }

View File

@@ -9,7 +9,7 @@ const LogoImage: React.FC<{
return ( return (
<svg <svg
width={width} width={width}
className={cn(`w-[95px] sm:w-[105px]`, className)} className={cn(`w-[95px]`, className)}
viewBox="0 0 733 140" viewBox="0 0 733 140"
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"

View File

@@ -7,6 +7,8 @@
"faqSubtitle": "Frequently asked questions about the platform", "faqSubtitle": "Frequently asked questions about the platform",
"pricing": "Pricing", "pricing": "Pricing",
"pricingSubtitle": "Pricing plans and payment options", "pricingSubtitle": "Pricing plans and payment options",
"backToBlog": "Back to blog",
"contactFaq": "If you have any questions, please contact us",
"contact": "Contact", "contact": "Contact",
"about": "About", "about": "About",
"product": "Product", "product": "Product",

View File

@@ -63,10 +63,7 @@ export function PricingTable({
' justify-center lg:flex-row' ' justify-center lg:flex-row'
} }
> >
{config.products.map((product, index) => { {config.products.map((product) => {
const isFirst = index === 0;
const isLast = index === config.products.length - 1;
const plan = product.plans.find((plan) => { const plan = product.plans.find((plan) => {
if (plan.paymentType === 'recurring') { if (plan.paymentType === 'recurring') {
return plan.interval === interval; return plan.interval === interval;
@@ -87,10 +84,7 @@ export function PricingTable({
return ( return (
<PricingItem <PricingItem
className={cn('border-b border-r border-t', { className={cn('border')}
['rounded-l-lg border-l']: isFirst,
['rounded-r-lg']: isLast,
})}
selectable selectable
key={plan.id} key={plan.id}
plan={plan} plan={plan}
@@ -160,7 +154,7 @@ function PricingItem(
`s-full flex flex-1 grow flex-col items-stretch `s-full flex flex-1 grow flex-col items-stretch
justify-between space-y-8 self-stretch p-6 lg:w-4/12 xl:max-w-[22rem] xl:p-8`, justify-between space-y-8 self-stretch p-6 lg:w-4/12 xl:max-w-[22rem] xl:p-8`,
{ {
['bg-primary text-primary-foreground border-primary']: highlighted, ['border-primary']: highlighted,
}, },
)} )}
> >
@@ -178,7 +172,7 @@ function PricingItem(
<If condition={props.product.badge}> <If condition={props.product.badge}>
<Badge <Badge
variant={'default'} variant={highlighted ? 'default' : 'outline'}
className={cn({ className={cn({
['border-primary-foreground']: highlighted, ['border-primary-foreground']: highlighted,
})} })}
@@ -218,9 +212,6 @@ function PricingItem(
<span <span
className={cn( className={cn(
`animate-in slide-in-from-left-4 fade-in flex items-center space-x-0.5 capitalize`, `animate-in slide-in-from-left-4 fade-in flex items-center space-x-0.5 capitalize`,
{
['text-muted-foreground']: !highlighted,
},
)} )}
> >
<span className={'text-sm'}> <span className={'text-sm'}>
@@ -240,9 +231,6 @@ function PricingItem(
<span <span
className={cn( className={cn(
`animate-in slide-in-from-left-4 fade-in text-sm capitalize`, `animate-in slide-in-from-left-4 fade-in text-sm capitalize`,
{
['text-muted-foreground']: !highlighted,
},
)} )}
> >
<If condition={props.primaryLineItem.type === 'per-seat'}> <If condition={props.primaryLineItem.type === 'per-seat'}>
@@ -319,7 +307,7 @@ function FeaturesList(
<ul className={'flex flex-col space-y-2'}> <ul className={'flex flex-col space-y-2'}>
{props.features.map((feature) => { {props.features.map((feature) => {
return ( return (
<ListItem highlighted={props.highlighted} key={feature}> <ListItem key={feature}>
<Trans <Trans
i18nKey={`common:plans.features.${feature}`} i18nKey={`common:plans.features.${feature}`}
defaults={feature} defaults={feature}
@@ -347,25 +335,18 @@ function Price({ children }: React.PropsWithChildren) {
); );
} }
function ListItem({ function ListItem({ children }: React.PropsWithChildren) {
children,
highlighted,
}: React.PropsWithChildren<{
highlighted?: boolean;
}>) {
return ( return (
<li className={'flex items-center space-x-1.5'}> <li className={'flex items-center space-x-1.5'}>
<CheckCircle <CheckCircle
className={cn('h-4', { className={cn('h-4', {
['text-primary-foreground']: highlighted, ['text-green-600']: true,
['text-green-600']: !highlighted,
})} })}
/> />
<span <span
className={cn('text-sm', { className={cn('text-sm', {
['text-secondary-foreground']: !highlighted, ['text-secondary-foreground']: true,
['text-primary-foreground']: highlighted,
})} })}
> >
{children} {children}
@@ -391,7 +372,7 @@ function PlanIntervalSwitcher(
{ {
'rounded-r-none border-r-transparent': index === 0, 'rounded-r-none border-r-transparent': index === 0,
'rounded-l-none': index === props.intervals.length - 1, 'rounded-l-none': index === props.intervals.length - 1,
['hover:text-current hover:bg-muted']: !selected, ['hover:text-primary border']: !selected,
['font-semibold cursor-default hover:text-initial hover:bg-background border-primary']: ['font-semibold cursor-default hover:text-initial hover:bg-background border-primary']:
selected, selected,
}, },
@@ -445,7 +426,7 @@ function DefaultCheckoutButton(
<Button <Button
size={'lg'} size={'lg'}
className={'w-full'} className={'w-full'}
variant={props.highlighted ? 'secondary' : 'outline'} variant={props.highlighted ? 'default' : 'outline'}
> >
<span> <span>
<Trans i18nKey={label} defaults={label} /> <Trans i18nKey={label} defaults={label} />