As a brief interlude to my ProcGen Fun series, I'd like to write up how I created a playground website which allows you to run the code I've been writing in that series.
The procedural generation is all being written in C#, so we need a website which can run .NET code (directly or indirectly). For many years the way to do this would be to build a static website (using HTML, CSS and JavaScript) which called a web API implemented in .NET. However, with the recent advent of WebAssembly (WASM) we can now run binary code client-side. Blazor is the .NET technology which uses WASM to run .NET code in the browser, using a templating syntax called Razor.
In my case, I've built a static website using Blazor, using Tailwind CSS for styling, and deployed it to GitHub Pages. What follows is a list of steps that you can follow, based on what I did. I hope some or all of it is helpful!
1. Add a blank Blazor project
In Visual Studio, create a new blank Blazor project. You can do this using "Add New Project" and selecting the "Blazor WebAssembly App Empty" template. You can also do this via the command line, using the following command.
dotnet new blazorwasm-empty -o YourProjectName
The reason I suggest using the "empty" template is that the standard template comes with Bootstrap CSS pre-installed, and we want to use Tailwind CSS.
2. Upgrade to the latest .NET
For some reason, on my machine step 1 generated a Blazor project targetting .NET 7, which is out of support. If this happens to you, I suggest upgrading to .NET 8 (the latest LTS version of .NET at the time of writing).
To do this, change the TargetFramework
property in the project file to
net8.0
, and upgrade the two Blazor NuGet packages to the latest v8 release.
3. Add Tailwind
The easiest way to add Tailwind CSS to a project is to install it using npm
,
and then use the init
command to create a blank config file.
npm install -D tailwindcss
npx tailwindcss init
In your newly-created config file, set the template paths (the content
setting) as follows. This tells Tailwind where to look for class names when
compiling the CSS.
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./**/*.{razor,html}"],
theme: {
extend: {},
},
plugins: [],
};
Then create your main CSS file. I called mine tailwind.css
and put it in the
Styles
directory.
@tailwind base;
@tailwind components;
@tailwind utilities;
The build script we're going to use (see below) will compile this file and
output the result to css/tailwind.css
. So in your index.html
file, add a
reference to this path in the head:
<!DOCTYPE html>
<html lang="en">
<head>
...
<link href="css/tailwind.css" rel="stylesheet" />
...
</head>
<body>
...
</body>
</html>
Now we can use Tailwind classes in our HTML files and Razor components.
For more information on how to configure Tailwind, see the official docs.
4. Add deployment to GitHub Pages
There are a few things which need to be done before you can publish your site to GitHub Pages. Fortunately there is a NuGet package called PublishSPAforGitHubPages.Build which does lots of the work for you! I suggest having a read of the documentation to understand what it does.
You can install it using this command.
dotnet add package PublishSPAforGitHubPages.Build
Then you need a GitHub Actions script, to be run on every push to your main
branch. This should be placed in a YAML file (I called mine gh-pages.yml
) in
the .github/workflows
folder of your repository.
Here's the script I used. If you use this script then you might need to change various branch names and paths to match your setup.
name: GitHub Pages Deployment
on:
push:
branches:
- main
jobs:
deploy:
permissions:
contents: write
runs-on: ubuntu-latest
steps:
# Checkout the code
- uses: actions/checkout@v4
# Install .NET SDK
- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
# Install NPM dependencies
- name: Install NPM dependencies
working-directory: ./ProcGenFun.Blazor
run: npm i
# Compile Tailwind CSS
- name: Compile Tailwind CSS
working-directory: ./ProcGenFun.Blazor
run: npx tailwindcss -i ./Styles/tailwind.css -o ./wwwroot/css/tailwind.css
# Publish the site
- name: Publish
run: dotnet publish ./ProcGenFun.Blazor/ProcGenFun.Blazor.csproj -c:Release -o:publish -p:GHPages=true
# Deploy the site
- name: Deploy
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: publish/wwwroot
force_orphan: true
publish_branch: gh-pages
The key thing here is that we compile the Tailwind CSS before compiling the
Blazor project, so that the CSS output is included in the compiled bundle. We
then push this bundle in a single orphaned commit to the gh-pages
branch.
The final step is to set up GitHub Pages to deploy from the gh-pages
branch.
This means that every time the gh-pages
branch changes (i.e. when our script
is run), GitHub Pages will update our public website to match the contents of
the repository on that branch. In the GitHub web UI, go to Settings (for the
repository), click on Pages, and then under "branch" select gh-pages
.
5. Add custom domain
Your site will now be deployed at {username}.github.io/{repository}
(e.g. mine
was available at djcarter85.github.io/ProcGenFun
). Often you'll want to use a
custom domain instead; for example, I have published mine at
procgenfun.carterdan.net.
The GitHub
docs
for this are reasonably clear (including how to configure the settings with your
DNS provider), but it's worth noting that if you use the GitHub web UI to
configure the custom domain, it'll make a change on your gh-pages
branch which
will be overwritten the next time the GitHub Actions script is run. Instead, add
the CNAME
file yourself in the wwwroot
directory of your Blazor project,
specifying your desired custom domain name on a single line. This will then be
placed in the right location within your published bundle.
As a final step, you'll need to set the site's base path in your build script.
The NuGet package referenced above assumes you're not using a custom domain
name, and sets the base path to /{repository}
. You can set the base path back
to /
by adding the GHPagesBase
parameter when publishing the site in your
GitHub Actions script:
...
# Publish the site
- name: Publish
run: dotnet publish ./ProcGenFun.Blazor/ProcGenFun.Blazor.csproj -c:Release -o:publish -p:GHPages=true -p:GHPagesBase=/
...
Appendix: development
The easiest way I found to develop using this setup is to use VSCode. In one terminal I set up Tailwind to automatically recompile the CSS when anything changes.
cd ProcGenFun.Blazor
npx tailwindcss -i .\Styles\tailwind.css -o .\wwwroot\css\tailwind.css --watch
And then in another terminal I started the Blazor project, using the watch
command to reload the site when anything changes.
cd ProcGenFun.Blazor
dotnet watch
This gives you fast feedback on the changes you make as you're developing your site.
Further reading
Here's a couple of related blog posts which I found helpful.