mirror of
https://github.com/johndoe6345789/postgres.git
synced 2026-04-24 13:55:00 +00:00
Merge pull request #22 from johndoe6345789/copilot/refactor-app-folder-structure
Refactor marketing pages to config-driven architecture
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import type { Metadata } from 'next';
|
||||
import { getTranslations, setRequestLocale } from 'next-intl/server';
|
||||
import Image from 'next/image';
|
||||
import { SponsorSection } from '@/components/SponsorSection';
|
||||
import { sponsors } from '@/config/sponsors';
|
||||
|
||||
type IAboutProps = {
|
||||
params: Promise<{ locale: string }>;
|
||||
@@ -31,25 +32,7 @@ export default async function About(props: IAboutProps) {
|
||||
<>
|
||||
<p>{t('about_paragraph')}</p>
|
||||
|
||||
<div className="mt-2 text-center text-sm">
|
||||
{`${t('translation_powered_by')} `}
|
||||
<a
|
||||
className="text-blue-700 hover:border-b-2 hover:border-blue-700"
|
||||
href="https://l.crowdin.com/next-js"
|
||||
>
|
||||
Crowdin
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<a href="https://l.crowdin.com/next-js">
|
||||
<Image
|
||||
className="mx-auto mt-2"
|
||||
src="/assets/images/crowdin-dark.png"
|
||||
alt="Crowdin Translation Management System"
|
||||
width={128}
|
||||
height={26}
|
||||
/>
|
||||
</a>
|
||||
<SponsorSection sponsors={sponsors.about} namespace="About" />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { Metadata } from 'next';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
import Image from 'next/image';
|
||||
import { CounterForm } from '@/components/CounterForm';
|
||||
import { CurrentCount } from '@/components/CurrentCount';
|
||||
import { SponsorSection } from '@/components/SponsorSection';
|
||||
import { sponsors } from '@/config/sponsors';
|
||||
|
||||
export async function generateMetadata(props: {
|
||||
params: Promise<{ locale: string }>;
|
||||
@@ -31,27 +32,7 @@ export default function Counter() {
|
||||
<CurrentCount />
|
||||
</div>
|
||||
|
||||
<div className="mt-5 text-center text-sm">
|
||||
{`${t('security_powered_by')} `}
|
||||
<a
|
||||
className="text-blue-700 hover:border-b-2 hover:border-blue-700"
|
||||
href="https://launch.arcjet.com/Q6eLbRE"
|
||||
>
|
||||
Arcjet
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<a
|
||||
href="https://launch.arcjet.com/Q6eLbRE"
|
||||
>
|
||||
<Image
|
||||
className="mx-auto mt-2"
|
||||
src="/assets/images/arcjet-light.svg"
|
||||
alt="Arcjet"
|
||||
width={128}
|
||||
height={38}
|
||||
/>
|
||||
</a>
|
||||
<SponsorSection sponsors={sponsors.counter} namespace="Counter" />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { getTranslations, setRequestLocale } from 'next-intl/server';
|
||||
import Link from 'next/link';
|
||||
import { DemoBanner } from '@/components/DemoBanner';
|
||||
import { LocaleSwitcher } from '@/components/LocaleSwitcher';
|
||||
import { NavLink } from '@/components/NavLink';
|
||||
import { marketingNavigation } from '@/config/navigation';
|
||||
import { styles } from '@/config/styles';
|
||||
import { BaseTemplate } from '@/templates/BaseTemplate';
|
||||
|
||||
export default async function Layout(props: {
|
||||
@@ -21,67 +23,24 @@ export default async function Layout(props: {
|
||||
<BaseTemplate
|
||||
leftNav={(
|
||||
<>
|
||||
<li>
|
||||
<Link
|
||||
href="/"
|
||||
className="border-none text-gray-700 hover:text-gray-900"
|
||||
>
|
||||
{t('home_link')}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/about/"
|
||||
className="border-none text-gray-700 hover:text-gray-900"
|
||||
>
|
||||
{t('about_link')}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/counter/"
|
||||
className="border-none text-gray-700 hover:text-gray-900"
|
||||
>
|
||||
{t('counter_link')}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/portfolio/"
|
||||
className="border-none text-gray-700 hover:text-gray-900"
|
||||
>
|
||||
{t('portfolio_link')}
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="border-none text-gray-700 hover:text-gray-900"
|
||||
href="https://github.com/ixartz/Next-js-Boilerplate"
|
||||
>
|
||||
GitHub
|
||||
</a>
|
||||
</li>
|
||||
{marketingNavigation.left.map(link => (
|
||||
<li key={link.id}>
|
||||
<NavLink href={link.href} external={link.external}>
|
||||
{link.label || t(link.translationKey)}
|
||||
</NavLink>
|
||||
</li>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
rightNav={(
|
||||
<>
|
||||
<li>
|
||||
<Link
|
||||
href="/sign-in/"
|
||||
className="border-none text-gray-700 hover:text-gray-900"
|
||||
>
|
||||
{t('sign_in_link')}
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<Link
|
||||
href="/sign-up/"
|
||||
className="border-none text-gray-700 hover:text-gray-900"
|
||||
>
|
||||
{t('sign_up_link')}
|
||||
</Link>
|
||||
</li>
|
||||
{marketingNavigation.right.map(link => (
|
||||
<li key={link.id}>
|
||||
<NavLink href={link.href}>
|
||||
{t(link.translationKey)}
|
||||
</NavLink>
|
||||
</li>
|
||||
))}
|
||||
|
||||
<li>
|
||||
<LocaleSwitcher />
|
||||
@@ -89,7 +48,7 @@ export default async function Layout(props: {
|
||||
</>
|
||||
)}
|
||||
>
|
||||
<div className="py-5 text-xl [&_p]:my-6">{props.children}</div>
|
||||
<div className={styles.containers.contentPadding}>{props.children}</div>
|
||||
</BaseTemplate>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import type { Metadata } from 'next';
|
||||
import { getTranslations, setRequestLocale } from 'next-intl/server';
|
||||
import { Sponsors } from '@/components/Sponsors';
|
||||
import { StyledLink } from '@/components/StyledLink';
|
||||
import { styles } from '@/config/styles';
|
||||
|
||||
type IIndexProps = {
|
||||
params: Promise<{ locale: string }>;
|
||||
@@ -31,20 +33,19 @@ export default async function Index(props: IIndexProps) {
|
||||
<>
|
||||
<p>
|
||||
{`Follow `}
|
||||
<a
|
||||
className="text-blue-700 hover:border-b-2 hover:border-blue-700"
|
||||
<StyledLink
|
||||
href="https://twitter.com/ixartz"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
@Ixartz on Twitter
|
||||
</a>
|
||||
</StyledLink>
|
||||
{` for updates and more information about the boilerplate.`}
|
||||
</p>
|
||||
<h2 className="mt-5 text-2xl font-bold">
|
||||
<h2 className={styles.headings.h2Bold}>
|
||||
Boilerplate Code for Your Next.js Project with Tailwind CSS
|
||||
</h2>
|
||||
<p className="text-base">
|
||||
<p className={styles.text.base}>
|
||||
Next.js Boilerplate is a developer-friendly starter code for Next.js projects, built with Tailwind CSS and TypeScript.
|
||||
{' '}
|
||||
<span role="img" aria-label="zap">
|
||||
@@ -53,19 +54,19 @@ export default async function Index(props: IIndexProps) {
|
||||
{' '}
|
||||
Designed with developer experience in mind, it includes:
|
||||
</p>
|
||||
<ul className="mt-3 text-base">
|
||||
<ul className={styles.lists.baseMarginTop}>
|
||||
<li>🚀 Next.js with App Router support</li>
|
||||
<li>🔥 TypeScript for type checking</li>
|
||||
<li>💎 Tailwind CSS integration</li>
|
||||
<li>
|
||||
🔒 Authentication with
|
||||
{' '}
|
||||
<a
|
||||
className="font-bold text-blue-700 hover:border-b-2 hover:border-blue-700"
|
||||
<StyledLink
|
||||
href="https://clerk.com?utm_source=github&utm_medium=sponsorship&utm_campaign=nextjs-boilerplate"
|
||||
variant="primaryBold"
|
||||
>
|
||||
Clerk
|
||||
</a>
|
||||
</StyledLink>
|
||||
{' '}
|
||||
(includes passwordless, social, and multi-factor auth)
|
||||
</li>
|
||||
@@ -76,12 +77,12 @@ export default async function Index(props: IIndexProps) {
|
||||
<li>
|
||||
🌐 Multi-language support (i18n) with next-intl and
|
||||
{' '}
|
||||
<a
|
||||
className="font-bold text-blue-700 hover:border-b-2 hover:border-blue-700"
|
||||
<StyledLink
|
||||
href="https://l.crowdin.com/next-js"
|
||||
variant="primaryBold"
|
||||
>
|
||||
Crowdin
|
||||
</a>
|
||||
</StyledLink>
|
||||
</li>
|
||||
<li>🔴 Form handling (React Hook Form) and validation (Zod)</li>
|
||||
<li>📏 Linting and formatting (ESLint, Prettier)</li>
|
||||
@@ -91,43 +92,43 @@ export default async function Index(props: IIndexProps) {
|
||||
<li>
|
||||
🐰 AI-powered code reviews with
|
||||
{' '}
|
||||
<a
|
||||
className="font-bold text-blue-700 hover:border-b-2 hover:border-blue-700"
|
||||
<StyledLink
|
||||
href="https://www.coderabbit.ai?utm_source=next_js_starter&utm_medium=github&utm_campaign=next_js_starter_oss_2025"
|
||||
variant="primaryBold"
|
||||
>
|
||||
CodeRabbit
|
||||
</a>
|
||||
</StyledLink>
|
||||
</li>
|
||||
<li>
|
||||
🚨 Error monitoring (
|
||||
<a
|
||||
className="font-bold text-blue-700 hover:border-b-2 hover:border-blue-700"
|
||||
<StyledLink
|
||||
href="https://sentry.io/for/nextjs/?utm_source=github&utm_medium=paid-community&utm_campaign=general-fy25q1-nextjs&utm_content=github-banner-nextjsboilerplate-logo"
|
||||
variant="primaryBold"
|
||||
>
|
||||
Sentry
|
||||
</a>
|
||||
</StyledLink>
|
||||
) and logging (LogTape, an alternative to Pino.js)
|
||||
</li>
|
||||
<li>🖥️ Monitoring as Code (Checkly)</li>
|
||||
<li>
|
||||
🔐 Security and bot protection (
|
||||
<a
|
||||
className="font-bold text-blue-700 hover:border-b-2 hover:border-blue-700"
|
||||
<StyledLink
|
||||
href="https://launch.arcjet.com/Q6eLbRE"
|
||||
variant="primaryBold"
|
||||
>
|
||||
Arcjet
|
||||
</a>
|
||||
</StyledLink>
|
||||
)
|
||||
</li>
|
||||
<li>🤖 SEO optimization (metadata, JSON-LD, Open Graph tags)</li>
|
||||
<li>⚙️ Development tools (VSCode config, bundler analyzer, changelog generation)</li>
|
||||
</ul>
|
||||
<p className="text-base">
|
||||
<p className={styles.text.base}>
|
||||
Our sponsors' exceptional support has made this project possible.
|
||||
Their services integrate seamlessly with the boilerplate, and we
|
||||
recommend trying them out.
|
||||
</p>
|
||||
<h2 className="mt-5 text-2xl font-bold">{t('sponsors_title')}</h2>
|
||||
<h2 className={styles.headings.h2Bold}>{t('sponsors_title')}</h2>
|
||||
<Sponsors />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Metadata } from 'next';
|
||||
import { getTranslations, setRequestLocale } from 'next-intl/server';
|
||||
import Image from 'next/image';
|
||||
import { SponsorSection } from '@/components/SponsorSection';
|
||||
import { sponsors } from '@/config/sponsors';
|
||||
import { routing } from '@/libs/I18nRouting';
|
||||
|
||||
type IPortfolioDetailProps = {
|
||||
@@ -44,27 +45,7 @@ export default async function PortfolioDetail(props: IPortfolioDetailProps) {
|
||||
<h1 className="capitalize">{t('header', { slug })}</h1>
|
||||
<p>{t('content')}</p>
|
||||
|
||||
<div className="mt-5 text-center text-sm">
|
||||
{`${t('code_review_powered_by')} `}
|
||||
<a
|
||||
className="text-blue-700 hover:border-b-2 hover:border-blue-700"
|
||||
href="https://www.coderabbit.ai?utm_source=next_js_starter&utm_medium=github&utm_campaign=next_js_starter_oss_2025"
|
||||
>
|
||||
CodeRabbit
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<a
|
||||
href="https://www.coderabbit.ai?utm_source=next_js_starter&utm_medium=github&utm_campaign=next_js_starter_oss_2025"
|
||||
>
|
||||
<Image
|
||||
className="mx-auto mt-2"
|
||||
src="/assets/images/coderabbit-logo-light.svg"
|
||||
alt="CodeRabbit"
|
||||
width={128}
|
||||
height={22}
|
||||
/>
|
||||
</a>
|
||||
<SponsorSection sponsors={sponsors['portfolio-slug']} namespace="PortfolioSlug" />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { Metadata } from 'next';
|
||||
import { getTranslations, setRequestLocale } from 'next-intl/server';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { StyledLink } from '@/components/StyledLink';
|
||||
import { SponsorSection } from '@/components/SponsorSection';
|
||||
import { sponsors } from '@/config/sponsors';
|
||||
|
||||
type IPortfolioProps = {
|
||||
params: Promise<{ locale: string }>;
|
||||
@@ -34,44 +35,17 @@ export default async function Portfolio(props: IPortfolioProps) {
|
||||
|
||||
<div className="grid grid-cols-1 justify-items-start gap-3 md:grid-cols-2 xl:grid-cols-3">
|
||||
{Array.from(Array.from({ length: 6 }).keys()).map(elt => (
|
||||
<Link
|
||||
className="hover:text-blue-700"
|
||||
<StyledLink
|
||||
variant="hoverBlue"
|
||||
key={elt}
|
||||
href={`/portfolio/${elt}`}
|
||||
>
|
||||
{t('portfolio_name', { name: elt })}
|
||||
</Link>
|
||||
</StyledLink>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="mt-5 text-center text-sm">
|
||||
{`${t('error_reporting_powered_by')} `}
|
||||
<a
|
||||
className="text-blue-700 hover:border-b-2 hover:border-blue-700"
|
||||
href="https://sentry.io/for/nextjs/?utm_source=github&utm_medium=paid-community&utm_campaign=general-fy25q1-nextjs&utm_content=github-banner-nextjsboilerplate-logo"
|
||||
>
|
||||
Sentry
|
||||
</a>
|
||||
{` - ${t('coverage_powered_by')} `}
|
||||
<a
|
||||
className="text-blue-700 hover:border-b-2 hover:border-blue-700"
|
||||
href="https://about.codecov.io/codecov-free-trial/?utm_source=github&utm_medium=paid-community&utm_campaign=general-fy25q1-nextjs&utm_content=github-banner-nextjsboilerplate-logo"
|
||||
>
|
||||
Codecov
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<a
|
||||
href="https://sentry.io/for/nextjs/?utm_source=github&utm_medium=paid-community&utm_campaign=general-fy25q1-nextjs&utm_content=github-banner-nextjsboilerplate-logo"
|
||||
>
|
||||
<Image
|
||||
className="mx-auto mt-2"
|
||||
src="/assets/images/sentry-dark.png"
|
||||
alt="Sentry"
|
||||
width={128}
|
||||
height={38}
|
||||
/>
|
||||
</a>
|
||||
<SponsorSection sponsors={sponsors.portfolio} namespace="Portfolio" />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
34
src/components/NavLink.tsx
Normal file
34
src/components/NavLink.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import Link from 'next/link';
|
||||
import { styles } from '@/config/styles';
|
||||
|
||||
type NavLinkProps = {
|
||||
href: string;
|
||||
children: React.ReactNode;
|
||||
external?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Navigation link component with consistent styling
|
||||
* Used for navigation menus in layouts
|
||||
*/
|
||||
export function NavLink({ href, children, external }: NavLinkProps) {
|
||||
if (external) {
|
||||
return (
|
||||
<a
|
||||
className={styles.links.nav}
|
||||
href={href}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
className={styles.links.nav}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
53
src/components/SponsorSection.tsx
Normal file
53
src/components/SponsorSection.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
import { useTranslations } from 'next-intl';
|
||||
import Image from 'next/image';
|
||||
import type { SponsorConfig } from '@/config/sponsors';
|
||||
import { styles } from '@/config/styles';
|
||||
|
||||
type SponsorSectionProps = {
|
||||
sponsors: SponsorConfig[];
|
||||
namespace: string;
|
||||
};
|
||||
|
||||
export function SponsorSection({ sponsors, namespace }: SponsorSectionProps) {
|
||||
const t = useTranslations(namespace);
|
||||
|
||||
if (!sponsors || sponsors.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={`${styles.spacing.marginTop5} ${styles.text.centerSmall}`}>
|
||||
{sponsors.map((sponsor, index) => (
|
||||
<span key={sponsor.id}>
|
||||
{index > 0 && ' - '}
|
||||
{`${t(sponsor.translationKey)} `}
|
||||
<a
|
||||
className={styles.links.primary}
|
||||
href={sponsor.url}
|
||||
>
|
||||
{sponsor.name}
|
||||
</a>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{sponsors
|
||||
.filter(sponsor => sponsor.logo.src && sponsor.logo.width > 0 && sponsor.logo.height > 0)
|
||||
.map(sponsor => (
|
||||
<a
|
||||
key={sponsor.id}
|
||||
href={sponsor.url}
|
||||
>
|
||||
<Image
|
||||
className={styles.image.centerMarginTop}
|
||||
src={sponsor.logo.src}
|
||||
alt={sponsor.logo.alt}
|
||||
width={sponsor.logo.width}
|
||||
height={sponsor.logo.height}
|
||||
/>
|
||||
</a>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
54
src/components/StyledLink.tsx
Normal file
54
src/components/StyledLink.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import Link from 'next/link';
|
||||
import { styles } from '@/config/styles';
|
||||
|
||||
type StyledLinkProps = {
|
||||
href: string;
|
||||
children: React.ReactNode;
|
||||
variant?: 'primary' | 'primaryBold' | 'hoverBlue';
|
||||
target?: '_blank' | '_self' | '_parent' | '_top';
|
||||
rel?: string;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Styled Link component that uses configured styles
|
||||
* Provides consistent link styling across the app
|
||||
*/
|
||||
export function StyledLink({
|
||||
href,
|
||||
children,
|
||||
variant = 'primary',
|
||||
target,
|
||||
rel,
|
||||
className,
|
||||
}: StyledLinkProps) {
|
||||
const baseClassName = styles.links[variant];
|
||||
const combinedClassName = className ? `${baseClassName} ${className}` : baseClassName;
|
||||
|
||||
// Use Next.js Link for internal links, regular <a> for external
|
||||
const isExternal = href.startsWith('http') || href.startsWith('//');
|
||||
|
||||
if (isExternal) {
|
||||
return (
|
||||
<a
|
||||
className={combinedClassName}
|
||||
href={href}
|
||||
target={target}
|
||||
rel={rel}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Link
|
||||
className={combinedClassName}
|
||||
href={href}
|
||||
target={target}
|
||||
rel={rel}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
61
src/config/navigation.ts
Normal file
61
src/config/navigation.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Navigation configuration for marketing layout
|
||||
* Defines navigation items for left and right navigation menus
|
||||
*/
|
||||
|
||||
export type NavLink = {
|
||||
id: string;
|
||||
translationKey: string;
|
||||
href: string;
|
||||
external?: boolean;
|
||||
label?: string; // For links without translation (like GitHub)
|
||||
};
|
||||
|
||||
export type NavigationConfig = {
|
||||
left: NavLink[];
|
||||
right: NavLink[];
|
||||
};
|
||||
|
||||
export const marketingNavigation: NavigationConfig = {
|
||||
left: [
|
||||
{
|
||||
id: 'home',
|
||||
translationKey: 'home_link',
|
||||
href: '/',
|
||||
},
|
||||
{
|
||||
id: 'about',
|
||||
translationKey: 'about_link',
|
||||
href: '/about/',
|
||||
},
|
||||
{
|
||||
id: 'counter',
|
||||
translationKey: 'counter_link',
|
||||
href: '/counter/',
|
||||
},
|
||||
{
|
||||
id: 'portfolio',
|
||||
translationKey: 'portfolio_link',
|
||||
href: '/portfolio/',
|
||||
},
|
||||
{
|
||||
id: 'github',
|
||||
label: 'GitHub',
|
||||
translationKey: '',
|
||||
href: 'https://github.com/ixartz/Next-js-Boilerplate',
|
||||
external: true,
|
||||
},
|
||||
],
|
||||
right: [
|
||||
{
|
||||
id: 'sign-in',
|
||||
translationKey: 'sign_in_link',
|
||||
href: '/sign-in/',
|
||||
},
|
||||
{
|
||||
id: 'sign-up',
|
||||
translationKey: 'sign_up_link',
|
||||
href: '/sign-up/',
|
||||
},
|
||||
],
|
||||
};
|
||||
85
src/config/sponsors.ts
Normal file
85
src/config/sponsors.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* Sponsors configuration for marketing pages
|
||||
* Defines sponsor sections that appear at the bottom of marketing pages
|
||||
*/
|
||||
|
||||
export type SponsorConfig = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
url: string;
|
||||
logo: {
|
||||
src: string;
|
||||
alt: string;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
translationKey: string;
|
||||
};
|
||||
|
||||
export type PageSponsorsConfig = {
|
||||
[page: string]: SponsorConfig[];
|
||||
};
|
||||
|
||||
export const sponsors: PageSponsorsConfig = {
|
||||
about: [
|
||||
{
|
||||
id: 'crowdin',
|
||||
name: 'Crowdin',
|
||||
description: 'Translation Management System',
|
||||
url: 'https://l.crowdin.com/next-js',
|
||||
logo: {
|
||||
src: '/assets/images/crowdin-dark.png',
|
||||
alt: 'Crowdin Translation Management System',
|
||||
width: 128,
|
||||
height: 26,
|
||||
},
|
||||
translationKey: 'translation_powered_by',
|
||||
},
|
||||
],
|
||||
portfolio: [
|
||||
{
|
||||
id: 'sentry',
|
||||
name: 'Sentry',
|
||||
description: 'Error Monitoring',
|
||||
url: 'https://sentry.io/for/nextjs/?utm_source=github&utm_medium=paid-community&utm_campaign=general-fy25q1-nextjs&utm_content=github-banner-nextjsboilerplate-logo',
|
||||
logo: {
|
||||
src: '/assets/images/sentry-dark.png',
|
||||
alt: 'Sentry',
|
||||
width: 128,
|
||||
height: 38,
|
||||
},
|
||||
translationKey: 'error_reporting_powered_by',
|
||||
},
|
||||
],
|
||||
'portfolio-slug': [
|
||||
{
|
||||
id: 'coderabbit',
|
||||
name: 'CodeRabbit',
|
||||
description: 'AI Code Reviews',
|
||||
url: 'https://www.coderabbit.ai?utm_source=next_js_starter&utm_medium=github&utm_campaign=next_js_starter_oss_2025',
|
||||
logo: {
|
||||
src: '/assets/images/coderabbit-logo-light.svg',
|
||||
alt: 'CodeRabbit',
|
||||
width: 128,
|
||||
height: 22,
|
||||
},
|
||||
translationKey: 'code_review_powered_by',
|
||||
},
|
||||
],
|
||||
counter: [
|
||||
{
|
||||
id: 'arcjet',
|
||||
name: 'Arcjet',
|
||||
description: 'Security and Bot Protection',
|
||||
url: 'https://launch.arcjet.com/Q6eLbRE',
|
||||
logo: {
|
||||
src: '/assets/images/arcjet-light.svg',
|
||||
alt: 'Arcjet',
|
||||
width: 128,
|
||||
height: 38,
|
||||
},
|
||||
translationKey: 'security_powered_by',
|
||||
},
|
||||
],
|
||||
};
|
||||
34
src/config/styles.ts
Normal file
34
src/config/styles.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Shared styles configuration
|
||||
* Defines reusable CSS class names for consistent styling across the app
|
||||
*/
|
||||
|
||||
export const styles = {
|
||||
links: {
|
||||
primary: 'text-blue-700 hover:border-b-2 hover:border-blue-700',
|
||||
primaryBold: 'font-bold text-blue-700 hover:border-b-2 hover:border-blue-700',
|
||||
hoverBlue: 'hover:text-blue-700',
|
||||
nav: 'border-none text-gray-700 hover:text-gray-900',
|
||||
},
|
||||
text: {
|
||||
centerSmall: 'text-center text-sm',
|
||||
base: 'text-base',
|
||||
},
|
||||
spacing: {
|
||||
marginTop2: 'mt-2',
|
||||
marginTop3: 'mt-3',
|
||||
marginTop5: 'mt-5',
|
||||
},
|
||||
image: {
|
||||
centerMarginTop: 'mx-auto mt-2',
|
||||
},
|
||||
headings: {
|
||||
h2Bold: 'mt-5 text-2xl font-bold',
|
||||
},
|
||||
lists: {
|
||||
baseMarginTop: 'mt-3 text-base',
|
||||
},
|
||||
containers: {
|
||||
contentPadding: 'py-5 text-xl [&_p]:my-6',
|
||||
},
|
||||
} as const;
|
||||
Reference in New Issue
Block a user