< Summary

Information
Class: Allyaria.Theming.ThemeTypes.Theme
Assembly: Allyaria.Theming
File(s): /home/runner/work/allyaria/allyaria/src/Allyaria.Theming/ThemeTypes/Theme.cs
Line coverage
100%
Covered lines: 150
Uncovered lines: 0
Coverable lines: 150
Total lines: 274
Line coverage: 100%
Branch coverage
100%
Covered branches: 2
Total branches: 2
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
GetBoxSizingCss()100%11100%
GetComponentCss(...)100%22100%
GetDocumentCss(...)100%11100%
GetFocusCss(...)100%11100%
GetGlobalCss(...)100%11100%
GetLinkCss(...)100%11100%
GetReducedMotionCss()100%11100%
GetRootCss()100%11100%
GetTextCss(...)100%11100%
Set(...)100%11100%
ToCss(...)100%11100%
ToCssVars()100%11100%

File(s)

/home/runner/work/allyaria/allyaria/src/Allyaria.Theming/ThemeTypes/Theme.cs

#LineLine coverage
 1namespace Allyaria.Theming.ThemeTypes;
 2
 3/// <summary>
 4/// Represents the internal core theme engine responsible for generating CSS strings based on Allyaria’s theming
 5/// definitions, including component-level, document-level, and global visual styles.
 6/// </summary>
 7/// <remarks>
 8///     <para>
 9///     This class composes CSS dynamically by mapping <see cref="ComponentType" />, <see cref="ThemeType" />, and
 10///     <see cref="ComponentState" /> values through the theming pipeline. The resulting CSS is applied via Blazor
 11///     component rendering or injected globally through <see cref="IThemingService" />.
 12///     </para>
 13///     <para>
 14///     The <see cref="Theme" /> class is internal and used by higher-level abstractions such as
 15///     <see cref="ThemingService" /> and <see cref="ThemeBuilder" />. It supports accessibility features such as reduce
 16///     motion and visible focus indicators, and automatically includes high-contrast adjustments where applicable.
 17///     </para>
 18/// </remarks>
 19internal sealed class Theme
 20{
 21    /// <summary>
 22    /// Internal reference to the <see cref="ThemeComponent" /> used to construct the CSS output for all theming targets
 23    /// </summary>
 4924    private ThemeComponent _component = new();
 25
 26    /// <summary>Generates a reusable CSS rule that enforces consistent box sizing across all elements.</summary>
 27    /// <returns>A CSS string that ensures all elements, including pseudo-elements, inherit box sizing.</returns>
 428    private static string GetBoxSizingCss() => "*,*::before,*::after{box-sizing:inherit;}";
 29
 30    /// <summary>Builds the CSS string for a specific themed component based on its type, state, and prefix.</summary>
 31    /// <param name="prefix">The CSS selector or class prefix for the component.</param>
 32    /// <param name="componentType">The <see cref="ComponentType" /> representing the element type.</param>
 33    /// <param name="themeType">The <see cref="ThemeType" /> defining the active theme variant.</param>
 34    /// <param name="componentState">The <see cref="ComponentState" /> defining the current visual state.</param>
 35    /// <returns>A formatted CSS string scoped to the provided prefix and theme parameters.</returns>
 36    public string GetComponentCss(string prefix,
 37        ComponentType componentType,
 38        ThemeType themeType,
 39        ComponentState componentState)
 40    {
 6841        var css = ToCss(componentType: componentType, themeType: themeType, componentState: componentState);
 42
 6843        return string.IsNullOrWhiteSpace(value: prefix)
 6844            ? css
 6845            : $"{prefix.Trim()}{{{css}}}";
 46    }
 47
 48    /// <summary>
 49    /// Builds a complete CSS string representing document-wide theming styles, including global variables, base resets,
 50    /// typography, focus outlines, and accessibility rules.
 51    /// </summary>
 52    /// <param name="themeType">The <see cref="ThemeType" /> for which to generate document styles.</param>
 53    /// <returns>A full CSS string suitable for document-level injection.</returns>
 54    public string GetDocumentCss(ThemeType themeType)
 55    {
 456        var builder = new StringBuilder();
 57
 458        builder.Append(value: GetRootCss());
 459        builder.Append(value: GetGlobalCss(themeType: themeType));
 460        builder.Append(value: GetBoxSizingCss());
 461        builder.Append(value: GetFocusCss(themeType: themeType));
 462        builder.Append(value: GetReducedMotionCss());
 463        builder.Append(value: GetTextCss(themeType: themeType));
 464        builder.Append(value: GetLinkCss(themeType: themeType));
 65
 466        return builder.ToString();
 67    }
 68
 69    /// <summary>Generates the CSS rules for focus-visible outlines and focusable elements.</summary>
 70    /// <param name="themeType">The current <see cref="ThemeType" /> being rendered.</param>
 71    /// <returns>A CSS string defining focus outlines for global and interactive elements.</returns>
 72    private string GetFocusCss(ThemeType themeType)
 73    {
 474        var globalFocus = GetComponentCss(
 475            prefix: ":focus-visible",
 476            componentType: ComponentType.GlobalFocus,
 477            themeType: themeType,
 478            componentState: ComponentState.Focused
 479        );
 80
 481        var whereFocus = GetComponentCss(
 482            prefix: ":where(a,button,input,textarea,select,[tabindex]):focus-visible",
 483            componentType: ComponentType.GlobalFocus,
 484            themeType: themeType,
 485            componentState: ComponentState.Focused
 486        );
 87
 488        return $"{globalFocus}{whereFocus}";
 89    }
 90
 91    /// <summary>Generates global CSS for base elements like <c>html</c> and <c>body</c>.</summary>
 92    /// <param name="themeType">The current <see cref="ThemeType" /> being rendered.</param>
 93    /// <returns>A CSS string for global page-level elements.</returns>
 94    private string GetGlobalCss(ThemeType themeType)
 95    {
 496        var html = GetComponentCss(
 497            prefix: "html",
 498            componentType: ComponentType.GlobalHtml,
 499            themeType: themeType,
 4100            componentState: ComponentState.Default
 4101        );
 102
 4103        var body = GetComponentCss(
 4104            prefix: "body",
 4105            componentType: ComponentType.GlobalBody,
 4106            themeType: themeType,
 4107            componentState: ComponentState.Default
 4108        );
 109
 4110        return $"{html}{body}";
 111    }
 112
 113    /// <summary>Generates link-specific CSS rules, including states for default, focused, pressed, and visited.</summar
 114    /// <param name="themeType">The active <see cref="ThemeType" />.</param>
 115    /// <returns>A CSS string containing anchor element theming rules.</returns>
 116    private string GetLinkCss(ThemeType themeType)
 117    {
 4118        var builder = new StringBuilder();
 119
 4120        builder.Append(
 4121            value: GetComponentCss(
 4122                prefix: "a",
 4123                componentType: ComponentType.Link,
 4124                themeType: themeType,
 4125                componentState: ComponentState.Default
 4126            )
 4127        );
 128
 4129        builder.Append(
 4130            value: GetComponentCss(
 4131                prefix: "a:focus-visible",
 4132                componentType: ComponentType.Link,
 4133                themeType: themeType,
 4134                componentState: ComponentState.Focused
 4135            )
 4136        );
 137
 4138        builder.Append(
 4139            value: GetComponentCss(
 4140                prefix: "a:active",
 4141                componentType: ComponentType.Link,
 4142                themeType: themeType,
 4143                componentState: ComponentState.Pressed
 4144            )
 4145        );
 146
 4147        builder.Append(
 4148            value: GetComponentCss(
 4149                prefix: "a:visited",
 4150                componentType: ComponentType.Link,
 4151                themeType: themeType,
 4152                componentState: ComponentState.Visited
 4153            )
 4154        );
 155
 4156        return builder.ToString();
 157    }
 158
 159    /// <summary>Generates a CSS block that disables animations and transitions when users prefer reduced motion.</summa
 160    /// <returns>A CSS string applying <c>prefers-reduced-motion</c> adjustments for accessibility.</returns>
 161    private static string GetReducedMotionCss()
 4162        => "@media(prefers-reduced-motion:reduce){*{animation:none !important;transition:none !important;}html,body{scro
 163
 164    /// <summary>Generates the <c>:root</c> CSS variables section containing theme-wide color and style tokens.</summary
 165    /// <returns>A CSS string containing variable declarations for the current theme.</returns>
 4166    private string GetRootCss() => $":root{{{ToCssVars()}}}";
 167
 168    /// <summary>Generates text and heading CSS rules based on the current theme type.</summary>
 169    /// <param name="themeType">The <see cref="ThemeType" /> being rendered.</param>
 170    /// <returns>A concatenated CSS string for paragraph and heading elements.</returns>
 171    private string GetTextCss(ThemeType themeType)
 172    {
 4173        var builder = new StringBuilder();
 174
 4175        builder.Append(
 4176            value: GetComponentCss(
 4177                prefix: "p",
 4178                componentType: ComponentType.Text,
 4179                themeType: themeType,
 4180                componentState: ComponentState.Default
 4181            )
 4182        );
 183
 4184        builder.Append(
 4185            value: GetComponentCss(
 4186                prefix: "h1",
 4187                componentType: ComponentType.Heading1,
 4188                themeType: themeType,
 4189                componentState: ComponentState.Default
 4190            )
 4191        );
 192
 4193        builder.Append(
 4194            value: GetComponentCss(
 4195                prefix: "h2",
 4196                componentType: ComponentType.Heading2,
 4197                themeType: themeType,
 4198                componentState: ComponentState.Default
 4199            )
 4200        );
 201
 4202        builder.Append(
 4203            value: GetComponentCss(
 4204                prefix: "h3",
 4205                componentType: ComponentType.Heading3,
 4206                themeType: themeType,
 4207                componentState: ComponentState.Default
 4208            )
 4209        );
 210
 4211        builder.Append(
 4212            value: GetComponentCss(
 4213                prefix: "h4",
 4214                componentType: ComponentType.Heading4,
 4215                themeType: themeType,
 4216                componentState: ComponentState.Default
 4217            )
 4218        );
 219
 4220        builder.Append(
 4221            value: GetComponentCss(
 4222                prefix: "h5",
 4223                componentType: ComponentType.Heading5,
 4224                themeType: themeType,
 4225                componentState: ComponentState.Default
 4226            )
 4227        );
 228
 4229        builder.Append(
 4230            value: GetComponentCss(
 4231                prefix: "h6",
 4232                componentType: ComponentType.Heading6,
 4233                themeType: themeType,
 4234                componentState: ComponentState.Default
 4235            )
 4236        );
 237
 4238        return builder.ToString();
 239    }
 240
 241    /// <summary>Updates this theme by applying the provided <see cref="ThemeUpdater" />.</summary>
 242    /// <param name="updater">The <see cref="ThemeUpdater" /> describing the update to apply.</param>
 243    /// <returns>The modified <see cref="Theme" /> instance (for chaining).</returns>
 244    public Theme Set(ThemeUpdater updater)
 245    {
 5626246        _component = _component.Set(updater: updater);
 247
 5626248        return this;
 249    }
 250
 251    /// <summary>Builds the CSS rules for a specific combination of component, theme, and state.</summary>
 252    /// <param name="componentType">The <see cref="ComponentType" /> to generate CSS for.</param>
 253    /// <param name="themeType">The <see cref="ThemeType" /> to apply.</param>
 254    /// <param name="componentState">The <see cref="ComponentState" /> representing the current UI state.</param>
 255    /// <returns>A CSS string containing the computed declarations.</returns>
 256    private string ToCss(ComponentType componentType, ThemeType themeType, ComponentState componentState)
 68257        => _component.BuildCss(
 68258                builder: new CssBuilder(),
 68259                navigator: ThemeNavigator.Initialize
 68260                    .SetComponentTypes(componentType)
 68261                    .SetThemeTypes(themeType)
 68262                    .SetComponentStates(componentState)
 68263            )
 68264            .ToString();
 265
 266    /// <summary>Builds CSS variable declarations for all theme properties.</summary>
 267    /// <returns>A CSS string containing root-level variable definitions.</returns>
 268    private string ToCssVars()
 4269        => _component
 4270            .BuildCss(
 4271                builder: new CssBuilder(), navigator: ThemeNavigator.Initialize, varPrefix: StyleDefaults.VarPrefix
 4272            )
 4273            .ToString();
 274}