Repository navigation
Refactor/UI redesign - #257
Merged
Merged
Conversation
Replaces the six-brand "Flat & Focused" theming with one light and one dark theme built from a shared token layer, and lays the CSS, font and motion groundwork for the redesign. See docs/adr/0013-editorial-design-system.md. Why now: the brand system had no consumer and blocked any design that assumes a known palette. Three defects came out of the audit and are fixed here — the dark-mode CSS hung on `data-mud-theme`, an attribute MudBlazor never emits; the Google Fonts link was blocked by the production CSP, so production silently rendered in system fonts; and roughly half of app.css was dead. - Tokens live twice on purpose (DesignTokens.cs for the MudBlazor theme, tokens.css for everything else) with TokensCssSyncTests guarding the pair and DesignTokensContrastTests asserting the documented WCAG ratios. - MudBlazor is imported into the lowest cascade layer, so overrides win on layer order without !important or a specificity war. - theme-boot.js stamps html[data-theme] before first paint; Program.cs reads the same value back so MudThemeProvider renders the right palette first time. - Fonts self-hosted under SIL OFL 1.1, which the CSP allows. - Interactive outlines get their own token: the 24% hairline reaches only 1.7:1 on canvas, short of the 3:1 WCAG 1.4.11 requires for control borders. legacy.css is transitional and holds the classes the not-yet-migrated pages still reference; it goes away with the component kit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ooter, page transition Second phase of the redesign (ADR 0013). The app shell now carries the editorial identity and fixes three navigation defects the audit found. - Header: serif wordmark, underline-rule nav links, a real search field that routes to /search (the page was reachable only by URL before), cart badge that pops on change, one LocaleMenu for language and currency instead of two icons, and a burger below md. - MobileNavDrawer: the storefront had no navigation at all on phones — the links simply disappeared under the md breakpoint. Serif links, settings and sign-in/out live in the drawer now. - Footer: four editorial columns covering every public route. The old footer linked /legal/privacy and /legal/imprint, which never existed. - PageTransition wraps @Body in both layouts and re-keys on path changes so the CSS enter animation replays; query-only changes keep the page mounted. - First kit components: OsButton and OsIconButton over native elements (MudButton needs ~6 selectors per variant to neutralise and has no loading state), OsBadge, OsSearchBar, AccountMenu, ThemeToggle with an animated sun/moon. - The brand link is a plain <a>: MudBlazor's colour utilities are declared !important, which beats the cascade layer. Documented in ui-conventions.md and the frontend agent. - PageHero and Home used inline 1fr 1fr grids that never collapsed; they share a responsive .os-split now. Verified in Chromium at 1280 and 390px, light and dark, incl. persistence across reload, the 404 route, and drawer open/close. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Third phase of the redesign (ADR 0013): the design system's component kit,
and the storefront, account and shared components moved onto it.
Kit (Components/Ui): OsCard, OsSection, OsPageHeader, OsBreadcrumbs,
OsDisplay, OsEyebrow, OsPrice, OsStat, OsStatusChip, OsSkeleton with four
composites, OsEmptyState, OsErrorState, OsQuantityStepper, OsProductImage,
OsReveal/OsRevealGroup, ConfirmDialog, AddToCartDialog and AddressForm.
OsDisplay and the reveal wrappers are plain classes because Razor cannot
pick an element tag at runtime; every page now renders a real <h1>.
Behavioural fixes that fell out of the migration:
- Search's add-to-cart button did nothing (an empty callback); it now opens
the same AddToCartDialog as the shop.
- Three divergent address forms (checkout, profile, onboarding) become one
MudForm-validated AddressForm over AddressFormModel; checkout validates
fields in place instead of a warning toast.
- Address deletion asks for confirmation.
- Product detail gets a /p/{slug} alias so advisor citations can navigate
without a category, and review dates use Formatting.Date instead of a
hard-coded culture.
- Every status switch collapses into StatusPresentation; every
Snackbar.Add into SnackbarExtensions (51 call sites in admin rewritten
mechanically, their layout redesign follows in the admin phase).
- Contrast: the vivid accent stays a fill colour; text uses --os-accent-ink.
Deleted: CheckoutAddressForm, QuantityStepper, StockBadge, LoadingWrapper,
ProductDetailSkeleton. PageHero, EmptyStateCard, ErrorStateWithRetry and
IconBadge remain until the admin, advisor and legal pages migrate.
legacy.css stays for the same reason and is removed in the polish phase.
A development-only gallery at /dev/kit renders every component in both
themes for review without signing in. New tests cover the kit, the status
mapping, the snackbar helpers, the address form and the page transition;
the suite is at 195.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Shop, product, category and search pages no longer require sign-in. Only the BFF was blocking anonymous reads — the gateway and the Catalog service already left their read groups open — so the change is mostly routing: - BFF: four GET/HEAD routes (products, categories, brands, reviews) with AuthorizationPolicy "anonymous", ordered before the authenticated catch-all. Writes on those paths still fall through to BffUserPolicy and the CSRF middleware. Both appsettings files carry them, because the Development file replaces the Routes section wholesale. - Gateway: the public catalog routes are now GET/HEAD only, with separate authenticated routes for writes; reviews, brands and admin reviews get routes at all — /api/v1/reviews/** had none, so product reviews have been 404ing since the endpoint was built. - Catalog: the stock decrement/restore POSTs were reachable anonymously through the public route. They now require CatalogAdminPolicy (nothing in the codebase calls them over HTTP; Basket uses the authenticated gRPC service). The product-by-slug cache key is tenant-scoped, so an anonymous miss on the default tenant cannot populate an organisation's entry. - Rate limits, retuned for browsing that costs 2-3 calls per page: the gateway's anonymous per-IP quota goes from 30 to 120/min, the Catalog public bucket (one service-wide ceiling) from 100 to 600/min. - Web: [Authorize] removed from the four pages; add-to-cart, the buy box and the review form redirect to or ask for sign-in. Sign-in controls navigate with a full load through LoginRedirect — Blazor's router would otherwise intercept an <a href="/bff/login"> and show the 404 page. Tests: BFF route-policy tests (anonymous reads pass, other reads and all writes stay 401/403), catalog integration test updated to expect 401 / 403 / 204 for anonymous / customer / admin stock adjustments, handler test carries the tenant context. Verified in Chromium against the running stack: 170 products browsable signed out, buy and review actions lead to Auth0, reviews load empty instead of failing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Home, Shop, Categories and the product page get their real layouts: - Home now loads live data — a category bento fed by the five largest categories, a scroll-snapping rail of the newest products, and a masthead that reports the actual catalogue size. The four hard-coded category tiles and their resource keys are gone. - Shop gains a sticky filter rail from md up and a bottom-sheet filter below it, both rendered from one definition. Category links carry their product counts and mark the active one; the category list scrolls on its own so price, sorting and the buttons stay in view. - Categories becomes a bento with the largest category leading full width. - The product page lists SKU, category and availability instead of SKU alone. Three defects surfaced during the browser pass and are fixed here: - Placeholder hues came from the first four bytes of the id, so every seeded category rendered the same colour. Hashing all sixteen bytes separates them. - The revealed flag moved from a CSS class to `data-revealed`. Blazor owns the class attribute of the elements it renders and rewrote it on re-render, which dropped the flag and left already-revealed tiles invisible for good. - `.os-page` filled its enter animation forwards, which kept it a containing block for fixed descendants and pushed the shop's filter sheet out of the viewport. It now fills backwards. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Checkout becomes three numbered editorial steps — address, payment, review — with the summary sticky beside them. A failed validation now moves focus to the first invalid field instead of only marking it, via a new focusFirstInvalid helper in motion.js and IMotionService. - The success page says what it is waiting for: the order number is written by a Service Bus worker, so the page shows a confirming state with the attempt count and, after six tries, a still-processing note with a retry. - The order detail page replaces MudTimeline with a hairline rail: one dot per status, mono timestamps, the newest entry emphasised. - The cart drawer gains a quantity stepper, so a line can be corrected without opening the cart page. - MudGrid, MudItem and MudStack are gone from cart, checkout, order detail, profile and onboarding; those pages now use their own grids and .os-stack. - Onboarding is two numbered steps with an action bar that sticks on a phone. The kit gallery at /dev/kit gains the numbered-step and timeline patterns, because every page using them sits behind a sign-in and could otherwise not be reviewed without a session. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Twelve admin pages, the admin layout and a new AdminListShell, moved onto the Os* kit: MudGrid/MudPaper cards become OsSection + OsCard, the copied spinner blocks become OsTableSkeleton, and every list now branches skeleton / error / empty / content in one place instead of once per page. Behaviour that changed with the markup: - destructive actions (order cancel, coupon deactivation, review rejection) ask through ConfirmDialog; approving a review still does not - the coupon dialog and both forms are MudForm-validated, Save stays disabled until the form is valid, and a failed submit focuses the first bad field - lists sort and page client-side (MudTableSortLabel, 25 rows, MudTablePager) - admin order and invoice statuses resolve through StatusPresentation, so the duplicate Admin.Orders.Status* resources are gone PageHero, EmptyStateCard and ErrorStateWithRetry have no callers left and are deleted. /dev/kit gains a data-table section: the admin routes sit behind a login, so the gallery is where the table styling can actually be reviewed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The drawer moves onto the kit: serif greeting with the conversation id in mono,
an empty state with three starter prompts, an inverted ink bubble for the user
against a hairline card for the assistant, and a composer on a hairline footer.
Messages animate in through os-msg.
Two fixes came with it:
- citations linked to /products/{slug}, which is not a route — every citation
was a 404. They now point at the /p/{slug} alias.
- the drawer carried ~14 hard-coded German strings; they are Advisor.* resources
now, including the confirm and cancel replies sent back to the model.
The FAB is a native .os-fab button instead of MudFab, so it gets the ink circle
and the same hover lift as the rest of the UI.
/dev/kit gains an advisor-thread section — like the admin tables, the drawer is
only reachable when signed in and cannot otherwise be reviewed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
wwwroot/css/*.css, wwwroot/js/*.js and index.html are served under stable URLs. UseStaticFiles emitted no Cache-Control for them, so a browser was free to reuse them heuristically and paired the previous build's stylesheets with the current markup — the UI then rendered half-styled until a hard reload. ETag revalidation costs one 304 per file; the self-hosted fonts, whose names change when their contents do, stay immutable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…trings The sticky rail and the category list inside it could both overflow, so two scrollbars stood side by side and the outer one travelled the ~15px the list was over. The rail is now clipped and lays its children out as a column; the category list, the only part that grows, takes the remaining height. Price and sorting stay visible at every viewport height, which was the point of capping the list in the first place. Five German strings still addressed the reader with "Sie" while the rest of the app uses "du". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- ArticleLayout: page header with the h1, numbered sections and a sticky mono table of contents from three sections up. The seven legal and support pages are rebuilt on it; their text moved into AppStrings (de/en). - Imprint cites § 5 DDG and § 18 Abs. 2 MStV, which replaced § 5 TMG and § 55 Abs. 2 RStV. - ApiResult default messages resolve from AppStrings in the current UI culture. - 404 page on the Os kit; framework error bar restyled. - legacy.css and IconBadge removed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
MudBlazor 9 no longer wires a custom ActivatorContent to the menu; the activator has to call MenuContext.ToggleAsync itself. Both header menus rendered a plain OsIconButton without a handler, so clicks did nothing. The activators now toggle the menu and expose aria-haspopup/aria-expanded. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The invoice now follows the storefront design system: Instrument Serif display type, Manrope text, Geist Mono figures, hairline tables and a single accent. Position numbers, a meta block (number, date, order) and a totals panel replace the unstyled table; totals never split across a page break. The fonts ship as embedded resources (SIL OFL 1.1, licences alongside) instead of relying on Arial being installed, which is not the case in Linux containers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Type of change
Checklist
dotnet build OrderSphere.slnx)dotnet test)Breaking changes
References