Skip to content

discovery: emit Gutenberg block markup for Squarespace 7.1 sqs-block HTML#67

Open
davipontesblog wants to merge 1 commit into
Automattic:mainfrom
davipontesblog:improvement/squarespace-gutenberg-blocks
Open

discovery: emit Gutenberg block markup for Squarespace 7.1 sqs-block HTML#67
davipontesblog wants to merge 1 commit into
Automattic:mainfrom
davipontesblog:improvement/squarespace-gutenberg-blocks

Conversation

@davipontesblog

Copy link
Copy Markdown

What this changes

Squarespace 7.1's per-post item.body is a deeply-nested layout of
<div class="sqs-layout"><div class="row"><div class="col"><div class="sqs-block ...">….
Passed straight into <content:encoded>, WordPress wraps the entire body in
a single Classic block during import: the editor can't reorder images, can't
swap individuals, can't toggle the native gallery lightbox.

New src/adapters/squarespace-blocks.ts exposes
squarespaceHtmlToGutenberg(html) which walks every top-level sqs-block
element and emits the matching Gutenberg block markup:

Squarespace class Gutenberg block
sqs-block image-block core/image
sqs-block gallery-block core/gallery (linkTo:media → core lightbox)
sqs-block html-block core/heading / core/paragraph / core/list / core/quote
sqs-block embed-block / video-block core/embed (with provider slug)
sqs-block quote-block core/quote
sqs-block horizontal-rule-block / line-block core/separator
sqs-block spacer-block dropped (Gutenberg handles spacing)
unrecognised sqs-block … core/html (lossless fallback)

Prefers data-image over src for lazy-loaded images (same root cause as
the DOM-fallback fix in the other PR). Gallery items get an <a href> wrapper
and linkTo:media so core's native click-to-zoom lightbox triggers without a
third-party plugin.

Wired into squarespace.ts extract()extractPage() right before the
bodyText quality scoring. No-op when input has no sqs-block markers, so
the DOM-fallback path is unaffected.

How I found it

While migrating walkaboutchronicles.com — every imported post body was a
single uneditable Classic block. We first built a PHP version of this
mapping inside our migration plugin (https://github.com/davipontesblog/sqs71-to-gutenberg)
and ported the learnings here.

Tested against

  • Real site (analogous PHP port; 1,570 posts emitted as proper Gutenberg
    blocks on a Studio site)
  • Tests pass (npx vitest run — 416 passed, 2 skipped)
  • npx tsc --noEmit clean
  • 12 new unit tests for squarespaceHtmlToGutenberg
  • No new dependencies (uses existing cheerio)

Discovery log entry added to DISCOVERIES.md

  • Yes

…HTML

Squarespace 7.1's per-post item.body is a deeply-nested layout of
<div class="sqs-layout"><div class="row"><div class="col">
<div class="sqs-block ...">...</div></div></div></div>. Passed
straight into <content:encoded>, WordPress wraps the entire body in
a single Classic block during import — editor can't reorder images,
can't swap individuals, can't use the native gallery lightbox.

New src/adapters/squarespace-blocks.ts exposes
squarespaceHtmlToGutenberg(html) which walks every top-level
sqs-block element and emits the matching Gutenberg block markup:

  sqs-block image-block           → core/image
  sqs-block gallery-block         → core/gallery (linkTo:media → core lightbox)
  sqs-block html-block            → core/heading / paragraph / list / quote / separator
  sqs-block embed-block/video     → core/embed (with provider slug)
  sqs-block quote-block           → core/quote
  sqs-block horizontal-rule-block → core/separator
  sqs-block spacer-block          → dropped (Gutenberg handles spacing)
  unrecognised sqs-block          → core/html (lossless fallback)

Prefers data-image over src for lazy-loaded images (same root cause
as the DOM-fallback fix). Gallery items get an <a href> wrapper and
linkTo:media so core's native lightbox triggers.

Wired into squarespace.ts extractPage() right before the bodyText
quality scoring. No-op when input has no sqs-block markers, so the
DOM-fallback path is unaffected.

12 new unit tests; full suite still passes (416 tests).

Found while migrating walkaboutchronicles.com — every post body was
a single uneditable Classic block. See DISCOVERIES.md for full context.
borkweb added a commit that referenced this pull request Jun 5, 2026
## Summary
Absorbs PR #67's sqs-block→Gutenberg transform as an `AdapterBlocks` capability attached to the squarespace adapter. The transform fires at reconstruct time (not extraction), so the extraction layer stays unchanged. Returns `null` for HTML with no `sqs-block` markers, making it safe to call on every post body.

## Why
Without this, every Squarespace 7.1 post imported as a single Classic block. Gallery lightbox, per-image editing, and block reordering were all unavailable until the user manually ran "Convert to blocks".

## How
`blocks.ts` ports all helpers from PR #67 verbatim, with two interface adaptations: the top-level function takes a `BlockRecipeContext` second arg (per the `AdapterBlocks` seam contract) and returns `null` instead of the input string in both passthrough branches. The export is an `AdapterBlocks` object (`{ htmlToBlocks }`) wired into `squarespaceAdapter.blocks` in `index.ts`.

## Testing
- 13 vitest tests (TDD: red → green) covering image with `data-image` preference, captioned image, 3-image gallery with `linkTo:media`, heading+paragraph+list, ordered list, YouTube embed with provider slug, spacer drop, horizontal rule, unrecognised block fallback, block-order preservation, and adapter attachment.
- `npx tsc --noEmit` clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant