I created a theme.ts where I'm deteting the site "brand" by the hostname (based in the awesome starter nuxt3 repository versoin) like so:
utils/theme.ts
export type IThemeSettingOptions = 'light' | 'system' | 'foo' | 'bar'
export type ITheme = 'light' | 'foo' | 'bar'
export const availableThemes: {
key: IThemeSettingOptions
text: string
}[] = [
{ key: 'light', text: 'Light' },
{ key: 'system', text: 'System' },
{ key: 'foo', text: 'Foo' },
{ key: 'bar', text: 'Bar' },
]
export function ThemeManager() {
// composable
const themeUserSetting = useCookie<IThemeSettingOptions>('theme')
// methods
const getUserSetting = (): IThemeSettingOptions => {
return themeUserSetting.value || 'system'
}
const getSiteTheme = (): ITheme => {
try {
const host = location.hostname
const sites = {
'foo.whitelabel.com': 'foo',
'bar.whitelabel.com': 'bar'
}
if (sites[host]) {
return sites[host]
}
} catch (e) {
return 'light' // This means generic styles, no customizable brand
}
}
// state
const themeSetting = useState<IThemeSettingOptions>('theme.setting', () =>
getSiteTheme()
)
const themeCurrent = useState<ITheme>('theme.current', () =>
process.client ? getSiteTheme() : 'light'
)
// init theme
const init = () => {
themeSetting.value = getSiteTheme()
}
// lifecycle
onBeforeMount(() => init())
onMounted(() => {
themeCurrent.value = getSiteTheme()
})
return {
themeSetting,
themeCurrent,
getUserSetting,
getSiteTheme
}
}
I know of this color mode that nuxt offers. But As I'm using windicss I would like to have a different config file/object for every theme/brand.
I tried like this in the windi.config.ts
import { defineConfig } from 'windicss/helpers';
import type { Plugin } from 'windicss/types/interfaces';
import { ThemeManager } from './utils/theme';
console.log(ThemeManager().themeCurrent)
// etc..
But this won't log anything in the console
is this posible? if not, a similar workaround? (perhaps with tailwindcss)
I want to do this so the primary color is different for foo than for bar brand, lets say something like this:
windi.config.ts
const MyThemes = {
light: {
colors: {
green: {
DEFAULT: '#3BA670'
},
blue: {
DEFAULT: '#0096F0'
}
}
},
foo: {
colors: {
green: {
DEFAULT: '#3BA675'
},
blue: {
DEFAULT: '#0096F5'
}
}
},
bar: {
colors: {
green: {
DEFAULT: '#3BA67F'
},
blue: {
DEFAULT: '#0096FF'
}
}
}
}
const MyTheme = MyThemes['foo'] // or 'bar' or 'light'
export default defineConfig({
// etc..
theme: {
extend: {
colors: {
primary: MyTheme.colors.green,
// etc..
}
},
}
// etc..
})
And if you want to have something dynamic depending of a specific variable, you need to map it to an object and you'll get your classes as shown here. This is quite handy if you want something dynamic while using an utility-based CSS framework with a proper design system.
Tailwind does not allow dynamic classes for performance reasons, hence why such mapping is a necessary evil.
UnoCSS is probably the most flexible solution that I know but it's still not adapted to solve an issue like that. Neither is the usage of using some CSS variables and overwriting them on the fly since a design system is based on far more things than just a color or a size.
TLDR: theming is hard and requires quite some hand-made mapping to be done efficiently. I've tried that at a client and it's complex quite early, especially if you think about a way of doing that dynamically.
I've tried that one and twind's shim. It's feasible to have it on runtime (if needed) but quite tricky for sure.