website refactor

This commit is contained in:
2026-01-18 18:43:13 +01:00
parent c35682cae5
commit 502d4aa092
8 changed files with 422 additions and 12 deletions

View File

@@ -1,4 +1,5 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
@import '../ui/theme/theme.css';
@tailwind base;
@tailwind components;

View File

@@ -71,7 +71,7 @@ export default async function RootLayout({
const enabledFlags = featureService.getEnabledFlags();
return (
<html lang="en" className="scroll-smooth overflow-x-hidden">
<html lang="en" className="scroll-smooth overflow-x-hidden" data-theme="default">
<head>
<meta name="mobile-web-app-capable" content="yes" />
</head>

View File

@@ -8,6 +8,7 @@ import { NotificationProvider } from '@/components/notifications/NotificationPro
import { NotificationIntegration } from '@/components/errors/NotificationIntegration';
import { EnhancedErrorBoundary } from '@/components/errors/EnhancedErrorBoundary';
import { DevToolbar } from '@/components/dev/DevToolbar';
import { ThemeProvider } from '@/ui/theme/ThemeProvider';
import React from 'react';
interface AppWrapperProps {
@@ -19,17 +20,19 @@ export function AppWrapper({ children, enabledFlags }: AppWrapperProps) {
return (
<ContainerProvider>
<QueryClientProvider>
<AuthProvider>
<FeatureFlagProvider flags={enabledFlags}>
<NotificationProvider>
<NotificationIntegration />
<EnhancedErrorBoundary enableDevOverlay={process.env.NODE_ENV === 'development'}>
{children}
{process.env.NODE_ENV === 'development' && <DevToolbar />}
</EnhancedErrorBoundary>
</NotificationProvider>
</FeatureFlagProvider>
</AuthProvider>
<ThemeProvider>
<AuthProvider>
<FeatureFlagProvider flags={enabledFlags}>
<NotificationProvider>
<NotificationIntegration />
<EnhancedErrorBoundary enableDevOverlay={process.env.NODE_ENV === 'development'}>
{children}
{process.env.NODE_ENV === 'development' && <DevToolbar />}
</EnhancedErrorBoundary>
</NotificationProvider>
</FeatureFlagProvider>
</AuthProvider>
</ThemeProvider>
</QueryClientProvider>
</ContainerProvider>
);

View File

@@ -0,0 +1,57 @@
export interface ThemeColors {
bg: {
base: string;
surface: string;
surfaceMuted: string;
};
border: {
default: string;
muted: string;
};
text: {
high: string;
med: string;
low: string;
};
intent: {
primary: string;
telemetry: string;
warning: string;
success: string;
critical: string;
};
}
export interface ThemeRadii {
none: string;
sm: string;
md: string;
lg: string;
xl: string;
full: string;
}
export interface ThemeShadows {
none: string;
sm: string;
md: string;
lg: string;
xl: string;
focus: string;
}
export interface ThemeTypography {
fontFamily: {
sans: string;
mono: string;
};
}
export interface Theme {
id: string;
name: string;
colors: ThemeColors;
radii: ThemeRadii;
shadows: ThemeShadows;
typography: ThemeTypography;
}

View File

@@ -0,0 +1,33 @@
'use client';
import React, { createContext, useContext, ReactNode } from 'react';
import { Theme } from './Theme';
import { defaultTheme } from './themes/default';
interface ThemeContextType {
theme: Theme;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
export function ThemeProvider({ children }: { children: ReactNode }) {
// For now, we only have the default theme.
// In the future, this could be driven by state, cookies, or user preferences.
const value = {
theme: defaultTheme,
};
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
}

View File

@@ -0,0 +1,41 @@
:root {
/* Base tokens mapped to default theme */
--ui-color-bg-base: #0C0D0F;
--ui-color-bg-surface: #141619;
--ui-color-bg-surface-muted: rgba(20, 22, 25, 0.7);
--ui-color-border-default: #23272B;
--ui-color-border-muted: rgba(35, 39, 43, 0.5);
--ui-color-text-high: #FFFFFF;
--ui-color-text-med: #A1A1AA;
--ui-color-text-low: #71717A;
--ui-color-intent-primary: #198CFF;
--ui-color-intent-telemetry: #4ED4E0;
--ui-color-intent-warning: #FFBE4D;
--ui-color-intent-success: #6FE37A;
--ui-color-intent-critical: #E35C5C;
--ui-radius-none: 0;
--ui-radius-sm: 0.125rem;
--ui-radius-md: 0.375rem;
--ui-radius-lg: 0.5rem;
--ui-radius-xl: 0.75rem;
--ui-radius-full: 9999px;
--ui-shadow-none: none;
--ui-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--ui-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--ui-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
--ui-shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
--ui-shadow-focus: 0 0 0 4px rgba(25, 140, 255, 0.5);
--ui-font-sans: 'Inter', system-ui, sans-serif;
--ui-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
}
/* Theme override block */
[data-theme='default'] {
/* Currently same as root, but allows for future overrides */
}

View File

@@ -0,0 +1,51 @@
import { Theme } from '../Theme';
export const defaultTheme: Theme = {
id: 'default',
name: 'Precision Racing (Dark)',
colors: {
bg: {
base: '#0C0D0F',
surface: '#141619',
surfaceMuted: 'rgba(20, 22, 25, 0.7)',
},
border: {
default: '#23272B',
muted: 'rgba(35, 39, 43, 0.5)',
},
text: {
high: '#FFFFFF',
med: '#A1A1AA',
low: '#71717A',
},
intent: {
primary: '#198CFF',
telemetry: '#4ED4E0',
warning: '#FFBE4D',
success: '#6FE37A',
critical: '#E35C5C',
},
},
radii: {
none: '0',
sm: '0.125rem',
md: '0.375rem',
lg: '0.5rem',
xl: '0.75rem',
full: '9999px',
},
shadows: {
none: 'none',
sm: '0 1px 2px 0 rgba(0, 0, 0, 0.05)',
md: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',
lg: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',
xl: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)',
focus: '0 0 0 4px rgba(25, 140, 255, 0.5)',
},
typography: {
fontFamily: {
sans: "'Inter', system-ui, sans-serif",
mono: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace",
},
},
};