Files
pn-new-crm/15-DESIGN-TOKENS.md
Matt 67d7e6e3d5
Some checks failed
Build & Push Docker Images / build-and-push (push) Has been cancelled
Build & Push Docker Images / deploy (push) Has been cancelled
Build & Push Docker Images / lint (push) Has been cancelled
Initial commit: Port Nimara CRM (Layers 0-4)
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>
2026-03-26 11:52:51 +01:00

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:
    • #1e2844 on #ffffff14.6:1 (passes AAA)
    • #ffffff on #3a7bc84.3:1 (passes AA for large text; use semibold 16px+ or 14px bold)
    • #ffffff on #1e284414.6:1 (passes AAA)
    • #474e66 on #ffffff8.2:1 (passes AAA)
    • #71768a on #ffffff4.5:1 (passes AA)
  • Focus rings: 2px solid #3a7bc8 with 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