diff --git a/www/package.json b/www/package.json index 8c8e271..f6f1c31 100644 --- a/www/package.json +++ b/www/package.json @@ -13,6 +13,8 @@ "@astrojs/react": "^4.3.0", "@radix-ui/react-icons": "^1.3.2", "@radix-ui/react-slot": "^1.2.3", + "@splinetool/react-spline": "^4.1.0", + "@splinetool/runtime": "^1.10.39", "@tailwindcss/vite": "^4.1.3", "@types/canvas-confetti": "^1.9.0", "@types/react": "^19.1.8", diff --git a/www/pnpm-lock.yaml b/www/pnpm-lock.yaml index 274df87..220de07 100644 --- a/www/pnpm-lock.yaml +++ b/www/pnpm-lock.yaml @@ -20,6 +20,12 @@ importers: '@radix-ui/react-slot': specifier: ^1.2.3 version: 1.2.3(@types/react@19.1.8)(react@19.1.0) + '@splinetool/react-spline': + specifier: ^4.1.0 + version: 4.1.0(@splinetool/runtime@1.10.39)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@splinetool/runtime': + specifier: ^1.10.39 + version: 1.10.39 '@tailwindcss/vite': specifier: ^4.1.3 version: 4.1.11(vite@6.3.5(@types/node@24.0.11)(jiti@2.4.2)(lightningcss@1.30.1)) @@ -631,6 +637,20 @@ packages: '@shikijs/vscode-textmate@10.0.2': resolution: {integrity: sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==} + '@splinetool/react-spline@4.1.0': + resolution: {integrity: sha512-Y379gm17gw+1nxT/YXTCJnVIWuu7tsUH1tp/YxsYb0pZnc9Gljk7Om4Kpq7WPq0bZ4zidVCxf6xn6jgDcbHifQ==} + peerDependencies: + '@splinetool/runtime': '*' + next: '>=14.2.0' + react: '*' + react-dom: '*' + peerDependenciesMeta: + next: + optional: true + + '@splinetool/runtime@1.10.39': + resolution: {integrity: sha512-Xjyq7+ON5uYWCARq7WteYQLwgmu/pO3Y3xwYaKI07Euz/7/GErDDNeD8OGt10DLG5JXuBeEddH+GNqvzJ7U5UQ==} + '@swc/helpers@0.5.17': resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} @@ -856,6 +876,9 @@ packages: blob-to-buffer@1.2.9: resolution: {integrity: sha512-BF033y5fN6OCofD3vgHmNtwZWRcq9NLyyxyILx9hfMy1sXYy4ojFl765hJ2lP0YaN2fuxPaLO2Vzzoxy0FLFFA==} + blurhash@2.0.5: + resolution: {integrity: sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w==} + boxen@8.0.1: resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} engines: {node: '>=18'} @@ -1321,6 +1344,9 @@ packages: resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==} engines: {node: '>= 12.0.0'} + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} @@ -1567,6 +1593,10 @@ packages: ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} + on-change@4.0.2: + resolution: {integrity: sha512-cMtCyuJmTx/bg2HCpHo3ZLeF7FZnBOapLqZHr2AlLeJ5Ul0Zu2mUJJz051Fdwu/Et2YW04ZD+TtU+gVy0ACNCA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + oniguruma-parser@0.12.1: resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} @@ -1637,6 +1667,9 @@ packages: peerDependencies: react: ^19.1.0 + react-merge-refs@2.1.1: + resolution: {integrity: sha512-jLQXJ/URln51zskhgppGJ2ub7b2WFKGq3cl3NYKtlHoTG+dN2q7EzWrn3hN3EgPsTMvpR9tpq5ijdp7YwFZkag==} + react-refresh@0.17.0: resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} engines: {node: '>=0.10.0'} @@ -1727,6 +1760,9 @@ packages: scheduler@0.26.0: resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + semver-compare@1.0.0: + resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} + semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -1803,6 +1839,9 @@ packages: resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} engines: {node: '>=18'} + thumbhash@0.1.1: + resolution: {integrity: sha512-kH5pKeIIBPQXAOni2AiY/Cu/NKdkFREdpH+TLdM0g6WA7RriCv0kPLgP731ady67MhTAqrVG/4mnEeibVuCJcg==} + tiny-inflate@1.0.3: resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} @@ -2630,6 +2669,21 @@ snapshots: '@shikijs/vscode-textmate@10.0.2': {} + '@splinetool/react-spline@4.1.0(@splinetool/runtime@1.10.39)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@splinetool/runtime': 1.10.39 + blurhash: 2.0.5 + lodash.debounce: 4.0.8 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-merge-refs: 2.1.1 + thumbhash: 0.1.1 + + '@splinetool/runtime@1.10.39': + dependencies: + on-change: 4.0.2 + semver-compare: 1.0.0 + '@swc/helpers@0.5.17': dependencies: tslib: 2.8.1 @@ -2927,6 +2981,8 @@ snapshots: blob-to-buffer@1.2.9: {} + blurhash@2.0.5: {} + boxen@8.0.1: dependencies: ansi-align: 3.0.1 @@ -3448,6 +3504,8 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.1 lightningcss-win32-x64-msvc: 1.30.1 + lodash.debounce@4.0.8: {} + longest-streak@3.1.0: {} lru-cache@10.4.3: {} @@ -3949,6 +4007,8 @@ snapshots: ohash@2.0.11: {} + on-change@4.0.2: {} + oniguruma-parser@0.12.1: {} oniguruma-to-es@4.3.3: @@ -4025,6 +4085,8 @@ snapshots: react: 19.1.0 scheduler: 0.26.0 + react-merge-refs@2.1.1: {} + react-refresh@0.17.0: {} react@19.1.0: {} @@ -4207,6 +4269,8 @@ snapshots: scheduler@0.26.0: {} + semver-compare@1.0.0: {} + semver@6.3.1: {} semver@7.7.2: {} @@ -4312,6 +4376,8 @@ snapshots: mkdirp: 3.0.1 yallist: 5.0.0 + thumbhash@0.1.1: {} + tiny-inflate@1.0.3: {} tinyexec@0.3.2: {} diff --git a/www/src/components/Hero.tsx b/www/src/components/Hero.tsx index 960e646..291ab6b 100644 --- a/www/src/components/Hero.tsx +++ b/www/src/components/Hero.tsx @@ -1,11 +1,12 @@ -import React from 'react'; -import { Button } from './ui/button'; -import { ArrowRight, Shield, RefreshCw } from 'lucide-react'; -import { GitHubLogoIcon } from '@radix-ui/react-icons'; +import { Button } from "./ui/button"; +import { ArrowRight, Shield, RefreshCw } from "lucide-react"; +import { GitHubLogoIcon } from "@radix-ui/react-icons"; +import Spline from "@splinetool/react-spline"; export function Hero() { + return ( -
+
{/* Elegant gradient background */}
@@ -13,26 +14,17 @@ export function Hero() {
-
-
-
- Gitea Mirror Logo - Gitea Mirror Logo -
-
- -

- - Keep Your Code - + {/* spline object */} +
+ +
+ {/* div to avoid clipping in lower screen heights */} + +
+

+ Keep Your Code
Safe & Synced @@ -40,8 +32,9 @@ export function Hero() {

- Automatically mirror your GitHub repositories to self-hosted Gitea. - Never lose access to your code with continuous backup and synchronization. + Automatically mirror your GitHub repositories to self-hosted Gitea. + Never lose access to your code with continuous backup and + synchronization.

@@ -59,20 +52,32 @@ export function Hero() {
+ {/* Call to action buttons */}
- -

); -} \ No newline at end of file +} diff --git a/www/src/styles/global.css b/www/src/styles/global.css index 6a0ea6c..4e6cf6d 100644 --- a/www/src/styles/global.css +++ b/www/src/styles/global.css @@ -1,10 +1,10 @@ -@import 'tailwindcss'; +@import "tailwindcss"; @import "tw-animate-css"; @custom-variant dark (&:is(.dark *)); @custom-media --xs (width >= 475px); -@import 'tailwindcss/theme' layer(theme); +@import "tailwindcss/theme" layer(theme); @theme inline { --radius-sm: calc(var(--radius) - 4px); @@ -134,78 +134,92 @@ /* Custom gradient utilities */ @layer utilities { .bg-gradient-radial { - background-image: radial-gradient(circle at center, var(--tw-gradient-stops)); + background-image: radial-gradient( + circle at center, + var(--tw-gradient-stops) + ); } - + .text-gradient { @apply bg-gradient-to-r bg-clip-text text-transparent; } - + .gradient-border { position: relative; - background: linear-gradient(var(--background), var(--background)) padding-box, - linear-gradient(to right, var(--tw-gradient-stops)) border-box; + background: linear-gradient(var(--background), var(--background)) + padding-box, + linear-gradient(to right, var(--tw-gradient-stops)) border-box; border: 2px solid transparent; } - + .glow-sm { box-shadow: 0 0 20px -5px var(--tw-shadow-color); } - + .glow-md { box-shadow: 0 0 40px -10px var(--tw-shadow-color); } - + .glow-lg { box-shadow: 0 0 60px -15px var(--tw-shadow-color); } - + /* Accent color utilities */ .text-accent-purple { color: var(--accent-purple); } - + .text-accent-teal { color: var(--accent-teal); } - + .text-accent-coral { color: var(--accent-coral); } - + .bg-accent-purple { background-color: var(--accent-purple); } - + .bg-accent-teal { background-color: var(--accent-teal); } - + .bg-accent-coral { background-color: var(--accent-coral); } - + .from-accent-purple\/10 { --tw-gradient-from: oklch(from var(--accent-purple) l c h / 0.1); } - + .from-accent-teal\/10 { --tw-gradient-from: oklch(from var(--accent-teal) l c h / 0.1); } - + .from-accent-coral\/10 { --tw-gradient-from: oklch(from var(--accent-coral) l c h / 0.1); } - + .to-accent-purple\/10 { --tw-gradient-to: oklch(from var(--accent-purple) l c h / 0.1); } - + .to-accent-teal\/10 { --tw-gradient-to: oklch(from var(--accent-teal) l c h / 0.1); } - + .to-accent-coral\/10 { --tw-gradient-to: oklch(from var(--accent-coral) l c h / 0.1); } + + @media (width >= 135rem /* 2160px */) { + .spline-object { + max-height: 70rem /* 960px */; + @apply -translate-y-40; + } + .clip-avoid { + height: 25rem /* 320px */; + } + } }