How to make a Dark-Mode in ASP.NET MVC 7
Tags: aspnetcore, csharp, dotnet, tutorial
Published: 2023-09-02 23:35:29

While creating my portfolio I found the need to implement a dark mode with no more than a few tools, because this project was made to learn ASP.NET, not a javascript framework. So go on...
### Tools
- C#
- CSS
- JavaScript

### Some background
When I was researching about how to make a dark mode I found [this website](https://css-tricks.com/a-complete-guide-to-dark-mode-on-the-web/). I followed up the "body-class" method, all worked fine but when I refresh the page the theme doesn't persist. ~~Then, I tried to save data to local storage with JavaScript, but it didn't work, so I decided to use cookies.~~
## Method 1 - LocalStorage
In this way, we keep the changes of the view on the client-side.
#### Step 1
Add a class to the components that you want to change their theme. This class will be your default theme, so **keep it in mind**.
```html
// Shared/_Layout.cshtml
<body class="dark-theme">
  @RenderBody()
<script src="~/js/site.js" asp-append-version="true"></script>
</body>
// Views/Home
<div class="home-lower dark-theme">
</div>
```
#### Step 2
Add a script to remove the class name based on the local storage variable name. 
```javascript
const themeName = 'dark-theme';
const itemDivs = document.querySelectorAll('.dark-theme');
const themeSwitcher = document.getElementById('theme-switcher');
const darkTheme = localStorage.getItem(themeName);

if(darkTheme === null){
localStorage.setItem(themeName,"active");
} else if(darkTheme === "active"){
addTheme();
} else {
removeTheme();
}

themeSwitcher.addEventListener('click',function (){
let currentTheme = localStorage.getItem(themeName);
if(currentTheme === "inactive"){
localStorage.setItem(themeName,"active");
addTheme();
} else if (currentTheme === "active"){
localStorage.setItem(themeName,"inactive");
removeTheme();
}
})

function removeTheme(){
itemDivs.forEach(itemDiv => {
itemDiv.classList.remove(themeName);
});
}

function addTheme(){
itemDivs.forEach(itemDiv => {
itemDiv.classList.add(themeName);
});
}

```
#### Step 3
Add a button with an id to be listened by the event from the script ([tutorial](https://dev.to/maxprogramming/why-you-should-avoid-the-onclick-attribute-in-your-code-560d))
```html
<button class="btn-toggle" id="theme-switcher"></button>
```
#### Step 4
Add your css rules to change your colors based on class name
```css
body {
    background-color: white;
}
body.dark-theme {
    background-color: gray;
}
```
## Method 2 - Cookies
This way, you need to call an action in the controller to make a new request to the server, but you will need to update the DOM with Javascript (Looking back, it sounds terrible to separate the logic between client and server).
#### HTTP Context
Reading about the request pipeline, we can find HttpContext class, which describes request and response information. In this case, we need to read the user request available through the Request member.
```csharp
public abstract class HttpContext
{
  public abstract HttpRequest Request { get; }
}
```
And what do we need to know about the request? 🍪🍪🍪 
#### Cookies
We will use cookies to store our dark theme information in something like `["dark-theme" = true]`and it is available through the Cookies request member.
```csharp
public abstract class HttpRequest
{
  public abstract IRequestCookieCollection Cookies { get; set; }
}
```
#### Executing request
As I tried to avoid using javascript except to handle the DOM, we will use an Action from our Controller to check if the user is requesting dark or light theme. **Let's go**
### How it works?
The theme changer will be executed on the Home view of my website. It will trigger another action method from the HomeController and redirect to the Index action of that controller.
#### Step 1
Add a class to the components that you want to change their theme. This class will be your default theme, so keep it in mind.
```html
// Shared/_Layout.cshtml
<body class="dark-theme">
  @RenderBody()
<script src="~/js/site.js" asp-append-version="true"></script>
</body>
// Views/Home
<div class="home-lower dark-theme">
</div>
```
#### Step 2
Add a script to remove the class name based on the cookie name. I use the readCookie method from [a Quirksmode post](https://www.quirksmode.org/js/cookies.html) and the script from ChatGPT because is not a technology that I handle.
⚠️ It's important that your template runs javascript on the pages where you will use the method to change the theme, that's why I used the base layout from ASP.NET
```js
// wwwroot/js/site.js
const darkTheme = 'dark-theme'; // Your class name
const itemDivs = document.querySelectorAll('.dark-theme');
// If dark theme is active, remove class names
if(readCookie(darkTheme) === 'true'){
    itemDivs.forEach(itemDiv => {
        itemDiv.classList.remove(darkTheme);
    });
}

function readCookie(name) {
    const nameEQ = name + "=";
    const ca = document.cookie.split(';');
    for(let i=0; i < ca.length; i++) {
        let c = ca[i];
        while (c.charAt(0)===' ') c = c.substring(1,c.length);
        if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length,c.length);
    }
    return null;
}
```
#### Step 3
Add a button to execute an action method called ToggleTheme in your HomeController
```html
<div>
  <a asp-action="ToggleTheme" asp-controller="Home">
</div>
```
#### Step 4
Add a method to set a default cookie called as your theming class 
_(dark-theme == false equals to "keep class name", and yeah, it is bad, do it better)_

```csharp
public class HomeController : Controller
{
  public IActionResult Index()
    {
        var requestCookie = HttpContext.Request.Cookies;
        var response = HttpContext.Response.Cookies;
        if (!requestCookie.ContainsKey("dark-theme"))
        {
            response.Append("dark-theme","false");
        }
        return View("Home");
    }
}
```
Add a method to change your cookie value based on user request and redirect to index because when you return the view your url will be like /Home/ToggleTheme

```csharp
public IActionResult ToggleTheme()
{
   var requestCookies = HttpContext.Request.Cookies;
   var responseCookies = HttpContext.Response.Cookies;
   if (requestCookies.ContainsKey("dark-theme") && requestCookies["dark-theme"]!.Contains("false"))
     {
         responseCookies.Append("dark-theme","true");
     }
     else
     {
         responseCookies.Append("dark-theme","false");
     }
     return RedirectToAction("Index");
}
```
#### Step 5
Add your css rules to change your colors based on class name
```css
body {
    background-color: white;
}
body.dark-theme {
    background-color: gray;
}
```
### That's all
If you have any corrections I look forward to your comments, I didn't find much information on StackOverflow or Youtube so I made it with what I know. I hope I haven't forgotten anything

Code: https://gitlab.com/vlank/portfolio

Greetings from 🇦🇷