create-audora-next 0.1.7 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +15 -5
  2. package/index.ts +9 -4
  3. package/package.json +6 -1
  4. package/templates/blog/README.md +164 -0
  5. package/templates/blog/bun.lock +1341 -0
  6. package/templates/blog/env.example.template +5 -0
  7. package/templates/blog/eslint.config.mjs +18 -0
  8. package/templates/blog/gitignore.template +41 -0
  9. package/templates/blog/husky.template/pre-commit +16 -0
  10. package/templates/blog/lint-staged.config.mjs +17 -0
  11. package/templates/blog/next.config.ts +38 -0
  12. package/templates/blog/package.json +59 -0
  13. package/templates/blog/postcss.config.mjs +7 -0
  14. package/templates/blog/public/favicon/apple-touch-icon.png +0 -0
  15. package/templates/blog/public/favicon/favicon-96x96.png +0 -0
  16. package/templates/blog/public/favicon/favicon.ico +0 -0
  17. package/templates/blog/public/favicon/favicon.svg +1 -0
  18. package/templates/blog/public/favicon/site.webmanifest +21 -0
  19. package/templates/blog/public/favicon/web-app-manifest-192x192.png +0 -0
  20. package/templates/blog/public/favicon/web-app-manifest-512x512.png +0 -0
  21. package/templates/blog/public/images/screenshot-desktop-dark.webp +0 -0
  22. package/templates/blog/public/images/screenshot-desktop-light.webp +0 -0
  23. package/templates/blog/public/images/screenshot-mobile-dark.webp +0 -0
  24. package/templates/blog/public/images/screenshot-mobile-light.webp +0 -0
  25. package/templates/blog/src/app/blogs/[slug]/page.tsx +171 -0
  26. package/templates/blog/src/app/blogs/page.tsx +108 -0
  27. package/templates/blog/src/app/layout.tsx +60 -0
  28. package/templates/blog/src/app/llms-full.txt/route.ts +97 -0
  29. package/templates/blog/src/app/llms.txt/route.ts +40 -0
  30. package/templates/blog/src/app/manifest.ts +61 -0
  31. package/templates/blog/src/app/page.tsx +57 -0
  32. package/templates/blog/src/app/robots.ts +16 -0
  33. package/templates/blog/src/app/sitemap.ts +52 -0
  34. package/templates/blog/src/blogs/components/animated-blog-list.tsx +33 -0
  35. package/templates/blog/src/blogs/components/blog-post-card.tsx +46 -0
  36. package/templates/blog/src/blogs/components/blog-section.tsx +34 -0
  37. package/templates/blog/src/blogs/components/blog-table-of-contents.tsx +369 -0
  38. package/templates/blog/src/blogs/components/copy-button.tsx +46 -0
  39. package/templates/blog/src/blogs/components/mdx.tsx +225 -0
  40. package/templates/blog/src/blogs/content/cosketch/cosketch-canvas-engine.mdx +186 -0
  41. package/templates/blog/src/blogs/content/cosketch/cosketch-docker-architecture.mdx +175 -0
  42. package/templates/blog/src/blogs/content/cosketch/cosketch-eraser-and-selection.mdx +207 -0
  43. package/templates/blog/src/blogs/content/hello-world.mdx +66 -0
  44. package/templates/blog/src/blogs/data/mdx.ts +68 -0
  45. package/templates/blog/src/blogs/utils/extract-headings.ts +38 -0
  46. package/templates/blog/src/components/copyable-code.tsx +41 -0
  47. package/templates/blog/src/components/footer.tsx +25 -0
  48. package/templates/blog/src/components/header.tsx +27 -0
  49. package/templates/blog/src/components/icons.tsx +84 -0
  50. package/templates/blog/src/components/section-heading.tsx +11 -0
  51. package/templates/blog/src/components/theme-provider.tsx +11 -0
  52. package/templates/blog/src/components/theme-toggle.tsx +20 -0
  53. package/templates/blog/src/components/view-all-link.tsx +56 -0
  54. package/templates/blog/src/config/site.ts +19 -0
  55. package/templates/blog/src/data/llms.ts +112 -0
  56. package/templates/blog/src/data/site.ts +52 -0
  57. package/templates/blog/src/lib/seo.ts +190 -0
  58. package/templates/blog/src/lib/utils.ts +83 -0
  59. package/templates/blog/src/styles/globals.css +99 -0
  60. package/templates/blog/src/utils/cn.ts +7 -0
  61. package/templates/blog/tsconfig.json +34 -0
@@ -0,0 +1,68 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import matter from "gray-matter";
4
+
5
+ type Metadata = {
6
+ title: string;
7
+ publishedAt: string;
8
+ summary: string;
9
+ image?: string;
10
+ repoUrl?: string;
11
+ };
12
+
13
+ type BlogPost = {
14
+ metadata: Metadata;
15
+ slug: string;
16
+ content: string;
17
+ };
18
+
19
+ function getMDXFiles(dir: string): string[] {
20
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
21
+
22
+ const files = entries
23
+ .filter((entry) => entry.isFile() && path.extname(entry.name) === ".mdx")
24
+ .map((entry) => path.join(dir, entry.name));
25
+
26
+ const folders = entries.filter((entry) => entry.isDirectory());
27
+
28
+ const nestedFiles = folders.flatMap((folder) =>
29
+ getMDXFiles(path.join(dir, folder.name)),
30
+ );
31
+
32
+ return [...files, ...nestedFiles];
33
+ }
34
+
35
+ function readMDXFile(filePath: string) {
36
+ const rawContent = fs.readFileSync(filePath, "utf-8");
37
+ return matter(rawContent);
38
+ }
39
+
40
+ function getMDXData(dir: string): BlogPost[] {
41
+ const mdxFiles = getMDXFiles(dir);
42
+ return mdxFiles.map((file) => {
43
+ const { data, content } = readMDXFile(file);
44
+ const slug = path.basename(file, path.extname(file));
45
+
46
+ return {
47
+ metadata: data as Metadata,
48
+ slug,
49
+ content,
50
+ };
51
+ });
52
+ }
53
+
54
+ export function getBlogPosts() {
55
+ return getMDXData(path.join(process.cwd(), "src/blogs/content"));
56
+ }
57
+
58
+ export function getBlogPost(slug: string) {
59
+ const posts = getMDXData(path.join(process.cwd(), "src/blogs/content"));
60
+
61
+ const post = posts.find((p) => p.slug === slug);
62
+
63
+ if (!post) {
64
+ return null;
65
+ }
66
+
67
+ return post;
68
+ }
@@ -0,0 +1,38 @@
1
+ export type Heading = {
2
+ id: string;
3
+ text: string;
4
+ level: 2 | 3;
5
+ };
6
+
7
+ /**
8
+ * Extracts h2 and h3 headings from MDX content
9
+ * Creates slug IDs matching rehype-slug behavior
10
+ */
11
+ export function extractHeadings(content: string): Heading[] {
12
+ const headings: Heading[] = [];
13
+
14
+ // Match ## and ### headings in MDX
15
+ const headingRegex = /^(#{2,3})\s+(.+)$/gm;
16
+ let match;
17
+
18
+ while ((match = headingRegex.exec(content)) !== null) {
19
+ const level = match[1].length as 2 | 3;
20
+ const text = match[2]
21
+ .replace(/\*\*([^*]+)\*\*/g, "$1") // Remove bold
22
+ .replace(/\*([^*]+)\*/g, "$1") // Remove italic
23
+ .replace(/`([^`]+)`/g, "$1") // Remove code blocks
24
+ .replace(/\[([^\]]+)\]\([^)]+\)/g, "$1") // Remove links, keep text
25
+ .trim();
26
+
27
+ // Generate slug matching rehype-slug styles (which seems to map each space to a dash)
28
+ const id = text
29
+ .toLowerCase()
30
+ .replace(/[^\w\s-]/g, "") // Remove special characters
31
+ .replace(/\s/g, "-") // Replace each space with a hyphen (don't squash)
32
+ .replace(/^-|-$/g, ""); // Remove leading/trailing hyphens
33
+
34
+ headings.push({ id, text, level });
35
+ }
36
+
37
+ return headings;
38
+ }
@@ -0,0 +1,41 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { CopyIcon, CheckIcon } from "./icons";
5
+
6
+ interface CopyableCodeProps {
7
+ code: string;
8
+ }
9
+
10
+ export const CopyableCode = ({ code }: CopyableCodeProps) => {
11
+ const [copied, setCopied] = useState(false);
12
+
13
+ const handleCopy = async () => {
14
+ try {
15
+ await navigator.clipboard.writeText(code);
16
+ setCopied(true);
17
+ setTimeout(() => setCopied(false), 2000);
18
+ } catch (err) {
19
+ console.error("Failed to copy:", err);
20
+ }
21
+ };
22
+
23
+ return (
24
+ <div className="relative mx-auto mt-12 max-w-lg rounded-md border border-neutral-200 bg-neutral-200 p-4 pr-12 dark:border-neutral-800 dark:bg-neutral-900">
25
+ <button
26
+ onClick={handleCopy}
27
+ className="absolute top-3 right-3 inline-flex h-8 w-8 items-center justify-center rounded-md text-neutral-600 transition-colors hover:bg-neutral-300 hover:text-neutral-950 dark:text-neutral-400 dark:hover:bg-neutral-800 dark:hover:text-neutral-200"
28
+ aria-label="Copy code"
29
+ >
30
+ {copied ? (
31
+ <CheckIcon className="h-4 w-4" />
32
+ ) : (
33
+ <CopyIcon className="h-4 w-4" />
34
+ )}
35
+ </button>
36
+ <code className="block text-xs text-neutral-700 md:text-sm dark:text-neutral-300">
37
+ <span className="text-neutral-400 dark:text-neutral-600">$</span> {code}
38
+ </code>
39
+ </div>
40
+ );
41
+ };
@@ -0,0 +1,25 @@
1
+ import Link from "next/link";
2
+
3
+ export function Footer() {
4
+ return (
5
+ <footer className="border-t border-border py-6">
6
+ <div className="container flex max-w-5xl flex-wrap items-center justify-center gap-4 px-4 mx-auto text-center">
7
+ <Link
8
+ href="/llms.txt"
9
+ className="text-xs text-muted-foreground transition-colors hover:text-foreground"
10
+ >
11
+ llms.txt
12
+ </Link>
13
+ <span className="text-border" aria-hidden>
14
+ ·
15
+ </span>
16
+ <Link
17
+ href="/llms-full.txt"
18
+ className="text-xs text-muted-foreground transition-colors hover:text-foreground"
19
+ >
20
+ llms-full.txt
21
+ </Link>
22
+ </div>
23
+ </footer>
24
+ );
25
+ }
@@ -0,0 +1,27 @@
1
+ import Link from "next/link";
2
+ import { SITE_CONFIG } from "@/config/site";
3
+ import { ThemeToggle } from "@/components/theme-toggle";
4
+
5
+ export function Header() {
6
+ return (
7
+ <header className="sticky top-0 z-50 w-full border-b border-border bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
8
+ <div className="container flex h-14 max-w-5xl items-center justify-between px-4 mx-auto">
9
+ <Link
10
+ href="/"
11
+ className="text-sm font-semibold tracking-tight text-foreground transition-colors hover:text-foreground/80"
12
+ >
13
+ {SITE_CONFIG.name}
14
+ </Link>
15
+ <nav className="flex items-center gap-4">
16
+ <Link
17
+ href="/blogs"
18
+ className="text-sm font-medium text-muted-foreground transition-colors hover:text-foreground"
19
+ >
20
+ Blog
21
+ </Link>
22
+ <ThemeToggle />
23
+ </nav>
24
+ </div>
25
+ </header>
26
+ );
27
+ }
@@ -0,0 +1,84 @@
1
+ export const MoonIcon = ({ className }: { className?: string }) => {
2
+ return (
3
+ <svg
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ width="20"
6
+ height="20"
7
+ viewBox="0 0 24 24"
8
+ fill="none"
9
+ stroke="currentColor"
10
+ strokeWidth="2"
11
+ strokeLinecap="round"
12
+ strokeLinejoin="round"
13
+ className={className}
14
+ >
15
+ <path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" />
16
+ </svg>
17
+ );
18
+ };
19
+
20
+ export const SunIcon = ({ className }: { className?: string }) => {
21
+ return (
22
+ <svg
23
+ xmlns="http://www.w3.org/2000/svg"
24
+ width="20"
25
+ height="20"
26
+ viewBox="0 0 24 24"
27
+ fill="none"
28
+ stroke="currentColor"
29
+ strokeWidth="2"
30
+ strokeLinecap="round"
31
+ strokeLinejoin="round"
32
+ className={className}
33
+ >
34
+ <circle cx="12" cy="12" r="4" />
35
+ <path d="M12 2v2" />
36
+ <path d="M12 20v2" />
37
+ <path d="m4.93 4.93 1.41 1.41" />
38
+ <path d="m17.66 17.66 1.41 1.41" />
39
+ <path d="M2 12h2" />
40
+ <path d="M20 12h2" />
41
+ <path d="m6.34 17.66-1.41 1.41" />
42
+ <path d="m19.07 4.93-1.41 1.41" />
43
+ </svg>
44
+ );
45
+ };
46
+
47
+ export const CopyIcon = ({ className }: { className?: string }) => {
48
+ return (
49
+ <svg
50
+ xmlns="http://www.w3.org/2000/svg"
51
+ width="16"
52
+ height="16"
53
+ viewBox="0 0 24 24"
54
+ fill="none"
55
+ stroke="currentColor"
56
+ strokeWidth="2"
57
+ strokeLinecap="round"
58
+ strokeLinejoin="round"
59
+ className={className}
60
+ >
61
+ <rect width="14" height="14" x="8" y="8" rx="2" ry="2" />
62
+ <path d="M4 16c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2h8c1.1 0 2 .9 2 2" />
63
+ </svg>
64
+ );
65
+ };
66
+
67
+ export const CheckIcon = ({ className }: { className?: string }) => {
68
+ return (
69
+ <svg
70
+ xmlns="http://www.w3.org/2000/svg"
71
+ width="16"
72
+ height="16"
73
+ viewBox="0 0 24 24"
74
+ fill="none"
75
+ stroke="currentColor"
76
+ strokeWidth="2"
77
+ strokeLinecap="round"
78
+ strokeLinejoin="round"
79
+ className={className}
80
+ >
81
+ <path d="M20 6 9 17l-5-5" />
82
+ </svg>
83
+ );
84
+ };
@@ -0,0 +1,11 @@
1
+ export default function SectionHeading({
2
+ children,
3
+ }: {
4
+ children: React.ReactNode;
5
+ }) {
6
+ return (
7
+ <h2 className="text-2xl font-bold tracking-tight text-foreground sm:text-3xl py-4">
8
+ {children}
9
+ </h2>
10
+ );
11
+ }
@@ -0,0 +1,11 @@
1
+ "use client";
2
+
3
+ import { ThemeProvider as NextThemesProvider } from "next-themes";
4
+ import type { ComponentProps } from "react";
5
+
6
+ export function ThemeProvider({
7
+ children,
8
+ ...props
9
+ }: ComponentProps<typeof NextThemesProvider>) {
10
+ return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
11
+ }
@@ -0,0 +1,20 @@
1
+ "use client";
2
+
3
+ import { useTheme } from "next-themes";
4
+ import { MoonIcon, SunIcon } from "./icons";
5
+
6
+ export const ThemeToggle = () => {
7
+ const { setTheme, resolvedTheme } = useTheme();
8
+
9
+ return (
10
+ <button
11
+ onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}
12
+ className="inline-flex h-9 w-9 items-center justify-center rounded-md text-sm font-medium transition-colors hover:bg-zinc-100 focus-visible:ring-1 focus-visible:ring-zinc-950 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50 dark:hover:bg-zinc-800 dark:focus-visible:ring-zinc-300"
13
+ aria-label="Toggle theme"
14
+ >
15
+ <SunIcon className="hidden text-muted-foreground transition-colors hover:text-foreground dark:block" />
16
+ <MoonIcon className="block text-muted-foreground transition-colors hover:text-foreground dark:hidden" />
17
+ <span className="sr-only">Toggle theme</span>
18
+ </button>
19
+ );
20
+ };
@@ -0,0 +1,56 @@
1
+ "use client";
2
+
3
+ import Link from "next/link";
4
+ import { ArrowUpRight } from "lucide-react";
5
+ import { motion } from "motion/react";
6
+
7
+ interface ViewAllLinkProps {
8
+ href: string;
9
+ children?: React.ReactNode;
10
+ }
11
+
12
+ const MotionLink = motion.create(Link);
13
+
14
+ export function ViewAllLink({ href, children = "View All" }: ViewAllLinkProps) {
15
+ return (
16
+ <motion.div
17
+ initial={{ opacity: 0, y: 8 }}
18
+ whileInView={{ opacity: 1, y: 0 }}
19
+ viewport={{ once: true, margin: "-40px" }}
20
+ transition={{
21
+ type: "spring",
22
+ stiffness: 400,
23
+ damping: 30,
24
+ }}
25
+ >
26
+ <MotionLink
27
+ href={href}
28
+ className="group inline-flex items-center gap-2 rounded-lg bg-zinc-900 px-5 py-2.5 text-sm font-medium text-white ring-2 ring-zinc-900/10 ring-offset-2 transition-all hover:bg-zinc-800 active:scale-95 dark:bg-zinc-100 dark:text-zinc-900 dark:ring-zinc-100/20 dark:ring-offset-black dark:hover:bg-zinc-200"
29
+ whileHover="hover"
30
+ >
31
+ {children}
32
+ <div className="relative h-4 w-4 overflow-hidden">
33
+ <motion.div
34
+ variants={{
35
+ hover: { x: "150%", y: "-150%" },
36
+ }}
37
+ transition={{ duration: 0.3, ease: "easeInOut" }}
38
+ className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"
39
+ >
40
+ <ArrowUpRight className="h-4 w-4" />
41
+ </motion.div>
42
+ <motion.div
43
+ initial={{ x: "-150%", y: "150%" }}
44
+ variants={{
45
+ hover: { x: "0%", y: "0%" },
46
+ }}
47
+ transition={{ duration: 0.3, ease: "easeInOut" }}
48
+ className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"
49
+ >
50
+ <ArrowUpRight className="h-4 w-4" />
51
+ </motion.div>
52
+ </div>
53
+ </MotionLink>
54
+ </motion.div>
55
+ );
56
+ }
@@ -0,0 +1,19 @@
1
+ import SITE_DATA from "@/data/site";
2
+
3
+ export const SITE_CONFIG = {
4
+ name: SITE_DATA.name,
5
+ url: SITE_DATA.url,
6
+ ogImage: SITE_DATA.ogImage,
7
+ tagline: SITE_DATA.tagline,
8
+ description: SITE_DATA.description,
9
+ shortDescription: SITE_DATA.shortDescription,
10
+ alternateNames: SITE_DATA.alternateNames,
11
+ twitterHandle: SITE_DATA.twitterHandle,
12
+ keywords: SITE_DATA.keywords,
13
+ features: SITE_DATA.features,
14
+ };
15
+
16
+ export const META_THEME_COLORS = {
17
+ light: "#ededed",
18
+ dark: "#09090b",
19
+ };
@@ -0,0 +1,112 @@
1
+ const LLMS_DATA = {
2
+ // Quick start command for cloning the starter
3
+ quickStart: "bunx degit AudoraLabs/next-starter my-app",
4
+
5
+ // GitHub repository URL
6
+ repository: "https://github.com/AudoraLabs/next-starter",
7
+
8
+ // Tech stack summary
9
+ techStack: [
10
+ "Next.js 16 with App Router",
11
+ "React 19",
12
+ "TypeScript (strict mode)",
13
+ "Tailwind CSS 4",
14
+ "Bun (package manager)",
15
+ "ESLint 9 (flat config)",
16
+ "Prettier",
17
+ "React Compiler",
18
+ ],
19
+
20
+ // Key features for the summary
21
+ features: [
22
+ "SEO-ready with robots.ts, sitemap.ts, and Open Graph metadata",
23
+ "Title templates and metadataBase configured",
24
+ "Path alias @/* mapped to ./src/*",
25
+ "Dark mode support via CSS custom properties",
26
+ "Geist font family pre-configured",
27
+ "Pre-commit hooks with Husky and lint-staged",
28
+ ],
29
+
30
+ // Folder structure for llms-full.txt
31
+ folderStructure: `src/
32
+ ├── app/
33
+ │ ├── layout.tsx # Root layout with metadata
34
+ │ ├── page.tsx # Home page
35
+ │ ├── manifest.ts # PWA manifest
36
+ │ ├── robots.ts # robots.txt generation
37
+ │ ├── sitemap.ts # Sitemap generation
38
+ │ ├── llms.txt/ # AI-friendly summary (this file)
39
+ │ └── llms-full.txt/ # AI-friendly full documentation
40
+ ├── components/
41
+ │ ├── icons.tsx # Icon components
42
+ │ ├── theme-provider.tsx # Theme context provider
43
+ │ └── theme-toggle.tsx # Dark/light mode toggle
44
+ ├── config/
45
+ │ └── site.ts # Site configuration exports
46
+ ├── data/
47
+ │ ├── site.ts # Site data (name, URL, description)
48
+ │ └── llms.ts # LLMs.txt content configuration
49
+ ├── lib/
50
+ │ └── seo.ts # SEO utilities and JSON-LD generators
51
+ ├── styles/
52
+ │ └── globals.css # Global styles and Tailwind imports
53
+ └── utils/
54
+ └── cn.ts # Class name utility`,
55
+
56
+ // Development commands
57
+ commands: {
58
+ dev: "bun dev",
59
+ build: "bun run build",
60
+ start: "bun start",
61
+ lint: "bun lint",
62
+ format: "bun format",
63
+ },
64
+
65
+ // SEO documentation for llms-full.txt
66
+ seoDetails: `## SEO Configuration
67
+
68
+ This starter includes production-ready SEO defaults:
69
+
70
+ ### Metadata (src/lib/seo.ts)
71
+ - getMetadata(): Default site metadata with Open Graph and Twitter cards
72
+ - getPageMetadata(): Page-specific metadata generator
73
+ - getViewport(): Viewport configuration with theme colors
74
+
75
+ ### Structured Data (JSON-LD)
76
+ - getWebSiteJsonLd(): WebSite schema
77
+ - getOrganizationJsonLd(): Organization schema
78
+ - getBreadcrumbJsonLd(): Breadcrumb navigation schema
79
+
80
+ ### Configuration (src/data/site.ts)
81
+ Customize these values for your project:
82
+ - name: Site name
83
+ - url: Base URL for canonical links
84
+ - ogImage: Default Open Graph image
85
+ - tagline: Short tagline
86
+ - description: Full description
87
+ - twitterHandle: Twitter/X handle
88
+
89
+ ### Files
90
+ - robots.ts: Generates robots.txt with sitemap reference
91
+ - sitemap.ts: Dynamic sitemap generation
92
+ - manifest.ts: PWA web app manifest`,
93
+
94
+ // Conventions documentation
95
+ conventions: `## Conventions
96
+
97
+ ### Path Aliases
98
+ Use @/* to import from src/*:
99
+ - import { cn } from "@/utils/cn"
100
+ - import { SITE_CONFIG } from "@/config/site"
101
+
102
+ ### Theme
103
+ Dark mode uses CSS custom properties. Toggle with ThemeProvider and ThemeToggle components.
104
+
105
+ ### Components
106
+ No component library included by default. Add shadcn/ui or Radix as needed.
107
+
108
+ ### Styling
109
+ Tailwind CSS 4 with PostCSS. Global styles in src/styles/globals.css.`,
110
+ };
111
+
112
+ export default LLMS_DATA;
@@ -0,0 +1,52 @@
1
+ const SITE_DATA = {
2
+ name: "next-starter",
3
+ url: process.env.NEXT_PUBLIC_SITE_URL ?? "http://localhost:3000",
4
+ ogImage:
5
+ process.env.NEXT_PUBLIC_OG_IMAGE ?? "/images/screenshot-desktop-light.webp",
6
+
7
+ tagline: "Kickstart your Next.js project",
8
+ description: "A modern, performant starter kit for Next.js applications.",
9
+ shortDescription:
10
+ "A modern, performant starter kit for Next.js applications.",
11
+
12
+ alternateNames: ["next-starter", "Next Starter", "NextJS Starter"],
13
+
14
+ twitterHandle: "@nextstarter",
15
+
16
+ keywords: [
17
+ "next-starter",
18
+ "next.js starter",
19
+ "react boilerplate",
20
+ "next.js boilerplate",
21
+ "nextjs template",
22
+ "starter project",
23
+ "next.js template",
24
+ "typescript nextjs starter",
25
+ "boilerplate for next.js",
26
+ "web app starter",
27
+ ],
28
+
29
+ features: [
30
+ {
31
+ title: "MDX blog",
32
+ description:
33
+ "Write posts in MDX with frontmatter, code blocks, and components.",
34
+ },
35
+ {
36
+ title: "SEO & metadata",
37
+ description:
38
+ "Built-in metadata, Open Graph, and JSON-LD for search and sharing.",
39
+ },
40
+ {
41
+ title: "Dark mode",
42
+ description: "System-aware theme toggle with persistent preference.",
43
+ },
44
+ {
45
+ title: "TypeScript",
46
+ description:
47
+ "Full TypeScript support and strict mode for safer refactors.",
48
+ },
49
+ ],
50
+ };
51
+
52
+ export default SITE_DATA;