Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM, PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source files covering clients, berths, interests/pipeline, documents/EOI, expenses/invoices, email, notifications, dashboard, admin, and client portal. CI/CD via Gitea Actions with Docker builds. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
21 KiB
15 — Design Tokens & CRM Theme
Source of truth: Port Nimara Brand Guidelines (30-page PDF) + CRM-specific extensions for UI states, data visualization, and accessibility.
1. Brand Color Palette (from Guidelines)
1.1 Primary Colors
| Pantone | HEX | RGB | Role in brand |
|---|---|---|---|
| PMS 553 | #1e2844 |
30, 40, 68 | Dark navy — headings, sidebar, dark backgrounds |
| PMS 660 | #3a7bc8 |
58, 123, 200 | Brand blue — logo, primary accent |
| Black | #000000 |
0, 0, 0 | Text, logo variant |
| White | #ffffff |
255, 255, 255 | Backgrounds, reversed logo |
1.2 Secondary Colors
| Pantone | HEX | RGB | Role in brand |
|---|---|---|---|
| PMS 7485 | #dae3c1 |
218, 227, 193 | Sage — soft accent, backgrounds |
| PMS 344 | #add5b3 |
173, 213, 179 | Mint — fresh accent, success hint |
| PMS 5493 | #83aab1 |
131, 170, 177 | Teal — muted accent, secondary info |
| PMS 2725 | #685aa3 |
104, 90, 163 | Purple — premium accent, highlights |
1.3 Brand Tint Ladder (from guidelines — 80%, 60%, 40%, 20% of each primary)
| Base | 80% | 60% | 40% | 20% |
|---|---|---|---|---|
#1e2844 |
#474e66 |
#71768a |
#9ea1af |
#cdcfd6 |
#3a7bc8 |
#6196d3 |
#89b0de |
#b1cbe9 |
#d8e5f4 |
#000000 |
#333333 |
#666666 |
#999999 |
#cccccc |
2. CRM Semantic Color Tokens
These map brand colors to UI purpose. Every component references semantic tokens, never raw hex values.
2.1 Light Mode (default)
/* === BACKGROUNDS === */
--background: #ffffff /* Page background */
--background-secondary: #f8f9fa /* Subtle section backgrounds (cards, sidebars) */
--background-tertiary: #f1f3f5 /* Inset panels, table header rows */
--background-brand: #3a7bc8 /* Brand-colored backgrounds (header bar, CTA buttons) */
--background-brand-dark: #1e2844 /* Dark brand sections (login page, onboarding) */
--background-muted: #d8e5f4 /* PMS 660 at 20% — subtle brand tint */
/* === TEXT === */
--text-primary: #1e2844 /* Primary body text — dark brand base */
--text-secondary: #474e66 /* Secondary/muted text — 80% of PMS 553 */
--text-tertiary: #71768a /* Placeholder text, captions — 60% of PMS 553 */
--text-on-brand: #ffffff /* Text on brand-colored backgrounds */
--text-on-dark: #ffffff /* Text on dark backgrounds */
--text-link: #3a7bc8 /* Hyperlinks — brand blue */
/* === BORDERS === */
--border: #cdcfd6 /* Default border — 20% of PMS 553 */
--border-strong: #9ea1af /* Emphasized border — 40% of PMS 553 */
--border-focus: #3a7bc8 /* Focus ring — brand blue */
--border-brand: #3a7bc8 /* Brand accent borders */
/* === INTERACTIVE === */
--primary: #3a7bc8 /* Primary button, active nav, selected tab */
--primary-hover: #2f6ab5 /* Primary hover (10% darker) */
--primary-active: #255a9e /* Primary pressed */
--primary-foreground: #ffffff /* Text on primary buttons */
--secondary: #1e2844 /* Secondary button fill */
--secondary-hover: #171f35 /* Secondary hover */
--secondary-foreground: #ffffff /* Text on secondary buttons */
--accent: #83aab1 /* Accent highlights — PMS 5493 teal */
--accent-hover: #6f959c /* Accent hover */
--accent-foreground: #ffffff /* Text on accent */
--ghost-hover: #f1f3f5 /* Ghost/outline button hover */
--muted: #f1f3f5 /* Muted/disabled backgrounds */
--muted-foreground: #71768a /* Muted text */
/* === STATUS === */
--success: #2d8a4e /* Confirmed, active, paid, signed */
--success-bg: #e8f5e9 /* Success background tint */
--success-border: #a5d6a7 /* Success border */
--warning: #e6a817 /* Expiring soon, pending, needs attention */
--warning-bg: #fff8e1 /* Warning background tint */
--warning-border: #ffe082 /* Warning border */
--error: #d32f2f /* Overdue, failed, rejected, expired */
--error-bg: #ffebee /* Error background tint */
--error-border: #ef9a9a /* Error border */
--info: #3a7bc8 /* Informational — uses brand blue */
--info-bg: #d8e5f4 /* Info background — brand blue 20% */
--info-border: #89b0de /* Info border — brand blue 60% */
/* === SIDEBAR / NAVIGATION === */
--sidebar-bg: #1e2844 /* Dark brand sidebar */
--sidebar-text: #cdcfd6 /* Sidebar text — 20% of PMS 553 (light) */
--sidebar-text-active: #ffffff /* Active nav item text */
--sidebar-icon: #83aab1 /* Nav icons — teal accent */
--sidebar-icon-active: #3a7bc8 /* Active nav icon — brand blue */
--sidebar-hover: #171f35 /* Sidebar hover background */
--sidebar-active: #3a7bc810 /* Brand blue at 6% opacity — subtle highlight */
--sidebar-divider: #474e66 /* Sidebar section dividers */
/* === DATA VISUALIZATION (6-color sequence) === */
--chart-1: #3a7bc8 /* Brand blue */
--chart-2: #1e2844 /* Dark brand */
--chart-3: #83aab1 /* Teal */
--chart-4: #685aa3 /* Purple */
--chart-5: #add5b3 /* Mint */
--chart-6: #dae3c1 /* Sage */
2.2 Dark Mode
The CRM is primarily a daytime work tool, but dark mode is supported for preference and low-light marina office use.
/* === BACKGROUNDS === */
--background: #131a2c /* Darkened navy base */
--background-secondary: #192239 /* Card/sidebar backgrounds */
--background-tertiary: #1e2844 /* PMS 553 as surface */
--background-brand: #3a7bc8 /* Brand blue stays consistent */
--background-brand-dark: #101625 /* Even darker navy */
/* === TEXT === */
--text-primary: #e8ece9 /* Light text on dark backgrounds */
--text-secondary: #9ea1af /* Secondary text */
--text-tertiary: #71768a /* Muted text */
--text-on-brand: #ffffff
--text-link: #6196d3 /* Lightened brand blue for readability */
/* === BORDERS === */
--border: #2d3c66 /* Subtle dark border */
--border-strong: #474e66 /* Emphasized */
--border-focus: #6196d3 /* Focus ring */
/* === INTERACTIVE === */
--primary: #4a8ad4 /* Slightly lightened for dark bg contrast */
--primary-hover: #6196d3
--primary-active: #3a7bc8
/* === STATUS (brightened for dark mode readability) === */
--success: #4caf50
--success-bg: #1b3d1e
--warning: #ffca28
--warning-bg: #3d3417
--error: #ef5350
--error-bg: #3d1a1a
--info: #6196d3
--info-bg: #1a2d3d
3. Typography
3.1 Brand Fonts (from guidelines)
| Role | Font | License | Notes |
|---|---|---|---|
| Primary | Bill Corporate | Commercial (MyFonts) | Outward-facing: marketing, website, PDFs, printed correspondence |
| Secondary | Adobe Garamond | Commercial (Adobe) | Captions only, outward-facing |
| Default (in-house) | Arial / Georgia | System fonts | Word documents, emails, internal communications |
3.2 CRM Font Strategy
The CRM is an internal tool — not outward-facing marketing collateral. Per the brand guidelines, Arial and Georgia are the approved default typefaces for in-house communications.
For a modern web application, we use Inter as the primary UI font. Inter is the de facto standard for web applications (used by Vercel, GitHub, Linear, etc.), is visually close to Arial (sans-serif, clean, neutral), and has excellent screen readability at all sizes. It's also the default font recommended by shadcn/ui.
| CRM Role | Font | Fallback | Notes |
|---|---|---|---|
| UI (body, labels, buttons) | Inter | system-ui, -apple-system, Arial, sans-serif |
Google Fonts or self-hosted |
| Headings (page titles, section headers) | Inter | Same fallback chain | Semi-bold (600) or Bold (700) weight |
| Data (tables, numbers, code) | Inter Tight or JetBrains Mono |
ui-monospace, monospace |
For tabular data alignment, code snippets |
| Generated PDFs | Arial | Helvetica, sans-serif | Brand-compliant for generated letters, invoices |
| Generated formal documents | Georgia | Times New Roman, serif |
Brand-compliant for formal correspondence |
3.3 Type Scale (Tailwind classes)
/* Using Tailwind's default scale with Inter */
text-xs: 0.75rem / 1rem /* 12px — fine print, timestamps */
text-sm: 0.875rem / 1.25rem /* 14px — table cells, secondary info, form labels */
text-base: 1rem / 1.5rem /* 16px — body text, descriptions */
text-lg: 1.125rem / 1.75rem /* 18px — card titles, sub-headings */
text-xl: 1.25rem / 1.75rem /* 20px — section headings */
text-2xl: 1.5rem / 2rem /* 24px — page titles */
text-3xl: 1.875rem / 2.25rem /* 30px — dashboard hero numbers */
3.4 Font Weights
font-normal: 400 /* Body text, descriptions */
font-medium: 500 /* Labels, table headers, nav items */
font-semibold: 600 /* Section headings, card titles, important values */
font-bold: 700 /* Page titles, dashboard hero numbers */
4. Spacing & Layout
4.1 Base Grid
The CRM uses Tailwind's default 4px base unit system. Key spacing tokens:
space-1: 0.25rem (4px) /* Tight padding (badge internal) */
space-2: 0.5rem (8px) /* Compact spacing (between inline elements) */
space-3: 0.75rem (12px) /* Form field internal padding */
space-4: 1rem (16px) /* Standard padding (cards, sections) */
space-5: 1.25rem (20px) /* Comfortable gaps */
space-6: 1.5rem (24px) /* Section separators */
space-8: 2rem (32px) /* Major section gaps */
space-10: 2.5rem (40px) /* Page-level margins */
space-12: 3rem (48px) /* Dashboard widget gaps */
4.2 Container Widths
max-w-screen-sm: 640px /* Login page, modal content */
max-w-screen-md: 768px /* Narrow forms, PWA scanner */
max-w-screen-lg: 1024px /* Standard content area */
max-w-screen-xl: 1280px /* Wide tables, dashboard */
max-w-screen-2xl: 1536px /* Full-width admin views */
4.3 Sidebar
sidebar-width-collapsed: 64px /* Icon-only sidebar */
sidebar-width-expanded: 256px /* Full sidebar with labels */
sidebar-transition: 200ms ease-in-out
5. Border Radius
Rounded corners give the maritime/luxury feel without being overly playful.
rounded-sm: 0.25rem (4px) /* Subtle rounding — tags, badges */
rounded: 0.375rem (6px) /* Default — buttons, inputs, cards */
rounded-md: 0.5rem (8px) /* Slightly more — dialogs, dropdowns */
rounded-lg: 0.75rem (12px) /* Prominent — dashboard cards, modals */
rounded-xl: 1rem (16px) /* Feature cards, hero elements */
rounded-full: 9999px /* Avatars, status dots, icon buttons */
6. Shadows & Elevation
Three shadow levels, using the dark brand color for a cohesive feel:
shadow-sm: 0 1px 2px rgba(30, 40, 68, 0.06) /* Subtle lift — cards at rest */
shadow: 0 1px 3px rgba(30, 40, 68, 0.10), 0 1px 2px rgba(30, 40, 68, 0.06) /* Default — raised cards, buttons */
shadow-md: 0 4px 6px rgba(30, 40, 68, 0.10), 0 2px 4px rgba(30, 40, 68, 0.06) /* Hover state, dropdowns */
shadow-lg: 0 10px 15px rgba(30, 40, 68, 0.10), 0 4px 6px rgba(30, 40, 68, 0.05) /* Modals, dialogs, sheets */
shadow-xl: 0 20px 25px rgba(30, 40, 68, 0.10), 0 8px 10px rgba(30, 40, 68, 0.04) /* Toast notifications */
7. Wave Pattern (CSS)
The brand's wave pattern (from guidelines p18-19) can be subtly referenced in the UI:
/* Subtle wave divider — used on login page, onboarding, section breaks */
.wave-divider {
background-image: url('data:image/svg+xml,...'); /* SVG wave in brand blue */
background-repeat: repeat-x;
height: 24px;
opacity: 0.15;
}
/* Wave watermark — login page background */
.wave-watermark {
background-image: repeating-linear-gradient(
135deg,
transparent,
transparent 10px,
rgba(58, 123, 200, 0.03) 10px,
rgba(58, 123, 200, 0.03) 20px
);
}
8. Component-Specific Tokens
8.1 Berth Status Colors
These are critical CRM semantics — each berth status gets a distinct color from the brand palette:
| Status | Color | HEX | Token |
|---|---|---|---|
| Available | Mint (PMS 344) | #add5b3 |
--berth-available |
| Occupied | Brand blue (PMS 660) | #3a7bc8 |
--berth-occupied |
| Reserved | Purple (PMS 2725) | #685aa3 |
--berth-reserved |
| Maintenance | Warning amber | #e6a817 |
--berth-maintenance |
| Unavailable | Muted grey | #999999 |
--berth-unavailable |
8.2 EOI / Interest Stage Colors
| Stage | Color | HEX |
|---|---|---|
| Lead | Sage (PMS 7485) | #dae3c1 |
| Contacted | Teal (PMS 5493) | #83aab1 |
| Qualified | Brand blue (PMS 660) | #3a7bc8 |
| Negotiating | Purple (PMS 2725) | #685aa3 |
| Won | Success green | #2d8a4e |
| Lost | Error red | #d32f2f |
| On Hold | Warning amber | #e6a817 |
8.3 Priority / Urgency Badges
| Level | Color | Text color |
|---|---|---|
| Low | #dae3c1 (sage) |
#1e2844 (dark) |
| Medium | #d8e5f4 (brand blue 20%) |
#3a7bc8 |
| High | #fff8e1 (warning bg) |
#e6a817 |
| Critical | #ffebee (error bg) |
#d32f2f |
9. Tailwind Config (actual tailwind.config.ts)
import type { Config } from 'tailwindcss';
export default {
darkMode: 'class',
content: ['./src/**/*.{ts,tsx}'],
theme: {
extend: {
colors: {
// Brand primaries
brand: {
DEFAULT: '#3a7bc8', // PMS 660 — primary blue
dark: '#1e2844', // PMS 553 — dark base
50: '#d8e5f4', // 20%
100: '#b1cbe9', // 40%
200: '#89b0de', // 60%
300: '#6196d3', // 80%
400: '#3a7bc8', // 100%
500: '#2f6ab5', // hover
600: '#255a9e', // active
700: '#1c4a87', // dark
},
navy: {
DEFAULT: '#1e2844', // PMS 553
50: '#cdcfd6', // 20%
100: '#9ea1af', // 40%
200: '#71768a', // 60%
300: '#474e66', // 80%
400: '#1e2844', // 100%
500: '#171f35', // darker (hover)
600: '#101625', // darkest
},
// Secondary palette
sage: {
DEFAULT: '#dae3c1', // PMS 7485
light: '#edf1e2',
dark: '#b8c49e',
},
mint: {
DEFAULT: '#add5b3', // PMS 344
light: '#d6ead9',
dark: '#7dba85',
},
teal: {
DEFAULT: '#83aab1', // PMS 5493
light: '#b1cdd2',
dark: '#5a8a92',
},
purple: {
DEFAULT: '#685aa3', // PMS 2725
light: '#a49ac6',
dark: '#4d4280',
},
// Status colors
success: {
DEFAULT: '#2d8a4e',
bg: '#e8f5e9',
border: '#a5d6a7',
},
warning: {
DEFAULT: '#e6a817',
bg: '#fff8e1',
border: '#ffe082',
},
error: {
DEFAULT: '#d32f2f',
bg: '#ffebee',
border: '#ef9a9a',
},
// Sidebar
sidebar: {
DEFAULT: '#1e2844',
text: '#cdcfd6',
hover: '#171f35',
active: '#3a7bc8',
divider: '#474e66',
},
},
fontFamily: {
sans: ['Inter', 'system-ui', '-apple-system', 'Arial', 'sans-serif'],
mono: ['JetBrains Mono', 'ui-monospace', 'monospace'],
// For generated formal documents only
serif: ['Georgia', 'Times New Roman', 'serif'],
},
boxShadow: {
sm: '0 1px 2px rgba(30, 40, 68, 0.06)',
DEFAULT: '0 1px 3px rgba(30, 40, 68, 0.10), 0 1px 2px rgba(30, 40, 68, 0.06)',
md: '0 4px 6px rgba(30, 40, 68, 0.10), 0 2px 4px rgba(30, 40, 68, 0.06)',
lg: '0 10px 15px rgba(30, 40, 68, 0.10), 0 4px 6px rgba(30, 40, 68, 0.05)',
xl: '0 20px 25px rgba(30, 40, 68, 0.10), 0 8px 10px rgba(30, 40, 68, 0.04)',
},
borderRadius: {
sm: '0.25rem',
DEFAULT: '0.375rem',
md: '0.5rem',
lg: '0.75rem',
xl: '1rem',
},
width: {
sidebar: '256px',
'sidebar-collapsed': '64px',
},
},
},
plugins: [require('tailwindcss-animate')],
} satisfies Config;
10. CSS Variables (for shadcn/ui compatibility)
shadcn/ui uses CSS custom properties in HSL format. Here are the Port Nimara brand values converted:
@layer base {
:root {
/* shadcn/ui variable format: H S% L% */
--background: 0 0% 100%; /* #ffffff */
--foreground: 224 39% 19%; /* #1e2844 */
--card: 0 0% 100%;
--card-foreground: 224 39% 19%;
--popover: 0 0% 100%;
--popover-foreground: 224 39% 19%;
--primary: 213 55% 56%; /* #3a7bc8 */
--primary-foreground: 0 0% 100%;
--secondary: 224 39% 19%; /* #1e2844 */
--secondary-foreground: 0 0% 100%;
--muted: 210 11% 96%; /* #f1f3f5 */
--muted-foreground: 228 10% 49%; /* #71768a */
--accent: 190 18% 60%; /* #83aab1 */
--accent-foreground: 0 0% 100%;
--destructive: 0 65% 51%; /* #d32f2f */
--destructive-foreground: 0 0% 100%;
--border: 227 10% 82%; /* #cdcfd6 */
--input: 227 10% 82%;
--ring: 213 55% 56%; /* #3a7bc8 focus ring */
--radius: 0.375rem;
/* Sidebar (using dark navy) */
--sidebar-background: 224 39% 19%;
--sidebar-foreground: 227 10% 82%;
--sidebar-primary: 213 55% 56%;
--sidebar-primary-foreground: 0 0% 100%;
--sidebar-accent: 224 39% 15%;
--sidebar-accent-foreground: 227 10% 82%;
--sidebar-border: 226 18% 34%;
--sidebar-ring: 213 55% 56%;
/* Chart colors for Recharts */
--chart-1: 213 55% 56%; /* Brand blue */
--chart-2: 224 39% 19%; /* Dark navy */
--chart-3: 190 18% 60%; /* Teal */
--chart-4: 254 29% 50%; /* Purple */
--chart-5: 130 30% 76%; /* Mint */
--chart-6: 75 30% 82%; /* Sage */
}
.dark {
--background: 224 40% 12%;
--foreground: 227 10% 91%;
--card: 224 39% 19%;
--card-foreground: 227 10% 91%;
--popover: 224 39% 19%;
--popover-foreground: 227 10% 91%;
--primary: 213 52% 62%;
--primary-foreground: 0 0% 100%;
--secondary: 224 39% 22%;
--secondary-foreground: 227 10% 82%;
--muted: 224 39% 18%;
--muted-foreground: 228 10% 49%;
--accent: 190 18% 50%;
--accent-foreground: 0 0% 100%;
--destructive: 0 72% 63%;
--destructive-foreground: 0 0% 100%;
--border: 224 35% 28%;
--input: 224 35% 28%;
--ring: 213 52% 62%;
}
}
11. Accessibility Notes
- WCAG AA contrast ratios verified for all text/background combinations:
#1e2844on#ffffff→ 14.6:1 (passes AAA)#ffffffon#3a7bc8→ 4.3:1 (passes AA for large text; use semibold 16px+ or 14px bold)#ffffffon#1e2844→ 14.6:1 (passes AAA)#474e66on#ffffff→ 8.2:1 (passes AAA)#71768aon#ffffff→ 4.5:1 (passes AA)
- Focus rings: 2px solid
#3a7bc8with 2px offset — clearly visible on both light and dark backgrounds - Status colors: Never rely on color alone — always pair with icons (checkmark, warning triangle, X circle) and text labels
- Color-blind safe: The berth status palette uses distinct hue families (green, blue, purple, yellow, grey) that remain distinguishable under protanopia, deuteranopia, and tritanopia