Some changes ported from the work on the makerkit.dev website related… (#89)

* Some changes ported from the work on the makerkit.dev website related to the marketing sections of the kit, such as documentation
* Added slight background hue to make darker theme better looking
* Support more complex configurations for documentation navigations.
* Do not fetch content from Keystatic when non-needed
* Add cursor pointers in dropdown
* Updated packages
This commit is contained in:
Giancarlo Buomprisco
2024-12-09 05:58:17 +01:00
committed by GitHub
parent a682b991f3
commit 079a8f857a
44 changed files with 762 additions and 456 deletions

View File

@@ -27,13 +27,13 @@
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:*",
"@supabase/supabase-js": "^2.47.1",
"@supabase/supabase-js": "^2.47.2",
"@types/react": "npm:types-react@19.0.0-rc.1",
"date-fns": "^4.1.0",
"lucide-react": "^0.468.0",
"next": "15.0.4",
"react": "19.0.0",
"react-hook-form": "^7.53.2",
"react-hook-form": "^7.54.0",
"react-i18next": "^15.1.3",
"zod": "^3.23.8"
},

View File

@@ -392,32 +392,36 @@ function PlanIntervalSwitcher(
}>,
) {
return (
<div className={'flex'}>
<div className={'flex gap-x-1 rounded-full border p-1.5'}>
{props.intervals.map((plan, index) => {
const selected = plan === props.interval;
const className = cn(
'animate-in fade-in !outline-none transition-all focus:!ring-0',
'animate-in fade-in rounded-full !outline-none transition-all focus:!ring-0',
{
'rounded-r-none border-r-transparent': index === 0,
'rounded-l-none': index === props.intervals.length - 1,
['hover:text-primary text-muted-foreground border']: !selected,
['hover:text-initial hover:bg-background cursor-default font-semibold']:
selected,
'border-r-transparent': index === 0,
['hover:text-primary text-muted-foreground']: !selected,
['cursor-default font-semibold']: selected,
['hover:bg-initial']: !selected
},
);
return (
<Button
key={plan}
variant={'outline'}
size={'sm'}
variant={selected ? 'default' : 'ghost'}
className={className}
onClick={() => props.setInterval(plan)}
>
<span className={'flex items-center space-x-1'}>
<If condition={selected}>
<CheckCircle className={'animate-in fade-in zoom-in-90 h-4'} />
</If>
<span className={'flex items-center'}>
<CheckCircle
className={cn('animate-in fade-in zoom-in-95 h-3.5', {
hidden: !selected,
'slide-in-from-left-4': index === 0,
'slide-in-from-right-4': index === props.intervals.length - 1,
})}
/>
<span className={'capitalize'}>
<Trans i18nKey={`common:billingInterval.${plan}`} />

View File

@@ -14,7 +14,7 @@
"./components": "./src/components/index.ts"
},
"dependencies": {
"@lemonsqueezy/lemonsqueezy.js": "3.3.1"
"@lemonsqueezy/lemonsqueezy.js": "4.0.0"
},
"devDependencies": {
"@kit/billing": "workspace:*",

View File

@@ -1,7 +1,7 @@
import { Cms, CmsClient } from '@kit/cms-types';
import { createKeystaticReader } from './create-reader';
import { PostEntryProps } from './keystatic.config';
import { DocumentationEntryProps, PostEntryProps } from './keystatic.config';
import { renderMarkdoc } from './markdoc';
export function createKeystaticClient() {
@@ -19,11 +19,13 @@ class KeystaticClient implements CmsClient {
throw new Error(`Collection ${collection} not found`);
}
const docs = await reader.collections[collection].all();
const fetchContent = options.content ?? true;
const startOffset = options?.offset ?? 0;
const endOffset = startOffset + (options?.limit ?? 10);
const docs = await reader.collections[collection].all();
const filtered = docs
.filter((item) => {
const status = options?.status ?? 'published';
@@ -80,72 +82,95 @@ class KeystaticClient implements CmsClient {
return right - left;
});
function processItems(items: typeof docs) {
const result: typeof docs = [...items];
function processItems(items: typeof filtered) {
const slugSet = new Set(items.map((item) => item.slug));
const indexFileCache = new Map<string, boolean>();
const parentCache = new Map<string, string | null>();
const indexFiles = items.filter((item) => {
const parts = item.slug.split('/');
const isIndexFile = (slug: string): boolean => {
if (indexFileCache.has(slug)) {
return indexFileCache.get(slug)!;
}
return (
parts.length > 1 &&
parts[parts.length - 1] === parts[parts.length - 2]
);
});
const parts = slug.split('/');
const result =
parts.length === 1 ||
(parts.length >= 2 &&
parts[parts.length - 1] === parts[parts.length - 2]);
indexFileCache.set(slug, result);
return result;
};
const findClosestValidParent = (pathParts: string[]): string | null => {
const path = pathParts.join('/');
if (parentCache.has(path)) {
return parentCache.get(path)!;
}
function findParentIndex(pathParts: string[]): string | null {
// Try each level up from the current path until we find an index file
for (let i = pathParts.length - 1; i > 0; i--) {
const currentPath = pathParts.slice(0, i).join('/');
const parentParts = pathParts.slice(0, i);
const lastPart = parentParts[parentParts.length - 1];
const possibleParent = indexFiles.find((indexFile) => {
const indexParts = indexFile.slug.split('/');
const indexFolderPath = indexParts.slice(0, -1).join('/');
if (!lastPart) {
continue;
}
return indexFolderPath === currentPath;
});
const possibleIndexParent = parentParts.concat(lastPart).join('/');
if (possibleParent) {
return possibleParent.slug;
if (slugSet.has(possibleIndexParent)) {
parentCache.set(path, possibleIndexParent);
return possibleIndexParent;
}
const regularParent = parentParts.join('/');
if (slugSet.has(regularParent)) {
parentCache.set(path, regularParent);
return regularParent;
}
}
return null;
}
result.forEach((item) => {
// never override the parent if it's already set in the config
if (item.entry.parent) {
return;
parentCache.set(path, null);
return null;
};
const results = new Array(items.length) as typeof items;
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (!item) {
continue;
}
if (isIndexFile(item.slug)) {
item.entry.parent = null;
results[i] = item;
continue;
}
const pathParts = item.slug.split('/');
const parentParts = pathParts.slice(0, -1);
const lastPart = parentParts[parentParts.length - 1]!;
const possibleIndexParent = parentParts.concat(lastPart).join('/');
// Skip if this is a root level index file (e.g., "authentication/authentication")
if (pathParts.length === 2 && pathParts[0] === pathParts[1]) {
item.entry.parent = null;
return;
}
// Check if current item is an index file
const isIndexFile =
pathParts[pathParts.length - 1] === pathParts[pathParts.length - 2];
if (isIndexFile) {
// For index files, find parent in the level above
const parentPath = pathParts.slice(0, -2);
if (parentPath.length > 0) {
item.entry.parent = findParentIndex(
parentPath.concat(parentPath[parentPath.length - 1]!),
);
} else {
item.entry.parent = null;
}
if (slugSet.has(possibleIndexParent)) {
item.entry.parent = possibleIndexParent;
} else {
// For regular files, find parent in the current folder
item.entry.parent = findParentIndex(pathParts);
const regularParent = parentParts.join('/');
if (slugSet.has(regularParent)) {
item.entry.parent = regularParent;
} else {
item.entry.parent = findClosestValidParent(pathParts);
}
}
});
results[i] = item;
}
return result;
return results;
}
const itemsWithParents = processItems(filtered);
@@ -156,7 +181,25 @@ class KeystaticClient implements CmsClient {
.sort((a, b) => {
return (a.entry.order ?? 0) - (b.entry.order ?? 0);
})
.map((item) => this.mapPost(item)),
.map((item) => {
if (collection === 'documentation') {
return this.mapDocumentationPost(
item as {
entry: DocumentationEntryProps;
slug: string;
},
{ fetchContent },
);
}
return this.mapPost(
item as {
entry: PostEntryProps;
slug: string;
},
{ fetchContent },
);
}),
);
return {
@@ -192,13 +235,7 @@ class KeystaticClient implements CmsClient {
return Promise.resolve(undefined);
}
const allPosts = await reader.collections[collection].all();
const children = allPosts.filter(
(item) => item.entry.parent === params.slug,
);
return this.mapPost({ entry: doc, slug: params.slug }, children);
return this.mapPost({ entry: doc as PostEntryProps, slug: params.slug });
}
async getCategories() {
@@ -217,18 +254,79 @@ class KeystaticClient implements CmsClient {
return Promise.resolve(undefined);
}
private async mapPost<
private async mapDocumentationPost<
Type extends {
entry: PostEntryProps;
entry: DocumentationEntryProps;
slug: string;
},
>(item: Type, children: Type[] = []): Promise<Cms.ContentItem> {
>(
item: Type,
params: {
fetchContent: boolean;
} = {
fetchContent: true,
},
): Promise<Cms.ContentItem> {
const publishedAt = item.entry.publishedAt
? new Date(item.entry.publishedAt)
: new Date();
const content = await item.entry.content();
const html = await renderMarkdoc(content.node);
const html = params.fetchContent ? await renderMarkdoc(content.node) : [];
return {
id: item.slug,
title: item.entry.title,
label: item.entry.label,
url: item.slug,
slug: item.slug,
description: item.entry.description,
publishedAt: publishedAt.toISOString(),
content: html as string,
image: item.entry.image ?? undefined,
status: item.entry.status,
collapsible: item.entry.collapsible,
collapsed: item.entry.collapsed,
categories:
(item.entry.categories ?? []).map((item) => {
return {
id: item,
name: item,
slug: item,
};
}) ?? [],
tags: (item.entry.tags ?? []).map((item) => {
return {
id: item,
name: item,
slug: item,
};
}),
parentId: item.entry.parent ?? undefined,
order: item.entry.order ?? 1,
children: [],
};
}
private async mapPost<
Type extends {
entry: PostEntryProps;
slug: string;
},
>(
item: Type,
params: {
fetchContent: boolean;
} = {
fetchContent: true,
},
): Promise<Cms.ContentItem> {
const publishedAt = item.entry.publishedAt
? new Date(item.entry.publishedAt)
: new Date();
const content = await item.entry.content();
const html = params.fetchContent ? await renderMarkdoc(content.node) : [];
return {
id: item.slug,
@@ -242,14 +340,14 @@ class KeystaticClient implements CmsClient {
image: item.entry.image ?? undefined,
status: item.entry.status,
categories:
item.entry.categories.map((item) => {
(item.entry.categories ?? []).map((item) => {
return {
id: item,
name: item,
slug: item,
};
}) ?? [],
tags: item.entry.tags.map((item) => {
tags: (item.entry.tags ?? []).map((item) => {
return {
id: item,
name: item,
@@ -258,9 +356,7 @@ class KeystaticClient implements CmsClient {
}),
parentId: item.entry.parent ?? undefined,
order: item.entry.order ?? 1,
children: await Promise.all(
children.map((child) => this.mapPost(child, [])),
),
children: [],
};
}
}

View File

@@ -39,6 +39,10 @@ export type PostEntryProps = Entry<
(typeof keyStaticConfig)['collections']['posts']
>;
export type DocumentationEntryProps = Entry<
(typeof keyStaticConfig)['collections']['documentation']
>;
function createKeyStaticConfig(path = '') {
if (path && !path.endsWith('/')) {
path += '/';
@@ -135,6 +139,14 @@ function getKeystaticCollections(path: string) {
{ label: 'Pending', value: 'pending' },
],
}),
collapsible: fields.checkbox({
label: 'Collapsible',
defaultValue: false,
}),
collapsed: fields.checkbox({
label: 'Collapsed',
defaultValue: false,
}),
},
}),
};

View File

@@ -16,6 +16,8 @@ export namespace Cms {
order: number;
children: ContentItem[];
parentId: string | undefined;
collapsible?: boolean;
collapsed?: boolean;
}
export type ContentItemStatus = 'draft' | 'published' | 'review' | 'pending';
@@ -38,6 +40,7 @@ export namespace Cms {
offset?: number;
categories?: string[];
tags?: string[];
content?: boolean;
parentIds?: string[];
language?: string | undefined;
sortDirection?: 'asc' | 'desc';

View File

@@ -23,7 +23,7 @@
"@kit/tailwind-config": "workspace:*",
"@kit/team-accounts": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@supabase/supabase-js": "^2.47.1",
"@supabase/supabase-js": "^2.47.2",
"zod": "^3.23.8"
},
"eslintConfig": {

View File

@@ -13,7 +13,7 @@
".": "./src/index.ts"
},
"dependencies": {
"@react-email/components": "0.0.29"
"@react-email/components": "0.0.30"
},
"devDependencies": {
"@kit/eslint-config": "workspace:*",

View File

@@ -34,8 +34,8 @@
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:*",
"@radix-ui/react-icons": "^1.3.2",
"@supabase/supabase-js": "^2.47.1",
"@tanstack/react-query": "5.62.2",
"@supabase/supabase-js": "^2.47.2",
"@tanstack/react-query": "5.62.3",
"@types/react": "npm:types-react@19.0.0-rc.1",
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.1",
"lucide-react": "^0.468.0",
@@ -43,9 +43,9 @@
"next-themes": "0.4.4",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-hook-form": "^7.53.2",
"react-hook-form": "^7.54.0",
"react-i18next": "^15.1.3",
"sonner": "^1.7.0",
"sonner": "^1.7.1",
"zod": "^3.23.8"
},
"prettier": "@kit/prettier-config",

View File

@@ -148,7 +148,7 @@ export function PersonalAccountDropdown({
<DropdownMenuItem asChild>
<Link
className={'s-full flex items-center space-x-2'}
className={'s-full flex items-center space-x-2 cursor-pointer'}
href={paths.home}
>
<Home className={'h-5'} />
@@ -162,7 +162,7 @@ export function PersonalAccountDropdown({
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link className={'s-full flex items-center space-x-2'} href={'/docs'}>
<Link className={'s-full flex items-center space-x-2 cursor-pointer'} href={'/docs'}>
<MessageCircleQuestion className={'h-5'} />
<span>
@@ -176,7 +176,7 @@ export function PersonalAccountDropdown({
<DropdownMenuItem asChild>
<Link
className={'s-full flex items-center space-x-2'}
className={'s-full flex items-center space-x-2 cursor-pointer'}
href={'/admin'}
>
<Shield className={'h-5'} />

View File

@@ -21,15 +21,15 @@
"@kit/ui": "workspace:*",
"@makerkit/data-loader-supabase-core": "^0.0.8",
"@makerkit/data-loader-supabase-nextjs": "^1.2.3",
"@supabase/supabase-js": "^2.47.1",
"@tanstack/react-query": "5.62.2",
"@supabase/supabase-js": "^2.47.2",
"@tanstack/react-query": "5.62.3",
"@tanstack/react-table": "^8.20.5",
"@types/react": "npm:types-react@19.0.0-rc.1",
"lucide-react": "^0.468.0",
"next": "15.0.4",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-hook-form": "^7.53.2",
"react-hook-form": "^7.54.0",
"zod": "^3.23.8"
},
"exports": {

View File

@@ -28,14 +28,14 @@
"@kit/ui": "workspace:*",
"@marsidev/react-turnstile": "^1.1.0",
"@radix-ui/react-icons": "^1.3.2",
"@supabase/supabase-js": "^2.47.1",
"@tanstack/react-query": "5.62.2",
"@supabase/supabase-js": "^2.47.2",
"@tanstack/react-query": "5.62.3",
"@types/react": "npm:types-react@19.0.0-rc.1",
"lucide-react": "^0.468.0",
"next": "15.0.4",
"react-hook-form": "^7.53.2",
"react-hook-form": "^7.54.0",
"react-i18next": "^15.1.3",
"sonner": "^1.7.0",
"sonner": "^1.7.1",
"zod": "^3.23.8"
},
"prettier": "@kit/prettier-config",

View File

@@ -20,8 +20,8 @@
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:*",
"@supabase/supabase-js": "^2.47.1",
"@tanstack/react-query": "5.62.2",
"@supabase/supabase-js": "^2.47.2",
"@tanstack/react-query": "5.62.3",
"@types/react": "npm:types-react@19.0.0-rc.1",
"lucide-react": "^0.468.0",
"react": "19.0.0",

View File

@@ -32,8 +32,8 @@
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@kit/ui": "workspace:*",
"@supabase/supabase-js": "^2.47.1",
"@tanstack/react-query": "5.62.2",
"@supabase/supabase-js": "^2.47.2",
"@tanstack/react-query": "5.62.3",
"@tanstack/react-table": "^8.20.5",
"@types/react": "npm:types-react@19.0.0-rc.1",
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.1",
@@ -43,9 +43,9 @@
"next": "15.0.4",
"react": "19.0.0",
"react-dom": "19.0.0",
"react-hook-form": "^7.53.2",
"react-hook-form": "^7.54.0",
"react-i18next": "^15.1.3",
"sonner": "^1.7.0",
"sonner": "^1.7.1",
"zod": "^3.23.8"
},
"prettier": "@kit/prettier-config",

View File

@@ -21,7 +21,7 @@
"@kit/shared": "workspace:*",
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@tanstack/react-query": "5.62.2",
"@tanstack/react-query": "5.62.3",
"next": "15.0.4",
"react": "19.0.0",
"react-dom": "19.0.0",
@@ -29,7 +29,7 @@
},
"dependencies": {
"i18next": "24.0.5",
"i18next-browser-languagedetector": "8.0.0",
"i18next-browser-languagedetector": "8.0.1",
"i18next-resources-to-backend": "^1.2.1"
},
"eslintConfig": {

View File

@@ -21,7 +21,7 @@
"@kit/supabase": "workspace:*",
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@supabase/supabase-js": "^2.47.1",
"@supabase/supabase-js": "^2.47.2",
"next": "15.0.4",
"zod": "^3.23.8"
},

View File

@@ -29,8 +29,8 @@
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@supabase/ssr": "^0.5.2",
"@supabase/supabase-js": "^2.47.1",
"@tanstack/react-query": "5.62.2",
"@supabase/supabase-js": "^2.47.2",
"@tanstack/react-query": "5.62.3",
"@types/react": "npm:types-react@19.0.0-rc.1",
"next": "15.0.4",
"react": "19.0.0",

View File

@@ -43,7 +43,7 @@
"@kit/tailwind-config": "workspace:*",
"@kit/tsconfig": "workspace:*",
"@radix-ui/react-icons": "^1.3.2",
"@tanstack/react-query": "5.62.2",
"@tanstack/react-query": "5.62.3",
"@tanstack/react-table": "^8.20.5",
"@types/react": "npm:types-react@19.0.0-rc.1",
"@types/react-dom": "npm:types-react-dom@19.0.0-rc.1",
@@ -54,9 +54,9 @@
"next-themes": "0.4.4",
"prettier": "^3.4.2",
"react-day-picker": "^8.10.1",
"react-hook-form": "^7.53.2",
"react-hook-form": "^7.54.0",
"react-i18next": "^15.1.3",
"sonner": "^1.7.0",
"sonner": "^1.7.1",
"tailwindcss": "3.4.16",
"tailwindcss-animate": "^1.0.7",
"typescript": "^5.7.2",
@@ -106,6 +106,7 @@
"./chart": "./src/shadcn/chart.tsx",
"./skeleton": "./src/shadcn/skeleton.tsx",
"./shadcn-sidebar": "./src/shadcn/sidebar.tsx",
"./collapsible": "./src/shadcn/collapsible.tsx",
"./utils": "./src/lib/utils/index.ts",
"./if": "./src/makerkit/if.tsx",
"./trans": "./src/makerkit/trans.tsx",

View File

@@ -30,7 +30,7 @@ export const FeatureCard = forwardRef<HTMLDivElement, FeatureCardProps>(
>
<CardHeader>
<CardTitle className="text-xl font-semibold">{label}</CardTitle>
<CardDescription className="max-w-xs text-sm font-semibold tracking-tight text-current">
<CardDescription className="max-w-xs text-sm font-semibold tracking-tight text-muted-foreground">
{description}
</CardDescription>
</CardHeader>

View File

@@ -10,7 +10,7 @@ export const FeatureGrid = forwardRef<
<div
ref={ref}
className={cn(
'grid w-full grid-cols-1 gap-6 space-y-0 lg:grid-cols-3',
'grid w-full grid-cols-1 gap-4 space-y-0 lg:grid-cols-3',
className,
)}
{...props}

View File

@@ -20,7 +20,7 @@ export const FeatureShowcase = forwardRef<HTMLDivElement, FeatureShowcaseProps>(
>
<div className="flex w-full max-w-5xl flex-col space-y-4">
{icon && <div className="flex">{icon}</div>}
<h3 className="text-3xl font-normal tracking-tighter xl:text-5xl">
<h3 className="text-3xl font-normal tracking-tight xl:text-5xl">
{heading}
</h3>
</div>

View File

@@ -24,7 +24,7 @@ export const Header = forwardRef<HTMLDivElement, HeaderProps>(
>
<div className="container">
<div className="grid h-14 grid-cols-3 items-center">
<div>{logo}</div>
<div className={'mx-auto lg:mx-0'}>{logo}</div>
<div className="order-first md:order-none">{navigation}</div>
<div className="flex items-center justify-end space-x-1">
{actions}

View File

@@ -16,7 +16,7 @@ export const HeroTitle = forwardRef<
<Comp
ref={ref}
className={cn(
'hero-title flex flex-col space-y-1 text-center font-sans text-4xl font-semibold tracking-tighter dark:text-white sm:text-6xl lg:max-w-5xl lg:text-7xl xl:text-[5.125rem]',
'hero-title flex flex-col text-center font-sans text-4xl font-semibold tracking-tighter dark:text-white sm:text-6xl lg:max-w-5xl lg:text-7xl xl:text-[4.85rem]',
className,
)}
{...props}

View File

@@ -1,7 +1,6 @@
import React from 'react';
import { cn } from '../../lib/utils';
import { Heading } from '../../shadcn/heading';
import { HeroTitle } from './hero-title';
interface HeroProps {
@@ -53,12 +52,11 @@ export function Hero({
{subtitle && (
<div className="flex max-w-2xl flex-col space-y-1">
<Heading
level={3}
className="p-0 text-center font-sans text-base font-normal"
<h3
className="p-0 text-center text-xl font-sans font-normal text-muted-foreground tracking-tight"
>
{subtitle}
</Heading>
</h3>
</div>
)}
</div>
@@ -78,8 +76,11 @@ export function Hero({
{image && (
<div
className={cn('mx-auto flex max-w-[85rem] justify-center py-8', {
['delay-300 duration-1000 animate-in fade-in zoom-in-95 slide-in-from-top-32 fill-mode-both']:
style={{
MozAnimationDuration: '100ms',
}}
className={cn('mx-auto container flex justify-center py-8', {
['delay-1000 duration-1000 animate-in fade-in zoom-in-95 slide-in-from-top-32 fill-mode-both']:
animate,
})}
>

View File

@@ -3,7 +3,6 @@ import { forwardRef } from 'react';
import { Slot, Slottable } from '@radix-ui/react-slot';
import { cn } from '../../lib/utils';
import { GradientSecondaryText } from './gradient-secondary-text';
export const Pill = forwardRef<
HTMLHeadingElement,
@@ -33,7 +32,7 @@ export const Pill = forwardRef<
</span>
)}
<Slottable>
<GradientSecondaryText>{props.children}</GradientSecondaryText>
<span className={'text-secondary-foreground'}>{props.children}</span>
</Slottable>
</Comp>
);

View File

@@ -18,7 +18,7 @@ export const SecondaryHero = forwardRef<HTMLDivElement, SecondaryHeroProps>(
<div
ref={ref}
className={cn(
'flex flex-col items-center space-y-4 text-center',
'flex flex-col items-center space-y-6 text-center',
className,
)}
{...props}
@@ -30,12 +30,11 @@ export const SecondaryHero = forwardRef<HTMLDivElement, SecondaryHeroProps>(
{heading}
</Heading>
<Heading
level={3}
className="font-sans font-normal tracking-tight text-muted-foreground"
<h3
className="font-sans font-normal text-xl tracking-tight text-muted-foreground"
>
{subheading}
</Heading>
</h3>
</div>
{children}

View File

@@ -74,7 +74,7 @@ export function SubMenuModeToggle() {
return (
<DropdownMenuItem
className={cn('flex items-center space-x-2', {
className={cn('flex items-center space-x-2 cursor-pointer', {
'bg-muted': isSelected,
})}
key={mode}