ダークモードは、OS、アプリケーション、今や様々な場面で実装されています。
Blazorでtwitter風のダークモードを実装してみましょう。
カスタムCSSによるUIの変更
「Blazor darkmode」で検索すると例がいくつか見つかりました。その中で、「Blazor WebAssembly - Dark Mode - CSS Variables - Applying Themes」というタイトルの動画を参考に、簡単にダークモードを実装してみたいと思います。
CSSのスコープ
Blazorプロジェクトを作成するといくつかのCSS
ファイルが作成されています。CSS
の範囲を確認してみます。CSS
の分離が行えるようになってから、app.css
に記述されていたスタイルがコンポーネントごとに設定できるのが確認できます。app.css
は全体に適応されます。app.css
に定義しておくことで、アプリケーション全体に適応できそうです。
app.cssへの記述
全てのコンポーネントに適用されるapp.css
へ、コンポーネントに適応したい定義をあらかじめ書いておきます。デフォルトは::root
、ダークブルーは.dark-mode-blue
、ブラックは.dark-mode-black
として、それぞれ適応したい色やスタイルを定義しておきます。
:root {
/* white */
--background: white;
--font-color: black;
--highlight: rgba(0,0,0,0.4);
--highlight-2: #e2e3e5;
--link: #0366d6;
--background-img: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
--border-color: #1861ac;
--btn-font-color: #fff;
--btn-background-color: #1b6ec2;
}
.dark-mode-blue {
/* dark-mode blue */
--background: rgb(21, 32, 43);
--font-color: white;
--highlight: rgb(21, 32, 43);
--highlight-2: #414648;
--link: #3ca4ff;
--background-img: none;
--border-color: #3ca4ff;
--btn-font-color: white;
--btn-background-color: rgb(21, 32, 43);
}
.dark-mode-black {
/* dark-mode black */
--background: black;
--font-color: white;
--highlight: black;
--highlight-2: #414648;
--link: #3ca4ff;
--background-img: none;
--border-color: #3ca4ff;
--btn-font-color: white;
--btn-background-color: black
}
これらのカスタムCSSを、ユーザーが選択したものが適用したい箇所に記述してみましょう。
例えば、MainLayout
のコンポーネントの背景色と文字色は次のようになります。
div.main {
background-color: var(--background);
color: var(--font-color);
}
ダークモードの適用
適用したいスタイルを使いたいところで指定します。今回は、twitterのようにすべてにダークモードを指定したかったので、最上位のpage
に対し、ダークモードを適用しています。
StateContainer
は、適用するデザインの状態管理クラスです。文字列Property
の変更とともにコンポーネントのStateHasChanged
を呼び出して、再描写を行えるようにしておきます。
(参考)[ASP.NET Core Blazor 状態管理]
(https://docs.microsoft.com/ja-jp/aspnet/core/blazor/state-management?view=aspnetcore-5.0&pivots=webassembly)
@inherits LayoutComponentBase
@implements IDisposable
@inject StateContainer StateContainer
<div class="page @(StateContainer.Property)"> 👈 ここで全体にスタイルが適用されるように指定
<div class="sidebar">
<NavMenu StateContainer="StateContainer"/>
</div>
<div class="main">
<div class="top-row px-4">
<a href="http://blazor.net" target="_blank" class="ml-md-auto">About</a>
</div>
<div class="content px-4">
@Body
</div>
</div>
</div>
@code {
protected override void OnInitialized()
{
StateContainer.OnChange += StateHasChanged;
}
public void Dispose()
{
StateContainer.OnChange -= StateHasChanged;
}
}
ダークモードへの切り替え
ダークモードへの切り替えは、StateContainer
のProperty
に、CSS
で定義したクラス名がセットされることで、コンポーネントのStateHasChanged
が呼ばれ、UIに反映されます。
Blazorではコンポーネント間におけるデータの受け渡しにStateContainer
のような状態管理は必要になってきますね🤔
<div class="top-row pl-4 navbar navbar-dark">
<a class="navbar-brand" href="">BlazorDarkMode</a>
<button class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span>
</button>
</div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-plus" aria-hidden="true"></span> Counter
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="fetchdata">
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
</NavLink>
</li>
👇ボタンにより適応したいスタイルのクラス名をStateContainerのPropertyにセット
<li class="nav-item px-3">
<button class="btn btn-primary " @onclick="@(_=>StateContainer.Property = "")">デフォルト</button>
</li>
<li class="nav-item px-3">
<button class="btn btn-primary dark-mode-blue" @onclick="@(_=>StateContainer.Property = "dark-mode-blue")">ダークブルー</button>
</li>
<li class="nav-item px-3">
<button class="btn btn-primary dark-mode-black" @onclick="@(_=>StateContainer.Property = "dark-mode-black")">ブラック</button>
</li>
</ul>
</div>
@code {
[Parameter]
public StateContainer StateContainer { get; set; }
private bool collapseNavMenu = true;
private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
private void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
}
参考
Blazor WebAssembly - Dark Mode - CSS Variables - Applying Themes
ASP.NET Core Blazor 状態管理