refactor: simplify frontend theme setup
This commit is contained in:
@@ -2,80 +2,25 @@
|
|||||||
@custom-variant dark (&:where(.dark, .dark *));
|
@custom-variant dark (&:where(.dark, .dark *));
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
--color-hBackground: var(--h-background);
|
--color-hBackground: rgb(var(--v-theme-background));
|
||||||
--color-hOnBackground: var(--h-on-background);
|
--color-hOnBackground: rgb(var(--v-theme-on-background));
|
||||||
--color-hSurface: var(--h-surface);
|
--color-hSurface: rgb(var(--v-theme-surface));
|
||||||
--color-hOnSurface: var(--h-on-surface);
|
--color-hOnSurface: rgb(var(--v-theme-on-surface));
|
||||||
--color-hPrimary: var(--h-primary);
|
--color-hPrimary: rgb(var(--v-theme-primary));
|
||||||
--color-hOnPrimary: var(--h-on-primary);
|
--color-hOnPrimary: rgb(var(--v-theme-on-primary));
|
||||||
--color-hSecondary: var(--h-secondary);
|
--color-hSecondary: rgb(var(--v-theme-secondary));
|
||||||
--color-hOnSecondary: var(--h-on-secondary);
|
--color-hOnSecondary: rgb(var(--v-theme-on-secondary));
|
||||||
--color-hTertiary: var(--h-tertiary);
|
--color-hTertiary: rgb(var(--v-theme-tertiary));
|
||||||
--color-hOnTertiary: var(--h-on-tertiary);
|
--color-hOnTertiary: rgb(var(--v-theme-on-tertiary));
|
||||||
--color-hError: var(--h-error);
|
--color-hError: rgb(var(--v-theme-error));
|
||||||
--color-hOnError: var(--h-on-error);
|
--color-hOnError: rgb(var(--v-theme-on-error));
|
||||||
}
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--socialize-primary: #172033;
|
|
||||||
--socialize-accent: #ff8a3d;
|
|
||||||
--socialize-accent-strong: #ef4444;
|
|
||||||
--socialize-brand-gradient: linear-gradient(135deg, var(--socialize-accent) 0%, var(--socialize-accent-strong) 100%);
|
|
||||||
--socialize-accent-shadow: rgba(255, 138, 61, 0.28);
|
|
||||||
--socialize-accent-strong-shadow: rgba(239, 68, 68, 0.28);
|
|
||||||
--socialize-highlight: #2fa58d;
|
|
||||||
--h-background: #f4f6f3;
|
|
||||||
--h-on-background: #172033;
|
|
||||||
--h-surface: #fbfaf6;
|
|
||||||
--h-surface-muted: #f1f5f2;
|
|
||||||
--h-on-surface: #172033;
|
|
||||||
--h-control: #eef3ef;
|
|
||||||
--h-control-hover: #e7eee9;
|
|
||||||
--h-control-focus: #ffffff;
|
|
||||||
--h-border: #c7d2cc;
|
|
||||||
--h-border-strong: #94a39d;
|
|
||||||
--h-primary: #172033;
|
|
||||||
--h-on-primary: #fbfaf6;
|
|
||||||
--h-secondary: #fff3e2;
|
|
||||||
--h-on-secondary: #172033;
|
|
||||||
--h-tertiary: #d9f6ee;
|
|
||||||
--h-on-tertiary: #0f766e;
|
|
||||||
--h-error: #bc2f2f;
|
|
||||||
--h-on-error: #ffffff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body,
|
body,
|
||||||
#app {
|
#app {
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
background: var(--h-background);
|
background: #f4f6f3;
|
||||||
}
|
|
||||||
|
|
||||||
input:not([type='checkbox']):not([type='radio']):not([type='range']):not([type='file']),
|
|
||||||
select,
|
|
||||||
textarea {
|
|
||||||
background-color: var(--h-control) !important;
|
|
||||||
border-color: var(--h-border) !important;
|
|
||||||
color: var(--h-on-surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
input:not([type='checkbox']):not([type='radio']):not([type='range']):not([type='file']):hover,
|
|
||||||
select:hover,
|
|
||||||
textarea:hover {
|
|
||||||
background-color: var(--h-control-hover) !important;
|
|
||||||
border-color: var(--h-border-strong) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:not([type='checkbox']):not([type='radio']):not([type='range']):not([type='file']):focus,
|
|
||||||
select:focus,
|
|
||||||
textarea:focus,
|
|
||||||
input:not([type='checkbox']):not([type='radio']):not([type='range']):not([type='file']):focus-visible,
|
|
||||||
select:focus-visible,
|
|
||||||
textarea:focus-visible {
|
|
||||||
background-color: var(--h-control-focus) !important;
|
|
||||||
border-color: var(--socialize-highlight) !important;
|
|
||||||
box-shadow: 0 0 0 3px rgba(47, 165, 141, 0.16);
|
|
||||||
outline: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
input::placeholder,
|
input::placeholder,
|
||||||
@@ -85,8 +30,8 @@ textarea::placeholder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.v-application {
|
.v-application {
|
||||||
background: var(--h-background) !important;
|
background: rgb(var(--v-theme-background)) !important;
|
||||||
color: var(--h-on-background);
|
color: rgb(var(--v-theme-on-background));
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-card,
|
.v-card,
|
||||||
@@ -94,41 +39,47 @@ textarea::placeholder {
|
|||||||
.v-list,
|
.v-list,
|
||||||
.v-menu > .v-overlay__content,
|
.v-menu > .v-overlay__content,
|
||||||
.v-dialog > .v-overlay__content {
|
.v-dialog > .v-overlay__content {
|
||||||
background-color: var(--h-surface) !important;
|
background-color: rgb(var(--v-theme-surface)) !important;
|
||||||
border: 1px solid var(--h-border);
|
border: 1px solid rgb(var(--v-theme-border));
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-field {
|
.v-field {
|
||||||
background-color: var(--h-control) !important;
|
background-color: rgb(var(--v-theme-control)) !important;
|
||||||
color: var(--h-on-surface);
|
color: rgb(var(--v-theme-on-surface));
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-field:hover {
|
.v-field:hover {
|
||||||
background-color: var(--h-control-hover) !important;
|
background-color: rgb(var(--v-theme-control-hover)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-field--focused {
|
.v-field--focused {
|
||||||
background-color: var(--h-control-focus) !important;
|
background-color: rgb(var(--v-theme-control-focus)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-field__outline {
|
.v-field__outline {
|
||||||
color: var(--h-border-strong);
|
color: rgb(var(--v-theme-border-strong));
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-field--focused .v-field__outline {
|
.v-field--focused .v-field__outline {
|
||||||
color: var(--socialize-highlight);
|
color: rgb(var(--v-theme-highlight));
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-field__input,
|
.v-field__input,
|
||||||
.v-field-label {
|
.v-field-label {
|
||||||
color: var(--h-on-surface);
|
color: rgb(var(--v-theme-on-surface));
|
||||||
|
}
|
||||||
|
|
||||||
|
.v-select .v-field .v-field__input > input,
|
||||||
|
.v-select .v-field .v-field__input > input::placeholder {
|
||||||
|
color: transparent !important;
|
||||||
|
caret-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel,
|
.panel,
|
||||||
[class$='-panel'],
|
[class$='-panel'],
|
||||||
[class$='-card'],
|
[class$='-card'],
|
||||||
div.card {
|
div.card {
|
||||||
border-color: var(--h-border) !important;
|
border-color: rgb(var(--v-theme-border)) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@layer components {
|
@layer components {
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
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})`;
|
|
||||||
}
|
|
||||||
@@ -7,44 +7,4 @@ export const branding = Object.freeze({
|
|||||||
authIllustration: '/images/brand/auth-illustration.svg',
|
authIllustration: '/images/brand/auth-illustration.svg',
|
||||||
favicon: '/favicon.ico',
|
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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -31,6 +31,6 @@
|
|||||||
|
|
||||||
.brand-logo-text {
|
.brand-logo-text {
|
||||||
@apply text-lg font-black uppercase tracking-[0.18em];
|
@apply text-lg font-black uppercase tracking-[0.18em];
|
||||||
color: var(--h-primary);
|
color: rgb(var(--v-theme-primary));
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -36,19 +36,19 @@
|
|||||||
|
|
||||||
.feedback-entry-button {
|
.feedback-entry-button {
|
||||||
@apply flex h-12 items-center gap-2 rounded-full border px-4 text-sm font-bold shadow-lg transition-colors;
|
@apply flex h-12 items-center gap-2 rounded-full border px-4 text-sm font-bold shadow-lg transition-colors;
|
||||||
background: var(--socialize-accent-strong);
|
background: rgb(var(--v-theme-accent-strong));
|
||||||
border-color: rgba(255, 255, 255, 0.55);
|
border-color: rgba(255, 255, 255, 0.55);
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
box-shadow: 0 16px 34px var(--socialize-accent-strong-shadow);
|
box-shadow: 0 16px 34px rgb(var(--v-theme-accent-strong) / 0.28);
|
||||||
}
|
}
|
||||||
|
|
||||||
.feedback-entry-button:hover {
|
.feedback-entry-button:hover {
|
||||||
background: color-mix(in srgb, var(--socialize-accent-strong) 82%, var(--socialize-primary));
|
background: color-mix(in srgb, rgb(var(--v-theme-accent-strong)) 82%, rgb(var(--v-theme-primary)));
|
||||||
box-shadow: 0 18px 38px var(--socialize-accent-strong-shadow);
|
box-shadow: 0 18px 38px rgb(var(--v-theme-accent-strong) / 0.28);
|
||||||
}
|
}
|
||||||
|
|
||||||
.feedback-entry-button:focus-visible {
|
.feedback-entry-button:focus-visible {
|
||||||
outline: 3px solid color-mix(in srgb, var(--socialize-accent) 35%, transparent);
|
outline: 3px solid color-mix(in srgb, rgb(var(--v-theme-accent)) 35%, transparent);
|
||||||
outline-offset: 3px;
|
outline-offset: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -676,7 +676,7 @@
|
|||||||
|
|
||||||
.brand-name {
|
.brand-name {
|
||||||
@apply min-w-0 text-lg font-black uppercase tracking-[0.18em];
|
@apply min-w-0 text-lg font-black uppercase tracking-[0.18em];
|
||||||
color: var(--h-primary);
|
color: rgb(var(--v-theme-primary));
|
||||||
line-height: 2.75rem;
|
line-height: 2.75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,10 +35,7 @@ import { useChannelsStore } from '@/features/channels/stores/channelsStore.js';
|
|||||||
import { i18n } from '@/plugins/i18n.js';
|
import { i18n } from '@/plugins/i18n.js';
|
||||||
import config from '@/config.js';
|
import config from '@/config.js';
|
||||||
import { createHead } from '@vueuse/head';
|
import { createHead } from '@vueuse/head';
|
||||||
import { applyBranding } from '@/branding/applyBranding.js';
|
import { socializeTheme } from '@/plugins/theme.js';
|
||||||
import { getVuetifyThemeColors } from '@/branding/branding.js';
|
|
||||||
|
|
||||||
applyBranding();
|
|
||||||
|
|
||||||
const vuetify = createVuetify({
|
const vuetify = createVuetify({
|
||||||
components: {
|
components: {
|
||||||
@@ -64,10 +61,7 @@ const vuetify = createVuetify({
|
|||||||
theme: {
|
theme: {
|
||||||
defaultTheme: 'socializeLight',
|
defaultTheme: 'socializeLight',
|
||||||
themes: {
|
themes: {
|
||||||
socializeLight: {
|
socializeLight: socializeTheme,
|
||||||
dark: false,
|
|
||||||
colors: getVuetifyThemeColors(),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
29
frontend/src/plugins/theme.js
Normal file
29
frontend/src/plugins/theme.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
export const socializeTheme = {
|
||||||
|
dark: false,
|
||||||
|
colors: {
|
||||||
|
background: '#f4f6f3',
|
||||||
|
'on-background': '#172033',
|
||||||
|
surface: '#fbfaf6',
|
||||||
|
'surface-muted': '#f1f5f2',
|
||||||
|
'on-surface': '#172033',
|
||||||
|
control: '#eef3ef',
|
||||||
|
'control-hover': '#e7eee9',
|
||||||
|
'control-focus': '#ffffff',
|
||||||
|
border: '#c7d2cc',
|
||||||
|
'border-strong': '#94a39d',
|
||||||
|
primary: '#172033',
|
||||||
|
'on-primary': '#fbfaf6',
|
||||||
|
secondary: '#fff3e2',
|
||||||
|
'on-secondary': '#172033',
|
||||||
|
tertiary: '#d9f6ee',
|
||||||
|
'on-tertiary': '#0f766e',
|
||||||
|
accent: '#ff8a3d',
|
||||||
|
'accent-strong': '#ef4444',
|
||||||
|
highlight: '#2fa58d',
|
||||||
|
error: '#bc2f2f',
|
||||||
|
'on-error': '#ffffff',
|
||||||
|
info: '#2563eb',
|
||||||
|
success: '#2fa58d',
|
||||||
|
warning: '#b45309',
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -276,7 +276,7 @@
|
|||||||
|
|
||||||
.site-brand {
|
.site-brand {
|
||||||
@apply flex min-w-0 items-start gap-3 no-underline;
|
@apply flex min-w-0 items-start gap-3 no-underline;
|
||||||
color: var(--h-primary);
|
color: rgb(var(--v-theme-primary));
|
||||||
}
|
}
|
||||||
|
|
||||||
.site-brand-mark {
|
.site-brand-mark {
|
||||||
|
|||||||
Reference in New Issue
Block a user