Add JavaScript functionality for restoring layouts#863
Open
wibjorn wants to merge 12 commits into
Open
Conversation
🦋 Changeset detectedLatest commit: 8ae4ba8 The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
wibjorn
commented
May 22, 2026
…lpers
createLayoutState is now live on return — no separate start() ceremony — and sets data-layout-restored=true on the root after its initial subscriber flush. A failure-fallback path (sync setup throw, deferCallbacksUntil rejection, or a queued subscriber throwing) still marks the sentinel so gated CSS rules cant permanently hide content; setup errors are re-thrown.
Adds .display-{value}-until-layout-restored helpers in atlas-css mirroring the existing .display-{value}-{state} family. Pares comments in layout.ts and trims the README; moves the restoration recipe (inline snippet, CSP notes) into site/src/components/layout.md.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
- Add tsconfig.eslint.json (include src + test) and point .eslintrc.js parserOptions.project at it. Restores test-file linting that was broken when tsconfig.json was constrained to `include: ["src"]`. - `LayoutStateOptions.viewName` now accepts `string | (() => string)`. The function variant is resolved every time a view name is needed so a single instance can follow a dynamically changing "current view". Unsafe-key guard moved into the new `getViewName()` helper so it applies to both the static and function variants. - 5 new tests for the accessor: static behaviour, dynamic value across mutations, event-payload viewName at fire time, unsafe-key coercion from the function, unsafe-key coercion from a static string. - README.md: document the function variant; correct stale `deferCallbacksUntil` default (resolved promise, not DOMContentLoaded). - site/components/layout.md: mention the function variant. - changeset: mention viewName accessor + failure-fallback sentinel, correct `useViewTransitionOnRestore` per-instance flag name, and fix the stale `contentLoaded` default claim. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2cc18c0 to
4d8788f
Compare
createLayoutState wrapped restoration and each opted-in subscriber callback in document.startViewTransition without coordinating between them. When two calls landed close together (multiple subscribers for one class change, or a callback firing while restoration was still animating) the second startViewTransition aborted the first, surfacing InvalidStateError in the console. Each instance now tracks an activeTransitionCount and a same-microtask batch: - Restoration keeps its synchronous wrap but registers itself in the count. - Subscriber callbacks queued in the same microtask coalesce into one transition. - Any callback that would otherwise start a second transition while one is animating runs synchronously instead — no animation for that callback, but no abort either. Includes 4 new tests covering coalescing, skip-while-active, fresh transitions after settle, and resilience to a batched callback throwing. Updates the existing changeset and the js README to document the coordination. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… /your-bundle.js The atlas-design site uses @microsoft/parcel-transformer-markdown-html, which renders fenced ```html blocks as live HTML examples in addition to a syntax-highlighted code listing. The inline-restore snippet in layout.md ends with a placeholder `<script type="module" src="/your-bundle.js"></script>`, so Parcel saw a real script tag in the rendered page and tried to resolve /your-bundle.js as an asset, breaking the build / dev server. Switching the fence to ```html-no-example tells the transformer to render only the highlighted code listing and skip the live HTML injection. The snippet still renders correctly in docs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reduce layout-state-subscriptions.md to 3 sentences; fix small typos in busy-planets-like.md and large-snakes-share.md. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop the Layout state API-reference section in favor of a one-line mention and link, merge install/import sections, fix typo (Accesssing) and grammar bug (fall into a one of), and remove the stub src-folder section. ~57 lines down to ~27. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Compress 'Display helpers gated on JS restoration' (drop redundant 'Class naming pattern' label, shorten comments, fix awkward 'rules will never disable'). Reorder and compress 'Persisting layout state across page loads': fold 'When to skip' into the opener, drop the no-op/MutationObserver mechanism detail, merge the CSP workarounds into one paragraph, and trim the sentinel subsection so it stops repeating the helpers subsection. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
wibjorn
commented
May 22, 2026
| Place this synchronous IIFE in `<head>` before the bundle. It reads the same `localStorage` key that `createLayoutState` uses and applies the saved classes to `<html>` during parse: | ||
|
|
||
| ```html-no-example | ||
| <head> |
Contributor
Author
There was a problem hiding this comment.
Here is the relevant snippet.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
changes.
Link: preview
I've been trying to create some code that helps us restore the layout related html classes on the documentElement, then create an API subscribers can listen for changes and also use the same api just to make changes they want to happen on page load. It's been hard to fully figure out, and I've been bouncing between ideas to get the right balance.
I landed on the following:
We put an IIEF directly in the markup of pages on the consumer site. This is a short snippet that essentially checks local storage and uses it to restore the same, if we're on the same page template. This happens quickly, synchronously, before we get into any bundle code. This is a common method for restoring color themes without flashes of different themes too. It just lives in documentation, not source.
Next, (we are in the bundles now), we read the state again (which is keyed on page templates / view names), and create a thing (
layoutState) that can both what for changes to the relevant html classes on the documentElement and save the changes to state.Subscribers and write functions that run when a particular state is true - and they also run right away once if the state matches their conditions.
Put in another set of css helper classes, which are design to hide things if the layout is still restoring.
What this'll do ensure that we never see any jank on load, and give us the flexibility we need to easily write functions that target specific states.
Testing
Additional information
[Optional]
Contributor checklist