Der Abzug stellte drei Punkte mit Begründung zurück (P-30 Desktop-Breite, P-36 Filterleiste, P-32 Bild-Nachladen) und verwies Scrim, Z-Index-Leiter und WebDAV-Beschriftungen in eine Flotten-Änderung. Alles davon ist inzwischen erledigt — der Absatz sagte weiter das Gegenteil. 25 Apps gehoben, 24 laufende deployt. Bei elf ist der Scrim direkt im ausgelieferten CSS nachgewiesen, bei den übrigen rendert die Startseite die Leiste nicht; dort steht der Nachweis über den gebauten Commit. Keine App laeuft noch auf der alten Version.
1967 lines
61 KiB
HTML
1967 lines
61 KiB
HTML
<title>Pageta — UI-Korrekturabzug</title>
|
||
|
||
<style>
|
||
:root {
|
||
--paper: #f2f1ed;
|
||
--paper-2: #fafaf8;
|
||
--paper-3: #e9e8e2;
|
||
--ink: #1c1e26;
|
||
--ink-2: #5b606e;
|
||
--ink-3: #878c99;
|
||
--rule: #d9d8d2;
|
||
--rule-strong: #bcbbb4;
|
||
--accent: #38468e;
|
||
--crit: #a81f37;
|
||
--warn: #8f5f12;
|
||
--fine: #3f6675;
|
||
--crit-bg: #a81f3714;
|
||
--warn-bg: #8f5f1214;
|
||
--fine-bg: #3f667514;
|
||
|
||
--sans: "SF Pro Display", -apple-system, BlinkMacSystemFont, "Segoe UI",
|
||
system-ui, sans-serif;
|
||
--serif: "Iowan Old Style", Charter, "Palatino Linotype", Palatino, Georgia,
|
||
serif;
|
||
--mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
|
||
}
|
||
|
||
@media (prefers-color-scheme: dark) {
|
||
:root {
|
||
--paper: #15161b;
|
||
--paper-2: #1c1e25;
|
||
--paper-3: #23262f;
|
||
--ink: #e7e7e2;
|
||
--ink-2: #9a9fad;
|
||
--ink-3: #6e7484;
|
||
--rule: #2c2f38;
|
||
--rule-strong: #3d414d;
|
||
--accent: #8e9be8;
|
||
--crit: #e4667c;
|
||
--warn: #d9a24b;
|
||
--fine: #7faec0;
|
||
--crit-bg: #e4667c1a;
|
||
--warn-bg: #d9a24b1a;
|
||
--fine-bg: #7faec01a;
|
||
}
|
||
}
|
||
|
||
:root[data-theme="dark"] {
|
||
--paper: #15161b;
|
||
--paper-2: #1c1e25;
|
||
--paper-3: #23262f;
|
||
--ink: #e7e7e2;
|
||
--ink-2: #9a9fad;
|
||
--ink-3: #6e7484;
|
||
--rule: #2c2f38;
|
||
--rule-strong: #3d414d;
|
||
--accent: #8e9be8;
|
||
--crit: #e4667c;
|
||
--warn: #d9a24b;
|
||
--fine: #7faec0;
|
||
--crit-bg: #e4667c1a;
|
||
--warn-bg: #d9a24b1a;
|
||
--fine-bg: #7faec01a;
|
||
}
|
||
|
||
:root[data-theme="light"] {
|
||
--paper: #f2f1ed;
|
||
--paper-2: #fafaf8;
|
||
--paper-3: #e9e8e2;
|
||
--ink: #1c1e26;
|
||
--ink-2: #5b606e;
|
||
--ink-3: #878c99;
|
||
--rule: #d9d8d2;
|
||
--rule-strong: #bcbbb4;
|
||
--accent: #38468e;
|
||
--crit: #a81f37;
|
||
--warn: #8f5f12;
|
||
--fine: #3f6675;
|
||
--crit-bg: #a81f3714;
|
||
--warn-bg: #8f5f1214;
|
||
--fine-bg: #3f667514;
|
||
}
|
||
|
||
body {
|
||
background: var(--paper);
|
||
color: var(--ink);
|
||
font-family: var(--serif);
|
||
font-size: 17px;
|
||
line-height: 1.62;
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
|
||
.wrap {
|
||
max-width: 66rem;
|
||
margin: 0 auto;
|
||
padding: 3.5rem 1.5rem 6rem;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4rem;
|
||
}
|
||
|
||
h1,
|
||
h2,
|
||
h3,
|
||
h4 {
|
||
font-family: var(--sans);
|
||
font-weight: 600;
|
||
letter-spacing: -0.015em;
|
||
text-wrap: balance;
|
||
margin: 0;
|
||
}
|
||
|
||
h1 {
|
||
font-size: clamp(2.1rem, 5vw, 3rem);
|
||
line-height: 1.06;
|
||
}
|
||
h2 {
|
||
font-size: 1.45rem;
|
||
line-height: 1.2;
|
||
}
|
||
h3 {
|
||
font-size: 1.02rem;
|
||
line-height: 1.35;
|
||
}
|
||
|
||
p {
|
||
margin: 0;
|
||
}
|
||
a {
|
||
color: var(--accent);
|
||
}
|
||
a:focus-visible,
|
||
button:focus-visible {
|
||
outline: 2px solid var(--accent);
|
||
outline-offset: 3px;
|
||
border-radius: 2px;
|
||
}
|
||
|
||
.eyebrow {
|
||
font-family: var(--sans);
|
||
font-size: 0.7rem;
|
||
font-weight: 600;
|
||
letter-spacing: 0.14em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-3);
|
||
}
|
||
|
||
.lede {
|
||
font-size: 1.15rem;
|
||
color: var(--ink-2);
|
||
max-width: 40em;
|
||
}
|
||
|
||
/* ---- Kopf ---- */
|
||
.masthead {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 1.1rem;
|
||
border-bottom: 2px solid var(--ink);
|
||
padding-bottom: 2rem;
|
||
}
|
||
.masthead .meta {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 0.4rem 1.6rem;
|
||
font-family: var(--mono);
|
||
font-size: 0.76rem;
|
||
color: var(--ink-3);
|
||
}
|
||
.masthead .meta b {
|
||
color: var(--ink-2);
|
||
font-weight: 500;
|
||
}
|
||
|
||
/* ---- Kennzahlen ---- */
|
||
.tally {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
|
||
gap: 1px;
|
||
background: var(--rule);
|
||
border: 1px solid var(--rule);
|
||
}
|
||
.tally div {
|
||
background: var(--paper-2);
|
||
padding: 1rem 1.1rem;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.15rem;
|
||
}
|
||
.tally .n {
|
||
font-family: var(--sans);
|
||
font-size: 2rem;
|
||
font-weight: 600;
|
||
line-height: 1;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.tally .k {
|
||
font-family: var(--sans);
|
||
font-size: 0.72rem;
|
||
letter-spacing: 0.1em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-3);
|
||
}
|
||
.tally .crit .n {
|
||
color: var(--crit);
|
||
}
|
||
.tally .warn .n {
|
||
color: var(--warn);
|
||
}
|
||
.tally .fine .n {
|
||
color: var(--fine);
|
||
}
|
||
|
||
section {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 1.5rem;
|
||
}
|
||
.section-head {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.5rem;
|
||
border-bottom: 1px solid var(--rule-strong);
|
||
padding-bottom: 0.7rem;
|
||
}
|
||
|
||
/* ---- Befunde ---- */
|
||
.findings {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
.finding {
|
||
display: grid;
|
||
grid-template-columns: 7.5rem 1fr;
|
||
gap: 0 1.75rem;
|
||
padding: 1.6rem 0;
|
||
border-bottom: 1px solid var(--rule);
|
||
}
|
||
.finding:first-child {
|
||
border-top: 1px solid var(--rule);
|
||
}
|
||
.rail {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.4rem;
|
||
align-items: flex-start;
|
||
}
|
||
.fid {
|
||
font-family: var(--mono);
|
||
font-size: 0.78rem;
|
||
font-weight: 600;
|
||
color: var(--ink-2);
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.sev {
|
||
font-family: var(--sans);
|
||
font-size: 0.66rem;
|
||
font-weight: 600;
|
||
letter-spacing: 0.1em;
|
||
text-transform: uppercase;
|
||
padding: 0.16rem 0.42rem;
|
||
border: 1px solid currentColor;
|
||
}
|
||
.sev-crit {
|
||
color: var(--crit);
|
||
background: var(--crit-bg);
|
||
}
|
||
.sev-warn {
|
||
color: var(--warn);
|
||
background: var(--warn-bg);
|
||
}
|
||
.sev-fine {
|
||
color: var(--fine);
|
||
background: var(--fine-bg);
|
||
}
|
||
.where {
|
||
font-family: var(--mono);
|
||
font-size: 0.7rem;
|
||
color: var(--ink-3);
|
||
word-break: break-word;
|
||
}
|
||
.body {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.6rem;
|
||
min-width: 0;
|
||
}
|
||
.body p {
|
||
max-width: 44em;
|
||
}
|
||
.evidence {
|
||
font-family: var(--mono);
|
||
font-size: 0.76rem;
|
||
line-height: 1.55;
|
||
color: var(--ink-2);
|
||
background: var(--paper-2);
|
||
border-left: 2px solid var(--rule-strong);
|
||
padding: 0.6rem 0.85rem;
|
||
overflow-x: auto;
|
||
}
|
||
.fix {
|
||
font-family: var(--sans);
|
||
font-size: 0.9rem;
|
||
line-height: 1.55;
|
||
}
|
||
.fix b {
|
||
font-weight: 600;
|
||
color: var(--accent);
|
||
}
|
||
|
||
@media (max-width: 46rem) {
|
||
.finding {
|
||
grid-template-columns: 1fr;
|
||
gap: 0.75rem;
|
||
}
|
||
.rail {
|
||
flex-direction: row;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
gap: 0.5rem 0.7rem;
|
||
}
|
||
}
|
||
|
||
/* ---- Tabelle ---- */
|
||
.tablewrap {
|
||
overflow-x: auto;
|
||
border: 1px solid var(--rule);
|
||
}
|
||
table {
|
||
border-collapse: collapse;
|
||
width: 100%;
|
||
font-family: var(--sans);
|
||
font-size: 0.85rem;
|
||
background: var(--paper-2);
|
||
}
|
||
th,
|
||
td {
|
||
text-align: left;
|
||
padding: 0.6rem 0.85rem;
|
||
border-bottom: 1px solid var(--rule);
|
||
vertical-align: top;
|
||
}
|
||
th {
|
||
font-size: 0.68rem;
|
||
letter-spacing: 0.1em;
|
||
text-transform: uppercase;
|
||
color: var(--ink-3);
|
||
font-weight: 600;
|
||
white-space: nowrap;
|
||
background: var(--paper-3);
|
||
}
|
||
td:first-child {
|
||
font-family: var(--mono);
|
||
font-size: 0.8rem;
|
||
white-space: nowrap;
|
||
}
|
||
tbody tr:last-child td {
|
||
border-bottom: 0;
|
||
}
|
||
.dot {
|
||
display: inline-block;
|
||
width: 0.55rem;
|
||
height: 0.55rem;
|
||
margin-right: 0.35rem;
|
||
vertical-align: baseline;
|
||
}
|
||
.d-crit {
|
||
background: var(--crit);
|
||
}
|
||
.d-warn {
|
||
background: var(--warn);
|
||
}
|
||
.d-fine {
|
||
background: var(--fine);
|
||
}
|
||
.d-ok {
|
||
background: var(--ink-3);
|
||
}
|
||
|
||
/* ---- Regeln ---- */
|
||
.rules {
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
.rule-item {
|
||
display: grid;
|
||
grid-template-columns: 2.5rem 1fr;
|
||
gap: 0 1.25rem;
|
||
padding: 1.35rem 0;
|
||
border-bottom: 1px solid var(--rule);
|
||
}
|
||
.rule-item:first-child {
|
||
border-top: 1px solid var(--rule);
|
||
}
|
||
.rule-n {
|
||
font-family: var(--mono);
|
||
font-size: 0.8rem;
|
||
color: var(--ink-3);
|
||
padding-top: 0.2rem;
|
||
font-variant-numeric: tabular-nums;
|
||
}
|
||
.rule-body {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.5rem;
|
||
min-width: 0;
|
||
}
|
||
.rule-body p {
|
||
max-width: 46em;
|
||
font-size: 0.98rem;
|
||
}
|
||
.dodont {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 1px;
|
||
background: var(--rule);
|
||
border: 1px solid var(--rule);
|
||
font-family: var(--mono);
|
||
font-size: 0.76rem;
|
||
line-height: 1.5;
|
||
}
|
||
.dodont > div {
|
||
background: var(--paper-2);
|
||
padding: 0.6rem 0.8rem;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.3rem;
|
||
}
|
||
.dodont .tag {
|
||
font-family: var(--sans);
|
||
font-size: 0.62rem;
|
||
letter-spacing: 0.12em;
|
||
text-transform: uppercase;
|
||
font-weight: 600;
|
||
}
|
||
.dodont .no .tag {
|
||
color: var(--crit);
|
||
}
|
||
.dodont .yes .tag {
|
||
color: var(--fine);
|
||
}
|
||
@media (max-width: 40rem) {
|
||
.dodont {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
.rule-item {
|
||
grid-template-columns: 1fr;
|
||
gap: 0.6rem;
|
||
}
|
||
}
|
||
|
||
/* ---- Wellen ---- */
|
||
.waves {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
|
||
gap: 1px;
|
||
background: var(--rule);
|
||
border: 1px solid var(--rule);
|
||
}
|
||
.wave {
|
||
background: var(--paper-2);
|
||
padding: 1.15rem 1.25rem;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.6rem;
|
||
}
|
||
.wave h3 {
|
||
font-size: 0.95rem;
|
||
}
|
||
.wave ul {
|
||
margin: 0;
|
||
padding-left: 1.1rem;
|
||
font-family: var(--sans);
|
||
font-size: 0.86rem;
|
||
line-height: 1.55;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.3rem;
|
||
}
|
||
.wave .ids {
|
||
font-family: var(--mono);
|
||
font-size: 0.7rem;
|
||
color: var(--ink-3);
|
||
}
|
||
|
||
ul.plain {
|
||
margin: 0;
|
||
padding-left: 1.15rem;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.45rem;
|
||
max-width: 44em;
|
||
}
|
||
code {
|
||
font-family: var(--mono);
|
||
font-size: 0.86em;
|
||
background: var(--paper-3);
|
||
padding: 0.08em 0.32em;
|
||
}
|
||
.note {
|
||
font-family: var(--sans);
|
||
font-size: 0.88rem;
|
||
color: var(--ink-2);
|
||
background: var(--paper-2);
|
||
border: 1px solid var(--rule);
|
||
padding: 1rem 1.15rem;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.5rem;
|
||
max-width: 48em;
|
||
}
|
||
footer.colophon {
|
||
font-family: var(--mono);
|
||
font-size: 0.73rem;
|
||
color: var(--ink-3);
|
||
border-top: 1px solid var(--rule);
|
||
padding-top: 1.25rem;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.3rem;
|
||
}
|
||
</style>
|
||
|
||
<div class="wrap">
|
||
<header class="masthead">
|
||
<span class="eyebrow">Pageta · Korrekturabzug der Web-App</span>
|
||
<h1>37 Befunde aus einem Durchgang durch alle Seiten</h1>
|
||
<p class="lede">
|
||
Visuelle Prüfung von <code>app.pageta.com</code> gegen
|
||
<code>mana/docs/DESIGN.md</code>. Jeder Befund hat eine Stelle, einen
|
||
Beleg und einen Fix. Die zweite Hälfte destilliert daraus die Regeln,
|
||
die den nächsten Durchgang kürzer machen.
|
||
</p>
|
||
<div class="meta">
|
||
<span><b>Datum</b> 26.07.2026</span>
|
||
<span><b>Build</b> live, angemeldet</span>
|
||
<span><b>Breiten</b> 1280 px · 567 px</span>
|
||
<span><b>Modi</b> hell + dunkel</span>
|
||
<span><b>Seiten</b> 17 Routen + 404</span>
|
||
</div>
|
||
</header>
|
||
|
||
<section>
|
||
<div class="tally">
|
||
<div class="fine"><span class="n">30</span><span class="k">Behoben, live</span></div>
|
||
<div class="warn"><span class="n">4</span><span class="k">Zurückgestellt</span></div>
|
||
<div><span class="n">3</span><span class="k">Befund korrigiert</span></div>
|
||
<div><span class="n">0</span><span class="k">Hex-Literale (vorher 44)</span></div>
|
||
<div><span class="n">140</span><span class="k">Tests grün</span></div>
|
||
</div>
|
||
|
||
<div class="note">
|
||
<p>
|
||
<b>Stand 27.07.2026 — umgesetzt und deployt</b>
|
||
(<code>fe6d9fb</code>, <code>0e024e4</code>). Alle drei Ursachen sind
|
||
weg: die untere Leiste hat einen Scrim, liegt auf <code>z-index: 100</code>
|
||
und veröffentlicht ihre Höhe als <code>--bottom-chrome</code>; die vier
|
||
am Theme vorbeigebauten Seiten laufen über Tokens und ein neuer
|
||
<code>audit:theme-tokens</code>-Wächter hält sie dort; alle Routen
|
||
sitzen auf einer <code>PageShell</code>.
|
||
</p>
|
||
<p>
|
||
<b>Drei Befunde haben sich beim Reparieren als anders herausgestellt</b> —
|
||
sie stehen unten korrigiert statt stillschweigend gestrichen:
|
||
</p>
|
||
<ul class="plain" style="font-family: var(--sans); font-size: 0.92rem">
|
||
<li>
|
||
<b>P-19</b> Der „rohe Slug" kam von einer URL, die ich selbst erfunden
|
||
hatte — echte Slugs sind der Hostname mit Punkten. Real und behoben
|
||
waren die zwei gleichzeitigen Leerzustände und der Untertitel, der den
|
||
Titel wiederholte.
|
||
</li>
|
||
<li>
|
||
<b>P-21</b> „Null <code><p></code>, null <code><h2></code>"
|
||
war ein Messfehler — der Container ist ein
|
||
<code>div[role=article]</code>, mein Selektor suchte
|
||
<code><article></code>. Die sichtbare Ursache war eine andere und
|
||
schwerere: <code>prose</code>/<code>prose-invert</code> stehen im
|
||
Markup, aber <code>@tailwindcss/typography</code> ist im Projekt gar
|
||
nicht installiert. Die Klassen taten nichts, und Tailwinds Preflight
|
||
setzt Überschriften auf Fließtextgröße. Jetzt: eigene, token-basierte
|
||
Lesetypografie ohne neue Abhängigkeit.
|
||
</li>
|
||
<li>
|
||
<b>P-29</b> „Konto öffnet in der Mitte" war Browser-Scroll-Restoration
|
||
beim Laden über die Adresszeile, kein App-Fehler. Über den Avatar
|
||
geklickt startet die Seite oben. Kein Eingriff.
|
||
</li>
|
||
</ul>
|
||
<p>
|
||
<b>Ein Fund kam beim Reparieren dazu:</b> <code>highlightToWire</code>
|
||
setzte <code>createdAt: s.userId</code> — daher die Konto-UUID als Datum
|
||
in /markierungen (P-17), und die Sortierung nach <code>createdAt</code>
|
||
war wirkungslos, weil alle Werte gleich waren. Der Zeitstempel kommt
|
||
jetzt aus der ULID.
|
||
</p>
|
||
<p>
|
||
<b>Zurückgestellt, mit Grund:</b> die Desktop-Breitennutzung (P-30) und
|
||
die Filterleisten am unteren Rand (P-36) sind Layout-Entscheidungen, keine
|
||
Patches — die eine würde die gerade vereinheitlichte Spaltenbreite wieder
|
||
aufbrechen, die andere kostet auf dem Telefon einen zweiten 56-px-Streifen.
|
||
Das Bild-Nachladen für Bestandsartikel (P-32) braucht API-Arbeit. Die
|
||
Varianten-Namen (P-31) und die WebDAV-Feldbeschriftungen (P-11, Teil)
|
||
leben in <code>@mana/shared-ui</code> und gehören in eine Flotten-Änderung —
|
||
so wie der Scrim selbst, der hier vorerst als lokales Override liegt.
|
||
</p>
|
||
<p>
|
||
<b>Nachgezogen am 28.07.2026 — die Zurückstellungen sind eingelöst.</b>
|
||
P-30 über ein <code>wide</code>-Flag an der <code>PageShell</code>, P-36 nur
|
||
unterhalb <code>lg</code>, P-32 über einen reinen Bild-Pfad ohne
|
||
<code>ArticleEnriched</code>. Und die Flotten-Änderung ist raus: Scrim,
|
||
Z-Index-Leiter und WebDAV-Beschriftungen sitzen seit
|
||
<code>@mana/shared-ui 1.29.0</code> im Paket statt als lokales Override in
|
||
pageta. Ausgerollt auf <b>25 Apps</b>, davon 24 laufende deployt
|
||
(<code>who</code> läuft derzeit nicht und ist nur gehoben). Bei elf Apps ist
|
||
der Scrim direkt im ausgelieferten CSS nachgewiesen; bei den übrigen rendert
|
||
die Startseite die Leiste nicht — dort steht der Nachweis über den gebauten
|
||
Commit. <b>Keine App zeigt noch die alte Version.</b>
|
||
</p>
|
||
</div>
|
||
<div class="note">
|
||
<p>
|
||
<b>Der rote Faden.</b> Drei Ursachen erklären die Hälfte aller Befunde:
|
||
die untere Navigationsleiste ist <em>durchsichtig und liegt über allem</em>
|
||
(sie verschluckt Klicks und überdeckt Modals und Fußzeile), einzelne
|
||
Seiten haben sich <em>am Theme vorbeigebaut</em> (Changelog, Devlog,
|
||
Reader), und es gibt <em>kein gemeinsames Seiten-Gerüst</em> — jede Route
|
||
erfindet Breite, Titel, Zurück-Weg und Leerzustand neu.
|
||
</p>
|
||
</div>
|
||
</section>
|
||
|
||
<section>
|
||
<div class="section-head">
|
||
<span class="eyebrow">Teil 1</span>
|
||
<h2>Befunde</h2>
|
||
<p class="lede" style="font-size: 1rem">
|
||
Sortiert nach Schwere. <b>Kritisch</b> = blockiert eine Aktion oder
|
||
zeigt Falsches. <b>Wichtig</b> = Regelbruch mit spürbarer Reibung.
|
||
<b>Feinschliff</b> = sichtbar, aber nicht im Weg.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="findings">
|
||
<!-- ======================= KRITISCH ======================= -->
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-01</span>
|
||
<span class="sev sev-crit">Kritisch</span>
|
||
<span class="where">@mana/shared-ui<br />PillNavigation.svelte:750</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Die untere Leiste verschluckt Klicks</h3>
|
||
<p>
|
||
<code>.pill-nav</code> ist <code>position: fixed</code>, 56 px hoch,
|
||
<code>z-index: 1000</code> — und komplett hintergrundlos. Inhalt
|
||
scrollt sichtbar dahinter durch, und was unter der Leiste liegt,
|
||
nimmt keine Klicks mehr an.
|
||
</p>
|
||
<p class="evidence">
|
||
Belegt: „Hell" unter /konto → Erscheinung → Modus reagierte zweimal nicht.
|
||
Erst nachdem die Zeile aus dem 56-px-Streifen gescrollt war, schaltete der
|
||
Modus um. Der Klick landete davor auf der Navigationsleiste.
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Scrim hinter die Leiste (<code>backdrop-filter: blur(12px)</code>
|
||
+ Verlauf zur Hintergrundfarbe, so wie DESIGN.md es für den
|
||
Pill-Container beschreibt) und die Höhe als CSS-Variable
|
||
veröffentlichen, damit Seiten <code>scroll-padding-bottom</code>
|
||
setzen können. Gehört ins geteilte Paket — alle mana-Web-Apps
|
||
haben denselben Fehler.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-02</span>
|
||
<span class="sev sev-crit">Kritisch</span>
|
||
<span class="where">SummarizeModal.svelte<br />lese-liste</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Das Modal liegt unter der Navigation</h3>
|
||
<p>
|
||
Der Overlay der KI-Zusammenfassung hat <code>z-index: 50</code>, die
|
||
fixierte Navigation <code>z-index: 1000</code>. Die Pillen
|
||
„Artikel / Marker / Rückblick / Mana / Feedback" zeichnen sich über
|
||
den Modal-Fuß mit „Neu erzeugen" und „Als Markdown kopieren".
|
||
</p>
|
||
<p class="evidence">
|
||
gemessen · overlayZ: 50 · dialogZ: auto · navZ: 1000
|
||
DESIGN.md fordert · Modal 9995 · Fixed Navigation 90–100
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Z-Index-Leiter aus DESIGN.md verdrahten. Escape schließt
|
||
bereits korrekt — nur die Ebene stimmt nicht.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-03</span>
|
||
<span class="sev sev-crit">Kritisch</span>
|
||
<span class="where">/lese-liste<br />„Zusammenfassung"</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Die KI-Zusammenfassung gibt den Prompt zurück</h3>
|
||
<p>
|
||
Statt einer Synthese steht im Modal der rohe Prompt, mit
|
||
<code>[noop]</code> davor. Nutzer sehen Interna und bekommen kein
|
||
Ergebnis — und weil die meisten Artikel keinen Text haben, besteht
|
||
die Liste aus „(kein Kurztext verfügbar)".
|
||
</p>
|
||
<p class="evidence">
|
||
[noop] Hier sind 40 Artikel, die die Person zum Zusammenfassen ausgewählt hat.
|
||
|
||
ARTIKEL:
|
||
1. „the-decoder.de" (the-decoder.de)
|
||
(kein Kurztext verfügbar)
|
||
2. „golem.de" (www.golem.de)
|
||
(kein Kurztext verfügbar)
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Erst die Backend-Kette prüfen (das
|
||
<code>[noop]</code>-Präfix kommt aus einem nicht konfigurierten
|
||
Modell-Pfad). Bis dahin: Fehlerzustand im Modal statt Prompt-Echo,
|
||
und den Knopf deaktivieren, wenn kein Artikel Volltext hat.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-04</span>
|
||
<span class="sev sev-crit">Kritisch</span>
|
||
<span class="where">/changelog · /devlog<br />/devlog/[date] · /article</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Vier Seiten haben keinen Dunkelmodus</h3>
|
||
<p>
|
||
<code>/changelog</code> ist im Dunkelmodus praktisch unlesbar: die
|
||
Überschrift steht in <code>#1c2127</code> auf einem Grund von
|
||
<code>rgb(26, 22, 20)</code> — ein Kontrast von etwa 1,05 : 1. Der
|
||
Link ist Browser-Blau, nicht die Akzentfarbe der App.
|
||
<code>/devlog</code> legt weiße Bänder in die dunkle Seite und
|
||
setzt Kartentitel in Fast-Schwarz auf Dunkelgrau.
|
||
</p>
|
||
<p class="evidence">
|
||
grep -rE "#[0-9a-fA-F]{6}" --include="*.svelte" → 44 Treffer in 4 Dateien
|
||
changelog/+page.svelte:42 color: #1c2127
|
||
changelog/+page.svelte:106 color: #2563eb
|
||
devlog/+page.svelte:85 background: #ffffff
|
||
article/[id]/+page.svelte bg-amber-50 / bg-neutral-900 / bg-white
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Alle Literale auf <code>hsl(var(--color-*))</code>
|
||
umstellen und <code>audit:theme-tokens</code> in
|
||
<code>validate</code> hängen — im pageta-Repo ist das Skript gar
|
||
nicht verdrahtet.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-05</span>
|
||
<span class="sev sev-crit">Kritisch</span>
|
||
<span class="where">/lese-liste</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Die Liste zeigt Domains statt Titel</h3>
|
||
<p>
|
||
Von 40 Einträgen tragen etliche als Titel nur „golem.de",
|
||
„the-decoder.de" oder „Golem", als Auszug den Cookie-Banner-Text
|
||
der Quelle — teils mehrfach identisch. Die Liste ist damit an ihrer
|
||
Kernaufgabe vorbei: Wiedererkennen. Es gibt im UI keinen Weg, das zu
|
||
reparieren.
|
||
</p>
|
||
<p class="evidence">
|
||
GOLEM.DE · READING
|
||
Golem
|
||
Besuchen Sie Golem.de wie gewohnt mit Werbung und Tracking, indem Sie der
|
||
Nutzung aller Cookies zustimmen. Details zum Tracking finden Sie im Privacy Center.
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Zwei Dinge, getrennt: (a) Speichern abbrechen bzw. warnen,
|
||
wenn der extrahierte Titel gleich dem Host ist — der Consent-Wall-Fall
|
||
ist erkennbar; (b) im Kontextmenü und auf der Artikelseite ein
|
||
„Erneut extrahieren" anbieten, damit Bestand reparierbar ist.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<!-- ======================= WICHTIG ======================= -->
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-06</span>
|
||
<span class="sev sev-warn">Wichtig</span>
|
||
<span class="where">+layout.svelte<br />AvatarLink</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Der Avatar überdeckt „Bookmarklet"</h3>
|
||
<p>
|
||
Bei 567 px Fensterbreite liegt der Konto-Avatar auf der rechten
|
||
Hälfte des „Bookmarklet"-Links der Lese-Liste. Der Link ist dort
|
||
nicht mehr anklickbar.
|
||
</p>
|
||
<p class="evidence">
|
||
Avatar left 515 top 12 right 551 bottom 48
|
||
Link left 468 top 34 right 551 bottom 54 → overlap: true
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Der Avatar ist globales Chrome — Seitenkopfzeilen brauchen
|
||
rechts einen reservierten Korridor (<code>padding-right</code> in
|
||
Avatargröße + Abstand), oder die Seitenaktion wandert unter den Titel.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-07</span>
|
||
<span class="sev sev-warn">Wichtig</span>
|
||
<span class="where">global</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Kein einziges Touch-Ziel erreicht 44 px</h3>
|
||
<p>
|
||
Gemessen auf der Lese-Liste — die Navigation ist der wichtigste Fall,
|
||
weil sie ausdrücklich für den Daumen gedacht ist.
|
||
</p>
|
||
<p class="evidence">
|
||
Navigations-Pillen 30 px hoch
|
||
Quellen-Filter-Chips 24–26 px hoch
|
||
„Bookmarklet"-Link 20 px hoch
|
||
Konto-Avatar 36 × 36 px
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Mindestens 44 px Trefferfläche, notfalls über transparentes
|
||
Padding statt größerer Optik.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-08</span>
|
||
<span class="sev sev-warn">Wichtig</span>
|
||
<span class="where">PillNavigation<br />.pill-nav-container</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Die Navigation braucht 562 px, ein Telefon hat 390</h3>
|
||
<p>
|
||
Der Container hat <code>overflow-x: auto</code> und eine intrinsische
|
||
Breite von 562 px. Auf einem üblichen Telefon scrollen „Mana" und
|
||
„Feedback" seitlich aus dem Bild — eine horizontal scrollende
|
||
Hauptnavigation, deren Scrollbalken zudem ausgeblendet ist.
|
||
</p>
|
||
<p class="evidence">
|
||
innerScrollW 562 · clientW 562 · overflow-x auto · scrollbar-width none
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Unter ~430 px auf Icon-only umschalten (Label nur beim
|
||
aktiven Tab) oder auf vier Ziele reduzieren. Feedback gehört ohnehin
|
||
eher ins Konto als in die Hauptnavigation.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-09</span>
|
||
<span class="sev sev-warn">Wichtig</span>
|
||
<span class="where">lese-liste/+page.svelte:573</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Rohe Status-Enums im Deutsch-UI</h3>
|
||
<p>
|
||
<code>· {a.status}</code> rendert den Datenbankwert direkt und
|
||
versaliert ihn per CSS. Die Karte zeigt dann „UNREAD" oben und
|
||
„✓ gelesen" unten — englischer Enum neben deutschem Satz.
|
||
</p>
|
||
<p class="evidence">
|
||
THE-DECODER.DE · UNREAD WINFUTURE.DE · FINISHED
|
||
WINFUTURE.DE · ARCHIVED GOLEM.DE · READING
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Eine Mapping-Funktion (ungelesen / am Lesen / gelesen /
|
||
archiviert) über <code>@mana/shared-i18n</code>. Und: „gelesen"
|
||
braucht kein zweites Signal, wenn schon ein Fortschrittsbalken da ist.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-10</span>
|
||
<span class="sev sev-warn">Wichtig</span>
|
||
<span class="where">9 Stellen in 4 Dateien</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Emojis als UI-Elemente</h3>
|
||
<p>
|
||
DESIGN.md Regel 9 verbietet Unicode-Emojis im UI — Icons kommen aus
|
||
<code>@mana/shared-icons</code>. Auf <code>/feed</code> sind sogar
|
||
alle sieben Themen mit Emoji gesetzt.
|
||
</p>
|
||
<p class="evidence">
|
||
lese-liste/+page.svelte:420,456 ✨ Zusammenfassung / ✨ Zusammenfassen
|
||
article/[id]/+page.svelte:778 🔍 Suchen
|
||
article/[id]/+page.svelte:940 🔑 Eigener Schlüssel
|
||
rueckblick/+page.svelte:266 ✨ Synthese erzeugen
|
||
SummarizeModal.svelte:206,264 ✨ / 🔑
|
||
/feed 💻 🔬 🌍 📈 🎭 🩺 🏛
|
||
dazu Textzeichen als Icon-Ersatz: ★ ✓ ✕ ← → ↗ ▶
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Phosphor-Icons, <code>duotone</code> für inhaltstragende,
|
||
<code>regular</code> für Nebenelemente. Gleiches gilt für die
|
||
Pfeil- und Häkchen-Textzeichen.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-11</span>
|
||
<span class="sev sev-warn">Wichtig</span>
|
||
<span class="where">/konto · /lese-liste/settings<br />/article/[id]</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Platzhalter statt Beschriftung</h3>
|
||
<p>
|
||
Sechs Felder tragen ihre Bedeutung nur im Placeholder — sobald man
|
||
tippt, ist sie weg. DESIGN.md: Label über dem Feld, Placeholder nur
|
||
als Hinweis.
|
||
</p>
|
||
<p class="evidence">
|
||
/konto https://cloud.example/remote.php/… · Benutzer · App-Passwort
|
||
/lese-liste/settings Blog-URL · Domain · Quellen-Titel
|
||
/lese-liste https://…
|
||
/article/[id] z.B. politik, klima, gelesen-2026
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> <code>/konto/ki</code> macht es bereits richtig
|
||
(„Anbieter", „Modell", „API-Schlüssel" als echte Labels) — das ist
|
||
das Muster für den Rest.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-12</span>
|
||
<span class="sev sev-warn">Wichtig</span>
|
||
<span class="where">/konto · /mana · /lese-liste</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Drei verschiedene Inhaltsbreiten</h3>
|
||
<p>
|
||
<code>+layout.svelte</code> setzt <code>max-w-3xl px-4</code>, also
|
||
736 px Inhalt. Zwei Seiten packen eigene Container darum. Beim
|
||
Navigieren springt die Spalte sichtbar.
|
||
</p>
|
||
<p class="evidence">
|
||
/lese-liste · /markierungen · /konto/ki 736 px
|
||
/mana 704 px
|
||
/konto 640 px
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Eine <code>PageShell</code>-Komponente, die Breite,
|
||
Titel, Untertitel, optionalen Zurück-Link und den Abstand zur
|
||
unteren Leiste zentral setzt. Seiten liefern nur noch Inhalt.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-13</span>
|
||
<span class="sev sev-warn">Wichtig</span>
|
||
<span class="where">src/routes/ — Datei fehlt</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Keine eigene Fehlerseite</h3>
|
||
<p>
|
||
Es gibt kein <code>+error.svelte</code>. Eine falsche URL landet auf
|
||
SvelteKits nacktem Standard: „404 / Not Found", englisch, ohne Titel,
|
||
ohne Weg zurück — mitten in einer deutschen App.
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> <code>+error.svelte</code> mit Icon, einem erklärenden
|
||
Satz und einem Knopf zurück in die Lese-Liste. Gleiche Sprache wie
|
||
der Rest.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-14</span>
|
||
<span class="sev sev-warn">Wichtig</span>
|
||
<span class="where">global · Tailwind base</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Der Fokusring ist Browser-Blau</h3>
|
||
<p>
|
||
Durch die Liste getabbt: Artikel-Links bekommen den Systemring, nicht
|
||
den Akzent. <code>:focus-visible</code> ist nur für
|
||
<code>.pill</code>, <code>.split-button</code> und
|
||
<code>.avatar-link</code> definiert — acht Regeln im ganzen Stylesheet.
|
||
</p>
|
||
<p class="evidence">
|
||
document.activeElement → A „the-decoder.de"
|
||
outline: rgb(0, 95, 204) auto 1px erwartet: 2px solid hsl(var(--color-primary))
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Eine globale <code>:focus-visible</code>-Regel in
|
||
<code>@layer base</code> (nicht in <code>components</code> — sonst
|
||
gewinnt sie gegen Utilities nicht zuverlässig).
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-15</span>
|
||
<span class="sev sev-warn">Wichtig</span>
|
||
<span class="where">lese-liste/+page.svelte:581</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Nur der Titel ist klickbar, nicht die Karte</h3>
|
||
<p>
|
||
Der <code><a></code> umschließt ausschließlich den Titeltext —
|
||
bei „the-decoder.de" sind das 119 px in einer 736 px breiten Karte.
|
||
Der Rest der Karte sieht klickbar aus und ist es nicht. Der Fokusring
|
||
zeigt dasselbe Missverhältnis.
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Karte als Ziel, Titel als Link darin
|
||
(<code>::after</code>-Stretch-Pattern), damit Semantik und
|
||
Trefferfläche zusammenpassen.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-16</span>
|
||
<span class="sev sev-warn">Wichtig</span>
|
||
<span class="where">+layout.svelte · footer</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Die Fußzeile liegt hinter der Navigation</h3>
|
||
<p>
|
||
Auf kurzen Seiten endet der Inhalt im 56-px-Streifen der fixierten
|
||
Leiste. „Datenschutz · pageta.com · Pageta · mana" ist dann
|
||
halb überdeckt — gesehen auf <code>/article/[id]</code>,
|
||
<code>/recherche</code> und <code>/konto/ki</code>.
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Fällt mit P-01 zusammen: die Leiste als belegter Raum,
|
||
<code>padding-bottom</code> aus einer geteilten Variable statt
|
||
einer geschätzten <code>pb-28</code>.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-17</span>
|
||
<span class="sev sev-warn">Wichtig</span>
|
||
<span class="where">/markierungen · /konto</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Interne UUIDs stehen im Interface</h3>
|
||
<p>
|
||
Auf <code>/markierungen</code> steht neben jedem Zitat die Konto-UUID
|
||
als wäre sie ein Metadatum — in derselben Zeile wie Artikeltitel und
|
||
Quelle. Auf <code>/konto</code> steht sie als „Konto-ID", dort
|
||
berechtigt, aber ohne Kopier-Aktion.
|
||
</p>
|
||
<p class="evidence">
|
||
Stable Audio 3.0 erscheint mit offenen… · The De… · 5fbb3222-543f-4a6b-b278-5432d1ce4cba
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Aus der Markierungs-Zeile entfernen; auf
|
||
<code>/konto</code> einen Kopier-Knopf danebenstellen.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-18</span>
|
||
<span class="sev sev-warn">Wichtig</span>
|
||
<span class="where">/rueckblick · /feed<br />/markierungen</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Leerzustände ohne Weg nach vorn</h3>
|
||
<p>
|
||
Ein grauer Satz, sonst nichts — kein Icon, keine Handlung, und
|
||
darunter 400 px Leere bis zur Fußzeile. DESIGN.md verlangt Icon,
|
||
erklärenden Satz und klare Aufforderung.
|
||
</p>
|
||
<p class="evidence">
|
||
/rueckblick „In diesem Zeitraum hast du noch nichts (zu Ende) gelesen.
|
||
Sobald du Artikel liest, erscheinen sie hier."
|
||
/feed „Noch keine Artikel im Feed."
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Eine <code>EmptyState</code>-Komponente mit genau einer
|
||
Aktion — auf <code>/rueckblick</code> etwa „Etwas zu lesen suchen"
|
||
zurück in die Liste.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-19</span>
|
||
<span class="sev sev-warn">Wichtig</span>
|
||
<span class="where">/quelle/[slug]</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Zwei Leerzustände gleichzeitig, dazu der rohe Slug</h3>
|
||
<p>
|
||
Die Seite zeigt eine Karte („Diese Quelle ist noch nicht angelegt…")
|
||
<em>und</em> darunter einen zweiten Satz („Noch keine Artikel dieser
|
||
Quelle…") — zwei Aufforderungen zu verschiedenen Dingen. Titel und
|
||
Untertitel sind beide der unformatierte Slug.
|
||
</p>
|
||
<p class="evidence">
|
||
QUELLE
|
||
howtomarketagame-com ← Titel (erwartet: howtomarketagame.com)
|
||
howtomarketagame-com ← Untertitel, identisch
|
||
0 Artikel
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Einen Zustand pro Situation, Slug zurück in eine Domain
|
||
übersetzen, und den Untertitel weglassen, wenn er den Titel wiederholt.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-20</span>
|
||
<span class="sev sev-warn">Wichtig</span>
|
||
<span class="where">/feed · /recherche</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Ruhende Seiten sind live erreichbar</h3>
|
||
<p>
|
||
Feed und Recherche sind seit 01.06.2026 aus der Navigation genommen,
|
||
antworten aber weiter — inklusive Emoji-Themenliste und einem
|
||
Formular, dessen Ergebnis nirgends landet. Wer den Link kennt oder
|
||
ein Lesezeichen hat, sieht halbfertige App.
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Entweder auf <code>/lese-liste</code> umleiten oder mit
|
||
einem ehrlichen Hinweis versehen („ruht — Begründung"). Der Code darf
|
||
im Repo bleiben, die Route nicht offen.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<!-- ======================= FEINSCHLIFF ======================= -->
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-21</span>
|
||
<span class="sev sev-fine">Feinschliff</span>
|
||
<span class="where">/article/[id]</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Der Lesemodus verliert die Struktur des Artikels</h3>
|
||
<p>
|
||
Im geprüften Artikel gibt es null <code><p></code> und null
|
||
<code><h2></code> — der Text kommt als ein Block. Folge:
|
||
Zwischenüberschriften („Zweifel an Teslas Autopilot", „Robotaxi mit
|
||
Einschränkungen") sehen aus wie Fließtext. Dazu Erstzeilen-Einzug
|
||
<em>und</em> Leerzeile zwischen Absätzen: zwei Signale für dasselbe.
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Struktur aus der Extraktion erhalten und im Reader
|
||
typografisch setzen. Eines der beiden Absatzsignale streichen —
|
||
bei ~80 Zeichen Zeilenlänge ist der Einzug der überflüssige.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-22</span>
|
||
<span class="sev sev-fine">Feinschliff</span>
|
||
<span class="where">/article/[id]</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Sieben gleichwertige Knöpfe über dem Artikel</h3>
|
||
<p>
|
||
Original · Favorit · Fertig gelesen · Archivieren · Aa · Suchen ·
|
||
Löschen stehen in einer Reihe, alle im selben Grau. DESIGN.md:
|
||
höchstens ein Primär pro Ansicht, danach Secondary, Ghost, Danger.
|
||
„Löschen" ist nur rot beschriftet, sonst identisch — die riskanteste
|
||
Aktion sitzt gleichberechtigt neben der harmlosesten.
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Zwei bis drei sichtbare Aktionen, der Rest in ein
|
||
Überlaufmenü. Löschen dorthin und mit Bestätigung.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-23</span>
|
||
<span class="sev sev-fine">Feinschliff</span>
|
||
<span class="where">/article/[id]</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>„? MIN" statt gar nichts</h3>
|
||
<p>
|
||
Ist die Lesezeit unbekannt, steht ein Fragezeichen in der Kopfzeile.
|
||
DESIGN.md: leere Angaben ausblenden statt Platzhalter zeigen.
|
||
</p>
|
||
<p class="evidence">
|
||
THE-DECODER.DE · ? MIN
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-24</span>
|
||
<span class="sev sev-fine">Feinschliff</span>
|
||
<span class="where">/lese-liste · /lese-liste/add<br />/recherche</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Deaktivierte Primärknöpfe sehen aus wie aktive</h3>
|
||
<p>
|
||
„Hinzufügen", „Speichern" und „Feeds finden" behalten die
|
||
Primärfarbe und werden nur transparenter. Weißer Text auf
|
||
halbtransparentem Braun landet unter 3 : 1 — man liest es schlecht
|
||
<em>und</em> hält es für klickbar.
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Deaktiviert = neutrale Fläche mit
|
||
<code>--color-muted-foreground</code>, nicht Primär mit Opacity.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-25</span>
|
||
<span class="sev sev-fine">Feinschliff</span>
|
||
<span class="where">alle Routen</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Fünf Schemata für den Seitentitel</h3>
|
||
<p>
|
||
Der Tab-Titel folgt keiner Regel. Die Hälfte der Seiten heißt einfach
|
||
„Pageta" — Lesezeichen und Verlauf sind dadurch nutzlos.
|
||
</p>
|
||
<p class="evidence">
|
||
/lese-liste Pageta
|
||
/konto /konto/ki Pageta
|
||
/recherche Pageta
|
||
/mana Mana – Pageta (Halbgeviertstrich)
|
||
/lese-liste/add Artikel speichern — Pageta (Geviertstrich)
|
||
/changelog Changelog — Was ist neu (ohne App-Namen)
|
||
/devlog Devlog
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Ein Schema: <code><Seite> — Pageta</code>, gesetzt
|
||
in der <code>PageShell</code>, nicht pro Route.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-26</span>
|
||
<span class="sev sev-fine">Feinschliff</span>
|
||
<span class="where">/lese-liste/add</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Eine eigene Seite für ein Feld, das schon in der Liste steht</h3>
|
||
<p>
|
||
<code>/lese-liste/add</code> zeigt Titel, ein URL-Feld und einen
|
||
Knopf — exakt das, was oben auf <code>/lese-liste</code> ohnehin
|
||
steht. DESIGN.md: Neues entsteht inline in der Liste, kein
|
||
Seitensprung.
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Route als Einstiegspunkt für Bookmarklet und Share-Target
|
||
behalten, aber auf die Liste umleiten und dort das Feld fokussieren.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-27</span>
|
||
<span class="sev sev-fine">Feinschliff</span>
|
||
<span class="where">/konto vs. /lese-liste/settings</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Zwei Grammatiken für Abschnittsüberschriften</h3>
|
||
<p>
|
||
<code>/konto</code> nutzt kleine Versalien („ANMELDUNG", „AURA",
|
||
„ERSCHEINUNG"), <code>/lese-liste/settings</code> große fette Sätze
|
||
(„Blog oder Feed einmalig importieren"). Beides sind
|
||
Einstellungsseiten.
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Versalien-Label als Sektionsmarke festlegen, fette Sätze
|
||
für Karten <em>innerhalb</em> einer Sektion.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-28</span>
|
||
<span class="sev sev-fine">Feinschliff</span>
|
||
<span class="where">/lese-liste/settings</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Das „EMPFOHLEN"-Etikett ragt aus der Karte</h3>
|
||
<p>
|
||
Das grüne Etikett sitzt auf der oberen linken Kartenkante und wird
|
||
beim Scrollen von der Navigationsleiste angeschnitten. Zudem markiert
|
||
Grün hier eine Empfehlung, nicht einen Erfolg — <code>--color-success</code>
|
||
für Betonung zweckentfremdet.
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Etikett in die Kartenkopfzeile, Farbe aus der
|
||
Akzent-Ebene statt aus der Semantik-Ebene.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-29</span>
|
||
<span class="sev sev-fine">Feinschliff</span>
|
||
<span class="where">/konto</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Konto öffnet in der Mitte</h3>
|
||
<p>
|
||
Über den Avatar aufgerufen, startet die Seite gescrollt — die
|
||
Überschrift „Konto" und der erklärende Satz sind oben abgeschnitten.
|
||
Reproduziert bei 1280 px und bei 567 px.
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Scroll-Restoration für Navigationsziele aus dem globalen
|
||
Chrome zurücksetzen.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-30</span>
|
||
<span class="sev sev-fine">Feinschliff</span>
|
||
<span class="where">alle Listen</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Am Desktop bleiben zwei Drittel leer</h3>
|
||
<p>
|
||
Bei 1280 px steht eine 736 px breite, einspaltige Liste in der Mitte,
|
||
links und rechts je ~270 px Leere. Mobile-First heißt nicht
|
||
Mobile-Only — ab ~1000 px wären zwei Spalten oder eine ruhige
|
||
Seitenspalte (Filter, Quellen) angemessen.
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Einen dritten Breakpoint definieren. Die Filter-Chips
|
||
wandern dabei aus der Kopfzeile in die Seitenspalte.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-31</span>
|
||
<span class="sev sev-fine">Feinschliff</span>
|
||
<span class="where">/konto → Variante</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Sieben namenlose Farbkreise</h3>
|
||
<p>
|
||
Die Theme-Varianten sind nur als Kreise sichtbar; der Name („Papier ·
|
||
Sepia, warm, lesefokussiert") erscheint erst <em>nach</em> der Auswahl.
|
||
Man wählt blind und ändert dabei das Aussehen der ganzen App.
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Name beim Hovern und Fokussieren zeigen, nicht erst nach
|
||
dem Klick.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-32</span>
|
||
<span class="sev sev-fine">Feinschliff</span>
|
||
<span class="where">/lese-liste</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Keine Vorschaubilder im Bestand</h3>
|
||
<p>
|
||
Die Karte rendert <code>a.imageUrl</code> korrekt als 64-px-Thumbnail —
|
||
nur hat im Bestand fast kein Artikel eines. Die Bild-Archivierung
|
||
greift organisch beim Neu-Speichern, es gibt aber keinen Weg im UI,
|
||
sie für Altbestand nachzuziehen.
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> „Bilder nachladen" in die Lese-Listen-Einstellungen —
|
||
derselbe Ort, an dem auch Quellen nachgezogen werden.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-33</span>
|
||
<span class="sev sev-fine">Feinschliff</span>
|
||
<span class="where">/markierungen</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Abgeschnitten, obwohl Platz da ist</h3>
|
||
<p>
|
||
Artikeltitel und Quelle werden gekürzt („Stable Audio 3.0 erscheint
|
||
mit offenen Gewichten und voll lizenzi…", „The De…") — bei 1280 px,
|
||
neben freier Fläche. „+ Notiz hinzufügen" ist ein grauer
|
||
Kleintext ohne jede Knopf-Anmutung.
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Titel zweizeilig erlauben, Quelle nicht kürzen; die
|
||
Notiz-Aktion als Ghost-Button mit Icon.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-34</span>
|
||
<span class="sev sev-fine">Feinschliff</span>
|
||
<span class="where">/datenschutz</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Die Datenschutz-Tabelle hat kein Tabellen-Styling</h3>
|
||
<p>
|
||
Drei Spalten (Datenkategorie / Zweck / Rechtsgrundlage) ohne
|
||
Kopfzeilen-Absetzung, ohne Zeilentrenner, ohne Zellabstand — die
|
||
Zeilen laufen optisch ineinander. Bei einem Rechtstext ist
|
||
Lesbarkeit nicht Kosmetik.
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Basis-Tabellenstile ins Theme (Kopfzeile abgesetzt,
|
||
Zeilentrenner, <code>overflow-x</code> am Container) — kommt allen
|
||
Rechtstexten der Flotte zugute.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-35</span>
|
||
<span class="sev sev-fine">Feinschliff</span>
|
||
<span class="where">+layout.svelte:152 (startSlot)</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Das Pageta-Logo hängt neben der Navigation</h3>
|
||
<p>
|
||
Der <code>startSlot</code> rendert ein 28 × 28-Bild ohne eigene
|
||
Pille. Zwischen den gerahmten Pillen wirkt das rote Lesezeichen wie
|
||
ein verrutschtes Element statt wie ein Absender.
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Entweder als Pille rahmen wie die Geschwister oder
|
||
weglassen — die App-Identität trägt schon der Tab-Titel.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-36</span>
|
||
<span class="sev sev-fine">Feinschliff</span>
|
||
<span class="where">/lese-liste · /rueckblick</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Filterleisten stehen oben statt am Daumen</h3>
|
||
<p>
|
||
DESIGN.md verankert nicht nur die Navigation unten, sondern
|
||
ausdrücklich auch Filter- und Segmentleisten. Die Quellen-Chips der
|
||
Lese-Liste und die Zeitraum-Segmente des Rückblicks sitzen im Kopf.
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Auf schmalen Breiten über der Navigation einblenden
|
||
(<code>safeAreaInset</code>-Pendant), am Desktop dürfen sie oben
|
||
oder in die Seitenspalte.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
|
||
<article class="finding">
|
||
<div class="rail">
|
||
<span class="fid">P-37</span>
|
||
<span class="sev sev-fine">Feinschliff</span>
|
||
<span class="where">package.json</span>
|
||
</div>
|
||
<div class="body">
|
||
<h3>Der Token-Wächter ist nicht verdrahtet</h3>
|
||
<p>
|
||
<code>audit:theme-tokens</code> ist in DESIGN.md als Drift-Schutz
|
||
vorgesehen und existiert in der Plattform — im pageta-Repo gibt es
|
||
weder das Skript noch einen <code>lint-staged</code>-Haken. Genau
|
||
deshalb konnten 44 Hex-Literale unbemerkt einziehen.
|
||
</p>
|
||
<p class="fix">
|
||
<b>Fix</b> Skript übernehmen und an <code>format</code> /
|
||
CI hängen. Das ist die Maßnahme, die P-04 dauerhaft verhindert.
|
||
</p>
|
||
</div>
|
||
</article>
|
||
</div>
|
||
</section>
|
||
|
||
<section>
|
||
<div class="section-head">
|
||
<span class="eyebrow">Teil 2</span>
|
||
<h2>Seiten-Matrix</h2>
|
||
<p class="lede" style="font-size: 1rem">
|
||
Alle geprüften Routen mit ihrem schwersten Befund.
|
||
</p>
|
||
</div>
|
||
<div class="tablewrap">
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Route</th>
|
||
<th>Zustand</th>
|
||
<th>Schwerster Befund</th>
|
||
<th>IDs</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>/lese-liste</td>
|
||
<td><span class="dot d-crit"></span>kritisch</td>
|
||
<td>Titel fehlen, Zusammenfassung liefert Prompt</td>
|
||
<td>P-03 P-05 P-09 P-15</td>
|
||
</tr>
|
||
<tr>
|
||
<td>/article/[id]</td>
|
||
<td><span class="dot d-crit"></span>kritisch</td>
|
||
<td>Hex-Literale, Struktur des Artikels verloren</td>
|
||
<td>P-04 P-21 P-22 P-23</td>
|
||
</tr>
|
||
<tr>
|
||
<td>/changelog</td>
|
||
<td><span class="dot d-crit"></span>kritisch</td>
|
||
<td>Im Dunkelmodus unlesbar (1,05 : 1)</td>
|
||
<td>P-04 P-25</td>
|
||
</tr>
|
||
<tr>
|
||
<td>/devlog · /devlog/[date]</td>
|
||
<td><span class="dot d-crit"></span>kritisch</td>
|
||
<td>Weiße Bänder und dunkle Titel im Dunkelmodus</td>
|
||
<td>P-04 P-25</td>
|
||
</tr>
|
||
<tr>
|
||
<td>/konto</td>
|
||
<td><span class="dot d-warn"></span>wichtig</td>
|
||
<td>Modus-Schalter unter der Navigation, Felder ohne Label</td>
|
||
<td>P-01 P-11 P-12 P-29</td>
|
||
</tr>
|
||
<tr>
|
||
<td>/lese-liste/settings</td>
|
||
<td><span class="dot d-warn"></span>wichtig</td>
|
||
<td>Felder ohne Label, Etikett unter der Leiste</td>
|
||
<td>P-11 P-27 P-28</td>
|
||
</tr>
|
||
<tr>
|
||
<td>/markierungen</td>
|
||
<td><span class="dot d-warn"></span>wichtig</td>
|
||
<td>UUID im Interface, Kürzung ohne Not</td>
|
||
<td>P-17 P-33</td>
|
||
</tr>
|
||
<tr>
|
||
<td>/quelle/[slug]</td>
|
||
<td><span class="dot d-warn"></span>wichtig</td>
|
||
<td>Zwei Leerzustände, roher Slug als Titel</td>
|
||
<td>P-19</td>
|
||
</tr>
|
||
<tr>
|
||
<td>/feed</td>
|
||
<td><span class="dot d-warn"></span>wichtig</td>
|
||
<td>Ruhend, aber live — mit Emoji-Themen</td>
|
||
<td>P-10 P-18 P-20</td>
|
||
</tr>
|
||
<tr>
|
||
<td>/recherche</td>
|
||
<td><span class="dot d-warn"></span>wichtig</td>
|
||
<td>Ruhend, aber live; Ergebnis landet nirgends</td>
|
||
<td>P-20 P-24</td>
|
||
</tr>
|
||
<tr>
|
||
<td>404 (beliebige URL)</td>
|
||
<td><span class="dot d-warn"></span>wichtig</td>
|
||
<td>SvelteKit-Standard, englisch, ohne Rückweg</td>
|
||
<td>P-13</td>
|
||
</tr>
|
||
<tr>
|
||
<td>/rueckblick</td>
|
||
<td><span class="dot d-fine"></span>feinschliff</td>
|
||
<td>Leerzustand ohne Aktion, Segmente oben</td>
|
||
<td>P-18 P-36</td>
|
||
</tr>
|
||
<tr>
|
||
<td>/mana</td>
|
||
<td><span class="dot d-fine"></span>feinschliff</td>
|
||
<td>Eigene Inhaltsbreite (704 px)</td>
|
||
<td>P-12</td>
|
||
</tr>
|
||
<tr>
|
||
<td>/lese-liste/add</td>
|
||
<td><span class="dot d-fine"></span>feinschliff</td>
|
||
<td>Dupliziert das Feld der Liste</td>
|
||
<td>P-24 P-26</td>
|
||
</tr>
|
||
<tr>
|
||
<td>/datenschutz</td>
|
||
<td><span class="dot d-fine"></span>feinschliff</td>
|
||
<td>Tabelle ohne Tabellen-Styling</td>
|
||
<td>P-34</td>
|
||
</tr>
|
||
<tr>
|
||
<td>/konto/ki</td>
|
||
<td><span class="dot d-ok"></span>Referenz</td>
|
||
<td>Echte Labels, Zurück-Link, Radio-Karten — Vorbild</td>
|
||
<td>—</td>
|
||
</tr>
|
||
<tr>
|
||
<td>/offline</td>
|
||
<td><span class="dot d-ok"></span>in Ordnung</td>
|
||
<td>Klare Aussage, eine Aktion</td>
|
||
<td>—</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</section>
|
||
|
||
<section>
|
||
<div class="section-head">
|
||
<span class="eyebrow">Teil 3</span>
|
||
<h2>Regeln, die aus diesen Befunden folgen</h2>
|
||
<p class="lede" style="font-size: 1rem">
|
||
DESIGN.md sagt <em>was</em> gilt. Diese zwölf Punkte sagen, <em>wie</em>
|
||
es in pageta konkret aussieht — mit dem Gegenbeispiel aus dem eigenen
|
||
Code daneben.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="rules">
|
||
<div class="rule-item">
|
||
<span class="rule-n">01</span>
|
||
<div class="rule-body">
|
||
<h3>Die untere Leiste ist belegter Raum, kein Overlay</h3>
|
||
<p>
|
||
56 px plus Safe-Area gehören der Navigation. Kein Inhalt endet darin,
|
||
kein Bedienelement liegt darunter, und dahinter läuft ein Scrim.
|
||
Die Höhe kommt aus einer Variable, nicht aus einer geratenen
|
||
Utility-Klasse.
|
||
</p>
|
||
<div class="dodont">
|
||
<div class="no">
|
||
<span class="tag">Nicht</span>
|
||
<span>main class="pb-28"<br />.pill-nav { background: none }</span>
|
||
</div>
|
||
<div class="yes">
|
||
<span class="tag">Sondern</span>
|
||
<span>padding-bottom: var(--bottom-chrome)<br />+ blur/scrim in der Leiste</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rule-item">
|
||
<span class="rule-n">02</span>
|
||
<div class="rule-body">
|
||
<h3>Die Z-Index-Leiter ist nicht verhandelbar</h3>
|
||
<p>
|
||
Sticky 10 · Dropdown 50 · fixierte Navigation 90–100 · Modal 9995 ·
|
||
Toast 9999. Wer eine Zahl frei wählt, überdeckt irgendwann etwas
|
||
Wichtiges — hier war es der Modal-Fuß.
|
||
</p>
|
||
<div class="dodont">
|
||
<div class="no">
|
||
<span class="tag">Ist</span>
|
||
<span>nav 1000 · overlay 50</span>
|
||
</div>
|
||
<div class="yes">
|
||
<span class="tag">Soll</span>
|
||
<span>nav 100 · overlay 9995</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rule-item">
|
||
<span class="rule-n">03</span>
|
||
<div class="rule-body">
|
||
<h3>Ein Seiten-Gerüst für alle Routen</h3>
|
||
<p>
|
||
Eine <code>PageShell</code> setzt Breite, Titel, Untertitel,
|
||
Dokumenttitel, optionalen Zurück-Link und den unteren Abstand.
|
||
Seiten liefern Inhalt, keine Container. Das erledigt P-12, P-16,
|
||
P-25 und die Hälfte von P-29 in einem Zug.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rule-item">
|
||
<span class="rule-n">04</span>
|
||
<div class="rule-body">
|
||
<h3>Datenwerte werden nie roh gerendert</h3>
|
||
<p>
|
||
Enums, IDs, Slugs und Hosts gehen durch eine Übersetzungs- oder
|
||
Formatierungsfunktion, bevor sie sichtbar werden. Wenn im UI
|
||
<code>UNREAD</code>, eine UUID oder <code>howtomarketagame-com</code>
|
||
steht, fehlt diese Funktion.
|
||
</p>
|
||
<div class="dodont">
|
||
<div class="no">
|
||
<span class="tag">Nicht</span>
|
||
<span>· {a.status}</span>
|
||
</div>
|
||
<div class="yes">
|
||
<span class="tag">Sondern</span>
|
||
<span>· {statusLabel(a.status)}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rule-item">
|
||
<span class="rule-n">05</span>
|
||
<div class="rule-body">
|
||
<h3>Icons aus dem Paket, nie Emojis oder Textzeichen</h3>
|
||
<p>
|
||
Emojis rendern je nach Plattform anders, ignorieren die Theme-Farbe
|
||
und lassen sich nicht in der Größe kontrollieren. Dasselbe gilt für
|
||
★ ✓ ✕ ← → ↗ ▶ als Icon-Ersatz. Phosphor aus
|
||
<code>@mana/shared-icons</code>: <code>duotone</code> für
|
||
inhaltstragend, <code>regular</code> für Nebensache,
|
||
<code>fill</code> für aktiv.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rule-item">
|
||
<span class="rule-n">06</span>
|
||
<div class="rule-body">
|
||
<h3>Jedes Feld hat eine Beschriftung über sich</h3>
|
||
<p>
|
||
Der Placeholder ist ein Beispiel, kein Label — er verschwindet beim
|
||
Tippen und ist für Screenreader kein Ersatz. Vorbild im eigenen
|
||
Haus: <code>/konto/ki</code>.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rule-item">
|
||
<span class="rule-n">07</span>
|
||
<div class="rule-body">
|
||
<h3>Jede Seite existiert in beiden Modi</h3>
|
||
<p>
|
||
Keine Hex-Literale, keine Tailwind-Palettenklassen
|
||
(<code>bg-amber-50</code>, <code>text-red-700</code>,
|
||
<code>bg-neutral-900</code>) in Komponenten — nur
|
||
<code>hsl(var(--color-*))</code>. Vor dem Merge beide Modi
|
||
anschauen; <code>audit:theme-tokens</code> macht daraus einen
|
||
automatischen Haken.
|
||
</p>
|
||
<div class="dodont">
|
||
<div class="no">
|
||
<span class="tag">Nicht</span>
|
||
<span>color: #1c2127<br />class="bg-amber-50 text-stone-900"</span>
|
||
</div>
|
||
<div class="yes">
|
||
<span class="tag">Sondern</span>
|
||
<span>color: hsl(var(--color-foreground))<br />bg-[hsl(var(--color-surface))]</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rule-item">
|
||
<span class="rule-n">08</span>
|
||
<div class="rule-body">
|
||
<h3>Ein Leerzustand ist Icon, Satz und genau eine Aktion</h3>
|
||
<p>
|
||
Und immer nur einer pro Situation. Zwei gleichzeitig
|
||
(<code>/quelle/[slug]</code>) verwirren mehr als keiner.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rule-item">
|
||
<span class="rule-n">09</span>
|
||
<div class="rule-body">
|
||
<h3>Die ganze Karte ist das Ziel, mindestens 44 px hoch</h3>
|
||
<p>
|
||
Was wie eine Fläche aussieht, verhält sich wie eine. Der Fokusring
|
||
umschließt dieselbe Fläche wie der Mauszeiger sie trifft.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rule-item">
|
||
<span class="rule-n">10</span>
|
||
<div class="rule-body">
|
||
<h3>Der Fokusring ist Akzentfarbe, global, in <code>@layer base</code></h3>
|
||
<p>
|
||
Eine Regel für alles Fokussierbare, nicht drei Regeln für drei
|
||
Komponenten. In <code>base</code>, damit Utilities sie nicht
|
||
überschreiben.
|
||
</p>
|
||
<div class="dodont">
|
||
<div class="no">
|
||
<span class="tag">Ist</span>
|
||
<span>outline: rgb(0,95,204) auto 1px<br>(Browser-Standard)</span>
|
||
</div>
|
||
<div class="yes">
|
||
<span class="tag">Soll</span>
|
||
<span>outline: 2px solid hsl(var(--color-primary));<br />outline-offset: 2px</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rule-item">
|
||
<span class="rule-n">11</span>
|
||
<div class="rule-body">
|
||
<h3>Fehler und Sackgassen tragen dieselbe Sprache wie der Rest</h3>
|
||
<p>
|
||
Eigene <code>+error.svelte</code>, deutscher Text, Weg zurück. Keine
|
||
Route antwortet halbfertig: was ruht, leitet um oder erklärt sich.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="rule-item">
|
||
<span class="rule-n">12</span>
|
||
<div class="rule-body">
|
||
<h3>Aktionen haben eine Rangfolge, Gefährliches steht abseits</h3>
|
||
<p>
|
||
Höchstens ein Primär pro Ansicht. Zwei bis drei sichtbare Aktionen,
|
||
der Rest ins Überlaufmenü. Löschen sitzt nicht in derselben Reihe wie
|
||
„Favorit". Deaktiviert heißt neutral, nicht durchsichtig-primär.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section>
|
||
<div class="section-head">
|
||
<span class="eyebrow">Teil 4</span>
|
||
<h2>Reihenfolge</h2>
|
||
<p class="lede" style="font-size: 1rem">
|
||
Nach Wirkung pro Aufwand. Welle 1 fasst die Befunde zusammen, die
|
||
gemeinsame Ursachen haben.
|
||
</p>
|
||
</div>
|
||
<div class="waves">
|
||
<div class="wave">
|
||
<h3>Welle 1 — die drei Ursachen</h3>
|
||
<ul>
|
||
<li>Scrim + Höhenvariable für die untere Leiste, Z-Index-Leiter richtigstellen</li>
|
||
<li>Hex-Literale in Changelog, Devlog, Reader auf Tokens ziehen</li>
|
||
<li><code>audit:theme-tokens</code> verdrahten, damit es nicht zurückkommt</li>
|
||
<li><code>PageShell</code> einführen, alle Routen darauf heben</li>
|
||
</ul>
|
||
<span class="ids">P-01 P-02 P-04 P-12 P-16 P-25 P-29 P-37</span>
|
||
</div>
|
||
<div class="wave">
|
||
<h3>Welle 2 — sichtbare Wahrheit</h3>
|
||
<ul>
|
||
<li>Consent-Wall-Erkennung beim Speichern, „Erneut extrahieren" im Kontextmenü</li>
|
||
<li>Prompt-Echo der Zusammenfassung stoppen, Fehlerzustand zeigen</li>
|
||
<li>Status-Mapping und Slug-Formatierung, UUID aus den Markierungen</li>
|
||
<li>Labels über alle Felder, <code>+error.svelte</code>, ruhende Routen umleiten</li>
|
||
</ul>
|
||
<span class="ids">P-03 P-05 P-09 P-11 P-13 P-17 P-19 P-20</span>
|
||
</div>
|
||
<div class="wave">
|
||
<h3>Welle 3 — Hand und Auge</h3>
|
||
<ul>
|
||
<li>44-px-Ziele, Karte als Ziel, Avatar-Korridor in der Kopfzeile</li>
|
||
<li>Navigation unter 430 px auf Icon-only</li>
|
||
<li>Globaler Fokusring, Icons statt Emojis</li>
|
||
<li>Leerzustand-Komponente, Aktions-Rangfolge im Reader</li>
|
||
</ul>
|
||
<span class="ids">P-06 P-07 P-08 P-10 P-14 P-15 P-18 P-22 P-24</span>
|
||
</div>
|
||
<div class="wave">
|
||
<h3>Welle 4 — Politur</h3>
|
||
<ul>
|
||
<li>Desktop-Breakpoint, Filter in die Seitenspalte</li>
|
||
<li>Reader-Typografie: Struktur erhalten, ein Absatzsignal</li>
|
||
<li>Tabellen-Basisstile, Varianten-Namen, Bild-Nachladen</li>
|
||
</ul>
|
||
<span class="ids">P-21 P-23 P-26 P-27 P-28 P-30 P-31 P-32 P-33 P-34 P-35 P-36</span>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section>
|
||
<div class="section-head">
|
||
<span class="eyebrow">Teil 5</span>
|
||
<h2>Methode und Grenzen</h2>
|
||
</div>
|
||
<ul class="plain">
|
||
<li>
|
||
<b>Geprüft</b> 17 Routen plus eine erfundene URL, angemeldet, im
|
||
Live-Build. Jede Seite in hell und dunkel, bei 1280 px und 567 px
|
||
Viewport. Messwerte stammen aus dem laufenden DOM
|
||
(<code>getBoundingClientRect</code>, <code>getComputedStyle</code>),
|
||
nicht aus Screenshot-Schätzungen.
|
||
</li>
|
||
<li>
|
||
<b>Nicht geprüft</b> Breiten unter 500 px — das Browserfenster ließ sich
|
||
nicht kleiner ziehen. Die Aussagen zu 390 px in P-08 sind aus der
|
||
gemessenen intrinsischen Breite (562 px) abgeleitet, nicht am Gerät
|
||
beobachtet. Ebenfalls offen: iOS-Safari, Screenreader,
|
||
<code>prefers-reduced-motion</code>, Ladezustände unter langsamem Netz,
|
||
und die nativen iOS-/Android-Fassungen.
|
||
</li>
|
||
<li>
|
||
<b>Nebenbefund für die Plattform</b> P-01, P-02 und P-08 stecken in
|
||
<code>@mana/shared-ui</code> — die transparente
|
||
<code>PillNavigation</code> mit <code>z-index: 1000</code> betrifft alle
|
||
Vereins-Web-Apps, nicht nur pageta. Der Fix gehört ins geteilte Paket.
|
||
</li>
|
||
<li>
|
||
<b>Was gut ist</b> Das Papier-Theme trägt, der Lesemodus ist typografisch
|
||
ruhig, die Leseposition wird gemerkt, Escape schließt Modals,
|
||
<code>/konto/ki</code> ist ein sauber gebautes Formular, und die
|
||
Sprache der App ist durchgehend direkt statt marketingheiß. Die Befunde
|
||
oben sind Reibung an einer funktionierenden App, nicht ihr Gegenteil.
|
||
</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<footer class="colophon">
|
||
<span>Pageta · Korrekturabzug 2026-07-26 · geprüft gegen mana/docs/DESIGN.md</span>
|
||
<span>apps/pageta/web/docs/ui-review-2026-07-26.html</span>
|
||
</footer>
|
||
</div>
|