Design Updates (#379)
* Enhance Marketing Pages and UI Components - Updated the marketing homepage to include an Ecosystem Showcase component, improving the presentation of the SaaS Starter Kit. - Refined various UI components, including adjustments to spacing, typography, and layout for better visual consistency. - Improved accessibility by adding aria-labels and ensuring proper semantic structure in components. - Adjusted styles across multiple components to enhance responsiveness and user experience. - Updated the pricing table and feature cards to align with the new design standards, ensuring a cohesive look and feel throughout the application. - Updated plan picker design
This commit is contained in:
committed by
GitHub
parent
d8bb7f56df
commit
54d6b4897f
@@ -29,8 +29,8 @@
|
||||
"@kit/shared": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@kit/ui": "workspace:*",
|
||||
"@tailwindcss/postcss": "^4.1.13",
|
||||
"@types/node": "^24.6.1",
|
||||
"@tailwindcss/postcss": "^4.1.14",
|
||||
"@types/node": "^24.6.2",
|
||||
"@types/nodemailer": "7.0.2",
|
||||
"@types/react": "19.1.16",
|
||||
"@types/react-dom": "19.1.9",
|
||||
@@ -38,7 +38,7 @@
|
||||
"pino-pretty": "13.0.0",
|
||||
"react-hook-form": "^7.63.0",
|
||||
"recharts": "2.15.3",
|
||||
"tailwindcss": "4.1.13",
|
||||
"tailwindcss": "4.1.14",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"typescript": "^5.9.3",
|
||||
"zod": "^3.25.74"
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.55.1",
|
||||
"@supabase/supabase-js": "2.58.0",
|
||||
"@types/node": "^24.6.1",
|
||||
"@types/node": "^24.6.2",
|
||||
"dotenv": "17.2.3",
|
||||
"node-html-parser": "^7.0.1",
|
||||
"totp-generator": "^2.0.0"
|
||||
|
||||
@@ -59,7 +59,9 @@ export function SiteHeaderAccountSection({
|
||||
|
||||
function AuthButtons() {
|
||||
return (
|
||||
<div className={'animate-in fade-in flex gap-x-2.5 duration-500'}>
|
||||
<div
|
||||
className={'animate-in fade-in flex items-center gap-x-2 duration-500'}
|
||||
>
|
||||
<div className={'hidden md:flex'}>
|
||||
<If condition={features.enableThemeToggle}>
|
||||
<ModeToggle />
|
||||
@@ -72,14 +74,24 @@ function AuthButtons() {
|
||||
</If>
|
||||
</div>
|
||||
|
||||
<div className={'flex gap-x-2.5'}>
|
||||
<Button className={'hidden md:block'} asChild variant={'ghost'}>
|
||||
<div className={'flex items-center gap-x-2'}>
|
||||
<Button
|
||||
className={'hidden md:flex md:text-sm'}
|
||||
asChild
|
||||
variant={'outline'}
|
||||
size={'sm'}
|
||||
>
|
||||
<Link href={pathsConfig.auth.signIn}>
|
||||
<Trans i18nKey={'auth:signIn'} />
|
||||
</Link>
|
||||
</Button>
|
||||
|
||||
<Button asChild className="text-xs md:text-sm" variant={'default'}>
|
||||
<Button
|
||||
asChild
|
||||
className="text-xs md:text-sm"
|
||||
variant={'default'}
|
||||
size={'sm'}
|
||||
>
|
||||
<Link href={pathsConfig.auth.signUp}>
|
||||
<Trans i18nKey={'auth:signUp'} />
|
||||
</Link>
|
||||
|
||||
@@ -14,11 +14,21 @@ export function SitePageHeader({
|
||||
const containerClass = container ? 'container' : '';
|
||||
|
||||
return (
|
||||
<div className={cn('border-b py-8 xl:py-10 2xl:py-12', className)}>
|
||||
<div className={cn('flex flex-col gap-y-3 lg:gap-y-4', containerClass)}>
|
||||
<div
|
||||
className={cn(
|
||||
'border-border/40 border-b py-6 xl:py-8 2xl:py-10',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-col items-center gap-y-2 lg:gap-y-3',
|
||||
containerClass,
|
||||
)}
|
||||
>
|
||||
<h1
|
||||
className={
|
||||
'font-heading text-3xl font-medium tracking-tighter xl:text-5xl dark:text-white'
|
||||
'font-heading text-3xl tracking-tighter xl:text-5xl dark:text-white'
|
||||
}
|
||||
>
|
||||
{title}
|
||||
|
||||
@@ -12,13 +12,9 @@ type Props = {
|
||||
export function CoverImage({ title, src, preloadImage, className }: Props) {
|
||||
return (
|
||||
<Image
|
||||
className={cn(
|
||||
'block rounded-xl object-cover duration-250' +
|
||||
' transition-all hover:opacity-90',
|
||||
{
|
||||
className,
|
||||
},
|
||||
)}
|
||||
className={cn('block rounded-md object-cover', {
|
||||
className,
|
||||
})}
|
||||
src={src}
|
||||
priority={preloadImage}
|
||||
alt={`Cover Image for ${title}`}
|
||||
|
||||
@@ -10,24 +10,24 @@ export function PostHeader({ post }: { post: Cms.ContentItem }) {
|
||||
|
||||
return (
|
||||
<div className={'flex flex-1 flex-col'}>
|
||||
<div className={cn('border-b py-8')}>
|
||||
<div className={'mx-auto flex max-w-3xl flex-col space-y-4'}>
|
||||
<div className={cn('border-border/50 border-b py-8')}>
|
||||
<div className={'mx-auto flex max-w-3xl flex-col gap-y-2.5'}>
|
||||
<div>
|
||||
<span className={'text-muted-foreground text-xs'}>
|
||||
<DateFormatter dateString={publishedAt} />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h1
|
||||
className={
|
||||
'font-heading text-3xl font-semibold tracking-tighter xl:text-5xl dark:text-white'
|
||||
'font-heading text-2xl font-medium tracking-tighter xl:text-4xl dark:text-white'
|
||||
}
|
||||
>
|
||||
{title}
|
||||
</h1>
|
||||
|
||||
<div>
|
||||
<span className={'text-muted-foreground'}>
|
||||
<DateFormatter dateString={publishedAt} />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h2
|
||||
className={'text-muted-foreground text-base xl:text-lg'}
|
||||
className={'text-muted-foreground text-base'}
|
||||
dangerouslySetInnerHTML={{ __html: description ?? '' }}
|
||||
></h2>
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,7 @@ type Props = {
|
||||
imageHeight?: string | number;
|
||||
};
|
||||
|
||||
const DEFAULT_IMAGE_HEIGHT = 250;
|
||||
const DEFAULT_IMAGE_HEIGHT = 220;
|
||||
|
||||
export function PostPreview({
|
||||
post,
|
||||
@@ -25,41 +25,44 @@ export function PostPreview({
|
||||
const slug = `/blog/${post.slug}`;
|
||||
|
||||
return (
|
||||
<div className="transition-shadow-sm flex flex-col gap-y-4 rounded-lg duration-500">
|
||||
<Link
|
||||
href={slug}
|
||||
className="hover:bg-muted/50 active:bg-muted flex flex-col gap-y-2.5 rounded-md p-4 transition-all"
|
||||
>
|
||||
<If condition={image}>
|
||||
{(imageUrl) => (
|
||||
<div className="relative mb-2 w-full" style={{ height }}>
|
||||
<Link href={slug}>
|
||||
<CoverImage
|
||||
preloadImage={preloadImage}
|
||||
title={title}
|
||||
src={imageUrl}
|
||||
/>
|
||||
</Link>
|
||||
<CoverImage
|
||||
preloadImage={preloadImage}
|
||||
title={title}
|
||||
src={imageUrl}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</If>
|
||||
|
||||
<div className={'flex flex-col space-y-4 px-1'}>
|
||||
<div className={'flex flex-col space-y-2'}>
|
||||
<div className={'flex flex-col space-y-2'}>
|
||||
<h2 className="text-xl leading-snug font-semibold tracking-tight">
|
||||
<Link href={slug} className="hover:underline">
|
||||
{title}
|
||||
</Link>
|
||||
</h2>
|
||||
|
||||
<div className="flex flex-row items-center gap-x-3 text-sm">
|
||||
<div className="flex flex-row items-center gap-x-3 text-xs">
|
||||
<div className="text-muted-foreground">
|
||||
<DateFormatter dateString={publishedAt} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 className="text-lg leading-snug font-medium tracking-tight">
|
||||
<span className="hover:underline">{title}</span>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<p
|
||||
className="text-muted-foreground mb-4 text-sm leading-relaxed"
|
||||
dangerouslySetInnerHTML={{ __html: description ?? '' }}
|
||||
dangerouslySetInnerHTML={{ __html: trimText(description ?? '', 200) }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
function trimText(text: string, maxLength: number) {
|
||||
return text.length > maxLength ? text.slice(0, maxLength) + '...' : text;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ async function BlogPage(props: BlogPageProps) {
|
||||
subtitle={t('marketing:blogSubtitle')}
|
||||
/>
|
||||
|
||||
<div className={'container flex flex-col space-y-6 py-12'}>
|
||||
<div className={'container flex flex-col space-y-6 py-8'}>
|
||||
<If
|
||||
condition={posts.length > 0}
|
||||
fallback={<Trans i18nKey="marketing:noPosts" />}
|
||||
@@ -115,7 +115,7 @@ export default withI18n(BlogPage);
|
||||
|
||||
function PostsGridList({ children }: React.PropsWithChildren) {
|
||||
return (
|
||||
<div className="grid grid-cols-1 gap-y-8 md:grid-cols-2 md:gap-x-8 md:gap-y-12 lg:grid-cols-3 lg:gap-x-12">
|
||||
<div className="grid grid-cols-1 gap-y-8 md:grid-cols-2 md:gap-x-2 md:gap-y-12 lg:grid-cols-3">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -26,7 +26,7 @@ async function ContactPage() {
|
||||
|
||||
<div className={'container mx-auto'}>
|
||||
<div
|
||||
className={'flex flex-1 flex-col items-center justify-center py-12'}
|
||||
className={'flex flex-1 flex-col items-center justify-center py-8'}
|
||||
>
|
||||
<div
|
||||
className={
|
||||
|
||||
@@ -59,15 +59,17 @@ async function DocumentationPage({ params }: DocumentationPageProps) {
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={'flex flex-1 flex-col gap-y-4 overflow-y-hidden py-5'}>
|
||||
<div className={'flex flex-1 flex-col gap-y-4 overflow-y-hidden py-4'}>
|
||||
<div className={'flex overflow-y-hidden'}>
|
||||
<article className={cn('gap-y-12 overflow-y-auto px-6')}>
|
||||
<section className={'flex flex-col gap-y-2.5'}>
|
||||
<h1 className={'text-foreground text-3xl font-semibold'}>
|
||||
{page.title}
|
||||
</h1>
|
||||
<article className={cn('gap-y-12 overflow-y-auto px-4')}>
|
||||
<section
|
||||
className={'flex flex-col gap-y-1 border-b border-dashed pb-4'}
|
||||
>
|
||||
<h1 className={'text-foreground text-3xl'}>{page.title}</h1>
|
||||
|
||||
<h2 className={'text-muted-foreground text-lg'}>{description}</h2>
|
||||
<h2 className={'text-secondary-foreground/80 text-lg'}>
|
||||
{description}
|
||||
</h2>
|
||||
</section>
|
||||
|
||||
<div className={'markdoc'}>
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
import { ChevronRight } from 'lucide-react';
|
||||
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
|
||||
export function DocsCard({
|
||||
title,
|
||||
subtitle,
|
||||
@@ -15,11 +11,11 @@ export function DocsCard({
|
||||
link: { url: string; label?: string };
|
||||
}>) {
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
<Link href={link.url} className="flex flex-col">
|
||||
<div
|
||||
className={`bg-background flex grow flex-col gap-y-2 border p-6 ${link ? 'rounded-t-lg border-b-0' : 'rounded-lg'}`}
|
||||
className={`bg-muted/50 hover:bg-muted/70 flex grow flex-col gap-y-2 rounded p-4`}
|
||||
>
|
||||
<h3 className="mt-0 text-lg font-semibold hover:underline dark:text-white">
|
||||
<h3 className="mt-0 text-lg font-medium hover:underline dark:text-white">
|
||||
<Link href={link.url}>{title}</Link>
|
||||
</h3>
|
||||
|
||||
@@ -31,23 +27,6 @@ export function DocsCard({
|
||||
|
||||
{children && <div className="text-sm">{children}</div>}
|
||||
</div>
|
||||
|
||||
{link && (
|
||||
<div className="bg-muted/50 rounded-b-lg border p-6 py-4">
|
||||
<Link
|
||||
className={
|
||||
'flex items-center space-x-2 text-sm font-medium hover:underline'
|
||||
}
|
||||
href={link.url}
|
||||
>
|
||||
<span>
|
||||
{link.label ?? <Trans i18nKey={'marketing:readMore'} />}
|
||||
</span>
|
||||
|
||||
<ChevronRight className={'h-4'} />
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ export function DocsCards({ cards }: { cards: Cms.ContentItem[] }) {
|
||||
const cardsSortedByOrder = [...cards].sort((a, b) => a.order - b.order);
|
||||
|
||||
return (
|
||||
<div className={'grid grid-cols-1 gap-6 lg:grid-cols-2'}>
|
||||
<div className={'grid grid-cols-1 gap-4 lg:grid-cols-2'}>
|
||||
{cardsSortedByOrder.map((item) => {
|
||||
return (
|
||||
<DocsCard
|
||||
|
||||
@@ -116,7 +116,9 @@ export function DocsNavigation({
|
||||
<>
|
||||
<Sidebar
|
||||
variant={'ghost'}
|
||||
className={'sticky z-1 mt-4 max-h-full overflow-y-auto'}
|
||||
className={
|
||||
'sticky z-1 mt-4 max-h-full overflow-y-auto border-r-transparent'
|
||||
}
|
||||
>
|
||||
<SidebarGroup>
|
||||
<SidebarGroupContent>
|
||||
|
||||
@@ -13,7 +13,7 @@ export function DocsTableOfContents(props: { data: NavItem[] }) {
|
||||
const navData = props.data;
|
||||
|
||||
return (
|
||||
<div className="bg-background sticky inset-y-0 hidden h-svh max-h-full min-w-[14em] border-l p-4 lg:block">
|
||||
<div className="bg-background sticky inset-y-0 hidden h-svh max-h-full min-w-[14em] p-2.5 lg:block">
|
||||
<ol
|
||||
role="list"
|
||||
className="relative text-sm text-gray-600 dark:text-gray-400"
|
||||
|
||||
@@ -21,7 +21,7 @@ async function DocsPage() {
|
||||
const cards = items.filter((item) => !item.parentId);
|
||||
|
||||
return (
|
||||
<div className={'flex flex-col gap-y-6 xl:gap-y-10'}>
|
||||
<div className={'flex flex-col gap-y-6 xl:gap-y-8'}>
|
||||
<SitePageHeader
|
||||
title={t('marketing:documentation')}
|
||||
subtitle={t('marketing:documentationSubtitle')}
|
||||
|
||||
@@ -79,8 +79,8 @@ async function FAQPage() {
|
||||
subtitle={t('marketing:faqSubtitle')}
|
||||
/>
|
||||
|
||||
<div className={'container flex flex-col space-y-8 pb-16'}>
|
||||
<div className="flex w-full max-w-xl flex-col">
|
||||
<div className={'container flex flex-col items-center space-y-8 pb-16'}>
|
||||
<div className="divide-border flex w-full max-w-xl flex-col divide-y divide-dashed rounded-md border">
|
||||
{faqItems.map((item, index) => {
|
||||
return <FaqItem key={index} item={item} />;
|
||||
})}
|
||||
@@ -114,17 +114,15 @@ function FaqItem({
|
||||
};
|
||||
}>) {
|
||||
return (
|
||||
<details className={'group border-b px-2 py-4 last:border-b-transparent'}>
|
||||
<details
|
||||
className={
|
||||
'hover:bg-muted/70 [&:open]:bg-muted/70 [&:open]:hover:bg-muted transition-all'
|
||||
}
|
||||
>
|
||||
<summary
|
||||
className={
|
||||
'flex items-center justify-between hover:cursor-pointer hover:underline'
|
||||
}
|
||||
className={'flex items-center justify-between p-4 hover:cursor-pointer'}
|
||||
>
|
||||
<h2
|
||||
className={
|
||||
'hover:underline-none cursor-pointer font-sans font-medium'
|
||||
}
|
||||
>
|
||||
<h2 className={'cursor-pointer font-sans text-base'}>
|
||||
<Trans i18nKey={item.question} defaults={item.question} />
|
||||
</h2>
|
||||
|
||||
@@ -135,7 +133,7 @@ function FaqItem({
|
||||
</div>
|
||||
</summary>
|
||||
|
||||
<div className={'text-muted-foreground flex flex-col gap-y-3 py-1'}>
|
||||
<div className={'text-muted-foreground flex flex-col gap-y-2 px-4 pb-2'}>
|
||||
<Trans i18nKey={item.answer} defaults={item.answer} />
|
||||
</div>
|
||||
</details>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { ArrowRightIcon, LayoutDashboard } from 'lucide-react';
|
||||
import { PricingTable } from '@kit/billing-gateway/marketing';
|
||||
import {
|
||||
CtaButton,
|
||||
EcosystemShowcase,
|
||||
FeatureCard,
|
||||
FeatureGrid,
|
||||
FeatureShowcase,
|
||||
@@ -24,7 +25,7 @@ import { withI18n } from '~/lib/i18n/with-i18n';
|
||||
function Home() {
|
||||
return (
|
||||
<div className={'mt-4 flex flex-col space-y-24 py-14'}>
|
||||
<div className={'container mx-auto'}>
|
||||
<div className={'mx-auto'}>
|
||||
<Hero
|
||||
pill={
|
||||
<Pill label={'New'}>
|
||||
@@ -37,15 +38,15 @@ function Home() {
|
||||
</Pill>
|
||||
}
|
||||
title={
|
||||
<>
|
||||
<span>The ultimate SaaS Starter</span>
|
||||
<span>for your next project</span>
|
||||
</>
|
||||
<span className="text-secondary-foreground">
|
||||
<span>Ship a SaaS faster than ever.</span>
|
||||
</span>
|
||||
}
|
||||
subtitle={
|
||||
<span>
|
||||
Build and Ship a SaaS faster than ever before with the next-gen
|
||||
SaaS Starter Kit. Ship your SaaS in days, not months.
|
||||
Makerkit gives you a production-ready boilerplate to build your
|
||||
SaaS faster than ever before with the next-gen SaaS Starter Kit.
|
||||
Get started in minutes.
|
||||
</span>
|
||||
}
|
||||
cta={<MainCallToActionButton />}
|
||||
@@ -53,7 +54,7 @@ function Home() {
|
||||
<Image
|
||||
priority
|
||||
className={
|
||||
'dark:border-primary/10 rounded-xl border border-gray-200'
|
||||
'dark:border-primary/10 w-full rounded-lg border border-gray-200'
|
||||
}
|
||||
width={3558}
|
||||
height={2222}
|
||||
@@ -65,17 +66,15 @@ function Home() {
|
||||
</div>
|
||||
|
||||
<div className={'container mx-auto'}>
|
||||
<div
|
||||
className={'flex flex-col space-y-16 xl:space-y-32 2xl:space-y-36'}
|
||||
>
|
||||
<div className={'py-4 xl:py-8'}>
|
||||
<FeatureShowcase
|
||||
heading={
|
||||
<>
|
||||
<b className="font-medium tracking-tighter dark:text-white">
|
||||
<b className="font-medium tracking-tight dark:text-white">
|
||||
The ultimate SaaS Starter Kit
|
||||
</b>
|
||||
.{' '}
|
||||
<span className="text-muted-foreground font-normal tracking-tighter">
|
||||
<span className="text-secondary-foreground/70 block font-normal tracking-tight">
|
||||
Unleash your creativity and build your SaaS faster than ever
|
||||
with Makerkit.
|
||||
</span>
|
||||
@@ -83,7 +82,7 @@ function Home() {
|
||||
}
|
||||
icon={
|
||||
<FeatureShowcaseIconContainer>
|
||||
<LayoutDashboard className="h-5" />
|
||||
<LayoutDashboard className="h-4 w-4" />
|
||||
<span>All-in-one solution</span>
|
||||
</FeatureShowcaseIconContainer>
|
||||
}
|
||||
@@ -108,7 +107,7 @@ function Home() {
|
||||
/>
|
||||
|
||||
<FeatureCard
|
||||
className={'relative col-span-1 overflow-hidden md:col-span-2'}
|
||||
className={'relative col-span-1 overflow-hidden'}
|
||||
label={'Billing'}
|
||||
description={`Makerkit supports multiple payment gateways to charge your customers.`}
|
||||
/>
|
||||
@@ -118,15 +117,36 @@ function Home() {
|
||||
label={'Plugins'}
|
||||
description={`Extend your SaaS with plugins that you can install using the CLI.`}
|
||||
/>
|
||||
|
||||
<FeatureCard
|
||||
className={'relative col-span-1 overflow-hidden'}
|
||||
label={'Documentation'}
|
||||
description={`Makerkit provides a comprehensive documentation to help you get started.`}
|
||||
/>
|
||||
</FeatureGrid>
|
||||
</FeatureShowcase>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={'container mx-auto'}>
|
||||
<EcosystemShowcase
|
||||
heading="The ultimate SaaS Starter Kit for founders."
|
||||
description="Unleash your creativity and build your SaaS faster than ever with Makerkit. Get started in minutes and ship your SaaS in no time."
|
||||
>
|
||||
<Image
|
||||
className="rounded-md"
|
||||
src={'/images/sign-in.webp'}
|
||||
alt="Sign in"
|
||||
width={1000}
|
||||
height={1000}
|
||||
/>
|
||||
</EcosystemShowcase>
|
||||
</div>
|
||||
|
||||
<div className={'container mx-auto'}>
|
||||
<div
|
||||
className={
|
||||
'flex flex-col items-center justify-center space-y-16 py-16'
|
||||
'flex flex-col items-center justify-center space-y-12 py-4 xl:py-8'
|
||||
}
|
||||
>
|
||||
<SecondaryHero
|
||||
@@ -154,8 +174,8 @@ export default withI18n(Home);
|
||||
|
||||
function MainCallToActionButton() {
|
||||
return (
|
||||
<div className={'flex space-x-4'}>
|
||||
<CtaButton>
|
||||
<div className={'flex space-x-2.5'}>
|
||||
<CtaButton className="h-10 text-sm">
|
||||
<Link href={'/auth/sign-up'}>
|
||||
<span className={'flex items-center space-x-0.5'}>
|
||||
<span>
|
||||
@@ -172,9 +192,9 @@ function MainCallToActionButton() {
|
||||
</Link>
|
||||
</CtaButton>
|
||||
|
||||
<CtaButton variant={'link'}>
|
||||
<Link href={'/contact'}>
|
||||
<Trans i18nKey={'common:contactUs'} />
|
||||
<CtaButton variant={'link'} className="h-10 text-sm">
|
||||
<Link href={'/pricing'}>
|
||||
<Trans i18nKey={'common:pricing'} />
|
||||
</Link>
|
||||
</CtaButton>
|
||||
</div>
|
||||
|
||||
@@ -23,7 +23,7 @@ async function PricingPage() {
|
||||
const { t } = await createI18nServerInstance();
|
||||
|
||||
return (
|
||||
<div className={'flex flex-col space-y-12'}>
|
||||
<div className={'flex flex-col space-y-8'}>
|
||||
<SitePageHeader
|
||||
title={t('marketing:pricing')}
|
||||
subtitle={t('marketing:pricingSubtitle')}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { PlanSchema, ProductSchema } from '@kit/billing';
|
||||
import { resolveProductPlan } from '@kit/billing-gateway';
|
||||
import {
|
||||
BillingPortalCard,
|
||||
@@ -38,16 +35,22 @@ async function PersonalAccountBillingPage() {
|
||||
const [subscription, order, customerId] =
|
||||
await loadPersonalAccountBillingPageData(user.id);
|
||||
|
||||
const subscriptionProductPlan = subscription
|
||||
? await getProductPlan(
|
||||
subscription.items[0]?.variant_id,
|
||||
subscription.currency,
|
||||
)
|
||||
: undefined;
|
||||
const subscriptionVariantId = subscription?.items[0]?.variant_id;
|
||||
const orderVariantId = order?.items[0]?.variant_id;
|
||||
|
||||
const orderProductPlan = order
|
||||
? await getProductPlan(order.items[0]?.variant_id, order.currency)
|
||||
: undefined;
|
||||
const subscriptionProductPlan =
|
||||
subscription && subscriptionVariantId
|
||||
? await resolveProductPlan(
|
||||
billingConfig,
|
||||
subscriptionVariantId,
|
||||
subscription.currency,
|
||||
)
|
||||
: undefined;
|
||||
|
||||
const orderProductPlan =
|
||||
order && orderVariantId
|
||||
? await resolveProductPlan(billingConfig, orderVariantId, order.currency)
|
||||
: undefined;
|
||||
|
||||
const hasBillingData = subscription || order;
|
||||
|
||||
@@ -59,7 +62,7 @@ async function PersonalAccountBillingPage() {
|
||||
/>
|
||||
|
||||
<PageBody>
|
||||
<div className={'flex flex-col space-y-4'}>
|
||||
<div className={'flex max-w-2xl flex-col space-y-4'}>
|
||||
<If
|
||||
condition={hasBillingData}
|
||||
fallback={
|
||||
@@ -68,7 +71,7 @@ async function PersonalAccountBillingPage() {
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div className={'flex w-full max-w-2xl flex-col space-y-6'}>
|
||||
<div className={'flex w-full flex-col space-y-6'}>
|
||||
<If condition={subscription}>
|
||||
{(subscription) => {
|
||||
return (
|
||||
@@ -95,9 +98,7 @@ async function PersonalAccountBillingPage() {
|
||||
</div>
|
||||
</If>
|
||||
|
||||
<If condition={customerId}>
|
||||
<CustomerBillingPortalForm />
|
||||
</If>
|
||||
<If condition={customerId}>{() => <CustomerBillingPortalForm />}</If>
|
||||
</div>
|
||||
</PageBody>
|
||||
</>
|
||||
@@ -113,20 +114,3 @@ function CustomerBillingPortalForm() {
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
async function getProductPlan(
|
||||
variantId: string | undefined,
|
||||
currency: string,
|
||||
): Promise<
|
||||
| {
|
||||
product: ProductSchema;
|
||||
plan: z.infer<typeof PlanSchema>;
|
||||
}
|
||||
| undefined
|
||||
> {
|
||||
if (!variantId) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return resolveProductPlan(billingConfig, variantId, currency);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { ExclamationTriangleIcon } from '@radix-ui/react-icons';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { PlanSchema, ProductSchema } from '@kit/billing';
|
||||
import { resolveProductPlan } from '@kit/billing-gateway';
|
||||
import {
|
||||
BillingPortalCard,
|
||||
@@ -47,15 +45,15 @@ async function TeamAccountBillingPage({ params }: TeamAccountBillingPageProps) {
|
||||
const [subscription, order, customerId] =
|
||||
await loadTeamAccountBillingPage(accountId);
|
||||
|
||||
const subscriptionProductPlan = subscription
|
||||
? await getProductPlan(
|
||||
subscription.items[0]?.variant_id,
|
||||
subscription.currency,
|
||||
)
|
||||
const variantId = subscription?.items[0]?.variant_id;
|
||||
const orderVariantId = order?.items[0]?.variant_id;
|
||||
|
||||
const subscriptionProductPlan = variantId
|
||||
? await resolveProductPlan(billingConfig, variantId, subscription.currency)
|
||||
: undefined;
|
||||
|
||||
const orderProductPlan = order
|
||||
? await getProductPlan(order.items[0]?.variant_id, order.currency)
|
||||
const orderProductPlan = orderVariantId
|
||||
? await resolveProductPlan(billingConfig, orderVariantId, order.currency)
|
||||
: undefined;
|
||||
|
||||
const hasBillingData = subscription || order;
|
||||
@@ -97,11 +95,7 @@ async function TeamAccountBillingPage({ params }: TeamAccountBillingPageProps) {
|
||||
/>
|
||||
|
||||
<PageBody>
|
||||
<div
|
||||
className={cn(`flex w-full flex-col space-y-4`, {
|
||||
'max-w-2xl': hasBillingData,
|
||||
})}
|
||||
>
|
||||
<div className={cn(`flex max-w-2xl flex-col space-y-4`)}>
|
||||
<If condition={!hasBillingData}>
|
||||
<Checkout />
|
||||
</If>
|
||||
@@ -154,20 +148,3 @@ function CannotManageBillingAlert() {
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
async function getProductPlan(
|
||||
variantId: string | undefined,
|
||||
currency: string,
|
||||
): Promise<
|
||||
| {
|
||||
product: ProductSchema;
|
||||
plan: z.infer<typeof PlanSchema>;
|
||||
}
|
||||
| undefined
|
||||
> {
|
||||
if (!variantId) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return resolveProductPlan(billingConfig, variantId, currency);
|
||||
}
|
||||
|
||||
@@ -77,8 +77,8 @@
|
||||
"@kit/prettier-config": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@next/bundle-analyzer": "15.5.4",
|
||||
"@tailwindcss/postcss": "^4.1.13",
|
||||
"@types/node": "^24.6.1",
|
||||
"@tailwindcss/postcss": "^4.1.14",
|
||||
"@types/node": "^24.6.2",
|
||||
"@types/react": "19.1.16",
|
||||
"@types/react-dom": "19.1.9",
|
||||
"babel-plugin-react-compiler": "19.1.0-rc.3",
|
||||
@@ -86,7 +86,7 @@
|
||||
"pino-pretty": "13.0.0",
|
||||
"prettier": "^3.6.2",
|
||||
"supabase": "2.47.2",
|
||||
"tailwindcss": "4.1.13",
|
||||
"tailwindcss": "4.1.14",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"typescript": "^5.9.3"
|
||||
},
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 22 KiB |
@@ -5,7 +5,8 @@
|
||||
"planRenewal": "Renews every {{interval}} at {{price}}",
|
||||
"planDetails": "Plan Details",
|
||||
"checkout": "Proceed to Checkout",
|
||||
"trialEndsOn": "Your trial ends on",
|
||||
"trialAlertTitle": "Your Trial is ending soon",
|
||||
"trialAlertDescription": "Your trial ends on {{date}}. Upgrade to a paid plan to continue using all features.",
|
||||
"billingPortalCardButton": "Visit Billing Portal",
|
||||
"billingPortalCardTitle": "Manage your Billing Details",
|
||||
"billingPortalCardDescription": "Visit your Billing Portal to manage your subscription and billing. You can update or cancel your plan, or download your invoices.",
|
||||
@@ -45,6 +46,7 @@
|
||||
"forEveryUnit": "for every {{ unit }}",
|
||||
"setupFee": "plus a {{ setupFee }} setup fee",
|
||||
"perUnitIncluded": "({{included}} included)",
|
||||
"features": "Features",
|
||||
"featuresLabel": "Features",
|
||||
"detailsLabel": "Details",
|
||||
"planPickerLabel": "Pick your preferred plan",
|
||||
@@ -52,7 +54,7 @@
|
||||
"planPickerAlertErrorTitle": "Error requesting checkout",
|
||||
"planPickerAlertErrorDescription": "There was an error requesting checkout. Please try again later.",
|
||||
"subscriptionCancelled": "Subscription Cancelled",
|
||||
"cancelSubscriptionDate": "Your subscription will be cancelled at the end of the period",
|
||||
"cancelSubscriptionDate": "Your subscription will be cancelled at the end of the billing period on {{date}}",
|
||||
"noPlanChosen": "Please choose a plan",
|
||||
"noIntervalPlanChosen": "Please choose a billing interval",
|
||||
"status": {
|
||||
|
||||
@@ -31,7 +31,9 @@
|
||||
@layer base {
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
font-feature-settings: "rlig" 1, "calt" 1;
|
||||
font-feature-settings:
|
||||
'rlig' 1,
|
||||
'calt' 1;
|
||||
}
|
||||
|
||||
*,
|
||||
@@ -46,4 +48,4 @@
|
||||
textarea::placeholder {
|
||||
color: theme(--color-muted-foreground);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,137 +6,138 @@
|
||||
*/
|
||||
|
||||
.markdoc {
|
||||
@apply text-foreground;
|
||||
@apply text-foreground;
|
||||
}
|
||||
|
||||
.markdoc h1 {
|
||||
@apply mt-8 lg:mt-14 text-4xl font-semibold font-heading tracking-tight dark:text-white text-foreground;
|
||||
@apply font-heading text-foreground text-3xl font-medium tracking-tight lg:mt-14 dark:text-white;
|
||||
}
|
||||
|
||||
.markdoc h2 {
|
||||
@apply mb-3 lg:mb-6 mt-6 lg:mt-12 font-semibold text-2xl font-heading tracking-tight dark:text-white text-foreground;
|
||||
@apply font-heading text-foreground text-2xl font-medium tracking-tight lg:mt-6 lg:mb-3 dark:text-white;
|
||||
}
|
||||
|
||||
.markdoc h3 {
|
||||
@apply mt-6 lg:mt-12 text-xl font-semibold font-heading tracking-tight dark:text-white text-foreground;
|
||||
@apply font-heading text-foreground text-xl font-medium tracking-tight lg:mt-12 dark:text-white;
|
||||
}
|
||||
|
||||
.markdoc h4 {
|
||||
@apply mt-4 lg:mt-8 text-lg font-medium tracking-tight dark:text-white text-foreground;
|
||||
@apply text-foreground mt-4 text-lg font-medium tracking-tight lg:mt-8 dark:text-white;
|
||||
}
|
||||
|
||||
.markdoc h5 {
|
||||
@apply mt-3 lg:mt-6 text-base font-medium tracking-tight dark:text-white text-foreground;
|
||||
@apply text-foreground mt-3 text-base font-medium tracking-tight lg:mt-6 dark:text-white;
|
||||
}
|
||||
|
||||
.markdoc h6 {
|
||||
@apply mt-2 text-sm font-normal tracking-tight dark:text-white text-foreground;
|
||||
@apply text-foreground mt-2 text-sm font-normal tracking-tight dark:text-white;
|
||||
}
|
||||
|
||||
.markdoc p {
|
||||
@apply mb-3 lg:mb-6 mt-2 lg:mt-4 text-base leading-7 text-muted-foreground;
|
||||
@apply text-muted-foreground my-3 text-base leading-7;
|
||||
}
|
||||
|
||||
.markdoc li {
|
||||
@apply relative my-1.5 text-base leading-7 text-muted-foreground;
|
||||
@apply text-muted-foreground relative my-0.5 text-base leading-7;
|
||||
}
|
||||
|
||||
.markdoc ul > li:before {
|
||||
content: '-';
|
||||
content: '-';
|
||||
|
||||
@apply mr-2;
|
||||
@apply mr-2;
|
||||
}
|
||||
|
||||
.markdoc ol > li:before {
|
||||
@apply inline-flex font-medium text-muted-foreground;
|
||||
@apply text-secondary-foreground inline-flex font-medium;
|
||||
|
||||
content: counters(counts, '.') '. ';
|
||||
font-feature-settings: 'tnum';
|
||||
content: counters(counts, '.') '. ';
|
||||
font-feature-settings: 'tnum';
|
||||
}
|
||||
|
||||
.markdoc b,
|
||||
.markdoc strong {
|
||||
@apply font-semibold text-secondary-foreground dark:text-white;
|
||||
@apply text-secondary-foreground font-semibold dark:text-white;
|
||||
}
|
||||
|
||||
.markdoc img,
|
||||
.markdoc video {
|
||||
@apply rounded-md;
|
||||
@apply rounded-md;
|
||||
}
|
||||
|
||||
.markdoc ul,
|
||||
.markdoc ol {
|
||||
@apply pl-1;
|
||||
@apply pl-1;
|
||||
}
|
||||
|
||||
.markdoc ol > li {
|
||||
counter-increment: counts;
|
||||
counter-increment: counts;
|
||||
}
|
||||
|
||||
.markdoc ol > li:before {
|
||||
@apply mr-2 inline-flex font-semibold;
|
||||
@apply mr-2 inline-flex font-semibold;
|
||||
|
||||
content: counters(counts, '.') '. ';
|
||||
font-feature-settings: 'tnum';
|
||||
content: counters(counts, '.') '. ';
|
||||
font-feature-settings: 'tnum';
|
||||
}
|
||||
|
||||
.markdoc p > code, .markdoc li > code {
|
||||
@apply p-0.5 text-sm font-semibold bg-muted/50 border font-mono text-secondary-foreground;
|
||||
.markdoc p > code,
|
||||
.markdoc li > code {
|
||||
@apply bg-muted/50 text-secondary-foreground border p-0.5 font-mono text-sm font-semibold;
|
||||
}
|
||||
|
||||
.markdoc pre {
|
||||
@apply overflow-x-auto bg-muted/50 rounded-md border border-border p-4 text-sm font-mono text-foreground;
|
||||
@apply bg-muted/50 border-border text-foreground my-4 overflow-x-auto rounded-md border p-4 font-mono text-sm;
|
||||
}
|
||||
|
||||
.markdoc blockquote {
|
||||
@apply my-4 border-l-8 border border-primary px-6 py-4 text-lg font-medium text-muted-foreground;
|
||||
@apply border-primary text-muted-foreground my-4 border border-l-8 px-6 py-4 text-lg font-medium;
|
||||
}
|
||||
|
||||
.markdoc a {
|
||||
@apply border-b-black border-b hover:border-b-2 pb-0.5 text-secondary-foreground font-semibold dark:border-yellow-300;
|
||||
@apply text-secondary-foreground border-b border-b-black pb-0.5 font-semibold hover:border-b-2 dark:border-yellow-300;
|
||||
}
|
||||
|
||||
.markdoc hr {
|
||||
@apply mt-8 mb-6 border-border;
|
||||
@apply border-border mt-8 mb-6;
|
||||
}
|
||||
|
||||
.markdoc [role='alert'] {
|
||||
@apply py-4 m-0 my-8;
|
||||
@apply m-0 my-8 py-4;
|
||||
}
|
||||
|
||||
.markdoc [role='alert'] * {
|
||||
color: inherit;
|
||||
@apply m-0 p-0 text-sm;
|
||||
color: inherit;
|
||||
@apply m-0 p-0 text-sm;
|
||||
}
|
||||
|
||||
.markdoc [role='alert'] h5 {
|
||||
color: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.markdoc table {
|
||||
@apply w-full caption-bottom text-sm my-4;
|
||||
@apply my-4 w-full caption-bottom text-sm;
|
||||
}
|
||||
|
||||
.markdoc th {
|
||||
@apply [&_tr]:border-b;
|
||||
@apply [&_tr]:border-b;
|
||||
}
|
||||
|
||||
.markdoc tbody {
|
||||
@apply [&_tr:last-child]:border-0;
|
||||
@apply [&_tr:last-child]:border-0;
|
||||
}
|
||||
|
||||
.markdoc tfoot {
|
||||
@apply bg-muted/50 border-t font-medium [&>tr]:last:border-b-0;
|
||||
@apply bg-muted/50 border-t font-medium [&>tr]:last:border-b-0;
|
||||
}
|
||||
|
||||
.markdoc tr {
|
||||
@apply data-[state=selected]:bg-muted border-b transition-colors;
|
||||
@apply data-[state=selected]:bg-muted border-b transition-colors;
|
||||
}
|
||||
|
||||
.markdoc th {
|
||||
@apply text-muted-foreground h-10 px-2 text-left align-middle font-medium [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px];
|
||||
@apply text-muted-foreground h-10 px-2 text-left align-middle font-medium [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px];
|
||||
}
|
||||
|
||||
.markdoc td {
|
||||
@apply p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px];
|
||||
}
|
||||
@apply p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px];
|
||||
}
|
||||
|
||||
@@ -7,98 +7,100 @@
|
||||
*/
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||
--font-heading: var(--font-sans);
|
||||
:root {
|
||||
--font-sans:
|
||||
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial,
|
||||
sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
||||
--font-heading: var(--font-sans);
|
||||
|
||||
--background: var(--color-white);
|
||||
--foreground: var(--color-neutral-950);
|
||||
--background: var(--color-white);
|
||||
--foreground: var(--color-neutral-950);
|
||||
|
||||
--card: var(--color-white);
|
||||
--card-foreground: var(--color-neutral-950);
|
||||
--card: var(--color-white);
|
||||
--card-foreground: var(--color-neutral-950);
|
||||
|
||||
--popover: var(--color-white);
|
||||
--popover-foreground: var(--color-neutral-950);
|
||||
--popover: var(--color-white);
|
||||
--popover-foreground: var(--color-neutral-950);
|
||||
|
||||
--primary: var(--color-neutral-950);
|
||||
--primary-foreground: var(--color-white);
|
||||
--primary: var(--color-neutral-950);
|
||||
--primary-foreground: var(--color-white);
|
||||
|
||||
--secondary: oklch(96.76% 0.0013 286.38);
|
||||
--secondary-foreground: oklch(21.03% 0.0318 264.65);
|
||||
--secondary: oklch(96.76% 0.0013 286.38);
|
||||
--secondary-foreground: oklch(21.03% 0.0318 264.65);
|
||||
|
||||
--muted: oklch(96.71% 0.0029 264.54);
|
||||
--muted-foreground: oklch(55.13% 0.0233 264.36);
|
||||
--muted: oklch(96.71% 0.0029 264.54);
|
||||
--muted-foreground: oklch(55.13% 0.0233 264.36);
|
||||
|
||||
--accent: oklch(96.76% 0.0013 286.38);
|
||||
--accent-foreground: oklch(21.03% 0.0318 264.65);
|
||||
--accent: oklch(96.76% 0.0013 286.38);
|
||||
--accent-foreground: oklch(21.03% 0.0318 264.65);
|
||||
|
||||
--destructive: var(--color-red-500);
|
||||
--destructive-foreground: var(--color-white);
|
||||
--destructive: var(--color-red-500);
|
||||
--destructive-foreground: var(--color-white);
|
||||
|
||||
--border: var(--color-gray-100);
|
||||
--input: var(--color-gray-200);
|
||||
--ring: var(--color-neutral-800);
|
||||
--border: var(--color-gray-100);
|
||||
--input: var(--color-gray-200);
|
||||
--ring: var(--color-neutral-800);
|
||||
|
||||
--radius: 0.5rem;
|
||||
--radius: 0.5rem;
|
||||
|
||||
--chart-1: var(--color-orange-400);
|
||||
--chart-2: var(--color-teal-600);
|
||||
--chart-3: var(--color-green-800);
|
||||
--chart-4: var(--color-yellow-200);
|
||||
--chart-5: var(--color-orange-200);
|
||||
--chart-1: var(--color-orange-400);
|
||||
--chart-2: var(--color-teal-600);
|
||||
--chart-3: var(--color-green-800);
|
||||
--chart-4: var(--color-yellow-200);
|
||||
--chart-5: var(--color-orange-200);
|
||||
|
||||
--sidebar-background: var(--color-neutral-50);
|
||||
--sidebar-foreground: oklch(37.05% 0.012 285.8);
|
||||
--sidebar-primary: var(--color-neutral-950);
|
||||
--sidebar-primary-foreground: var(--color-white);
|
||||
--sidebar-accent: var(--color-neutral-100);
|
||||
--sidebar-accent-foreground: var(--color-neutral-950);
|
||||
--sidebar-border: var(--border);
|
||||
--sidebar-ring: var(--color-blue-500);
|
||||
}
|
||||
--sidebar-background: var(--color-neutral-50);
|
||||
--sidebar-foreground: oklch(37.05% 0.012 285.8);
|
||||
--sidebar-primary: var(--color-neutral-950);
|
||||
--sidebar-primary-foreground: var(--color-white);
|
||||
--sidebar-accent: var(--color-neutral-100);
|
||||
--sidebar-accent-foreground: var(--color-neutral-950);
|
||||
--sidebar-border: var(--border);
|
||||
--sidebar-ring: var(--color-blue-500);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--background: var(--color-neutral-900);
|
||||
--foreground: var(--color-white);
|
||||
.dark {
|
||||
--background: var(--color-neutral-900);
|
||||
--foreground: var(--color-white);
|
||||
|
||||
--card: var(--color-neutral-900);
|
||||
--card-foreground: var(--color-white);
|
||||
--card: var(--color-neutral-900);
|
||||
--card-foreground: var(--color-white);
|
||||
|
||||
--popover: var(--color-neutral-900);
|
||||
--popover-foreground: var(--color-white);
|
||||
--popover: var(--color-neutral-900);
|
||||
--popover-foreground: var(--color-white);
|
||||
|
||||
--primary: var(--color-white);
|
||||
--primary-foreground: var(--color-neutral-900);
|
||||
--primary: var(--color-white);
|
||||
--primary-foreground: var(--color-neutral-900);
|
||||
|
||||
--secondary: var(--color-neutral-800);
|
||||
--secondary-foreground: oklch(98.43% 0.0017 247.84);
|
||||
--secondary: var(--color-neutral-800);
|
||||
--secondary-foreground: oklch(98.43% 0.0017 247.84);
|
||||
|
||||
--muted: var(--color-neutral-800);
|
||||
--muted-foreground: oklch(71.19% 0.0129 286.07);
|
||||
--muted: var(--color-neutral-800);
|
||||
--muted-foreground: oklch(71.19% 0.0129 286.07);
|
||||
|
||||
--accent: var(--color-neutral-800);
|
||||
--accent-foreground: oklch(98.48% 0 0);
|
||||
--accent: var(--color-neutral-800);
|
||||
--accent-foreground: oklch(98.48% 0 0);
|
||||
|
||||
--destructive: var(--color-red-700);
|
||||
--destructive-foreground: var(--color-white);
|
||||
--destructive: var(--color-red-700);
|
||||
--destructive-foreground: var(--color-white);
|
||||
|
||||
--border: var(--color-neutral-800);
|
||||
--input: var(--color-neutral-700);
|
||||
--ring: oklch(87.09% 0.0055 286.29);
|
||||
--border: var(--color-neutral-800);
|
||||
--input: var(--color-neutral-700);
|
||||
--ring: oklch(87.09% 0.0055 286.29);
|
||||
|
||||
--chart-1: var(--color-blue-600);
|
||||
--chart-2: var(--color-emerald-400);
|
||||
--chart-3: var(--color-orange-400);
|
||||
--chart-4: var(--color-purple-500);
|
||||
--chart-5: var(--color-pink-500);
|
||||
--chart-1: var(--color-blue-600);
|
||||
--chart-2: var(--color-emerald-400);
|
||||
--chart-3: var(--color-orange-400);
|
||||
--chart-4: var(--color-purple-500);
|
||||
--chart-5: var(--color-pink-500);
|
||||
|
||||
--sidebar-background: var(--color-neutral-900);
|
||||
--sidebar-foreground: var(--color-white);
|
||||
--sidebar-primary: var(--color-blue-500);
|
||||
--sidebar-primary-foreground: var(--color-white);
|
||||
--sidebar-accent: var(--color-neutral-800);
|
||||
--sidebar-accent-foreground: var(--color-white);
|
||||
--sidebar-border: var(--border);
|
||||
--sidebar-ring: var(--color-blue-500);
|
||||
}
|
||||
}
|
||||
--sidebar-background: var(--color-neutral-900);
|
||||
--sidebar-foreground: var(--color-white);
|
||||
--sidebar-primary: var(--color-blue-500);
|
||||
--sidebar-primary-foreground: var(--color-white);
|
||||
--sidebar-accent: var(--color-neutral-800);
|
||||
--sidebar-accent-foreground: var(--color-white);
|
||||
--sidebar-border: var(--border);
|
||||
--sidebar-ring: var(--color-blue-500);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user