feat(frontend): bootstrap React + Vite + Tailwind + Router + Layout

This commit is contained in:
2026-05-20 21:10:27 +02:00
parent 4a382b5dd7
commit 480ee15df9
12 changed files with 3243 additions and 1 deletions

View File

@@ -0,0 +1,12 @@
<!doctype html>
<html lang="nl" class="h-full">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Flashcards</title>
</head>
<body class="h-full bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-slate-100">
<div id="root" class="h-full"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View File

@@ -0,0 +1,37 @@
{
"name": "@flashcard/frontend",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc --noEmit && vite build",
"preview": "vite preview",
"typecheck": "tsc --noEmit",
"test": "vitest run"
},
"dependencies": {
"@flashcard/shared": "*",
"canvas-confetti": "^1.9.0",
"framer-motion": "^11.0.0",
"react": "^18.3.0",
"react-dom": "^18.3.0",
"react-router-dom": "^6.26.0",
"zustand": "^4.5.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.5.0",
"@testing-library/react": "^16.0.0",
"@types/canvas-confetti": "^1.6.0",
"@types/react": "^18.3.0",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.0",
"autoprefixer": "^10.4.0",
"jsdom": "^25.0.0",
"postcss": "^8.4.0",
"tailwindcss": "^3.4.0",
"typescript": "^5.5.0",
"vite": "^7.0.0",
"vitest": "^2.0.0"
}
}

View File

@@ -0,0 +1 @@
export default { plugins: { tailwindcss: {}, autoprefixer: {} } };

View File

@@ -0,0 +1,16 @@
import { Link, Outlet } from 'react-router-dom';
export function Layout() {
return (
<div className="flex h-full flex-col">
<header className="border-b border-slate-200 bg-white px-6 py-3 dark:border-slate-800 dark:bg-slate-900">
<nav className="flex gap-4 text-sm">
<Link to="/" className="font-semibold">Flashcards</Link>
<Link to="/admin">Admin</Link>
<Link to="/stats">Stats</Link>
</nav>
</header>
<main className="flex-1 overflow-auto"><Outlet /></main>
</div>
);
}

View File

@@ -0,0 +1,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import { RouterProvider } from 'react-router-dom';
import { router } from './router.js';
import './styles.css';
const root = createRoot(document.getElementById('root')!);
root.render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);

View File

@@ -0,0 +1,14 @@
import { createBrowserRouter, Navigate } from 'react-router-dom';
import { Layout } from './components/Layout.js';
export const router = createBrowserRouter([
{
path: '/',
element: <Layout />,
children: [
{ index: true, element: <div className="p-6">Dashboard placeholder</div> },
{ path: 'admin', element: <div className="p-6">Admin placeholder</div> },
{ path: '*', element: <Navigate to="/" replace /> },
],
},
]);

View File

@@ -0,0 +1,7 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
html, body, #root { height: 100%; }
.card-perspective { perspective: 1000px; }
.card-face { backface-visibility: hidden; }

View File

@@ -0,0 +1 @@
import '@testing-library/jest-dom/vitest';

View File

@@ -0,0 +1,17 @@
import type { Config } from 'tailwindcss';
export default {
content: ['./index.html', './src/**/*.{ts,tsx}'],
darkMode: 'class',
theme: {
extend: {
animation: { 'flip': 'flip 0.4s ease-out forwards' },
keyframes: {
flip: {
'0%': { transform: 'rotateY(0)' },
'100%': { transform: 'rotateY(180deg)' },
},
},
},
},
plugins: [],
} satisfies Config;

View File

@@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"jsx": "react-jsx",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"moduleResolution": "Bundler"
},
"include": ["src/**/*", "index.html"]
}

View File

@@ -0,0 +1,11 @@
import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [react()],
server: {
port: 5173,
proxy: { '/api': 'http://localhost:3000' },
},
test: { environment: 'jsdom', setupFiles: ['./src/test-setup.ts'] },
});