Schnelle und schmutzige Bootstrap-Überschreibungen zur Laufzeit

Avatar of Meredith Matthews
Meredith Matthews am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Bootstrap 5.2 wurde mit Lösungen für die in diesem Beitrag behandelten Methoden ausgeliefert. Wenn Sie also Bootstrap 5.2 oder neuer verwenden, sollten Sie in der Lage sein, CSS-Variablen direkt aus der Box für benutzerdefinierte Überschreibungen zu bearbeiten.

Oh, Bootstrap, diese alte Standard-Webbibliothek, die du entweder hasst oder für die du die ganze Zeit damit verbringst, sie zu verteidigen als „ist schon in Ordnung, ist nicht so schlimm“. Unabhängig davon, auf welcher Seite du stehst, ist es ein mächtiges UI-Framework, das überall präsent ist, die meisten Leute die Grundlagen davon kennen und dir extrem vorhersehbare Ergebnisse liefert.

Ob gut oder schlecht, Bootstrap ist *meinungsbildend*. Es möchte, dass du deinen HTML auf eine bestimmte Weise aufbaust, es möchte, dass du Stile auf eine bestimmte Weise überschreibst, es möchte auf eine bestimmte Weise aus Kern-Dateien erstellt werden und es möchte auf eine bestimmte Weise in Websites integriert werden. Meistens, es sei denn, du hast einen Kollegen, der Bootstrap schlecht schreibt, ist das in Ordnung, aber es deckt nicht alle Anwendungsfälle ab.

Bootstrap möchte serverseitig generiert werden und mag es nicht, wenn seine Stile zur Laufzeit überschrieben werden. Wenn du eine Art visuelle Themenfunktion in deiner Anwendung benötigst, ist das, was Bootstrap von dir möchte, separate Stylesheets für jedes Thema zu generieren und die Stylesheets nach Bedarf auszutauschen. Das ist ein großartiger Weg, wenn du vordefinierte Themen hast, die du den Benutzern anbietest. Aber was ist, wenn du benutzerdefinierte Themen möchtest? Du könntest deine App so einrichten, dass sie Sass ausführt und neue Stylesheets kompiliert und auf dem Server speichert, aber das ist viel Arbeit – außerdem musst du dich mit den Backend-Leuten und DevOps auseinandersetzen, was eine Menge Mühe ist, wenn du nur, sagen wir, primäre und sekundäre Farben austauschen möchtest.

Das ist also meine Situation.

Ich baue eine Multi-User-SaaS-App mit Django und Vue mit einem festen Layout, aber auch der Anforderung, die Branding-Farben für jedes Benutzerkonto mit einem automatischen Standardfarbthema ändern zu können. Es gibt eine weitere Anforderung, dass wir die App nicht jedes Mal neu bereitstellen, wenn ein neuer Benutzer hinzugefügt wird. Und schließlich sind alle Backend- und DevOps-Entwickler derzeit mit anderen Projekten überlastet, also muss ich dieses Problem selbst lösen.

Da ich Sass wirklich nicht zur Laufzeit kompilieren möchte, könnte ich einfach Stylesheets erstellen und sie auf Seiten injizieren, aber das ist eine schlechte Lösung, da wir uns auf Farben konzentrieren. Kompilierte Bootstrap-Stylesheets rendern die Farbwerte als explizite Hex-Werte, und (ich habe es gerade überprüft) es gibt 23 verschiedene Instanzen von Primärblau in meinem Stylesheet. Ich müsste jede dieser Instanzen nur für Primärfarben überschreiben, dann wieder für Sekundärfarben, Warnfarben, Gefahrenfarben und alle anderen Konventionen und Farbstandards, die wir ändern möchten. Das ist kompliziert und viel Arbeit. Das will ich nicht.

Glücklicherweise muss diese neue App den Internet Explorer 11 nicht unterstützen, was bedeutet, dass mir CSS-Variablen zur Verfügung stehen. Sie sind auch großartig und können nach dem Laden eines Stylesheets definiert werden, fließen in jede Richtung und ändern alle gewünschten Farben, richtig? Und Bootstrap generiert diese lange Liste von Variablen im :root-Element, also sollte das einfach sein.

Das ist, als ich lernte, dass Bootstrap nur einige seiner Werte als Variablen im Stylesheet rendert und dass diese Liste von Variablen ausschließlich für den Endverbraucher bestimmt ist. Die meisten Variablen in dieser Liste werden im restlichen Stylesheet nicht referenziert, daher hat die Neudefinition keine Auswirkung. (Es ist jedoch erwähnenswert, dass eine bessere Variablenunterstützung zur Laufzeit in Zukunft kommen könnte.)

Was ich möchte, ist, dass mein Bootstrap-Stylesheet mit CSS-Variablen gerendert wird, die ich serverseitig manipulieren kann, anstatt statische Farbwerte. Streng genommen ist das nicht möglich. Sass kompiliert nicht, wenn du Farbvariablen als CSS-Variablen festlegst. Es gibt ein paar clevere Tricks, um Sass dazu zu bringen ( hier ist einer, und ein weiterer), aber sie erfordern das Verzweigen von Bootstrap, und das Abweichen vom Upgrade-Pfad führt zu einer gewissen Sprödigkeit in meiner App, die ich nicht hinzufügen möchte. Und wenn ich ehrlich bin, der wirkliche Grund, warum ich diese Lösungen nicht implementiert habe, war, dass ich nicht herausfinden konnte, wie ich eine davon mit meinem Sass-Compiler zum Laufen bringe. Aber du hast vielleicht mehr Glück.

Hier möchte ich meinen bevorzugten Workflow erklären. Ich bevorzuge es, Sass lokal auf meinem Entwicklungsrechner auszuführen, um Stylesheets zu erstellen und die kompilierten Stylesheets im Repository zu committen. Best Practices würden vorschlagen, dass die Stylesheets während der Bereitstellung kompiliert werden sollten, und das ist korrekt, aber ich arbeite für ein wachsendes, ständig unterbesetztes Startup. Ich arbeite mit Sass, weil ich es mag, aber in dem, was eindeutig ein Thema für meinen Job ist, habe ich nicht die Zeit, die Macht oder die spirituelle Stärke, meinen Sass-Build in unsere verschiedenen Bereitstellungspipelines zu integrieren.

Es ist auch ein bisschen gesetzliche böse Selbstverteidigung: Ich möchte nicht, dass unsere Full-Stack-Entwickler meine fein ausgearbeiteten Stile in die Finger bekommen und anfangen, zu schreiben, was sie wollen; und ich habe festgestellt, dass sie aus irgendeinem Grund schreckliche Probleme haben, Node auf ihren Laptops zu installieren. Ach! Sie stecken fest und bitten mich, es zu tun, und genau so möchte ich es haben.

All das bedeutet: Wenn ich die Stylesheets nicht mit den Variablen darin rendern lassen kann, hält mich nichts davon ab, die Variablen *nachdem* sie kompiliert wurden, in das Stylesheet zu injizieren.

Siehe die Macht von Suchen und Ersetzen!

Was wir tun, ist, in Bootstrap zu gehen und die Farben zu finden, die wir ersetzen wollen, die sich praktischerweise am Anfang deines kompilierten Stylesheets im :root-Stil befinden.

:root {
  --bs-blue: #002E6D;
  --bs-indigo: #6610F2;
  --bs-purple: #6F42C1;
  --bs-pink: #E83E8C;
  --bs-red: #DC3545;
  --bs-orange: #F2581C;
  --bs-yellow: #FFC107;
  --bs-green: #28A745;
  --bs-teal: #0C717A;
  --bs-cyan: #007DBC;
  --bs-white: #fff;
  --bs-gray: #6c757d;
  --bs-gray-dark: #343a40;
  --bs-gray-100: #f8f9fa;
  --bs-gray-200: #e9ecef;
  --bs-gray-300: #dee2e6;
  --bs-gray-400: #ced4da;
  --bs-gray-500: #adb5bd;
  --bs-gray-600: #6c757d;
  --bs-gray-700: #495057;
  --bs-gray-800: #343a40;
  --bs-gray-900: #212529;
  --bs-primary: #002E6D;
  --bs-brand: #DC3545;
  --bs-secondary: #495057;
  --bs-success: #28A745;
  --bs-danger: #DC3545;
  --bs-warning: #FFC107;
  --bs-info: #007DBC;
  --bs-light: #fff;
  --bs-dark: #212529;
  --bs-background-color: #e9ecef;
  --bs-bg-light: #f8f9fa;
  --bs-primary-rgb: 13, 110, 253;
  --bs-secondary-rgb: 108, 117, 125;
  --bs-success-rgb: 25, 135, 84;
  --bs-info-rgb: 13, 202, 240;
  --bs-warning-rgb: 255, 193, 7;
  --bs-danger-rgb: 220, 53, 69;
  --bs-light-rgb: 248, 249, 250;
  --bs-dark-rgb: 33, 37, 41;
  --bs-white-rgb: 255, 255, 255;
  --bs-black-rgb: 0, 0, 0;
  --bs-body-rgb: 33, 37, 41;
  --bs-font-sans-serif: system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, Liberation Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
  --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
  --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
  --bs-body-font-family: Source Sans Pro;
  --bs-body-font-size: 1rem;
  --bs-body-font-weight: 400;
  --bs-body-line-height: 1.5;
  --bs-body-color: #212529;
  --bs-body-bg: #e9ecef;
}

Nimm den Wert für, sagen wir, --bs-primary, das gute alte Bootstrap-Blau. Ich verwende Gulp, um meine Stylesheets zu kompilieren, also werfen wir einen Blick auf die Sass-Task-Funktion dafür in der gulpfile.js.

var gulp = require('gulp');
var sass = require('gulp-sass')(require('sass'));
var sourcemaps = require('gulp-sourcemaps');

function sassCompile() {
  return gulp.src('static/sass/project.scss')
  .pipe(sourcemaps.init())
  .pipe(sass({outputStyle: 'expanded'}))
  .pipe(sourcemaps.write('.'))
  .pipe(gulp.dest('/static/css/'));
}
exports.sass = sassCompile;

Ich möchte diese Farbe in meinem gesamten Stylesheet kopieren und durch eine CSS-Variable ersetzen. Deshalb habe ich gulp-replace installiert. Wir möchten, dass unser Suchen-und-Ersetzen-Vorgang ganz am Ende des Prozesses stattfindet, nachdem das Stylesheet kompiliert wurde, aber bevor es gespeichert wird. Das bedeutet, wir sollten die Pipe am Ende der Sequenz platzieren, so:

var gulp = require('gulp');
var sass = require('gulp-sass')(require('sass'));
var sourcemaps = require('gulp-sourcemaps');
var gulpreplace = require('gulp-replace');
function sassCompile() {
  return gulp.src('static/sass/project.scss')
    .pipe(sourcemaps.init())
    .pipe(sass({outputStyle: 'expanded'}))
    .pipe(sourcemaps.write('.'))
    .pipe(gulpreplace(/#002E6D/ig, 'var(--ct-primary)'))
    .pipe(gulp.dest('static/css/'));
}
exports.sass = sassCompile; 

Kompiliere das Stylesheet und schau es dir an.

:root {
  --bs-blue: var(--ct-primary);
  --bs-indigo: #6610F2;
  --bs-purple: #6F42C1;
  --bs-pink: #E83E8C;
  --bs-red: #DC3545;
  --bs-orange: #F2581C;
  --bs-yellow: #FFC107;
  --bs-green: #28A745;
  --bs-teal: #0C717A;
  --bs-cyan: #007DBC;
  --bs-white: #fff;
  --bs-gray: #6c757d;
  --bs-gray-dark: #343a40;
  --bs-gray-100: #f8f9fa;
  --bs-gray-200: #e9ecef;
  --bs-gray-300: #dee2e6;
  --bs-gray-400: #ced4da;
  --bs-gray-500: #adb5bd;
  --bs-gray-600: #6c757d;
  --bs-gray-700: #495057;
  --bs-gray-800: #343a40;
  --bs-gray-900: #212529;
  --bs-primary: var(--ct-primary);
  --bs-brand: #DC3545;
  --bs-secondary: #495057;
  --bs-success: #28A745;
  --bs-danger: #DC3545;
  --bs-warning: #FFC107;
  --bs-info: #007DBC;
  --bs-light: #fff;
  --bs-dark: #212529;
  --bs-background-color: #e9ecef;
  --bs-bg-light: #f8f9fa;
  --bs-primary-rgb: 13, 110, 253;
  --bs-secondary-rgb: 108, 117, 125;
  --bs-success-rgb: 25, 135, 84;
  --bs-info-rgb: 13, 202, 240;
  --bs-warning-rgb: 255, 193, 7;
  --bs-danger-rgb: 220, 53, 69;
  --bs-light-rgb: 248, 249, 250;
  --bs-dark-rgb: 33, 37, 41;
  --bs-white-rgb: 255, 255, 255;
  --bs-black-rgb: 0, 0, 0;
  --bs-body-rgb: 33, 37, 41;
  --bs-font-sans-serif: system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, Liberation Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
  --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
  --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
  --bs-body-font-family: Source Sans Pro;
  --bs-body-font-size: 1rem;
  --bs-body-font-weight: 400;
  --bs-body-line-height: 1.5;
  --bs-body-color: #212529;
  --bs-body-bg: #e9ecef;
}

Cool, OK, wir haben jetzt ein komplettes Stylesheet, das einen Variablenwert für Blau haben möchte. Beachte, dass sowohl die Primärfarbe als auch die Farbe "Blau" geändert wurden. Das ist keine subtile Technik. Ich nenne es aus gutem Grund schnell und schmutzig, aber es ist recht einfach, eine feinere Kontrolle über deine Farb-Ersetzungen zu erhalten, wenn du sie benötigst. Wenn du zum Beispiel "Blau" und "Primär" als getrennte Werte beibehalten möchtest, gehe in dein Sass und definiere die Sass-Variablen $blue und $primary neu mit unterschiedlichen Werten, und dann kannst du sie separat suchen und ersetzen, wie es nötig ist.

Als nächstes müssen wir unseren neuen Standard-Variablenwert in der App definieren. Das ist so einfach wie dies im HTML-Head zu tun:

<link href="/static/css/project.css" rel="stylesheet">
<style>
  :root {
    --ct-primary: #002E6D;
  }
</style>

Führe das aus und alles erscheint. Alles, was blau sein soll, ist blau. Wiederhole diesen Vorgang ein paar Mal, und du hast plötzlich viel Kontrolle über die Farben in deinem Bootstrap-Stylesheet. Das sind die Variablen, die ich den Benutzern zur Verfügung stellen möchte, zusammen mit ihren Standardfarbwerten:

--ct-primary: #002E6D;
--ct-primary-hover: #00275d;
--ct-secondary: #495057;
--ct-secondary-hover: #3e444a;
--ct-success: #28A745;
--ct-success-hover: #48b461;
--ct-danger: #DC3545;
--ct-danger-hover: #bb2d3b;
--ct-warning: #FFC107;
--ct-warning-hover: #ffca2c;
--ct-info: #007DBC;
--ct-info-hover: #006aa0;
--ct-dark: #212529;
--ct-background-color: #e9ecef;
--ct-bg-light: #f8f9fa;
--bs-primary-rgb: 0, 46, 109;
--bs-secondary-rgb: 73, 80, 87;
--bs-success-rgb: 40, 167, 69;
--bs-info-rgb: 0, 125, 188;
--bs-warning-rgb: 255, 193, 7;
--bs-danger-rgb: 220, 53, 69;
--bs-light-rgb: 248, 249, 250;
--bs-dark-rgb: 33, 37, 41;
--bs-white-rgb: 255, 255, 255;
--bs-black-rgb: 0, 0, 0;
--bs-body-rgb: 33, 37, 41;

Jetzt beginnt der Spaß! Von hier aus kannst du diese Standardwerte direkt manipulieren, wenn du möchtest, oder einen zweiten :root-Stil unter den Standardwerten hinzufügen, um nur die gewünschten Farben zu überschreiben. Oder mach es wie ich und platziere ein Textfeld im Benutzerprofil, das einen :root-Stil in deinen Header ausgibt und alles überschreibt, was du brauchst. Voilà, du kannst Bootstrap jetzt zur Laufzeit überschreiben, ohne das Stylesheet neu zu kompilieren oder den Verstand zu verlieren.

Das ist sicherlich keine elegante Lösung, aber sie löst einen sehr spezifischen Anwendungsfall, den Entwickler seit Jahren zu lösen versuchen. Und bis Bootstrap beschließt, uns Variablen zur Laufzeit einfach überschreiben zu lassen, hat sich dies für mich als eine sehr effektive Lösung erwiesen.