Skip to content

refactor: use getDOMSlot to build headings with anchors#3117

Open
Soxasora wants to merge 1 commit into
stackernews:masterfrom
Soxasora:feat/better-anchor-headings
Open

refactor: use getDOMSlot to build headings with anchors#3117
Soxasora wants to merge 1 commit into
stackernews:masterfrom
Soxasora:feat/better-anchor-headings

Conversation

@Soxasora

@Soxasora Soxasora commented Jun 21, 2026

Copy link
Copy Markdown
Member

Description

Before, to create headings with anchors, we would render both the heading and the anchor as siblings (Lexical reconciles this way) and hide the anchor behind the heading text via CSS. This required careful orchestration with the HTML placeholder and the live reader variant.

This PR, instead, uses the new getDOMSlot API to create heading nodes with anchor support, wrapping the children TextNodes directly into the <a> anchor.

other fixes:

  • prevent quote node vertical scrollbar
    this was surfaced by headings on mobile due to the different line height

Screenshots

Left is before, right is after

image

The left side shows the current behavior, anchor and <span> as siblings controlled via CSS; the right side instead shows the right HTML without any kind of CSS hacks.

Checklist

Are your changes backward compatible? Please answer below:

yes

On a scale of 1-10 how well and how have you QA'd this change and any features it might affect? Please answer below:

7, can export html, create the right ssr html and works perfectly in editor and in reader.

For frontend changes: Tested on mobile, light and dark mode? Please answer below:

Yes

Did you introduce any new environment variables? If so, call them out explicitly here:

No

Did you use AI for this? If so, how much did it assist you?

Yes, getDOMSlot usage


Note

Medium Risk
Changes reader DOM structure, SSR export, and HTML import for headings, but scope is limited to SNHeadingNode and related styles with plain-text-only permalink wrapping.

Overview
Read-only heading permalinks no longer rely on a hidden, absolutely positioned anchor sibling. In the reader, plain-text headings (SNHeadingNode) get an empty decorative <a> and Lexical getDOMSlot reconciles text/line breaks inside that link so the visible title is the real permalink. Headings with rich inline content skip the wrapper and keep only the id.

exportDOM mirrors that structure for SSR via an append hook that drops exported children into the anchor. importDOM still strips data-lexical-heading-link anchors without duplicating link nodes on round-trip.

Styles drop the overlay/position: relative hacks; .sn-heading__link is styled like normal heading text. .sn-quote adds overflow-y: hidden to avoid an unwanted vertical scrollbar (notably on mobile).

Reviewed by Cursor Bugbot for commit e1ec893. Bugbot is set up for automated code reviews on this repo. Configure here.

fixes:
- prevent quote node vertical scrollbar
  this was surfaced by headings on mobile due to the
  different line height

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e1ec893. Configure here.

if (!node.hasAttribute(HEADING_LINK_ATTRIBUTE)) return null
return {
conversion: () => ({ node: null, forChild: () => null }),
conversion: () => ({ node: null }),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Legacy heading import duplicates text

Medium Severity

Removing forChild: () => null from the decorative heading-link import means text inside the permalink a is imported as well as sibling heading content. HTML produced by the previous sibling-anchor export (anchor text plus reconciled spans) can round-trip into duplicated heading text when pasted or imported.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e1ec893. Configure here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We render the lexical state and the HTML dynamically, this can't happen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant