Parcourir la source

feat: Add dark/light theme with system preference detection

- Add ThemeContext with light/dark/system modes and localStorage persistence
- Add ThemeToggle component with sun/moon/system icons
- Update all components and pages to use CSS variables for theming
- Use amber accent color in dark mode (blue in light mode)
- Update footer to link PicoBaaS to picobaas.eu
- Enable Tailwind dark mode with 'class' strategy
Fszontagh il y a 1 jour
Parent
commit
ba4021aa2a

+ 129 - 31
src/components/AnalyticsDashboard.tsx

@@ -71,7 +71,7 @@ export default function AnalyticsDashboard({ shortCode, onClose }: AnalyticsDash
   if (error) {
   if (error) {
     return (
     return (
       <div className="p-6 text-center">
       <div className="p-6 text-center">
-        <div className="text-red-600 mb-4">{error}</div>
+        <div className="text-red-600 dark:text-red-400 mb-4">{error}</div>
         <button onClick={fetchAnalytics} className="btn btn-secondary">
         <button onClick={fetchAnalytics} className="btn btn-secondary">
           Try Again
           Try Again
         </button>
         </button>
@@ -88,10 +88,16 @@ export default function AnalyticsDashboard({ shortCode, onClose }: AnalyticsDash
       {/* Header */}
       {/* Header */}
       {onClose && (
       {onClose && (
         <div className="flex justify-between items-center">
         <div className="flex justify-between items-center">
-          <h2 className="text-xl font-semibold text-gray-900">Image Analytics</h2>
+          <h2
+            className="text-xl font-semibold"
+            style={{ color: 'var(--text-primary)' }}
+          >
+            Image Analytics
+          </h2>
           <button
           <button
             onClick={onClose}
             onClick={onClose}
-            className="text-gray-400 hover:text-gray-600"
+            className="transition-colors"
+            style={{ color: 'var(--text-muted)' }}
           >
           >
             <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
             <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
               <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
               <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
@@ -102,34 +108,88 @@ export default function AnalyticsDashboard({ shortCode, onClose }: AnalyticsDash
 
 
       {/* Stats Grid */}
       {/* Stats Grid */}
       <div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
       <div className="grid grid-cols-1 sm:grid-cols-3 gap-4">
-        <div className="bg-gray-50 rounded-lg p-4">
-          <div className="text-sm text-gray-500 mb-1">Total Views</div>
-          <div className="text-2xl font-bold text-gray-900">{analytics.totalViews}</div>
+        <div
+          className="rounded-lg p-4"
+          style={{ backgroundColor: 'var(--bg-tertiary)' }}
+        >
+          <div className="text-sm mb-1" style={{ color: 'var(--text-muted)' }}>
+            Total Views
+          </div>
+          <div
+            className="text-2xl font-bold"
+            style={{ color: 'var(--text-primary)' }}
+          >
+            {analytics.totalViews}
+          </div>
         </div>
         </div>
-        <div className="bg-gray-50 rounded-lg p-4">
-          <div className="text-sm text-gray-500 mb-1">Unique Visitors</div>
-          <div className="text-2xl font-bold text-gray-900">{analytics.uniqueVisitors}</div>
+        <div
+          className="rounded-lg p-4"
+          style={{ backgroundColor: 'var(--bg-tertiary)' }}
+        >
+          <div className="text-sm mb-1" style={{ color: 'var(--text-muted)' }}>
+            Unique Visitors
+          </div>
+          <div
+            className="text-2xl font-bold"
+            style={{ color: 'var(--text-primary)' }}
+          >
+            {analytics.uniqueVisitors}
+          </div>
         </div>
         </div>
-        <div className="bg-gray-50 rounded-lg p-4">
-          <div className="text-sm text-gray-500 mb-1">Proxy/VPN Views</div>
-          <div className="text-2xl font-bold text-gray-900">{analytics.proxyViewsCount}</div>
+        <div
+          className="rounded-lg p-4"
+          style={{ backgroundColor: 'var(--bg-tertiary)' }}
+        >
+          <div className="text-sm mb-1" style={{ color: 'var(--text-muted)' }}>
+            Proxy/VPN Views
+          </div>
+          <div
+            className="text-2xl font-bold"
+            style={{ color: 'var(--text-primary)' }}
+          >
+            {analytics.proxyViewsCount}
+          </div>
         </div>
         </div>
       </div>
       </div>
 
 
       {/* Referrers */}
       {/* Referrers */}
       {Object.keys(analytics.viewsByReferrer).length > 0 && (
       {Object.keys(analytics.viewsByReferrer).length > 0 && (
         <div>
         <div>
-          <h3 className="text-sm font-medium text-gray-700 mb-3">Top Referrers</h3>
-          <div className="bg-gray-50 rounded-lg divide-y divide-gray-200">
+          <h3
+            className="text-sm font-medium mb-3"
+            style={{ color: 'var(--text-secondary)' }}
+          >
+            Top Referrers
+          </h3>
+          <div
+            className="rounded-lg divide-y"
+            style={{
+              backgroundColor: 'var(--bg-tertiary)',
+              borderColor: 'var(--border)',
+            }}
+          >
             {Object.entries(analytics.viewsByReferrer)
             {Object.entries(analytics.viewsByReferrer)
               .sort(([, a], [, b]) => b - a)
               .sort(([, a], [, b]) => b - a)
               .slice(0, 5)
               .slice(0, 5)
               .map(([referrer, count]) => (
               .map(([referrer, count]) => (
-                <div key={referrer} className="px-4 py-3 flex justify-between items-center">
-                  <span className="text-sm text-gray-600 truncate max-w-xs" title={referrer}>
+                <div
+                  key={referrer}
+                  className="px-4 py-3 flex justify-between items-center"
+                  style={{ borderColor: 'var(--border)' }}
+                >
+                  <span
+                    className="text-sm truncate max-w-xs"
+                    style={{ color: 'var(--text-secondary)' }}
+                    title={referrer}
+                  >
                     {referrer}
                     {referrer}
                   </span>
                   </span>
-                  <span className="text-sm font-medium text-gray-900">{count}</span>
+                  <span
+                    className="text-sm font-medium"
+                    style={{ color: 'var(--text-primary)' }}
+                  >
+                    {count}
+                  </span>
                 </div>
                 </div>
               ))}
               ))}
           </div>
           </div>
@@ -138,47 +198,85 @@ export default function AnalyticsDashboard({ shortCode, onClose }: AnalyticsDash
 
 
       {/* Recent Views Table */}
       {/* Recent Views Table */}
       <div>
       <div>
-        <h3 className="text-sm font-medium text-gray-700 mb-3">Recent Views</h3>
+        <h3
+          className="text-sm font-medium mb-3"
+          style={{ color: 'var(--text-secondary)' }}
+        >
+          Recent Views
+        </h3>
         {analytics.recentViews.length === 0 ? (
         {analytics.recentViews.length === 0 ? (
-          <p className="text-gray-500 text-sm">No views recorded yet</p>
+          <p className="text-sm" style={{ color: 'var(--text-muted)' }}>
+            No views recorded yet
+          </p>
         ) : (
         ) : (
           <div className="overflow-x-auto">
           <div className="overflow-x-auto">
-            <table className="min-w-full divide-y divide-gray-200">
-              <thead className="bg-gray-50">
+            <table
+              className="min-w-full divide-y"
+              style={{ borderColor: 'var(--border)' }}
+            >
+              <thead style={{ backgroundColor: 'var(--bg-tertiary)' }}>
                 <tr>
                 <tr>
-                  <th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
+                  <th
+                    className="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider"
+                    style={{ color: 'var(--text-muted)' }}
+                  >
                     Time
                     Time
                   </th>
                   </th>
-                  <th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
+                  <th
+                    className="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider"
+                    style={{ color: 'var(--text-muted)' }}
+                  >
                     IP Address
                     IP Address
                   </th>
                   </th>
-                  <th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
+                  <th
+                    className="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider"
+                    style={{ color: 'var(--text-muted)' }}
+                  >
                     Referrer
                     Referrer
                   </th>
                   </th>
-                  <th className="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
+                  <th
+                    className="px-4 py-3 text-left text-xs font-medium uppercase tracking-wider"
+                    style={{ color: 'var(--text-muted)' }}
+                  >
                     Proxy
                     Proxy
                   </th>
                   </th>
                 </tr>
                 </tr>
               </thead>
               </thead>
-              <tbody className="bg-white divide-y divide-gray-200">
+              <tbody
+                className="divide-y"
+                style={{
+                  backgroundColor: 'var(--bg-secondary)',
+                  borderColor: 'var(--border)',
+                }}
+              >
                 {analytics.recentViews.slice(0, 20).map((view: ViewLog) => (
                 {analytics.recentViews.slice(0, 20).map((view: ViewLog) => (
                   <tr key={view.id}>
                   <tr key={view.id}>
-                    <td className="px-4 py-3 whitespace-nowrap text-sm text-gray-600">
+                    <td
+                      className="px-4 py-3 whitespace-nowrap text-sm"
+                      style={{ color: 'var(--text-secondary)' }}
+                    >
                       {formatDate(view.viewedAt)}
                       {formatDate(view.viewedAt)}
                     </td>
                     </td>
-                    <td className="px-4 py-3 whitespace-nowrap text-sm text-gray-600">
+                    <td
+                      className="px-4 py-3 whitespace-nowrap text-sm"
+                      style={{ color: 'var(--text-secondary)' }}
+                    >
                       {view.viewerIp}
                       {view.viewerIp}
                     </td>
                     </td>
-                    <td className="px-4 py-3 text-sm text-gray-600 max-w-xs truncate" title={view.referrer || undefined}>
+                    <td
+                      className="px-4 py-3 text-sm max-w-xs truncate"
+                      style={{ color: 'var(--text-secondary)' }}
+                      title={view.referrer || undefined}
+                    >
                       {view.referrer || '-'}
                       {view.referrer || '-'}
                     </td>
                     </td>
                     <td className="px-4 py-3 whitespace-nowrap text-sm">
                     <td className="px-4 py-3 whitespace-nowrap text-sm">
                       {view.isProxyDetected ? (
                       {view.isProxyDetected ? (
-                        <span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 text-yellow-800">
+                        <span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400">
                           Detected
                           Detected
                         </span>
                         </span>
                       ) : (
                       ) : (
-                        <span className="text-gray-400">-</span>
+                        <span style={{ color: 'var(--text-muted)' }}>-</span>
                       )}
                       )}
                     </td>
                     </td>
                   </tr>
                   </tr>

+ 18 - 3
src/components/ExpirySelector.tsx

@@ -13,7 +13,10 @@ export default function ExpirySelector({
 }: ExpirySelectorProps) {
 }: ExpirySelectorProps) {
   return (
   return (
     <div className={className}>
     <div className={className}>
-      <label className="block text-sm font-medium text-gray-700 mb-2">
+      <label
+        className="block text-sm font-medium mb-2"
+        style={{ color: 'var(--text-secondary)' }}
+      >
         Expires after
         Expires after
       </label>
       </label>
       <div className="flex flex-wrap gap-2">
       <div className="flex flex-wrap gap-2">
@@ -24,9 +27,21 @@ export default function ExpirySelector({
             onClick={() => onChange(option.value)}
             onClick={() => onChange(option.value)}
             className={`px-3 py-1.5 text-sm rounded-full border transition-colors ${
             className={`px-3 py-1.5 text-sm rounded-full border transition-colors ${
               value === option.value
               value === option.value
-                ? 'bg-primary-600 text-white border-primary-600'
-                : 'bg-white text-gray-700 border-gray-300 hover:border-primary-400'
+                ? 'text-white'
+                : 'hover:border-[var(--accent-500)]'
             }`}
             }`}
+            style={
+              value === option.value
+                ? {
+                    backgroundColor: 'var(--accent-600)',
+                    borderColor: 'var(--accent-600)',
+                  }
+                : {
+                    backgroundColor: 'var(--bg-secondary)',
+                    color: 'var(--text-secondary)',
+                    borderColor: 'var(--border-strong)',
+                  }
+            }
           >
           >
             {option.label}
             {option.label}
           </button>
           </button>

+ 13 - 6
src/components/ImageCard.tsx

@@ -39,7 +39,10 @@ export default function ImageCard({
       <div className="card overflow-hidden group">
       <div className="card overflow-hidden group">
         {/* Image */}
         {/* Image */}
         <Link to={`/image/${encodeURIComponent(image.path)}`}>
         <Link to={`/image/${encodeURIComponent(image.path)}`}>
-          <div className="aspect-square bg-gray-100 relative overflow-hidden">
+          <div
+            className="aspect-square relative overflow-hidden"
+            style={{ backgroundColor: 'var(--bg-tertiary)' }}
+          >
             <img
             <img
               src={image.url}
               src={image.url}
               alt={image.name}
               alt={image.name}
@@ -52,10 +55,10 @@ export default function ImageCard({
               <div
               <div
                 className={`absolute top-2 right-2 px-2 py-1 rounded-full text-xs font-medium ${
                 className={`absolute top-2 right-2 px-2 py-1 rounded-full text-xs font-medium ${
                   expiryStatus.isExpired
                   expiryStatus.isExpired
-                    ? 'bg-red-100 text-red-700'
+                    ? 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400'
                     : expiryStatus.isExpiringSoon
                     : expiryStatus.isExpiringSoon
-                    ? 'bg-yellow-100 text-yellow-700'
-                    : 'bg-gray-100 text-gray-600'
+                    ? 'bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400'
+                    : 'bg-gray-100 text-gray-600 dark:bg-gray-700 dark:text-gray-300'
                 }`}
                 }`}
               >
               >
                 {expiryStatus.label}
                 {expiryStatus.label}
@@ -66,10 +69,14 @@ export default function ImageCard({
 
 
         {/* Info */}
         {/* Info */}
         <div className="p-3">
         <div className="p-3">
-          <p className="font-medium text-gray-900 truncate" title={image.name}>
+          <p
+            className="font-medium truncate"
+            style={{ color: 'var(--text-primary)' }}
+            title={image.name}
+          >
             {image.name}
             {image.name}
           </p>
           </p>
-          <p className="text-sm text-gray-500">
+          <p className="text-sm" style={{ color: 'var(--text-muted)' }}>
             {formatRelativeTime(image.createdAt)}
             {formatRelativeTime(image.createdAt)}
           </p>
           </p>
 
 

+ 7 - 3
src/components/ImageGrid.tsx

@@ -28,9 +28,13 @@ export default function ImageGrid({
   if (images.length === 0) {
   if (images.length === 0) {
     return (
     return (
       <div className="text-center py-12">
       <div className="text-center py-12">
-        <div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-gray-100 mb-4">
+        <div
+          className="inline-flex items-center justify-center w-16 h-16 rounded-full mb-4"
+          style={{ backgroundColor: 'var(--bg-tertiary)' }}
+        >
           <svg
           <svg
-            className="h-8 w-8 text-gray-400"
+            className="h-8 w-8"
+            style={{ color: 'var(--text-muted)' }}
             fill="none"
             fill="none"
             viewBox="0 0 24 24"
             viewBox="0 0 24 24"
             stroke="currentColor"
             stroke="currentColor"
@@ -43,7 +47,7 @@ export default function ImageGrid({
             />
             />
           </svg>
           </svg>
         </div>
         </div>
-        <p className="text-gray-500">{emptyMessage}</p>
+        <p style={{ color: 'var(--text-muted)' }}>{emptyMessage}</p>
       </div>
       </div>
     );
     );
   }
   }

+ 37 - 18
src/components/ImageUploader.tsx

@@ -117,6 +117,20 @@ export default function ImageUploader({
     }
     }
   };
   };
 
 
+  const getDropZoneClasses = () => {
+    const base = `relative border-2 border-dashed rounded-xl transition-colors cursor-pointer ${
+      compact ? 'p-6' : 'p-12'
+    }`;
+
+    if (dragActive) {
+      return `${base} border-[var(--accent-500)] bg-[var(--accent-50)]`;
+    }
+    if (preview) {
+      return `${base} border-[var(--border-strong)] bg-[var(--bg-tertiary)]`;
+    }
+    return `${base} border-[var(--border-strong)] hover:border-[var(--accent-500)] hover:bg-[var(--bg-tertiary)]`;
+  };
+
   return (
   return (
     <div className="w-full">
     <div className="w-full">
       {/* Drop zone */}
       {/* Drop zone */}
@@ -126,15 +140,7 @@ export default function ImageUploader({
         onDragOver={handleDrag}
         onDragOver={handleDrag}
         onDrop={handleDrop}
         onDrop={handleDrop}
         onClick={() => inputRef.current?.click()}
         onClick={() => inputRef.current?.click()}
-        className={`relative border-2 border-dashed rounded-xl transition-colors cursor-pointer ${
-          compact ? 'p-6' : 'p-12'
-        } ${
-          dragActive
-            ? 'border-primary-500 bg-primary-50'
-            : preview
-            ? 'border-gray-300 bg-gray-50'
-            : 'border-gray-300 hover:border-primary-400 hover:bg-gray-50'
-        }`}
+        className={getDropZoneClasses()}
       >
       >
         <input
         <input
           ref={inputRef}
           ref={inputRef}
@@ -152,16 +158,22 @@ export default function ImageUploader({
               alt="Preview"
               alt="Preview"
               className="max-h-64 rounded-lg shadow-md"
               className="max-h-64 rounded-lg shadow-md"
             />
             />
-            <p className="text-sm text-gray-600">{selectedFile?.name}</p>
-            <p className="text-xs text-gray-500">
+            <p className="text-sm" style={{ color: 'var(--text-secondary)' }}>
+              {selectedFile?.name}
+            </p>
+            <p className="text-xs" style={{ color: 'var(--text-muted)' }}>
               {selectedFile && formatFileSize(selectedFile.size)}
               {selectedFile && formatFileSize(selectedFile.size)}
             </p>
             </p>
           </div>
           </div>
         ) : (
         ) : (
           <div className="flex flex-col items-center gap-4 text-center">
           <div className="flex flex-col items-center gap-4 text-center">
-            <div className="p-4 bg-primary-100 rounded-full">
+            <div
+              className="p-4 rounded-full"
+              style={{ backgroundColor: 'var(--accent-100)' }}
+            >
               <svg
               <svg
-                className="h-8 w-8 text-primary-600"
+                className="h-8 w-8"
+                style={{ color: 'var(--accent-600)' }}
                 fill="none"
                 fill="none"
                 viewBox="0 0 24 24"
                 viewBox="0 0 24 24"
                 stroke="currentColor"
                 stroke="currentColor"
@@ -175,10 +187,13 @@ export default function ImageUploader({
               </svg>
               </svg>
             </div>
             </div>
             <div>
             <div>
-              <p className="text-lg font-medium text-gray-900">
+              <p
+                className="text-lg font-medium"
+                style={{ color: 'var(--text-primary)' }}
+              >
                 Drop your image here
                 Drop your image here
               </p>
               </p>
-              <p className="text-sm text-gray-500">
+              <p className="text-sm" style={{ color: 'var(--text-muted)' }}>
                 or click to browse (max 10MB)
                 or click to browse (max 10MB)
               </p>
               </p>
             </div>
             </div>
@@ -188,7 +203,7 @@ export default function ImageUploader({
 
 
       {/* Error message */}
       {/* Error message */}
       {error && (
       {error && (
-        <p className="mt-2 text-sm text-red-600 flex items-center gap-1">
+        <p className="mt-2 text-sm text-red-600 dark:text-red-400 flex items-center gap-1">
           <svg className="h-4 w-4" fill="currentColor" viewBox="0 0 20 20">
           <svg className="h-4 w-4" fill="currentColor" viewBox="0 0 20 20">
             <path
             <path
               fillRule="evenodd"
               fillRule="evenodd"
@@ -214,10 +229,14 @@ export default function ImageUploader({
                 type="checkbox"
                 type="checkbox"
                 checked={downloadAllowed}
                 checked={downloadAllowed}
                 onChange={(e) => setDownloadAllowed(e.target.checked)}
                 onChange={(e) => setDownloadAllowed(e.target.checked)}
-                className="w-5 h-5 rounded border-gray-300 text-primary-600 focus:ring-primary-500 cursor-pointer"
+                className="w-5 h-5 rounded cursor-pointer"
+                style={{
+                  borderColor: 'var(--border-strong)',
+                  accentColor: 'var(--accent-600)',
+                }}
                 disabled={isUploading}
                 disabled={isUploading}
               />
               />
-              <span className="text-sm text-gray-700">
+              <span className="text-sm" style={{ color: 'var(--text-secondary)' }}>
                 Allow viewers to download this image
                 Allow viewers to download this image
               </span>
               </span>
             </label>
             </label>

+ 13 - 14
src/components/Layout.tsx

@@ -8,21 +8,20 @@ export default function Layout() {
       <main className="flex-1">
       <main className="flex-1">
         <Outlet />
         <Outlet />
       </main>
       </main>
-      <footer className="bg-white border-t border-gray-200 py-6">
+      <footer className="border-t py-6 transition-colors duration-200" style={{ backgroundColor: 'var(--bg-secondary)', borderColor: 'var(--border)' }}>
         <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
         <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
-          <div className="flex flex-col md:flex-row justify-between items-center gap-4">
-            <p className="text-gray-500 text-sm">
-              ImageDrop - Powered by PicoBaaS
-            </p>
-            <div className="flex gap-6 text-sm text-gray-500">
-              <a href="https://github.com" className="hover:text-gray-900">
-                GitHub
-              </a>
-              <a href="/docs" className="hover:text-gray-900">
-                API Docs
-              </a>
-            </div>
-          </div>
+          <p className="text-sm text-center" style={{ color: 'var(--text-muted)' }}>
+            ImageDrop - Powered by{' '}
+            <a
+              href="https://picobaas.eu"
+              target="_blank"
+              rel="noopener noreferrer"
+              className="hover:opacity-80 transition-opacity"
+              style={{ color: 'var(--accent-600)' }}
+            >
+              PicoBaaS
+            </a>
+          </p>
         </div>
         </div>
       </footer>
       </footer>
     </div>
     </div>

+ 2 - 1
src/components/LoadingSpinner.tsx

@@ -15,7 +15,8 @@ export default function LoadingSpinner({
 }: LoadingSpinnerProps) {
 }: LoadingSpinnerProps) {
   return (
   return (
     <div
     <div
-      className={`animate-spin rounded-full border-primary-600 border-t-transparent ${sizeClasses[size]} ${className}`}
+      className={`animate-spin rounded-full border-t-transparent ${sizeClasses[size]} ${className}`}
+      style={{ borderColor: 'var(--accent-600)', borderTopColor: 'transparent' }}
     />
     />
   );
   );
 }
 }

+ 30 - 13
src/components/Navbar.tsx

@@ -1,6 +1,7 @@
 import { Link, useNavigate } from 'react-router-dom';
 import { Link, useNavigate } from 'react-router-dom';
 import { useAuth } from '@picobaas/client/react';
 import { useAuth } from '@picobaas/client/react';
 import { useState } from 'react';
 import { useState } from 'react';
+import ThemeToggle from './ThemeToggle';
 
 
 export default function Navbar() {
 export default function Navbar() {
   const { isAuthenticated, logout } = useAuth();
   const { isAuthenticated, logout } = useAuth();
@@ -13,15 +14,16 @@ export default function Navbar() {
   };
   };
 
 
   return (
   return (
-    <nav className="bg-white shadow-sm border-b border-gray-200">
+    <nav className="shadow-sm border-b transition-colors duration-200" style={{ backgroundColor: 'var(--bg-secondary)', borderColor: 'var(--border)' }}>
       <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
       <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
         <div className="flex justify-between h-16">
         <div className="flex justify-between h-16">
           {/* Logo */}
           {/* Logo */}
           <div className="flex items-center">
           <div className="flex items-center">
             <Link to="/" className="flex items-center gap-2">
             <Link to="/" className="flex items-center gap-2">
               <svg
               <svg
-                className="h-8 w-8 text-primary-600"
+                className="h-8 w-8"
                 viewBox="0 0 100 100"
                 viewBox="0 0 100 100"
+                style={{ color: 'var(--accent-600)' }}
                 fill="currentColor"
                 fill="currentColor"
               >
               >
                 <rect width="100" height="100" rx="20" />
                 <rect width="100" height="100" rx="20" />
@@ -35,7 +37,7 @@ export default function Navbar() {
                 />
                 />
                 <circle cx="35" cy="35" r="8" fill="white" />
                 <circle cx="35" cy="35" r="8" fill="white" />
               </svg>
               </svg>
-              <span className="text-xl font-bold text-gray-900">ImageDrop</span>
+              <span className="text-xl font-bold" style={{ color: 'var(--text-primary)' }}>ImageDrop</span>
             </Link>
             </Link>
           </div>
           </div>
 
 
@@ -43,7 +45,10 @@ export default function Navbar() {
           <div className="hidden md:flex items-center gap-4">
           <div className="hidden md:flex items-center gap-4">
             <Link
             <Link
               to="/"
               to="/"
-              className="text-gray-600 hover:text-gray-900 px-3 py-2 rounded-md text-sm font-medium"
+              className="px-3 py-2 rounded-md text-sm font-medium transition-colors duration-200"
+              style={{ color: 'var(--text-secondary)' }}
+              onMouseEnter={(e) => e.currentTarget.style.color = 'var(--text-primary)'}
+              onMouseLeave={(e) => e.currentTarget.style.color = 'var(--text-secondary)'}
             >
             >
               Upload
               Upload
             </Link>
             </Link>
@@ -51,10 +56,14 @@ export default function Navbar() {
               <>
               <>
                 <Link
                 <Link
                   to="/dashboard"
                   to="/dashboard"
-                  className="text-gray-600 hover:text-gray-900 px-3 py-2 rounded-md text-sm font-medium"
+                  className="px-3 py-2 rounded-md text-sm font-medium transition-colors duration-200"
+                  style={{ color: 'var(--text-secondary)' }}
+                  onMouseEnter={(e) => e.currentTarget.style.color = 'var(--text-primary)'}
+                  onMouseLeave={(e) => e.currentTarget.style.color = 'var(--text-secondary)'}
                 >
                 >
                   My Images
                   My Images
                 </Link>
                 </Link>
+                <ThemeToggle />
                 <button
                 <button
                   onClick={handleLogout}
                   onClick={handleLogout}
                   className="btn btn-secondary text-sm"
                   className="btn btn-secondary text-sm"
@@ -64,6 +73,7 @@ export default function Navbar() {
               </>
               </>
             ) : (
             ) : (
               <>
               <>
+                <ThemeToggle />
                 <Link to="/login" className="btn btn-secondary text-sm">
                 <Link to="/login" className="btn btn-secondary text-sm">
                   Login
                   Login
                 </Link>
                 </Link>
@@ -75,10 +85,12 @@ export default function Navbar() {
           </div>
           </div>
 
 
           {/* Mobile menu button */}
           {/* Mobile menu button */}
-          <div className="md:hidden flex items-center">
+          <div className="md:hidden flex items-center gap-2">
+            <ThemeToggle />
             <button
             <button
               onClick={() => setMenuOpen(!menuOpen)}
               onClick={() => setMenuOpen(!menuOpen)}
-              className="text-gray-600 hover:text-gray-900 p-2"
+              className="p-2 transition-colors duration-200"
+              style={{ color: 'var(--text-secondary)' }}
             >
             >
               <svg
               <svg
                 className="h-6 w-6"
                 className="h-6 w-6"
@@ -109,11 +121,12 @@ export default function Navbar() {
 
 
       {/* Mobile menu */}
       {/* Mobile menu */}
       {menuOpen && (
       {menuOpen && (
-        <div className="md:hidden border-t border-gray-200 bg-white">
+        <div className="md:hidden border-t transition-colors duration-200" style={{ borderColor: 'var(--border)', backgroundColor: 'var(--bg-secondary)' }}>
           <div className="px-4 py-3 space-y-2">
           <div className="px-4 py-3 space-y-2">
             <Link
             <Link
               to="/"
               to="/"
-              className="block px-3 py-2 rounded-md text-gray-600 hover:text-gray-900 hover:bg-gray-50"
+              className="block px-3 py-2 rounded-md transition-colors duration-200"
+              style={{ color: 'var(--text-secondary)' }}
               onClick={() => setMenuOpen(false)}
               onClick={() => setMenuOpen(false)}
             >
             >
               Upload
               Upload
@@ -122,7 +135,8 @@ export default function Navbar() {
               <>
               <>
                 <Link
                 <Link
                   to="/dashboard"
                   to="/dashboard"
-                  className="block px-3 py-2 rounded-md text-gray-600 hover:text-gray-900 hover:bg-gray-50"
+                  className="block px-3 py-2 rounded-md transition-colors duration-200"
+                  style={{ color: 'var(--text-secondary)' }}
                   onClick={() => setMenuOpen(false)}
                   onClick={() => setMenuOpen(false)}
                 >
                 >
                   My Images
                   My Images
@@ -132,7 +146,8 @@ export default function Navbar() {
                     handleLogout();
                     handleLogout();
                     setMenuOpen(false);
                     setMenuOpen(false);
                   }}
                   }}
-                  className="block w-full text-left px-3 py-2 rounded-md text-gray-600 hover:text-gray-900 hover:bg-gray-50"
+                  className="block w-full text-left px-3 py-2 rounded-md transition-colors duration-200"
+                  style={{ color: 'var(--text-secondary)' }}
                 >
                 >
                   Logout
                   Logout
                 </button>
                 </button>
@@ -141,14 +156,16 @@ export default function Navbar() {
               <>
               <>
                 <Link
                 <Link
                   to="/login"
                   to="/login"
-                  className="block px-3 py-2 rounded-md text-gray-600 hover:text-gray-900 hover:bg-gray-50"
+                  className="block px-3 py-2 rounded-md transition-colors duration-200"
+                  style={{ color: 'var(--text-secondary)' }}
                   onClick={() => setMenuOpen(false)}
                   onClick={() => setMenuOpen(false)}
                 >
                 >
                   Login
                   Login
                 </Link>
                 </Link>
                 <Link
                 <Link
                   to="/register"
                   to="/register"
-                  className="block px-3 py-2 rounded-md text-primary-600 hover:text-primary-700 hover:bg-gray-50"
+                  className="block px-3 py-2 rounded-md transition-colors duration-200"
+                  style={{ color: 'var(--accent-600)' }}
                   onClick={() => setMenuOpen(false)}
                   onClick={() => setMenuOpen(false)}
                 >
                 >
                   Sign Up
                   Sign Up

+ 27 - 8
src/components/ShareModal.tsx

@@ -91,13 +91,22 @@ export default function ShareModal({ isOpen, onClose, image }: ShareModalProps)
       />
       />
 
 
       {/* Modal */}
       {/* Modal */}
-      <div className="relative bg-white rounded-xl shadow-xl max-w-md w-full p-6">
+      <div
+        className="relative rounded-xl shadow-xl max-w-md w-full p-6"
+        style={{ backgroundColor: 'var(--bg-secondary)' }}
+      >
         {/* Header */}
         {/* Header */}
         <div className="flex items-center justify-between mb-4">
         <div className="flex items-center justify-between mb-4">
-          <h3 className="text-lg font-semibold text-gray-900">Share Image</h3>
+          <h3
+            className="text-lg font-semibold"
+            style={{ color: 'var(--text-primary)' }}
+          >
+            Share Image
+          </h3>
           <button
           <button
             onClick={onClose}
             onClick={onClose}
-            className="text-gray-400 hover:text-gray-600"
+            className="transition-colors"
+            style={{ color: 'var(--text-muted)' }}
           >
           >
             <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
             <svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
               <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
               <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
@@ -117,7 +126,10 @@ export default function ShareModal({ isOpen, onClose, image }: ShareModalProps)
         {/* Link expiry selector (for signed URLs) */}
         {/* Link expiry selector (for signed URLs) */}
         {!image.isPublic && (
         {!image.isPublic && (
           <div className="mb-4">
           <div className="mb-4">
-            <label className="block text-sm font-medium text-gray-700 mb-2">
+            <label
+              className="block text-sm font-medium mb-2"
+              style={{ color: 'var(--text-secondary)' }}
+            >
               Link expires in
               Link expires in
             </label>
             </label>
             <select
             <select
@@ -136,22 +148,29 @@ export default function ShareModal({ isOpen, onClose, image }: ShareModalProps)
 
 
         {/* Share URL */}
         {/* Share URL */}
         <div className="mb-4">
         <div className="mb-4">
-          <label className="block text-sm font-medium text-gray-700 mb-2">
+          <label
+            className="block text-sm font-medium mb-2"
+            style={{ color: 'var(--text-secondary)' }}
+          >
             Share link
             Share link
           </label>
           </label>
           {isGenerating ? (
           {isGenerating ? (
-            <div className="flex items-center justify-center h-10 bg-gray-100 rounded-lg">
+            <div
+              className="flex items-center justify-center h-10 rounded-lg"
+              style={{ backgroundColor: 'var(--bg-tertiary)' }}
+            >
               <LoadingSpinner size="sm" />
               <LoadingSpinner size="sm" />
             </div>
             </div>
           ) : error ? (
           ) : error ? (
-            <p className="text-sm text-red-600">{error}</p>
+            <p className="text-sm text-red-600 dark:text-red-400">{error}</p>
           ) : (
           ) : (
             <div className="flex gap-2">
             <div className="flex gap-2">
               <input
               <input
                 type="text"
                 type="text"
                 value={shareUrl || ''}
                 value={shareUrl || ''}
                 readOnly
                 readOnly
-                className="input flex-1 bg-gray-50 text-sm"
+                className="input flex-1 text-sm"
+                style={{ backgroundColor: 'var(--bg-tertiary)' }}
               />
               />
               <button
               <button
                 onClick={copyToClipboard}
                 onClick={copyToClipboard}

+ 62 - 0
src/components/ThemeToggle.tsx

@@ -0,0 +1,62 @@
+import { useTheme, Theme } from '../contexts/ThemeContext';
+
+function SunIcon({ className }: { className?: string }) {
+  return (
+    <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
+      <path strokeLinecap="round" strokeLinejoin="round" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
+    </svg>
+  );
+}
+
+function MoonIcon({ className }: { className?: string }) {
+  return (
+    <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
+      <path strokeLinecap="round" strokeLinejoin="round" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
+    </svg>
+  );
+}
+
+function SystemIcon({ className }: { className?: string }) {
+  return (
+    <svg className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
+      <path strokeLinecap="round" strokeLinejoin="round" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
+    </svg>
+  );
+}
+
+const themeOrder: Theme[] = ['light', 'dark', 'system'];
+const themeLabels: Record<Theme, string> = {
+  light: 'Light mode',
+  dark: 'Dark mode',
+  system: 'System theme',
+};
+
+export default function ThemeToggle() {
+  const { theme, resolvedTheme, setTheme } = useTheme();
+
+  const cycleTheme = () => {
+    const currentIndex = themeOrder.indexOf(theme);
+    const nextTheme = themeOrder[(currentIndex + 1) % themeOrder.length];
+    setTheme(nextTheme);
+  };
+
+  const getIcon = () => {
+    if (theme === 'system') {
+      return <SystemIcon className="h-5 w-5" />;
+    }
+    return resolvedTheme === 'dark'
+      ? <MoonIcon className="h-5 w-5" />
+      : <SunIcon className="h-5 w-5" />;
+  };
+
+  return (
+    <button
+      onClick={cycleTheme}
+      className="p-2 rounded-lg transition-colors duration-200 text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-tertiary)]"
+      title={themeLabels[theme]}
+      aria-label={themeLabels[theme]}
+    >
+      {getIcon()}
+    </button>
+  );
+}

+ 80 - 0
src/contexts/ThemeContext.tsx

@@ -0,0 +1,80 @@
+import { createContext, useContext, useEffect, useState, ReactNode } from 'react';
+
+export type Theme = 'light' | 'dark' | 'system';
+export type ResolvedTheme = 'light' | 'dark';
+
+interface ThemeContextType {
+  theme: Theme;
+  resolvedTheme: ResolvedTheme;
+  setTheme: (theme: Theme) => void;
+}
+
+const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
+
+const STORAGE_KEY = 'imagedrop-theme';
+
+function getSystemTheme(): ResolvedTheme {
+  if (typeof window === 'undefined') return 'light';
+  return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
+}
+
+function getInitialTheme(): Theme {
+  if (typeof window === 'undefined') return 'system';
+  const stored = localStorage.getItem(STORAGE_KEY);
+  if (stored === 'light' || stored === 'dark' || stored === 'system') {
+    return stored;
+  }
+  return 'system';
+}
+
+export function ThemeProvider({ children }: { children: ReactNode }) {
+  const [theme, setThemeState] = useState<Theme>(getInitialTheme);
+  const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>(() => {
+    const initial = getInitialTheme();
+    return initial === 'system' ? getSystemTheme() : initial;
+  });
+
+  const setTheme = (newTheme: Theme) => {
+    setThemeState(newTheme);
+    localStorage.setItem(STORAGE_KEY, newTheme);
+  };
+
+  useEffect(() => {
+    const root = document.documentElement;
+
+    const applyTheme = (isDark: boolean) => {
+      if (isDark) {
+        root.classList.add('dark');
+        setResolvedTheme('dark');
+      } else {
+        root.classList.remove('dark');
+        setResolvedTheme('light');
+      }
+    };
+
+    if (theme === 'system') {
+      const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
+      applyTheme(mediaQuery.matches);
+
+      const handler = (e: MediaQueryListEvent) => applyTheme(e.matches);
+      mediaQuery.addEventListener('change', handler);
+      return () => mediaQuery.removeEventListener('change', handler);
+    } else {
+      applyTheme(theme === 'dark');
+    }
+  }, [theme]);
+
+  return (
+    <ThemeContext.Provider value={{ theme, resolvedTheme, setTheme }}>
+      {children}
+    </ThemeContext.Provider>
+  );
+}
+
+export function useTheme() {
+  const context = useContext(ThemeContext);
+  if (!context) {
+    throw new Error('useTheme must be used within a ThemeProvider');
+  }
+  return context;
+}

+ 70 - 5
src/index.css

@@ -3,8 +3,52 @@
 @tailwind utilities;
 @tailwind utilities;
 
 
 @layer base {
 @layer base {
+  /* Light theme (default) */
+  :root {
+    --bg-primary: #f9fafb;    /* gray-50 */
+    --bg-secondary: #ffffff;   /* white */
+    --bg-tertiary: #f3f4f6;   /* gray-100 */
+    --text-primary: #111827;   /* gray-900 */
+    --text-secondary: #4b5563; /* gray-600 */
+    --text-muted: #6b7280;     /* gray-500 */
+    --border: #e5e7eb;         /* gray-200 */
+    --border-strong: #d1d5db;  /* gray-300 */
+
+    /* Accent colors - Blue for light theme */
+    --accent-50: #eff6ff;
+    --accent-100: #dbeafe;
+    --accent-200: #bfdbfe;
+    --accent-500: #3b82f6;
+    --accent-600: #2563eb;
+    --accent-700: #1d4ed8;
+    --accent-800: #1e40af;
+  }
+
+  /* Dark theme */
+  .dark {
+    --bg-primary: #111827;    /* gray-900 */
+    --bg-secondary: #1f2937;  /* gray-800 */
+    --bg-tertiary: #374151;   /* gray-700 */
+    --text-primary: #f9fafb;  /* gray-50 */
+    --text-secondary: #d1d5db;/* gray-300 */
+    --text-muted: #9ca3af;    /* gray-400 */
+    --border: #374151;        /* gray-700 */
+    --border-strong: #4b5563; /* gray-600 */
+
+    /* Accent colors - Amber for dark theme */
+    --accent-50: #fffbeb;
+    --accent-100: #fef3c7;
+    --accent-200: #fde68a;
+    --accent-500: #f59e0b;
+    --accent-600: #d97706;
+    --accent-700: #b45309;
+    --accent-800: #92400e;
+  }
+
   body {
   body {
-    @apply bg-gray-50 text-gray-900 antialiased;
+    @apply antialiased transition-colors duration-200;
+    background-color: var(--bg-primary);
+    color: var(--text-primary);
   }
   }
 }
 }
 
 
@@ -14,11 +58,21 @@
   }
   }
 
 
   .btn-primary {
   .btn-primary {
-    @apply bg-primary-600 text-white hover:bg-primary-700 focus:ring-primary-500;
+    background-color: var(--accent-600);
+    @apply text-white focus:ring-offset-2;
+  }
+  .btn-primary:hover {
+    background-color: var(--accent-700);
+  }
+  .btn-primary:focus {
+    --tw-ring-color: var(--accent-500);
+  }
+  .dark .btn-primary {
+    @apply focus:ring-offset-gray-900;
   }
   }
 
 
   .btn-secondary {
   .btn-secondary {
-    @apply bg-gray-200 text-gray-800 hover:bg-gray-300 focus:ring-gray-500;
+    @apply bg-gray-200 text-gray-800 hover:bg-gray-300 focus:ring-gray-500 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600;
   }
   }
 
 
   .btn-danger {
   .btn-danger {
@@ -26,10 +80,21 @@
   }
   }
 
 
   .input {
   .input {
-    @apply w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent;
+    @apply w-full px-4 py-2 rounded-lg focus:outline-none focus:ring-2 focus:border-transparent transition-colors duration-200;
+    background-color: var(--bg-secondary);
+    color: var(--text-primary);
+    border: 1px solid var(--border-strong);
+  }
+  .input:focus {
+    --tw-ring-color: var(--accent-500);
+  }
+  .input::placeholder {
+    color: var(--text-muted);
   }
   }
 
 
   .card {
   .card {
-    @apply bg-white rounded-xl shadow-sm border border-gray-200;
+    @apply rounded-xl shadow-sm transition-colors duration-200;
+    background-color: var(--bg-secondary);
+    border: 1px solid var(--border);
   }
   }
 }
 }

+ 8 - 5
src/main.tsx

@@ -2,6 +2,7 @@ import React from 'react';
 import ReactDOM from 'react-dom/client';
 import ReactDOM from 'react-dom/client';
 import { HashRouter } from 'react-router-dom';
 import { HashRouter } from 'react-router-dom';
 import { BaaSProvider } from '@picobaas/client/react';
 import { BaaSProvider } from '@picobaas/client/react';
+import { ThemeProvider } from './contexts/ThemeContext';
 import App from './App';
 import App from './App';
 import './index.css';
 import './index.css';
 import { config } from './config';
 import { config } from './config';
@@ -10,10 +11,12 @@ import { config } from './config';
 // Routes will be: /#/, /#/login, /#/dashboard, etc.
 // Routes will be: /#/, /#/login, /#/dashboard, etc.
 ReactDOM.createRoot(document.getElementById('root')!).render(
 ReactDOM.createRoot(document.getElementById('root')!).render(
   <React.StrictMode>
   <React.StrictMode>
-    <HashRouter>
-      <BaaSProvider config={config}>
-        <App />
-      </BaaSProvider>
-    </HashRouter>
+    <ThemeProvider>
+      <HashRouter>
+        <BaaSProvider config={config}>
+          <App />
+        </BaaSProvider>
+      </HashRouter>
+    </ThemeProvider>
   </React.StrictMode>
   </React.StrictMode>
 );
 );

+ 17 - 6
src/pages/DashboardPage.tsx

@@ -38,8 +38,13 @@ export default function DashboardPage() {
       {/* Header */}
       {/* Header */}
       <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-8">
       <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4 mb-8">
         <div>
         <div>
-          <h1 className="text-2xl font-bold text-gray-900">My Images</h1>
-          <p className="text-gray-600">Manage your uploaded images</p>
+          <h1
+            className="text-2xl font-bold"
+            style={{ color: 'var(--text-primary)' }}
+          >
+            My Images
+          </h1>
+          <p style={{ color: 'var(--text-secondary)' }}>Manage your uploaded images</p>
         </div>
         </div>
         <div className="flex gap-3">
         <div className="flex gap-3">
           <button
           <button
@@ -67,10 +72,16 @@ export default function DashboardPage() {
       {showUploader && (
       {showUploader && (
         <div className="card p-6 mb-8">
         <div className="card p-6 mb-8">
           <div className="flex justify-between items-center mb-4">
           <div className="flex justify-between items-center mb-4">
-            <h2 className="text-lg font-semibold text-gray-900">Upload New Image</h2>
+            <h2
+              className="text-lg font-semibold"
+              style={{ color: 'var(--text-primary)' }}
+            >
+              Upload New Image
+            </h2>
             <button
             <button
               onClick={() => setShowUploader(false)}
               onClick={() => setShowUploader(false)}
-              className="text-gray-400 hover:text-gray-600"
+              className="transition-colors"
+              style={{ color: 'var(--text-muted)' }}
             >
             >
               <svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
               <svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                 <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
                 <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
@@ -79,7 +90,7 @@ export default function DashboardPage() {
           </div>
           </div>
 
 
           {uploadError && (
           {uploadError && (
-            <div className="mb-4 p-3 bg-red-50 border border-red-200 rounded-lg text-red-700 text-sm">
+            <div className="mb-4 p-3 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg text-red-700 dark:text-red-400 text-sm">
               {uploadError}
               {uploadError}
             </div>
             </div>
           )}
           )}
@@ -94,7 +105,7 @@ export default function DashboardPage() {
 
 
       {/* Error message */}
       {/* Error message */}
       {error && (
       {error && (
-        <div className="mb-6 p-4 bg-red-50 border border-red-200 rounded-lg text-red-700">
+        <div className="mb-6 p-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg text-red-700 dark:text-red-400">
           {error}
           {error}
         </div>
         </div>
       )}
       )}

+ 82 - 29
src/pages/HomePage.tsx

@@ -59,15 +59,25 @@ export default function HomePage() {
     <div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
     <div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
       {/* Hero section */}
       {/* Hero section */}
       <div className="text-center mb-12">
       <div className="text-center mb-12">
-        <h1 className="text-4xl sm:text-5xl font-bold text-gray-900 mb-4">
+        <h1
+          className="text-4xl sm:text-5xl font-bold mb-4"
+          style={{ color: 'var(--text-primary)' }}
+        >
           Share images instantly
           Share images instantly
         </h1>
         </h1>
-        <p className="text-xl text-gray-600 mb-6">
+        <p
+          className="text-xl mb-6"
+          style={{ color: 'var(--text-secondary)' }}
+        >
           Upload an image and get a shareable link. No account required.
           Upload an image and get a shareable link. No account required.
         </p>
         </p>
         {!isAuthenticated && (
         {!isAuthenticated && (
-          <p className="text-sm text-gray-500">
-            <Link to="/register" className="text-primary-600 hover:text-primary-700 font-medium">
+          <p className="text-sm" style={{ color: 'var(--text-muted)' }}>
+            <Link
+              to="/register"
+              className="font-medium"
+              style={{ color: 'var(--accent-600)' }}
+            >
               Create an account
               Create an account
             </Link>{' '}
             </Link>{' '}
             to manage your uploads and access more features.
             to manage your uploads and access more features.
@@ -78,20 +88,20 @@ export default function HomePage() {
       {/* Upload result */}
       {/* Upload result */}
       {uploadResult && (
       {uploadResult && (
         <div
         <div
-          className={`mb-8 p-6 rounded-xl ${
+          className={`mb-8 p-6 rounded-xl border ${
             uploadResult.success
             uploadResult.success
-              ? 'bg-green-50 border border-green-200'
-              : 'bg-red-50 border border-red-200'
+              ? 'bg-green-50 border-green-200 dark:bg-green-900/20 dark:border-green-800'
+              : 'bg-red-50 border-red-200 dark:bg-red-900/20 dark:border-red-800'
           }`}
           }`}
         >
         >
           {uploadResult.success ? (
           {uploadResult.success ? (
             <div className="text-center">
             <div className="text-center">
-              <div className="inline-flex items-center justify-center w-12 h-12 rounded-full bg-green-100 mb-4">
-                <svg className="h-6 w-6 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+              <div className="inline-flex items-center justify-center w-12 h-12 rounded-full bg-green-100 dark:bg-green-900/30 mb-4">
+                <svg className="h-6 w-6 text-green-600 dark:text-green-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                   <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
                   <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
                 </svg>
                 </svg>
               </div>
               </div>
-              <h3 className="text-lg font-semibold text-green-900 mb-2">
+              <h3 className="text-lg font-semibold text-green-900 dark:text-green-300 mb-2">
                 Image uploaded successfully!
                 Image uploaded successfully!
               </h3>
               </h3>
               <div className="flex gap-2 max-w-xl mx-auto">
               <div className="flex gap-2 max-w-xl mx-auto">
@@ -99,7 +109,7 @@ export default function HomePage() {
                   type="text"
                   type="text"
                   value={uploadResult.url}
                   value={uploadResult.url}
                   readOnly
                   readOnly
-                  className="input flex-1 bg-white text-sm"
+                  className="input flex-1 text-sm"
                 />
                 />
                 <button
                 <button
                   onClick={() => uploadResult.url && copyToClipboard(uploadResult.url)}
                   onClick={() => uploadResult.url && copyToClipboard(uploadResult.url)}
@@ -113,29 +123,30 @@ export default function HomePage() {
                   href={uploadResult.url}
                   href={uploadResult.url}
                   target="_blank"
                   target="_blank"
                   rel="noopener noreferrer"
                   rel="noopener noreferrer"
-                  className="text-green-700 hover:text-green-800 text-sm font-medium"
+                  className="text-green-700 dark:text-green-400 hover:opacity-80 text-sm font-medium"
                 >
                 >
                   Open image in new tab →
                   Open image in new tab →
                 </a>
                 </a>
               </div>
               </div>
               <button
               <button
                 onClick={() => setUploadResult(null)}
                 onClick={() => setUploadResult(null)}
-                className="mt-4 text-sm text-gray-600 hover:text-gray-800"
+                className="mt-4 text-sm transition-opacity hover:opacity-80"
+                style={{ color: 'var(--text-secondary)' }}
               >
               >
                 Upload another image
                 Upload another image
               </button>
               </button>
             </div>
             </div>
           ) : (
           ) : (
             <div className="text-center">
             <div className="text-center">
-              <div className="inline-flex items-center justify-center w-12 h-12 rounded-full bg-red-100 mb-4">
-                <svg className="h-6 w-6 text-red-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+              <div className="inline-flex items-center justify-center w-12 h-12 rounded-full bg-red-100 dark:bg-red-900/30 mb-4">
+                <svg className="h-6 w-6 text-red-600 dark:text-red-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                   <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
                   <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
                 </svg>
                 </svg>
               </div>
               </div>
-              <h3 className="text-lg font-semibold text-red-900 mb-2">
+              <h3 className="text-lg font-semibold text-red-900 dark:text-red-300 mb-2">
                 Upload failed
                 Upload failed
               </h3>
               </h3>
-              <p className="text-red-700 mb-4">{uploadResult.error}</p>
+              <p className="text-red-700 dark:text-red-400 mb-4">{uploadResult.error}</p>
               <button
               <button
                 onClick={() => setUploadResult(null)}
                 onClick={() => setUploadResult(null)}
                 className="btn btn-secondary"
                 className="btn btn-secondary"
@@ -160,37 +171,79 @@ export default function HomePage() {
       {/* Features */}
       {/* Features */}
       <div className="mt-16 grid md:grid-cols-3 gap-8">
       <div className="mt-16 grid md:grid-cols-3 gap-8">
         <div className="text-center">
         <div className="text-center">
-          <div className="inline-flex items-center justify-center w-12 h-12 rounded-full bg-primary-100 mb-4">
-            <svg className="h-6 w-6 text-primary-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+          <div
+            className="inline-flex items-center justify-center w-12 h-12 rounded-full mb-4"
+            style={{ backgroundColor: 'var(--accent-100)' }}
+          >
+            <svg
+              className="h-6 w-6"
+              style={{ color: 'var(--accent-600)' }}
+              fill="none"
+              viewBox="0 0 24 24"
+              stroke="currentColor"
+            >
               <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
               <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
             </svg>
             </svg>
           </div>
           </div>
-          <h3 className="text-lg font-semibold text-gray-900 mb-2">Instant Upload</h3>
-          <p className="text-gray-600">
+          <h3
+            className="text-lg font-semibold mb-2"
+            style={{ color: 'var(--text-primary)' }}
+          >
+            Instant Upload
+          </h3>
+          <p style={{ color: 'var(--text-secondary)' }}>
             Drop your image and get a link instantly. No sign-up needed.
             Drop your image and get a link instantly. No sign-up needed.
           </p>
           </p>
         </div>
         </div>
 
 
         <div className="text-center">
         <div className="text-center">
-          <div className="inline-flex items-center justify-center w-12 h-12 rounded-full bg-primary-100 mb-4">
-            <svg className="h-6 w-6 text-primary-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+          <div
+            className="inline-flex items-center justify-center w-12 h-12 rounded-full mb-4"
+            style={{ backgroundColor: 'var(--accent-100)' }}
+          >
+            <svg
+              className="h-6 w-6"
+              style={{ color: 'var(--accent-600)' }}
+              fill="none"
+              viewBox="0 0 24 24"
+              stroke="currentColor"
+            >
               <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
               <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
             </svg>
             </svg>
           </div>
           </div>
-          <h3 className="text-lg font-semibold text-gray-900 mb-2">Auto Expiry</h3>
-          <p className="text-gray-600">
+          <h3
+            className="text-lg font-semibold mb-2"
+            style={{ color: 'var(--text-primary)' }}
+          >
+            Auto Expiry
+          </h3>
+          <p style={{ color: 'var(--text-secondary)' }}>
             Set expiration times to automatically delete images after a period.
             Set expiration times to automatically delete images after a period.
           </p>
           </p>
         </div>
         </div>
 
 
         <div className="text-center">
         <div className="text-center">
-          <div className="inline-flex items-center justify-center w-12 h-12 rounded-full bg-primary-100 mb-4">
-            <svg className="h-6 w-6 text-primary-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+          <div
+            className="inline-flex items-center justify-center w-12 h-12 rounded-full mb-4"
+            style={{ backgroundColor: 'var(--accent-100)' }}
+          >
+            <svg
+              className="h-6 w-6"
+              style={{ color: 'var(--accent-600)' }}
+              fill="none"
+              viewBox="0 0 24 24"
+              stroke="currentColor"
+            >
               <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z" />
               <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z" />
             </svg>
             </svg>
           </div>
           </div>
-          <h3 className="text-lg font-semibold text-gray-900 mb-2">Easy Sharing</h3>
-          <p className="text-gray-600">
+          <h3
+            className="text-lg font-semibold mb-2"
+            style={{ color: 'var(--text-primary)' }}
+          >
+            Easy Sharing
+          </h3>
+          <p style={{ color: 'var(--text-secondary)' }}>
             Get shareable links for your images with just one click.
             Get shareable links for your images with just one click.
           </p>
           </p>
         </div>
         </div>

+ 17 - 4
src/pages/ImageAnalyticsPage.tsx

@@ -9,7 +9,12 @@ export default function ImageAnalyticsPage() {
     return (
     return (
       <div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
       <div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
         <div className="text-center">
         <div className="text-center">
-          <h1 className="text-2xl font-bold text-gray-900 mb-4">Image not found</h1>
+          <h1
+            className="text-2xl font-bold mb-4"
+            style={{ color: 'var(--text-primary)' }}
+          >
+            Image not found
+          </h1>
           <Link to="/dashboard" className="btn btn-primary">
           <Link to="/dashboard" className="btn btn-primary">
             Back to Dashboard
             Back to Dashboard
           </Link>
           </Link>
@@ -22,9 +27,15 @@ export default function ImageAnalyticsPage() {
     <div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
     <div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
       {/* Breadcrumb */}
       {/* Breadcrumb */}
       <nav className="mb-6">
       <nav className="mb-6">
-        <ol className="flex items-center space-x-2 text-sm text-gray-500">
+        <ol
+          className="flex items-center space-x-2 text-sm"
+          style={{ color: 'var(--text-muted)' }}
+        >
           <li>
           <li>
-            <Link to="/dashboard" className="hover:text-gray-700">
+            <Link
+              to="/dashboard"
+              className="hover:opacity-80 transition-opacity"
+            >
               Dashboard
               Dashboard
             </Link>
             </Link>
           </li>
           </li>
@@ -33,7 +44,9 @@ export default function ImageAnalyticsPage() {
               <path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
               <path fillRule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clipRule="evenodd" />
             </svg>
             </svg>
           </li>
           </li>
-          <li className="font-medium text-gray-900">Analytics</li>
+          <li className="font-medium" style={{ color: 'var(--text-primary)' }}>
+            Analytics
+          </li>
         </ol>
         </ol>
       </nav>
       </nav>
 
 

+ 32 - 14
src/pages/ImageDetailPage.tsx

@@ -88,8 +88,13 @@ export default function ImageDetailPage() {
     return (
     return (
       <div className="min-h-[calc(100vh-12rem)] flex items-center justify-center">
       <div className="min-h-[calc(100vh-12rem)] flex items-center justify-center">
         <div className="text-center">
         <div className="text-center">
-          <h2 className="text-xl font-semibold text-gray-900 mb-2">Image not found</h2>
-          <p className="text-gray-600 mb-4">{error}</p>
+          <h2
+            className="text-xl font-semibold mb-2"
+            style={{ color: 'var(--text-primary)' }}
+          >
+            Image not found
+          </h2>
+          <p className="mb-4" style={{ color: 'var(--text-secondary)' }}>{error}</p>
           <button onClick={() => navigate('/dashboard')} className="btn btn-primary">
           <button onClick={() => navigate('/dashboard')} className="btn btn-primary">
             Back to Dashboard
             Back to Dashboard
           </button>
           </button>
@@ -105,7 +110,8 @@ export default function ImageDetailPage() {
       {/* Back button */}
       {/* Back button */}
       <button
       <button
         onClick={() => navigate('/dashboard')}
         onClick={() => navigate('/dashboard')}
-        className="flex items-center gap-2 text-gray-600 hover:text-gray-900 mb-6"
+        className="flex items-center gap-2 mb-6 transition-colors"
+        style={{ color: 'var(--text-secondary)' }}
       >
       >
         <svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
         <svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
           <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
           <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
@@ -129,30 +135,42 @@ export default function ImageDetailPage() {
         <div className="space-y-6">
         <div className="space-y-6">
           {/* Info card */}
           {/* Info card */}
           <div className="card p-6">
           <div className="card p-6">
-            <h1 className="text-xl font-bold text-gray-900 mb-4 break-words">
+            <h1
+              className="text-xl font-bold mb-4 break-words"
+              style={{ color: 'var(--text-primary)' }}
+            >
               {image.name}
               {image.name}
             </h1>
             </h1>
 
 
             <dl className="space-y-3 text-sm">
             <dl className="space-y-3 text-sm">
               <div className="flex justify-between">
               <div className="flex justify-between">
-                <dt className="text-gray-500">Size</dt>
-                <dd className="text-gray-900 font-medium">{formatFileSize(image.size)}</dd>
+                <dt style={{ color: 'var(--text-muted)' }}>Size</dt>
+                <dd className="font-medium" style={{ color: 'var(--text-primary)' }}>
+                  {formatFileSize(image.size)}
+                </dd>
               </div>
               </div>
               <div className="flex justify-between">
               <div className="flex justify-between">
-                <dt className="text-gray-500">Type</dt>
-                <dd className="text-gray-900 font-medium">{image.mimeType}</dd>
+                <dt style={{ color: 'var(--text-muted)' }}>Type</dt>
+                <dd className="font-medium" style={{ color: 'var(--text-primary)' }}>
+                  {image.mimeType}
+                </dd>
               </div>
               </div>
               <div className="flex justify-between">
               <div className="flex justify-between">
-                <dt className="text-gray-500">Uploaded</dt>
-                <dd className="text-gray-900 font-medium">{formatDateTime(image.createdAt)}</dd>
+                <dt style={{ color: 'var(--text-muted)' }}>Uploaded</dt>
+                <dd className="font-medium" style={{ color: 'var(--text-primary)' }}>
+                  {formatDateTime(image.createdAt)}
+                </dd>
               </div>
               </div>
               {expiryStatus && (
               {expiryStatus && (
                 <div className="flex justify-between">
                 <div className="flex justify-between">
-                  <dt className="text-gray-500">Expires</dt>
+                  <dt style={{ color: 'var(--text-muted)' }}>Expires</dt>
                   <dd className={`font-medium ${
                   <dd className={`font-medium ${
-                    expiryStatus.isExpired ? 'text-red-600' :
-                    expiryStatus.isExpiringSoon ? 'text-yellow-600' : 'text-gray-900'
-                  }`}>
+                    expiryStatus.isExpired
+                      ? 'text-red-600 dark:text-red-400'
+                      : expiryStatus.isExpiringSoon
+                      ? 'text-yellow-600 dark:text-yellow-400'
+                      : ''
+                  }`} style={!expiryStatus.isExpired && !expiryStatus.isExpiringSoon ? { color: 'var(--text-primary)' } : undefined}>
                     {expiryStatus.label}
                     {expiryStatus.label}
                   </dd>
                   </dd>
                 </div>
                 </div>

+ 27 - 7
src/pages/LoginPage.tsx

@@ -36,8 +36,13 @@ export default function LoginPage() {
     <div className="min-h-[calc(100vh-12rem)] flex items-center justify-center py-12 px-4">
     <div className="min-h-[calc(100vh-12rem)] flex items-center justify-center py-12 px-4">
       <div className="max-w-md w-full">
       <div className="max-w-md w-full">
         <div className="text-center mb-8">
         <div className="text-center mb-8">
-          <h1 className="text-3xl font-bold text-gray-900">Welcome back</h1>
-          <p className="mt-2 text-gray-600">
+          <h1
+            className="text-3xl font-bold"
+            style={{ color: 'var(--text-primary)' }}
+          >
+            Welcome back
+          </h1>
+          <p className="mt-2" style={{ color: 'var(--text-secondary)' }}>
             Sign in to manage your images
             Sign in to manage your images
           </p>
           </p>
         </div>
         </div>
@@ -45,13 +50,17 @@ export default function LoginPage() {
         <div className="card p-6">
         <div className="card p-6">
           <form onSubmit={handleSubmit} className="space-y-4">
           <form onSubmit={handleSubmit} className="space-y-4">
             {error && (
             {error && (
-              <div className="p-3 bg-red-50 border border-red-200 rounded-lg text-red-700 text-sm">
+              <div className="p-3 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg text-red-700 dark:text-red-400 text-sm">
                 {error}
                 {error}
               </div>
               </div>
             )}
             )}
 
 
             <div>
             <div>
-              <label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-1">
+              <label
+                htmlFor="email"
+                className="block text-sm font-medium mb-1"
+                style={{ color: 'var(--text-secondary)' }}
+              >
                 Email
                 Email
               </label>
               </label>
               <input
               <input
@@ -67,7 +76,11 @@ export default function LoginPage() {
             </div>
             </div>
 
 
             <div>
             <div>
-              <label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-1">
+              <label
+                htmlFor="password"
+                className="block text-sm font-medium mb-1"
+                style={{ color: 'var(--text-secondary)' }}
+              >
                 Password
                 Password
               </label>
               </label>
               <input
               <input
@@ -98,9 +111,16 @@ export default function LoginPage() {
             </button>
             </button>
           </form>
           </form>
 
 
-          <div className="mt-6 text-center text-sm text-gray-600">
+          <div
+            className="mt-6 text-center text-sm"
+            style={{ color: 'var(--text-secondary)' }}
+          >
             Don't have an account?{' '}
             Don't have an account?{' '}
-            <Link to="/register" className="text-primary-600 hover:text-primary-700 font-medium">
+            <Link
+              to="/register"
+              className="font-medium"
+              style={{ color: 'var(--accent-600)' }}
+            >
               Sign up
               Sign up
             </Link>
             </Link>
           </div>
           </div>

+ 47 - 14
src/pages/RegisterPage.tsx

@@ -74,13 +74,18 @@ export default function RegisterPage() {
     return (
     return (
       <div className="min-h-[calc(100vh-12rem)] flex items-center justify-center py-12 px-4">
       <div className="min-h-[calc(100vh-12rem)] flex items-center justify-center py-12 px-4">
         <div className="max-w-md w-full text-center">
         <div className="max-w-md w-full text-center">
-          <div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-green-100 mb-4">
-            <svg className="h-8 w-8 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+          <div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-green-100 dark:bg-green-900/30 mb-4">
+            <svg className="h-8 w-8 text-green-600 dark:text-green-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
               <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
               <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
             </svg>
             </svg>
           </div>
           </div>
-          <h1 className="text-2xl font-bold text-gray-900 mb-2">Check your email</h1>
-          <p className="text-gray-600 mb-6">
+          <h1
+            className="text-2xl font-bold mb-2"
+            style={{ color: 'var(--text-primary)' }}
+          >
+            Check your email
+          </h1>
+          <p className="mb-6" style={{ color: 'var(--text-secondary)' }}>
             We've sent a verification link to <strong>{email}</strong>.
             We've sent a verification link to <strong>{email}</strong>.
             Please click the link to verify your account.
             Please click the link to verify your account.
           </p>
           </p>
@@ -96,8 +101,13 @@ export default function RegisterPage() {
     <div className="min-h-[calc(100vh-12rem)] flex items-center justify-center py-12 px-4">
     <div className="min-h-[calc(100vh-12rem)] flex items-center justify-center py-12 px-4">
       <div className="max-w-md w-full">
       <div className="max-w-md w-full">
         <div className="text-center mb-8">
         <div className="text-center mb-8">
-          <h1 className="text-3xl font-bold text-gray-900">Create an account</h1>
-          <p className="mt-2 text-gray-600">
+          <h1
+            className="text-3xl font-bold"
+            style={{ color: 'var(--text-primary)' }}
+          >
+            Create an account
+          </h1>
+          <p className="mt-2" style={{ color: 'var(--text-secondary)' }}>
             Start managing your images today
             Start managing your images today
           </p>
           </p>
         </div>
         </div>
@@ -105,13 +115,17 @@ export default function RegisterPage() {
         <div className="card p-6">
         <div className="card p-6">
           <form onSubmit={handleSubmit} className="space-y-4">
           <form onSubmit={handleSubmit} className="space-y-4">
             {error && (
             {error && (
-              <div className="p-3 bg-red-50 border border-red-200 rounded-lg text-red-700 text-sm">
+              <div className="p-3 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg text-red-700 dark:text-red-400 text-sm">
                 {error}
                 {error}
               </div>
               </div>
             )}
             )}
 
 
             <div>
             <div>
-              <label htmlFor="username" className="block text-sm font-medium text-gray-700 mb-1">
+              <label
+                htmlFor="username"
+                className="block text-sm font-medium mb-1"
+                style={{ color: 'var(--text-secondary)' }}
+              >
                 Username
                 Username
               </label>
               </label>
               <input
               <input
@@ -127,7 +141,11 @@ export default function RegisterPage() {
             </div>
             </div>
 
 
             <div>
             <div>
-              <label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-1">
+              <label
+                htmlFor="email"
+                className="block text-sm font-medium mb-1"
+                style={{ color: 'var(--text-secondary)' }}
+              >
                 Email
                 Email
               </label>
               </label>
               <input
               <input
@@ -143,7 +161,11 @@ export default function RegisterPage() {
             </div>
             </div>
 
 
             <div>
             <div>
-              <label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-1">
+              <label
+                htmlFor="password"
+                className="block text-sm font-medium mb-1"
+                style={{ color: 'var(--text-secondary)' }}
+              >
                 Password
                 Password
               </label>
               </label>
               <input
               <input
@@ -156,13 +178,17 @@ export default function RegisterPage() {
                 required
                 required
                 autoComplete="new-password"
                 autoComplete="new-password"
               />
               />
-              <p className="mt-1 text-xs text-gray-500">
+              <p className="mt-1 text-xs" style={{ color: 'var(--text-muted)' }}>
                 At least 8 characters with uppercase, lowercase, and number
                 At least 8 characters with uppercase, lowercase, and number
               </p>
               </p>
             </div>
             </div>
 
 
             <div>
             <div>
-              <label htmlFor="confirmPassword" className="block text-sm font-medium text-gray-700 mb-1">
+              <label
+                htmlFor="confirmPassword"
+                className="block text-sm font-medium mb-1"
+                style={{ color: 'var(--text-secondary)' }}
+              >
                 Confirm Password
                 Confirm Password
               </label>
               </label>
               <input
               <input
@@ -193,9 +219,16 @@ export default function RegisterPage() {
             </button>
             </button>
           </form>
           </form>
 
 
-          <div className="mt-6 text-center text-sm text-gray-600">
+          <div
+            className="mt-6 text-center text-sm"
+            style={{ color: 'var(--text-secondary)' }}
+          >
             Already have an account?{' '}
             Already have an account?{' '}
-            <Link to="/login" className="text-primary-600 hover:text-primary-700 font-medium">
+            <Link
+              to="/login"
+              className="font-medium"
+              style={{ color: 'var(--accent-600)' }}
+            >
               Sign in
               Sign in
             </Link>
             </Link>
           </div>
           </div>

+ 19 - 9
src/pages/VerifyEmailPage.tsx

@@ -45,7 +45,7 @@ export default function VerifyEmailPage() {
       <div className="min-h-[calc(100vh-12rem)] flex items-center justify-center py-12 px-4">
       <div className="min-h-[calc(100vh-12rem)] flex items-center justify-center py-12 px-4">
         <div className="text-center">
         <div className="text-center">
           <LoadingSpinner size="lg" className="mx-auto mb-4" />
           <LoadingSpinner size="lg" className="mx-auto mb-4" />
-          <p className="text-gray-600">Verifying your email...</p>
+          <p style={{ color: 'var(--text-secondary)' }}>Verifying your email...</p>
         </div>
         </div>
       </div>
       </div>
     );
     );
@@ -55,13 +55,18 @@ export default function VerifyEmailPage() {
     return (
     return (
       <div className="min-h-[calc(100vh-12rem)] flex items-center justify-center py-12 px-4">
       <div className="min-h-[calc(100vh-12rem)] flex items-center justify-center py-12 px-4">
         <div className="max-w-md w-full text-center">
         <div className="max-w-md w-full text-center">
-          <div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-red-100 mb-4">
-            <svg className="h-8 w-8 text-red-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+          <div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-red-100 dark:bg-red-900/30 mb-4">
+            <svg className="h-8 w-8 text-red-600 dark:text-red-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
               <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
               <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
             </svg>
             </svg>
           </div>
           </div>
-          <h1 className="text-2xl font-bold text-gray-900 mb-2">Verification Failed</h1>
-          <p className="text-gray-600 mb-6">
+          <h1
+            className="text-2xl font-bold mb-2"
+            style={{ color: 'var(--text-primary)' }}
+          >
+            Verification Failed
+          </h1>
+          <p className="mb-6" style={{ color: 'var(--text-secondary)' }}>
             {error || 'Unable to verify your email. The link may have expired.'}
             {error || 'Unable to verify your email. The link may have expired.'}
           </p>
           </p>
           <div className="space-x-4">
           <div className="space-x-4">
@@ -80,13 +85,18 @@ export default function VerifyEmailPage() {
   return (
   return (
     <div className="min-h-[calc(100vh-12rem)] flex items-center justify-center py-12 px-4">
     <div className="min-h-[calc(100vh-12rem)] flex items-center justify-center py-12 px-4">
       <div className="max-w-md w-full text-center">
       <div className="max-w-md w-full text-center">
-        <div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-green-100 mb-4">
-          <svg className="h-8 w-8 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
+        <div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-green-100 dark:bg-green-900/30 mb-4">
+          <svg className="h-8 w-8 text-green-600 dark:text-green-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
             <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
             <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
           </svg>
           </svg>
         </div>
         </div>
-        <h1 className="text-2xl font-bold text-gray-900 mb-2">Email Verified!</h1>
-        <p className="text-gray-600 mb-6">
+        <h1
+          className="text-2xl font-bold mb-2"
+          style={{ color: 'var(--text-primary)' }}
+        >
+          Email Verified!
+        </h1>
+        <p className="mb-6" style={{ color: 'var(--text-secondary)' }}>
           Your email has been successfully verified. You can now sign in to your account.
           Your email has been successfully verified. You can now sign in to your account.
         </p>
         </p>
         <Link to="/login" className="btn btn-primary">
         <Link to="/login" className="btn btn-primary">

+ 1 - 0
tailwind.config.js

@@ -4,6 +4,7 @@ export default {
     "./index.html",
     "./index.html",
     "./src/**/*.{js,ts,jsx,tsx}",
     "./src/**/*.{js,ts,jsx,tsx}",
   ],
   ],
+  darkMode: 'class',
   theme: {
   theme: {
     extend: {
     extend: {
       colors: {
       colors: {