Set colorscheme.

This commit is contained in:
Drew 2025-09-22 20:43:03 -07:00
parent 6ef9843835
commit 4a596abedf
7 changed files with 80 additions and 344 deletions

View file

@ -1,59 +1,8 @@
@import 'tailwindcss';
@theme {
--font-sans:
'Inter', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji',
'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
/* Captain's Log Design Tokens */
--color-primary-50: #eff6ff;
--color-primary-100: #dbeafe;
--color-primary-500: #3b82f6;
--color-primary-950: #172554;
--color-task-todo: #3b82f6;
--color-task-done: #22c55e;
--color-task-backlog: #6b7280;
}
/* Captain's Log - Global Styles */
/* Base font family is handled by Material-UI theme */
html,
body {
@apply bg-gray-50 dark:bg-gray-950 text-gray-900 dark:text-gray-100;
@media (prefers-color-scheme: dark) {
color-scheme: dark;
}
}
/* Captain's Log Component Styles */
.task-card {
@apply bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 p-4 transition-all duration-200;
}
.task-card:hover {
@apply shadow-md border-gray-300 dark:border-gray-600;
}
.quick-capture {
@apply bg-white dark:bg-gray-800 border-2 border-dashed border-gray-300 dark:border-gray-600 rounded-xl p-4 transition-colors duration-200;
}
.quick-capture:focus-within {
@apply border-blue-500 bg-blue-50 dark:bg-blue-950;
}
.status-badge {
@apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium;
}
.status-todo {
@apply bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200;
}
.status-done {
@apply bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200;
}
.status-backlog {
@apply bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200;
margin: 0;
padding: 0;
}

View file

@ -1,4 +1,4 @@
import React, { useState, useEffect } from 'react'
import React, { useState } from 'react'
import {
AppBar,
Box,
@ -21,8 +21,6 @@ import {
Dashboard as DashboardIcon,
Add as AddIcon,
Settings as SettingsIcon,
DarkMode as DarkModeIcon,
LightMode as LightModeIcon,
} from '@mui/icons-material'
const drawerWidth = 240
@ -35,28 +33,11 @@ interface LayoutProps {
export default function Layout({ children, loading = false }: LayoutProps) {
const theme = useTheme()
const [mobileOpen, setMobileOpen] = useState(false)
const [darkMode, setDarkMode] = useState(false)
// Check system preference and localStorage on mount
useEffect(() => {
const savedTheme = localStorage.getItem('theme')
const prefersDark = window.matchMedia(
'(prefers-color-scheme: dark)'
).matches
setDarkMode(savedTheme === 'dark' || (!savedTheme && prefersDark))
}, [])
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen)
}
const handleThemeToggle = () => {
const newTheme = !darkMode
setDarkMode(newTheme)
localStorage.setItem('theme', newTheme ? 'dark' : 'light')
document.documentElement.classList.toggle('dark', newTheme)
}
const menuItems = [
{
text: 'Tasks',
@ -72,17 +53,7 @@ export default function Layout({ children, loading = false }: LayoutProps) {
const drawer = (
<div>
<Toolbar>
<Typography
variant="h6"
noWrap
component="div"
sx={{ fontWeight: 700 }}
>
Captain's Log
</Typography>
</Toolbar>
<List>
<List sx={{ pt: 2 }}>
{menuItems.map(item => (
<ListItem key={item.text} disablePadding>
<ListItemButton
@ -134,8 +105,10 @@ export default function Layout({ children, loading = false }: LayoutProps) {
<AppBar
position="fixed"
sx={{
width: { md: `calc(100% - ${drawerWidth}px)` },
ml: { md: `${drawerWidth}px` },
width: '100%',
backgroundColor: theme.palette.primary.main,
color: theme.palette.primary.contrastText,
zIndex: theme.zIndex.drawer + 1,
}}
>
<Toolbar>
@ -149,17 +122,21 @@ export default function Layout({ children, loading = false }: LayoutProps) {
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap component="div" sx={{ flexGrow: 1 }}>
Captain's Log
</Typography>
<IconButton
color="inherit"
onClick={handleThemeToggle}
aria-label="toggle dark mode"
<Typography
variant="h6"
noWrap
component="div"
sx={{
flexGrow: 1,
color: '#ffffff',
fontWeight: 700,
display: 'flex',
alignItems: 'center',
gap: 1,
}}
>
{darkMode ? <LightModeIcon /> : <DarkModeIcon />}
</IconButton>
Captain's Log
</Typography>
</Toolbar>
</AppBar>
@ -196,6 +173,8 @@ export default function Layout({ children, loading = false }: LayoutProps) {
'& .MuiDrawer-paper': {
boxSizing: 'border-box',
width: drawerWidth,
top: '64px', // Position below AppBar
height: 'calc(100% - 64px)', // Adjust height to account for AppBar
},
}}
open

View file

@ -10,7 +10,7 @@ import { ThemeProvider } from '@mui/material/styles'
import { CssBaseline } from '@mui/material'
import type { Route } from './+types/root'
import { theme, darkTheme } from './theme'
import { theme } from './theme'
import AppLayout from './components/Layout'
import './app.css'
@ -46,14 +46,8 @@ export function Layout({ children }: { children: React.ReactNode }) {
}
export default function App() {
const isDarkMode =
typeof window !== 'undefined' &&
(localStorage.getItem('theme') === 'dark' ||
(!localStorage.getItem('theme') &&
window.matchMedia('(prefers-color-scheme: dark)').matches))
return (
<ThemeProvider theme={isDarkMode ? darkTheme : theme}>
<ThemeProvider theme={theme}>
<CssBaseline />
<AppLayout>
<Outlet />
@ -78,14 +72,8 @@ export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
stack = error.stack
}
const isDarkMode =
typeof window !== 'undefined' &&
(localStorage.getItem('theme') === 'dark' ||
(!localStorage.getItem('theme') &&
window.matchMedia('(prefers-color-scheme: dark)').matches))
return (
<ThemeProvider theme={isDarkMode ? darkTheme : theme}>
<ThemeProvider theme={theme}>
<CssBaseline />
<AppLayout>
<main className="pt-16 p-4 container mx-auto">

View file

@ -1,5 +1,24 @@
import { createTheme } from '@mui/material/styles'
// Color palette constants
const colors = {
// Primary Colors
deepNavy: '#1e3a5f', // Main brand color for headers and primary actions
oceanBlue: '#2c5282', // Secondary blue for links and active states
compassGold: '#d69e2e', // Accent color for highlights and call-to-actions
// Status Colors
chartGreen: '#48bb78', // Completed tasks and success states
sunsetCoral: '#f56565', // Urgent tasks and error states
seaFoam: '#4fd1c7', // Information and notification states
// Neutrals
parchment: '#f7fafc', // Clean background color
fogGray: '#e2e8f0', // Subtle borders and dividers
stormGray: '#718096', // Secondary text
anchorDark: '#2d3748', // Primary text and headings
}
declare module '@mui/material/styles' {
interface Theme {
custom: {
@ -26,29 +45,37 @@ export const theme = createTheme({
palette: {
mode: 'light',
primary: {
50: '#eff6ff',
100: '#dbeafe',
500: '#3b82f6',
main: '#3b82f6',
main: colors.deepNavy,
light: colors.oceanBlue,
contrastText: '#ffffff',
},
secondary: {
main: colors.compassGold,
contrastText: '#ffffff',
},
success: {
main: colors.chartGreen,
},
error: {
main: colors.sunsetCoral,
},
info: {
main: colors.seaFoam,
},
background: {
default: '#f9fafb',
default: colors.parchment,
paper: '#ffffff',
},
text: {
primary: '#111827',
secondary: '#6b7280',
primary: colors.anchorDark,
secondary: colors.stormGray,
},
grey: {
50: '#f9fafb',
100: '#f3f4f6',
200: '#e5e7eb',
300: '#d1d5db',
600: '#4b5563',
700: '#374151',
800: '#1f2937',
900: '#111827',
100: '#edf2f7',
200: colors.fogGray,
300: '#cbd5e0',
500: colors.stormGray,
700: colors.anchorDark,
},
},
typography: {
@ -84,11 +111,11 @@ export const theme = createTheme({
MuiAppBar: {
styleOverrides: {
root: {
backgroundColor: '#ffffff',
color: '#111827',
backgroundColor: colors.deepNavy,
color: '#ffffff',
boxShadow:
'0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
borderBottom: '1px solid #e5e7eb',
borderBottom: 'none',
},
},
},
@ -96,7 +123,7 @@ export const theme = createTheme({
styleOverrides: {
paper: {
backgroundColor: '#ffffff',
borderRight: '1px solid #e5e7eb',
borderRight: `1px solid ${colors.fogGray}`,
},
},
},
@ -122,11 +149,11 @@ export const theme = createTheme({
borderRadius: '0.75rem',
boxShadow:
'0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
border: '1px solid #e5e7eb',
border: `1px solid ${colors.fogGray}`,
'&:hover': {
boxShadow:
'0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
borderColor: '#d1d5db',
borderColor: '#cbd5e0',
},
},
},
@ -143,138 +170,9 @@ export const theme = createTheme({
},
custom: {
task: {
todo: '#3b82f6',
done: '#22c55e',
backlog: '#6b7280',
},
},
})
export const darkTheme = createTheme({
palette: {
mode: 'dark',
primary: {
50: '#eff6ff',
100: '#dbeafe',
500: '#3b82f6',
main: '#3b82f6',
contrastText: '#ffffff',
},
background: {
default: '#030712',
paper: '#1f2937',
},
text: {
primary: '#f9fafb',
secondary: '#9ca3af',
},
grey: {
50: '#f9fafb',
100: '#f3f4f6',
200: '#e5e7eb',
300: '#d1d5db',
600: '#4b5563',
700: '#374151',
800: '#1f2937',
900: '#111827',
},
},
typography: {
fontFamily: '"Inter", ui-sans-serif, system-ui, sans-serif',
h1: {
fontSize: '2rem',
fontWeight: 700,
lineHeight: 1.2,
},
h2: {
fontSize: '1.5rem',
fontWeight: 600,
lineHeight: 1.3,
},
h3: {
fontSize: '1.25rem',
fontWeight: 600,
lineHeight: 1.4,
},
body1: {
fontSize: '1rem',
lineHeight: 1.6,
},
body2: {
fontSize: '0.875rem',
lineHeight: 1.5,
},
},
shape: {
borderRadius: 12,
},
components: {
MuiAppBar: {
styleOverrides: {
root: {
backgroundColor: '#1f2937',
color: '#f9fafb',
boxShadow:
'0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
borderBottom: '1px solid #374151',
},
},
},
MuiDrawer: {
styleOverrides: {
paper: {
backgroundColor: '#1f2937',
borderRight: '1px solid #374151',
},
},
},
MuiButton: {
styleOverrides: {
root: {
textTransform: 'none',
borderRadius: '0.75rem',
fontWeight: 500,
},
contained: {
boxShadow: '0 1px 2px 0 rgb(0 0 0 / 0.05)',
'&:hover': {
boxShadow:
'0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
},
},
},
},
MuiCard: {
styleOverrides: {
root: {
backgroundColor: '#1f2937',
borderRadius: '0.75rem',
boxShadow:
'0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)',
border: '1px solid #374151',
'&:hover': {
boxShadow:
'0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
borderColor: '#4b5563',
},
},
},
},
MuiTextField: {
styleOverrides: {
root: {
'& .MuiOutlinedInput-root': {
borderRadius: '0.75rem',
},
},
},
},
},
custom: {
task: {
todo: '#3b82f6',
done: '#22c55e',
backlog: '#6b7280',
todo: colors.oceanBlue,
done: colors.chartGreen,
backlog: colors.stormGray,
},
},
})

View file

@ -33,7 +33,6 @@
"eslint-plugin-react-refresh": "^0.4.20",
"jsdom": "^27.0.0",
"prettier": "^3.6.2",
"tailwindcss": "^4.1.4",
"typescript": "^5.8.3",
"typescript-eslint": "^8.44.0",
"vite": "^6.3.3",

View file

@ -43,7 +43,6 @@
"eslint-plugin-react-refresh": "^0.4.20",
"jsdom": "^27.0.0",
"prettier": "^3.6.2",
"tailwindcss": "^4.1.4",
"typescript": "^5.8.3",
"typescript-eslint": "^8.44.0",
"vite": "^6.3.3",

View file

@ -1,76 +0,0 @@
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./app/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {
fontFamily: {
sans: ["Inter", "ui-sans-serif", "system-ui", "sans-serif"],
},
colors: {
primary: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
},
success: {
50: '#f0fdf4',
100: '#dcfce7',
200: '#bbf7d0',
300: '#86efac',
400: '#4ade80',
500: '#22c55e',
600: '#16a34a',
700: '#15803d',
800: '#166534',
900: '#14532d',
},
warning: {
50: '#fffbeb',
100: '#fef3c7',
200: '#fde68a',
300: '#fcd34d',
400: '#fbbf24',
500: '#f59e0b',
600: '#d97706',
700: '#b45309',
800: '#92400e',
900: '#78350f',
},
danger: {
50: '#fef2f2',
100: '#fee2e2',
200: '#fecaca',
300: '#fca5a5',
400: '#f87171',
500: '#ef4444',
600: '#dc2626',
700: '#b91c1c',
800: '#991b1b',
900: '#7f1d1d',
},
},
spacing: {
'18': '4.5rem',
'88': '22rem',
},
borderRadius: {
'xl': '0.75rem',
'2xl': '1rem',
},
boxShadow: {
'card': '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)',
'card-hover': '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)',
},
},
},
plugins: [],
}