diff --git a/deploy/k8s/secret.example.yaml b/deploy/k8s/secret.example.yaml index d5471020..d6611c11 100644 --- a/deploy/k8s/secret.example.yaml +++ b/deploy/k8s/secret.example.yaml @@ -10,10 +10,15 @@ stringData: # Core — connect to Stalwart over JMAP JMAP_SERVER_URL: "https://stalwart.sandbox.vnc.de" SESSION_SECRET: "REPLACE_ME__openssl_rand_base64_32" - # Branding + # Branding (theme defaults to VNClagoon in code; these set name + logo) APP_NAME: "VNCmail+" APP_SHORT_NAME: "VNCmail+" - LOGIN_COMPANY_NAME: "VNC" + LOGIN_COMPANY_NAME: "VNClagoon" + LOGIN_LOGO_DARK_URL: "/branding/vncmail-wordmark-on-dark.svg" + LOGIN_LOGO_LIGHT_URL: "/branding/vncmail-wordmark-on-light.svg" + APP_LOGO_DARK_URL: "/branding/vncmail-wordmark-on-dark.svg" + APP_LOGO_LIGHT_URL: "/branding/vncmail-wordmark-on-light.svg" + LOGIN_LOGO_MAX_HEIGHT: "52" # Housekeeping BULWARK_UPDATE_CHECK: "off" # Data dirs default to /app/data/* (mounted to the PVCs) — no need to set them. diff --git a/lib/admin/types.ts b/lib/admin/types.ts index b02a687d..90445203 100644 --- a/lib/admin/types.ts +++ b/lib/admin/types.ts @@ -106,7 +106,7 @@ export interface ThemePolicy { export const DEFAULT_THEME_POLICY: ThemePolicy = { disabledBuiltinThemes: [], disabledThemes: [], - defaultThemeId: null, + defaultThemeId: 'builtin-vnclagoon', }; export interface SettingsPolicy { diff --git a/lib/builtin-themes.ts b/lib/builtin-themes.ts index 0fc940fd..6a7eb510 100644 --- a/lib/builtin-themes.ts +++ b/lib/builtin-themes.ts @@ -874,7 +874,125 @@ body[data-theme-skin="builtin-aurora-glass"] [role="menu"] [role="menuitem"]:foc background-color: rgba(139, 123, 255, 0.22) !important; }`; +// "VNClagoon" — VNC's brand theme. Deep-navy grounds (#0A0E1A) with the +// VNClagoon cyan (#00D4FF) as the single accent, DM Sans for body and Syne for +// display. Dark is the brand-primary variant; the light variant deepens the +// cyan (#00A5CC) so it stays legible on white. Fonts are self-hosted under +// /fonts (OFL) so they render offline / behind the CSP. +const vnclagoonCSS = ` +@font-face { font-family: 'DM Sans'; font-style: normal; font-weight: 400; font-display: swap; src: url('/fonts/dmsans-400.woff2') format('woff2'); } +@font-face { font-family: 'DM Sans'; font-style: normal; font-weight: 500; font-display: swap; src: url('/fonts/dmsans-500.woff2') format('woff2'); } +@font-face { font-family: 'DM Sans'; font-style: normal; font-weight: 700; font-display: swap; src: url('/fonts/dmsans-700.woff2') format('woff2'); } +@font-face { font-family: 'Syne'; font-style: normal; font-weight: 700; font-display: swap; src: url('/fonts/syne-700.woff2') format('woff2'); } +@font-face { font-family: 'Syne'; font-style: normal; font-weight: 800; font-display: swap; src: url('/fonts/syne-800.woff2') format('woff2'); } +:root { + --color-border: #dce3ee; + --color-input: #dce3ee; + --color-ring: #00a5cc; + --color-background: #ffffff; + --color-foreground: #0a0e1a; + --color-primary: #00a5cc; + --color-primary-foreground: #ffffff; + --color-secondary: #eef2f8; + --color-secondary-foreground: #0a0e1a; + --color-muted: #eef2f8; + --color-muted-foreground: #5a6478; + --color-accent: #e3f7fd; + --color-accent-foreground: #006f8c; + --color-destructive: #e5484d; + --color-destructive-foreground: #ffffff; + --color-popover: #ffffff; + --color-popover-foreground: #0a0e1a; + --color-sidebar: #f7f9fc; + --color-sidebar-foreground: #0a0e1a; + --color-sidebar-border: #dce3ee; + --color-sidebar-accent: #eef2f8; + --color-sidebar-accent-foreground: #0a0e1a; + --color-card: #ffffff; + --color-card-foreground: #0a0e1a; + --color-success: #16a34a; + --color-success-foreground: #ffffff; + --color-warning: #ca8a04; + --color-warning-foreground: #ffffff; + --color-info: #00a5cc; + --color-info-foreground: #ffffff; + --color-selection: #e3f7fd; + --color-selection-foreground: #006f8c; + --color-unread: #00a5cc; + --color-chart-1: #00a5cc; + --color-chart-2: #16a34a; + --color-chart-3: #ca8a04; + --color-chart-4: #e5484d; + --color-chart-5: #6d8bff; +} +.dark { + --color-border: #1e2740; + --color-input: #1e2740; + --color-ring: #00d4ff; + --color-background: #0a0e1a; + --color-foreground: #e8ecf4; + --color-primary: #00d4ff; + --color-primary-foreground: #05121b; + --color-secondary: #141b2e; + --color-secondary-foreground: #e8ecf4; + --color-muted: #141b2e; + --color-muted-foreground: #8a97b0; + --color-accent: #10263a; + --color-accent-foreground: #7fe6ff; + --color-destructive: #ff5a5f; + --color-destructive-foreground: #ffffff; + --color-popover: #111726; + --color-popover-foreground: #e8ecf4; + --color-sidebar: #080b14; + --color-sidebar-foreground: #e8ecf4; + --color-sidebar-border: #1e2740; + --color-sidebar-accent: #141b2e; + --color-sidebar-accent-foreground: #e8ecf4; + --color-card: #111726; + --color-card-foreground: #e8ecf4; + --color-success: #22c55e; + --color-success-foreground: #05121b; + --color-warning: #f6c544; + --color-warning-foreground: #05121b; + --color-info: #00d4ff; + --color-info-foreground: #05121b; + --color-selection: rgba(0, 212, 255, 0.20); + --color-selection-foreground: #7fe6ff; + --color-unread: #00d4ff; + --color-chart-1: #00d4ff; + --color-chart-2: #22c55e; + --color-chart-3: #f6c544; + --color-chart-4: #ff5a5f; + --color-chart-5: #6d8bff; +}`; + +// Syne display face on headings; DM Sans everywhere else. Scoped to the skin +// so it detaches cleanly when the theme is switched off. +const vnclagoonSkin = ` +body[data-theme-skin="builtin-vnclagoon"] { + font-family: "DM Sans", system-ui, -apple-system, "Segoe UI", sans-serif; +} +body[data-theme-skin="builtin-vnclagoon"] h1, +body[data-theme-skin="builtin-vnclagoon"] h2, +body[data-theme-skin="builtin-vnclagoon"] h3 { + font-family: "Syne", "DM Sans", sans-serif; + letter-spacing: -0.01em; +}`; + export const BUILTIN_THEMES: InstalledTheme[] = [ + { + id: 'builtin-vnclagoon', + name: 'VNClagoon', + version: '1.0.0', + author: 'VNC', + description: 'VNClagoon brand theme — deep navy with cyan accent, DM Sans + Syne', + css: vnclagoonCSS, + skin: vnclagoonSkin, + variants: ['light', 'dark'], + typography: { fontSans: '"DM Sans", system-ui, -apple-system, "Segoe UI", sans-serif' }, + enabled: true, + builtIn: true, + }, { id: 'builtin-qui', name: 'Qui', diff --git a/public/branding/vncmail-wordmark-on-dark.svg b/public/branding/vncmail-wordmark-on-dark.svg new file mode 100644 index 00000000..1d578bfb --- /dev/null +++ b/public/branding/vncmail-wordmark-on-dark.svg @@ -0,0 +1,8 @@ + + + + VNCmail + + diff --git a/public/branding/vncmail-wordmark-on-light.svg b/public/branding/vncmail-wordmark-on-light.svg new file mode 100644 index 00000000..c11e3a42 --- /dev/null +++ b/public/branding/vncmail-wordmark-on-light.svg @@ -0,0 +1,8 @@ + + + + VNCmail + + diff --git a/public/fonts/dmsans-400.woff2 b/public/fonts/dmsans-400.woff2 new file mode 100644 index 00000000..8f8cb550 Binary files /dev/null and b/public/fonts/dmsans-400.woff2 differ diff --git a/public/fonts/dmsans-500.woff2 b/public/fonts/dmsans-500.woff2 new file mode 100644 index 00000000..8f8cb550 Binary files /dev/null and b/public/fonts/dmsans-500.woff2 differ diff --git a/public/fonts/dmsans-700.woff2 b/public/fonts/dmsans-700.woff2 new file mode 100644 index 00000000..8f8cb550 Binary files /dev/null and b/public/fonts/dmsans-700.woff2 differ diff --git a/public/fonts/syne-700.woff2 b/public/fonts/syne-700.woff2 new file mode 100644 index 00000000..fb292f31 Binary files /dev/null and b/public/fonts/syne-700.woff2 differ diff --git a/public/fonts/syne-800.woff2 b/public/fonts/syne-800.woff2 new file mode 100644 index 00000000..fb292f31 Binary files /dev/null and b/public/fonts/syne-800.woff2 differ diff --git a/stores/theme-store.ts b/stores/theme-store.ts index 1313ca60..8af41746 100644 --- a/stores/theme-store.ts +++ b/stores/theme-store.ts @@ -78,8 +78,8 @@ let themeSyncPromise: Promise | null = null; export const useThemeStore = create()( persist( (set, get) => ({ - theme: 'system', - resolvedTheme: 'light', + theme: 'dark', + resolvedTheme: 'dark', hydrated: false, installedThemes: [...BUILTIN_THEMES], activeThemeId: null, diff --git a/vnc/VNC-CHANGES.md b/vnc/VNC-CHANGES.md index 30c3b6ff..8edbcaf8 100644 --- a/vnc/VNC-CHANGES.md +++ b/vnc/VNC-CHANGES.md @@ -25,6 +25,11 @@ that merging new upstream releases stays a triage exercise, not an archaeology d | 2026-08-03 | `deploy/k8s/` (new) | k8s manifests + runbook for microk8s deploy | Bulwark is stateful → runs as a container w/ persistent volumes, not Vercel serverless | | 2026-08-03 | `.gitignore` | ignore `deploy/k8s/secret.yaml` | keep the real env secret out of git | +| 2026-08-03 | `public/branding/*.svg` (new) | VNCmail wordmark (on-dark + on-light) | VNC logo (placeholder — swap official SVG) | +| 2026-08-03 | `public/fonts/*.woff2` (new) | DM Sans 400/500/700 + Syne 700/800 (OFL, self-hosted) | VNClagoon typography | +| 2026-08-03 | `lib/builtin-themes.ts` | add `builtin-vnclagoon` theme (navy+cyan, DM Sans/Syne, @font-face) | VNClagoon brand theme | +| 2026-08-03 | `lib/admin/types.ts` | `DEFAULT_THEME_POLICY.defaultThemeId` → `builtin-vnclagoon` | make VNClagoon the default theme | +| 2026-08-03 | `stores/theme-store.ts` | default `theme`/`resolvedTheme` → `dark` | dark-first per VNClagoon styleguide | _Note: Vercel was tried and **abandoned** on 2026-08-03. Bulwark writes to a local data dir (`/app/data/*`); Vercel serverless has a read-only filesystem → crash