diff --git a/netbox/project-static/bundle.js b/netbox/project-static/bundle.js
index 69b553282..85cf3c57b 100644
--- a/netbox/project-static/bundle.js
+++ b/netbox/project-static/bundle.js
@@ -73,8 +73,8 @@ async function bundleScripts() {
async function bundleStyles() {
try {
const entryPoints = {
- 'netbox-external': 'styles/_external.scss',
- 'netbox': 'styles/_netbox.scss',
+ 'netbox-external': 'styles/external.scss',
+ 'netbox': 'styles/netbox.scss',
rack_elevation: 'styles/svg/rack_elevation.scss',
cable_trace: 'styles/svg/cable_trace.scss',
graphiql: 'netbox-graphiql/graphiql.scss',
diff --git a/netbox/project-static/dist/netbox-external.css b/netbox/project-static/dist/netbox-external.css
index 1e1873436..ff401f105 100644
Binary files a/netbox/project-static/dist/netbox-external.css and b/netbox/project-static/dist/netbox-external.css differ
diff --git a/netbox/project-static/dist/netbox.css b/netbox/project-static/dist/netbox.css
index 0183904b8..cc0218f35 100644
Binary files a/netbox/project-static/dist/netbox.css and b/netbox/project-static/dist/netbox.css differ
diff --git a/netbox/project-static/styles/_external.scss b/netbox/project-static/styles/_external.scss
deleted file mode 100644
index 2036934e0..000000000
--- a/netbox/project-static/styles/_external.scss
+++ /dev/null
@@ -1,4 +0,0 @@
-// Entry for all 3rd party library imports that do not rely on Bootstrap or NetBox styles.
-@import '../node_modules/@mdi/font/css/materialdesignicons.min.css';
-@import '../node_modules/flatpickr/dist/flatpickr.css';
-@import 'gridstack/dist/gridstack.min.css';
diff --git a/netbox/project-static/styles/_netbox.scss b/netbox/project-static/styles/_netbox.scss
deleted file mode 100644
index c37c59b5a..000000000
--- a/netbox/project-static/styles/_netbox.scss
+++ /dev/null
@@ -1,14 +0,0 @@
-@import 'variables';
-
-// Tabler
-@import '../node_modules/@tabler/core/src/scss/_core.scss';
-
-// slim-select
-@import './select.scss';
-
-// Transitional styling
-@import './transitional/cards.scss';
-@import './transitional/tables.scss';
-
-// Temporary overrides
-@import './temp.scss';
diff --git a/netbox/project-static/styles/variables.scss b/netbox/project-static/styles/_variables.scss
similarity index 89%
rename from netbox/project-static/styles/variables.scss
rename to netbox/project-static/styles/_variables.scss
index 529456cfa..b387045f3 100644
--- a/netbox/project-static/styles/variables.scss
+++ b/netbox/project-static/styles/_variables.scss
@@ -1,4 +1,8 @@
-// NetBox CSS Variables
+// Global variables
+
+// Set base fonts
+$font-google: 'Inter';
+$font-google-monospaced: 'Roboto Mono';
// Set the navigation sidebar width
$sidebar-width: 18rem;
@@ -10,6 +14,3 @@ $btn-padding-y: 0.25rem;
// Reduce the default table cell padding
$table-cell-padding-x: 0.5rem;
$table-cell-padding-y: 0.5rem;
-
-$font-google: 'Inter';
-$font-google-monospaced: 'Roboto Mono';
diff --git a/netbox/project-static/styles/bootstrap.scss b/netbox/project-static/styles/bootstrap.scss
deleted file mode 100644
index 9f4191057..000000000
--- a/netbox/project-static/styles/bootstrap.scss
+++ /dev/null
@@ -1,40 +0,0 @@
-// Import the rest of bootstrap.
-@import '../node_modules/bootstrap/scss/maps';
-@import '../node_modules/bootstrap/scss/mixins';
-@import '../node_modules/bootstrap/scss/utilities';
-@import './extensions';
-@import '../node_modules/bootstrap/scss/mixins';
-@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/accordion';
-@import '../node_modules/bootstrap/scss/breadcrumb';
-@import '../node_modules/bootstrap/scss/pagination';
-@import '../node_modules/bootstrap/scss/badge';
-@import '../node_modules/bootstrap/scss/alert';
-@import '../node_modules/bootstrap/scss/progress';
-@import '../node_modules/bootstrap/scss/list-group';
-@import '../node_modules/bootstrap/scss/close';
-@import '../node_modules/bootstrap/scss/toasts';
-@import '../node_modules/bootstrap/scss/modal';
-@import '../node_modules/bootstrap/scss/tooltip';
-@import '../node_modules/bootstrap/scss/popover';
-@import '../node_modules/bootstrap/scss/carousel';
-@import '../node_modules/bootstrap/scss/spinners';
-@import '../node_modules/bootstrap/scss/offcanvas';
-@import '../node_modules/bootstrap/scss/placeholders';
-@import '../node_modules/bootstrap/scss/spinners';
-@import '../node_modules/bootstrap/scss/helpers';
-@import '../node_modules/bootstrap/scss/utilities/api';
diff --git a/netbox/project-static/styles/extensions.scss b/netbox/project-static/styles/extensions.scss
deleted file mode 100644
index 79d19d17e..000000000
--- a/netbox/project-static/styles/extensions.scss
+++ /dev/null
@@ -1,45 +0,0 @@
-// Bootstrap utility extensions.
-// See https://getbootstrap.com/docs/5.0/utilities/api/
-
-// Add responsive max-width classes for more granular controls over max-width.
-$max-width-extension: (
- 'max-width': (
- property: max-width,
- class: mw,
- responsive: true,
- values: (
- 20: 20%,
- 25: 25%,
- 33: 33%,
- 50: 50%,
- 66: 66%,
- 75: 75%,
- 80: 80%,
- 90: 90%,
- 100: 100%,
- ),
- ),
-);
-
-// Add more opacity classes.
-$opacity-extension: (
- 'opacity': (
- property: opacity,
- class: opacity,
- values: (
- 0: 0,
- 10: 0.1,
- 20: 0.2,
- 25: 0.25,
- 33: 0.33,
- 50: 0.5,
- 66: 0.66,
- 75: 0.75,
- 80: 0.8,
- 90: 0.9,
- 100: 1,
- ),
- ),
-);
-
-$utilities: map-merge($utilities, $max-width-extension, $opacity-extension);
diff --git a/netbox/project-static/styles/external.scss b/netbox/project-static/styles/external.scss
new file mode 100644
index 000000000..729f70596
--- /dev/null
+++ b/netbox/project-static/styles/external.scss
@@ -0,0 +1,4 @@
+// Entry for all 3rd party library imports that do not rely on Tabler or NetBox styles.
+@import '../node_modules/@mdi/font/css/materialdesignicons.min.css';
+@import '../node_modules/flatpickr/dist/flatpickr.min.css';
+@import 'gridstack/dist/gridstack.min.css';
diff --git a/netbox/project-static/styles/netbox.scss b/netbox/project-static/styles/netbox.scss
index 8d0192593..35f879b1e 100644
--- a/netbox/project-static/styles/netbox.scss
+++ b/netbox/project-static/styles/netbox.scss
@@ -1,1078 +1,11 @@
-// NetBox-specific Styles and Overrides.
+@import 'variables';
-@use 'sass:map';
-@use 'sass:math';
-//@import './sidenav';
-@import './overrides';
-@import './utilities';
-@import './variables';
+// Tabler
+@import '../node_modules/@tabler/core/src/scss/_core.scss';
-@each $color, $value in $theme-colors {
- // Override CSS values on each theme color.
+// Overrides of external libraries
+@import 'overrides/slim-select';
- // Use Bootstrap's method of coloring alert links to appropriately color close buttons within
- // another colored element.
- // See: https://github.com/twbs/bootstrap/blob/2bdbb42dcf6bfb99b5e9e5444d9e64589eb8c08f/scss/_alert.scss#L50-L52
- // See: https://github.com/twbs/bootstrap/blob/2bdbb42dcf6bfb99b5e9e5444d9e64589eb8c08f/scss/_close.scss#L12
- $shifted-bg: shift-color($value, $alert-bg-scale);
- $shifted-color: shift-color($value, $alert-color-scale);
-
- @if (contrast-ratio($shifted-bg, $shifted-color) < $min-contrast-ratio) {
- $shifted-color: mix($value, color-contrast($shifted-bg), abs($alert-color-scale));
- }
-
- $btn-close-bg: url("data:image/svg+xml,");
- .bg-#{$color} button.btn-close {
- background: transparent escape-svg($btn-close-bg) center / $btn-close-width auto no-repeat;
- }
-
- .btn.btn-ghost-#{$color} {
- color: $value;
- &:hover {
- background-color: rgba($value, 0.12);
- }
- }
-
- // Use Bootstrap's method of coloring the .alert-link class automatically.
- // See: https://github.com/twbs/bootstrap/blob/2bdbb42dcf6bfb99b5e9e5444d9e64589eb8c08f/scss/_alert.scss#L50-L52
-
- .alert.alert-#{$color},
- .table-#{$color} {
- // Exclude buttons.
- a:not(.btn) {
- font-weight: $font-weight-bold;
- color: $shifted-color;
- }
- // Apply a border to buttons contained within colored elements, if they're not already a
- // bordered button class.
- .btn:not([class*='btn-outline']) {
- border-color: $gray-700;
- }
- }
-
- // Toasts required a slightly different approach because the background color isn't "shifted",
- // it's the direct theme color.
- .toast.bg-#{$color} {
- $shifted-color: shift-color($value, $alert-color-scale);
-
- @if (contrast-ratio($value, $shifted-color) < $min-contrast-ratio) {
- $shifted-color: mix($value, color-contrast($value), abs($alert-color-scale));
- }
- a:not(.btn) {
- font-weight: $font-weight-bold;
- color: $shifted-color;
- }
- }
-
- // Use proper contrasting color foreground color for special components.
- .badge,
- .toast,
- .toast-header,
- .progress-bar {
- &.bg-#{$color} {
- color: color-contrast($value);
- }
- }
-}
-
-// Ensure progress bars (utilization graph) in tables aren't too narrow to display the percentage.
-table td > .progress {
- min-width: 6rem;
-}
-
-// Override Bootstrap form-control font-size when contained by .small element.
-.small .form-control {
- font-size: $font-size-sm;
-}
-
-// Automatically space out adjacent columns, but not within card bodies.
-:not(.card-body) > .col:not(:last-child):not(:only-child) {
- margin-bottom: $spacer;
-}
-
-.nav-mobile {
- display: none;
- flex-direction: column;
- align-items: center;
- justify-content: space-between;
- width: 100%;
-
- @include media-breakpoint-down(lg) {
- display: flex;
- }
-
- .nav-mobile-top {
- display: flex;
- align-items: center;
- justify-content: space-between;
- width: 100%;
- }
-}
-
-.card > .table.table-flush {
- margin-bottom: 0;
- overflow: hidden;
- border-bottom-right-radius: $card-border-radius;
- border-bottom-left-radius: $card-border-radius;
-
- thead th[scope='col'] {
- padding-top: map.get($spacers, 3);
- padding-bottom: map.get($spacers, 3);
- text-transform: uppercase;
- vertical-align: middle;
- background-color: $table-flush-header-bg;
- border-top: 1px solid $card-border-color;
- border-bottom-color: $card-border-color;
- }
-
- th,
- td {
- padding-right: map.get($spacers, 4) !important;
- padding-left: map.get($spacers, 4) !important;
- border-right: 0;
- border-left: 0;
- }
- tr[class] {
- border-color: $card-border-color !important;
- &:last-of-type {
- border-bottom-color: transparent !important;
- border-bottom-right-radius: $card-border-radius;
- border-bottom-left-radius: $card-border-radius;
- }
- }
-}
-
-// Primarily used for the new release notification, but could be used for other alerts as needed.
-// Wrap any alerts in .header-alert-container to ensure the layout is consistent.
-.header-alert-container {
- // Center-align the alert(s).
- display: flex;
- align-items: center;
- justify-content: center;
- // Apply the same spacing that's applied to the #content div's first child (.px-3).
- padding: 0 $spacer;
-
- // By default, alerts inside .header-alert-container should take up the full width.
- .alert {
- width: 100%;
-
- // Adjust the max-width for larger screens so there's not a big ugly blue blob taking up the
- // entire screen.
- @include media-breakpoint-up(md) {
- max-width: 75%;
- }
- @include media-breakpoint-up(lg) {
- max-width: 50%;
- }
- }
-}
-
-.alert {
- code {
- color: $gray-600;
- }
-}
-
-span.profile-button .dropdown-menu {
- right: 0;
- left: auto;
- display: block !important;
- margin-top: 0.5rem;
- box-shadow: $box-shadow;
- transition: opacity 0.2s ease-in-out;
-
- &:not(.show) {
- pointer-events: none;
- opacity: 0;
- }
- &.show {
- pointer-events: auto;
- opacity: 1;
- }
-}
-
-div#advanced-search-content div.card div.card-body div.col:not(:last-child) {
- margin-right: 1rem;
-}
-
-table {
- td {
- a {
- text-decoration: none;
- &:hover {
- text-decoration: underline;
- }
- }
- .dropdown {
- // Presence of 'overflow: scroll' on a table causes dropdowns to be improperly hidden when
- // opened. See: https://github.com/twbs/bootstrap/issues/24251
- position: static;
- }
- }
- th {
- a,
- a:hover {
- color: $body-color;
- text-decoration: none;
- }
- }
-
- td,
- th {
- font-size: $font-size-sm;
- line-height: $line-height-sm;
- vertical-align: middle;
- &.min-width {
- width: 1%;
- }
- .form-check-input {
- // Ensure checkboxes aren't too small inside object tables.
- margin-top: 0.125em;
- font-size: $font-size-base;
- }
-
- .btn-sm {
- line-height: $line-height-xs;
- }
-
- p {
- // Remove spacing from paragraph elements within tables.
- margin-bottom: 0.5em;
- }
-
- p:last-child {
- margin-bottom: 0;
- }
- }
-
- th.asc > a::after {
- content: '\f0140';
- font-family: 'Material Design Icons';
- }
-
- th.desc > a::after {
- content: '\f0143';
- font-family: 'Material Design Icons';
- }
-
- &.table > :not(caption) > * > * {
- padding-right: $table-cell-padding-x-sm !important;
- padding-left: $table-cell-padding-x-sm !important;
- }
-
- &.object-list {
- th {
- font-size: $font-size-xs;
- line-height: $line-height-xs;
- vertical-align: bottom;
- }
- }
-
- &.attr-table {
- th {
- font-weight: normal;
- width: 25%;
- }
- }
-}
-
-div.title-container {
- display: flex;
- // On small screens, `flex-direction: column;` ensures the control buttons don't appear on the
- // same line as the title, but rather break to the next line.
- flex-direction: column;
- flex-wrap: wrap;
- justify-content: space-between;
-
- @include media-breakpoint-up(lg) {
- // On large screens, `flex-direction: row;` allows the control buttons to appear vertically
- // aligned with the title.
- flex-direction: row;
- }
-
- #content-title {
- display: flex;
- flex: 1 0;
- flex-direction: column;
- padding-bottom: map.get($spacers, 2);
- }
-}
-
-// Object list control buttons (Add/Clone/Import/Export)
-.controls {
- margin-bottom: map.get($spacers, 2);
-
- @media print {
- // Never print controls. Use this over the .noprint utility so plugin authors don't need to
- // remember to add the class.
- display: none !important;
- }
-
- // Each group of buttons.
- .control-group {
- display: flex;
- flex-wrap: wrap;
- // Left-align controls on mobile.
- justify-content: flex-start;
-
- // Right-align controls on larger screens.
- @include media-breakpoint-up(lg) {
- justify-content: flex-end;
- }
-
- > * {
- // Pad each control button.
- margin: map.get($spacers, 1);
-
- &:first-child {
- // Don't pad the left side of the first control button.
- margin-left: 0;
- }
-
- &:last-child {
- // Don't pad the right side of the last control button.
- margin-right: 0;
- }
- }
- }
-}
-
-.object-subtitle {
- display: block;
- font-size: $font-size-sm;
- color: $text-muted;
-
- @include media-breakpoint-up(md) {
- display: inline-block;
- }
-
- > span {
- display: block;
-
- // Hide the separator on small screens.
- &.separator {
- display: none;
- }
-
- &,
- &.separator {
- @include media-breakpoint-up(md) {
- display: inline-block;
- }
- }
- }
-}
-
-// Global Search
-nav.search {
- // Don't overtake dropdowns
- z-index: 999;
- justify-content: center;
- background-color: $navbar-light-color;
-
- .search-container {
- display: flex;
- width: 100%;
-
- @include media-breakpoint-down(lg) {
- display: none;
- }
- }
-
- // Search Input & Selected Object Value & Object Selector
- .input-group {
- // Selected Object
- .search-obj-selected {
- border-color: $input-border-color;
- }
-
- // Object Selector Dropdown Button
- .dropdown-toggle {
- // Generate the same styles as a regular Bootstrap button.
- //@include button-variant($input-group-addon-bg, $input-border-color);
- margin-left: 0;
- font-weight: $input-group-addon-font-weight;
- line-height: $input-line-height;
- color: $input-group-addon-color;
- background-color: $input-group-addon-bg;
- border: $input-border-width solid $input-border-color;
- @include border-radius($input-border-radius);
- border-left: 1px solid var(--nbx-search-filter-border-left-color);
-
- &:focus {
- box-shadow: unset !important;
- }
- // Don't show the dropdown icon — the filter icon is basically the same thing.
- &:after {
- display: none;
- }
- }
-
- // Object Selector Dropdown Menu
- .search-obj-selector {
- // Limit the height and enable scrolling on mobile devices.
- max-height: 70vh;
- overflow-y: auto;
-
- .dropdown-item,
- .dropdown-header {
- font-size: $font-size-sm;
- }
-
- .dropdown-header {
- text-transform: uppercase;
- }
- }
- }
-}
-
-// Styles for the quicksearch and its clear button;
-// Overrides input-group styles and adds transition effects
-.quicksearch {
- input[type='search'] {
- border-radius: $border-radius !important;
- }
-
- button {
- margin-left: -32px !important;
- z-index: 100 !important;
- outline: none !important;
- border-radius: $border-radius !important;
- transition: visibility 0s, opacity 0.2s linear;
- }
-
- button :hover {
- opacity: 50%;
- transition: visibility 0s, opacity 0.1s linear;
- }
-}
-
-main.layout {
- display: flex;
- flex-wrap: nowrap;
- height: 100vh;
- height: -webkit-fill-available;
- max-height: 100vh;
- overflow-x: auto;
- overflow-y: hidden;
-
- // Override styles when printing. Fixes issue where only the first page is visible when printing.
- @media print {
- position: static !important;
- display: block !important;
- height: 100%;
- overflow-x: visible !important;
- overflow-y: visible !important;
- }
-}
-
-main.login-container {
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- width: 100%;
- max-width: 100vw;
- height: calc(100vh - 4rem);
- padding-top: 40px;
- padding-bottom: 40px;
-
- + footer.footer button.color-mode-toggle {
- color: var(--nbx-color-mode-toggle-color);
- }
-}
-
-.footer {
- background-color: $tab-content-bg;
- padding: 0;
- .nav-link {
- padding: 0.5rem;
- }
-
- @include media-breakpoint-down(md) {
- // Pad the bottom of the footer on mobile devices to account for mobile browser controls.
- margin-bottom: 8rem;
- }
-}
-
-footer.login-footer {
- height: 4rem;
- margin-top: auto;
-
- .container-fluid {
- display: flex;
- justify-content: flex-end;
- padding: $container-padding-x $grid-gutter-width;
- }
-}
-
-h1.accordion-item-title,
-h2.accordion-item-title,
-h3.accordion-item-title,
-h4.accordion-item-title,
-h5.accordion-item-title,
-h6.accordion-item-title {
- padding: 0.25rem 0.5rem;
- font-size: $font-size-sm;
- font-weight: $font-weight-bold;
- color: var(--nbx-sidebar-title-color);
- text-transform: uppercase;
-}
-
-.form-login {
- width: 100%;
- max-width: 330px;
- padding: 15px;
-
- input:focus {
- z-index: 1;
- }
-
- input[type='text'] {
- margin-bottom: -1px;
- border-bottom-right-radius: 0;
- border-bottom-left-radius: 0;
- }
- input[type='password'] {
- margin-bottom: 10px;
- border-top-left-radius: 0;
- border-top-right-radius: 0;
- }
-
- .form-control {
- position: relative;
- box-sizing: border-box;
- height: auto;
- padding: 10px;
- font-size: 16px;
- }
-}
-
-.navbar {
- border-bottom: 1px solid $border-color;
-}
-
-.navbar-brand {
- padding-top: 0.75rem;
- padding-bottom: 0.75rem;
- font-size: 1rem;
-}
-
-nav.nav.nav-pills {
- .nav-item.nav-link {
- padding: 0.25rem 0.5rem;
- font-size: $font-size-sm;
- border-radius: $border-radius;
-
- &:hover {
- color: $accordion-button-active-color;
- background-color: $accordion-button-active-bg;
- }
- }
-}
-
-// Ensure the content container is full-height, and that the content block is also full height so
-// that the footer is always at the bottom.
-div.content-container {
- position: relative;
- display: flex;
- flex-direction: column;
- width: calc(100% - #{$sidenav-width-closed});
- min-height: 100vh;
- overflow-x: hidden;
- overflow-y: auto;
-
- // Don't show an outline when the content container is focused.
- &:focus,
- &:focus-visible {
- outline: 0;
- }
-
- div.content {
- background-color: $tab-content-bg;
- flex: 1;
- }
-
- @include media-breakpoint-down(lg) {
- width: 100%;
- }
-
- // Make the content full-width when printing.
- @media print {
- width: 100% !important;
- margin-left: 0 !important;
- }
-}
-
-// Prevent scrolling of body content when nav menu is open on mobile.
-.sidebar.collapse.show ~ .content-container > .content {
- @media (max-width: map.get($grid-breakpoints, 'md')) {
- position: fixed;
- top: 0;
- left: 0;
- overflow-y: hidden;
- }
-}
-
-.tooltip {
- pointer-events: none;
-}
-
-span.color-label {
- display: block;
- width: 5rem;
- height: 1rem;
- padding: $badge-padding-y $badge-padding-x;
- border: 1px solid #303030;
- border-radius: $border-radius;
- box-shadow: $box-shadow-sm;
-}
-
-.badge a {
- color: inherit;
-}
-
-.btn {
- white-space: nowrap;
-}
-
-.card {
- box-shadow: $box-shadow-sm;
-
- .card-header {
- padding: $card-cap-padding-x;
- color: var(--nbx-body-color);
- border-bottom: none;
- }
-
- .card-header + .card-body {
- padding-top: 0;
- }
-
- .card-body.small .form-control,
- .card-body.small .form-select {
- font-size: $input-font-size-sm;
- }
-
- .card-divider {
- width: 100%;
- height: 1px;
- margin: $hr-margin-y 0;
- border-top: 1px solid $card-border-color;
- opacity: $hr-opacity;
- }
-
- // Remove shadow when printing.
- @media print {
- box-shadow: unset !important;
- }
-}
-
-.form-floating {
- position: relative;
-
- > .input-group > .form-control,
- > .input-group > .form-select {
- height: $form-floating-height;
- padding: $form-floating-padding-y $form-floating-padding-x;
- }
-
- > .input-group > label {
- position: absolute;
- top: 0;
- left: 0;
- height: 100%; // allow textareas
- padding: $form-floating-padding-y $form-floating-padding-x;
- pointer-events: none;
- border: $input-border-width solid transparent; // Required for aligning label's text with the input as it affects inner box model
- transform-origin: 0 0;
- @include transition($form-floating-transition);
- }
-
- > .input-group > .form-control {
- &::placeholder {
- color: transparent;
- }
-
- &:focus,
- &:not(:placeholder-shown) {
- padding-top: $form-floating-input-padding-t;
- padding-bottom: $form-floating-input-padding-b;
- }
- // Duplicated because `:-webkit-autofill` invalidates other selectors when grouped
- &:-webkit-autofill {
- padding-top: $form-floating-input-padding-t;
- padding-bottom: $form-floating-input-padding-b;
- }
- }
-
- > .input-group > .form-select,
- > .choices > .choices__inner,
- > .ss-main span.placeholder, // SlimSelect Single
- > .ss-main div.ss-values // SlimSelect Multiple
- {
- padding-top: $form-floating-input-padding-t;
- padding-bottom: $form-floating-input-padding-b;
- }
-
- > .input-group > .form-control:focus,
- > .input-group > .form-control:not(:placeholder-shown),
- > .input-group > .form-select,
- > .choices,
- > .ss-main {
- ~ label {
- opacity: $form-floating-label-opacity;
- transform: $form-floating-label-transform;
- z-index: 4;
- }
- }
- // Duplicated because `:-webkit-autofill` invalidates other selectors when grouped
- > .input-group > .form-control:-webkit-autofill {
- ~ label {
- z-index: 4;
- opacity: $form-floating-label-opacity;
- transform: $form-floating-label-transform;
- }
- }
-}
-
-.form-object-edit {
- margin: 0 auto;
- max-width: 800px;
-}
-
-textarea.form-control[rows='10'] {
- height: 18rem;
-}
-
-textarea.markdown,
-textarea.form-control[name='csv'] {
- font-family: $font-family-monospace;
-}
-
-.card:not(:only-of-type) {
- margin-bottom: $spacer;
-}
-
-.stat-btn {
- min-width: $spacer * 3;
-}
-
-nav.breadcrumb-container {
- width: fit-content;
- padding: $badge-padding-y $badge-padding-x;
- font-size: $font-size-sm;
-
- ol.breadcrumb {
- margin-bottom: 0;
- li.breadcrumb-item > a {
- text-decoration: none;
- }
- li.breadcrumb-item > a:hover {
- text-decoration: underline;
- }
- }
-}
-
-label.required {
- font-weight: $font-weight-bold;
-
- &:after {
- position: absolute;
- display: inline-block;
- margin: 0 0 0 2px;
- font-family: 'Material Design Icons';
- font-size: 8px;
- font-style: normal;
- font-weight: $font-weight-medium;
- text-decoration: none;
- content: '\f06C4';
- }
-}
-
-// Applied to containing element around table bulk-action buttons (bulk-edit, bulk-disconnect
-// bulk-delete, etc). Each usage of .bulk-buttons needs to have groups of buttons wrapped with
-// .bulk-button-group so that proper spacing is applied (even if there is only one group).
-div.bulk-buttons {
- display: flex;
- justify-content: space-between;
- margin: math.div($spacer, 2) 0;
-
- // Each group of buttons needs to be contained separately for alignment purposes. This way, you
- // can put some buttons in a group that aligns left, and other buttons in a group that aligns
- // right.
- > div.bulk-button-group {
- display: flex;
- flex-wrap: wrap;
- // For small screens: don't fill the available space (thereby expanding the size of the button).
- align-items: flex-start;
-
- &:first-of-type:not(:last-of-type) {
- // If there are multiple bulk button groups and this is the first, the first button in the
- // group should *not* have left spacing applied, so the button group aligns with the rest
- // of the page elements.
- > *:first-child {
- margin-left: 0;
- }
- }
-
- &:last-of-type:not(:first-of-type) {
- // If there are multiple bulk button groups and this is the last, the last button in the
- // group should *not* have right spacing applied, so the button group aligns with the rest
- // of the page elements.
- > *:last-child {
- margin-right: 0;
- }
- }
-
- // However, the rest of the buttons should have spacing applied in all directions.
- > * {
- margin: math.div($spacer, 4);
- }
- }
-}
-
-table tbody {
- @each $color, $value in $theme-colors {
- tr.#{$color} {
- background-color: rgba($value, 0.15);
- border-color: $gray-500;
- }
- }
-}
-
-// Style objects with statuses/roles within a table. As of implementation, used for IP addresses
-// assigned to interfaces.
-table .table-badge-group {
- .table-badge {
- display: block;
- width: min-content;
- font-size: $font-size-sm;
- font-weight: $font-weight-base;
-
- &:not(.badge) {
- // Apply badge horizontal padding so that IP addresses *not* within a badge appear aligned
- // with IP addresses that *are* within a badge.
- padding: 0 $badge-padding-x;
- }
-
- &.badge:not(:last-of-type):not(:only-child) {
- margin-bottom: map.get($spacers, 1);
- }
- }
-}
-
-pre.change-data {
- padding-right: 0;
- padding-left: 0;
-
- > span {
- display: block;
- padding-right: $spacer;
- padding-left: $spacer;
-
- &.added {
- background-color: var(--nbx-change-added);
- }
-
- &.removed {
- background-color: var(--nbx-change-removed);
- }
- }
-}
-
-pre.change-diff {
- border-color: transparent;
-
- &.change-removed {
- background-color: var(--nbx-change-removed);
- }
-
- &.change-added {
- background-color: var(--nbx-change-added);
- }
-}
-
-div.card-overlay {
- position: absolute;
- display: flex;
- align-items: center;
- justify-content: center;
- width: 100%;
- height: 100%;
- background-color: rgba($white, 0.75);
- border-radius: $border-radius;
-
- > div.spinner-border {
- width: 6rem;
- height: 6rem;
- color: $secondary;
- }
-}
-
-.table-controls {
- display: flex;
-
- @include media-breakpoint-up(md) {
- // `!important` needed because of inherited margin-bottom from `.col`
- margin-top: 0 !important;
- margin-bottom: 0 !important;
- }
-
- .table-configure {
- justify-content: flex-start;
-
- @include media-breakpoint-up(md) {
- justify-content: flex-end;
- }
- }
-
- .form-switch.form-check-inline {
- flex: 1 0 auto;
- font-size: $font-size-sm;
- }
-}
-
-// Tabbed content
-.nav-tabs {
- background-color: $body-bg;
- .nav-link {
- &:hover {
- // Don't show a bottom-border on a hovered nav link because it overlaps with the .nav-tab border.
- border-bottom-color: transparent;
- }
- &.active {
- // Set the background-color of an active tab to the same color as the .tab-content
- // background-color so it visually indicates which tab is open.
- background-color: $tab-content-bg;
- border-bottom-color: $tab-content-bg;
- // Move the active tab down 1px to overtake the .nav-tabs element's border, but only for that
- // tab. This is an ugly hack, but it works.
- transform: translateY(1px);
- }
- }
-}
-
-.tab-content {
- display: flex;
- flex-direction: column;
- padding: $spacer;
-}
-
-// Override masonry-layout styles when printing.
-.masonry {
- @media print {
- position: static !important;
- display: block !important;
- height: unset !important;
- }
- .masonry-item {
- @media print {
- position: static !important;
- top: unset !important;
- left: unset !important;
- display: block !important;
- }
- }
-}
-
-// Object hierarchy indicators.
-.record-depth {
- display: inline;
- font-size: $font-size-base;
- user-select: none;
- opacity: 0.33;
-
- // Add spacing to the last or only dot.
- span:only-of-type,
- span:last-of-type {
- margin-right: map.get($spacers, 1);
- }
-}
-
-// Remove the max-width from image preview popovers as this is controlled on the image element.
-.popover.image-preview-popover {
- max-width: unset;
-}
-
-/* Rendered Markdown */
-.rendered-markdown table {
- width: 100%;
-}
-.rendered-markdown th {
- border-bottom: 2px solid #dddddd;
- padding: 8px;
-}
-.rendered-markdown td {
- border-top: 1px solid #dddddd;
- padding: 8px;
-}
-
-th[align="left"] {
- text-align: left;
-}
-
-th[align="center"] {
- text-align: center;
-}
-
-th[align="right"] {
- text-align: right;
-}
-
-/* Markdown widget */
-.markdown-widget {
- .nav-link {
- border-bottom: 0;
-
- &.active {
- background-color: var(--nbx-body-bg);
- }
- }
-
- .nav-tabs {
- background-color: var(--nbx-pre-bg);
- }
-}
-
-// Preformatted text blocks
-td pre {
- margin-bottom: 0;
-}
-pre.block {
- padding: $spacer;
- background-color: var(--nbx-pre-bg);
- border: 1px solid var(--nbx-pre-border-color);
- border-radius: $border-radius;
-}
-
-#django-messages {
- position: fixed;
- right: $spacer;
- bottom: 0;
- margin: $spacer;
-}
-
-// Page-specific styles.
-html {
- // Shade the home page content background-color.
- &[data-netbox-url-name='home'] {
- .content-container,
- .search {
- background-color: $gray-100 !important;
- }
- &[data-netbox-color-mode='dark'] {
- .content-container,
- .search {
- background-color: $darkest !important;
- }
- }
- }
-
- // Don't show the django-messages toasts on the login screen in favor of the alert component.
- &[data-netbox-url-name='login'] {
- #django-messages {
- display: none;
- }
- }
-}
+// Transitional styling
+@import 'transitional/cards';
+@import 'transitional/tables';
diff --git a/netbox/project-static/styles/old/netbox.scss b/netbox/project-static/styles/old/netbox.scss
new file mode 100644
index 000000000..8d0192593
--- /dev/null
+++ b/netbox/project-static/styles/old/netbox.scss
@@ -0,0 +1,1078 @@
+// NetBox-specific Styles and Overrides.
+
+@use 'sass:map';
+@use 'sass:math';
+//@import './sidenav';
+@import './overrides';
+@import './utilities';
+@import './variables';
+
+@each $color, $value in $theme-colors {
+ // Override CSS values on each theme color.
+
+ // Use Bootstrap's method of coloring alert links to appropriately color close buttons within
+ // another colored element.
+ // See: https://github.com/twbs/bootstrap/blob/2bdbb42dcf6bfb99b5e9e5444d9e64589eb8c08f/scss/_alert.scss#L50-L52
+ // See: https://github.com/twbs/bootstrap/blob/2bdbb42dcf6bfb99b5e9e5444d9e64589eb8c08f/scss/_close.scss#L12
+ $shifted-bg: shift-color($value, $alert-bg-scale);
+ $shifted-color: shift-color($value, $alert-color-scale);
+
+ @if (contrast-ratio($shifted-bg, $shifted-color) < $min-contrast-ratio) {
+ $shifted-color: mix($value, color-contrast($shifted-bg), abs($alert-color-scale));
+ }
+
+ $btn-close-bg: url("data:image/svg+xml,");
+ .bg-#{$color} button.btn-close {
+ background: transparent escape-svg($btn-close-bg) center / $btn-close-width auto no-repeat;
+ }
+
+ .btn.btn-ghost-#{$color} {
+ color: $value;
+ &:hover {
+ background-color: rgba($value, 0.12);
+ }
+ }
+
+ // Use Bootstrap's method of coloring the .alert-link class automatically.
+ // See: https://github.com/twbs/bootstrap/blob/2bdbb42dcf6bfb99b5e9e5444d9e64589eb8c08f/scss/_alert.scss#L50-L52
+
+ .alert.alert-#{$color},
+ .table-#{$color} {
+ // Exclude buttons.
+ a:not(.btn) {
+ font-weight: $font-weight-bold;
+ color: $shifted-color;
+ }
+ // Apply a border to buttons contained within colored elements, if they're not already a
+ // bordered button class.
+ .btn:not([class*='btn-outline']) {
+ border-color: $gray-700;
+ }
+ }
+
+ // Toasts required a slightly different approach because the background color isn't "shifted",
+ // it's the direct theme color.
+ .toast.bg-#{$color} {
+ $shifted-color: shift-color($value, $alert-color-scale);
+
+ @if (contrast-ratio($value, $shifted-color) < $min-contrast-ratio) {
+ $shifted-color: mix($value, color-contrast($value), abs($alert-color-scale));
+ }
+ a:not(.btn) {
+ font-weight: $font-weight-bold;
+ color: $shifted-color;
+ }
+ }
+
+ // Use proper contrasting color foreground color for special components.
+ .badge,
+ .toast,
+ .toast-header,
+ .progress-bar {
+ &.bg-#{$color} {
+ color: color-contrast($value);
+ }
+ }
+}
+
+// Ensure progress bars (utilization graph) in tables aren't too narrow to display the percentage.
+table td > .progress {
+ min-width: 6rem;
+}
+
+// Override Bootstrap form-control font-size when contained by .small element.
+.small .form-control {
+ font-size: $font-size-sm;
+}
+
+// Automatically space out adjacent columns, but not within card bodies.
+:not(.card-body) > .col:not(:last-child):not(:only-child) {
+ margin-bottom: $spacer;
+}
+
+.nav-mobile {
+ display: none;
+ flex-direction: column;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+
+ @include media-breakpoint-down(lg) {
+ display: flex;
+ }
+
+ .nav-mobile-top {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 100%;
+ }
+}
+
+.card > .table.table-flush {
+ margin-bottom: 0;
+ overflow: hidden;
+ border-bottom-right-radius: $card-border-radius;
+ border-bottom-left-radius: $card-border-radius;
+
+ thead th[scope='col'] {
+ padding-top: map.get($spacers, 3);
+ padding-bottom: map.get($spacers, 3);
+ text-transform: uppercase;
+ vertical-align: middle;
+ background-color: $table-flush-header-bg;
+ border-top: 1px solid $card-border-color;
+ border-bottom-color: $card-border-color;
+ }
+
+ th,
+ td {
+ padding-right: map.get($spacers, 4) !important;
+ padding-left: map.get($spacers, 4) !important;
+ border-right: 0;
+ border-left: 0;
+ }
+ tr[class] {
+ border-color: $card-border-color !important;
+ &:last-of-type {
+ border-bottom-color: transparent !important;
+ border-bottom-right-radius: $card-border-radius;
+ border-bottom-left-radius: $card-border-radius;
+ }
+ }
+}
+
+// Primarily used for the new release notification, but could be used for other alerts as needed.
+// Wrap any alerts in .header-alert-container to ensure the layout is consistent.
+.header-alert-container {
+ // Center-align the alert(s).
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ // Apply the same spacing that's applied to the #content div's first child (.px-3).
+ padding: 0 $spacer;
+
+ // By default, alerts inside .header-alert-container should take up the full width.
+ .alert {
+ width: 100%;
+
+ // Adjust the max-width for larger screens so there's not a big ugly blue blob taking up the
+ // entire screen.
+ @include media-breakpoint-up(md) {
+ max-width: 75%;
+ }
+ @include media-breakpoint-up(lg) {
+ max-width: 50%;
+ }
+ }
+}
+
+.alert {
+ code {
+ color: $gray-600;
+ }
+}
+
+span.profile-button .dropdown-menu {
+ right: 0;
+ left: auto;
+ display: block !important;
+ margin-top: 0.5rem;
+ box-shadow: $box-shadow;
+ transition: opacity 0.2s ease-in-out;
+
+ &:not(.show) {
+ pointer-events: none;
+ opacity: 0;
+ }
+ &.show {
+ pointer-events: auto;
+ opacity: 1;
+ }
+}
+
+div#advanced-search-content div.card div.card-body div.col:not(:last-child) {
+ margin-right: 1rem;
+}
+
+table {
+ td {
+ a {
+ text-decoration: none;
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ .dropdown {
+ // Presence of 'overflow: scroll' on a table causes dropdowns to be improperly hidden when
+ // opened. See: https://github.com/twbs/bootstrap/issues/24251
+ position: static;
+ }
+ }
+ th {
+ a,
+ a:hover {
+ color: $body-color;
+ text-decoration: none;
+ }
+ }
+
+ td,
+ th {
+ font-size: $font-size-sm;
+ line-height: $line-height-sm;
+ vertical-align: middle;
+ &.min-width {
+ width: 1%;
+ }
+ .form-check-input {
+ // Ensure checkboxes aren't too small inside object tables.
+ margin-top: 0.125em;
+ font-size: $font-size-base;
+ }
+
+ .btn-sm {
+ line-height: $line-height-xs;
+ }
+
+ p {
+ // Remove spacing from paragraph elements within tables.
+ margin-bottom: 0.5em;
+ }
+
+ p:last-child {
+ margin-bottom: 0;
+ }
+ }
+
+ th.asc > a::after {
+ content: '\f0140';
+ font-family: 'Material Design Icons';
+ }
+
+ th.desc > a::after {
+ content: '\f0143';
+ font-family: 'Material Design Icons';
+ }
+
+ &.table > :not(caption) > * > * {
+ padding-right: $table-cell-padding-x-sm !important;
+ padding-left: $table-cell-padding-x-sm !important;
+ }
+
+ &.object-list {
+ th {
+ font-size: $font-size-xs;
+ line-height: $line-height-xs;
+ vertical-align: bottom;
+ }
+ }
+
+ &.attr-table {
+ th {
+ font-weight: normal;
+ width: 25%;
+ }
+ }
+}
+
+div.title-container {
+ display: flex;
+ // On small screens, `flex-direction: column;` ensures the control buttons don't appear on the
+ // same line as the title, but rather break to the next line.
+ flex-direction: column;
+ flex-wrap: wrap;
+ justify-content: space-between;
+
+ @include media-breakpoint-up(lg) {
+ // On large screens, `flex-direction: row;` allows the control buttons to appear vertically
+ // aligned with the title.
+ flex-direction: row;
+ }
+
+ #content-title {
+ display: flex;
+ flex: 1 0;
+ flex-direction: column;
+ padding-bottom: map.get($spacers, 2);
+ }
+}
+
+// Object list control buttons (Add/Clone/Import/Export)
+.controls {
+ margin-bottom: map.get($spacers, 2);
+
+ @media print {
+ // Never print controls. Use this over the .noprint utility so plugin authors don't need to
+ // remember to add the class.
+ display: none !important;
+ }
+
+ // Each group of buttons.
+ .control-group {
+ display: flex;
+ flex-wrap: wrap;
+ // Left-align controls on mobile.
+ justify-content: flex-start;
+
+ // Right-align controls on larger screens.
+ @include media-breakpoint-up(lg) {
+ justify-content: flex-end;
+ }
+
+ > * {
+ // Pad each control button.
+ margin: map.get($spacers, 1);
+
+ &:first-child {
+ // Don't pad the left side of the first control button.
+ margin-left: 0;
+ }
+
+ &:last-child {
+ // Don't pad the right side of the last control button.
+ margin-right: 0;
+ }
+ }
+ }
+}
+
+.object-subtitle {
+ display: block;
+ font-size: $font-size-sm;
+ color: $text-muted;
+
+ @include media-breakpoint-up(md) {
+ display: inline-block;
+ }
+
+ > span {
+ display: block;
+
+ // Hide the separator on small screens.
+ &.separator {
+ display: none;
+ }
+
+ &,
+ &.separator {
+ @include media-breakpoint-up(md) {
+ display: inline-block;
+ }
+ }
+ }
+}
+
+// Global Search
+nav.search {
+ // Don't overtake dropdowns
+ z-index: 999;
+ justify-content: center;
+ background-color: $navbar-light-color;
+
+ .search-container {
+ display: flex;
+ width: 100%;
+
+ @include media-breakpoint-down(lg) {
+ display: none;
+ }
+ }
+
+ // Search Input & Selected Object Value & Object Selector
+ .input-group {
+ // Selected Object
+ .search-obj-selected {
+ border-color: $input-border-color;
+ }
+
+ // Object Selector Dropdown Button
+ .dropdown-toggle {
+ // Generate the same styles as a regular Bootstrap button.
+ //@include button-variant($input-group-addon-bg, $input-border-color);
+ margin-left: 0;
+ font-weight: $input-group-addon-font-weight;
+ line-height: $input-line-height;
+ color: $input-group-addon-color;
+ background-color: $input-group-addon-bg;
+ border: $input-border-width solid $input-border-color;
+ @include border-radius($input-border-radius);
+ border-left: 1px solid var(--nbx-search-filter-border-left-color);
+
+ &:focus {
+ box-shadow: unset !important;
+ }
+ // Don't show the dropdown icon — the filter icon is basically the same thing.
+ &:after {
+ display: none;
+ }
+ }
+
+ // Object Selector Dropdown Menu
+ .search-obj-selector {
+ // Limit the height and enable scrolling on mobile devices.
+ max-height: 70vh;
+ overflow-y: auto;
+
+ .dropdown-item,
+ .dropdown-header {
+ font-size: $font-size-sm;
+ }
+
+ .dropdown-header {
+ text-transform: uppercase;
+ }
+ }
+ }
+}
+
+// Styles for the quicksearch and its clear button;
+// Overrides input-group styles and adds transition effects
+.quicksearch {
+ input[type='search'] {
+ border-radius: $border-radius !important;
+ }
+
+ button {
+ margin-left: -32px !important;
+ z-index: 100 !important;
+ outline: none !important;
+ border-radius: $border-radius !important;
+ transition: visibility 0s, opacity 0.2s linear;
+ }
+
+ button :hover {
+ opacity: 50%;
+ transition: visibility 0s, opacity 0.1s linear;
+ }
+}
+
+main.layout {
+ display: flex;
+ flex-wrap: nowrap;
+ height: 100vh;
+ height: -webkit-fill-available;
+ max-height: 100vh;
+ overflow-x: auto;
+ overflow-y: hidden;
+
+ // Override styles when printing. Fixes issue where only the first page is visible when printing.
+ @media print {
+ position: static !important;
+ display: block !important;
+ height: 100%;
+ overflow-x: visible !important;
+ overflow-y: visible !important;
+ }
+}
+
+main.login-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ max-width: 100vw;
+ height: calc(100vh - 4rem);
+ padding-top: 40px;
+ padding-bottom: 40px;
+
+ + footer.footer button.color-mode-toggle {
+ color: var(--nbx-color-mode-toggle-color);
+ }
+}
+
+.footer {
+ background-color: $tab-content-bg;
+ padding: 0;
+ .nav-link {
+ padding: 0.5rem;
+ }
+
+ @include media-breakpoint-down(md) {
+ // Pad the bottom of the footer on mobile devices to account for mobile browser controls.
+ margin-bottom: 8rem;
+ }
+}
+
+footer.login-footer {
+ height: 4rem;
+ margin-top: auto;
+
+ .container-fluid {
+ display: flex;
+ justify-content: flex-end;
+ padding: $container-padding-x $grid-gutter-width;
+ }
+}
+
+h1.accordion-item-title,
+h2.accordion-item-title,
+h3.accordion-item-title,
+h4.accordion-item-title,
+h5.accordion-item-title,
+h6.accordion-item-title {
+ padding: 0.25rem 0.5rem;
+ font-size: $font-size-sm;
+ font-weight: $font-weight-bold;
+ color: var(--nbx-sidebar-title-color);
+ text-transform: uppercase;
+}
+
+.form-login {
+ width: 100%;
+ max-width: 330px;
+ padding: 15px;
+
+ input:focus {
+ z-index: 1;
+ }
+
+ input[type='text'] {
+ margin-bottom: -1px;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+ }
+ input[type='password'] {
+ margin-bottom: 10px;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+ }
+
+ .form-control {
+ position: relative;
+ box-sizing: border-box;
+ height: auto;
+ padding: 10px;
+ font-size: 16px;
+ }
+}
+
+.navbar {
+ border-bottom: 1px solid $border-color;
+}
+
+.navbar-brand {
+ padding-top: 0.75rem;
+ padding-bottom: 0.75rem;
+ font-size: 1rem;
+}
+
+nav.nav.nav-pills {
+ .nav-item.nav-link {
+ padding: 0.25rem 0.5rem;
+ font-size: $font-size-sm;
+ border-radius: $border-radius;
+
+ &:hover {
+ color: $accordion-button-active-color;
+ background-color: $accordion-button-active-bg;
+ }
+ }
+}
+
+// Ensure the content container is full-height, and that the content block is also full height so
+// that the footer is always at the bottom.
+div.content-container {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ width: calc(100% - #{$sidenav-width-closed});
+ min-height: 100vh;
+ overflow-x: hidden;
+ overflow-y: auto;
+
+ // Don't show an outline when the content container is focused.
+ &:focus,
+ &:focus-visible {
+ outline: 0;
+ }
+
+ div.content {
+ background-color: $tab-content-bg;
+ flex: 1;
+ }
+
+ @include media-breakpoint-down(lg) {
+ width: 100%;
+ }
+
+ // Make the content full-width when printing.
+ @media print {
+ width: 100% !important;
+ margin-left: 0 !important;
+ }
+}
+
+// Prevent scrolling of body content when nav menu is open on mobile.
+.sidebar.collapse.show ~ .content-container > .content {
+ @media (max-width: map.get($grid-breakpoints, 'md')) {
+ position: fixed;
+ top: 0;
+ left: 0;
+ overflow-y: hidden;
+ }
+}
+
+.tooltip {
+ pointer-events: none;
+}
+
+span.color-label {
+ display: block;
+ width: 5rem;
+ height: 1rem;
+ padding: $badge-padding-y $badge-padding-x;
+ border: 1px solid #303030;
+ border-radius: $border-radius;
+ box-shadow: $box-shadow-sm;
+}
+
+.badge a {
+ color: inherit;
+}
+
+.btn {
+ white-space: nowrap;
+}
+
+.card {
+ box-shadow: $box-shadow-sm;
+
+ .card-header {
+ padding: $card-cap-padding-x;
+ color: var(--nbx-body-color);
+ border-bottom: none;
+ }
+
+ .card-header + .card-body {
+ padding-top: 0;
+ }
+
+ .card-body.small .form-control,
+ .card-body.small .form-select {
+ font-size: $input-font-size-sm;
+ }
+
+ .card-divider {
+ width: 100%;
+ height: 1px;
+ margin: $hr-margin-y 0;
+ border-top: 1px solid $card-border-color;
+ opacity: $hr-opacity;
+ }
+
+ // Remove shadow when printing.
+ @media print {
+ box-shadow: unset !important;
+ }
+}
+
+.form-floating {
+ position: relative;
+
+ > .input-group > .form-control,
+ > .input-group > .form-select {
+ height: $form-floating-height;
+ padding: $form-floating-padding-y $form-floating-padding-x;
+ }
+
+ > .input-group > label {
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 100%; // allow textareas
+ padding: $form-floating-padding-y $form-floating-padding-x;
+ pointer-events: none;
+ border: $input-border-width solid transparent; // Required for aligning label's text with the input as it affects inner box model
+ transform-origin: 0 0;
+ @include transition($form-floating-transition);
+ }
+
+ > .input-group > .form-control {
+ &::placeholder {
+ color: transparent;
+ }
+
+ &:focus,
+ &:not(:placeholder-shown) {
+ padding-top: $form-floating-input-padding-t;
+ padding-bottom: $form-floating-input-padding-b;
+ }
+ // Duplicated because `:-webkit-autofill` invalidates other selectors when grouped
+ &:-webkit-autofill {
+ padding-top: $form-floating-input-padding-t;
+ padding-bottom: $form-floating-input-padding-b;
+ }
+ }
+
+ > .input-group > .form-select,
+ > .choices > .choices__inner,
+ > .ss-main span.placeholder, // SlimSelect Single
+ > .ss-main div.ss-values // SlimSelect Multiple
+ {
+ padding-top: $form-floating-input-padding-t;
+ padding-bottom: $form-floating-input-padding-b;
+ }
+
+ > .input-group > .form-control:focus,
+ > .input-group > .form-control:not(:placeholder-shown),
+ > .input-group > .form-select,
+ > .choices,
+ > .ss-main {
+ ~ label {
+ opacity: $form-floating-label-opacity;
+ transform: $form-floating-label-transform;
+ z-index: 4;
+ }
+ }
+ // Duplicated because `:-webkit-autofill` invalidates other selectors when grouped
+ > .input-group > .form-control:-webkit-autofill {
+ ~ label {
+ z-index: 4;
+ opacity: $form-floating-label-opacity;
+ transform: $form-floating-label-transform;
+ }
+ }
+}
+
+.form-object-edit {
+ margin: 0 auto;
+ max-width: 800px;
+}
+
+textarea.form-control[rows='10'] {
+ height: 18rem;
+}
+
+textarea.markdown,
+textarea.form-control[name='csv'] {
+ font-family: $font-family-monospace;
+}
+
+.card:not(:only-of-type) {
+ margin-bottom: $spacer;
+}
+
+.stat-btn {
+ min-width: $spacer * 3;
+}
+
+nav.breadcrumb-container {
+ width: fit-content;
+ padding: $badge-padding-y $badge-padding-x;
+ font-size: $font-size-sm;
+
+ ol.breadcrumb {
+ margin-bottom: 0;
+ li.breadcrumb-item > a {
+ text-decoration: none;
+ }
+ li.breadcrumb-item > a:hover {
+ text-decoration: underline;
+ }
+ }
+}
+
+label.required {
+ font-weight: $font-weight-bold;
+
+ &:after {
+ position: absolute;
+ display: inline-block;
+ margin: 0 0 0 2px;
+ font-family: 'Material Design Icons';
+ font-size: 8px;
+ font-style: normal;
+ font-weight: $font-weight-medium;
+ text-decoration: none;
+ content: '\f06C4';
+ }
+}
+
+// Applied to containing element around table bulk-action buttons (bulk-edit, bulk-disconnect
+// bulk-delete, etc). Each usage of .bulk-buttons needs to have groups of buttons wrapped with
+// .bulk-button-group so that proper spacing is applied (even if there is only one group).
+div.bulk-buttons {
+ display: flex;
+ justify-content: space-between;
+ margin: math.div($spacer, 2) 0;
+
+ // Each group of buttons needs to be contained separately for alignment purposes. This way, you
+ // can put some buttons in a group that aligns left, and other buttons in a group that aligns
+ // right.
+ > div.bulk-button-group {
+ display: flex;
+ flex-wrap: wrap;
+ // For small screens: don't fill the available space (thereby expanding the size of the button).
+ align-items: flex-start;
+
+ &:first-of-type:not(:last-of-type) {
+ // If there are multiple bulk button groups and this is the first, the first button in the
+ // group should *not* have left spacing applied, so the button group aligns with the rest
+ // of the page elements.
+ > *:first-child {
+ margin-left: 0;
+ }
+ }
+
+ &:last-of-type:not(:first-of-type) {
+ // If there are multiple bulk button groups and this is the last, the last button in the
+ // group should *not* have right spacing applied, so the button group aligns with the rest
+ // of the page elements.
+ > *:last-child {
+ margin-right: 0;
+ }
+ }
+
+ // However, the rest of the buttons should have spacing applied in all directions.
+ > * {
+ margin: math.div($spacer, 4);
+ }
+ }
+}
+
+table tbody {
+ @each $color, $value in $theme-colors {
+ tr.#{$color} {
+ background-color: rgba($value, 0.15);
+ border-color: $gray-500;
+ }
+ }
+}
+
+// Style objects with statuses/roles within a table. As of implementation, used for IP addresses
+// assigned to interfaces.
+table .table-badge-group {
+ .table-badge {
+ display: block;
+ width: min-content;
+ font-size: $font-size-sm;
+ font-weight: $font-weight-base;
+
+ &:not(.badge) {
+ // Apply badge horizontal padding so that IP addresses *not* within a badge appear aligned
+ // with IP addresses that *are* within a badge.
+ padding: 0 $badge-padding-x;
+ }
+
+ &.badge:not(:last-of-type):not(:only-child) {
+ margin-bottom: map.get($spacers, 1);
+ }
+ }
+}
+
+pre.change-data {
+ padding-right: 0;
+ padding-left: 0;
+
+ > span {
+ display: block;
+ padding-right: $spacer;
+ padding-left: $spacer;
+
+ &.added {
+ background-color: var(--nbx-change-added);
+ }
+
+ &.removed {
+ background-color: var(--nbx-change-removed);
+ }
+ }
+}
+
+pre.change-diff {
+ border-color: transparent;
+
+ &.change-removed {
+ background-color: var(--nbx-change-removed);
+ }
+
+ &.change-added {
+ background-color: var(--nbx-change-added);
+ }
+}
+
+div.card-overlay {
+ position: absolute;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+ background-color: rgba($white, 0.75);
+ border-radius: $border-radius;
+
+ > div.spinner-border {
+ width: 6rem;
+ height: 6rem;
+ color: $secondary;
+ }
+}
+
+.table-controls {
+ display: flex;
+
+ @include media-breakpoint-up(md) {
+ // `!important` needed because of inherited margin-bottom from `.col`
+ margin-top: 0 !important;
+ margin-bottom: 0 !important;
+ }
+
+ .table-configure {
+ justify-content: flex-start;
+
+ @include media-breakpoint-up(md) {
+ justify-content: flex-end;
+ }
+ }
+
+ .form-switch.form-check-inline {
+ flex: 1 0 auto;
+ font-size: $font-size-sm;
+ }
+}
+
+// Tabbed content
+.nav-tabs {
+ background-color: $body-bg;
+ .nav-link {
+ &:hover {
+ // Don't show a bottom-border on a hovered nav link because it overlaps with the .nav-tab border.
+ border-bottom-color: transparent;
+ }
+ &.active {
+ // Set the background-color of an active tab to the same color as the .tab-content
+ // background-color so it visually indicates which tab is open.
+ background-color: $tab-content-bg;
+ border-bottom-color: $tab-content-bg;
+ // Move the active tab down 1px to overtake the .nav-tabs element's border, but only for that
+ // tab. This is an ugly hack, but it works.
+ transform: translateY(1px);
+ }
+ }
+}
+
+.tab-content {
+ display: flex;
+ flex-direction: column;
+ padding: $spacer;
+}
+
+// Override masonry-layout styles when printing.
+.masonry {
+ @media print {
+ position: static !important;
+ display: block !important;
+ height: unset !important;
+ }
+ .masonry-item {
+ @media print {
+ position: static !important;
+ top: unset !important;
+ left: unset !important;
+ display: block !important;
+ }
+ }
+}
+
+// Object hierarchy indicators.
+.record-depth {
+ display: inline;
+ font-size: $font-size-base;
+ user-select: none;
+ opacity: 0.33;
+
+ // Add spacing to the last or only dot.
+ span:only-of-type,
+ span:last-of-type {
+ margin-right: map.get($spacers, 1);
+ }
+}
+
+// Remove the max-width from image preview popovers as this is controlled on the image element.
+.popover.image-preview-popover {
+ max-width: unset;
+}
+
+/* Rendered Markdown */
+.rendered-markdown table {
+ width: 100%;
+}
+.rendered-markdown th {
+ border-bottom: 2px solid #dddddd;
+ padding: 8px;
+}
+.rendered-markdown td {
+ border-top: 1px solid #dddddd;
+ padding: 8px;
+}
+
+th[align="left"] {
+ text-align: left;
+}
+
+th[align="center"] {
+ text-align: center;
+}
+
+th[align="right"] {
+ text-align: right;
+}
+
+/* Markdown widget */
+.markdown-widget {
+ .nav-link {
+ border-bottom: 0;
+
+ &.active {
+ background-color: var(--nbx-body-bg);
+ }
+ }
+
+ .nav-tabs {
+ background-color: var(--nbx-pre-bg);
+ }
+}
+
+// Preformatted text blocks
+td pre {
+ margin-bottom: 0;
+}
+pre.block {
+ padding: $spacer;
+ background-color: var(--nbx-pre-bg);
+ border: 1px solid var(--nbx-pre-border-color);
+ border-radius: $border-radius;
+}
+
+#django-messages {
+ position: fixed;
+ right: $spacer;
+ bottom: 0;
+ margin: $spacer;
+}
+
+// Page-specific styles.
+html {
+ // Shade the home page content background-color.
+ &[data-netbox-url-name='home'] {
+ .content-container,
+ .search {
+ background-color: $gray-100 !important;
+ }
+ &[data-netbox-color-mode='dark'] {
+ .content-container,
+ .search {
+ background-color: $darkest !important;
+ }
+ }
+ }
+
+ // Don't show the django-messages toasts on the login screen in favor of the alert component.
+ &[data-netbox-url-name='login'] {
+ #django-messages {
+ display: none;
+ }
+ }
+}
diff --git a/netbox/project-static/styles/overrides.scss b/netbox/project-static/styles/old/overrides.scss
similarity index 100%
rename from netbox/project-static/styles/overrides.scss
rename to netbox/project-static/styles/old/overrides.scss
diff --git a/netbox/project-static/styles/theme-light.scss b/netbox/project-static/styles/old/theme-light.scss
similarity index 93%
rename from netbox/project-static/styles/theme-light.scss
rename to netbox/project-static/styles/old/theme-light.scss
index f90843fa3..9b861c3a5 100644
--- a/netbox/project-static/styles/theme-light.scss
+++ b/netbox/project-static/styles/old/theme-light.scss
@@ -1,6 +1,6 @@
// Base NetBox Theme Overrides and Settings - color mode agnostic.
-@import '../node_modules/bootstrap/scss/functions';
+@import '../../node_modules/bootstrap/scss/functions';
$card-cap-bg: 'unset';
@@ -31,8 +31,8 @@ $line-height-lg: 1.75;
$darker: #1b1f22;
$darkest: #171b1d;
-@import '../node_modules/bootstrap/scss/variables';
-@import '../node_modules/bootstrap/scss/variables-dark';
+@import '../../node_modules/bootstrap/scss/variables';
+@import '../../node_modules/bootstrap/scss/variables-dark';
// This is the same value as the default from Bootstrap, but it needs to be in scope prior to
// importing _variables.scss from Bootstrap.
diff --git a/netbox/project-static/styles/utilities.scss b/netbox/project-static/styles/old/utilities.scss
similarity index 100%
rename from netbox/project-static/styles/utilities.scss
rename to netbox/project-static/styles/old/utilities.scss
diff --git a/netbox/project-static/styles/select.scss b/netbox/project-static/styles/overrides/_slim-select.scss
similarity index 96%
rename from netbox/project-static/styles/select.scss
rename to netbox/project-static/styles/overrides/_slim-select.scss
index d8fcd218e..419f765cd 100644
--- a/netbox/project-static/styles/select.scss
+++ b/netbox/project-static/styles/overrides/_slim-select.scss
@@ -184,3 +184,14 @@ $spacing-s: $input-padding-x;
}
}
}
+
+// Fix slim-select 1.x placeholder styling
+.ss-main {
+ .ss-single-selected {
+ .placeholder {
+ cursor: pointer;
+ opacity: 1;
+ background-color: transparent !important;
+ }
+ }
+}
diff --git a/netbox/project-static/styles/svg/cable_trace.scss b/netbox/project-static/styles/svg/cable_trace.scss
index f184982ce..4a8fdf61a 100644
--- a/netbox/project-static/styles/svg/cable_trace.scss
+++ b/netbox/project-static/styles/svg/cable_trace.scss
@@ -1,4 +1,4 @@
-@import '../theme-light.scss';
+@import '../old/theme-light';
// Cable Trace Styles.
diff --git a/netbox/project-static/styles/svg/rack_elevation.scss b/netbox/project-static/styles/svg/rack_elevation.scss
index 0c08f167d..424dd823e 100644
--- a/netbox/project-static/styles/svg/rack_elevation.scss
+++ b/netbox/project-static/styles/svg/rack_elevation.scss
@@ -1,4 +1,4 @@
-@import '../theme-light.scss';
+@import '../old/theme-light';
// Rack Elevation Styles.
diff --git a/netbox/project-static/styles/temp.scss b/netbox/project-static/styles/temp.scss
deleted file mode 100644
index 5fa35f649..000000000
--- a/netbox/project-static/styles/temp.scss
+++ /dev/null
@@ -1,10 +0,0 @@
-// Fix slim-select 1.x placeholder styling
-.ss-main {
- .ss-single-selected {
- .placeholder {
- cursor: pointer;
- opacity: 1;
- background-color: transparent !important;
- }
- }
-}
diff --git a/netbox/project-static/styles/transitional/cards.scss b/netbox/project-static/styles/transitional/_cards.scss
similarity index 100%
rename from netbox/project-static/styles/transitional/cards.scss
rename to netbox/project-static/styles/transitional/_cards.scss
diff --git a/netbox/project-static/styles/transitional/tables.scss b/netbox/project-static/styles/transitional/_tables.scss
similarity index 100%
rename from netbox/project-static/styles/transitional/tables.scss
rename to netbox/project-static/styles/transitional/_tables.scss