Files
PyRIGS/pipeline/source_assets/scss/screen.scss
Joe Banks a29384a314 Bootstrap 5 & remove jQuery (#661)
* build: move frontend assets to Bootstrap 5.3 and Tom Select

* feat: migrate templates and scripts to Bootstrap 5, dropping jQuery

Templates
- Rename Bootstrap 4 classes and data attributes (data-bs-*, ms-/me-/ps-/pe-,
  text-start/end, form-group -> mb-3, input-group wrappers removed, badges,
  custom controls -> form-check/form-select, media -> d-flex, btn-close, ...)
- Dark mode now uses Bootstrap 5.3 colour modes (data-bs-theme on <html>),
  set from <head> to avoid a flash of the light theme
- Remove the Bootstrap 4 /bootstrap/ demo page
- Contextual table classes (table-success etc.) still work as plain
  backgrounds for the rigboard key

JavaScript
- Remove jQuery: src.js/interaction.js and every inline template script are
  rewritten in plain JS on top of small helpers (helpers.js, bundled with
  Bootstrap into jpop.js so inline scripts can use them)
- Replace bootstrap-select/ajax-bootstrap-select with Tom Select. The remote
  search now sends the `q` parameter and reads `text`, which is what the
  API actually uses (the old picker sent `term` and read `label`)
- HTML fragments are fetched with an X-Requested-With header so views keep
  rendering their modal variants, and fetched scripts are re-executed
- Remove the Bootstrap 4 plugin script includes

* fix: stop rigboard header buttons stretching to full width under Bootstrap 5

* fix: restore dark-mode brand colours and contextual table colours under Bootstrap 5

* fix: brighten dark-mode links and make coloured card outlines visible

* fix: calendar layout and grid borders, navbar search button width, guard missing modal links

* fix: readable text in dark-mode Tom Select inputs and placeholders, restore form-control column widths

* build: split the Dockerfile into cached stages with prod and dev targets

* deploy: make compose.yml development-only with live reload, and document it

* fix: keep badge text colour for links inside badges

* fix: restore table-bordered grid lines in dark mode

* fix: include Bootstrap's progress styles and keep progress labels readable
2026-10-02 16:28:11 +01:00

548 lines
13 KiB
SCSS

// Bootstrap 5 is consumed via @import (the Sass module system is not yet supported by Bootstrap's own build)
@import "custom-variables";
//Required
@import "node_modules/bootstrap/scss/functions";
@import "node_modules/bootstrap/scss/variables";
@import "node_modules/bootstrap/scss/variables-dark";
@import "node_modules/bootstrap/scss/maps";
@import "node_modules/bootstrap/scss/mixins";
@import "node_modules/bootstrap/scss/utilities";
//Optional
@import "node_modules/bootstrap/scss/root";
@import "node_modules/bootstrap/scss/reboot";
@import "node_modules/bootstrap/scss/type";
@import "node_modules/bootstrap/scss/images";
@import "node_modules/bootstrap/scss/containers";
@import "node_modules/bootstrap/scss/grid";
@import "node_modules/bootstrap/scss/tables";
@import "node_modules/bootstrap/scss/forms";
@import "node_modules/bootstrap/scss/buttons";
@import "node_modules/bootstrap/scss/transitions";
@import "node_modules/bootstrap/scss/dropdown";
@import "node_modules/bootstrap/scss/button-group";
@import "node_modules/bootstrap/scss/nav";
@import "node_modules/bootstrap/scss/navbar";
@import "node_modules/bootstrap/scss/card";
@import "node_modules/bootstrap/scss/pagination";
@import "node_modules/bootstrap/scss/badge";
@import "node_modules/bootstrap/scss/alert";
@import "node_modules/bootstrap/scss/list-group";
@import "node_modules/bootstrap/scss/close";
@import "node_modules/bootstrap/scss/modal";
@import "node_modules/bootstrap/scss/tooltip";
@import "node_modules/bootstrap/scss/popover";
@import "node_modules/bootstrap/scss/progress";
@import "node_modules/bootstrap/scss/spinners";
@import "node_modules/bootstrap/scss/helpers";
@import "node_modules/bootstrap/scss/utilities/api";
//FontAwesome
$fa-font-path: '/static/fonts';
@import "node_modules/@fortawesome/fontawesome-free/scss/fontawesome";
@import "node_modules/@fortawesome/fontawesome-free/scss/solid";
html {
--brand: #3F58AA;
scrollbar-color: #3F58AA Canvas !important;
}
:root { accent-color: var(--brand); }
:focus-visible { outline-color: var(--brand); }
::selection { background-color: var(--brand); }
::marker { color: var(--brand); }
:is(
::-webkit-calendar-picker-indicator,
::-webkit-clear-button,
::-webkit-inner-spin-button,
::-webkit-outer-spin-button
) {
color: var(--brand);
}
@media screen and
(prefers-reduced-motion: reduce),
(update: slow) {
* {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
}
}
.fc-event-main {
color: black !important;
}
.fc-daygrid-dot-event {
color: black !important;
}
.fc-timeGridDay-view {
.fc-event-main {
font-size: large !important;
}
}
.fc-timeGridWeek-view {
.fc-event-main {
font-size: 0.9rem;
}
}
#content {
padding: 40px 15px;
}
#userdropdown > li {
padding: 0 0.3em;
}
#userdropdown > li, #activity {
.media-object {
max-width: 3em;
}
}
// Bootstrap 4's contextual table classes also worked as background colours on non-table elements
@each $color, $value in $table-variants {
.table-#{$color}:not(table):not(tr):not(td):not(th) {
background-color: var(--bs-table-bg);
}
}
// Bootstrap 4 columns used max-width, so `.form-control.col-sm-6` stayed narrow; Bootstrap 5's
// `.form-control { width: 100% }` now wins over the column width
@each $breakpoint, $_min in $grid-breakpoints {
@include media-breakpoint-up($breakpoint) {
$infix: breakpoint-infix($breakpoint, $grid-breakpoints);
@for $i from 1 through $grid-columns {
.form-control.col#{$infix}-#{$i},
.form-select.col#{$infix}-#{$i} {
width: calc(#{$i} / #{$grid-columns} * 100%);
}
}
}
}
// Links inside badges (e.g. "Reviewed by ...") sit on a coloured background, so keep the badge's text colour
.badge a {
color: inherit;
text-decoration: underline;
}
// Bootstrap 4 gave labels a bottom margin, Bootstrap 5 expects .form-label
label {
margin-bottom: 0.5rem;
}
.table tbody > tr > td.vert-align {
vertical-align: middle;
}
.align-right {
text-align: right;
}
textarea {
width: 100%;
resize: vertical;
}
.custom-combobox {
display: block;
}
.event-mic-photo {
max-width: 2em;
border-radius: 0.25em;
}
.item-description {
margin-left: 1em;
}
.overflow-ellipsis {
text-overflow: ellipsis;
display: inline-block;
max-width: 100%;
overflow: hidden;
}
.dont-break-out {
overflow-wrap: break-word;
word-wrap: break-word;
hyphens: auto;
}
del {
background-color: #f2dede;
border-radius: 3px;
padding: 0.1em;
}
ins {
background-color: #dff0d8;
border-radius: 3px;
padding: 0.1em;
}
.nav-link {
color: $gray-400 !important;
}
.btn-light {
--bs-btn-bg: #{$gray-200};
}
.skip-link {
background: $dark;
outline: unset;
height: 30px;
left: 50%;
padding: 8px;
position: absolute;
transform: translateY(-100%);
transition: transform 0.3s;
}
.skip-link:focus {
transform: translateY(45px); /* TODO Remove absolute positioning */
}
.errorlist > li {
list-style-type: none;
}
pre {
page-break-inside: avoid;
font-family: monospace;
font-size: 15px;
line-height: 1.6;
margin-bottom: 1.6em;
max-width: 100%;
overflow: auto;
padding: 1em 1.5em;
display: block;
word-wrap: break-word;
}
svg {
display: inline;
white-space: no-wrap;
}
span.fas {
padding-left: 0.1em !important;
padding-right: 0.3em !important;
}
html.embedded {
display: flex;
flex-direction: column;
width: 100%;
height: 100%;
max-height: 100%;
overflow: hidden;
justify-content: center;
body{
padding:0;
width:100%;
background:none;
overflow: auto;
}
.embed_container{
border:5px solid #e9e9e9;
padding:12px 0px;
min-height:100%;
width:100%;
}
.source{
background: url('/static/imgs/pyrigs-avatar.png') no-repeat;
background-size: 16px 16px;
padding-left: 20px;
color: #000;
}
h3{
margin-top:10px;
margin-bottom:5px;
}
p{
margin-bottom:2px;
font-size: 11px;
}
.event-mic-photo{
max-width: 3em;
}
}
.markdown {
h1 {
font-size: $h1-font-size * 0.75;
}
h2 {
font-size: $h2-font-size * 0.8;
}
h3 {
font-size: $h3-font-size * 0.85;
}
h4 {
font-size: $h4-font-size * 0.9;
}
h5 {
font-size: $h5-font-size * 0.95;
}
img {
max-width: 100%;
}
}
#rigboard {
.markdown {
img {
max-width: 30rem;
}
}
}
.CodeMirror, .editor-toolbar {
border-radius: 0 !important;
}
.ts-wrapper.form-select, .ts-wrapper.form-control {
padding: 0;
}
// New implementation of class dropped in Bootstrap 3
.dl-horizontal {
display: grid;
grid-template-columns: auto 1fr;
gap: 0.7rem 0;
}
.dl-horizontal > dd, .dl-horizontal .markdown > p {
margin-bottom: 0 !important;
}
// Dark mode: Bootstrap 5.3 colour modes (set via data-bs-theme on <html>), with a few project-specific tweaks
$darktheme: #121416;
// Brighter brand colours for dark mode (the light-mode ones are pastel tints on a dark background)
$dark-theme-colors: (
"success": #3AB54A,
"warning": #FFE89B,
"danger": #BF1E2E,
"info": #25AAE2,
"primary": #3A52A2,
"secondary": $secondary,
"light": $light,
"dark": $dark
);
[data-bs-theme="dark"] {
@each $name, $value in $dark-theme-colors {
--bs-#{$name}: #{$value};
--bs-#{$name}-rgb: #{to-rgb($value)};
.btn-#{$name} {
@include button-variant($value, $value);
}
.btn-outline-#{$name} {
@include button-outline-variant($value);
}
.text-bg-#{$name} {
color: color-contrast($value) !important;
background-color: RGBA(to-rgb($value), var(--bs-bg-opacity, 1)) !important;
}
// Contextual table classes (tables, rows, cells and the plain-element variant used for the rigboard key)
.table-#{$name} {
--bs-table-color: #{color-contrast(shade-color($value, 55%))};
--bs-table-bg: #{shade-color($value, 55%)};
--bs-table-border-color: #{shade-color($value, 35%)};
--bs-table-striped-bg: #{shade-color($value, 50%)};
--bs-table-hover-bg: #{shade-color($value, 45%)};
color: var(--bs-table-color);
border-color: var(--bs-table-border-color);
}
}
--bs-body-bg: #222;
--bs-body-color: #{$gray-100};
// Bootstrap derives these from $primary, which is too dim on the dark background
--bs-link-color: #6ea8fe;
--bs-link-color-rgb: #{to-rgb(#6ea8fe)};
--bs-link-hover-color: #8bb9fe;
--bs-link-hover-color-rgb: #{to-rgb(#8bb9fe)};
--bs-secondary-color: #c9c9c9;
--bs-tertiary-bg: #{$darktheme};
.navbar {
background-color: #111111 !important;
}
// Coloured card outlines (training levels): make them visible, including the "dark" and "light" ones
.card[class*="border-"] {
--bs-card-border-width: 2px;
}
.border-dark {
border-color: $gray-500 !important;
}
.dropdown-menu, .popover {
box-shadow: 0 0.5rem 1rem black;
--bs-dropdown-bg: #{$darktheme};
--bs-popover-bg: #{$darktheme};
}
.form-control:not(.btn), .form-select, .ts-control, .ts-dropdown {
background-color: $darktheme;
color: $gray-100;
}
// Tom Select hard-codes dark text colours
.ts-wrapper .ts-control,
.ts-wrapper .ts-control input,
.ts-dropdown,
.ts-dropdown .option,
.ts-dropdown .optgroup-header,
.ts-dropdown .no-results,
.ts-dropdown .create {
color: $gray-100;
}
.ts-wrapper .ts-control input::placeholder {
color: $gray-500;
}
.ts-wrapper.multi .ts-control > div {
background: $gray-700;
color: $gray-100;
border-color: $gray-600;
}
.ts-wrapper.multi .ts-control > div.active {
background: $primary;
color: #fff;
}
.ts-dropdown .active {
background-color: $gray-800;
color: $gray-100;
}
.ts-dropdown .active.create {
color: $gray-100;
}
.ts-dropdown .optgroup-header {
background: $darktheme;
}
::placeholder {
color: $gray-500;
opacity: 1;
}
.btn-light, .status {
--bs-btn-bg: #{$darktheme};
--bs-btn-color: #{$gray-100};
--bs-btn-border-color: #{$darktheme};
--bs-btn-hover-bg: #{tint-color($darktheme, 8%)};
--bs-btn-hover-color: #{$gray-100};
--bs-btn-hover-border-color: #{$darktheme};
background-color: $darktheme !important;
color: $gray-100 !important;
border-color: $darktheme;
}
.btn-link {
color: white;
}
.input-group-text {
border-color: $darktheme;
}
.page-item.disabled {
opacity: 0.5;
}
.bg-light {
color: black;
background-color: $gray-200 !important;
border-radius: 0.2em;
}
.fc-day-today {
background-color: transparent !important;
border: 0.2em solid $info !important;
}
.fc-daygrid-dot-event {
color: white !important;
}
@each $color, $value in $dark-theme-colors {
#event_row.table-#{$color} {
border: 0.3em solid shade-color($value, 60%) !important;
background-color: #222 !important;
color: white !important;
}
}
del {
color: black;
background-color: $danger;
border-radius: 3px;
}
ins {
color: black;
background-color: $success;
border-radius: 3px;
}
pre {
background: $gray-600;
color: white;
}
.source {
color: white !important;
}
.embed_container {
border-color: #3853a4 !important;
background: #222;
color: $gray-100;
}
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
textarea:-webkit-autofill,
textarea:-webkit-autofill:hover,
textarea:-webkit-autofill:focus,
select:-webkit-autofill,
select:-webkit-autofill:hover,
select:-webkit-autofill:focus {
border: 1px solid $info;
-webkit-text-fill-color: white;
-webkit-box-shadow: 0 0 0px 1000px rgba($info, .3) inset;
transition: background-color 5000s ease-in-out 0s;
}
.editor-toolbar > a {
color: white !important;
}
.editor-toolbar > a:hover {
background: transparent !important;
}
.editor-toolbar > a.active {
background: $info !important;
}
.cm-s-easymde {
color: white;
background-color: $darktheme;
border-color: #bbb;
}
.CodeMirror-cursor {
border-color: white !important;
}
}
.ts-wrapper.has-items .ts-control > input::placeholder {
color: transparent;
}
// FullCalendar (standard theme) colours, matching the site's light/dark modes
.fc {
--fc-border-color: var(--bs-border-color);
--fc-page-bg-color: var(--bs-body-bg);
--fc-neutral-bg-color: var(--bs-tertiary-bg);
--fc-today-bg-color: rgba(var(--bs-warning-rgb), 0.15);
--fc-list-event-hover-bg-color: var(--bs-tertiary-bg);
}
.fc a {
color: inherit;
text-decoration: none;
}