@jodijonatan/jo-frontend 1.0.0
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/index.js +129 -0
- package/package.json +21 -0
- package/templates/landing-page/README.md +73 -0
- package/templates/landing-page/eslint.config.js +23 -0
- package/templates/landing-page/index.html +13 -0
- package/templates/landing-page/package.json +32 -0
- package/templates/landing-page/public/favicon.svg +1 -0
- package/templates/landing-page/public/icons.svg +24 -0
- package/templates/landing-page/src/App.css +184 -0
- package/templates/landing-page/src/App.tsx +136 -0
- package/templates/landing-page/src/assets/hero.png +0 -0
- package/templates/landing-page/src/assets/react.svg +1 -0
- package/templates/landing-page/src/assets/vite.svg +1 -0
- package/templates/landing-page/src/index.css +29 -0
- package/templates/landing-page/src/main.tsx +10 -0
- package/templates/landing-page/tsconfig.app.json +25 -0
- package/templates/landing-page/tsconfig.json +7 -0
- package/templates/landing-page/tsconfig.node.json +24 -0
- package/templates/landing-page/vite.config.ts +7 -0
- package/templates/minimal/README.md +73 -0
- package/templates/minimal/eslint.config.js +23 -0
- package/templates/minimal/index.html +13 -0
- package/templates/minimal/package.json +32 -0
- package/templates/minimal/public/favicon.svg +1 -0
- package/templates/minimal/public/icons.svg +24 -0
- package/templates/minimal/src/App.css +184 -0
- package/templates/minimal/src/App.tsx +35 -0
- package/templates/minimal/src/assets/hero.png +0 -0
- package/templates/minimal/src/assets/react.svg +1 -0
- package/templates/minimal/src/assets/vite.svg +1 -0
- package/templates/minimal/src/index.css +29 -0
- package/templates/minimal/src/main.tsx +10 -0
- package/templates/minimal/tsconfig.app.json +25 -0
- package/templates/minimal/tsconfig.json +7 -0
- package/templates/minimal/tsconfig.node.json +24 -0
- package/templates/minimal/vite.config.ts +7 -0
- package/templates/portfolio/README.md +73 -0
- package/templates/portfolio/eslint.config.js +23 -0
- package/templates/portfolio/index.html +13 -0
- package/templates/portfolio/package.json +32 -0
- package/templates/portfolio/public/favicon.svg +1 -0
- package/templates/portfolio/public/icons.svg +24 -0
- package/templates/portfolio/src/App.css +184 -0
- package/templates/portfolio/src/App.tsx +133 -0
- package/templates/portfolio/src/assets/hero.png +0 -0
- package/templates/portfolio/src/assets/react.svg +1 -0
- package/templates/portfolio/src/assets/vite.svg +1 -0
- package/templates/portfolio/src/index.css +29 -0
- package/templates/portfolio/src/main.tsx +10 -0
- package/templates/portfolio/tsconfig.app.json +25 -0
- package/templates/portfolio/tsconfig.json +7 -0
- package/templates/portfolio/tsconfig.node.json +24 -0
- package/templates/portfolio/vite.config.ts +7 -0
package/index.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from "commander";
|
|
4
|
+
import inquirer from "inquirer";
|
|
5
|
+
import fs from "fs-extra";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
8
|
+
import { execa } from "execa";
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
import ora from "ora";
|
|
11
|
+
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = path.dirname(__filename);
|
|
14
|
+
const program = new Command();
|
|
15
|
+
|
|
16
|
+
program
|
|
17
|
+
.name("create-jo-web")
|
|
18
|
+
.description("CLI untuk men-generate React + Tailwind website yang aesthetic")
|
|
19
|
+
.version("1.0.0");
|
|
20
|
+
|
|
21
|
+
program
|
|
22
|
+
.argument("[project-name]", "Nama project baru")
|
|
23
|
+
.action(async (projectName) => {
|
|
24
|
+
console.log(chalk.cyan.bold("\n✨ Welcome to Create JO Web ✨\n"));
|
|
25
|
+
|
|
26
|
+
// 1. Interactive Prompts
|
|
27
|
+
const answers = await inquirer.prompt([
|
|
28
|
+
{
|
|
29
|
+
type: "input",
|
|
30
|
+
name: "name",
|
|
31
|
+
message: "Masukkan nama project:",
|
|
32
|
+
default: projectName || "my-awesome-web",
|
|
33
|
+
when: !projectName,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
type: "list",
|
|
37
|
+
name: "template",
|
|
38
|
+
message: "Pilih template website:",
|
|
39
|
+
choices: [
|
|
40
|
+
{ name: "Minimalist (Base React + Vite + Tailwind)", value: "minimal" },
|
|
41
|
+
{ name: "Portfolio (Framer Motion + Dark Theme)", value: "portfolio" },
|
|
42
|
+
{ name: "Landing Page (Hero + Features + CTA)", value: "landing-page" },
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
const finalProjectName = projectName || answers.name;
|
|
48
|
+
const selectedTemplate = answers.template;
|
|
49
|
+
|
|
50
|
+
const targetDir = path.join(process.cwd(), finalProjectName);
|
|
51
|
+
const templateDir = path.join(__dirname, "templates", selectedTemplate);
|
|
52
|
+
|
|
53
|
+
if (fs.existsSync(targetDir)) {
|
|
54
|
+
console.error(chalk.red(`\n❌ Error: Folder "${targetDir}" sudah ada!`));
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
console.log(chalk.blue(`\n🚀 Membuat project "${finalProjectName}" menggunakan template "${selectedTemplate}"...\n`));
|
|
59
|
+
|
|
60
|
+
const spinner = ora("1. Meng-copy template...").start();
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
// 1. Copy template
|
|
64
|
+
await fs.copy(templateDir, targetDir, {
|
|
65
|
+
filter: (src) => !src.includes("node_modules") && !src.includes("package-lock.json"),
|
|
66
|
+
});
|
|
67
|
+
spinner.succeed("Template berhasil disalin!");
|
|
68
|
+
|
|
69
|
+
// Khusus NPM saat publish, file .gitignore diganti menjadi .npmignore.
|
|
70
|
+
// Kita kembalikan .npmignore menjadi .gitignore (jika ada) saat generate.
|
|
71
|
+
const npmIgnorePath = path.join(targetDir, ".npmignore");
|
|
72
|
+
const gitIgnorePath = path.join(targetDir, ".gitignore");
|
|
73
|
+
if (fs.existsSync(npmIgnorePath)) {
|
|
74
|
+
await fs.rename(npmIgnorePath, gitIgnorePath);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 2. Sesuaikan package.json target
|
|
78
|
+
spinner.start("2. Mengkonfigurasi project...");
|
|
79
|
+
const pkgPath = path.join(targetDir, "package.json");
|
|
80
|
+
const pkg = await fs.readJson(pkgPath);
|
|
81
|
+
pkg.name = finalProjectName;
|
|
82
|
+
pkg.version = "0.0.0";
|
|
83
|
+
|
|
84
|
+
// Inject dependency tambahan berdasarkan template
|
|
85
|
+
pkg.dependencies = pkg.dependencies || {};
|
|
86
|
+
pkg.devDependencies = pkg.devDependencies || {};
|
|
87
|
+
|
|
88
|
+
// Core dependencies
|
|
89
|
+
pkg.dependencies["react"] = "^18.2.0";
|
|
90
|
+
pkg.dependencies["react-dom"] = "^18.2.0";
|
|
91
|
+
pkg.devDependencies["@types/react"] = "^18.2.66";
|
|
92
|
+
pkg.devDependencies["@types/react-dom"] = "^18.2.22";
|
|
93
|
+
pkg.devDependencies["@vitejs/plugin-react"] = "^4.2.1";
|
|
94
|
+
pkg.devDependencies["autoprefixer"] = "^10.4.19";
|
|
95
|
+
pkg.devDependencies["postcss"] = "^8.4.38";
|
|
96
|
+
pkg.devDependencies["tailwindcss"] = "^3.4.3";
|
|
97
|
+
pkg.devDependencies["typescript"] = "^5.2.2";
|
|
98
|
+
pkg.devDependencies["vite"] = "^5.2.0";
|
|
99
|
+
|
|
100
|
+
if (selectedTemplate === "portfolio" || selectedTemplate === "landing-page") {
|
|
101
|
+
pkg.dependencies["framer-motion"] = "^11.0.24";
|
|
102
|
+
pkg.dependencies["lucide-react"] = "^0.368.0";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
await fs.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
106
|
+
spinner.succeed("Konfigurasi project selesai!");
|
|
107
|
+
|
|
108
|
+
// 3. Install Dependencies
|
|
109
|
+
spinner.start("3. Menginstall dependencies... (ini mungkin butuh waktu)");
|
|
110
|
+
// execa tidak ditampilin stdio id-nya ke user agar tampilan rapi karena kita pakai spinner
|
|
111
|
+
await execa("npm", ["install"], { cwd: targetDir });
|
|
112
|
+
spinner.succeed("Dependencies berhasil diinstall!");
|
|
113
|
+
|
|
114
|
+
console.log(chalk.green(`\n✅ Project "${finalProjectName}" berhasil dibuat dengan sempurna!\n`));
|
|
115
|
+
console.log(chalk.cyan(`Coba jalankan perintah berikut untuk memulai:`));
|
|
116
|
+
console.log(chalk.white(` cd ${finalProjectName}`));
|
|
117
|
+
console.log(chalk.white(` npm run dev\n`));
|
|
118
|
+
|
|
119
|
+
if(selectedTemplate !== "minimal") {
|
|
120
|
+
console.log(chalk.magenta(`✨ Template ${selectedTemplate} sudah terpasang dengan Framer Motion & Lucide React!`));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
} catch (err) {
|
|
124
|
+
spinner.fail("Terjadi kesalahan!");
|
|
125
|
+
console.error(chalk.red(err));
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
program.parse(process.argv);
|
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jodijonatan/jo-frontend",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"bin": {
|
|
6
|
+
"jo-frontend": "./index.js",
|
|
7
|
+
"create-jo-web": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"index.js",
|
|
11
|
+
"templates"
|
|
12
|
+
],
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"chalk": "^5.3.0",
|
|
15
|
+
"commander": "^12.0.0",
|
|
16
|
+
"execa": "^9.0.0",
|
|
17
|
+
"fs-extra": "^11.2.0",
|
|
18
|
+
"inquirer": "^9.2.16",
|
|
19
|
+
"ora": "^8.2.0"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# React + TypeScript + Vite
|
|
2
|
+
|
|
3
|
+
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
|
4
|
+
|
|
5
|
+
Currently, two official plugins are available:
|
|
6
|
+
|
|
7
|
+
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs)
|
|
8
|
+
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
|
|
9
|
+
|
|
10
|
+
## React Compiler
|
|
11
|
+
|
|
12
|
+
The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation).
|
|
13
|
+
|
|
14
|
+
## Expanding the ESLint configuration
|
|
15
|
+
|
|
16
|
+
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
export default defineConfig([
|
|
20
|
+
globalIgnores(['dist']),
|
|
21
|
+
{
|
|
22
|
+
files: ['**/*.{ts,tsx}'],
|
|
23
|
+
extends: [
|
|
24
|
+
// Other configs...
|
|
25
|
+
|
|
26
|
+
// Remove tseslint.configs.recommended and replace with this
|
|
27
|
+
tseslint.configs.recommendedTypeChecked,
|
|
28
|
+
// Alternatively, use this for stricter rules
|
|
29
|
+
tseslint.configs.strictTypeChecked,
|
|
30
|
+
// Optionally, add this for stylistic rules
|
|
31
|
+
tseslint.configs.stylisticTypeChecked,
|
|
32
|
+
|
|
33
|
+
// Other configs...
|
|
34
|
+
],
|
|
35
|
+
languageOptions: {
|
|
36
|
+
parserOptions: {
|
|
37
|
+
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
|
38
|
+
tsconfigRootDir: import.meta.dirname,
|
|
39
|
+
},
|
|
40
|
+
// other options...
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
])
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
|
|
47
|
+
|
|
48
|
+
```js
|
|
49
|
+
// eslint.config.js
|
|
50
|
+
import reactX from 'eslint-plugin-react-x'
|
|
51
|
+
import reactDom from 'eslint-plugin-react-dom'
|
|
52
|
+
|
|
53
|
+
export default defineConfig([
|
|
54
|
+
globalIgnores(['dist']),
|
|
55
|
+
{
|
|
56
|
+
files: ['**/*.{ts,tsx}'],
|
|
57
|
+
extends: [
|
|
58
|
+
// Other configs...
|
|
59
|
+
// Enable lint rules for React
|
|
60
|
+
reactX.configs['recommended-typescript'],
|
|
61
|
+
// Enable lint rules for React DOM
|
|
62
|
+
reactDom.configs.recommended,
|
|
63
|
+
],
|
|
64
|
+
languageOptions: {
|
|
65
|
+
parserOptions: {
|
|
66
|
+
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
|
67
|
+
tsconfigRootDir: import.meta.dirname,
|
|
68
|
+
},
|
|
69
|
+
// other options...
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
])
|
|
73
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import js from '@eslint/js'
|
|
2
|
+
import globals from 'globals'
|
|
3
|
+
import reactHooks from 'eslint-plugin-react-hooks'
|
|
4
|
+
import reactRefresh from 'eslint-plugin-react-refresh'
|
|
5
|
+
import tseslint from 'typescript-eslint'
|
|
6
|
+
import { defineConfig, globalIgnores } from 'eslint/config'
|
|
7
|
+
|
|
8
|
+
export default defineConfig([
|
|
9
|
+
globalIgnores(['dist']),
|
|
10
|
+
{
|
|
11
|
+
files: ['**/*.{ts,tsx}'],
|
|
12
|
+
extends: [
|
|
13
|
+
js.configs.recommended,
|
|
14
|
+
tseslint.configs.recommended,
|
|
15
|
+
reactHooks.configs.flat.recommended,
|
|
16
|
+
reactRefresh.configs.vite,
|
|
17
|
+
],
|
|
18
|
+
languageOptions: {
|
|
19
|
+
ecmaVersion: 2020,
|
|
20
|
+
globals: globals.browser,
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
])
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<title>template</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="root"></div>
|
|
11
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "template",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "tsc -b && vite build",
|
|
9
|
+
"lint": "eslint .",
|
|
10
|
+
"preview": "vite preview"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"framer-motion": "^12.38.0",
|
|
14
|
+
"lucide-react": "^1.8.0",
|
|
15
|
+
"react": "^19.2.4",
|
|
16
|
+
"react-dom": "^19.2.4"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@eslint/js": "^9.39.4",
|
|
20
|
+
"@types/node": "^24.12.2",
|
|
21
|
+
"@types/react": "^19.2.14",
|
|
22
|
+
"@types/react-dom": "^19.2.3",
|
|
23
|
+
"@vitejs/plugin-react": "^6.0.1",
|
|
24
|
+
"eslint": "^9.39.4",
|
|
25
|
+
"eslint-plugin-react-hooks": "^7.0.1",
|
|
26
|
+
"eslint-plugin-react-refresh": "^0.5.2",
|
|
27
|
+
"globals": "^17.4.0",
|
|
28
|
+
"typescript": "~6.0.2",
|
|
29
|
+
"typescript-eslint": "^8.58.0",
|
|
30
|
+
"vite": "^8.0.4"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="46" fill="none" viewBox="0 0 48 46"><path fill="#863bff" d="M25.946 44.938c-.664.845-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.287c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.497 0-3.578-1.842-3.578H1.237c-.92 0-1.456-1.04-.92-1.788L10.013.474c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.579 1.842 3.579h11.377c.943 0 1.473 1.088.89 1.83L25.947 44.94z" style="fill:#863bff;fill:color(display-p3 .5252 .23 1);fill-opacity:1"/><mask id="a" width="48" height="46" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#000" d="M25.842 44.938c-.664.844-2.021.375-2.021-.698V33.937a2.26 2.26 0 0 0-2.262-2.262H10.183c-.92 0-1.456-1.04-.92-1.788l7.48-10.471c1.07-1.498 0-3.579-1.842-3.579H1.133c-.92 0-1.456-1.04-.92-1.787L9.91.473c.214-.297.556-.474.92-.474h28.894c.92 0 1.456 1.04.92 1.788l-7.48 10.471c-1.07 1.498 0 3.578 1.842 3.578h11.377c.943 0 1.473 1.088.89 1.832L25.843 44.94z" style="fill:#000;fill-opacity:1"/></mask><g mask="url(#a)"><g filter="url(#b)"><ellipse cx="5.508" cy="14.704" fill="#ede6ff" rx="5.508" ry="14.704" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -4.47 31.516)"/></g><g filter="url(#c)"><ellipse cx="10.399" cy="29.851" fill="#ede6ff" rx="10.399" ry="29.851" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -39.328 7.883)"/></g><g filter="url(#d)"><ellipse cx="5.508" cy="30.487" fill="#7e14ff" rx="5.508" ry="30.487" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -25.913 -14.639)scale(1 -1)"/></g><g filter="url(#e)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.814 -32.644 -3.334)scale(1 -1)"/></g><g filter="url(#f)"><ellipse cx="5.508" cy="30.599" fill="#7e14ff" rx="5.508" ry="30.599" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="matrix(.00324 1 1 -.00324 -34.34 30.47)"/></g><g filter="url(#g)"><ellipse cx="14.072" cy="22.078" fill="#ede6ff" rx="14.072" ry="22.078" style="fill:#ede6ff;fill:color(display-p3 .9275 .9033 1);fill-opacity:1" transform="rotate(93.35 24.506 48.493)scale(-1 1)"/></g><g filter="url(#h)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#i)"><ellipse cx="3.47" cy="21.501" fill="#7e14ff" rx="3.47" ry="21.501" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(89.009 28.708 47.59)scale(-1 1)"/></g><g filter="url(#j)"><ellipse cx=".387" cy="8.972" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(39.51 .387 8.972)"/></g><g filter="url(#k)"><ellipse cx="47.523" cy="-6.092" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 47.523 -6.092)"/></g><g filter="url(#l)"><ellipse cx="41.412" cy="6.333" fill="#47bfff" rx="5.971" ry="9.665" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 41.412 6.333)"/></g><g filter="url(#m)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#n)"><ellipse cx="-1.879" cy="38.332" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 -1.88 38.332)"/></g><g filter="url(#o)"><ellipse cx="35.651" cy="29.907" fill="#7e14ff" rx="4.407" ry="29.108" style="fill:#7e14ff;fill:color(display-p3 .4922 .0767 1);fill-opacity:1" transform="rotate(37.892 35.651 29.907)"/></g><g filter="url(#p)"><ellipse cx="38.418" cy="32.4" fill="#47bfff" rx="5.971" ry="15.297" style="fill:#47bfff;fill:color(display-p3 .2799 .748 1);fill-opacity:1" transform="rotate(37.892 38.418 32.4)"/></g></g><defs><filter id="b" width="60.045" height="41.654" x="-19.77" y="16.149" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="c" width="90.34" height="51.437" x="-54.613" y="-7.533" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="d" width="79.355" height="29.4" x="-49.64" y="2.03" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="e" width="79.579" height="29.4" x="-45.045" y="20.029" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="f" width="79.579" height="29.4" x="-43.513" y="21.178" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="g" width="74.749" height="58.852" x="15.756" y="-17.901" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="7.659"/></filter><filter id="h" width="61.377" height="25.362" x="23.548" y="2.284" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="i" width="61.377" height="25.362" x="23.548" y="2.284" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="j" width="56.045" height="63.649" x="-27.636" y="-22.853" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="k" width="54.814" height="64.646" x="20.116" y="-38.415" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="l" width="33.541" height="35.313" x="24.641" y="-11.323" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="m" width="54.814" height="64.646" x="-29.286" y="6.009" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="n" width="54.814" height="64.646" x="-29.286" y="6.009" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="o" width="54.814" height="64.646" x="8.244" y="-2.416" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter><filter id="p" width="39.409" height="43.623" x="18.713" y="10.588" color-interpolation-filters="sRGB" filterUnits="userSpaceOnUse"><feFlood flood-opacity="0" result="BackgroundImageFix"/><feBlend in="SourceGraphic" in2="BackgroundImageFix" result="shape"/><feGaussianBlur result="effect1_foregroundBlur_2002_17158" stdDeviation="4.596"/></filter></defs></svg>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<symbol id="bluesky-icon" viewBox="0 0 16 17">
|
|
3
|
+
<g clip-path="url(#bluesky-clip)"><path fill="#08060d" d="M7.75 7.735c-.693-1.348-2.58-3.86-4.334-5.097-1.68-1.187-2.32-.981-2.74-.79C.188 2.065.1 2.812.1 3.251s.241 3.602.398 4.13c.52 1.744 2.367 2.333 4.07 2.145-2.495.37-4.71 1.278-1.805 4.512 3.196 3.309 4.38-.71 4.987-2.746.608 2.036 1.307 5.91 4.93 2.746 2.72-2.746.747-4.143-1.747-4.512 1.702.189 3.55-.4 4.07-2.145.156-.528.397-3.691.397-4.13s-.088-1.186-.575-1.406c-.42-.19-1.06-.395-2.741.79-1.755 1.24-3.64 3.752-4.334 5.099"/></g>
|
|
4
|
+
<defs><clipPath id="bluesky-clip"><path fill="#fff" d="M.1.85h15.3v15.3H.1z"/></clipPath></defs>
|
|
5
|
+
</symbol>
|
|
6
|
+
<symbol id="discord-icon" viewBox="0 0 20 19">
|
|
7
|
+
<path fill="#08060d" d="M16.224 3.768a14.5 14.5 0 0 0-3.67-1.153c-.158.286-.343.67-.47.976a13.5 13.5 0 0 0-4.067 0c-.128-.306-.317-.69-.476-.976A14.4 14.4 0 0 0 3.868 3.77C1.546 7.28.916 10.703 1.231 14.077a14.7 14.7 0 0 0 4.5 2.306q.545-.748.965-1.587a9.5 9.5 0 0 1-1.518-.74q.191-.14.372-.293c2.927 1.369 6.107 1.369 8.999 0q.183.152.372.294-.723.437-1.52.74.418.838.963 1.588a14.6 14.6 0 0 0 4.504-2.308c.37-3.911-.63-7.302-2.644-10.309m-9.13 8.234c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.894 0 1.614.82 1.599 1.82.001 1-.705 1.82-1.6 1.82m5.91 0c-.878 0-1.599-.82-1.599-1.82 0-.998.705-1.82 1.6-1.82.893 0 1.614.82 1.599 1.82 0 1-.706 1.82-1.6 1.82"/>
|
|
8
|
+
</symbol>
|
|
9
|
+
<symbol id="documentation-icon" viewBox="0 0 21 20">
|
|
10
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="m15.5 13.333 1.533 1.322c.645.555.967.833.967 1.178s-.322.623-.967 1.179L15.5 18.333m-3.333-5-1.534 1.322c-.644.555-.966.833-.966 1.178s.322.623.966 1.179l1.534 1.321"/>
|
|
11
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M17.167 10.836v-4.32c0-1.41 0-2.117-.224-2.68-.359-.906-1.118-1.621-2.08-1.96-.599-.21-1.349-.21-2.848-.21-2.623 0-3.935 0-4.983.369-1.684.591-3.013 1.842-3.641 3.428C3 6.449 3 7.684 3 10.154v2.122c0 2.558 0 3.838.706 4.726q.306.383.713.671c.76.536 1.79.64 3.581.66"/>
|
|
12
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M3 10a2.78 2.78 0 0 1 2.778-2.778c.555 0 1.209.097 1.748-.047.48-.129.854-.503.982-.982.145-.54.048-1.194.048-1.749a2.78 2.78 0 0 1 2.777-2.777"/>
|
|
13
|
+
</symbol>
|
|
14
|
+
<symbol id="github-icon" viewBox="0 0 19 19">
|
|
15
|
+
<path fill="#08060d" fill-rule="evenodd" d="M9.356 1.85C5.05 1.85 1.57 5.356 1.57 9.694a7.84 7.84 0 0 0 5.324 7.44c.387.079.528-.168.528-.376 0-.182-.013-.805-.013-1.454-2.165.467-2.616-.935-2.616-.935-.349-.91-.864-1.143-.864-1.143-.71-.48.051-.48.051-.48.787.051 1.2.805 1.2.805.695 1.194 1.817.857 2.268.649.064-.507.27-.857.49-1.052-1.728-.182-3.545-.857-3.545-3.87 0-.857.31-1.558.8-2.104-.078-.195-.349-1 .077-2.078 0 0 .657-.208 2.14.805a7.5 7.5 0 0 1 1.946-.26c.657 0 1.328.092 1.946.26 1.483-1.013 2.14-.805 2.14-.805.426 1.078.155 1.883.078 2.078.502.546.799 1.247.799 2.104 0 3.013-1.818 3.675-3.558 3.87.284.247.528.714.528 1.454 0 1.052-.012 1.896-.012 2.156 0 .208.142.455.528.377a7.84 7.84 0 0 0 5.324-7.441c.013-4.338-3.48-7.844-7.773-7.844" clip-rule="evenodd"/>
|
|
16
|
+
</symbol>
|
|
17
|
+
<symbol id="social-icon" viewBox="0 0 20 20">
|
|
18
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M12.5 6.667a4.167 4.167 0 1 0-8.334 0 4.167 4.167 0 0 0 8.334 0"/>
|
|
19
|
+
<path fill="none" stroke="#aa3bff" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.35" d="M2.5 16.667a5.833 5.833 0 0 1 8.75-5.053m3.837.474.513 1.035c.07.144.257.282.414.309l.93.155c.596.1.736.536.307.965l-.723.73a.64.64 0 0 0-.152.531l.207.903c.164.715-.213.991-.84.618l-.872-.52a.63.63 0 0 0-.577 0l-.872.52c-.624.373-1.003.094-.84-.618l.207-.903a.64.64 0 0 0-.152-.532l-.723-.729c-.426-.43-.289-.864.306-.964l.93-.156a.64.64 0 0 0 .412-.31l.513-1.034c.28-.562.735-.562 1.012 0"/>
|
|
20
|
+
</symbol>
|
|
21
|
+
<symbol id="x-icon" viewBox="0 0 19 19">
|
|
22
|
+
<path fill="#08060d" fill-rule="evenodd" d="M1.893 1.98c.052.072 1.245 1.769 2.653 3.77l2.892 4.114c.183.261.333.48.333.486s-.068.089-.152.183l-.522.593-.765.867-3.597 4.087c-.375.426-.734.834-.798.905a1 1 0 0 0-.118.148c0 .01.236.017.664.017h.663l.729-.83c.4-.457.796-.906.879-.999a692 692 0 0 0 1.794-2.038c.034-.037.301-.34.594-.675l.551-.624.345-.392a7 7 0 0 1 .34-.374c.006 0 .93 1.306 2.052 2.903l2.084 2.965.045.063h2.275c1.87 0 2.273-.003 2.266-.021-.008-.02-1.098-1.572-3.894-5.547-2.013-2.862-2.28-3.246-2.273-3.266.008-.019.282-.332 2.085-2.38l2-2.274 1.567-1.782c.022-.028-.016-.03-.65-.03h-.674l-.3.342a871 871 0 0 1-1.782 2.025c-.067.075-.405.458-.75.852a100 100 0 0 1-.803.91c-.148.172-.299.344-.99 1.127-.304.343-.32.358-.345.327-.015-.019-.904-1.282-1.976-2.808L6.365 1.85H1.8zm1.782.91 8.078 11.294c.772 1.08 1.413 1.973 1.425 1.984.016.017.241.02 1.05.017l1.03-.004-2.694-3.766L7.796 5.75 5.722 2.852l-1.039-.004-1.039-.004z" clip-rule="evenodd"/>
|
|
23
|
+
</symbol>
|
|
24
|
+
</svg>
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
.counter {
|
|
2
|
+
font-size: 16px;
|
|
3
|
+
padding: 5px 10px;
|
|
4
|
+
border-radius: 5px;
|
|
5
|
+
color: var(--accent);
|
|
6
|
+
background: var(--accent-bg);
|
|
7
|
+
border: 2px solid transparent;
|
|
8
|
+
transition: border-color 0.3s;
|
|
9
|
+
margin-bottom: 24px;
|
|
10
|
+
|
|
11
|
+
&:hover {
|
|
12
|
+
border-color: var(--accent-border);
|
|
13
|
+
}
|
|
14
|
+
&:focus-visible {
|
|
15
|
+
outline: 2px solid var(--accent);
|
|
16
|
+
outline-offset: 2px;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.hero {
|
|
21
|
+
position: relative;
|
|
22
|
+
|
|
23
|
+
.base,
|
|
24
|
+
.framework,
|
|
25
|
+
.vite {
|
|
26
|
+
inset-inline: 0;
|
|
27
|
+
margin: 0 auto;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.base {
|
|
31
|
+
width: 170px;
|
|
32
|
+
position: relative;
|
|
33
|
+
z-index: 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.framework,
|
|
37
|
+
.vite {
|
|
38
|
+
position: absolute;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.framework {
|
|
42
|
+
z-index: 1;
|
|
43
|
+
top: 34px;
|
|
44
|
+
height: 28px;
|
|
45
|
+
transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg)
|
|
46
|
+
scale(1.4);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.vite {
|
|
50
|
+
z-index: 0;
|
|
51
|
+
top: 107px;
|
|
52
|
+
height: 26px;
|
|
53
|
+
width: auto;
|
|
54
|
+
transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg)
|
|
55
|
+
scale(0.8);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
#center {
|
|
60
|
+
display: flex;
|
|
61
|
+
flex-direction: column;
|
|
62
|
+
gap: 25px;
|
|
63
|
+
place-content: center;
|
|
64
|
+
place-items: center;
|
|
65
|
+
flex-grow: 1;
|
|
66
|
+
|
|
67
|
+
@media (max-width: 1024px) {
|
|
68
|
+
padding: 32px 20px 24px;
|
|
69
|
+
gap: 18px;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
#next-steps {
|
|
74
|
+
display: flex;
|
|
75
|
+
border-top: 1px solid var(--border);
|
|
76
|
+
text-align: left;
|
|
77
|
+
|
|
78
|
+
& > div {
|
|
79
|
+
flex: 1 1 0;
|
|
80
|
+
padding: 32px;
|
|
81
|
+
@media (max-width: 1024px) {
|
|
82
|
+
padding: 24px 20px;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.icon {
|
|
87
|
+
margin-bottom: 16px;
|
|
88
|
+
width: 22px;
|
|
89
|
+
height: 22px;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@media (max-width: 1024px) {
|
|
93
|
+
flex-direction: column;
|
|
94
|
+
text-align: center;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
#docs {
|
|
99
|
+
border-right: 1px solid var(--border);
|
|
100
|
+
|
|
101
|
+
@media (max-width: 1024px) {
|
|
102
|
+
border-right: none;
|
|
103
|
+
border-bottom: 1px solid var(--border);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
#next-steps ul {
|
|
108
|
+
list-style: none;
|
|
109
|
+
padding: 0;
|
|
110
|
+
display: flex;
|
|
111
|
+
gap: 8px;
|
|
112
|
+
margin: 32px 0 0;
|
|
113
|
+
|
|
114
|
+
.logo {
|
|
115
|
+
height: 18px;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
a {
|
|
119
|
+
color: var(--text-h);
|
|
120
|
+
font-size: 16px;
|
|
121
|
+
border-radius: 6px;
|
|
122
|
+
background: var(--social-bg);
|
|
123
|
+
display: flex;
|
|
124
|
+
padding: 6px 12px;
|
|
125
|
+
align-items: center;
|
|
126
|
+
gap: 8px;
|
|
127
|
+
text-decoration: none;
|
|
128
|
+
transition: box-shadow 0.3s;
|
|
129
|
+
|
|
130
|
+
&:hover {
|
|
131
|
+
box-shadow: var(--shadow);
|
|
132
|
+
}
|
|
133
|
+
.button-icon {
|
|
134
|
+
height: 18px;
|
|
135
|
+
width: 18px;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@media (max-width: 1024px) {
|
|
140
|
+
margin-top: 20px;
|
|
141
|
+
flex-wrap: wrap;
|
|
142
|
+
justify-content: center;
|
|
143
|
+
|
|
144
|
+
li {
|
|
145
|
+
flex: 1 1 calc(50% - 8px);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
a {
|
|
149
|
+
width: 100%;
|
|
150
|
+
justify-content: center;
|
|
151
|
+
box-sizing: border-box;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
#spacer {
|
|
157
|
+
height: 88px;
|
|
158
|
+
border-top: 1px solid var(--border);
|
|
159
|
+
@media (max-width: 1024px) {
|
|
160
|
+
height: 48px;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.ticks {
|
|
165
|
+
position: relative;
|
|
166
|
+
width: 100%;
|
|
167
|
+
|
|
168
|
+
&::before,
|
|
169
|
+
&::after {
|
|
170
|
+
content: '';
|
|
171
|
+
position: absolute;
|
|
172
|
+
top: -4.5px;
|
|
173
|
+
border: 5px solid transparent;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
&::before {
|
|
177
|
+
left: 0;
|
|
178
|
+
border-left-color: var(--border);
|
|
179
|
+
}
|
|
180
|
+
&::after {
|
|
181
|
+
right: 0;
|
|
182
|
+
border-right-color: var(--border);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { motion } from "framer-motion";
|
|
2
|
+
import { ArrowRight, Github, Code2, Sparkles, Layout, Zap } from "lucide-react";
|
|
3
|
+
|
|
4
|
+
export default function App() {
|
|
5
|
+
const fadeIn = {
|
|
6
|
+
initial: { opacity: 0, y: 20 },
|
|
7
|
+
animate: { opacity: 1, y: 0 },
|
|
8
|
+
transition: { duration: 0.5 },
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<div className="min-h-screen bg-[#0a0a0a] text-zinc-100 selection:bg-indigo-500/30 font-sans">
|
|
13
|
+
{/* Background Decor */}
|
|
14
|
+
<div className="fixed inset-0 overflow-hidden pointer-events-none">
|
|
15
|
+
<div className="absolute -top-[25%] -left-[10%] w-[70%] h-[70%] bg-indigo-500/10 blur-[120px] rounded-full" />
|
|
16
|
+
<div className="absolute -bottom-[25%] -right-[10%] w-[60%] h-[60%] bg-purple-500/10 blur-[120px] rounded-full" />
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
{/* Navbar */}
|
|
20
|
+
<nav className="relative z-10 flex items-center justify-between p-6 max-w-7xl mx-auto">
|
|
21
|
+
<div className="flex items-center gap-2 font-bold text-xl tracking-tighter">
|
|
22
|
+
<div className="w-8 h-8 bg-white rounded-lg flex items-center justify-center">
|
|
23
|
+
<Code2 className="text-black w-5 h-5" />
|
|
24
|
+
</div>
|
|
25
|
+
<span>ProdSync</span>
|
|
26
|
+
</div>
|
|
27
|
+
<div className="hidden md:flex items-center gap-8 text-sm font-medium text-zinc-400">
|
|
28
|
+
<a href="#" className="hover:text-white transition">
|
|
29
|
+
Features
|
|
30
|
+
</a>
|
|
31
|
+
<a href="#" className="hover:text-white transition">
|
|
32
|
+
Showcase
|
|
33
|
+
</a>
|
|
34
|
+
<a href="#" className="hover:text-white transition">
|
|
35
|
+
Docs
|
|
36
|
+
</a>
|
|
37
|
+
</div>
|
|
38
|
+
<button className="bg-zinc-900 border border-zinc-800 px-4 py-2 rounded-full text-sm font-medium hover:bg-zinc-800 transition">
|
|
39
|
+
Launch App
|
|
40
|
+
</button>
|
|
41
|
+
</nav>
|
|
42
|
+
|
|
43
|
+
{/* Hero Section */}
|
|
44
|
+
<main className="relative z-10 max-w-7xl mx-auto px-6 pt-20 pb-32">
|
|
45
|
+
<motion.div
|
|
46
|
+
initial="initial"
|
|
47
|
+
animate="animate"
|
|
48
|
+
className="text-center space-y-8"
|
|
49
|
+
>
|
|
50
|
+
<motion.div
|
|
51
|
+
{...fadeIn}
|
|
52
|
+
className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-zinc-900 border border-zinc-800 text-xs font-medium text-zinc-400"
|
|
53
|
+
>
|
|
54
|
+
<Sparkles className="w-3 h-3 text-yellow-500" />
|
|
55
|
+
<span>New: Vercel-style deployment ready</span>
|
|
56
|
+
</motion.div>
|
|
57
|
+
|
|
58
|
+
<motion.h1
|
|
59
|
+
{...fadeIn}
|
|
60
|
+
transition={{ delay: 0.1 }}
|
|
61
|
+
className="text-6xl md:text-8xl font-bold tracking-tight bg-clip-text text-transparent bg-gradient-to-b from-white to-zinc-500"
|
|
62
|
+
>
|
|
63
|
+
Launch products <br /> faster than ever.
|
|
64
|
+
</motion.h1>
|
|
65
|
+
|
|
66
|
+
<motion.p
|
|
67
|
+
{...fadeIn}
|
|
68
|
+
transition={{ delay: 0.2 }}
|
|
69
|
+
className="text-zinc-400 text-lg md:text-xl max-w-2xl mx-auto leading-relaxed"
|
|
70
|
+
>
|
|
71
|
+
A powerful, modern landing page designed to convert. Build your next SaaS product with React, Framer Motion, and Tailwind CSS.
|
|
72
|
+
</motion.p>
|
|
73
|
+
|
|
74
|
+
<motion.div
|
|
75
|
+
{...fadeIn}
|
|
76
|
+
transition={{ delay: 0.3 }}
|
|
77
|
+
className="flex flex-col sm:flex-row gap-4 justify-center items-center"
|
|
78
|
+
>
|
|
79
|
+
<button className="group relative bg-white text-black px-8 py-3 rounded-full font-semibold flex items-center gap-2 hover:bg-zinc-200 transition overflow-hidden">
|
|
80
|
+
Get Started Free
|
|
81
|
+
<ArrowRight className="w-4 h-4 group-hover:translate-x-1 transition-transform" />
|
|
82
|
+
</button>
|
|
83
|
+
<button className="px-8 py-3 rounded-full font-semibold flex items-center gap-2 border border-zinc-800 hover:bg-zinc-900 transition">
|
|
84
|
+
<Github className="w-4 h-4" />
|
|
85
|
+
Star on GitHub
|
|
86
|
+
</button>
|
|
87
|
+
</motion.div>
|
|
88
|
+
</motion.div>
|
|
89
|
+
|
|
90
|
+
{/* Feature Grid */}
|
|
91
|
+
<motion.div
|
|
92
|
+
initial={{ opacity: 0, y: 40 }}
|
|
93
|
+
animate={{ opacity: 1, y: 0 }}
|
|
94
|
+
transition={{ delay: 0.5, duration: 0.8 }}
|
|
95
|
+
className="grid grid-cols-1 md:grid-cols-3 gap-6 mt-32"
|
|
96
|
+
>
|
|
97
|
+
<FeatureCard
|
|
98
|
+
icon={<Zap className="w-5 h-5 text-yellow-500" />}
|
|
99
|
+
title="Blazing Fast"
|
|
100
|
+
desc="Ditenagai oleh Vite untuk HMR yang instan dan build yang dioptimalkan."
|
|
101
|
+
/>
|
|
102
|
+
<FeatureCard
|
|
103
|
+
icon={<Layout className="w-5 h-5 text-blue-500" />}
|
|
104
|
+
title="Modern Layout"
|
|
105
|
+
desc="Sistem grid dan flexbox yang responsif secara default."
|
|
106
|
+
/>
|
|
107
|
+
<FeatureCard
|
|
108
|
+
icon={<Code2 className="w-5 h-5 text-purple-500" />}
|
|
109
|
+
title="Type Safe"
|
|
110
|
+
desc="Dukungan penuh TypeScript untuk meminimalisir bug saat pengembangan."
|
|
111
|
+
/>
|
|
112
|
+
</motion.div>
|
|
113
|
+
</main>
|
|
114
|
+
</div>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function FeatureCard({
|
|
119
|
+
icon,
|
|
120
|
+
title,
|
|
121
|
+
desc,
|
|
122
|
+
}: {
|
|
123
|
+
icon: React.ReactNode;
|
|
124
|
+
title: string;
|
|
125
|
+
desc: string;
|
|
126
|
+
}) {
|
|
127
|
+
return (
|
|
128
|
+
<div className="p-8 rounded-3xl bg-zinc-900/50 border border-zinc-800 hover:border-zinc-700 transition group">
|
|
129
|
+
<div className="w-12 h-12 rounded-2xl bg-zinc-900 border border-zinc-800 flex items-center justify-center mb-6 group-hover:scale-110 transition-transform">
|
|
130
|
+
{icon}
|
|
131
|
+
</div>
|
|
132
|
+
<h3 className="text-xl font-bold mb-2">{title}</h3>
|
|
133
|
+
<p className="text-zinc-400 leading-relaxed text-sm">{desc}</p>
|
|
134
|
+
</div>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
Binary file
|