mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-10-27 00:02:51 +08:00
454 lines
10 KiB
CSS
454 lines
10 KiB
CSS
/* SPDX-License-Identifier: MIT */
|
|
|
|
/* The page is stock LuCI markup and each theme draws .cbi-section its own way,
|
|
so sections are left alone. Theme variables come first here, with a neutral
|
|
fallback for themes without them. */
|
|
#qos-app {
|
|
--qos-bd: var(--border-color-medium, rgba(128, 128, 128, .35));
|
|
--qos-ln: var(--border-color-low, rgba(128, 128, 128, .2));
|
|
--qos-hd: var(--background-color-low, rgba(128, 128, 128, .06));
|
|
--qos-r: 4px;
|
|
--qos-gap: .75em;
|
|
--qos-pad: .45em 1em;
|
|
}
|
|
|
|
/* The service bar sits outside any section, so it gets its own box. Flex
|
|
items ignore float, so the theme's page-actions floats cannot reorder the
|
|
buttons and they stay in declared order whatever their colour class. */
|
|
#qos-svc-btns {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
justify-content: flex-end;
|
|
gap: .4em;
|
|
margin: 0 0 var(--qos-gap);
|
|
padding: var(--qos-pad);
|
|
border: 1px solid var(--qos-bd);
|
|
border-radius: var(--qos-r);
|
|
}
|
|
|
|
#qos-svc-btns::after {
|
|
content: none;
|
|
}
|
|
|
|
#qos-svc-btns > * {
|
|
margin: 0;
|
|
}
|
|
|
|
/* Bootstrap and OpenWrt 2020 define .label.warning and .label.success but no
|
|
.label.danger. */
|
|
#qos-app .label.danger {
|
|
background-color: var(--error-color-high, #e8210d);
|
|
color: var(--on-error-color, #fff);
|
|
}
|
|
|
|
#qos-app .table {
|
|
margin: 0;
|
|
}
|
|
|
|
#qos-app .table .td {
|
|
padding: .45em .75em;
|
|
vertical-align: middle;
|
|
border-top-color: var(--qos-ln);
|
|
}
|
|
|
|
/* A folding section is a details.cbi-section whose summary holds the theme's h3. */
|
|
#qos-app .cbi-section > summary {
|
|
margin: 0 0 var(--qos-gap);
|
|
cursor: pointer;
|
|
list-style: none;
|
|
}
|
|
|
|
#qos-app .cbi-section > summary::-webkit-details-marker {
|
|
display: none;
|
|
}
|
|
|
|
#qos-app .cbi-section > summary::before {
|
|
content: "\25B8";
|
|
display: inline-block;
|
|
width: 1.1em;
|
|
transition: transform .15s;
|
|
}
|
|
|
|
#qos-app details.cbi-section[open] > summary::before {
|
|
transform: rotate(90deg);
|
|
}
|
|
|
|
#qos-app details.cbi-section:not([open]) > summary {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
#qos-app summary > h3 {
|
|
display: inline;
|
|
margin: 0;
|
|
}
|
|
|
|
/* Quick Add and Reference folds, and every Advanced section, each get their
|
|
own box, drawn like the quick settings and counters boxes, with a heading
|
|
sized to the rest of the app. */
|
|
#qos-app .qa > details.cbi-section,
|
|
#qos-app .cbi-section.qos-box {
|
|
margin: 0 0 var(--qos-gap);
|
|
padding: var(--qos-pad);
|
|
border: 1px solid var(--qos-bd);
|
|
border-radius: var(--qos-r);
|
|
}
|
|
|
|
#qos-app .qa > details.cbi-section > summary > h3,
|
|
#qos-app .qos-box > h3 {
|
|
font-size: 1em;
|
|
font-weight: 600;
|
|
line-height: 1.4;
|
|
padding: 0;
|
|
}
|
|
|
|
/* The fold box sits just clear of its heading; the theme's h3 line-height
|
|
would otherwise double its depth. */
|
|
#qos-app .qa > details.cbi-section {
|
|
padding: .4em .75em;
|
|
}
|
|
|
|
#qos-app .qos-box > h3 {
|
|
margin: 0 0 var(--qos-gap);
|
|
}
|
|
|
|
/* Advanced: every section is a box with a table and its buttons on the right. */
|
|
#qos-ad .cbi-page-actions {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: .4em;
|
|
margin: var(--qos-gap) 0 0;
|
|
}
|
|
|
|
#qos-ad .cbi-page-actions::after {
|
|
content: none;
|
|
}
|
|
|
|
/* Bootstrap floats an action button left; every Advanced button sits right. */
|
|
#qos-ad .cbi-page-actions > * {
|
|
float: none;
|
|
margin: 0;
|
|
}
|
|
|
|
#qos-ad-bk .th:last-child,
|
|
#qos-ad-bk .td:last-child {
|
|
width: 1%;
|
|
text-align: right;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* Reference panels inside a section. */
|
|
#qos-app details:not(.cbi-section) {
|
|
margin: var(--qos-gap) 0 0;
|
|
}
|
|
|
|
#qos-app details:not(.cbi-section) > summary {
|
|
cursor: pointer;
|
|
font-weight: 600;
|
|
}
|
|
|
|
#qos-app details:not(.cbi-section) > p,
|
|
#qos-app details:not(.cbi-section) > .table {
|
|
margin: .5em 0 0;
|
|
}
|
|
|
|
/* Quick Add: forms, so the grids stay tight; the folds are ordinary sections. */
|
|
#qos-app .qa .table {
|
|
table-layout: fixed;
|
|
margin: 0 0 .3em;
|
|
}
|
|
|
|
#qos-app .qa .th,
|
|
#qos-app .qa .td {
|
|
padding: .15em .3em;
|
|
}
|
|
|
|
#qos-app .qa .th {
|
|
font-size: .85em;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
#qos-app .qa .td input:not([type=checkbox]),
|
|
#qos-app .qa .td select {
|
|
width: 100%;
|
|
min-width: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
#qos-app .qa .td input[type=checkbox] {
|
|
margin: 0;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
#qos-app .qa-foot {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: .3em 1em;
|
|
}
|
|
|
|
#qos-app .qa-foot > details {
|
|
flex: 1 1 0;
|
|
min-width: 0;
|
|
margin: .15em 0 0;
|
|
}
|
|
|
|
#qos-app .qa details:not(.cbi-section) > summary {
|
|
font-size: .9em;
|
|
}
|
|
|
|
#qos-app .qa details:not(.cbi-section) .td {
|
|
padding: .2em .5em;
|
|
border-top: 1px solid var(--qos-ln);
|
|
}
|
|
|
|
#qos-app .cbi-section-node > .cbi-value:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
#qos-app .qos-up-name {
|
|
margin-left: .5em;
|
|
opacity: .75;
|
|
}
|
|
|
|
/* The tc output takes the rest of the window: the viewport less the LuCI
|
|
header and the tab bar, caught by min-height on a short screen. The editors
|
|
are fitted to the window by fitEditor(); the calc() height only holds until
|
|
the first fit. Width is on the id so the theme's textarea width cannot win,
|
|
and resize: both keeps a wrong width correctable. */
|
|
#qos-st-pre {
|
|
margin: 0;
|
|
padding: .75em;
|
|
overflow: auto;
|
|
white-space: pre;
|
|
height: calc(100vh - 310px);
|
|
min-height: 320px;
|
|
resize: vertical;
|
|
font-family: var(--font-mono, monospace);
|
|
font-size: 12px;
|
|
border: 1px solid var(--qos-bd);
|
|
border-radius: var(--qos-r);
|
|
}
|
|
|
|
#qos-config-ta,
|
|
#qos-rules-ta {
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
height: calc(100vh - 300px);
|
|
min-height: 160px;
|
|
resize: both;
|
|
padding: 6px;
|
|
line-height: 1.4;
|
|
tab-size: 4;
|
|
font-family: var(--font-mono, monospace);
|
|
font-size: 12px;
|
|
}
|
|
|
|
/* Counters. Tin colours: bulk, best effort, video and voice take the theme's
|
|
error, primary, warn and success colours; the extra diffserv8 and precedence
|
|
tins use fallbacks with light and dark values, switched by LuCI's
|
|
data-darkmode or, on a theme that does not set it, prefers-color-scheme. */
|
|
#qos-app {
|
|
--qos-f-red: #e41a1c; --qos-f-blue: #377eb8; --qos-f-yellow: #e6b422;
|
|
--qos-f-green: #4daf4a; --qos-f-purple: #984ea3; --qos-f-orange: #ff7f00;
|
|
--qos-f-cyan: #17becf; --qos-f-forest: #1b7837; --qos-f-pine: #0f4d24;
|
|
--qos-f-grey: #5c5c5c; --qos-f-brown: #a65628; --qos-f-pink: #f781bf;
|
|
--qos-f-none: rgba(128, 128, 128, .45);
|
|
}
|
|
|
|
:root[data-darkmode="true"] #qos-app {
|
|
--qos-f-red: #e57373; --qos-f-blue: #64a0d8; --qos-f-yellow: #d4b44a;
|
|
--qos-f-green: #66bb6a; --qos-f-purple: #b085c0; --qos-f-orange: #ffa24d;
|
|
--qos-f-cyan: #4dd0e1; --qos-f-forest: #43a047; --qos-f-pine: #2e7d4f;
|
|
--qos-f-grey: #9e9e9e; --qos-f-brown: #c08050; --qos-f-pink: #f4a6cf;
|
|
--qos-f-none: rgba(160, 160, 160, .45);
|
|
}
|
|
|
|
@media (prefers-color-scheme: dark) {
|
|
:root:not([data-darkmode]) #qos-app {
|
|
--qos-f-red: #e57373; --qos-f-blue: #64a0d8; --qos-f-yellow: #d4b44a;
|
|
--qos-f-green: #66bb6a; --qos-f-purple: #b085c0; --qos-f-orange: #ffa24d;
|
|
--qos-f-cyan: #4dd0e1; --qos-f-forest: #43a047; --qos-f-pine: #2e7d4f;
|
|
--qos-f-grey: #9e9e9e; --qos-f-brown: #c08050; --qos-f-pink: #f4a6cf;
|
|
--qos-f-none: rgba(160, 160, 160, .45);
|
|
}
|
|
}
|
|
|
|
#qos-app {
|
|
--qos-c-red: var(--error-color-high, var(--qos-f-red));
|
|
--qos-c-blue: var(--primary-color-high, var(--qos-f-blue));
|
|
--qos-c-yellow: var(--warn-color-high, var(--qos-f-yellow));
|
|
--qos-c-green: var(--success-color-high, var(--qos-f-green));
|
|
--qos-c-purple: var(--qos-f-purple); --qos-c-orange: var(--qos-f-orange);
|
|
--qos-c-cyan: var(--qos-f-cyan); --qos-c-forest: var(--qos-f-forest);
|
|
--qos-c-pine: var(--qos-f-pine); --qos-c-grey: var(--qos-f-grey);
|
|
--qos-c-brown: var(--qos-f-brown); --qos-c-pink: var(--qos-f-pink);
|
|
--qos-c-none: var(--qos-f-none);
|
|
}
|
|
|
|
/* A header table above a boxed body; bars and figures are updated in place. */
|
|
#qos-cn .qbox {
|
|
overflow: hidden;
|
|
border: 1px solid var(--qos-bd);
|
|
border-radius: var(--qos-r);
|
|
}
|
|
|
|
#qos-cn .qbox + .qbox {
|
|
margin-top: var(--qos-gap);
|
|
}
|
|
|
|
#qos-cn .qhead {
|
|
background: var(--qos-hd);
|
|
border-bottom: 1px solid var(--qos-ln);
|
|
}
|
|
|
|
#qos-cn .qhead .th,
|
|
#qos-cn .qbox .td {
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
#qos-cn .qbox .tr:not(.table-titles):hover .td {
|
|
background: var(--qos-hd);
|
|
}
|
|
|
|
#qos-cn .cbi-progressbar {
|
|
height: .75em;
|
|
margin: 0;
|
|
min-width: 0;
|
|
border-radius: var(--qos-r);
|
|
}
|
|
|
|
#qos-cn .qn {
|
|
width: 1%;
|
|
text-align: right;
|
|
white-space: nowrap;
|
|
font-variant-numeric: tabular-nums;
|
|
}
|
|
|
|
#qos-cn .qt .td {
|
|
font-weight: 600;
|
|
}
|
|
|
|
#qos-cn-map-box {
|
|
height: 24rem;
|
|
min-height: 6rem;
|
|
overflow-y: scroll;
|
|
resize: vertical;
|
|
}
|
|
|
|
/* Quick settings: one inner box per tab, rows split by the same line as the
|
|
tables, and the control then its hint on one line so every hint starts in
|
|
the same column. */
|
|
#qos-app .qs-box {
|
|
min-width: 0;
|
|
min-height: 13em;
|
|
padding: .3em 1.5em;
|
|
border: 1px solid var(--qos-bd);
|
|
border-radius: var(--qos-r);
|
|
}
|
|
|
|
#qos-qs-sect .cbi-value {
|
|
align-items: flex-start;
|
|
margin: 0;
|
|
padding: .25em 0;
|
|
}
|
|
|
|
#qos-qs-sect .cbi-value + .cbi-value {
|
|
border-top: 1px solid var(--qos-ln);
|
|
}
|
|
|
|
#qos-qs-sect .cbi-value label.cbi-value-title {
|
|
flex: 0 0 16em;
|
|
padding-top: 0;
|
|
line-height: 30px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
#qos-qs-sect .cbi-value-field {
|
|
display: grid;
|
|
grid-template-columns: minmax(0, 18em) minmax(0, 1fr);
|
|
align-items: center;
|
|
gap: .3em 1.5em;
|
|
flex: 1 1 auto;
|
|
margin-left: 1.5em;
|
|
min-width: 0;
|
|
line-height: 30px;
|
|
}
|
|
|
|
#qos-qs-sect .cbi-value-description {
|
|
margin-top: 0;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
#qos-qs-sect .qs-note {
|
|
grid-column: 1 / -1;
|
|
}
|
|
|
|
#qos-qs-sect .cbi-value-field input[type=checkbox] {
|
|
justify-self: start;
|
|
margin: 0;
|
|
vertical-align: middle;
|
|
}
|
|
|
|
#qos-qs-sect .qs-ctl {
|
|
justify-self: start;
|
|
}
|
|
|
|
#qos-qs-sect .cbi-value-field input[type=text],
|
|
#qos-qs-sect .cbi-value-field input[type=number],
|
|
#qos-qs-sect .cbi-value-field select {
|
|
max-width: 100%;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
/* Sized by what goes in: a byte count gets a byte-sized box, CAKE options get the row. */
|
|
#qos-qs-sect [data-q=ovh_bytes],
|
|
#qos-qs-sect [data-q=overhead_mpu] {
|
|
width: 7em;
|
|
}
|
|
|
|
#qos-qs-sect [data-q=overhead_vlan],
|
|
#qos-qs-sect [data-q=overhead_encap] {
|
|
width: 9em;
|
|
}
|
|
|
|
#qos-qs-sect [data-q=name],
|
|
#qos-qs-sect [data-q=bw_up],
|
|
#qos-qs-sect [data-q=bw_down] {
|
|
width: 14em;
|
|
}
|
|
|
|
#qos-qs-sect [data-q=mode],
|
|
#qos-qs-sect [data-q=overhead] {
|
|
width: 18em;
|
|
}
|
|
|
|
#qos-qs-sect .qs-wide .cbi-value-field {
|
|
grid-template-columns: minmax(0, 28em) minmax(0, 1fr);
|
|
}
|
|
|
|
#qos-qs-sect [data-q$=opts] {
|
|
width: 100%;
|
|
}
|
|
|
|
/* Phones: label, then control, then hint, one under the other. */
|
|
@media (max-width: 600px) {
|
|
#qos-qs-sect .cbi-value {
|
|
display: block;
|
|
}
|
|
|
|
#qos-qs-sect .cbi-value label.cbi-value-title {
|
|
line-height: 1.5;
|
|
}
|
|
|
|
#qos-qs-sect .cbi-value-field {
|
|
grid-template-columns: minmax(0, 1fr);
|
|
margin-left: 0;
|
|
}
|
|
|
|
#qos-qs-sect .cbi-value-field > * {
|
|
grid-column: 1;
|
|
}
|
|
}
|