create-react-scaffold-cli 1.0.2 → 1.0.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 (63) hide show
  1. package/package.json +1 -1
  2. package/templates/base/{readme.md → README.md} +165 -95
  3. package/templates/base/docs/DOCS.md +7 -0
  4. package/templates/base/index.html +3 -3
  5. package/templates/base/package.json +2 -0
  6. package/templates/base/public/icons/react.svg +1 -0
  7. package/templates/base/src/app/App.jsx +8 -6
  8. package/templates/base/src/app/Router.jsx +42 -2
  9. package/templates/base/src/app/index.css +36 -0
  10. package/templates/base/src/app/main.jsx +1 -1
  11. package/templates/base/src/app/middlewares/AuthMiddleware.jsx +5 -0
  12. package/templates/base/src/app/middlewares/index.js +1 -0
  13. package/templates/base/src/app/providers/QueryProvider.jsx +1 -1
  14. package/templates/base/src/app/routes.registry.js +8 -0
  15. package/templates/base/src/features/sample/constants/index.js +3 -0
  16. package/templates/base/src/features/sample/constants/sample.assets.js +8 -0
  17. package/templates/base/src/features/sample/constants/sample.navigations.js +5 -0
  18. package/templates/base/src/features/sample/constants/sample.queryKeys.js +3 -0
  19. package/templates/base/src/features/sample/index.js +1 -0
  20. package/templates/base/src/features/sample/pages/SamplePage.jsx +8 -0
  21. package/templates/base/src/features/sample/pages/index.js +3 -0
  22. package/templates/base/src/features/sample/sample.routes.js +12 -0
  23. package/templates/base/src/features/welcome/components/CodeLine.jsx +54 -0
  24. package/templates/base/src/features/welcome/components/Divider.jsx +7 -0
  25. package/templates/base/src/features/welcome/components/Footer.jsx +78 -0
  26. package/templates/base/src/features/welcome/components/Hero.jsx +131 -0
  27. package/templates/base/src/features/welcome/components/IconLink.jsx +18 -0
  28. package/templates/base/src/features/welcome/components/QuickStartPanel.jsx +63 -0
  29. package/templates/base/src/features/welcome/components/RingSoft.jsx +16 -0
  30. package/templates/base/src/features/welcome/components/StorySections.jsx +63 -0
  31. package/templates/base/src/features/welcome/components/WhatYouGet.jsx +49 -0
  32. package/templates/base/src/features/welcome/components/index.js +5 -0
  33. package/templates/base/src/features/welcome/constants/index.js +2 -0
  34. package/templates/base/src/features/welcome/constants/welcome.constants.js +21 -0
  35. package/templates/base/src/features/welcome/constants/welcome.navigations.js +3 -0
  36. package/templates/base/src/features/welcome/index.js +1 -0
  37. package/templates/base/src/features/welcome/pages/WelcomePage.jsx +28 -0
  38. package/templates/base/src/features/welcome/pages/index.js +3 -0
  39. package/templates/base/src/features/welcome/welcome.routes.js +12 -0
  40. package/templates/base/src/shared/{shared_readme.md → SHARED.md} +7 -1
  41. package/templates/base/src/shared/constants/app.constants.js +6 -0
  42. package/templates/base/src/shared/constants/assets.constants.js +5 -0
  43. package/templates/base/src/shared/constants/index.js +2 -0
  44. package/templates/base/src/shared/theme/theme.js +26 -15
  45. package/templates/base/src/shared/ui/Box.jsx +1 -14
  46. package/templates/base/src/shared/ui/Button.jsx +4 -5
  47. package/templates/base/src/shared/ui/DropdownMenu.jsx +57 -91
  48. package/templates/base/src/shared/ui/GridItem.jsx +1 -0
  49. package/templates/base/src/shared/ui/Modal.jsx +1 -0
  50. package/templates/base/src/shared/ui/Text.jsx +3 -3
  51. package/templates/base/src/shared/ui/index.js +17 -0
  52. package/templates/base/src/shared/utils/localStorage.js +18 -0
  53. package/templates/base/src/shared/utils/memo.js +6 -0
  54. package/templates/base/src/shared/utils/regix.js +3 -0
  55. package/templates/base/vercel.json +3 -0
  56. package/templates/base/vite.config.js +4 -5
  57. package/templates/base/src/features/sample/sample.assets.js +0 -0
  58. package/templates/base/src/features/sample/sample.navigations.js +0 -0
  59. package/templates/base/src/features/sample/sample.queryKeys.js +0 -0
  60. package/templates/base/src/features/sample/sample.routes.jsx +0 -0
  61. /package/templates/base/src/app/{app_readme.md → APP.md} +0 -0
  62. /package/templates/base/src/features/{features_readme.md → FEATURES.md} +0 -0
  63. /package/templates/base/src/{features/index.js → shared/utils/motion.js} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-react-scaffold-cli",
3
- "version": "1.0.2",
3
+ "version": "1.0.5",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -1,95 +1,165 @@
1
- # React Feature‑First Scaffold
2
-
3
- ## Purpose
4
-
5
- This repository is an **internal, opinionated React scaffold** designed for large-scale frontend applications. It enforces a **feature‑first architecture**, clear ownership boundaries, and predictable patterns so teams can scale safely without architectural drift.
6
-
7
- This scaffold will later be distributed via an internal **NPM CLI** (similar to `create-react-app`) to bootstrap projects with best practices preconfigured.
8
-
9
- ---
10
-
11
- ## Core Principles
12
-
13
- - **Feature-first, not layer-first**
14
- - **Explicit boundaries** between app / features / shared
15
- - **Locality of logic** (keep things close to where they are used)
16
- - **Predictable imports** and naming conventions
17
- - **Low cognitive load** for new developers
18
-
19
- ---
20
-
21
- ## Tech Stack
22
-
23
- ### Runtime
24
-
25
- - **React 19** – UI layer
26
- - **Vite** – fast dev & build tooling
27
-
28
- ### Styling
29
-
30
- - **Tailwind CSS** – utility-first styling
31
- - **tailwind-merge** – class conflict resolution
32
- - **clsx** – conditional class composition
33
-
34
- ### Data & State
35
-
36
- - **@tanstack/react-query** – server-state management
37
- - **Axios** – HTTP client
38
-
39
- ### UI Utilities
40
-
41
- - **Radix UI** – accessible headless components
42
- - **react-hot-toast** – notifications
43
- - **react-icons** – icon system
44
-
45
- ### Code Quality
46
-
47
- - **ESLint** – linting with strict rules
48
- - **Prettier** – formatting
49
- - **Husky + lint-staged** – pre-commit enforcement
50
-
51
- ---
52
-
53
- ## High-Level Folder Structure
54
-
55
- ```txt
56
- src/
57
- app/ # Application bootstrap & global wiring
58
- features/ # Business features (isolated, self-contained)
59
- shared/ # Cross-feature reusable modules
60
- ```
61
-
62
- Each top-level folder has **its own README** explaining rules and responsibilities.
63
-
64
- ---
65
-
66
- ## Installation (WIP)
67
-
68
- > CLI installation instructions.
69
-
70
- ```bash
71
- npx create-react-scaffold-cli
72
-
73
- ```
74
-
75
- ---
76
-
77
- ## Documentation Index
78
-
79
- - `src/app/README.md` – Application bootstrap & providers
80
- - `src/features/README.md` – Feature architecture rules
81
- - `src/shared/README.md` – Shared modules & reuse policy
82
-
83
- ---
84
-
85
- ## Philosophy
86
-
87
- > **Architecture is a product feature.**
88
-
89
- This scaffold exists to reduce decision fatigue, prevent architectural erosion, and help teams move faster with confidence.
90
-
91
- ---
92
-
93
- ## Ownership
94
-
95
- This scaffold is maintained internally and should be treated as a **living standard**. Changes must be intentional, documented, and agreed upon by the frontend team.
1
+ # React Feature‑First Scaffold
2
+
3
+ ## Purpose
4
+
5
+ This repository is an **opinionated React scaffold** designed for large-scale frontend applications. It enforces a **feature‑first architecture**, clear ownership boundaries, and predictable patterns so teams can scale safely without architectural drift.
6
+
7
+ This scaffold will later be distributed via an internal **NPM CLI** (similar to `create-react-app`) to bootstrap projects with best practices preconfigured.
8
+
9
+ ---
10
+
11
+ ## Core Principles
12
+
13
+ - **Feature-first, not layer-first**
14
+ - **Explicit boundaries** between app / features / shared
15
+ - **Locality of logic** (keep things close to where they are used)
16
+ - **Predictable imports** and naming conventions
17
+ - **Low cognitive load** for new developers
18
+
19
+ ---
20
+
21
+ ## Tech Stack
22
+
23
+ ### Runtime
24
+
25
+ - **React 19** – UI layer
26
+ - **Vite** – fast dev & build tooling
27
+
28
+ ### Styling
29
+
30
+ - **Tailwind CSS** – utility-first styling
31
+ - **tailwind-merge** – class conflict resolution
32
+ - **clsx** – conditional class composition
33
+
34
+ ### Data & State
35
+
36
+ - **@tanstack/react-query** – server-state management
37
+ - **Axios** – HTTP client
38
+
39
+ ### UI Utilities
40
+
41
+ - **Radix UI** – accessible headless components
42
+ - **react-hot-toast** – notifications
43
+ - **react-icons** – icon system
44
+
45
+ ### Code Quality
46
+
47
+ - **ESLint** – linting with strict rules
48
+ - **Prettier** – formatting
49
+ - **Husky + lint-staged** – pre-commit enforcement
50
+
51
+ ---
52
+
53
+ ## High-Level Folder Structure
54
+
55
+ ```txt
56
+ src/
57
+ app/ # Application bootstrap & global wiring
58
+ features/ # Business features (isolated, self-contained)
59
+ shared/ # Cross-feature reusable modules
60
+ ```
61
+
62
+ Each top-level folder has **its own README** explaining rules and responsibilities.
63
+
64
+ ---
65
+
66
+ ## Installation
67
+
68
+ > CLI installation instructions.
69
+
70
+ ```bash
71
+ npx create-react-scaffold-cli
72
+
73
+ ```
74
+
75
+ ---
76
+
77
+ ## Documentation Index
78
+
79
+ - `src/app/README.md` – Application bootstrap & providers
80
+ - `src/features/README.md` – Feature architecture rules
81
+ - `src/shared/README.md` – Shared modules & reuse policy
82
+
83
+ ---
84
+
85
+ ## Set Up
86
+
87
+ ### Install dependencies
88
+
89
+ ```bash
90
+ npm i
91
+ ```
92
+
93
+ ### Run application
94
+
95
+ ---
96
+
97
+ ```
98
+ npm run dev
99
+ ```
100
+
101
+ Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
102
+
103
+ ### Lint application
104
+
105
+ ```
106
+ npm run lint
107
+ ```
108
+
109
+ ### Prettify code
110
+
111
+ ```
112
+ npm run prettier
113
+ ```
114
+
115
+ ### Build application
116
+
117
+ ```
118
+ npm run build
119
+ ```
120
+
121
+ ### Preview application
122
+
123
+ ```
124
+ npm run preview
125
+ ```
126
+
127
+ Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
128
+
129
+ # Learning Resources
130
+
131
+ ## Tailwind CSS
132
+
133
+ To learn more about `Tailwind CSS`, take a look at the following resources:
134
+
135
+ - [Tailwind ](https://tailwindcss.com) - Tailwind documentation and related resources.
136
+
137
+ ## Framer Motion
138
+
139
+ To learn more about `Framer Motion`, take a look at the following resources:
140
+
141
+ - [Framer Motion](https://www.framer.com/motion) - Framer Motion documentation and related resources.
142
+
143
+ ## React Query
144
+
145
+ To learn more about `TanStack Query`, take a look at the following resources:
146
+
147
+ - [TanStack Query](https://tanstack.com/query/latest) - React Query documentation and related resources.
148
+
149
+ ## Nuqs
150
+
151
+ To learn more about `Nuqs`, take a look at the following resources:
152
+
153
+ - [Nuqs](https://nuqs.dev/) - React Query documentation and related resources.
154
+
155
+ ## Philosophy
156
+
157
+ > **Architecture is a product feature.**
158
+
159
+ This scaffold exists to reduce decision fatigue, prevent architectural erosion, and help teams move faster with confidence.
160
+
161
+ ---
162
+
163
+ ## Ownership
164
+
165
+ This scaffold is maintained internally and should be treated as a **living standard**. Changes must be intentional, documented, and agreed upon by the frontend team.
@@ -0,0 +1,7 @@
1
+ # Scaffold Guides
2
+
3
+ ## Codebase Modules (source of truth)
4
+
5
+ - [app](../src/app/app.readme.md)
6
+ - [shared](../src/shared/shared.readme.md)
7
+ - [features](../src/features/features.readme.md)
@@ -2,15 +2,15 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
- <link rel="icon" type="image/svg+xml" href="/vite.svg" />
5
+ <link rel="icon" type="image/svg+xml" href="/public/icons/react.svg" />
6
6
  <meta
7
7
  name="viewport"
8
8
  content="width=device-width, initial-scale=1, viewport-fit=cover, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
9
9
  />
10
- <title>vite-project</title>
10
+ <title>React Scaffold App</title>
11
11
  </head>
12
12
  <body>
13
13
  <div id="root"></div>
14
- <script type="module" src="/src/main.jsx"></script>
14
+ <script type="module" src="/src/app/main.jsx"></script>
15
15
  </body>
16
16
  </html>
@@ -32,12 +32,14 @@
32
32
  "@tanstack/react-query-devtools": "^5.91.2",
33
33
  "axios": "^1.13.2",
34
34
  "clsx": "^2.1.1",
35
+ "framer-motion": "^12.26.2",
35
36
  "nuqs": "^2.8.6",
36
37
  "postcss": "^8.5.6",
37
38
  "react": "^19.2.0",
38
39
  "react-dom": "^19.2.0",
39
40
  "react-hot-toast": "^2.6.0",
40
41
  "react-icons": "^5.5.0",
42
+ "react-router-dom": "^7.12.0",
41
43
  "tailwind-merge": "^3.4.0",
42
44
  "tailwindcss": "^4.1.18"
43
45
  },
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 32 32"><g transform="matrix(.05696 0 0 .05696 .647744 2.43826)" fill="none" fill-rule="evenodd"><circle r="50.167" cy="237.628" cx="269.529" fill="#00d8ff"/><g stroke="#00d8ff" stroke-width="24"><path d="M269.53 135.628c67.356 0 129.928 9.665 177.107 25.907 56.844 19.57 91.794 49.233 91.794 76.093 0 27.99-37.04 59.503-98.083 79.728-46.15 15.29-106.88 23.272-170.818 23.272-65.554 0-127.63-7.492-174.3-23.44-59.046-20.182-94.61-52.103-94.61-79.56 0-26.642 33.37-56.076 89.415-75.616 47.355-16.51 111.472-26.384 179.486-26.384z"/><path d="M180.736 186.922c33.65-58.348 73.28-107.724 110.92-140.48C337.006 6.976 380.163-8.48 403.43 4.937c24.248 13.983 33.042 61.814 20.067 124.796-9.8 47.618-33.234 104.212-65.176 159.6-32.75 56.788-70.25 106.82-107.377 139.272-46.98 41.068-92.4 55.93-116.185 42.213-23.08-13.3-31.906-56.92-20.834-115.233 9.355-49.27 32.832-109.745 66.8-168.664z"/><path d="M180.82 289.482C147.075 231.2 124.1 172.195 114.51 123.227c-11.544-59-3.382-104.11 19.864-117.566 24.224-14.024 70.055 2.244 118.14 44.94 36.356 32.28 73.688 80.837 105.723 136.173 32.844 56.733 57.46 114.21 67.036 162.582 12.117 61.213 2.31 107.984-21.453 121.74-23.057 13.348-65.25-.784-110.24-39.5-38.013-32.71-78.682-83.253-112.76-142.115z"/></g></g></svg>
@@ -1,13 +1,15 @@
1
- import { Toaster } from '@/shared/ui';
1
+ import { Scrollable, Toaster } from '@/shared/ui';
2
2
  import { QueryProvider } from './providers';
3
+ import { Router } from './Router';
4
+ import { memo } from '@/shared/utils';
3
5
 
4
- function App({ children }) {
6
+ export const App = memo(() => {
5
7
  return (
6
8
  <QueryProvider>
7
- {children}
9
+ <Scrollable>
10
+ <Router />
11
+ </Scrollable>
8
12
  <Toaster />
9
13
  </QueryProvider>
10
14
  );
11
- }
12
-
13
- export default App;
15
+ });
@@ -1,4 +1,44 @@
1
+ import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
2
+ import { featureRoutes } from './routes.registry';
3
+ import { AuthMiddleware } from './middlewares';
4
+ import { Layouts } from '@/shared/constants';
5
+ import { memo } from '@/shared/utils';
6
+ import React from 'react';
1
7
 
2
- export const Router = () => {
3
- return <div className="">{authRoute}</div>;
8
+ const layoutMap = {
9
+ dashboard: null,
10
+ none: ({ children }) => children,
4
11
  };
12
+
13
+ export const Router = memo(() => {
14
+ return (
15
+ <BrowserRouter>
16
+ <Routes>
17
+ {featureRoutes.map((route, index) => {
18
+ const Layout = layoutMap[route.layout ?? Layouts.None];
19
+ const Page = route.element;
20
+
21
+ let element = (
22
+ <Layout>
23
+ <Page />
24
+ </Layout>
25
+ );
26
+
27
+ if (route.protected) {
28
+ element = (
29
+ <AuthMiddleware>
30
+ <Layout>
31
+ <Page />
32
+ </Layout>
33
+ </AuthMiddleware>
34
+ );
35
+ }
36
+
37
+ return <Route key={index} path={route.path} element={element} />;
38
+ })}
39
+
40
+ <Route path="/*" element={<Navigate to="/welcome" replace />} />
41
+ </Routes>
42
+ </BrowserRouter>
43
+ );
44
+ });
@@ -1 +1,37 @@
1
1
  @import 'tailwindcss';
2
+ @import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,200..800;1,200..800&display=swap');
3
+ @import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap');
4
+
5
+ @theme {
6
+ /* Colors */
7
+ --color-primary: #3452ff;
8
+ --color-primary-foreground: #ffffff;
9
+ --color-badge: #fef1a7;
10
+
11
+ --color-bg: #ffffff;
12
+ --color-surface: #f7f7f7;
13
+
14
+ --color-ink: #03003e;
15
+ --color-muted: #797979;
16
+
17
+ --color-ring: color-mix(in oklab, var(--color-primary) 35%, transparent);
18
+ --color-border: color-mix(in oklab, var(--color-ink) 10%, transparent);
19
+ --color-soft: color-mix(in oklab, var(--color-primary) 10%, transparent);
20
+
21
+ /* Fonts */
22
+ --font-plus-jakarta-sans:
23
+ 'Plus Jakarta Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Arial, sans-serif;
24
+ --font-inter: 'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Arial, sans-serif;
25
+ }
26
+
27
+ :root {
28
+ color: var(--color-ink);
29
+ background: var(--color-bg);
30
+ text-rendering: optimizeLegibility;
31
+ -webkit-font-smoothing: antialiased;
32
+ -moz-osx-font-smoothing: grayscale;
33
+ }
34
+
35
+ body {
36
+ font-family: var(--font-inter);
37
+ }
@@ -1,7 +1,7 @@
1
1
  import { StrictMode } from 'react';
2
2
  import { createRoot } from 'react-dom/client';
3
- import App from './App.jsx';
4
3
  import './index.css';
4
+ import { App } from './App';
5
5
 
6
6
  createRoot(document.getElementById('root')).render(
7
7
  <StrictMode>
@@ -0,0 +1,5 @@
1
+ import { memo } from '@/shared/utils';
2
+
3
+ export const AuthMiddleware = memo(({ children }) => {
4
+ return children;
5
+ });
@@ -0,0 +1 @@
1
+ export { AuthMiddleware } from './AuthMiddleware';
@@ -1,8 +1,8 @@
1
1
  import React from 'react';
2
- import { MutationCache, QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query';
3
2
  import { AxiosError } from 'axios';
4
3
  import toast from 'react-hot-toast';
5
4
  import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
5
+ import { MutationCache, QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query';
6
6
 
7
7
  export const QueryProvider = ({ children }) => {
8
8
  const [queryCache] = React.useState(
@@ -0,0 +1,8 @@
1
+ import { sampleRoutes } from '@/features/sample';
2
+ import { welcomeRoutes } from '@/features/welcome';
3
+
4
+ export const featureRoutes = [
5
+ ...sampleRoutes,
6
+ ...welcomeRoutes,
7
+ // ...anotherFeatureRoutes
8
+ ];
@@ -0,0 +1,3 @@
1
+ export { SampleAssets } from './sample.assets';
2
+ export { SampleNavigation, SampleSearchParamsKey } from './sample.navigations';
3
+ export { sampleQueryKey } from './sample.queryKeys';
@@ -0,0 +1,8 @@
1
+ export const SampleAssets = {
2
+ Images: {
3
+ Sample: './assets/images/',
4
+ },
5
+ Icons: {
6
+ Sample: './assets/icons/',
7
+ },
8
+ };
@@ -0,0 +1,5 @@
1
+ export const SampleNavigation = {
2
+ Sample: '/sample',
3
+ };
4
+
5
+ export const SampleSearchParamsKey = {};
@@ -0,0 +1,3 @@
1
+ export const sampleQueryKey = {
2
+ all: ['sample'],
3
+ };
@@ -0,0 +1 @@
1
+ export { sampleRoutes } from './sample.routes';
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import { memo } from '@/shared/utils';
3
+
4
+ const SamplePage = memo(() => {
5
+ return <div>SamplePage</div>;
6
+ });
7
+
8
+ export default SamplePage;
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+
3
+ export const SamplePage = React.lazy(() => import('./SamplePage'));
@@ -0,0 +1,12 @@
1
+ import { Layouts } from '@/shared/constants';
2
+ import { SamplePage } from './pages';
3
+ import { SampleNavigation } from './constants';
4
+
5
+ export const sampleRoutes = [
6
+ {
7
+ path: SampleNavigation.Sample,
8
+ element: SamplePage,
9
+ protected: true,
10
+ layout: Layouts.None,
11
+ },
12
+ ];
@@ -0,0 +1,54 @@
1
+ import React from 'react';
2
+ import { PiCopySimpleBold, PiCheckBold } from 'react-icons/pi';
3
+ import { Flex } from '@/shared/ui/Flex';
4
+ import { Text } from '@/shared/ui/Text';
5
+ import { useToggleState } from '@/shared/hooks';
6
+ import { Box, FlexItem } from '@/shared/ui';
7
+ import { cn } from '@/shared/libs';
8
+
9
+ export const CodeLine = React.memo(({ value, isStorySection }) => {
10
+ const [copied, toggleCopied] = useToggleState();
11
+
12
+ const onCopy = React.useCallback(async () => {
13
+ try {
14
+ await navigator.clipboard.writeText(value);
15
+ } catch {
16
+ const ta = document.createElement('textarea');
17
+ ta.value = value;
18
+ document.body.appendChild(ta);
19
+ ta.select();
20
+ document.execCommand('copy');
21
+ document.body.removeChild(ta);
22
+ }
23
+ toggleCopied();
24
+ window.setTimeout(() => toggleCopied(), 1200);
25
+ }, [toggleCopied]);
26
+
27
+ return (
28
+ <Box
29
+ radius="xl"
30
+ padding={{ x: 4, y: 2 }}
31
+ className={cn('ring-1 ring-border/50', isStorySection ? 'bg-surface' : 'bg-bg/80 ')}
32
+ >
33
+ <Flex gap={3} align="center" justify="between">
34
+ <FlexItem className="min-w-0">
35
+ <Text size="sm" color="ink" truncate>
36
+ {value}
37
+ </Text>
38
+ </FlexItem>
39
+
40
+ <FlexItem>
41
+ <button
42
+ type="button"
43
+ onClick={onCopy}
44
+ className="shrink-0 inline-flex size-9 items-center justify-center rounded-lg bg-black/5 text-ink hover:bg-black/10 transition-colors cursor-pointer"
45
+ aria-label={copied ? 'Copied' : 'Copy command'}
46
+ title={copied ? 'Copied' : 'Copy'}
47
+ >
48
+ {copied ? <PiCheckBold className="size-4" /> : <PiCopySimpleBold className="size-4" />}
49
+ </button>
50
+ </FlexItem>
51
+ </Flex>
52
+ </Box>
53
+ );
54
+ });
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ import { cn } from '@/shared/libs';
3
+ import { Box } from '@/shared/ui';
4
+
5
+ export const Divider = React.memo(({ className = '' }) => {
6
+ return <Box className={cn('h-px w-full bg-border/70', className)} />;
7
+ });
@@ -0,0 +1,78 @@
1
+ import React from 'react';
2
+ import { Box, Flex, FlexItem, Text } from '@/shared/ui';
3
+ import { Links } from '../constants';
4
+ import { Divider } from './Divider';
5
+
6
+ export const Footer = React.memo(() => {
7
+ return (
8
+ <Box className="mt-12 pt-8">
9
+ <Divider className="mb-6" />
10
+
11
+ <Flex
12
+ gap={4}
13
+ align={{ md: 'center' }}
14
+ justify={{ md: 'between' }}
15
+ direction={{ base: 'column', md: 'row' }}
16
+ >
17
+ <FlexItem>
18
+ <Text size="sm" color="muted">
19
+ <span className="font-semibold text-ink">React Scaffold</span>
20
+ <>
21
+ <span className="mx-2">•</span>
22
+ Created by{' '}
23
+ <a
24
+ href={Links.CreatorGithub}
25
+ target="_blank"
26
+ rel="noreferrer"
27
+ className="font-semibold text-ink hover:text-primary transition-colors"
28
+ >
29
+ {Links.CreatorName}
30
+ </a>
31
+ </>
32
+ </Text>
33
+ </FlexItem>
34
+
35
+ <FlexItem>
36
+ <Flex gap={5} className="flex-wrap text-sm font-medium">
37
+ <a
38
+ className="text-muted hover:text-primary"
39
+ href={Links.AppGithub}
40
+ target="_blank"
41
+ rel="noreferrer"
42
+ >
43
+ Project Repo
44
+ </a>
45
+ <a
46
+ className="text-muted hover:text-primary"
47
+ href={Links.CliGithub}
48
+ target="_blank"
49
+ rel="noreferrer"
50
+ >
51
+ CLI Repo
52
+ </a>
53
+ <a
54
+ className="text-muted hover:text-primary"
55
+ href={Links.NpmPackage}
56
+ target="_blank"
57
+ rel="noreferrer"
58
+ >
59
+ NPM Package
60
+ </a>
61
+
62
+ <>
63
+ <span className="text-border">|</span>
64
+ <a
65
+ className="text-muted hover:text-primary"
66
+ href={Links.CreatorLinkedIn}
67
+ target="_blank"
68
+ rel="noreferrer"
69
+ >
70
+ Creator
71
+ </a>
72
+ </>
73
+ </Flex>
74
+ </FlexItem>
75
+ </Flex>
76
+ </Box>
77
+ );
78
+ });