feat(ui): redesign TeaBoy OS to light premium SaaS theme
Theme overhaul (artifacts/teaboy-os/src/index.css):
- New light palette: background #F8FAFC, white cards, slate-200 borders
- Typography: IBM Plex Sans Arabic loaded via Google Fonts (Arabic-first, RTL)
- Soft glassmorphism: rgba(255,255,255,0.7) + 12px blur (vs heavy dark blur)
- Box shadows: 0 8px 30px rgba(15,23,42,0.05) (very soft, premium)
- .os-bg: light bg with two animated gradient blobs (soft purple top-left,
soft teal bottom-right) — Apple/SaaS style backdrop
- New helper classes: .topbar-glass (clean light header),
.dock-glass (floating macOS-like dock, lighter blur), .card-hover (lift)
- Gradient icon tiles: .icon-tile-{blue,purple,green,orange,pink,teal}
using soft from→to gradients per spec
Pages:
- home.tsx: AppIcon now uses gradient icon tiles mapped from app.color;
status bar uses .topbar-glass; bottom dock uses .dock-glass
- All pages: bulk-replaced dark-glass utilities so they read on light bg:
hover:bg-white/10 → hover:bg-slate-100,
bg-white/10 → bg-white/70,
border-white/10 → border-slate-200/70,
border-white/20 → border-slate-200,
bg-black/50 (modal backdrops) → bg-slate-900/30
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Arabic:wght@300;400;500;600;700&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 20 KiB |
@@ -60,39 +60,40 @@
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
}
|
||||
|
||||
:root, .dark {
|
||||
--background: 225 45% 8%;
|
||||
--foreground: 220 20% 95%;
|
||||
--card: 225 30% 15%;
|
||||
--card-foreground: 220 20% 95%;
|
||||
--popover: 225 30% 15%;
|
||||
--popover-foreground: 220 20% 95%;
|
||||
--primary: 230 80% 65%;
|
||||
--primary-foreground: 225 45% 8%;
|
||||
--secondary: 195 70% 55%;
|
||||
--secondary-foreground: 225 45% 8%;
|
||||
--muted: 225 25% 20%;
|
||||
--muted-foreground: 220 20% 80%;
|
||||
--accent: 280 70% 65%;
|
||||
--accent-foreground: 225 45% 8%;
|
||||
--destructive: 0 84.2% 60.2%;
|
||||
--destructive-foreground: 210 40% 98%;
|
||||
--border: 225 25% 20%;
|
||||
--input: 225 25% 20%;
|
||||
--ring: 230 80% 65%;
|
||||
|
||||
--sidebar: 225 30% 15%;
|
||||
--sidebar-foreground: 220 20% 95%;
|
||||
--sidebar-border: 225 25% 20%;
|
||||
--sidebar-primary: 230 80% 65%;
|
||||
--sidebar-primary-foreground: 225 45% 8%;
|
||||
--sidebar-accent: 225 25% 20%;
|
||||
--sidebar-accent-foreground: 220 20% 95%;
|
||||
--sidebar-ring: 230 80% 65%;
|
||||
:root {
|
||||
/* Light, calm, premium SaaS palette */
|
||||
--background: 210 40% 98%; /* #F8FAFC */
|
||||
--foreground: 222 47% 11%; /* slate-900 */
|
||||
--card: 0 0% 100%; /* #FFFFFF */
|
||||
--card-foreground: 222 47% 11%;
|
||||
--popover: 0 0% 100%;
|
||||
--popover-foreground: 222 47% 11%;
|
||||
--primary: 217 91% 60%; /* #3B82F6 */
|
||||
--primary-foreground: 0 0% 100%;
|
||||
--secondary: 173 80% 40%; /* teal-500 */
|
||||
--secondary-foreground: 0 0% 100%;
|
||||
--muted: 210 40% 96%;
|
||||
--muted-foreground: 215 16% 47%; /* slate-500 */
|
||||
--accent: 262 83% 66%; /* violet-500 */
|
||||
--accent-foreground: 0 0% 100%;
|
||||
--destructive: 0 84% 60%;
|
||||
--destructive-foreground: 0 0% 100%;
|
||||
--border: 214 32% 91%; /* #E2E8F0 */
|
||||
--input: 214 32% 91%;
|
||||
--ring: 217 91% 60%;
|
||||
|
||||
--app-font-sans: "Inter", sans-serif;
|
||||
--sidebar: 0 0% 100%;
|
||||
--sidebar-foreground: 222 47% 11%;
|
||||
--sidebar-border: 214 32% 91%;
|
||||
--sidebar-primary: 217 91% 60%;
|
||||
--sidebar-primary-foreground: 0 0% 100%;
|
||||
--sidebar-accent: 210 40% 96%;
|
||||
--sidebar-accent-foreground: 222 47% 11%;
|
||||
--sidebar-ring: 217 91% 60%;
|
||||
|
||||
--app-font-sans: "IBM Plex Sans Arabic", "Inter", system-ui, sans-serif;
|
||||
--app-font-serif: Georgia, serif;
|
||||
--app-font-mono: Menlo, monospace;
|
||||
--app-font-mono: "IBM Plex Mono", Menlo, monospace;
|
||||
--radius: 1rem;
|
||||
}
|
||||
|
||||
@@ -101,22 +102,109 @@
|
||||
@apply border-border;
|
||||
}
|
||||
body {
|
||||
@apply font-sans antialiased bg-background text-foreground;
|
||||
@apply font-sans antialiased text-foreground;
|
||||
background-color: #F8FAFC;
|
||||
}
|
||||
}
|
||||
|
||||
/* Soft glassmorphism — light, very subtle blur */
|
||||
.glass-panel {
|
||||
@apply bg-white/10 dark:bg-black/20 backdrop-blur-xl border border-white/20 dark:border-white/10 shadow-lg;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
backdrop-filter: blur(12px) saturate(140%);
|
||||
-webkit-backdrop-filter: blur(12px) saturate(140%);
|
||||
border: 1px solid #E2E8F0;
|
||||
box-shadow: 0 8px 30px rgba(15, 23, 42, 0.05);
|
||||
}
|
||||
|
||||
/* Light gradient background with floating blobs (Apple / SaaS style) */
|
||||
.os-bg {
|
||||
background: linear-gradient(135deg, hsl(230, 45%, 15%), hsl(195, 45%, 12%), hsl(280, 45%, 15%));
|
||||
background-size: 400% 400%;
|
||||
animation: gradientBG 15s ease infinite;
|
||||
position: relative;
|
||||
background-color: #F8FAFC;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
@keyframes gradientBG {
|
||||
0% { background-position: 0% 50%; }
|
||||
50% { background-position: 100% 50%; }
|
||||
100% { background-position: 0% 50%; }
|
||||
.os-bg::before,
|
||||
.os-bg::after {
|
||||
content: "";
|
||||
position: fixed;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
border-radius: 9999px;
|
||||
filter: blur(80px);
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.os-bg::before {
|
||||
width: 520px;
|
||||
height: 520px;
|
||||
top: -160px;
|
||||
left: -120px;
|
||||
background: radial-gradient(circle at 30% 30%, #C7D2FE 0%, #A5B4FC 40%, transparent 70%);
|
||||
animation: blobFloat 18s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.os-bg::after {
|
||||
width: 560px;
|
||||
height: 560px;
|
||||
bottom: -200px;
|
||||
right: -160px;
|
||||
background: radial-gradient(circle at 60% 60%, #A7F3D0 0%, #99F6E4 35%, transparent 70%);
|
||||
animation: blobFloat 22s ease-in-out infinite alternate-reverse;
|
||||
}
|
||||
|
||||
.os-bg > * {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
@keyframes blobFloat {
|
||||
0% { transform: translate(0, 0) scale(1); }
|
||||
50% { transform: translate(30px, -20px) scale(1.05); }
|
||||
100% { transform: translate(-20px, 25px) scale(0.98); }
|
||||
}
|
||||
|
||||
/* Gradient icon tile helpers */
|
||||
.icon-tile {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 6px 18px rgba(59, 130, 246, 0.18);
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
.icon-tile:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 24px rgba(59, 130, 246, 0.22);
|
||||
}
|
||||
.icon-tile-blue { background: linear-gradient(135deg, #60A5FA, #3B82F6); }
|
||||
.icon-tile-purple { background: linear-gradient(135deg, #C084FC, #8B5CF6); }
|
||||
.icon-tile-green { background: linear-gradient(135deg, #34D399, #10B981); }
|
||||
.icon-tile-orange { background: linear-gradient(135deg, #FDBA74, #F59E0B); }
|
||||
.icon-tile-pink { background: linear-gradient(135deg, #F9A8D4, #EC4899); }
|
||||
.icon-tile-teal { background: linear-gradient(135deg, #5EEAD4, #14B8A6); }
|
||||
|
||||
/* Top bar variant — flat very-light glass, no heavy border */
|
||||
.topbar-glass {
|
||||
background: rgba(255, 255, 255, 0.75);
|
||||
backdrop-filter: blur(16px) saturate(160%);
|
||||
-webkit-backdrop-filter: blur(16px) saturate(160%);
|
||||
border-bottom: 1px solid rgba(226, 232, 240, 0.7);
|
||||
}
|
||||
|
||||
/* Floating dock — macOS-like but lighter */
|
||||
.dock-glass {
|
||||
background: rgba(255, 255, 255, 0.65);
|
||||
backdrop-filter: blur(20px) saturate(180%);
|
||||
-webkit-backdrop-filter: blur(20px) saturate(180%);
|
||||
border: 1px solid rgba(226, 232, 240, 0.9);
|
||||
box-shadow: 0 12px 40px rgba(15, 23, 42, 0.08);
|
||||
}
|
||||
|
||||
/* Card hover lift */
|
||||
.card-hover {
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
.card-hover:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 14px 36px rgba(15, 23, 42, 0.08);
|
||||
}
|
||||
|
||||
@@ -159,10 +159,10 @@ export default function AdminPage() {
|
||||
return (
|
||||
<div className="min-h-screen os-bg flex flex-col">
|
||||
{/* Header */}
|
||||
<div className="glass-panel border-b border-white/10 px-4 py-4 flex items-center gap-3 sticky top-0 z-10">
|
||||
<div className="glass-panel border-b border-slate-200/70 px-4 py-4 flex items-center gap-3 sticky top-0 z-10">
|
||||
<button
|
||||
onClick={() => setLocation("/")}
|
||||
className="p-2 rounded-xl hover:bg-white/10 text-muted-foreground hover:text-foreground transition-colors"
|
||||
className="p-2 rounded-xl hover:bg-slate-100 text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<BackIcon size={20} />
|
||||
</button>
|
||||
@@ -174,7 +174,7 @@ export default function AdminPage() {
|
||||
|
||||
{/* Edit App Modal */}
|
||||
{editingApp && (
|
||||
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4">
|
||||
<div className="fixed inset-0 bg-slate-900/30 backdrop-blur-sm z-50 flex items-center justify-center p-4">
|
||||
<div className="glass-panel rounded-3xl p-6 w-full max-w-sm space-y-4 max-h-[90vh] overflow-y-auto">
|
||||
<h2 className="font-semibold text-foreground">
|
||||
{editingApp.id ? t("admin.editApp") : t("admin.addApp")}
|
||||
@@ -194,7 +194,7 @@ export default function AdminPage() {
|
||||
<Input
|
||||
value={String(editingApp.form[field])}
|
||||
onChange={(e) => setEditingApp({ ...editingApp, form: { ...editingApp.form, [field]: e.target.value } })}
|
||||
className="bg-white/10 border-white/20"
|
||||
className="bg-white/70 border-slate-200"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
@@ -208,7 +208,7 @@ export default function AdminPage() {
|
||||
|
||||
{/* Edit Service Modal */}
|
||||
{editingService && (
|
||||
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4">
|
||||
<div className="fixed inset-0 bg-slate-900/30 backdrop-blur-sm z-50 flex items-center justify-center p-4">
|
||||
<div className="glass-panel rounded-3xl p-6 w-full max-w-sm space-y-4 max-h-[90vh] overflow-y-auto">
|
||||
<h2 className="font-semibold text-foreground">
|
||||
{editingService.id ? t("admin.editService") : t("admin.addService")}
|
||||
@@ -227,7 +227,7 @@ export default function AdminPage() {
|
||||
<Input
|
||||
value={String(editingService.form[field])}
|
||||
onChange={(e) => setEditingService({ ...editingService, form: { ...editingService.form, [field]: e.target.value } })}
|
||||
className="bg-white/10 border-white/20"
|
||||
className="bg-white/70 border-slate-200"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
@@ -248,7 +248,7 @@ export default function AdminPage() {
|
||||
|
||||
{/* Create User Modal */}
|
||||
{newUserForm && (
|
||||
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4">
|
||||
<div className="fixed inset-0 bg-slate-900/30 backdrop-blur-sm z-50 flex items-center justify-center p-4">
|
||||
<div className="glass-panel rounded-3xl p-6 w-full max-w-sm space-y-4">
|
||||
<h2 className="font-semibold text-foreground">{t("admin.addUser")}</h2>
|
||||
<div className="space-y-1">
|
||||
@@ -256,7 +256,7 @@ export default function AdminPage() {
|
||||
<Input
|
||||
value={newUserForm.username}
|
||||
onChange={(e) => setNewUserForm({ ...newUserForm, username: e.target.value })}
|
||||
className="bg-white/10 border-white/20"
|
||||
className="bg-white/70 border-slate-200"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
@@ -265,7 +265,7 @@ export default function AdminPage() {
|
||||
type="email"
|
||||
value={newUserForm.email}
|
||||
onChange={(e) => setNewUserForm({ ...newUserForm, email: e.target.value })}
|
||||
className="bg-white/10 border-white/20"
|
||||
className="bg-white/70 border-slate-200"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
@@ -274,7 +274,7 @@ export default function AdminPage() {
|
||||
type="password"
|
||||
value={newUserForm.password}
|
||||
onChange={(e) => setNewUserForm({ ...newUserForm, password: e.target.value })}
|
||||
className="bg-white/10 border-white/20"
|
||||
className="bg-white/70 border-slate-200"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
@@ -338,7 +338,7 @@ export default function AdminPage() {
|
||||
sortOrder: app.sortOrder ?? 0,
|
||||
},
|
||||
})}
|
||||
className="p-1.5 hover:bg-white/10 rounded-lg text-muted-foreground hover:text-foreground"
|
||||
className="p-1.5 hover:bg-slate-100 rounded-lg text-muted-foreground hover:text-foreground"
|
||||
>
|
||||
<Pencil size={14} />
|
||||
</button>
|
||||
@@ -398,7 +398,7 @@ export default function AdminPage() {
|
||||
isAvailable: service.isAvailable ?? true,
|
||||
},
|
||||
})}
|
||||
className="p-1.5 hover:bg-white/10 rounded-lg text-muted-foreground hover:text-foreground"
|
||||
className="p-1.5 hover:bg-slate-100 rounded-lg text-muted-foreground hover:text-foreground"
|
||||
>
|
||||
<Pencil size={14} />
|
||||
</button>
|
||||
|
||||
@@ -162,10 +162,10 @@ export default function ChatPage() {
|
||||
return (
|
||||
<div className="min-h-screen os-bg flex flex-col">
|
||||
{/* Header */}
|
||||
<div className="glass-panel border-b border-white/10 px-4 py-4 flex items-center gap-3 sticky top-0 z-10">
|
||||
<div className="glass-panel border-b border-slate-200/70 px-4 py-4 flex items-center gap-3 sticky top-0 z-10">
|
||||
<button
|
||||
onClick={() => (selectedConvId ? setSelectedConvId(null) : setLocation("/"))}
|
||||
className="p-2 rounded-xl hover:bg-white/10 text-muted-foreground hover:text-foreground transition-colors"
|
||||
className="p-2 rounded-xl hover:bg-slate-100 text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<BackIcon size={20} />
|
||||
</button>
|
||||
@@ -178,7 +178,7 @@ export default function ChatPage() {
|
||||
{!selectedConvId && (
|
||||
<button
|
||||
onClick={() => setShowNewConv(true)}
|
||||
className="p-2 rounded-xl hover:bg-white/10 text-muted-foreground hover:text-foreground transition-colors"
|
||||
className="p-2 rounded-xl hover:bg-slate-100 text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<Plus size={20} />
|
||||
</button>
|
||||
@@ -187,14 +187,14 @@ export default function ChatPage() {
|
||||
|
||||
{/* New Conversation Dialog */}
|
||||
{showNewConv && (
|
||||
<div className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4">
|
||||
<div className="fixed inset-0 bg-slate-900/30 backdrop-blur-sm z-50 flex items-center justify-center p-4">
|
||||
<div className="glass-panel rounded-3xl p-6 w-full max-w-sm space-y-4">
|
||||
<h2 className="font-semibold text-foreground">{t("chat.createConversation")}</h2>
|
||||
<p className="text-sm text-muted-foreground">{t("chat.searchUsers")}</p>
|
||||
<ScrollArea className="h-48">
|
||||
<div className="space-y-2">
|
||||
{users?.filter((u) => u.id !== user?.id).map((u) => (
|
||||
<label key={u.id} className="flex items-center gap-3 p-2 rounded-xl hover:bg-white/10 cursor-pointer">
|
||||
<label key={u.id} className="flex items-center gap-3 p-2 rounded-xl hover:bg-slate-100 cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={selectedUserIds.includes(u.id)}
|
||||
@@ -279,14 +279,14 @@ export default function ChatPage() {
|
||||
</ScrollArea>
|
||||
|
||||
{/* Message Input */}
|
||||
<div className="glass-panel border-t border-white/10 p-4">
|
||||
<div className="glass-panel border-t border-slate-200/70 p-4">
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
value={messageText}
|
||||
onChange={(e) => setMessageText(e.target.value)}
|
||||
onKeyDown={(e) => e.key === "Enter" && !e.shiftKey && (e.preventDefault(), handleSend())}
|
||||
placeholder={t("chat.typeMessage")}
|
||||
className="flex-1 bg-white/10 border-white/20"
|
||||
className="flex-1 bg-white/70 border-slate-200"
|
||||
/>
|
||||
<Button
|
||||
onClick={handleSend}
|
||||
@@ -326,7 +326,7 @@ export default function ChatPage() {
|
||||
<button
|
||||
key={conv.id}
|
||||
onClick={() => setSelectedConvId(conv.id)}
|
||||
className="w-full glass-panel rounded-2xl p-4 flex items-center gap-3 hover:bg-white/10 transition-colors text-start"
|
||||
className="w-full glass-panel rounded-2xl p-4 flex items-center gap-3 hover:bg-slate-100 transition-colors text-start"
|
||||
>
|
||||
<Avatar className="w-10 h-10 shrink-0">
|
||||
<AvatarFallback className="bg-primary/20 text-primary font-medium">
|
||||
|
||||
@@ -18,6 +18,16 @@ import { Bell, LogOut, Globe } from "lucide-react";
|
||||
import * as LucideIcons from "lucide-react";
|
||||
import i18n from "@/i18n";
|
||||
|
||||
function gradientForColor(color: string): string {
|
||||
const c = (color ?? "").toLowerCase();
|
||||
if (c.includes("a855f7") || c.includes("8b5cf6") || c.includes("purple") || c.includes("violet")) return "icon-tile-purple";
|
||||
if (c.includes("10b981") || c.includes("22c55e") || c.includes("green") || c.includes("emerald")) return "icon-tile-green";
|
||||
if (c.includes("f59e0b") || c.includes("f97316") || c.includes("orange") || c.includes("amber")) return "icon-tile-orange";
|
||||
if (c.includes("ec4899") || c.includes("pink") || c.includes("rose")) return "icon-tile-pink";
|
||||
if (c.includes("14b8a6") || c.includes("06b6d4") || c.includes("teal") || c.includes("cyan")) return "icon-tile-teal";
|
||||
return "icon-tile-blue";
|
||||
}
|
||||
|
||||
function AppIcon({ app, onClick }: { app: { nameAr: string; nameEn: string; iconName: string; color: string; route: string }; onClick: () => void }) {
|
||||
const { i18n: i18nInstance } = useTranslation();
|
||||
const lang = i18nInstance.language;
|
||||
@@ -31,11 +41,8 @@ function AppIcon({ app, onClick }: { app: { nameAr: string; nameEn: string; icon
|
||||
onClick={onClick}
|
||||
className="flex flex-col items-center gap-2 group"
|
||||
>
|
||||
<div
|
||||
className="w-16 h-16 rounded-2xl flex items-center justify-center glass-panel shadow-lg group-hover:scale-110 transition-transform duration-200"
|
||||
style={{ backgroundColor: `${app.color}30`, borderColor: `${app.color}50` }}
|
||||
>
|
||||
<Icon size={28} color={app.color} />
|
||||
<div className={`icon-tile w-16 h-16 ${gradientForColor(app.color)} group-hover:scale-105`}>
|
||||
<Icon size={28} color="#FFFFFF" />
|
||||
</div>
|
||||
<span className="text-xs text-foreground/80 font-medium text-center leading-tight max-w-16 truncate">
|
||||
{name}
|
||||
@@ -96,7 +103,7 @@ export default function HomePage() {
|
||||
return (
|
||||
<div className="min-h-screen os-bg flex flex-col overflow-hidden relative">
|
||||
{/* Status Bar */}
|
||||
<div className="glass-panel border-b border-white/10 px-4 py-3 flex items-center justify-between z-10 sticky top-0">
|
||||
<div className="topbar-glass px-4 py-3 flex items-center justify-between z-10 sticky top-0">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-foreground font-medium text-sm truncate max-w-32">{displayName}</span>
|
||||
</div>
|
||||
@@ -111,13 +118,13 @@ export default function HomePage() {
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={toggleLanguage}
|
||||
className="text-muted-foreground hover:text-foreground transition-colors text-xs font-medium px-2 py-1 rounded-lg hover:bg-white/10"
|
||||
className="text-muted-foreground hover:text-foreground transition-colors text-xs font-medium px-2 py-1 rounded-lg hover:bg-slate-100"
|
||||
>
|
||||
{t("common.language")}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setLocation("/notifications")}
|
||||
className="relative p-1.5 rounded-lg hover:bg-white/10 text-muted-foreground hover:text-foreground transition-colors"
|
||||
className="relative p-1.5 rounded-lg hover:bg-slate-100 text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<Bell size={18} />
|
||||
{unreadCount > 0 && (
|
||||
@@ -128,7 +135,7 @@ export default function HomePage() {
|
||||
</button>
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="p-1.5 rounded-lg hover:bg-white/10 text-muted-foreground hover:text-foreground transition-colors"
|
||||
className="p-1.5 rounded-lg hover:bg-slate-100 text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<LogOut size={18} />
|
||||
</button>
|
||||
@@ -182,7 +189,7 @@ export default function HomePage() {
|
||||
|
||||
{/* Bottom Dock */}
|
||||
<div className="fixed bottom-0 left-0 right-0 p-4 z-10">
|
||||
<div className="glass-panel rounded-3xl px-6 py-3 flex items-center justify-around max-w-sm mx-auto">
|
||||
<div className="dock-glass rounded-3xl px-6 py-3 flex items-center justify-around max-w-sm mx-auto">
|
||||
{dockApps.slice(0, 4).map((app) => {
|
||||
const Icon = (LucideIcons as unknown as Record<string, React.ComponentType<{ size?: number; color?: string }>>)[app.iconName]
|
||||
?? LucideIcons.Grid2X2;
|
||||
|
||||
@@ -59,7 +59,7 @@ export default function LoginPage() {
|
||||
value={form.username}
|
||||
onChange={(e) => setForm((f) => ({ ...f, username: e.target.value }))}
|
||||
required
|
||||
className="bg-white/10 border-white/20 text-foreground placeholder:text-muted-foreground"
|
||||
className="bg-white/70 border-slate-200 text-foreground placeholder:text-muted-foreground"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -71,7 +71,7 @@ export default function LoginPage() {
|
||||
value={form.password}
|
||||
onChange={(e) => setForm((f) => ({ ...f, password: e.target.value }))}
|
||||
required
|
||||
className="bg-white/10 border-white/20 text-foreground placeholder:text-muted-foreground"
|
||||
className="bg-white/70 border-slate-200 text-foreground placeholder:text-muted-foreground"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -51,11 +51,11 @@ export default function NotificationsPage() {
|
||||
return (
|
||||
<div className="min-h-screen os-bg flex flex-col">
|
||||
{/* Header */}
|
||||
<div className="glass-panel border-b border-white/10 px-4 py-4 flex items-center justify-between sticky top-0 z-10">
|
||||
<div className="glass-panel border-b border-slate-200/70 px-4 py-4 flex items-center justify-between sticky top-0 z-10">
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
onClick={() => setLocation("/")}
|
||||
className="p-2 rounded-xl hover:bg-white/10 text-muted-foreground hover:text-foreground transition-colors"
|
||||
className="p-2 rounded-xl hover:bg-slate-100 text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<BackIcon size={20} />
|
||||
</button>
|
||||
|
||||
@@ -60,7 +60,7 @@ export default function RegisterPage() {
|
||||
value={form.username}
|
||||
onChange={(e) => setForm((f) => ({ ...f, username: e.target.value }))}
|
||||
required
|
||||
className="bg-white/10 border-white/20"
|
||||
className="bg-white/70 border-slate-200"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -72,7 +72,7 @@ export default function RegisterPage() {
|
||||
value={form.email}
|
||||
onChange={(e) => setForm((f) => ({ ...f, email: e.target.value }))}
|
||||
required
|
||||
className="bg-white/10 border-white/20"
|
||||
className="bg-white/70 border-slate-200"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -84,7 +84,7 @@ export default function RegisterPage() {
|
||||
value={form.password}
|
||||
onChange={(e) => setForm((f) => ({ ...f, password: e.target.value }))}
|
||||
required
|
||||
className="bg-white/10 border-white/20"
|
||||
className="bg-white/70 border-slate-200"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -94,7 +94,7 @@ export default function RegisterPage() {
|
||||
id="displayNameAr"
|
||||
value={form.displayNameAr}
|
||||
onChange={(e) => setForm((f) => ({ ...f, displayNameAr: e.target.value }))}
|
||||
className="bg-white/10 border-white/20"
|
||||
className="bg-white/70 border-slate-200"
|
||||
dir="rtl"
|
||||
/>
|
||||
</div>
|
||||
@@ -105,7 +105,7 @@ export default function RegisterPage() {
|
||||
id="displayNameEn"
|
||||
value={form.displayNameEn}
|
||||
onChange={(e) => setForm((f) => ({ ...f, displayNameEn: e.target.value }))}
|
||||
className="bg-white/10 border-white/20"
|
||||
className="bg-white/70 border-slate-200"
|
||||
dir="ltr"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -22,10 +22,10 @@ export default function ServicesPage() {
|
||||
return (
|
||||
<div className="min-h-screen os-bg flex flex-col">
|
||||
{/* Header */}
|
||||
<div className="glass-panel border-b border-white/10 px-4 py-4 flex items-center gap-3 sticky top-0 z-10">
|
||||
<div className="glass-panel border-b border-slate-200/70 px-4 py-4 flex items-center gap-3 sticky top-0 z-10">
|
||||
<button
|
||||
onClick={() => setLocation("/")}
|
||||
className="p-2 rounded-xl hover:bg-white/10 text-muted-foreground hover:text-foreground transition-colors"
|
||||
className="p-2 rounded-xl hover:bg-slate-100 text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<BackIcon size={20} />
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user