Css fixes, Tailwind forward

This commit is contained in:
Reece 2025-06-08 13:45:45 +01:00
parent b1584151ba
commit 1e0949ff2b
10 changed files with 140 additions and 484 deletions

View File

@ -4,9 +4,7 @@ import { mantineTheme } from './theme/mantineTheme';
import HomePage from './pages/HomePage'; import HomePage from './pages/HomePage';
// Import global styles // Import global styles
import './styles/theme.css';
import './styles/tailwind.css'; import './styles/tailwind.css';
import './styles/components.css';
import './index.css'; import './index.css';
export default function App() { export default function App() {

View File

@ -12,7 +12,7 @@ import { Group } from "@mantine/core";
const VIEW_OPTIONS = [ const VIEW_OPTIONS = [
{ {
label: ( label: (
<Group gap={4}> <Group gap={5}>
<VisibilityIcon fontSize="small" /> <VisibilityIcon fontSize="small" />
</Group> </Group>
), ),
@ -48,8 +48,8 @@ const TopControls: React.FC<TopControlsProps> = ({
const { colorScheme, toggleColorScheme } = useMantineColorScheme(); const { colorScheme, toggleColorScheme } = useMantineColorScheme();
return ( return (
<div className="absolute left-0 w-full top-0 z-sticky pointer-events-none"> <div className="absolute left-0 w-full top-0 z-[9999] pointer-events-none">
<div className="absolute left-app-md top-1/2 -translate-y-1/2 pointer-events-auto flex gap-app-sm items-center"> <div className="absolute left-4 top-1/2 -translate-y-1/2 pointer-events-auto flex gap-2 items-center">
<Button <Button
onClick={toggleColorScheme} onClick={toggleColorScheme}
variant="subtle" variant="subtle"
@ -61,7 +61,6 @@ const TopControls: React.FC<TopControlsProps> = ({
<LanguageSelector /> <LanguageSelector />
</div> </div>
<div className="flex justify-center items-center h-full pointer-events-auto"> <div className="flex justify-center items-center h-full pointer-events-auto">
<div className="bg-bg-overlay backdrop-blur-sm rounded-app-xl p-app-sm">
<SegmentedControl <SegmentedControl
data={VIEW_OPTIONS} data={VIEW_OPTIONS}
value={currentView} value={currentView}
@ -71,10 +70,9 @@ const TopControls: React.FC<TopControlsProps> = ({
size="md" size="md"
fullWidth fullWidth
/> />
</div>
</div> </div>
</div> </div>
); );
}; };
export default TopControls; export default TopControls;

View File

@ -25,8 +25,8 @@ interface LazyPageImageProps {
setPageRef: (index: number, ref: HTMLImageElement | null) => void; setPageRef: (index: number, ref: HTMLImageElement | null) => void;
} }
const LazyPageImage: React.FC<LazyPageImageProps> = ({ const LazyPageImage: React.FC<LazyPageImageProps> = ({
pageIndex, zoom, theme, isFirst, renderPage, pageImages, setPageRef pageIndex, zoom, theme, isFirst, renderPage, pageImages, setPageRef
}) => { }) => {
const [isVisible, setIsVisible] = useState(false); const [isVisible, setIsVisible] = useState(false);
const [imageUrl, setImageUrl] = useState<string | null>(pageImages[pageIndex]); const [imageUrl, setImageUrl] = useState<string | null>(pageImages[pageIndex]);
@ -41,9 +41,9 @@ const LazyPageImage: React.FC<LazyPageImageProps> = ({
} }
}); });
}, },
{ {
rootMargin: '200px', // Start loading 200px before visible rootMargin: '200px', // Start loading 200px before visible
threshold: 0.1 threshold: 0.1
} }
); );
@ -104,10 +104,10 @@ const LazyPageImage: React.FC<LazyPageImageProps> = ({
> >
{isVisible ? ( {isVisible ? (
<div style={{ textAlign: 'center' }}> <div style={{ textAlign: 'center' }}>
<div style={{ <div style={{
width: 20, width: 20,
height: 20, height: 20,
border: '2px solid #ddd', border: '2px solid #ddd',
borderTop: '2px solid #666', borderTop: '2px solid #666',
borderRadius: '50%', borderRadius: '50%',
animation: 'spin 1s linear infinite', animation: 'spin 1s linear infinite',
@ -163,7 +163,7 @@ const Viewer: React.FC<ViewerProps> = ({
} }
renderingPagesRef.current.add(pageIndex); renderingPagesRef.current.add(pageIndex);
try { try {
const page = await pdfDocRef.current.getPage(pageNum); const page = await pdfDocRef.current.getPage(pageNum);
const viewport = page.getViewport({ scale: 1.2 }); const viewport = page.getViewport({ scale: 1.2 });
@ -171,25 +171,25 @@ const Viewer: React.FC<ViewerProps> = ({
canvas.width = viewport.width; canvas.width = viewport.width;
canvas.height = viewport.height; canvas.height = viewport.height;
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext("2d");
if (ctx) { if (ctx) {
await page.render({ canvasContext: ctx, viewport }).promise; await page.render({ canvasContext: ctx, viewport }).promise;
const dataUrl = canvas.toDataURL(); const dataUrl = canvas.toDataURL();
// Update the pageImages array // Update the pageImages array
setPageImages(prev => { setPageImages(prev => {
const newImages = [...prev]; const newImages = [...prev];
newImages[pageIndex] = dataUrl; newImages[pageIndex] = dataUrl;
return newImages; return newImages;
}); });
renderingPagesRef.current.delete(pageIndex); renderingPagesRef.current.delete(pageIndex);
return dataUrl; return dataUrl;
} }
} catch (error) { } catch (error) {
console.error(`Failed to render page ${pageNum}:`, error); console.error(`Failed to render page ${pageNum}:`, error);
} }
renderingPagesRef.current.delete(pageIndex); renderingPagesRef.current.delete(pageIndex);
return null; return null;
}; };
@ -284,21 +284,21 @@ const Viewer: React.FC<ViewerProps> = ({
setLoading(true); setLoading(true);
try { try {
let pdfUrl = pdfFile.url; let pdfUrl = pdfFile.url;
// Handle special IndexedDB URLs for large files // Handle special IndexedDB URLs for large files
if (pdfFile.url.startsWith('indexeddb:')) { if (pdfFile.url.startsWith('indexeddb:')) {
const fileId = pdfFile.url.replace('indexeddb:', ''); const fileId = pdfFile.url.replace('indexeddb:', '');
console.log('Loading large file from IndexedDB:', fileId); console.log('Loading large file from IndexedDB:', fileId);
// Get data directly from IndexedDB // Get data directly from IndexedDB
const arrayBuffer = await fileStorage.getFileData(fileId); const arrayBuffer = await fileStorage.getFileData(fileId);
if (!arrayBuffer) { if (!arrayBuffer) {
throw new Error('File not found in IndexedDB - may have been purged by browser'); throw new Error('File not found in IndexedDB - may have been purged by browser');
} }
// Store reference for cleanup // Store reference for cleanup
currentArrayBufferRef.current = arrayBuffer; currentArrayBufferRef.current = arrayBuffer;
// Use ArrayBuffer directly instead of creating blob URL // Use ArrayBuffer directly instead of creating blob URL
const pdf = await getDocument({ data: arrayBuffer }).promise; const pdf = await getDocument({ data: arrayBuffer }).promise;
pdfDocRef.current = pdf; pdfDocRef.current = pdf;
@ -321,8 +321,8 @@ const Viewer: React.FC<ViewerProps> = ({
if (!cancelled) setLoading(false); if (!cancelled) setLoading(false);
} }
loadPdfInfo(); loadPdfInfo();
return () => { return () => {
cancelled = true; cancelled = true;
// Cleanup ArrayBuffer reference to help garbage collection // Cleanup ArrayBuffer reference to help garbage collection
currentArrayBufferRef.current = null; currentArrayBufferRef.current = null;
}; };
@ -339,16 +339,7 @@ const Viewer: React.FC<ViewerProps> = ({
}, [pageImages]); }, [pageImages]);
return ( return (
<Paper <>
shadow="xs"
radius="md"
style={{
height: "100vh",
display: "flex",
flexDirection: "column",
position: "relative",
}}
>
{!pdfFile ? ( {!pdfFile ? (
<Center style={{ flex: 1 }}> <Center style={{ flex: 1 }}>
<Stack align="center"> <Stack align="center">
@ -586,7 +577,7 @@ const Viewer: React.FC<ViewerProps> = ({
</ScrollArea> </ScrollArea>
)} )}
</Paper> </>
); );
}; };

View File

@ -3,7 +3,6 @@ import React from 'react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
import { ColorSchemeScript, MantineProvider, mantineHtmlProps } from '@mantine/core'; import { ColorSchemeScript, MantineProvider, mantineHtmlProps } from '@mantine/core';
import { BrowserRouter } from 'react-router-dom'; import { BrowserRouter } from 'react-router-dom';
import './index.css';
import App from './App'; import App from './App';
import './i18n'; // Initialize i18next import './i18n'; // Initialize i18next

View File

@ -16,7 +16,6 @@ import PageEditor from "../components/PageEditor";
import Viewer from "../components/Viewer"; import Viewer from "../components/Viewer";
import TopControls from "../components/TopControls"; import TopControls from "../components/TopControls";
import ToolRenderer from "../components/ToolRenderer"; import ToolRenderer from "../components/ToolRenderer";
import styles from "../styles/HomePage.module.css";
type ToolRegistryEntry = { type ToolRegistryEntry = {
icon: React.ReactNode; icon: React.ReactNode;
@ -55,7 +54,6 @@ export default function HomePage() {
// URL parameter management // URL parameter management
const { toolParams, updateParams } = useToolParams(selectedToolKey, currentView); const { toolParams, updateParams } = useToolParams(selectedToolKey, currentView);
// Create translated tool registry
const toolRegistry: ToolRegistry = { const toolRegistry: ToolRegistry = {
split: { ...baseToolRegistry.split, name: t("home.split.title", "Split PDF") }, split: { ...baseToolRegistry.split, name: t("home.split.title", "Split PDF") },
compress: { ...baseToolRegistry.compress, name: t("home.compressPdfs.title", "Compress PDF") }, compress: { ...baseToolRegistry.compress, name: t("home.compressPdfs.title", "Compress PDF") },
@ -82,32 +80,27 @@ export default function HomePage() {
> >
{/* Left: Tool Picker */} {/* Left: Tool Picker */}
{sidebarsVisible && ( {sidebarsVisible && (
<Box <div
className={`${styles.leftSidebar} h-screen z-sticky flex flex-col bg-bg-surface border-r border-border-subtle`} className="h-screen z-sticky flex flex-col bg-surface border-r border-border min-w-[180px] max-w-[240px] w-[16vw]"
style={{ padding: '1rem' }}
> >
<ToolPicker <ToolPicker
selectedToolKey={selectedToolKey} selectedToolKey={selectedToolKey}
onSelect={handleToolSelect} onSelect={handleToolSelect}
toolRegistry={toolRegistry} toolRegistry={toolRegistry}
/> />
</Box> </div>
)} )}
{/* Middle: Main View */} {/* Middle: Main View */}
<Box className="flex-1 h-screen min-w-80 relative flex flex-col transition-all duration-300 bg-bg-app"> <Box className="flex-1 h-screen min-w-80 relative flex flex-col transition-all duration-300 bg-background">
{/* Top Controls */} {/* Top Controls */}
<TopControls <TopControls
currentView={currentView} currentView={currentView}
setCurrentView={setCurrentView} setCurrentView={setCurrentView}
/> />
{/* Main content area */} {/* Main content area */}
<Paper <Box className="flex-1 min-h-0 margin-top-200 relative z-10">
radius="0 0 xl xl"
shadow="sm"
p={0}
className="flex-1 min-h-0 mt-0 box-border overflow-hidden flex flex-col"
>
<Box className="flex-1 min-h-0">
{(currentView === "viewer" || currentView === "pageEditor") && !pdfFile ? ( {(currentView === "viewer" || currentView === "pageEditor") && !pdfFile ? (
<FileManager <FileManager
files={files} files={files}
@ -138,13 +131,13 @@ export default function HomePage() {
/> />
)} )}
</Box> </Box>
</Paper>
</Box> </Box>
{/* Right: Tool Interaction */} {/* Right: Tool Interaction */}
{sidebarsVisible && ( {sidebarsVisible && (
<Box <div
className={`${styles.rightSidebar} h-screen bg-bg-surface border-l border-border-subtle p-app-lg gap-app-md z-sticky flex flex-col`} className="h-screen bg-surface border-l border-border gap-6 z-sticky flex flex-col min-w-[260px] max-w-[400px] w-[22vw]"
style={{ padding: '1.5rem' }}
> >
<ToolRenderer <ToolRenderer
selectedToolKey={selectedToolKey} selectedToolKey={selectedToolKey}
@ -156,19 +149,8 @@ export default function HomePage() {
toolParams={toolParams} toolParams={toolParams}
updateParams={updateParams} updateParams={updateParams}
/> />
</Box> </div>
)} )}
{/* Sidebar toggle button */}
<Button
variant="light"
color="blue"
size="xs"
className="fixed top-app-md right-app-md z-fixed"
onClick={() => setSidebarsVisible((v) => !v)}
>
{t("sidebar.toggle", sidebarsVisible ? "Hide Sidebars" : "Show Sidebars")}
</Button>
</Group> </Group>
); );
} }

View File

@ -1,13 +0,0 @@
/* Use Tailwind for most styles, keep only complex layouts in CSS modules */
.leftSidebar {
min-width: var(--sidebar-width-min);
max-width: var(--sidebar-width-max);
width: var(--sidebar-width);
}
.rightSidebar {
min-width: 260px;
max-width: 400px;
width: 22vw;
}

View File

@ -1,123 +0,0 @@
/* Custom component styles that don't fit Tailwind patterns */
/* Mantine component overrides */
.mantine-override {
/* Custom overrides for Mantine components when needed */
}
/* PDF-specific custom components */
.pdf-page-thumbnail {
aspect-ratio: 3/4;
background: var(--bg-surface);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-sm);
overflow: hidden;
position: relative;
}
.pdf-page-thumbnail:hover {
border-color: var(--color-primary-500);
box-shadow: var(--shadow-md);
}
.pdf-page-thumbnail img {
width: 100%;
height: 100%;
object-fit: cover;
}
/* Loading spinner for PDF operations */
.pdf-loading {
display: flex;
align-items: center;
justify-content: center;
min-height: 200px;
background: var(--bg-surface);
border-radius: var(--radius-md);
}
/* Custom scrollbar for PDF viewer */
.pdf-viewer-container {
scrollbar-width: thin;
scrollbar-color: var(--border-strong) var(--bg-muted);
}
.pdf-viewer-container::-webkit-scrollbar {
width: 8px;
height: 8px;
}
.pdf-viewer-container::-webkit-scrollbar-track {
background: var(--bg-muted);
border-radius: var(--radius-md);
}
.pdf-viewer-container::-webkit-scrollbar-thumb {
background: var(--border-strong);
border-radius: var(--radius-md);
}
.pdf-viewer-container::-webkit-scrollbar-thumb:hover {
background: var(--color-primary-500);
}
/* File upload drag and drop animations */
.file-drop-zone.drag-over {
animation: pulse 1s infinite;
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.02);
}
}
/* Progress bar for operations */
.progress-bar {
width: 100%;
height: 4px;
background: var(--bg-muted);
border-radius: var(--radius-full);
overflow: hidden;
}
.progress-bar-fill {
height: 100%;
background: var(--color-primary-500);
border-radius: var(--radius-full);
transition: width 0.3s ease;
}
/* Tool-specific layouts that need custom CSS */
.split-preview-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: var(--space-md);
padding: var(--space-md);
background: var(--bg-surface);
border-radius: var(--radius-lg);
border: 1px solid var(--border-subtle);
}
/* Error states */
.error-state {
padding: var(--space-lg);
text-align: center;
background: var(--bg-surface);
border: 1px solid var(--color-error);
border-radius: var(--radius-md);
color: var(--color-error);
}
/* Success states */
.success-state {
padding: var(--space-lg);
text-align: center;
background: var(--bg-surface);
border: 1px solid var(--color-success);
border-radius: var(--radius-md);
color: var(--color-success);
}

View File

@ -1,66 +1,7 @@
/* Import minimal theme variables */
@import './theme.css';
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
/* Custom component classes using Tailwind (with app- prefix to avoid Mantine conflicts) */
@layer components {
.app-card {
@apply bg-bg-surface border border-border-subtle rounded-app-lg p-app-lg shadow-app-md;
}
.app-surface {
@apply bg-bg-surface border border-border-subtle rounded-app-md shadow-app-sm;
}
.app-raised {
@apply bg-bg-raised shadow-app-sm rounded-app-md;
}
.app-interactive {
@apply cursor-pointer transition-all duration-200 hover:bg-hover-bg active:bg-active-bg;
}
.app-file-drop-zone {
@apply border-2 border-dashed border-file-drop rounded-app-lg bg-bg-surface transition-all duration-200;
}
.app-file-drop-zone:hover,
.app-file-drop-zone.drag-over {
@apply border-app-primary-500 bg-file-drop-hover;
}
/* PDF-specific component classes */
.app-pdf-viewer-bg {
@apply bg-pdf-viewer;
}
.app-pdf-toolbar {
@apply bg-pdf-toolbar border-b border-border-subtle;
}
/* Button variants */
.app-btn-pdf-tool {
@apply bg-bg-surface border border-border-default text-text-primary px-app-md py-app-sm rounded-app-md font-medium transition-all duration-200 hover:bg-hover-bg hover:border-app-primary-500;
}
/* Focus styles */
.app-focus-ring {
@apply focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-offset-2;
}
}
/* Custom utilities */
@layer utilities {
.text-balance {
text-wrap: balance;
}
.scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
}
.scrollbar-hide::-webkit-scrollbar {
display: none;
}
}

View File

@ -1,7 +1,24 @@
/* Design System CSS Variables - Single Source of Truth */ /* CSS variables for Tailwind + Mantine integration */
:root { :root {
/* Color System */ /* Standard gray scale */
--gray-50: 249 250 251;
--gray-100: 243 244 246;
--gray-200: 229 231 235;
--gray-300: 209 213 219;
--gray-400: 156 163 175;
--gray-500: 107 114 128;
--gray-600: 75 85 99;
--gray-700: 55 65 81;
--gray-800: 31 41 55;
--gray-900: 17 24 39;
/* Semantic colors for Tailwind */
--surface: 255 255 255;
--background: 249 250 251;
--border: 229 231 235;
/* Colors for Mantine integration */
--color-primary-50: #eff6ff; --color-primary-50: #eff6ff;
--color-primary-100: #dbeafe; --color-primary-100: #dbeafe;
--color-primary-200: #bfdbfe; --color-primary-200: #bfdbfe;
@ -24,123 +41,91 @@
--color-gray-800: #1f2937; --color-gray-800: #1f2937;
--color-gray-900: #111827; --color-gray-900: #111827;
/* Semantic Colors */ /* Spacing system */
--color-success: #10b981;
--color-warning: #f59e0b;
--color-error: #ef4444;
--color-info: #3b82f6;
/* Spacing System */
--space-xs: 4px; --space-xs: 4px;
--space-sm: 8px; --space-sm: 8px;
--space-md: 16px; --space-md: 16px;
--space-lg: 24px; --space-lg: 24px;
--space-xl: 32px; --space-xl: 32px;
--space-2xl: 48px;
/* Radius System */ /* Radius system */
--radius-xs: 2px; --radius-xs: 2px;
--radius-sm: 4px; --radius-sm: 4px;
--radius-md: 8px; --radius-md: 8px;
--radius-lg: 12px; --radius-lg: 12px;
--radius-xl: 16px; --radius-xl: 16px;
--radius-2xl: 24px;
--radius-full: 9999px;
/* Shadow System */ /* Shadow system */
--shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.05); --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.05);
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1); --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1); --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1); --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1);
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.1); --shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.1);
/* Layout */ /* Font weights */
--sidebar-width-min: 180px;
--sidebar-width-max: 240px;
--sidebar-width: 16vw;
--header-height: 60px;
--border-width: 1px;
/* Typography */
--font-weight-normal: 400; --font-weight-normal: 400;
--font-weight-medium: 500; --font-weight-medium: 500;
--font-weight-semibold: 600; --font-weight-semibold: 600;
--font-weight-bold: 700; --font-weight-bold: 700;
/* Z-Index Scale */ /* Light theme semantic colors */
--z-dropdown: 1000;
--z-sticky: 1020;
--z-fixed: 1030;
--z-modal-backdrop: 1040;
--z-modal: 1050;
--z-popover: 1060;
--z-tooltip: 1070;
}
/* Light Theme */
:root, [data-mantine-color-scheme="light"] {
/* Background Colors */
--bg-app: var(--color-gray-50);
--bg-surface: #ffffff; --bg-surface: #ffffff;
--bg-raised: var(--color-gray-50); --bg-raised: #f9fafb;
--bg-overlay: rgba(255, 255, 255, 0.8); --bg-muted: #f3f4f6;
--bg-muted: var(--color-gray-100); --text-primary: #111827;
--text-secondary: #4b5563;
/* Text Colors */ --text-muted: #6b7280;
--text-primary: var(--color-gray-900); --border-subtle: #e5e7eb;
--text-secondary: var(--color-gray-600); --border-default: #d1d5db;
--text-muted: var(--color-gray-500); --border-strong: #9ca3af;
--text-inverse: #ffffff; --hover-bg: #f9fafb;
--active-bg: #f3f4f6;
/* Border Colors */
--border-subtle: var(--color-gray-200);
--border-default: var(--color-gray-300);
--border-strong: var(--color-gray-400);
/* Interactive States */
--hover-bg: var(--color-gray-50);
--active-bg: var(--color-gray-100);
--focus-ring: var(--color-primary-500);
/* PDF Tool Specific */
--pdf-viewer-bg: #f8fafc;
--pdf-toolbar-bg: var(--bg-surface);
--file-drop-border: var(--color-gray-300);
--file-drop-hover: var(--color-primary-100);
} }
/* Dark Theme */
[data-mantine-color-scheme="dark"] { [data-mantine-color-scheme="dark"] {
/* Background Colors */ /* Dark theme gray scale (inverted) */
--bg-app: var(--color-gray-900); --gray-50: 17 24 39;
--bg-surface: var(--color-gray-800); --gray-100: 31 41 55;
--bg-raised: var(--color-gray-700); --gray-200: 55 65 81;
--bg-overlay: rgba(17, 24, 39, 0.8); --gray-300: 75 85 99;
--bg-muted: var(--color-gray-700); --gray-400: 107 114 128;
--gray-500: 156 163 175;
--gray-600: 209 213 219;
--gray-700: 229 231 235;
--gray-800: 243 244 246;
--gray-900: 249 250 251;
/* Text Colors */ /* Dark semantic colors for Tailwind */
--text-primary: var(--color-gray-50); --surface: 31 41 55;
--text-secondary: var(--color-gray-300); --background: 17 24 39;
--text-muted: var(--color-gray-400); --border: 75 85 99;
--text-inverse: var(--color-gray-900);
/* Border Colors */ /* Dark theme Mantine colors */
--border-subtle: var(--color-gray-700); --color-gray-50: #111827;
--border-default: var(--color-gray-600); --color-gray-100: #1f2937;
--border-strong: var(--color-gray-500); --color-gray-200: #374151;
--color-gray-300: #4b5563;
--color-gray-400: #6b7280;
--color-gray-500: #9ca3af;
--color-gray-600: #d1d5db;
--color-gray-700: #e5e7eb;
--color-gray-800: #f3f4f6;
--color-gray-900: #f9fafb;
/* Interactive States */ /* Dark theme semantic colors */
--hover-bg: var(--color-gray-700); --bg-surface: #1f2937;
--active-bg: var(--color-gray-600); --bg-raised: #374151;
--focus-ring: var(--color-primary-400); --bg-muted: #374151;
--text-primary: #f9fafb;
--text-secondary: #d1d5db;
--text-muted: #9ca3af;
--border-subtle: #374151;
--border-default: #4b5563;
--border-strong: #6b7280;
--hover-bg: #374151;
--active-bg: #4b5563;
/* PDF Tool Specific */ /* Adjust shadows for dark mode */
--pdf-viewer-bg: var(--color-gray-800);
--pdf-toolbar-bg: var(--bg-surface);
--file-drop-border: var(--color-gray-600);
--file-drop-hover: var(--color-primary-900);
/* Shadow adjustments for dark mode */
--shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.3); --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.3);
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4); --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.4); --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.4);
@ -151,10 +136,4 @@
/* Smooth transitions for theme switching */ /* Smooth transitions for theme switching */
* { * {
transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease; transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}
/* Focus styles */
*:focus-visible {
outline: 2px solid var(--focus-ring);
outline-offset: 2px;
} }

View File

@ -4,141 +4,45 @@ module.exports = {
"./index.html", "./index.html",
"./src/**/*.{js,ts,jsx,tsx}", "./src/**/*.{js,ts,jsx,tsx}",
], ],
darkMode: ['class', '[data-mantine-color-scheme="dark"]'],
theme: { theme: {
extend: { extend: {
// Colors using CSS variables (namespaced to avoid Mantine conflicts) // Use standard Tailwind color system with CSS variables for theme switching
colors: { colors: {
// Custom palette (avoid 'primary' and 'gray' which Mantine uses) // Override gray to work with both themes
'app-primary': { gray: {
50: 'var(--color-primary-50)', 50: 'rgb(var(--gray-50) / <alpha-value>)',
100: 'var(--color-primary-100)', 100: 'rgb(var(--gray-100) / <alpha-value>)',
200: 'var(--color-primary-200)', 200: 'rgb(var(--gray-200) / <alpha-value>)',
300: 'var(--color-primary-300)', 300: 'rgb(var(--gray-300) / <alpha-value>)',
400: 'var(--color-primary-400)', 400: 'rgb(var(--gray-400) / <alpha-value>)',
500: 'var(--color-primary-500)', 500: 'rgb(var(--gray-500) / <alpha-value>)',
600: 'var(--color-primary-600)', 600: 'rgb(var(--gray-600) / <alpha-value>)',
700: 'var(--color-primary-700)', 700: 'rgb(var(--gray-700) / <alpha-value>)',
800: 'var(--color-primary-800)', 800: 'rgb(var(--gray-800) / <alpha-value>)',
900: 'var(--color-primary-900)', 900: 'rgb(var(--gray-900) / <alpha-value>)',
}, },
// Custom gray palette // Custom semantic colors for app-specific usage
'app-gray': { surface: 'rgb(var(--surface) / <alpha-value>)',
50: 'var(--color-gray-50)', background: 'rgb(var(--background) / <alpha-value>)',
100: 'var(--color-gray-100)', border: 'rgb(var(--border) / <alpha-value>)',
200: 'var(--color-gray-200)',
300: 'var(--color-gray-300)',
400: 'var(--color-gray-400)',
500: 'var(--color-gray-500)',
600: 'var(--color-gray-600)',
700: 'var(--color-gray-700)',
800: 'var(--color-gray-800)',
900: 'var(--color-gray-900)',
},
// Semantic colors
success: 'var(--color-success)',
warning: 'var(--color-warning)',
error: 'var(--color-error)',
info: 'var(--color-info)',
// Background colors
'bg-app': 'var(--bg-app)',
'bg-surface': 'var(--bg-surface)',
'bg-raised': 'var(--bg-raised)',
'bg-muted': 'var(--bg-muted)',
'bg-overlay': 'var(--bg-overlay)',
// Text colors
'text-primary': 'var(--text-primary)',
'text-secondary': 'var(--text-secondary)',
'text-muted': 'var(--text-muted)',
'text-inverse': 'var(--text-inverse)',
// Border colors
'border-subtle': 'var(--border-subtle)',
'border-default': 'var(--border-default)',
'border-strong': 'var(--border-strong)',
// Interactive states
'hover-bg': 'var(--hover-bg)',
'active-bg': 'var(--active-bg)',
'focus-ring': 'var(--focus-ring)',
// PDF-specific colors
'pdf-viewer': 'var(--pdf-viewer-bg)',
'pdf-toolbar': 'var(--pdf-toolbar-bg)',
'file-drop': 'var(--file-drop-border)',
'file-drop-hover': 'var(--file-drop-hover)',
},
// Spacing using CSS variables (namespaced to avoid Mantine conflicts)
spacing: {
'app-xs': 'var(--space-xs)',
'app-sm': 'var(--space-sm)',
'app-md': 'var(--space-md)',
'app-lg': 'var(--space-lg)',
'app-xl': 'var(--space-xl)',
'app-2xl': 'var(--space-2xl)',
},
// Border radius using CSS variables (namespaced)
borderRadius: {
'app-xs': 'var(--radius-xs)',
'app-sm': 'var(--radius-sm)',
'app-md': 'var(--radius-md)',
'app-lg': 'var(--radius-lg)',
'app-xl': 'var(--radius-xl)',
'app-2xl': 'var(--radius-2xl)',
'app-full': 'var(--radius-full)',
},
// Box shadows using CSS variables (namespaced)
boxShadow: {
'app-xs': 'var(--shadow-xs)',
'app-sm': 'var(--shadow-sm)',
'app-md': 'var(--shadow-md)',
'app-lg': 'var(--shadow-lg)',
'app-xl': 'var(--shadow-xl)',
},
// Font weights using CSS variables
fontWeight: {
'normal': 'var(--font-weight-normal)',
'medium': 'var(--font-weight-medium)',
'semibold': 'var(--font-weight-semibold)',
'bold': 'var(--font-weight-bold)',
}, },
// Z-index scale // Z-index scale
zIndex: { zIndex: {
'dropdown': 'var(--z-dropdown)', 'dropdown': '1000',
'sticky': 'var(--z-sticky)', 'sticky': '1020',
'fixed': 'var(--z-fixed)', 'fixed': '1030',
'modal-backdrop': 'var(--z-modal-backdrop)', 'modal-backdrop': '1040',
'modal': 'var(--z-modal)', 'modal': '1050',
'popover': 'var(--z-popover)', 'popover': '1060',
'tooltip': 'var(--z-tooltip)', 'tooltip': '1070',
},
// Layout variables
width: {
'sidebar': 'var(--sidebar-width)',
'sidebar-min': 'var(--sidebar-width-min)',
'sidebar-max': 'var(--sidebar-width-max)',
},
height: {
'header': 'var(--header-height)',
},
// Border width
borderWidth: {
DEFAULT: 'var(--border-width)',
}, },
}, },
}, },
plugins: [], plugins: [],
// Prevent conflicts with Mantine classes // Enable preflight for standard Tailwind functionality
corePlugins: { corePlugins: {
preflight: false, preflight: true,
}, },
} }