1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

BlazorでLight/Dark Mode切替+ローカルストレージにモードを保存

Last updated at Posted at 2024-12-09

1.この記事の目的

  • BlazorでLight/Dark Mode切替です
  • LocalStorageにモードを保管し、ブラウザを閉じても状態を維持します

LocalStorageはブラウザーを再起動しても保持されます

image.png

image.png

2.前提条件

  • Visual studio 2022 Version 17.12.2
  • .Net 8
  • UI:Blazor Web App
  • Blazor rendermode: InteractiveServerRenderMode

3.ソリューションのソース

4.構成

4.1 CustomBasePage.cs

  • 全てのrazorページの継承元
  • OnAfterRenderAsyncで現在のテーマを取得し反映
using BlazorTheme.Components.Services;
using Microsoft.AspNetCore.Components;

namespace BlazorTheme.Components
{
    public class CustomBasePage : ComponentBase
    {
        [Inject]
        protected ThemeService ThemeService { get; set; } = default!;

        protected override async Task OnAfterRenderAsync(bool firstRender)
        {
            // StreamRenderingの場合、OnAfterRenderは複数回実行される。
            // 初回だけ実行するようにしてしまうと、<table>タグが生成されるまえにだけ
            // Applyする可能性があるので、冗長だがAfterRenderのたびに実行する。
            await ThemeService.GetCurrentTheme();
            await ThemeService.ApplyCurrentTheme();
        }
    }
}

Home.razorがCustomBasePageを継承する例

@inherits CustomBasePage
@page "/"

<PageTitle>Home</PageTitle>

<h1>Hello, world!</h1>

Welcome to your new app.

4.2 ToggleTheme.razor

  • テーマの切り替えボタン
@inject ThemeService ThemeService

<button @onclick="ToggleThemeOnClick" class="theme-toggle @ThemeService.CurrentTheme">
    <span class="theme-icon light">☀️</span>
    <span class="theme-icon dark">🌙</span>
</button>

@code {
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await ThemeService.GetCurrentTheme();
        await ThemeService.ApplyCurrentTheme();
    }

    private async Task ToggleThemeOnClick()
    {
        await ThemeService.ToggleTheme();
        await ThemeService.ApplyCurrentTheme();
    }
}

4.3 ThemeService.cs

  • テーマ取得、変更の実行サービス
  • 現在のテーマをjavascriptを呼び出しページへ反映
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
using Microsoft.JSInterop;
using System.Security.Cryptography;

namespace BlazorTheme.Components.Services
{
    public class ThemeService
    {
        private readonly ProtectedLocalStorage _localStorage;
        private readonly IJSRuntime _jsRuntime;
        private const string ThemeKey = "theme";
        private const string DefaultTheme = "light";

        public string CurrentTheme { get; private set; } = DefaultTheme;

        public ThemeService(ProtectedLocalStorage localStorage, IJSRuntime jsRuntime)
        {
            _localStorage = localStorage;
            _jsRuntime = jsRuntime;
        }

        public async Task<string> GetCurrentTheme()
        {
            try
            {
                var result = await _localStorage.GetAsync<string>(ThemeKey);
                CurrentTheme = result.Success ? result.Value! : DefaultTheme;
            }
            catch (CryptographicException)
            {
                CurrentTheme = "light";
            }
            return CurrentTheme;
        }

        public async Task<string> ToggleTheme()
        {
            CurrentTheme = CurrentTheme == DefaultTheme ? "dark" : DefaultTheme;
            await _localStorage.SetAsync(ThemeKey, CurrentTheme);
            return CurrentTheme;
        }

        public async Task ApplyCurrentTheme()
        {
            await _jsRuntime.InvokeVoidAsync("setTheme", CurrentTheme);
        }
    }
}

4.4 site.js

  • javascriptでテーマを反映
window.setTheme = (theme) => {
    document.documentElement.setAttribute("data-theme", theme);

    const tables = document.querySelectorAll(".table");
    tables.forEach((table) => {
        if (theme === "dark") {
            table.classList.add("table-dark");
        } else {
            table.classList.remove("table-dark");
        }
    });
};

4.5 app.css

  • テーマ色の指定など
/* Light Mode */
:root {
    --background-color: #ffffff;
    --text-color: #000000;
    --top-color: #f7f7f7;
    --theme-color1: rgb(5, 39, 103);
    --theme-color2: #3a0647;
}

    /* Dark Mode */
    :root[data-theme='dark'] {
        --background-color: #000000;
        --text-color: #4cff00;
        --top-color: #1a1a1a;
        --theme-color1: #1a1a1a;
        --theme-color2: #3b2e2e;
    }

/* Mode */
body {
    background-color: var(--background-color);
    color: var(--text-color);
}

4.6 MainLayout.razor.css

  • サイドバーとトップローにテーマ色を反映
.sidebar {
    background-image: linear-gradient(180deg, var(--theme-color1) 0%, var(--theme-color2) 70%);
}

.top-row {
    background-color: var(--top-color);
    color: var(--text-color);
    border-bottom: 1px solid #d6d5d5;
    justify-content: flex-end;
    height: 3.5rem;
    display: flex;
    align-items: center;
}

5.動作確認

ローカルストレージへ暗号化されたテーマが記録されていることが分かります
image.png

ローカルストレージを削除してからGETし直すとデフォルト(ライトモード)
image.png

もう一度モードを変更するとローカルストレージにキーが追加されます
image.png

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?