< Summary

Information
Class: Allyaria.Theming.ThemeTypes.ThemeStyle
Assembly: Allyaria.Theming
File(s): /home/runner/work/allyaria/allyaria/src/Allyaria.Theming/ThemeTypes/ThemeStyle.cs
Line coverage
100%
Covered lines: 51
Uncovered lines: 0
Coverable lines: 51
Total lines: 159
Line coverage: 100%
Branch coverage
94%
Covered branches: 53
Total branches: 56
Branch coverage: 94.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
BuildCss(...)100%66100%
BuildCssValue(...)75%44100%
EnsureContrast()100%3030100%
Get(...)100%11100%
Set(...)100%88100%
SetColor(...)75%44100%
SetValue(...)75%44100%

File(s)

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

#LineLine coverage
 1namespace Allyaria.Theming.ThemeTypes;
 2
 3/// <summary>
 4/// Represents the lowest layer in the Allyaria theming hierarchy, responsible for holding and rendering individual styl
 5/// properties (such as colors, borders, and font-related values).
 6/// </summary>
 7/// <remarks>
 8///     <para>
 9///     A <see cref="ThemeStyle" /> corresponds to a single <see cref="ComponentState" /> (e.g., Default, Hovered) and
 10///     manages a collection of <see cref="StyleType" /> → <see cref="IStyleValue" /> mappings.
 11///     </para>
 12///     <para>
 13///     This class ensures contrast compliance for color-related properties according to WCAG 2.2 AA standards and produ
 14///     normalized CSS through <see cref="BuildCss" />.
 15///     </para>
 16/// </remarks>
 17internal sealed class ThemeStyle
 18{
 19    /// <summary>Stores all style values for this theme style, keyed by <see cref="StyleType" />.</summary>
 271920    private readonly Dictionary<StyleType, IStyleValue> _children = new();
 21
 22    /// <summary>
 23    /// Builds the CSS output for this style definition and its contained <see cref="IStyleValue" /> instances.
 24    /// </summary>
 25    /// <param name="builder">The <see cref="CssBuilder" /> used to accumulate CSS declarations.</param>
 26    /// <param name="navigator">
 27    /// The <see cref="ThemeNavigator" /> defining which <see cref="StyleType" /> values should be included.
 28    /// </param>
 29    /// <param name="varPrefix">An optional CSS variable prefix used when generating scoped variable names.</param>
 30    /// <returns>A <see cref="CssBuilder" /> containing the merged style declarations.</returns>
 31    internal CssBuilder BuildCss(CssBuilder builder, ThemeNavigator navigator, string? varPrefix = "")
 32    {
 4033        if (navigator.StyleTypes.Count is 0)
 34        {
 10635            foreach (var child in _children)
 36            {
 3137                builder = BuildCssValue(builder: builder, key: child.Key, value: child.Value, varPrefix: varPrefix);
 38            }
 39        }
 40        else
 41        {
 7442            foreach (var key in navigator.StyleTypes)
 43            {
 1944                builder = BuildCssValue(builder: builder, key: key, value: Get(key: key), varPrefix: varPrefix);
 45            }
 46        }
 47
 4048        return builder;
 49    }
 50
 51    /// <summary>Appends an individual CSS property or grouped value to the provided builder.</summary>
 52    /// <param name="builder">The <see cref="CssBuilder" /> used for output.</param>
 53    /// <param name="key">The <see cref="StyleType" /> identifying the CSS property.</param>
 54    /// <param name="value">The value associated with the style property.</param>
 55    /// <param name="varPrefix">An optional CSS variable prefix for variable-scoped styles.</param>
 56    /// <returns>The updated <see cref="CssBuilder" />.</returns>
 57    private static CssBuilder BuildCssValue(CssBuilder builder, StyleType key, IStyleValue? value, string? varPrefix)
 5058        => value is null
 5059            ? builder
 5060            : value is StyleGroup group
 5061                ? group.BuildCss(builder: builder, varPrefix: varPrefix)
 5062                : builder.Add(name: key.GetDescription(), value: value.Value, varPrefix: varPrefix);
 63
 64    /// <summary>
 65    /// Ensures sufficient contrast between foreground and background color properties, following WCAG 2.2 AA standards 
 66    /// visual accessibility.
 67    /// </summary>
 68    /// <remarks>
 69    /// This method adjusts key color-related properties—such as text, border, and accent colors— to maintain a minimum
 70    /// contrast ratio of 4.5:1 against the background.
 71    /// </remarks>
 72    private void EnsureContrast()
 73    {
 470874        var accentColor = ((StyleColor?)Get(key: StyleType.AccentColor))?.Color;
 470875        var backgroundColor = ((StyleColor?)Get(key: StyleType.BackgroundColor))?.Color;
 470876        var borderColor = ((StyleColor?)Get(key: StyleType.BorderColor))?.Color;
 470877        var caretColor = ((StyleColor?)Get(key: StyleType.CaretColor))?.Color;
 470878        var color = ((StyleColor?)Get(key: StyleType.Color))?.Color;
 470879        var outlineColor = ((StyleColor?)Get(key: StyleType.OutlineColor))?.Color;
 470880        var textDecorationColor = ((StyleColor?)Get(key: StyleType.TextDecorationColor))?.Color;
 81
 470882        if (backgroundColor?.IsTransparent() ?? true)
 83        {
 157084            return;
 85        }
 86
 313887        accentColor = accentColor?.EnsureContrast(background: backgroundColor.Value, minimumRatio: 4.5);
 313888        borderColor = borderColor?.EnsureContrast(background: backgroundColor.Value, minimumRatio: 4.5);
 313889        caretColor = caretColor?.EnsureContrast(background: backgroundColor.Value, minimumRatio: 4.5);
 313890        color = color?.EnsureContrast(background: backgroundColor.Value, minimumRatio: 4.5);
 313891        outlineColor = outlineColor?.EnsureContrast(background: backgroundColor.Value, minimumRatio: 4.5);
 313892        textDecorationColor = textDecorationColor?.EnsureContrast(background: backgroundColor.Value, minimumRatio: 4.5);
 93
 313894        SetColor(key: StyleType.AccentColor, color: accentColor)
 313895            .SetColor(key: StyleType.BackgroundColor, color: backgroundColor)
 313896            .SetColor(key: StyleType.BorderColor, color: borderColor)
 313897            .SetColor(key: StyleType.CaretColor, color: caretColor)
 313898            .SetColor(key: StyleType.Color, color: color)
 313899            .SetColor(key: StyleType.OutlineColor, color: outlineColor)
 3138100            .SetColor(key: StyleType.TextDecorationColor, color: textDecorationColor);
 3138101    }
 102
 103    /// <summary>Retrieves the <see cref="IStyleValue" /> associated with the given <see cref="StyleType" />.</summary>
 104    /// <param name="key">The <see cref="StyleType" /> to retrieve.</param>
 105    /// <returns>The associated <see cref="IStyleValue" />, or <see langword="null" /> if not found.</returns>
 32975106    private IStyleValue? Get(StyleType key) => _children.GetValueOrDefault(key: key);
 107
 108    /// <summary>Applies a <see cref="ThemeUpdater" /> to set or update style values for this theme style.</summary>
 109    /// <param name="updater">The <see cref="ThemeUpdater" /> containing the update definition.</param>
 110    /// <returns>The same <see cref="ThemeStyle" /> instance for fluent configuration.</returns>
 111    internal ThemeStyle Set(ThemeUpdater updater)
 112    {
 17512113        var isColor = false;
 114
 70048115        foreach (var key in updater.Navigator.StyleTypes)
 116        {
 17512117            SetValue(key: key, value: updater.Value);
 118
 17512119            if (!isColor && updater.Value is StyleColor)
 120            {
 4708121                isColor = true;
 122            }
 123        }
 124
 17512125        if (isColor)
 126        {
 4708127            EnsureContrast();
 128        }
 129
 17512130        return this;
 131    }
 132
 133    /// <summary>Assigns a specific color value to the given <see cref="StyleType" />.</summary>
 134    /// <param name="key">The <see cref="StyleType" /> identifying the color property.</param>
 135    /// <param name="color">The <see cref="HexColor" /> to assign.</param>
 136    /// <returns>The current <see cref="ThemeStyle" /> instance for chaining.</returns>
 137    private ThemeStyle SetColor(StyleType key, HexColor? color)
 21966138        => color is null
 21966139            ? this
 21966140            : SetValue(key: key, value: new StyleColor(value: color));
 141
 142    /// <summary>Sets or removes a style value for the specified <see cref="StyleType" />.</summary>
 143    /// <param name="key">The <see cref="StyleType" /> identifying the CSS property.</param>
 144    /// <param name="value">The <see cref="IStyleValue" /> representing the style value.</param>
 145    /// <returns>The same <see cref="ThemeStyle" /> instance for fluent chaining.</returns>
 146    private ThemeStyle SetValue(StyleType key, IStyleValue? value)
 147    {
 30059148        if (string.IsNullOrWhiteSpace(value: value?.Value))
 149        {
 1150            _children.Remove(key: key);
 151        }
 152        else
 153        {
 30058154            _children[key: key] = value;
 155        }
 156
 30059157        return this;
 158    }
 159}