From 5c0e40db7ea19260978f6ece4707d7b91ef812db Mon Sep 17 00:00:00 2001 From: Jonathan Bourdon Date: Wed, 6 May 2026 14:27:09 -0400 Subject: [PATCH] feat: centralize frontend branding --- .../app-shell/004-global-frontend-branding.md | 26 ++++++++ .../public/images/brand/auth-illustration.svg | 16 +++++ frontend/public/images/brand/logo-mark.svg | 11 ++++ frontend/public/images/brand/logo.svg | 12 ++++ frontend/src/branding/applyBranding.js | 63 +++++++++++++++++++ frontend/src/branding/branding.js | 50 +++++++++++++++ .../src/components/branding/BrandLogo.vue | 35 +++++++++++ .../src/components/branding/BrandMark.vue | 26 ++++++++ .../src/features/auth/views/LoginView.vue | 17 ++--- .../src/features/auth/views/RegisterView.vue | 15 +++-- frontend/src/layouts/main/AppSidebar.vue | 12 ++-- frontend/src/main.js | 16 ++--- .../src/static/components/LandingSiteMenu.vue | 12 ++-- frontend/tailwind.config.js | 3 - 14 files changed, 272 insertions(+), 42 deletions(-) create mode 100644 docs/TASKS/app-shell/004-global-frontend-branding.md create mode 100644 frontend/public/images/brand/auth-illustration.svg create mode 100644 frontend/public/images/brand/logo-mark.svg create mode 100644 frontend/public/images/brand/logo.svg create mode 100644 frontend/src/branding/applyBranding.js create mode 100644 frontend/src/branding/branding.js create mode 100644 frontend/src/components/branding/BrandLogo.vue create mode 100644 frontend/src/components/branding/BrandMark.vue diff --git a/docs/TASKS/app-shell/004-global-frontend-branding.md b/docs/TASKS/app-shell/004-global-frontend-branding.md new file mode 100644 index 0000000..100f89b --- /dev/null +++ b/docs/TASKS/app-shell/004-global-frontend-branding.md @@ -0,0 +1,26 @@ +# Task: Add global frontend branding configuration + +## Goal + +Centralize product branding for the frontend so product name, visible brand marks, brand assets, and theme colors can be changed from one module instead of being hardcoded across shell and auth surfaces. + +## Relevant Files + +- `frontend/src/branding/branding.js` +- `frontend/src/branding/applyBranding.js` +- `frontend/src/components/branding/BrandMark.vue` +- `frontend/src/components/branding/BrandLogo.vue` +- `frontend/src/main.js` +- `frontend/src/assets/main.css` +- `frontend/src/layouts/main/AppSidebar.vue` +- `frontend/src/static/components/LandingSiteMenu.vue` +- `frontend/src/features/auth/views/RegisterView.vue` +- `frontend/src/features/auth/views/LoginView.vue` +- `frontend/public/images/brand/*` + +## Validation + +```bash +cd frontend +npm run build +``` diff --git a/frontend/public/images/brand/auth-illustration.svg b/frontend/public/images/brand/auth-illustration.svg new file mode 100644 index 0000000..50fcfd3 --- /dev/null +++ b/frontend/public/images/brand/auth-illustration.svg @@ -0,0 +1,16 @@ + + Socialize brand illustration + + + + + + + + + + + + + + diff --git a/frontend/public/images/brand/logo-mark.svg b/frontend/public/images/brand/logo-mark.svg new file mode 100644 index 0000000..f52fed6 --- /dev/null +++ b/frontend/public/images/brand/logo-mark.svg @@ -0,0 +1,11 @@ + + Socialize mark + + + + + + + + + diff --git a/frontend/public/images/brand/logo.svg b/frontend/public/images/brand/logo.svg new file mode 100644 index 0000000..c7f068b --- /dev/null +++ b/frontend/public/images/brand/logo.svg @@ -0,0 +1,12 @@ + + Socialize + + + + + + + + + SOCIALIZE + diff --git a/frontend/src/branding/applyBranding.js b/frontend/src/branding/applyBranding.js new file mode 100644 index 0000000..8825d0a --- /dev/null +++ b/frontend/src/branding/applyBranding.js @@ -0,0 +1,63 @@ +import { branding } from './branding.js'; + +const cssVariableMap = { + '--socialize-primary': 'primary', + '--socialize-accent': 'accent', + '--socialize-accent-strong': 'accentStrong', + '--socialize-highlight': 'highlight', + '--h-background': 'background', + '--h-on-background': 'onBackground', + '--h-surface': 'surface', + '--h-surface-muted': 'surfaceMuted', + '--h-on-surface': 'onSurface', + '--h-control': 'control', + '--h-control-hover': 'controlHover', + '--h-control-focus': 'controlFocus', + '--h-border': 'border', + '--h-border-strong': 'borderStrong', + '--h-primary': 'primary', + '--h-on-primary': 'onPrimary', + '--h-secondary': 'secondary', + '--h-on-secondary': 'onSecondary', + '--h-tertiary': 'tertiary', + '--h-on-tertiary': 'onTertiary', + '--h-error': 'error', + '--h-on-error': 'onError', +}; + +export function applyBranding(target = getDefaultTarget()) { + if (!target) { + return; + } + + Object.entries(cssVariableMap).forEach(([variableName, colorKey]) => { + target.style.setProperty(variableName, branding.colors[colorKey]); + }); + + target.style.setProperty( + '--socialize-brand-gradient', + `linear-gradient(135deg, ${branding.colors.accent} 0%, ${branding.colors.accentStrong} 100%)` + ); + target.style.setProperty('--socialize-accent-shadow', getRgbShadow(branding.colors.accent, 0.28)); + target.style.setProperty('--socialize-accent-strong-shadow', getRgbShadow(branding.colors.accentStrong, 0.28)); +} + +function getDefaultTarget() { + return typeof document === 'undefined' + ? null + : document.documentElement; +} + +function getRgbShadow(hexColor, opacity) { + const normalizedHex = hexColor.replace('#', ''); + + if (normalizedHex.length !== 6) { + return hexColor; + } + + const red = Number.parseInt(normalizedHex.slice(0, 2), 16); + const green = Number.parseInt(normalizedHex.slice(2, 4), 16); + const blue = Number.parseInt(normalizedHex.slice(4, 6), 16); + + return `rgba(${red}, ${green}, ${blue}, ${opacity})`; +} diff --git a/frontend/src/branding/branding.js b/frontend/src/branding/branding.js new file mode 100644 index 0000000..ca6d552 --- /dev/null +++ b/frontend/src/branding/branding.js @@ -0,0 +1,50 @@ +export const branding = Object.freeze({ + productName: 'Socialize', + shortName: 'S', + assets: { + logo: '/images/brand/logo.svg', + logoMark: '/images/brand/logo-mark.svg', + authIllustration: '/images/brand/auth-illustration.svg', + favicon: '/favicon.ico', + }, + colors: { + background: '#f4f6f3', + onBackground: '#172033', + surface: '#fbfaf6', + surfaceMuted: '#f1f5f2', + onSurface: '#172033', + control: '#eef3ef', + controlHover: '#e7eee9', + controlFocus: '#ffffff', + border: '#c7d2cc', + borderStrong: '#94a39d', + primary: '#172033', + onPrimary: '#fbfaf6', + secondary: '#fff3e2', + onSecondary: '#172033', + tertiary: '#d9f6ee', + onTertiary: '#0f766e', + accent: '#ff8a3d', + accentStrong: '#ef4444', + highlight: '#2fa58d', + error: '#bc2f2f', + onError: '#ffffff', + info: '#2563eb', + success: '#2fa58d', + warning: '#b45309', + }, +}); + +export function getVuetifyThemeColors() { + return { + background: branding.colors.background, + surface: branding.colors.surface, + primary: branding.colors.primary, + secondary: branding.colors.secondary, + accent: branding.colors.accent, + error: branding.colors.error, + info: branding.colors.info, + success: branding.colors.success, + warning: branding.colors.warning, + }; +} diff --git a/frontend/src/components/branding/BrandLogo.vue b/frontend/src/components/branding/BrandLogo.vue new file mode 100644 index 0000000..18c6efd --- /dev/null +++ b/frontend/src/components/branding/BrandLogo.vue @@ -0,0 +1,35 @@ + + + + + diff --git a/frontend/src/components/branding/BrandMark.vue b/frontend/src/components/branding/BrandMark.vue new file mode 100644 index 0000000..21ffacd --- /dev/null +++ b/frontend/src/components/branding/BrandMark.vue @@ -0,0 +1,26 @@ + + + + + diff --git a/frontend/src/features/auth/views/LoginView.vue b/frontend/src/features/auth/views/LoginView.vue index c6746da..52f7704 100644 --- a/frontend/src/features/auth/views/LoginView.vue +++ b/frontend/src/features/auth/views/LoginView.vue @@ -5,8 +5,7 @@ class="login-brand" to="/" > - S - Socialize +