uix(sticky): fix · pin con inset FÍSICO (top/bottom), no lógico — hallazgo de review

Adversarial review confirmó (minor): el recipe fijaba en el eje LÓGICO
(inset-block-start/end) mientras el rootMargin del observer es FÍSICO
(top/bottom). Coinciden en todos los modos horizontales (LTR y RTL: top/bottom
son eje de bloque, invariantes al flip inline), pero DIVERGEN en writing-modes
verticales, donde inset-block-start = izquierda/derecha física ≠ el borde que
el observer vigila → data-stuck se dispararía en la posición equivocada (o
nunca). La API `edge` es física ('top'|'bottom') y el observer es físico, así
que el CSS debe ser físico también (mi razón previa «lógico por RTL» era
incorrecta — RTL no afecta el eje de bloque).

- sticky.css: inset-block-start/end → top/bottom; margin-block-end/start del
  centinela → margin-bottom/top (mismo eje de bloque, RTL-safe)
- demo: corregidas las 3 menciones «logical inset / writing-mode correct»

Verificado: component:audit PASS 0E/0W (físico top/bottom = eje bloque, no
dispara regla RTL) · test real-IntersectionObserver en chromium PASS (físico
top en horizontal = comportamiento idéntico). Nota: la lente teardown-ssr del
review falló por API stall mid-stream; esos caminos ya están cubiertos por los
tests (cleanup/disconnect aserted, disabled aserted, SSR no-op por contrato de
ActiveDom).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
alpha-0.1-sec-dom
dev 3 months ago
parent 86e5e5791e
commit 6cba9a0b19

@ -9,6 +9,14 @@
*
* `--sticky-z-index` is a public token (recipes.sticky.z-index). The offset
* arrives as the soma-injected `--_sticky-offset` data custom-property.
*
* PHYSICAL inset + margins, not logical: the `edge` API is physical
* ('top'|'bottom') and the observer's `rootMargin` is physical, so the pin
* edge and the observed edge MUST be the same physical edge. Logical
* (`inset-block-*`) would diverge from the observer in vertical writing modes
* (block-start = physical left/right there). In every horizontal mode
* (LTR + RTL) physical top/bottom == block-start/end, so RTL is unaffected.
* Vertical writing modes are out of scope for a sticky header/footer.
*/
[data-sticky] {
@ -17,11 +25,11 @@
}
[data-sticky][data-edge='top'] {
inset-block-start: var(--_sticky-offset, 0px);
top: var(--_sticky-offset, 0px);
}
[data-sticky][data-edge='bottom'] {
inset-block-end: var(--_sticky-offset, 0px);
bottom: var(--_sticky-offset, 0px);
}
/*
@ -39,12 +47,12 @@
visibility: hidden;
}
/* top edge: sentinel precedes the box → collapse the trailing side */
/* top edge: sentinel precedes the box → collapse the bottom (toward the box) */
[data-sticky-sentinel][data-edge='top'] {
margin-block-end: calc(-1 * var(--_sticky-sentinel-size));
margin-bottom: calc(-1 * var(--_sticky-sentinel-size));
}
/* bottom edge: sentinel follows the box → collapse the leading side */
/* bottom edge: sentinel follows the box → collapse the top (toward the box) */
[data-sticky-sentinel][data-edge='bottom'] {
margin-block-start: calc(-1 * var(--_sticky-sentinel-size));
margin-top: calc(-1 * var(--_sticky-sentinel-size));
}

@ -241,9 +241,12 @@
<section data-uix-section>
<h2 data-uix-section-title>System axes</h2>
<p data-uix-section-desc>
Foundation knobs applied to the stage. RTL flips the accent-agnostic geometry (the recipe
uses logical <code>inset-block-start</code> / <code>inset-block-end</code>, so top/bottom
pinning is writing-mode correct).
Foundation knobs applied to the stage. The <code>edge</code> API is physical (<code
>top</code
>
/ <code>bottom</code>), matching the observer's physical geometry; the recipe pins with
physical <code>top</code> / <code>bottom</code> too, so RTL is unaffected (top/bottom are the
block axis, invariant under the inline flip).
</p>
<SystemAxes bind:density bind:scaling bind:mode bind:dir bind:borderWidth />
</section>
@ -395,8 +398,9 @@
<h2 data-uix-section-title>Eidos recipe</h2>
<p data-uix-section-desc>
Recipe lives in <code>src/uix/eidos/components/sticky/sticky.css</code>. It POSITIONS only:
<code>position: sticky</code> + the logical inset (<code>inset-block-start</code> /
<code>inset-block-end</code> from <code>--_sticky-offset</code>) + the
<code>position: sticky</code> + the physical inset (<code>top</code> /
<code>bottom</code> from <code>--_sticky-offset</code> — physical, matching the physical
edge API + observer) + the
<code>--sticky-z-index</code> token + the layout-neutral sentinel. The pinned TREATMENT is
yours: <code>[data-sticky][data-stuck]</code>.
</p>
@ -412,7 +416,7 @@
<tr>
<td class="name"><code>[data-sticky][data-edge='top'|'bottom']</code></td>
<td><span data-uix-tag data-kind="morfo">morfo</span></td>
<td>The logical inset from <code>--_sticky-offset</code>.</td>
<td>The physical inset (top/bottom) from <code>--_sticky-offset</code>.</td>
</tr>
<tr>
<td class="name"><code>[data-sticky-sentinel]</code></td>

Loading…
Cancel
Save

Powered by TurnKey Linux.