feat: dark / light theme almost complete

This commit is contained in:
Eric Gullickson
2025-12-25 20:32:38 -06:00
parent 1fd77cd757
commit 50baec390f
18 changed files with 380 additions and 170 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 MiB

View File

@@ -8,9 +8,7 @@ import { Routes, Route, Navigate, useLocation } from 'react-router-dom';
import { useAuth0 } from '@auth0/auth0-react';
import { useIsAuthInitialized } from './core/auth/auth-gate';
import { motion, AnimatePresence } from 'framer-motion';
import { ThemeProvider } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import { md3Theme } from './shared-minimal/theme/md3Theme';
import { ThemeProvider } from './shared-minimal/theme/ThemeContext';
import { Layout } from './components/Layout';
import { UnitsProvider } from './core/units/UnitsContext';
@@ -451,8 +449,7 @@ function App() {
if (isLoading) {
if (mobileMode) {
return (
<ThemeProvider theme={md3Theme}>
<CssBaseline />
<ThemeProvider>
<Layout mobileMode={true}>
<div className="flex items-center justify-center h-64">
<div className="text-slate-500">Loading...</div>
@@ -462,8 +459,7 @@ function App() {
);
}
return (
<ThemeProvider theme={md3Theme}>
<CssBaseline />
<ThemeProvider>
<div className="flex items-center justify-center min-h-screen">
<div className="text-lg">Loading...</div>
</div>
@@ -474,8 +470,7 @@ function App() {
// Callback route requires authentication - handled by CallbackPage component
if (isCallbackRoute && isAuthenticated) {
return (
<ThemeProvider theme={md3Theme}>
<CssBaseline />
<ThemeProvider>
<React.Suspense fallback={
<div className="flex items-center justify-center min-h-screen">
<div className="text-lg">Processing login...</div>
@@ -490,8 +485,7 @@ function App() {
if (shouldShowHomePage) {
return (
<ThemeProvider theme={md3Theme}>
<CssBaseline />
<ThemeProvider>
<HomePage />
<DebugInfo />
</ThemeProvider>
@@ -501,8 +495,7 @@ function App() {
// Signup route is public - no authentication required
if (isSignupRoute) {
return (
<ThemeProvider theme={md3Theme}>
<CssBaseline />
<ThemeProvider>
<React.Suspense fallback={
<div className="flex items-center justify-center min-h-screen">
<div className="text-lg">Loading...</div>
@@ -519,8 +512,7 @@ function App() {
// (Auth0 blocks unverified users from logging in)
if (isVerifyEmailRoute) {
return (
<ThemeProvider theme={md3Theme}>
<CssBaseline />
<ThemeProvider>
<React.Suspense fallback={
<div className="flex items-center justify-center min-h-screen">
<div className="text-lg">Loading...</div>
@@ -539,8 +531,7 @@ function App() {
if (isOnboardingRoute) {
return (
<ThemeProvider theme={md3Theme}>
<CssBaseline />
<ThemeProvider>
<React.Suspense fallback={
<div className="flex items-center justify-center min-h-screen">
<div className="text-lg">Loading...</div>
@@ -559,8 +550,7 @@ function App() {
console.log('[DEBUG App] Auth gate not ready yet, showing loading state');
if (mobileMode) {
return (
<ThemeProvider theme={md3Theme}>
<CssBaseline />
<ThemeProvider>
<Layout mobileMode={true}>
<div className="flex items-center justify-center h-64">
<div className="text-slate-500">Initializing session...</div>
@@ -570,8 +560,7 @@ function App() {
);
}
return (
<ThemeProvider theme={md3Theme}>
<CssBaseline />
<ThemeProvider>
<div className="flex items-center justify-center min-h-screen">
<div className="text-lg">Initializing session...</div>
</div>
@@ -582,8 +571,7 @@ function App() {
// Mobile app rendering
if (mobileMode) {
return (
<ThemeProvider theme={md3Theme}>
<CssBaseline />
<ThemeProvider>
<UnitsProvider>
<Layout mobileMode={true}>
<AnimatePresence mode="popLayout" initial={false}>
@@ -881,8 +869,7 @@ function App() {
// Desktop app rendering (fallback)
return (
<ThemeProvider theme={md3Theme}>
<CssBaseline />
<ThemeProvider>
<UnitsProvider>
<Layout mobileMode={false}>
<RouteSuspense>

View File

@@ -6,7 +6,6 @@ import React from 'react';
import { useAuth0 } from '@auth0/auth0-react';
import { Link, useLocation } from 'react-router-dom';
import { Container, Paper, Typography, Box, IconButton, Avatar } from '@mui/material';
import { useTheme } from '@mui/material/styles';
import DirectionsCarRoundedIcon from '@mui/icons-material/DirectionsCarRounded';
import LocalGasStationRoundedIcon from '@mui/icons-material/LocalGasStationRounded';
import BuildRoundedIcon from '@mui/icons-material/BuildRounded';
@@ -28,7 +27,6 @@ export const Layout: React.FC<LayoutProps> = ({ children, mobileMode = false })
const { user, logout } = useAuth0();
const { sidebarOpen, toggleSidebar, setSidebarOpen } = useAppStore();
const location = useLocation();
const theme = useTheme();
// Ensure desktop has a visible navigation by default (only on mount)
React.useEffect(() => {
@@ -67,7 +65,11 @@ export const Layout: React.FC<LayoutProps> = ({ children, mobileMode = false })
{/* App header */}
<div className="px-5 pt-5 pb-3">
<div className="flex items-center justify-between">
<div className="text-lg font-semibold tracking-tight">MotoVaultPro</div>
<img
src="/images/logos/motovaultpro-title-slogan.png"
alt="MotoVaultPro"
className="h-6 w-auto"
/>
<div className="flex items-center gap-2">
<NotificationBell />
<div className="text-xs text-slate-500">v1.0</div>
@@ -107,31 +109,31 @@ export const Layout: React.FC<LayoutProps> = ({ children, mobileMode = false })
flexDirection: 'column'
}}
>
<Paper
elevation={0}
square
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
height: 64,
px: 3,
borderBottom: 1,
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
height: 64,
px: 2,
borderBottom: 1,
borderColor: 'divider',
borderRadius: 0
gap: 1
}}
>
<Typography variant="h6" sx={{ fontWeight: 700, color: 'primary.main' }}>
MotoVaultPro
</Typography>
<img
src="/images/logos/motovaultpro-title-slogan.png"
alt="MotoVaultPro"
style={{ height: 24, width: 'auto', maxWidth: 180 }}
/>
<IconButton
onClick={toggleSidebar}
size="small"
sx={{ color: 'text.secondary' }}
sx={{ color: 'text.secondary', flexShrink: 0 }}
>
<CloseIcon />
</IconButton>
</Paper>
</Box>
<Box sx={{ mt: 3, px: 2, flex: 1 }}>
{navigation.map((item) => {
@@ -151,15 +153,15 @@ export const Layout: React.FC<LayoutProps> = ({ children, mobileMode = false })
mb: 0.5,
borderRadius: 2,
transition: 'all 0.2s',
backgroundColor: isActive
? theme.palette.primary.main + '12'
backgroundColor: isActive
? 'primary.main'
: 'transparent',
color: isActive
? 'primary.main'
color: isActive
? '#FFFFFF'
: 'text.primary',
'&:hover': {
backgroundColor: isActive
? theme.palette.primary.main + '18'
backgroundColor: isActive
? 'primary.dark'
: 'action.hover',
}
}}

View File

@@ -160,7 +160,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
<div className="flex flex-col">
<label className="text-sm font-medium text-slate-700 mb-1">Vehicle</label>
<select
className="h-11 min-h-[44px] rounded-lg border border-slate-300 px-3 focus:outline-none focus:ring-2 focus:ring-primary-500"
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi"
value={vehicleID}
onChange={(e) => setVehicleID(e.target.value)}
required
@@ -175,7 +175,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
<div className="flex flex-col">
<label className="text-sm font-medium text-slate-700 mb-1">Document Type</label>
<select
className="h-11 min-h-[44px] rounded-lg border border-slate-300 px-3 focus:outline-none focus:ring-2 focus:ring-primary-500"
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi"
value={documentType}
onChange={(e) => setDocumentType(e.target.value as DocumentType)}
>
@@ -188,7 +188,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
<div className="flex flex-col md:col-span-2">
<label className="text-sm font-medium text-slate-700 mb-1">Title</label>
<input
className="h-11 min-h-[44px] rounded-lg border border-slate-300 px-3 focus:outline-none focus:ring-2 focus:ring-primary-500"
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:placeholder-canna dark:focus:ring-abudhabi dark:focus:border-abudhabi"
type="text"
value={title}
placeholder={
@@ -206,7 +206,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
<div className="flex flex-col">
<label className="text-sm font-medium text-slate-700 mb-1">Insurance company</label>
<input
className="h-11 min-h-[44px] rounded-lg border border-slate-300 px-3 focus:outline-none focus:ring-2 focus:ring-primary-500"
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi"
type="text"
value={insuranceCompany}
onChange={(e) => setInsuranceCompany(e.target.value)}
@@ -215,7 +215,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
<div className="flex flex-col">
<label className="text-sm font-medium text-slate-700 mb-1">Policy number</label>
<input
className="h-11 min-h-[44px] rounded-lg border border-slate-300 px-3 focus:outline-none focus:ring-2 focus:ring-primary-500"
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi"
type="text"
value={policyNumber}
onChange={(e) => setPolicyNumber(e.target.value)}
@@ -262,7 +262,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
<div className="flex flex-col">
<label className="text-sm font-medium text-slate-700 mb-1">Bodily Injury (Person)</label>
<input
className="h-11 min-h-[44px] rounded-lg border border-slate-300 px-3 focus:outline-none focus:ring-2 focus:ring-primary-500"
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:placeholder-canna dark:focus:ring-abudhabi dark:focus:border-abudhabi"
type="text"
placeholder="$25,000"
value={bodilyInjuryPerson}
@@ -272,7 +272,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
<div className="flex flex-col">
<label className="text-sm font-medium text-slate-700 mb-1">Bodily Injury (Incident)</label>
<input
className="h-11 min-h-[44px] rounded-lg border border-slate-300 px-3 focus:outline-none focus:ring-2 focus:ring-primary-500"
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:placeholder-canna dark:focus:ring-abudhabi dark:focus:border-abudhabi"
type="text"
placeholder="$50,000"
value={bodilyInjuryIncident}
@@ -283,7 +283,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
<div className="flex flex-col">
<label className="text-sm font-medium text-slate-700 mb-1">Property Damage</label>
<input
className="h-11 min-h-[44px] rounded-lg border border-slate-300 px-3 focus:outline-none focus:ring-2 focus:ring-primary-500"
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:placeholder-canna dark:focus:ring-abudhabi dark:focus:border-abudhabi"
type="text"
placeholder="$25,000"
value={propertyDamage}
@@ -293,7 +293,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
<div className="flex flex-col">
<label className="text-sm font-medium text-slate-700 mb-1">Premium</label>
<input
className="h-11 min-h-[44px] rounded-lg border border-slate-300 px-3 focus:outline-none focus:ring-2 focus:ring-primary-500"
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:placeholder-canna dark:focus:ring-abudhabi dark:focus:border-abudhabi"
type="number"
step="0.01"
placeholder="0.00"
@@ -309,7 +309,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
<div className="flex flex-col">
<label className="text-sm font-medium text-slate-700 mb-1">License Plate</label>
<input
className="h-11 min-h-[44px] rounded-lg border border-slate-300 px-3 focus:outline-none focus:ring-2 focus:ring-primary-500"
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi"
type="text"
value={licensePlate}
onChange={(e) => setLicensePlate(e.target.value)}
@@ -336,7 +336,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
<div className="flex flex-col md:col-span-2">
<label className="text-sm font-medium text-slate-700 mb-1">Cost</label>
<input
className="h-11 min-h-[44px] rounded-lg border border-slate-300 px-3 focus:outline-none focus:ring-2 focus:ring-primary-500"
className="h-11 min-h-[44px] rounded-lg border px-3 bg-white text-gray-900 border-slate-300 placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:placeholder-canna dark:focus:ring-abudhabi dark:focus:border-abudhabi"
type="number"
step="0.01"
placeholder="0.00"
@@ -354,7 +354,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
type="checkbox"
checked={scanForMaintenance}
onChange={(e) => setScanForMaintenance(e.target.checked)}
className="w-5 h-5 rounded border-slate-300 text-primary-600 focus:ring-primary-500"
className="w-5 h-5 rounded border-slate-300 text-primary-600 focus:ring-primary-500 dark:border-silverstone dark:focus:ring-abudhabi"
/>
<span className="ml-2 text-sm font-medium text-slate-700">
Scan for Maintenance Schedule
@@ -367,7 +367,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
<div className="flex flex-col md:col-span-2">
<label className="text-sm font-medium text-slate-700 mb-1">Notes</label>
<textarea
className="min-h-[88px] rounded-lg border border-slate-300 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary-500"
className="min-h-[88px] rounded-lg border px-3 py-2 bg-white text-gray-900 border-slate-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi"
value={notes}
onChange={(e) => setNotes(e.target.value)}
/>
@@ -376,7 +376,7 @@ export const DocumentForm: React.FC<DocumentFormProps> = ({ onSuccess, onCancel
<div className="flex flex-col md:col-span-2">
<label className="text-sm font-medium text-slate-700 mb-1">Upload image/PDF</label>
<input
className="h-11 min-h-[44px] rounded-lg border border-slate-300 px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary-500"
className="h-11 min-h-[44px] rounded-lg border px-3 py-2 bg-white text-gray-900 border-slate-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-medium file:bg-primary-500/10 file:text-primary-600 dark:file:bg-abudhabi/20 dark:file:text-abudhabi"
type="file"
accept="image/jpeg,image/png,application/pdf"
onChange={(e) => setFile(e.target.files?.[0] || null)}

View File

@@ -416,7 +416,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
</p>
<input
{...register('vin')}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500 text-base"
className="w-full px-3 py-2 border rounded-md text-base bg-white text-gray-900 border-gray-300 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:placeholder-canna dark:focus:ring-abudhabi dark:focus:border-abudhabi"
placeholder="Enter 17-character VIN (optional if License Plate provided)"
style={{ fontSize: '16px' }}
/>
@@ -438,7 +438,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
const year = parseInt(e.target.value);
setValue('year', year);
}}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500 min-h-[44px]"
className="w-full px-3 py-2 border rounded-md min-h-[44px] bg-white text-gray-900 border-gray-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi"
style={{ fontSize: '16px' }}
>
<option value="">Select Year</option>
@@ -461,7 +461,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
const make = e.target.value;
setValue('make', make);
}}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500 min-h-[44px]"
className="w-full px-3 py-2 border rounded-md min-h-[44px] bg-white text-gray-900 border-gray-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi"
disabled={loadingDropdowns || !watchedYear || makes.length === 0}
style={{ fontSize: '16px' }}
>
@@ -493,7 +493,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
const model = e.target.value;
setValue('model', model);
}}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500 min-h-[44px]"
className="w-full px-3 py-2 border rounded-md min-h-[44px] bg-white text-gray-900 border-gray-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi"
disabled={loadingDropdowns || !watchedMake || models.length === 0}
style={{ fontSize: '16px' }}
>
@@ -528,7 +528,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
const trim = e.target.value;
setValue('trimLevel', trim);
}}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500 min-h-[44px]"
className="w-full px-3 py-2 border rounded-md min-h-[44px] bg-white text-gray-900 border-gray-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi"
disabled={loadingDropdowns || !watchedModel || trims.length === 0}
style={{ fontSize: '16px' }}
>
@@ -556,7 +556,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
</label>
<select
{...register('engine')}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500 min-h-[44px]"
className="w-full px-3 py-2 border rounded-md min-h-[44px] bg-white text-gray-900 border-gray-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi"
disabled={loadingDropdowns || !selectedTrim || engines.length === 0}
style={{ fontSize: '16px' }}
>
@@ -584,7 +584,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
</label>
<select
{...register('transmission')}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500 min-h-[44px]"
className="w-full px-3 py-2 border rounded-md min-h-[44px] bg-white text-gray-900 border-gray-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi"
disabled={loadingDropdowns || !selectedTrim || transmissions.length === 0}
style={{ fontSize: '16px' }}
>
@@ -612,7 +612,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
</label>
<input
{...register('nickname')}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500 min-h-[44px]"
className="w-full px-3 py-2 border rounded-md min-h-[44px] bg-white text-gray-900 border-gray-300 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:placeholder-canna dark:focus:ring-abudhabi dark:focus:border-abudhabi"
placeholder="e.g., Family Car"
style={{ fontSize: '16px' }}
/>
@@ -625,7 +625,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
</label>
<input
{...register('color')}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500 min-h-[44px]"
className="w-full px-3 py-2 border rounded-md min-h-[44px] bg-white text-gray-900 border-gray-300 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:placeholder-canna dark:focus:ring-abudhabi dark:focus:border-abudhabi"
placeholder="e.g., Blue"
style={{ fontSize: '16px' }}
/>
@@ -637,7 +637,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
</label>
<input
{...register('licensePlate')}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500 min-h-[44px]"
className="w-full px-3 py-2 border rounded-md min-h-[44px] bg-white text-gray-900 border-gray-300 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:placeholder-canna dark:focus:ring-abudhabi dark:focus:border-abudhabi"
placeholder="e.g., ABC-123 (required if VIN omitted)"
style={{ fontSize: '16px' }}
/>
@@ -655,7 +655,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
{...register('odometerReading', { valueAsNumber: true })}
type="number"
inputMode="numeric"
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary-500 min-h-[44px]"
className="w-full px-3 py-2 border rounded-md min-h-[44px] bg-white text-gray-900 border-gray-300 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:placeholder-canna dark:focus:ring-abudhabi dark:focus:border-abudhabi"
placeholder="e.g., 50000"
style={{ fontSize: '16px' }}
/>

View File

@@ -22,9 +22,9 @@ import { FuelLogForm } from '../../fuel-logs/components/FuelLogForm';
// Unit conversions now handled by backend
import { fuelLogsApi } from '../../fuel-logs/api/fuel-logs.api';
const DetailField: React.FC<{
label: string;
value?: string | number;
const DetailField: React.FC<{
label: string;
value?: string | number;
isRequired?: boolean;
className?: string;
}> = ({ label, value, isRequired, className = "" }) => (

View File

@@ -24,39 +24,45 @@ export const HomePage = () => {
};
return (
<div className="min-h-screen bg-white">
<div className="min-h-screen bg-nero text-avus">
{/* Navigation Bar */}
<nav className="bg-white shadow-md sticky top-0 z-50">
<nav className="sticky top-0 z-50 bg-nero/90 backdrop-blur border-b border-white/10">
<div className="max-w-7xl mx-auto px-4 md:px-8">
<div className="flex justify-between items-center h-16">
{/* Logo */}
<div className="flex-shrink-0">
<h1 className="text-2xl font-bold text-primary-500">MotoVaultPro</h1>
<a href="#home" className="flex items-center">
<img
src="/images/logos/motovaultpro-title-slogan.png"
alt="MotoVaultPro - Precision Vehicle Management"
className="h-8 md:h-10 w-auto"
/>
</a>
</div>
{/* Desktop Menu */}
<div className="hidden md:flex items-center space-x-8">
<a href="#home" className="text-gray-700 hover:text-primary-500 transition-colors">
<a href="#home" className="text-white/75 hover:text-white transition-colors">
Home
</a>
<a
href="#features"
className="text-gray-700 hover:text-primary-500 transition-colors"
className="text-white/75 hover:text-white transition-colors"
>
Features
</a>
<a href="#about" className="text-gray-700 hover:text-primary-500 transition-colors">
<a href="#about" className="text-white/75 hover:text-white transition-colors">
About
</a>
<button
onClick={handleSignup}
className="border-2 border-primary-500 text-primary-500 hover:bg-primary-50 font-semibold py-2 px-6 rounded-lg transition-colors duration-300"
className="border border-primary-500/90 text-primary-500 hover:bg-primary-500/10 hover:border-primary-500 font-semibold py-2 px-6 rounded-lg transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-primary-500/50"
>
Sign Up
</button>
<button
onClick={handleAuthAction}
className="bg-primary-500 hover:bg-primary-700 text-white font-semibold py-2 px-6 rounded-lg transition-colors duration-300"
className="bg-primary-500 hover:bg-primary-600 text-white font-semibold py-2 px-6 rounded-lg transition-colors duration-300 shadow-lg shadow-black/30 focus:outline-none focus:ring-2 focus:ring-primary-500/50"
>
Login
</button>
@@ -66,7 +72,7 @@ export const HomePage = () => {
<div className="md:hidden">
<button
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
className="text-gray-700 hover:text-primary-500 focus:outline-none"
className="text-white/80 hover:text-white focus:outline-none"
>
<svg
className="h-6 w-6"
@@ -93,35 +99,35 @@ export const HomePage = () => {
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
className="md:hidden py-4 space-y-3"
className="md:hidden py-4 space-y-3 bg-nero border-t border-white/10"
>
<a
href="#home"
className="block text-gray-700 hover:text-primary-500 transition-colors py-2"
className="block text-white/75 hover:text-white transition-colors py-2"
>
Home
</a>
<a
href="#features"
className="block text-gray-700 hover:text-primary-500 transition-colors py-2"
className="block text-white/75 hover:text-white transition-colors py-2"
>
Features
</a>
<a
href="#about"
className="block text-gray-700 hover:text-primary-500 transition-colors py-2"
className="block text-white/75 hover:text-white transition-colors py-2"
>
About
</a>
<button
onClick={handleSignup}
className="w-full border-2 border-primary-500 text-primary-500 hover:bg-primary-50 font-semibold py-2 px-6 rounded-lg transition-colors duration-300"
className="w-full border border-primary-500/90 text-primary-500 hover:bg-primary-500/10 font-semibold py-2 px-6 rounded-lg transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-primary-500/50"
>
Sign Up
</button>
<button
onClick={handleAuthAction}
className="w-full bg-primary-500 hover:bg-primary-700 text-white font-semibold py-2 px-6 rounded-lg transition-colors duration-300"
className="w-full bg-primary-500 hover:bg-primary-600 text-white font-semibold py-2 px-6 rounded-lg transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-primary-500/50"
>
Login
</button>
@@ -136,28 +142,31 @@ export const HomePage = () => {
</section>
{/* Welcome Section */}
<section className="py-16 px-4 md:px-8 bg-white">
<section className="py-16 px-4 md:px-8 bg-[#1D1A18] border-t border-white/5">
<div className="max-w-4xl mx-auto text-center">
<p className="text-primary-500 text-sm font-semibold uppercase tracking-wide mb-4">
Welcome
</p>
<h2 className="text-3xl md:text-4xl font-bold text-gray-900 mb-6">
Thank you for your interest in MotoVaultPro!
</h2>
<p className="text-lg text-gray-600 leading-relaxed mb-8">
{/* Full Logo */}
<div className="mb-8">
<img
src="/images/logos/motovaultpro-logo-title.png"
alt="MotoVaultPro - Precision Vehicle Management"
className="w-[280px] sm:w-[380px] md:w-[520px] lg:w-[620px] max-w-[90vw] h-auto mx-auto"
loading="eager"
/>
</div>
<p className="text-lg text-titanio leading-relaxed mb-8">
We are pleased to provide comprehensive vehicle management solutions including Vehicle
Tracking, Fuel Log Management, Maintenance Records, Document Storage, Service Station
Locations, and detailed Analytics for all your vehicles. A combination of these features
can create a perfect management system for your fleet. Based on your specific needs, our
platform will help you determine the best approach to managing your vehicles.
</p>
<p className="text-lg text-gray-600 leading-relaxed mb-8">
<p className="text-lg text-titanio leading-relaxed mb-8">
Do not hesitate to reach out for assistance in creating a custom workflow that best fits
your needs.
</p>
<button
onClick={handleAuthAction}
className="bg-primary-500 hover:bg-primary-700 text-white font-semibold py-3 px-8 rounded-lg transition-colors duration-300"
className="bg-primary-500 hover:bg-primary-600 text-white font-semibold py-3 px-8 rounded-lg transition-colors duration-300 shadow-lg shadow-black/30 focus:outline-none focus:ring-2 focus:ring-primary-500/50"
>
Get Started
</button>
@@ -165,30 +174,30 @@ export const HomePage = () => {
</section>
{/* About Section */}
<section id="about" className="py-16 px-4 md:px-8 bg-gray-100">
<section id="about" className="py-16 px-4 md:px-8 bg-nero border-t border-white/5">
<div className="max-w-6xl mx-auto">
<div className="grid md:grid-cols-2 gap-12 items-center">
<div>
<h3 className="text-sm font-semibold text-primary-500 uppercase tracking-wide mb-4">
About Us
</h3>
<h2 className="text-3xl md:text-4xl font-bold text-gray-900 mb-6">
<h2 className="text-3xl md:text-4xl font-bold text-avus mb-6">
Overall, our goal is to meet each individual&apos;s needs with quality, passion, and
professionalism.
</h2>
<p className="text-lg text-gray-600 leading-relaxed mb-6">
<p className="text-lg text-titanio leading-relaxed mb-6">
Most importantly, we treat each and every vehicle as if it were our own and strive to
achieve perfection in vehicle management. If you are unsure of what you need for your
vehicles, we are happy to help talk you through the best options for comprehensive
tracking.
</p>
<p className="text-lg text-gray-600 leading-relaxed">
<p className="text-lg text-titanio leading-relaxed">
We are proud to use the finest technology and best practices to provide quality and
satisfaction for our users.
</p>
</div>
<div className="flex justify-center">
<div className="w-64 h-64 bg-primary-500 rounded-lg flex items-center justify-center">
<div className="w-64 h-64 bg-primary-500 rounded-lg border border-white/10 flex items-center justify-center shadow-lg shadow-black/30">
<div className="text-center text-white p-8">
<svg
className="w-32 h-32 mx-auto mb-4"
@@ -217,14 +226,14 @@ export const HomePage = () => {
</section>
{/* Bottom CTA */}
<section className="py-16 px-4 md:px-8 bg-primary-500 text-white">
<section className="py-16 px-4 md:px-8 bg-gradient-to-r from-primary-700 via-primary-500 to-primary-700 text-white border-t border-white/10">
<div className="max-w-4xl mx-auto text-center">
<h2 className="text-2xl md:text-3xl font-bold mb-6">
We are a cloud-based platform accessible anywhere, anytime.
</h2>
<button
onClick={handleAuthAction}
className="bg-white text-primary-500 hover:bg-gray-100 font-semibold py-3 px-8 rounded-lg transition-colors duration-300"
className="bg-white/95 text-primary-500 hover:bg-white font-semibold py-3 px-8 rounded-lg transition-colors duration-300 shadow-lg shadow-black/30 focus:outline-none focus:ring-2 focus:ring-white/50"
>
Get Started
</button>
@@ -232,9 +241,9 @@ export const HomePage = () => {
</section>
{/* Footer */}
<footer className="bg-gray-900 text-white py-8 px-4 md:px-8">
<footer className="bg-black text-white py-8 px-4 md:px-8 border-t border-white/10">
<div className="max-w-7xl mx-auto text-center">
<p className="text-gray-400">
<p className="text-white/50">
&copy; {new Date().getFullYear()} MotoVaultPro. All rights reserved.
</p>
</div>

View File

@@ -17,7 +17,7 @@ export const FeatureCard = ({ title, description, imageSrc, imageAlt }: FeatureC
transition={{ duration: 0.5 }}
whileHover={{ y: -5 }}
>
<div className="overflow-hidden rounded-lg shadow-lg hover:shadow-xl transition-shadow duration-300">
<div className="overflow-hidden rounded-lg bg-white/5 border border-white/10 shadow-lg shadow-black/30 hover:border-white/20 hover:shadow-xl hover:shadow-black/40 transition-all duration-300">
<div className="relative h-56 overflow-hidden">
<img
src={imageSrc}
@@ -25,9 +25,9 @@ export const FeatureCard = ({ title, description, imageSrc, imageAlt }: FeatureC
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-500"
/>
</div>
<div className="bg-white p-6">
<h3 className="text-xl font-bold text-gray-900 mb-2">{title}</h3>
<p className="text-gray-600 leading-relaxed">{description}</p>
<div className="p-6">
<h3 className="text-xl font-bold text-avus mb-2">{title}</h3>
<p className="text-titanio leading-relaxed">{description}</p>
</div>
</div>
</motion.div>

View File

@@ -53,13 +53,13 @@ const features = [
export const FeaturesGrid = () => {
return (
<section className="py-16 px-4 md:px-8 bg-gray-50">
<section className="py-16 px-4 md:px-8 bg-[#1D1A18] border-t border-white/5">
<div className="max-w-7xl mx-auto">
<div className="text-center mb-12">
<p className="text-primary-500 text-sm font-semibold uppercase tracking-wide mb-2">
Our Features
</p>
<h2 className="text-3xl md:text-4xl font-bold text-gray-900">What We Offer</h2>
<h2 className="text-3xl md:text-4xl font-bold text-avus">What We Offer</h2>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
@@ -69,7 +69,7 @@ export const FeaturesGrid = () => {
</div>
<div className="text-center mt-12">
<p className="text-lg text-gray-600 mb-6">
<p className="text-lg text-titanio mb-6">
We are a cloud-based platform accessible anywhere, anytime.
</p>
</div>

View File

@@ -71,17 +71,6 @@ export const HeroCarousel = () => {
className="w-full h-full object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-b from-black/60 via-black/40 to-black/60" />
<div className="absolute inset-0 flex flex-col items-center justify-center text-center px-4">
<p className="text-white text-sm md:text-base font-semibold uppercase tracking-widest mb-4">
Welcome to
</p>
<h1 className="text-white text-4xl md:text-6xl lg:text-7xl font-bold mb-6 leading-tight">
MOTOVAULTPRO
</h1>
<button className="bg-primary-500 hover:bg-primary-700 text-white font-semibold py-3 px-8 rounded-lg transition-colors duration-300">
Learn More
</button>
</div>
</div>
</div>
))}
@@ -121,12 +110,12 @@ export const HeroCarousel = () => {
.hero-carousel .slick-prev:before,
.hero-carousel .slick-next:before {
font-size: 50px;
opacity: 0.75;
opacity: 0.6;
}
.hero-carousel .slick-prev:hover:before,
.hero-carousel .slick-next:hover:before {
opacity: 1;
opacity: 0.9;
}
`}</style>
</div>

View File

@@ -8,6 +8,7 @@ import { useNavigate } from 'react-router-dom';
import { useUnits } from '../core/units/UnitsContext';
import { useAdminAccess } from '../core/auth/useAdminAccess';
import { useProfile, useUpdateProfile } from '../features/settings/hooks/useProfile';
import { useTheme } from '../shared-minimal/theme/ThemeContext';
import { DeleteAccountDialog } from '../features/settings/components/DeleteAccountDialog';
import { PendingDeletionBanner } from '../features/settings/components/PendingDeletionBanner';
import {
@@ -44,9 +45,9 @@ export const SettingsPage: React.FC = () => {
const navigate = useNavigate();
const { unitSystem, setUnitSystem } = useUnits();
const { isAdmin, loading: adminLoading } = useAdminAccess();
const { isDarkMode, setDarkMode } = useTheme();
const [notifications, setNotifications] = useState(true);
const [emailUpdates, setEmailUpdates] = useState(false);
const [darkMode, setDarkMode] = useState(false);
// Profile state
const { data: profile, isLoading: profileLoading } = useProfile();
@@ -314,7 +315,7 @@ export const SettingsPage: React.FC = () => {
/>
<ListItemSecondaryAction>
<Switch
checked={darkMode}
checked={isDarkMode}
onChange={(e) => setDarkMode(e.target.checked)}
color="primary"
/>

View File

@@ -0,0 +1,140 @@
/**
* @ai-summary Theme context for managing light/dark mode across the app
* Uses same localStorage key as useSettings for consistency
* Applies Tailwind dark class to document root
*/
import { createContext, useContext, useEffect, useMemo, useState, useCallback, ReactNode } from 'react';
import { ThemeProvider as MuiThemeProvider, Theme } from '@mui/material/styles';
import { CssBaseline } from '@mui/material';
import { md3LightTheme, md3DarkTheme } from './md3Theme';
// Same storage key as useSettingsPersistence for consistency
const SETTINGS_STORAGE_KEY = 'motovaultpro-mobile-settings';
interface ThemeContextValue {
isDarkMode: boolean;
toggleDarkMode: () => void;
setDarkMode: (value: boolean) => void;
theme: Theme;
}
const ThemeContext = createContext<ThemeContextValue | undefined>(undefined);
interface ThemeProviderProps {
children: ReactNode;
}
// Read dark mode preference from localStorage (synced with useSettings)
const getStoredDarkMode = (): boolean => {
try {
const stored = localStorage.getItem(SETTINGS_STORAGE_KEY);
if (stored) {
const settings = JSON.parse(stored);
return settings.darkMode ?? false;
}
} catch {
// Ignore parse errors
}
return false;
};
// Update dark mode in localStorage while preserving other settings
const setStoredDarkMode = (darkMode: boolean): void => {
try {
const stored = localStorage.getItem(SETTINGS_STORAGE_KEY);
const settings = stored ? JSON.parse(stored) : {};
settings.darkMode = darkMode;
localStorage.setItem(SETTINGS_STORAGE_KEY, JSON.stringify(settings));
} catch {
// Ignore storage errors
}
};
export const ThemeProvider = ({ children }: ThemeProviderProps) => {
const [isDarkMode, setIsDarkMode] = useState<boolean>(getStoredDarkMode);
const [isInitialized, setIsInitialized] = useState(false);
// Initialize on mount
useEffect(() => {
setIsDarkMode(getStoredDarkMode());
setIsInitialized(true);
}, []);
// Apply dark class to document root for Tailwind
useEffect(() => {
const root = document.documentElement;
if (isDarkMode) {
root.classList.add('dark');
} else {
root.classList.remove('dark');
}
}, [isDarkMode]);
// Listen for storage changes from other tabs/components
useEffect(() => {
const handleStorageChange = (e: StorageEvent) => {
if (e.key === SETTINGS_STORAGE_KEY && e.newValue) {
try {
const settings = JSON.parse(e.newValue);
if (typeof settings.darkMode === 'boolean') {
setIsDarkMode(settings.darkMode);
}
} catch {
// Ignore parse errors
}
}
};
window.addEventListener('storage', handleStorageChange);
return () => window.removeEventListener('storage', handleStorageChange);
}, []);
const setDarkMode = useCallback((value: boolean) => {
setIsDarkMode(value);
setStoredDarkMode(value);
}, []);
const toggleDarkMode = useCallback(() => {
setDarkMode(!isDarkMode);
}, [isDarkMode, setDarkMode]);
const theme = useMemo(() => {
return isDarkMode ? md3DarkTheme : md3LightTheme;
}, [isDarkMode]);
const value = useMemo(
() => ({
isDarkMode,
toggleDarkMode,
setDarkMode,
theme,
}),
[isDarkMode, toggleDarkMode, setDarkMode, theme]
);
// Prevent flash of wrong theme during SSR/initial load
if (!isInitialized) {
return null;
}
return (
<ThemeContext.Provider value={value}>
<MuiThemeProvider theme={theme}>
<CssBaseline />
{children}
</MuiThemeProvider>
</ThemeContext.Provider>
);
};
export const useTheme = (): ThemeContextValue => {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
};
// Re-export for convenience
export { md3LightTheme, md3DarkTheme } from './md3Theme';

View File

@@ -1,35 +1,29 @@
/**
* @ai-summary Material Design 3 theme configuration for MotoVaultPro
* Supports both light and dark modes
*/
import { createTheme, alpha } from '@mui/material/styles';
import { createTheme, alpha, ThemeOptions } from '@mui/material/styles';
// Brand color from mockup
// Brand colors
const primaryHex = '#7A212A';
export const md3Theme = createTheme({
palette: {
mode: 'light',
primary: {
main: primaryHex
},
secondary: {
main: alpha(primaryHex, 0.8)
},
background: {
default: '#F8F5F3',
paper: '#FFFFFF'
},
},
shape: {
borderRadius: 16
// Dark theme palette (Ferrari-inspired)
const nero = '#231F1C'; // Nero Daytona - page base
const avus = '#F2F3F6'; // Bianco Avus - primary text
const titanio = '#A8B8C0'; // Grigio Titanio - secondary text
// Shared theme options
const baseThemeOptions: ThemeOptions = {
shape: {
borderRadius: 16
},
typography: {
fontFamily:
'Inter, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif',
h3: {
fontWeight: 700,
letterSpacing: -0.5
h3: {
fontWeight: 700,
letterSpacing: -0.5
},
},
components: {
@@ -37,8 +31,6 @@ export const md3Theme = createTheme({
styleOverrides: {
root: {
borderRadius: 20,
boxShadow:
'0 1px 2px rgba(16,24,40,.04), 0 4px 16px rgba(16,24,40,.06)',
},
},
},
@@ -54,8 +46,8 @@ export const md3Theme = createTheme({
textTransform: 'none',
fontWeight: 600,
paddingInline: 18,
'&:hover': {
backgroundColor: alpha(theme.palette.primary.main, 0.18)
'&:hover': {
backgroundColor: alpha(theme.palette.primary.main, 0.18)
},
}),
},
@@ -71,6 +63,47 @@ export const md3Theme = createTheme({
},
],
},
MuiBottomNavigationAction: {
styleOverrides: {
root: {
minWidth: 0,
paddingTop: 8,
paddingBottom: 8,
'&.Mui-selected': {
color: primaryHex
},
},
},
},
},
};
// Light theme
export const md3LightTheme = createTheme({
...baseThemeOptions,
palette: {
mode: 'light',
primary: { main: primaryHex },
secondary: { main: alpha(primaryHex, 0.8) },
background: {
default: '#F8F5F3',
paper: '#FFFFFF'
},
text: {
primary: '#1a1a1a',
secondary: '#666666',
},
},
components: {
...baseThemeOptions.components,
MuiCard: {
styleOverrides: {
root: {
borderRadius: 20,
boxShadow: '0 1px 2px rgba(16,24,40,.04), 0 4px 16px rgba(16,24,40,.06)',
},
},
},
MuiBottomNavigation: {
styleOverrides: {
root: {
@@ -80,17 +113,48 @@ export const md3Theme = createTheme({
},
},
},
MuiBottomNavigationAction: {
},
});
// Dark theme
export const md3DarkTheme = createTheme({
...baseThemeOptions,
palette: {
mode: 'dark',
primary: { main: primaryHex },
secondary: { main: alpha(primaryHex, 0.8) },
background: {
default: nero,
paper: '#1D1A18'
},
text: {
primary: avus,
secondary: titanio,
},
},
components: {
...baseThemeOptions.components,
MuiCard: {
styleOverrides: {
root: {
minWidth: 0,
paddingTop: 8,
paddingBottom: 8,
'&.Mui-selected': {
color: primaryHex
},
borderRadius: 20,
boxShadow: '0 1px 2px rgba(0,0,0,.2), 0 4px 16px rgba(0,0,0,.3)',
backgroundColor: 'rgba(255,255,255,0.05)',
border: '1px solid rgba(255,255,255,0.1)',
},
},
},
MuiBottomNavigation: {
styleOverrides: {
root: {
borderTop: '1px solid rgba(255,255,255,.1)',
background: alpha(nero, 0.9),
backdropFilter: 'blur(8px)',
},
},
},
},
});
});
// Default export for backward compatibility
export const md3Theme = md3LightTheme;

View File

@@ -4,23 +4,41 @@ export default {
"./index.html",
"./src/**/*.{js,ts,jsx,tsx}",
],
darkMode: 'class',
theme: {
extend: {
colors: {
// Primary color scale (Rosso Mugello based)
primary: {
50: '#eff6ff',
50: '#fdf2f3',
100: '#fce4e6',
200: '#f9cdd1',
300: '#f4a5ad',
400: '#ec717e',
500: '#7A212A',
600: '#7A212A',
700: '#9c2a36',
600: '#6a1c24',
700: '#5a171e',
800: '#4a1318',
900: '#3a0f12',
},
gray: {
850: '#18202f',
},
// Dark theme tokens (Ferrari-inspired palette)
nero: '#231F1C', // Nero Daytona - page base
avus: '#F2F3F6', // Bianco Avus - primary text on dark
titanio: '#A8B8C0', // Grigio Titanio - secondary text on dark
canna: '#7E8792', // Canna Di Fucile - placeholder text
scuro: '#4C4E4D', // Grigio Scuro - input backgrounds
silverstone: '#585C64', // Grigio Silverstone - input borders
abudhabi: '#2885B5', // Blu Abu Dhabi - focus rings
// Legacy aliases
'moto-red': '#7A212A',
'moto-red-light': '#9c2a36',
},
backgroundImage: {
'gradient-moto': 'linear-gradient(90deg, #7A212A, #9c2a36)',
'gradient-cta': 'linear-gradient(90deg, #5a171e 0%, #7A212A 50%, #5a171e 100%)',
},
},
},