98 | <%- if not headers or headers["x-luci-login-required"] ~= "yes" then -%> 99 | <%- if sys.process.info("uid") == 0 and sys.user.getuser("root") and not sys.user.getpasswd("root") then -%> 100 |
101 |

<%:No password set!%>

102 |

<%:There is no password set on this router. Please configure a root password to protect the web interface and enable SSH.%>

103 |

<%:Go to password configuration...%>

104 |
105 | <%- end -%> 106 | <%- end -%> 107 | -------------------------------------------------------------------------------- /src/luci-theme-tano/po/ru/tano.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: luci-theme-tano\n" 4 | "PO-Revision-Date: 2022-12-16 06:49+0300\n" 5 | "Last-Translator: Anton Kikin \n" 6 | "Language-Team: Tano Systems LLC\n" 7 | "Language: ru\n" 8 | "MIME-Version: 1.0\n" 9 | "Content-Type: text/plain; charset=UTF-8\n" 10 | "Content-Transfer-Encoding: 8bit\n" 11 | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n" 12 | "%100<10 || n%100>=20) ? 1 : 2);\n" 13 | "POT-Creation-Date: 2022-12-16 06:49+0300\n" 14 | "X-Generator: Poedit 2.3\n" 15 | 16 | #: luasrc/view/themes/tano/header.htm:45 17 | msgid "Auto Refresh" 18 | msgstr "Автообновление" 19 | 20 | #: luasrc/view/themes/tano/header.htm:103 21 | msgid "Go to password configuration..." 22 | msgstr "Перейти к настройке пароля..." 23 | 24 | #: luasrc/view/themes/tano/header.htm:85 25 | msgid "Hostname" 26 | msgstr "Имя" 27 | 28 | #: luasrc/view/themes/tano/header.htm:101 29 | msgid "No password set!" 30 | msgstr "Пароль не установлен!" 31 | 32 | #: luasrc/view/themes/tano/header.htm:82 33 | msgid "RAMFS" 34 | msgstr "RAMFS" 35 | 36 | #: luasrc/view/themes/tano/header.htm:82 37 | msgid "" 38 | "System running in RAM-disk mode. No changes to settings will be stored and " 39 | "are lost after rebooting." 40 | msgstr "" 41 | "Система работает в режиме RAM диска. Никакие изменения настроек не " 42 | "сохраняются и теряются после перезагрузки." 43 | 44 | #: luasrc/view/themes/tano/header.htm:102 45 | msgid "" 46 | "There is no password set on this router. Please configure a root password to " 47 | "protect the web interface and enable SSH." 48 | msgstr "" 49 | "Пароль пользователя root не установлен. Установите пароль, чтобы защитить " 50 | "веб-интерфейс." 51 | 52 | #: luasrc/view/themes/tano/header.htm:53 53 | msgid "Unsaved Changes" 54 | msgstr "Не принятые изменения" 55 | -------------------------------------------------------------------------------- /src/luci-theme-tano/po/templates/tano.pot: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "Content-Type: text/plain; charset=UTF-8" 3 | 4 | #: luasrc/view/themes/tano/header.htm:45 5 | msgid "Auto Refresh" 6 | msgstr "" 7 | 8 | #: luasrc/view/themes/tano/header.htm:103 9 | msgid "Go to password configuration..." 10 | msgstr "" 11 | 12 | #: luasrc/view/themes/tano/header.htm:85 13 | msgid "Hostname" 14 | msgstr "" 15 | 16 | #: luasrc/view/themes/tano/header.htm:101 17 | msgid "No password set!" 18 | msgstr "" 19 | 20 | #: luasrc/view/themes/tano/header.htm:82 21 | msgid "RAMFS" 22 | msgstr "" 23 | 24 | #: luasrc/view/themes/tano/header.htm:82 25 | msgid "" 26 | "System running in RAM-disk mode. No changes to settings will be stored and " 27 | "are lost after rebooting." 28 | msgstr "" 29 | 30 | #: luasrc/view/themes/tano/header.htm:102 31 | msgid "" 32 | "There is no password set on this router. Please configure a root password to " 33 | "protect the web interface and enable SSH." 34 | msgstr "" 35 | 36 | #: luasrc/view/themes/tano/header.htm:53 37 | msgid "Unsaved Changes" 38 | msgstr "" 39 | -------------------------------------------------------------------------------- /src/luci-theme-tano/root/etc/uci-defaults/luci-theme-tano: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ "$PKG_UPGRADE" != 1 ]; then 3 | uci batch <<-EOF 4 | set luci.themes.Tano=/luci-static/tano 5 | commit luci 6 | EOF 7 | fi 8 | exit 0 9 | -------------------------------------------------------------------------------- /src/scripts/index.js: -------------------------------------------------------------------------------- 1 | import "../styles/index.scss" 2 | -------------------------------------------------------------------------------- /src/styles/alerts.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | .alert-message { 5 | border: 0; 6 | border-radius: $border-radius; 7 | margin: 8px 0; 8 | padding: 5px 8px; 9 | box-shadow: 0 1px 3px 0 #808080; 10 | 11 | @include alert($color-gray-lighten-30, $color-gray-lighten-70, $color-gray-lighten-30); 12 | 13 | a { 14 | color: $color-gray-lighten-10; 15 | } 16 | 17 | a:hover { 18 | color: $color-gray-lighten-20; 19 | } 20 | 21 | &.info { 22 | @include alert($color-blue-darken-10, $color-blue-lighten-45, $color-blue-darken-10); 23 | } 24 | 25 | &.warning { 26 | @include alert($color-yellow-darken-25, $color-yellow-lighten-32, $color-yellow-darken-25); 27 | } 28 | 29 | &.success { 30 | @include alert($color-green-darken-5, $color-green-lighten-60, $color-green-darken-5); 31 | } 32 | 33 | &.error, 34 | &.danger { 35 | @include alert($color-red-darken-5, $color-red-lighten-40, $color-red-darken-5); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/styles/breadcrumbs.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | 3 | ul.breadcrumbs { 4 | color: $color-gray-lighten-50; 5 | font-size: $base-font-size; 6 | margin: 0; 7 | 8 | li { 9 | display: inline-block; 10 | 11 | &:not(:last-of-type) { 12 | color: $color-gray-lighten-30; 13 | 14 | &::after { 15 | content: "»"; 16 | padding: 0 4px; 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/styles/buttons.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | button, 5 | .btn, 6 | .cbi-button, 7 | a.cbi-button, 8 | input[type="button"], 9 | input[type="reset"], 10 | input[type="submit"] { 11 | @include tooltip-shadow; 12 | 13 | user-select: none; 14 | align-items: center; 15 | border: none; 16 | border-radius: 3px; 17 | color: #ffffff; 18 | fill: #ffffff; 19 | cursor: pointer; 20 | display: inline-flex; 21 | font-family: $base-font-family; 22 | font-size: $button-font-size; 23 | font-weight: normal; 24 | height: $input-height; 25 | justify-content: center; 26 | line-height: $input-height; 27 | outline: none; 28 | padding: 0 10px; 29 | transition: $transition-time; 30 | white-space: nowrap; 31 | 32 | > img { 33 | margin-right: 6px; 34 | } 35 | } 36 | 37 | .btn { 38 | &.success { 39 | @include button($color-green-base, $color-green-lighten-5, $color-green-darken-5); 40 | } 41 | 42 | &.info { 43 | @include button($color-blue-base, $color-blue-lighten-5, $color-blue-darken-5); 44 | } 45 | 46 | &.danger { 47 | @include button($color-orange-base, $color-orange-lighten-10, $color-orange-darken-10); 48 | } 49 | } 50 | 51 | a.btn { 52 | color: #ffffff; 53 | } 54 | 55 | button, 56 | .btn, 57 | .cbi-button { 58 | @include button($color-blue-darken-10, $color-blue-base, $color-blue-darken-10); 59 | } 60 | 61 | .btn.reset, 62 | .btn.error, 63 | .btn.important, 64 | .cbi-button-reset, 65 | .cbi-button-remove, 66 | .cbi-button-negative, 67 | .cbi-section-remove input[type="submit"] { 68 | @include button($color-red-base, $color-red-lighten-5, $color-red-darken-5); 69 | } 70 | 71 | button, 72 | .btn, 73 | .cbi-button { 74 | &:disabled, 75 | &[disabled] { 76 | @include button($color-gray-lighten-70, $color-gray-lighten-70, $color-gray-lighten-70); 77 | color: #888; 78 | fill: #888; 79 | cursor: not-allowed; 80 | } 81 | } 82 | 83 | input.cbi-input-text[disabled] + button[aria-label].cbi-button, /* CIDR switch buttons */ 84 | input.cbi-input-password[disabled] + button[aria-label].cbi-button, /* Password show/hide buttons */ 85 | .cbi-dynlist[disabled] .cbi-button { 86 | @include button($color-gray-lighten-70, $color-gray-lighten-70, $color-gray-lighten-70); 87 | color: #888; 88 | fill: #888; 89 | cursor: not-allowed; 90 | } 91 | 92 | /* Special tweaks for WireGuard protocol QR-code generation button */ 93 | .btn.qr-code { 94 | svg { 95 | fill: #fff; 96 | background-color: inherit; 97 | path { 98 | &:first-of-type { 99 | display: none; 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/styles/cbi.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | .cbi-section-node { 5 | @media screen and (min-width: $breakpoint-md) { 6 | padding: 4px 0 0 0; 7 | } 8 | } 9 | 10 | .cbi-map { 11 | margin-bottom: 20px; 12 | 13 | & + .cbi-map { 14 | margin-top: 50px; 15 | } 16 | } 17 | 18 | .cbi-value { 19 | display: flex; 20 | flex-direction: column; 21 | margin-bottom: 10px; 22 | 23 | @media screen and (min-width: $breakpoint-md) { 24 | display: block; 25 | 26 | &::before { 27 | content: ""; 28 | display: block; 29 | } 30 | 31 | &::after { 32 | clear: both; 33 | content: ""; 34 | display: block; 35 | } 36 | } 37 | } 38 | 39 | .cbi-value-title { 40 | color: $color-gray-lighten-10; 41 | display: block; 42 | float: left; 43 | font-weight: bolder; 44 | margin-bottom: 3px; 45 | padding-top: ($input-height - $base-line-height) / 2; 46 | 47 | @media screen and (min-width: $breakpoint-md) { 48 | font-weight: normal; 49 | margin-bottom: 0; 50 | margin-right: 16px; 51 | min-width: $cbi-title-width; 52 | text-align: right; 53 | width: $cbi-title-width; 54 | 55 | &:hover { 56 | cursor: pointer; 57 | } 58 | } 59 | } 60 | 61 | .cbi-value-description, 62 | .cbi-map-descr, 63 | .cbi-tab-descr, 64 | .cbi-section-descr { 65 | color: $color-gray-lighten-40; 66 | margin-bottom: 8px; 67 | margin-top: 8px; 68 | } 69 | 70 | .cbi-section-descr, 71 | .cbi-tab-descr, 72 | .cbi-map-descr { 73 | line-height: $base-font-size * 1.3; 74 | } 75 | 76 | .cbi-value-description { 77 | font-size: $base-font-size * 0.9; 78 | line-height: $base-font-size * 0.9 * 1.3; 79 | margin-top: 5px; 80 | } 81 | 82 | .cbi-checkbox + .cbi-value-description { 83 | margin-top: 0; 84 | } 85 | 86 | .cbi-section { 87 | margin-top: 20px; 88 | 89 | legend, 90 | h3 { 91 | border-bottom: solid 2px $color-blue-base; 92 | font-size: $base-font-size * 1.2; 93 | line-height: $base-line-height * 1.2; 94 | margin: 10px 0; 95 | padding: 5px 0; 96 | width: 100%; 97 | font-weight: normal; 98 | } 99 | 100 | h3 { 101 | margin-bottom: 5px; 102 | } 103 | } 104 | 105 | /* Keep old styles for h3 on overview page */ 106 | [data-path="admin-status-overview"] { 107 | .cbi-section { 108 | h3 { 109 | @include h(1.4); 110 | border-bottom: none; 111 | margin-bottom: 5px; 112 | padding: 0; 113 | } 114 | } 115 | } 116 | 117 | .cbi-value > .cbi-section { 118 | margin-top: 0; 119 | } 120 | 121 | /* Wireless pages/modals fixes */ 122 | #cbi-wireless > .cbi-section { 123 | margin-top: 0; 124 | 125 | .cbi-section { 126 | .cbi-value[data-name="_freq"] { 127 | .cbi-value-title { 128 | br { 129 | display: none; 130 | } 131 | } 132 | 133 | .cbi-value-field > div { 134 | display: flex; 135 | flex-flow: column; 136 | width: 100%; 137 | 138 | label { 139 | display: flex; 140 | flex-flow: column; 141 | width: 100%; 142 | margin-bottom: 4px; 143 | 144 | select { 145 | flex-basis: auto; 146 | } 147 | } 148 | 149 | br { 150 | display: none; 151 | } 152 | } 153 | } 154 | 155 | .cbi-value[data-name="txpower"] { 156 | .cbi-value-field > div { 157 | & > span { 158 | flex-basis: 100%; 159 | margin-top: 8px; 160 | } 161 | } 162 | } 163 | } 164 | } 165 | 166 | .cbi-progressbar { 167 | @include tooltip-shadow; 168 | background-color: $color-gray-lighten-70; 169 | border-radius: 12px; 170 | height: 24px; 171 | overflow: hidden; 172 | position: relative; 173 | 174 | div { 175 | background-color: $color-blue-lighten-30; 176 | height: 100%; 177 | } 178 | 179 | &::after { 180 | align-items: center; 181 | bottom: 0; 182 | content: attr(title); 183 | display: flex; 184 | justify-content: center; 185 | left: 0; 186 | position: absolute; 187 | right: 0; 188 | top: 0; 189 | } 190 | } 191 | 192 | .cbi-value-field > .cbi-progressbar { 193 | flex-basis: 100%; 194 | } 195 | 196 | .cbi-section-actions > div { 197 | display: flex; 198 | text-align: right; 199 | 200 | flex-flow: row; 201 | flex-wrap: wrap; 202 | justify-content: flex-end; 203 | 204 | button, 205 | input[type="button"], 206 | input[type="submit"], 207 | div.cbi-button { 208 | margin-bottom: 5px; 209 | margin-left: 5px; 210 | flex-shrink: 1; 211 | flex-grow: 1; 212 | } 213 | } 214 | 215 | .cbi-value-field { 216 | display: flex; 217 | flex-wrap: wrap; 218 | 219 | .cbi-value-description { 220 | width: 100%; 221 | } 222 | 223 | label { 224 | display: flex; 225 | width: 100%; 226 | 227 | .cbi-input-radio, 228 | .cbi-input-checkbox { 229 | height: $base-line-height; 230 | margin-right: 8px; 231 | } 232 | } 233 | 234 | & > div[id] { 235 | display: flex; 236 | flex-wrap: wrap; 237 | width: 100%; 238 | } 239 | 240 | & > div.cbi-dynlist { 241 | display: block; 242 | width: 100%; 243 | } 244 | 245 | & > div + div { 246 | margin-top: 5px; 247 | } 248 | 249 | & > :not([id]):not([class]):first-child { 250 | padding-top: ($input-height - $base-line-height) / 2; 251 | } 252 | } 253 | 254 | /* Remove top padding for CBI.ButtonValue widgets */ 255 | .cbi-value { 256 | &[data-widget="CBI.ButtonValue"] { 257 | .cbi-value-field { 258 | & > :first-child { 259 | padding-top: 0; 260 | } 261 | } 262 | } 263 | } 264 | 265 | .cbi-upload-info { 266 | color: $color-green-lighten-5; 267 | margin: 7px 0 5px 0; 268 | width: 100%; 269 | } 270 | 271 | .cbi-input-radio + label, 272 | input[type="radio"] + label { 273 | display: inline-flex; 274 | margin-left: 5px; 275 | } 276 | 277 | .cbi-page-actions { 278 | display: flex; 279 | flex-direction: column; 280 | 281 | @media screen and (min-width: $breakpoint-sm) { 282 | border-radius: 3px; 283 | display: flex; 284 | flex-direction: row; 285 | justify-content: flex-end; 286 | } 287 | 288 | form { 289 | display: inline-block; 290 | } 291 | 292 | .cbi-button { 293 | width: 100%; 294 | display: inline-flex; 295 | 296 | @media screen and (min-width: $breakpoint-sm) { 297 | width: auto; 298 | } 299 | } 300 | 301 | form + form, 302 | .cbi-button + .cbi-button { 303 | margin-top: 5px; 304 | 305 | @media screen and (min-width: $breakpoint-sm) { 306 | margin-left: 6px; 307 | margin-top: 0; 308 | } 309 | } 310 | } 311 | 312 | .cbi-value-field-password { 313 | display: flex; 314 | flex-wrap: wrap; 315 | 316 | input[type="password"] { 317 | flex-shrink: 1; 318 | width: auto; 319 | } 320 | 321 | .cbi-value-description { 322 | &::before { 323 | width: 100%; 324 | } 325 | 326 | &#passstrength > span { 327 | padding-left: 4px; 328 | } 329 | } 330 | } 331 | 332 | .cbi-dynlist { 333 | .item { 334 | border: solid 1px $color-gray-lighten-70; 335 | border-radius: $border-radius; 336 | margin: 5px 0; 337 | padding: 5px 10px; 338 | pointer-events: none; 339 | position: relative; 340 | transition: $transition-time; 341 | 342 | &::after { 343 | align-items: center; 344 | background-color: #ffffff; 345 | border-left: solid 1px $color-gray-lighten-70; 346 | bottom: 0; 347 | color: $color-red-base; 348 | content: "\D7"; 349 | cursor: pointer; 350 | display: flex; 351 | padding: 5px 10px; 352 | pointer-events: auto; 353 | position: absolute; 354 | right: 0; 355 | top: 0; 356 | } 357 | 358 | &:hover { 359 | border-color: $color-red-lighten-10; 360 | } 361 | } 362 | 363 | &.cbi-input-invalid { 364 | > div { 365 | > input[type="text"], 366 | > div.cbi-button { 367 | border: solid 1px $color-red-lighten-10; 368 | } 369 | } 370 | } 371 | } 372 | 373 | .cbi-optionals, 374 | .cbi-section-create { 375 | display: flex; 376 | flex-wrap: wrap; 377 | gap: 5px; 378 | 379 | @media screen and (min-width: $breakpoint-md) { 380 | margin-left: $cbi-title-width + 16px; 381 | } 382 | 383 | button { 384 | flex-grow: 1; 385 | 386 | @media screen and (min-width: $breakpoint-md) { 387 | flex-grow: 0; 388 | } 389 | } 390 | } 391 | 392 | .cbi-section-create { 393 | justify-content: flex-end; 394 | margin-top: 16px; 395 | 396 | /* stylelint-disable-next-line no-descending-specificity */ 397 | & > div { 398 | display: flex; 399 | margin: 0 5px 0 10px; 400 | width: 100%; 401 | } 402 | } 403 | 404 | /* Fix cbi-section-create in DHCP-server tab 405 | * at network interface configuration */ 406 | div#cbi-dhcp-dhcp { 407 | margin-top: 0; 408 | 409 | & > .cbi-section-create { 410 | flex-flow: row; 411 | flex-wrap: wrap; 412 | justify-content: center; 413 | align-items: baseline; 414 | margin-left: 0; 415 | margin-top: 0; 416 | 417 | & > p { 418 | margin-bottom: 5px; 419 | flex-basis: 100%; 420 | text-align: center; 421 | } 422 | } 423 | } 424 | 425 | .cbi-section-remove { 426 | margin-top: 10px; 427 | padding: 10px 0; 428 | } 429 | 430 | .cbi-section-node + .cbi-section-remove { 431 | border-top: dotted 1px $color-gray-lighten-60; 432 | padding: 20px 0px 10px 10px; 433 | } 434 | 435 | .cbi-section-error { 436 | border: solid 1px; 437 | border-radius: $border-radius; 438 | padding: 5px 10px; 439 | 440 | @include alert($color-red-darken-5, $color-red-lighten-30, $color-red-darken-5); 441 | } 442 | 443 | .cbi-value-error { 444 | input[type="text"], 445 | input[type="password"], 446 | select, 447 | textarea { 448 | border-color: $color-red-lighten-10; 449 | 450 | &:focus { 451 | @include input-shadow($color-red-lighten-10); 452 | } 453 | } 454 | 455 | .cbi-value-title { 456 | color: $color-red-lighten-10; 457 | } 458 | } 459 | 460 | .ui-select { 461 | &[data-widget="radio"] { 462 | margin-top: 0; 463 | display: flex; 464 | flex-direction: row; 465 | flex-wrap: wrap; 466 | 467 | &[data-orientation="vertical"] { 468 | flex-direction: column; 469 | } 470 | 471 | > .cbi-radio { 472 | cursor: pointer; 473 | margin-right: 10px; 474 | margin-top: 7px; 475 | display: flex; 476 | flex-wrap: nowrap; 477 | flex-direction: row; 478 | align-items: flex-start; 479 | 480 | > input[type=radio] { 481 | margin-top: 2px; 482 | } 483 | 484 | > label { 485 | display: none !important; 486 | } 487 | 488 | > span { 489 | margin-left: 6px; 490 | } 491 | } 492 | } 493 | } 494 | -------------------------------------------------------------------------------- /src/styles/changes.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | 3 | .uci-change-legend { 4 | display: flex; 5 | flex-wrap: wrap; 6 | margin-bottom: 20px; 7 | margin-top: 8px; 8 | 9 | ins, 10 | del { 11 | text-decoration: none; 12 | } 13 | } 14 | 15 | .uci-change-legend-label { 16 | align-items: center; 17 | display: flex; 18 | white-space: nowrap; 19 | width: 50%; 20 | 21 | @media screen and (min-width: $breakpoint-md) { 22 | width: 25%; 23 | } 24 | 25 | ins, 26 | del { 27 | border-radius: $border-radius; 28 | display: inline-block; 29 | height: $change-legend-size; 30 | margin-bottom: 5px; 31 | margin-right: 5px; 32 | width: $change-legend-size; 33 | } 34 | 35 | ins { 36 | background-color: $changes-add-background-color; 37 | border: $changes-add-border; 38 | } 39 | 40 | del { 41 | background-color: $changes-remove-background-color; 42 | border: $changes-remove-border; 43 | } 44 | 45 | var { 46 | align-items: center; 47 | background-color: $changes-option-background-color; 48 | border: $changes-option-border; 49 | border-radius: $border-radius; 50 | display: inline-flex; 51 | height: $change-legend-size; 52 | justify-content: center; 53 | margin-right: 5px; 54 | width: $change-legend-size; 55 | 56 | ins, 57 | del { 58 | display: block; 59 | height: $change-legend-size * 0.7; 60 | margin: 0; 61 | width: $change-legend-size * 0.7; 62 | } 63 | } 64 | } 65 | 66 | /* stylelint-disable no-descending-specificity */ 67 | .uci-change-list { 68 | font-family: monospace; 69 | 70 | ins, 71 | del { 72 | border-radius: $border-radius; 73 | display: block; 74 | margin: 3px 7px; 75 | padding: 3px 8px; 76 | text-decoration: none; 77 | } 78 | 79 | ins { 80 | background-color: $changes-add-background-color; 81 | border: $changes-add-border; 82 | color: $color-green-darken-10; 83 | } 84 | 85 | del { 86 | background-color: $changes-remove-background-color; 87 | border: $changes-remove-border; 88 | color: $color-red-darken-10; 89 | } 90 | 91 | /* stylelint-enable no-descending-specificity */ 92 | 93 | var { 94 | background-color: $changes-option-background-color; 95 | border: $changes-option-border; 96 | border-radius: $border-radius; 97 | display: block; 98 | margin-bottom: 3px; 99 | padding: 5px; 100 | 101 | ins, 102 | del { 103 | border-radius: $border-radius; 104 | margin: 0; 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/styles/dropdown.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | .cbi-dropdown { 5 | border: solid 1px $color-gray-lighten-70; 6 | border-radius: $border-radius; 7 | cursor: pointer; 8 | display: flex; 9 | outline: none; 10 | position: relative; 11 | width: 100%; 12 | 13 | &.btn, &.cbi-button { 14 | width: auto; 15 | padding-right: 0; 16 | padding-left: 28px; 17 | @media screen and (min-width: $breakpoint-sm) { 18 | padding-left: 10px; 19 | } 20 | 21 | & > ul:not(.dropdown) { 22 | & > li { 23 | justify-content: center; 24 | } 25 | } 26 | 27 | & > ul.dropdown { 28 | color: $base-font-color; 29 | } 30 | 31 | & > .open { 32 | margin-left: 10px; 33 | border-left: 1px solid; 34 | padding: 0 10px; 35 | } 36 | } 37 | 38 | label { 39 | margin: 0 !important; 40 | } 41 | 42 | & > ul { 43 | display: flex; 44 | list-style: none; 45 | margin: 0 !important; 46 | overflow-x: hidden; 47 | overflow-y: auto; 48 | padding: 0; 49 | width: 100%; 50 | 51 | &.preview { 52 | display: none; 53 | } 54 | 55 | &:focus { 56 | outline: none; 57 | } 58 | 59 | & > li { 60 | align-items: center; 61 | align-self: center; 62 | display: none; 63 | flex-grow: 1; 64 | flex-shrink: 1; 65 | min-height: $cbi-dropdown-height; 66 | overflow: hidden; 67 | padding: 0 8px; 68 | text-overflow: ellipsis; 69 | white-space: nowrap; 70 | 71 | &:focus { 72 | outline: none; 73 | } 74 | 75 | &[unselectable] { 76 | cursor: not-allowed; 77 | opacity: 0.5; 78 | 79 | > label { 80 | cursor: not-allowed; 81 | } 82 | } 83 | 84 | &[placeholder] { 85 | color: $color-gray-lighten-50; 86 | display: none; 87 | } 88 | 89 | & > form { 90 | display: none; 91 | margin: 0; 92 | padding: 0; 93 | pointer-events: none; 94 | 95 | & > input[type="checkbox"] { 96 | margin: 0 8px 0 0; 97 | } 98 | } 99 | 100 | img { 101 | margin: 0 10px; 102 | vertical-align: middle; 103 | } 104 | 105 | span, 106 | em { 107 | margin-right: 8px; 108 | } 109 | 110 | input[type="text"] { 111 | height: $cbi-dropdown-input-height; 112 | width: 100%; 113 | 114 | @media screen and (min-width: $breakpoint-md) { 115 | width: auto; 116 | } 117 | } 118 | 119 | .hide-open { 120 | display: block; 121 | } 122 | 123 | .hide-close { 124 | display: none; 125 | } 126 | } 127 | } 128 | 129 | & > .open { 130 | display: flex; 131 | flex-direction: column; 132 | flex-grow: 0; 133 | flex-shrink: 0; 134 | justify-content: center; 135 | line-height: $cbi-dropdown-height; 136 | padding: 0 5px; 137 | text-align: center; 138 | font-size: $base-font-size * 1.4; 139 | 140 | &:focus { 141 | outline: none; 142 | } 143 | } 144 | 145 | & > .more { 146 | color: $color-gray-lighten-50; 147 | display: none; 148 | height: $cbi-dropdown-height; 149 | outline: none; 150 | padding: 0 10px; 151 | } 152 | 153 | &[empty] > ul { 154 | max-width: 1px; 155 | } 156 | 157 | &:focus { 158 | @include input-shadow; 159 | border: solid 1px $color-blue-base; 160 | } 161 | 162 | &.cbi-input-invalid { 163 | border-color: $color-red-lighten-10; 164 | 165 | &:focus { 166 | @include input-shadow($color-red-lighten-10); 167 | } 168 | } 169 | } 170 | 171 | .cbi-dropdown > ul:not(.dropdown) > li[display]:not([display="0"]) { 172 | border-left: 1px solid $color-gray-lighten-70; 173 | } 174 | 175 | .cbi-dropdown:not(.btn):not(.cbi-button)[open] { 176 | @include input-shadow; 177 | border: solid 1px $color-blue-base; 178 | 179 | &.cbi-input-invalid { 180 | border-color: $color-red-lighten-10; 181 | @include input-shadow($color-red-lighten-10); 182 | } 183 | } 184 | 185 | .cbi-dropdown[open] > ul.dropdown { 186 | @include tooltip-shadow; 187 | 188 | background: #ffffff; 189 | display: block; 190 | left: 0 !important; 191 | min-width: 100%; 192 | position: absolute; 193 | right: 0 !important; 194 | width: 100%; 195 | z-index: 1000; 196 | 197 | @media screen and (min-width: $breakpoint-md) { 198 | left: auto !important; 199 | max-width: none; 200 | min-width: 100%; 201 | right: auto !important; 202 | width: auto; 203 | } 204 | } 205 | 206 | .cbi-dropdown.btn[open] > ul.dropdown, 207 | .cbi-dropdown.cbi-button[open] > ul.dropdown { 208 | @media screen and (min-width: $breakpoint-md) { 209 | left: 0 !important; 210 | } 211 | } 212 | 213 | /* stylelint-disable no-descending-specificity */ 214 | .cbi-dropdown > ul > li[display], 215 | .cbi-dropdown[open] > ul.preview, 216 | .cbi-dropdown[open] > ul.dropdown > li, 217 | .cbi-dropdown[multiple] > ul > li > label, 218 | .cbi-dropdown[multiple][open] > ul.dropdown > li, 219 | .cbi-dropdown[multiple][more] > .more, 220 | .cbi-dropdown[multiple][empty] > .more { 221 | align-items: center; 222 | display: flex; 223 | flex-grow: 1; 224 | } 225 | 226 | .cbi-dropdown[open] > ul.dropdown > li { 227 | border-bottom: solid 1px $color-gray-lighten-70; 228 | } 229 | 230 | .cbi-dropdown[empty] > ul > li, 231 | .cbi-dropdown[optional][open] > ul.dropdown > li[placeholder], 232 | .cbi-dropdown[multiple][open] > ul.dropdown > li > form { 233 | display: flex; 234 | } 235 | 236 | /* stylelint-enable no-descending-specificity */ 237 | 238 | .cbi-dropdown[open] > ul.dropdown > li .hide-open { display: none; } 239 | .cbi-dropdown[open] > ul.dropdown > li .hide-close { display: block; } 240 | 241 | .cbi-dropdown[open] > ul.dropdown > li[selected] { 242 | background-color: $color-gray-lighten-70; 243 | } 244 | 245 | .cbi-dropdown[open] > ul.dropdown > li.focus { 246 | background-color: $color-gray-lighten-75; 247 | } 248 | 249 | .cbi-dropdown[open] > ul.dropdown > li:last-child { 250 | border-bottom: none; 251 | margin-bottom: 0; 252 | } 253 | 254 | .cbi-dropdown[disabled] { 255 | pointer-events: none; 256 | opacity: 0.7; 257 | } 258 | 259 | .td.cbi-value-field { 260 | .cbi-dropdown { 261 | width: auto; 262 | } 263 | } 264 | 265 | .cbi-value-field { 266 | div[id].cbi-dropdown { 267 | flex-wrap: nowrap; 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /src/styles/filebrowser.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | 3 | .cbi-filebrowser { 4 | width: 100%; 5 | min-width: 210px; 6 | max-width: 100%; 7 | border: solid 1px $color-gray-lighten-70; 8 | border-radius: $border-radius; 9 | display: flex; 10 | flex-direction: column; 11 | opacity: 0; 12 | height: 0; 13 | overflow: hidden; 14 | padding-bottom: 2px; 15 | 16 | &.open { 17 | opacity: 1; 18 | height: auto; 19 | overflow: visible; 20 | } 21 | 22 | > * { 23 | max-width: 100%; 24 | overflow: hidden; 25 | text-overflow: ellipsis; 26 | padding: 0 0 .25em 0; 27 | margin: .25em .25em 0px .25em; 28 | white-space: nowrap; 29 | border-bottom: 1px solid $color-gray-lighten-70; 30 | } 31 | 32 | > div { 33 | border-bottom: none; 34 | } 35 | 36 | .cbi-button-positive { 37 | margin-right: .25em; 38 | } 39 | 40 | > ul { 41 | max-height: 300px; 42 | overflow-y: scroll; 43 | overflow-x: auto; 44 | 45 | > em { 46 | margin-right: 24px; 47 | } 48 | 49 | > li { 50 | display: flex; 51 | flex-direction: row; 52 | 53 | &:hover { 54 | background: #f5f5f5; 55 | } 56 | 57 | > div { 58 | &:first-child { 59 | overflow: hidden; 60 | text-overflow: ellipsis; 61 | } 62 | 63 | &.name { 64 | flex-basis: 200px; 65 | min-width: 200px; 66 | } 67 | 68 | &.name, &.mtime { 69 | margin: .1em 8px .1em 0; 70 | } 71 | 72 | &.name > a { 73 | font-weight: normal; 74 | } 75 | 76 | &:last-child { 77 | text-align: right; 78 | margin-right: 4px; 79 | margin-left: auto; 80 | 81 | > button { 82 | margin: .1em 0 .1em .25em; 83 | padding: 0 6px 0 6px; 84 | height: 16px; 85 | line-height: 16px; 86 | font-size: $base-font-size * 0.9; 87 | } 88 | } 89 | } 90 | 91 | &.cbi-filebrowser-selected { 92 | background-color: #e0e0e0; 93 | .name > a, .mtime { 94 | color: #606060; 95 | font-weight: normal !important; 96 | } 97 | } 98 | } 99 | } 100 | 101 | .upload { 102 | display: flex; 103 | flex-direction: row; 104 | flex-wrap: wrap; 105 | margin: 0 -.125em .25em -.125em; 106 | padding: 0 0 .125em 0px; 107 | border-bottom: 1px solid $color-gray-lighten-70; 108 | 109 | > * { 110 | margin: .125em; 111 | flex: 1; 112 | } 113 | 114 | > .btn { 115 | flex-basis: 60px; 116 | } 117 | 118 | > div { 119 | flex: 10; 120 | min-width: 150px; 121 | 122 | > input { 123 | width: 100%; 124 | } 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/styles/fonts.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Carlito'; 3 | font-style: italic; 4 | font-weight: normal; 5 | src: local('Carlito Italic'), local('Carlito-Italic'), url('../tano/fonts/Carlito-Italic.woff2') format('woff2'), url('../tano/fonts/Carlito-Italic.woff') format('woff'); 6 | } 7 | 8 | @font-face { 9 | font-family: 'Carlito'; 10 | font-style: normal; 11 | font-weight: bold; 12 | src: local('Carlito Bold'), local('Carlito-Bold'), url('../tano/fonts/Carlito-Bold.woff2') format('woff2'), url('../tano/fonts/Carlito-Bold.woff') format('woff'); 13 | } 14 | 15 | @font-face { 16 | font-family: 'Carlito'; 17 | font-style: normal; 18 | font-weight: normal; 19 | src: local('Carlito'), url('../tano/fonts/Carlito.woff2') format('woff2'), url('../tano/fonts/Carlito.woff') format('woff'); 20 | } 21 | 22 | @font-face { 23 | font-family: 'Carlito'; 24 | font-style: italic; 25 | font-weight: bold; 26 | src: local('Carlito Bold Italic'), local('Carlito-BoldItalic'), url('../tano/fonts/Carlito-BoldItalic.woff2') format('woff2'), url('../tano/fonts/Carlito-BoldItalic.woff') format('woff'); 27 | } 28 | -------------------------------------------------------------------------------- /src/styles/index.scss: -------------------------------------------------------------------------------- 1 | @import "~reset-css/_reset"; 2 | @import "layout"; 3 | @import "fonts"; 4 | @import "menu"; 5 | @import "breadcrumbs"; 6 | @import "dropdown"; 7 | @import "cbi"; 8 | @import "tabs"; 9 | @import "inputs"; 10 | @import "alerts"; 11 | @import "labels"; 12 | @import "tables"; 13 | @import "buttons"; 14 | @import "changes"; 15 | @import "network-widgets"; 16 | @import "modal"; 17 | @import "tooltip"; 18 | @import "spinner"; 19 | @import "filebrowser"; 20 | @import "luci-app-opkg"; 21 | @import "luci-app-nlbwmon"; 22 | -------------------------------------------------------------------------------- /src/styles/inputs.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | input[type="text"], 5 | input[type="password"], 6 | select { 7 | color: $color-gray-base; 8 | font-family: $base-font-family; 9 | height: $input-height; 10 | padding: 0 10px; 11 | width: 100%; 12 | } 13 | 14 | select { 15 | text-overflow: ellipsis; 16 | } 17 | 18 | textarea { 19 | font-family: monospace; 20 | padding: 5px 8px; 21 | resize: vertical; 22 | width: 100%; 23 | } 24 | 25 | input[type="text"], 26 | input[type="password"], 27 | select, 28 | textarea { 29 | background-color: white; 30 | border: solid 1px $color-gray-lighten-70; 31 | border-radius: $border-radius; 32 | flex-basis: 45%; 33 | flex-grow: 1; 34 | font-size: $input-font-size; 35 | outline: none; 36 | position: relative; 37 | transition-duration: $transition-time; 38 | transition-property: border, box-shadow; 39 | 40 | &:focus { 41 | @include input-shadow; 42 | 43 | border: solid 1px $color-blue-base; 44 | } 45 | 46 | &.cbi-input-invalid { 47 | border-color: $color-red-lighten-10; 48 | 49 | &:focus { 50 | @include input-shadow($color-red-lighten-10); 51 | } 52 | } 53 | 54 | &::placeholder { 55 | color: $color-gray-lighten-50; 56 | } 57 | } 58 | 59 | .cbi-input-invalid { 60 | > button.btn, 61 | > .cbi-filebrowser { 62 | border: solid 1px $color-red-lighten-10; 63 | } 64 | } 65 | 66 | span.cbi-input-invalid { 67 | border: solid 1px $color-red-lighten-10; 68 | border-radius: $border-radius; 69 | color: $color-red-lighten-10; 70 | padding: 3px 6px; 71 | } 72 | 73 | input[type="checkbox"] { 74 | height: $input-height; 75 | margin: 0; 76 | } 77 | 78 | .cbi-value-field { 79 | .cbi-checkbox { 80 | height: auto; 81 | display: flex; 82 | 83 | > label { 84 | width: auto; 85 | } 86 | 87 | > span { 88 | margin-left: 5px; 89 | margin-top: 10px; 90 | margin-right: 10px; 91 | line-height: 1.0; 92 | cursor: pointer; 93 | } 94 | } 95 | 96 | /* support for 'vertical' orientation */ 97 | br + .cbi-checkbox { 98 | flex-basis: 100%; 99 | } 100 | } 101 | 102 | input[type="radio"] { 103 | margin: 0; 104 | } 105 | 106 | input[type="file"] { 107 | cursor: pointer; 108 | display: block; 109 | margin: 5px 0; 110 | } 111 | 112 | input[readonly], 113 | input[disabled], 114 | textarea[readonly], 115 | textarea[disabled] { 116 | cursor: text; 117 | } 118 | 119 | .cbi-dropdown[disabled], 120 | .cbi-dynlist[disabled] .item, 121 | select[readonly], 122 | select[disabled] { 123 | cursor: not-allowed; 124 | opacity: 0.7; 125 | } 126 | 127 | .cbi-dropdown[disabled], 128 | .cbi-dynlist[disabled] .item, 129 | input[readonly], 130 | select[readonly], 131 | textarea[readonly], 132 | input[disabled], 133 | select[disabled], 134 | textarea[disabled] { 135 | background-color: $color-gray-lighten-70; 136 | border-color: $color-gray-lighten-60; 137 | opacity: 0.7; 138 | 139 | &:focus { 140 | border-color: $color-gray-lighten-60; 141 | box-shadow: none; 142 | } 143 | } 144 | 145 | .cbi-dynlist[disabled] .item::after { 146 | background-color: $color-gray-lighten-70; 147 | opacity: 0.7; 148 | } 149 | 150 | select[multiple] { 151 | height: auto; 152 | 153 | option { 154 | padding: 5px 0; 155 | } 156 | } 157 | 158 | select + .cbi-button, 159 | input[type="text"] + .cbi-button, 160 | input[type="password"] + .cbi-button { 161 | align-items: center; 162 | border-radius: 3px; 163 | display: inline-flex; 164 | margin-bottom: 5px; 165 | margin-left: 3px; 166 | text-align: center; 167 | } 168 | 169 | input[type="text"] + select { 170 | width: auto; 171 | } 172 | 173 | .cbi-value-error { 174 | /* stylelint-disable-next-line no-descending-specificity */ 175 | select, 176 | input[type="text"], 177 | input[type="password"], 178 | textarea { 179 | border-color: $color-red-lighten-10; 180 | 181 | &:focus { 182 | @include input-shadow($color-red-lighten-10); 183 | } 184 | } 185 | 186 | .cbi-value-title { 187 | color: $color-red-lighten-10; 188 | } 189 | } 190 | 191 | /* luci-app-statistics after 11.09.2020 */ 192 | .statistics-graph-controls { 193 | display: flex; 194 | width: 100%; 195 | flex-wrap: wrap; 196 | justify-content: space-between; 197 | 198 | div { 199 | display: flex; 200 | flex-basis: 100%; 201 | flex-direction: row; 202 | } 203 | 204 | button { 205 | min-width: 200px; 206 | margin-left: 8px; 207 | } 208 | 209 | select { 210 | max-width: 100% !important; 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/styles/labels.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | span.label { 5 | border: 0; 6 | border-radius: $border-radius; 7 | margin: 0; 8 | padding: 2px 6px; 9 | box-shadow: 0 1px 2px 0 #808080; 10 | 11 | @include label($color-gray-lighten-30, $color-gray-lighten-70); 12 | 13 | &.notice, 14 | &.info { 15 | @include label($color-blue-darken-10, $color-blue-lighten-45); 16 | } 17 | 18 | &.warning { 19 | @include label($color-yellow-darken-25, $color-yellow-lighten-32); 20 | } 21 | 22 | &.success { 23 | @include label($color-green-darken-5, $color-green-lighten-60); 24 | } 25 | 26 | &.error, 27 | &.danger { 28 | @include label($color-red-darken-5, $color-red-lighten-40); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/styles/layout.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | * { 5 | box-sizing: border-box; 6 | } 7 | 8 | html { 9 | color: $color-gray-base; 10 | font-family: $base-font-family; 11 | font-size: $base-font-size; 12 | min-height: 100vh; 13 | } 14 | 15 | body { 16 | display: flex; 17 | flex-direction: column; 18 | line-height: $base-line-height; 19 | min-height: 100vh; 20 | 21 | &.menu-opened { 22 | overflow: hidden; 23 | } 24 | } 25 | 26 | h1 { 27 | @include h(1.6); 28 | } 29 | 30 | h2 { 31 | @include h(1.5); 32 | } 33 | 34 | h3 { 35 | @include h(1.4); 36 | } 37 | 38 | h4 { 39 | @include h(1.3); 40 | } 41 | 42 | h5 { 43 | @include h(1.2); 44 | } 45 | 46 | h6 { 47 | @include h(1.1); 48 | } 49 | 50 | hr { 51 | background-color: $color-gray-lighten-70; 52 | border: none; 53 | color: $color-gray-lighten-70; 54 | height: 1px; 55 | } 56 | 57 | em { 58 | font-style: italic; 59 | } 60 | 61 | strong { 62 | font-weight: bold; 63 | } 64 | 65 | code, 66 | pre, 67 | tt { 68 | font-family: monospace; 69 | } 70 | 71 | pre { 72 | font-family: monospace; 73 | padding: 5px 8px; 74 | border: solid 1px $color-gray-lighten-70; 75 | background-color: $color-gray-lighten-75; 76 | border-radius: $border-radius; 77 | font-size: $input-font-size; 78 | } 79 | 80 | a { 81 | color: $color-blue-base; 82 | font-weight: bold; 83 | text-decoration: none; 84 | 85 | &:hover { 86 | color: $color-blue-lighten-10; 87 | } 88 | } 89 | 90 | abbr { 91 | cursor: help; 92 | } 93 | 94 | p { 95 | margin: 1rem 0; 96 | } 97 | 98 | ul { 99 | list-style-type: circle; 100 | } 101 | 102 | ol { 103 | list-style-type: decimal; 104 | } 105 | 106 | ul, 107 | ol { 108 | margin-left: 20px; 109 | } 110 | 111 | .container { 112 | padding: 0 $main-content-padding; 113 | width: 100%; 114 | 115 | @media screen and (min-width: $breakpoint-lg) { 116 | margin: 0 auto; 117 | padding: 0; 118 | width: $content-width; 119 | } 120 | } 121 | 122 | .header, 123 | .content, 124 | .footer { 125 | width: 100%; 126 | } 127 | 128 | .content { 129 | flex-grow: 1; 130 | margin-top: $header-full-height; 131 | 132 | .container { 133 | display: flex; 134 | } 135 | } 136 | 137 | .footer { 138 | align-items: center; 139 | background-color: #ffffff; 140 | border-top: solid 1px $color-gray-lighten-70; 141 | display: flex; 142 | flex-direction: column; 143 | height: $header-controls-height; 144 | justify-content: center; 145 | margin-top: 50px; 146 | text-align: center; 147 | z-index: 1; 148 | 149 | .container { 150 | width: auto; 151 | } 152 | 153 | .content { 154 | color: $color-gray-lighten-60; 155 | justify-content: center; 156 | 157 | a { 158 | text-decoration: none; 159 | transition: $transition-time; 160 | } 161 | } 162 | 163 | @media screen and (min-width: $breakpoint-lg) { 164 | z-index: 3; 165 | } 166 | } 167 | 168 | .header { 169 | background-color: #ffffff; 170 | box-shadow: 0 -2px 5px 3px $color-gray-lighten-50; 171 | position: fixed; 172 | z-index: 3; 173 | 174 | .header-controls { 175 | align-items: center; 176 | display: flex; 177 | flex-direction: row-reverse; 178 | flex-wrap: wrap; 179 | height: $header-controls-height; 180 | justify-content: space-between; 181 | 182 | @media screen and (min-width: $breakpoint-lg) { 183 | flex-direction: row; 184 | flex-wrap: nowrap; 185 | } 186 | } 187 | 188 | .menu-icon { 189 | cursor: pointer; 190 | display: block; 191 | height: $sidemenu-toggle-icon-size; 192 | width: $sidemenu-toggle-icon-size; 193 | 194 | @media screen and (min-width: $breakpoint-lg) { 195 | display: none; 196 | } 197 | } 198 | } 199 | 200 | .content-wrapper, 201 | .main-content { 202 | flex-grow: 1; 203 | position: relative; 204 | } 205 | 206 | .dn { 207 | display: none; 208 | } 209 | 210 | .main-content { 211 | padding: $main-content-padding 0 $main-content-padding * 2 0; 212 | width: 100%; 213 | 214 | @media screen and (min-width: $breakpoint-lg) { 215 | margin-left: $menu-width; 216 | width: calc(100% - #{$menu-width}); 217 | padding: 20px 0 0 20px; 218 | } 219 | } 220 | 221 | /* Fix wrapping in UI error messages */ 222 | .main-content > .alert-message.fade-in > div > pre { 223 | white-space: pre-wrap; 224 | } 225 | 226 | /* Login page customization */ 227 | [data-path="login"] { 228 | .main-content { 229 | /* No left margin on login page */ 230 | margin-left: 0; 231 | } 232 | } 233 | 234 | .left { 235 | text-align: left !important; 236 | justify-content: flex-start !important; 237 | } 238 | 239 | .right { 240 | text-align: right !important; 241 | justify-content: flex-end !important; 242 | } 243 | 244 | .center { 245 | text-align: center !important; 246 | justify-content: center !important; 247 | } 248 | 249 | .top { 250 | vertical-align: top !important; 251 | } 252 | 253 | .middle { 254 | vertical-align: middle !important; 255 | } 256 | 257 | .bottom { 258 | vertical-align: bottom !important; 259 | } 260 | 261 | .nowrap { 262 | white-space: nowrap; 263 | } 264 | 265 | .logo { 266 | align-items: center; 267 | display: flex; 268 | 269 | img { 270 | height: $logo-size; 271 | } 272 | } 273 | 274 | .hidden { 275 | display: none !important; 276 | } 277 | 278 | .additional-info { 279 | align-items: center; 280 | background-color: $color-gray-lighten-75; 281 | border-top: solid 1px $color-gray-lighten-70; 282 | display: flex; 283 | height: $header-additional-info-height; 284 | 285 | .container { 286 | align-items: center; 287 | display: flex; 288 | justify-content: space-between; 289 | white-space: nowrap; 290 | } 291 | 292 | .breadcrumbs-wrapper { 293 | margin-right: $header-additional-info-spacing; 294 | overflow-x: auto; 295 | } 296 | 297 | .hostname-container { 298 | display: flex; 299 | flex-direction: row; 300 | align-items: center; 301 | 302 | .ramdisk-indicator { 303 | height: 18px; 304 | line-height: 18px; 305 | margin-left: 8px; 306 | color: #000000; 307 | background-color: #ffe998; 308 | padding: 0px 6px; 309 | border-radius: $border-radius; 310 | border: 1px solid #aaaaaa; 311 | font-size: 90%; 312 | cursor: help; 313 | } 314 | 315 | .hostname { 316 | align-items: center; 317 | display: flex; 318 | font-family: monospace; 319 | font-size: $input-font-size; 320 | margin-left: $header-additional-info-spacing; 321 | overflow-x: auto; 322 | 323 | .title { 324 | color: $color-gray-lighten-50; 325 | font-family: $base-font-family; 326 | margin-right: 5px; 327 | } 328 | } 329 | } 330 | } 331 | 332 | span[data-clickable="true"] { 333 | cursor: pointer; 334 | } 335 | 336 | 337 | div.header #indicators { 338 | display: flex; 339 | flex-grow: 1; 340 | align-items: center; 341 | justify-content: flex-start; 342 | 343 | @media screen and (min-width: $breakpoint-lg) { 344 | justify-content: flex-end; 345 | } 346 | 347 | span[data-indicator] { 348 | margin-left: 20px; 349 | margin-right: 0; 350 | 351 | @media screen and (min-width: $breakpoint-lg) { 352 | margin-left: 20px; 353 | margin-right: 0; 354 | } 355 | 356 | &[data-indicator="poll-status"] { 357 | order: 1; 358 | @media screen and (min-width: $breakpoint-lg) { 359 | order: 2; 360 | } 361 | 362 | .xhr-poll-status { 363 | display: flex; 364 | flex-flow: row; 365 | flex-wrap: nowrap; 366 | align-items: center; 367 | 368 | .xhr-poll-label { 369 | display: none; 370 | @media screen and (min-width: $breakpoint-sm) { 371 | color: $color-gray-lighten-30; 372 | display: block; 373 | font-weight: bold; 374 | margin-left: 8px; 375 | } 376 | } 377 | } 378 | } 379 | 380 | &[data-indicator="uci-changes"] { 381 | order: 2; 382 | @media screen and (min-width: $breakpoint-lg) { 383 | order: 1; 384 | } 385 | align-items: center; 386 | color: #fff; 387 | display: flex; 388 | font-weight: bold; 389 | text-decoration: none; 390 | background-color: $color-orange-base; 391 | border-radius: 10px; 392 | padding: 2px 10px; 393 | box-shadow: 0 1px 3px 0 grey; 394 | 395 | &:hover { 396 | background-color: #ff9900; 397 | } 398 | 399 | .uci-changes-label { 400 | display: none; 401 | margin-right: 4px; 402 | @media screen and (min-width: $breakpoint-sm) { 403 | display: inline-block; 404 | } 405 | } 406 | } 407 | 408 | &[data-clickable="true"] { 409 | cursor: pointer; 410 | } 411 | } 412 | } 413 | 414 | .xhr-poll-status-active, 415 | .xhr-poll-status-inactive { 416 | align-items: center; 417 | color: #ffffff; 418 | cursor: pointer; 419 | display: flex; 420 | justify-content: center; 421 | position: relative; 422 | width: 48px; 423 | 424 | .bar { 425 | border-radius: 8px; 426 | height: 16px; 427 | width: 100%; 428 | } 429 | 430 | .circle { 431 | align-items: center; 432 | background-color: #ffffff; 433 | border: solid 1px $color-gray-lighten-60; 434 | border-radius: 50%; 435 | box-shadow: 0 1px 3px 0 $color-gray-lighten-30; 436 | display: flex; 437 | height: 28px; 438 | justify-content: center; 439 | position: absolute; 440 | width: 28px; 441 | top: -6px; 442 | 443 | svg { 444 | height: 18px; 445 | width: 18px; 446 | } 447 | } 448 | } 449 | 450 | .xhr-poll-status-inactive { 451 | .bar { 452 | background-color: $color-gray-lighten-30; 453 | border: solid 1px $color-gray-lighten-30; 454 | } 455 | 456 | .circle { 457 | left: 0; 458 | 459 | svg { 460 | path { 461 | fill: $color-gray-lighten-30; 462 | } 463 | } 464 | } 465 | } 466 | 467 | .xhr-poll-status-active { 468 | .bar { 469 | background-color: $color-green-base; 470 | border: solid 1px $color-green-base; 471 | } 472 | 473 | .circle { 474 | right: 0; 475 | 476 | svg { 477 | path { 478 | fill: $color-green-base; 479 | } 480 | } 481 | } 482 | } 483 | 484 | .add-item { 485 | display: flex; 486 | } 487 | 488 | .tano-copyright { 489 | color: $color-gray-lighten-20; 490 | text-align: center; 491 | } 492 | 493 | #cbi-system-system { 494 | .cbi-value-field { 495 | display: flex; 496 | flex-direction: row; 497 | 498 | #localtime { 499 | background-color: $color-gray-lighten-70; 500 | border: solid 1px $color-gray-lighten-60; 501 | border-radius: $border-radius; 502 | color: $color-gray-lighten-20; 503 | padding: 8px 10px; 504 | margin-bottom: 5px; 505 | text-align: center; 506 | } 507 | } 508 | } 509 | 510 | #cbi-ddns { 511 | .cbi-section { 512 | .cbi-section-node { 513 | .cbi-tabcontainer { 514 | &[data-tab="logview"] { 515 | & > .cbi-value { 516 | display: block; 517 | } 518 | } 519 | } 520 | } 521 | } 522 | } 523 | 524 | .net-diag-output { 525 | width: 100%; 526 | overflow-x: auto; 527 | } 528 | 529 | #diag-rc-output { 530 | width: 100%; 531 | 532 | pre { 533 | background-color: $color-gray-lighten-75; 534 | border: solid 1px $color-gray-lighten-60; 535 | border-radius: $border-radius; 536 | overflow: auto; 537 | padding: 5px 10px; 538 | } 539 | } 540 | 541 | .control-group:not(.cbi-page-actions) { 542 | display: flex; 543 | flex-wrap: wrap; 544 | flex-basis: 100%; 545 | 546 | button, 547 | .cbi-button { 548 | margin-right: 4px; 549 | margin-bottom: 5px; 550 | } 551 | } 552 | -------------------------------------------------------------------------------- /src/styles/luci-app-nlbwmon.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | div.cbi-section[data-tab="traffic"], 5 | div.cbi-section[data-tab="layer7"], 6 | div.cbi-section[data-tab="ipv6"] { 7 | > div.head { 8 | margin-bottom: 20px; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/styles/luci-app-opkg.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | /* luci-app-opkg customization */ 5 | [data-path="admin-system-opkg"] { 6 | #modal_overlay { 7 | .modal { 8 | label { 9 | margin: 8px 0; 10 | 11 | > input[type="checkbox"] { 12 | vertical-align: middle; 13 | margin-right: 4px; 14 | height: inherit; 15 | } 16 | } 17 | 18 | ul { 19 | width: auto; 20 | 21 | &.errors { 22 | margin-bottom: 16px; 23 | } 24 | 25 | &.deps { 26 | padding-right: 16px; 27 | 28 | li { 29 | > span { 30 | white-space: normal; 31 | } 32 | } 33 | 34 | span.label { 35 | font-weight: bold; 36 | font-size: 80%; 37 | text-transform: uppercase; 38 | padding: 0px 6px; 39 | margin-left: 4px; 40 | } 41 | } 42 | } 43 | } 44 | } 45 | 46 | .main-content { 47 | .table { 48 | ins { 49 | background-color: #cceeff; 50 | } 51 | } 52 | 53 | .controls { 54 | justify-content: space-between; 55 | > div { 56 | align-self: flex-start; 57 | padding: 0 0 10px 0; 58 | 59 | /* Free space */ 60 | &:first-child { 61 | order: 1; 62 | } 63 | 64 | /* Filter */ 65 | &:nth-child(2) { 66 | order: 4; 67 | flex-basis: 100%; 68 | } 69 | 70 | /* Install URL */ 71 | &:nth-child(3) { 72 | order: 3; 73 | } 74 | 75 | &:nth-child(2), 76 | &:nth-child(3) { 77 | flex-basis: 100%; 78 | 79 | > button { 80 | flex-basis: 120px; 81 | flex-grow: 0; 82 | } 83 | } 84 | 85 | /* Controls */ 86 | &:nth-child(4) { 87 | order: 2; 88 | margin-right: -0.125em; 89 | 90 | > button { 91 | margin-bottom: 5px; 92 | } 93 | 94 | &::after { 95 | content: '\00a0'; 96 | } 97 | } 98 | 99 | > button { 100 | margin-bottom: 0; 101 | } 102 | 103 | &#pager { 104 | > button { 105 | flex-grow: 0; 106 | flex-basis: 50px; 107 | } 108 | } 109 | } 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/styles/menu.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | 3 | #menu-json { 4 | display: none; 5 | } 6 | 7 | .menu-overlay { 8 | background-color: $color-gray-base; 9 | bottom: 0; 10 | left: 0; 11 | opacity: 0.8; 12 | position: fixed; 13 | right: 0; 14 | top: $header-full-height; 15 | z-index: 2; 16 | 17 | @media screen and (min-width: $breakpoint-lg) { 18 | display: none; 19 | } 20 | } 21 | 22 | .menu { 23 | background-color: #ffffff; 24 | border-right: solid 1px $color-gray-lighten-70; 25 | bottom: 0; 26 | left: 0; 27 | overflow-y: auto; 28 | padding: 10px 0; 29 | position: fixed; 30 | top: $header-full-height; 31 | width: $menu-width; 32 | z-index: 2; 33 | 34 | @media screen and (min-width: $breakpoint-lg) { 35 | display: block; 36 | height: 100%; 37 | padding: 10px 0; 38 | position: static; 39 | } 40 | } 41 | 42 | #sidemenu { 43 | &.dn { 44 | display: none; 45 | } 46 | 47 | @media screen and (min-width: $breakpoint-lg) { 48 | bottom: 0; 49 | position: fixed; 50 | top: $header-full-height; 51 | z-index: 2; 52 | 53 | &.dn { 54 | display: block; 55 | } 56 | } 57 | } 58 | 59 | .menu a { 60 | font-weight: normal; 61 | } 62 | 63 | .menu li, 64 | .menu ul { 65 | list-style-type: none; 66 | margin: 0; 67 | } 68 | 69 | .menu ul { 70 | display: none; 71 | 72 | &:first-child { 73 | padding-left: 0; 74 | } 75 | } 76 | 77 | .menu-label-wrapper { 78 | align-items: center; 79 | cursor: pointer; 80 | display: flex; 81 | padding: 5px 10px; 82 | 83 | .menu-label { 84 | color: $color-gray-lighten-20; 85 | cursor: pointer; 86 | display: block; 87 | line-height: $base-line-height; 88 | text-decoration: none; 89 | transition: 0.3s; 90 | width: 100%; 91 | 92 | &.active, 93 | &:hover { 94 | color: $color-blue-base; 95 | } 96 | 97 | &.active { 98 | font-weight: bolder; 99 | } 100 | } 101 | 102 | .icon { 103 | border-bottom: solid $menu-icon-size transparent; 104 | border-left: solid $menu-icon-size $color-gray-lighten-60; 105 | border-right: solid $menu-icon-size transparent; 106 | border-top: solid $menu-icon-size transparent; 107 | display: block; 108 | height: 0; 109 | margin-right: $menu-icon-size; 110 | transform-origin: 25% 50%; 111 | transition: $transition-time; 112 | width: 0; 113 | 114 | &.placeholder { 115 | border: none; 116 | width: $menu-icon-size * 2; 117 | } 118 | } 119 | } 120 | 121 | .menu-container { 122 | &.open > .menu-label-wrapper > .icon { 123 | transform: rotate(90deg); 124 | } 125 | } 126 | 127 | ul.expanded { 128 | display: block; 129 | padding-left: 16px; 130 | } 131 | -------------------------------------------------------------------------------- /src/styles/mixins.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | 3 | @mixin h($font-coefficient) { 4 | font-size: $base-font-size * $font-coefficient; 5 | font-weight: bold; 6 | line-height: $base-font-size * $font-coefficient * 1.3; 7 | margin: ceil($font-coefficient * 7px) 0; 8 | } 9 | 10 | @mixin button($bg-color, $bg-hover-color, $bg-active-color) { 11 | background-color: $bg-color; 12 | 13 | &:hover { 14 | background-color: $bg-hover-color; 15 | } 16 | 17 | &:active { 18 | background-color: $bg-active-color; 19 | } 20 | } 21 | 22 | @mixin alert($color, $bg-color, $border-color) { 23 | background-color: $bg-color; 24 | border-color: $border-color; 25 | color: $color; 26 | } 27 | 28 | @mixin tooltip-shadow { 29 | box-shadow: 0 1px 3px 0 $color-gray-lighten-30; 30 | } 31 | 32 | @mixin input-shadow($shadow-color: $color-blue-base) { 33 | box-shadow: 0 0 0 1px $shadow-color; 34 | } 35 | 36 | @mixin label($color, $bg-color) { 37 | background-color: $bg-color; 38 | color: $color; 39 | } 40 | -------------------------------------------------------------------------------- /src/styles/modal.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | 3 | #modal_overlay { 4 | background: rgba(0, 0, 0, 0.7); 5 | bottom: 0; 6 | left: -10000px; 7 | opacity: 0; 8 | overflow-y: auto; 9 | position: fixed; 10 | right: 10000px; 11 | top: 0; 12 | transition: opacity 0.125s ease-in; 13 | z-index: 900; 14 | } 15 | 16 | .modal { 17 | align-items: center; 18 | background-color: #ffffff; 19 | border-radius: $border-radius * 2.5; 20 | display: block; 21 | margin: 5em auto; 22 | max-width: $content-width * 0.9; 23 | min-height: 32px; 24 | min-width: $content-width / 5; 25 | padding: 1.2em 1em 1.2em 1em; 26 | width: 90vw; 27 | box-shadow: 0px 5px 10px 0px rgba(0, 0, 0, 0.5); 28 | 29 | pre, 30 | textarea { 31 | overflow: auto; 32 | white-space: pre-wrap; 33 | } 34 | 35 | & > pre, 36 | & > textarea { 37 | margin-bottom: 1em; 38 | } 39 | 40 | & > * { 41 | display: block; 42 | width: 100%; 43 | line-height: normal; 44 | 45 | &.table { 46 | display: table; 47 | } 48 | } 49 | 50 | & > h4 { 51 | margin-top: 0; 52 | } 53 | } 54 | 55 | body.modal-overlay-active { 56 | height: 100vh; 57 | overflow: hidden; 58 | 59 | #modal_overlay { 60 | left: 0; 61 | opacity: 1; 62 | right: 0; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/styles/network-widgets.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | div[id *= "-ifc-description"] { 5 | display: flex; 6 | flex-flow: column; 7 | 8 | > br { 9 | display: none; 10 | } 11 | } 12 | 13 | .ifacebadge { 14 | align-items: center; 15 | background-color: $color-gray-lighten-75; 16 | border: solid 1px $color-gray-lighten-60; 17 | border-radius: 5px; 18 | display: flex; 19 | flex-direction: row; 20 | padding: 0 8px; 21 | white-space: nowrap; 22 | 23 | img { 24 | align-self: center; 25 | height: auto !important; 26 | margin: 5px 10px; 27 | width: auto !important; 28 | } 29 | 30 | > small { 31 | line-height: $base-font-size; 32 | margin: 5px 10px; 33 | text-align: center; 34 | white-space: nowrap; 35 | 36 | > img { 37 | margin-bottom: 0; 38 | margin-top: 0; 39 | } 40 | } 41 | 42 | > span { 43 | padding: 5px 10px; 44 | } 45 | 46 | &.port-status-link { 47 | border: none; 48 | padding: 0; 49 | 50 | > img { 51 | margin-left: 0; 52 | } 53 | } 54 | } 55 | 56 | #cbi-wireless-wifi-device { 57 | .ifacebadge { 58 | > span { 59 | padding: 0; 60 | } 61 | } 62 | } 63 | 64 | /* Interface badges in the table on network devices page 65 | * Interface badges in the bridge VLAN table */ 66 | #cbi-network-device td > .ifacebadge, 67 | #cbi-network-bridge-vlan .ifacebadge.port-status-device { 68 | display: inline-block; 69 | width: auto; 70 | border: 0; 71 | padding: 0 10px; 72 | 73 | @include tooltip-shadow; 74 | 75 | > span { 76 | padding: 0; 77 | } 78 | 79 | > .cbi-tooltip-container { 80 | padding: 0; 81 | } 82 | 83 | > img, 84 | .cbi-tooltip-container > img { 85 | margin-right: 10px; 86 | margin-left: 0px; 87 | } 88 | } 89 | 90 | .table.assoclist .tr:not(.table-titles) .td { 91 | vertical-align: top; 92 | > .ifacebadge { 93 | white-space: normal; 94 | > span { 95 | line-height: 1.2; 96 | padding: 4px 0; 97 | } 98 | } 99 | 100 | &:nth-child(1) { 101 | white-space: normal; 102 | } 103 | 104 | &:nth-child(5) { 105 | white-space: nowrap; 106 | } 107 | } 108 | 109 | .cbi-value-field > .ifacebadge { 110 | width: 100%; 111 | } 112 | 113 | .ifacebox { 114 | @include tooltip-shadow; 115 | 116 | user-select: none; 117 | background-color: white; 118 | display: flex; 119 | flex-direction: column; 120 | margin: $ifacebox-margin; 121 | overflow: hidden; 122 | border-radius: $border-radius; 123 | 124 | .ifacebadge { 125 | border: none; 126 | border-radius: 0; 127 | display: flex; 128 | flex-direction: row-reverse; 129 | justify-content: space-between; 130 | padding: 0; 131 | } 132 | } 133 | 134 | .ifacebox-head { 135 | background-color: $color-gray-lighten-75; 136 | padding: 3px; 137 | text-align: center; 138 | 139 | &.active { 140 | strong { 141 | &::after { 142 | background-color: $color-green-lighten-10; 143 | border-radius: 5px; 144 | content: ""; 145 | display: inline-block; 146 | height: 10px; 147 | margin-left: 5px; 148 | width: 10px; 149 | } 150 | } 151 | } 152 | } 153 | 154 | .ifacebox-body { 155 | display: flex; 156 | flex-grow: 1; 157 | flex-wrap: wrap; 158 | justify-content: center; 159 | text-align: center; 160 | 161 | small { 162 | width: 100%; 163 | } 164 | 165 | & > span { 166 | padding: 0 5px; 167 | } 168 | 169 | &.left { 170 | flex-direction: column; 171 | justify-content: flex-start; 172 | } 173 | 174 | .cbi-tooltip { 175 | display: none; 176 | 177 | @media screen and (min-width: $breakpoint-md) { 178 | display: flex; 179 | } 180 | } 181 | } 182 | 183 | #upstream_status_table, 184 | #wifi_status_table, 185 | .network-status-table { 186 | display: flex; 187 | flex-direction: column; 188 | flex-wrap: wrap; 189 | justify-content: space-between; 190 | 191 | @media screen and (min-width: $breakpoint-sm) { 192 | flex-direction: row; 193 | } 194 | 195 | .ifacebox { 196 | flex-grow: 1; 197 | 198 | @media screen and (min-width: $breakpoint-sm) { 199 | flex-basis: 48%; 200 | flex-grow: 0; 201 | } 202 | } 203 | 204 | .ifacebox-body { 205 | & > span { 206 | order: 1; 207 | padding: 10px; 208 | } 209 | } 210 | } 211 | 212 | .zonebadge { 213 | align-items: center; 214 | border-radius: $border-radius; 215 | display: flex; 216 | padding: 3px 8px; 217 | 218 | .ifacebadge { 219 | background: transparent; 220 | border: none; 221 | } 222 | } 223 | 224 | /* Zone badges in firewall rules table cells */ 225 | #cbi-firewall .td .zonebadge { 226 | display: inline-block; 227 | padding: 0px 4px; 228 | line-height: $base-line-height * 0.9; 229 | 230 | &.zonebadge-empty { 231 | padding: 0; 232 | } 233 | } 234 | 235 | .zone-forwards { 236 | display: flex; 237 | 238 | & > span { 239 | padding: 0 4px; 240 | } 241 | } 242 | 243 | #cbi-network-interface .table .td { 244 | vertical-align: top !important; 245 | } 246 | -------------------------------------------------------------------------------- /src/styles/spinner.scss: -------------------------------------------------------------------------------- 1 | .spinning { 2 | padding-left: 32px !important; 3 | position: relative; 4 | } 5 | 6 | .spinning::before { 7 | background: url(../resources/icons/loading.gif) no-repeat center; 8 | background-size: 16px; 9 | bottom: 0; 10 | content: " "; 11 | left: 0; 12 | position: absolute; 13 | top: 0; 14 | width: 32px; 15 | } 16 | -------------------------------------------------------------------------------- /src/styles/tables.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | 3 | .table-wrapper, 4 | .graph-wrapper { 5 | overflow-x: auto; 6 | 7 | @media screen and (min-width: $breakpoint-md) { 8 | overflow-x: inherit; 9 | } 10 | } 11 | 12 | .table { 13 | border-collapse: collapse; 14 | display: table; 15 | margin-bottom: 16px; 16 | overflow-x: auto; 17 | position: relative; 18 | width: 100%; 19 | 20 | input[type="text"], 21 | input[type="password"], 22 | select { 23 | width: 100%; 24 | } 25 | } 26 | 27 | .td, 28 | .th { 29 | display: table-cell; 30 | line-height: $base-line-height; 31 | min-width: 120px; 32 | padding: 8px; 33 | vertical-align: middle; 34 | text-align: left; 35 | 36 | @media screen and (min-width: $breakpoint-md) { 37 | min-width: auto; 38 | } 39 | 40 | &[width="33%"] { 41 | width: 33%; 42 | } 43 | 44 | &.cbi-value-field var { 45 | color: $color-blue-base; 46 | font-style: italic; 47 | } 48 | } 49 | 50 | .td { 51 | border-top: solid 1px $color-gray-lighten-70 !important; 52 | } 53 | 54 | .th { 55 | background-color: $color-gray-lighten-70; 56 | font-weight: bold; 57 | } 58 | 59 | th[data-sortable-row] { 60 | cursor: pointer; 61 | 62 | &[data-sort-direction="asc"]::after { 63 | content: "\a0\25b2"; 64 | } 65 | 66 | &[data-sort-direction="desc"]::after { 67 | content: "\a0\25bc"; 68 | } 69 | } 70 | 71 | .tr { 72 | display: table-row; 73 | user-select: auto; 74 | 75 | &.placeholder { 76 | height: 30px; 77 | 78 | > .td { 79 | font-style: italic; 80 | left: 0; 81 | position: absolute; 82 | right: 0; 83 | text-align: center; 84 | } 85 | } 86 | 87 | &.cbi-rowstyle-2 { 88 | background-color: #f8f8f8; 89 | } 90 | } 91 | 92 | .cbi-section-table-descr { 93 | background-color: $color-gray-lighten-75; 94 | .th { 95 | background-color: $color-gray-lighten-75; 96 | color: $color-gray-lighten-20; 97 | font-size: $base-font-size * 0.85; 98 | line-height: $base-line-height * 0.85; 99 | padding: 5px 8px; 100 | } 101 | } 102 | 103 | .cbi-section-table-titles { 104 | background-color: $color-gray-lighten-70; 105 | } 106 | 107 | .cbi-section-table-row[data-title], 108 | .cbi-section-table-titles.named, 109 | .cbi-section-table-descr.named { 110 | &::before { 111 | border-top: solid 1px $color-gray-lighten-70; 112 | content: attr(data-title); 113 | display: table-cell; 114 | font-weight: bold; 115 | padding: 0 5px; 116 | vertical-align: middle; 117 | } 118 | } 119 | 120 | .td.cbi-value-field { 121 | display: table-cell; 122 | width: auto; 123 | } 124 | 125 | .tr.drag-over-above, 126 | .tr.drag-over-below { 127 | border: 2px solid $color-blue-base; 128 | border-width: 2px 0 0 0; 129 | } 130 | 131 | .tr.drag-over-below { 132 | border-width: 0 0 2px 0; 133 | } 134 | 135 | /* 136 | * Small fixes for tables that are embedded in value fields. 137 | * This can be found, for example, in the settings of the 138 | * "Exec" plugin of the luci-app-statistics. 139 | */ 140 | .cbi-value-field { 141 | .cbi-section.cbi-tblsection { 142 | margin-top: 10px; 143 | 144 | .table-wrapper { 145 | width: 100%; 146 | 147 | .table { 148 | margin-bottom: 0; 149 | } 150 | } 151 | } 152 | 153 | .cbi-section-create.cbi-tblsection-create { 154 | width: 100%; 155 | margin-top: 8px; 156 | margin-bottom: 8px; 157 | } 158 | } 159 | 160 | /* Tweaks for WireGuard peers table */ 161 | div[data-name="_peers"] { 162 | .ifacebadge { 163 | display: inline; 164 | } 165 | 166 | .cbi-value-field>:not([id]):not([class]):first-child { 167 | padding-top: 0; 168 | } 169 | 170 | table > tr > td { 171 | vertical-align: top; 172 | 173 | /* Peer description (title) */ 174 | > p { 175 | margin: 0; 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/styles/tabs.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | 3 | [data-tab-title] { 4 | display: none; 5 | } 6 | 7 | [data-tab-active="true"] { 8 | display: block; 9 | } 10 | 11 | [data-tab-active="false"] { 12 | display: none; 13 | } 14 | 15 | .cbi-tabmenu, 16 | .tabs { 17 | display: flex; 18 | flex-wrap: wrap; 19 | margin: 10px 0; 20 | padding: 0; 21 | 22 | li { 23 | align-items: center; 24 | border-bottom: solid 3px $color-gray-lighten-70; 25 | display: inline-flex; 26 | flex-grow: 1; 27 | justify-content: center; 28 | margin: 4px 1px; 29 | text-align: center; 30 | transition: $transition-time; 31 | 32 | a { 33 | color: $color-gray-lighten-30; 34 | cursor: pointer; 35 | display: block; 36 | font-size: $base-font-size * 1.05; 37 | font-weight: normal; 38 | line-height: $base-line-height; 39 | padding: 8px 10px; 40 | text-decoration: none; 41 | transition: $transition-time; 42 | white-space: normal; 43 | width: 100%; 44 | } 45 | 46 | &[data-errors] { 47 | a { 48 | width: auto; 49 | } 50 | 51 | &::after { 52 | background-color: $color-red-lighten-10; 53 | border-radius: $base-line-height / 2; 54 | color: #ffffff; 55 | content: attr(data-errors); 56 | height: $base-line-height; 57 | line-height: $base-line-height; 58 | padding: 0 6px; 59 | min-width: $base-line-height - 6px; 60 | } 61 | } 62 | 63 | &:hover { 64 | background-color: $color-gray-lighten-75; 65 | } 66 | 67 | &.cbi-tab, 68 | &.active { 69 | a { 70 | color: $color-blue-base; 71 | } 72 | 73 | border-color: $color-blue-base; 74 | } 75 | 76 | &.cbi-tab-disabled { 77 | &:hover { 78 | border-color: $color-gray-lighten-60; 79 | } 80 | } 81 | } 82 | 83 | & > .cbi-tabmenu { 84 | margin-top: -20px; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/styles/tooltip.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | @import "mixins"; 3 | 4 | .cbi-tooltip { 5 | background-color: #ffffff; 6 | border-radius: $border-radius; 7 | opacity: 0; 8 | padding: 3px 5px; 9 | position: absolute; 10 | transition: opacity $transition-time; 11 | z-index: 999; 12 | left: -10000px; 13 | 14 | @include tooltip-shadow; 15 | 16 | &.error { 17 | background-color: $color-red-lighten-10; 18 | border-color: $color-red-darken-10; 19 | color: #ffffff; 20 | } 21 | } 22 | 23 | .cbi-tooltip-container { 24 | cursor: help; 25 | 26 | .cbi-tooltip { 27 | border: none; 28 | cursor: default; 29 | pointer-events: none; 30 | 31 | &.ifacebadge { 32 | border-radius: $border-radius; 33 | background-color: #ffffff; 34 | flex-direction: row; 35 | } 36 | } 37 | 38 | &:hover { 39 | .cbi-tooltip { 40 | left: auto; 41 | display: flex; 42 | opacity: 1; 43 | pointer-events: all; 44 | } 45 | } 46 | 47 | /* Chain references tooltips on firewall status page */ 48 | &.references { 49 | .cbi-tooltip { 50 | li { 51 | white-space: nowrap; 52 | } 53 | ul { 54 | margin-left: 0; 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/styles/variables.scss: -------------------------------------------------------------------------------- 1 | $base-font-family: Carlito, sans-serif; 2 | $base-font-size: 14px; 3 | $base-line-height: ceil($base-font-size * 1.4); 4 | $base-font-color: #333333; 5 | 6 | $color-main: #008dd2; 7 | 8 | $color-blue-base: $color-main; 9 | $color-blue-darken-5: darken($color-blue-base, 5); 10 | $color-blue-darken-10: darken($color-blue-base, 10); 11 | $color-blue-darken-20: darken($color-blue-base, 20); 12 | $color-blue-darken-25: darken($color-blue-base, 25); 13 | $color-blue-lighten-5: lighten($color-blue-base, 5); 14 | $color-blue-lighten-10: lighten($color-blue-base, 10); 15 | $color-blue-lighten-25: lighten($color-blue-base, 25); 16 | $color-blue-lighten-30: lighten($color-blue-base, 30); 17 | $color-blue-lighten-45: lighten($color-blue-base, 45); 18 | 19 | $color-gray-base: #333333; 20 | $color-gray-lighten-10: lighten($color-gray-base, 10); 21 | $color-gray-lighten-20: lighten($color-gray-base, 20); 22 | $color-gray-lighten-30: lighten($color-gray-base, 30); 23 | $color-gray-lighten-40: lighten($color-gray-base, 40); 24 | $color-gray-lighten-50: lighten($color-gray-base, 50); 25 | $color-gray-lighten-60: lighten($color-gray-base, 60); 26 | $color-gray-lighten-65: lighten($color-gray-base, 65); 27 | $color-gray-lighten-70: lighten($color-gray-base, 70); 28 | $color-gray-lighten-75: lighten($color-gray-base, 75); 29 | 30 | $color-red-base: #d2323e; 31 | $color-red-darken-5: darken($color-red-base, 8); 32 | $color-red-darken-10: darken($color-red-base, 10); 33 | $color-red-lighten-5: lighten($color-red-base, 8); 34 | $color-red-lighten-10: lighten($color-red-base, 10); 35 | $color-red-lighten-20: lighten($color-red-base, 20); 36 | $color-red-lighten-30: lighten($color-red-base, 30); 37 | $color-red-lighten-40: lighten($color-red-base, 40); 38 | 39 | $color-orange-base: #ff8800; 40 | $color-orange-lighten-10: lighten($color-orange-base, 10); 41 | $color-orange-lighten-30: lighten($color-orange-base, 30); 42 | $color-orange-lighten-40: lighten($color-orange-base, 40); 43 | $color-orange-darken-10: darken($color-orange-base, 10); 44 | 45 | $color-yellow-base: #f4bf00; 46 | $color-yellow-lighten-32: lighten($color-yellow-base, 32); 47 | $color-yellow-darken-25: darken($color-yellow-base, 25); 48 | 49 | $color-green-base: #018a00; 50 | $color-green-darken-5: darken($color-green-base, 5); 51 | $color-green-darken-10: darken($color-green-base, 10); 52 | $color-green-lighten-5: lighten($color-green-base, 5); 53 | $color-green-lighten-10: lighten($color-green-base, 10); 54 | $color-green-lighten-20: lighten($color-green-base, 20); 55 | $color-green-lighten-30: lighten($color-green-base, 30); 56 | $color-green-lighten-40: lighten($color-green-base, 40); 57 | $color-green-lighten-50: lighten($color-green-base, 50); 58 | $color-green-lighten-60: lighten($color-green-base, 60); 59 | 60 | $transition-time: 0.3s; 61 | 62 | $border-radius: 5px; 63 | 64 | $content-width: 1160px; 65 | 66 | $menu-width: 250px; 67 | $input-height: $base-line-height * 1.7; 68 | $main-content-padding: 10px; 69 | 70 | $cbi-title-width: ($content-width - $menu-width) / 3; 71 | $input-width: $cbi-title-width; 72 | $input-font-size: ceil($base-font-size * 0.9); 73 | 74 | $breakpoint-sm: 768px; 75 | $breakpoint-md: 1024px; 76 | $breakpoint-lg: 1200px; 77 | 78 | $menu-icon-size: 5px; 79 | 80 | $button-font-size: $base-font-size; 81 | 82 | $logo-size: 42px; 83 | $sidemenu-toggle-icon-size: ceil($logo-size * 0.8); 84 | $header-additional-info-height: 30px; 85 | $header-additional-info-spacing: 10px; 86 | $header-controls-height: $logo-size + 16px; 87 | $header-full-height: $header-controls-height + $header-additional-info-height; 88 | 89 | $header-action-icon-size: $base-font-size * 1.6; 90 | 91 | $changes-indicator-color: $color-orange-base; 92 | $changes-indicator-color-hover: $color-orange-lighten-10; 93 | 94 | $change-legend-size: 32px; 95 | 96 | $changes-add-border: solid 1px $color-green-lighten-20; 97 | $changes-add-background-color: $color-green-lighten-60; 98 | 99 | $changes-remove-border: solid 1px $color-red-lighten-30; 100 | $changes-remove-background-color: $color-red-lighten-40; 101 | 102 | $changes-option-border: solid 1px $color-gray-lighten-60; 103 | $changes-option-background-color: $color-gray-lighten-75; 104 | 105 | $cbi-dropdown-height: $input-height - 2px; 106 | $cbi-dropdown-input-height: ceil($cbi-dropdown-height * 0.8); 107 | 108 | $ifacebox-margin: 4px; 109 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const MiniCssExtractPlugin = require("mini-css-extract-plugin"); 3 | 4 | module.exports = { 5 | mode: "production", 6 | entry: { 7 | theme: path.resolve(__dirname, "src", "scripts", "index.js") 8 | }, 9 | output: { 10 | filename: "[name].js", 11 | path: path.resolve(__dirname, "dist") 12 | }, 13 | module: { 14 | rules: [ 15 | { 16 | test: /\.scss$/, 17 | use: [ 18 | { loader: MiniCssExtractPlugin.loader }, 19 | { loader: "css-loader", options: { url: false, minimize: true } }, 20 | { loader: "postcss-loader" }, 21 | { loader: "sass-loader"} 22 | ] 23 | } 24 | ] 25 | }, 26 | plugins: [ 27 | new MiniCssExtractPlugin({ 28 | filename: "cascade.css" 29 | }) 30 | ] 31 | }; 32 | --------------------------------------------------------------------------------