style: update

This commit is contained in:
Nathnael
2026-06-10 07:27:42 +00:00
parent 02621d05d8
commit f423fc2e6f
8 changed files with 852 additions and 790 deletions

View File

@@ -1,2 +1,33 @@
@import "tailwindcss";
@import "@edr/ui-common/theme.css" layer(theme);
/* Bridge the central Mantine theme into Tailwind. Mantine (createTheme) is the
single source of truth; these just alias its generated CSS variables so
`bg-edr-*`, `text-edr-*`, `border-edr-*` utilities resolve to the same tokens. */
@theme {
--color-edr-primary: var(--mantine-color-edr-green-5);
--color-edr-primary-dark: var(--mantine-color-edr-green-7);
--color-edr-bg: var(--mantine-color-edr-bg-6);
--color-edr-card: var(--mantine-color-edr-card-6);
--color-edr-border: var(--mantine-color-edr-border-6);
--color-edr-divider: var(--mantine-color-edr-divider-6);
--color-edr-text: var(--mantine-color-edr-text-6);
--color-edr-muted: var(--mantine-color-edr-muted-6);
--color-edr-soft: var(--mantine-color-edr-soft-6);
--color-edr-ink: var(--mantine-color-edr-ink-6);
--color-edr-accent: var(--mantine-color-edr-accent-6);
--color-edr-amber-soft: var(--mantine-color-edr-amber-soft-6);
--color-edr-amber-text: var(--mantine-color-edr-amber-text-6);
--color-edr-blue: var(--mantine-color-edr-blue-6);
--color-edr-blue-soft: var(--mantine-color-edr-blue-soft-6);
--color-edr-blue-dot: var(--mantine-color-edr-blue-dot-6);
--color-edr-red: var(--mantine-color-edr-red-6);
--color-edr-red-soft: var(--mantine-color-edr-red-soft-6);
--color-edr-slate: var(--mantine-color-edr-slate-6);
--color-edr-slate-soft: var(--mantine-color-edr-slate-soft-6);
--color-edr-slate-soft2: var(--mantine-color-edr-slate-soft2-6);
--color-edr-step: var(--mantine-color-edr-step-6);
--color-edr-step-idle: var(--mantine-color-edr-step-idle-6);
--color-edr-conn-idle: var(--mantine-color-edr-conn-idle-6);
}

View File

@@ -8,7 +8,7 @@
<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=Plus+Jakarta+Sans:wght@500;600;700;800&display=swap"
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@600;700&display=swap"
rel="stylesheet"
/>
</head>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

View File

@@ -1,58 +1,55 @@
import {
useNavigate,
useLocation,
Routes,
Route,
Outlet,
} from "react-router-dom";
import { AppLayout, type SidebarItem } from "@/components/AppLayout";
import {
CalendarCheck,
MapPin,
Receipt,
Home,
Loader2,
User,
MapPin,
Receipt,
Settings,
User,
} from "lucide-react";
import {
Outlet,
Route,
Routes,
useLocation,
useNavigate,
} from "react-router-dom";
import useAuth from "./hooks/useAuth";
import { useEffect } from "react";
import EDRFreightLandingPage from "./pages/EDRFreightLandingPage";
import MyPortalPage from "./pages/MyPortalPage";
import ProfilePage from "./pages/ProfilePage";
import SettingsPage from "./pages/SettingsPage";
import MyPortalPage from "./pages/MyPortalPage";
import EDRFreightLandingPage from "./pages/EDRFreightLandingPage";
import SignupPage from "./pages/accounts/SignupPage";
import OnboardingPage from "./pages/accounts/OnboardingPage";
import VerificationOtpPage from "./pages/accounts/VerificationOtpPage";
import SetPasswordPage from "./pages/accounts/SetPasswordPage";
import LoginPage from "./pages/accounts/LoginPage";
import MyBookings from "./pages/bookings/MyBookings";
import OnboardingPage from "./pages/accounts/OnboardingPage";
import SetPasswordPage from "./pages/accounts/SetPasswordPage";
import SignupPage from "./pages/accounts/SignupPage";
import VerificationOtpPage from "./pages/accounts/VerificationOtpPage";
import BillingPage from "./pages/billing/BillingPage";
import BookingContractPage from "./pages/bookings/BookingContractPage";
import BookingDetailPage from "./pages/bookings/BookingDetailPage";
import NewBookingPage from "./pages/bookings/NewBookingPage";
import EditBookingPage from "./pages/bookings/EditBookingPage";
import MyBookings from "./pages/bookings/MyBookings";
import NewBookingPage from "./pages/bookings/NewBookingPage";
import CheckPaymentPage from "./pages/payments/CheckPaymentPage";
import TrackingPage from "./pages/tracking/TrackingPage";
import BillingPage from "./pages/billing/BillingPage";
import { useEffect } from "react";
const sidebarItems: SidebarItem[] = [
{ section: "Overview", label: "Home", href: "/portal", icon: <Home size={18} /> },
{ label: "Home", href: "/portal", icon: <Home size={18} /> },
{
section: "Operations",
label: "My Bookings",
href: "/bookings",
icon: <CalendarCheck size={18} />,
},
{
section: "Operations",
label: "Tracking",
href: "/tracking",
icon: <MapPin size={18} />,
},
{
section: "Operations",
label: "Billing",
href: "/billing",
icon: <Receipt size={18} />,

View File

@@ -1,13 +1,9 @@
import { Fragment, type ReactNode, useState } from "react";
import {
ActionIcon,
AppShell,
Avatar,
Box,
Burger,
Divider,
Group,
Indicator,
Menu,
NavLink,
ScrollArea,
@@ -19,21 +15,20 @@ import { useDisclosure } from "@mantine/hooks";
import {
Bell,
ChevronDown,
ChevronRight,
Languages,
LogOut,
Menu as MenuIcon,
Moon,
Search,
Sun,
Train,
User,
} from "lucide-react";
import { type CSSProperties, Fragment, type ReactNode, useState } from "react";
export interface SidebarItem {
label: string;
href: string;
icon?: ReactNode;
children?: SidebarItem[];
/** Optional group heading; a small label is rendered when it changes. */
section?: string;
}
@@ -52,13 +47,23 @@ export interface AppLayoutProps {
type Theme = "light" | "dark";
const THEME_KEY = "edr-theme";
const EDR = {
primary: "#0EA371",
primaryDark: "#0A6F4D",
soft: "#ECF6F1",
border: "#E6ECF2",
bg: "#F4F7FA",
text: "#10202F",
muted: "#6B7C8E",
ink: "#0C1A2B",
accent: "#F2A516",
};
function getStoredTheme(): Theme {
if (typeof window === "undefined") return "light";
const stored = localStorage.getItem(THEME_KEY);
if (stored === "dark" || stored === "light") return stored;
return window.matchMedia?.("(prefers-color-scheme: dark)").matches
? "dark"
: "light";
return window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light";
}
function getInitials(name: string): string {
@@ -70,23 +75,15 @@ function getInitials(name: string): string {
.join("");
}
function getActivePage(
items: SidebarItem[],
activePath: string,
): { label: string } | null {
function getActivePage(items: SidebarItem[], activePath: string): { label: string } | null {
const path = activePath.toLowerCase();
for (const item of items) {
if (
path === item.href.toLowerCase() ||
path.startsWith(item.href.toLowerCase() + "/")
) {
if (path === item.href.toLowerCase() || path.startsWith(item.href.toLowerCase() + "/")) {
return { label: item.label };
}
if (item.children) {
const childMatch = item.children.find(
(c) =>
path === c.href.toLowerCase() ||
path.startsWith(c.href.toLowerCase() + "/"),
(c) => path === c.href.toLowerCase() || path.startsWith(c.href.toLowerCase() + "/"),
);
if (childMatch) return { label: childMatch.label };
}
@@ -94,22 +91,18 @@ function getActivePage(
return null;
}
// NavLink classNames for the dark sidebar — Tailwind utilities (with v4 `!`
// important suffix) override Mantine's default active styling.
const navClassNames = (active: boolean) => {
const base =
"rounded-[10px] font-medium transition-all duration-150 active:scale-[0.98]";
if (active) {
return {
root: `${base} bg-gradient-to-br! from-emerald-600! to-emerald-500! text-white! shadow-[0_2px_8px_-4px_rgba(16,185,129,0.45)]`,
label: "text-white!",
section: "text-white!",
root: `rounded-[10px] font-medium transition-all duration-150 bg-[#ECF6F1]! ring-1 ring-inset ring-[#0EA371]/15`,
label: `text-[#0A6F4D]! font-bold!`,
section: `text-[#0A6F4D]!`,
};
}
return {
root: `${base} text-white/60! hover:bg-white/[0.07]! hover:text-white!`,
label: "text-inherit!",
section: "text-inherit! opacity-90",
root: `rounded-[10px] font-medium transition-all duration-150 hover:bg-[#F1F4F7]!`,
label: `text-[#3D4D5C]! font-semibold! hover:text-[#0C1A2B]!`,
section: `text-[#54657A]! hover:text-[#0C1A2B]!`,
};
};
@@ -146,153 +139,142 @@ export function AppLayout({
activePath === item.href.toLowerCase() ||
activePath.startsWith(item.href.toLowerCase() + "/");
// Shared header "island" styles — every control is a consistent 36px chip.
const islandStyle: CSSProperties = {
width: 36,
height: 36,
borderRadius: 999,
border: `1px solid ${EDR.border}`,
backgroundColor: "#fff",
display: "flex",
alignItems: "center",
justifyContent: "center",
flexShrink: 0,
cursor: "pointer",
};
const toggleStyle: CSSProperties = { ...islandStyle, borderRadius: 10 };
return (
<AppShell
layout="alt"
navbar={{
width: 264,
breakpoint: "sm",
collapsed: { mobile: !mobileOpen },
}}
header={{ height: 60 }}
navbar={{ width: 260, breakpoint: "sm", collapsed: { mobile: !mobileOpen } }}
header={{ height: 56 }}
padding={0}
>
{/* ── Header ──────────────────────────────────────────────────────────── */}
{/* ── Header (frosted glass; compact floating islands, mirrors the pen) ── */}
<AppShell.Header
withBorder={false}
className="border-b border-[var(--mantine-color-gray-2)]! bg-white/70! backdrop-blur-md backdrop-saturate-150"
style={{
backdropFilter: "blur(14px)",
WebkitBackdropFilter: "blur(14px)",
border: "none",
boxShadow: "none",
background:'transparent',
}}
>
<Group h="100%" px="lg" justify="space-between">
<Group gap="sm">
<Burger
opened={mobileOpen}
onClick={toggleMobile}
hiddenFrom="sm"
size="sm"
/>
<Group gap={6} wrap="nowrap">
<Text
size="sm"
className="font-medium text-[var(--mantine-color-gray-5)]"
visibleFrom="xs"
>
{title}
</Text>
<Box visibleFrom="xs" className="flex">
<ChevronRight size={14} color="var(--mantine-color-gray-4)" />
</Box>
<Text
size="md"
className="font-semibold tracking-tight text-[var(--mantine-color-gray-8)]"
>
{activePage ? activePage.label : title}
</Text>
</Group>
<Group h="100%" px={24} justify="space-between" wrap="nowrap">
{/* Left: sidebar toggle + page title */}
<Group gap={12} wrap="nowrap" align="center">
<UnstyledButton onClick={toggleMobile} style={toggleStyle} aria-label="Toggle sidebar">
<MenuIcon size={18} color={EDR.text} strokeWidth={1.8} />
</UnstyledButton>
<Text
style={{
fontFamily: '"Inter", sans-serif',
fontSize: 16,
fontWeight: 700,
color: EDR.text,
lineHeight: 1,
}}
>
{activePage ? activePage.label : title}
</Text>
</Group>
{/* Right: utility actions + user menu */}
<Group gap={4}>
<ActionIcon
variant="subtle"
color="gray"
size="lg"
radius="md"
className="transition-transform hover:-translate-y-px"
aria-label="Change language"
{/* Right: search + bell + avatar */}
<Group gap={10} wrap="nowrap" align="center">
{/* Search pill */}
<Group
gap={8}
align="center"
visibleFrom="sm"
style={{
width: 260,
height: 36,
borderRadius: 999,
border: `1px solid ${EDR.border}`,
backgroundColor: "#fff",
padding: "0 14px",
cursor: "text",
}}
>
<Languages size={18} />
</ActionIcon>
<Search size={15} color={EDR.muted} strokeWidth={1.8} />
<Text size="sm" style={{ color: EDR.muted, userSelect: "none" }}>
Search shipments, bookings
</Text>
</Group>
<Indicator color="red" size={7} offset={6} zIndex={10} withBorder>
<ActionIcon
variant="subtle"
color="gray"
size="lg"
radius="md"
className="transition-transform hover:-translate-y-px"
aria-label="Notifications"
>
<Bell size={18} />
</ActionIcon>
</Indicator>
{/* Bell */}
<Box style={{ position: "relative" }}>
<UnstyledButton style={islandStyle} aria-label="Notifications">
<Bell size={17} color={EDR.text} strokeWidth={1.8} />
</UnstyledButton>
<Box
style={{
position: "absolute",
top: 7,
right: 7,
width: 7,
height: 7,
borderRadius: "50%",
backgroundColor: EDR.accent,
border: "1.5px solid #fff",
pointerEvents: "none",
}}
/>
</Box>
{enableThemeToggle && (
<ActionIcon
variant="subtle"
color="gray"
size="lg"
radius="md"
className="transition-transform hover:-translate-y-px"
onClick={toggleTheme}
aria-label="Toggle theme"
>
{theme === "dark" ? <Sun size={18} /> : <Moon size={18} />}
</ActionIcon>
<UnstyledButton onClick={toggleTheme} style={islandStyle} aria-label="Toggle theme">
{theme === "dark"
? <Sun size={17} color={EDR.text} strokeWidth={1.8} />
: <Moon size={17} color={EDR.text} strokeWidth={1.8} />}
</UnstyledButton>
)}
<Divider orientation="vertical" my={14} mx={6} />
<Menu
width={232}
position="bottom-end"
withinPortal
shadow="md"
offset={8}
radius="md"
>
{/* Avatar pill */}
<Menu width={220} position="bottom-end" withinPortal shadow="md" offset={8} radius="md">
<Menu.Target>
<UnstyledButton
px={6}
py={4}
className="rounded-[10px] transition-colors hover:bg-[var(--mantine-color-gray-1)]"
style={{
height: 36,
borderRadius: 999,
border: `1px solid ${EDR.border}`,
backgroundColor: "#fff",
padding: "0 10px 0 4px",
display: "flex",
alignItems: "center",
gap: 7,
cursor: "pointer",
}}
>
<Group gap={8} wrap="nowrap">
<Avatar
radius="xl"
size={32}
gradient={{ from: "edr-green.5", to: "edr-green.8", deg: 135 }}
variant="gradient"
>
<Text fw={600} fz={11} c="white">
{initials}
</Text>
</Avatar>
<Box visibleFrom="sm" className="min-w-0">
<Text size="sm" fw={600} maw={120} truncate lh={1.2}>
{userName}
</Text>
<Text size="xs" c="dimmed" lh={1.2}>
Customer
</Text>
</Box>
<ChevronDown size={14} color="var(--mantine-color-gray-5)" />
</Group>
<Avatar radius="xl" size={28} color="edr-green.5">
<Text fw={700} fz={12} c="white">{initials}</Text>
</Avatar>
<ChevronDown size={15} color={EDR.muted} strokeWidth={1.8} />
</UnstyledButton>
</Menu.Target>
<Menu.Dropdown>
<Box px="sm" py="xs">
<Text size="sm" fw={600} truncate>
{userName}
</Text>
{userEmail && (
<Text size="xs" c="dimmed" truncate>
{userEmail}
</Text>
)}
<Text size="sm" fw={600} truncate style={{ color: EDR.text }}>{userName}</Text>
{userEmail && <Text size="xs" c="dimmed" truncate>{userEmail}</Text>}
</Box>
<Divider />
<Menu.Item
leftSection={<User size={15} />}
onClick={() => navigate("/profile")}
>
<Menu.Item leftSection={<User size={15} />} onClick={() => navigate("/profile")}>
Profile
</Menu.Item>
<Menu.Item
leftSection={<LogOut size={15} />}
color="red"
onClick={onLogout}
>
<Menu.Item leftSection={<LogOut size={15} />} color="red" onClick={onLogout}>
Logout
</Menu.Item>
</Menu.Dropdown>
@@ -301,38 +283,67 @@ export function AppLayout({
</Group>
</AppShell.Header>
{/* ── Sidebar ─────────────────────────────────────────────────────────── */}
{/* ── Sidebar ── */}
<AppShell.Navbar
withBorder={false}
className="flex flex-col border-r border-white/[0.06]! bg-gradient-to-b from-[#1a2230] to-[#141a26]"
style={{
backgroundColor: "#ffffff",
borderRight: `1px solid ${EDR.border}`,
display: "flex",
flexDirection: "column",
}}
>
{/* Brand */}
<Box className="flex h-[60px] flex-shrink-0 items-center border-b border-white/[0.07] px-4">
<Group gap="sm" wrap="nowrap">
<Box className="flex h-[34px] w-[34px] flex-shrink-0 items-center justify-center rounded-[10px] bg-gradient-to-br from-emerald-500 to-emerald-700 shadow-[inset_0_1px_0_rgba(255,255,255,0.2)]">
<Train size={18} color="white" strokeWidth={2.2} />
</Box>
<Box
style={{
height: 60,
flexShrink: 0,
display: "flex",
alignItems: "center",
padding: "0 18px",
}}
>
<Group gap={11} wrap="nowrap">
<img
src="/assets/edr-logo.png"
alt="EDR"
style={{ width: 40, height: 40, objectFit: "contain", flexShrink: 0 }}
/>
<Box>
<Text className="font-bold leading-tight tracking-tight text-white" size="md">
{title}
<Text
style={{
fontFamily: '"Inter", sans-serif',
fontWeight: 800,
fontSize: 19,
letterSpacing: "0.03em",
color: EDR.primary,
lineHeight: 1.15,
}}
>
EDR FREIGHT
</Text>
<Text className="text-[10px] font-semibold uppercase tracking-[0.12em] text-white/40">
Logistics Portal
<Text
style={{
fontSize: 10.5,
fontWeight: 500,
color: EDR.muted,
lineHeight: 1.2,
}}
>
EthioDjibouti Railway
</Text>
</Box>
</Group>
</Box>
{/* Nav links */}
{/* Nav */}
<ScrollArea flex={1} type="never" p="sm">
<Stack gap={3}>
{sidebarItems.map((item, i) => {
const active = isItemActive(item);
const hasChildren = !!item.children?.length;
const childActive =
item.children?.some((c) =>
activePath.startsWith(c.href.toLowerCase()),
) ?? false;
item.children?.some((c) => activePath.startsWith(c.href.toLowerCase())) ?? false;
const prevSection = sidebarItems[i - 1]?.section;
const sectionLabel =
@@ -343,8 +354,13 @@ export function AppLayout({
tt="uppercase"
px="sm"
mt={i === 0 ? 4 : "md"}
mb={6}
className="font-bold tracking-[0.1em] text-white/30"
mb={4}
style={{
fontWeight: 600,
letterSpacing: "0.08em",
color: EDR.muted,
fontSize: 11,
}}
>
{item.section}
</Text>
@@ -394,35 +410,118 @@ export function AppLayout({
</Stack>
</ScrollArea>
{/* Bottom user */}
<Box className="m-3 flex-shrink-0 rounded-xl border border-white/[0.07] bg-white/[0.04] p-2.5">
<Group gap="sm" wrap="nowrap">
{/* Promo card */}
<Box style={{ padding: "0 12px 12px" }}>
<Box
style={{
borderRadius: 16,
overflow: "hidden",
position: "relative",
height: 260,
marginBottom: 10,
}}
>
{/* Train image fills the full card */}
<img
src="/assets/train-edr.jpg"
alt=""
style={{
position: "absolute",
bottom:"-16px",
left:0,
right:"-70px",
width: "120%",
objectFit: "cover",
}}
/>
{/* Diagonal green overlay: lower-left → upper-right cut */}
<Box
style={{
position: "absolute",
inset: 0,
top:"-75%",
background: "linear-gradient(165deg, #006149 40%, #0DC9A4 70%, #0DC9A402 80%)",
clipPath: "polygon(0 0, 100% 0, 100% 58%, 0 72%)",
}}
/>
{/* Text sits on top of the green overlay */}
<Box style={{ position: "relative", zIndex: 1, padding: "16px 16px 0" }} className="max-w-[180px]">
<Text style={{ fontSize: 15, fontWeight: 800, color: "#fff", lineHeight: 1.3, marginBottom: 4 }}>
Moving Africa Forward
</Text>
<Text style={{ fontSize: 11, color: "rgba(255,255,255,0.7)", lineHeight: 1.5 }}>
Reliable. Efficient. Connected.
</Text>
</Box>
{/* Learn More — visible on the image area */}
<Box
style={{
position: "absolute",
bottom: 10,
right: 10,
zIndex: 2,
display: "inline-flex",
alignItems: "center",
gap: 5,
backgroundColor: "#fff",
borderRadius: 8,
padding: "6px 12px",
cursor: "pointer",
}}
>
<Text style={{ fontSize: 12, fontWeight: 600, color: EDR.primaryDark }}>Learn More</Text>
<span style={{ fontSize: 12, color: EDR.primaryDark }}></span>
</Box>
</Box>
{/* Profile */}
<Box
style={{
borderRadius: 12,
border: `1px solid ${EDR.border}`,
padding: "10px",
display: "flex",
alignItems: "center",
gap: 10,
cursor: "pointer",
}}
>
<Avatar
radius="xl"
size={34}
gradient={{ from: "edr-green.5", to: "edr-green.8", deg: 135 }}
variant="gradient"
size={38}
color="edr-green.5"
style={{ flexShrink: 0 }}
>
<Text fw={600} fz={12} c="white">
{initials}
</Text>
<Text fw={600} fz={13} c="white">{initials}</Text>
</Avatar>
<Box className="min-w-0">
<Text size="sm" fw={600} truncate className="text-white">
<Box style={{ minWidth: 0, flex: 1 }}>
<Text size="sm" fw={600} truncate style={{ color: EDR.text, lineHeight: 1.3 }}>
{userName}
</Text>
{userEmail && (
<Text size="xs" truncate className="text-white/40">
<Text size="xs" truncate style={{ color: EDR.muted, lineHeight: 1.3 }}>
{userEmail}
</Text>
)}
</Box>
</Group>
<ChevronDown size={16} color={EDR.muted} style={{ flexShrink: 0 }} />
</Box>
</Box>
</AppShell.Navbar>
{/* ── Main ────────────────────────────────────────────────────────────── */}
<AppShell.Main className="bg-[var(--mantine-color-gray-0)]">
{/* ── Main ── */}
<AppShell.Main
style={{
backgroundColor: EDR.bg,
backgroundImage:
"radial-gradient(58% 42% at 100% 0%, rgba(14,163,113,0.16) 0%, rgba(14,163,113,0.06) 38%, rgba(14,163,113,0) 72%)",
backgroundRepeat: "no-repeat",
backgroundAttachment: "fixed",
}}
>
{children}
</AppShell.Main>
</AppShell>

File diff suppressed because it is too large Load Diff

View File

@@ -1,46 +1,71 @@
import { createTheme, type MantineColorsTuple } from "@mantine/core";
import { colorsTuple, createTheme, type MantineColorsTuple } from "@mantine/core";
// Brand accent — used sparingly: primary actions, active nav, key highlights.
const edrGreen: MantineColorsTuple = [
"#ecfdf5",
"#d1fae5",
"#a7f3d0",
"#6ee7b7",
"#34d399",
"#10b981",
"#059669",
"#047857",
"#0EA371",
"#0A8A5F",
"#0A6F4D",
"#065f46",
"#064e3b",
];
// Neutral gray ramp tuned for clean, low-contrast surfaces (Stripe/Notion feel).
const neutral: MantineColorsTuple = [
"#f8fafc",
"#f1f5f9",
"#e8edf2",
"#dbe2ea",
"#c2cbd6",
"#9aa6b4",
"#6b7785",
"#4b5563",
"#2f3742",
"#1c2129",
"#F4F7FA",
"#eef2f7",
"#E6ECF2",
"#d0dae6",
"#b0bfce",
"#8fa0b2",
"#6B7C8E",
"#4b5a6a",
"#10202F",
"#0C1A2B",
];
export const mantineTheme = createTheme({
colors: {
"edr-green": edrGreen,
gray: neutral,
// Brand surface + text tokens (single-value semantic colors).
// Each generates --mantine-color-{name}-{0..9} CSS variables.
"edr-bg": colorsTuple("#F4F7FA"),
"edr-card": colorsTuple("#FFFFFF"),
"edr-border": colorsTuple("#E6ECF2"),
"edr-divider": colorsTuple("#EEF1F5"),
"edr-text": colorsTuple("#10202F"),
"edr-muted": colorsTuple("#6B7C8E"),
"edr-soft": colorsTuple("#ECF6F1"),
"edr-ink": colorsTuple("#0C1A2B"),
"edr-accent": colorsTuple("#F2A516"),
// Semantic status shades (from the design system).
"edr-amber-soft": colorsTuple("#FDF3E0"),
"edr-amber-text": colorsTuple("#9A5B00"),
"edr-blue": colorsTuple("#2E5B96"),
"edr-blue-soft": colorsTuple("#E9F0F8"),
"edr-blue-dot": colorsTuple("#3B6FB0"),
"edr-red": colorsTuple("#C0392B"),
"edr-red-soft": colorsTuple("#FBEAE7"),
"edr-slate": colorsTuple("#475569"),
"edr-slate-soft": colorsTuple("#F1F4F7"),
"edr-slate-soft2": colorsTuple("#EEF2F6"),
"edr-step": colorsTuple("#94A3B8"),
"edr-step-idle": colorsTuple("#D5DBE2"),
"edr-conn-idle": colorsTuple("#E6EAEF"),
},
primaryColor: "edr-green",
primaryShade: { light: 6, dark: 5 },
primaryShade: { light: 5, dark: 4 },
white: "#ffffff",
black: "#1c2129",
black: "#10202F",
fontFamily:
'"Plus Jakarta Sans",ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
'"Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
defaultRadius: "md",
@@ -49,7 +74,7 @@ export const mantineTheme = createTheme({
sm: "6px",
md: "8px",
lg: "12px",
xl: "16px",
xl: "18px",
},
spacing: {
@@ -76,17 +101,13 @@ export const mantineTheme = createTheme({
xl: "1.5",
},
// Hierarchy comes from a strong, exponential size scale (~1.25 modular ratio),
// not from heavy weights. Big steps at the top, calm weights throughout.
// "Plus Jakarta Sans" gives headings a distinctive geometric character while
// body text stays on the neutral system stack.
headings: {
fontFamily: '"Plus Jakarta Sans", var(--mantine-font-family)',
fontWeight: "600",
fontFamily: '"Inter", var(--mantine-font-family)',
fontWeight: "700",
sizes: {
h1: { fontSize: "40px", lineHeight: "1.1", fontWeight: "800" },
h2: { fontSize: "30px", lineHeight: "1.2", fontWeight: "700" },
h3: { fontSize: "23px", lineHeight: "1.3", fontWeight: "600" },
h1: { fontSize: "36px", lineHeight: "1.1", fontWeight: "800" },
h2: { fontSize: "28px", lineHeight: "1.2", fontWeight: "700" },
h3: { fontSize: "22px", lineHeight: "1.3", fontWeight: "600" },
h4: { fontSize: "18px", lineHeight: "1.4", fontWeight: "600" },
h5: { fontSize: "15px", lineHeight: "1.45", fontWeight: "600" },
h6: { fontSize: "13px", lineHeight: "1.45", fontWeight: "600" },
@@ -95,53 +116,39 @@ export const mantineTheme = createTheme({
shadows: {
xs: "0 1px 2px rgba(16, 24, 40, 0.04)",
sm: "0 1px 3px rgba(16, 24, 40, 0.06), 0 1px 2px rgba(16, 24, 40, 0.04)",
sm: "0 1px 3px rgba(16, 24, 40, 0.06)",
md: "0 4px 12px rgba(16, 24, 40, 0.06)",
},
components: {
Card: {
defaultProps: {
radius: "lg",
radius: "xl",
withBorder: true,
shadow: "none",
padding: "lg",
},
styles: {
root: { borderColor: "#E6ECF2" },
},
},
Button: {
defaultProps: {
radius: "md",
},
styles: {
root: { fontWeight: 550 },
},
defaultProps: { radius: "md" },
styles: { root: { fontWeight: 600 } },
},
Badge: {
defaultProps: {
radius: "sm",
variant: "light",
},
styles: {
root: { fontWeight: 550, textTransform: "none" },
},
defaultProps: { radius: "xl", variant: "light" },
styles: { root: { fontWeight: 600, textTransform: "none" } },
},
Paper: {
defaultProps: {
radius: "lg",
shadow: "none",
withBorder: true,
},
defaultProps: { radius: "xl", shadow: "none", withBorder: true },
styles: { root: { borderColor: "#E6ECF2" } },
},
Table: {
defaultProps: {
verticalSpacing: "sm",
horizontalSpacing: "md",
},
defaultProps: { verticalSpacing: "sm", horizontalSpacing: "md" },
},
Title: {
styles: {
root: { letterSpacing: "-0.01em" },
},
styles: { root: { letterSpacing: "-0.01em" } },
},
},
});