Skip to main content
πŸ‘€ Interested in the latest enterprise backend features of refine? πŸ‘‰ Join now and get early access!
Implementing Dark Mode In Ant Design Using gulp
Junior Fullstack Developer
6 min read

Implementing Dark Mode In Ant Design Using gulp

CAUTION

This post was created using version 3.x.x of refine. Although we plan to update it with the latest version of refine as soon as possible, you can still benefit from the post in the meantime.

You should know that refine version 4.x.x is backward compatible with version 3.x.x, so there is no need to worry. If you want to see the differences between the two versions, check out the migration guide.

In this article, we will provide an example on how to implement darkmode with refine. In order to switch between light and dark mode, we need 2 different styles and the possibility to switch between one and the other without restarting the application. Since the Less stylesheets with React doesn't allow variables to be modified without compilation and therefore a restart of the application. To solve this, we are going to use gulp that will compile the Less files into swappable CSS, directly accessible to the running application.

The solution was presented in this blog and lightly adapted to Refine.

Initial setup​

For this article, we started from a basic Refine app with Ant Design:

npm create refine-app@latest tutorial -- -p refine-react -b v3

Select the following options to complete the CLI wizard:

Cloned remote source successfully.
βœ” What will be the name of your app Β· tutorial
βœ” Package manager: Β· Npm
βœ” Do you want to use a UI Framework?: Β· Ant Design
βœ” Do you want a customized theme?: Β· Yes (Custom Variables)
βœ” Router Provider: Β· React Router v6
βœ” Data Provider: Β· REST API
βœ” Auth Provider: Β· None
βœ” Do you want to add example pages? Β· Yes (Recommended)
βœ” Do you want a customized layout? Β· Yes
βœ” i18n - Internationalization: Β· No

From there, install the following packages:

npm install -s gulp gulp-less gulp-postcss gulp-debug gulp-csso autoprefixer less-plugin-npm-import
npm install -s react-redux react-css-theme-switcher

Create the Less and then CSS files​

Copy the antd.less file​

Make a copy of the src/styles/antd.less file into antd.light-theme.less and add the following lines inside (because this setting will be removed from the Header/index.tsx file later on):

// Header
@layout-header-background:#fff;

Create a Less file for the dark mode​

Create the following file src/styles/antd.dark-theme.less with this content:

src/styles/antd.dark-theme.less
// Run 'npx gulp less' after modifying this file

@import '~antd/lib/style/color/colorPalette.less';
@import '~antd/dist/antd.less';
@import '~antd/lib/style/themes/dark.less';

@primary-color: rgba(255, 255, 255, 0.75);
@border-radius-base: 4px;
@icon-color: rgba(255, 255, 255, 0.75);

@component-background: #303030;
@body-background: #303030;
@popover-background: #303030;
@border-color-base: #6f6c6c;
@border-color-split: #424242;
@table-header-sort-active-bg: #424242;
@card-skeleton-bg: #424242;
@skeleton-color: #424242;
@table-header-sort-active-bg: #424242;
@layout-header-background:#424242;

Compile the CSS files with gulp​

Create the following gulpfile.js in the root of the repo:

gulpfile.js
const gulp = require('gulp')
const gulpless = require('gulp-less')
const postcss = require('gulp-postcss')
const debug = require('gulp-debug')
var csso = require('gulp-csso')
const autoprefixer = require('autoprefixer')
const NpmImportPlugin = require('less-plugin-npm-import')

gulp.task('less', function () {
const plugins = [autoprefixer()]

return gulp
.src('src/styles/*-theme.less')
.pipe(debug({title: 'Less files:'}))
.pipe(
gulpless({
javascriptEnabled: true,
plugins: [new NpmImportPlugin({prefix: '~'})],
}),
)
.pipe(postcss(plugins))
.pipe(
csso({
debug: true,
}),
)
.pipe(gulp.dest('./public'))
})

exports.sync = gulp.series('less');

And run npx gulp less

β–Άnpx gulp less                       
[22:36:28] Using gulpfile ./gulpfile.js
[22:36:28] Starting 'less'...
[22:36:28] Less files: src/styles/antd.dark-theme.less
[22:36:28] Less files: src/styles/antd.light-theme.less
[22:36:28] Less files: 2 items
## parsing done in 165 ms

Compress block #1
[0.028s] init
[0.038s] clean
[0.068s] replace
[0.076s] prepare
[0.011s] mergeAtrule
[0.052s] initialMergeRuleset
[0.011s] disjoinRuleset
[0.068s] restructShorthand
[0.076s] restructBlock
[0.015s] mergeRuleset
[0.075s] restructRuleset
## compress done in 522 ms

## generate done in 44 ms

## parsing done in 114 ms

Compress block #1
[0.007s] init
[0.020s] clean
[0.057s] replace
[0.067s] prepare
[0.006s] mergeAtrule
[0.068s] initialMergeRuleset
[0.012s] disjoinRuleset
[0.045s] restructShorthand
[0.033s] restructBlock
[0.010s] mergeRuleset
[0.061s] restructRuleset
## compress done in 389 ms

## generate done in 15 ms

[22:36:33] Finished 'less' after 5 s
INFORMATION

this command must be repeated each time the Less files are modified and the application restarted to see the changes)

You should now have 2 CSS files inside the public folder: antd.dark-theme.cssand antd.light-theme.css

signin

Adapt the Refine application to be able to switch between the 2 styles​

App.tsx file​

// highlight-start // highlight-end In App.tsx, adapt the file so it looks like thisΒ :

import { Refine, } from '@refinedev/core';
import { notificationProvider } from '@refinedev/antd';
import routerProvider from "@refinedev/react-router-v6";
import "styles/antd.less";
import dataProvider from "@refinedev/simple-rest";
import { PostList, PostCreate, PostEdit, PostShow } from "pages/posts";
import { Title, Header, Sider, Footer, Layout, OffLayoutArea } from "components/layout"
import { ThemeSwitcherProvider } from "react-css-theme-switcher";

function App() {
const currThemes = {
dark: `${process.env.PUBLIC_URL}/antd.dark-theme.css`,
light: `${process.env.PUBLIC_URL}/antd.light-theme.css`,
};

return (
<ThemeSwitcherProvider themeMap={currThemes} defaultTheme="light">
<Refine routerProvider={routerProvider} notificationProvider={notificationProvider}
dataProvider={dataProvider("https://api.fake-rest.refine.dev")}
resources={[
{
name: "posts",
list: PostList,
create: PostCreate,
edit: PostEdit,
show: PostShow,
},
]}
Title={Title}
Header={Header}
Sider={Sider}
Footer={Footer}
Layout={Layout}
OffLayoutArea={OffLayoutArea} />
</ThemeSwitcherProvider>
);
};

export default App;

Add a theme switcher in the Header (src/components/layout/header/index.tsx) with the added lines so it looks like this (the Header has been simplified for the sake of clarity and the switch can be installed somewhere else in the application obviously):

import { useState } from "react";
import { useThemeSwitcher } from "react-css-theme-switcher";
import {
AntdLayout,
Switch,
} from "@refinedev/antd";

export const Header: React.FC = () => {
const [isDarkMode, setIsDarkMode] = useState<boolean>();
const { switcher, themes } = useThemeSwitcher();

function toggleTheme(isChecked: boolean) { // added
setIsDarkMode(isChecked);
switcher({ theme: isChecked ? themes.dark : themes.light });
};

return (
<AntdLayout.Header
style={{
display: "flex",
justifyContent: "flex-end",
alignItems: "center",
padding: "0px 24px",
height: "64px",
//backgroundColor: "#FFF", // commented out, otherwise the header remains white in dark mode
}}

>
<div className="main fade-in"> // added
<Switch
checkedChildren="🌜"
unCheckedChildren="🌞"
checked={isDarkMode}
onChange={toggleTheme}
/>
</div>
</AntdLayout.Header>
);
};

You should now have a light/dark mode switcher in the header:

signin
signin

Related Articles

Adding Realtime Collaboration

We'll implement realtime broadcast and subscription of pixels updates in Pixels app.

Create a Feedback Admin panel in 15 Minutes with Refine and Strapi

We will quickly create an api with Strapi and then develop its frontend with refine. Thus, let's see how an admin panel can be created in a very short time with the perfect harmony of Strapi and refine.

React useMemo hook guide with examples

Improve the React app performance with useMemo() hook