refactor: Use Mantine CSS variables and modules for theme styling
Replaced all hardcoded colors and JS template literal styling with Mantine's canonical approach using CSS modules and CSS variables. This ensures colors transition programmatically without JS interpolation. - Updated globals.css to use data-mantine-color-scheme selectors - Created CSS modules for all navigation components and chat page - Removed useComputedColorScheme/useMantineTheme hooks where not needed - Fixed body background to properly adapt to light/dark mode - All borders, backgrounds, and colors now use CSS variables - Maintained full theme support across all components 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
66
app/chat/page.module.css
Normal file
66
app/chat/page.module.css
Normal file
@@ -0,0 +1,66 @@
|
||||
/* Message bubbles */
|
||||
.userMessage {
|
||||
align-self: flex-end;
|
||||
max-width: 80%;
|
||||
}
|
||||
|
||||
.assistantMessage {
|
||||
align-self: flex-start;
|
||||
max-width: 80%;
|
||||
}
|
||||
|
||||
[data-mantine-color-scheme="light"] .userMessage {
|
||||
background-color: var(--mantine-color-gray-1);
|
||||
}
|
||||
|
||||
[data-mantine-color-scheme="dark"] .userMessage {
|
||||
background-color: var(--mantine-color-dark-6);
|
||||
}
|
||||
|
||||
[data-mantine-color-scheme="light"] .assistantMessage {
|
||||
background-color: var(--mantine-color-gray-0);
|
||||
}
|
||||
|
||||
[data-mantine-color-scheme="dark"] .assistantMessage {
|
||||
background-color: var(--mantine-color-dark-7);
|
||||
}
|
||||
|
||||
/* Voice controls */
|
||||
.voiceControls {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.voiceControls {
|
||||
left: 260px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.voiceControls {
|
||||
bottom: 90px;
|
||||
}
|
||||
}
|
||||
|
||||
[data-mantine-color-scheme="light"] .voiceControls {
|
||||
border-top: 1px solid var(--mantine-color-gray-3);
|
||||
background-color: var(--mantine-color-white);
|
||||
}
|
||||
|
||||
[data-mantine-color-scheme="dark"] .voiceControls {
|
||||
border-top: 1px solid var(--mantine-color-dark-5);
|
||||
background-color: var(--mantine-color-dark-8);
|
||||
}
|
||||
|
||||
/* Dev panel */
|
||||
[data-mantine-color-scheme="light"] .devPanel {
|
||||
background-color: var(--mantine-color-white);
|
||||
}
|
||||
|
||||
[data-mantine-color-scheme="dark"] .devPanel {
|
||||
background-color: var(--mantine-color-dark-8);
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import { useAppMachine } from '@/hooks/useAppMachine';
|
||||
import { notifications } from '@mantine/notifications';
|
||||
import { useMediaQuery } from '@mantine/hooks';
|
||||
import { useSelector } from '@xstate/react';
|
||||
import styles from './page.module.css';
|
||||
|
||||
/**
|
||||
* Get the voice button text based on the current state
|
||||
@@ -242,10 +243,7 @@ export default function ChatPage() {
|
||||
shadow="md"
|
||||
p="sm"
|
||||
radius="lg"
|
||||
style={{
|
||||
alignSelf: m.role === 'user' ? 'flex-end' : 'flex-start',
|
||||
backgroundColor: m.role === 'user' ? '#343a40' : '#212529',
|
||||
}}
|
||||
className={m.role === 'user' ? styles.userMessage : styles.assistantMessage}
|
||||
w="80%"
|
||||
>
|
||||
<Title order={6} size="sm">
|
||||
@@ -276,7 +274,7 @@ export default function ChatPage() {
|
||||
shadow="md"
|
||||
p="sm"
|
||||
radius="lg"
|
||||
style={{ alignSelf: 'flex-start', backgroundColor: '#212529' }}
|
||||
className={styles.assistantMessage}
|
||||
w="80%"
|
||||
>
|
||||
<Title order={6} size="sm">
|
||||
@@ -298,7 +296,7 @@ export default function ChatPage() {
|
||||
shadow="md"
|
||||
p="sm"
|
||||
radius="lg"
|
||||
style={{ alignSelf: 'flex-end', backgroundColor: '#343a40' }}
|
||||
className={styles.userMessage}
|
||||
w="80%"
|
||||
>
|
||||
<Title order={6} size="sm">
|
||||
@@ -315,15 +313,7 @@ export default function ChatPage() {
|
||||
withBorder
|
||||
p="md"
|
||||
radius={0}
|
||||
style={{
|
||||
position: 'fixed',
|
||||
bottom: isMobile ? '90px' : 0,
|
||||
left: isMobile ? 0 : '260px',
|
||||
right: 0,
|
||||
zIndex: 50,
|
||||
borderTop: '1px solid #373A40',
|
||||
backgroundColor: '#1a1b1e',
|
||||
}}
|
||||
className={styles.voiceControls}
|
||||
>
|
||||
<Container size="md">
|
||||
<Stack gap="sm">
|
||||
@@ -380,7 +370,12 @@ export default function ChatPage() {
|
||||
|
||||
{/* Development Test Controls */}
|
||||
{process.env.NODE_ENV === 'development' && (
|
||||
<Paper withBorder p="sm" radius="md" style={{ backgroundColor: '#1a1b1e' }}>
|
||||
<Paper
|
||||
withBorder
|
||||
p="sm"
|
||||
radius="md"
|
||||
className={styles.devPanel}
|
||||
>
|
||||
<Stack gap="xs">
|
||||
<Text size="xs" fw={700} c="dimmed">
|
||||
DEV: State Machine Testing
|
||||
|
||||
@@ -3,6 +3,15 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #181a1d; /* Our darkest gray */
|
||||
color: #e9ecef; /* Our lightest gray */
|
||||
}
|
||||
|
||||
/* Use Mantine's data attribute for theme-aware styling */
|
||||
[data-mantine-color-scheme="light"] body {
|
||||
background-color: var(--mantine-color-white);
|
||||
color: var(--mantine-color-black);
|
||||
}
|
||||
|
||||
[data-mantine-color-scheme="dark"] body {
|
||||
background-color: var(--mantine-color-dark-8);
|
||||
color: var(--mantine-color-dark-0);
|
||||
}
|
||||
|
||||
10
app/theme.ts
10
app/theme.ts
@@ -56,16 +56,6 @@ export const theme = createTheme({
|
||||
radius: 'md',
|
||||
withBorder: true,
|
||||
},
|
||||
styles: (theme) => ({
|
||||
root: {
|
||||
backgroundColor: theme.colorScheme === 'dark'
|
||||
? theme.colors.dark[7]
|
||||
: theme.white,
|
||||
borderColor: theme.colorScheme === 'dark'
|
||||
? theme.colors.dark[5]
|
||||
: theme.colors.gray[3],
|
||||
},
|
||||
}),
|
||||
},
|
||||
TextInput: {
|
||||
defaultProps: {
|
||||
|
||||
27
components/Navigation/DesktopSidebar.module.css
Normal file
27
components/Navigation/DesktopSidebar.module.css
Normal file
@@ -0,0 +1,27 @@
|
||||
.sidebar {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
[data-mantine-color-scheme="light"] .sidebar {
|
||||
border-right: 1px solid var(--mantine-color-gray-3);
|
||||
}
|
||||
|
||||
[data-mantine-color-scheme="dark"] .sidebar {
|
||||
border-right: 1px solid var(--mantine-color-dark-5);
|
||||
}
|
||||
|
||||
.devPanel {
|
||||
margin-top: var(--mantine-spacing-xl);
|
||||
padding: var(--mantine-spacing-sm);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
[data-mantine-color-scheme="light"] .devPanel {
|
||||
border: 1px solid var(--mantine-color-gray-4);
|
||||
}
|
||||
|
||||
[data-mantine-color-scheme="dark"] .devPanel {
|
||||
border: 1px solid var(--mantine-color-dark-4);
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import { useSelector } from '@xstate/react';
|
||||
import { useAppMachine } from '@/hooks/useAppMachine';
|
||||
import { UserMenu } from '@/components/UserMenu';
|
||||
import { ThemeToggle } from '@/components/ThemeToggle';
|
||||
import styles from './DesktopSidebar.module.css';
|
||||
|
||||
export function DesktopSidebar() {
|
||||
const actor = useAppMachine();
|
||||
@@ -43,14 +44,7 @@ export function DesktopSidebar() {
|
||||
});
|
||||
|
||||
return (
|
||||
<Box
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
borderRight: '1px solid #373A40',
|
||||
padding: '1rem',
|
||||
}}
|
||||
>
|
||||
<Box className={styles.sidebar}>
|
||||
<Stack gap="xs">
|
||||
<Group gap="md" mb="lg" align="center" wrap="nowrap">
|
||||
<img
|
||||
@@ -110,7 +104,7 @@ export function DesktopSidebar() {
|
||||
}}
|
||||
/>
|
||||
|
||||
<Divider my="md" color="#373A40" />
|
||||
<Divider my="md" />
|
||||
|
||||
{/* Theme Toggle */}
|
||||
<ThemeToggle />
|
||||
@@ -120,7 +114,7 @@ export function DesktopSidebar() {
|
||||
|
||||
{/* Development state panel */}
|
||||
{process.env.NODE_ENV === 'development' && (
|
||||
<Box mt="xl" p="sm" style={{ border: '1px solid #495057', borderRadius: '4px' }}>
|
||||
<Box className={styles.devPanel}>
|
||||
<Text size="xs" fw={700} c="dimmed" mb="xs">
|
||||
DEV: App State
|
||||
</Text>
|
||||
|
||||
15
components/Navigation/MobileBottomBar.module.css
Normal file
15
components/Navigation/MobileBottomBar.module.css
Normal file
@@ -0,0 +1,15 @@
|
||||
.bottomBar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
[data-mantine-color-scheme="light"] .bottomBar {
|
||||
border-top: 1px solid var(--mantine-color-gray-3);
|
||||
}
|
||||
|
||||
[data-mantine-color-scheme="dark"] .bottomBar {
|
||||
border-top: 1px solid var(--mantine-color-dark-5);
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import { useSelector } from '@xstate/react';
|
||||
import { useAppMachine } from '@/hooks/useAppMachine';
|
||||
import { UserMenu } from '@/components/UserMenu';
|
||||
import { useState, useEffect } from 'react';
|
||||
import styles from './MobileBottomBar.module.css';
|
||||
|
||||
interface UserProfile {
|
||||
did: string;
|
||||
@@ -68,14 +69,7 @@ export function MobileBottomBar() {
|
||||
withBorder
|
||||
p="md"
|
||||
radius={0}
|
||||
style={{
|
||||
position: 'fixed',
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 100,
|
||||
borderTop: '1px solid #373A40',
|
||||
}}
|
||||
className={styles.bottomBar}
|
||||
>
|
||||
<Group justify="space-around" grow>
|
||||
<Stack gap={4} align="center" onClick={() => handleNavigation('convo')} style={{ cursor: 'pointer' }}>
|
||||
|
||||
15
components/Navigation/MobileHeader.module.css
Normal file
15
components/Navigation/MobileHeader.module.css
Normal file
@@ -0,0 +1,15 @@
|
||||
.header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
[data-mantine-color-scheme="light"] .header {
|
||||
border-bottom: 1px solid var(--mantine-color-gray-3);
|
||||
}
|
||||
|
||||
[data-mantine-color-scheme="dark"] .header {
|
||||
border-bottom: 1px solid var(--mantine-color-dark-5);
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import { Group, Title, Paper } from '@mantine/core';
|
||||
import styles from './MobileHeader.module.css';
|
||||
|
||||
export function MobileHeader() {
|
||||
return (
|
||||
@@ -14,14 +15,7 @@ export function MobileHeader() {
|
||||
withBorder
|
||||
p="sm"
|
||||
radius={0}
|
||||
style={{
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 100,
|
||||
borderBottom: '1px solid #373A40',
|
||||
}}
|
||||
className={styles.header}
|
||||
>
|
||||
<Group gap="md" align="center" wrap="nowrap">
|
||||
<img
|
||||
|
||||
Reference in New Issue
Block a user