bsmnt 0.2.0 → 0.2.5

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 (60) hide show
  1. package/README.md +1 -1
  2. package/package.json +22 -5
  3. package/src/templates/next-default/.biome/plugins/no-anchor-element.grit +12 -0
  4. package/src/templates/next-default/.biome/plugins/no-relative-parent-imports.grit +10 -0
  5. package/src/templates/next-default/.biome/plugins/no-unnecessary-forwardref.grit +9 -0
  6. package/src/templates/next-default/.github/PULL_REQUEST_TEMPLATE.md +14 -0
  7. package/src/templates/next-default/.vscode/extensions.json +20 -0
  8. package/src/templates/next-default/.vscode/settings.json +105 -0
  9. package/src/templates/next-default/app/favicon.ico +0 -0
  10. package/src/templates/next-default/app/layout.tsx +104 -0
  11. package/src/templates/next-default/app/page.tsx +11 -0
  12. package/src/templates/next-default/app/robots.ts +15 -0
  13. package/src/templates/next-default/app/sitemap.ts +16 -0
  14. package/src/templates/next-default/biome.json +2 -2
  15. package/src/templates/next-default/components/layout/footer/index.tsx +31 -0
  16. package/src/templates/next-default/components/layout/header/index.tsx +9 -0
  17. package/src/templates/next-default/components/layout/theme/index.tsx +66 -0
  18. package/src/templates/next-default/components/layout/wrapper/index.tsx +63 -0
  19. package/src/templates/next-default/components/ui/README.md +77 -0
  20. package/src/templates/next-default/components/ui/image/README.md +37 -0
  21. package/src/templates/next-default/components/ui/image/index.tsx +219 -0
  22. package/src/templates/next-default/components/ui/link/index.tsx +152 -0
  23. package/src/templates/next-default/lib/utils/metadata.ts +26 -26
  24. package/src/templates/next-default/package.json +4 -4
  25. package/src/templates/next-default/public/fonts/geist/Geist-Mono.woff2 +0 -0
  26. package/src/templates/next-experiments/app/layout.tsx +18 -18
  27. package/src/templates/next-experiments/app/robots.ts +3 -3
  28. package/src/templates/next-experiments/app/sitemap.ts +4 -4
  29. package/src/templates/next-experiments/biome.json +2 -2
  30. package/src/templates/next-experiments/components/layout/theme/index.tsx +1 -1
  31. package/src/templates/next-experiments/components/layout/wrapper/index.tsx +7 -9
  32. package/src/templates/next-experiments/components/ui/image/index.tsx +39 -46
  33. package/src/templates/next-experiments/components/ui/link/index.tsx +6 -0
  34. package/src/templates/next-experiments/lib/utils/metadata.ts +26 -26
  35. package/src/templates/next-experiments/package.json +4 -4
  36. package/src/templates/next-webgl/app/layout.tsx +18 -18
  37. package/src/templates/next-webgl/app/robots.ts +3 -3
  38. package/src/templates/next-webgl/app/sitemap.ts +4 -4
  39. package/src/templates/next-webgl/biome.json +2 -2
  40. package/src/templates/next-webgl/components/layout/theme/index.tsx +1 -1
  41. package/src/templates/next-webgl/components/layout/wrapper/index.tsx +0 -2
  42. package/src/templates/next-webgl/components/ui/image/index.tsx +6 -11
  43. package/src/templates/next-webgl/components/ui/link/index.tsx +9 -2
  44. package/src/templates/next-webgl/components/webgl/components/scene/index.tsx +1 -0
  45. package/src/templates/next-webgl/lib/utils/metadata.ts +26 -26
  46. package/src/templates/next-webgl/package.json +4 -4
  47. package/src/templates/next-experiments/.cursor/rules/README.md +0 -184
  48. package/src/templates/next-experiments/.cursor/rules/architecture.mdc +0 -437
  49. package/src/templates/next-experiments/.cursor/rules/components.mdc +0 -436
  50. package/src/templates/next-experiments/.cursor/rules/integrations.mdc +0 -447
  51. package/src/templates/next-experiments/.cursor/rules/main.mdc +0 -278
  52. package/src/templates/next-experiments/.cursor/rules/styling.mdc +0 -433
  53. package/src/templates/next-experiments/.github/workflows/lighthouse-to-slack.yml +0 -136
  54. package/src/templates/next-webgl/.cursor/rules/README.md +0 -184
  55. package/src/templates/next-webgl/.cursor/rules/architecture.mdc +0 -437
  56. package/src/templates/next-webgl/.cursor/rules/components.mdc +0 -436
  57. package/src/templates/next-webgl/.cursor/rules/integrations.mdc +0 -447
  58. package/src/templates/next-webgl/.cursor/rules/main.mdc +0 -278
  59. package/src/templates/next-webgl/.cursor/rules/styling.mdc +0 -433
  60. package/src/templates/next-webgl/.github/workflows/lighthouse-to-slack.yml +0 -136
@@ -0,0 +1,37 @@
1
+ # Image Component
2
+
3
+ Optimized images with smart loading, blur placeholders, and responsive sizing.
4
+
5
+ ## Usage
6
+
7
+ ```tsx
8
+ import { Image } from '@/components/ui/image'
9
+
10
+ // Basic
11
+ <Image src="/hero.jpg" alt="Hero" aspectRatio={16/9} />
12
+
13
+ // Priority (LCP images)
14
+ <Image src="/hero.jpg" alt="Hero" aspectRatio={16/9} priority />
15
+
16
+ // Responsive
17
+ <Image
18
+ src="/product.jpg"
19
+ alt="Product"
20
+ mobileSize="100vw"
21
+ desktopSize="33vw"
22
+ />
23
+ ```
24
+
25
+ ## Props
26
+
27
+ | Prop | Description |
28
+ |------|-------------|
29
+ | `aspectRatio` | Prevents layout shift, enables blur placeholder |
30
+ | `priority` | Eager loading for above-the-fold images |
31
+ | `mobileSize` / `desktopSize` | Responsive sizing |
32
+
33
+ ## Best Practices
34
+
35
+ - Always provide `aspectRatio` (prevents CLS)
36
+ - Use `priority` for LCP images
37
+ - Never use `next/image` directly
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Enhanced Image Component
3
+ *
4
+ * Next.js Image wrapper with optimized defaults and error handling.
5
+ * Always use this component instead of next/image directly.
6
+ */
7
+ "use client"
8
+
9
+ import cn from "clsx"
10
+ import NextImage, { type ImageProps as NextImageProps } from "next/image"
11
+ import type { CSSProperties, Ref } from "react"
12
+ import { breakpoints } from "@/lib/styles/config"
13
+ import s from "./image.module.css"
14
+
15
+ /**
16
+ * Enhanced Image component props extending Next.js Image.
17
+ *
18
+ * Adds responsive sizing, aspect ratio support, and automatic blur placeholders.
19
+ * Always use this component instead of next/image directly.
20
+ */
21
+ export type ImageProps = Omit<NextImageProps, "objectFit" | "alt"> & {
22
+ /** CSS object-fit property for image positioning */
23
+ objectFit?: CSSProperties["objectFit"]
24
+ /** Display as block element (adds display: block) */
25
+ block?: boolean
26
+ /** Size on mobile devices (e.g., "100vw", "50vw") */
27
+ mobileSize?: `${number}vw`
28
+ /** Size on desktop devices (e.g., "33vw", "25vw") */
29
+ desktopSize?: `${number}vw`
30
+ /** Ref for accessing the underlying img element */
31
+ ref?: Ref<HTMLImageElement>
32
+ /** Alt text for accessibility (required for meaningful images) */
33
+ alt?: string
34
+ /** Aspect ratio for automatic placeholder and layout stability */
35
+ aspectRatio?: number
36
+ }
37
+
38
+ // Memoize helper functions to avoid recreation
39
+ const toBase64 = (str: string) =>
40
+ typeof window === "undefined"
41
+ ? Buffer.from(str).toString("base64")
42
+ : window.btoa(str)
43
+
44
+ // Helper to generate blur placeholder with transparent background by default
45
+ const generateShimmer = (w: number, h: number) => `
46
+ <svg width="${w}" height="${h}" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
47
+ <defs>
48
+ <linearGradient id="g">
49
+ <stop stop-color="rgba(255,255,255,0.1)" offset="20%" />
50
+ <stop stop-color="rgba(255,255,255,0.2)" offset="50%" />
51
+ <stop stop-color="rgba(255,255,255,0.1)" offset="70%" />
52
+ </linearGradient>
53
+ </defs>
54
+ <rect width="${w}" height="${h}" fill="rgba(0,0,0,0)" />
55
+ <rect id="r" width="${w}" height="${h}" fill="url(#g)" />
56
+ <animate xlink:href="#r" attributeName="x" from="-${w}" to="${w}" dur="1s" repeatCount="indefinite" />
57
+ </svg>`
58
+
59
+ // Helper to determine if blur placeholder should be used
60
+ const shouldUseBlurPlaceholder = (
61
+ src: NextImageProps["src"],
62
+ placeholder: string,
63
+ blurDataURL: string | undefined
64
+ ): boolean => {
65
+ if (!src) return false
66
+ const isSvg = typeof src === "string" && src.includes(".svg")
67
+ return !isSvg && placeholder === "blur" && !blurDataURL
68
+ }
69
+
70
+ // Helper to generate blur data URL
71
+ const generateBlurDataURL = (
72
+ shouldUse: boolean,
73
+ aspectRatio: number | undefined,
74
+ existingBlurDataURL: string | undefined
75
+ ): string | undefined => {
76
+ if (!(shouldUse && aspectRatio)) return existingBlurDataURL
77
+
78
+ const shimmerSvg = generateShimmer(700, Math.round(700 / aspectRatio))
79
+ return `data:image/svg+xml;base64,${toBase64(shimmerSvg)}`
80
+ }
81
+
82
+ // Helper to determine final placeholder value
83
+ const getFinalPlaceholder = (
84
+ shouldUse: boolean,
85
+ aspectRatio: number | undefined,
86
+ blurDataURL: string | undefined,
87
+ originalPlaceholder: NextImageProps["placeholder"]
88
+ ): NextImageProps["placeholder"] => {
89
+ if (!shouldUse) {
90
+ return originalPlaceholder === "blur" && !blurDataURL
91
+ ? "empty"
92
+ : originalPlaceholder
93
+ }
94
+
95
+ return aspectRatio || blurDataURL ? "blur" : "empty"
96
+ }
97
+
98
+ /**
99
+ * Enhanced Image component with responsive sizing and automatic optimizations.
100
+ *
101
+ * Always use this component instead of next/image directly. Provides:
102
+ * - Automatic responsive sizes generation
103
+ * - Smart blur placeholders with aspect ratio support
104
+ * - Performance optimizations (lazy loading by default)
105
+ * - Priority flag for LCP images
106
+ *
107
+ * @param props - Image props extending Next.js Image
108
+ * @param props.aspectRatio - Aspect ratio for layout stability and blur placeholder
109
+ * @param props.mobileSize - Size on mobile (e.g., "100vw")
110
+ * @param props.desktopSize - Size on desktop (e.g., "50vw")
111
+ * @param props.block - Display as block element
112
+ * @param props.priority - Prioritize loading for LCP images
113
+ *
114
+ * @example
115
+ * ```tsx
116
+ * // Basic usage with aspect ratio
117
+ * <Image
118
+ * src="/hero.jpg"
119
+ * alt="Hero image"
120
+ * aspectRatio={16/9}
121
+ * />
122
+ * ```
123
+ *
124
+ * @example
125
+ * ```tsx
126
+ * // LCP image with priority
127
+ * <Image
128
+ * src="/hero.jpg"
129
+ * alt="Hero image"
130
+ * aspectRatio={16/9}
131
+ * priority // Preloads image for LCP
132
+ * />
133
+ * ```
134
+ *
135
+ * @example
136
+ * ```tsx
137
+ * // Responsive grid image
138
+ * <Image
139
+ * src="/product.jpg"
140
+ * alt="Product"
141
+ * aspectRatio={1}
142
+ * mobileSize="100vw"
143
+ * desktopSize="33vw"
144
+ * />
145
+ * ```
146
+ */
147
+ export function Image({
148
+ style,
149
+ className,
150
+ objectFit = "cover",
151
+ quality = 90,
152
+ alt = "",
153
+ fill,
154
+ block = !fill,
155
+ width = block ? 1 : undefined,
156
+ height = block ? 1 : undefined,
157
+ mobileSize = "100vw",
158
+ desktopSize = "100vw",
159
+ sizes,
160
+ src,
161
+ unoptimized,
162
+ ref,
163
+ aspectRatio,
164
+ placeholder = "blur",
165
+ priority = false,
166
+ ...props
167
+ }: ImageProps) {
168
+ // Generate responsive sizes if not provided
169
+ const finalSizes =
170
+ sizes ||
171
+ `(max-width: ${breakpoints.desktop}px) ${mobileSize}, ${desktopSize}`
172
+
173
+ // Early return after hooks
174
+ if (!src) return null
175
+
176
+ // Determine SVG status and placeholder logic
177
+ const isSvg = typeof src === "string" && src.includes(".svg")
178
+ const shouldUsePlaceholder = shouldUseBlurPlaceholder(
179
+ src,
180
+ placeholder,
181
+ props.blurDataURL
182
+ )
183
+ const blurDataURL = generateBlurDataURL(
184
+ shouldUsePlaceholder,
185
+ aspectRatio,
186
+ props.blurDataURL
187
+ )
188
+ const finalPlaceholder = getFinalPlaceholder(
189
+ shouldUsePlaceholder,
190
+ aspectRatio,
191
+ props.blurDataURL,
192
+ placeholder
193
+ )
194
+
195
+ return (
196
+ <NextImage
197
+ ref={ref}
198
+ fill={!block}
199
+ {...(width !== undefined && { width })}
200
+ {...(height !== undefined && { height })}
201
+ priority={priority}
202
+ quality={quality}
203
+ alt={alt}
204
+ style={{
205
+ objectFit,
206
+ ...style,
207
+ }}
208
+ className={cn(className, block && s.block)}
209
+ sizes={finalSizes}
210
+ src={src}
211
+ unoptimized={unoptimized || isSvg}
212
+ draggable={false}
213
+ onDragStart={(e) => e.preventDefault()}
214
+ {...(finalPlaceholder && { placeholder: finalPlaceholder })}
215
+ {...(blurDataURL && { blurDataURL })}
216
+ {...props}
217
+ />
218
+ )
219
+ }
@@ -0,0 +1,152 @@
1
+ "use client";
2
+
3
+ import NextLink from "next/link";
4
+ import { usePathname } from "next/navigation";
5
+ import {
6
+ type AnchorHTMLAttributes,
7
+ type ComponentProps,
8
+ type MouseEvent,
9
+ useEffect,
10
+ useState,
11
+ } from "react";
12
+
13
+ // Helper to extract props safe for button elements
14
+ function getButtonProps(props: Record<string, unknown>) {
15
+ const {
16
+ href,
17
+ target,
18
+ rel,
19
+ "data-external": _dataExternal,
20
+ ...buttonProps
21
+ } = props;
22
+ return buttonProps;
23
+ }
24
+
25
+ // Helper to extract props safe for div elements
26
+ function getDivProps(props: Record<string, unknown>) {
27
+ const {
28
+ href,
29
+ target,
30
+ rel,
31
+ onClick,
32
+ "data-external": _dataExternal,
33
+ ...divProps
34
+ } = props;
35
+ return divProps;
36
+ }
37
+
38
+ type CustomLinkProps = Omit<
39
+ AnchorHTMLAttributes<HTMLAnchorElement>,
40
+ keyof ComponentProps<typeof NextLink> | "href"
41
+ > &
42
+ Omit<ComponentProps<typeof NextLink>, "href"> & {
43
+ href?: string;
44
+ onClick?: (e: MouseEvent<HTMLElement>) => void;
45
+ scroll?: boolean;
46
+ };
47
+
48
+ export function Link({
49
+ href,
50
+ children,
51
+ onClick,
52
+ scroll = false, // Default to false to prevent scroll restoration warnings with fixed/sticky elements
53
+ ...props
54
+ }: CustomLinkProps) {
55
+ const [shouldPrefetch, setShouldPrefetch] = useState(false);
56
+ const [isExternal, setIsExternal] = useState(false);
57
+ const [isActive, setIsActive] = useState(false);
58
+
59
+ // Get pathname - deferred to avoid blocking static generation
60
+ // usePathname is safe to call but we defer the active check to useEffect
61
+ const pathname = usePathname();
62
+
63
+ useEffect(() => {
64
+ // Check if this link is active (current page)
65
+ if (href && pathname) {
66
+ setIsActive(pathname === href);
67
+ }
68
+ }, [href, pathname]);
69
+
70
+ useEffect(() => {
71
+ // Skip if no href
72
+ if (!href) return;
73
+
74
+ // Check if external link
75
+ try {
76
+ const url = new URL(href, window.location.href);
77
+ setIsExternal(url.host !== window.location.host);
78
+ } catch {
79
+ setIsExternal(false);
80
+ }
81
+
82
+ // Only prefetch on good connections
83
+ const connection = (
84
+ navigator as Navigator & {
85
+ connection?: { effectiveType: string; saveData: boolean };
86
+ }
87
+ ).connection;
88
+ if (connection) {
89
+ const { effectiveType, saveData } = connection;
90
+ setShouldPrefetch(effectiveType === "4g" && !saveData);
91
+ } else {
92
+ // Default to prefetching if API not available
93
+ setShouldPrefetch(true);
94
+ }
95
+ }, [href]);
96
+
97
+ // If no href is provided but there's an onClick, render a button
98
+ if (!href && onClick) {
99
+ return (
100
+ <button
101
+ onClick={(e: MouseEvent<HTMLButtonElement>) => onClick(e)}
102
+ type="button"
103
+ {...getButtonProps(props)}
104
+ >
105
+ {children}
106
+ </button>
107
+ );
108
+ }
109
+
110
+ // If no href and no onClick, render a div
111
+ if (!href) {
112
+ return <div {...getDivProps(props)}>{children}</div>;
113
+ }
114
+
115
+ // Block dangerous URIs (javascript:, data:, vbscript:)
116
+ const isDangerousHref = /^(javascript|data|vbscript):/i.test(href);
117
+ if (isDangerousHref) {
118
+ return <span {...getDivProps(props)}>{children}</span>;
119
+ }
120
+
121
+ // For SSR, check if it's external based on the href pattern
122
+ const isExternalSSR =
123
+ href.startsWith("http://") || href.startsWith("https://");
124
+
125
+ if (isExternalSSR || isExternal) {
126
+ return (
127
+ <a
128
+ href={href}
129
+ target="_blank"
130
+ rel="noopener noreferrer"
131
+ data-external
132
+ onClick={onClick}
133
+ {...props}
134
+ >
135
+ {children}
136
+ </a>
137
+ );
138
+ }
139
+
140
+ return (
141
+ <NextLink
142
+ href={href as ComponentProps<typeof NextLink>["href"]}
143
+ prefetch={shouldPrefetch}
144
+ scroll={scroll}
145
+ data-active={isActive || undefined}
146
+ {...(onClick && { onClick })}
147
+ {...props}
148
+ >
149
+ {children}
150
+ </NextLink>
151
+ );
152
+ }
@@ -1,4 +1,4 @@
1
- import type { Metadata } from "next"
1
+ import type { Metadata } from "next";
2
2
 
3
3
  /**
4
4
  * Metadata Generation Utilities
@@ -8,26 +8,26 @@ import type { Metadata } from "next"
8
8
  */
9
9
 
10
10
  interface GenerateMetadataOptions {
11
- title?: string
12
- description?: string
13
- keywords?: string[]
11
+ title?: string;
12
+ description?: string;
13
+ keywords?: string[];
14
14
  image?: {
15
- url?: string
16
- width?: number
17
- height?: number
18
- alt?: string
19
- }
20
- url?: string
21
- siteName?: string
22
- noIndex?: boolean
23
- type?: "website" | "article"
24
- publishedTime?: string
25
- modifiedTime?: string
26
- authors?: string[]
15
+ url?: string;
16
+ width?: number;
17
+ height?: number;
18
+ alt?: string;
19
+ };
20
+ url?: string;
21
+ siteName?: string;
22
+ noIndex?: boolean;
23
+ type?: "website" | "article";
24
+ publishedTime?: string;
25
+ modifiedTime?: string;
26
+ authors?: string[];
27
27
  }
28
28
 
29
29
  const APP_BASE_URL =
30
- process.env.NEXT_PUBLIC_BASE_URL ?? "https://localhost:3000"
30
+ process.env.NEXT_PUBLIC_BASE_URL ?? "http://localhost:3000";
31
31
 
32
32
  /**
33
33
  * Generate complete metadata object for pages
@@ -62,13 +62,13 @@ export function generatePageMetadata(
62
62
  publishedTime,
63
63
  modifiedTime,
64
64
  authors,
65
- } = options
65
+ } = options;
66
66
 
67
- const fullUrl = url ? `${APP_BASE_URL}${url}` : APP_BASE_URL
68
- const imageUrl = image?.url || "/opengraph-image.jpg"
69
- const imageWidth = image?.width || 1200
70
- const imageHeight = image?.height || 630
71
- const imageAlt = image?.alt || title || siteName
67
+ const fullUrl = url ? `${APP_BASE_URL}${url}` : APP_BASE_URL;
68
+ const imageUrl = image?.url || "/opengraph-image.jpg";
69
+ const imageWidth = image?.width || 1200;
70
+ const imageHeight = image?.height || 630;
71
+ const imageAlt = image?.alt || title || siteName;
72
72
 
73
73
  const metadata: Metadata = {
74
74
  metadataBase: new URL(APP_BASE_URL),
@@ -113,14 +113,14 @@ export function generatePageMetadata(
113
113
  other: {
114
114
  "fb:app_id": process.env.NEXT_PUBLIC_FACEBOOK_APP_ID || "",
115
115
  },
116
- }
116
+ };
117
117
 
118
118
  if (noIndex) {
119
119
  metadata.robots = {
120
120
  index: false,
121
121
  follow: false,
122
- }
122
+ };
123
123
  }
124
124
 
125
- return metadata
125
+ return metadata;
126
126
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "basement-next-starter",
2
+ "name": "bsmnt-next-starter",
3
3
  "description": "Basement Next.js starter template",
4
4
  "version": "0.1.0",
5
5
  "private": true,
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "class-variance-authority": "^0.7.1",
29
- "next": "16.1.4",
29
+ "next": "16.1.6",
30
30
  "react": "19.2.4",
31
31
  "react-dom": "19.2.4",
32
32
  "react-use": "^17.6.0",
@@ -35,10 +35,10 @@
35
35
  "zustand": "^5.0.10"
36
36
  },
37
37
  "devDependencies": {
38
- "@biomejs/biome": "2.3.11",
38
+ "@biomejs/biome": "2.3.14",
39
39
  "@clack/prompts": "^0.11.0",
40
40
  "@csstools/postcss-global-data": "^3.1.0",
41
- "@next/bundle-analyzer": "16.1.1",
41
+ "@next/bundle-analyzer": "16.1.6",
42
42
  "@svgr/webpack": "^8.1.0",
43
43
  "@tailwindcss/postcss": "^4.1.18",
44
44
  "@types/bun": "^1.3.6",
@@ -1,23 +1,23 @@
1
- import type { Metadata, Viewport } from "next"
2
- import { Geist } from "next/font/google"
3
- import { type PropsWithChildren, Suspense } from "react"
4
- import { Link } from "@/components/ui/link"
5
- import { themes } from "@/lib/styles/colors"
6
- import { fontsVariable } from "@/lib/styles/fonts"
7
- import AppData from "@/package.json"
8
- import "@/lib/styles/css/index.css"
9
- import { cn } from "@/lib/styles/cn"
1
+ import type { Metadata, Viewport } from "next";
2
+ import { Geist } from "next/font/google";
3
+ import { type PropsWithChildren, Suspense } from "react";
4
+ import { Link } from "@/components/ui/link";
5
+ import { themes } from "@/lib/styles/colors";
6
+ import { fontsVariable } from "@/lib/styles/fonts";
7
+ import AppData from "@/package.json";
8
+ import "@/lib/styles/css/index.css";
9
+ import { cn } from "@/lib/styles/cn";
10
10
 
11
- const APP_NAME = AppData.name
12
- const APP_DEFAULT_TITLE = "Basement Starter"
13
- const APP_TITLE_TEMPLATE = "%s - Basement Starter"
14
- const APP_DESCRIPTION = AppData.description
11
+ const APP_NAME = AppData.name;
12
+ const APP_DEFAULT_TITLE = "Basement Starter";
13
+ const APP_TITLE_TEMPLATE = "%s - Basement Starter";
14
+ const APP_DESCRIPTION = AppData.description;
15
15
  const APP_BASE_URL =
16
- process.env.NEXT_PUBLIC_BASE_URL ?? "https://localhost:3000"
16
+ process.env.NEXT_PUBLIC_BASE_URL ?? "http://localhost:3000";
17
17
 
18
18
  const geist = Geist({
19
19
  subsets: ["latin"],
20
- })
20
+ });
21
21
 
22
22
  export const metadata: Metadata = {
23
23
  alternates: {
@@ -70,12 +70,12 @@ export const metadata: Metadata = {
70
70
  template: APP_TITLE_TEMPLATE,
71
71
  },
72
72
  },
73
- }
73
+ };
74
74
 
75
75
  export const viewport: Viewport = {
76
76
  colorScheme: "normal",
77
77
  themeColor: themes.dark.primary,
78
- }
78
+ };
79
79
 
80
80
  export default async function Layout({ children }: PropsWithChildren) {
81
81
  return (
@@ -100,5 +100,5 @@ export default async function Layout({ children }: PropsWithChildren) {
100
100
  {children}
101
101
  </body>
102
102
  </html>
103
- )
103
+ );
104
104
  }
@@ -1,7 +1,7 @@
1
- import type { MetadataRoute } from "next"
1
+ import type { MetadataRoute } from "next";
2
2
 
3
3
  const APP_BASE_URL =
4
- process.env.NEXT_PUBLIC_BASE_URL ?? "https://localhost:3000"
4
+ process.env.NEXT_PUBLIC_BASE_URL ?? "http://localhost:3000";
5
5
 
6
6
  export default function robots(): MetadataRoute.Robots {
7
7
  return {
@@ -11,5 +11,5 @@ export default function robots(): MetadataRoute.Robots {
11
11
  disallow: [],
12
12
  },
13
13
  sitemap: `${APP_BASE_URL}/sitemap.xml`,
14
- }
14
+ };
15
15
  }
@@ -1,7 +1,7 @@
1
- import type { MetadataRoute } from "next"
1
+ import type { MetadataRoute } from "next";
2
2
 
3
3
  const APP_BASE_URL =
4
- process.env.NEXT_PUBLIC_BASE_URL ?? "https://localhost:3000"
4
+ process.env.NEXT_PUBLIC_BASE_URL ?? "http://localhost:3000";
5
5
 
6
6
  export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
7
7
  const baseRoutes: MetadataRoute.Sitemap = [
@@ -11,6 +11,6 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
11
11
  changeFrequency: "daily",
12
12
  priority: 1,
13
13
  },
14
- ]
15
- return baseRoutes
14
+ ];
15
+ return baseRoutes;
16
16
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "root": false,
2
+ "root": true,
3
3
  "$schema": "node_modules/@biomejs/biome/configuration_schema.json",
4
4
  "assist": {
5
5
  "actions": {
@@ -245,6 +245,6 @@
245
245
  "vcs": {
246
246
  "clientKind": "git",
247
247
  "enabled": true,
248
- "useIgnoreFile": true
248
+ "useIgnoreFile": false
249
249
  }
250
250
  }
@@ -47,7 +47,7 @@ export function Theme({
47
47
 
48
48
  return (
49
49
  <>
50
- {global && (
50
+ {global && /^[a-z]+$/.test(currentTheme) && (
51
51
  <script>
52
52
  {`document.documentElement.setAttribute('data-theme', '${currentTheme}');`}
53
53
  </script>
@@ -3,20 +3,18 @@
3
3
  *
4
4
  * Customize the Header and Footer components for your project needs.
5
5
  */
6
- "use client"
7
-
8
- import cn from "clsx"
9
- import { Footer } from "@/components/layout/footer"
10
- import { Header } from "@/components/layout/header"
11
- import { Theme } from "@/components/layout/theme"
12
- import type { ThemeName } from "@/lib/styles/config"
6
+ import cn from "clsx";
7
+ import { Footer } from "@/components/layout/footer";
8
+ import { Header } from "@/components/layout/header";
9
+ import { Theme } from "@/components/layout/theme";
10
+ import type { ThemeName } from "@/lib/styles/config";
13
11
 
14
12
  /**
15
13
  * Props for the Wrapper component.
16
14
  */
17
15
  interface WrapperProps extends React.HTMLAttributes<HTMLDivElement> {
18
16
  /** Theme to apply ('dark' | 'light'). Defaults to 'dark'. */
19
- theme?: ThemeName
17
+ theme?: ThemeName;
20
18
  }
21
19
 
22
20
  /**
@@ -61,5 +59,5 @@ export function Wrapper({
61
59
  </main>
62
60
  <Footer />
63
61
  </Theme>
64
- )
62
+ );
65
63
  }