Search code examples
twitter-bootstrapsassblazorblazorise

How can I apply a bootstrap style to an input with my SASS-generated bootstrap in a Blazor App?


I'm creating a Blazor Mobile app, generated using "dotnet new mobileblazorbindings", and I want an editable title on my page.

I'm using Blazorise.Bootstrap in my app, and I am generating the title with the following line in my Razor file:

<TextEdit Plaintext="true" @bind-Text="Title" Class="h3"/>

which comes out as the following HTML:

<input id="<some_guff>" type="text" class="form-control-plaintext h3" value="My title text"></input>

(It's very nice that bootstrap contains classes .h1-.h6 which match headings h1-h6), which should make this a breeze...or so I thought...)

When I run the app with the default Bootstrap CSS as imported by the mobileblazorbindings template (wwwroot\css\bootstrap\bootstrap.min.css, which appears to be Bootstrap 4.3.1), this works perfectly - the text appears to be a heading, but clicking on it allows me to edit it.

If I try customising the Bootstrap using SASS and load my generated bootstrap CSS instead, it immediately stops working, and the input appears like a normal input.

I'm guessing there's something missing in my SASS build, but I've tried all sorts of things over the last few days and I cannot find it.

Here's my package.json:

{
  "name": "myapp",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "workspaces": [
    "bootstrap"
  ],
  "dependencies": {
    "bootstrap": "next",
    "jquery": "^3.5.1",
    "open-iconic": "^1.1.1",
  },
  "devDependencies": {
    "@babel/cli": "^7.0.0",
    "@babel/core": "^7.12.10",
    "@babel/preset-env": "^7.12.11",
    "@babel/preset-react": "^7.12.10",
    "@popperjs/core": "^2.6.0",
    "autoprefixer": "^10.2.1",
    "babel-loader": "^8.2.2",
    "babel-preset-es2015-ie": "^6.7.0",
    "css-loader": "^5.0.1",
    "mini-css-extract-plugin": "^1.3.3",
    "node-gyp": "^7.1.2",
    "node-sass": "^5.0.0",
    "npm-run-all": "^4.1.5",
    "postcss": "^8.2.4",
    "postcss-cli": "^8.3.1",
    "sass-loader": "^10.1.0",
    "style-loader": "^2.0.0",
    "webpack": "^5.12.2",
    "webpack-cli": "^4.3.1"
  },
  "scripts": {
    "css-deploy": "npm run css-build && npm run css-postcss",
    "css-build": "node-sass app.scss dist/app.css",
    "css-postcss": "postcss --use autoprefixer --output dist/app.css dist/app.css",
    "css-watch": "npm run css-build -- --watch",
    "deploy": "npm run css-deploy && npm run js-build",
    "js-build": "babel _javascript --out-dir lib",
    "js-watch": "npm run js-build -- --watch",
    "start": "npm-run-all css-watch js-watch"
  }
}

Here's my bootstrap-customisation.scss:

/* Import the open-iconic icons */
@import "../node_modules/open-iconic/font/css/open-iconic-bootstrap.min.css";

/* Required */
@import "bootstrap/scss/_functions";
@import "bootstrap/scss/_variables";
@import "bootstrap/scss/_mixins";

// Modify variables here

/* Standard bootstrap imports */
@import "bootstrap/scss/_root";
@import "bootstrap/scss/_reboot";
@import "bootstrap/scss/_type";
@import "bootstrap/scss/_images";
@import "bootstrap/scss/_code";
@import "bootstrap/scss/_grid";
@import "bootstrap/scss/_tables";
@import "bootstrap/scss/_forms";
@import "bootstrap/scss/_buttons";
@import "bootstrap/scss/_transitions";
@import "bootstrap/scss/_dropdown";
@import "bootstrap/scss/_button-group";
@import "bootstrap/scss/_input-group";
@import "bootstrap/scss/_custom-forms";
@import "bootstrap/scss/_nav";
@import "bootstrap/scss/_navbar";
@import "bootstrap/scss/_card";
@import "bootstrap/scss/_breadcrumb";
@import "bootstrap/scss/_pagination";
@import "bootstrap/scss/_badge";
@import "bootstrap/scss/_jumbotron";
@import "bootstrap/scss/_alert";
@import "bootstrap/scss/_progress";
@import "bootstrap/scss/_media";
@import "bootstrap/scss/_list-group";
@import "bootstrap/scss/_close";
@import "bootstrap/scss/_toasts";
@import "bootstrap/scss/_modal";
@import "bootstrap/scss/_tooltip";
@import "bootstrap/scss/_popover";
@import "bootstrap/scss/_carousel";
@import "bootstrap/scss/_spinners";
@import "bootstrap/scss/_utilities";
@import "bootstrap/scss/_print";

And here's my root app.scss:

/* Source SASS file to build custom Bootstrap site file */
@import "bootstrap-customisation";

/* App-specific customizations */
@import "mystyles.scss";

where mystyles.scss currently contains the original contents of app.css as autogenerated.

I am building my app.css using yarn run css-deploy and then manually copying the generated dist\app.css to wwwroot\css\app.css

When I run the app now, it's almost there, but there are two things missing:

  • the icons in the navbar (from open-iconic)
  • the input is now in normal text rather than picking up the h3 class

I've been bashing my head off this brick wall for the past week, trying all sorts of different ways of building Bootstrap, but I haven't been able to find what's missing. It feels like this should be trivial, but it just doesn't work, which makes me worry about any other customisation I might do to Bootstrap.

Things I've tried:

  • Using the WebCompiler NuGet package to build the SASS (this was my first attempt; I read on the Bootstrap build instructions that it needs autoprefixer and when I enabled that I got errors because the embedded autoprefixer is 2 years old; it definitely had the same problem)
  • downloading Bootstrap manually, via libman and via npm/yarn
  • assorted versions of Bootstrap from 4.3.1 to 5.0.0-beta-1
  • Rebuilding Bootstrap in the module following the instructions in https://getbootstrap.com/docs/4.5/getting-started/build-tools/ ; when I get to the bundle install it complains there's no gemfile
  • switching to Bulma instead of Bootstrap - apart from the fact this would be quite a bit of work to switch styles of everything, it doesn't have the handy classes which replicate the headers
  • I don't have a chicken to sacrifice...

Any help or pointers very gratefully received.

Thanks in advance,

Ian


Solution

  • Okay, I've found what the problem was. Ultimately I was looking in the wrong place. It came down to a CSS class ordering problem rather than any sort of build problem.

    Remember my input control:

    <input id="<some_guff>" type="text" class="form-control-plaintext h3" value="My title text"></input>
    

    Digging into the bootstrap source code, class .form-control-plaintext is defined in _forms.scss, and .h3 is defined in _type.scss.

    Now look at my bootstrap imports:

    /* Standard bootstrap imports */
    @import "bootstrap/scss/_type";
    ...
    @import "bootstrap/scss/_forms";
    

    _type (with .h3) comes before _forms (with .form-control-plaintext), so the definitions of the .form-control-plaintext class take priority over those of .h3. Moving the _type line to the end of the list of imports solved it:

    /* Standard bootstrap imports */
    ...
    @import "bootstrap/scss/_forms";
    ...
    
    @import "bootstrap/scss/_type";
    

    Now class .h3 is defined after .form-control-plaintext so it takes priority, and all works perfectly. I have an input which looks like a header.