BlazorComponentUtilities 1.1.0

A clean code approach to conditional CSS Classes in Blazor Components (Razor Components). When building complex components for ASP.NET Blazor there are often CSS classes generated by multiple factors. Styles can often be added by system & component state, public parameters, and static base CSS classes. Combining many CSS classes together doesn't scale well, and when components get large it can be tough to reason about. The CSS builder pattern fixes this problem by offering a easy to repeat, easy to read pattern to use across components in a project.

Install-Package BlazorComponentUtilities -Version 1.1.0
dotnet add package BlazorComponentUtilities --version 1.1.0
<PackageReference Include="BlazorComponentUtilities" Version="1.1.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add BlazorComponentUtilities --version 1.1.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.

CSS Builder Pattern

When building complex components for ASP.NET Razor Components there are often CSS classes generated by multiple factors.
Styles can often be added by system & component state, public parameters, and static base CSS classes.
Combining many CSS classes together doesn't scale well, and when components get large it can be tough to reason about.
The CSS builder pattern fixes this problem by offering a easy to repeat, easy to read pattern to use across components in a project.
A clean code approach to conditional CSS in Razor Components.

The problem

Given the component below, the Tab's CSS class is set by multiple sources.
The componet has paramater UserCss which can be set at externally.
A base CSS class bl-nav-link which does not change.
Two state based CSS classes bl-active and bl-disabled which are dependent on if the Tab is active or disabled.
Rendering for the CSS class is spread across multiple concerns and is hard to maintain.

Component

<Tab UserCss="my-class" Disabled=true >...</Tab>

Component Source Markup

<li class="bl-nav-item">
    <a onclick=@Activate class="bl-nav-link @ActiveCssClass @DisabledCssClass" role="button">
        @Title
    </a>
</li>

Component Source Logic

[Parameter] string UserCss = String.Empty;
protected string ActiveCssClass => IsActive ? "bl-active" : String.Empty;
protected string DisabledCssClass => Disabled ? "bl-disabled" : String.Empty;

The Solution

Using the CSS Builder pattern we can clean up the code and move everything to a single concern.
Refactoring the component, we move all of the CSS code to the components logic.
In the OnParameterSet lifecycle method, we condence all of the CSS responsibilities into a single variable ClassToRender (this name variable is optional).
Next, using the CssBuilder we define what classes should be added to the builder and when they should be rendered.
The pattern is as follows AddClass the CSS class and the condition when it should appear AddClass(value, when: bool).
If the value is static, we can discard the when parameter.
Finally, Build is called. All CSS classes are then combined into a single string, properly spaced and trimmed.

The refactored code becomes:

Component Source Markup (refactor)

<li class="bl-nav-item">
    <a onclick=@Activate class="@ClassToRender" role="button">
        @Title
    </a>
</li>

Component Source Logic (refactor)

protected override void OnParametersSet()
{
    ClassToRender = new CssBuilder(UserCss)
                        .AddClass("bl-nav-link")
                        .AddClass("bl-active", when: IsActive)
                        .AddClass("bl-disabled", when: Disabled)
                        .Build();
}

Dynamic values

If you encounter a dynamic value where some string is added to a CSS class name, we can easily handle this as well.
Consider the CSS namespace bl- must be prefixed to a value SomeValue supplied by the component state or user input.
This is easily handled with string interpolation ${var}.

protected override void OnParametersSet()
{
    ClassToRender = new CssBuilder(UserCss)
                        .AddClass("bl-nav-link")
                        .AddClass("bl-active", when: IsActive)
                        .AddClass("bl-disabled", when: Disabled)
			.AddClass("bl-${SomeValue}", when: IsConditionMet)
                        .Build();
}

Func When

Func<bool> is also accepted as an arguement for the when parameter. This allows either inline functions or named functions to be called directly.

protected override void OnParametersSet()
{
    ClassToRender = new CssBuilder(UserCss)
                        .AddClass("bl-nav-link")
			.AddClass("bl-foo", when: ()=> 
			   !string.IsNullOrEmpty(Foo) ||
                           !Disabled ||
                           IsActive)
                        .AddClass("bl-active", when: IsActive)
                        .AddClass("bl-disabled", when: Disabled)
                        .Build();
}

Named function, example.


bool HasMeaningfulName() => !string.IsNullOrEmpty(Foo) || !Disabled || IsActive);

protected override void OnParametersSet()
{
    ClassToRender = new CssBuilder(UserCss)
                        .AddClass("bl-nav-link")
			.AddClass("bl-foo", when: HasMeaningfulName)
                        .AddClass("bl-active", when: IsActive)
                        .AddClass("bl-disabled", when: Disabled)
                        .Build();
}

CSS Builder Pattern

When building complex components for ASP.NET Razor Components there are often CSS classes generated by multiple factors.
Styles can often be added by system & component state, public parameters, and static base CSS classes.
Combining many CSS classes together doesn't scale well, and when components get large it can be tough to reason about.
The CSS builder pattern fixes this problem by offering a easy to repeat, easy to read pattern to use across components in a project.
A clean code approach to conditional CSS in Razor Components.

The problem

Given the component below, the Tab's CSS class is set by multiple sources.
The componet has paramater UserCss which can be set at externally.
A base CSS class bl-nav-link which does not change.
Two state based CSS classes bl-active and bl-disabled which are dependent on if the Tab is active or disabled.
Rendering for the CSS class is spread across multiple concerns and is hard to maintain.

Component

<Tab UserCss="my-class" Disabled=true >...</Tab>

Component Source Markup

<li class="bl-nav-item">
    <a onclick=@Activate class="bl-nav-link @ActiveCssClass @DisabledCssClass" role="button">
        @Title
    </a>
</li>

Component Source Logic

[Parameter] string UserCss = String.Empty;
protected string ActiveCssClass => IsActive ? "bl-active" : String.Empty;
protected string DisabledCssClass => Disabled ? "bl-disabled" : String.Empty;

The Solution

Using the CSS Builder pattern we can clean up the code and move everything to a single concern.
Refactoring the component, we move all of the CSS code to the components logic.
In the OnParameterSet lifecycle method, we condence all of the CSS responsibilities into a single variable ClassToRender (this name variable is optional).
Next, using the CssBuilder we define what classes should be added to the builder and when they should be rendered.
The pattern is as follows AddClass the CSS class and the condition when it should appear AddClass(value, when: bool).
If the value is static, we can discard the when parameter.
Finally, Build is called. All CSS classes are then combined into a single string, properly spaced and trimmed.

The refactored code becomes:

Component Source Markup (refactor)

<li class="bl-nav-item">
    <a onclick=@Activate class="@ClassToRender" role="button">
        @Title
    </a>
</li>

Component Source Logic (refactor)

protected override void OnParametersSet()
{
    ClassToRender = new CssBuilder(UserCss)
                        .AddClass("bl-nav-link")
                        .AddClass("bl-active", when: IsActive)
                        .AddClass("bl-disabled", when: Disabled)
                        .Build();
}

Dynamic values

If you encounter a dynamic value where some string is added to a CSS class name, we can easily handle this as well.
Consider the CSS namespace bl- must be prefixed to a value SomeValue supplied by the component state or user input.
This is easily handled with string interpolation ${var}.

protected override void OnParametersSet()
{
    ClassToRender = new CssBuilder(UserCss)
                        .AddClass("bl-nav-link")
                        .AddClass("bl-active", when: IsActive)
                        .AddClass("bl-disabled", when: Disabled)
			.AddClass("bl-${SomeValue}", when: IsConditionMet)
                        .Build();
}

Func When

Func<bool> is also accepted as an arguement for the when parameter. This allows either inline functions or named functions to be called directly.

protected override void OnParametersSet()
{
    ClassToRender = new CssBuilder(UserCss)
                        .AddClass("bl-nav-link")
			.AddClass("bl-foo", when: ()=> 
			   !string.IsNullOrEmpty(Foo) ||
                           !Disabled ||
                           IsActive)
                        .AddClass("bl-active", when: IsActive)
                        .AddClass("bl-disabled", when: Disabled)
                        .Build();
}

Named function, example.


bool HasMeaningfulName() => !string.IsNullOrEmpty(Foo) || !Disabled || IsActive);

protected override void OnParametersSet()
{
    ClassToRender = new CssBuilder(UserCss)
                        .AddClass("bl-nav-link")
			.AddClass("bl-foo", when: HasMeaningfulName)
                        .AddClass("bl-active", when: IsActive)
                        .AddClass("bl-disabled", when: Disabled)
                        .Build();
}

Release Notes

Stability fixes.
- Guards added for Null Ref Exception when CssClass is empty

  • .NETStandard 2.0

    • No dependencies.

This package is not used by any popular GitHub repositories.

Version History

Version Downloads Last updated
1.1.0 7,168 7/10/2019
1.0.0 282 4/11/2019