Tailwind CSS 4 Migration (#100)

* Updated to TailwindCSS v4
* Moved CSS module to its own CSS file because of lightingcss strict validation
* Respect next parameter in middleware
* Updated all packages. 
* Split CSSs for better organization.
* Redesigned theme and auth pages
* Improved pill and header design
* Formatted files using Prettier
* Better footer layout
* Better auth layout
* Bump version of the repository to 2.0.0
This commit is contained in:
Giancarlo Buomprisco
2025-01-28 13:19:52 +07:00
committed by GitHub
parent d799f54ede
commit 4e91f267e0
109 changed files with 1347 additions and 1178 deletions

View File

@@ -5,8 +5,6 @@ import Link from 'next/link';
import type { User } from '@supabase/supabase-js';
import { ArrowRightIcon } from 'lucide-react';
import { PersonalAccountDropdown } from '@kit/accounts/personal-account-dropdown';
import { useSignOut } from '@kit/supabase/hooks/use-sign-out';
import { useUser } from '@kit/supabase/hooks/use-user';
@@ -65,30 +63,26 @@ function SuspendedPersonalAccountDropdown(props: { user: User | null }) {
function AuthButtons() {
return (
<div className={'flex space-x-2'}>
<div className={'hidden space-x-0.5 md:flex'}>
<div className={'flex gap-x-2.5'}>
<div className={'hidden md:flex'}>
<If condition={features.enableThemeToggle}>
<ModeToggle />
</If>
</div>
<Button asChild variant={'ghost'}>
<div className={'flex gap-x-2.5'}>
<Button className={'hidden md:block'} asChild variant={'ghost'}>
<Link href={pathsConfig.auth.signIn}>
<Trans i18nKey={'auth:signIn'} />
</Link>
</Button>
<Button asChild className="group" variant={'default'}>
<Link href={pathsConfig.auth.signUp}>
<Trans i18nKey={'auth:signUp'} />
</Link>
</Button>
</div>
<Button asChild className="group" variant={'default'}>
<Link href={pathsConfig.auth.signUp}>
<Trans i18nKey={'auth:signUp'} />
<ArrowRightIcon
className={
'ml-1 hidden h-4 w-4 transition-transform duration-500 group-hover:translate-x-1 lg:block'
}
/>
</Link>
</Button>
</div>
);
}

View File

@@ -48,8 +48,8 @@ export function SiteNavigation() {
return (
<>
<div className={'hidden items-center justify-center md:flex'}>
<NavigationMenu className={'px-4 py-2'}>
<NavigationMenuList className={'space-x-5'}>
<NavigationMenu>
<NavigationMenuList className={'gap-x-2.5'}>
{NavItems}
</NavigationMenuList>
</NavigationMenu>

View File

@@ -15,12 +15,10 @@ export function SitePageHeader({
return (
<div className={cn('border-b py-8 xl:py-10 2xl:py-12', className)}>
<div
className={cn('flex flex-col space-y-2 lg:space-y-4', containerClass)}
>
<div className={cn('flex flex-col gap-y-3 lg:gap-y-4', containerClass)}>
<h1
className={
'font-heading text-3xl font-medium tracking-tighter dark:text-white xl:text-5xl'
'font-heading text-3xl font-medium tracking-tighter xl:text-5xl dark:text-white'
}
>
{title}
@@ -28,7 +26,7 @@ export function SitePageHeader({
<h2
className={
'text-lg tracking-tight text-muted-foreground 2xl:text-2xl'
'text-muted-foreground text-lg tracking-tight 2xl:text-2xl'
}
>
{subtitle}

View File

@@ -1,126 +0,0 @@
.HTML {
@apply text-secondary-foreground;
}
.HTML h1 {
@apply mt-14 text-4xl font-semibold font-heading tracking-tight;
}
.HTML h2 {
@apply mb-6 mt-12 font-semibold text-2xl font-heading tracking-tight;
}
.HTML h3 {
@apply mt-12 text-xl font-semibold font-heading tracking-tight;
}
.HTML h4 {
@apply mt-8 text-lg font-medium tracking-tight;
}
.HTML h5 {
@apply mt-6 text-base font-medium tracking-tight;
}
.HTML h6 {
@apply mt-2 text-sm font-normal tracking-tight;
}
/**
Tailwind "dark" variants do not work with CSS Modules
We work it around using :global(.dark)
For more info: https://github.com/tailwindlabs/tailwindcss/issues/3258#issuecomment-770215347
*/
:global(.dark) .HTML h1,
:global(.dark) .HTML h2,
:global(.dark) .HTML h3,
:global(.dark) .HTML h4,
:global(.dark) .HTML h5,
:global(.dark) .HTML h6 {
@apply text-white;
}
.HTML p {
@apply mb-6 mt-4 text-base leading-7 text-muted-foreground;
}
.HTML li {
@apply relative my-1.5 text-base leading-7 text-muted-foreground;
}
.HTML ul > li:before {
content: '-';
@apply mr-2;
}
.HTML ol > li:before {
@apply inline-flex font-medium text-muted-foreground;
content: counters(counts, '.') '. ';
font-feature-settings: 'tnum';
}
.HTML b,
.HTML strong {
@apply font-semibold text-secondary-foreground;
}
:global(.dark) .HTML b,
:global(.dark) .HTML strong {
@apply text-white;
}
.HTML img,
.HTML video {
@apply rounded-md;
}
.HTML ul,
.HTML ol {
@apply pl-1;
}
.HTML ol > li {
counter-increment: counts;
}
.HTML ol > li:before {
@apply mr-2 inline-flex font-semibold;
content: counters(counts, '.') '. ';
font-feature-settings: 'tnum';
}
.HTML p > code, .HTML li > code {
@apply p-0.5 text-sm font-semibold bg-muted/50 border font-mono text-secondary-foreground;
}
.HTML blockquote {
@apply my-4 border-l-8 border border-primary px-6 py-4 text-lg font-medium text-muted-foreground;
}
.HTML a {
@apply border-b-black border-b hover:border-b-2 pb-0.5 text-secondary-foreground font-semibold;
}
:global(.dark) .HTML a {
@apply border-yellow-300;
}
.HTML hr {
@apply mt-8 mb-6 border-border;
}
.HTML [role='alert'] {
@apply py-4 m-0 my-8;
}
.HTML [role='alert'] * {
color: inherit;
@apply m-0 p-0 text-sm;
}
.HTML [role='alert'] h5 {
color: inherit;
}

View File

@@ -25,7 +25,7 @@ export function PostPreview({
const slug = `/blog/${post.slug}`;
return (
<div className="flex flex-col space-y-4 rounded-lg transition-shadow duration-500">
<div className="transition-shadow-sm flex flex-col gap-y-4 rounded-lg duration-500">
<If condition={image}>
{(imageUrl) => (
<div className="relative mb-2 w-full" style={{ height }}>
@@ -42,13 +42,13 @@ export function PostPreview({
<div className={'flex flex-col space-y-4 px-1'}>
<div className={'flex flex-col space-y-2'}>
<h2 className="text-2xl font-semibold leading-snug tracking-tight">
<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 space-x-2 text-sm">
<div className="flex flex-row items-center gap-x-3 text-sm">
<div className="text-muted-foreground">
<DateFormatter dateString={publishedAt} />
</div>
@@ -56,7 +56,7 @@ export function PostPreview({
</div>
<p
className="mb-4 text-sm leading-relaxed text-muted-foreground"
className="text-muted-foreground mb-4 text-sm leading-relaxed"
dangerouslySetInnerHTML={{ __html: description ?? '' }}
/>
</div>

View File

@@ -1,7 +1,6 @@
import type { Cms } from '@kit/cms';
import { ContentRenderer } from '@kit/cms';
import styles from './html-renderer.module.css';
import { PostHeader } from './post-header';
export function Post({
@@ -16,7 +15,7 @@ export function Post({
<PostHeader post={post} />
<div className={'mx-auto flex max-w-3xl flex-col space-y-6 py-8'}>
<article className={styles.HTML}>
<article className="markdoc">
<ContentRenderer content={content} />
</article>
</div>

View File

@@ -9,8 +9,6 @@ import { cn } from '@kit/ui/utils';
import { withI18n } from '~/lib/i18n/with-i18n';
// styles
import styles from '../../blog/_components/html-renderer.module.css';
// local imports
import { DocsCards } from '../_components/docs-cards';
import { DocsTableOfContents } from '../_components/docs-table-of-contents';
@@ -61,20 +59,18 @@ async function DocumentationPage({ params }: DocumentationPageProps) {
);
return (
<div className={'flex flex-1 flex-col space-y-4 overflow-y-hidden'}>
<div className={'flex flex-1 flex-col gap-y-4 overflow-y-hidden py-5'}>
<div className={'flex overflow-y-hidden'}>
<article
className={cn(styles.HTML, 'container space-y-12 overflow-y-auto')}
>
<section className={'flex flex-col space-y-4 pt-6'}>
<h1 className={'!my-0'}>{page.title}</h1>
<article className={cn('gap-y-12 overflow-y-auto px-6')}>
<section className={'flex flex-col gap-y-2.5'}>
<h1 className={'text-3xl font-semibold text-foreground'}>{page.title}</h1>
<h2 className={'!mb-0 !font-normal !text-muted-foreground'}>
{description}
</h2>
<h2 className={'text-muted-foreground text-lg'}>{description}</h2>
</section>
<ContentRenderer content={page.content} />
<div className={'markdoc'}>
<ContentRenderer content={page.content} />
</div>
</article>
<DocsTableOfContents data={headings} />

View File

@@ -17,14 +17,14 @@ export function DocsCard({
return (
<div className="flex flex-col">
<div
className={`flex grow flex-col space-y-2.5 border bg-background p-6 ${link ? 'rounded-t-2xl border-b-0' : 'rounded-2xl'}`}
className={`bg-background flex grow flex-col gap-y-2 border p-6 ${link ? 'rounded-t-lg border-b-0' : 'rounded-lg'}`}
>
<h3 className="mt-0 text-lg font-semibold hover:underline dark:text-white">
<Link href={link.url}>{title}</Link>
</h3>
{subtitle && (
<div className="text-sm text-muted-foreground">
<div className="text-muted-foreground text-sm">
<p dangerouslySetInnerHTML={{ __html: subtitle }}></p>
</div>
)}
@@ -33,17 +33,19 @@ export function DocsCard({
</div>
{link && (
<div className="rounded-b-2xl border bg-muted p-6 py-4 dark:bg-background">
<span className={'flex items-center space-x-2'}>
<Link
className={'text-sm font-medium hover:underline'}
href={link.url}
>
<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'} />}
</Link>
</span>
<ChevronRight className={'h-4'} />
</span>
</Link>
</div>
)}
</div>

View File

@@ -22,8 +22,8 @@ export function DocsNavLink({
<SidebarMenuButton
asChild
isActive={isCurrent}
className={cn('border-l-3 transition-background !font-normal', {
'font-bold text-secondary-foreground': isCurrent,
className={cn('transition-background font-normal!', {
'text-secondary-foreground font-bold': isCurrent,
})}
>
<Link href={url}>

View File

@@ -116,7 +116,7 @@ export function DocsNavigation({
<>
<Sidebar
variant={'ghost'}
className={'z-1 sticky max-h-full overflow-y-auto'}
className={'sticky z-1 mt-4 max-h-full overflow-y-auto'}
>
<SidebarGroup>
<SidebarGroupContent>

View File

@@ -13,7 +13,7 @@ export function DocsTableOfContents(props: { data: NavItem[] }) {
const navData = props.data;
return (
<div className="sticky inset-y-0 hidden h-svh max-h-full min-w-[14em] border-l bg-background p-4 lg:block">
<div className="bg-background sticky inset-y-0 hidden h-svh max-h-full min-w-[14em] border-l p-4 lg:block">
<ol
role="list"
className="relative text-sm text-gray-600 dark:text-gray-400"
@@ -22,7 +22,7 @@ export function DocsTableOfContents(props: { data: NavItem[] }) {
<li key={item.href} className="group/item relative mt-3 first:mt-0">
<a
href={item.href}
className="block transition-colors hover:text-gray-950 dark:hover:text-white [&_*]:[font:inherit]"
className="block transition-colors **:[font:inherit] hover:text-gray-950 dark:hover:text-white"
>
{item.text}
</a>
@@ -35,7 +35,7 @@ export function DocsTableOfContents(props: { data: NavItem[] }) {
>
<Link
href={child.href}
className="block transition-colors hover:text-gray-950 dark:hover:text-white [&_*]:[font:inherit]"
className="block transition-colors **:[font:inherit] hover:text-gray-950 dark:hover:text-white"
>
{child.text}
</Link>

View File

@@ -14,7 +14,7 @@ async function DocsLayout({ children }: React.PropsWithChildren) {
return (
<SidebarProvider
style={{ '--sidebar-width': '20em' } as React.CSSProperties}
style={{ '--sidebar-width': '18em' } as React.CSSProperties}
className={'h-[calc(100vh-72px)] overflow-y-hidden lg:container'}
>
<DocsNavigation pages={tree} />

View File

@@ -21,7 +21,7 @@ async function DocsPage() {
const cards = items.filter((item) => !item.parentId);
return (
<div className={'flex flex-col space-y-6 xl:space-y-10'}>
<div className={'flex flex-col gap-y-6 xl:gap-y-10'}>
<SitePageHeader
title={t('marketing:documentation')}
subtitle={t('marketing:documentationSubtitle')}

View File

@@ -135,7 +135,7 @@ function FaqItem({
</div>
</summary>
<div className={'flex flex-col space-y-2 py-1 text-muted-foreground'}>
<div className={'text-muted-foreground flex flex-col gap-y-3 py-1'}>
<Trans i18nKey={item.answer} defaults={item.answer} />
</div>
</details>

View File

@@ -2,7 +2,6 @@ import { getSupabaseServerClient } from '@kit/supabase/server-client';
import { SiteFooter } from '~/(marketing)/_components/site-footer';
import { SiteHeader } from '~/(marketing)/_components/site-header';
import { BackgroundHue } from '~/components/background-hue';
import { withI18n } from '~/lib/i18n/with-i18n';
async function SiteLayout(props: React.PropsWithChildren) {
@@ -18,7 +17,6 @@ async function SiteLayout(props: React.PropsWithChildren) {
{props.children}
<BackgroundHue />
<SiteFooter />
</div>
);

View File

@@ -12,6 +12,7 @@ import {
FeatureShowcaseIconContainer,
Hero,
Pill,
PillActionButton,
SecondaryHero,
} from '@kit/ui/marketing';
import { Trans } from '@kit/ui/trans';
@@ -27,7 +28,12 @@ function Home() {
<Hero
pill={
<Pill label={'New'}>
<span>The leading SaaS Starter Kit for ambitious developers</span>
<span>The SaaS Starter Kit for ambitious developers</span>
<PillActionButton asChild>
<Link href={'/auth/sign-up'}>
<ArrowRightIcon className={'h-4 w-4'} />
</Link>
</PillActionButton>
</Pill>
}
title={
@@ -47,7 +53,7 @@ function Home() {
<Image
priority
className={
'rounded-2xl border border-gray-200 dark:border-primary/10'
'dark:border-primary/10 rounded-xl border border-gray-200'
}
width={3558}
height={2222}
@@ -65,11 +71,11 @@ function Home() {
<FeatureShowcase
heading={
<>
<b className="font-semibold dark:text-white">
<b className="font-medium tracking-tighter dark:text-white">
The ultimate SaaS Starter Kit
</b>
.{' '}
<span className="font-normal text-muted-foreground">
<span className="text-muted-foreground font-normal tracking-tighter">
Unleash your creativity and build your SaaS faster than ever
with Makerkit.
</span>
@@ -84,64 +90,36 @@ function Home() {
>
<FeatureGrid>
<FeatureCard
className={'relative col-span-2 overflow-hidden lg:h-96'}
className={'relative col-span-1 overflow-hidden'}
label={'Beautiful Dashboard'}
description={`Makerkit provides a beautiful dashboard to manage your SaaS business.`}
>
<Image
className="absolute right-0 top-0 hidden h-full w-full rounded-tl-2xl border border-border lg:top-36 lg:flex lg:h-auto lg:w-10/12"
src={'/images/dashboard-header.webp'}
width={'2061'}
height={'800'}
alt={'Dashboard Header'}
/>
</FeatureCard>
></FeatureCard>
<FeatureCard
className={
'relative col-span-2 w-full overflow-hidden lg:col-span-1'
'relative col-span-1 w-full overflow-hidden lg:col-span-1'
}
label={'Authentication'}
description={`Makerkit provides a variety of providers to allow your users to sign in.`}
>
<Image
className="absolute left-16 top-32 hidden h-auto w-8/12 rounded-l-2xl lg:flex"
src={'/images/sign-in.webp'}
width={'1760'}
height={'1680'}
alt={'Sign In'}
/>
</FeatureCard>
></FeatureCard>
<FeatureCard
className={
'relative col-span-2 overflow-hidden lg:col-span-1 lg:h-96'
}
className={'relative col-span-2 overflow-hidden lg:col-span-1'}
label={'Multi Tenancy'}
description={`Multi tenant memberships for your SaaS business.`}
>
<Image
className="absolute right-0 top-0 hidden h-full w-full rounded-tl-2xl border lg:top-28 lg:flex lg:h-auto lg:w-8/12"
src={'/images/multi-tenancy.webp'}
width={'2061'}
height={'800'}
alt={'Multi Tenancy'}
/>
</FeatureCard>
/>
<FeatureCard
className={'relative col-span-2 overflow-hidden lg:h-96'}
className={'relative col-span-1 overflow-hidden lg:col-span-2'}
label={'Billing'}
description={`Makerkit supports multiple payment gateways to charge your customers.`}
>
<Image
className="absolute right-0 top-0 hidden h-full w-full rounded-tl-2xl border border-border lg:top-36 lg:flex lg:h-auto lg:w-11/12"
src={'/images/billing.webp'}
width={'2061'}
height={'800'}
alt={'Billing'}
/>
</FeatureCard>
/>
<FeatureCard
className={'relative col-span-1 overflow-hidden'}
label={'Plugins'}
description={`Extend your SaaS with plugins that you can install using the CLI.`}
/>
</FeatureGrid>
</FeatureShowcase>
</div>
@@ -188,8 +166,8 @@ function MainCallToActionButton() {
<ArrowRightIcon
className={
'h-4 animate-in fade-in slide-in-from-left-8' +
' delay-1000 duration-1000 zoom-in fill-mode-both'
'animate-in fade-in slide-in-from-left-8 h-4' +
' zoom-in fill-mode-both delay-1000 duration-1000'
}
/>
</span>

View File

@@ -1,16 +1,9 @@
import { AuthLayoutShell } from '@kit/auth/shared';
import { AppLogo } from '~/components/app-logo';
import { BackgroundHue } from '~/components/background-hue';
function AuthLayout({ children }: React.PropsWithChildren) {
return (
<AuthLayoutShell Logo={AppLogo}>
{children}
<BackgroundHue />
</AuthLayoutShell>
);
return <AuthLayoutShell Logo={AppLogo}>{children}</AuthLayoutShell>;
}
export default AuthLayout;

View File

@@ -23,9 +23,15 @@ const redirectPath = `${callback}?next=${passwordUpdate}`;
function PasswordResetPage() {
return (
<>
<Heading level={5} className={'tracking-tight'}>
<Trans i18nKey={'auth:passwordResetLabel'} />
</Heading>
<div className={'flex flex-col items-center gap-1'}>
<Heading level={4} className={'tracking-tight'}>
<Trans i18nKey={'auth:passwordResetLabel'} />
</Heading>
<p className={'text-muted-foreground text-sm'}>
<Trans i18nKey={'auth:passwordResetSubheading'} />
</p>
</div>
<div className={'flex flex-col space-y-4'}>
<PasswordResetRequestContainer redirectPath={redirectPath} />

View File

@@ -39,9 +39,15 @@ async function SignInPage({ searchParams }: SignInPageProps) {
return (
<>
<Heading level={5} className={'tracking-tight'}>
<Trans i18nKey={'auth:signInHeading'} />
</Heading>
<div className={'flex flex-col items-center gap-1'}>
<Heading level={4} className={'tracking-tight'}>
<Trans i18nKey={'auth:signInHeading'} />
</Heading>
<p className={'text-muted-foreground text-sm'}>
<Trans i18nKey={'auth:signInSubheading'} />
</p>
</div>
<SignInMethodsContainer
inviteToken={inviteToken}

View File

@@ -38,9 +38,15 @@ async function SignUpPage({ searchParams }: Props) {
return (
<>
<Heading level={5} className={'tracking-tight'}>
<Trans i18nKey={'auth:signUpHeading'} />
</Heading>
<div className={'flex flex-col items-center gap-1'}>
<Heading level={4} className={'tracking-tight'}>
<Trans i18nKey={'auth:signUpHeading'} />
</Heading>
<p className={'text-muted-foreground text-sm'}>
<Trans i18nKey={'auth:signUpSubheading'} />
</p>
</div>
<SignUpMethodsContainer
providers={authConfig.providers}
@@ -49,7 +55,7 @@ async function SignUpPage({ searchParams }: Props) {
paths={paths}
/>
<div className={'justify-centers flex'}>
<div className={'flex justify-center'}>
<Button asChild variant={'link'} size={'sm'}>
<Link href={signInPath}>
<Trans i18nKey={'auth:alreadyHaveAnAccount'} />

View File

@@ -39,7 +39,7 @@ const ErrorPage = ({
<div className={'flex flex-col items-center space-y-8'}>
<div
className={
'flex max-w-xl flex-col items-center space-y-1 text-center'
'flex max-w-xl flex-col items-center gap-y-2 text-center'
}
>
<div>
@@ -48,7 +48,7 @@ const ErrorPage = ({
</Heading>
</div>
<p className={'text-lg text-muted-foreground'}>
<p className={'text-muted-foreground text-lg'}>
<Trans i18nKey={'common:genericErrorSubHeading'} />
</p>
</div>

View File

@@ -41,7 +41,7 @@ const GlobalErrorPage = ({
<div className={'flex flex-col items-center space-y-8'}>
<div
className={
'flex max-w-xl flex-col items-center space-y-1 text-center'
'flex max-w-xl flex-col items-center gap-y-2 text-center'
}
>
<div>
@@ -50,7 +50,7 @@ const GlobalErrorPage = ({
</Heading>
</div>
<p className={'text-lg text-muted-foreground'}>
<p className={'text-muted-foreground text-lg'}>
<Trans i18nKey={'common:genericErrorSubHeading'} />
</p>
</div>

View File

@@ -29,7 +29,7 @@ export function HomeSidebar(props: HomeSidebarProps) {
return (
<Sidebar>
<SidebarHeader className={'h-16 justify-center'}>
<div className={'flex items-center justify-between space-x-2'}>
<div className={'flex items-center justify-between gap-x-3'}>
<If
condition={featuresFlagConfig.enableTeamAccounts}
fallback={

View File

@@ -47,7 +47,7 @@ export default function DashboardDemo() {
return (
<div
className={
'flex flex-col space-y-4 pb-36 duration-500 animate-in fade-in'
'animate-in fade-in flex flex-col space-y-4 pb-36 duration-500'
}
>
<div
@@ -189,11 +189,11 @@ function Chart(
const chartConfig = {
desktop: {
label: 'Desktop',
color: 'hsl(var(--chart-1))',
color: 'var(--chart-1)',
},
mobile: {
label: 'Mobile',
color: 'hsl(var(--chart-2))',
color: 'var(--chart-2)',
},
} satisfies ChartConfig;
@@ -423,7 +423,7 @@ function CustomersTable() {
<TableRow key={customer.name}>
<TableCell className={'flex flex-col'}>
<span>{customer.name}</span>
<span className={'text-sm text-muted-foreground'}>
<span className={'text-muted-foreground text-sm'}>
{customer.email}
</span>
</TableCell>
@@ -482,7 +482,7 @@ function Trend(
case 'up':
return <ArrowUp className={'h-3 w-3 text-green-500'} />;
case 'down':
return <ArrowDown className={'h-3 w-3 text-destructive'} />;
return <ArrowDown className={'text-destructive h-3 w-3'} />;
case 'stale':
return <Menu className={'h-3 w-3 text-orange-500'} />;
}
@@ -604,11 +604,11 @@ export function VisitorsChart() {
},
desktop: {
label: 'Desktop',
color: 'hsl(var(--chart-1))',
color: 'var(--chart-1)',
},
mobile: {
label: 'Mobile',
color: 'hsl(var(--chart-2))',
color: 'var(--chart-2)',
},
} satisfies ChartConfig;
@@ -685,10 +685,10 @@ export function VisitorsChart() {
<CardFooter>
<div className="flex w-full items-start gap-2 text-sm">
<div className="grid gap-2">
<div className="flex items-center gap-2 font-medium leading-none">
<div className="flex items-center gap-2 leading-none font-medium">
Trending up by 5.2% this month <TrendingUp className="h-4 w-4" />
</div>
<div className="flex items-center gap-2 leading-none text-muted-foreground">
<div className="text-muted-foreground flex items-center gap-2 leading-none">
January - June 2024
</div>
</div>
@@ -802,11 +802,11 @@ export function PageViewsChart() {
},
desktop: {
label: 'Desktop',
color: 'hsl(var(--chart-1))',
color: 'var(--chart-1)',
},
mobile: {
label: 'Mobile',
color: 'hsl(var(--chart-2))',
color: 'var(--chart-2)',
},
} satisfies ChartConfig;
@@ -836,13 +836,13 @@ export function PageViewsChart() {
<button
key={chart}
data-active={activeChart === chart}
className="relative z-30 flex flex-1 flex-col justify-center gap-1 border-t px-6 py-4 text-left even:border-l data-[active=true]:bg-muted/50 sm:border-l sm:border-t-0 sm:px-8 sm:py-6"
className="data-[active=true]:bg-muted/50 relative z-30 flex flex-1 flex-col justify-center gap-1 border-t px-6 py-4 text-left even:border-l sm:border-t-0 sm:border-l sm:px-8 sm:py-6"
onClick={() => setActiveChart(chart)}
>
<span className="text-xs text-muted-foreground">
<span className="text-muted-foreground text-xs">
{chartConfig[chart].label}
</span>
<span className="text-lg font-bold leading-none sm:text-3xl">
<span className="text-lg leading-none font-bold sm:text-3xl">
{total[key as keyof typeof total].toLocaleString()}
</span>
</button>

View File

@@ -98,7 +98,7 @@ function DropdownLink(
<DropdownMenuItem asChild>
<Link
href={props.path}
className={'flex h-12 w-full items-center space-x-2 px-3'}
className={'flex h-12 w-full items-center gap-x-3 px-3'}
>
{props.Icon}