From d4f04cbf1ea0814877c24475e025d3f1bc47490d Mon Sep 17 00:00:00 2001 From: catborise Date: Mon, 12 Oct 2026 08:20:48 +0300 Subject: [PATCH] build(deps): Bootstrap and Bootswatch 5.3.8 Bootstrap SCSS, the JS bundle and the Bootswatch themes go from 5.3.3 to 5.3.8 (bug fixes only), and wvc-main.min.css is recompiled from zephyr. Bootswatch now gives table text the body color, and Lux drops card and modal header borders. Tests compile every theme with the pinned libsass and check that the shipped CSS is zephyr from these sources. The update and rollback steps put back the two files a theme change rewrites, or git refuses to pull over them. --- README.md | 3 + appsettings/test_themes.py | 33 +++++ dev/scss/bootstrap-overrides.scss | 4 +- dev/scss/bootstrap/_accordion.scss | 13 +- dev/scss/bootstrap/_button-group.scss | 11 +- dev/scss/bootstrap/_card.scss | 19 ++- dev/scss/bootstrap/_carousel.scss | 40 +++--- dev/scss/bootstrap/_close.scss | 15 ++- dev/scss/bootstrap/_functions.scss | 4 +- dev/scss/bootstrap/_list-group.scss | 52 ++++---- dev/scss/bootstrap/_modal.scss | 8 +- dev/scss/bootstrap/_nav.scss | 2 +- dev/scss/bootstrap/_navbar.scss | 2 +- dev/scss/bootstrap/_offcanvas.scss | 6 +- dev/scss/bootstrap/_pagination.scss | 2 +- dev/scss/bootstrap/_progress.scss | 2 +- dev/scss/bootstrap/_reboot.scss | 8 +- dev/scss/bootstrap/_spinners.scss | 1 + dev/scss/bootstrap/_type.scss | 2 +- dev/scss/bootstrap/_variables-dark.scss | 19 ++- dev/scss/bootstrap/_variables.scss | 30 +++-- .../bootstrap/forms/_floating-labels.scss | 34 ++--- dev/scss/bootstrap/forms/_input-group.scss | 2 +- dev/scss/bootstrap/mixins/_banner.scss | 4 +- dev/scss/bootstrap/mixins/_box-shadow.scss | 16 ++- dev/scss/bootstrap/mixins/_grid.scss | 2 +- .../bootstrap/mixins/_visually-hidden.scss | 7 +- dev/scss/bootswatch/cerulean/_bootswatch.scss | 14 +- dev/scss/bootswatch/cerulean/_variables.scss | 6 +- dev/scss/bootswatch/cosmo/_bootswatch.scss | 20 ++- dev/scss/bootswatch/cosmo/_variables.scss | 6 +- dev/scss/bootswatch/cyborg/_bootswatch.scss | 2 +- dev/scss/bootswatch/cyborg/_variables.scss | 9 +- dev/scss/bootswatch/darkly/_bootswatch.scss | 19 +-- dev/scss/bootswatch/darkly/_variables.scss | 10 +- dev/scss/bootswatch/flatly/_bootswatch.scss | 2 +- dev/scss/bootswatch/flatly/_variables.scss | 3 +- dev/scss/bootswatch/journal/_bootswatch.scss | 14 +- dev/scss/bootswatch/journal/_variables.scss | 6 +- dev/scss/bootswatch/litera/_bootswatch.scss | 2 +- dev/scss/bootswatch/litera/_variables.scss | 4 +- dev/scss/bootswatch/lumen/_bootswatch.scss | 9 +- dev/scss/bootswatch/lumen/_variables.scss | 4 +- dev/scss/bootswatch/lux/_bootswatch.scss | 122 ++++++++++++++---- dev/scss/bootswatch/lux/_variables.scss | 25 ++-- dev/scss/bootswatch/materia/_bootswatch.scss | 14 +- dev/scss/bootswatch/materia/_variables.scss | 14 +- dev/scss/bootswatch/minty/_bootswatch.scss | 8 +- dev/scss/bootswatch/minty/_variables.scss | 3 +- dev/scss/bootswatch/morph/_bootswatch.scss | 14 +- dev/scss/bootswatch/morph/_variables.scss | 6 +- dev/scss/bootswatch/pulse/_bootswatch.scss | 12 +- dev/scss/bootswatch/pulse/_variables.scss | 5 +- dev/scss/bootswatch/quartz/_bootswatch.scss | 2 +- dev/scss/bootswatch/quartz/_variables.scss | 6 +- .../bootswatch/sandstone/_bootswatch.scss | 12 +- dev/scss/bootswatch/sandstone/_variables.scss | 6 +- dev/scss/bootswatch/simplex/_bootswatch.scss | 14 +- dev/scss/bootswatch/simplex/_variables.scss | 6 +- dev/scss/bootswatch/sketchy/_bootswatch.scss | 72 ++++++++++- dev/scss/bootswatch/sketchy/_variables.scss | 6 +- dev/scss/bootswatch/slate/_bootswatch.scss | 3 +- dev/scss/bootswatch/slate/_variables.scss | 6 +- dev/scss/bootswatch/solar/_bootswatch.scss | 2 +- dev/scss/bootswatch/solar/_variables.scss | 6 +- dev/scss/bootswatch/spacelab/_bootswatch.scss | 14 +- dev/scss/bootswatch/spacelab/_variables.scss | 6 +- .../bootswatch/superhero/_bootswatch.scss | 6 +- dev/scss/bootswatch/superhero/_variables.scss | 6 +- dev/scss/bootswatch/united/_bootswatch.scss | 14 +- dev/scss/bootswatch/united/_variables.scss | 4 +- dev/scss/bootswatch/vapor/_bootswatch.scss | 10 +- dev/scss/bootswatch/vapor/_variables.scss | 8 +- dev/scss/bootswatch/yeti/_bootswatch.scss | 7 +- dev/scss/bootswatch/yeti/_variables.scss | 3 +- dev/scss/bootswatch/zephyr/_bootswatch.scss | 12 +- dev/scss/bootswatch/zephyr/_variables.scss | 4 +- static/css/wvc-main.min.css | 10 +- static/js/bootstrap.bundle.min.js | 6 +- static/js/bootstrap.bundle.min.js.map | 2 +- 80 files changed, 701 insertions(+), 246 deletions(-) create mode 100644 appsettings/test_themes.py diff --git a/README.md b/README.md index 67fcbad4..ccd7e615 100644 --- a/README.md +++ b/README.md @@ -444,6 +444,7 @@ docker compose up -d venv/bin/python3 -c "import sqlite3,sys; sqlite3.connect('db.sqlite3').backup(sqlite3.connect(sys.argv[1]))" "$b/db.sqlite3" && cp -p webvirtcloud/settings.py /etc/nginx/conf.d/webvirtcloud.conf "$b"/ && git rev-parse HEAD > "$b/commit" && + git checkout -- static/css/wvc-main.min.css dev/scss/wvc-main.scss && git checkout master && git pull ``` @@ -507,6 +508,7 @@ b=/root/webvirtcloud-backup-YYYYMMDD-HHMMSS supervisorctl stop webvirtcloud novncd cd /srv/webvirtcloud && ! supervisorctl status webvirtcloud novncd | grep RUNNING && +git checkout -- static/css/wvc-main.min.css dev/scss/wvc-main.scss && git checkout "$(cat "$b/commit")" && venv/bin/python3 -c "import os,sqlite3,sys; [os.remove(p) for p in ('db.sqlite3-journal','db.sqlite3-wal','db.sqlite3-shm') if os.path.exists(p)]; sqlite3.connect(sys.argv[1]).backup(sqlite3.connect('db.sqlite3'))" "$b/db.sqlite3" && cp "$b/settings.py" webvirtcloud/settings.py && @@ -552,6 +554,7 @@ A rollback leaves the clone on a detached HEAD at the restored commit; the updat - A running VM without a guest agent shows the IPv4 address the host learned by ARP (the lookup always failed before). - After a live migration the VM's NVRAM file is deleted on the source, when it is in libvirt's `/var/lib/libvirt/qemu/nvram` and the destination does not share that directory (a VM created there later with the same name would start with those UEFI variables). - gstfsd finds the guest's operating system the way libguestfs inspects it, so a root on LVM works, and it always answers (an error when it finds no or several operating systems). Update `/usr/local/bin/gstfsd` on every compute (see [Adding a Compute Node](#adding-a-compute-node), step 3). +- A theme picked on the Settings page rewrites two tracked files; the update and rollback steps put back the shipped theme. Pick yours again afterwards, also after Docker recreates the container. - Docker no longer publishes port 6080; the console goes through nginx on the panel's port (`WS_PUBLIC_PORT`, default 80). ## Usage diff --git a/appsettings/test_themes.py b/appsettings/test_themes.py new file mode 100644 index 00000000..40066979 --- /dev/null +++ b/appsettings/test_themes.py @@ -0,0 +1,33 @@ +"""The themes in dev/scss/wvc-themes compile with the pinned libsass, and the +shipped wvc-main.min.css is the default theme compiled from these sources.""" + +from pathlib import Path + +import sass +from django.conf import settings +from django.test import SimpleTestCase + +SCSS = Path(settings.BASE_DIR, "dev", "scss") + + +def compile_theme(theme): + # the imports the settings view compiles + return sass.compile( + string=f"@import '{SCSS}/wvc-themes/{theme}/variables';\n" + f"@import '{SCSS}/bootstrap-overrides.scss';\n" + f"@import '{SCSS}/wvc-themes/{theme}/bootswatch';", + output_style="compressed", + ) + + +class ThemeTests(SimpleTestCase): + def test_every_theme_compiles(self): + themes = sorted(p.name for p in (SCSS / "wvc-themes").iterdir() if p.is_dir()) + self.assertIn("zephyr", themes) + for theme in themes: + with self.subTest(theme=theme): + self.assertIn(".btn", compile_theme(theme)) + + def test_the_shipped_css_is_zephyr_from_these_sources(self): + shipped = Path(settings.BASE_DIR, "static", "css", "wvc-main.min.css").read_text() + self.assertEqual(shipped, compile_theme("zephyr")) diff --git a/dev/scss/bootstrap-overrides.scss b/dev/scss/bootstrap-overrides.scss index 94e6a0db..ba4a8243 100644 --- a/dev/scss/bootstrap-overrides.scss +++ b/dev/scss/bootstrap-overrides.scss @@ -1,6 +1,6 @@ /*! - * Bootstrap v5.3.3 (https://getbootstrap.com/) - * Copyright 2011-2024 The Bootstrap Authors + * Bootstrap v5.3.8 (https://getbootstrap.com/) + * Copyright 2011-2025 The Bootstrap Authors * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) */ diff --git a/dev/scss/bootstrap/_accordion.scss b/dev/scss/bootstrap/_accordion.scss index 17e5436e..e9f267fb 100755 --- a/dev/scss/bootstrap/_accordion.scss +++ b/dev/scss/bootstrap/_accordion.scss @@ -134,17 +134,12 @@ &:last-child { border-bottom: 0; } // stylelint-disable selector-max-class - > .accordion-header .accordion-button { - &, - &.collapsed { - @include border-radius(0); - } - } - // stylelint-enable selector-max-class - - > .accordion-collapse { + > .accordion-collapse, + > .accordion-header .accordion-button, + > .accordion-header .accordion-button.collapsed { @include border-radius(0); } + // stylelint-enable selector-max-class } } diff --git a/dev/scss/bootstrap/_button-group.scss b/dev/scss/bootstrap/_button-group.scss index 55ae3f65..78e12522 100755 --- a/dev/scss/bootstrap/_button-group.scss +++ b/dev/scss/bootstrap/_button-group.scss @@ -39,7 +39,7 @@ // Prevent double borders when buttons are next to each other > :not(.btn-check:first-child) + .btn, > .btn-group:not(:first-child) { - margin-left: calc(#{$btn-border-width} * -1); // stylelint-disable-line function-disallowed-list + margin-left: calc(-1 * #{$btn-border-width}); // stylelint-disable-line function-disallowed-list } // Reset rounded corners @@ -126,7 +126,7 @@ > .btn:not(:first-child), > .btn-group:not(:first-child) { - margin-top: calc(#{$btn-border-width} * -1); // stylelint-disable-line function-disallowed-list + margin-top: calc(-1 * #{$btn-border-width}); // stylelint-disable-line function-disallowed-list } // Reset rounded corners @@ -135,7 +135,12 @@ @include border-bottom-radius(0); } - > .btn ~ .btn, + // The top radius should be 0 if the button is: + // - the "third or more" child + // - the second child and the previous element isn't `.btn-check` (making it the first child visually) + // - part of a btn-group which isn't the first child + > .btn:nth-child(n + 3), + > :not(.btn-check) + .btn, > .btn-group:not(:first-child) > .btn { @include border-top-radius(0); } diff --git a/dev/scss/bootstrap/_card.scss b/dev/scss/bootstrap/_card.scss index d3535a98..dcebe6ac 100755 --- a/dev/scss/bootstrap/_card.scss +++ b/dev/scss/bootstrap/_card.scss @@ -193,8 +193,7 @@ // The child selector allows nested `.card` within `.card-group` // to display properly. > .card { - // Flexbugs #4: https://github.com/philipwalton/flexbugs#flexbug-4 - flex: 1 0 0%; + flex: 1 0 0; margin-bottom: 0; + .card { @@ -207,13 +206,13 @@ &:not(:last-child) { @include border-end-radius(0); - .card-img-top, - .card-header { + > .card-img-top, + > .card-header { // stylelint-disable-next-line property-disallowed-list border-top-right-radius: 0; } - .card-img-bottom, - .card-footer { + > .card-img-bottom, + > .card-footer { // stylelint-disable-next-line property-disallowed-list border-bottom-right-radius: 0; } @@ -222,13 +221,13 @@ &:not(:first-child) { @include border-start-radius(0); - .card-img-top, - .card-header { + > .card-img-top, + > .card-header { // stylelint-disable-next-line property-disallowed-list border-top-left-radius: 0; } - .card-img-bottom, - .card-footer { + > .card-img-bottom, + > .card-footer { // stylelint-disable-next-line property-disallowed-list border-bottom-left-radius: 0; } diff --git a/dev/scss/bootstrap/_carousel.scss b/dev/scss/bootstrap/_carousel.scss index 3a135220..5ebf6b15 100755 --- a/dev/scss/bootstrap/_carousel.scss +++ b/dev/scss/bootstrap/_carousel.scss @@ -99,6 +99,7 @@ color: $carousel-control-color; text-align: center; background: none; + filter: var(--#{$prefix}carousel-control-icon-filter); border: 0; opacity: $carousel-control-opacity; @include transition($carousel-control-transition); @@ -168,7 +169,7 @@ margin-left: $carousel-indicator-spacer; text-indent: -999px; cursor: pointer; - background-color: $carousel-indicator-active-bg; + background-color: var(--#{$prefix}carousel-indicator-active-bg); background-clip: padding-box; border: 0; // Use transparent borders to increase the hit area by 10px on top and bottom. @@ -195,42 +196,31 @@ left: (100% - $carousel-caption-width) * .5; padding-top: $carousel-caption-padding-y; padding-bottom: $carousel-caption-padding-y; - color: $carousel-caption-color; + color: var(--#{$prefix}carousel-caption-color); text-align: center; } // Dark mode carousel @mixin carousel-dark() { - .carousel-control-prev-icon, - .carousel-control-next-icon { - filter: $carousel-dark-control-icon-filter; - } - - .carousel-indicators [data-bs-target] { - background-color: $carousel-dark-indicator-active-bg; - } - - .carousel-caption { - color: $carousel-dark-caption-color; - } + --#{$prefix}carousel-indicator-active-bg: #{$carousel-indicator-active-bg-dark}; + --#{$prefix}carousel-caption-color: #{$carousel-caption-color-dark}; + --#{$prefix}carousel-control-icon-filter: #{$carousel-control-icon-filter-dark}; } .carousel-dark { @include carousel-dark(); } +:root, +[data-bs-theme="light"] { + --#{$prefix}carousel-indicator-active-bg: #{$carousel-indicator-active-bg}; + --#{$prefix}carousel-caption-color: #{$carousel-caption-color}; + --#{$prefix}carousel-control-icon-filter: #{$carousel-control-icon-filter}; +} + @if $enable-dark-mode { - @include color-mode(dark) { - @if $color-mode-type == "media-query" { - .carousel { - @include carousel-dark(); - } - } @else { - .carousel, - &.carousel { - @include carousel-dark(); - } - } + @include color-mode(dark, true) { + @include carousel-dark(); } } diff --git a/dev/scss/bootstrap/_close.scss b/dev/scss/bootstrap/_close.scss index 4d6e73c1..d53c96fb 100755 --- a/dev/scss/bootstrap/_close.scss +++ b/dev/scss/bootstrap/_close.scss @@ -12,7 +12,6 @@ --#{$prefix}btn-close-focus-shadow: #{$btn-close-focus-shadow}; --#{$prefix}btn-close-focus-opacity: #{$btn-close-focus-opacity}; --#{$prefix}btn-close-disabled-opacity: #{$btn-close-disabled-opacity}; - --#{$prefix}btn-close-white-filter: #{$btn-close-white-filter}; // scss-docs-end close-css-vars box-sizing: content-box; @@ -21,6 +20,7 @@ padding: $btn-close-padding-y $btn-close-padding-x; color: var(--#{$prefix}btn-close-color); background: transparent var(--#{$prefix}btn-close-bg) center / $btn-close-width auto no-repeat; // include transparent for button elements + filter: var(--#{$prefix}btn-close-filter); border: 0; // for button elements @include border-radius(); opacity: var(--#{$prefix}btn-close-opacity); @@ -47,17 +47,20 @@ } @mixin btn-close-white() { - filter: var(--#{$prefix}btn-close-white-filter); + --#{$prefix}btn-close-filter: #{$btn-close-filter-dark}; } .btn-close-white { @include btn-close-white(); } +:root, +[data-bs-theme="light"] { + --#{$prefix}btn-close-filter: #{$btn-close-filter}; +} + @if $enable-dark-mode { - @include color-mode(dark) { - .btn-close { - @include btn-close-white(); - } + @include color-mode(dark, true) { + @include btn-close-white(); } } diff --git a/dev/scss/bootstrap/_functions.scss b/dev/scss/bootstrap/_functions.scss index 90296586..59d431a1 100755 --- a/dev/scss/bootstrap/_functions.scss +++ b/dev/scss/bootstrap/_functions.scss @@ -157,7 +157,7 @@ $_luminance-list: .0008 .001 .0011 .0013 .0015 .0017 .002 .0022 .0025 .0027 .003 @each $color in $foregrounds { $contrast-ratio: contrast-ratio($background, $color); - @if $contrast-ratio > $min-contrast-ratio { + @if $contrast-ratio >= $min-contrast-ratio { @return $color; } @else if $contrast-ratio > $max-ratio { $max-ratio: $contrast-ratio; @@ -177,7 +177,7 @@ $_luminance-list: .0008 .001 .0011 .0013 .0015 .0017 .002 .0022 .0025 .0027 .003 @return if($l1 > $l2, divide($l1 + .05, $l2 + .05), divide($l2 + .05, $l1 + .05)); } -// Return WCAG2.1 relative luminance +// Return WCAG2.2 relative luminance // See https://www.w3.org/TR/WCAG/#dfn-relative-luminance // See https://www.w3.org/TR/WCAG/#dfn-contrast-ratio @function luminance($color) { diff --git a/dev/scss/bootstrap/_list-group.scss b/dev/scss/bootstrap/_list-group.scss index 455531ee..3bdff679 100755 --- a/dev/scss/bootstrap/_list-group.scss +++ b/dev/scss/bootstrap/_list-group.scss @@ -43,31 +43,6 @@ } } -// Interactive list items -// -// Use anchor or button elements instead of `li`s or `div`s to create interactive -// list items. Includes an extra `.active` modifier class for selected items. - -.list-group-item-action { - width: 100%; // For `