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
@@ -1,5 +1,5 @@
1
1
  import { forwardRef } from 'react';
2
- import { cn } from '@/lib/utils';
2
+ import styled, { css } from 'styled-components';
3
3
 
4
4
  export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
5
5
  variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'danger';
@@ -7,29 +7,110 @@ export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
7
7
  isLoading?: boolean;
8
8
  };
9
9
 
10
- const variants = {
11
- primary:
12
- 'bg-indigo-600 text-white hover:bg-indigo-500 focus-visible:outline-indigo-600',
13
- secondary:
14
- 'bg-gray-100 text-gray-900 hover:bg-gray-200 focus-visible:outline-gray-600',
15
- outline:
16
- 'border border-gray-300 bg-transparent text-gray-700 hover:bg-gray-50 focus-visible:outline-gray-600',
17
- ghost:
18
- 'bg-transparent text-gray-700 hover:bg-gray-100 focus-visible:outline-gray-600',
19
- danger:
20
- 'bg-red-600 text-white hover:bg-red-500 focus-visible:outline-red-600',
10
+ const variantStyles = {
11
+ primary: css`
12
+ background-color: #4f46e5;
13
+ color: white;
14
+ &:hover:not(:disabled) {
15
+ background-color: #6366f1;
16
+ }
17
+ `,
18
+ secondary: css`
19
+ background-color: #f3f4f6;
20
+ color: #111827;
21
+ &:hover:not(:disabled) {
22
+ background-color: #e5e7eb;
23
+ }
24
+ `,
25
+ outline: css`
26
+ background-color: transparent;
27
+ color: #374151;
28
+ border: 1px solid #d1d5db;
29
+ &:hover:not(:disabled) {
30
+ background-color: #f9fafb;
31
+ }
32
+ `,
33
+ ghost: css`
34
+ background-color: transparent;
35
+ color: #374151;
36
+ &:hover:not(:disabled) {
37
+ background-color: #f3f4f6;
38
+ }
39
+ `,
40
+ danger: css`
41
+ background-color: #dc2626;
42
+ color: white;
43
+ &:hover:not(:disabled) {
44
+ background-color: #ef4444;
45
+ }
46
+ `,
21
47
  };
22
48
 
23
- const sizes = {
24
- sm: 'px-2.5 py-1.5 text-xs',
25
- md: 'px-3.5 py-2.5 text-sm',
26
- lg: 'px-4 py-3 text-base',
49
+ const sizeStyles = {
50
+ sm: css`
51
+ padding: 0.375rem 0.625rem;
52
+ font-size: 0.75rem;
53
+ `,
54
+ md: css`
55
+ padding: 0.625rem 0.875rem;
56
+ font-size: 0.875rem;
57
+ `,
58
+ lg: css`
59
+ padding: 0.75rem 1rem;
60
+ font-size: 1rem;
61
+ `,
27
62
  };
28
63
 
64
+ const StyledButton = styled.button<{
65
+ $variant: ButtonProps['variant'];
66
+ $size: ButtonProps['size'];
67
+ }>`
68
+ display: inline-flex;
69
+ align-items: center;
70
+ justify-content: center;
71
+ border-radius: 0.375rem;
72
+ font-weight: 600;
73
+ box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
74
+ transition: background-color 0.2s, color 0.2s;
75
+ border: none;
76
+ cursor: pointer;
77
+
78
+ &:disabled {
79
+ cursor: not-allowed;
80
+ opacity: 0.5;
81
+ }
82
+
83
+ &:focus-visible {
84
+ outline: 2px solid #4f46e5;
85
+ outline-offset: 2px;
86
+ }
87
+
88
+ ${(props) => variantStyles[props.$variant || 'primary']}
89
+ ${(props) => sizeStyles[props.$size || 'md']}
90
+ `;
91
+
92
+ const LoadingSpinner = styled.span`
93
+ margin-right: 0.5rem;
94
+ width: 1rem;
95
+ height: 1rem;
96
+ border: 2px solid currentColor;
97
+ border-top-color: transparent;
98
+ border-radius: 50%;
99
+ animation: spin 1s linear infinite;
100
+
101
+ @keyframes spin {
102
+ from {
103
+ transform: rotate(0deg);
104
+ }
105
+ to {
106
+ transform: rotate(360deg);
107
+ }
108
+ }
109
+ `;
110
+
29
111
  export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
30
112
  (
31
113
  {
32
- className,
33
114
  variant = 'primary',
34
115
  size = 'md',
35
116
  isLoading = false,
@@ -40,27 +121,18 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
40
121
  ref
41
122
  ) => {
42
123
  return (
43
- <button
124
+ <StyledButton
44
125
  ref={ref}
45
- className={cn(
46
- 'inline-flex items-center justify-center rounded-md font-semibold shadow-sm transition-colors',
47
- 'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2',
48
- 'disabled:cursor-not-allowed disabled:opacity-50',
49
- variants[variant],
50
- sizes[size],
51
- className
52
- )}
126
+ $variant={variant}
127
+ $size={size}
53
128
  disabled={disabled || isLoading}
54
129
  {...props}
55
130
  >
56
- {isLoading && (
57
- <span className="mr-2 h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" />
58
- )}
131
+ {isLoading && <LoadingSpinner />}
59
132
  {children}
60
- </button>
133
+ </StyledButton>
61
134
  );
62
135
  }
63
136
  );
64
137
 
65
138
  Button.displayName = 'Button';
66
-
@@ -1,51 +1,77 @@
1
1
  import { forwardRef } from 'react';
2
- import { cn } from '@/lib/utils';
2
+ import styled from 'styled-components';
3
3
 
4
4
  export type InputProps = React.InputHTMLAttributes<HTMLInputElement> & {
5
5
  label?: string;
6
6
  error?: string;
7
7
  };
8
8
 
9
+ const InputWrapper = styled.div`
10
+ width: 100%;
11
+ `;
12
+
13
+ const Label = styled.label`
14
+ display: block;
15
+ margin-bottom: 0.25rem;
16
+ font-size: 0.875rem;
17
+ font-weight: 500;
18
+ color: #374151;
19
+ `;
20
+
21
+ const StyledInput = styled.input<{ $hasError?: boolean }>`
22
+ display: block;
23
+ width: 100%;
24
+ padding: 0.5rem 0.75rem;
25
+ font-size: 1rem;
26
+ color: #111827;
27
+ background-color: white;
28
+ border: 1px solid ${(props) => (props.$hasError ? '#fca5a5' : '#d1d5db')};
29
+ border-radius: 0.375rem;
30
+ box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
31
+ transition: border-color 0.2s, box-shadow 0.2s;
32
+
33
+ &::placeholder {
34
+ color: #9ca3af;
35
+ }
36
+
37
+ &:focus {
38
+ outline: none;
39
+ border-color: ${(props) => (props.$hasError ? '#ef4444' : '#4f46e5')};
40
+ box-shadow: 0 0 0 2px ${(props) => (props.$hasError ? '#fecaca' : '#c7d2fe')};
41
+ }
42
+
43
+ &:disabled {
44
+ cursor: not-allowed;
45
+ background-color: #f9fafb;
46
+ color: #6b7280;
47
+ }
48
+ `;
49
+
50
+ const ErrorMessage = styled.p`
51
+ margin-top: 0.25rem;
52
+ font-size: 0.875rem;
53
+ color: #dc2626;
54
+ `;
55
+
9
56
  export const Input = forwardRef<HTMLInputElement, InputProps>(
10
- ({ className, label, error, id, ...props }, ref) => {
57
+ ({ label, error, id, ...props }, ref) => {
11
58
  const inputId = id || props.name;
12
59
 
13
60
  return (
14
- <div className="w-full">
15
- {label && (
16
- <label
17
- htmlFor={inputId}
18
- className="mb-1 block text-sm font-medium text-gray-700"
19
- >
20
- {label}
21
- </label>
22
- )}
23
- <input
61
+ <InputWrapper>
62
+ {label && <Label htmlFor={inputId}>{label}</Label>}
63
+ <StyledInput
24
64
  ref={ref}
25
65
  id={inputId}
26
- className={cn(
27
- 'block w-full rounded-md border-0 px-3 py-2 text-gray-900 shadow-sm ring-1 ring-inset',
28
- 'placeholder:text-gray-400',
29
- 'focus:ring-2 focus:ring-inset focus:ring-indigo-600',
30
- 'disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-500',
31
- error
32
- ? 'ring-red-300 focus:ring-red-500'
33
- : 'ring-gray-300',
34
- className
35
- )}
66
+ $hasError={!!error}
36
67
  aria-invalid={error ? 'true' : 'false'}
37
68
  aria-describedby={error ? `${inputId}-error` : undefined}
38
69
  {...props}
39
70
  />
40
- {error && (
41
- <p id={`${inputId}-error`} className="mt-1 text-sm text-red-600">
42
- {error}
43
- </p>
44
- )}
45
- </div>
71
+ {error && <ErrorMessage id={`${inputId}-error`}>{error}</ErrorMessage>}
72
+ </InputWrapper>
46
73
  );
47
74
  }
48
75
  );
49
76
 
50
77
  Input.displayName = 'Input';
51
-
@@ -1,12 +1,3 @@
1
- import { clsx, type ClassValue } from 'clsx';
2
-
3
- /**
4
- * Utility function for constructing className strings conditionally
5
- */
6
- export function cn(...inputs: ClassValue[]) {
7
- return clsx(inputs);
8
- }
9
-
10
1
  /**
11
2
  * Format a date to a human-readable string
12
3
  */
@@ -31,4 +22,3 @@ export function sleep(ms: number): Promise<void> {
31
22
  export function generateId(): string {
32
23
  return Math.random().toString(36).substring(2, 9);
33
24
  }
34
-
@@ -2,6 +2,40 @@
2
2
 
3
3
  import { useEffect } from 'react';
4
4
 
5
+ const styles = {
6
+ container: {
7
+ display: 'flex',
8
+ minHeight: '100vh',
9
+ flexDirection: 'column' as const,
10
+ alignItems: 'center',
11
+ justifyContent: 'center',
12
+ },
13
+ content: {
14
+ textAlign: 'center' as const,
15
+ },
16
+ title: {
17
+ fontSize: '1.5rem',
18
+ fontWeight: 700,
19
+ color: '#dc2626',
20
+ },
21
+ message: {
22
+ marginTop: '1rem',
23
+ color: '#4b5563',
24
+ },
25
+ retryButton: {
26
+ marginTop: '1.5rem',
27
+ padding: '0.625rem 0.875rem',
28
+ fontSize: '0.875rem',
29
+ fontWeight: 600,
30
+ color: 'white',
31
+ backgroundColor: '#4f46e5',
32
+ border: 'none',
33
+ borderRadius: '0.375rem',
34
+ boxShadow: '0 1px 2px 0 rgb(0 0 0 / 0.05)',
35
+ cursor: 'pointer',
36
+ },
37
+ };
38
+
5
39
  export default function Error({
6
40
  error,
7
41
  reset,
@@ -10,25 +44,20 @@ export default function Error({
10
44
  reset: () => void;
11
45
  }) {
12
46
  useEffect(() => {
13
- // Log the error to an error reporting service
14
47
  console.error(error);
15
48
  }, [error]);
16
49
 
17
50
  return (
18
- <div className="flex min-h-screen flex-col items-center justify-center" role="alert">
19
- <div className="text-center">
20
- <h1 className="text-2xl font-bold text-red-600">Something went wrong</h1>
21
- <p className="mt-4 text-gray-600">
51
+ <div style={styles.container} role="alert">
52
+ <div style={styles.content}>
53
+ <h1 style={styles.title}>Something went wrong</h1>
54
+ <p style={styles.message}>
22
55
  {error.message || 'An unexpected error occurred'}
23
56
  </p>
24
- <button
25
- onClick={reset}
26
- className="mt-6 rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500"
27
- >
57
+ <button onClick={reset} style={styles.retryButton}>
28
58
  Try again
29
59
  </button>
30
60
  </div>
31
61
  </div>
32
62
  );
33
63
  }
34
-
@@ -1,12 +1,30 @@
1
+ const styles = {
2
+ container: {
3
+ display: 'flex',
4
+ minHeight: '100vh',
5
+ alignItems: 'center',
6
+ justifyContent: 'center',
7
+ },
8
+ spinner: {
9
+ width: '2rem',
10
+ height: '2rem',
11
+ border: '2px solid #d1d5db',
12
+ borderTopColor: '#4f46e5',
13
+ borderRadius: '50%',
14
+ animation: 'spin 1s linear infinite',
15
+ },
16
+ };
17
+
1
18
  export default function Loading() {
2
19
  return (
3
- <div className="flex min-h-screen items-center justify-center">
4
- <div
5
- className="h-8 w-8 animate-spin rounded-full border-2 border-gray-300 border-t-indigo-600"
6
- role="status"
7
- aria-label="Loading"
8
- />
20
+ <div style={styles.container}>
21
+ <style>{`
22
+ @keyframes spin {
23
+ from { transform: rotate(0deg); }
24
+ to { transform: rotate(360deg); }
25
+ }
26
+ `}</style>
27
+ <div style={styles.spinner} role="status" aria-label="Loading" />
9
28
  </div>
10
29
  );
11
30
  }
12
-
@@ -1,21 +1,63 @@
1
1
  import Link from 'next/link';
2
2
 
3
+ const styles = {
4
+ container: {
5
+ display: 'flex',
6
+ minHeight: '100vh',
7
+ flexDirection: 'column' as const,
8
+ alignItems: 'center',
9
+ justifyContent: 'center',
10
+ },
11
+ content: {
12
+ textAlign: 'center' as const,
13
+ },
14
+ errorCode: {
15
+ fontSize: '1rem',
16
+ fontWeight: 600,
17
+ color: '#4f46e5',
18
+ },
19
+ title: {
20
+ marginTop: '1rem',
21
+ fontSize: '1.875rem',
22
+ fontWeight: 700,
23
+ letterSpacing: '-0.025em',
24
+ },
25
+ description: {
26
+ marginTop: '1.5rem',
27
+ fontSize: '1rem',
28
+ lineHeight: '1.75rem',
29
+ color: '#4b5563',
30
+ },
31
+ actions: {
32
+ marginTop: '2.5rem',
33
+ display: 'flex',
34
+ alignItems: 'center',
35
+ justifyContent: 'center',
36
+ gap: '1.5rem',
37
+ },
38
+ homeLink: {
39
+ padding: '0.625rem 0.875rem',
40
+ fontSize: '0.875rem',
41
+ fontWeight: 600,
42
+ color: 'white',
43
+ backgroundColor: '#4f46e5',
44
+ borderRadius: '0.375rem',
45
+ textDecoration: 'none',
46
+ boxShadow: '0 1px 2px 0 rgb(0 0 0 / 0.05)',
47
+ },
48
+ };
49
+
3
50
  export default function NotFound() {
4
51
  return (
5
- <div className="flex min-h-screen flex-col items-center justify-center">
6
- <div className="text-center">
7
- <p className="text-base font-semibold text-indigo-600">404</p>
8
- <h1 className="mt-4 text-3xl font-bold tracking-tight sm:text-5xl">
9
- Page not found
10
- </h1>
11
- <p className="mt-6 text-base leading-7 text-gray-600">
52
+ <div style={styles.container}>
53
+ <div style={styles.content}>
54
+ <p style={styles.errorCode}>404</p>
55
+ <h1 style={styles.title}>Page not found</h1>
56
+ <p style={styles.description}>
12
57
  Sorry, we couldn't find the page you're looking for.
13
58
  </p>
14
- <div className="mt-10 flex items-center justify-center gap-x-6">
15
- <Link
16
- href="/"
17
- className="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
18
- >
59
+ <div style={styles.actions}>
60
+ <Link href="/" style={styles.homeLink}>
19
61
  Go back home
20
62
  </Link>
21
63
  </div>
@@ -23,4 +65,3 @@ export default function NotFound() {
23
65
  </div>
24
66
  );
25
67
  }
26
-
@@ -1,25 +1,68 @@
1
1
  import Link from 'next/link';
2
2
 
3
+ const styles = {
4
+ container: {
5
+ display: 'flex',
6
+ minHeight: '100vh',
7
+ flexDirection: 'column' as const,
8
+ alignItems: 'center',
9
+ justifyContent: 'center',
10
+ },
11
+ content: {
12
+ textAlign: 'center' as const,
13
+ },
14
+ title: {
15
+ fontSize: '2.25rem',
16
+ fontWeight: 700,
17
+ letterSpacing: '-0.025em',
18
+ },
19
+ description: {
20
+ marginTop: '1.5rem',
21
+ fontSize: '1.125rem',
22
+ lineHeight: '2rem',
23
+ color: '#4b5563',
24
+ },
25
+ actions: {
26
+ marginTop: '2.5rem',
27
+ display: 'flex',
28
+ alignItems: 'center',
29
+ justifyContent: 'center',
30
+ gap: '1.5rem',
31
+ },
32
+ primaryLink: {
33
+ padding: '0.625rem 0.875rem',
34
+ fontSize: '0.875rem',
35
+ fontWeight: 600,
36
+ color: 'white',
37
+ backgroundColor: '#4f46e5',
38
+ borderRadius: '0.375rem',
39
+ textDecoration: 'none',
40
+ boxShadow: '0 1px 2px 0 rgb(0 0 0 / 0.05)',
41
+ },
42
+ secondaryLink: {
43
+ fontSize: '0.875rem',
44
+ fontWeight: 600,
45
+ lineHeight: '1.5rem',
46
+ color: 'inherit',
47
+ textDecoration: 'none',
48
+ },
49
+ };
50
+
3
51
  export default function HomePage() {
4
52
  return (
5
- <div className="flex min-h-screen flex-col items-center justify-center">
6
- <div className="text-center">
7
- <h1 className="text-4xl font-bold tracking-tight sm:text-6xl">
8
- Welcome to Your App
9
- </h1>
10
- <p className="mt-6 text-lg leading-8 text-gray-600">
53
+ <div style={styles.container}>
54
+ <div style={styles.content}>
55
+ <h1 style={styles.title}>Welcome to Your App</h1>
56
+ <p style={styles.description}>
11
57
  A production-ready Next.js application scaffolded with create-react-forge.
12
58
  </p>
13
- <div className="mt-10 flex items-center justify-center gap-x-6">
14
- <Link
15
- href="/dashboard"
16
- className="rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
17
- >
59
+ <div style={styles.actions}>
60
+ <Link href="/dashboard" style={styles.primaryLink}>
18
61
  Get started
19
62
  </Link>
20
63
  <a
21
64
  href="https://github.com/alan2207/bulletproof-react"
22
- className="text-sm font-semibold leading-6"
65
+ style={styles.secondaryLink}
23
66
  target="_blank"
24
67
  rel="noopener noreferrer"
25
68
  >
@@ -30,4 +73,3 @@ export default function HomePage() {
30
73
  </div>
31
74
  );
32
75
  }
33
-
@@ -1,4 +1,4 @@
1
- /* Base styles - will be enhanced by styling overlay (tailwind/css-modules) */
1
+ /* Base styles - will be enhanced by styling overlay (tailwind) if selected */
2
2
 
3
3
  *,
4
4
  *::before,
@@ -1,21 +1,55 @@
1
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
+ padding: 0.625rem 0.875rem;
30
+ font-size: 0.875rem;
31
+ font-weight: 600;
32
+ color: white;
33
+ background-color: #4f46e5;
34
+ border: none;
35
+ border-radius: 0.375rem;
36
+ box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
37
+ cursor: pointer;
38
+ transition: background-color 0.2s;
39
+
40
+ &:hover {
41
+ background-color: #6366f1;
42
+ }
43
+ `;
2
44
 
3
45
  export function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
4
46
  return (
5
- <div className="flex min-h-screen flex-col items-center justify-center" role="alert">
6
- <div className="text-center">
7
- <h1 className="text-2xl font-bold text-red-600">Something went wrong</h1>
8
- <p className="mt-4 text-gray-600">
9
- {error.message || 'An unexpected error occurred'}
10
- </p>
11
- <button
12
- onClick={resetErrorBoundary}
13
- className="mt-6 rounded-md bg-indigo-600 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500"
14
- >
15
- Try again
16
- </button>
17
- </div>
18
- </div>
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>
19
54
  );
20
55
  }
21
-