diff --git a/skills/leaf/packages/default/theme.css b/skills/leaf/packages/default/theme.css index 39c0f15f3..f32c0ea51 100644 --- a/skills/leaf/packages/default/theme.css +++ b/skills/leaf/packages/default/theme.css @@ -38,14 +38,18 @@ lf-gloss:has(> [data-lf-said="tip"])::after { content: none; } lf-gloss > .lf-gloss-mark { background: var(--accent); color: var(--card); } /* The compact badge stands in a line of prose, so its visible box cannot be the thing a * reader aims at: growing it to a usable size would open the line it is set in, which is - * the one thing a mark this small must not do. The aim is a box of its own, - * centred on the glyph and outside layout entirely โ it overlaps the tail of the phrase - * it belongs to, which costs nothing, because hovering that same phrase already reveals - * the same explanation. */ + * the one thing a mark this small must not do. The aim is a box of its own, outside + * layout entirely โ it overlaps the tail of the phrase it belongs to, which costs + * nothing, because hovering that same phrase already reveals the same explanation. It + * ends where the badge ends rather than straddling it: an absolutely positioned box is + * outside layout but still inside its ancestors' scrollable overflow, so a box centred + * on a badge that sits near a cell's inline edge makes that table scroll sideways by + * the half it hangs over. Reaching back over the phrase costs nothing; reaching past + * the edge the badge itself fits inside does. */ .lf-gloss-mark::after { - content: ""; position: absolute; top: 50%; left: 50%; + content: ""; position: absolute; top: 50%; inset-inline-end: 0; width: var(--aim-floor); height: var(--aim-floor); - transform: translate(-50%, -50%); + transform: translateY(-50%); } .lf-gloss-mark:is(:focus-visible, .lf-focus-visible) { outline: var(--here-ring); --lf-here-ring: gloss-mark; outline-offset: var(--here-ring-gap); diff --git a/tests/test_render_projection.py b/tests/test_render_projection.py index 038343e50..1f49e2085 100644 --- a/tests/test_render_projection.py +++ b/tests/test_render_projection.py @@ -5090,7 +5090,7 @@ def test_nested_command_projections_stop_at_their_own_boundary(browser, serve): expect(page.locator("#outer-goal")).not_to_have_attribute("data-lf-open", "") page.locator("#inner > .lf-command-head").click(position={"x": 5, "y": 5}) page.locator("#inner > .lf-fleet-view summary").click() - page.get_by_role("link", name="inner-worker", exact=True).click() + page.get_by_role("link", name="ยง inner-worker", exact=True).click() expect(page.locator("#outer-goal")).not_to_have_attribute("data-lf-open", "") assert errors == [] page.close() diff --git a/tests/test_render_widgets.py b/tests/test_render_widgets.py index 3cf10113f..fd8571e40 100644 --- a/tests/test_render_widgets.py +++ b/tests/test_render_widgets.py @@ -981,6 +981,34 @@ def test_a_gloss_opens_at_its_phrase_for_pointer_keyboard_and_touch(browser, ser context.close() +def test_a_gloss_aim_box_does_not_make_its_table_scroll_sideways(browser, serve): + """The mark's aim box is outside layout but not outside overflow. A gloss that ends + a cell puts the badge against the table's inline edge, and an aim box straddling the + badge would hang half its width past that edge โ leaving a table the reader can drag + sideways over a target nothing draws.""" + page, errors = open_page( + browser, + serve( + leaf_page( + "gloss at the edge", + """ +