Build CSS with Tailwind 4.3.3 standalone and daisyUI 5.7.47

tools/tailwind.sh downloads the pinned Tailwind binary and daisyUI's .mjs
plugins into the gitignored .tools/tailwind/, checking each against a pinned
sha256, so neither developers nor CI need Node. MSBuild compiles
Styles/app.css into wwwroot/css/app.css (also gitignored) on every build,
skipping the step when no source changed. The output is added to Content before
ResolveProjectStaticWebAssets, so a clean-clone publish still gets its .gz/.br
copies and a service-worker-assets.js entry.

index.html now loads css/app.css, plus ionicons as its own stylesheet. The old
bundle stays in the tree until the markup is migrated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-10-03 11:24:51 +02:00
1 parent b9dc865053
commit 3551da1b02
5 files changed
+141 -1

No files matched your search

+4
View File
@@ -363,3 +363,7 @@ MigrationBackup/
FodyWeavers.xsd FodyWeavers.xsd
/.idea/.idea.decePubClient/.idea /.idea/.idea.decePubClient/.idea
/.idea/config /.idea/config
# Tailwind standalone + daisyUI downloads (tools/tailwind.sh) and the CSS they generate
.tools/
wwwroot/css/app.css
+11
View File
@@ -0,0 +1,11 @@
/* Entry point compiled by Tailwind CSS standalone (tools/tailwind.sh, TailwindCompile in decePubClient.csproj) into
wwwroot/css/app.css. Tailwind scans every file under the project that .gitignore doesn't exclude. */
@import "tailwindcss" source("../");
@source not "../wwwroot/vendor";
@source not "../Docs";
@plugin "../.tools/tailwind/daisyui.mjs" {
themes: false;
logs: false;
}
+36
View File
@@ -26,6 +26,42 @@
<ServiceWorker Include="wwwroot\service-worker.js" PublishedContent="wwwroot\service-worker.published.js" /> <ServiceWorker Include="wwwroot\service-worker.js" PublishedContent="wwwroot\service-worker.published.js" />
</ItemGroup> </ItemGroup>
<!-- Tailwind CSS standalone + daisyUI (tools/tailwind.sh) compile Styles/app.css into wwwroot/css/app.css on every
build. The output is gitignored, so on a clean clone it doesn't exist when the wwwroot\** Content glob is
evaluated; IncludeTailwindOutput adds it before ResolveProjectStaticWebAssets, without which it would be
published without its .gz/.br copies and outside service-worker-assets.js. -->
<PropertyGroup>
<TailwindToolsDir>$(MSBuildProjectDirectory)/.tools/tailwind/</TailwindToolsDir>
<TailwindExe>$(TailwindToolsDir)tailwindcss</TailwindExe>
<TailwindMode Condition="'$(Configuration)' == 'Release'">--minify</TailwindMode>
<TailwindMode Condition="'$(TailwindMode)' == ''">--optimize</TailwindMode>
</PropertyGroup>
<ItemGroup>
<TailwindSource Include="Styles/**/*.css;**/*.razor;**/*.cs;wwwroot/index.html" Exclude="bin/**;obj/**;.tools/**" />
</ItemGroup>
<Target Name="EnsureTailwindTools" Condition="!Exists('$(TailwindExe)') Or !Exists('$(TailwindToolsDir)daisyui.mjs') Or !Exists('$(TailwindToolsDir)daisyui-theme.mjs')">
<Exec Command="bash tools/tailwind.sh fetch" WorkingDirectory="$(MSBuildProjectDirectory)" />
</Target>
<Target Name="TailwindCompile" DependsOnTargets="EnsureTailwindTools"
Condition="'$(DesignTimeBuild)' != 'true' And '$(NoBuild)' != 'true' And '$(SkipTailwind)' != 'true'"
Inputs="@(TailwindSource);$(TailwindExe);$(TailwindToolsDir)daisyui.mjs;$(TailwindToolsDir)daisyui-theme.mjs;$(MSBuildProjectFullPath)"
Outputs="wwwroot/css/app.css;$(IntermediateOutputPath)tailwind.stamp">
<Exec Command="&quot;$(TailwindExe)&quot; -i Styles/app.css -o wwwroot/css/app.css $(TailwindMode)" WorkingDirectory="$(MSBuildProjectDirectory)" />
<!-- The CLI leaves an unchanged output untouched, which would make this target rerun on every build. -->
<Touch Files="wwwroot/css/app.css;$(IntermediateOutputPath)tailwind.stamp" AlwaysCreate="true" />
</Target>
<Target Name="IncludeTailwindOutput" BeforeTargets="ResolveProjectStaticWebAssets" DependsOnTargets="TailwindCompile">
<!-- Spelled exactly like the SDK's wwwroot\** glob: publish de-duplicates by comparing OriginalItemSpec strings. -->
<ItemGroup>
<Content Remove="wwwroot\css\app.css" />
<Content Include="wwwroot\css\app.css" Condition="Exists('wwwroot\css\app.css')" />
</ItemGroup>
</Target>
<ItemGroup> <ItemGroup>
<Compile Update="Resources\ErrorMessages.Designer.cs"> <Compile Update="Resources\ErrorMessages.Designer.cs">
<DesignTime>True</DesignTime> <DesignTime>True</DesignTime>
+88
View File
@@ -0,0 +1,88 @@
#!/usr/bin/env bash
# Tailwind CSS standalone + daisyUI, with no Node. MSBuild runs `fetch` (decePubClient.csproj, EnsureTailwindTools)
# and then compiles Styles/app.css itself; run `watch` beside `dotnet watch` for live CSS work.
# bash tools/tailwind.sh fetch download and verify the pinned binaries into .tools/tailwind/
# bash tools/tailwind.sh build one-off build of wwwroot/css/app.css (what MSBuild does in Debug)
# bash tools/tailwind.sh watch rebuild wwwroot/css/app.css on every change
# bash tools/tailwind.sh docs fetch daisyUI's llms.txt (component reference, unpinned) into .tools/tailwind/
set -euo pipefail
TAILWIND_VERSION=4.3.3
DAISYUI_VERSION=5.7.47
# sha256 of each release asset. Tailwind's are from its release sha256sums.txt; daisyUI publishes none, so these were
# computed from the v5.7.47 release assets.
pinned_sha256() {
case "$1" in
tailwindcss-linux-x64) echo dc61b3ac6b8c9ca874c0cc4c57b2409791a64c5540404ca5f5367360babc313a ;;
tailwindcss-linux-x64-musl) echo a04d34ceacc8f52cbe8920ad846cdeb61d3d0021dba32db0d1f77c9d9fad7a6c ;;
tailwindcss-linux-arm64) echo 55fd0b241214eff3de1e8ee4f22796662f2d2e7a49bcfca7477cfd0bac398195 ;;
tailwindcss-linux-arm64-musl) echo 71ea4be79c9de9827545682df3e040053fb535d37c71ed2cfdedf9385a0868e0 ;;
tailwindcss-macos-x64) echo 7922e0953f2110c05976e3bf58f14e643d90427575e766b7d433f5f80cbee7e1 ;;
tailwindcss-macos-arm64) echo cdf646702987a743464dff4d9c60fd4480d1c1e73dd819a9a67f1078815dce9d ;;
daisyui.mjs) echo 85819d3fe86a852237b439b13f481481aac58e562ac47694cd11c3038e994f2a ;;
daisyui-theme.mjs) echo d92d2f488933dab757f046e81ebd4986e51e9423307798dc3545d09a15542c89 ;;
*) echo "tailwind.sh: no pinned checksum for $1" >&2; exit 1 ;;
esac
}
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
TOOLS="$ROOT/.tools/tailwind"
TAILWIND="$TOOLS/tailwindcss"
tailwind_asset() {
local os arch libc=""
case "$(uname -s)" in
Linux) os=linux ;;
Darwin) os=macos ;;
*) echo "tailwind.sh: unsupported OS $(uname -s)" >&2; exit 1 ;;
esac
case "$(uname -m)" in
x86_64|amd64) arch=x64 ;;
aarch64|arm64) arch=arm64 ;;
*) echo "tailwind.sh: unsupported architecture $(uname -m)" >&2; exit 1 ;;
esac
if [ "$os" = linux ] && ldd --version 2>&1 | grep -qi musl; then libc=-musl; fi
echo "tailwindcss-$os-$arch$libc"
}
sha256_of() {
if command -v sha256sum >/dev/null; then sha256sum "$1" | cut -d' ' -f1; else shasum -a 256 "$1" | cut -d' ' -f1; fi
}
# fetch_one <url> <destination> <expected sha256>
fetch_one() {
local url="$1" dest="$2" expected="$3"
if [ -f "$dest" ] && [ "$(sha256_of "$dest")" = "$expected" ]; then return; fi
echo "tailwind.sh: downloading $url"
curl -fsSL --retry 3 -o "$dest.part" "$url"
local actual
actual="$(sha256_of "$dest.part")"
if [ "$actual" != "$expected" ]; then
rm -f "$dest.part"
echo "tailwind.sh: checksum mismatch for $url (expected $expected, got $actual)" >&2
exit 1
fi
mv "$dest.part" "$dest"
}
fetch() {
mkdir -p "$TOOLS"
local asset
asset="$(tailwind_asset)"
fetch_one "https://github.com/tailwindlabs/tailwindcss/releases/download/v$TAILWIND_VERSION/$asset" \
"$TAILWIND" "$(pinned_sha256 "$asset")"
chmod +x "$TAILWIND"
for f in daisyui.mjs daisyui-theme.mjs; do
fetch_one "https://github.com/saadeghi/daisyui/releases/download/v$DAISYUI_VERSION/$f" "$TOOLS/$f" "$(pinned_sha256 "$f")"
done
}
cd "$ROOT"
case "${1:-build}" in
fetch) fetch ;;
build) fetch; "$TAILWIND" -i Styles/app.css -o wwwroot/css/app.css --optimize ;;
watch) fetch; exec "$TAILWIND" -i Styles/app.css -o wwwroot/css/app.css --watch ;;
docs) mkdir -p "$TOOLS"; curl -fsSL -o "$TOOLS/llms.txt" https://daisyui.com/llms.txt; echo "$TOOLS/llms.txt" ;;
*) echo "usage: tailwind.sh [fetch|build|watch|docs]" >&2; exit 2 ;;
esac
+2 -1
View File
@@ -6,7 +6,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Index</title> <title>Index</title>
<base href="/" /> <base href="/" />
<link href="css/style.min.css" rel="stylesheet" /> <link href="vendor/ionicons.min.css" rel="stylesheet" />
<link href="css/app.css" rel="stylesheet" />
<link href="manifest.json" rel="manifest" /> <link href="manifest.json" rel="manifest" />
<link rel="apple-touch-icon" sizes="512x512" href="imgs/icon-512.png" /> <link rel="apple-touch-icon" sizes="512x512" href="imgs/icon-512.png" />
<link rel="apple-touch-icon" sizes="192x192" href="imgs/icon-192.png" /> <link rel="apple-touch-icon" sizes="192x192" href="imgs/icon-192.png" />