Browse Source

touch ups

drawing-pad
Stephanie Gredell 1 month ago
parent
commit
5eef4096da
  1. 15
      frontend/index.html
  2. BIN
      frontend/public/tic-tac-toe.png
  3. 29
      frontend/src/App.tsx
  4. 8
      frontend/src/components/Navbar/Navbar.tsx
  5. 10
      frontend/src/config/apps.ts
  6. 19
      frontend/src/pages/LandingPage.tsx
  7. 16
      frontend/vite.config.ts

15
frontend/index.html

@ -4,9 +4,19 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- Resource hints -->
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Butterfly+Kids&display=swap" rel="stylesheet"> <link rel="dns-prefetch" href="https://fonts.googleapis.com">
<link rel="dns-prefetch" href="https://fonts.gstatic.com">
<!-- Critical images preload -->
<link rel="preload" href="/rainbow.png" as="image" type="image/png" fetchpriority="high">
<link rel="preload" href="/cupcake.png" as="image" type="image/png" fetchpriority="high">
<!-- Preload first game icon (likely LCP element) -->
<link rel="preload" href="/video-marketing.png" as="image" type="image/png" fetchpriority="high">
<!-- Async font loading -->
<link rel="preload" href="https://fonts.googleapis.com/css2?family=Butterfly+Kids&display=swap&text=Rainbows%2CCupcakesUnicorns" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link href="https://fonts.googleapis.com/css2?family=Butterfly+Kids&display=swap" rel="stylesheet"></noscript>
<title>Rainbow, Cupcakes & Unicorns - Free Games for Kids</title> <title>Rainbow, Cupcakes & Unicorns - Free Games for Kids</title>
</head> </head>
<body class="font-sans antialiased"> <body class="font-sans antialiased">
@ -14,6 +24,3 @@
<script type="module" src="/src/main.tsx"></script> <script type="module" src="/src/main.tsx"></script>
</body> </body>
</html> </html>

BIN
frontend/public/tic-tac-toe.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

29
frontend/src/App.tsx

@ -1,17 +1,30 @@
import { BrowserRouter, Routes, Route } from 'react-router-dom'; import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { Suspense, lazy } from 'react';
import { AuthProvider } from './hooks/useAuth'; import { AuthProvider } from './hooks/useAuth';
import { ErrorBoundary } from './components/ErrorBoundary'; import { ErrorBoundary } from './components/ErrorBoundary';
import { Navbar } from './components/Navbar/Navbar'; import { Navbar } from './components/Navbar/Navbar';
import { Footer } from './components/Footer/Footer'; import { Footer } from './components/Footer/Footer';
import { ProtectedRoute } from './components/ProtectedRoute'; import { ProtectedRoute } from './components/ProtectedRoute';
import { LandingPage } from './pages/LandingPage'; import { LandingPage } from './pages/LandingPage';
import { AdminPage } from './pages/AdminPage';
import { VideosAdminPage } from './pages/VideosAdminPage';
import { SpeechSoundsAdminPage } from './pages/SpeechSoundsAdminPage';
import { LoginPage } from './pages/LoginPage';
import { APPS } from './config/apps'; import { APPS } from './config/apps';
import './globals.css'; import './globals.css';
// Lazy load admin and login pages
const AdminPage = lazy(() => import('./pages/AdminPage').then(module => ({ default: module.AdminPage })));
const VideosAdminPage = lazy(() => import('./pages/VideosAdminPage').then(module => ({ default: module.VideosAdminPage })));
const SpeechSoundsAdminPage = lazy(() => import('./pages/SpeechSoundsAdminPage').then(module => ({ default: module.SpeechSoundsAdminPage })));
const LoginPage = lazy(() => import('./pages/LoginPage').then(module => ({ default: module.LoginPage })));
// Loading fallback component
const PageLoader = () => (
<div className="min-h-screen flex items-center justify-center">
<div className="text-center">
<div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-primary mb-4"></div>
<p className="text-muted-foreground">Loading...</p>
</div>
</div>
);
function App() { function App() {
return ( return (
<ErrorBoundary> <ErrorBoundary>
@ -20,6 +33,7 @@ function App() {
<div className="min-h-screen flex flex-col"> <div className="min-h-screen flex flex-col">
<Navbar /> <Navbar />
<main className="flex-1"> <main className="flex-1">
<Suspense fallback={<PageLoader />}>
<Routes> <Routes>
<Route path="/" element={<LandingPage />} /> <Route path="/" element={<LandingPage />} />
{/* Dynamically generate routes for enabled apps */} {/* Dynamically generate routes for enabled apps */}
@ -27,7 +41,11 @@ function App() {
<Route <Route
key={app.id} key={app.id}
path={app.link} path={app.link}
element={<app.component />} element={
<Suspense fallback={<PageLoader />}>
<app.component />
</Suspense>
}
/> />
))} ))}
{/* Keep non-app routes separate */} {/* Keep non-app routes separate */}
@ -57,6 +75,7 @@ function App() {
} }
/> />
</Routes> </Routes>
</Suspense>
</main> </main>
<Footer /> <Footer />
</div> </div>

8
frontend/src/components/Navbar/Navbar.tsx

@ -76,12 +76,20 @@ export function Navbar() {
src="/rainbow.png" src="/rainbow.png"
alt="Rainbow" alt="Rainbow"
className="h-10 w-10 md:h-12 md:w-12 object-contain" className="h-10 w-10 md:h-12 md:w-12 object-contain"
width="48"
height="48"
loading="eager"
fetchPriority="high"
/> />
<h1 className="text-3xl md:text-4xl font-bold text-foreground" style={{ fontFamily: "'Butterfly Kids', cursive" }}>Rainbows, Cupcakes & Unicorns</h1> <h1 className="text-3xl md:text-4xl font-bold text-foreground" style={{ fontFamily: "'Butterfly Kids', cursive" }}>Rainbows, Cupcakes & Unicorns</h1>
<img <img
src="/cupcake.png" src="/cupcake.png"
alt="Cupcake" alt="Cupcake"
className="h-10 w-10 md:h-12 md:w-12 object-contain" className="h-10 w-10 md:h-12 md:w-12 object-contain"
width="48"
height="48"
loading="eager"
fetchPriority="high"
/> />
</Link> </Link>

10
frontend/src/config/apps.ts

@ -1,7 +1,9 @@
import React from 'react'; import React, { lazy } from 'react';
import { VideoApp } from '../pages/VideoApp';
import { SpeechSoundsApp } from '../pages/SpeechSoundsApp'; // Lazy load game pages for code splitting
import { TicTacToeApp } from '../pages/TicTacToeApp'; const VideoApp = lazy(() => import('../pages/VideoApp').then(module => ({ default: module.VideoApp })));
const SpeechSoundsApp = lazy(() => import('../pages/SpeechSoundsApp').then(module => ({ default: module.SpeechSoundsApp })));
const TicTacToeApp = lazy(() => import('../pages/TicTacToeApp').then(module => ({ default: module.TicTacToeApp })));
export type App = { export type App = {
id: string; id: string;

19
frontend/src/pages/LandingPage.tsx

@ -28,6 +28,7 @@ export function LandingPage() {
<div className="bg-background"> <div className="bg-background">
<section className="px-4 py-8"> <section className="px-4 py-8">
<div className="max-w-5xl mx-auto"> <div className="max-w-5xl mx-auto">
{/* First card is likely LCP element - prioritize it */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 mb-12"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 mb-12">
{APPS.map(app => { {APPS.map(app => {
const color = categoryColors[app.id] || 'pink'; const color = categoryColors[app.id] || 'pink';
@ -52,12 +53,30 @@ export function LandingPage() {
src="/video-marketing.png" src="/video-marketing.png"
alt="Video App" alt="Video App"
className="w-20 h-20 object-contain" className="w-20 h-20 object-contain"
width="80"
height="80"
loading="eager"
fetchPriority={app.id === 'videos' ? 'high' : 'auto'}
/> />
) : app.id === 'speechsounds' ? ( ) : app.id === 'speechsounds' ? (
<img <img
src="/unicorn-talking.png" src="/unicorn-talking.png"
alt="Speech Sounds" alt="Speech Sounds"
className="w-20 h-20 object-contain" className="w-20 h-20 object-contain"
width="80"
height="80"
loading="eager"
fetchPriority="auto"
/>
) : app.id === 'tictactoe' ? (
<img
src="/tic-tac-toe.png"
alt="Tic Tac Toe"
className="w-20 h-20 object-contain"
width="80"
height="80"
loading="eager"
fetchPriority="auto"
/> />
) : ( ) : (
<span className="text-5xl">{emoji}</span> <span className="text-5xl">{emoji}</span>

16
frontend/vite.config.ts

@ -5,6 +5,22 @@ export default defineConfig({
plugins: [react()], plugins: [react()],
server: { server: {
port: 5173 port: 5173
},
build: {
rollupOptions: {
output: {
manualChunks: {
'react-vendor': ['react', 'react-dom', 'react-router-dom'],
'ui-vendor': ['axios']
}
}
},
chunkSizeWarningLimit: 1000,
cssCodeSplit: true,
minify: 'esbuild'
},
optimizeDeps: {
include: ['react', 'react-dom', 'react-router-dom']
} }
}) })

Loading…
Cancel
Save