⌇ Seamble

The same guide AI assistants read through Seamble's MCP server. View as Markdown

Squarespace

7.1 markup is observed, not documented: null-check lookups, and inspect rather than guess.

Page structure

#siteWrapper
  header#header
  main#page                                  the stable root
    article#sections > section.page-section
    or article#page-regions > section.region > section.page-section
  footer#footer-sections
  • A .page-section carries data-section-theme (may be "", meaning the site default) and data-section-id (a rendered id, not Squarespace's backend id). Fluid Engine sections have data-sqsp-section="fluid-engine".
  • The mount is a plain div inside a Code Block. Find its section with el.closest(".page-section"); sections below are its nextElementSiblings that match .page-section.
  • Squarespace buttons aren't always links: match a[href] and button[data-href].

The editor

A signed-in owner visiting their site lands in /config, with the site in an iframe (window.self !== window.top). Squarespace disables scripts embedded in Code Blocks while the owner is logged in and editing, so they may not see the component there; the accurate view is logged out. A browser holding the seamble_preview cookie shows drafts in the editor too.

Clicking Edit adds sqs-edit-mode-active to <body> without a reload. If you hid, moved or rewrote Squarespace's markup, undo it then, or the owner can't edit it:

function onEditMode(undo) {
  if (window.self === window.top) return;
  const on = () => document.body.classList.contains("sqs-edit-mode-active");
  if (on()) return undo();
  const obs = new MutationObserver(() => { if (on()) { obs.disconnect(); undo(); } });
  obs.observe(document.body, { attributes: true, attributeFilter: ["class"] });
}

Taking over the page

  • Sections into a new layout (slider, stack): mount in the first section. Insert your container into the page first, then move the sections into it; anchoring on a section already moved inside throws (HierarchyRequestError). Give the container the first section's data-section-theme. Undo: move them back in reverse order.
  • Hiding sections (tabs): hidden alone doesn't hide a .page-section (Styling).
  • List sections (auto layouts: team, testimonials): section.user-items-list-section[data-sqsp-section="auto-layout"] holds [data-controller^="UserItemsList"][data-current-context]: JSON with userItems (title, description HTML, button, image.assetUrl). Render from it beside .user-items-list; hide the original in the editor, remove it live if you like.
  • Popups: sections clip their content, so append the overlay to #siteWrapper. Give it a theme: the mount's el.closest("[data-section-theme]") value, or the first non-empty one in #page (the header and cookie banner often have ""). The site's default theme is defined only on :root: set on an element inside a differently themed section, it doesn't apply.

Inserted markup doesn't start

Squarespace starts its blocks once, at load. Its markup inserted later (a fetched or cloned section) keeps its HTML, but carousels, lazy images and background video never run, and calling Squarespace's init functions doesn't start them. Build your own UI from the data. Moving sections that are already running is fine.

Reading content

Collections (blog, events, products): add ?format=json to the collection URL.

  • items[], for a blog: title, fullUrl, excerpt and body (HTML), assetUrl, categories, tags, author, publishOn (epoch ms). Blog-wide: collection.categories, .tags.
  • 20 items per page. Follow pagination.nextPageUrl while pagination.nextPage, adding format=json back (it's dropped). Never compute offsets; don't trust collection.itemCount.
  • The response can be HTML with status 200: parse in try/catch, show an empty state.
  • The mount's data-source (e.g. /blog) says where, if set.
  • Images: assetUrl + "?format=750w", never the original.
  • A list section's data is already in data-current-context: no fetch.

Another page: ?format=json doesn't include a normal page's sections. Fetch its HTML, parse with DOMParser, take what you need from #page, and use it as static HTML.

Site context: window.Static?.SQUARESPACE_CONTEXT. Signed in, it holds account details and a CSRF token: never send it anywhere.