Click here to Skip to main content
15,884,388 members
Articles / Web Development / Blazor

Blazor and CSS Frameworks

Rate me:
Please Sign up or sign in to vote.
4.87/5 (9 votes)
16 Dec 2020CPOL5 min read 17.1K   12   7
How to customize default CSS setup in Blazor, and a look at the new Scoped CSS
This article describes how to customize the default CSS setup in Blazor, and looks at the new Scoped CSS.

Introduction

This article covers:

  1. Customizing the deployed BootStrap
  2. How to switch to a different CSS Framework
  3. The new Scoped CSS functionality

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 available at Blazor.CSS on GitHub.

Getting Started

  1. Create a new Blazor Application using Net5. I've used Server in the supplied code, but there's no difference between Server and WASM.
  2. Run the site to make sure it works.
  3. Install the Web Compiler extension. Extensions > Manage Extensions. You'll need to restart Visual Studio to complete the installation.

Set Up SASS

  1. Add folder SASS to the project.
  2. Move wwwroot/css/site.css file to SASS and rename it custom.scss.
  3. Add bootstrap-site.scss to the folder.
  4. Right mouse click on the file > Web Compiler > Compile File.
  5. This will add a compilerconfig.json file to the project. This is the configuration file for Web Compiler.

    compilerconfig.json will look like this:

    JSON
    [
      {
        "outputFile": "SASS/bootstrap-site.css",
        "inputFile": "SASS/bootstrap-site.scss"
      }
    ]
  6. Change this to output the compiled file into the web site:
    JSON
    [
      {
        "outputFile": "wwwroot/css/site.css",
        "inputFile": "SASS/bootstrap-site.scss"
      }
    ]
  7. Save and a new site.css should appear in wwwroot/css. There'll be nothing in it because the source file is empty.

Set up Bootstrap

  1. Add a Bootstrap folder to SASS.
  2. Add a custom folder to Bootstrap.
  3. 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:

    SASS folder

  4. Edit SASS/bootstrap-site.scss.
    CSS
    /* 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";
  5. 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.

  6. Edit _Host.cshtml, and remove the reference to bootstrap.min.css - all the CSS is now compiled into site.css.
    HTML
    <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>
  7. Run the project. Everything should be the same as before.

Customize Bootstrap

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.

CSS
// 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. It demonstrates the sort of changes you can make - we don't actually use it on the site.

CSS
/* 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;
}

Build the Customized Bootstrap

To build the custom version of Bootstrap, we need to add the new SASS files into the compile process.

Edit SASS/bootstrap-site.scss:

CSS
/* 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.

HTML
<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:

custom buttons

Changing CSS Frameworks

Not everyone wants to use Bootstrap - some people like being a little different! In this section, we'll change over to Spectre.

  1. Download the Spectre code from Github.
  2. Create a Spectre directory in SASS.
  3. Create a scss directory in Spectre.
  4. Copy the contents of spectre.src into SASS/Spectre/scss.
  5. Create site-spectre.scss in SASS and add the following code:
    SCSS
    @import "Spectre/scss/spectre";
  6. Edit compilerconfig.json:
    JSON
    [
      {
        "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 and spectre-icons.css in wwwroot/css.

Customization

Add folder custom to SASS/Spectre.

Add _variables.scss:

CSS
// Control colors
$brand-color: #7952b3 !default;
$exit-color: #66758c !default;
$save-color: #32b643 !default;
$delete-color: #e85600 !default;

Add _buttons.scss:

CSS
// 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:

CSS
@import "Spectre/custom/_variables";
@import "Spectre/scss/spectre";
@import "Spectre/custom/_buttons";

Finally, change _Host.cshtml over to the new css:

HTML
<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.

spectre buttons

Scoped CSS - Component Styling

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.

component css

To explore how this works, let's re-style the FetchData data table.

  1. Add a new Razor component DataGrid.razor to Shared, and add the following code - it's a modified version of the existing fetchdata code.
    HTML
    @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;
    }
  2. Add DataGrid.razor.css to Shared. It should associate with DataGrid.razor. The component specific css goes here. We're adding some css to format the max column and handle text overflow gracefully.
    CSS
    .max-column {
        width:50%;
    }
    
    .grid-overflow {
        display: flex;
    }
    
    .grid-overflowinner {
        flex: 1;
        width: 1px;
        overflow-x: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
    }
  3. Modify Fetchdata to use the new component.
    HTML
    @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);
        }
    }
  4. Run the site and Fetchdata should look like this:

    fatchdata

    Note the max column operating with the ellipsis shortening as the page is narrowed.

What's Going On

Open Developer Tools in the browser and take a look at the HTML.

Elements

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.

sources

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.

obj-view

Wrap Up

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.

History

  • 16th December, 2020: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Retired Cold Elm
United Kingdom United Kingdom
Ex Geologist, Project Manager, Web Hoster, Business Owner and IT Consultant. Now, a traveller to places less travelled. And part time developer trying to keep up!

If you feel like saying thanks, the next time you see a charity request, DONATE. No matter how small, no matter who, it'll count. If you have a choice in the UK, Barnados.

Comments and Discussions

 
QuestionLibSass is deprecated Pin
Neetflash9-Jul-21 7:28
Neetflash9-Jul-21 7:28 
QuestionIncompatible units: 'rem' and 'em'. Pin
Member 47731146-Jun-21 11:18
Member 47731146-Jun-21 11:18 
AnswerRe: Incompatible units: 'rem' and 'em'. Pin
Member 47731146-Jun-21 11:19
Member 47731146-Jun-21 11:19 
GeneralRe: Incompatible units: 'rem' and 'em'. Pin
Shaun C Curtis6-Jun-21 12:36
mvaShaun C Curtis6-Jun-21 12:36 
GeneralRe: Incompatible units: 'rem' and 'em'. Pin
Member 47731146-Jun-21 12:59
Member 47731146-Jun-21 12:59 
GeneralRe: Incompatible units: 'rem' and 'em'. Pin
Shaun C Curtis6-Jun-21 23:19
mvaShaun C Curtis6-Jun-21 23:19 
NP. I was just confused by your post.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.