Files
nebula/src/app/globals.css
T
florian.wildeandClaude Opus 5 f3c3383231
CI / pruefen (push) Canceled after 0s
feat(board): Listenansicht, und die mobile Ansicht laeuft nicht mehr aus dem Bild
Auf 375px war das Dashboard 40px breiter als der Bildschirm. Ursache war die
Steuerleiste: Suchfeld, Sortierung, Ansichtsumschalter und "Neues Projekt"
lagen in einer Flex-Zeile ohne Umbruch, und weil das Sortierfeld ein
min-width mitbringt, wurde der Knopf am Ende schlicht hinausgeschoben. Das
overflow-x: hidden am body hat ihn dann abgeschnitten statt erreichbar zu
lassen. Dasselbe Muster im Projektkopf, wo "Loeschen" ausserhalb lag.

Die Zeilen brechen jetzt um, der Ansichtsumschalter steht auf schmalen
Anzeigen ueber die volle Breite. Gemessen ueber alle Seiten und beide
Testbreiten: scrollWidth == clientWidth.

Weil der Umschalter ohnehin umgebaut wurde, kommt die vierte Ansicht gleich
mit: eine Zeile je Projekt statt einer Karte. Auf dem Telefon ist das der
eigentliche Gewinn — eine Karte braucht rund 260px Hoehe und steht dort
zwangslaeufig allein in der Zeile, fuenf Projekte fuellen also zwei
Bildschirme. Als Liste passen sie auf einen, und weil alle Zeilen gleich
gebaut sind, liegen Name, Fortschritt und Status untereinander. Was nicht
mehr in die Breite passt, faellt der Reihe nach weg, statt sie zu sprengen.

Dazu die Kleinigkeiten, die auf dem Telefon dranhaengen:

- .field bekommt auf Beruehrungsgeraeten 16px. Darunter zoomt iOS Safari
  beim Hineintippen die Seite heran und kehrt nicht von selbst zurueck.
- .btn mindestens 44px hoch, der Schieberegler einen 24px-Griff.
- Der Popover klemmt seine Breite ans Fenster. Unter 280px lieferte die
  Rechnung eine negative linke Kante, das Menue stand halb ausserhalb.
- Das Tag-Formular der Auswahlleiste war 16px breiter als sein Popover und
  wurde von dessen overflow-hidden beschnitten – auf jeder Breite.
- Die Heatmap scrollt wieder seitlich, statt sich zu stauchen.
- Die Kennzahlen stehen zweispaltig; einspaltig fuellten die fuenf Kacheln
  den ganzen ersten Bildschirm, bevor das erste Projekt kam.
- viewportFit und die safe-area-Abstaende, damit im installierten Betrieb
  nichts unter Statusleiste und Home-Anzeige verschwindet.
- Die beiden Verweise in der Kopfleiste und "Neues Projekt" tragen ein
  aria-label. Unterhalb von sm sind sie reine Symbole und hiessen fuer einen
  Screenreader gar nichts; axe-core meldete das als link-name.

axe-core laeuft auf 375px ueber alle vier Ansichten, den Dialog und die
Projektseite jetzt ohne Beanstandung.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 01:58:27 +02:00

238 lines
6.9 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@import "tailwindcss";
@import "./components.css";
/* ─────────────────────────────────────────────────────────────
Nebula – Designsystem
Dark Glassmorphism mit Neon-Akzenten.
Hier stehen Theme-Variablen, Grundlayout und Hintergrund.
Die wiederverwendbaren Bausteine (.glass, .btn, .field …) liegen in
components.css, damit sie in @layer components landen und Tailwind-
Utilities sie ueberschreiben koennen.
───────────────────────────────────────────────────────────── */
@theme {
--color-void: #05060f;
--color-void-soft: #0a0c1a;
--color-haze: #12142b;
/* ── Gedaempfte Textfarben ──────────────────────────────────
Tailwinds slate-500 und slate-600 kommen auf diesem sehr dunklen
Untergrund nur auf 3,8 bzw. 2,5 zu 1. WCAG AA verlangt 4,5 fuer
normalen Text, und beide werden hier reichlich fuer Hinweise und
Nebenangaben benutzt – axe-core hat das an 26 Stellen beanstandet.
Statt an hundert Stellen die Klasse zu tauschen, werden die beiden
Stufen hier aufgehellt. Der Abstand zwischen ihnen bleibt sichtbar,
der Rest des Entwurfs unangetastet.
Geprueft gegen die drei vorkommenden Untergruende #0b0d1c (Seite),
#0e0f17 (.glass) und #14141d (.popover) – der schlechteste Wert
zaehlt. */
--color-slate-500: #8a98b0; /* 6,3 : 1 */
--color-slate-600: #78879f; /* 5,0 : 1 */
--font-sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
"Helvetica Neue", Arial, "Noto Sans", sans-serif;
--font-mono: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo,
Consolas, "DejaVu Sans Mono", monospace;
--radius-card: 1.25rem;
/* Eine Stufe unterhalb von Tailwinds "sm" (40rem). Zwischen 320px und
640px liegt der gesamte Telefonbereich, und dort ist der Unterschied
zwischen "Beschriftung passt noch" und "passt nicht mehr" oft nur ein
Zentimeter breit. 22rem trennt die wirklich schmalen Geraete (iPhone SE
der ersten Reihe, 320px) vom Rest, der bei 360px und mehr anfaengt. */
--breakpoint-xs: 22rem;
}
:root {
color-scheme: dark;
--glass-bg: rgba(255, 255, 255, 0.035);
--glass-border: rgba(255, 255, 255, 0.09);
--glass-highlight: rgba(255, 255, 255, 0.16);
}
@layer base {
* {
border-color: var(--glass-border);
}
html,
body {
min-height: 100%;
}
body {
background-color: var(--color-void);
color: #e6e8f5;
font-family: var(--font-sans);
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
/* Ein Notnagel gegen seitliches Scrollen, kein Ersatz fuer richtiges
Umbrechen: was hier abgeschnitten wird, ist auf dem Telefon einfach
nicht mehr erreichbar. Die Ursachen gehoeren in die Komponenten. */
overflow-x: hidden;
/* Mit viewportFit: "cover" reicht die Seite bis in die Ecken; ohne diese
Einrueckung stuende der Inhalt im Querformat unter der Kameraleiste. */
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
}
::selection {
background: rgba(139, 92, 246, 0.35);
color: #fff;
}
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: rgba(148, 163, 184, 0.22);
border-radius: 9999px;
border: 2px solid transparent;
background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(167, 139, 250, 0.5);
background-clip: content-box;
}
}
/* ── Hintergrund: langsam driftende Nebel ───────────────────── */
.aurora {
position: fixed;
inset: 0;
z-index: -2;
overflow: hidden;
pointer-events: none;
}
.aurora::after {
/* feines Rauschen, damit die Verlaeufe nicht bandieren */
content: "";
position: absolute;
inset: 0;
opacity: 0.35;
background-image: radial-gradient(rgba(255, 255, 255, 0.06) 1px, transparent 1px);
background-size: 3px 3px;
mix-blend-mode: overlay;
}
.aurora-blob {
position: absolute;
border-radius: 9999px;
filter: blur(90px);
opacity: 0.5;
will-change: transform;
}
.aurora-blob--one {
width: 46rem;
height: 46rem;
top: -18rem;
left: -12rem;
background: radial-gradient(circle at 30% 30%, #7c3aed, transparent 65%);
animation: drift-a 26s ease-in-out infinite alternate;
}
.aurora-blob--two {
width: 40rem;
height: 40rem;
top: -10rem;
right: -14rem;
background: radial-gradient(circle at 60% 40%, #0ea5e9, transparent 65%);
animation: drift-b 32s ease-in-out infinite alternate;
}
.aurora-blob--three {
width: 38rem;
height: 38rem;
bottom: -20rem;
left: 28%;
background: radial-gradient(circle at 50% 50%, #db2777, transparent 68%);
animation: drift-c 38s ease-in-out infinite alternate;
opacity: 0.35;
}
.aurora-grid {
position: fixed;
inset: 0;
z-index: -1;
pointer-events: none;
background-image:
linear-gradient(rgba(255, 255, 255, 0.028) 1px, transparent 1px),
linear-gradient(90deg, rgba(255, 255, 255, 0.028) 1px, transparent 1px);
background-size: 64px 64px;
mask-image: radial-gradient(ellipse 100% 70% at 50% 0%, #000 40%, transparent 100%);
}
@keyframes drift-a {
from { transform: translate3d(0, 0, 0) scale(1); }
to { transform: translate3d(6rem, 4rem, 0) scale(1.12); }
}
@keyframes drift-b {
from { transform: translate3d(0, 0, 0) scale(1.05); }
to { transform: translate3d(-7rem, 5rem, 0) scale(0.95); }
}
@keyframes drift-c {
from { transform: translate3d(0, 0, 0) scale(0.95); }
to { transform: translate3d(4rem, -6rem, 0) scale(1.1); }
}
/* ── Bewegung & Effekte ─────────────────────────────────────── */
.shimmer {
background: linear-gradient(
100deg,
rgba(255, 255, 255, 0) 20%,
rgba(255, 255, 255, 0.12) 50%,
rgba(255, 255, 255, 0) 80%
);
background-size: 200% 100%;
animation: shimmer 2.2s linear infinite;
}
@keyframes shimmer {
from { background-position: 200% 0; }
to { background-position: -200% 0; }
}
.fade-up {
animation: fade-up 0.45s cubic-bezier(0.22, 1, 0.36, 1) both;
}
@keyframes fade-up {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
.pulse-ring {
animation: pulse-ring 2.4s ease-out infinite;
}
@keyframes pulse-ring {
0% { box-shadow: 0 0 0 0 currentColor; opacity: 0.7; }
70% { box-shadow: 0 0 0 8px transparent; opacity: 0; }
100% { box-shadow: 0 0 0 0 transparent; opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
}
}