feat: Cursor-Zeile hebt sich aus der Ebene, plus Puls beim Zeilenwechsel
Die Hervorhebung der Cursor-Zeile (D25) war zu leise. Sie benutzte denselben Kanal, den schon jeder Knoten belegt — jeder Status hat einen Rahmen (SPEC §4); ein weiterer Ring darunter ist ein Unterschied im Grad, nicht in der Art. Dazu kommt jetzt Tiefe: Schlagschatten + scale(1.04). Der einzige Kanal, den im Diagramm noch nichts belegt, also ohne Konflikt mit einer Farbcodierung. Um die Mitte skaliert, damit die Knotenmitte erhalten bleibt (alignStems, Stationspunkte messen genau die — nachgemessen dx=dy=0). Beim Zeilenwechsel meldet sie sich zusätzlich mit einem einmaligen Puls (Hüpfer + auslaufender Ring auf ::after); kein Blinken, prefers-reduced-motion schaltet ihn ab. Behoben dabei: Die Pfad-Inversion (D18) blasste den Ring auf 0,32 aus — die Ausnahme, die .fresh (D28) und .focusmark (D32) längst haben, fehlte für .current als einziger, obwohl der Fall der häufigste ist (Umschalter default an, im Beispiel 7 von 18 Knoten betroffen). diagramToSvg() muss die Erhebung abschalten (Klasse `exporting`): Der Export zieht die Live-Geometrie per getBoundingClientRect() nach, anders als beim box-shadow schlägt eine Vergrößerung dort durch. Nachgemessen: 155,6 statt 161,9 px. Im Druck fällt beides ebenso weg. SPEC §9 und D25 (Nachtrag) nachgezogen; llms.md unberührt (Notation, keine Editor-UI, D43). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
618149a36d
commit
099ee87bc1
@@ -634,6 +634,82 @@ weiter, Alt+Enter zurück in die Zeile.
|
|||||||
Umbruch. Auf einer Zeile ohne Knoten (Kommentar, Leerzeile, ausgeblendetes
|
Umbruch. Auf einer Zeile ohne Knoten (Kommentar, Leerzeile, ausgeblendetes
|
||||||
Verworfenes) geschieht nichts — dieselbe stille Regel wie bei der Cursor-Zeile.
|
Verworfenes) geschieht nichts — dieselbe stille Regel wie bei der Cursor-Zeile.
|
||||||
|
|
||||||
|
**Nachtrag — die Cursor-Zeile hebt sich jetzt aus der Ebene, und die fehlende
|
||||||
|
Ausnahme gegen die Pfad-Inversion.** Gemeldet als „müsste etwas deutlicher
|
||||||
|
sein". Beim Nachsehen kam zuerst etwas anderes heraus, das keine
|
||||||
|
Gestaltungsfrage ist:
|
||||||
|
|
||||||
|
**Auf einem guten Drittel der Knoten war der Ring gar nicht zu sehen.** Die
|
||||||
|
Pfad-Inversion (D18) setzt `.cheap-on .node:not(.cheap){opacity:.32;
|
||||||
|
filter:saturate(.4)}` — und das trifft den `box-shadow` mit. Genau diese Falle
|
||||||
|
haben D28 (gelber Kranz) und D32 (Fokusmarke) je für sich gefunden und mit
|
||||||
|
einer Ausnahmeregel behoben; `.current` hat seine nie bekommen. Gemessen im
|
||||||
|
mitgelieferten Beispiel mit dem Cursor auf `+ [?] Dark mode`: Deckkraft 0,32.
|
||||||
|
Der Fall ist zudem der häufigste der drei — der Pfad-Umschalter ist
|
||||||
|
voreingestellt an, und jeder optionale Knoten und jede nicht gewählte
|
||||||
|
Alternative fällt darunter (im Beispiel 7 von 18 Knoten). Anders als beim
|
||||||
|
optionalen Knoten (D29), wo das Zurücktreten die **Aussage** ist, wird hier
|
||||||
|
überhaupt keine Aussage über den Plan gemacht: Es ist eine Editierhilfe, und wo
|
||||||
|
der Cursor steht, muss sichtbar bleiben, egal wie der Pfad entschieden hat.
|
||||||
|
|
||||||
|
**Warum der Ring auch ungedimmt leise wirkt:** Er benutzt denselben Kanal, den
|
||||||
|
schon jeder Knoten belegt — **jeder Status hat einen Rahmen** (SPEC §4). Ein
|
||||||
|
weiterer Ring in einem Feld aus lauter gerahmten Kästchen ist ein Unterschied
|
||||||
|
im Grad, nicht in der Art. Die beiden Strahlenkränze entkommen dem, weil sie
|
||||||
|
einen Kanal benutzen, den sonst nichts hat (Leuchten nach außen); die
|
||||||
|
Cursor-Zeile war auf dem Rahmen-Kanal sitzen geblieben.
|
||||||
|
|
||||||
|
**Gewählt: Tiefe** — Ring behalten, dazu Schlagschatten und `scale(1.04)`. Der
|
||||||
|
Knoten hebt sich aus der Ebene. Das ist der einzige Kanal, den im Diagramm noch
|
||||||
|
gar nichts belegt (nichts anderes wirft Schatten oder bewegt sich), kollidiert
|
||||||
|
also mit **keiner** Farbcodierung und wirkt über allen acht Statusfarben
|
||||||
|
gleich. Zwei angenehme Eigenschaften, beide nachgemessen: `transform` ändert
|
||||||
|
kein Layout (die Linien bleiben stehen), und weil um die **Mitte** skaliert
|
||||||
|
wird, bleibt die Knotenmitte punktgenau erhalten (dx = dy = 0) — `alignStems()`
|
||||||
|
(D29) und die Stationspunkte des günstigsten Pfads (D18) messen genau die und
|
||||||
|
bleiben unberührt. Nur `drawDepLinks()` (D41) setzt auf Knoten**kanten** auf und
|
||||||
|
verschiebt sich für diesen einen Knoten um ~4 px; das ist flüchtig und fällt
|
||||||
|
nicht auf. Ein Spalt zur Anschlusslinie entsteht nicht — der Knoten wächst
|
||||||
|
darüber, statt sich zurückzuziehen.
|
||||||
|
|
||||||
|
**Dazu ein einmaliger Puls beim Zeilenwechsel:** Man verliert den Knoten beim
|
||||||
|
**Bewegen**, nicht im Stillstand — also meldet er sich genau dann einmal, mit
|
||||||
|
einem kurzen Hüpfer und einem auslaufenden Ring. Kein WCAG-Problem: 2.2.2 zielt
|
||||||
|
auf Blinkendes, 2.3.1 auf Flackern über 3 Hz, ein einzelner Durchlauf ist
|
||||||
|
keines von beidem (dieselbe Prüfung wie in D28); `prefers-reduced-motion`
|
||||||
|
schaltet ihn ab. Ausgelöst wird er an derselben Bedingung, an der schon das
|
||||||
|
Scrollen hängt (`caretLine` hat sich geändert) — sonst pulste er bei jedem
|
||||||
|
Tastendruck. Bewusst **nicht** über `box-shadow` animiert: Die Kombinationen
|
||||||
|
mit `.fresh` und `.focusmark` haben je eigene Schatten-Listen, eine Animation
|
||||||
|
darauf ließe den gelben bzw. petrolfarbenen Kranz für die Dauer des Pulses
|
||||||
|
verschwinden. Stattdessen `transform` plus das freie `.node::after`
|
||||||
|
(`::before` gehört dem Optional-Kreis, D29).
|
||||||
|
|
||||||
|
**Der Export brauchte eine eigene Behandlung** — und das ist die Stelle, an der
|
||||||
|
die Erhebung anders liegt als der Ring. D25 konnte sich darauf verlassen, dass
|
||||||
|
`diagramToSvg()` nie `box-shadow` ausliest; die Vergrößerung schlägt aber über
|
||||||
|
`getBoundingClientRect()` durch, mit dem der Export die Live-Geometrie nachzieht
|
||||||
|
— genau ein Knoten stünde 4 % zu groß im Bild. Während des Messens trägt `#out`
|
||||||
|
deshalb die Klasse `exporting`, die Erhebung und Puls neutralisiert.
|
||||||
|
Nachgemessen: 155,6 statt 161,9 px, also die unskalierte Breite. Per Klasse
|
||||||
|
statt durch Abnehmen von `.current`/`.pulse`, damit der Export keine laufende
|
||||||
|
Animation abreißt und hinterher neu startet. Im Druck fällt beides ebenso weg.
|
||||||
|
|
||||||
|
**Verworfene Alternativen:** ein **dritter Strahlenkranz** (ein dunkles
|
||||||
|
Tinte-Leuchten liest sich auf hellem Grund als Schatten — die Lehre steht schon
|
||||||
|
im D32-Nachtrag —, und ein heller Schein bräuchte eine dritte Signalfarbe neben
|
||||||
|
Gelb und Teal); **Invertieren** des Knotens auf Tinte-Füllung mit weißer Schrift
|
||||||
|
(das stärkste Signal, kostet aber für die Cursor-Zeile die Statusfarbe — genau
|
||||||
|
die Regel, wegen der D28 und D32 ihre Kränze nach außen gelegt haben, und für
|
||||||
|
eine Editierhilfe wäre sie schlecht gebrochen); ein **Zeiger-Dreieck** links am
|
||||||
|
Knoten (`::after` wäre frei, bräuchte aber wie der Optional-Kreis eigene
|
||||||
|
Geometrie für alle drei Darstellungsmodi); und **die Umgebung zurücktreten
|
||||||
|
lassen** (flackerte bei jedem Pfeiltastendruck durch den ganzen Baum).
|
||||||
|
|
||||||
|
Nebenbefund beim Aufräumen: Der Kommentar über der Regel behauptete noch, die
|
||||||
|
Fokusmarke `!!!` trage „bewusst DIESELBE Hervorhebung" — überholt, seit der
|
||||||
|
D32-Nachtrag ihr den eigenen Petrol-Kranz gegeben hat. Ersetzt.
|
||||||
|
|
||||||
## D26 — Legende scrollbar: eigener Container statt `<details>`, plus Splitter
|
## D26 — Legende scrollbar: eigener Container statt `<details>`, plus Splitter
|
||||||
Die Legende („Agenda") im Editor-Panel war zu hoch für ihren Platz und wurde
|
Die Legende („Agenda") im Editor-Panel war zu hoch für ihren Platz und wurde
|
||||||
**abgeschnitten** statt scrollbar zu sein — obwohl `.hint` seit jeher
|
**abgeschnitten** statt scrollbar zu sein — obwohl `.hint` seit jeher
|
||||||
|
|||||||
+11
-3
@@ -556,9 +556,17 @@ verknüpft (siehe D25):
|
|||||||
„hinschauen": Er holt **keine Bildschirmtastatur** herauf — die erscheint erst,
|
„hinschauen": Er holt **keine Bildschirmtastatur** herauf — die erscheint erst,
|
||||||
wenn das Textfeld selbst angetippt wird.
|
wenn das Textfeld selbst angetippt wird.
|
||||||
- **Text → Diagramm:** Der Knoten der **Cursor-Zeile** wird im Diagramm
|
- **Text → Diagramm:** Der Knoten der **Cursor-Zeile** wird im Diagramm
|
||||||
hervorgehoben (weißer Halo + Ring in Tinte) und beim Zeilenwechsel ins Bild
|
hervorgehoben und beim Zeilenwechsel ins Bild gescrollt. Zeilen ohne Knoten
|
||||||
gescrollt. Zeilen ohne Knoten (Kommentar, Leerzeile, ausgeblendetes
|
(Kommentar, Leerzeile, ausgeblendetes verworfenes Element) heben nichts
|
||||||
verworfenes Element) heben nichts hervor.
|
hervor. Die Hervorhebung arbeitet auf einer **eigenen Achse — Tiefe**: weißer
|
||||||
|
Halo + Ring in Tinte, dazu ein Schlagschatten und eine leichte Vergrößerung,
|
||||||
|
sodass der Knoten sich aus der Ebene hebt. Das ist der einzige Kanal, den
|
||||||
|
sonst nichts im Diagramm belegt, kollidiert also mit keiner Farbcodierung
|
||||||
|
(Status §4, gelber Kranz und Petrol-Kranz oben). **Beim Zeilenwechsel** meldet
|
||||||
|
sie sich zusätzlich mit einem **einmaligen Puls** — einem kurzen Hüpfer und
|
||||||
|
einem auslaufenden Ring; kein Blinken, und bei `prefers-reduced-motion`
|
||||||
|
entfällt er. Der Pfad-Filter (§9, günstigster Pfad) blasst sie nie aus: Wo der
|
||||||
|
Cursor steht, bleibt sichtbar. Siehe D25.
|
||||||
- **Text → Diagramm, ausdrücklich:** **Alt+Klick** im Textfeld (Tastatur:
|
- **Text → Diagramm, ausdrücklich:** **Alt+Klick** im Textfeld (Tastatur:
|
||||||
**Alt+Enter**) holt den Knoten der Cursor-Zeile in die **Mitte** des Diagramms
|
**Alt+Enter**) holt den Knoten der Cursor-Zeile in die **Mitte** des Diagramms
|
||||||
und gibt ihm den Tastaturfokus — dieselbe Geste in beide Richtungen. Sie
|
und gibt ihm den Tastaturfokus — dieselbe Geste in beide Richtungen. Sie
|
||||||
|
|||||||
+16
-1
@@ -211,7 +211,22 @@ verworfene Elemente. Quelle sind ES-Module unter `src/`; `index.html` ist der
|
|||||||
`syncCaret()` setzt die Klasse `current` auf den Knoten der Cursor-Zeile;
|
`syncCaret()` setzt die Klasse `current` auf den Knoten der Cursor-Zeile;
|
||||||
`render()` stellt sie nach jedem Neubau wieder her (ohne zu scrollen). Die
|
`render()` stellt sie nach jedem Neubau wieder her (ohne zu scrollen). Die
|
||||||
CSS-Regel braucht den `#out`-Präfix (`ul.or .node{box-shadow:none}` ist
|
CSS-Regel braucht den `#out`-Präfix (`ul.or .node{box-shadow:none}` ist
|
||||||
spezifischer). Auffindbarkeit: `setAltMode()` setzt bei gedrückter Alt-Taste
|
spezifischer) **und** — wie `.fresh`/`.focusmark` — die Ausnahme
|
||||||
|
`.cheap-on .node:not(.cheap).current{opacity:1;filter:none}`; sie fehlte lange
|
||||||
|
als einzige, obwohl der Fall der häufigste ist (Pfad-Umschalter default an,
|
||||||
|
jeder `+`-Knoten und jede nicht gewählte Alternative betroffen).
|
||||||
|
Zum Ring kommen **Schlagschatten + `scale(1.04)`** (D25-Nachtrag): Tiefe ist der
|
||||||
|
einzige noch freie Kanal. Um die **Mitte** skalieren ist Bedingung — `alignStems()`
|
||||||
|
und die Stationspunkte messen die Knotenmitte, die dadurch unverändert bleibt.
|
||||||
|
**`diagramToSvg()` muss die Erhebung abschalten** (Klasse `exporting` an `#out`,
|
||||||
|
am Ende wieder ab): Der Export zieht die Live-Geometrie per
|
||||||
|
`getBoundingClientRect()` nach, anders als beim `box-shadow` schlägt die
|
||||||
|
Vergrößerung dort durch. Der **Puls** (`pulse`, Hüpfer + Ring auf dem freien
|
||||||
|
`.node::after`) wird nur bei geändertem `caretLine` gesetzt — sonst pulst es bei
|
||||||
|
jedem Tastendruck — und braucht das Lesen von `offsetWidth` als Neustart-Trick,
|
||||||
|
weil dieselbe Aufgabe die Klasse vorher entfernt. Nicht über `box-shadow`
|
||||||
|
animieren: Das löschte für die Dauer des Pulses den gelben bzw. Petrol-Kranz der
|
||||||
|
Kombinationen. Auffindbarkeit: `setAltMode()` setzt bei gedrückter Alt-Taste
|
||||||
die Klasse `alt` an `#out` (Cursor + Ring am Knoten unter dem Zeiger) — der
|
die Klasse `alt` an `#out` (Cursor + Ring am Knoten unter dem Zeiger) — der
|
||||||
`blur`-Handler ist Pflicht, sonst bleibt der Modus nach Alt+Tab hängen; dazu
|
`blur`-Handler ist Pflicht, sonst bleibt der Modus nach Alt+Tab hängen; dazu
|
||||||
`jumpHint` im Knoten-Tooltip und `hint_jump` als letzte Zeile der Legende.
|
`jumpHint` im Knoten-Tooltip und `hint_jump` als letzte Zeile der Legende.
|
||||||
|
|||||||
+21
-4
@@ -394,6 +394,13 @@ out.addEventListener('focusout', () => setTimeout(drawDepLinks, 0));
|
|||||||
(und = durchgezogen Tinte, oder = gestrichelt Grau) neu gezogen und treffen
|
(und = durchgezogen Tinte, oder = gestrichelt Grau) neu gezogen und treffen
|
||||||
so garantiert die Knoten — unabhängig vom Darstellungsmodus. */
|
so garantiert die Knoten — unabhängig vom Darstellungsmodus. */
|
||||||
function diagramToSvg(){
|
function diagramToSvg(){
|
||||||
|
/* Der Export misst die **Live-Geometrie**. Den Ring der Cursor-Zeile liest er
|
||||||
|
nie aus (`box-shadow` steht nicht in der Liste), ihre Erhebung
|
||||||
|
(D25-Nachtrag) schlüge aber über `getBoundingClientRect()` durch und
|
||||||
|
exportierte genau einen Knoten 4 % zu groß. Neutralisiert wird sie per
|
||||||
|
Klasse statt durch Abnehmen von `.current`/`.pulse` — so reißt der Export
|
||||||
|
keine laufende Puls-Animation ab und startet sie hinterher nicht neu. */
|
||||||
|
out.classList.add('exporting');
|
||||||
const treeRect = out.getBoundingClientRect();
|
const treeRect = out.getBoundingClientRect();
|
||||||
const PAD = 24;
|
const PAD = 24;
|
||||||
const W = Math.ceil(treeRect.width) + PAD*2;
|
const W = Math.ceil(treeRect.width) + PAD*2;
|
||||||
@@ -570,6 +577,7 @@ function diagramToSvg(){
|
|||||||
});
|
});
|
||||||
|
|
||||||
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}" font-family="'IBM Plex Sans',system-ui,sans-serif">${parts.join('')}</svg>`;
|
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${W}" height="${H}" viewBox="0 0 ${W} ${H}" font-family="'IBM Plex Sans',system-ui,sans-serif">${parts.join('')}</svg>`;
|
||||||
|
out.classList.remove('exporting');
|
||||||
return {svg, W, H};
|
return {svg, W, H};
|
||||||
}
|
}
|
||||||
function svgToPng(svg, W, H, scale){
|
function svgToPng(svg, W, H, scale){
|
||||||
@@ -925,9 +933,9 @@ out.addEventListener('touchcancel', disarmPress);
|
|||||||
out.addEventListener('contextmenu', e => { if(pressTimer || armedEl) e.preventDefault(); });
|
out.addEventListener('contextmenu', e => { if(pressTimer || armedEl) e.preventDefault(); });
|
||||||
|
|
||||||
/* Text -> Diagramm: Knoten der Cursor-Zeile hervorheben (`caretLine` steht oben). */
|
/* Text -> Diagramm: Knoten der Cursor-Zeile hervorheben (`caretLine` steht oben). */
|
||||||
function highlightCurrentNode(scroll){
|
function highlightCurrentNode(moved){
|
||||||
markCurrentLineNo(); /* die eine Stelle, an der die Cursor-Zeile neu gesetzt wird */
|
markCurrentLineNo(); /* die eine Stelle, an der die Cursor-Zeile neu gesetzt wird */
|
||||||
if(currentNodeEl) currentNodeEl.classList.remove('current');
|
if(currentNodeEl) currentNodeEl.classList.remove('current', 'pulse');
|
||||||
currentNodeEl = caretLine == null
|
currentNodeEl = caretLine == null
|
||||||
? null
|
? null
|
||||||
: out.querySelector('.node[data-line="' + caretLine + '"]');
|
: out.querySelector('.node[data-line="' + caretLine + '"]');
|
||||||
@@ -936,8 +944,17 @@ function highlightCurrentNode(scroll){
|
|||||||
/* Die Cursor-Zeile ist die zweite Lesart von „ausgewählt" — ihre
|
/* Die Cursor-Zeile ist die zweite Lesart von „ausgewählt" — ihre
|
||||||
Abhängigkeits-Kanten hervorheben (D41). */
|
Abhängigkeits-Kanten hervorheben (D41). */
|
||||||
drawDepLinks();
|
drawDepLinks();
|
||||||
/* Nur beim Zeilenwechsel scrollen, sonst ruckelte das Diagramm beim Tippen. */
|
/* Beides nur beim **Zeilenwechsel**: Scrollen ruckelte sonst bei jedem
|
||||||
if(scroll) currentNodeEl.scrollIntoView({block:'nearest', inline:'nearest', behavior:'smooth'});
|
Tastendruck, und der Puls (D25-Nachtrag) flackerte beim Tippen. */
|
||||||
|
if(!moved) return;
|
||||||
|
/* Neustart der Animation erzwingen: Steht der Cursor wieder auf demselben
|
||||||
|
Knoten (Zeile mit Knoten -> Zeile ohne -> zurück), wurde `pulse` oben in
|
||||||
|
DERSELBEN Aufgabe entfernt — der Browser sieht dann keinen Klassenwechsel
|
||||||
|
und startet nichts. Das Lesen von `offsetWidth` erzwingt den Zwischenstand. */
|
||||||
|
currentNodeEl.classList.remove('pulse');
|
||||||
|
void currentNodeEl.offsetWidth;
|
||||||
|
currentNodeEl.classList.add('pulse');
|
||||||
|
currentNodeEl.scrollIntoView({block:'nearest', inline:'nearest', behavior:'smooth'});
|
||||||
}
|
}
|
||||||
function caretLineOf(){
|
function caretLineOf(){
|
||||||
return src.value.slice(0, src.selectionStart).split('\n').length;
|
return src.value.slice(0, src.selectionStart).split('\n').length;
|
||||||
|
|||||||
+66
-9
@@ -578,11 +578,21 @@
|
|||||||
im Druck. Der `#out`-Präfix ist nötig: `ul.or .node{box-shadow:none}`
|
im Druck. Der `#out`-Präfix ist nötig: `ul.or .node{box-shadow:none}`
|
||||||
(weiter unten) ist spezifischer als `.node.current` und schluckte den Ring
|
(weiter unten) ist spezifischer als `.node.current` und schluckte den Ring
|
||||||
sonst überall unterhalb einer any-of-Gruppe. */
|
sonst überall unterhalb einer any-of-Gruppe. */
|
||||||
/* Die Fokusmarke `!!!` (SPEC §1) trägt bewusst DIESELBE Hervorhebung: Es gibt
|
/* Der Ring allein war zu leise (D25-Nachtrag): Er benutzt denselben Kanal, den
|
||||||
nur einen Begriff „hier schauen", und ein dritter Ring neben Fokusring,
|
schon jeder Knoten belegt — **jeder Status hat einen Rahmen** (SPEC §4). Ein
|
||||||
Strahlenkranz und Cursor-Ring wäre ein Zeichen zu viel. Der Unterschied
|
weiterer Ring in einem Feld aus lauter gerahmten Kästchen ist ein Unterschied
|
||||||
steht im `aria-label`. */
|
im Grad, nicht in der Art. Deshalb kommt **Tiefe** dazu: Schlagschatten plus
|
||||||
#out .node.current{box-shadow:0 0 0 2px var(--card),0 0 0 4px var(--ink)}
|
leichte Vergrößerung. Das ist der einzige Kanal, den im Diagramm noch nichts
|
||||||
|
belegt (nichts anderes wirft Schatten oder hebt sich), kollidiert also mit
|
||||||
|
keiner Farbcodierung und wirkt über allen acht Statusfarben gleich.
|
||||||
|
`transform` ändert **kein Layout** — die Linien bleiben stehen; und weil um
|
||||||
|
die **Mitte** skaliert wird, bleibt auch die Knotenmitte unverändert, auf die
|
||||||
|
`alignStems()` und `drawCheapPath()` messen. `z-index`, weil Schatten und
|
||||||
|
vergrößerter Knoten AUSSERHALB der Box liegen (dasselbe wie bei `.fresh`). */
|
||||||
|
#out .node.current{
|
||||||
|
box-shadow:0 0 0 2px var(--card),0 0 0 4px var(--ink),0 10px 20px -6px rgba(36,52,71,.5);
|
||||||
|
transform:scale(1.04);z-index:3;
|
||||||
|
}
|
||||||
/* Fokusmarke `!!!` (SPEC §1, D32): **Strahlenkranz in Petrol**, nach dem Muster
|
/* Fokusmarke `!!!` (SPEC §1, D32): **Strahlenkranz in Petrol**, nach dem Muster
|
||||||
der Neuheiten-Markierung (D28), nur in der Akzentfarbe der App statt in Gelb.
|
der Neuheiten-Markierung (D28), nur in der Akzentfarbe der App statt in Gelb.
|
||||||
Der geteilte Ring mit der Cursor-Zeile war zu leise — „das erkennt man zu
|
Der geteilte Ring mit der Cursor-Zeile war zu leise — „das erkennt man zu
|
||||||
@@ -598,8 +608,10 @@
|
|||||||
obwohl die Geometrie dieselbe war. Der Ring bleibt dunkles Petrol und ordnet
|
obwohl die Geometrie dieselbe war. Der Ring bleibt dunkles Petrol und ordnet
|
||||||
die Marke der Akzentfarbe zu; das Leuchten macht die Reichweite. */
|
die Marke der Akzentfarbe zu; das Leuchten macht die Reichweite. */
|
||||||
#out .node.focusmark{box-shadow:0 0 0 3px var(--or),0 0 20px 7px rgba(20,184,166,.85);z-index:2}
|
#out .node.focusmark{box-shadow:0 0 0 3px var(--or),0 0 20px 7px rgba(20,184,166,.85);z-index:2}
|
||||||
/* Beides zugleich: Cursor-Zeile innen (Tinte), fremde Marke außen (Petrol). */
|
/* Beides zugleich: Cursor-Zeile innen (Tinte), fremde Marke außen (Petrol).
|
||||||
#out .node.focusmark.current{box-shadow:0 0 0 2px var(--card),0 0 0 4px var(--ink),0 0 20px 7px rgba(20,184,166,.85)}
|
Die Erhebung der Cursor-Zeile gilt in jeder Kombination — der Schlagschatten
|
||||||
|
wird deshalb hier mit angehängt (das `transform` erbt die Regel oben). */
|
||||||
|
#out .node.focusmark.current{box-shadow:0 0 0 2px var(--card),0 0 0 4px var(--ink),0 0 20px 7px rgba(20,184,166,.85),0 10px 20px -6px rgba(36,52,71,.5);z-index:3}
|
||||||
/* Neu in Produktion seit dem letzten Besuch (D28): gelber Strahlenkranz um den
|
/* Neu in Produktion seit dem letzten Besuch (D28): gelber Strahlenkranz um den
|
||||||
Knoten. Bewusst ein Schein nach AUSSEN statt Farbe im Knoten — die Füllung
|
Knoten. Bewusst ein Schein nach AUSSEN statt Farbe im Knoten — die Füllung
|
||||||
gehört dem Status (SPEC §4) und bleibt lesbar. Kein Blinken: WCAG 2.2.2/2.3.1
|
gehört dem Status (SPEC §4) und bleibt lesbar. Kein Blinken: WCAG 2.2.2/2.3.1
|
||||||
@@ -610,13 +622,44 @@
|
|||||||
Kante stehen — sieht aus wie ein halber Hintergrund statt eines Rahmens). */
|
Kante stehen — sieht aus wie ein halber Hintergrund statt eines Rahmens). */
|
||||||
#out .node.fresh{box-shadow:0 0 0 2px #FACC15,0 0 16px 5px rgba(234,179,8,.65);z-index:2}
|
#out .node.fresh{box-shadow:0 0 0 2px #FACC15,0 0 16px 5px rgba(234,179,8,.65);z-index:2}
|
||||||
/* Beides zugleich: Cursor-Zeile innen (Tinte), Neuheit außen (gelb). */
|
/* Beides zugleich: Cursor-Zeile innen (Tinte), Neuheit außen (gelb). */
|
||||||
#out .node.fresh.current{box-shadow:0 0 0 2px var(--card),0 0 0 4px var(--ink),0 0 18px 6px rgba(234,179,8,.65)}
|
#out .node.fresh.current{box-shadow:0 0 0 2px var(--card),0 0 0 4px var(--ink),0 0 18px 6px rgba(234,179,8,.65),0 10px 20px -6px rgba(36,52,71,.5);z-index:3}
|
||||||
/* Neu in Produktion UND jemand zeigt darauf: Ring in Petrol (der bewusste
|
/* Neu in Produktion UND jemand zeigt darauf: Ring in Petrol (der bewusste
|
||||||
Zuruf, innen), Kranz in Gelb (die stehende Nachricht, außen). Zwei Scheine
|
Zuruf, innen), Kranz in Gelb (die stehende Nachricht, außen). Zwei Scheine
|
||||||
nach außen gingen nicht — einer muss der Ring sein. Steht bewusst **nach**
|
nach außen gingen nicht — einer muss der Ring sein. Steht bewusst **nach**
|
||||||
`.node.focusmark.current`: gleiche Spezifität (drei Klassen), und wenn alles
|
`.node.focusmark.current`: gleiche Spezifität (drei Klassen), und wenn alles
|
||||||
drei zutrifft, soll Zuruf + Neuheit gewinnen, nicht die eigene Cursor-Zeile. */
|
drei zutrifft, soll Zuruf + Neuheit gewinnen, nicht die eigene Cursor-Zeile. */
|
||||||
#out .node.fresh.focusmark{box-shadow:0 0 0 3px var(--or),0 0 20px 7px rgba(234,179,8,.65);z-index:2}
|
#out .node.fresh.focusmark{box-shadow:0 0 0 3px var(--or),0 0 20px 7px rgba(234,179,8,.65);z-index:2}
|
||||||
|
/* Puls beim Zeilenwechsel (D25-Nachtrag): Man verliert den Knoten beim
|
||||||
|
**Bewegen**, nicht im Stillstand — also meldet er sich genau dann einmal.
|
||||||
|
Einmalig, kein Blinken: WCAG 2.2.2 zielt auf Blinkendes, 2.3.1 auf Flackern
|
||||||
|
über 3 Hz; ein einzelner Durchlauf ist keins von beidem (vgl. D28).
|
||||||
|
Ausgelöst wird er in `highlightCurrentNode()` **nur** bei geändertem
|
||||||
|
`caretLine` — sonst pulste er bei jedem Tastendruck.
|
||||||
|
Bewusst NICHT über `box-shadow` animiert: die Kombinationen mit `.fresh` und
|
||||||
|
`.focusmark` haben je eigene Schatten-Listen, eine Animation darauf ließe den
|
||||||
|
gelben bzw. petrolfarbenen Kranz für die Dauer des Pulses verschwinden.
|
||||||
|
Stattdessen zwei Kanäle, die niemand sonst benutzt: ein kurzer Hüpfer im
|
||||||
|
`transform` und ein auslaufender Ring auf dem freien `::after`
|
||||||
|
(`::before` gehört dem Optional-Kreis, D29). */
|
||||||
|
@keyframes nodeLift{0%{transform:scale(1.04)}40%{transform:scale(1.11)}100%{transform:scale(1.04)}}
|
||||||
|
@keyframes nodeRipple{from{transform:scale(1);opacity:.5}to{transform:scale(1.4);opacity:0}}
|
||||||
|
#out .node.current.pulse{animation:nodeLift .36s ease-out 1}
|
||||||
|
/* `both` ist Pflicht: ohne Fill-Mode schnappt der Ring am Ende auf die
|
||||||
|
Anfangsdeckkraft zurück und bliebe stehen. */
|
||||||
|
#out .node.current.pulse::after{
|
||||||
|
content:'';position:absolute;inset:-3px;border-radius:calc(var(--r) + 3px);
|
||||||
|
border:2px solid var(--ink);pointer-events:none;
|
||||||
|
animation:nodeRipple .45s ease-out 1 both;
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce){
|
||||||
|
#out .node.current.pulse{animation:none}
|
||||||
|
#out .node.current.pulse::after{display:none}
|
||||||
|
}
|
||||||
|
/* Während `diagramToSvg()` misst: Erhebung und Puls abschalten — der Export
|
||||||
|
zeichnet aus der Live-Geometrie nach, und die Vergrößerung ist eine
|
||||||
|
Editierhilfe, keine Aussage über den Plan (D25). */
|
||||||
|
#out.exporting .node.current{transform:none;animation:none}
|
||||||
|
#out.exporting .node.current::after{display:none}
|
||||||
/* Alt gedrückt = Sprung-Geste scharf: Cursor an allen Knoten, Petrol-Ring am
|
/* Alt gedrückt = Sprung-Geste scharf: Cursor an allen Knoten, Petrol-Ring am
|
||||||
Knoten unter dem Zeiger. Auf verlinkten Knoten ist das die einzige
|
Knoten unter dem Zeiger. Auf verlinkten Knoten ist das die einzige
|
||||||
Rückmeldung, weil dort der einfache Klick dem Link gehört (D25). */
|
Rückmeldung, weil dort der einfache Klick dem Link gehört (D25). */
|
||||||
@@ -696,6 +739,16 @@
|
|||||||
der wahrscheinlichsten Gründe, überhaupt zu zeigen. Anders als beim
|
der wahrscheinlichsten Gründe, überhaupt zu zeigen. Anders als beim
|
||||||
optionalen Knoten (D29), wo das Zurücktreten die Aussage IST. */
|
optionalen Knoten (D29), wo das Zurücktreten die Aussage IST. */
|
||||||
.cheap-on .node:not(.cheap).focusmark{opacity:1;filter:none}
|
.cheap-on .node:not(.cheap).focusmark{opacity:1;filter:none}
|
||||||
|
/* Und dasselbe für die Cursor-Zeile (D25-Nachtrag) — die Ausnahme fehlte hier
|
||||||
|
als einzige, obwohl der Fall der häufigste ist: Der Umschalter ist
|
||||||
|
voreingestellt an, und jeder optionale Knoten und jede nicht gewählte
|
||||||
|
Alternative ist „nicht auf dem Pfad" (im mitgelieferten Beispiel 7 von 18
|
||||||
|
Knoten). Gemessen auf `+ [?] Dark mode`: Deckkraft 0,32 — der Ring war dort
|
||||||
|
praktisch unsichtbar. Wo der Cursor steht, muss sichtbar bleiben, egal wie
|
||||||
|
der Pfad entschieden hat; anders als beim optionalen Knoten (D29), wo das
|
||||||
|
Zurücktreten die Aussage IST, ist hier gar keine Aussage über den Plan
|
||||||
|
gemeint — es ist eine Editierhilfe. */
|
||||||
|
.cheap-on .node:not(.cheap).current{opacity:1;filter:none}
|
||||||
/* implizit als M angenommene Größe: invertiertes Badge (weiß statt petrol) */
|
/* implizit als M angenommene Größe: invertiertes Badge (weiß statt petrol) */
|
||||||
.size.implicit{background:var(--card);color:var(--or);border-color:var(--or)}
|
.size.implicit{background:var(--card);color:var(--or);border-color:var(--or)}
|
||||||
/* Günstigster-Pfad-Linie: gestrichelt, geschwungen, petrol — fädelt durch
|
/* Günstigster-Pfad-Linie: gestrichelt, geschwungen, petrol — fädelt durch
|
||||||
@@ -1078,8 +1131,12 @@
|
|||||||
*{-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important}
|
*{-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important}
|
||||||
/* Knoten nicht über den Seitenrand zerschneiden */
|
/* Knoten nicht über den Seitenrand zerschneiden */
|
||||||
#out li{break-inside:avoid}
|
#out li{break-inside:avoid}
|
||||||
/* Editierhilfe bzw. Zuruf, nicht drucken (D25, Fokusmarke SPEC §1) */
|
/* Editierhilfe bzw. Zuruf, nicht drucken (D25, Fokusmarke SPEC §1).
|
||||||
|
Bei der Cursor-Zeile gehören Erhebung und Puls mit dazu — sonst stünde ein
|
||||||
|
Knoten im Druck grundlos vergrößert und schief zu seiner Anschlusslinie. */
|
||||||
.node.current,.node.focusmark{box-shadow:none!important}
|
.node.current,.node.focusmark{box-shadow:none!important}
|
||||||
|
.node.current{transform:none!important;animation:none!important}
|
||||||
|
.node.current::after{display:none!important}
|
||||||
.node.fresh{box-shadow:none!important} /* „neu seit dem letzten Besuch" ist persönlich (D28) */
|
.node.fresh{box-shadow:none!important} /* „neu seit dem letzten Besuch" ist persönlich (D28) */
|
||||||
/* Faltung (D38): das ▾ offener Knoten ist Bedienelement — nicht drucken;
|
/* Faltung (D38): das ▾ offener Knoten ist Bedienelement — nicht drucken;
|
||||||
das „▸ n" eingeklappter Knoten ist Kennzeichnung und bleibt. */
|
das „▸ n" eingeklappter Knoten ist Kennzeichnung und bleibt. */
|
||||||
|
|||||||
Reference in New Issue
Block a user