CSS extracted into separate file
All checks were successful
Build containers when image tags change / build-if-image-changed (., web, containers, main container, git.baumann.gr/adebaumann/labhelper) (push) Successful in 26s
Build containers when image tags change / build-if-image-changed (data-loader, loader, initContainers, init-container, git.baumann.gr/adebaumann/labhelper-data-loader) (push) Successful in 8s

This commit is contained in:
2026-01-28 10:15:14 +01:00
parent dbfb38bb8a
commit 7bae0d12de
9 changed files with 552 additions and 534 deletions

View File

@@ -209,6 +209,11 @@ labhelper/
│ │ ├── clean_orphaned_files.py # Cleanup orphaned ThingFile attachments │ │ ├── clean_orphaned_files.py # Cleanup orphaned ThingFile attachments
│ │ └── clean_orphaned_images.py # Cleanup orphaned Thing images │ │ └── clean_orphaned_images.py # Cleanup orphaned Thing images
│ ├── migrations/ # Database migrations │ ├── migrations/ # Database migrations
│ ├── static/
│ │ └── css/
│ │ ├── base.css # Base styles (layout, navbar, buttons, alerts, etc.)
│ │ ├── edit_thing.css # Edit thing form styles
│ │ └── thing_detail.css # Markdown content + lightbox styles
│ ├── templates/ │ ├── templates/
│ │ └── boxes/ │ │ └── boxes/
│ │ ├── add_things.html # Form to add multiple things │ │ ├── add_things.html # Form to add multiple things
@@ -356,14 +361,21 @@ The project uses a base template system at `labhelper/templates/base.html`. All
### CSS Guidelines ### CSS Guidelines
**Static CSS Files:**
- Base styles live in `boxes/static/css/base.css` (loaded by `base.html` via `{% static %}`)
- Page-specific styles live in separate static CSS files (e.g., `thing_detail.css`, `edit_thing.css`)
- Child templates load their CSS via `{% block extra_css %}` with `<link>` tags
- WhiteNoise serves and cache-busts static files via `CompressedManifestStaticFilesStorage`
- Run `python manage.py collectstatic` after adding or modifying static CSS files
**Naming:** **Naming:**
- Use descriptive class names - Use descriptive class names
- BEM pattern encouraged for complex components - BEM pattern encouraged for complex components
- Inline styles allowed for template-specific styling - Inline styles allowed for template-specific one-off styling
**Styles:** **Styles:**
- Use base template styles when possible - Use base CSS classes when possible
- Template-specific styles in `{% block extra_css %}` - Page-specific styles in dedicated static CSS files loaded via `{% block extra_css %}`
- JavaScript in `{% block extra_js %}` - JavaScript in `{% block extra_js %}`
- Smooth transitions (0.2s - 0.3s) - Smooth transitions (0.2s - 0.3s)
- Hover effects with transform and box-shadow - Hover effects with transform and box-shadow
@@ -410,28 +422,36 @@ The project uses a base template system at `labhelper/templates/base.html`. All
- `title`: Page title tag - `title`: Page title tag
- `page_header`: Page header with breadcrumbs - `page_header`: Page header with breadcrumbs
- `content`: Main page content - `content`: Main page content
- `extra_css`: Additional styles - `extra_css`: Additional CSS via `<link>` tags to static files
- `extra_head`: Additional head elements
- `extra_js`: Additional JavaScript - `extra_js`: Additional JavaScript
3. **Load required template tags** 3. **Load required template tags**
```django ```django
{% load static %} {% load static %} {# Required when using {% static %} for CSS/asset links #}
{% load mptt_tags %} {% load mptt_tags %}
{% load thumbnail %} {% load thumbnail %}
{% load dict_extras %} {% load dict_extras %}
``` ```
4. **Use URL names for links** 4. **Link page-specific CSS from static files**
```django
{% block extra_css %}
<link rel="stylesheet" href="{% static 'css/thing_detail.css' %}">
{% endblock %}
```
5. **Use URL names for links**
```django ```django
<a href="{% url 'box_detail' box.id %}"> <a href="{% url 'box_detail' box.id %}">
``` ```
5. **Use icons with Font Awesome** 6. **Use icons with Font Awesome**
```django ```django
<i class="fas fa-box"></i> <i class="fas fa-box"></i>
``` ```
6. **Add breadcrumbs for navigation** 7. **Add breadcrumbs for navigation**
```django ```django
<p class="breadcrumb"> <p class="breadcrumb">
<a href="/"><i class="fas fa-home"></i> Home</a> / <a href="/"><i class="fas fa-home"></i> Home</a> /
@@ -439,7 +459,7 @@ The project uses a base template system at `labhelper/templates/base.html`. All
</p> </p>
``` ```
7. **Icon alignment in lists**: When using icons in list items, use fixed width containers to ensure proper alignment 8. **Icon alignment in lists**: When using icons in list items, use fixed width containers to ensure proper alignment
```django ```django
<a href="{% url 'thing_detail' thing.id %}" style="display: inline-block; width: 20px; text-align: center;"> <a href="{% url 'thing_detail' thing.id %}" style="display: inline-block; width: 20px; text-align: center;">
<i class="fas fa-link"></i> <i class="fas fa-link"></i>

View File

@@ -18,4 +18,4 @@ data:
LOGIN_REDIRECT_URL: "index" LOGIN_REDIRECT_URL: "index"
LOGOUT_REDIRECT_URL: "login" LOGOUT_REDIRECT_URL: "login"
GUNICORN_OPTS: "--access-logfile -" GUNICORN_OPTS: "--access-logfile -"
IMAGE_TAG: "0.074" IMAGE_TAG: "0.075"

View File

@@ -27,7 +27,7 @@ spec:
mountPath: /data mountPath: /data
containers: containers:
- name: web - name: web
image: git.baumann.gr/adebaumann/labhelper:0.074 image: git.baumann.gr/adebaumann/labhelper:0.075
imagePullPolicy: Always imagePullPolicy: Always
ports: ports:
- containerPort: 8000 - containerPort: 8000

378
boxes/static/css/base.css Normal file
View File

@@ -0,0 +1,378 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 0 20px;
}
.navbar {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 15px;
padding: 15px 30px;
margin: 20px auto;
max-width: 1200px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 15px;
}
.navbar-brand {
font-size: 28px;
font-weight: 700;
color: #667eea;
text-decoration: none;
display: flex;
align-items: center;
gap: 10px;
}
.navbar-brand i {
font-size: 24px;
}
.navbar-toggle {
display: none;
background: none;
border: none;
color: #555;
font-size: 24px;
cursor: pointer;
padding: 10px;
border-radius: 8px;
transition: all 0.3s ease;
}
.navbar-toggle:hover {
background: #667eea;
color: white;
}
.navbar-nav {
display: flex;
gap: 20px;
align-items: center;
}
.navbar-nav a,
.navbar-nav form {
color: #555;
text-decoration: none;
font-weight: 500;
font-size: 15px;
padding: 8px 16px;
border-radius: 8px;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 8px;
}
.navbar-nav a:hover,
.navbar-nav button:hover {
background: #667eea;
color: white;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
.navbar-nav a i {
font-size: 14px;
}
.navbar-nav button {
background: none;
border: none;
color: #555;
font: inherit;
cursor: pointer;
padding: 8px 16px;
border-radius: 8px;
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
gap: 8px;
}
@media (max-width: 768px) {
.navbar {
padding: 15px 20px;
}
.navbar-brand {
font-size: 24px;
}
.navbar-toggle {
display: block;
}
.navbar-nav {
display: none;
width: 100%;
flex-direction: column;
gap: 0;
padding-top: 10px;
}
.navbar-nav.active {
display: flex;
}
.navbar-nav a,
.navbar-nav form {
width: 100%;
padding: 12px 16px;
border-radius: 0;
}
.navbar-nav a:first-child,
.navbar-nav form:first-child {
border-radius: 8px 8px 0 0;
}
.navbar-nav a:last-child,
.navbar-nav form:last-child {
border-radius: 0 0 8px 8px;
}
.navbar-nav button {
width: 100%;
justify-content: flex-start;
}
}
.container {
max-width: 1200px;
margin: 20px auto;
}
.page-header {
background: white;
padding: 30px;
border-radius: 15px;
margin-bottom: 30px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}
.page-header h1 {
color: #333;
font-size: 32px;
font-weight: 700;
margin-bottom: 10px;
}
.page-header .breadcrumb {
color: #888;
font-size: 14px;
}
.page-header .breadcrumb a {
color: #667eea;
text-decoration: none;
}
.page-header .breadcrumb a:hover {
text-decoration: underline;
}
.section {
background: white;
padding: 30px;
border-radius: 15px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
margin-bottom: 30px;
}
.section h2 {
color: #667eea;
font-size: 24px;
font-weight: 700;
margin-top: 0;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 3px solid #667eea;
display: flex;
align-items: center;
gap: 10px;
}
.section h2 i {
font-size: 20px;
}
.btn {
display: inline-flex;
align-items: center;
gap: 8px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 12px 24px;
border: none;
border-radius: 10px;
font-size: 15px;
font-weight: 600;
cursor: pointer;
text-decoration: none;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
}
.btn:active {
transform: translateY(0);
}
.btn-secondary {
background: linear-gradient(135deg, #7f8c8d 0%, #95a5a6 100%);
box-shadow: 0 4px 15px rgba(127, 140, 141, 0.4);
}
.btn-secondary:hover {
box-shadow: 0 6px 20px rgba(127, 140, 141, 0.6);
}
.btn-sm {
padding: 8px 16px;
font-size: 14px;
}
.alert {
padding: 15px 20px;
border-radius: 10px;
margin-bottom: 20px;
font-weight: 500;
}
.alert-success {
background: linear-gradient(135deg, #00b894 0%, #00cec9 100%);
color: white;
box-shadow: 0 4px 15px rgba(0, 184, 148, 0.3);
}
.alert-error {
background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
color: white;
box-shadow: 0 4px 15px rgba(231, 76, 60, 0.3);
}
.footer {
text-align: center;
color: white;
padding: 30px;
margin-top: 30px;
}
.footer a {
color: white;
text-decoration: none;
font-weight: 500;
}
.footer a:hover {
text-decoration: underline;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
display: none;
position: absolute;
right: 0;
background: white;
min-width: 200px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
border-radius: 10px;
padding: 10px 0;
z-index: 1000;
top: 100%;
margin-top: 10px;
}
.dropdown-content a {
display: block;
padding: 12px 20px;
color: #555;
text-decoration: none;
transition: all 0.2s ease;
}
.dropdown-content a:hover {
background: #667eea;
color: white;
}
.dropdown-content a:first-child {
border-radius: 10px 10px 0 0;
}
.dropdown-content a:last-child {
border-radius: 0 0 10px 10px;
}
.dropdown-content button:hover {
background: #667eea;
color: white;
}
.dropdown:hover .dropdown-content,
.dropdown:focus-within .dropdown-content {
display: block;
}
.dropdown-btn {
background: none;
border: none;
color: #667eea;
font-weight: 600;
font-size: 15px;
cursor: pointer;
padding: 8px 16px;
border-radius: 8px;
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
gap: 8px;
}
.dropdown-btn:hover {
background: #667eea;
color: white;
}
@media (max-width: 768px) {
.dropdown-content {
position: static;
box-shadow: none;
border-radius: 0;
margin-top: 0;
padding: 0;
}
.dropdown-content a {
width: 100%;
padding: 12px 16px;
border-radius: 0;
}
.dropdown-btn {
width: 100%;
justify-content: flex-start;
}
}

View File

@@ -0,0 +1,21 @@
#id_name, #id_description {
width: 100%;
padding: 10px 15px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 15px;
background: white;
transition: all 0.3s;
}
#id_name:focus, #id_description:focus {
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
outline: none;
}
#id_description {
min-height: 120px;
font-family: inherit;
}
.detail-row {
margin-bottom: 25px;
}

View File

@@ -0,0 +1,115 @@
.markdown-content p {
margin: 0 0 1em 0;
}
.markdown-content p:last-child {
margin-bottom: 0;
}
.markdown-content h1, .markdown-content h2, .markdown-content h3,
.markdown-content h4, .markdown-content h5, .markdown-content h6 {
margin: 1.5em 0 0.5em 0;
color: #333;
font-weight: 600;
}
.markdown-content h1:first-child, .markdown-content h2:first-child,
.markdown-content h3:first-child {
margin-top: 0;
}
.markdown-content ul, .markdown-content ol {
margin: 0.5em 0;
padding-left: 2em;
}
.markdown-content li {
margin: 0.25em 0;
}
.markdown-content code {
background: #f4f4f4;
padding: 2px 6px;
border-radius: 4px;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 0.9em;
}
.markdown-content pre {
background: #f4f4f4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
margin: 1em 0;
}
.markdown-content pre code {
background: none;
padding: 0;
}
.markdown-content blockquote {
border-left: 4px solid #667eea;
margin: 1em 0;
padding: 0.5em 1em;
background: #f8f9fa;
color: #666;
}
.markdown-content a {
color: #667eea;
text-decoration: none;
}
.markdown-content a:hover {
text-decoration: underline;
}
.markdown-content table {
border-collapse: collapse;
width: 100%;
margin: 1em 0;
}
.markdown-content th, .markdown-content td {
border: 1px solid #e0e0e0;
padding: 8px 12px;
text-align: left;
}
.markdown-content th {
background: #f8f9fa;
font-weight: 600;
}
.markdown-content hr {
border: none;
border-top: 2px solid #e0e0e0;
margin: 1.5em 0;
}
.lightbox {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.9);
z-index: 9999;
justify-content: center;
align-items: center;
cursor: zoom-out;
}
.lightbox.active {
display: flex;
}
.lightbox img {
max-width: 90%;
max-height: 90%;
object-fit: contain;
border-radius: 8px;
box-shadow: 0 0 30px rgba(0, 0, 0, 0.5);
}
.lightbox-close {
position: absolute;
top: 20px;
right: 30px;
color: white;
font-size: 40px;
cursor: pointer;
z-index: 10000;
transition: opacity 0.2s;
}
.lightbox-close:hover {
opacity: 0.7;
}

View File

@@ -1,4 +1,5 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static %}
{% load thumbnail %} {% load thumbnail %}
{% load dict_extras %} {% load dict_extras %}
@@ -286,29 +287,7 @@
{% endblock %} {% endblock %}
{% block extra_css %} {% block extra_css %}
<style> <link rel="stylesheet" href="{% static 'css/edit_thing.css' %}">
#id_name, #id_description {
width: 100%;
padding: 10px 15px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 15px;
background: white;
transition: all 0.3s;
}
#id_name:focus, #id_description:focus {
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
outline: none;
}
#id_description {
min-height: 120px;
font-family: inherit;
}
.detail-row {
margin-bottom: 25px;
}
</style>
{% endblock %} {% endblock %}
{% block extra_js %} {% block extra_js %}

View File

@@ -1,4 +1,5 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static %}
{% load thumbnail %} {% load thumbnail %}
{% load dict_extras %} {% load dict_extras %}
@@ -169,121 +170,5 @@ $(document).ready(function() {
{% endblock %} {% endblock %}
{% block extra_css %} {% block extra_css %}
<style> <link rel="stylesheet" href="{% static 'css/thing_detail.css' %}">
.markdown-content p {
margin: 0 0 1em 0;
}
.markdown-content p:last-child {
margin-bottom: 0;
}
.markdown-content h1, .markdown-content h2, .markdown-content h3,
.markdown-content h4, .markdown-content h5, .markdown-content h6 {
margin: 1.5em 0 0.5em 0;
color: #333;
font-weight: 600;
}
.markdown-content h1:first-child, .markdown-content h2:first-child,
.markdown-content h3:first-child {
margin-top: 0;
}
.markdown-content ul, .markdown-content ol {
margin: 0.5em 0;
padding-left: 2em;
}
.markdown-content li {
margin: 0.25em 0;
}
.markdown-content code {
background: #f4f4f4;
padding: 2px 6px;
border-radius: 4px;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 0.9em;
}
.markdown-content pre {
background: #f4f4f4;
padding: 15px;
border-radius: 8px;
overflow-x: auto;
margin: 1em 0;
}
.markdown-content pre code {
background: none;
padding: 0;
}
.markdown-content blockquote {
border-left: 4px solid #667eea;
margin: 1em 0;
padding: 0.5em 1em;
background: #f8f9fa;
color: #666;
}
.markdown-content a {
color: #667eea;
text-decoration: none;
}
.markdown-content a:hover {
text-decoration: underline;
}
.markdown-content table {
border-collapse: collapse;
width: 100%;
margin: 1em 0;
}
.markdown-content th, .markdown-content td {
border: 1px solid #e0e0e0;
padding: 8px 12px;
text-align: left;
}
.markdown-content th {
background: #f8f9fa;
font-weight: 600;
}
.markdown-content hr {
border: none;
border-top: 2px solid #e0e0e0;
margin: 1.5em 0;
}
.lightbox {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.9);
z-index: 9999;
justify-content: center;
align-items: center;
cursor: zoom-out;
}
.lightbox.active {
display: flex;
}
.lightbox img {
max-width: 90%;
max-height: 90%;
object-fit: contain;
border-radius: 8px;
box-shadow: 0 0 30px rgba(0, 0, 0, 0.5);
}
.lightbox-close {
position: absolute;
top: 20px;
right: 30px;
color: white;
font-size: 40px;
cursor: pointer;
z-index: 10000;
transition: opacity 0.2s;
}
.lightbox-close:hover {
opacity: 0.7;
}
</style>
{% endblock %} {% endblock %}

View File

@@ -7,388 +7,8 @@
<title>{% block title %}LabHelper{% endblock %}</title> <title>{% block title %}LabHelper{% endblock %}</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<style> <link rel="stylesheet" href="{% static 'css/base.css' %}">
* { {% block extra_css %}{% endblock %}
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 0 20px;
}
.navbar {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 15px;
padding: 15px 30px;
margin: 20px auto;
max-width: 1200px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 15px;
}
.navbar-brand {
font-size: 28px;
font-weight: 700;
color: #667eea;
text-decoration: none;
display: flex;
align-items: center;
gap: 10px;
}
.navbar-brand i {
font-size: 24px;
}
.navbar-toggle {
display: none;
background: none;
border: none;
color: #555;
font-size: 24px;
cursor: pointer;
padding: 10px;
border-radius: 8px;
transition: all 0.3s ease;
}
.navbar-toggle:hover {
background: #667eea;
color: white;
}
.navbar-nav {
display: flex;
gap: 20px;
align-items: center;
}
.navbar-nav a,
.navbar-nav form {
color: #555;
text-decoration: none;
font-weight: 500;
font-size: 15px;
padding: 8px 16px;
border-radius: 8px;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 8px;
}
.navbar-nav a:hover,
.navbar-nav button:hover {
background: #667eea;
color: white;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
.navbar-nav a i {
font-size: 14px;
}
.navbar-nav button {
background: none;
border: none;
color: #555;
font: inherit;
cursor: pointer;
padding: 8px 16px;
border-radius: 8px;
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
gap: 8px;
}
@media (max-width: 768px) {
.navbar {
padding: 15px 20px;
}
.navbar-brand {
font-size: 24px;
}
.navbar-toggle {
display: block;
}
.navbar-nav {
display: none;
width: 100%;
flex-direction: column;
gap: 0;
padding-top: 10px;
}
.navbar-nav.active {
display: flex;
}
.navbar-nav a,
.navbar-nav form {
width: 100%;
padding: 12px 16px;
border-radius: 0;
}
.navbar-nav a:first-child,
.navbar-nav form:first-child {
border-radius: 8px 8px 0 0;
}
.navbar-nav a:last-child,
.navbar-nav form:last-child {
border-radius: 0 0 8px 8px;
}
.navbar-nav button {
width: 100%;
justify-content: flex-start;
}
}
.container {
max-width: 1200px;
margin: 20px auto;
}
.page-header {
background: white;
padding: 30px;
border-radius: 15px;
margin-bottom: 30px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
}
.page-header h1 {
color: #333;
font-size: 32px;
font-weight: 700;
margin-bottom: 10px;
}
.page-header .breadcrumb {
color: #888;
font-size: 14px;
}
.page-header .breadcrumb a {
color: #667eea;
text-decoration: none;
}
.page-header .breadcrumb a:hover {
text-decoration: underline;
}
.section {
background: white;
padding: 30px;
border-radius: 15px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
margin-bottom: 30px;
}
.section h2 {
color: #667eea;
font-size: 24px;
font-weight: 700;
margin-top: 0;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 3px solid #667eea;
display: flex;
align-items: center;
gap: 10px;
}
.section h2 i {
font-size: 20px;
}
.btn {
display: inline-flex;
align-items: center;
gap: 8px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 12px 24px;
border: none;
border-radius: 10px;
font-size: 15px;
font-weight: 600;
cursor: pointer;
text-decoration: none;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6);
}
.btn:active {
transform: translateY(0);
}
.btn-secondary {
background: linear-gradient(135deg, #7f8c8d 0%, #95a5a6 100%);
box-shadow: 0 4px 15px rgba(127, 140, 141, 0.4);
}
.btn-secondary:hover {
box-shadow: 0 6px 20px rgba(127, 140, 141, 0.6);
}
.btn-sm {
padding: 8px 16px;
font-size: 14px;
}
.alert {
padding: 15px 20px;
border-radius: 10px;
margin-bottom: 20px;
font-weight: 500;
}
.alert-success {
background: linear-gradient(135deg, #00b894 0%, #00cec9 100%);
color: white;
box-shadow: 0 4px 15px rgba(0, 184, 148, 0.3);
}
.alert-error {
background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
color: white;
box-shadow: 0 4px 15px rgba(231, 76, 60, 0.3);
}
.footer {
text-align: center;
color: white;
padding: 30px;
margin-top: 30px;
}
.footer a {
color: white;
text-decoration: none;
font-weight: 500;
}
.footer a:hover {
text-decoration: underline;
}
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-content {
display: none;
position: absolute;
right: 0;
background: white;
min-width: 200px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
border-radius: 10px;
padding: 10px 0;
z-index: 1000;
top: 100%;
margin-top: 10px;
}
.dropdown-content a {
display: block;
padding: 12px 20px;
color: #555;
text-decoration: none;
transition: all 0.2s ease;
}
.dropdown-content a:hover {
background: #667eea;
color: white;
}
.dropdown-content a:first-child {
border-radius: 10px 10px 0 0;
}
.dropdown-content a:last-child {
border-radius: 0 0 10px 10px;
}
.dropdown-content button:hover {
background: #667eea;
color: white;
}
.dropdown:hover .dropdown-content,
.dropdown:focus-within .dropdown-content {
display: block;
}
.dropdown-btn {
background: none;
border: none;
color: #667eea;
font-weight: 600;
font-size: 15px;
cursor: pointer;
padding: 8px 16px;
border-radius: 8px;
transition: all 0.3s ease;
display: inline-flex;
align-items: center;
gap: 8px;
}
.dropdown-btn:hover {
background: #667eea;
color: white;
}
@media (max-width: 768px) {
.dropdown-content {
position: static;
box-shadow: none;
border-radius: 0;
margin-top: 0;
padding: 0;
}
.dropdown-content a {
width: 100%;
padding: 12px 16px;
border-radius: 0;
}
.dropdown-btn {
width: 100%;
justify-content: flex-start;
}
}
{% block extra_css %}{% endblock %}
</style>
{% block extra_head %}{% endblock %} {% block extra_head %}{% endblock %}
</head> </head>
<body> <body>