Repository navigation
Releases: demonjic/Osirion.Blazor
Releases · demonjic/Osirion.Blazor
Release list
4.2.0
Added
- Osirion.Blazor.Templates: a new template package.
dotnet new install Osirion.Blazor.Templatesthendotnet new osirion-blog -n MyBlogcreates a Markdown blog on .NET 10 with static server-side rendering: the newest posts on the home page, a blog list, posts, an About page, a 404 page, RSS, Atom and a sitemap, with the menu and footer fromappsettings.json.--SiteNameand--SiteUrlfill in the name and public address. The template references the library version it is released with; tests hold the two equal.tools/verify-template.shtimes the walkthrough on a clean NuGet cache against packed packages - Osirion.Blazor.Templates:
dotnet new osirion-docscreates a Markdown documentation site: a home page, pages incontent/docswith side navigation in front matterorder, a table of contents, previous and next links, a 404 page and a sitemap;/docsleads to the first page - Osirion.Blazor.Templates:
dotnet new osirion-sitecreates a product or company site: a home page with a hero, three steps and the latest news, news incontent/newswith RSS, About and Contact pages, and a sitemap. The contact page offers an email link; the comment beside it explains how to switch on the form with the email service - Examples: the Bootstrap example site, which getosirion.com runs, renders its Google Analytics, Clarity and Matomo trackers only after the visitor accepts analytics cookies (
Osirion:Analytics:Consent:RequireConsent), and none in Development. It loaded all three on the first page view, before the cookie banner was answered, and local runs sent page views to the live accounts - Docs: the getting started guide opens with the template, three commands to a running blog, before the steps for an existing app
- Docs: the example site has its remaining pages: a home page that follows the site plan (the quickest path, a live palette picker, the packages, featured components, the latest posts),
/about,/contact(the channels that work, no form because the site has no email provider),/privacy-policy(the cookies and the three analytics services the site runs, and how to withdraw consent), and/showcase, which now lists the sites built with Osirion.Blazor in place of the 4.0 component showcase that every component page covers with a live demo. The shared privacy page stays a placeholder unlessExample:SitePrivacyPolicyis true. - Docs: the component pages that said "Available from 4.1" for
OsirionAnnouncementBar,OsirionAuthorCard, the Plausible and Umami trackers and the feeds now say 4.0, where the changelog and the v4.0.0 tag have them; pages no longer tell readers to callAddJSComponents()whenAddOsirionalready registers the script loader, and the README snippet drops the extra call - Docs: the documentation sidebar has a Guides group under Getting started, with its page at
/docs/guidesand twelve guides: installation, configuration, content providers, deployment, how content is organized, the front matter reference, localization, SEO, accessibility, security, migration from 3.x and 4.0, and troubleshooting. Each was checked against the code and its snippets compiled; the limits found in the code (for examplepermalinkignored for content items, a missinglangread asen) are stated where they matter - Docs: every component page moves to
/components/<name>, and the setup pages of each package to/docs/packages/<package>. Each of the 72 earlier addresses under/docs/componentsanswers with a 301 to its new page, fromredirect_fromfront matter. The example site gets a documentation sidebar grouped by what components are for, previous and next links in that order, and a component gallery at/components - Docs: the example site has a theme page at
/themes: the palette picker and the theme toggle, the palettes and what each is inspired by, how to make your own, and every colour token with a chip in the current palette and the pairs the contrast tests hold it to. It heads the Theming group of the sidebar - Docs: the card pages follow the component page template: a lead, the package, namespace and source link, a live demo with its compiled source, and a parameter table generated from the component (
<!-- osirion:parameters Name -->), which a test holds to the code.OsirionMetricCardhas its own page - Docs: the forms and feedback pages (
OsirionContactForm,OsirionCookieConsent,OsirionAnnouncementBar,OsirionContentNotFound) follow the component page template, with live contact form and empty state demos and generated parameter tables - Docs: the theming pages (
OsirionStyles,ThemeToggle,ThemePicker, palettes) follow the component page template, with live toggle and picker demos and generated parameter tables.ThemeToggle.OnThemeChanged, which is never raised, says so and points to theosirion-theme-updatebrowser event - Docs: the CMS page components (
ContentPage,ContentRenderer,ContentView,LocalizedContentView) follow the component page template, with live demos that load one of this site's own pages and generated parameter tables, andArticlePage,DocumentPage,HomePage,LandingPage,AboutPage,ContactPageandNotFoundPagehave their own pages - Docs:
SeoMetadataRendererand the feeds and sitemap page follow the component page template under a new SEO and feeds group page, and each analytics tracker (ClarityTracker,GA4Tracker,MatomoTracker,PlausibleTracker,UmamiTracker,YandexMetricaTracker) has its own page under a new Analytics group; the analytics package page keeps setup, configuration and the consent gate - Docs: the CMS navigation pages (
CategoriesList,TagCloud,ContentBreadcrumbs,DirectoryNavigation,LocalizedNavigation,OsirionContentNavigation,ContentSeriesNavigation,TableOfContents,SearchBox) follow the component page template, with live demos built from the site's own content and generated parameter tables - Docs: the navigation pages (
OsirionBreadcrumbs,OsirionReadMoreLink) follow the component page template, with live demos and generated parameter tables, andMenuwith its parts,ScrollToTopandEnhancedNavigationhave their own pages. The navigation package page keeps setup and configuration and links to each component - Docs: the content component pages (article metadata, callouts, tabs, accordion and FAQ, responsive image, video embed, reading progress,
OsirionHtmlRenderer) follow the component page template, with live demos and generated parameter tables, and are marked available from the release the changelog lists them in.OsirionRevealandOsirionContentDemohave their own pages - Docs: the section pages (
HeroSection,OsirionBaseSection,OsirionContactInfoSection,OsirionPricingTableandOsirionFeatureComparison,OsirionSteps,OsirionResponsiveShowcaseSection, the logo and testimonial carousels) follow the component page template, with live demos and generated parameter tables.OsirionMetricGrid,OsirionContextCardGridandOsirionPageHeadinghave their own pages. The pricing table and steps are marked available from 4.0, where the changelog lists them - Docs: the layout pages (
OsirionPageLayout,OsirionPageLoading,OsirionFooter,OsirionStickySidebar,OsirionBackgroundPattern) follow the component page template, with live demos and generated parameter tables; the parameter docs of these components say what each parameter does - Docs: the example site has
/templatesfor thedotnet newtemplates and/changelog, rendered from this file. Addresses no longer take a locale:/en/...answers with a 301 to the same path without it, and a made-up locale such as/xx/blogno longer answers 200 with an empty list. Every unknown address answers 404 with the not-found page instead of an empty response, and the Blazor template's Counter and Weather pages are gone - Osirion.Blazor.Cms.Web:
SeoMetadataOptions.SiteUrl(Osirion:Seo:SiteUrl). When it is an absolute http(s) address, the canonical URL,og:urland the addresses in structured data use it with the page's path instead of the host that served the request, so a site behind a proxy, on a preview host or on a second domain declares its public address
Fixed
- Osirion.Blazor.Core:
OsirionCookieConsentreopens its category panel for a page opened with?customize-cookiesafter the visitor answered, with their saved choices checked, so a "Cookie settings" link can change or withdraw consent. Before, the banner never rendered again once answered, and consent could only be withdrawn by deleting the cookie - Osirion.Blazor.Core: the Copy button restores its own accessible name after a copy. Two seconds later the script set a generic "Copy code to clipboard", so "Copy C# code to clipboard" lost its language
- Osirion.Blazor.Theming, Osirion.Blazor.Core: the theme, palette and announcement dismissal cookies are always
Secure. The flag followedRequest.IsHttps, so behind a proxy that ends TLS without forwarded headers an HTTPS site set them without it. Browsers treatlocalhostas secure, so local development keeps them. The theme and palette cookies stay readable by script, which applies and replaces them - Osirion.Blazor.Navigation:
MenuGroupwithCollapsiblerenders its label as thesummaryof adetailselement, so a click, Enter or Space opens and closes the items without script, andExpandedsets the starting state. The label was a plaindivand a rule hid the items of a group withExpanded="false", so they stayed hidden with no control to open them. A collapsible group without aLabelnow stays open - Osirion.Blazor.Navigation:
Menu.AutoExpandActivekeeps the submenu of the active item open in a vertical menu, as its description says. Only theosirion-menu-auto-expandclass was written. The rule written for it neededosirion-menu-item-has-submenuandosirion-menu-item-activeon one element, which no markup had (th...
4.1.0
Changed
- All packages: the repository moved to github.com/demonjic/Osirion.Blazor, which the package metadata and Source Link now name, and every package README was corrected against the code: their examples used APIs and parameters that do not exist
- All packages: the Microsoft.AspNetCore and Microsoft.Extensions dependencies move to the October servicing releases, 9.0.21 for net9.0 and 10.0.13 for net10.0, and AngleSharp to 1.8.5
- Osirion.Blazor.Cms.Infrastructure: a file's write time no longer stands in for a content item's last-modified date. Without
last_modifiedin its front matter, an item was last modified when it was created (itsdate). A fresh checkout gives every file the time of the checkout, which reacheddateModified,article:modified_timeand the sitemap'slastmod, often hours before the published date.FileSystemOptions.UseFileWriteTimeAsLastModifiedturns the old behaviour back on where files are edited in place
Added
- Osirion.Blazor.Cms.Web:
SeoMetadataOptions.AuthorUrland theAuthorUrlparameter ofSeoMetadataRenderername a page that identifies the author, such as/about, as the author'surlin every article and posting - Osirion.Blazor.Cms.Web:
ItemTemplateonContentList,ContentListPageandOsirionContentListSectionrenders each item through the host's markup, in anol.osirion-content-index, instead of the built-in cards. Paging, the pageh1, feeds and structured data are unchanged - Osirion.Blazor.Cms.Web:
ArticlePage.HeaderContentreplaces the hero with the host's article header - Osirion.Blazor:
Osirion:Site:CopyrightandOsirion:Site:SocialTitlereach the footerOsirionSiteLayoutrenders - Osirion.Blazor.Cms.Web:
TableOfContentsmarks the section being read as the page scrolls (osirion-toc-link-activeandaria-current="location"), through a JS component the page loads only when it lists headings;TrackActiveHeading="false"turns it off.AddOsirionContentregisters the JS component scope when the host has not - Osirion.Blazor.Theming: named palettes (
ThemePalette: Osirion, Commit, Editor, Studio, StudioBlue, Contrast), each with a light and a dark variant, set withThemingOptions.DefaultPaletteorUsePalette, kept per visitor in theosirion-preferred-palettecookie and applied byOsirionStylesbefore the first paint and after enhanced navigation. Every variant reaches WCAG 2.2 AA for text, links, buttons, status messages and code (Contrast: 7:1) and 3:1 for focus, checked by tests. Code blocks follow the palette.Framework, the default, changes nothing. Colours inspired by GitHub Primer and Visual Studio Code (MIT; notices inTHIRD-PARTY-NOTICES.md), generated bytools/Osirion.Blazor.Palettes - Osirion.Blazor.Theming:
ThemePicker, a labelled native list from which visitors pick a palette. The choice applies at once, is kept in theosirion-preferred-palettecookie and survives enhanced navigation and static exports;Palettes,LabelandFrameworkLabelshape the list. No interactive render mode, inline script or event handler attribute is needed - Osirion.Blazor.Theming:
IThemeService.CurrentPaletteandSetPalette, with default implementations so existing implementations keep compiling;ThemingOptions.AllowPaletteSelectionlinks the palette stylesheet on every page
Fixed
- Osirion.Blazor.Core: Osirion's own elements use border-box sizing. Without a CSS framework (
Framework: None) they kept content-box, so the footer, the menu and full-width buttons were wider than their container and the page scrolled sideways - Osirion.Blazor.Core: the skip link's address is relative to the document's base instead of absolute, so a page rendered on one host (a static export) works on another; with script running it moves focus to
mainwithout following the link - Osirion.Blazor.Theming:
OsirionStylesno longer linksOsirion.Blazor.Core.bundle.scp.css, which the host's{App}.styles.cssalready imports; every page downloaded it (about 250 KB) twice - Osirion.Blazor.Core:
OsirionFooter's default copyright line no longer uses an em dash - Osirion.Blazor.Cms.Web:
SeoMetadataRendererappliesAllowAiDiscovery,AllowAiTraining,AllowSearchIndexing,EnableGeoOptimization,EnableAeoOptimizationandGenerateMultipleSchemasfromSeoMetadataOptionsunless the page sets the parameter. The component's own default oftruewon over the configuration, soAllowAiTraining: falsestill renderedai-training: allow - Osirion.Blazor.Cms.Web: the
<title>element is no longer cut at 60 characters. The cut took the site name with it ("Title | Site..."); search engines shorten a title link themselves.og:titleandtwitter:titlekeep their lengths - Osirion.Blazor.Cms.Web: the structured data author no longer carries a URL guessed from the name (
/author/{name}), a page that does not exist; it carriesAuthorUrlwhen one is configured and no URL otherwise - Osirion.Blazor.Cms.Web: the publisher logo no longer declares a fixed
widthof 600 andheightof 60, whatever the logo's real size - Osirion.Blazor.Cms.Web:
SeoMetadataRendererno longer preloads the share image. The page does not show it, so browsers warned on every page that a preloaded resource went unused - Osirion.Blazor.Core:
OsirionHtmlRenderergives code blocks and tablestabindex="0". Its styles let wide code, and tables on screens up to 768px, scroll sideways, and a scroll box with nothing focusable inside cannot be scrolled from a keyboard (WCAG 2.1.1; axescrollable-region-focusable). An element with its owntabindexkeeps it;FocusableScrollBlocks="false"turns this off - Osirion.Blazor.Cms.Infrastructure: a
CategoryorTagquery matches a category or tag by its name (ignoring case) or by its slug, exactly. The category filter turned the slug back into a name by replacing hyphens with spaces and then matched part of a name, so a category with a hyphen of its own ("Multi-tenant platforms") listed nothing at its address, and "Dev" listed "Web Dev". A tag with a space is now found from its address too - Osirion.Blazor.Theming: in the dark theme, the active navigation item, danger and info messages reach 4.5:1 (they were 3.43, 3.62 and 4.07):
--osirion-nav-active-text,--osirion-status-danger-textand--osirion-status-info-textmove one step lighter on their scales - Osirion.Blazor.Cms.Infrastructure: a refresh or cold read whose cancellation token is already cancelled throws
OperationCanceledExceptionat once and no longer starts a load. It started one, so the next refresh waited for a second load, and when the load finished first the cancelled call returned as if it had succeeded
4.0.0
Security
- Osirion.Blazor.Cms.Admin: every routed admin page (
/osirion,/osirion/dashboard,/osirion/content,/osirion/content/edit,/osirion/settings,/osirion/login) requires the authorization policy"Osirion.Cms.Admin"(CmsAdminAuthorization.PolicyName). The default policy requires an authenticated user; a host policy with that name replaces it. These pages, and the repository writes behind them, were open to anonymous visitors - Osirion.Blazor.Cms.Admin: the configured
Osirion:Cms:Admin:Authentication:PersonalAccessTokenno longer signs in every visitor of/osirion/loginand is never sent to the browser. Admin services use it on the server only and never use a content provider'sApiToken - Osirion.Blazor.Cms.Admin: a user's own GitHub token is kept in
ProtectedLocalStorage, encrypted with Data Protection under a purpose bound to the signed-in host user, instead of plain-text localStorage (github_auth_token); plain-text copies left by earlier versions are removed - Osirion.Blazor.Cms.Admin: GitHub OAuth sends a single-use
state, compared in constant time, and a PKCE S256code_challenge, which closes login CSRF through a planted?code=. LoginreturnUrlvalues are limited to local paths, and login errors no longer show exception text - Osirion.Blazor.Cms.Infrastructure: GitHub API clients are no longer shared and send their headers per request, so a token, repository or branch set in one admin session can no longer reach another user or the public site. Cookies are turned off on the GitHub HTTP clients' primary handlers
- Osirion.Blazor.Cms.Infrastructure: GitHub paths, branch names, owner and repository are percent-encoded segment by segment, and empty,
.or..segments are rejected withArgumentExceptionbefore any request is sent.GitHubApiClient.DeleteFileAsyncthrows instead of sending a request when the owner, repository or token is missing - Osirion.Blazor.Cms.Infrastructure: the GitHub webhook (
UseGitHubWebhook,POST /api/github/webhook) verifiesX-Hub-Signature-256(HMAC-SHA256 over the raw body, constant-time comparison) against the providers'WebhookSecret, rejects every delivery while no provider has a secret, refuses bodies over 5 MiB and refreshes only providers whose owner, repository, branch and secret match exactly - Osirion.Blazor.Cms.Infrastructure:
FileSystemContentRepositoryandFileSystemDirectoryRepositoryno longer write, delete, create or move anything outsideFileSystemOptions.BasePath. Osirion.Blazor.Cms.Application:CreateContentCommandValidatorandUpdateContentCommandValidatorreject rooted paths,.,..and empty segments, and control characters - Content HTML is sanitized with an allow-list (HtmlSanitizer on the AngleSharp HTML5 parser) when markdown is rendered or ingested, and again where it is rendered as raw markup (
OsirionHtmlRenderer,ContentView,LocalizedContentView,DocumentPage,MarkdownPreviewand the admin preview). Script, event handler attributes,javascript:,vbscript:anddata:URLs, forms, iframes, inline styles,<style>,svgand thenameattribute are removed;class,id,role,aria-*and table celltext-alignare kept - Front matter JSON-LD is parsed and re-serialized with
<,>and&escaped beforeSeoMetadataRendererwrites it into a script element; invalid JSON-LD is dropped with a warning - Osirion.Blazor.Core: the cookie consent endpoint (
MapOsirionCookieConsent) requires an antiforgery token, which the banner form now carries, and redirects only to local paths - Osirion.Blazor.Cms.Admin: the admin event buses are scoped to one circuit or request. As singletons they delivered one admin's events to every other admin's editor, so opening or creating content in one session could replace another admin's unsaved draft, and authentication and error events crossed sessions
- Osirion.Blazor.Cms.Admin:
AddGitHubAdminServicesturns cookies off on the GitHub HTTP clients itself, through the same registration as Osirion.Blazor.Cms.Infrastructure, instead of relying on another registration method to do it - Osirion.Blazor.Core:
HeroSectionCSS-escapesImageUrlwhen it writes the background image, so a front matterfeatured_image(passed byArticlePage,HomePageandLandingPage) can no longer close theurl()value and add style declarations such as a full-page overlay - Analytics trackers (Clarity, Matomo, GA4, Yandex Metrica) JSON-encode every configured value they write into scripts and attributes; invalid GA4 ids, Yandex counters and tracker or CDN URLs fail at startup with a message naming the setting
- Navigation links (
MenuItem,MenuGroupfooter,MenuPanelLink,MenuImageCard,MenuCallToActionand theMenubrand link) render nohreffor schemes other than relative, http, https, mailto and tel, so ajavascript:URL from CMS-driven menu data cannot run ScrollToTop.CustomIconis sanitized; the newIconCssClassis the way to use a custom icon- The CMS query cache key includes every query field, so a draft query (
IncludeUnpublished) and a public query can no longer share a cached result and leak drafts to the public site OsirionHtmlRendererloads the pinned Prism 1.29.0 from cdnjs with Subresource Integrity (newPrismBaseUrlto self-host),OsirionBaseSectionCSS-escapesBackgroundImageUrllikeHeroSection, andOsirionContactInfoSectionvalidates website URLs, encodes phone, email and website, and sanitizes its markup fields
Added
- Osirion.Blazor.Core:
OsirionBreadcrumbs.CurrentPageText, the text of the last breadcrumb - Content HTML sanitizer in Osirion.Blazor.Core:
IHtmlContentSanitizer,HtmlContentSanitizer,HtmlContentSanitizerOptions(AllowedTags,AllowedAttributes,AllowedSchemes,AllowElement,AllowAriaAttributes,AllowTableCellAlignment),AddOsirionHtmlSanitizer,GetHtmlContentSanitizerandJsonLdNormalizer - Osirion.Blazor.Cms.Admin:
CmsAdminAuthorization.PolicyName,AuthGuard.Policy,IGitHubTokenStore,IGitHubOAuthStateStore,GitHubOAuthRequest, and theLoginViewModelmembersStartGitHubLoginAsync,LoginWithGitHubAsync(code, state, cancellationToken),IsServerCredentialConfigured,IsGitHubOAuthConfiguredandGitHubRedirectUri IGitHubTokenProvider.ExchangeCodeForTokenAsync(code, clientId, clientSecret, redirectUri, codeVerifier, cancellationToken)andIMarkdownProcessor.SanitizeHtml(string)- Osirion.Blazor.Core:
LocalUrlinOsirion.Blazor.Core.Utils(IsLocalPath,GetSafeLocalPathandDefaultPath), the local return-URL check used by the cookie consent endpoint and the admin login - Analytics consent gate, off by default:
IAnalyticsBuilder.RequireConsent()orOsirion:Analytics:Consent:RequireConsentrenders trackers only after the visitor accepted the analytics category inOsirionCookieConsent, and GA4 then starts with Google Consent Mode defaults set to denied - Matomo and Yandex Metrica report page views after Blazor enhanced navigation through a static script (
_content/Osirion.Blazor.Analytics/js/osirion-analytics.js) whenAutoTrackPageViewsis on IGitHubApiClient.GetTreeAsync(default implementation returns null),GitHubTree,GitHubRateLimitException,BaseContentRepository.GenerateMarkdownWithFrontMatterAsyncandLastRefreshSucceeded, andUseOsirionCmsAsyncexamples/Osirion.Blazor.Example.Mud: the CMS example site on MudBlazor 9, with a theme that follows Osirion's theme toggle without a round trip.osirion-visually-hiddenutility in the Core stylesheet for text that only screen readers announceexamples/Osirion.Blazor.Example.FluentUI: the CMS example site on Fluent UI Blazor 5, registered in the examples AppHost on port 5298OsirionPageLayoutrenders a skip link to the main content as the first focusable element (WCAG 2.4.1), shown only while it has keyboard focus;ShowSkipLink,SkipLinkTextandMainContentIdconfigure it, and the main element now carries that id andtabindex="-1"- Callouts: GitHub-style alerts in Markdown (
> [!NOTE],[!TIP],[!IMPORTANT],[!WARNING],[!CAUTION]) are now styled, with their icons drawn in CSS so the HTML sanitizer can keep removing inline SVG, and the newOsirionCalloutcomponent (CalloutType) renders the same look from Razor OsirionTabsandOsirionTab: tabs that switch without script (a radio group per tab set), keyboard accessible with the arrow keys, for code samples and other alternative contentOsirionAccordionandOsirionAccordionItemon the native<details>element (optionally one open at a time), andOsirionFaqwithFaqItem, which also writes FAQPage structured data- Osirion.Blazor.Cms.Web:
MapOsirionFeedsmaps/sitemap.xml, an RSS 2.0 feed at/feed.xmland an Atom feed at/atom.xmlfrom the published content (OsirionFeedOptions: site URL, title, directory, locale, item count, extra sitemap paths, URL formatter, cache duration);noindexitems stay out of the sitemap - Osirion.Blazor.Analytics: Plausible and Umami, both cookie-free.
AddPlausiblewithPlausibleTrackerrenders the site's own Plausible script (ScriptUrl, optionalEndpointand tracking switches written toplausible.initonly when set);AddUmamiwithUmamiTrackerrenders one attribute-configured script (SiteId= website ID,ScriptUrlfor self-hosting,HostUrl,Domains,Tag, exclusions, Do Not Track). Both readOsirion:Analytics:PlausibleandOsirion:Analytics:Umami, are validated at startup and honour the consent gate OsirionResponsiveImage: an<img>with intrinsicWidthandHeight(no layout shift),srcsetfromSrcSetorWidthswith aSrcFormatter,Sizes(defaultauto, 100vw), lazy loading with async decoding, aPriorityswitch for the main image (eager,fetchpriority="high") and an optionalCaptionin a<figure>- `OsirionVideoEmbed...
Release v3.0.1
[3.0.1] - 2026-09-24
Added
- Mega menu building blocks in Osirion.Blazor.Navigation:
MenuPanel+MenuPanelLink: high-contrast promotional aside with pill links ("Platform highlights", "Build your business case"); brandable via--osirion-menu-panel-background/--osirion-menu-panel-colorMenuImageCard: linked image card with overlay caption for mega menu asidesMenuItem.SubmenuAsiderender fragment: dedicated right-hand aside column for mega submenusMenuGroup.Description: column subtitle with divider (mega menu header style)MenuGroup.FooterText/FooterHref/FooterTarget: "Explore X →" footer links per group
- bUnit coverage for all new mega menu components and parameters
Changed
- Mega submenus now center under the whole navigation bar on desktop, use a responsive auto-fit column grid with column headers, and keep an invisible hover bridge so the pointer can travel from the trigger into the panel
- Mega submenu asides and group descriptions are hidden in the collapsed drawer and mobile views to keep the drawer scannable
- Collapsed drawer submenu toggle no longer paints an opaque hover background over the row label
Fixed
MenuGroupwithout anIdgenerated a new identifier on every read, so the items container'saria-labelledbypointed at an element that did not exist and the label and items ids changed on every render; the generated identifier is now created once per instance- Navigation tests updated to the current
Menu,MenuItemandMenuGroupmarkup, andNavigationServicelog assertions no longer try to match theLogInformationextension method with NSubstitute