Die Leiste war eine flache Reihe aus sieben gleichrangigen Pillen. Zwei Probleme: sie zeigte nie an, wo man gerade ist (es gab schlicht keinen Aktiv-Zustand), und auf dem Handy fielen nur die Beschriftungen weg - uebrig blieben sieben unbeschriftete Icons. Struktur folgt jetzt dem tatsaechlichen Ablauf (finden -> bewerben -> antworten) statt der Reihenfolge, in der die Seiten entstanden sind: Bewerbungen | Jobangebote | Stellensuche v | Postfach | Vorlagen "Stellensuche" buendelt als Dropdown, was Angebote erzeugt und filtert: Suchprofil & Laeufe, Uebernommene Angebote, Blacklist - jeweils mit einer Zeile, die erklaert, wozu der Punkt gut ist. Einstellungen, Benutzerverwaltung und Abmelden wandern ins Konto-Menue rechts, wo Systemkram hingehoert. Die taeglichen Wege (Bewerbungen, Jobangebote, Postfach) bleiben ein Klick weit weg. Gestaltung: Die Leiste behaelt in beiden Themes ihr dunkles Navy - sie ist der Rahmen des Werkzeugs und die einzige Flaeche, die sich beim Navigieren nie aendert. Alles darin ist bewusst still (Slate auf Navy); der einzige Akzent ist der aktive Punkt, markiert durch eine schmale leuchtende Schiene an der Unterkante. Beim Hover deutet sich dieselbe Schiene gedaempft an. Zaehler (ungelesene Post, offene Angebote) haengen jetzt an Klassen statt IDs, weil sie in Leiste UND Mobilmenue erscheinen. Ein Controller bedient alle Dropdowns: Klick oeffnet, andere schliessen, Escape und Klick daneben schliessen, aria-expanded bleibt ehrlich. Fokus ist sichtbar, prefers-reduced-motion wird respektiert, das Mobilmenue zeigt alle Punkte ausgeschrieben und gruppiert. Der aktive Pfad kommt aus res.locals.pfad (Auth-Middleware). Die Navigation liegt weiterhin in genau einem Partial - sie ist damit auf allen Seiten identisch; nur die Login-Seite hat bewusst keine. Geprueft mit echtem Browser (Screenshots): hell/dunkel, Dropdowns, Mobilmenue, und der Aktiv-Zustand je Seite - Unterseiten wie /blacklist oder /jobsuche heben korrekt "Stellensuche" hervor. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
459 lines
11 KiB
CSS
459 lines
11 KiB
CSS
/* ============================================
|
|
Bewerbungs-Tracker - Custom Styles
|
|
============================================ */
|
|
|
|
/* Base styles */
|
|
* {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
/* ============================================
|
|
Top navigation (views/partials/header.ejs)
|
|
============================================
|
|
The bar keeps its deep navy in both themes: it frames the light content area
|
|
like the chrome of a tool, and it is the one surface that never changes as you
|
|
move through the app. Everything in it is quiet — slate on navy — except the
|
|
active item, which is the single accent: a thin, glowing sky rail. That rail is
|
|
the whole point, since the previous header had no active state at all.
|
|
The dropdown panels follow the theme (white / slate) so they read as content. */
|
|
|
|
.nav-item {
|
|
position: relative;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.375rem;
|
|
padding: 0.5rem 0.75rem;
|
|
border-radius: 0.5rem;
|
|
font-size: 0.875rem;
|
|
font-weight: 500;
|
|
letter-spacing: -0.01em;
|
|
color: rgb(148 163 184); /* slate-400 */
|
|
white-space: nowrap;
|
|
transition: color 150ms ease, background-color 150ms ease;
|
|
}
|
|
button.nav-item, .icon-btn { cursor: pointer; }
|
|
.nav-item:hover {
|
|
color: #fff;
|
|
background-color: rgba(255, 255, 255, 0.06);
|
|
}
|
|
.nav-item.is-active {
|
|
color: #fff;
|
|
}
|
|
.nav-item:focus-visible {
|
|
outline: none;
|
|
box-shadow: 0 0 0 2px #0b1220, 0 0 0 4px rgba(56, 189, 248, 0.7);
|
|
}
|
|
|
|
/* The rail: sits on the header's bottom edge, lights up for the active section
|
|
and previews faintly on hover. */
|
|
.nav-rail {
|
|
position: absolute;
|
|
left: 0.75rem;
|
|
right: 0.75rem;
|
|
bottom: -1px;
|
|
height: 2px;
|
|
border-radius: 9999px;
|
|
background-color: transparent;
|
|
transform: scaleX(0);
|
|
transition: transform 200ms ease, background-color 200ms ease;
|
|
}
|
|
.nav-item:hover .nav-rail {
|
|
background-color: rgba(148, 163, 184, 0.45);
|
|
transform: scaleX(1);
|
|
}
|
|
.nav-item.is-active .nav-rail {
|
|
background-color: rgb(56 189 248); /* sky-400 */
|
|
box-shadow: 0 0 12px rgba(56, 189, 248, 0.8);
|
|
transform: scaleX(1);
|
|
}
|
|
|
|
/* Counters shown inline next to a nav label.
|
|
`:not(.hidden)` is deliberate: a bare `display: inline-flex` would fight
|
|
Tailwind's `.hidden` (same specificity, load order decides) and the badge would
|
|
show even at count 0. This way "hidden" always wins. */
|
|
.nav-badge:not(.hidden) {
|
|
display: inline-flex;
|
|
}
|
|
.nav-badge {
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-width: 1.125rem;
|
|
height: 1.125rem;
|
|
padding: 0 0.3rem;
|
|
border-radius: 9999px;
|
|
font-size: 0.625rem;
|
|
font-weight: 700;
|
|
line-height: 1;
|
|
color: #fff;
|
|
}
|
|
.nav-badge--red { background-color: rgb(244 63 94); } /* rose-500 */
|
|
.nav-badge--green { background-color: rgb(16 185 129); } /* emerald-500 */
|
|
|
|
/* Icon-only controls in the bar (bell, burger). */
|
|
.icon-btn {
|
|
position: relative;
|
|
display: grid;
|
|
place-items: center;
|
|
width: 2.25rem;
|
|
height: 2.25rem;
|
|
border-radius: 0.5rem;
|
|
color: rgb(148 163 184);
|
|
transition: color 150ms ease, background-color 150ms ease;
|
|
}
|
|
.icon-btn:hover {
|
|
color: #fff;
|
|
background-color: rgba(255, 255, 255, 0.08);
|
|
}
|
|
.icon-btn:focus-visible {
|
|
outline: none;
|
|
box-shadow: 0 0 0 2px #0b1220, 0 0 0 4px rgba(56, 189, 248, 0.7);
|
|
}
|
|
|
|
/* Count badge floating on an icon button. Same `:not(.hidden)` reason as above. */
|
|
.dot-badge:not(.hidden) {
|
|
display: inline-flex;
|
|
}
|
|
.dot-badge {
|
|
position: absolute;
|
|
top: -0.125rem;
|
|
right: -0.125rem;
|
|
align-items: center;
|
|
justify-content: center;
|
|
min-width: 1.125rem;
|
|
height: 1.125rem;
|
|
padding: 0 0.25rem;
|
|
border-radius: 9999px;
|
|
font-size: 0.625rem;
|
|
font-weight: 700;
|
|
line-height: 1;
|
|
color: #fff;
|
|
box-shadow: 0 0 0 2px #0b1220;
|
|
}
|
|
.dot-badge--red { background-color: rgb(244 63 94); }
|
|
|
|
/* Dropdown panels. */
|
|
.dd-panel {
|
|
position: absolute;
|
|
right: 0;
|
|
top: calc(100% + 0.5rem);
|
|
z-index: 50;
|
|
padding: 0.375rem;
|
|
border-radius: 0.75rem;
|
|
background-color: #fff;
|
|
box-shadow: 0 20px 40px -12px rgba(0, 0, 0, 0.35);
|
|
border: 1px solid rgba(15, 23, 42, 0.08);
|
|
animation: dd-in 140ms ease-out;
|
|
}
|
|
.dark .dd-panel {
|
|
background-color: rgb(30 41 59); /* slate-800 */
|
|
border-color: rgba(255, 255, 255, 0.08);
|
|
box-shadow: 0 20px 40px -12px rgba(0, 0, 0, 0.6);
|
|
}
|
|
/* Menus that hang off a left-aligned nav item open to the left edge. */
|
|
nav .dd-panel { right: auto; left: 0; }
|
|
|
|
@keyframes dd-in {
|
|
from { opacity: 0; transform: translateY(-4px) scale(0.98); }
|
|
to { opacity: 1; transform: none; }
|
|
}
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.dd-panel { animation: none; }
|
|
.nav-rail { transition: none; }
|
|
}
|
|
|
|
.dd-item {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 0.625rem;
|
|
padding: 0.5rem 0.625rem;
|
|
border-radius: 0.5rem;
|
|
color: rgb(51 65 85); /* slate-700 */
|
|
transition: background-color 150ms ease, color 150ms ease;
|
|
}
|
|
.dd-item:hover { background-color: rgb(241 245 249); } /* slate-100 */
|
|
.dark .dd-item { color: rgb(203 213 225); }
|
|
.dark .dd-item:hover { background-color: rgba(255, 255, 255, 0.06); }
|
|
.dd-item:focus-visible {
|
|
outline: none;
|
|
box-shadow: 0 0 0 2px rgba(56, 189, 248, 0.8);
|
|
}
|
|
.dd-item.is-active { background-color: rgb(239 246 255); color: rgb(29 78 216); } /* blue-50/700 */
|
|
.dark .dd-item.is-active { background-color: rgba(56, 189, 248, 0.12); color: rgb(125 211 252); }
|
|
.dd-item--danger { color: rgb(190 18 60); } /* rose-700 */
|
|
.dd-item--danger:hover { background-color: rgb(255 241 242); }
|
|
.dark .dd-item--danger { color: rgb(253 164 175); }
|
|
.dark .dd-item--danger:hover { background-color: rgba(244, 63, 94, 0.12); }
|
|
|
|
.dd-icon {
|
|
width: 1.125rem;
|
|
height: 1.125rem;
|
|
flex-shrink: 0;
|
|
margin-top: 0.075rem;
|
|
opacity: 0.7;
|
|
}
|
|
.dd-title {
|
|
display: block;
|
|
font-size: 0.875rem;
|
|
font-weight: 500;
|
|
line-height: 1.35;
|
|
}
|
|
.dd-desc {
|
|
display: block;
|
|
margin-top: 0.075rem;
|
|
font-size: 0.75rem;
|
|
line-height: 1.35;
|
|
color: rgb(100 116 139); /* slate-500 */
|
|
}
|
|
.dark .dd-desc { color: rgb(148 163 184); }
|
|
.dd-item.is-active .dd-desc { color: inherit; opacity: 0.75; }
|
|
|
|
/* Mobile menu. */
|
|
.m-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
padding: 0.625rem 0.75rem;
|
|
border-radius: 0.5rem;
|
|
font-size: 0.9375rem;
|
|
font-weight: 500;
|
|
color: rgb(148 163 184);
|
|
transition: background-color 150ms ease, color 150ms ease;
|
|
}
|
|
.m-item:hover { background-color: rgba(255, 255, 255, 0.06); color: #fff; }
|
|
.m-item.is-active {
|
|
color: #fff;
|
|
background-color: rgba(56, 189, 248, 0.12);
|
|
box-shadow: inset 2px 0 0 rgb(56 189 248);
|
|
}
|
|
.m-label {
|
|
padding: 0 0.75rem;
|
|
font-size: 0.6875rem;
|
|
font-weight: 600;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.12em;
|
|
color: rgb(100 116 139);
|
|
}
|
|
|
|
/* Smooth scrolling */
|
|
html {
|
|
scroll-behavior: smooth;
|
|
}
|
|
|
|
/* Custom scrollbar for dark mode */
|
|
.dark ::-webkit-scrollbar {
|
|
width: 8px;
|
|
height: 8px;
|
|
}
|
|
|
|
.dark ::-webkit-scrollbar-track {
|
|
background: #374151;
|
|
}
|
|
|
|
.dark ::-webkit-scrollbar-thumb {
|
|
background: #6b7280;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.dark ::-webkit-scrollbar-thumb:hover {
|
|
background: #8b94a3;
|
|
}
|
|
|
|
/* Light mode scrollbar */
|
|
::-webkit-scrollbar {
|
|
width: 8px;
|
|
height: 8px;
|
|
}
|
|
|
|
::-webkit-scrollbar-track {
|
|
background: #f1f1f1;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
background: #c1c1c1;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb:hover {
|
|
background: #a1a1a1;
|
|
}
|
|
|
|
/* Modal backdrop animation */
|
|
.fixed.inset-0.bg-black.bg-opacity-50 {
|
|
animation: fadeIn 0.2s ease-out;
|
|
}
|
|
|
|
@keyframes fadeIn {
|
|
from {
|
|
opacity: 0;
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
/* Modal content animation */
|
|
.fixed.inset-0 > div > div {
|
|
animation: slideIn 0.3s ease-out;
|
|
}
|
|
|
|
@keyframes slideIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(-20px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
/* Table hover effect */
|
|
.tbody tr:hover {
|
|
transition: background-color 0.15s ease;
|
|
}
|
|
|
|
/* Focus styles for inputs */
|
|
input:focus,
|
|
textarea:focus,
|
|
select:focus {
|
|
outline: none;
|
|
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.5);
|
|
}
|
|
|
|
/* Dark mode focus */
|
|
.dark input:focus,
|
|
.dark textarea:focus,
|
|
.dark select:focus {
|
|
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.3);
|
|
}
|
|
|
|
/* Button focus */
|
|
button:focus {
|
|
outline: none;
|
|
box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.5);
|
|
}
|
|
|
|
/* Loading spinner for buttons */
|
|
button.loading {
|
|
position: relative;
|
|
pointer-events: none;
|
|
}
|
|
|
|
button.loading::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
width: 16px;
|
|
height: 16px;
|
|
border: 2px solid transparent;
|
|
border-top-color: currentColor;
|
|
border-radius: 50%;
|
|
animation: spin 0.8s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
to {
|
|
transform: translate(-50%, -50%) rotate(360deg);
|
|
}
|
|
}
|
|
|
|
/* Status badges */
|
|
.status-badge {
|
|
display: inline-block;
|
|
padding: 4px 8px;
|
|
border-radius: 9999px;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* Table cell wrapping */
|
|
.table-cell-wrap {
|
|
white-space: normal;
|
|
word-wrap: break-word;
|
|
}
|
|
|
|
/* Responsive adjustments */
|
|
@media (max-width: 768px) {
|
|
/* Make filter section stack on mobile */
|
|
#filterForm {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
}
|
|
|
|
#filterForm > div {
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
/* Adjust buttons on mobile */
|
|
#filterForm .flex.space-x-2,
|
|
#filterForm .ml-auto,
|
|
#filterForm > div:last-child {
|
|
margin-bottom: 0 !important;
|
|
}
|
|
|
|
/* Make table scroll horizontally on mobile */
|
|
.overflow-hidden {
|
|
overflow-x: auto;
|
|
}
|
|
|
|
table {
|
|
min-width: 768px;
|
|
}
|
|
|
|
/* Adjust modal width on mobile */
|
|
.max-w-lg {
|
|
max-width: 90vw;
|
|
margin: 0 5vw;
|
|
}
|
|
|
|
.max-w-md {
|
|
max-width: 90vw;
|
|
margin: 0 5vw;
|
|
}
|
|
}
|
|
|
|
/* Print styles */
|
|
@media print {
|
|
.no-print {
|
|
display: none !important;
|
|
}
|
|
|
|
body {
|
|
background: white;
|
|
color: black;
|
|
}
|
|
|
|
.dark body {
|
|
background: white;
|
|
color: black;
|
|
}
|
|
}
|
|
|
|
/* Header shadow */
|
|
header {
|
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
|
}
|
|
|
|
/* Card shadows */
|
|
.bg-white,
|
|
.bg-gray-800 {
|
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
/* Utility classes */
|
|
.truncate-2 {
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.truncate-3 {
|
|
display: -webkit-box;
|
|
-webkit-line-clamp: 3;
|
|
-webkit-box-orient: vertical;
|
|
overflow: hidden;
|
|
}
|