fix(ui) LogoHeading: account for bee width in span layout #22

Open
araemer wants to merge 11 commits from fix-logo-heading-bee-overflow into stage
Owner

Summary

The bee favicon in LogoHeading was absolutely positioned with a negative right offset (-right-12/8/6), placing it outside the span's layout box. The browser wraps the title text at the full container width without knowing the bee needs space to the right, so on narrow screens the bee overflows off the right edge.

Fix: Add right padding to the span equal to the bee's width at each heading level (pr-12/8/6) and anchor the bee with right-0 instead of -right-{n}. The bee's visual position relative to the text end is identical — only the span's layout box now includes that space, so text wraps before the bee would clip.

Test plan

  • Long recipe title on a narrow mobile screen: bee stays visible, title wraps to a second line
  • Short title: bee position unchanged
  • All three heading levels (h1 recipe list, h2/h3 where used) look correct

🤖 Generated with Claude Code

## Summary The bee favicon in `LogoHeading` was absolutely positioned with a negative `right` offset (`-right-12/8/6`), placing it outside the span's layout box. The browser wraps the title text at the full container width without knowing the bee needs space to the right, so on narrow screens the bee overflows off the right edge. **Fix:** Add right padding to the span equal to the bee's width at each heading level (`pr-12/8/6`) and anchor the bee with `right-0` instead of `-right-{n}`. The bee's visual position relative to the text end is identical — only the span's layout box now includes that space, so text wraps before the bee would clip. ## Test plan - [ ] Long recipe title on a narrow mobile screen: bee stays visible, title wraps to a second line - [ ] Short title: bee position unchanged - [ ] All three heading levels (h1 recipe list, h2/h3 where used) look correct 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The bee was positioned with a negative right offset, placing it outside
the span's layout box. On narrow screens the text wrapped at the full
container width and the bee overflowed off the right edge.

Fix: add right padding equal to the bee's width and anchor it with
right-0 instead of -right-{n}. The bee's visual position relative to
the text is unchanged, but the span now reserves the bee's space in
the layout so text wraps before the bee would clip.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The padded-span approach tracked the widest line, so on a wrapped title
the bee floated at the right of the longest line rather than the last one.

New approach: drop the wrapping span entirely; place a zero-width
inline-block anchor (<span w-0>) directly in the heading's text flow,
immediately after the text node. Because it is inline it always lands
at the cursor position after the last character on the last line.
The bee is absolutely positioned with left-0 relative to this anchor,
keeping it glued to the text end whether the title is single-line or
wraps to multiple lines.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without it, vertical-align defaults to baseline — a zero-height span
sits at the text baseline rather than the top of the line box, so the
bee is painted at mid-text level, hidden behind the heading glyphs.
align-top anchors the span to the line box top, matching the vertical
position from the original implementation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The zero-width span approach caused the bee to disappear — browser
behaviour for absolutely positioned children of 0×0 inline-block
containing blocks is too inconsistent to rely on.

Revert to the original outer-span structure (reliable and battle-tested)
and constrain the span with max-width: calc(100% - bee_width) so the bee
can never overflow the container on narrow screens.

Note: when a long title wraps to multiple lines the bee tracks the end
of the longest line, not the last line. Tracking the true last-line
cursor requires a JS layout measurement and is left for a follow-up.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Pure CSS cannot know where the last character of wrapped text lands.
Use getClientRects() on an inline text span — it returns one DOMRect
per line box — and take the last rect's right edge as the bee's left
position. A ResizeObserver re-runs the measurement on every layout
change so the bee tracks correctly across all viewport widths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The bee's left was already measured from the last line's right edge, but
top was still -top-6 relative to the outer span — so on a wrapped title
the bee floated above line 1 while pointing to the end of the last line.

Now both coordinates come from the last line's DOMRect: left tracks the
final character's right edge, top is placed beeTopOffset px above the
last line's top. ResizeObserver re-fires on every layout change so the
position stays correct at all viewport widths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two remaining edge cases:
- Bee was placed at the last line's right edge, not the widest line's.
  Now iterate all line rects and pick the one with the maximum right.
- On single-line titles that fill the container, lastLine.right lands at
  the container edge and the 48px bee overflows. Cap left so the bee's
  right edge never exceeds the heading element's available width.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the widest line is line 2 (e.g. "getrockneten Cranberries" under
a shorter "Apfelbrot mit"), deriving top from widestLine.top placed the
bee between the lines rather than at the heading's top-right corner.

Fix: revert top to the Tailwind -top-{n} class (constant offset above
the outer span's top = above the first line). Only left is measured
dynamically. This restores the original "top-right corner of the text
block" intent: vertical = above line 1, horizontal = end of widest line.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The flex-1 title container in PageHeader lacks min-w-0, allowing it to
expand beyond the visible content area. h1.getBoundingClientRect().width
then reports a value larger than what is on screen, and the bee overflows.

Add a second upper bound: viewport available width from the outer span's
left edge (clientWidth - outerRect.left). The bee's right edge is now
capped by whichever is smaller — the h1 width or the viewport edge.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without min-w-0, the default min-width:auto on flex items allows the
title container to expand beyond its share of the flex row, keeping
long titles on one line when they should wrap. This caused the bee to
sit at the end of an overly long single line and bleed into the action
buttons area ("bee moves into the header").

Adding min-w-0 to both nested flex-1 divs properly constrains the title
area so text wraps at the correct breakpoint and the bee stays within bounds.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The JS clamp against h1Width / viewport was fragile — flex layout and
scroll offsets made the measured bounds unreliable at certain viewport
widths, leaving edge cases where the bee overflowed.

The correct constraint is CSS: right padding on the outer span equal to
the bee's width (pr-12/8/6) forces text to wrap before it reaches the
bee's space. The bee can never overflow because the text never gets there.

The JS measurement (getClientRects on the inner text span) is now used
purely to pinpoint the widest line's exact right edge — no clamping
needed. The update() function reduces to: find max right, set left.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin fix-logo-heading-bee-overflow:fix-logo-heading-bee-overflow
git switch fix-logo-heading-bee-overflow

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch stage
git merge --no-ff fix-logo-heading-bee-overflow
git switch fix-logo-heading-bee-overflow
git rebase stage
git switch stage
git merge --ff-only fix-logo-heading-bee-overflow
git switch fix-logo-heading-bee-overflow
git rebase stage
git switch stage
git merge --no-ff fix-logo-heading-bee-overflow
git switch stage
git merge --squash fix-logo-heading-bee-overflow
git switch stage
git merge --ff-only fix-logo-heading-bee-overflow
git switch stage
git merge fix-logo-heading-bee-overflow
git push origin stage
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
araemer/recipe-app!22
No description provided.