# AccentColorSwitcher **Also known as:** Accent, Brand, Theming ## Description BitAccentColorSwitcher renders a row of accent color swatches that re-theme the whole app live: one picked brand color is fed to BitThemeFactory as the seed of a complete palette, applied through BitThemeManager, and optionally persisted (localStorage and/or cookie, see BitAccentColorConfig.Persistence) so it survives a refresh - with two opt-in first-paint strategies (beyond the default None, which simply re-applies the accent after hydration) that keep the accent correct even when the served HTML comes from a cache, before any Blazor runtime is up. ## Parameters | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | Classes | `BitAccentColorSwitcherClassStyles?` | null | Custom CSS classes for different parts of the switcher. | | Config | `BitAccentColorConfig?` | null | The app-wide accent configuration: the accents offered as swatches, the stores the picked one is persisted to, and the first-paint strategy to maintain when applying it. When null, the BitAccentColorConfig registered in DI (the accentColor option of AddBitBlazorUIExtrasServices) is used; with neither, the DefaultAccents are offered, nothing is persisted and no first-paint machinery runs. The configuration is app-wide state on the shared BitAccentColorService - the first initialized instance (or an explicit BitAccentColorService.InitializeAsync call) fixes it - so state it once: register it in DI, or hand one shared instance to every switcher and to the host page's BitAccentColorHead. | | OnChange | `EventCallback<string>` | | The callback that is called when the accent color changes, receiving the applied accent color. | | Styles | `BitAccentColorSwitcherClassStyles?` | null | Custom CSS styles for different parts of the switcher. | | AriaLabel | `string?` | null | Gets or sets the accessible label for the component, used by assistive technologies. | | Class | `string?` | null | Gets or sets the CSS class name(s) to apply to the rendered element. | | Dir | `BitDir?` | null | Gets or sets the text directionality for the component's content. | | ForceAnimation | `bool` | false | Gets or sets a value indicating whether the component's animations play at their full duration even when reduced motion is requested. | | HtmlAttributes | `Dictionary<string, object>` | new Dictionary<string, object>() | Captures additional HTML attributes to be applied to the rendered element, in addition to the component's parameters. | | Id | `string?` | null | Gets or sets the unique identifier for the component's root element. | | IsEnabled | `bool` | true | Gets or sets a value indicating whether the component is enabled and can respond to user interaction. | | Style | `string?` | null | Gets or sets the CSS style string to apply to the rendered element. | | TabIndex | `string?` | null | Gets or sets the tab order index for the component when navigating with the keyboard. | | Visibility | `BitVisibility` | BitVisibility.Visible | Gets or sets the visibility state (visible, hidden, or collapsed) of the component. | ## Public Members | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | UniqueId | `Guid` | Guid.NewGuid() | Gets the readonly unique identifier for the component's root element, assigned when the component instance is constructed. | | RootElement | `ElementReference` | | Gets the reference to the root HTML element associated with this component. | ## Enums ### BitAccentColorFirstPaintStrategy Enum | Name | Value | Description | | :--- | :--- | :---------- | | None | 0 | No first-paint machinery (the default): the accent is applied only after hydration, so a server-rendered page paints the packaged palette first and flips to the persisted accent once the client is up. The preference is still persisted to (and restored from) whatever stores BitAccentColorPersistence enables; no bit-accent attribute is set, no palette snapshot is kept, and BitAccentColorHead emits nothing. | | StaticCss | 1 | First paint comes from a static stylesheet holding the palettes of every offered accent, keyed on the bit-accent root attribute the inline head script sets pre-paint. The served HTML is accent-agnostic and safe to cache. Recommended when the accents are a fixed set known at build time. | | StoredCss | 2 | First paint comes from a snapshot of the generated palette CSS kept in localStorage, injected pre-paint by the inline head script. No static stylesheet is needed, so the accents do not have to be known up front. The snapshot lives in localStorage, so this strategy needs the LocalStorage store enabled. | ### BitAccentColorPersistence Enum | Name | Value | Description | | :--- | :--- | :---------- | | None | 0 | Nothing is persisted (the default): the accent applies for the current session only and is gone on the next load. | | LocalStorage | 1 | The localStorage entry the client restores the accent from. | | Cookie | 2 | The cookie that carries the preference to the server, so SSR can prerender the visitor's accent. Required for the server halves of BitAccentColorSsr to see anything. | | All | 3 | Both stores. Writing both also self-heals either store going missing on its own. | ### BitVisibility Enum | Name | Value | Description | | :--- | :--- | :---------- | | Visible | 0 | The content of the component is visible. | | Hidden | 1 | The content of the component is hidden, but the space it takes on the page remains (visibility:hidden). | | Collapsed | 2 | The component is hidden (display:none). | ### BitDir Enum | Name | Value | Description | | :--- | :--- | :---------- | | Ltr | 0 | Ltr (left to right) is to be used for languages that are written from the left to the right (like English). | | Rtl | 1 | Rtl (right to left) is to be used for languages that are written from the right to the left (like Arabic). | | Auto | 2 | Auto lets the user agent decide. It uses a basic algorithm as it parses the characters inside the element until it finds a character with a strong directionality, then applies that directionality to the whole element. | ## Sub Classes ### BitAccentColorConfig Properties The app-wide configuration of the accent color feature. The host page's BitAccentColorHead and every BitAccentColorSwitcher are the head and body halves of one mechanism, so they have to agree on these values - state them once, in code both the server and the client compile: either register the configuration in DI through the accentColor option of AddBitBlazorUIExtrasServices (in the service-registration method the server and client Program.cs share), which every component falls back to when no Config parameter is handed to it, or define one shared instance (e.g. a static field in a shared project) and pass that same instance to each of them. | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | Accents | `IReadOnlyList<BitAccentColorItem>?` | null | The accent colors of the app: the swatches the switchers offer, the palettes the head emits, and the list a persisted value is validated against on restore. When null, the DefaultAccents (the six BitAccentColorPresets hues) are used. | | FirstPaintStrategy | `BitAccentColorFirstPaintStrategy` | BitAccentColorFirstPaintStrategy.None | How the accent palette reaches the very first paint of a page load, before any Blazor runtime is up: None (the default) applies the accent after hydration only, StaticCss keys a prebuilt all-accents stylesheet on the bit-accent root attribute, StoredCss keeps a snapshot of the generated palette CSS in localStorage. The CSS strategies restore from the stores Persistence enables, so they need a persistence other than None to have any effect. | | Persistence | `BitAccentColorPersistence` | BitAccentColorPersistence.None | The stores the picked accent is persisted to: LocalStorage, Cookie, or both (All); None (the default) keeps the accent for the current session only. The cookie half is what lets the server read the preference while prerendering (SSR), so enable it when the server takes part in painting or seeding the accent. | ### BitAccentColorItem Properties An accent color offered by the BitAccentColorSwitcher. | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | Name | `string?` | null | The display name of the accent color, used as the swatch tooltip and accessible label. | | AriaLabel | `string?` | null | The accessible label of the swatch button. When not set, an English label is composed from Name ("Apply the {Name} accent color") - set this to localize it. | | Color | `string` | | The accent color in #RGB or #RRGGBB hex format, fed to BitThemeFactory as the seed the whole palette is derived from. | ### BitAccentColorSwitcherClassStyles Properties Custom CSS classes/styles for different parts of the BitAccentColorSwitcher. | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | Root | `string?` | null | Custom CSS classes/styles for the root element of the BitAccentColorSwitcher. | | Swatch | `string?` | null | Custom CSS classes/styles for each swatch button of the BitAccentColorSwitcher. | | ActiveSwatch | `string?` | null | Custom CSS classes/styles for the swatch button of the active accent of the BitAccentColorSwitcher. | ### BitAccentColorService Properties The scoped service (registered by AddBitBlazorUIExtrasServices) that owns the accent: state, persistence, the theme overlay and the dark/light re-derivation. Inject it to seed, restore or apply the accent from app code. | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | ActiveAccent | `string` | BitAccentColorPresets.Blue | The accent currently applied, as the canonical hex of the matching accent item. BitAccentColorPresets.Blue is the packaged palette's own primary, i.e. "no override". | | AccentChanged | `event EventHandler?` | | Raised after ActiveAccent changes, so the switchers can re-render. | | SeedFromPrerender | `void SeedFromPrerender(string? accent)` | | Adopts the accent the server read from the accent cookie while prerendering, so the prerendered markup already marks the right swatch as active. | | InitializeAsync | `Task InitializeAsync(BitAccentColorConfig? config = null)` | | Restores the persisted accent, applies it, and starts tracking dark/light switches. Call it after the first interactive render (the switcher does this itself); only the first interactive call does the work, and its config (falling back to the DI-registered BitAccentColorConfig when null) becomes the app-wide configuration - and every later call awaits that same restore rather than returning before the accent has been read. A persisted accent outside the configured ones is restored as well (re-validated as plain hex, like ApplyAsync); no swatch marks it active, because none of them is it. | | ApplyAsync | `Task ApplyAsync(string accentColor)` | | Applies the given accent color and persists it, per the first-paint strategy and persistence configured by the first InitializeAsync call. Values outside the configured accents are re-validated as plain hex, so an app can programmatically apply an accent it never offers as a swatch. | ### IBitAccentColorStore Properties An app-supplied store for the accent preference, for hosts where the built-in web stores are not the right home for it - e.g. a Blazor Hybrid app persisting through native preferences instead of the webview's localStorage. Register an implementation in DI and BitAccentColorService restores from it first (ahead of the stores the Persistence flags enable) and (re)writes it on every apply, so it can be combined with the built-in stores or run alone with BitAccentColorPersistence.None. | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | GetAccentAsync | `Task<string?> GetAccentAsync()` | | Reads the persisted accent color, or null when none is stored. The value goes through the same validation as the built-in stores - anything unrecognized is treated as "nothing persisted". | | SetAccentAsync | `Task SetAccentAsync(string accent)` | | Persists the accent color. | | RemoveAccentAsync | `Task RemoveAccentAsync()` | | Removes the persisted accent color. Called when the accent reverts to the packaged palette's own primary, which is stored as "no preference" rather than as a value. | ### BitAccentColorHead Properties The single-drop first-paint setup: place it at the top of the host page's <head> (after BitThemeSsr.InlineHeadScript, before any stylesheet) and it emits the accent inline script plus the palette CSS the selected FirstPaintStrategy needs (nothing for the default None) - see the "First paint setup" demo above. | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | Config | `BitAccentColorConfig?` | null | The app-wide accent configuration this component emits the head half of: its FirstPaintStrategy selects what is emitted (nothing for the default None), its Persistence selects the stores the inline script reads, and its Accents are the palettes emitted in StaticCss mode. When null, the BitAccentColorConfig registered in DI (the accentColor option of AddBitBlazorUIExtrasServices) is used; alternatively hand it the same instance the app's BitAccentColorSwitcher instances use. | | Nonce | `string?` | null | Optional CSP nonce for the emitted inline script, to satisfy a script-src 'nonce-…' Content-Security-Policy. | | PersistedAccent | `string?` | null | The persisted accent preference, usually the BitAccentColorNames.CookieName cookie's value read from the request. Only used by the StoredCss strategy; the StaticCss strategy is deliberately cookie-independent. | | StylesheetHref | `string?` | null | StaticCss mode only: when set, the all-accents stylesheet is referenced as an external stylesheet at this href (with the library version appended as a cache-buster) instead of being inlined - serve BitAccentColorSsr.BuildStaticCss there with long cache headers. When null, the stylesheet is inlined into the response, which needs no endpoint at the cost of re-sending the (well-compressing) palette CSS per page load. | ### BitAccentColorSsr Properties Static first-paint helpers for the app's host page - see the "First paint setup" demo above. | Name | Type | Default Value | Description | | :--- | :--- | :------------ | :---------- | | InlineHeadScript | `string` | | Full <script> markup ready to drop into <head>, before the stylesheets. Re-resolves the accent from localStorage / the preference cookie pre-paint: sets the bit-accent root attribute and injects the StoredCss snapshot when one matches. BuildInlineHeadScript(nonce, persistence) is the parameterized variant: the optional CSP nonce is emitted onto the script element, and the optional persistence restricts which stores the script reads. | | BuildRootAccentAttributes | `string BuildRootAccentAttributes(string? persistedAccent)` | | Builds the bit-accent attribute for the root <html> element from the persisted preference (usually the accent cookie), so origin-rendered markup paints correctly with no script involved. BuildRootAccentAttributeMap is the @attributes-splat variant. | | BuildStaticCss | `string BuildStaticCss(IEnumerable<BitAccentColorItem>? accents = null)` | | The accent-agnostic stylesheet of the StaticCss mode: every offered accent's palette, scoped to its bit-accent attribute value and split dark/light on bit-theme. Identical for every visitor, so serve it as a long-cached static asset. | | BuildPrerenderCss | `string? BuildPrerenderCss(string? persistedAccent, IEnumerable<BitAccentColorItem>? accents = null)` | | The per-request style the server emits (as <style id="@BitAccentColorNames.StyleElementId">) so an origin-rendered page paints the persisted accent immediately - the server half of the StoredCss mode. Mark that style with @BitAccentColorNames.StyleAccentAttribute="<token>" and follow it with PrerenderCssGuardScript. | | BuildSwatchMarkerCss | `string BuildSwatchMarkerCss(IEnumerable<BitAccentColorItem>? accents = null)` | | The pre-paint active-swatch marker of the CSS strategies: one rule ringing the BitAccentColorSwitcher swatch whose token the bit-accent root attribute carries (plus the packaged primary's swatch when no attribute is set), so prerendered and cached markup rings the visitor's accent from the first paint. Accent-agnostic, so it is safe in a cached response. Emitted here rather than by the switcher because this is where the response's CSP nonce is - a style element the switcher rendered would have none to carry. | | PrerenderCssGuardScript | `string` | | Full <script> markup to emit immediately after the BuildPrerenderCss style: it drops that style unless the accent it was built for is the one the inline head script resolved from this visitor's own stores - which is what keeps a cached response from painting the accent of whichever visitor the origin rendered it for. The inline head script cannot do this itself, having run before that style was parsed. BuildPrerenderCssGuardScript(nonce) is the parameterized variant. | ## Examples \n**Basic**: ```razor ``` \n**Accents**: ```razor @code { private readonly BitAccentColorConfig customAccentsConfig = new() { Accents = [ new() { Name = "Crimson", Color = "#DC143C" }, new() { Name = "Indigo", Color = "#4B0082" }, new() { Name = "Chocolate", Color = "#D2691E" }, ], }; } ``` \n**Persistence**: ```razor @code { private readonly BitAccentColorConfig persistenceConfig = new() { Persistence = BitAccentColorPersistence.All, }; } ``` \n**FirstPaintStrategy**: ```razor @code { private readonly BitAccentColorConfig firstPaintConfig = new() { FirstPaintStrategy = BitAccentColorFirstPaintStrategy.StoredCss, Persistence = BitAccentColorPersistence.All, }; } ``` \n**OnChange**: ```razor
Changed accent color: @(changedAccentColor ?? "-")
``` ```csharp private string? changedAccentColor; ``` \n**First paint setup**: ```razor @* In the host page (e.g. App.razor of a Blazor Web App). ONE BitAccentColorHead goes into , after BitThemeSsr.InlineHeadScript and before the stylesheets; the three usages below are alternatives, one per first-paint setup - pick the one matching the configured strategy. Each emits the inline script that re-resolves the accent from localStorage / the cookie pre-paint - which is what keeps the accent correct when the HTML comes out of a cache that served it to a visitor whose cookie never reached the server - plus the palette CSS that strategy needs. The configuration comes from the BitAccentColorConfig registered in DI (see the C# tab); a Config parameter would override it. *@ @* StaticCss strategy, inlined: the whole all-accents stylesheet goes into the response; no endpoint needed. *@ ... @* StaticCss strategy, as a long-cached asset instead of inlined (see the C# tab; the library version is appended as a cache-buster automatically). Keep the href root-relative: this sits before any , so a relative href would resolve against the current page path and 404 on every non-root route. *@ ... @* StoredCss strategy: no stylesheet at all; pass the accent cookie so origin-rendered responses paint immediately (cached responses are covered by the localStorage snapshot). *@ ... @* Independent of the choice above: wherever the switcher renders (a layout, a settings page, ...) - it falls back to the same DI-registered configuration: *@ @* The other half of the cascading value the C# tab registers: consume it in the component hosting the switcher (the layout, typically) and seed the service before the first render, so the prerendered markup already marks the visitor's swatch instead of the default one. *@ @code { [Inject] private BitAccentColorService accentColorService { get; set; } = default!; [CascadingParameter(Name = "PrerenderedAccentColor")] private string? prerenderedAccentColor { get; set; } protected override void OnInitialized() { accentColorService.SeedFromPrerender(prerenderedAccentColor); base.OnInitialized(); } } ``` ```csharp // The app-wide configuration, stated ONCE in the service-registration method both the server and // the client Program.cs already call (the usual shared AddClientServices-style extension), so the // BitAccentColorHead and every BitAccentColorSwitcher - in whichever process they render - resolve // the same values: services.AddBitBlazorUIExtrasServices(accentColor: options => { options.FirstPaintStrategy = BitAccentColorFirstPaintStrategy.StaticCss; options.Persistence = BitAccentColorPersistence.All; // options.Accents = ...; }); // Alternative without DI: define one shared BitAccentColorConfig instance (e.g. a static field in // a shared project) and hand that same instance to the Config parameter of the head and of every // switcher - a Config parameter always outranks the DI-registered configuration. // StaticCss mode: serve the all-accents stylesheet as a long-cached asset (e.g. a minimal API): app.MapGet("/accent-colors.css", context => { context.Response.Headers.ContentType = "text/css"; context.Response.Headers.CacheControl = "public, max-age=31536000, immutable"; return context.Response.WriteAsync(BitAccentColorSsr.BuildStaticCss(), context.RequestAborted); }); // To mark the right swatch active in prerendered markup, cascade the cookie into the component // tree and hand it to BitAccentColorService.SeedFromPrerender before the first render: services.AddCascadingValue("PrerenderedAccentColor", sp => sp.GetRequiredService().HttpContext?.Request.Cookies[BitAccentColorNames.CookieName]); ``` \n**Style & Class**: ```razor ```