Compare commits
3 Commits
a220204004
...
92b00be8e2
| Author | SHA1 | Date | |
|---|---|---|---|
| 92b00be8e2 | |||
| c63b15d5d6 | |||
| 602eabc7ba |
@ -8,6 +8,7 @@ Landing page for hypthetical client **Great Music LLM**'s event and employee man
|
|||||||
|
|
||||||
- [`vite`](https://vite.dev/) build tooling
|
- [`vite`](https://vite.dev/) build tooling
|
||||||
- [`tailwindcss`](https://tailwindcss.com/) styling
|
- [`tailwindcss`](https://tailwindcss.com/) styling
|
||||||
|
- [Magic UI](https://magicui.design/) component library
|
||||||
- [`prettier`](https://prettier.io/) code formatting
|
- [`prettier`](https://prettier.io/) code formatting
|
||||||
- [`eslint`](https://eslint.org/) code linting
|
- [`eslint`](https://eslint.org/) code linting
|
||||||
- [`husky`](https://typicode.github.io/husky/) and [`lint-staged`](https://github.com/lint-staged/lint-staged) pre-commit hooks for testing/formatting/linting
|
- [`husky`](https://typicode.github.io/husky/) and [`lint-staged`](https://github.com/lint-staged/lint-staged) pre-commit hooks for testing/formatting/linting
|
||||||
|
|||||||
24
components.json
Normal file
24
components.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://ui.shadcn.com/schema.json",
|
||||||
|
"style": "new-york",
|
||||||
|
"rsc": false,
|
||||||
|
"tsx": true,
|
||||||
|
"tailwind": {
|
||||||
|
"config": "",
|
||||||
|
"css": "src/index.css",
|
||||||
|
"baseColor": "neutral",
|
||||||
|
"cssVariables": true,
|
||||||
|
"prefix": ""
|
||||||
|
},
|
||||||
|
"iconLibrary": "lucide",
|
||||||
|
"aliases": {
|
||||||
|
"components": "@/components",
|
||||||
|
"utils": "@/lib/utils",
|
||||||
|
"ui": "@/components/ui",
|
||||||
|
"lib": "@/lib",
|
||||||
|
"hooks": "@/hooks"
|
||||||
|
},
|
||||||
|
"registries": {
|
||||||
|
"@magicui": "https://magicui.design/r/{name}.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
188
package-lock.json
generated
188
package-lock.json
generated
@ -8,9 +8,17 @@
|
|||||||
"name": "bcdigital-challenge",
|
"name": "bcdigital-challenge",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fontsource-variable/merriweather": "^5.2.6",
|
||||||
|
"@fontsource/alex-brush": "^5.2.8",
|
||||||
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
"@tailwindcss/vite": "^4.1.16",
|
"@tailwindcss/vite": "^4.1.16",
|
||||||
|
"class-variance-authority": "^0.7.1",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"lucide-react": "^0.552.0",
|
||||||
|
"motion": "^12.23.24",
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
|
"tailwind-merge": "^3.3.1",
|
||||||
"tailwindcss": "^4.1.16"
|
"tailwindcss": "^4.1.16"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -27,6 +35,7 @@
|
|||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"lint-staged": "^16.2.6",
|
"lint-staged": "^16.2.6",
|
||||||
"prettier": "3.6.2",
|
"prettier": "3.6.2",
|
||||||
|
"tw-animate-css": "^1.4.0",
|
||||||
"typescript": "~5.9.3",
|
"typescript": "~5.9.3",
|
||||||
"typescript-eslint": "^8.45.0",
|
"typescript-eslint": "^8.45.0",
|
||||||
"vite": "^7.1.7"
|
"vite": "^7.1.7"
|
||||||
@ -887,6 +896,24 @@
|
|||||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@fontsource-variable/merriweather": {
|
||||||
|
"version": "5.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fontsource-variable/merriweather/-/merriweather-5.2.6.tgz",
|
||||||
|
"integrity": "sha512-bHCDt99f/M48eUcFA86uh/oSPyn8r/ZxXR9l578wqLvjTwDzXx8A/XOAI05WfJ3LnH1rDufQX5RJwiZtbXUCkw==",
|
||||||
|
"license": "OFL-1.1",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ayuhito"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@fontsource/alex-brush": {
|
||||||
|
"version": "5.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/@fontsource/alex-brush/-/alex-brush-5.2.8.tgz",
|
||||||
|
"integrity": "sha512-ShSsZkWN7O6H6x56Jc2c1d+YXDTCGIRBvLKiVIa7r3tyg6Ern56fN2xriAjNNBmvn37Y96XCkavrpERm/IRCEQ==",
|
||||||
|
"license": "OFL-1.1",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ayuhito"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@humanfs/core": {
|
"node_modules/@humanfs/core": {
|
||||||
"version": "0.19.1",
|
"version": "0.19.1",
|
||||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
||||||
@ -1022,6 +1049,39 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-compose-refs": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@radix-ui/react-slot": {
|
||||||
|
"version": "1.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
|
||||||
|
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rolldown/pluginutils": {
|
"node_modules/@rolldown/pluginutils": {
|
||||||
"version": "1.0.0-beta.43",
|
"version": "1.0.0-beta.43",
|
||||||
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.43.tgz",
|
"resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.43.tgz",
|
||||||
@ -1644,7 +1704,7 @@
|
|||||||
"version": "19.2.2",
|
"version": "19.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz",
|
||||||
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
@ -2167,6 +2227,18 @@
|
|||||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/class-variance-authority": {
|
||||||
|
"version": "0.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
|
||||||
|
"integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"clsx": "^2.1.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://polar.sh/cva"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cli-cursor": {
|
"node_modules/cli-cursor": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
|
||||||
@ -2200,6 +2272,15 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/clsx": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
@ -2270,7 +2351,7 @@
|
|||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
@ -2736,6 +2817,33 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/framer-motion": {
|
||||||
|
"version": "12.23.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.24.tgz",
|
||||||
|
"integrity": "sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"motion-dom": "^12.23.23",
|
||||||
|
"motion-utils": "^12.23.6",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/is-prop-valid": "*",
|
||||||
|
"react": "^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/is-prop-valid": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
@ -3376,6 +3484,15 @@
|
|||||||
"yallist": "^3.0.2"
|
"yallist": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lucide-react": {
|
||||||
|
"version": "0.552.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.552.0.tgz",
|
||||||
|
"integrity": "sha512-g9WCjmfwqbexSnZE+2cl21PCfXOcqnGeWeMTNAOGEfpPbm/ZF4YIq77Z8qWrxbu660EKuLB4nSLggoKnCb+isw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/magic-string": {
|
"node_modules/magic-string": {
|
||||||
"version": "0.30.21",
|
"version": "0.30.21",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||||
@ -3435,6 +3552,47 @@
|
|||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/motion": {
|
||||||
|
"version": "12.23.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/motion/-/motion-12.23.24.tgz",
|
||||||
|
"integrity": "sha512-Rc5E7oe2YZ72N//S3QXGzbnXgqNrTESv8KKxABR20q2FLch9gHLo0JLyYo2hZ238bZ9Gx6cWhj9VO0IgwbMjCw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"framer-motion": "^12.23.24",
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@emotion/is-prop-valid": "*",
|
||||||
|
"react": "^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^18.0.0 || ^19.0.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@emotion/is-prop-valid": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/motion-dom": {
|
||||||
|
"version": "12.23.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz",
|
||||||
|
"integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"motion-utils": "^12.23.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/motion-utils": {
|
||||||
|
"version": "12.23.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
|
||||||
|
"integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/ms": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
@ -4004,6 +4162,16 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tailwind-merge": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/dcastil"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/tailwindcss": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "4.1.16",
|
"version": "4.1.16",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.16.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.16.tgz",
|
||||||
@ -4094,6 +4262,22 @@
|
|||||||
"typescript": ">=4.8.4"
|
"typescript": ">=4.8.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "2.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
|
"license": "0BSD"
|
||||||
|
},
|
||||||
|
"node_modules/tw-animate-css": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/Wombosvideo"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/type-check": {
|
"node_modules/type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
|
||||||
|
|||||||
@ -16,9 +16,17 @@
|
|||||||
"**/*": "prettier --write --ignore-unknown"
|
"**/*": "prettier --write --ignore-unknown"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@fontsource-variable/merriweather": "^5.2.6",
|
||||||
|
"@fontsource/alex-brush": "^5.2.8",
|
||||||
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
"@tailwindcss/vite": "^4.1.16",
|
"@tailwindcss/vite": "^4.1.16",
|
||||||
|
"class-variance-authority": "^0.7.1",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"lucide-react": "^0.552.0",
|
||||||
|
"motion": "^12.23.24",
|
||||||
"react": "^19.1.1",
|
"react": "^19.1.1",
|
||||||
"react-dom": "^19.1.1",
|
"react-dom": "^19.1.1",
|
||||||
|
"tailwind-merge": "^3.3.1",
|
||||||
"tailwindcss": "^4.1.16"
|
"tailwindcss": "^4.1.16"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -35,6 +43,7 @@
|
|||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"lint-staged": "^16.2.6",
|
"lint-staged": "^16.2.6",
|
||||||
"prettier": "3.6.2",
|
"prettier": "3.6.2",
|
||||||
|
"tw-animate-css": "^1.4.0",
|
||||||
"typescript": "~5.9.3",
|
"typescript": "~5.9.3",
|
||||||
"typescript-eslint": "^8.45.0",
|
"typescript-eslint": "^8.45.0",
|
||||||
"vite": "^7.1.7"
|
"vite": "^7.1.7"
|
||||||
|
|||||||
77
src/app.tsx
77
src/app.tsx
@ -1,17 +1,14 @@
|
|||||||
|
import { Navbar } from "./components/navbar/navbar";
|
||||||
|
import { PartnersMarquee } from "./components/partners-marquee/partners-marquee";
|
||||||
|
import { AuroraText } from "./components/ui/aurora-text";
|
||||||
|
import { DotPattern } from "./components/ui/dot-pattern";
|
||||||
|
import { RainbowButton } from "./components/ui/rainbow-button";
|
||||||
|
import { TextAnimate } from "./components/ui/text-animate";
|
||||||
|
|
||||||
export function App() {
|
export function App() {
|
||||||
return (
|
return (
|
||||||
<div className="h-screen w-screen flex flex-col">
|
<div className="h-screen w-screen overflow-hidden flex flex-col font-merriweather">
|
||||||
<nav className="flex justify-between items-center p-4 px-6 shadow-md">
|
<Navbar />
|
||||||
<h1 className="text-2xl font-bold">Great Music LLM</h1>
|
|
||||||
<div className="hidden sm:flex gap-2">
|
|
||||||
<button className="bg-blue-500 text-white px-4 py-2 rounded">
|
|
||||||
Button 1
|
|
||||||
</button>
|
|
||||||
<button className="bg-green-500 text-white px-4 py-2 rounded">
|
|
||||||
Button 2
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
<main className="flex-1 overflow-auto">
|
<main className="flex-1 overflow-auto">
|
||||||
<div
|
<div
|
||||||
className="h-full bg-cover bg-center
|
className="h-full bg-cover bg-center
|
||||||
@ -19,25 +16,57 @@ export function App() {
|
|||||||
"
|
"
|
||||||
>
|
>
|
||||||
<div className="h-full text-white flex items-center justify-center backdrop-blur-[3px] backdrop-grayscale-50">
|
<div className="h-full text-white flex items-center justify-center backdrop-blur-[3px] backdrop-grayscale-50">
|
||||||
<div className="flex flex-col gap-4 items-center text-center">
|
<div className="flex flex-col gap-16 items-center text-center">
|
||||||
<div className="flex flex-col gap-1 items-center">
|
<div className="flex flex-col gap-4 items-center">
|
||||||
<h2 className="text-4xl font-bold">
|
<h2 className="text-4xl font-bold">
|
||||||
Orchestral Event Management
|
<AuroraText>Orchestral</AuroraText> Event Management
|
||||||
</h2>
|
</h2>
|
||||||
<h3 className="text-xl">without the hassle</h3>
|
<TextAnimate
|
||||||
|
animation="blurInUp"
|
||||||
|
by="character"
|
||||||
|
as="h3"
|
||||||
|
className="text-xl"
|
||||||
|
>
|
||||||
|
Without The Hassle
|
||||||
|
</TextAnimate>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-4">
|
<RainbowButton variant="outline">Learn More</RainbowButton>
|
||||||
<button className="bg-blue-500 text-white px-4 py-2 rounded">
|
|
||||||
Get Started
|
|
||||||
</button>
|
|
||||||
<button className="bg-gray-300 text-black px-4 py-2 rounded">
|
|
||||||
Learn More
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="relative overflow-hidden py-5 flex flex-col gap-5">
|
||||||
|
<TextAnimate
|
||||||
|
animation="blurInUp"
|
||||||
|
by="character"
|
||||||
|
as="h3"
|
||||||
|
className="text-xl font-bold text-center"
|
||||||
|
>
|
||||||
|
Our Partners
|
||||||
|
</TextAnimate>
|
||||||
|
<PartnersMarquee />
|
||||||
|
<span></span> {/* Spacer for DotPattern */}
|
||||||
|
<DotPattern className="-z-10 mask-[radial-gradient(300px_circle_at_center,white,transparent)]" />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-col gap-5 p-6 bg-stone-200 text-center">
|
||||||
|
<TextAnimate
|
||||||
|
animation="blurInUp"
|
||||||
|
by="character"
|
||||||
|
as="h3"
|
||||||
|
className="text-xl font-bold text-center"
|
||||||
|
>
|
||||||
|
About Us
|
||||||
|
</TextAnimate>
|
||||||
|
<p>
|
||||||
|
We are dedicated to simplifying orchestral event management through
|
||||||
|
innovative solutions tailored to the unique needs of orchestras and
|
||||||
|
their audiences.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Our platform streamlines the planning, coordination, and execution
|
||||||
|
of orchestral events, allowing musicians and organizers to focus on
|
||||||
|
what they do best: creating unforgettable musical experiences.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div>Section 2 coming soon...</div>
|
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
15
src/components/navbar/navbar.tsx
Normal file
15
src/components/navbar/navbar.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
export function Navbar() {
|
||||||
|
return (
|
||||||
|
<nav className="flex justify-between items-center p-4 px-6 shadow-md">
|
||||||
|
<h1 className="text-2xl font-bold font-alex-brush">Great Music LLM</h1>
|
||||||
|
<div className="hidden sm:flex gap-2">
|
||||||
|
<button className="bg-blue-500 text-white px-4 py-2 rounded">
|
||||||
|
Button 1
|
||||||
|
</button>
|
||||||
|
<button className="bg-green-500 text-white px-4 py-2 rounded">
|
||||||
|
Button 2
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
}
|
||||||
48
src/components/partners-marquee/partners-marquee.tsx
Normal file
48
src/components/partners-marquee/partners-marquee.tsx
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { Marquee } from "../ui/marquee";
|
||||||
|
|
||||||
|
export function PartnersMarquee() {
|
||||||
|
const partners = [
|
||||||
|
{
|
||||||
|
name: "Conductor Orchestra",
|
||||||
|
logo: "https://t3.ftcdn.net/jpg/05/71/43/76/360_F_571437617_ZNppyF5qpbJn9dYifhjWEQkgjbZNBXP9.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Chicago Symphony Orchestra",
|
||||||
|
logo: "https://upload.wikimedia.org/wikipedia/commons/1/1f/Wiki-CSO_logo2024_stacked-whitebg.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Toronto Symphony Orchestra",
|
||||||
|
logo: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSfThsC3B2BGCKjnyr_IMiJmp-0jI9I9blZrw&s",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Los Angeles Chamber Orchestra",
|
||||||
|
logo: "https://admin.itsnicethat.com/images/TOHFR2DIydoDTaGM6NbfVRFZfJU=/168760/format-webp%7Cwidth-2880/5d947a047fa44cbd2200fd0f.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Richmond Symphony Orchestra",
|
||||||
|
logo: "https://richmondsymphony.org/wp-content/uploads/2021/07/rso-logo-2023.png",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative flex w-full flex-col items-center justify-center overflow-hidden">
|
||||||
|
<Marquee>
|
||||||
|
{partners.map((partner) => (
|
||||||
|
<div
|
||||||
|
key={partner.name}
|
||||||
|
className="flex flex-col items-center justify-center p-4 bg-white border-2 border-gray-300 rounded-lg shadow-md"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={partner.logo}
|
||||||
|
alt={`${partner.name} logo`}
|
||||||
|
className="max-h-24"
|
||||||
|
/>
|
||||||
|
<p>{partner.name}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Marquee>
|
||||||
|
<div className="from-background pointer-events-none absolute inset-y-0 left-0 w-4 bg-linear-to-r"></div>
|
||||||
|
<div className="from-background pointer-events-none absolute inset-y-0 right-0 w-4 bg-linear-to-l"></div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
41
src/components/ui/aurora-text.tsx
Normal file
41
src/components/ui/aurora-text.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import React, { memo } from "react";
|
||||||
|
|
||||||
|
interface AuroraTextProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
className?: string;
|
||||||
|
colors?: string[];
|
||||||
|
speed?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AuroraText = memo(
|
||||||
|
({
|
||||||
|
children,
|
||||||
|
className = "",
|
||||||
|
colors = ["#FF0080", "#7928CA", "#0070F3", "#38bdf8"],
|
||||||
|
speed = 1,
|
||||||
|
}: AuroraTextProps) => {
|
||||||
|
const gradientStyle = {
|
||||||
|
backgroundImage: `linear-gradient(135deg, ${colors.join(", ")}, ${
|
||||||
|
colors[0]
|
||||||
|
})`,
|
||||||
|
WebkitBackgroundClip: "text",
|
||||||
|
WebkitTextFillColor: "transparent",
|
||||||
|
animationDuration: `${10 / speed}s`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className={`relative inline-block ${className}`}>
|
||||||
|
<span className="sr-only">{children}</span>
|
||||||
|
<span
|
||||||
|
className="animate-aurora relative bg-size-[200%_auto] bg-clip-text text-transparent"
|
||||||
|
style={gradientStyle}
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
AuroraText.displayName = "AuroraText";
|
||||||
156
src/components/ui/dot-pattern.tsx
Normal file
156
src/components/ui/dot-pattern.tsx
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
import React, { useEffect, useId, useRef, useState } from "react";
|
||||||
|
import { motion } from "motion/react";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DotPattern Component Props
|
||||||
|
*
|
||||||
|
* @param {number} [width=16] - The horizontal spacing between dots
|
||||||
|
* @param {number} [height=16] - The vertical spacing between dots
|
||||||
|
* @param {number} [x=0] - The x-offset of the entire pattern
|
||||||
|
* @param {number} [y=0] - The y-offset of the entire pattern
|
||||||
|
* @param {number} [cx=1] - The x-offset of individual dots
|
||||||
|
* @param {number} [cy=1] - The y-offset of individual dots
|
||||||
|
* @param {number} [cr=1] - The radius of each dot
|
||||||
|
* @param {string} [className] - Additional CSS classes to apply to the SVG container
|
||||||
|
* @param {boolean} [glow=false] - Whether dots should have a glowing animation effect
|
||||||
|
*/
|
||||||
|
interface DotPatternProps extends React.SVGProps<SVGSVGElement> {
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
x?: number;
|
||||||
|
y?: number;
|
||||||
|
cx?: number;
|
||||||
|
cy?: number;
|
||||||
|
cr?: number;
|
||||||
|
className?: string;
|
||||||
|
glow?: boolean;
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DotPattern Component
|
||||||
|
*
|
||||||
|
* A React component that creates an animated or static dot pattern background using SVG.
|
||||||
|
* The pattern automatically adjusts to fill its container and can optionally display glowing dots.
|
||||||
|
*
|
||||||
|
* @component
|
||||||
|
*
|
||||||
|
* @see DotPatternProps for the props interface.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* // Basic usage
|
||||||
|
* <DotPattern />
|
||||||
|
*
|
||||||
|
* // With glowing effect and custom spacing
|
||||||
|
* <DotPattern
|
||||||
|
* width={20}
|
||||||
|
* height={20}
|
||||||
|
* glow={true}
|
||||||
|
* className="opacity-50"
|
||||||
|
* />
|
||||||
|
*
|
||||||
|
* @notes
|
||||||
|
* - The component is client-side only ("use client")
|
||||||
|
* - Automatically responds to container size changes
|
||||||
|
* - When glow is enabled, dots will animate with random delays and durations
|
||||||
|
* - Uses Motion for animations
|
||||||
|
* - Dots color can be controlled via the text color utility classes
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function DotPattern({
|
||||||
|
width = 16,
|
||||||
|
height = 16,
|
||||||
|
x = 0,
|
||||||
|
y = 0,
|
||||||
|
cx = 1,
|
||||||
|
cy = 1,
|
||||||
|
cr = 1,
|
||||||
|
className,
|
||||||
|
glow = false,
|
||||||
|
...props
|
||||||
|
}: DotPatternProps) {
|
||||||
|
const id = useId();
|
||||||
|
const containerRef = useRef<SVGSVGElement>(null);
|
||||||
|
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const updateDimensions = () => {
|
||||||
|
if (containerRef.current) {
|
||||||
|
const { width, height } = containerRef.current.getBoundingClientRect();
|
||||||
|
setDimensions({ width, height });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
updateDimensions();
|
||||||
|
window.addEventListener("resize", updateDimensions);
|
||||||
|
return () => window.removeEventListener("resize", updateDimensions);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const dots = Array.from(
|
||||||
|
{
|
||||||
|
length:
|
||||||
|
Math.ceil(dimensions.width / width) *
|
||||||
|
Math.ceil(dimensions.height / height),
|
||||||
|
},
|
||||||
|
(_, i) => {
|
||||||
|
const col = i % Math.ceil(dimensions.width / width);
|
||||||
|
const row = Math.floor(i / Math.ceil(dimensions.width / width));
|
||||||
|
return {
|
||||||
|
x: col * width + cx,
|
||||||
|
y: row * height + cy,
|
||||||
|
delay: Math.random() * 5,
|
||||||
|
duration: Math.random() * 3 + 2,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
ref={containerRef}
|
||||||
|
aria-hidden="true"
|
||||||
|
className={cn(
|
||||||
|
"pointer-events-none absolute inset-0 h-full w-full text-neutral-400/80",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<defs>
|
||||||
|
<radialGradient id={`${id}-gradient`}>
|
||||||
|
<stop offset="0%" stopColor="currentColor" stopOpacity="1" />
|
||||||
|
<stop offset="100%" stopColor="currentColor" stopOpacity="0" />
|
||||||
|
</radialGradient>
|
||||||
|
</defs>
|
||||||
|
{dots.map((dot, index) => (
|
||||||
|
<motion.circle
|
||||||
|
key={`${dot.x}-${dot.y}`}
|
||||||
|
cx={dot.x}
|
||||||
|
cy={dot.y}
|
||||||
|
r={cr}
|
||||||
|
fill={glow ? `url(#${id}-gradient)` : "currentColor"}
|
||||||
|
initial={glow ? { opacity: 0.4, scale: 1 } : {}}
|
||||||
|
animate={
|
||||||
|
glow
|
||||||
|
? {
|
||||||
|
opacity: [0.4, 1, 0.4],
|
||||||
|
scale: [1, 1.5, 1],
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
}
|
||||||
|
transition={
|
||||||
|
glow
|
||||||
|
? {
|
||||||
|
duration: dot.duration,
|
||||||
|
repeat: Infinity,
|
||||||
|
repeatType: "reverse",
|
||||||
|
delay: dot.delay,
|
||||||
|
ease: "easeInOut",
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
74
src/components/ui/marquee.tsx
Normal file
74
src/components/ui/marquee.tsx
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import type { ComponentPropsWithoutRef } from "react";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
interface MarqueeProps extends ComponentPropsWithoutRef<"div"> {
|
||||||
|
/**
|
||||||
|
* Optional CSS class name to apply custom styles
|
||||||
|
*/
|
||||||
|
className?: string;
|
||||||
|
/**
|
||||||
|
* Whether to reverse the animation direction
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
reverse?: boolean;
|
||||||
|
/**
|
||||||
|
* Whether to pause the animation on hover
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
pauseOnHover?: boolean;
|
||||||
|
/**
|
||||||
|
* Content to be displayed in the marquee
|
||||||
|
*/
|
||||||
|
children: React.ReactNode;
|
||||||
|
/**
|
||||||
|
* Whether to animate vertically instead of horizontally
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
vertical?: boolean;
|
||||||
|
/**
|
||||||
|
* Number of times to repeat the content
|
||||||
|
* @default 4
|
||||||
|
*/
|
||||||
|
repeat?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Marquee({
|
||||||
|
className,
|
||||||
|
reverse = false,
|
||||||
|
pauseOnHover = false,
|
||||||
|
children,
|
||||||
|
vertical = false,
|
||||||
|
repeat = 4,
|
||||||
|
...props
|
||||||
|
}: MarqueeProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
{...props}
|
||||||
|
className={cn(
|
||||||
|
"group flex [gap:var(--gap)] overflow-hidden p-2 [--duration:40s] [--gap:1rem]",
|
||||||
|
{
|
||||||
|
"flex-row": !vertical,
|
||||||
|
"flex-col": vertical,
|
||||||
|
},
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{Array(repeat)
|
||||||
|
.fill(0)
|
||||||
|
.map((_, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className={cn("flex shrink-0 justify-around [gap:var(--gap)]", {
|
||||||
|
"animate-marquee flex-row": !vertical,
|
||||||
|
"animate-marquee-vertical flex-col": vertical,
|
||||||
|
"group-hover:[animation-play-state:paused]": pauseOnHover,
|
||||||
|
"[animation-direction:reverse]": reverse,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
60
src/components/ui/rainbow-button.tsx
Normal file
60
src/components/ui/rainbow-button.tsx
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { Slot } from "@radix-ui/react-slot";
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
const rainbowButtonVariants = cva(
|
||||||
|
cn(
|
||||||
|
"relative cursor-pointer group transition-all animate-rainbow",
|
||||||
|
"inline-flex items-center justify-center gap-2 shrink-0",
|
||||||
|
"rounded-sm outline-none focus-visible:ring-[3px] aria-invalid:border-destructive",
|
||||||
|
"text-sm font-medium whitespace-nowrap",
|
||||||
|
"disabled:pointer-events-none disabled:opacity-50",
|
||||||
|
"[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0",
|
||||||
|
),
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default:
|
||||||
|
"border-0 bg-[linear-gradient(#121213,#121213),linear-gradient(#121213_50%,rgba(18,18,19,0.6)_80%,rgba(18,18,19,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] bg-[length:200%] text-primary-foreground [background-clip:padding-box,border-box,border-box] [background-origin:border-box] [border:calc(0.125rem)_solid_transparent] before:absolute before:bottom-[-20%] before:left-1/2 before:z-0 before:h-1/5 before:w-3/5 before:-translate-x-1/2 before:animate-rainbow before:bg-[linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] before:[filter:blur(0.75rem)] dark:bg-[linear-gradient(#fff,#fff),linear-gradient(#fff_50%,rgba(255,255,255,0.6)_80%,rgba(0,0,0,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))]",
|
||||||
|
outline:
|
||||||
|
"border border-input border-b-transparent bg-[linear-gradient(#ffffff,#ffffff),linear-gradient(#ffffff_50%,rgba(18,18,19,0.6)_80%,rgba(18,18,19,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] bg-[length:200%] text-accent-foreground [background-clip:padding-box,border-box,border-box] [background-origin:border-box] before:absolute before:bottom-[-20%] before:left-1/2 before:z-0 before:h-1/5 before:w-3/5 before:-translate-x-1/2 before:animate-rainbow before:bg-[linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))] before:[filter:blur(0.75rem)] dark:bg-[linear-gradient(#0a0a0a,#0a0a0a),linear-gradient(#0a0a0a_50%,rgba(255,255,255,0.6)_80%,rgba(0,0,0,0)),linear-gradient(90deg,var(--color-1),var(--color-5),var(--color-3),var(--color-4),var(--color-2))]",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
default: "h-9 px-4 py-2",
|
||||||
|
sm: "h-8 rounded-xl px-3 text-xs",
|
||||||
|
lg: "h-11 rounded-xl px-8",
|
||||||
|
icon: "size-9",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
size: "default",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
interface RainbowButtonProps
|
||||||
|
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||||
|
VariantProps<typeof rainbowButtonVariants> {
|
||||||
|
asChild?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const RainbowButton = React.forwardRef<HTMLButtonElement, RainbowButtonProps>(
|
||||||
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||||
|
const Comp = asChild ? Slot : "button";
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
data-slot="button"
|
||||||
|
className={cn(rainbowButtonVariants({ variant, size, className }))}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
RainbowButton.displayName = "RainbowButton";
|
||||||
|
|
||||||
|
export { RainbowButton, rainbowButtonVariants, type RainbowButtonProps };
|
||||||
422
src/components/ui/text-animate.tsx
Normal file
422
src/components/ui/text-animate.tsx
Normal file
@ -0,0 +1,422 @@
|
|||||||
|
import { type ElementType, memo } from "react";
|
||||||
|
import {
|
||||||
|
AnimatePresence,
|
||||||
|
motion,
|
||||||
|
type MotionProps,
|
||||||
|
type Variants,
|
||||||
|
} from "motion/react";
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
type AnimationType = "text" | "word" | "character" | "line";
|
||||||
|
type AnimationVariant =
|
||||||
|
| "fadeIn"
|
||||||
|
| "blurIn"
|
||||||
|
| "blurInUp"
|
||||||
|
| "blurInDown"
|
||||||
|
| "slideUp"
|
||||||
|
| "slideDown"
|
||||||
|
| "slideLeft"
|
||||||
|
| "slideRight"
|
||||||
|
| "scaleUp"
|
||||||
|
| "scaleDown";
|
||||||
|
|
||||||
|
interface TextAnimateProps extends MotionProps {
|
||||||
|
/**
|
||||||
|
* The text content to animate
|
||||||
|
*/
|
||||||
|
children: string;
|
||||||
|
/**
|
||||||
|
* The class name to be applied to the component
|
||||||
|
*/
|
||||||
|
className?: string;
|
||||||
|
/**
|
||||||
|
* The class name to be applied to each segment
|
||||||
|
*/
|
||||||
|
segmentClassName?: string;
|
||||||
|
/**
|
||||||
|
* The delay before the animation starts
|
||||||
|
*/
|
||||||
|
delay?: number;
|
||||||
|
/**
|
||||||
|
* The duration of the animation
|
||||||
|
*/
|
||||||
|
duration?: number;
|
||||||
|
/**
|
||||||
|
* Custom motion variants for the animation
|
||||||
|
*/
|
||||||
|
variants?: Variants;
|
||||||
|
/**
|
||||||
|
* The element type to render
|
||||||
|
*/
|
||||||
|
as?: ElementType;
|
||||||
|
/**
|
||||||
|
* How to split the text ("text", "word", "character")
|
||||||
|
*/
|
||||||
|
by?: AnimationType;
|
||||||
|
/**
|
||||||
|
* Whether to start animation when component enters viewport
|
||||||
|
*/
|
||||||
|
startOnView?: boolean;
|
||||||
|
/**
|
||||||
|
* Whether to animate only once
|
||||||
|
*/
|
||||||
|
once?: boolean;
|
||||||
|
/**
|
||||||
|
* The animation preset to use
|
||||||
|
*/
|
||||||
|
animation?: AnimationVariant;
|
||||||
|
/**
|
||||||
|
* Whether to enable accessibility features (default: true)
|
||||||
|
*/
|
||||||
|
accessible?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const staggerTimings: Record<AnimationType, number> = {
|
||||||
|
text: 0.06,
|
||||||
|
word: 0.05,
|
||||||
|
character: 0.03,
|
||||||
|
line: 0.06,
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultContainerVariants = {
|
||||||
|
hidden: { opacity: 1 },
|
||||||
|
show: {
|
||||||
|
opacity: 1,
|
||||||
|
transition: {
|
||||||
|
delayChildren: 0,
|
||||||
|
staggerChildren: 0.05,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exit: {
|
||||||
|
opacity: 0,
|
||||||
|
transition: {
|
||||||
|
staggerChildren: 0.05,
|
||||||
|
staggerDirection: -1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultItemVariants: Variants = {
|
||||||
|
hidden: { opacity: 0 },
|
||||||
|
show: {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
exit: {
|
||||||
|
opacity: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultItemAnimationVariants: Record<
|
||||||
|
AnimationVariant,
|
||||||
|
{ container: Variants; item: Variants }
|
||||||
|
> = {
|
||||||
|
fadeIn: {
|
||||||
|
container: defaultContainerVariants,
|
||||||
|
item: {
|
||||||
|
hidden: { opacity: 0, y: 20 },
|
||||||
|
show: {
|
||||||
|
opacity: 1,
|
||||||
|
y: 0,
|
||||||
|
transition: {
|
||||||
|
duration: 0.3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exit: {
|
||||||
|
opacity: 0,
|
||||||
|
y: 20,
|
||||||
|
transition: { duration: 0.3 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
blurIn: {
|
||||||
|
container: defaultContainerVariants,
|
||||||
|
item: {
|
||||||
|
hidden: { opacity: 0, filter: "blur(10px)" },
|
||||||
|
show: {
|
||||||
|
opacity: 1,
|
||||||
|
filter: "blur(0px)",
|
||||||
|
transition: {
|
||||||
|
duration: 0.3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exit: {
|
||||||
|
opacity: 0,
|
||||||
|
filter: "blur(10px)",
|
||||||
|
transition: { duration: 0.3 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
blurInUp: {
|
||||||
|
container: defaultContainerVariants,
|
||||||
|
item: {
|
||||||
|
hidden: { opacity: 0, filter: "blur(10px)", y: 20 },
|
||||||
|
show: {
|
||||||
|
opacity: 1,
|
||||||
|
filter: "blur(0px)",
|
||||||
|
y: 0,
|
||||||
|
transition: {
|
||||||
|
y: { duration: 0.3 },
|
||||||
|
opacity: { duration: 0.4 },
|
||||||
|
filter: { duration: 0.3 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exit: {
|
||||||
|
opacity: 0,
|
||||||
|
filter: "blur(10px)",
|
||||||
|
y: 20,
|
||||||
|
transition: {
|
||||||
|
y: { duration: 0.3 },
|
||||||
|
opacity: { duration: 0.4 },
|
||||||
|
filter: { duration: 0.3 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
blurInDown: {
|
||||||
|
container: defaultContainerVariants,
|
||||||
|
item: {
|
||||||
|
hidden: { opacity: 0, filter: "blur(10px)", y: -20 },
|
||||||
|
show: {
|
||||||
|
opacity: 1,
|
||||||
|
filter: "blur(0px)",
|
||||||
|
y: 0,
|
||||||
|
transition: {
|
||||||
|
y: { duration: 0.3 },
|
||||||
|
opacity: { duration: 0.4 },
|
||||||
|
filter: { duration: 0.3 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
slideUp: {
|
||||||
|
container: defaultContainerVariants,
|
||||||
|
item: {
|
||||||
|
hidden: { y: 20, opacity: 0 },
|
||||||
|
show: {
|
||||||
|
y: 0,
|
||||||
|
opacity: 1,
|
||||||
|
transition: {
|
||||||
|
duration: 0.3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exit: {
|
||||||
|
y: -20,
|
||||||
|
opacity: 0,
|
||||||
|
transition: {
|
||||||
|
duration: 0.3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
slideDown: {
|
||||||
|
container: defaultContainerVariants,
|
||||||
|
item: {
|
||||||
|
hidden: { y: -20, opacity: 0 },
|
||||||
|
show: {
|
||||||
|
y: 0,
|
||||||
|
opacity: 1,
|
||||||
|
transition: { duration: 0.3 },
|
||||||
|
},
|
||||||
|
exit: {
|
||||||
|
y: 20,
|
||||||
|
opacity: 0,
|
||||||
|
transition: { duration: 0.3 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
slideLeft: {
|
||||||
|
container: defaultContainerVariants,
|
||||||
|
item: {
|
||||||
|
hidden: { x: 20, opacity: 0 },
|
||||||
|
show: {
|
||||||
|
x: 0,
|
||||||
|
opacity: 1,
|
||||||
|
transition: { duration: 0.3 },
|
||||||
|
},
|
||||||
|
exit: {
|
||||||
|
x: -20,
|
||||||
|
opacity: 0,
|
||||||
|
transition: { duration: 0.3 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
slideRight: {
|
||||||
|
container: defaultContainerVariants,
|
||||||
|
item: {
|
||||||
|
hidden: { x: -20, opacity: 0 },
|
||||||
|
show: {
|
||||||
|
x: 0,
|
||||||
|
opacity: 1,
|
||||||
|
transition: { duration: 0.3 },
|
||||||
|
},
|
||||||
|
exit: {
|
||||||
|
x: 20,
|
||||||
|
opacity: 0,
|
||||||
|
transition: { duration: 0.3 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
scaleUp: {
|
||||||
|
container: defaultContainerVariants,
|
||||||
|
item: {
|
||||||
|
hidden: { scale: 0.5, opacity: 0 },
|
||||||
|
show: {
|
||||||
|
scale: 1,
|
||||||
|
opacity: 1,
|
||||||
|
transition: {
|
||||||
|
duration: 0.3,
|
||||||
|
scale: {
|
||||||
|
type: "spring",
|
||||||
|
damping: 15,
|
||||||
|
stiffness: 300,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exit: {
|
||||||
|
scale: 0.5,
|
||||||
|
opacity: 0,
|
||||||
|
transition: { duration: 0.3 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
scaleDown: {
|
||||||
|
container: defaultContainerVariants,
|
||||||
|
item: {
|
||||||
|
hidden: { scale: 1.5, opacity: 0 },
|
||||||
|
show: {
|
||||||
|
scale: 1,
|
||||||
|
opacity: 1,
|
||||||
|
transition: {
|
||||||
|
duration: 0.3,
|
||||||
|
scale: {
|
||||||
|
type: "spring",
|
||||||
|
damping: 15,
|
||||||
|
stiffness: 300,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exit: {
|
||||||
|
scale: 1.5,
|
||||||
|
opacity: 0,
|
||||||
|
transition: { duration: 0.3 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const TextAnimateBase = ({
|
||||||
|
children,
|
||||||
|
delay = 0,
|
||||||
|
duration = 0.3,
|
||||||
|
variants,
|
||||||
|
className,
|
||||||
|
segmentClassName,
|
||||||
|
as: Component = "p",
|
||||||
|
startOnView = true,
|
||||||
|
once = false,
|
||||||
|
by = "word",
|
||||||
|
animation = "fadeIn",
|
||||||
|
accessible = true,
|
||||||
|
...props
|
||||||
|
}: TextAnimateProps) => {
|
||||||
|
const MotionComponent = motion.create(Component);
|
||||||
|
|
||||||
|
let segments: string[] = [];
|
||||||
|
switch (by) {
|
||||||
|
case "word":
|
||||||
|
segments = children.split(/(\s+)/);
|
||||||
|
break;
|
||||||
|
case "character":
|
||||||
|
segments = children.split("");
|
||||||
|
break;
|
||||||
|
case "line":
|
||||||
|
segments = children.split("\n");
|
||||||
|
break;
|
||||||
|
case "text":
|
||||||
|
default:
|
||||||
|
segments = [children];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const finalVariants = variants
|
||||||
|
? {
|
||||||
|
container: {
|
||||||
|
hidden: { opacity: 0 },
|
||||||
|
show: {
|
||||||
|
opacity: 1,
|
||||||
|
transition: {
|
||||||
|
opacity: { duration: 0.01, delay },
|
||||||
|
delayChildren: delay,
|
||||||
|
staggerChildren: duration / segments.length,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exit: {
|
||||||
|
opacity: 0,
|
||||||
|
transition: {
|
||||||
|
staggerChildren: duration / segments.length,
|
||||||
|
staggerDirection: -1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
item: variants,
|
||||||
|
}
|
||||||
|
: animation
|
||||||
|
? {
|
||||||
|
container: {
|
||||||
|
...defaultItemAnimationVariants[animation].container,
|
||||||
|
show: {
|
||||||
|
...defaultItemAnimationVariants[animation].container.show,
|
||||||
|
transition: {
|
||||||
|
delayChildren: delay,
|
||||||
|
staggerChildren: duration / segments.length,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
exit: {
|
||||||
|
...defaultItemAnimationVariants[animation].container.exit,
|
||||||
|
transition: {
|
||||||
|
staggerChildren: duration / segments.length,
|
||||||
|
staggerDirection: -1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
item: defaultItemAnimationVariants[animation].item,
|
||||||
|
}
|
||||||
|
: { container: defaultContainerVariants, item: defaultItemVariants };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AnimatePresence mode="popLayout">
|
||||||
|
<MotionComponent
|
||||||
|
variants={finalVariants.container as Variants}
|
||||||
|
initial="hidden"
|
||||||
|
whileInView={startOnView ? "show" : undefined}
|
||||||
|
animate={startOnView ? undefined : "show"}
|
||||||
|
exit="exit"
|
||||||
|
className={cn("whitespace-pre-wrap", className)}
|
||||||
|
viewport={{ once }}
|
||||||
|
aria-label={accessible ? children : undefined}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{accessible && <span className="sr-only">{children}</span>}
|
||||||
|
{segments.map((segment, i) => (
|
||||||
|
<motion.span
|
||||||
|
key={`${by}-${segment}-${i}`}
|
||||||
|
variants={finalVariants.item}
|
||||||
|
custom={i * staggerTimings[by]}
|
||||||
|
className={cn(
|
||||||
|
by === "line" ? "block" : "inline-block whitespace-pre",
|
||||||
|
by === "character" && "",
|
||||||
|
segmentClassName,
|
||||||
|
)}
|
||||||
|
aria-hidden={accessible ? true : undefined}
|
||||||
|
>
|
||||||
|
{segment}
|
||||||
|
</motion.span>
|
||||||
|
))}
|
||||||
|
</MotionComponent>
|
||||||
|
</AnimatePresence>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Export the memoized version
|
||||||
|
export const TextAnimate = memo(TextAnimateBase);
|
||||||
3
src/globals.d.ts
vendored
Normal file
3
src/globals.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
declare module "*.css";
|
||||||
|
declare module "@fontsource/*" {}
|
||||||
|
declare module "@fontsource-variable/*" {}
|
||||||
198
src/index.css
198
src/index.css
@ -1 +1,199 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
@import "tw-animate-css";
|
||||||
|
|
||||||
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
|
@theme inline {
|
||||||
|
--font-alex-brush: "Alex Brush", cursive;
|
||||||
|
--font-merriweather: "Merriweather Variable", serif;
|
||||||
|
|
||||||
|
--radius-sm: calc(var(--radius) - 4px);
|
||||||
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
|
--radius-lg: var(--radius);
|
||||||
|
--radius-xl: calc(var(--radius) + 4px);
|
||||||
|
--color-background: var(--background);
|
||||||
|
--color-foreground: var(--foreground);
|
||||||
|
--color-card: var(--card);
|
||||||
|
--color-card-foreground: var(--card-foreground);
|
||||||
|
--color-popover: var(--popover);
|
||||||
|
--color-popover-foreground: var(--popover-foreground);
|
||||||
|
--color-primary: var(--primary);
|
||||||
|
--color-primary-foreground: var(--primary-foreground);
|
||||||
|
--color-secondary: var(--secondary);
|
||||||
|
--color-secondary-foreground: var(--secondary-foreground);
|
||||||
|
--color-muted: var(--muted);
|
||||||
|
--color-muted-foreground: var(--muted-foreground);
|
||||||
|
--color-accent: var(--accent);
|
||||||
|
--color-accent-foreground: var(--accent-foreground);
|
||||||
|
--color-destructive: var(--destructive);
|
||||||
|
--color-border: var(--border);
|
||||||
|
--color-input: var(--input);
|
||||||
|
--color-ring: var(--ring);
|
||||||
|
--color-chart-1: var(--chart-1);
|
||||||
|
--color-chart-2: var(--chart-2);
|
||||||
|
--color-chart-3: var(--chart-3);
|
||||||
|
--color-chart-4: var(--chart-4);
|
||||||
|
--color-chart-5: var(--chart-5);
|
||||||
|
--color-sidebar: var(--sidebar);
|
||||||
|
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||||
|
--color-sidebar-primary: var(--sidebar-primary);
|
||||||
|
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||||
|
--color-sidebar-accent: var(--sidebar-accent);
|
||||||
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||||
|
--color-sidebar-border: var(--sidebar-border);
|
||||||
|
--color-sidebar-ring: var(--sidebar-ring);
|
||||||
|
--animate-aurora: aurora 8s ease-in-out infinite alternate;
|
||||||
|
|
||||||
|
@keyframes aurora {
|
||||||
|
0% {
|
||||||
|
background-position: 0% 50%;
|
||||||
|
transform: rotate(-5deg) scale(0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
25% {
|
||||||
|
background-position: 50% 100%;
|
||||||
|
transform: rotate(5deg) scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
background-position: 100% 50%;
|
||||||
|
transform: rotate(-3deg) scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
75% {
|
||||||
|
background-position: 50% 0%;
|
||||||
|
transform: rotate(3deg) scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
background-position: 0% 50%;
|
||||||
|
transform: rotate(-5deg) scale(0.9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
--animate-rainbow: rainbow var(--speed, 2s) infinite linear;
|
||||||
|
--color-color-5: var(--color-5);
|
||||||
|
--color-color-4: var(--color-4);
|
||||||
|
--color-color-3: var(--color-3);
|
||||||
|
--color-color-2: var(--color-2);
|
||||||
|
--color-color-1: var(--color-1);
|
||||||
|
|
||||||
|
@keyframes rainbow {
|
||||||
|
0% {
|
||||||
|
background-position: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
background-position: 200%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
--animate-marquee: marquee var(--duration) infinite linear;
|
||||||
|
|
||||||
|
--animate-marquee-vertical: marquee-vertical var(--duration) linear infinite;
|
||||||
|
@keyframes marquee {
|
||||||
|
from {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateX(calc(-100% - var(--gap)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes marquee-vertical {
|
||||||
|
from {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateY(calc(-100% - var(--gap)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--radius: 0.625rem;
|
||||||
|
--background: oklch(1 0 0);
|
||||||
|
--foreground: oklch(0.145 0 0);
|
||||||
|
--card: oklch(1 0 0);
|
||||||
|
--card-foreground: oklch(0.145 0 0);
|
||||||
|
--popover: oklch(1 0 0);
|
||||||
|
--popover-foreground: oklch(0.145 0 0);
|
||||||
|
--primary: oklch(0.205 0 0);
|
||||||
|
--primary-foreground: oklch(0.985 0 0);
|
||||||
|
--secondary: oklch(0.97 0 0);
|
||||||
|
--secondary-foreground: oklch(0.205 0 0);
|
||||||
|
--muted: oklch(0.97 0 0);
|
||||||
|
--muted-foreground: oklch(0.556 0 0);
|
||||||
|
--accent: oklch(0.97 0 0);
|
||||||
|
--accent-foreground: oklch(0.205 0 0);
|
||||||
|
--destructive: oklch(0.577 0.245 27.325);
|
||||||
|
--border: oklch(0.922 0 0);
|
||||||
|
--input: oklch(0.922 0 0);
|
||||||
|
--ring: oklch(0.708 0 0);
|
||||||
|
--chart-1: oklch(0.646 0.222 41.116);
|
||||||
|
--chart-2: oklch(0.6 0.118 184.704);
|
||||||
|
--chart-3: oklch(0.398 0.07 227.392);
|
||||||
|
--chart-4: oklch(0.828 0.189 84.429);
|
||||||
|
--chart-5: oklch(0.769 0.188 70.08);
|
||||||
|
--sidebar: oklch(0.985 0 0);
|
||||||
|
--sidebar-foreground: oklch(0.145 0 0);
|
||||||
|
--sidebar-primary: oklch(0.205 0 0);
|
||||||
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||||
|
--sidebar-accent: oklch(0.97 0 0);
|
||||||
|
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||||
|
--sidebar-border: oklch(0.922 0 0);
|
||||||
|
--sidebar-ring: oklch(0.708 0 0);
|
||||||
|
--color-1: oklch(66.2% 0.225 25.9);
|
||||||
|
--color-2: oklch(60.4% 0.26 302);
|
||||||
|
--color-3: oklch(69.6% 0.165 251);
|
||||||
|
--color-4: oklch(80.2% 0.134 225);
|
||||||
|
--color-5: oklch(90.7% 0.231 133);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: oklch(0.145 0 0);
|
||||||
|
--foreground: oklch(0.985 0 0);
|
||||||
|
--card: oklch(0.205 0 0);
|
||||||
|
--card-foreground: oklch(0.985 0 0);
|
||||||
|
--popover: oklch(0.205 0 0);
|
||||||
|
--popover-foreground: oklch(0.985 0 0);
|
||||||
|
--primary: oklch(0.922 0 0);
|
||||||
|
--primary-foreground: oklch(0.205 0 0);
|
||||||
|
--secondary: oklch(0.269 0 0);
|
||||||
|
--secondary-foreground: oklch(0.985 0 0);
|
||||||
|
--muted: oklch(0.269 0 0);
|
||||||
|
--muted-foreground: oklch(0.708 0 0);
|
||||||
|
--accent: oklch(0.269 0 0);
|
||||||
|
--accent-foreground: oklch(0.985 0 0);
|
||||||
|
--destructive: oklch(0.704 0.191 22.216);
|
||||||
|
--border: oklch(1 0 0 / 10%);
|
||||||
|
--input: oklch(1 0 0 / 15%);
|
||||||
|
--ring: oklch(0.556 0 0);
|
||||||
|
--chart-1: oklch(0.488 0.243 264.376);
|
||||||
|
--chart-2: oklch(0.696 0.17 162.48);
|
||||||
|
--chart-3: oklch(0.769 0.188 70.08);
|
||||||
|
--chart-4: oklch(0.627 0.265 303.9);
|
||||||
|
--chart-5: oklch(0.645 0.246 16.439);
|
||||||
|
--sidebar: oklch(0.205 0 0);
|
||||||
|
--sidebar-foreground: oklch(0.985 0 0);
|
||||||
|
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||||
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||||
|
--sidebar-accent: oklch(0.269 0 0);
|
||||||
|
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||||
|
--sidebar-border: oklch(1 0 0 / 10%);
|
||||||
|
--sidebar-ring: oklch(0.556 0 0);
|
||||||
|
--color-1: oklch(66.2% 0.225 25.9);
|
||||||
|
--color-2: oklch(60.4% 0.26 302);
|
||||||
|
--color-3: oklch(69.6% 0.165 251);
|
||||||
|
--color-4: oklch(80.2% 0.134 225);
|
||||||
|
--color-5: oklch(90.7% 0.231 133);
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border outline-ring/50;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
6
src/lib/utils.ts
Normal file
6
src/lib/utils.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { clsx, type ClassValue } from "clsx";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]) {
|
||||||
|
return twMerge(clsx(inputs));
|
||||||
|
}
|
||||||
@ -2,6 +2,8 @@ import { StrictMode } from "react";
|
|||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
import { App } from "./app";
|
import { App } from "./app";
|
||||||
|
import "@fontsource/alex-brush";
|
||||||
|
import "@fontsource-variable/merriweather";
|
||||||
|
|
||||||
createRoot(document.getElementById("root")!).render(
|
createRoot(document.getElementById("root")!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
|
|||||||
@ -7,7 +7,6 @@
|
|||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"types": ["vite/client"],
|
"types": ["vite/client"],
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
|
|
||||||
/* Bundler mode */
|
/* Bundler mode */
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
@ -15,14 +14,17 @@
|
|||||||
"moduleDetection": "force",
|
"moduleDetection": "force",
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
/* Linting */
|
/* Linting */
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"noUnusedParameters": true,
|
||||||
"erasableSyntaxOnly": true,
|
"erasableSyntaxOnly": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"noUncheckedSideEffectImports": true
|
"noUncheckedSideEffectImports": true,
|
||||||
|
/* Path aliases */
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"include": ["src"]
|
"include": ["src"]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,17 @@
|
|||||||
{
|
{
|
||||||
"files": [],
|
"files": [],
|
||||||
"references": [
|
"references": [
|
||||||
{ "path": "./tsconfig.app.json" },
|
{
|
||||||
{ "path": "./tsconfig.node.json" }
|
"path": "./tsconfig.app.json"
|
||||||
]
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.node.json"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,14 @@
|
|||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import react from "@vitejs/plugin-react";
|
import react from "@vitejs/plugin-react";
|
||||||
import tailwindcss from "@tailwindcss/vite";
|
import tailwindcss from "@tailwindcss/vite";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react(), tailwindcss()],
|
plugins: [react(), tailwindcss()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": path.resolve(__dirname, "./src"),
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user