virastack 0.0.1 → 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/LICENSE +1 -1
- package/README.md +94 -18
- package/bin/virastack.js +7 -0
- package/package.json +43 -9
- package/src/commands/add.js +64 -0
- package/src/commands/init.js +286 -0
- package/src/i18n.js +231 -0
- package/src/index.js +64 -0
- package/src/utils/args.js +60 -0
- package/src/utils/copy-template.js +80 -0
- package/src/utils/exec.js +29 -0
- package/src/utils/git.js +38 -0
- package/src/utils/is-online.js +14 -0
- package/src/utils/package-info.js +12 -0
- package/src/utils/package-manager.js +33 -0
- package/src/utils/setup-env.js +25 -0
- package/src/utils/telemetry.js +86 -0
- package/src/utils/tools.js +48 -0
- package/src/utils/update-package-json.js +44 -0
- package/src/utils/update-readme.js +25 -0
- package/src/utils/validate-project-name.js +26 -0
- package/templates/nextjs/.changeset/README.md +8 -0
- package/templates/nextjs/.changeset/config.json +11 -0
- package/templates/nextjs/.env.example +6 -0
- package/templates/nextjs/.github/workflows/ci.yml +41 -0
- package/templates/nextjs/.husky/commit-msg +1 -0
- package/templates/nextjs/.husky/pre-commit +1 -0
- package/templates/nextjs/.husky/pre-push +1 -0
- package/templates/nextjs/.prettierignore +6 -0
- package/templates/nextjs/.prettierrc +42 -0
- package/templates/nextjs/.vscode/extensions.json +7 -0
- package/templates/nextjs/.vscode/settings.json +20 -0
- package/templates/nextjs/LICENSE +21 -0
- package/templates/nextjs/README.md +202 -0
- package/templates/nextjs/commitlint.config.js +6 -0
- package/templates/nextjs/eslint.config.mjs +27 -0
- package/templates/nextjs/knip.config.js +25 -0
- package/templates/nextjs/next.config.ts +39 -0
- package/templates/nextjs/package.json +115 -0
- package/templates/nextjs/pnpm-lock.yaml +6467 -0
- package/templates/nextjs/pnpm-workspace.yaml +3 -0
- package/templates/nextjs/postcss.config.mjs +7 -0
- package/templates/nextjs/public/favicon.ico +0 -0
- package/templates/nextjs/public/llms.txt +96 -0
- package/templates/nextjs/public/logo.png +0 -0
- package/templates/nextjs/public/logo.webp +0 -0
- package/templates/nextjs/public/og.png +0 -0
- package/templates/nextjs/src/app/error.tsx +34 -0
- package/templates/nextjs/src/app/favicon.ico +0 -0
- package/templates/nextjs/src/app/global-error.tsx +32 -0
- package/templates/nextjs/src/app/layout.tsx +37 -0
- package/templates/nextjs/src/app/loading.tsx +12 -0
- package/templates/nextjs/src/app/manifest.ts +22 -0
- package/templates/nextjs/src/app/not-found.tsx +18 -0
- package/templates/nextjs/src/app/page.tsx +5 -0
- package/templates/nextjs/src/app/robots.ts +13 -0
- package/templates/nextjs/src/app/sitemap.ts +14 -0
- package/templates/nextjs/src/components/shared/ThemeToggle.tsx +30 -0
- package/templates/nextjs/src/components/ui/avatar.tsx +54 -0
- package/templates/nextjs/src/components/ui/button.tsx +46 -0
- package/templates/nextjs/src/components/ui/dialog.tsx +107 -0
- package/templates/nextjs/src/components/ui/field.tsx +24 -0
- package/templates/nextjs/src/components/ui/index.ts +27 -0
- package/templates/nextjs/src/components/ui/input.tsx +23 -0
- package/templates/nextjs/src/components/ui/label.tsx +17 -0
- package/templates/nextjs/src/components/ui/skeleton.tsx +13 -0
- package/templates/nextjs/src/components/ui/table.tsx +94 -0
- package/templates/nextjs/src/components/ui/tabs.tsx +46 -0
- package/templates/nextjs/src/config/seo.config.ts +37 -0
- package/templates/nextjs/src/config/site.config.ts +15 -0
- package/templates/nextjs/src/constants/index.ts +5 -0
- package/templates/nextjs/src/env.ts +41 -0
- package/templates/nextjs/src/features/landing/api/get-users.api.ts +7 -0
- package/templates/nextjs/src/features/landing/api/index.ts +2 -0
- package/templates/nextjs/src/features/landing/api/query-keys.ts +7 -0
- package/templates/nextjs/src/features/landing/components/CartDemo.tsx +66 -0
- package/templates/nextjs/src/features/landing/components/Features.tsx +39 -0
- package/templates/nextjs/src/features/landing/components/Hero.tsx +65 -0
- package/templates/nextjs/src/features/landing/components/LandingPage.tsx +17 -0
- package/templates/nextjs/src/features/landing/components/ProjectFormDemo.tsx +62 -0
- package/templates/nextjs/src/features/landing/components/Reveal.tsx +83 -0
- package/templates/nextjs/src/features/landing/components/Showcase.tsx +66 -0
- package/templates/nextjs/src/features/landing/components/UsersDemo.tsx +188 -0
- package/templates/nextjs/src/features/landing/data/features.data.tsx +47 -0
- package/templates/nextjs/src/features/landing/data/index.ts +1 -0
- package/templates/nextjs/src/features/landing/helpers/get-initials.ts +15 -0
- package/templates/nextjs/src/features/landing/helpers/index.ts +7 -0
- package/templates/nextjs/src/features/landing/helpers/motion.ts +21 -0
- package/templates/nextjs/src/features/landing/hooks/index.ts +1 -0
- package/templates/nextjs/src/features/landing/hooks/use-users.ts +16 -0
- package/templates/nextjs/src/features/landing/icons/index.ts +6 -0
- package/templates/nextjs/src/features/landing/icons/nextjs.tsx +68 -0
- package/templates/nextjs/src/features/landing/icons/react-hook-form.tsx +18 -0
- package/templates/nextjs/src/features/landing/icons/react.tsx +20 -0
- package/templates/nextjs/src/features/landing/icons/tailwind.tsx +14 -0
- package/templates/nextjs/src/features/landing/icons/tanstack.tsx +321 -0
- package/templates/nextjs/src/features/landing/icons/typescript.tsx +16 -0
- package/templates/nextjs/src/features/landing/index.ts +1 -0
- package/templates/nextjs/src/features/landing/schemas/index.ts +1 -0
- package/templates/nextjs/src/features/landing/schemas/project.schema.ts +7 -0
- package/templates/nextjs/src/features/landing/stores/counter.store.ts +18 -0
- package/templates/nextjs/src/features/landing/stores/index.ts +1 -0
- package/templates/nextjs/src/features/landing/types/index.ts +1 -0
- package/templates/nextjs/src/features/landing/types/user.types.ts +9 -0
- package/templates/nextjs/src/helpers/format-date.ts +13 -0
- package/templates/nextjs/src/helpers/index.ts +1 -0
- package/templates/nextjs/src/hooks/index.ts +5 -0
- package/templates/nextjs/src/lib/api.ts +97 -0
- package/templates/nextjs/src/lib/query-client.ts +18 -0
- package/templates/nextjs/src/lib/utils.ts +12 -0
- package/templates/nextjs/src/providers/Providers.tsx +15 -0
- package/templates/nextjs/src/providers/QueryProvider.tsx +19 -0
- package/templates/nextjs/src/providers/ThemeProvider.tsx +9 -0
- package/templates/nextjs/src/providers/index.ts +1 -0
- package/templates/nextjs/src/proxy.ts +6 -0
- package/templates/nextjs/src/schemas/index.ts +5 -0
- package/templates/nextjs/src/stores/index.ts +5 -0
- package/templates/nextjs/src/styles/tailwind.css +122 -0
- package/templates/nextjs/src/types/site-config.types.ts +10 -0
- package/templates/nextjs/tsconfig.json +45 -0
- package/templates/tanstack/.changeset/README.md +8 -0
- package/templates/tanstack/.changeset/config.json +11 -0
- package/templates/tanstack/.env.example +6 -0
- package/templates/tanstack/.github/workflows/ci.yml +41 -0
- package/templates/tanstack/.husky/commit-msg +1 -0
- package/templates/tanstack/.husky/pre-commit +1 -0
- package/templates/tanstack/.husky/pre-push +1 -0
- package/templates/tanstack/.prettierignore +7 -0
- package/templates/tanstack/.prettierrc +41 -0
- package/templates/tanstack/.vscode/extensions.json +7 -0
- package/templates/tanstack/.vscode/settings.json +22 -0
- package/templates/tanstack/LICENSE +21 -0
- package/templates/tanstack/README.md +204 -0
- package/templates/tanstack/commitlint.config.js +6 -0
- package/templates/tanstack/eslint.config.js +28 -0
- package/templates/tanstack/knip.config.js +22 -0
- package/templates/tanstack/package.json +130 -0
- package/templates/tanstack/pnpm-lock.yaml +5835 -0
- package/templates/tanstack/pnpm-workspace.yaml +3 -0
- package/templates/tanstack/public/favicon.ico +0 -0
- package/templates/tanstack/public/llms.txt +86 -0
- package/templates/tanstack/public/logo.png +0 -0
- package/templates/tanstack/public/logo.webp +0 -0
- package/templates/tanstack/public/og.png +0 -0
- package/templates/tanstack/src/components/shared/DefaultCatchBoundary.tsx +36 -0
- package/templates/tanstack/src/components/shared/NotFound.tsx +18 -0
- package/templates/tanstack/src/components/shared/Pending.tsx +13 -0
- package/templates/tanstack/src/components/shared/ThemeToggle.tsx +31 -0
- package/templates/tanstack/src/components/ui/avatar.tsx +52 -0
- package/templates/tanstack/src/components/ui/button.tsx +47 -0
- package/templates/tanstack/src/components/ui/dialog.tsx +107 -0
- package/templates/tanstack/src/components/ui/field.tsx +24 -0
- package/templates/tanstack/src/components/ui/index.ts +27 -0
- package/templates/tanstack/src/components/ui/input.tsx +23 -0
- package/templates/tanstack/src/components/ui/label.tsx +17 -0
- package/templates/tanstack/src/components/ui/skeleton.tsx +13 -0
- package/templates/tanstack/src/components/ui/table.tsx +94 -0
- package/templates/tanstack/src/components/ui/tabs.tsx +46 -0
- package/templates/tanstack/src/config/seo.config.ts +52 -0
- package/templates/tanstack/src/config/site.config.ts +15 -0
- package/templates/tanstack/src/constants/index.ts +5 -0
- package/templates/tanstack/src/env.ts +47 -0
- package/templates/tanstack/src/features/landing/api/get-users.api.ts +7 -0
- package/templates/tanstack/src/features/landing/api/index.ts +2 -0
- package/templates/tanstack/src/features/landing/api/query-keys.ts +7 -0
- package/templates/tanstack/src/features/landing/components/CartDemo.tsx +66 -0
- package/templates/tanstack/src/features/landing/components/Features.tsx +39 -0
- package/templates/tanstack/src/features/landing/components/Hero.tsx +63 -0
- package/templates/tanstack/src/features/landing/components/LandingPage.tsx +17 -0
- package/templates/tanstack/src/features/landing/components/ProjectFormDemo.tsx +63 -0
- package/templates/tanstack/src/features/landing/components/Reveal.tsx +83 -0
- package/templates/tanstack/src/features/landing/components/Showcase.tsx +66 -0
- package/templates/tanstack/src/features/landing/components/UsersDemo.tsx +191 -0
- package/templates/tanstack/src/features/landing/data/features.data.tsx +45 -0
- package/templates/tanstack/src/features/landing/data/index.ts +1 -0
- package/templates/tanstack/src/features/landing/helpers/get-initials.ts +15 -0
- package/templates/tanstack/src/features/landing/helpers/index.ts +7 -0
- package/templates/tanstack/src/features/landing/helpers/motion.ts +21 -0
- package/templates/tanstack/src/features/landing/hooks/index.ts +1 -0
- package/templates/tanstack/src/features/landing/hooks/use-users.ts +19 -0
- package/templates/tanstack/src/features/landing/icons/index.ts +5 -0
- package/templates/tanstack/src/features/landing/icons/react-hook-form.tsx +18 -0
- package/templates/tanstack/src/features/landing/icons/react.tsx +20 -0
- package/templates/tanstack/src/features/landing/icons/tailwind.tsx +14 -0
- package/templates/tanstack/src/features/landing/icons/tanstack.tsx +321 -0
- package/templates/tanstack/src/features/landing/icons/typescript.tsx +16 -0
- package/templates/tanstack/src/features/landing/index.ts +1 -0
- package/templates/tanstack/src/features/landing/schemas/index.ts +1 -0
- package/templates/tanstack/src/features/landing/schemas/project.schema.ts +7 -0
- package/templates/tanstack/src/features/landing/stores/counter.store.ts +18 -0
- package/templates/tanstack/src/features/landing/stores/index.ts +1 -0
- package/templates/tanstack/src/features/landing/types/index.ts +1 -0
- package/templates/tanstack/src/features/landing/types/user.types.ts +9 -0
- package/templates/tanstack/src/helpers/format-date.ts +13 -0
- package/templates/tanstack/src/helpers/index.ts +1 -0
- package/templates/tanstack/src/hooks/index.ts +5 -0
- package/templates/tanstack/src/lib/api.ts +97 -0
- package/templates/tanstack/src/lib/query-client.ts +18 -0
- package/templates/tanstack/src/lib/utils.ts +13 -0
- package/templates/tanstack/src/providers/Providers.tsx +12 -0
- package/templates/tanstack/src/providers/ThemeProvider.tsx +98 -0
- package/templates/tanstack/src/providers/devtools.tsx +6 -0
- package/templates/tanstack/src/providers/index.ts +1 -0
- package/templates/tanstack/src/providers/root-provider.ts +9 -0
- package/templates/tanstack/src/routeTree.gen.ts +123 -0
- package/templates/tanstack/src/router.tsx +27 -0
- package/templates/tanstack/src/routes/__root.tsx +78 -0
- package/templates/tanstack/src/routes/index.tsx +13 -0
- package/templates/tanstack/src/routes/robots[.]txt.ts +21 -0
- package/templates/tanstack/src/routes/site[.]webmanifest.ts +32 -0
- package/templates/tanstack/src/routes/sitemap[.]xml.ts +27 -0
- package/templates/tanstack/src/schemas/index.ts +5 -0
- package/templates/tanstack/src/start.ts +20 -0
- package/templates/tanstack/src/stores/index.ts +5 -0
- package/templates/tanstack/src/styles/tailwind.css +123 -0
- package/templates/tanstack/src/types/site-config.types.ts +10 -0
- package/templates/tanstack/tsconfig.json +25 -0
- package/templates/tanstack/tsr.config.json +3 -0
- package/templates/tanstack/vite.config.ts +12 -0
- package/templates-i18n/nextjs/.changeset/README.md +8 -0
- package/templates-i18n/nextjs/.changeset/config.json +11 -0
- package/templates-i18n/nextjs/.env.example +6 -0
- package/templates-i18n/nextjs/.github/workflows/ci.yml +41 -0
- package/templates-i18n/nextjs/.husky/commit-msg +1 -0
- package/templates-i18n/nextjs/.husky/pre-commit +1 -0
- package/templates-i18n/nextjs/.husky/pre-push +1 -0
- package/templates-i18n/nextjs/.prettierignore +6 -0
- package/templates-i18n/nextjs/.prettierrc +42 -0
- package/templates-i18n/nextjs/.vscode/extensions.json +7 -0
- package/templates-i18n/nextjs/.vscode/settings.json +20 -0
- package/templates-i18n/nextjs/LICENSE +21 -0
- package/templates-i18n/nextjs/README.md +202 -0
- package/templates-i18n/nextjs/commitlint.config.js +6 -0
- package/templates-i18n/nextjs/eslint.config.mjs +27 -0
- package/templates-i18n/nextjs/knip.config.js +29 -0
- package/templates-i18n/nextjs/next.config.ts +42 -0
- package/templates-i18n/nextjs/package.json +117 -0
- package/templates-i18n/nextjs/pnpm-lock.yaml +6854 -0
- package/templates-i18n/nextjs/pnpm-workspace.yaml +3 -0
- package/templates-i18n/nextjs/postcss.config.mjs +7 -0
- package/templates-i18n/nextjs/public/favicon.ico +0 -0
- package/templates-i18n/nextjs/public/llms.txt +96 -0
- package/templates-i18n/nextjs/public/logo.png +0 -0
- package/templates-i18n/nextjs/public/logo.webp +0 -0
- package/templates-i18n/nextjs/public/og.png +0 -0
- package/templates-i18n/nextjs/src/app/[locale]/error.tsx +34 -0
- package/templates-i18n/nextjs/src/app/[locale]/layout.tsx +60 -0
- package/templates-i18n/nextjs/src/app/[locale]/loading.tsx +12 -0
- package/templates-i18n/nextjs/src/app/[locale]/not-found.tsx +18 -0
- package/templates-i18n/nextjs/src/app/[locale]/page.tsx +10 -0
- package/templates-i18n/nextjs/src/app/favicon.ico +0 -0
- package/templates-i18n/nextjs/src/app/global-error.tsx +32 -0
- package/templates-i18n/nextjs/src/app/manifest.ts +22 -0
- package/templates-i18n/nextjs/src/app/robots.ts +13 -0
- package/templates-i18n/nextjs/src/app/sitemap.ts +14 -0
- package/templates-i18n/nextjs/src/components/shared/LanguageSwitcher.tsx +48 -0
- package/templates-i18n/nextjs/src/components/shared/ThemeToggle.tsx +30 -0
- package/templates-i18n/nextjs/src/components/ui/avatar.tsx +54 -0
- package/templates-i18n/nextjs/src/components/ui/button.tsx +46 -0
- package/templates-i18n/nextjs/src/components/ui/dialog.tsx +107 -0
- package/templates-i18n/nextjs/src/components/ui/field.tsx +24 -0
- package/templates-i18n/nextjs/src/components/ui/index.ts +27 -0
- package/templates-i18n/nextjs/src/components/ui/input.tsx +23 -0
- package/templates-i18n/nextjs/src/components/ui/label.tsx +17 -0
- package/templates-i18n/nextjs/src/components/ui/skeleton.tsx +13 -0
- package/templates-i18n/nextjs/src/components/ui/table.tsx +94 -0
- package/templates-i18n/nextjs/src/components/ui/tabs.tsx +46 -0
- package/templates-i18n/nextjs/src/config/seo.config.ts +37 -0
- package/templates-i18n/nextjs/src/config/site.config.ts +15 -0
- package/templates-i18n/nextjs/src/constants/index.ts +5 -0
- package/templates-i18n/nextjs/src/env.ts +41 -0
- package/templates-i18n/nextjs/src/features/landing/api/get-users.api.ts +7 -0
- package/templates-i18n/nextjs/src/features/landing/api/index.ts +2 -0
- package/templates-i18n/nextjs/src/features/landing/api/query-keys.ts +7 -0
- package/templates-i18n/nextjs/src/features/landing/components/CartDemo.tsx +68 -0
- package/templates-i18n/nextjs/src/features/landing/components/Features.tsx +84 -0
- package/templates-i18n/nextjs/src/features/landing/components/Hero.tsx +76 -0
- package/templates-i18n/nextjs/src/features/landing/components/LandingPage.tsx +17 -0
- package/templates-i18n/nextjs/src/features/landing/components/ProjectFormDemo.tsx +64 -0
- package/templates-i18n/nextjs/src/features/landing/components/Reveal.tsx +83 -0
- package/templates-i18n/nextjs/src/features/landing/components/Showcase.tsx +83 -0
- package/templates-i18n/nextjs/src/features/landing/components/UsersDemo.tsx +191 -0
- package/templates-i18n/nextjs/src/features/landing/data/features.data.tsx +47 -0
- package/templates-i18n/nextjs/src/features/landing/data/index.ts +1 -0
- package/templates-i18n/nextjs/src/features/landing/helpers/get-initials.ts +15 -0
- package/templates-i18n/nextjs/src/features/landing/helpers/index.ts +7 -0
- package/templates-i18n/nextjs/src/features/landing/helpers/motion.ts +21 -0
- package/templates-i18n/nextjs/src/features/landing/hooks/index.ts +1 -0
- package/templates-i18n/nextjs/src/features/landing/hooks/use-users.ts +16 -0
- package/templates-i18n/nextjs/src/features/landing/icons/index.ts +6 -0
- package/templates-i18n/nextjs/src/features/landing/icons/nextjs.tsx +68 -0
- package/templates-i18n/nextjs/src/features/landing/icons/react-hook-form.tsx +18 -0
- package/templates-i18n/nextjs/src/features/landing/icons/react.tsx +20 -0
- package/templates-i18n/nextjs/src/features/landing/icons/tailwind.tsx +14 -0
- package/templates-i18n/nextjs/src/features/landing/icons/tanstack.tsx +321 -0
- package/templates-i18n/nextjs/src/features/landing/icons/typescript.tsx +16 -0
- package/templates-i18n/nextjs/src/features/landing/index.ts +1 -0
- package/templates-i18n/nextjs/src/features/landing/schemas/index.ts +1 -0
- package/templates-i18n/nextjs/src/features/landing/schemas/project.schema.ts +9 -0
- package/templates-i18n/nextjs/src/features/landing/stores/counter.store.ts +18 -0
- package/templates-i18n/nextjs/src/features/landing/stores/index.ts +1 -0
- package/templates-i18n/nextjs/src/features/landing/types/index.ts +1 -0
- package/templates-i18n/nextjs/src/features/landing/types/user.types.ts +9 -0
- package/templates-i18n/nextjs/src/helpers/format-date.ts +13 -0
- package/templates-i18n/nextjs/src/helpers/index.ts +1 -0
- package/templates-i18n/nextjs/src/hooks/index.ts +5 -0
- package/templates-i18n/nextjs/src/i18n/request.ts +16 -0
- package/templates-i18n/nextjs/src/i18n/routing.ts +11 -0
- package/templates-i18n/nextjs/src/lib/api.ts +97 -0
- package/templates-i18n/nextjs/src/lib/query-client.ts +18 -0
- package/templates-i18n/nextjs/src/lib/utils.ts +12 -0
- package/templates-i18n/nextjs/src/messages/en.json +55 -0
- package/templates-i18n/nextjs/src/messages/tr.json +55 -0
- package/templates-i18n/nextjs/src/providers/Providers.tsx +15 -0
- package/templates-i18n/nextjs/src/providers/QueryProvider.tsx +19 -0
- package/templates-i18n/nextjs/src/providers/ThemeProvider.tsx +9 -0
- package/templates-i18n/nextjs/src/providers/index.ts +1 -0
- package/templates-i18n/nextjs/src/proxy.ts +16 -0
- package/templates-i18n/nextjs/src/schemas/index.ts +5 -0
- package/templates-i18n/nextjs/src/stores/index.ts +5 -0
- package/templates-i18n/nextjs/src/styles/tailwind.css +122 -0
- package/templates-i18n/nextjs/src/types/site-config.types.ts +10 -0
- package/templates-i18n/nextjs/tsconfig.json +45 -0
- package/templates-i18n/tanstack/.changeset/README.md +8 -0
- package/templates-i18n/tanstack/.changeset/config.json +11 -0
- package/templates-i18n/tanstack/.env.example +6 -0
- package/templates-i18n/tanstack/.github/workflows/ci.yml +41 -0
- package/templates-i18n/tanstack/.husky/commit-msg +1 -0
- package/templates-i18n/tanstack/.husky/pre-commit +1 -0
- package/templates-i18n/tanstack/.husky/pre-push +1 -0
- package/templates-i18n/tanstack/.prettierignore +7 -0
- package/templates-i18n/tanstack/.prettierrc +41 -0
- package/templates-i18n/tanstack/.vscode/extensions.json +7 -0
- package/templates-i18n/tanstack/.vscode/settings.json +22 -0
- package/templates-i18n/tanstack/LICENSE +21 -0
- package/templates-i18n/tanstack/README.md +204 -0
- package/templates-i18n/tanstack/commitlint.config.js +6 -0
- package/templates-i18n/tanstack/eslint.config.js +29 -0
- package/templates-i18n/tanstack/knip.config.js +26 -0
- package/templates-i18n/tanstack/messages/en.json +65 -0
- package/templates-i18n/tanstack/messages/tr.json +65 -0
- package/templates-i18n/tanstack/package.json +132 -0
- package/templates-i18n/tanstack/pnpm-lock.yaml +5998 -0
- package/templates-i18n/tanstack/pnpm-workspace.yaml +3 -0
- package/templates-i18n/tanstack/project.inlang/README.md +157 -0
- package/templates-i18n/tanstack/project.inlang/settings.json +9 -0
- package/templates-i18n/tanstack/public/favicon.ico +0 -0
- package/templates-i18n/tanstack/public/llms.txt +86 -0
- package/templates-i18n/tanstack/public/logo.png +0 -0
- package/templates-i18n/tanstack/public/logo.webp +0 -0
- package/templates-i18n/tanstack/public/og.png +0 -0
- package/templates-i18n/tanstack/src/components/shared/DefaultCatchBoundary.tsx +36 -0
- package/templates-i18n/tanstack/src/components/shared/LanguageSwitcher.tsx +57 -0
- package/templates-i18n/tanstack/src/components/shared/NotFound.tsx +18 -0
- package/templates-i18n/tanstack/src/components/shared/Pending.tsx +13 -0
- package/templates-i18n/tanstack/src/components/shared/ThemeToggle.tsx +31 -0
- package/templates-i18n/tanstack/src/components/ui/avatar.tsx +52 -0
- package/templates-i18n/tanstack/src/components/ui/button.tsx +47 -0
- package/templates-i18n/tanstack/src/components/ui/dialog.tsx +107 -0
- package/templates-i18n/tanstack/src/components/ui/field.tsx +24 -0
- package/templates-i18n/tanstack/src/components/ui/index.ts +27 -0
- package/templates-i18n/tanstack/src/components/ui/input.tsx +23 -0
- package/templates-i18n/tanstack/src/components/ui/label.tsx +17 -0
- package/templates-i18n/tanstack/src/components/ui/skeleton.tsx +13 -0
- package/templates-i18n/tanstack/src/components/ui/table.tsx +94 -0
- package/templates-i18n/tanstack/src/components/ui/tabs.tsx +46 -0
- package/templates-i18n/tanstack/src/config/seo.config.ts +52 -0
- package/templates-i18n/tanstack/src/config/site.config.ts +15 -0
- package/templates-i18n/tanstack/src/constants/index.ts +5 -0
- package/templates-i18n/tanstack/src/env.ts +47 -0
- package/templates-i18n/tanstack/src/features/landing/api/get-users.api.ts +7 -0
- package/templates-i18n/tanstack/src/features/landing/api/index.ts +2 -0
- package/templates-i18n/tanstack/src/features/landing/api/query-keys.ts +7 -0
- package/templates-i18n/tanstack/src/features/landing/components/CartDemo.tsx +67 -0
- package/templates-i18n/tanstack/src/features/landing/components/Features.tsx +79 -0
- package/templates-i18n/tanstack/src/features/landing/components/Hero.tsx +68 -0
- package/templates-i18n/tanstack/src/features/landing/components/LandingPage.tsx +17 -0
- package/templates-i18n/tanstack/src/features/landing/components/ProjectFormDemo.tsx +64 -0
- package/templates-i18n/tanstack/src/features/landing/components/Reveal.tsx +83 -0
- package/templates-i18n/tanstack/src/features/landing/components/Showcase.tsx +81 -0
- package/templates-i18n/tanstack/src/features/landing/components/UsersDemo.tsx +191 -0
- package/templates-i18n/tanstack/src/features/landing/data/features.data.tsx +45 -0
- package/templates-i18n/tanstack/src/features/landing/data/index.ts +1 -0
- package/templates-i18n/tanstack/src/features/landing/helpers/get-initials.ts +15 -0
- package/templates-i18n/tanstack/src/features/landing/helpers/index.ts +7 -0
- package/templates-i18n/tanstack/src/features/landing/helpers/motion.ts +21 -0
- package/templates-i18n/tanstack/src/features/landing/hooks/index.ts +1 -0
- package/templates-i18n/tanstack/src/features/landing/hooks/use-users.ts +19 -0
- package/templates-i18n/tanstack/src/features/landing/icons/index.ts +5 -0
- package/templates-i18n/tanstack/src/features/landing/icons/react-hook-form.tsx +18 -0
- package/templates-i18n/tanstack/src/features/landing/icons/react.tsx +20 -0
- package/templates-i18n/tanstack/src/features/landing/icons/tailwind.tsx +14 -0
- package/templates-i18n/tanstack/src/features/landing/icons/tanstack.tsx +321 -0
- package/templates-i18n/tanstack/src/features/landing/icons/typescript.tsx +16 -0
- package/templates-i18n/tanstack/src/features/landing/index.ts +1 -0
- package/templates-i18n/tanstack/src/features/landing/schemas/index.ts +1 -0
- package/templates-i18n/tanstack/src/features/landing/schemas/project.schema.ts +10 -0
- package/templates-i18n/tanstack/src/features/landing/stores/counter.store.ts +18 -0
- package/templates-i18n/tanstack/src/features/landing/stores/index.ts +1 -0
- package/templates-i18n/tanstack/src/features/landing/types/index.ts +1 -0
- package/templates-i18n/tanstack/src/features/landing/types/user.types.ts +9 -0
- package/templates-i18n/tanstack/src/helpers/format-date.ts +13 -0
- package/templates-i18n/tanstack/src/helpers/index.ts +1 -0
- package/templates-i18n/tanstack/src/hooks/index.ts +5 -0
- package/templates-i18n/tanstack/src/lib/api.ts +97 -0
- package/templates-i18n/tanstack/src/lib/query-client.ts +18 -0
- package/templates-i18n/tanstack/src/lib/utils.ts +13 -0
- package/templates-i18n/tanstack/src/paraglide/README.md +162 -0
- package/templates-i18n/tanstack/src/providers/Providers.tsx +12 -0
- package/templates-i18n/tanstack/src/providers/ThemeProvider.tsx +98 -0
- package/templates-i18n/tanstack/src/providers/devtools.tsx +6 -0
- package/templates-i18n/tanstack/src/providers/index.ts +1 -0
- package/templates-i18n/tanstack/src/providers/root-provider.ts +9 -0
- package/templates-i18n/tanstack/src/routeTree.gen.ts +179 -0
- package/templates-i18n/tanstack/src/router.tsx +27 -0
- package/templates-i18n/tanstack/src/routes/$lang/index.tsx +13 -0
- package/templates-i18n/tanstack/src/routes/$lang.tsx +22 -0
- package/templates-i18n/tanstack/src/routes/__root.tsx +80 -0
- package/templates-i18n/tanstack/src/routes/index.tsx +13 -0
- package/templates-i18n/tanstack/src/routes/robots[.]txt.ts +21 -0
- package/templates-i18n/tanstack/src/routes/site[.]webmanifest.ts +32 -0
- package/templates-i18n/tanstack/src/routes/sitemap[.]xml.ts +27 -0
- package/templates-i18n/tanstack/src/schemas/index.ts +5 -0
- package/templates-i18n/tanstack/src/server.ts +10 -0
- package/templates-i18n/tanstack/src/start.ts +20 -0
- package/templates-i18n/tanstack/src/stores/index.ts +5 -0
- package/templates-i18n/tanstack/src/styles/tailwind.css +123 -0
- package/templates-i18n/tanstack/src/types/site-config.types.ts +10 -0
- package/templates-i18n/tanstack/tsconfig.json +26 -0
- package/templates-i18n/tanstack/tsr.config.json +3 -0
- package/templates-i18n/tanstack/vite.config.ts +31 -0
- package/index.js +0 -3
- package/public/start.jpg +0 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ComponentProps } from "react";
|
|
2
|
+
|
|
3
|
+
import { Tabs as BaseTabs } from "@base-ui/react/tabs";
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
export function Tabs({ className, ...props }: ComponentProps<typeof BaseTabs.Root>) {
|
|
8
|
+
return (
|
|
9
|
+
<BaseTabs.Root data-slot="tabs" className={cn("flex flex-col gap-3", className)} {...props} />
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function TabsList({ className, ...props }: ComponentProps<typeof BaseTabs.List>) {
|
|
14
|
+
return (
|
|
15
|
+
<BaseTabs.List
|
|
16
|
+
data-slot="tabs-list"
|
|
17
|
+
className={cn(
|
|
18
|
+
"relative inline-flex h-9 w-fit items-center rounded-lg bg-muted p-[3px] text-muted-foreground",
|
|
19
|
+
className,
|
|
20
|
+
)}
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function TabsTrigger({ className, ...props }: ComponentProps<typeof BaseTabs.Tab>) {
|
|
27
|
+
return (
|
|
28
|
+
<BaseTabs.Tab
|
|
29
|
+
data-slot="tabs-trigger"
|
|
30
|
+
className={cn(
|
|
31
|
+
"inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none",
|
|
32
|
+
"text-foreground data-[selected]:bg-background data-[selected]:shadow-sm",
|
|
33
|
+
"focus-visible:ring-[3px] focus-visible:ring-ring/50",
|
|
34
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
35
|
+
className,
|
|
36
|
+
)}
|
|
37
|
+
{...props}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function TabsContent({ className, ...props }: ComponentProps<typeof BaseTabs.Panel>) {
|
|
43
|
+
return (
|
|
44
|
+
<BaseTabs.Panel data-slot="tabs-content" className={cn("outline-none", className)} {...props} />
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { siteConfig } from "@/config/site.config";
|
|
2
|
+
|
|
3
|
+
/** Shared SEO fields used by the root route `head()` and social meta tags. */
|
|
4
|
+
const defaultSeo = {
|
|
5
|
+
title: siteConfig.name,
|
|
6
|
+
titleTemplate: `%s · ${siteConfig.name}`,
|
|
7
|
+
description: siteConfig.description,
|
|
8
|
+
keywords: ["TanStack Start", "React", "TypeScript", "Tailwind CSS", "ViraStack", "Boilerplate"],
|
|
9
|
+
authors: [{ name: "ViraStack", url: siteConfig.url }],
|
|
10
|
+
creator: "ViraStack",
|
|
11
|
+
openGraph: {
|
|
12
|
+
type: "website",
|
|
13
|
+
locale: "en_US",
|
|
14
|
+
url: siteConfig.url,
|
|
15
|
+
title: siteConfig.name,
|
|
16
|
+
description: siteConfig.description,
|
|
17
|
+
siteName: siteConfig.name,
|
|
18
|
+
images: [{ url: siteConfig.ogImage, width: 1200, height: 630, alt: siteConfig.name }],
|
|
19
|
+
},
|
|
20
|
+
twitter: {
|
|
21
|
+
card: "summary_large_image",
|
|
22
|
+
title: siteConfig.name,
|
|
23
|
+
description: siteConfig.description,
|
|
24
|
+
images: [siteConfig.ogImage],
|
|
25
|
+
},
|
|
26
|
+
} as const;
|
|
27
|
+
|
|
28
|
+
/** Flatten `defaultSeo` into TanStack Router `head().meta` entries. */
|
|
29
|
+
export function seoHeadMeta() {
|
|
30
|
+
const og = defaultSeo.openGraph;
|
|
31
|
+
const twitter = defaultSeo.twitter;
|
|
32
|
+
|
|
33
|
+
return [
|
|
34
|
+
{ title: defaultSeo.title },
|
|
35
|
+
{ name: "description", content: defaultSeo.description },
|
|
36
|
+
{ name: "keywords", content: defaultSeo.keywords.join(", ") },
|
|
37
|
+
{ name: "author", content: defaultSeo.creator },
|
|
38
|
+
{ name: "creator", content: defaultSeo.creator },
|
|
39
|
+
{ name: "robots", content: "index, follow" },
|
|
40
|
+
{ property: "og:type", content: og.type },
|
|
41
|
+
{ property: "og:locale", content: og.locale },
|
|
42
|
+
{ property: "og:url", content: og.url },
|
|
43
|
+
{ property: "og:title", content: og.title },
|
|
44
|
+
{ property: "og:description", content: og.description },
|
|
45
|
+
{ property: "og:site_name", content: og.siteName },
|
|
46
|
+
{ property: "og:image", content: og.images[0].url },
|
|
47
|
+
{ name: "twitter:card", content: twitter.card },
|
|
48
|
+
{ name: "twitter:title", content: twitter.title },
|
|
49
|
+
{ name: "twitter:description", content: twitter.description },
|
|
50
|
+
{ name: "twitter:image", content: twitter.images[0] },
|
|
51
|
+
];
|
|
52
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SiteConfig } from "@/types/site-config.types";
|
|
2
|
+
import { env } from "@/env";
|
|
3
|
+
|
|
4
|
+
// FIXME: replace with your own site metadata.
|
|
5
|
+
export const siteConfig: SiteConfig = {
|
|
6
|
+
name: env.VITE_APP_NAME,
|
|
7
|
+
description:
|
|
8
|
+
"Premium TanStack Start boilerplate built for scalable UI/UX, clean architecture, and agent-ready developer experience.",
|
|
9
|
+
url: env.VITE_APP_URL,
|
|
10
|
+
ogImage: `${env.VITE_APP_URL}/og.png`,
|
|
11
|
+
links: {
|
|
12
|
+
github: "https://github.com/virastack/start",
|
|
13
|
+
twitter: "https://x.com/virastack",
|
|
14
|
+
},
|
|
15
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Type-safe environment variable schema.
|
|
5
|
+
*
|
|
6
|
+
* Client-exposed variables MUST be prefixed with `VITE_` (Vite inlines these
|
|
7
|
+
* at build time). Server-only variables have no prefix and must never be
|
|
8
|
+
* read via `import.meta.env` on the client.
|
|
9
|
+
*/
|
|
10
|
+
const serverSchema = z.object({
|
|
11
|
+
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const clientSchema = z.object({
|
|
15
|
+
VITE_APP_URL: z.string().url().default("http://localhost:3000"),
|
|
16
|
+
VITE_APP_NAME: z.string().min(1).default("ViraStack Start - TanStack Edition"),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
function readEnv(key: string): string | undefined {
|
|
20
|
+
if (typeof process !== "undefined" && process.env[key] != null) {
|
|
21
|
+
return process.env[key];
|
|
22
|
+
}
|
|
23
|
+
return (import.meta.env as Record<string, string | undefined>)[key];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const serverParsed = serverSchema.safeParse({
|
|
27
|
+
NODE_ENV: readEnv("NODE_ENV"),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const clientParsed = clientSchema.safeParse({
|
|
31
|
+
VITE_APP_URL: readEnv("VITE_APP_URL"),
|
|
32
|
+
VITE_APP_NAME: readEnv("VITE_APP_NAME"),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (!serverParsed.success || !clientParsed.success) {
|
|
36
|
+
console.error(
|
|
37
|
+
"❌ Invalid environment variables:",
|
|
38
|
+
serverParsed.error?.flatten().fieldErrors,
|
|
39
|
+
clientParsed.error?.flatten().fieldErrors,
|
|
40
|
+
);
|
|
41
|
+
throw new Error("Invalid environment variables. Check src/env.ts");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const env = {
|
|
45
|
+
...serverParsed.data,
|
|
46
|
+
...clientParsed.data,
|
|
47
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const userKeys = {
|
|
2
|
+
all: ["users"] as const,
|
|
3
|
+
lists: () => [...userKeys.all, "list"] as const,
|
|
4
|
+
list: (filters?: object) => [...userKeys.lists(), { filters }] as const,
|
|
5
|
+
details: () => [...userKeys.all, "detail"] as const,
|
|
6
|
+
detail: (id: string) => [...userKeys.details(), id] as const,
|
|
7
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { MinusIcon, PlusIcon, ShirtIcon, Trash2Icon } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
import { Button } from "@/components/ui/button";
|
|
6
|
+
import { useCounterStore } from "@/features/landing/stores";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Demonstrates global client state with Zustand via a mini cart UI.
|
|
10
|
+
*/
|
|
11
|
+
export function CartDemo() {
|
|
12
|
+
const { count, increment, decrement, reset } = useCounterStore();
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<div className="flex flex-col gap-4">
|
|
16
|
+
<div className="flex items-center gap-4 rounded-xl border border-border/50 bg-background/50 p-3">
|
|
17
|
+
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary">
|
|
18
|
+
<ShirtIcon className="size-6" />
|
|
19
|
+
</div>
|
|
20
|
+
<div className="flex flex-1 flex-col">
|
|
21
|
+
<h5 className="font-medium text-foreground">Cotton T-Shirt</h5>
|
|
22
|
+
<p className="text-sm text-muted-foreground">White / XL</p>
|
|
23
|
+
</div>
|
|
24
|
+
<div className="flex flex-col items-end gap-1">
|
|
25
|
+
<span className="font-semibold text-foreground">{count * 350}</span>
|
|
26
|
+
<span className="text-xs text-muted-foreground">Total</span>
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div className="flex items-center justify-between">
|
|
31
|
+
<div className="flex items-center gap-2">
|
|
32
|
+
<Button
|
|
33
|
+
variant="outline"
|
|
34
|
+
size="icon"
|
|
35
|
+
className="h-8 w-8 rounded-full"
|
|
36
|
+
aria-label="Decrement"
|
|
37
|
+
onClick={decrement}
|
|
38
|
+
disabled={count === 0}
|
|
39
|
+
>
|
|
40
|
+
<MinusIcon className="size-4" />
|
|
41
|
+
</Button>
|
|
42
|
+
<span className="w-6 text-center font-medium tabular-nums">{count}</span>
|
|
43
|
+
<Button
|
|
44
|
+
variant="outline"
|
|
45
|
+
size="icon"
|
|
46
|
+
className="h-8 w-8 rounded-full"
|
|
47
|
+
aria-label="Increment"
|
|
48
|
+
onClick={increment}
|
|
49
|
+
>
|
|
50
|
+
<PlusIcon className="size-4" />
|
|
51
|
+
</Button>
|
|
52
|
+
</div>
|
|
53
|
+
<Button
|
|
54
|
+
variant="ghost"
|
|
55
|
+
size="sm"
|
|
56
|
+
className="h-8 gap-1.5 px-2 text-muted-foreground hover:text-destructive"
|
|
57
|
+
onClick={reset}
|
|
58
|
+
disabled={count === 0}
|
|
59
|
+
>
|
|
60
|
+
<Trash2Icon className="size-3.5" />
|
|
61
|
+
<span className="text-xs">Clear</span>
|
|
62
|
+
</Button>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { RevealGroup, RevealItem } from "@/features/landing/components/Reveal";
|
|
2
|
+
import { FEATURES } from "@/features/landing/data";
|
|
3
|
+
import { featureCardClassName } from "@/features/landing/helpers";
|
|
4
|
+
|
|
5
|
+
export function Features() {
|
|
6
|
+
return (
|
|
7
|
+
<section id="features" className="mx-auto max-w-5xl px-6 py-16">
|
|
8
|
+
<RevealGroup className="mb-16 text-center">
|
|
9
|
+
<RevealItem>
|
|
10
|
+
<h2 className="text-3xl font-semibold tracking-tight text-balance">Features</h2>
|
|
11
|
+
</RevealItem>
|
|
12
|
+
<RevealItem>
|
|
13
|
+
<p className="mx-auto mt-3 max-w-xl text-base text-balance text-muted-foreground">
|
|
14
|
+
Everything you need to build a modern web application, already configured with the best
|
|
15
|
+
practices.
|
|
16
|
+
</p>
|
|
17
|
+
</RevealItem>
|
|
18
|
+
</RevealGroup>
|
|
19
|
+
|
|
20
|
+
<RevealGroup className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
|
21
|
+
{FEATURES.map((feature) => (
|
|
22
|
+
<RevealItem key={feature.title} className={featureCardClassName}>
|
|
23
|
+
<div className="mb-2 flex h-12 w-12 items-center justify-center rounded-xl bg-primary/10 transition-transform duration-200 ease-out group-hover:scale-105">
|
|
24
|
+
{feature.icon}
|
|
25
|
+
</div>
|
|
26
|
+
<div className="flex flex-col gap-1.5">
|
|
27
|
+
<h3 className="text-base font-semibold text-balance text-foreground md:text-lg">
|
|
28
|
+
{feature.title}
|
|
29
|
+
</h3>
|
|
30
|
+
<p className="text-sm leading-relaxed text-pretty text-muted-foreground">
|
|
31
|
+
{feature.description}
|
|
32
|
+
</p>
|
|
33
|
+
</div>
|
|
34
|
+
</RevealItem>
|
|
35
|
+
))}
|
|
36
|
+
</RevealGroup>
|
|
37
|
+
</section>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { siteConfig } from "@/config/site.config";
|
|
2
|
+
|
|
3
|
+
import { ThemeToggle } from "@/components/shared/ThemeToggle";
|
|
4
|
+
import { Button } from "@/components/ui/button";
|
|
5
|
+
import { Reveal } from "@/features/landing/components/Reveal";
|
|
6
|
+
import { ReactIcon, TailwindIcon, TanstackIcon } from "@/features/landing/icons";
|
|
7
|
+
|
|
8
|
+
export function Hero() {
|
|
9
|
+
return (
|
|
10
|
+
<section className="mx-auto flex max-w-3xl flex-col items-center gap-6 px-6 pt-24 pb-16 text-center">
|
|
11
|
+
<Reveal mode="mount" delay={0} y={-8}>
|
|
12
|
+
<span className="rounded-full border border-border px-3 py-1 text-xs font-medium text-muted-foreground">
|
|
13
|
+
Premium UI/UX · Agent-ready · Zero bloat
|
|
14
|
+
</span>
|
|
15
|
+
</Reveal>
|
|
16
|
+
|
|
17
|
+
<h1 className="mb-2 text-center text-4xl lg:text-6xl">
|
|
18
|
+
<span className="font-black text-primary">ViraStack</span>{" "}
|
|
19
|
+
<span className="font-medium text-teal-500 italic">Start</span>
|
|
20
|
+
<span className="mt-2 block text-lg font-normal text-muted-foreground lg:text-xl">
|
|
21
|
+
TanStack Edition
|
|
22
|
+
</span>
|
|
23
|
+
</h1>
|
|
24
|
+
|
|
25
|
+
<p className="max-w-2xl text-base leading-relaxed text-balance text-muted-foreground sm:text-lg">
|
|
26
|
+
Built with{" "}
|
|
27
|
+
<span className="inline-flex items-center gap-1 font-semibold text-foreground">
|
|
28
|
+
<TanstackIcon className="h-4 w-4" />
|
|
29
|
+
TanStack Start
|
|
30
|
+
</span>
|
|
31
|
+
,{" "}
|
|
32
|
+
<span className="inline-flex items-center gap-1 font-semibold text-foreground">
|
|
33
|
+
<ReactIcon className="h-4 w-4" />
|
|
34
|
+
React 19
|
|
35
|
+
</span>
|
|
36
|
+
, and{" "}
|
|
37
|
+
<span className="inline-flex items-center gap-1 font-semibold text-foreground">
|
|
38
|
+
<TailwindIcon className="h-4 w-4" />
|
|
39
|
+
Tailwind CSS 4
|
|
40
|
+
</span>{" "}
|
|
41
|
+
- wired together with <span className="font-medium text-foreground italic">
|
|
42
|
+
Zustand
|
|
43
|
+
</span>, <span className="font-medium text-foreground italic">TanStack Query</span>, and{" "}
|
|
44
|
+
<span className="font-medium text-foreground italic">React Hook Form</span> so you can ship
|
|
45
|
+
a polished product from day one.
|
|
46
|
+
</p>
|
|
47
|
+
|
|
48
|
+
<Reveal mode="mount" delay={0.1} className="mt-2 flex items-center gap-2">
|
|
49
|
+
<Button
|
|
50
|
+
className="h-10 px-4 py-2"
|
|
51
|
+
nativeButton={false}
|
|
52
|
+
render={<a href={siteConfig.links.github} target="_blank" rel="noreferrer" />}
|
|
53
|
+
>
|
|
54
|
+
<span>⭐</span>
|
|
55
|
+
<span>Star on GitHub</span>
|
|
56
|
+
</Button>
|
|
57
|
+
<div className="h-10 w-10 [&>button]:h-full [&>button]:w-full">
|
|
58
|
+
<ThemeToggle />
|
|
59
|
+
</div>
|
|
60
|
+
</Reveal>
|
|
61
|
+
</section>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Features } from "@/features/landing/components/Features";
|
|
2
|
+
import { Hero } from "@/features/landing/components/Hero";
|
|
3
|
+
import { Showcase } from "@/features/landing/components/Showcase";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Demo landing surface. Delete `src/features/landing` and replace
|
|
7
|
+
* `src/routes/index.tsx` when you start your own product UI.
|
|
8
|
+
*/
|
|
9
|
+
export function LandingPage() {
|
|
10
|
+
return (
|
|
11
|
+
<main className="flex-1">
|
|
12
|
+
<Hero />
|
|
13
|
+
<Features />
|
|
14
|
+
<Showcase />
|
|
15
|
+
</main>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { zodResolver } from "@hookform/resolvers/zod";
|
|
4
|
+
import { Controller, useForm } from "react-hook-form";
|
|
5
|
+
import { toast } from "sonner";
|
|
6
|
+
|
|
7
|
+
import { Button } from "@/components/ui/button";
|
|
8
|
+
import { FieldDescription, FieldError } from "@/components/ui/field";
|
|
9
|
+
import { Input } from "@/components/ui/input";
|
|
10
|
+
import { Label } from "@/components/ui/label";
|
|
11
|
+
import { projectSchema } from "@/features/landing/schemas";
|
|
12
|
+
import type { ProjectInput } from "@/features/landing/schemas";
|
|
13
|
+
|
|
14
|
+
export function ProjectFormDemo() {
|
|
15
|
+
const {
|
|
16
|
+
control,
|
|
17
|
+
handleSubmit,
|
|
18
|
+
reset,
|
|
19
|
+
formState: { errors, isSubmitting },
|
|
20
|
+
} = useForm<ProjectInput>({
|
|
21
|
+
resolver: zodResolver(projectSchema),
|
|
22
|
+
defaultValues: { name: "" },
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
async function onSubmit(values: ProjectInput) {
|
|
26
|
+
await new Promise((resolve) => setTimeout(resolve, 600));
|
|
27
|
+
toast.success(`Created project: ${values.name}`);
|
|
28
|
+
reset();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<form onSubmit={handleSubmit(onSubmit)} noValidate className="flex flex-col gap-4">
|
|
33
|
+
<div className="flex flex-col gap-2.5 text-left">
|
|
34
|
+
<Label htmlFor="name">Project Name</Label>
|
|
35
|
+
<Controller
|
|
36
|
+
control={control}
|
|
37
|
+
name="name"
|
|
38
|
+
render={({ field }) => (
|
|
39
|
+
<Input
|
|
40
|
+
id="name"
|
|
41
|
+
type="text"
|
|
42
|
+
placeholder="virastack-app"
|
|
43
|
+
aria-invalid={!!errors.name}
|
|
44
|
+
value={field.value}
|
|
45
|
+
onChange={(event) => field.onChange(event.target.value)}
|
|
46
|
+
onBlur={field.onBlur}
|
|
47
|
+
ref={field.ref}
|
|
48
|
+
/>
|
|
49
|
+
)}
|
|
50
|
+
/>
|
|
51
|
+
{errors.name ? (
|
|
52
|
+
<FieldError>{errors.name.message}</FieldError>
|
|
53
|
+
) : (
|
|
54
|
+
<FieldDescription>Use 3–50 characters for the project name.</FieldDescription>
|
|
55
|
+
)}
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<Button type="submit" disabled={isSubmitting}>
|
|
59
|
+
{isSubmitting ? "Creating…" : "Create Project"}
|
|
60
|
+
</Button>
|
|
61
|
+
</form>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
|
|
5
|
+
import { motion } from "framer-motion";
|
|
6
|
+
|
|
7
|
+
import { fadeInUp, staggerContainer, viewport } from "@/features/landing/helpers";
|
|
8
|
+
|
|
9
|
+
type RevealProps = {
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
className?: string;
|
|
12
|
+
/** Mount animation delay in seconds (hero-style). Ignored when `mode="view"`. */
|
|
13
|
+
delay?: number;
|
|
14
|
+
/** `mount` = animate on load; `view` = animate when scrolled into view. */
|
|
15
|
+
mode?: "mount" | "view";
|
|
16
|
+
/** Initial Y offset for mount animations. */
|
|
17
|
+
y?: number;
|
|
18
|
+
/**
|
|
19
|
+
* When false (mount mode), only translate — keep opacity at 1 so LCP text
|
|
20
|
+
* is painted on first frame. Default true.
|
|
21
|
+
*/
|
|
22
|
+
fadeOpacity?: boolean;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Client-only motion shell. Keep copy/markup in Server Components and wrap
|
|
27
|
+
* animated regions with this so the page stays SSR-first.
|
|
28
|
+
*/
|
|
29
|
+
export function Reveal({
|
|
30
|
+
children,
|
|
31
|
+
className,
|
|
32
|
+
delay = 0,
|
|
33
|
+
mode = "view",
|
|
34
|
+
y = 12,
|
|
35
|
+
fadeOpacity = true,
|
|
36
|
+
}: RevealProps) {
|
|
37
|
+
if (mode === "mount") {
|
|
38
|
+
return (
|
|
39
|
+
<motion.div
|
|
40
|
+
className={className}
|
|
41
|
+
initial={fadeOpacity ? { opacity: 0, y } : { y }}
|
|
42
|
+
animate={fadeOpacity ? { opacity: 1, y: 0 } : { y: 0 }}
|
|
43
|
+
transition={{ duration: 0.45, delay, ease: "easeOut" }}
|
|
44
|
+
>
|
|
45
|
+
{children}
|
|
46
|
+
</motion.div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<motion.div
|
|
52
|
+
className={className}
|
|
53
|
+
initial="hidden"
|
|
54
|
+
whileInView="visible"
|
|
55
|
+
viewport={viewport}
|
|
56
|
+
variants={fadeInUp}
|
|
57
|
+
>
|
|
58
|
+
{children}
|
|
59
|
+
</motion.div>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function RevealGroup({ children, className }: { children: ReactNode; className?: string }) {
|
|
64
|
+
return (
|
|
65
|
+
<motion.div
|
|
66
|
+
className={className}
|
|
67
|
+
initial="hidden"
|
|
68
|
+
whileInView="visible"
|
|
69
|
+
viewport={viewport}
|
|
70
|
+
variants={staggerContainer}
|
|
71
|
+
>
|
|
72
|
+
{children}
|
|
73
|
+
</motion.div>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function RevealItem({ children, className }: { children: ReactNode; className?: string }) {
|
|
78
|
+
return (
|
|
79
|
+
<motion.div className={className} variants={fadeInUp}>
|
|
80
|
+
{children}
|
|
81
|
+
</motion.div>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { siteConfig } from "@/config/site.config";
|
|
2
|
+
|
|
3
|
+
import { formatDate } from "@/helpers";
|
|
4
|
+
|
|
5
|
+
import { CartDemo } from "@/features/landing/components/CartDemo";
|
|
6
|
+
import { ProjectFormDemo } from "@/features/landing/components/ProjectFormDemo";
|
|
7
|
+
import { RevealGroup, RevealItem } from "@/features/landing/components/Reveal";
|
|
8
|
+
import { UsersDemo } from "@/features/landing/components/UsersDemo";
|
|
9
|
+
import { featureCardClassName } from "@/features/landing/helpers";
|
|
10
|
+
|
|
11
|
+
export function Showcase() {
|
|
12
|
+
return (
|
|
13
|
+
<section id="showcase" className="mx-auto max-w-5xl px-6 pb-16">
|
|
14
|
+
<RevealGroup className="mb-10 text-center">
|
|
15
|
+
<RevealItem>
|
|
16
|
+
<h2 className="text-3xl font-semibold tracking-tight text-balance">Showcase</h2>
|
|
17
|
+
</RevealItem>
|
|
18
|
+
<RevealItem>
|
|
19
|
+
<p className="mx-auto mt-3 max-w-2xl text-base text-balance text-muted-foreground">
|
|
20
|
+
Experience the seamless integration of UI components and state management.
|
|
21
|
+
</p>
|
|
22
|
+
</RevealItem>
|
|
23
|
+
</RevealGroup>
|
|
24
|
+
|
|
25
|
+
<RevealGroup className="grid grid-cols-1 gap-6 lg:grid-cols-2 lg:items-stretch">
|
|
26
|
+
<RevealItem className={`${featureCardClassName} h-full min-h-0 [&>*]:min-h-0 [&>*]:flex-1`}>
|
|
27
|
+
<UsersDemo />
|
|
28
|
+
</RevealItem>
|
|
29
|
+
|
|
30
|
+
<div className="flex flex-col gap-6">
|
|
31
|
+
<RevealItem className={featureCardClassName}>
|
|
32
|
+
<div className="mb-4">
|
|
33
|
+
<h4 className="text-base font-semibold text-foreground md:text-lg">New Project</h4>
|
|
34
|
+
<p className="mt-1 text-sm text-pretty text-muted-foreground">
|
|
35
|
+
Validated forms with{" "}
|
|
36
|
+
<span className="font-semibold text-foreground">React Hook Form</span> and{" "}
|
|
37
|
+
<span className="font-semibold text-foreground">Zod</span>.
|
|
38
|
+
</p>
|
|
39
|
+
</div>
|
|
40
|
+
<ProjectFormDemo />
|
|
41
|
+
</RevealItem>
|
|
42
|
+
|
|
43
|
+
<RevealItem className={`${featureCardClassName} flex flex-col`}>
|
|
44
|
+
<div>
|
|
45
|
+
<h4 className="text-base font-semibold text-foreground md:text-lg">Global State</h4>
|
|
46
|
+
<p className="mt-1 text-sm text-pretty text-muted-foreground">
|
|
47
|
+
Shared client state managed with{" "}
|
|
48
|
+
<span className="font-semibold text-foreground">Zustand</span>.
|
|
49
|
+
</p>
|
|
50
|
+
</div>
|
|
51
|
+
<CartDemo />
|
|
52
|
+
</RevealItem>
|
|
53
|
+
</div>
|
|
54
|
+
</RevealGroup>
|
|
55
|
+
|
|
56
|
+
<div className="mt-20 flex flex-col items-center justify-center gap-2 text-sm text-muted-foreground">
|
|
57
|
+
<p suppressHydrationWarning>
|
|
58
|
+
© {new Date().getFullYear()} {siteConfig.name}. MIT Licensed.
|
|
59
|
+
</p>
|
|
60
|
+
<p className="italic" suppressHydrationWarning>
|
|
61
|
+
Last built on {formatDate(new Date())}.
|
|
62
|
+
</p>
|
|
63
|
+
</div>
|
|
64
|
+
</section>
|
|
65
|
+
);
|
|
66
|
+
}
|