diff --git a/app.vue b/app.vue index ff4b6c3..d33467a 100644 --- a/app.vue +++ b/app.vue @@ -34,7 +34,8 @@ body { /* GLOBAL */ .btn, .select, -.input { +.input.input, +.join-item.btn { min-height: 2.5rem; height: 2.5rem; } diff --git a/composables/states.ts b/composables/states.ts index d975021..4986249 100644 --- a/composables/states.ts +++ b/composables/states.ts @@ -1,3 +1,5 @@ +import { useThemeOverrideState } from '@/composables/theming' + // AUTH export const useIsLoggedIn = () => { @@ -133,6 +135,11 @@ export const useSettings = async () => { const colorMode = useColorMode() colorMode.preference = settings.value.portal_theme + + const themeOverride = useThemeOverrideState() + if (themeOverride.value) { + useThemeOverride().init(themeOverride.value) + } } return settings diff --git a/composables/theming.ts b/composables/theming.ts new file mode 100644 index 0000000..03e8773 --- /dev/null +++ b/composables/theming.ts @@ -0,0 +1,93 @@ +import { reactive } from 'vue' +import C from 'colorjs.io' + +import { keysOf } from '@/utils/common' + +enum Color { + primary = 'p', + secondary = 's', + accent = 'a', + neutral = 'n', + 'base-100' = 'b1', + 'base-200' = 'b2', + 'base-300' = 'b3', + 'base-content' = 'bc', + info = 'in', + success = 'su', + warning = 'wa', + error = 'er', +} + +enum Size { + 'card-radius' = 'rounded-box', + 'btn-radius' = 'rounded-btn', + 'btn-border' = 'border-btn', + // Other possible vars, not ~used atm + // "rounded-badge", + // "animation-btn", + // "animation-input", + // "btn-focus-scale", + // "tab-border", + // "tab-radius", +} + +type Colors = Record +type Sizes = Record +type Theme = Colors & Sizes + +export const useThemeOverrideState = () => + useState('customTheme', () => { + const theme = localStorage.getItem('customTheme') + return theme ? JSON.parse(theme) : null + }) + +export const useThemeOverride = () => { + const themeOverride = useThemeOverrideState() + const colorNames = keysOf(Color) + const sizeNames = keysOf(Size) + const colors = reactive( + Object.fromEntries( + colorNames.map((key) => [key, themeOverride.value?.[key] ?? '']), + ) as Colors, + ) + const sizes = reactive( + Object.fromEntries( + sizeNames.map((key) => [key, themeOverride.value?.[key] ?? '']), + ) as Sizes, + ) + + const toCss = (theme: Theme) => { + return keysOf(theme) + .reduce((cssVars, key) => { + if (theme[key] === '') return cssVars + if (key in Color) { + const oklch = new C(theme[key]) + .to('oklch') + .coords.map((n) => (isNaN(n) ? 0 : n)) + .join(' ') + cssVars.push(`--${Color[key as keyof Colors]}: ${oklch};`) + } else { + cssVars.push(`--${Size[key as keyof Sizes]}: ${theme[key]}rem;`) + } + return cssVars + }, []) + .join('') + } + + const update = (theme: Theme | null) => { + localStorage.setItem('customTheme', theme ? JSON.stringify(theme) : '') + document + .querySelector('html')! + .setAttribute('style', theme ? toCss(theme) : '') + } + + watch([sizes, colors], () => { + update({ ...colors, ...sizes }) + }) + + return { + init: update, + colors, + sizes, + } +} diff --git a/layouts/default.vue b/layouts/default.vue index 071ebce..748388d 100644 --- a/layouts/default.vue +++ b/layouts/default.vue @@ -37,7 +37,7 @@ async function logout() { + + diff --git a/pages/index.vue b/pages/index.vue index 0d0a980..30d26fd 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -87,7 +87,7 @@ async function onSearchSubmit() {
  • ( }) return filtered } + +export function keysOf>(obj: T) { + return Object.keys(obj) as Array +} diff --git a/yarn.lock b/yarn.lock index b9d557b..fcd8b9e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2507,6 +2507,11 @@ colorette@^2.0.20: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== +colorjs.io@^0.4.5: + version "0.4.5" + resolved "https://registry.yarnpkg.com/colorjs.io/-/colorjs.io-0.4.5.tgz#7775f787ff90aca7a38f6edb7b7c0f8cce1e6418" + integrity sha512-yCtUNCmge7llyfd/Wou19PMAcf5yC3XXhgFoAh6zsO2pGswhUPBaaUh8jzgHnXtXuZyFKzXZNAnyF5i+apICow== + commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"