create-react-forge 1.6.0 → 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 (86) hide show
  1. package/README.md +1 -1
  2. package/dist/cli/index.d.ts +1 -1
  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/config/defaults.d.ts +1 -1
  7. package/dist/config/defaults.js +2 -2
  8. package/dist/config/defaults.js.map +1 -1
  9. package/dist/config/schema.d.ts +11 -9
  10. package/dist/config/schema.d.ts.map +1 -1
  11. package/dist/config/schema.js +4 -3
  12. package/dist/config/schema.js.map +1 -1
  13. package/dist/templates/registry.d.ts +6 -2
  14. package/dist/templates/registry.d.ts.map +1 -1
  15. package/dist/templates/registry.js +31 -16
  16. package/dist/templates/registry.js.map +1 -1
  17. package/dist/templates/utils.js +4 -4
  18. package/dist/templates/utils.js.map +1 -1
  19. package/package.json +1 -1
  20. package/src/templates/overlays/base/manifest.json +4 -3
  21. package/src/templates/overlays/base/src/components/ui/Button.tsx +103 -31
  22. package/src/templates/overlays/base/src/components/ui/Input.tsx +55 -29
  23. package/src/templates/overlays/base/src/lib/utils.ts +0 -10
  24. package/src/templates/overlays/runtime/nextjs/src/app/error.tsx +39 -10
  25. package/src/templates/overlays/runtime/nextjs/src/app/loading.tsx +25 -7
  26. package/src/templates/overlays/runtime/nextjs/src/app/not-found.tsx +54 -13
  27. package/src/templates/overlays/runtime/nextjs/src/app/page.tsx +55 -13
  28. package/src/templates/overlays/runtime/nextjs/src/styles/globals.css +1 -1
  29. package/src/templates/overlays/runtime/vite/src/components/errors/ErrorFallback.tsx +49 -15
  30. package/src/templates/overlays/runtime/vite/src/components/ui/LoadingSpinner.tsx +33 -13
  31. package/src/templates/overlays/runtime/vite/src/features/misc/routes/Landing.tsx +78 -21
  32. package/src/templates/overlays/runtime/vite/src/features/misc/routes/NotFound.tsx +77 -19
  33. package/src/templates/overlays/runtime/vite/src/main.tsx +0 -2
  34. package/src/templates/overlays/styling/css/_nextjs/src/app/error.css +49 -0
  35. package/src/templates/overlays/styling/css/_nextjs/src/app/error.tsx +34 -0
  36. package/src/templates/overlays/styling/css/_nextjs/src/app/loading.css +28 -0
  37. package/src/templates/overlays/styling/css/_nextjs/src/app/loading.tsx +13 -0
  38. package/src/templates/overlays/styling/css/_nextjs/src/app/not-found.css +70 -0
  39. package/src/templates/overlays/styling/css/_nextjs/src/app/not-found.tsx +25 -0
  40. package/src/templates/overlays/styling/css/_nextjs/src/app/page.css +73 -0
  41. package/src/templates/overlays/styling/css/_nextjs/src/app/page.tsx +32 -0
  42. package/src/templates/overlays/styling/css/_vite/src/components/errors/ErrorFallback.css +49 -0
  43. package/src/templates/overlays/styling/css/_vite/src/components/errors/ErrorFallback.tsx +22 -0
  44. package/src/templates/overlays/styling/css/_vite/src/components/ui/LoadingSpinner.css +40 -0
  45. package/src/templates/overlays/styling/css/_vite/src/components/ui/LoadingSpinner.tsx +21 -0
  46. package/src/templates/overlays/styling/css/_vite/src/features/misc/routes/Landing.css +73 -0
  47. package/src/templates/overlays/styling/css/_vite/src/features/misc/routes/Landing.tsx +32 -0
  48. package/src/templates/overlays/styling/css/_vite/src/features/misc/routes/NotFound.css +70 -0
  49. package/src/templates/overlays/styling/css/_vite/src/features/misc/routes/NotFound.tsx +25 -0
  50. package/src/templates/overlays/styling/css/manifest.json +17 -0
  51. package/src/templates/overlays/styling/css/src/styles/globals.css +107 -0
  52. package/src/templates/overlays/styling/css-modules/_nextjs/src/app/loading.tsx +13 -0
  53. package/src/templates/overlays/styling/css-modules/_nextjs/src/app/page.module.css +73 -0
  54. package/src/templates/overlays/styling/css-modules/_nextjs/src/app/page.tsx +32 -0
  55. package/src/templates/overlays/styling/css-modules/_vite/src/components/errors/ErrorFallback.module.css +45 -0
  56. package/src/templates/overlays/styling/css-modules/_vite/src/components/errors/ErrorFallback.tsx +22 -0
  57. package/src/templates/overlays/styling/css-modules/_vite/src/components/ui/LoadingSpinner.module.css +40 -0
  58. package/src/templates/overlays/styling/css-modules/_vite/src/components/ui/LoadingSpinner.tsx +23 -0
  59. package/src/templates/overlays/styling/css-modules/_vite/src/features/misc/routes/Landing.module.css +73 -0
  60. package/src/templates/overlays/styling/css-modules/_vite/src/features/misc/routes/Landing.tsx +32 -0
  61. package/src/templates/overlays/styling/css-modules/_vite/src/features/misc/routes/NotFound.module.css +67 -0
  62. package/src/templates/overlays/styling/css-modules/_vite/src/features/misc/routes/NotFound.tsx +25 -0
  63. package/src/templates/overlays/styling/css-modules/manifest.json +6 -3
  64. package/src/templates/overlays/styling/css-modules/src/css.d.ts +13 -0
  65. package/src/templates/overlays/styling/styled-components/_nextjs/src/app/error.tsx +72 -0
  66. package/src/templates/overlays/styling/styled-components/_nextjs/src/app/layout.tsx +25 -0
  67. package/src/templates/overlays/styling/styled-components/_nextjs/src/app/loading.tsx +40 -0
  68. package/src/templates/overlays/styling/styled-components/_nextjs/src/app/not-found.tsx +91 -0
  69. package/src/templates/overlays/styling/styled-components/_nextjs/src/app/page.tsx +102 -0
  70. package/src/templates/overlays/styling/styled-components/_nextjs/src/app/providers.tsx +22 -0
  71. package/src/templates/overlays/styling/styled-components/{src → _nextjs/src}/lib/StyledComponentsRegistry.tsx +10 -2
  72. package/src/templates/overlays/styling/styled-components/_vite/src/app/provider.tsx +27 -0
  73. package/src/templates/overlays/styling/styled-components/_vite/src/components/errors/ErrorFallback.tsx +59 -0
  74. package/src/templates/overlays/styling/styled-components/_vite/src/components/ui/LoadingSpinner.tsx +47 -0
  75. package/src/templates/overlays/styling/styled-components/_vite/src/features/misc/routes/Landing.tsx +100 -0
  76. package/src/templates/overlays/styling/styled-components/_vite/src/features/misc/routes/NotFound.tsx +89 -0
  77. package/src/templates/overlays/styling/styled-components/_vite/src/main.tsx +13 -0
  78. package/src/templates/overlays/styling/styled-components/manifest.json +5 -1
  79. package/src/templates/overlays/styling/tailwind/_nextjs/src/app/error.tsx +33 -0
  80. package/src/templates/overlays/styling/tailwind/_nextjs/src/app/loading.tsx +12 -0
  81. package/src/templates/overlays/styling/tailwind/_nextjs/src/app/not-found.tsx +26 -0
  82. package/src/templates/overlays/styling/tailwind/_nextjs/src/app/page.tsx +33 -0
  83. package/src/templates/overlays/styling/tailwind/manifest.json +5 -3
  84. package/src/templates/overlays/runtime/vite/src/styles/globals.css +0 -55
  85. package/src/templates/overlays/styling/css-modules/src/components/ui/Button.module.css +0 -87
  86. package/src/templates/overlays/styling/css-modules/src/styles/globals.css +0 -91
@@ -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
+
@@ -0,0 +1,27 @@
1
+ import { ErrorFallback } from '@/components/errors/ErrorFallback';
2
+ import { LoadingSpinner } from '@/components/ui/LoadingSpinner';
3
+ import { GlobalStyles } from '@/styles/globals';
4
+ import { Suspense } from 'react';
5
+ import { ErrorBoundary } from 'react-error-boundary';
6
+ import { BrowserRouter } from 'react-router-dom';
7
+
8
+ type AppProviderProps = {
9
+ children: React.ReactNode;
10
+ };
11
+
12
+ export function AppProvider({ children }: AppProviderProps) {
13
+ return (
14
+ <Suspense fallback={<LoadingSpinner />}>
15
+ <ErrorBoundary FallbackComponent={ErrorFallback}>
16
+ <BrowserRouter>
17
+ <GlobalStyles />
18
+ {children}
19
+ </BrowserRouter>
20
+ </ErrorBoundary>
21
+ </Suspense>
22
+ );
23
+ }
24
+
25
+
26
+
27
+
@@ -0,0 +1,59 @@
1
+ import { FallbackProps } from 'react-error-boundary';
2
+ import styled from 'styled-components';
3
+
4
+ const Container = styled.div`
5
+ display: flex;
6
+ min-height: 100vh;
7
+ flex-direction: column;
8
+ align-items: center;
9
+ justify-content: center;
10
+ `;
11
+
12
+ const Content = styled.div`
13
+ text-align: center;
14
+ `;
15
+
16
+ const Title = styled.h1`
17
+ font-size: 1.5rem;
18
+ font-weight: 700;
19
+ color: #dc2626;
20
+ `;
21
+
22
+ const Message = styled.p`
23
+ margin-top: 1rem;
24
+ color: #4b5563;
25
+ `;
26
+
27
+ const RetryButton = styled.button`
28
+ margin-top: 1.5rem;
29
+ border-radius: 0.375rem;
30
+ background-color: #4f46e5;
31
+ padding: 0.625rem 0.875rem;
32
+ font-size: 0.875rem;
33
+ font-weight: 600;
34
+ color: white;
35
+ border: none;
36
+ cursor: pointer;
37
+ box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
38
+ transition: background-color 0.2s;
39
+
40
+ &:hover {
41
+ background-color: #6366f1;
42
+ }
43
+ `;
44
+
45
+ export function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
46
+ return (
47
+ <Container role="alert">
48
+ <Content>
49
+ <Title>Something went wrong</Title>
50
+ <Message>{error.message || 'An unexpected error occurred'}</Message>
51
+ <RetryButton onClick={resetErrorBoundary}>Try again</RetryButton>
52
+ </Content>
53
+ </Container>
54
+ );
55
+ }
56
+
57
+
58
+
59
+
@@ -0,0 +1,47 @@
1
+ import styled, { keyframes } from 'styled-components';
2
+
3
+ type LoadingSpinnerProps = {
4
+ size?: 'sm' | 'md' | 'lg';
5
+ };
6
+
7
+ const spin = keyframes`
8
+ from {
9
+ transform: rotate(0deg);
10
+ }
11
+ to {
12
+ transform: rotate(360deg);
13
+ }
14
+ `;
15
+
16
+ const sizes = {
17
+ sm: '1rem',
18
+ md: '2rem',
19
+ lg: '4rem',
20
+ };
21
+
22
+ const Container = styled.div`
23
+ display: flex;
24
+ align-items: center;
25
+ justify-content: center;
26
+ `;
27
+
28
+ const Spinner = styled.div<{ $size: 'sm' | 'md' | 'lg' }>`
29
+ width: ${(props) => sizes[props.$size]};
30
+ height: ${(props) => sizes[props.$size]};
31
+ border-radius: 50%;
32
+ border: 2px solid #d1d5db;
33
+ border-top-color: #4f46e5;
34
+ animation: ${spin} 1s linear infinite;
35
+ `;
36
+
37
+ export function LoadingSpinner({ size = 'md' }: LoadingSpinnerProps) {
38
+ return (
39
+ <Container>
40
+ <Spinner $size={size} role="status" aria-label="Loading" />
41
+ </Container>
42
+ );
43
+ }
44
+
45
+
46
+
47
+
@@ -0,0 +1,100 @@
1
+ import { Link } from 'react-router-dom';
2
+ import styled from 'styled-components';
3
+
4
+ const Container = styled.div`
5
+ display: flex;
6
+ min-height: 100vh;
7
+ flex-direction: column;
8
+ align-items: center;
9
+ justify-content: center;
10
+ `;
11
+
12
+ const Content = styled.div`
13
+ text-align: center;
14
+ `;
15
+
16
+ const Title = styled.h1`
17
+ font-size: 2.25rem;
18
+ font-weight: 700;
19
+ letter-spacing: -0.025em;
20
+ color: #111827;
21
+
22
+ @media (min-width: 640px) {
23
+ font-size: 3.75rem;
24
+ }
25
+ `;
26
+
27
+ const Description = styled.p`
28
+ margin-top: 1.5rem;
29
+ font-size: 1.125rem;
30
+ line-height: 2rem;
31
+ color: #4b5563;
32
+ `;
33
+
34
+ const ButtonGroup = styled.div`
35
+ margin-top: 2.5rem;
36
+ display: flex;
37
+ align-items: center;
38
+ justify-content: center;
39
+ gap: 1.5rem;
40
+ `;
41
+
42
+ const PrimaryButton = styled(Link)`
43
+ border-radius: 0.375rem;
44
+ background-color: #4f46e5;
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
+ &:hover {
54
+ background-color: #6366f1;
55
+ }
56
+
57
+ &:focus-visible {
58
+ outline: 2px solid #4f46e5;
59
+ outline-offset: 2px;
60
+ }
61
+ `;
62
+
63
+ const SecondaryLink = styled.a`
64
+ font-size: 0.875rem;
65
+ font-weight: 600;
66
+ line-height: 1.5rem;
67
+ color: #111827;
68
+ text-decoration: none;
69
+
70
+ &:hover {
71
+ color: #4f46e5;
72
+ }
73
+ `;
74
+
75
+ export function Landing() {
76
+ return (
77
+ <Container>
78
+ <Content>
79
+ <Title>Welcome to Your App</Title>
80
+ <Description>
81
+ A production-ready React application scaffolded with create-react-forge.
82
+ </Description>
83
+ <ButtonGroup>
84
+ <PrimaryButton to="/dashboard">Get started</PrimaryButton>
85
+ <SecondaryLink
86
+ href="https://github.com/alan2207/bulletproof-react"
87
+ target="_blank"
88
+ rel="noopener noreferrer"
89
+ >
90
+ Learn more <span aria-hidden="true">→</span>
91
+ </SecondaryLink>
92
+ </ButtonGroup>
93
+ </Content>
94
+ </Container>
95
+ );
96
+ }
97
+
98
+
99
+
100
+