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

React Hook Form Validation with Complete Examples

We'll implement React Hook Form custom validations and schema validations using yup.

Let's Build an App to Manage Your Hackathons with Refine

We'll be building a demo app to manage hackathons with refine.

Creating a Responsive React Navbar with Tailwind CSS

In this brief guide, we'll create a responsive navbar with Tailwind CSS and Bootstrap in React.