Firma "(a) completada" (2026-08-26), cerrada tras rechazar la especie
`output` por abrir excepciones (clase barata - frontera de prosa - patron ya
rechazado dos veces): las cuatro hermanas --navigation-menu-indicator-
{x,y,w,h} pasan a --_ (la adjudicacion que tabs ya firmo citando a ESTE
componente como precedente), indicator-w se RETIRA del contrato, y el README
de soma deja de enseñar a saltarse la composicion.
- Flip atomico escritor+lectores. Trampa del barrido, medida: el grep del
nombre LITERAL es ciego a la forma con llaves {x,y,w,h} - escondia tres
menciones. Barrido final: 0 consumos vivos de los nombres viejos.
- indicator-w retirada con acta MEDIDA (y una auto-correccion: la primera
acta decia "con la barra cerrada" y era falso - el indicador se monta
condicional y hay 0 nodos; lo real, con transiciones CONGELADAS: solo una
regla !important de autor le llegaba, y la unica ventana donde asomaba
era el indicador montado sin rect - un subrayado de ancho cero). Contrato
44 -> 43, generated -1 linea, ningun ledger rancio.
- README de soma: las medidas siguen tabuladas como canales privados "not
consumer API"; la seccion nueva enseña la PARTE ([data-navigation-menu-
indicator] + data-state + los tres pomos thickness/bg/radius) con
ejemplos VERIFICADOS POR MEDICION (el ejemplo 1 es literalmente el
control positivo: block-size 2 -> 9px). Hueco FLAGGED sin acuñar: el
ejemplo viejo de pildora no tiene traduccion - falta un eje de forma en
la parte; en SS13.
- Registro PENDING_PRIVATE_RENAME -> UNA entrada (motion). SS14 NUEVO en
next-features: unificar tabs (--indicator-* SIN prefijo, MeasuredIndicator
:80-83, 2 claves global en el ledger) - con el hallazgo de que ningun
guard los caza HOY porque no llevan prefijo: el espacio cerrado premio al
que nombraba mejor.
- Numeros: public 3073->3072, channel 37->38, reach 73% y atHundred 65
intactos (ningun redondeo cayo), --names 4566 con 0 desviadas, centinela
nav-menu 42/43 con la misma unica adjudicacion, --debt identico.
- Dinamica verbatim: el indicador DESLIZA (left 0 -> 111px, ancho 107 ->
108.56 = rect del trigger, canal w/h == triggerRect), los nombres viejos
computan vacio en el elemento (control negativo 777/333: nada se mueve).
Guards: 78 tests verdes + soma 9/9 + mutacion por las dos caras + audit
162 PASS + docs:check 0/0.
BREAKING: los cuatro nombres publicos del indicador ya no existen y
--navigation-menu-indicator-w sale del contrato; el indicador se estiliza
por su parte y sus pomos.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ -170,43 +170,53 @@ In the anatomy file (`anima/components/navigation-menu.anatomy.ts`) the `content
## CSS Variables
The `Indicator` is inline-styled with `position: absolute` + `left` / `top` anchored to the active trigger's top-left corner (relative to the Provider root). Its **shape** — width, height, offset — is consumer-controlled via CSS using these variables:
The `Indicator` is inline-styled with `position: absolute` + `left` / `top`, anchored to the active trigger's top-left corner (relative to the Provider root), and soma writes the trigger's four measurements into **private value channels** on that same inline `style`:
| `--_navigation-menu-indicator-x` | Left offset of the active trigger |
| `--_navigation-menu-indicator-y` | Top offset |
| `--_navigation-menu-indicator-w` | Width of the active trigger |
| `--_navigation-menu-indicator-h` | Height of the active trigger |
This split matters: if the provider forced `width`/`height` on the indicator element, a 2px underline would be stretched to cover the full trigger and hide its text. Instead, the component tells you "here are the trigger's measurements — style the decoration however you want".
**These are not consumer API.** The leading `_` is the house mark of a value channel: a per-instance measurement soma writes and the eidos recipe reads. They are written INLINE on the element, so a stylesheet — yours or a theme's — can never win against them; a rule that tried would be a rule that lies. They are listed here to describe the mechanism, not to invite a `var()`.
### Example — underline at the bottom of the trigger
The reason the provider does not simply force `width` / `height` on the element still holds: a 2px underline stretched to cover the full trigger would hide its text. Soma measures; the recipe shapes.
### What you style: the part
You style the `Indicator`**part** — `[data-navigation-menu-indicator]`, which carries `data-state="open" | "closed"` — and you re-shape it through the recipe's contract knobs, not through the measurements:
| `--navigation-menu-indicator-thickness` | `var(--border-width-medium)` | The underline's thickness |
| `--navigation-menu-indicator-bg` | `var(--color-primary-solid)` | Its fill |
| `--navigation-menu-indicator-radius` | `var(--radius-full)` | Its corners |
The recipe already tracks the active trigger for you: it takes the trigger's width, sits under it, slides between triggers and fades in on `data-state="open"`.
### Example — a thicker underline in another tone
```css
[data-navigation-menu-indicator] {
width: var(--navigation-menu-indicator-w);
height: 2px;
background: currentColor;
/* `top` is the trigger's top — slide down by its height to sit underneath */
> **Not reachable today: the full-trigger highlight (pill / backdrop).** The recipe pins the indicator's `block-size` to `--navigation-menu-indicator-thickness` and offsets it below the trigger, so there is no contract knob that makes the decoration fill the trigger's box instead of underlining it. That would need a knob the part does not have (a shape/variant axis), and inventing one here — or reading the private measurements — is the thing this section stopped teaching. Flagged as a gap: `docs/next-features.md` §13.
<code>Item</code> is the wrapper around each trigger / content pair (gets <code>value</code> for routing in the bar's <code>value</code> binding). <code>Trigger</code> renders a native <code><button></code> with chevron affordance; <code>Link</code> renders <code><a></code> with <code>aria-current</code> support — both styled by the recipe with the same flat in-bar chrome, NOT routed through <code><Button></code>. <code>Indicator</code> is a 2px underline that slides between active triggers via CSS vars (<code>{`--navigation-menu-indicator-{x,y,w,h}`}</code>) written by soma.
<code>Item</code> is the wrapper around each trigger / content pair (gets <code>value</code> for routing in the bar's <code>value</code> binding). <code>Trigger</code> renders a native <code><button></code> with chevron affordance; <code>Link</code> renders <code><a></code> with <code>aria-current</code> support — both styled by the recipe with the same flat in-bar chrome, NOT routed through <code><Button></code>. <code>Indicator</code> is a 2px underline that slides between active triggers via private value channels (<code>{`--_navigation-menu-indicator-{x,y,w,h}`}</code>) written by soma.
<tr><tdclass="name">[data-navigation-menu-content][data-motion]</td><td><spandata-uix-tagdata-kind="soma">morfo</span></td><td>Directional slide on trigger swap (from-start/end + to-start/end).</td></tr>
<tr><tdclass="name">[data-navigation-menu-indicator]</td><td><spandata-uix-tagdata-kind="soma">morfo</span></td><td>2px underline that slides via CSS vars (<code>{`--navigation-menu-indicator-{x,y,w,h}`}</code>).</td></tr>
<tr><tdclass="name">[data-navigation-menu-indicator]</td><td><spandata-uix-tagdata-kind="soma">morfo</span></td><td>2px underline that slides via private value channels (<code>{`--_navigation-menu-indicator-{x,y,w,h}`}</code>).</td></tr>