This article describes how to customize the default CSS setup in Blazor, and looks at the new Scoped CSS.
It covers:
Please note that this article is aimed at programmers relatively new to DotNetCore and Blazor, and assumes you have some knowledge of SASS. The article also assumes you're using Visual Studio 2019 - I use the Community Edition.
The code is avalable at Blazor.CSS on GitHub.
compilerconfig.json will look like this:
[ { "outputFile": "SASS/bootstrap-site.css", "inputFile": "SASS/bootstrap-site.scss" } ]
[ { "outputFile": "wwwroot/css/site.css", "inputFile": "SASS/bootstrap-site.scss" } ]
Add a Bootstrap folder to SASS.
Add a custom folder to Bootstrap.
Download the Bootstrap Source from the Bootstrap site and copy the scss folder to Bootstrap.
The full SASS folder (including the Spectre and other files that we will add later) should look like this:
/* Source SASS file to build custom Bootstrap site file */
@import "../wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css";
@import "Bootstrap/scss/_functions";
@import "Bootstrap/scss/bootstrap";
/* This is the original site.css file that contains the site specific customizations*/
@import "custom.scss";
Save and Web Compiler will compile a new site.css. Watch the status in the bottom left corner of Visual Studio.
You should have a *site.css and a site.min.css in wwwroot/css.
Edit _Host.cshtml, and remove the reference to bootstrap.min.css - all the css is now compiled into site.css.
<head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Blazor.CSS</title> <base href="~/" /> \\ Remove bootstrap CSS reference <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" /> \\ <link href="css/site.css" rel="stylesheet" /> \\ This is the new Net5 build generated Scoped CSS stylesheet \\ more later in this article <link href="Blazor.CSS.styles.css" rel="stylesheet" /> </head>
We customize Bootstrap by adding new scss files. I'm assuming you have a basic understanding of SASS. If not then do a bit of background reading - it isn't rocket science.
To demonstrate customization, we'll adjust some of the colours and add some new button styles. Most of the code is derived from the SB2 Bootstrap template.
Add _variables.scss to SASS/custom and add the following content. You can compare it with the base SASS/Bootstrap/scss/_variables.scss to see the differences.
// Override Bootstrap default variables here
// Do not edit any of the files in /bootstrap/scss/!
// Color Variables
// Bootstrap Color Overrides
$white: #fff !default;
$gray-100: #f8f9fc !default;
$gray-200: #eaecf4 !default;
$gray-300: #dddfeb !default;
$gray-400: #d1d3e2 !default;
$gray-500: #b7b9cc !default;
$gray-600: #858796 !default;
$gray-700: #6e707e !default;
$gray-800: #5a5c69 !default;
$gray-900: #3a3b45 !default;
$black: #000 !default;
// We've adjusted the colors
$blue: #4e73df !default;
$indigo: #6610f2 !default;
$purple: #6f42c1 !default;
$pink: #e83e8c !default;
$red: #e74a3b !default;
$orange: #fd7e14 !default;
$yellow: #f6c23e !default;
$green: #1cc88a !default;
$teal: #20c9a6 !default;
$cyan: #36b9cc !default;
$primary: $blue !default;
$secondary: $gray-600 !default;
$success: $green !default;
$info: $cyan !default;
$warning: $yellow !default;
$danger: $red !default;
$light: $gray-100 !default;
$dark: $gray-800 !default;
$brand: #b3ccff;
$theme-colors: () !default;
// stylelint-disable-next-line scss/dollar-variable-default
// We've added brand, add, edit,...
$theme-colors: map-merge( (
"primary": $primary,
"secondary": $secondary,
"success": $success,
"info": $info,
"warning": $warning,
"danger": $danger,
"error": $danger,
"light": $light,
"dark": $dark,
"brand": $brand,
"add": $primary,
"new": $info,
"edit": $primary,
"delete": $danger,
"nav": $secondary,
"change": $warning,
"save": $success
),
$theme-colors );
// Custom Colors
$brand-google: #ea4335 !default;
$brand-facebook: #3b5998 !default;
// Set Contrast Threshold
$yiq-contrasted-threshold: 195 !default;
// Typography
$body-color: $gray-600 !default;
$font-family-sans-serif: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", 'Noto Color Emoji' !default;
$font-size-base: .925rem !default;
$font-size-lg: $font-size-base * 1.20 !default;
$font-size-sm: $font-size-base * .875 !default;
$font-weight-light: 300 !default;
// $font-weight-base: 400;
$headings-font-weight: 400 !default;
// Shadows
$box-shadow-sm: 0 0.125rem 0.25rem 0 rgba($gray-900, .2) !default;
$box-shadow: 0 0.15rem 1.75rem 0 rgba($gray-900, .15) !default;
// $box-shadow-lg: 0 1rem 3rem rgba($black, .175) !default;
// Borders Radius
$border-radius: 0.35rem !default;
$border-color: darken($gray-200, 2%) !default;
// Spacing Variables
// Change below variable if the height of the navbar changes
$topbar-base-height: 4.375rem !default;
// Change below variable to change the width of the sidenav
$sidebar-base-width: 14rem !default;
// Change below variable to change the width of the sidenav when collapsed
$sidebar-collapsed-width: 6.5rem !default;
// Card
$card-cap-bg: $gray-100 !default;
$card-border-color: $border-color !default;
// Adjust column spacing for symmetry
$spacer: 1rem !default;
$grid-gutter-width: $spacer * 1.5 !default;
// Transitions
$transition-collapse: height .15s ease !default;
// Dropdowns
$dropdown-font-size: 0.85rem !default;
$dropdown-border-color: $border-color !default;
/* turn off rounding */
$enable-rounded: false;
Add _overrides.scss to SASS/custom and add the following content. This demonstrates the sort of changes you can make - we don't actually use it on the site.
/* Reduce the default form-group bottom margin*/ .form-group { margin-bottom: .25rem; } /* set new margins and padding for small alerts*/ div.alert-sm .alert { padding: .25rem 1.25rem; margin-bottom: 0rem; }
To build the custom version of Bootstrap, we need to add the new SASS files into the compile process.
Edit SASS/bootstrap-site.scss
/* Source SASS file to build custom Bootstrap site file */ @import "../wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css"; @import "Bootstrap/scss/_functions"; @import "Bootstrap/Custom/_variables"; @import "Bootstrap/scss/bootstrap"; @import "Bootstrap/Custom/_overrides"; /* This is the original site.css file that contains the site specific customizations*/ @import "custom.scss";
Save and this should compile.
To see the changes, edit Pages/Counter.razor and add a few extra buttons to the page.
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button> <button class="btn btn-save" @onclick="IncrementCount">Save Styled Click me</button> <button class="btn btn-delete" @onclick="IncrementCount">Delete Styled Click me</button> <button class="btn btn-brand" @onclick="IncrementCount">Brand Styled Click me</button>
Run the site and navigate to the counter page to check the button customization. They should look like this:
Not everyone wants to use Bootstrap - some people like their sites being a little different! In this section we'll change to Spectre.
@import "Spectre/scss/spectre";
[ { "outputFile": "wwwroot/css/site.css", "inputFile": "SASS/site.scss" }, { "outputFile": "wwwroot/css/spectre.css", "inputFile": "SASS/site-spectre.scss" }, { "outputFile": "wwwroot/css/spectre-icons.css", "inputFile": "SASS/Spectre/scss/Spectre-icons.scss" } ]
Once you save this you should get a compiled *spectre.css in wwwroot/css
Add folder custom to SASS/Spectre
Add _variables.scss
// Control colors
$brand-color: #7952b3 !default;
$exit-color: #66758c !default;
$save-color: #32b643 !default;
$delete-color: #e85600 !default;
Add _buttons.scss
// Button Colors
.btn {
&.btn-exit {
@include button-variant($exit-color);
}
&.btn-brand {
@include button-variant($brand-color);
}
&.btn-delete {
@include button-variant($delete-color);
}
}
Edit SASS/site-spectre.scss
@import "Spectre/custom/_variables";
@import "Spectre/scss/spectre";
@import "Spectre/custom/_buttons";
Finally change _Host.cshtml over to the new css.
<head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Blazor.CSS</title> <base href="~/" /> \\ Link to the new custom Spectre CSS <link href="css/spectre.css" rel="stylesheet" /> <link href="Blazor.CSS.styles.css" rel="stylesheet" /> </head>
Run the site.
It will look a little different - it needs some work to fix small problems, but basically works. Go to the Counter page to see the different buttons - Spectre and Bootstrap button class names are very similar so work.
A new feature in Net5 is scoped CSS a.k.a. component styling. Take a look at the Shared folder in the project and you'll see css files attached to the NavMenu and MainLayout razor components.
To explore how this works, let's re-style the FetchData data table.
@using Blazor.CSS.Data @if (forecasts == null) { <p><em>Loading...</em></p> } else { <table class="table"> <thead> <tr> <th>Date</th> <th>Temp. (C)</th> <th>Temp. (F)</th> <th>Summary</th> <th class="max-column">Detail</th> </tr> </thead> <tbody> @foreach (var forecast in forecasts) { <tr> <td>@forecast.Date.ToShortDateString()</td> <td>@forecast.TemperatureC</td> <td>@forecast.TemperatureF</td> <td>@forecast.Summary</td> <td class="max-column"> <div class="grid-overflow"> <div class="grid-overflowinner"> @($"The Weather Forecast for this {forecast.Date.DayOfWeek}, the {forecast.Date.Day} of the month {forecast.Date.Month} in the year of our Lord {forecast.Date.Year} is {forecast.Summary}") </div> </div> </td> </tr> } </tbody> </table> } @code { [Parameter] public WeatherForecast[] forecasts { get; set; } = null; }
.max-column { width:50%; } .grid-overflow { display: flex; } .grid-overflowinner { flex: 1; width: 1px; overflow-x: hidden; white-space: nowrap; text-overflow: ellipsis; }
@page "/fetchdata" @using Blazor.CSS.Data @inject WeatherForecastService ForecastService <h1>Weather forecast</h1> <p>This component demonstrates fetching data from a service and a data grid to displaying it.</p> <DataGrid forecasts="this.forecasts"></DataGrid> @code { private WeatherForecast[] forecasts; protected override async Task OnInitializedAsync() { forecasts = await ForecastService.GetForecastAsync(DateTime.Now); } }
Note the max column operating with the ellipsis shortening as the page is narrowed.
Open Developer Tools in the browser and take a look at the HTML.
Note the new unique ID attribute used on various elements in the HTML.
Look at Blazor.CSS.styles.css - the CSS file generated by Blazor during the build process. You can see the css scoped to the components using the unique IDs.
Finally, look at the obj view in Solution Explorer in the project. You can see the specific css files generated for each component and the bundled file based on the project name. This is exposed in the wwwroot folder.
There are many ways to deploy and use custom CSS in Blazor. This is one of many, but probably one of the simplest.
I've covered Scoped CSS here, though I'm not sure how much I'll use it.