@aditokmo/react-setup-cli 0.1.2 → 0.1.4

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.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@aditokmo/react-setup-cli?color=blue)](https://www.npmjs.com/package/@aditokmo/react-setup-cli)
4
4
 
5
- A React CLI built with Vite that helps you build and structure your projects in seconds. It eliminates manual setup by configuring your favorite tools into a **clean, modular architecture** automatically.
5
+ A React CLI built on top of Vite that helps you build and structure projects in seconds. It eliminates manual setup by configuring your favorite tools into a **clean, modular architecture** automatically.
6
6
 
7
7
  **Note:** This package is a CLI tool. Do not install it with `npm i`. Instead check `Quick Start` down below.
8
8
 
@@ -42,9 +42,7 @@ yarn dlx @aditokmo/react-setup-cli
42
42
 
43
43
  ---
44
44
 
45
- ## Decisions About Arhitecture
46
-
47
- This CLI isn't just a downloader it is architectural choice designed to enforce professional standards.
45
+ ## About Arhitecture
48
46
 
49
47
  ### Feature-Based Structure
50
48
 
@@ -64,14 +62,14 @@ TanStack Query is integrated to handle server-state management. It is optional,
64
62
 
65
63
  ### Styling
66
64
 
67
- You can choose between CSS, SCSS (soon), or Tailwind CSS. While I personally recommend Tailwind for modern and faster development, the CLI ensures that regardless of your choice, the project is configured with a global styles directory and a consistent entry point. If you select TailwindCSS you will also have option to use Shadcn/UI, and with that you will have option to choose components that you want to install instead of doing it manually.
65
+ You can choose between CSS, SCSS, or Tailwind CSS. While I personally recommend Tailwind for modern and faster development, the CLI ensures that regardless of your choice, the project is configured with a global styles directory and a consistent entry point. If you select TailwindCSS you will also have option to use Shadcn/UI, and with that you will have option to choose components that you want to install instead of doing it manually.
68
66
 
69
67
  ### Routing
70
68
 
71
- The CLI offers two powerful options: React Router and TanStack Router.
69
+ The CLI offers two options: React Router and TanStack Router.
72
70
 
73
- - **React Router** is the industry standard that most developers are familiar with.
74
- - **TanStack Router** is included for those who want a fully type-safe routing experience with built-in data loading capabilities. Whichever you choose, the CLI doesn't just install the library it will generate a `routes/` directory system to help you easily separate your public pages from protected pages.
71
+ - React Router is the industry standard that most developers are familiar with.
72
+ - TanStack Router is included for those who want a fully type-safe routing experience with built-in data loading capabilities. Whichever you choose, the CLI doesn't just install the library it will generate a `routes/` directory system to help you easily separate your public pages from protected pages.
75
73
 
76
74
  ### Package Manager Detection
77
75
 
package/dist/index.js CHANGED
@@ -3,11 +3,27 @@ import fs from 'fs-extra';
3
3
  import path from 'path';
4
4
  import { execSync } from 'child_process';
5
5
  import { askQuestions } from './questions.js';
6
- import { copyTemplate, patchViteConfig, finalizeViteConfig, patchAppFile, finalizeAppFile, detectPackageManager } from './utils.js';
6
+ import { copyTemplate, patchViteConfig, finalizeViteConfig, patchAppFile, finalizeAppFile, detectPackageManager, patchPackageJsonFile, patchFileContent } from './utils.js';
7
7
  import { collectDependencies } from './installers.js';
8
8
  import { fileURLToPath } from 'url';
9
+ import { FONT_QUERIES } from './mapper.js';
9
10
  const __filename = fileURLToPath(import.meta.url);
10
11
  const __dirname = path.dirname(__filename);
12
+ const baseDeps = ['react', 'react-dom'];
13
+ const baseDevDeps = [
14
+ 'vite',
15
+ 'typescript',
16
+ '@types/node',
17
+ '@types/react',
18
+ '@types/react-dom',
19
+ '@vitejs/plugin-react-swc',
20
+ 'eslint',
21
+ '@eslint/js',
22
+ 'eslint-plugin-react-hooks',
23
+ 'eslint-plugin-react-refresh',
24
+ 'globals',
25
+ 'typescript-eslint'
26
+ ];
11
27
  async function main() {
12
28
  console.log('⚛️ Welcome to React CLI Setup by github/aditokmo');
13
29
  let projectDir = '';
@@ -19,9 +35,12 @@ async function main() {
19
35
  projectDir = path.join(process.cwd(), answers.projectName);
20
36
  const templateRoot = path.join(__dirname, '../templates');
21
37
  const appFilePath = path.join(projectDir, 'src', 'App.tsx');
38
+ const indexHTMLPath = path.join(projectDir, 'index.html');
39
+ const notFoundPath = path.join(projectDir, 'src/modules/common/pages/NotFound.tsx');
22
40
  const viteConfigPath = path.join(projectDir, 'vite.config.ts');
23
41
  fs.ensureDirSync(projectDir);
24
42
  copyTemplate(path.join(templateRoot, 'base'), projectDir);
43
+ patchPackageJsonFile(path.join(projectDir, 'package.json'), answers.projectName);
25
44
  // Styles
26
45
  if (answers.style === 'tailwind') {
27
46
  copyTemplate(path.join(templateRoot, 'styles', 'tailwind', 'src'), path.join(projectDir, 'src/styles'));
@@ -30,6 +49,26 @@ async function main() {
30
49
  if (answers.style === 'css') {
31
50
  copyTemplate(path.join(templateRoot, 'styles', 'css', 'src'), path.join(projectDir, 'src/styles'));
32
51
  }
52
+ if (answers.style === 'scss') {
53
+ copyTemplate(path.join(templateRoot, 'styles', 'scss', 'src'), path.join(projectDir, 'src/styles'));
54
+ patchFileContent(notFoundPath, "import '@/styles/404.css'", "import '@/styles/404.scss'");
55
+ patchFileContent(indexHTMLPath, '<link rel="stylesheet" href="./src/styles/main.css" />', '<link rel="stylesheet" href="./src/styles/main.scss" />');
56
+ }
57
+ // Fonts
58
+ if (answers?.fonts && answers.fonts.length > 0) {
59
+ const fontString = answers.fonts
60
+ .map(name => FONT_QUERIES[name])
61
+ .join('&');
62
+ const url = `https://fonts.googleapis.com/css2?${fontString}&display=swap`;
63
+ const content = `
64
+ <link rel="preconnect" href="https://fonts.googleapis.com">
65
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
66
+ <link href="${url}" rel="stylesheet">`;
67
+ patchFileContent(indexHTMLPath, '<!-- [HEAD_LINK_IMPORT] -->', content);
68
+ }
69
+ else {
70
+ patchFileContent(indexHTMLPath, '<!-- [HEAD_LINK_IMPORT] -->', '');
71
+ }
33
72
  // State Management
34
73
  if (answers.reactQuery) {
35
74
  copyTemplate(path.join(templateRoot, 'state', 'react-query', 'src', 'provider'), path.join(projectDir, 'src/providers'));
@@ -55,16 +94,18 @@ async function main() {
55
94
  finalizeAppFile(appFilePath);
56
95
  finalizeViteConfig(viteConfigPath);
57
96
  const { dependency, devDependency, cmd } = collectDependencies(answers, packageManager);
97
+ const allDeps = [...baseDeps, ...dependency];
98
+ const allDevDeps = [...baseDevDeps, ...devDependency];
58
99
  process.chdir(projectDir);
59
100
  console.log(`📦 Initializing ${packageManager} project...`);
60
101
  execSync(`${packageManager} install`, { stdio: 'inherit' });
61
- if (dependency.length) {
102
+ if (allDeps.length) {
62
103
  console.log('📦 Installing dependencies...');
63
- execSync(`${packageManager} ${installAction} ${dependency.join(' ')}`, { stdio: 'inherit' });
104
+ execSync(`${packageManager} ${installAction} ${allDeps.join(' ')}`, { stdio: 'inherit' });
64
105
  }
65
- if (devDependency.length) {
106
+ if (allDevDeps.length) {
66
107
  console.log('📦 Installing dev dependencies...');
67
- execSync(`${packageManager} ${installAction} -D ${devDependency.join(' ')}`, { stdio: 'inherit' });
108
+ execSync(`${packageManager} ${installAction} -D ${allDevDeps.join(' ')}`, { stdio: 'inherit' });
68
109
  }
69
110
  // Extra cmds like shadcn
70
111
  for (const command of cmd) {
package/dist/mapper.js CHANGED
@@ -1,10 +1,11 @@
1
- import { fontAwesomeIconsInstaller, phosphorIconsInstaller, reactFormHookInstaller, reactHotToastInstaller, reactIconsInstaller, reactQueryInstaller, reactRouterInstaller, reactToastifyInstaller, sonnerInstaller, tailwindInstaller, tanstackFormInstaller, tanstackRouterInstaller, yupInstaller, zodInstaller, zustandInstaller } from './packages.js';
1
+ import { fontAwesomeIconsInstaller, phosphorIconsInstaller, reactFormHookInstaller, reactHotToastInstaller, reactIconsInstaller, reactQueryInstaller, reactRouterInstaller, reactToastifyInstaller, scssInstaller, sonnerInstaller, tailwindInstaller, tanstackFormInstaller, tanstackRouterInstaller, yupInstaller, zodInstaller, zustandInstaller } from './packages.js';
2
2
  export const installers = {
3
3
  'reactQuery': reactQueryInstaller,
4
4
  'zustand': zustandInstaller,
5
5
  'react-router': reactRouterInstaller,
6
6
  'tanstack-router': tanstackRouterInstaller,
7
7
  'tailwind': tailwindInstaller,
8
+ 'scss': scssInstaller,
8
9
  'react-icons': reactIconsInstaller,
9
10
  'font-awesome': fontAwesomeIconsInstaller,
10
11
  'phosphor-icons': phosphorIconsInstaller,
@@ -16,3 +17,12 @@ export const installers = {
16
17
  'zod': zodInstaller,
17
18
  'yup': yupInstaller,
18
19
  };
20
+ export const FONT_QUERIES = {
21
+ "geist": "family=Geist:wght@100..900",
22
+ "inter": "family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900",
23
+ "lato": "family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900",
24
+ "montserrat": "family=Montserrat:ital,wght@0,100..900;1,100..900",
25
+ "open-sans": "family=Open+Sans:ital,wght@0,300..800;1,300..800",
26
+ "poppins": "family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900",
27
+ "roboto": "family=Roboto:ital,wght@0,100..900;1,100..900"
28
+ };
package/dist/packages.js CHANGED
@@ -11,6 +11,9 @@ export const zustandInstaller = {
11
11
  export const tailwindInstaller = {
12
12
  dependency: ['tailwindcss', '@tailwindcss/vite'],
13
13
  };
14
+ export const scssInstaller = {
15
+ devDependency: ['sass']
16
+ };
14
17
  // Icons
15
18
  export const reactIconsInstaller = {
16
19
  dependency: ['react-icons'],
package/dist/questions.js CHANGED
@@ -4,13 +4,13 @@ export async function askQuestions() {
4
4
  projectName: () => text({
5
5
  message: 'Project name:',
6
6
  placeholder: 'my-app',
7
- validate: (value) => (value.length < 0 ? 'Project name is required' : undefined),
8
7
  }),
9
8
  style: () => select({
10
9
  message: 'Choose styling:',
11
10
  options: [
12
11
  { value: 'tailwind', label: 'Tailwind' },
13
12
  { value: 'css', label: 'CSS' },
13
+ { value: 'scss', label: 'SCSS' },
14
14
  ],
15
15
  }),
16
16
  shadcn: ({ results }) => {
@@ -38,6 +38,19 @@ export async function askQuestions() {
38
38
  required: false,
39
39
  });
40
40
  },
41
+ fonts: () => multiselect({
42
+ message: 'Use space to select your fonts',
43
+ options: [
44
+ { value: 'geist', label: 'Geist' },
45
+ { value: 'inter', label: 'Inter' },
46
+ { value: 'lato', label: 'Lato' },
47
+ { value: 'montserrat', label: 'Montserrat' },
48
+ { value: 'open-sans', label: 'Open Sans' },
49
+ { value: 'poppins', label: 'Poppins' },
50
+ { value: 'roboto', label: 'Roboto' }
51
+ ],
52
+ required: false
53
+ }),
41
54
  icons: () => select({
42
55
  message: 'Choose icon library:',
43
56
  options: [
@@ -95,13 +108,16 @@ export async function askQuestions() {
95
108
  isCancel(results.shadcnComponents) ||
96
109
  isCancel(results.icons) ||
97
110
  isCancel(results.toast) ||
98
- isCancel(results.reactQuery)) {
111
+ isCancel(results.reactQuery) ||
112
+ isCancel(results.fonts)) {
99
113
  cancel('Setup cancelled.');
100
114
  process.exit(0);
101
115
  }
102
116
  return {
103
117
  ...results,
118
+ projectName: results.projectName || 'my-app',
104
119
  shadcn: results.shadcn,
105
120
  shadcnComponents: (results.shadcnComponents ?? []),
121
+ fonts: (results.fonts ?? [])
106
122
  };
107
123
  }
package/dist/utils.js CHANGED
@@ -1,11 +1,18 @@
1
1
  import fs from 'fs-extra';
2
2
  export function copyTemplate(src, path) {
3
3
  if (!fs.existsSync(src)) {
4
- console.warn(`⚠️ Template folder ${src} dosn't exist`);
4
+ console.warn(`⚠️ Template folder ${src} doesn't exist`);
5
5
  return;
6
6
  }
7
7
  fs.copySync(src, path, { overwrite: true });
8
8
  }
9
+ export function patchFileContent(filePath, searchValue, replaceValue) {
10
+ if (!fs.existsSync(filePath))
11
+ return;
12
+ const content = fs.readFileSync(filePath, 'utf-8');
13
+ const newContent = content.replace(searchValue, replaceValue);
14
+ fs.writeFileSync(filePath, newContent);
15
+ }
9
16
  export function patchAppFile(filePath, importLine, openTag, closeTag) {
10
17
  if (!fs.existsSync(filePath))
11
18
  return;
@@ -28,6 +35,18 @@ export function patchAppFile(filePath, importLine, openTag, closeTag) {
28
35
  }
29
36
  fs.writeFileSync(filePath, content);
30
37
  }
38
+ export function patchPackageJsonFile(filePath, projectName) {
39
+ if (!fs.existsSync(filePath))
40
+ return;
41
+ try {
42
+ const pkg = fs.readJsonSync(filePath);
43
+ pkg.name = projectName;
44
+ fs.writeJsonSync(filePath, pkg, { spaces: 2 });
45
+ }
46
+ catch (error) {
47
+ console.error('⚠️ Could not update package.json name');
48
+ }
49
+ }
31
50
  export function patchViteConfig(filePath, beforeReactPlugin, importLine, pluginLine) {
32
51
  if (!fs.existsSync(filePath))
33
52
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aditokmo/react-setup-cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "A fast React CLI to jumpstart your projects. It sets up your libraries and organizes a scalable folder structure so you can skip the configuration and go straight to coding.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -1,4 +1,4 @@
1
- <!DOCTYPE html>
1
+ <!doctype html>
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
@@ -6,6 +6,7 @@
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <link rel="stylesheet" href="./src/styles/main.css" />
8
8
  <title>vite-project</title>
9
+ <!-- [HEAD_LINK_IMPORT] -->
9
10
  </head>
10
11
  <body>
11
12
  <div id="root"></div>
@@ -1,7 +1,7 @@
1
1
  {
2
- "name": "vite-project",
2
+ "name": "{{PROJECT_NAME}}",
3
3
  "private": true,
4
- "version": "0.0.0",
4
+ "version": "0.1.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "dev": "vite",
@@ -9,22 +9,6 @@
9
9
  "lint": "eslint .",
10
10
  "preview": "vite preview"
11
11
  },
12
- "dependencies": {
13
- "react": "^19.2.0",
14
- "react-dom": "^19.2.0"
15
- },
16
- "devDependencies": {
17
- "@eslint/js": "^9.39.1",
18
- "@types/node": "^24.10.1",
19
- "@types/react": "^19.2.5",
20
- "@types/react-dom": "^19.2.3",
21
- "@vitejs/plugin-react-swc": "^4.2.2",
22
- "eslint": "^9.39.1",
23
- "eslint-plugin-react-hooks": "^7.0.1",
24
- "eslint-plugin-react-refresh": "^0.4.24",
25
- "globals": "^16.5.0",
26
- "typescript": "~5.9.3",
27
- "typescript-eslint": "^8.46.4",
28
- "vite": "^7.2.4"
29
- }
12
+ "dependencies": {},
13
+ "devDependencies": {}
30
14
  }
@@ -0,0 +1,101 @@
1
+ @import "./variables.css";
2
+
3
+ .not-found-wrapper {
4
+ display: flex;
5
+ flex-direction: column;
6
+ align-items: center;
7
+ justify-content: center;
8
+ min-height: 100vh;
9
+ padding: 20px;
10
+ text-align: center;
11
+ font-family: var(--font-main);
12
+ background: var(--bg-light);
13
+ }
14
+
15
+ .not-found-content {
16
+ padding: 100px 70px;
17
+ background: var(--white);
18
+ max-width: 700px;
19
+ width: 100%;
20
+ display: flex;
21
+ flex-direction: column;
22
+ justify-content: center;
23
+ align-items: center;
24
+ border-radius: var(--radius-lg);
25
+ box-shadow: var(--shadow-main);
26
+ }
27
+
28
+ .not-found-content span {
29
+ color: var(--text-muted);
30
+ font-weight: 700;
31
+ font-size: 6rem;
32
+ line-height: 1;
33
+ }
34
+
35
+ .not-found-content h1 {
36
+ font-size: 2.5rem;
37
+ font-weight: 600;
38
+ margin: 0;
39
+ color: var(--text-dark);
40
+ }
41
+
42
+ .not-found-content p {
43
+ font-size: 1rem;
44
+ color: var(--text-muted);
45
+ max-width: 360px;
46
+ width: 100%;
47
+ margin: 10px 0;
48
+ }
49
+
50
+ .not-found-content a {
51
+ font-size: 0.9rem;
52
+ color: var(--white);
53
+ background-color: var(--primary);
54
+ padding: 10px 30px;
55
+ margin-top: 20px;
56
+ border-radius: var(--radius-sm);
57
+ transition: 0.2s;
58
+ cursor: pointer;
59
+ text-decoration: none;
60
+ display: inline-block;
61
+ }
62
+
63
+ .not-found-content a:hover {
64
+ background: var(--primary-hover);
65
+ }
66
+
67
+ @media (max-width: 768px) {
68
+ .not-found-content {
69
+ padding: 60px 40px;
70
+ max-width: 90%;
71
+ }
72
+ .not-found-content span {
73
+ font-size: 4.5rem;
74
+ margin-bottom: 20px;
75
+ }
76
+ .not-found-content h1 {
77
+ font-size: 2rem;
78
+ }
79
+ }
80
+
81
+ @media (max-width: 480px) {
82
+ .not-found-content {
83
+ padding: 40px 20px;
84
+ border-radius: var(--radius-md);
85
+ }
86
+ .not-found-content span {
87
+ font-size: 3.5rem;
88
+ margin-bottom: 20px;
89
+ }
90
+ .not-found-content h1 {
91
+ font-size: 1.5rem;
92
+ }
93
+ .not-found-content p {
94
+ font-size: 0.9rem;
95
+ }
96
+ .not-found-content a {
97
+ width: 100%;
98
+ box-sizing: border-box;
99
+ text-align: center;
100
+ }
101
+ }
@@ -0,0 +1,70 @@
1
+ @import "./variables.css";
2
+
3
+ * {
4
+ margin: 0;
5
+ padding: 0;
6
+ box-sizing: border-box;
7
+ }
8
+
9
+ html {
10
+ scroll-behavior: smooth;
11
+ }
12
+
13
+ body {
14
+ font-family: var(--font-main);
15
+ background-color: var(--white);
16
+ color: var(--text-dark);
17
+ line-height: 1.6;
18
+ min-height: 100vh;
19
+ -webkit-font-smoothing: antialiased;
20
+ -moz-osx-font-smoothing: grayscale;
21
+ }
22
+
23
+ h1,
24
+ h2,
25
+ h3,
26
+ h4,
27
+ h5,
28
+ h6 {
29
+ color: var(--text-dark);
30
+ font-weight: 600;
31
+ line-height: 1.2;
32
+ }
33
+
34
+ p {
35
+ color: var(--text-muted);
36
+ }
37
+
38
+ .container {
39
+ width: 100%;
40
+ max-width: 1200px;
41
+ margin: 0 auto;
42
+ padding: 0 20px;
43
+ }
44
+
45
+ .flex-center {
46
+ display: flex;
47
+ align-items: center;
48
+ justify-content: center;
49
+ }
50
+
51
+ a {
52
+ text-decoration: none;
53
+ color: var(--primary);
54
+ transition: 0.3s ease;
55
+ }
56
+
57
+ a:hover {
58
+ color: var(--primary-hover);
59
+ }
60
+
61
+ button {
62
+ font-family: inherit;
63
+ cursor: pointer;
64
+ }
65
+
66
+ img {
67
+ max-width: 100%;
68
+ height: auto;
69
+ display: block;
70
+ }
@@ -0,0 +1,15 @@
1
+ :root {
2
+ --primary: #37538a;
3
+ --primary-hover: #263c67;
4
+ --bg-light: #e6edfc;
5
+ --white: #ffffff;
6
+ --text-dark: #111111;
7
+ --text-muted: #666666;
8
+
9
+ --font-main: "Arial", sans-serif;
10
+
11
+ --shadow-main: 0px 0px 14px -14px rgba(0, 0, 0, 0.75);
12
+ --radius-lg: 24px;
13
+ --radius-md: 16px;
14
+ --radius-sm: 10px;
15
+ }
@@ -0,0 +1,103 @@
1
+ @use "./index.scss" as *;
2
+
3
+ .not-found-wrapper {
4
+ display: flex;
5
+ flex-direction: column;
6
+ align-items: center;
7
+ justify-content: center;
8
+ min-height: 100vh;
9
+ padding: 20px;
10
+ text-align: center;
11
+ font-family: $font-main;
12
+ background: $bg-light;
13
+
14
+ .not-found-content {
15
+ padding: 100px 70px;
16
+ background: $white;
17
+ max-width: 700px;
18
+ width: 100%;
19
+ display: flex;
20
+ flex-direction: column;
21
+ justify-content: center;
22
+ align-items: center;
23
+ border-radius: $radius-lg;
24
+ box-shadow: $shadow-main;
25
+
26
+ span {
27
+ color: $text-muted;
28
+ font-weight: 700;
29
+ font-size: 6rem;
30
+ line-height: 1;
31
+ }
32
+
33
+ h1 {
34
+ font-size: 2.5rem;
35
+ font-weight: 600;
36
+ margin: 0;
37
+ color: $text-dark;
38
+ }
39
+
40
+ p {
41
+ font-size: 1rem;
42
+ color: $text-muted;
43
+ max-width: 360px;
44
+ width: 100%;
45
+ margin: 10px 0;
46
+ }
47
+
48
+ a {
49
+ font-size: 0.9rem;
50
+ color: $white;
51
+ background-color: $primary;
52
+ padding: 10px 30px;
53
+ margin-top: 20px;
54
+ border-radius: $radius-sm;
55
+ transition: 0.2s;
56
+ cursor: pointer;
57
+ text-decoration: none;
58
+ display: inline-block;
59
+
60
+ &:hover {
61
+ background: $primary-hover;
62
+ }
63
+ }
64
+
65
+ @media (max-width: 768px) {
66
+ padding: 60px 40px;
67
+ max-width: 90%;
68
+
69
+ span {
70
+ font-size: 4.5rem;
71
+ margin-bottom: 20px;
72
+ }
73
+
74
+ h1 {
75
+ font-size: 2rem;
76
+ }
77
+ }
78
+
79
+ @media (max-width: 480px) {
80
+ padding: 40px 20px;
81
+ border-radius: $radius-md;
82
+
83
+ span {
84
+ font-size: 3.5rem;
85
+ margin-bottom: 20px;
86
+ }
87
+
88
+ h1 {
89
+ font-size: 1.5rem;
90
+ }
91
+
92
+ p {
93
+ font-size: 0.9rem;
94
+ }
95
+
96
+ a {
97
+ width: 100%;
98
+ box-sizing: border-box;
99
+ text-align: center;
100
+ }
101
+ }
102
+ }
103
+ }
@@ -0,0 +1,2 @@
1
+ @forward "variables";
2
+ @forward "mixins";
@@ -0,0 +1,27 @@
1
+ $breakpoint-tablet: 768px;
2
+ $breakpoint-mobile: 480px;
3
+
4
+ @mixin tablet {
5
+ @media (max-width: #{$breakpoint-tablet}) {
6
+ @content;
7
+ }
8
+ }
9
+
10
+ @mixin mobile {
11
+ @media (max-width: #{$breakpoint-mobile}) {
12
+ @content;
13
+ }
14
+ }
15
+
16
+ // helpers
17
+ @mixin flex-center {
18
+ display: flex;
19
+ align-items: center;
20
+ justify-content: center;
21
+ }
22
+
23
+ @mixin text-truncate {
24
+ overflow: hidden;
25
+ text-overflow: ellipsis;
26
+ white-space: nowrap;
27
+ }
@@ -0,0 +1,28 @@
1
+ // Brand Colors
2
+ $primary: #37538a;
3
+ $primary-hover: #263c67;
4
+ $primary-dark: #263c67;
5
+ $accent: #e6edfc;
6
+
7
+ // Text
8
+ $text-dark: #111111;
9
+ $text-muted: #666666;
10
+
11
+ // Background
12
+ $bg-light: #e6edfc;
13
+
14
+ // Radius
15
+ $radius-sm: 10px;
16
+ $radius-md: 16px;
17
+ $radius-lg: 24px;
18
+
19
+ // Shadows
20
+ $shadow-main: 0px 0px 14px -14px rgba(0, 0, 0, 0.75);
21
+
22
+ // Neutrals
23
+ $white: #ffffff;
24
+ $black: #111111;
25
+ $gray: #666666;
26
+
27
+ // Fonts
28
+ $font-main: "Arial", sans-serif;
@@ -0,0 +1,20 @@
1
+ @use "index" as *;
2
+
3
+ * {
4
+ margin: 0;
5
+ padding: 0;
6
+ box-sizing: border-box;
7
+ }
8
+
9
+ body {
10
+ font-family: $font-main;
11
+ background-color: $white;
12
+ color: $black;
13
+ line-height: 1.5;
14
+ -webkit-font-smoothing: antialiased;
15
+ }
16
+
17
+ a {
18
+ text-decoration: none;
19
+ color: inherit;
20
+ }
@@ -6,7 +6,6 @@
6
6
  min-height: 100vh;
7
7
  padding: 20px;
8
8
  text-align: center;
9
- font-family: "Poppins", sans-serif;
10
9
  background: #e6edfc;
11
10
  }
12
11
 
@@ -24,7 +23,7 @@
24
23
  }
25
24
 
26
25
  .not-found-content span {
27
- color: #666;
26
+ color: #666666;
28
27
  font-weight: 700;
29
28
  font-size: 6rem;
30
29
  line-height: 1;
@@ -34,12 +33,12 @@
34
33
  font-size: 2.5rem;
35
34
  font-weight: 600;
36
35
  margin: 0;
37
- color: #111;
36
+ color: #111111;
38
37
  }
39
38
 
40
39
  .not-found-content p {
41
40
  font-size: 1rem;
42
- color: #666;
41
+ color: #666666;
43
42
  max-width: 360px;
44
43
  width: 100%;
45
44
  margin: 10px 0;
@@ -67,12 +66,10 @@
67
66
  padding: 60px 40px;
68
67
  max-width: 90%;
69
68
  }
70
-
71
69
  .not-found-content span {
72
70
  font-size: 4.5rem;
73
71
  margin-bottom: 20px;
74
72
  }
75
-
76
73
  .not-found-content h1 {
77
74
  font-size: 2rem;
78
75
  }
@@ -83,20 +80,16 @@
83
80
  padding: 40px 20px;
84
81
  border-radius: 16px;
85
82
  }
86
-
87
83
  .not-found-content span {
88
84
  font-size: 3.5rem;
89
85
  margin-bottom: 20px;
90
86
  }
91
-
92
87
  .not-found-content h1 {
93
88
  font-size: 1.5rem;
94
89
  }
95
-
96
90
  .not-found-content p {
97
91
  font-size: 0.9rem;
98
92
  }
99
-
100
93
  .not-found-content a {
101
94
  width: 100%;
102
95
  box-sizing: border-box;