Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions skills/leaf/packages/default/theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion tests/test_render_projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
28 changes: 28 additions & 0 deletions tests/test_render_widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
"""
<h1>Areas</h1>
<table id="edge-table" style="width: fit-content">
<tbody><tr><td style="padding: 0"><lf-gloss
tip="The test reads the index before and after."
>byte-identical</lf-gloss></td></tr></tbody>
</table>
""",
)
),
)
expect(page.locator("#edge-table .lf-gloss-mark")).to_be_visible()
room = page.locator("#edge-table").evaluate("el => el.scrollWidth - el.clientWidth")
assert room <= 1, f"the table scrolls {room}px sideways"
assert errors == []
page.close()


def test_a_nested_platform_control_does_not_pin_its_gloss(browser, serve):
"""A nested control owns its click even when its platform contract is an ARIA role."""
page, errors = open_page(
Expand Down
Loading