feat(web): set up React app shell with routing and layout

Adds react-router-dom with routes for ship list, dashboard, and rules pages.
Includes TopBar with navigation and mobile-first dark theme CSS.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Bas van Rossem
2026-02-19 16:20:22 +01:00
parent 76ad839abb
commit 5f275bfcc7
7 changed files with 182 additions and 4 deletions

View File

@@ -1,9 +1,19 @@
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import ShipListPage from './pages/ShipListPage';
import ShipDashboardPage from './pages/ShipDashboardPage';
import RulesPage from './pages/RulesPage';
function App() {
return (
<BrowserRouter>
<div className="app">
<h1>Spelljammer Ship Tracker</h1>
<p>App is running. Routes coming soon.</p>
<Routes>
<Route path="/" element={<ShipListPage />} />
<Route path="/ship/:id" element={<ShipDashboardPage />} />
<Route path="/rules" element={<RulesPage />} />
</Routes>
</div>
</BrowserRouter>
);
}

View File

@@ -0,0 +1,5 @@
import type { ReactNode } from 'react';
export default function PageContainer({ children }: { children: ReactNode }) {
return <main className="page-container">{children}</main>;
}

View File

@@ -0,0 +1,32 @@
import { useNavigate, useLocation } from 'react-router-dom';
export default function TopBar({ title }: { title?: string }) {
const navigate = useNavigate();
const location = useLocation();
const isHome = location.pathname === '/';
return (
<header className="top-bar">
<div className="top-bar-left">
{!isHome && (
<button className="top-bar-back" onClick={() => navigate(-1)}>
&larr;
</button>
)}
<h1 className="top-bar-title">{title || 'Spelljammer'}</h1>
</div>
<nav className="top-bar-right">
{location.pathname !== '/rules' && (
<button className="top-bar-link" onClick={() => navigate('/rules')}>
Rules
</button>
)}
{!isHome && location.pathname !== '/rules' && (
<button className="top-bar-link" onClick={() => navigate('/')}>
Ships
</button>
)}
</nav>
</header>
);
}

View File

@@ -88,3 +88,92 @@ a {
a:hover {
text-decoration: underline;
}
/* Top Bar */
.top-bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--spacing-sm) 0;
margin-bottom: var(--spacing-md);
border-bottom: 1px solid var(--color-border);
}
.top-bar-left {
display: flex;
align-items: center;
gap: var(--spacing-sm);
}
.top-bar-title {
font-size: 1.2rem;
font-weight: 600;
}
.top-bar-back {
background: none;
color: var(--color-primary);
font-size: 1.2rem;
padding: var(--spacing-xs) var(--spacing-sm);
}
.top-bar-right {
display: flex;
gap: var(--spacing-sm);
}
.top-bar-link {
background: var(--color-accent);
color: var(--color-text);
font-size: 0.85rem;
padding: var(--spacing-xs) var(--spacing-sm);
}
.top-bar-link:hover {
background: var(--color-surface-hover);
}
/* Page Container */
.page-container {
padding-bottom: var(--spacing-xl);
}
/* Buttons */
.btn-primary {
background: var(--color-primary);
color: #fff;
}
.btn-primary:hover {
background: var(--color-primary-hover);
}
.btn-danger {
background: var(--color-danger);
color: #fff;
}
.btn-danger:hover {
background: #d32f2f;
}
.btn-secondary {
background: var(--color-accent);
color: var(--color-text);
}
.btn-secondary:hover {
background: var(--color-surface-hover);
}
.btn-icon {
background: none;
padding: var(--spacing-xs);
font-size: 1.2rem;
line-height: 1;
color: var(--color-text-muted);
}
.btn-icon:hover {
color: var(--color-text);
}

View File

@@ -0,0 +1,13 @@
import TopBar from '../components/layout/TopBar';
import PageContainer from '../components/layout/PageContainer';
export default function RulesPage() {
return (
<>
<TopBar title="Battle Reference" />
<PageContainer>
<p style={{ color: 'var(--color-text-muted)' }}>Rules reference coming soon...</p>
</PageContainer>
</>
);
}

View File

@@ -0,0 +1,16 @@
import { useParams } from 'react-router-dom';
import TopBar from '../components/layout/TopBar';
import PageContainer from '../components/layout/PageContainer';
export default function ShipDashboardPage() {
const { id } = useParams<{ id: string }>();
return (
<>
<TopBar title="Ship Dashboard" />
<PageContainer>
<p style={{ color: 'var(--color-text-muted)' }}>Dashboard for ship {id} coming soon...</p>
</PageContainer>
</>
);
}

View File

@@ -0,0 +1,13 @@
import TopBar from '../components/layout/TopBar';
import PageContainer from '../components/layout/PageContainer';
export default function ShipListPage() {
return (
<>
<TopBar title="Ships" />
<PageContainer>
<p style={{ color: 'var(--color-text-muted)' }}>Ship list coming soon...</p>
</PageContainer>
</>
);
}