Throughout the book, we have been building Blazor Server and Blazor WebAssembly side by side. This is a great way to build our projects if we want to switch technologies further down the road or, as we do at work, share components between the customer portal and our internal CRM system.
Most of us will probably have one hosting model that we are working with, not usually two, but building it this way does have some perks.
Always think about if there might be a sharable part of the component we are building; that way, we can reuse it, and if we add something to the component, we get that benefit for all our components.
But it’s not only about sharing components inside our own projects. What if we want to create a library that can be shared with other departments, or even an open-source project sharing components with the world?
In this chapter, we will look at some of the things we already use when sharing components, and also at sharing CSS and other static files.
In this chapter, we will cover the following topics:
Make sure you have followed the previous chapters or use the Chapter08
folder as a starting point.
You can find the source code for this chapter’s result at https://github.com/PacktPublishing/Web-Development-with-Blazor-Second-Edition/tree/main/Chapter09.
If you are jumping into this chapter using the code from GitHub, make sure you have added Auth0 account information in the settings files. You can find the instructions in Chapter 8, Authentication and Authorization.
Blazor can use static files, such as images, CSS, and JavaScript. If we put our files in the wwwroot
folder, they will automatically be exposed to the internet and be accessible from the root of our site. The nice thing about Blazor is that we can do the same with a library; it is super easy to distribute static files within a library.
At work, we share components between all of our Blazor projects, and the shared library can also depend on other libraries. By sharing components and building our own components (sometimes on top of other libraries), we ensure we have the same look and feel throughout a site. We also share static content like images and CSS, and this makes it simple and fast if we need to change something and we want all our sites to be affected.
To link to a resource in another library/assembly, we can use the _content
folder.
Take a look at this example:
<link rel="stylesheet" href="_content/Components/MyBlogStyle.min.css" />
The HTML link
tag, rel
, and href
are ordinary HTML tags and attributes, but adding the URL that starts with _content
tells us that the content we want to access is in another library. The name of the library (assembly name), in our case Components
, is followed by the file we want to access, which is stored in the wwwroot
folder in our library.
Blazor is, in the end, just HTML, and HTML can be styled using CSS. As mentioned, the Blazor templates are using Bootstrap by default, and we will continue to use that as well.
There is an excellent site with easy-to-use Bootstrap themes ready to be downloaded, which can be found at https://bootswatch.com/.
I like the Darkly theme, so that’s the one we’ll use, but feel free to experiment with this later on.
I often get asked about how to style out Blazor apps, and the truth is you can use all the things you are used to. In the end, Blazor will output HTML. There are many languages and frameworks we can use to write our CSS.
We can use CSS, SASS, and LESS. As long as the output is CSS, we can use it.
In this chapter, we will stick with Bootstrap and continue using CSS. SASS and LESS are outside this book’s scope.
Tailwind is a popular framework for Blazor, and it is absolutely possible to use it together with Blazor. Tailwind is very component-focused and needs a bit of configuration to start, but if it is something you have worked with and like, you can use it together with Blazor.
Many templates use Bootstrap as a base, so if you are looking for a design for your website, using a Bootstrap-based template will be an easy implementation.
The problem with Bootstrap (and why some people don’t like it) is that many sites use Bootstrap and “all” sites look the same. This can be good if we are building a LOB (Line of Business), but it can be bad if we are trying to be innovative. Bootstrap is also quite large when it comes to downloading, so that is also an argument against it.
This chapter is about making our blog look a bit nicer, so we will stick with Bootstrap, but we should know that if we use something else to handle our CSS, it will work with Blazor.
One of these template sites is Bootswatch, which gives us some nice variations from the traditional Bootstrap themes:
bootstrap.min.css
.wwwroot
folder, add the bootstrap.min.css
file.We have all the prerequisites and CSS that we can add to our site.
Now it’s time to add a new style to our sites. Let’s start with BlazorServer:
Pages/_Host.cshtml
.<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
With:
<link rel="stylesheet" href="_content/Components/bootstrap.min.css" />
Great! Our Blazor Server project is now updated to use the new style. The main color should now be dark, but there is still some work to do.
Now let’s do the same with the Blazor WebAssembly project:
wwwroot/index.html
.<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
With:
<link rel="stylesheet" href="_content/Components/bootstrap.min.css" />
Now we have the same layout for both projects.
Let’s now clean it up some more. We have only started with the admin functionality, so let’s make it more accessible. The menu on the left is no longer required, so let’s change it so that it is only visible if you are an administrator:
Components/Shared/MainLayout.razor
and put AuthorizeView
around the sidebar
div
like this:
<AuthorizeView Roles="Administrator">
<div class="sidebar">
<NavMenu />
</div>
</AuthorizeView>
In this case, we are not specifying Authorized
or NotAuthorized
. The default behavior is Authorized
, so if we are only looking for an authorized state, we don’t need to specify it by name.
Since this is already a shared component, we are all set. Start one of the projects (BlazorServer or BlazorWebAssembly.Server) to see it in action. The menu should not be shown if we are not logged in.
Now we need to make the menu look better. Even though the counter is really fun to click on, it doesn’t make much sense regarding our blog.
Since the nav
menu is now shared, we can put it in one place, which will change for both Blazor Server and Blazor WebAssembly.
We should replace the links with links to our admin pages instead:
Shared/Navmenu.razor
file.
Edit the code so that it looks like this (keep the code block as is):
<div class="top-row pl-4 navbar navbar-dark">
<a class="navbar-brand" href="">MyBlog Admin</a>
<button class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span>
</button>
</div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<ul class="nav flex-column">
<li class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="Admin/Blogposts">
<span class="oi oi-signpost" aria-hidden="true"></span> Blog posts
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="Admin/Tags">
<span class="oi oi-tags" aria-hidden="true"></span> Tags
</NavLink>
</li>
<li class="nav-item px-3">
<NavLink class="nav-link" href="Admin/Categories">
<span class="oi oi-tags" aria-hidden="true"></span> Categories
</NavLink>
</li>
</ul>
</div>
Great! Our blog is looking more like a blog, but we can do more!
The admin interface is done (at least, for now), and we should focus on the front page of our blog. The front page should have the title of the blog post and some descriptions.
Pages/Index.razor
file.using
statement for Markdig
at the top of the file:
@using Markdig;
OnInitializedAsync
method to handle the instantiation of the Markdig
pipeline (this is the same code we have in the Post.razor
file):
MarkdownPipeline pipeline;
protected override Task OnInitializedAsync()
{
pipeline = new MarkdownPipelineBuilder()
.UseEmojiAndSmiley()
.Build();
return base.OnInitializedAsync();
}
Virtualize
component, change the content (RenderFragment
) to the following:
<Virtualize ItemsProvider="LoadPosts" Context="p">
<article>
<h2>@p.Title</h2>
@((MarkupString)Markdig.Markdown.ToHtml(new string(p.Text.Take(100).ToArray()), pipeline))
<a href="/Post/@p.Id">Read more</a>
</article>
</Virtualize>
<ul>
tags.Now, run the project using Ctrl + F5 and look at our new front page. Our blog is starting to take form, but we still have work to do.
In .NET 5, Microsoft added something called isolated CSS. This is something that many other frameworks have as well. The idea is to write CSS specifically for one component. The upsides, of course, are that the CSS that we create won’t impact any of the other components.
The template for Blazor uses isolated CSS for Components/Shared/MainLayout.razor
and NavMenu.Razor
. If we expand MainLayout.razor
, we’ll see a file called MainLayout.razor.css
.
We can also use SASS here by adding a file called MainLayout.razor.scss
. The important thing is that the file we add should generate a file called MainLayout.razor.css
for the compiler to pick up.
This naming convention will make sure to rewrite CSS and the HTML output.
CSS has the following naming convention:
main {
flex: 1;
}
It will be rewritten as follows:
main[b-bfl5h5967n] {
flex: 1;
}
This means the elements need to have an attribute called b-bfl5h5967n
(in this case) for the style to be applied.
The div
tag that has the CSS
tag within the MainLayout
component will be outputted like this:
<main b-bfl5h5967n>
For all of this to happen, we also need to have a link to the CSS (which is provided by the template), and it looks like this:
<link href="{Assemblyname}.styles.css" rel="stylesheet">
This becomes useful for component libraries. We have components that have isolated CSS in our shared library (NavMenu
and MainLayout
), and the CSS for the NavMenu
component is included in the {Assemblyname}.styles.css
file.
We don’t have to do anything extra for our shared CSS to be included. If we are creating a library for anyone to use, we should think about using the isolated CSS approach if our components need some CSS to work correctly.
If we are starting our Blazor project from an empty template, we need to add a link to the isolated CSS.
This way, our users won’t have to add a reference to our CSS, and there is no risk of our CSS breaking something in the user’s app (since it’s isolated). The important thing is that we use the right approach when it makes sense.
Suppose we are creating a component that has very specific styles, which only that component will use. In that case, isolated CSS is a great way to go, it is easier to find (right by the component), and we can use CSS variables for colors and such.
We should be careful when styling similar things inside of the isolated CSS, so we don’t end up having a bunch of different CSS files styling a button, for example.
As mentioned, the isolated CSS only affects the HTML tags inside the component, but what if we have a component inside our component?
If we open Component/Shared/NavMenu.css
, we can see that for the .nav-item
styles, some of them are using the keyword ::deep
; this is to say that even child components should also be affected by this style.
Take a look at this code:
.nav-item ::deep a {…}
It is targeting the <a>
tag, but the Razor code looks like this:
<li class="nav-item px-3">
<NavLink class="nav-link" href="Admin/Blogposts">
<span class="oi oi-signpost" aria-hidden="true"></span> Blog posts
</NavLink>
</li>
It is the NavLink
component that renders the <a>
t
ag; by adding ::deep
, we are saying we want to apply this style to all elements with the class .nav-item
and all the <a>
t
ags inside that element.
There is one more thing we need to know about – ::deep
; it makes sure to share the ID of the attribute (b-bfl5h5967n
, for example), and it needs an HTML tag to do so. So if we have a component that consists of other components (not adding any HTML tags at all), we need to add an HTML tag around the content to make ::deep
work.
Before we summarize this chapter, let us do one more thing.
Let’s fix the background color of the menu:
Components/Shared/MainLayout.razor.css
..sidebar
style and replace it with:
.sidebar {
background-image: linear-gradient(180deg, var(--bs-body-bg) 0%,var(--bs-gray-800) 70%);
}
.top-row
style with:
.top-row {
background-color: var(--bs-primary);
justify-content: flex-end;
height: 3.5rem;
display: flex;
align-items: center;
}
We replaced the background color and removed a border.
.top-row ::deep a, .top-row ::deep .btn-link
style, add:
color:white;
Now we are able to see the login/logout link a bit better.
We now have a working admin interface and a good-looking site.
In this chapter, we have moved components into a shared library and used that library with both our Blazor Server and Blazor WebAssembly projects.
Using shared libraries like this is the way to create shared libraries (for others to use), and it is also a great way to structure our in-house projects (so that it is easy to change from Blazor Server to Blazor WebAssembly, or the other way around). If you have a site already, you can build your Blazor components in a shared library, which we have done throughout the book.
Using components as part of your site (using Blazor Server), you can get started with Blazor bit by bit until you have converted the whole thing. When that is done, you can decide whether or not to keep using Blazor Server (as I mentioned, we use Blazor Server at work) or move to Blazor WebAssembly.
We talked about how we can use SASS and CSS in our site, both regular CSS and isolated CSS.
In the next chapter, we will learn about the one thing we are trying to avoid (at least I am) as Blazor developers – JavaScript.