create-react-forge 1.5.2 → 1.6.1

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 (104) hide show
  1. package/README.md +6 -5
  2. package/dist/cli/index.d.ts +2 -2
  3. package/dist/cli/parser.d.ts.map +1 -1
  4. package/dist/cli/parser.js +3 -2
  5. package/dist/cli/parser.js.map +1 -1
  6. package/dist/cli/prompts.d.ts.map +1 -1
  7. package/dist/cli/prompts.js +21 -7
  8. package/dist/cli/prompts.js.map +1 -1
  9. package/dist/config/defaults.d.ts +2 -2
  10. package/dist/config/defaults.d.ts.map +1 -1
  11. package/dist/config/defaults.js +3 -2
  12. package/dist/config/defaults.js.map +1 -1
  13. package/dist/config/schema.d.ts +15 -13
  14. package/dist/config/schema.d.ts.map +1 -1
  15. package/dist/config/schema.js +5 -4
  16. package/dist/config/schema.js.map +1 -1
  17. package/dist/docs/index.d.ts +1 -0
  18. package/dist/docs/index.d.ts.map +1 -1
  19. package/dist/docs/index.js +1 -0
  20. package/dist/docs/index.js.map +1 -1
  21. package/dist/docs/readme-generator.d.ts +6 -0
  22. package/dist/docs/readme-generator.d.ts.map +1 -0
  23. package/dist/docs/readme-generator.js +276 -0
  24. package/dist/docs/readme-generator.js.map +1 -0
  25. package/dist/generator/index.d.ts.map +1 -1
  26. package/dist/generator/index.js +5 -2
  27. package/dist/generator/index.js.map +1 -1
  28. package/dist/templates/registry.d.ts +6 -2
  29. package/dist/templates/registry.d.ts.map +1 -1
  30. package/dist/templates/registry.js +31 -16
  31. package/dist/templates/registry.js.map +1 -1
  32. package/dist/templates/utils.js +4 -4
  33. package/dist/templates/utils.js.map +1 -1
  34. package/package.json +1 -1
  35. package/src/templates/overlays/base/manifest.json +4 -3
  36. package/src/templates/overlays/base/src/components/ui/Button.tsx +103 -31
  37. package/src/templates/overlays/base/src/components/ui/Input.tsx +55 -29
  38. package/src/templates/overlays/base/src/lib/utils.ts +0 -10
  39. package/src/templates/overlays/runtime/nextjs/src/app/error.tsx +39 -10
  40. package/src/templates/overlays/runtime/nextjs/src/app/loading.tsx +25 -7
  41. package/src/templates/overlays/runtime/nextjs/src/app/not-found.tsx +54 -13
  42. package/src/templates/overlays/runtime/nextjs/src/app/page.tsx +55 -13
  43. package/src/templates/overlays/runtime/nextjs/src/styles/globals.css +1 -1
  44. package/src/templates/overlays/runtime/vite/src/components/errors/ErrorFallback.tsx +49 -15
  45. package/src/templates/overlays/runtime/vite/src/components/ui/LoadingSpinner.tsx +33 -13
  46. package/src/templates/overlays/runtime/vite/src/features/misc/routes/Landing.tsx +78 -21
  47. package/src/templates/overlays/runtime/vite/src/features/misc/routes/NotFound.tsx +77 -19
  48. package/src/templates/overlays/runtime/vite/src/main.tsx +0 -2
  49. package/src/templates/overlays/state/jotai/manifest.json +16 -0
  50. package/src/templates/overlays/state/jotai/src/stores/atoms.ts +52 -0
  51. package/src/templates/overlays/state/jotai/src/stores/index.ts +30 -0
  52. package/src/templates/overlays/styling/css/_nextjs/src/app/error.css +49 -0
  53. package/src/templates/overlays/styling/css/_nextjs/src/app/error.tsx +34 -0
  54. package/src/templates/overlays/styling/css/_nextjs/src/app/loading.css +28 -0
  55. package/src/templates/overlays/styling/css/_nextjs/src/app/loading.tsx +13 -0
  56. package/src/templates/overlays/styling/css/_nextjs/src/app/not-found.css +70 -0
  57. package/src/templates/overlays/styling/css/_nextjs/src/app/not-found.tsx +25 -0
  58. package/src/templates/overlays/styling/css/_nextjs/src/app/page.css +73 -0
  59. package/src/templates/overlays/styling/css/_nextjs/src/app/page.tsx +32 -0
  60. package/src/templates/overlays/styling/css/_vite/src/components/errors/ErrorFallback.css +49 -0
  61. package/src/templates/overlays/styling/css/_vite/src/components/errors/ErrorFallback.tsx +22 -0
  62. package/src/templates/overlays/styling/css/_vite/src/components/ui/LoadingSpinner.css +40 -0
  63. package/src/templates/overlays/styling/css/_vite/src/components/ui/LoadingSpinner.tsx +21 -0
  64. package/src/templates/overlays/styling/css/_vite/src/features/misc/routes/Landing.css +73 -0
  65. package/src/templates/overlays/styling/css/_vite/src/features/misc/routes/Landing.tsx +32 -0
  66. package/src/templates/overlays/styling/css/_vite/src/features/misc/routes/NotFound.css +70 -0
  67. package/src/templates/overlays/styling/css/_vite/src/features/misc/routes/NotFound.tsx +25 -0
  68. package/src/templates/overlays/styling/css/manifest.json +17 -0
  69. package/src/templates/overlays/styling/css/src/styles/globals.css +107 -0
  70. package/src/templates/overlays/styling/css-modules/_nextjs/src/app/loading.tsx +13 -0
  71. package/src/templates/overlays/styling/css-modules/_nextjs/src/app/page.module.css +73 -0
  72. package/src/templates/overlays/styling/css-modules/_nextjs/src/app/page.tsx +32 -0
  73. package/src/templates/overlays/styling/css-modules/_vite/src/components/errors/ErrorFallback.module.css +45 -0
  74. package/src/templates/overlays/styling/css-modules/_vite/src/components/errors/ErrorFallback.tsx +22 -0
  75. package/src/templates/overlays/styling/css-modules/_vite/src/components/ui/LoadingSpinner.module.css +40 -0
  76. package/src/templates/overlays/styling/css-modules/_vite/src/components/ui/LoadingSpinner.tsx +23 -0
  77. package/src/templates/overlays/styling/css-modules/_vite/src/features/misc/routes/Landing.module.css +73 -0
  78. package/src/templates/overlays/styling/css-modules/_vite/src/features/misc/routes/Landing.tsx +32 -0
  79. package/src/templates/overlays/styling/css-modules/_vite/src/features/misc/routes/NotFound.module.css +67 -0
  80. package/src/templates/overlays/styling/css-modules/_vite/src/features/misc/routes/NotFound.tsx +25 -0
  81. package/src/templates/overlays/styling/css-modules/manifest.json +6 -3
  82. package/src/templates/overlays/styling/css-modules/src/css.d.ts +13 -0
  83. package/src/templates/overlays/styling/styled-components/_nextjs/src/app/error.tsx +72 -0
  84. package/src/templates/overlays/styling/styled-components/_nextjs/src/app/layout.tsx +25 -0
  85. package/src/templates/overlays/styling/styled-components/_nextjs/src/app/loading.tsx +40 -0
  86. package/src/templates/overlays/styling/styled-components/_nextjs/src/app/not-found.tsx +91 -0
  87. package/src/templates/overlays/styling/styled-components/_nextjs/src/app/page.tsx +102 -0
  88. package/src/templates/overlays/styling/styled-components/_nextjs/src/app/providers.tsx +22 -0
  89. package/src/templates/overlays/styling/styled-components/{src → _nextjs/src}/lib/StyledComponentsRegistry.tsx +10 -2
  90. package/src/templates/overlays/styling/styled-components/_vite/src/app/provider.tsx +27 -0
  91. package/src/templates/overlays/styling/styled-components/_vite/src/components/errors/ErrorFallback.tsx +59 -0
  92. package/src/templates/overlays/styling/styled-components/_vite/src/components/ui/LoadingSpinner.tsx +47 -0
  93. package/src/templates/overlays/styling/styled-components/_vite/src/features/misc/routes/Landing.tsx +100 -0
  94. package/src/templates/overlays/styling/styled-components/_vite/src/features/misc/routes/NotFound.tsx +89 -0
  95. package/src/templates/overlays/styling/styled-components/_vite/src/main.tsx +13 -0
  96. package/src/templates/overlays/styling/styled-components/manifest.json +5 -1
  97. package/src/templates/overlays/styling/tailwind/_nextjs/src/app/error.tsx +33 -0
  98. package/src/templates/overlays/styling/tailwind/_nextjs/src/app/loading.tsx +12 -0
  99. package/src/templates/overlays/styling/tailwind/_nextjs/src/app/not-found.tsx +26 -0
  100. package/src/templates/overlays/styling/tailwind/_nextjs/src/app/page.tsx +33 -0
  101. package/src/templates/overlays/styling/tailwind/manifest.json +5 -3
  102. package/src/templates/overlays/runtime/vite/src/styles/globals.css +0 -55
  103. package/src/templates/overlays/styling/css-modules/src/components/ui/Button.module.css +0 -87
  104. package/src/templates/overlays/styling/css-modules/src/styles/globals.css +0 -91
@@ -0,0 +1,40 @@
1
+ .container {
2
+ display: flex;
3
+ align-items: center;
4
+ justify-content: center;
5
+ }
6
+
7
+ .spinner {
8
+ border-radius: 50%;
9
+ border: 2px solid var(--color-border);
10
+ border-top-color: var(--color-primary);
11
+ animation: spin 1s linear infinite;
12
+ }
13
+
14
+ .sm {
15
+ width: 1rem;
16
+ height: 1rem;
17
+ }
18
+
19
+ .md {
20
+ width: 2rem;
21
+ height: 2rem;
22
+ }
23
+
24
+ .lg {
25
+ width: 4rem;
26
+ height: 4rem;
27
+ }
28
+
29
+ @keyframes spin {
30
+ from {
31
+ transform: rotate(0deg);
32
+ }
33
+ to {
34
+ transform: rotate(360deg);
35
+ }
36
+ }
37
+
38
+
39
+
40
+
@@ -0,0 +1,23 @@
1
+ import styles from './LoadingSpinner.module.css';
2
+
3
+ type LoadingSpinnerProps = {
4
+ size?: 'sm' | 'md' | 'lg';
5
+ };
6
+
7
+ export function LoadingSpinner({ size = 'md' }: LoadingSpinnerProps) {
8
+ const sizeClass = styles[size] || styles.md;
9
+
10
+ return (
11
+ <div className={styles.container}>
12
+ <div
13
+ className={`${styles.spinner} ${sizeClass}`}
14
+ role="status"
15
+ aria-label="Loading"
16
+ />
17
+ </div>
18
+ );
19
+ }
20
+
21
+
22
+
23
+
@@ -0,0 +1,73 @@
1
+ .container {
2
+ display: flex;
3
+ min-height: 100vh;
4
+ flex-direction: column;
5
+ align-items: center;
6
+ justify-content: center;
7
+ }
8
+
9
+ .content {
10
+ text-align: center;
11
+ }
12
+
13
+ .title {
14
+ font-size: 2.25rem;
15
+ font-weight: 700;
16
+ letter-spacing: -0.025em;
17
+ color: var(--color-text);
18
+ }
19
+
20
+ @media (min-width: 640px) {
21
+ .title {
22
+ font-size: 3.75rem;
23
+ }
24
+ }
25
+
26
+ .description {
27
+ margin-top: 1.5rem;
28
+ font-size: 1.125rem;
29
+ line-height: 2rem;
30
+ color: var(--color-text-muted);
31
+ }
32
+
33
+ .buttons {
34
+ margin-top: 2.5rem;
35
+ display: flex;
36
+ align-items: center;
37
+ justify-content: center;
38
+ gap: 1.5rem;
39
+ }
40
+
41
+ .primaryBtn {
42
+ display: inline-block;
43
+ border-radius: var(--radius-md);
44
+ background-color: var(--color-primary);
45
+ padding: 0.625rem 0.875rem;
46
+ font-size: 0.875rem;
47
+ font-weight: 600;
48
+ color: white;
49
+ text-decoration: none;
50
+ box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
51
+ transition: background-color 0.2s;
52
+ }
53
+
54
+ .primaryBtn:hover {
55
+ background-color: var(--color-primary-hover);
56
+ }
57
+
58
+ .linkBtn {
59
+ font-size: 0.875rem;
60
+ font-weight: 600;
61
+ line-height: 1.5rem;
62
+ color: var(--color-text);
63
+ text-decoration: none;
64
+ transition: color 0.2s;
65
+ }
66
+
67
+ .linkBtn:hover {
68
+ color: var(--color-primary);
69
+ }
70
+
71
+
72
+
73
+
@@ -0,0 +1,32 @@
1
+ import { Link } from 'react-router-dom';
2
+ import styles from './Landing.module.css';
3
+
4
+ export function Landing() {
5
+ return (
6
+ <div className={styles.container}>
7
+ <div className={styles.content}>
8
+ <h1 className={styles.title}>Welcome to Your App</h1>
9
+ <p className={styles.description}>
10
+ A production-ready React application scaffolded with create-react-forge.
11
+ </p>
12
+ <div className={styles.buttons}>
13
+ <Link to="/dashboard" className={styles.primaryBtn}>
14
+ Get started
15
+ </Link>
16
+ <a
17
+ href="https://github.com/alan2207/bulletproof-react"
18
+ className={styles.linkBtn}
19
+ target="_blank"
20
+ rel="noopener noreferrer"
21
+ >
22
+ Learn more <span aria-hidden="true">→</span>
23
+ </a>
24
+ </div>
25
+ </div>
26
+ </div>
27
+ );
28
+ }
29
+
30
+
31
+
32
+
@@ -0,0 +1,67 @@
1
+ .container {
2
+ display: flex;
3
+ min-height: 100vh;
4
+ flex-direction: column;
5
+ align-items: center;
6
+ justify-content: center;
7
+ }
8
+
9
+ .content {
10
+ text-align: center;
11
+ }
12
+
13
+ .code {
14
+ font-size: 1rem;
15
+ font-weight: 600;
16
+ color: var(--color-primary);
17
+ }
18
+
19
+ .title {
20
+ margin-top: 1rem;
21
+ font-size: 1.875rem;
22
+ font-weight: 700;
23
+ letter-spacing: -0.025em;
24
+ color: var(--color-text);
25
+ }
26
+
27
+ @media (min-width: 640px) {
28
+ .title {
29
+ font-size: 3rem;
30
+ }
31
+ }
32
+
33
+ .description {
34
+ margin-top: 1.5rem;
35
+ font-size: 1rem;
36
+ line-height: 1.75rem;
37
+ color: var(--color-text-muted);
38
+ }
39
+
40
+ .buttons {
41
+ margin-top: 2.5rem;
42
+ display: flex;
43
+ align-items: center;
44
+ justify-content: center;
45
+ gap: 1.5rem;
46
+ }
47
+
48
+ .primaryBtn {
49
+ display: inline-block;
50
+ border-radius: var(--radius-md);
51
+ background-color: var(--color-primary);
52
+ padding: 0.625rem 0.875rem;
53
+ font-size: 0.875rem;
54
+ font-weight: 600;
55
+ color: white;
56
+ text-decoration: none;
57
+ box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
58
+ transition: background-color 0.2s;
59
+ }
60
+
61
+ .primaryBtn:hover {
62
+ background-color: var(--color-primary-hover);
63
+ }
64
+
65
+
66
+
67
+
@@ -0,0 +1,25 @@
1
+ import { Link } from 'react-router-dom';
2
+ import styles from './NotFound.module.css';
3
+
4
+ export function NotFound() {
5
+ return (
6
+ <div className={styles.container}>
7
+ <div className={styles.content}>
8
+ <p className={styles.code}>404</p>
9
+ <h1 className={styles.title}>Page not found</h1>
10
+ <p className={styles.description}>
11
+ Sorry, we couldn't find the page you're looking for.
12
+ </p>
13
+ <div className={styles.buttons}>
14
+ <Link to="/" className={styles.primaryBtn}>
15
+ Go back home
16
+ </Link>
17
+ </div>
18
+ </div>
19
+ </div>
20
+ );
21
+ }
22
+
23
+
24
+
25
+
@@ -1,14 +1,17 @@
1
1
  {
2
2
  "name": "styling-css-modules",
3
3
  "version": "1.0.0",
4
- "description": "CSS Modules styling with scoped CSS",
4
+ "description": "CSS Modules styling solution",
5
5
  "compatibleWith": ["runtime-vite", "runtime-nextjs"],
6
6
  "dependencies": {},
7
7
  "devDependencies": {},
8
8
  "scripts": {},
9
9
  "filePatterns": {
10
10
  "include": ["**/*"],
11
- "exclude": ["manifest.json"]
11
+ "exclude": ["manifest.json", "_vite", "_nextjs"]
12
+ },
13
+ "runtimeOverrides": {
14
+ "vite": "_vite",
15
+ "nextjs": "_nextjs"
12
16
  }
13
17
  }
14
-
@@ -0,0 +1,13 @@
1
+ declare module '*.module.css' {
2
+ const classes: { readonly [key: string]: string };
3
+ export default classes;
4
+ }
5
+
6
+ declare module '*.css' {
7
+ const content: string;
8
+ export default content;
9
+ }
10
+
11
+
12
+
13
+
@@ -0,0 +1,72 @@
1
+ 'use client';
2
+
3
+ import { useEffect } from 'react';
4
+ import styled from 'styled-components';
5
+
6
+ const Container = styled.div`
7
+ display: flex;
8
+ min-height: 100vh;
9
+ flex-direction: column;
10
+ align-items: center;
11
+ justify-content: center;
12
+ `;
13
+
14
+ const Content = styled.div`
15
+ text-align: center;
16
+ `;
17
+
18
+ const Title = styled.h1`
19
+ font-size: 1.5rem;
20
+ font-weight: 700;
21
+ color: #dc2626;
22
+ `;
23
+
24
+ const Message = styled.p`
25
+ margin-top: 1rem;
26
+ color: #4b5563;
27
+ `;
28
+
29
+ const RetryButton = styled.button`
30
+ margin-top: 1.5rem;
31
+ border-radius: 0.375rem;
32
+ background-color: #4f46e5;
33
+ padding: 0.625rem 0.875rem;
34
+ font-size: 0.875rem;
35
+ font-weight: 600;
36
+ color: white;
37
+ border: none;
38
+ cursor: pointer;
39
+ box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
40
+ transition: background-color 0.2s;
41
+
42
+ &:hover {
43
+ background-color: #6366f1;
44
+ }
45
+ `;
46
+
47
+ export default function Error({
48
+ error,
49
+ reset,
50
+ }: {
51
+ error: Error & { digest?: string };
52
+ reset: () => void;
53
+ }) {
54
+ useEffect(() => {
55
+ // Log the error to an error reporting service
56
+ console.error(error);
57
+ }, [error]);
58
+
59
+ return (
60
+ <Container role="alert">
61
+ <Content>
62
+ <Title>Something went wrong</Title>
63
+ <Message>{error.message || 'An unexpected error occurred'}</Message>
64
+ <RetryButton onClick={reset}>Try again</RetryButton>
65
+ </Content>
66
+ </Container>
67
+ );
68
+ }
69
+
70
+
71
+
72
+
@@ -0,0 +1,25 @@
1
+ import type { Metadata } from 'next';
2
+ import { Providers } from './providers';
3
+
4
+ export const metadata: Metadata = {
5
+ title: '{{PROJECT_NAME}}',
6
+ description: 'A production-ready Next.js application',
7
+ };
8
+
9
+ export default function RootLayout({
10
+ children,
11
+ }: {
12
+ children: React.ReactNode;
13
+ }) {
14
+ return (
15
+ <html lang="en">
16
+ <body>
17
+ <Providers>{children}</Providers>
18
+ </body>
19
+ </html>
20
+ );
21
+ }
22
+
23
+
24
+
25
+
@@ -0,0 +1,40 @@
1
+ 'use client';
2
+
3
+ import styled, { keyframes } from 'styled-components';
4
+
5
+ const spin = keyframes`
6
+ from {
7
+ transform: rotate(0deg);
8
+ }
9
+ to {
10
+ transform: rotate(360deg);
11
+ }
12
+ `;
13
+
14
+ const Container = styled.div`
15
+ display: flex;
16
+ min-height: 100vh;
17
+ align-items: center;
18
+ justify-content: center;
19
+ `;
20
+
21
+ const Spinner = styled.div`
22
+ width: 2rem;
23
+ height: 2rem;
24
+ border-radius: 50%;
25
+ border: 2px solid #d1d5db;
26
+ border-top-color: #4f46e5;
27
+ animation: ${spin} 1s linear infinite;
28
+ `;
29
+
30
+ export default function Loading() {
31
+ return (
32
+ <Container>
33
+ <Spinner role="status" aria-label="Loading" />
34
+ </Container>
35
+ );
36
+ }
37
+
38
+
39
+
40
+
@@ -0,0 +1,91 @@
1
+ 'use client';
2
+
3
+ import Link from 'next/link';
4
+ import styled from 'styled-components';
5
+
6
+ const Container = styled.div`
7
+ display: flex;
8
+ min-height: 100vh;
9
+ flex-direction: column;
10
+ align-items: center;
11
+ justify-content: center;
12
+ `;
13
+
14
+ const Content = styled.div`
15
+ text-align: center;
16
+ `;
17
+
18
+ const ErrorCode = styled.p`
19
+ font-size: 1rem;
20
+ font-weight: 600;
21
+ color: #4f46e5;
22
+ `;
23
+
24
+ const Title = styled.h1`
25
+ margin-top: 1rem;
26
+ font-size: 1.875rem;
27
+ font-weight: 700;
28
+ letter-spacing: -0.025em;
29
+ color: #111827;
30
+
31
+ @media (min-width: 640px) {
32
+ font-size: 3rem;
33
+ }
34
+ `;
35
+
36
+ const Description = styled.p`
37
+ margin-top: 1.5rem;
38
+ font-size: 1rem;
39
+ line-height: 1.75rem;
40
+ color: #4b5563;
41
+ `;
42
+
43
+ const ButtonGroup = styled.div`
44
+ margin-top: 2.5rem;
45
+ display: flex;
46
+ align-items: center;
47
+ justify-content: center;
48
+ gap: 1.5rem;
49
+ `;
50
+
51
+ const PrimaryButton = styled(Link)`
52
+ border-radius: 0.375rem;
53
+ background-color: #4f46e5;
54
+ padding: 0.625rem 0.875rem;
55
+ font-size: 0.875rem;
56
+ font-weight: 600;
57
+ color: white;
58
+ text-decoration: none;
59
+ box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
60
+ transition: background-color 0.2s;
61
+
62
+ &:hover {
63
+ background-color: #6366f1;
64
+ }
65
+
66
+ &:focus-visible {
67
+ outline: 2px solid #4f46e5;
68
+ outline-offset: 2px;
69
+ }
70
+ `;
71
+
72
+ export default function NotFound() {
73
+ return (
74
+ <Container>
75
+ <Content>
76
+ <ErrorCode>404</ErrorCode>
77
+ <Title>Page not found</Title>
78
+ <Description>
79
+ Sorry, we couldn't find the page you're looking for.
80
+ </Description>
81
+ <ButtonGroup>
82
+ <PrimaryButton href="/">Go back home</PrimaryButton>
83
+ </ButtonGroup>
84
+ </Content>
85
+ </Container>
86
+ );
87
+ }
88
+
89
+
90
+
91
+
@@ -0,0 +1,102 @@
1
+ 'use client';
2
+
3
+ import Link from 'next/link';
4
+ import styled from 'styled-components';
5
+
6
+ const Container = styled.div`
7
+ display: flex;
8
+ min-height: 100vh;
9
+ flex-direction: column;
10
+ align-items: center;
11
+ justify-content: center;
12
+ `;
13
+
14
+ const Content = styled.div`
15
+ text-align: center;
16
+ `;
17
+
18
+ const Title = styled.h1`
19
+ font-size: 2.25rem;
20
+ font-weight: 700;
21
+ letter-spacing: -0.025em;
22
+ color: #111827;
23
+
24
+ @media (min-width: 640px) {
25
+ font-size: 3.75rem;
26
+ }
27
+ `;
28
+
29
+ const Description = styled.p`
30
+ margin-top: 1.5rem;
31
+ font-size: 1.125rem;
32
+ line-height: 2rem;
33
+ color: #4b5563;
34
+ `;
35
+
36
+ const ButtonGroup = styled.div`
37
+ margin-top: 2.5rem;
38
+ display: flex;
39
+ align-items: center;
40
+ justify-content: center;
41
+ gap: 1.5rem;
42
+ `;
43
+
44
+ const PrimaryButton = styled(Link)`
45
+ border-radius: 0.375rem;
46
+ background-color: #4f46e5;
47
+ padding: 0.625rem 0.875rem;
48
+ font-size: 0.875rem;
49
+ font-weight: 600;
50
+ color: white;
51
+ text-decoration: none;
52
+ box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
53
+ transition: background-color 0.2s;
54
+
55
+ &:hover {
56
+ background-color: #6366f1;
57
+ }
58
+
59
+ &:focus-visible {
60
+ outline: 2px solid #4f46e5;
61
+ outline-offset: 2px;
62
+ }
63
+ `;
64
+
65
+ const SecondaryLink = styled.a`
66
+ font-size: 0.875rem;
67
+ font-weight: 600;
68
+ line-height: 1.5rem;
69
+ color: #111827;
70
+ text-decoration: none;
71
+
72
+ &:hover {
73
+ color: #4f46e5;
74
+ }
75
+ `;
76
+
77
+ export default function HomePage() {
78
+ return (
79
+ <Container>
80
+ <Content>
81
+ <Title>Welcome to Your App</Title>
82
+ <Description>
83
+ A production-ready Next.js application scaffolded with create-react-forge.
84
+ </Description>
85
+ <ButtonGroup>
86
+ <PrimaryButton href="/dashboard">Get started</PrimaryButton>
87
+ <SecondaryLink
88
+ href="https://github.com/alan2207/bulletproof-react"
89
+ target="_blank"
90
+ rel="noopener noreferrer"
91
+ >
92
+ Learn more <span aria-hidden="true">→</span>
93
+ </SecondaryLink>
94
+ </ButtonGroup>
95
+ </Content>
96
+ </Container>
97
+ );
98
+ }
99
+
100
+
101
+
102
+
@@ -0,0 +1,22 @@
1
+ 'use client';
2
+
3
+ import StyledComponentsRegistry from '@/lib/StyledComponentsRegistry';
4
+ import { GlobalStyles } from '@/styles/globals';
5
+ import { ReactNode } from 'react';
6
+
7
+ type ProvidersProps = {
8
+ children: ReactNode;
9
+ };
10
+
11
+ export function Providers({ children }: ProvidersProps) {
12
+ return (
13
+ <StyledComponentsRegistry>
14
+ <GlobalStyles />
15
+ {children}
16
+ </StyledComponentsRegistry>
17
+ );
18
+ }
19
+
20
+
21
+
22
+
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
- import React, { useState } from 'react';
4
3
  import { useServerInsertedHTML } from 'next/navigation';
4
+ import React, { useState } from 'react';
5
5
  import { ServerStyleSheet, StyleSheetManager } from 'styled-components';
6
6
 
7
7
  export default function StyledComponentsRegistry({
@@ -10,6 +10,7 @@ export default function StyledComponentsRegistry({
10
10
  children: React.ReactNode;
11
11
  }) {
12
12
  // Only create stylesheet once with lazy initial state
13
+ // useState with initializer ensures this only runs once
13
14
  const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet());
14
15
 
15
16
  useServerInsertedHTML(() => {
@@ -18,7 +19,11 @@ export default function StyledComponentsRegistry({
18
19
  return <>{styles}</>;
19
20
  });
20
21
 
21
- if (typeof window !== 'undefined') return <>{children}</>;
22
+ // On the client side, styled-components automatically injects styles
23
+ // We only need StyleSheetManager on the server for SSR collection
24
+ if (typeof window !== 'undefined') {
25
+ return <>{children}</>;
26
+ }
22
27
 
23
28
  return (
24
29
  <StyleSheetManager sheet={styledComponentsStyleSheet.instance}>
@@ -27,3 +32,6 @@ export default function StyledComponentsRegistry({
27
32
  );
28
33
  }
29
34
 
35
+
36
+
37
+