Document the vendored tools, the theme helpers and the auth leftovers

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 12:43:07 +02:00
1 parent 483ed271e0
commit ca060f25fb
1 file changed
+37 -11
+37 -11
View File
@@ -40,22 +40,30 @@ dotnet publish decePubClient.csproj -c Release -o publish # static site lands
`global.json` pins SDK 10.0.100 with `rollForward: latestFeature`. There are no tests and no linter.
## CSS: Tailwind 4 + daisyUI 5, no Node
## CSS: Tailwind 4 + daisyUI 5, vendored, offline
`Styles/app.css` is compiled into `wwwroot/css/app.css` by the Tailwind CSS standalone binary with the daisyUI
plugin. `app.css` is gitignored and regenerated on every build.
- `tools/tailwind.sh` downloads Tailwind 4.3.3 and daisyUI 5.7.47 into the gitignored `.tools/tailwind/`, checking each
against a pinned sha256. To upgrade, change the versions and hashes there.
**Nothing in the build or the CI may download from github.com.** Tools live in the repo, and the CSS build runs
without network access. NuGet restore still uses nuget.org.
- `tools/tailwind/` holds the pinned tools:
- Tailwind 4.3.3 as `tailwindcss-linux-x64.xz`;
- daisyUI 5.7.47 (`daisyui.mjs`, `daisyui-theme.mjs`);
- daisyUI's component reference, `daisyui-llms.txt`.
- `.gitattributes` keeps these files byte-exact.
- `tools/tailwind.sh prepare` checks them against the sha256 values pinned in the script, and unpacks the binary into
the gitignored `.tools/tailwind/`. It needs `xz`.
- Only linux-x64 is vendored.
- The script's header explains how to upgrade or add a platform.
- The `TailwindCompile` target in the csproj runs it, so `dotnet build` / `publish` are enough, in CI as well. Debug
builds use `--optimize`, Release builds `--minify`. Pass `-p:SkipTailwind=true` to skip it.
- `IncludeTailwindOutput` adds the generated file to `Content` before static web assets are resolved. Without that, a
clean-clone publish would ship `app.css` without its `.gz`/`.br` copies and outside `service-worker-assets.js`.
The deploy workflow checks for `css/app.css.gz`.
- `bash tools/tailwind.sh watch` rebuilds the CSS live next to `dotnet watch`. `bash tools/tailwind.sh docs` fetches
daisyUI's `llms.txt` (its component reference) into `.tools/tailwind/`.
- Tailwind scans every file `.gitignore` doesn't exclude, and only generates the classes it finds written out
**whole**. Never build a class name by concatenation or interpolation (`"btn-" + size`, `$"btn-{size}"`). Write
- `bash tools/tailwind.sh watch` rebuilds the CSS live next to `dotnet watch`.
- Tailwind scans every file `.gitignore` doesn't exclude (minus `tools/`, `wwwroot/vendor/`, `Docs/`), and only
generates the classes it finds written out **whole**. Never build a class name by concatenation or interpolation (`"btn-" + size`, `$"btn-{size}"`). Write
every variant as a literal, e.g. in a switch arm.
Ionicons 4.5 (`wwwroot/vendor/ionicons.min.css`, `<i class="ion-md-...">`) is loaded as its own stylesheet.
@@ -68,6 +76,12 @@ Ionicons 4.5 (`wwwroot/vendor/ionicons.min.css`, `<i class="ion-md-...">`) is lo
from localStorage, so there is no flash before Blazor boots. After that, CascadingState calls it through
`AppStatusService.ApplyTheme` whenever the settings change. With `PreferSystemTheming` it leaves `data-theme` off,
and the OS decides.
- Change the theme flags through `PageSettings.SetDarkMode` / `SetGray` / `SetHue` and
`CascadingState.UpdatePageSettings`, which keep the flags consistent:
- choosing light or dark leaves system theming;
- picking a hue leaves grey.
- `AppStatusService.IsDarkTheme()` tells which variant is showing.
- Settings → General edits every theme setting. NavMenu's toggle and slider are shortcuts to the dark and hue ones.
- `Styles/neo.css` holds the neumorphic layer:
- `neo-raised` / `neo-inset` / `neo-flat`, with sizes `neo-2xs` / `neo-xs` / `neo-sm` / `neo-md` (these replace the
old `neomorph is-nxsmall` classes);
@@ -88,7 +102,11 @@ all of them in every theme. When you add or change one:
- Value-holding fields derive from `DInputBase<TValue>`, Blazor's `InputBase`, so `@bind-Value` and EditForm
validation work. Everything else derives from `DComponentBase` (`Class`, pass-through attributes, `Cx(...)`).
- Open/active state lives in C# (`@bind-IsOpen`, `@bind-Active`) and is rendered with daisyUI's `*-open` modifier
classes. No JS interop. `DCalendar` is the one exception: it wraps the vendored Cally web component.
classes. No JS interop.
- What daisyUI does with scripts has a script-free equivalent here: `DCheckbox.Indeterminate` renders
`aria-checked="mixed"`, and `DCarousel`'s navigation links to the slides' ids, which Blazor scrolls to.
- `DCalendar` is the one exception: it wraps the vendored Cally web component (`wwwroot/vendor/cally.js`).
- Images and links in `/styleguide` stay local (`wwwroot/imgs/styleguide/`).
- Don't name a parameter after an HTML attribute (`Title`, `Style`, `Id`, `Label`, ...). Blazor matches attributes
to parameters without regard to case, so the parameter would swallow the attribute.
- Components don't use `Localizer` or `CascadingState`; their user-visible strings are parameters.
@@ -123,13 +141,18 @@ take it as `[CascadingParameter] CascadingState`. It handles:
`nameof(Type)`.
- **IndexedDB** (DnetIndexedDb, database `data`) is reached through `IStorage`. Its schema is declared in
`GenericExtensions.AddIndexedDb()`, and you must bump `WithVersion` when you change it.
- Known gap: `IStorage` reads and writes a `ClientLogs` store that the schema never declares, so the error log writes
fail silently (they are wrapped in try/catch).
- The schema is at version 2, which added the `ClientLogs` store that `IStorage.AddLog` writes. Before it, every log
write failed silently.
**Auth.**
- `TokenAuthStateProvider` builds the auth state from the `AuthData` token in localStorage.
- `SetToken` is mostly commented out, so there is no real login flow yet.
- `AddApiAuthorization` and `Pages/Authentication.razor` are leftovers from the Blazor template.
- `Pages/Logout.razor` signs out through `TokenAuthStateProvider.LogoutAsync`, which clears the token and the local
database.
- `AddApiAuthorization`, `Pages/Authentication.razor` (`/authentication/{action}`) and `RedirectToLogin` are leftovers
from the Blazor OIDC template. That route's `RemoteAuthenticatorView` throws `InvalidCastException`, because the
registered `AuthenticationStateProvider` is `TokenAuthStateProvider`. The Login links that point there need a real
login page first.
**JS interop.**
- `wwwroot/main.js` defines `window.*` helpers. `Services/AppStatusService.cs` calls them, mostly through the
@@ -141,6 +164,9 @@ take it as `[CascadingParameter] CascadingState`. It handles:
## Deployment
Both workflows check the repo out with a plain `git fetch` from this Gitea, not `actions/checkout`, because the runner
would download that action from github.com. Never add a `uses:` step.
`.gitea/workflows/deploy.yml` runs on a `v*` tag or a manual dispatch, on a self-hosted runner that is also the web
host. The steps:
1. Publish the site.