- The VM list's power forms (style="display:inline") are d-inline.
- The fields scripts show and hide are d-none, and the scripts add and
remove that class instead of calling jQuery's show() and hide(),
which could not undo it: the custom memory boxes of the Resize tab,
the ISO upload's error message and the clone dialog's metadata row.
- The please-wait dialog's full bar is w-100.
- The create-VM pages' own 56rem width (.create-vm) goes: pages stop
at 960px now, about the same.
- static/css/font-awesome.min.css goes: no page loaded it, no element
used its classes and its fonts were never in the repository.
static/css/signin.css (308 lines) gave the sign-in and two-factor
sign-in pages their own components: a card, input groups, a submit
button, a footer and a status dot, with gradients, a blurred card,
entry and pulse animations, and rules that undid base.html's layout
(its container, its padding) from a "signin-page" class the pages'
scripts put on the body.
The pages are Bootstrap now: a card in the grid's centre column, the
logo a rounded-3 bg-primary block, the fields Bootstrap input-groups
with the password toggle an outline button, the submit a w-100
btn-primary, the footer a border-top line with a rounded-circle dot.
They follow the selected Bootswatch theme like every other page.
signin.css, its body class and its decoration go; so does the footer's
"v2.0.0", a version the panel never had.
The sign-out page (logout.html) was never shown: LOGOUT_REDIRECT_URL
sends a signed-out user to the sign-in page. It goes, with the view's
template name.
Three upstream rules in webvirtcloud.css set every page's layout
against Bootstrap 5.3:
- .container { max-width: 900px } held each page to 900px at any
screen width, below Bootstrap's own widths. Pages now take
Bootstrap's container widths up to its lg one, 960px, and stop
there on wider screens: its xl and xxl widths (1140px, 1320px) leave
the panel's forms and lists too wide;
- .page-header (margins of 5px 5px 20px 20px) put headings 20px right
of the content under them; its 14 uses are mb-3, flush with the
content (one carried Bootstrap 4's mr-auto, now me-auto);
- p { margin: 10px } indented every paragraph 10px from the content
beside it; paragraphs take Bootstrap's own bottom margin.
webvirtcloud.css keeps only what Bootstrap has no utility for: the
page width cap, the XML editor's place and height, the Tom Select
colour fix, the sortable table headers' arrows, the grouped VM list's
collapsed hosts and the charts' height.
webvirtcloud.css still carried the upstream panel's rules for what
Bootstrap 5.3 has as utilities and components:
- the page's bottom space (body padding) is pb-5 on base.html's body,
and the console page no longer has to undo it;
- the VM page's action tabs (.action-button, .action-block with a
fixed 65px width) are text-center with a d-block icon; its tab panes
(#power ... #undefine, one rule each) mt-3;
- the bordered sub-tab panes (.tab-pane-bordered, a fixed #eee border
that dark themes could not change) border border-top-0 p-3;
- the search box and header buttons (.search, .btn-header) m-1 and
mt-1; the SSH key select (.keyselect) a form-select;
- the boot order list (.multipleselect, fixed size, #c0c0c0 border) a
list-group of form-check-input items; its unused .multipleselect-on
goes;
- table cells centred by a global rule are align-middle on each table.
Two rules against Bootstrap's behaviour go: menus opened on hover on
wide screens (Bootstrap opens them on a click, and keyboard and touch
users had no hover), and "a { outline: 0 }", which hid the keyboard
focus ring.
The paragraph and page-header margins and the 900px container stay
for a separate change: they set every page's layout.
The fork's own rules in webvirtcloud.css redid what Bootstrap 5.3 has:
- the capacity bars (.cap-*) are Bootstrap progress bars: the memory a
.progress-stacked of running VMs' memory and shut-off VMs' (at half
opacity), each segment a progressbar with its amount as its value
text; the pool bars a thin .progress. The line where the host's
memory ends goes: the share above the bar says it, red when over;
- the status dot (.compute-dot) is a rounded-circle, the big figures
(.compute-ratio) fs-4, the Computes table's cell padding py-3;
- the steps of creating a VM (.create-steps) are a breadcrumb with "›"
as its divider;
- the XML and Advanced disclosures (.disclosure with a turning chevron)
are Bootstrap accordions;
- the chosen volumes and networks (.row-list) are list-unstyled, their
names text-break;
- a chart's container is position-relative by utility; its height stays.
Dead rules go too: .netIN, .netOUT, .diskRD, .diskWR, .input-xs and
.bottom-bar-margin had no users.
Each host is a row of what it has committed, read live from the host (in
parallel, by a 10 s deadline):
- Host: a status dot, the name, its address and details; when it cannot
be reached, why.
- vCPUs: the overcommit ratio, large (warning above 2:1, red above
4:1), over "N vCPUs on M CPUs" and the running VMs' share.
- Memory: memory given to all VMs against the host's memory, as one bar:
running VMs dark, shut-off VMs light (they take their memory again
when they start), and a line where the host's memory ends; red when
the VMs were given more than the host has.
- Storage: the default pool (or the first) with its used share and free
space; the others, a host may have dozens, open on a click.
A host that cannot be reached, or does not answer in time, says so in
one line instead. vCPUs and Memory sort by ratio and share. A host whose
read hangs is not read again until that read ends, so a stuck host keeps
at most one thread. The table's hidden status texts stay inside it, so
the page does not widen on a phone.
sortable.js 0.8.0 (abandoned since 2014) sorted only a table's first
body: in the grouped VM list the first host's row moved among its VMs
and the other hosts never sorted, and memory sorted as text ("1024 MB"
before "512 MB"). tablesort 5.7.1 sorts each table body on its own and
keeps a row marked data-sort-method="none" in place, so hosts stay in
name order with their row on top and their VMs sort below it. Memory,
vCPUs and volume sizes sort by their numbers (data-sort). Headers are
reachable by keyboard and carry aria-sort.
base.html loads tablesort for every table marked data-sortable; the
per-page sortable includes and sortable-theme-bootstrap.css go, so these
tables take the theme's table style like the others. The grouped list's
row numbers go (meaningless once sorted). The VM owners and VM logs
tables were marked sortable but never loaded the script; they stay
unsorted.
The list gets a state selector beside the search box. The search now
matches plain text in any case (a '(' no longer breaks it), and in the
grouped list a host's name shows all of its VMs while a host row shows
only while one of its VMs does. The filter moves to
static/js/instance_filter.js with a Node test; the other lists keep
filter-table.js. Collapsing a host now sets a class on its table body
instead of each row's display, so a collapsed host and the filter no
longer undo each other.
The create page had three tabs (flavor, custom, template), each with its
own copy of the same create form; every flavor rendered one more copy in
a dialog, and fixes had to be made in each copy. All of them posted the
same form to the same job, differing only in where the disk comes from.
The page is now one form in sections: name and size, disk, networks and
an Advanced section. The disk section offers three sources as radio
cards (a new disk, existing volumes, a copy of a template); only the
chosen source's fields are shown, and the others are disabled, so they
are neither sent nor validated. A template's copy goes into the
template's pool unless another is picked. Flavors are presets that fill
vCPU, memory and disk size and pick a new disk; they are added and
deleted in a dialog outside the form. Every source uses the
per-interface network rows, so a new disk no longer requires a MAC:
libvirt gives an interface without one its address, as it did for the
other sources. A refused form comes back with its source, volumes,
template and networks, and the focus on its first error. Without a
storage pool or a network the page shows why instead of the form, and a
host that cannot be read is a message, not a 500.
The host dialog and the machine page share the form's look: hosts are
rows with their CPU and memory use, the machine page is a card with
Cancel/Next and the XML way is a collapsible section. All three steps
show the same step indicator, done steps linking back.
The create job refuses more than one disk source (the page sends one; a
crafted request could send two, and the job would have used the first).
A field with help text is described by its help and its error.
Frontend review fixes: the presets and the disclosure buttons show a
focus ring for keyboard users; a disclosure spans its card and its
chevron turns while open; a preset says it picks a new disk; numeric
fields bring a numeric keyboard and MAC fields no autocorrection; the
machine page's Next waits for a machine type. Tom Select's control
keeps the text colour of the select it replaces: its own fixed colour
made typed text invisible on dark Bootswatch themes, on every page that
uses it.
Column offsets without a breakpoint applied below it, where the column
is already full width, so the change password, compute, user, VM
create and VM settings forms ran past the right edge on a phone. Each
offset now starts at its column's breakpoint. The interface and network
detail lists were rows directly inside rows and sit in a column now;
the compute form loses a Bootstrap 3 thumbnail padding that broke its
gutters, and the add-rule dialog's hint is no longer cut off. The page
test checks both grid rules on every page.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Rendered through every URL on libvirt's test driver, the pages had
duplicate IDs and markup browsers repaired silently:
- Each flavor modal's "Advanced" button toggled #flav-add-collapse, the
first modal's section; every modal now has its own. The flavor and
template copies of the custom tab's control IDs, and the template
tab's unused images input, are gone.
- The VM page's action icons and the XML editors were styled by
repeated IDs; they use classes.
- Modal forms opened inside modal-body and closed after modal-footer;
they wrap both. Stray end tags and duplicate attributes are removed,
paragraphs no longer hold modals or rows, table heads have a row, and
the create page closes its row and column.
- html lang is the active language instead of "en".
A test walks every URL as a superuser and fails on a repeated ID. The
console page still has no doctype: its full-height layout relies on
quirks mode and needs its own check.
A theme change compiled Bootswatch with libsass (34 MB, upstream
deprecated) into two tracked files, which updates, rollbacks and new
Docker containers then reset. The 27 themes now ship as
static/css/themes/<theme>.min.css, built by "manage.py build_themes";
the Settings page only saves the theme name and every page links that
file (zephyr when no settings are in context, as on the 500 page).
libsass moves to dev/requirements.txt; a test checks that each shipped
file is its theme compiled from dev/scss. The "Theme SASS Path" setting
is removed, and the theme default "flaty", a typo that never applied,
becomes zephyr, the theme those installs showed. The update and
rollback steps restore an older version's rewritten theme files only
where they are modified.
@xterm/xterm 6.0.0 and @xterm/addon-fit 0.11.0 replace xterm 3.6.0; the page reads typed and pasted input with onData, and no longer writes every key and all terminal output to the browser console. The unused fullscreen addon goes.
The create wizard's image and network pickers use Tom Select 2.6.2 (its
Bootstrap 5 theme and checkbox list), which does not need jQuery;
bootstrap-multiselect calls $.isArray, which jQuery 4 removed. The
selected images and networks still fill the wizard's own lists.
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.
- Replace hardcoded colors with Bootstrap & Bootswatch CSS custom properties
- Adapt background, card, borders, and inputs dynamically to active light and dark themes
- Inherit primary color, border-radius, and font-family from active theme
- Use native Bootswatch alert and button styles for consistent visual hierarchy
- Overhaul signin.css with responsive dark glassmorphic card and ambient lighting
- Add server virtualization branding badge and typography hierarchy
- Enhance input fields with leading Bootstrap icons and interactive password visibility toggle
- Modernize 2FA/OTP login and signed-out pages to match design language
- Add real-time loading feedback on form submission