how can I use an environment variable to specify the base color in my CSS ?
I use NEXTJS
My colors are all defined as variables in my globals.css :
@layer base {
:root {
...
--primary: #0096d0;
--primary-bright: hsl(from var(--primary) h s calc(l*1.5));
...
I tried
--primary: env(NEXT_PUBLIC_PRIMARY_COLOR, #0096d0);
But the env variable is ignored
Any idea ?
While env()
does work with custom properties, it reads from UA-defined environment variables, not from the published NEXT_PUBLIC_
variables in the client-side, and definetly not server-side environment variables. Though in the future we might be able to add custom environment variables via JS or CSS, currently you can't define client-side Custom CSS Environment Variables yet.
Hence, for now you will need to either consider using CSS preprocessors, inline-styles in JSX, or PostCSS plugins. They all have their own drawbacks.
The main disadvantage is that this can't be used in the global.css like you were. However, the advantage is that you can create the style
object however you want. You can also update the style
object and trigger an update after the app is running. (Note that all NEXT_PUBLIC_
variables will be frozen from being updated during runtime.)
In index.js
:
//this can be created dynamically by looping over keys in .env.local
const style = {"--primary": process.env.NEXT_PUBLIC_PRIMARY_COLOR ?? '#0096d0'}
<div style={style}>
<MyRoot></MyRoot>
</div>
One way is to import specific environment variables into SCSS variables using the SASS additional-data loader option.
For Next, you can follow the Next document Styling with SCSS to install SASS:
npm install --save-dev sass
In next.config.js
, prepend the SASS variables:
const nextConfig = {
sassOptions: {
additionalData: Object.keys(process.env).reduce((accumulator, currentValue) => {
if (currentValue.startsWith('NEXT_THEME_')) {
return `${accumulator}$${currentValue}: ${process.env[currentValue]};`
}
else {
return accumulator
}
}, ''),
includePaths: [path.join(__dirname, 'styles')],
}
}
In .env.local
:
NEXT_THEME_PRIMARY_COLOR=red
NEXT_THEME_SECONDARY_COLOR='#fff'
Then you can use the environment variables in globals.scss
by:
@layer base {
:root {
--primary: #{$NEXT_THEME_PRIMARY_COLOR};
}
}
The drawback is that SCSS is "preprocessed", means that the SCSS variables also don't exist for you to update during JS runtime. Also, the environment variable have to always be presented, or else you need to use variable-exists()
or !default
explicitly, which is somewhat verbose. For example:
:root {
--primary: #0096d0;
@if variable-exists(NEXT_THEME_PRIMARY_COLOR) {
--primary: #{$NEXT_THEME_PRIMARY_COLOR};
}
}
Since you are using Nextjs, PostCSS and some plugins are built-in already. However, currently none of the built-in ones matches your need. You need to find a plugin or implement one that solves your problem.
One example is the postcss-functions plugin that allows you define your own env function that reads from environment variables.
In postcss.config.js
:
const theme = Object.keys(process.env).reduce((accumulator, currentValue) => {
if (currentValue.startsWith('NEXT_THEME_')) {
accumulator[currentValue] = process.env[currentValue];
return accumulator;
}
else {
return accumulator
}
}, {})
function getEnv(variable, fallback = null) {
return theme[variable] ?? fallback;
}
module.exports = {
"plugins": {
"postcss-functions": {
"functions": {
getEnv
}
},
}
}
Then you can use it in global.css as follow:
@layer base {
:root {
--primary: getEnv(NEXT_THEME_PRIMARY_COLOR, #0096d0);
}
}