Bootstrap persisted theme before hydration
This commit is contained in:
@@ -49,6 +49,7 @@ It is intentionally implementation-focused:
|
|||||||
| 2026-03-21 | Post-P1 fix 1 | Completed | Closed the 768px-1023px navigation gap by adding a shell hamburger menu and drawer so primary navigation never disappears at tablet widths. |
|
| 2026-03-21 | Post-P1 fix 1 | Completed | Closed the 768px-1023px navigation gap by adding a shell hamburger menu and drawer so primary navigation never disappears at tablet widths. |
|
||||||
| 2026-03-21 | Post-P1 fix 2 | Completed | Replaced the most visible light-only surface and control colors with theme-aware tokens so switching between `Light`, `Dark`, and `System` produces a clear visual change. |
|
| 2026-03-21 | Post-P1 fix 2 | Completed | Replaced the most visible light-only surface and control colors with theme-aware tokens so switching between `Light`, `Dark`, and `System` produces a clear visual change. |
|
||||||
| 2026-03-21 | Post-P1 fix 3 | Completed | Restored layout-level shell interactivity by rendering routed content in `InteractiveServer` mode, which re-enabled shell event handlers such as the hamburger menu and theme selector. |
|
| 2026-03-21 | Post-P1 fix 3 | Completed | Restored layout-level shell interactivity by rendering routed content in `InteractiveServer` mode, which re-enabled shell event handlers such as the hamburger menu and theme selector. |
|
||||||
|
| 2026-03-21 | Post-P1 fix 4 | Completed | Added early theme bootstrapping in `App.razor` and `theme.js` so the stored mode is applied before hydration and remains visible after refresh. |
|
||||||
|
|
||||||
### Lessons Learned
|
### Lessons Learned
|
||||||
|
|
||||||
@@ -67,6 +68,7 @@ It is intentionally implementation-focused:
|
|||||||
- Responsive shell design needs an explicit tablet state, not just desktop and phone states. The original breakpoints left a navigation dead zone between the top nav and bottom nav layouts.
|
- Responsive shell design needs an explicit tablet state, not just desktop and phone states. The original breakpoints left a navigation dead zone between the top nav and bottom nav layouts.
|
||||||
- Theme infrastructure is not enough on its own. Any surface that keeps hardcoded light values will make the theme switch feel broken even when the selector logic is correct.
|
- Theme infrastructure is not enough on its own. Any surface that keeps hardcoded light values will make the theme switch feel broken even when the selector logic is correct.
|
||||||
- In Blazor Web Apps, page-level render modes do not automatically make layout-level controls interactive in the way this shell expects. The routed shell itself needs an interactive render boundary.
|
- In Blazor Web Apps, page-level render modes do not automatically make layout-level controls interactive in the way this shell expects. The routed shell itself needs an interactive render boundary.
|
||||||
|
- Persisted theme state should be applied before Blazor hydrates, not only after layout initialization. Otherwise refresh can look broken even when storage writes succeed.
|
||||||
|
|
||||||
## Target Outcomes
|
## Target Outcomes
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
<link rel="stylesheet" href="@Assets["lib/bootstrap/dist/css/bootstrap.min.css"]" />
|
<link rel="stylesheet" href="@Assets["lib/bootstrap/dist/css/bootstrap.min.css"]" />
|
||||||
<link rel="stylesheet" href="@Assets["app.css"]" />
|
<link rel="stylesheet" href="@Assets["app.css"]" />
|
||||||
<link rel="stylesheet" href="@Assets["RolemasterDb.App.styles.css"]" />
|
<link rel="stylesheet" href="@Assets["RolemasterDb.App.styles.css"]" />
|
||||||
|
<script src="@Assets["theme.js"]"></script>
|
||||||
|
<script>window.rolemasterTheme?.init("rolemaster.theme.mode");</script>
|
||||||
<ImportMap />
|
<ImportMap />
|
||||||
<link rel="icon" type="image/png" href="favicon.png" />
|
<link rel="icon" type="image/png" href="favicon.png" />
|
||||||
<HeadOutlet />
|
<HeadOutlet />
|
||||||
@@ -20,7 +22,6 @@
|
|||||||
<body>
|
<body>
|
||||||
<Routes @rendermode="InteractiveServer" />
|
<Routes @rendermode="InteractiveServer" />
|
||||||
<ReconnectModal />
|
<ReconnectModal />
|
||||||
<script src="@Assets["theme.js"]"></script>
|
|
||||||
<script src="@Assets["tables.js"]"></script>
|
<script src="@Assets["tables.js"]"></script>
|
||||||
<script src="@Assets["_framework/blazor.web.js"]"></script>
|
<script src="@Assets["_framework/blazor.web.js"]"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -1,5 +1,23 @@
|
|||||||
window.rolemasterTheme = {
|
window.rolemasterTheme = {
|
||||||
|
storageKey: "rolemaster.theme.mode",
|
||||||
|
|
||||||
apply(mode) {
|
apply(mode) {
|
||||||
document.documentElement.dataset.theme = mode || "system";
|
document.documentElement.dataset.theme = mode || "system";
|
||||||
|
},
|
||||||
|
|
||||||
|
init(storageKey) {
|
||||||
|
if (storageKey) {
|
||||||
|
this.storageKey = storageKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mode = "system";
|
||||||
|
|
||||||
|
try {
|
||||||
|
mode = localStorage.getItem(this.storageKey) || "system";
|
||||||
|
} catch {
|
||||||
|
mode = "system";
|
||||||
|
}
|
||||||
|
|
||||||
|
this.apply(mode);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user