なつねこメモ

主にプログラミング関連のメモ帳 ♪(✿╹ヮ╹)ノ 書いてあるコードは自己責任でご自由にどうぞ。記事本文の無断転載は禁止です。

.NET MAUI Blazor Hybrid アプリで Tailwind CSS を使いたい

.NET MAUI Blazor Hybrid アプリというものがあります。
これは、 .NET でクロスプラットフォームなアプリを作成するためのフレームワークで、 Xamarin.Forms の後継です。
同様に、 ASP.NET Core Blazor というものがあり、これは .NET と C# を利用して、 Web アプリ (バックエンドではなくフロントエンドである) を作ることが出来るフレームワークです。
これらを組み合わせて、 .NET のみでいわゆるガワネイティブアプリを実現することが出来る仕組みが .NET MAUI Blazor Hybrid アプリです。

メリットとしては、 OS のネイティブ機能をフルで使うことが可能な一方、 iOS や macOS、 Android、 Windows 間で統一した UI 表現をすることが出来ます。
この記事では、そんなフレームワークである .NET MAUI Blazor Hybrid アプリで Tailwind CSS を使ってスタイリングを行います。

インターネットで探して出てくるものは、開発中に裏で npx tailwindcss --watch を動かす手法です。
しかし、これではビルド時の手順が増えたり、 tailwindcss コマンドが落ちてしまったケースに気がつきにくい、ハンドリングが面倒などの問題があります。
そこで、今回は Play CDN 上で提供されている Web のみで完結する版を使用することで、これらの煩わしさから解消されようと思います。

まず、アプリケーションルートとなる Razor テンプレートに、以下のような JavaScript を書きます。

@inherits LayoutComponentBase

<script>
  window.onApplicationInitialize = ({ configuration, ...rest }) => {
    if (configuration === "DEBUG") {
      const head = document.head;
      const { content } = rest;

      const script = document.createElement("script");
      script.src = "https://cdn.tailwindcss.com";
      script.onload = () => {
        // workaround for TailwindCSS CDN execution
        head.appendChild(document.createElement("script"));
      };

      head.appendChild(script);

      const config = document.createElement("script");
      config.innerHTML = `const tailwind = window.tailwind || {};${content}`;
      head.appendChild(config);
    }
  };
</script>

やっていることは簡単で、公式インストール手順にある CDN の読み込みと設定の記述を行っているだけです。
ただし、 Release ビルドでは事前にビルドしたものを使用したいこと、設定ファイルは共通化したいことなどの理由から、 .NET 側から起動後にこの関数を実行、 script タグを差し込みます。
ちなみに script.onload を設定しているのは、後から差し込んだ場合、初期化タイミングの違いにより Tailwind 側がクラスを読み取れないため、 DOM に変更を加えることで意図的に描画を再実行しています。

同様にして、 C# コード側も書いておきましょう:

// @inject IJSRuntime JsRuntime してね
@code
{
    protected override async Task OnInitializedAsync()
    {
#if DEBUG
        var path = new StreamReader(await FileSystem.Current.OpenAppPackageFileAsync("tailwind.config.js"));
        var content = await path.ReadToEndAsync();
        await JsRuntime.InvokeVoidAsync("onApplicationInitialize", new { configuration = "DEBUG", content = content.Replace("module.exports", "tailwind.config") });
#endif
    }
}

中身は単純で、 tailwind.config.js を読み込み、起動後に window.onApplicationInitialize を引数付きで呼び出しているだけ。
戻り値は特に必要が無い (し何も返していない) ので、 InvokeVoidAsync にしておきましょう。

あとは Debug ビルドで起動し、適当に <h1 class="text-2xl">Hello!?</h1> とすれば、その場でスタイルが当たります。
楽ですね。

ということで、メモでした。