1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
# Effects: feel reviews
**The rule above all the others:** no effect may alter or cover a focus
indicator (the focused pane's tag colour, its grip, the cursor, the
selection) or reduce its contrast. The effects are sugar; the indicators are
how a person knows where they are. Every G stage is reviewed against this.
Lift, for one, falls only on pane bodies and rails, never on a tag, a grip or
a header, and its strength is capped so text and the selection keep min(their
contrast, 4.5) (tests in src/gui/gui.zig). The rule binds effects, not a
theme's structure: the rules between panes (2px, `rule_px`) are the theme's
borders, and the one over a tag band stays in the band's slack, never over
its text. A dim touches only the unfocused
panes, with the same floor, and the focused pane's text is never at less
contrast than theirs (tests in src/draw.zig).
Each effect of docs/render-pipeline.md §9 lands behind its own switch, off,
and is kept only after a feel review (§8.4): a frame series on the virtual
clock, a recording, and a verdict — keep, polish or drop — with one line of
why. The verdict is the user's.
| effect | switch | state | review material | verdict |
|---|---|---|---|---|
| Crt (bundled post pass) | `Crt 0..3` | kept, rewritten (no barrel) | fx-compare stills, live window | kept at the user's live look |
| Ripple, Glitch | — | removed | live window | dropped by the user after a live look |
| G1 lift | `Lift shadow\|rim\|auto` (off), `InactiveDim <percent>` | opt-in until the focus-lift default is decided | lift-shots-2 (off, shadow, rim, auto on acme, dusk, forge); frame series + mp4 per Motion flavour | shadow on acme: keep as is. Round 1 dropped glow and surface (surface lowered the focused text's contrast) and made rim a hairline just above the focused tag (light on a dark page, shade on a light one). auto on a dark page recedes the others (InactiveDim 30) |
## Motion flavours
`Motion off|crisp|smooth|bouncy|playful` (default smooth) is one parameter set
(animation.Motion) every fx animation reads, so a flavour is data, not a
branch in each effect. Input is never blocked, and a new target always
retargets from the current position and velocity.
| flavour | timing (ω) | follow-through (ζ) | anticipation | squash/stretch | secondary lag | arcs |
|---|---|---|---|---|---|---|
| off | instant | — | — | — | — | — |
| crisp | 44 | 1 (none) | — | — | in step | — |
| smooth | 26 | 1 (none) | — | — | 0.85 | 0.04 |
| bouncy | 30 | 0.55 (overshoots ~12%) | — | 0.12 | 0.8 | 0.08 |
| playful | 24 | 0.42 (overshoots ~23%) | 0.08 of the move | 0.25 | 0.7 | 0.15 |
Which principles each motion uses:
- **Lift** (G1): timing, slow in / slow out (the spring), follow-through
(bouncy and playful lift past full and settle back), anticipation (playful
dips before it rises; below zero nothing is drawn, so it reads as a beat
before the lift), staging (only the focused pane lifts; nothing else moves
with a focus change). Squash, stretch, arcs and secondary lag have nothing
to act on in a lift.
- **Cursor** (G3, next): designed around the same set: glide on the spring,
stretch along the path, an arc on long jumps, the trailing corners as the
secondary action.
Notes on G1: the lift and the dim run on one focus spring per pane, at the
Motion flavour's pace, sampled at each frame's own time; while it moves the
GUI draws at the display's rate, and a grid snaps. A notice floats on a lift
of 1 while a shadow or rim is on. InactiveDim under `Lift auto` defaults to
30. The fade is in linear light, so on a dark page it is gentle: at 30
forge's text goes from 15.5:1 to 11.2:1 and dusk's from 6.4:1 to 4.8:1,
plainly quieter and still easy reading; at 10 forge barely moves (14.1:1).
On a light page the same percent bites far harder (acme: 7.0:1 at 10, the
4.5 floor at 30), which is why `auto` shades there instead of dimming.
|