Fable audit follow-through (fable_audit.md + fable-eidos-audit.md):
- RECIPE_CONTRACT.md (E2 canon): the transversal systems every recipe must
consume, enforced by component-audit R-4.1-4.6 (all at error; escape
valves /* literal */ + /* functional */; WIP tracks excluded). Stale
audit rules fixed against the current architecture (E-2.2 wrapper
imports, D-1.2 v2 9-tab union, D-3.1 single snippet, TabsVariant
mirror) - verdicts went 0/117/15 -> 75/50/5.
- Motion migration 15/15: recipes off local @keyframes onto the channel -
preset stamps (dropdown/context/select/combobox/tooltip/link-preview/
clipboard), new expand/collapse + value-flash signatures, shared-axis
reverse pair, delayed-open open-alias in the preset trigger
(PRESET_STATE_ALIASES), materials pattern for irreducible triggers
(card/timeline/tabs/nav-menu/metrics). Duration/scale hooks keep every
recipe's tuned values.
- buildScheme (fase C): full a1..a12 alpha ramp per role (was a2/a3 -
stale alphas after applyColorScheme), intentSeeds so temper starts from
the ACTIVE theme's intent mapping (risk stays orange), alpha background
self-derived from the scheme's own neutral step 1 (was hardcoded
#fff/#111); mode now forces the donor variant.
- Inventory decisions (fase D): semanticTracking axis removed (all-zero),
border-hover slot dropped (0 consumers), separator slot adopted across
line dividers (step 6, Radix divider tone), size-bundle consumption
pilot on toggle (canonical coordinates consumed, deliberate deviations
kept visible).
- Docs: building-a-component.md (the one door, 9-phase route + known
traps), PLAN-docs-reconciliation.md (fase 6 kickoff for a fresh
session), THEMING wiring updates.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A perceptible line showed across the caret base on every side but bottom: the
panel's border draws across the caret's base edge, "cutting" it off from the panel.
Add the popover's per-side fix — pull the caret base into the panel by the border
width (margin toward the panel, keyed on data-side) so the fill covers that border
and the caret's side strokes continue the panel outline seamlessly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A single-direction drop-shadow on the caret is wrong: the caret sits at the panel
edge pointing AT the trigger, so on a bottom-side tooltip (caret at the top) the
downward shadow falls onto the panel below it — a dark smudge over the content.
Remove it. The caret keeps the panel-bg fill + border edges (no black blob, no
smudge). A shadow that reads correctly would have to wrap the whole panel+caret
silhouette (content-level drop-shadow), not the caret alone.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Follow-up to the un-clip fix (c3def5d6). With the caret no longer clipped it was
visible, but it rendered in `currentColor` (the dark text colour) — a black triangle
on a light panel. Colour it to the panel bg (a seamless caret) + the panel border on
the two exposed edges, and give it its own `drop-shadow` so the matched (light) caret
still reads against the page — the panel's box-shadow is rectangular and never reaches
the caret. This is the "matched + visible" caret (the drop-shadow-on-the-silhouette
idea) now that overflow:visible lets it show.
VERIFICATION CAVEAT: confirmed the colour at the computed-style level (polygon fill ==
panel background, was the dark text colour). Could NOT capture a stable screenshot —
the demo tooltip closes on every programmatic/cursor interaction, so the visual pop of
the drop-shadow is unverified here. If the matched caret reads too faint, bump the
drop-shadow alpha or fall back to the darker caret.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The tooltip content is `position: relative; overflow: auto`, so the caret
(`position: absolute`, containing block = the content) that pokes past the panel
edge was CLIPPED away — invisible regardless of its colour. Confirmed by measuring:
the caret sits fully outside the content box, and the content clips its overflow.
Change the content to `overflow: visible` — the same reason the popover content is
`overflow: visible` (its comment literally says "fixes the arrow that overflow:auto
used to clip"). Tooltips are short, so no inner scroll viewport is needed.
Verified in a real browser: the dark caret is now clearly visible pointing at the
trigger. Combined with a40d1573 (caret colour back to currentColor) + the earlier
`display: block` (correct arrow height → correct trigger gap), the tooltip arrow is
visible and correctly placed again.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
My previous commit (4afadfd7) over-corrected: it changed the caret fill from
`currentColor` (the panel's dark text colour — long-standing, visible) to the
panel background, aiming for a "seamless extension". On a light-on-light tooltip
that makes the caret INVISIBLE — the panel reads only because of its box-shadow,
which the separate caret element doesn't share. The user had a visible caret
before; the "correct" colour regressed it.
Revert the polygon/path colour rules (caret returns to `currentColor` = visible)
and keep ONLY the `display: block` on the SVG — that was the fix that mattered: it
removes the ~21px of inline-baseline phantom height that both misaligned the caret
and, because the positioner adds the measured arrow height to the gap, pushed the
panel too far from the trigger. So: caret visible again + panel sits at the correct
~13px gap. A properly matched-and-visible caret (bg fill + its own drop-shadow) is
deferred until it can be verified.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The tooltip arrow SVG carries `fill="currentColor"` / `stroke="currentColor"`
presentation attributes, so setting `fill`/`stroke` on the wrapping `<span>`
(`[data-tooltip-arrow] { fill: … }`) never reached the shapes — an element's own
presentation attribute beats an inherited value. The caret therefore painted with
`color` (the dark text colour) instead of the panel background, and the `<svg>` was
left `display: inline`, adding phantom baseline height (26px box for a 5px caret)
that misaligned it. Net effect: the caret didn't read as a caret.
Fix mirrors the popover recipe: colour the `polygon` (fill = panel bg) + `path`
(stroke = panel border) directly, and set the `<svg>` to `display: block`. The
tooltip caret is now byte-identical in treatment to the popover's (fill = content
bg, 1px border-coloured edge). Also fixes the outline variant, which had the same
span-vs-shape mistake.
Pre-existing bug, unrelated to the avoidCollisions:'flip' rollout. Verified in a
real browser: polygon fill now equals the content background, svg is block, caret
positioned correctly.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
24h picker driven by a horizontal daylight gradient — drag the sun/moon
thumb across the night→day→night sky, jump with moment chips, or nudge
with hour/minute/second steppers. Inline panel + trigger/popover variants,
built on the generic Picker (deferValue) composing Slider, Button,
PickerShell, Popover and Tooltip.
- granularity (hour|minute|second) + optional band props; steppers keep a
fixed 3-column grid; shape/size propagate to panel, trigger and chips.
- fixed-tone sky palette as named recipe tokens (theme-independent).
- fix soma Tooltip.Arrow (empty children slot hid the default SVG) and
make PickerShell footer labels locale-reactive.
- floating-gap canon: --floating-gap-menu 0 -> var(--space-1); FloatingContent
reads --floating-gap via a reactive $derived (was a one-shot rAF that never
reached portaled menus -> they fell back to sideOffset, flush); nav-menu wired
to the token via CSS.
- focus-ring: exclude item/option/content from the universal archetype ring; menu
rows use the canonical highlight on :focus-visible too (no thick keyboard border,
no ring framing the whole float).
- overscroll-behavior: contain on the ScrollArea viewport + 7 overlay scroll regions
(no scroll chaining to the page).
- archetypes.css: default rules wrapped in :where() so component recipes always win.
- radius decoupled from size (Radix model) in toggle/textarea/menubar/nav-menu/
virtual-list/grid; toggle `rounded` prop.
Canon-read + token reach portaled menus pending a fresh-load check (the dev tab
cached the floating module) -- see CONTINUE-floating-gap-canon.md.
The tooltip's `delayed-open` entrance (the per-side slide, and now the bounce) NEVER
fired: the content only mounts when `open` becomes true, and the hover-delay timer
set `internalTooltipState = 'instant-open'` AT THAT MOMENT — so the content always
mounted as `instant-open` (no entrance). `delayed-open` was only ever the pending
state while the content was still unmounted. A long-standing dead animation.
Fix: the hover-delay timer now keeps `'delayed-open'` as the content mounts, so the
eidos `[data-state='delayed-open']` entrance plays. `'instant-open'` stays reserved
for focus / skip-delay (intentionally no entrance). Test updated (it asserted the
buggy `instant-open`); tooltip provider suite 4/4.
Also makes the bounce clearly perceptible (it was too subtle to notice even when
forced): scale 0.5 → 1.18 (overshoot) → 0.9 (undershoot) → 1.06 (second bounce) → 1,
over `--duration-slow`. Verified end-to-end in-browser: hover → content mounts
`delayed-open` → `animation-name: eidos-tooltip-bounce-in`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the tooltip's per-side 2px slide-in with a scale BOUNCE: grow from 0.7,
overshoot to 1.06, dip to 0.97 (the undershoot — the "rebote"), settle to 1, over
`--duration-moderate`. It scales from `transform-origin` (the floating origin,
toward the anchor), so it keeps a directional feel without per-side keyframes —
8 keyframes collapse to 2.
Bespoke recipe keyframe, NOT a registered `motion` preset: the tooltip's state is
3-valued (`delayed-open` / `instant-open` / `closed`), not the generic `open` /
`closed` the shared presets key on — so a registered preset wouldn't fire here.
This also confirms the architecture boundary: components whose state shape doesn't
match open/closed use a bespoke recipe animation, not the `motion` selector.
`instant-open` (keyboard focus) keeps no entrance; the reduced-motion block (kills
the animation) already covers the new rules — verified the bounce curve in-browser
(scale 0.7 → 1.06 → 0.97 → 1). Recipe-only change, no generator/base.css touched.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Closes the second BROKEN component from the audit (after date-range-picker).
Five errors collapse to zero:
- Morfo declares the three `emerge` events the recipe animations were
already keying off (`open`, `close`, `close-dismiss`) plus a
`texts.label` slot. `data-disabled` is now declared on `Trigger` (with
optional severity) so the recipe rule has a real contract to land on.
`aria-disabled` and `texts.label` round out the surface; the new
`tooltip.label` catalog entry ships under
`src/uix/langs/components/tooltip.ts`.
- Recipe CSS: explicit `[data-tooltip-trigger][data-disabled]` and
`[data-tooltip-trigger]:focus-visible` rules. The disabled rule
suppresses pointer events; the focus rule provides a defensive
outline so a focusable trigger shell is at least keyboard-visible.
- README: convert the old `## Pendiente deliberado` section into a
proper `## Gaps` table with explicit dispositions
(implementar/diferir/descartar), and update `## Eventos Sema` to
reflect that tooltip now declares its three emerge events instead of
claiming zero. Comparativa was already complete; the regex fix from
the previous commit picked it up.
Audit: tooltip BROKEN → PASS. Project-wide goes from 1/64/2 to 3/64/0
(PASS / NEEDS-WORK / BROKEN). No more BROKEN components.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>