/** Django-specific fixes for AdminLTE v4 / Bootstrap 5 UI **/ /* ============================================================================ DJANGO ADMIN CSS VARIABLES - Map to Bootstrap 5 equivalents Django's admin CSS (base.css, changelists.css, forms.css) uses these variables. Without them, pagination, tables, and forms look unstyled. ========================================================================== */ :root { --bs-body-font-size: 0.875rem; /* Django admin variable overrides */ --primary: var(--bs-primary); --secondary: var(--bs-secondary); --accent: var(--bs-warning); --primary-fg: #fff; --body-fg: var(--bs-body-color); --body-bg: var(--bs-body-bg); --body-quiet-color: var(--bs-secondary-color); --body-medium-color: var(--bs-body-color); --body-loud-color: var(--bs-body-color); --header-color: var(--bs-white); --header-branding-color: var(--bs-warning); --header-bg: var(--bs-primary); --header-link-color: var(--bs-white); --breadcrumbs-fg: var(--bs-secondary-color); --breadcrumbs-link-fg: var(--bs-white); --breadcrumbs-bg: var(--bs-primary); --link-fg: var(--bs-link-color); --link-hover-color: var(--bs-link-hover-color); --link-selected-fg: var(--bs-primary); --hairline-color: var(--bs-border-color); --border-color: var(--bs-border-color); --error-fg: var(--bs-danger); --message-debug-bg: var(--bs-tertiary-bg); --message-info-bg: #ccefff; --message-success-bg: #dfd; --message-warning-bg: #ffc; --message-error-bg: #ffefef; --darkened-bg: var(--bs-tertiary-bg); --selected-bg: var(--bs-tertiary-bg); --selected-row: rgba(var(--bs-primary-rgb), 0.1); --button-fg: var(--bs-white); --button-bg: var(--bs-primary); --button-hover-bg: var(--bs-primary); --default-button-bg: var(--bs-primary); --default-button-hover-bg: var(--bs-primary); --close-button-bg: var(--bs-secondary); --close-button-hover-bg: var(--bs-secondary); --delete-button-bg: var(--bs-danger); --delete-button-hover-bg: var(--bs-danger); --object-tools-fg: var(--bs-white); --object-tools-bg: var(--bs-secondary); --object-tools-hover-bg: var(--bs-secondary); --font-family-primary: var(--bs-body-font-family); --font-family-monospace: var(--bs-font-monospace); } /* Dark mode variable overrides */ html[data-bs-theme="dark"] { --message-debug-bg: var(--bs-tertiary-bg); --message-info-bg: #1a3a4a; --message-success-bg: #1a3a1a; --message-warning-bg: #3a3a1a; --message-error-bg: #3a1a1a; --selected-row: rgba(var(--bs-primary-rgb), 0.2); } /* ============================================================================ GLOBAL LINK STYLING - Remove default Bootstrap 5 underlines in admin ========================================================================== */ .app-wrapper a { text-decoration: none; } /* ============================================================================ LOGIN LOGO - Light/Dark Mode Support ========================================================================== */ .login-logo .logo-dark { display: none; } .login-logo .logo-light { display: inline; } html[data-bs-theme="dark"] .login-logo .logo-light { display: none; } html[data-bs-theme="dark"] .login-logo .logo-dark { display: inline; } /* ============================================================================ DJANGO FORM INLINES - Hide empty extra form rows ========================================================================== */ .empty-form { display: none !important; } /* Django's .form-row conflicts with Bootstrap - ensure table display */ .table tr.form-row { display: table-row; } /* ============================================================================ DJANGO FORM FIELDS - Style Django's widget classes to match Bootstrap ========================================================================== */ .vTextField, .vLargeTextField, .vURLField, .vIntegerField, .vBigIntegerField, .vForeignKeyRawIdAdminField, .vDateField, .vTimeField { height: calc(2.25rem + 2px); padding: .375rem .75rem; font-size: var(--bs-body-font-size); font-weight: 400; line-height: 1.5; background-color: var(--bs-body-bg); background-clip: padding-box; border: 1px solid var(--bs-border-color); border-radius: .375rem; color: var(--bs-body-color); transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out; } .vTextField:focus, .vLargeTextField:focus, .vURLField:focus, .vIntegerField:focus, .vBigIntegerField:focus, .vForeignKeyRawIdAdminField:focus, .vDateField:focus, .vTimeField:focus { border-color: var(--bs-primary); outline: 0; box-shadow: 0 0 0 0.25rem rgba(var(--bs-primary-rgb), 0.25); } .vDateField, .vTimeField { display: inline-block; min-width: 200px; } .vLargeTextField { height: auto; min-height: calc(2.25rem + 2px); } /* Dark mode theming for bare inputs (no .form-control class) */ input[type="text"], input[type="email"], input[type="url"], input[type="number"], input[type="date"], input[type="time"], input[type="datetime-local"], input[type="password"], textarea { background-color: var(--bs-body-bg); border-color: var(--bs-border-color); color: var(--bs-body-color); } /* Style Django's plain select elements to match Bootstrap 5 form-select */ select:not(.form-select):not([multiple]) { display: block; width: 100%; padding: .375rem 2.25rem .375rem .75rem; font-size: var(--bs-body-font-size); font-weight: 400; line-height: 1.5; color: var(--bs-body-color); appearance: none; background-color: var(--bs-body-bg); background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); background-repeat: no-repeat; background-position: right .75rem center; background-size: 16px 12px; border: 1px solid var(--bs-border-color); border-radius: .375rem; transition: border-color .15s ease-in-out, box-shadow .15s ease-in-out; } select:not(.form-select):not([multiple]):focus { border-color: var(--bs-primary); outline: 0; box-shadow: 0 0 0 .25rem rgba(var(--bs-primary-rgb), .25); } html[data-bs-theme="dark"] select:not(.form-select):not([multiple]) { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23adb5bd' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); } /* Inline selects (action bars) should not be full width */ .actions select { width: auto; display: inline-block; } /* ============================================================================ DJANGO CALENDAR & CLOCK WIDGETS ========================================================================== */ .calendarbox, .clockbox { margin: 5px auto; font-size: 12px; width: 19em; text-align: center; background-color: var(--bs-body-bg); border: 1px solid var(--bs-border-color); border-radius: .375rem; box-shadow: var(--bs-box-shadow-sm); overflow: hidden; z-index: 1000; } .clockbox { width: auto; } .calendar table { margin: 0; padding: 0; border-collapse: collapse; width: 100%; } .calendar th { padding: 8px 5px; border-bottom: 1px solid var(--bs-border-color); font-weight: 400; font-size: 12px; } .calendar td { font-weight: 400; font-size: 12px; padding: 0; border-top: 1px solid var(--bs-border-color); } .calendar td.selected a { background: var(--bs-primary); color: #fff !important; } .calendar td a, .timelist a { display: block; padding: 6px; } .calendar td a:hover, .timelist a:hover { background: var(--bs-primary); color: white; } .date-icon::before, .clock-icon::before { font-family: "Font Awesome 5 Free" !important; display: inline-block; font: normal normal normal 14px/1 FontAwesome !important; content: "\f073"; } .clock-icon::before { content: "\f017"; } /* ============================================================================ DJANGO RELATED WIDGET - FK selects with edit/add/view icons ========================================================================== */ .related-widget-wrapper { display: flex; align-items: center; gap: 0.25rem; } .related-widget-wrapper .select2-container { flex: 1; min-width: 0; } .related-widget-wrapper-link { flex-shrink: 0; } /* ============================================================================ DJANGO SIDEBAR - Fixed layout scrolling ========================================================================== */ .layout-fixed #jazzy-sidebar { top: 0; bottom: 0; overflow-y: scroll; overflow-x: hidden; scrollbar-width: none; } .layout-fixed #jazzy-sidebar::-webkit-scrollbar { width: 0; } #jazzy-sidebar .nav-sidebar > .nav-item { width: 100%; } /* ============================================================================ SELECT2 - Dark theme support ========================================================================== */ html[data-bs-theme="dark"] .select2-container--default .select2-selection--single, html[data-bs-theme="dark"] .select2-container--default .select2-selection--multiple { background-color: var(--bs-body-bg); border-color: var(--bs-border-color); color: var(--bs-body-color); } html[data-bs-theme="dark"] .select2-container--default .select2-selection--single .select2-selection__rendered, html[data-bs-theme="dark"] .select2-container--default .select2-selection--multiple .select2-selection__rendered { color: var(--bs-body-color); } html[data-bs-theme="dark"] .select2-dropdown { background-color: var(--bs-body-bg); border-color: var(--bs-border-color); } html[data-bs-theme="dark"] .select2-container--default .select2-results__option { background-color: var(--bs-body-bg); color: var(--bs-body-color); } html[data-bs-theme="dark"] .select2-container--default .select2-results__option--highlighted[aria-selected] { background-color: var(--bs-primary); color: var(--bs-white); } /* SELECT2 - Border theming */ .select2-container--default .select2-selection--single, .select2-container--default .select2-selection--multiple { border: 1px solid var(--bs-border-color); border-radius: .375rem; } /* ============================================================================ FORM SPACING - Django form-row / form-group ========================================================================== */ .form-row { margin-bottom: 1rem; } .form-group { margin-bottom: 1rem; } .inline-related .form-row, .inline-related .form-group { padding: 0.5rem; } /* ============================================================================ HORIZONTAL SELECTORS - filter_horizontal / filter_vertical (permissions) ========================================================================== */ .selector { display: flex; align-items: stretch; width: 100%; } .selector-available, .selector-chosen { flex: 1; min-width: 0; } .selector ul.selector-chooser { flex: 0 0 auto; display: flex; flex-direction: column; justify-content: center; padding: 0.5rem; margin: 0; list-style: none; gap: 0.25rem; } .selector ul.selector-chooser li { list-style: none; } .selector ul.selector-chooser a { display: block; width: 2rem; height: 2rem; line-height: 2rem; text-align: center; background-color: var(--bs-secondary-bg); border: 1px solid var(--bs-border-color); border-radius: .375rem; color: var(--bs-body-color); font-size: 0; } .selector ul.selector-chooser a::before { font-family: "Font Awesome 5 Free"; font-weight: 900; font-size: 0.75rem; } .selector ul.selector-chooser a.selector-chooseall::before { content: "\f101"; } .selector ul.selector-chooser a.selector-add::before { content: "\f054"; } .selector ul.selector-chooser a.selector-remove::before { content: "\f053"; } .selector ul.selector-chooser a.selector-clearall::before { content: "\f100"; } .selector ul.selector-chooser a:hover { background-color: var(--bs-primary); border-color: var(--bs-primary); color: #fff; } .selector select { width: 100%; height: 15em; padding: .25rem .5rem; font-size: var(--bs-body-font-size); color: var(--bs-body-color); background-color: var(--bs-body-bg); border: 1px solid var(--bs-border-color); border-radius: 0 0 .375rem .375rem; border-top: none; } .selector-available h2, .selector-chosen h2 { margin: 0; padding: 0.375rem 0.75rem; background-color: var(--bs-secondary-bg); border: 1px solid var(--bs-border-color); border-radius: .375rem .375rem 0 0; font-size: 0.8125rem; font-weight: 600; } .selector .selector-filter { padding: 0.375rem; border: 1px solid var(--bs-border-color); border-top: none; background-color: var(--bs-body-bg); } .selector .selector-filter label { display: none; } .selector .selector-filter input { width: 100%; padding: .25rem .5rem; font-size: var(--bs-body-font-size); border: 1px solid var(--bs-border-color); border-radius: .25rem; background-color: var(--bs-body-bg); color: var(--bs-body-color); } html[data-bs-theme="dark"] .selector select option:checked { background-color: var(--bs-primary); color: var(--bs-white); } /* ============================================================================ ACTIONS PANEL - Detail page sidebar (save, delete, history, etc.) ========================================================================== */ #jazzy-actions { position: sticky; top: 4rem; } #jazzy-actions .d-flex { flex-direction: column; } #jazzy-actions .d-flex .btn, #jazzy-actions .d-flex input[type="submit"] { width: 100%; } #jazzy-actions .object-tools { list-style: none; padding: 0; margin: 1rem 0 0 0; } #jazzy-actions .object-tools li { margin-bottom: 0; } #jazzy-actions .object-tools li a { display: block; padding: .25rem .5rem; font-size: var(--bs-body-font-size); text-align: center; border-radius: .375rem; background-color: var(--bs-secondary-bg); border: 1px solid var(--bs-border-color); color: var(--bs-body-color); } #jazzy-actions .object-tools li a:hover { background-color: var(--bs-tertiary-bg); } /* ============================================================================ TAB CONTENT - Prevent inline overflow in tabbed change forms ========================================================================== */ .tab-content { overflow-x: auto; } .tab-pane .inline-related { overflow-x: auto; } /* ============================================================================ HELP TEXT AND ERRORS ========================================================================== */ .help-block, .helptext { font-size: 0.8125rem; color: var(--bs-secondary-color); margin-top: 0.25rem; display: block; } .help-block.red, .errorlist { color: var(--bs-danger); font-size: 0.8125rem; } /* Required field indicator */ .text-red { color: var(--bs-danger); } /* Readonly fields */ .readonly { padding: .375rem .75rem; background-color: var(--bs-secondary-bg); border: 1px solid var(--bs-border-color); border-radius: .375rem; color: var(--bs-secondary-color); } /* ============================================================================ CHANGELIST - Checkbox select-all column ========================================================================== */ .djn-checkbox-select-all { width: 1.5em; text-align: center; padding-right: 0 !important; } .table td.action-checkbox { width: 1.5em; text-align: center; padding-right: 0 !important; } /* ============================================================================ USER PANEL - Sidebar user info (not a native AdminLTE v4 component) ========================================================================== */ .user-panel { padding: 0.5rem; border-bottom: 1px solid rgba(255, 255, 255, 0.1); } .user-panel .info { display: flex; align-items: center; padding-left: 0.5rem; } /* ============================================================================ PAGINATION - Style Django's .paginator as Bootstrap pagination ========================================================================== */ .paginator { display: flex; align-items: center; justify-content: center; gap: 0.25rem; padding: 0.75rem; } .paginator .this-page, .paginator a { display: inline-flex; align-items: center; justify-content: center; min-width: 2rem; height: 2rem; padding: 0 0.5rem; font-size: var(--bs-body-font-size); border-radius: .375rem; text-decoration: none; transition: all .15s ease-in-out; } .paginator .this-page { background-color: var(--bs-primary); color: var(--bs-white); font-weight: 600; border: 1px solid var(--bs-primary); } .paginator a { background-color: var(--bs-body-bg); color: var(--bs-body-color); border: 1px solid var(--bs-border-color); } .paginator a:hover { background-color: var(--bs-primary); color: var(--bs-white); border-color: var(--bs-primary); text-decoration: none; } .paginator .showall, .paginator .showall a { margin-left: 0.5rem; font-size: 0.8125rem; color: var(--bs-secondary-color); } .paginator .showall a:hover { color: var(--bs-primary); background: none; border: none; } #changelist .paginator { border-top: 1px solid var(--bs-border-color); border-bottom: 1px solid var(--bs-border-color); background: var(--bs-body-bg); } /* ============================================================================ CHANGELIST TABLES - Ensure proper styling within AdminLTE ========================================================================== */ #changelist table { width: 100%; margin-bottom: 0; } #changelist table thead th { padding: 0.5rem; white-space: nowrap; vertical-align: middle; border-bottom: 2px solid var(--bs-border-color); background: var(--bs-tertiary-bg); font-weight: 600; font-size: var(--bs-body-font-size); text-align: left; } #changelist table tbody td { padding: 0.5rem; border-bottom: 1px solid var(--bs-border-color); font-size: var(--bs-body-font-size); vertical-align: middle; } #changelist table tbody tr:hover { background-color: var(--bs-tertiary-bg); } #changelist table input { margin: 0; vertical-align: middle; } #changelist .results { overflow-x: auto; width: 100%; } /* ============================================================================ DJANGO ADMIN BUTTONS - Style as Bootstrap buttons ========================================================================== */ #changelist .actions .button, .submit-row input[type="submit"], .submit-row button { display: inline-block; padding: 0.375rem 0.75rem; font-size: var(--bs-body-font-size); font-weight: 400; line-height: 1.5; border-radius: .375rem; cursor: pointer; transition: all .15s ease-in-out; border: 1px solid var(--bs-border-color); background-color: var(--bs-body-bg); color: var(--bs-body-color); } #changelist .actions .button:hover, .submit-row input[type="submit"]:hover, .submit-row button:hover { border-color: var(--bs-primary); color: var(--bs-primary); } .submit-row input[type="submit"].default, .submit-row input[name="_save"] { background-color: var(--bs-primary); color: var(--bs-white); border-color: var(--bs-primary); } .submit-row input[type="submit"].default:hover, .submit-row input[name="_save"]:hover { background-color: var(--bs-primary); opacity: 0.9; color: var(--bs-white); } .submit-row input[name="_delete"] { background-color: var(--bs-danger); color: var(--bs-white); border-color: var(--bs-danger); } .submit-row input[name="_delete"]:hover { opacity: 0.9; color: var(--bs-white); } /* ============================================================================ TOOLBAR & SEARCH - Style Django admin toolbar ========================================================================== */ #toolbar { padding: 0.5rem 0.75rem; margin-bottom: 0.75rem; border: 1px solid var(--bs-border-color); border-radius: .375rem; background: var(--bs-tertiary-bg); } #toolbar #searchbar { height: calc(1.5rem + 0.75rem + 2px); padding: 0.375rem 0.75rem; font-size: var(--bs-body-font-size); border: 1px solid var(--bs-border-color); border-radius: .375rem; max-width: 100%; background-color: var(--bs-body-bg); color: var(--bs-body-color); } #toolbar #searchbar:focus { border-color: var(--bs-primary); outline: 0; box-shadow: 0 0 0 0.25rem rgba(var(--bs-primary-rgb), 0.25); } /* ============================================================================ CHANGE LIST FILTERS - Style the filter sidebar ========================================================================== */ #changelist-filter { flex: 0 0 240px; background: var(--bs-tertiary-bg); border: 1px solid var(--bs-border-color); border-radius: .375rem; padding: 0; margin: 0 0 0 1rem; } #changelist-filter h2 { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.5px; padding: 0.5rem 0.75rem; margin: 0; border-bottom: 1px solid var(--bs-border-color); color: var(--bs-secondary-color); font-weight: 600; } #changelist-filter h3, #changelist-filter details summary { font-weight: 600; padding: 0.5rem 0.75rem; margin: 0; font-size: var(--bs-body-font-size); color: var(--bs-body-color); } #changelist-filter ul { margin: 0; padding: 0 0.75rem 0.75rem; border-bottom: 1px solid var(--bs-border-color); } #changelist-filter ul:last-child { border-bottom: none; } #changelist-filter li { list-style: none; padding: 0.25rem 0; } #changelist-filter li.selected { font-weight: 600; color: var(--bs-primary); } #changelist-filter a { color: var(--bs-body-color); text-decoration: none; } #changelist-filter a:hover { color: var(--bs-primary); }