[@layout]: ../docs.layout.md

# Open Sources alongside a page

Keep supporting material in a reference sidebar, or open a Source in an overlay
without leaving the current page. Layouts supply the destinations; links and
browser code share them.

[Try the working lesson](../../examples/source-opening.demo.md).

---

## Choose how a link opens

Use a [Quickstart project](../start/first-site.guide.md): its default
`paths/site.layout.md` already provides reference and overlay destinations.
Create `paths/reference.page.md`:

```md
# Keep these ideas handy

Contrast helps readers distinguish a heading from its surroundings.
```

Create `paths/lesson.page.md`:

```md
# Practice contrast

Try a dark heading on a light page.

[Read normally](./reference.page.md)

[@link(open=reference) Keep this handy](./reference.page.md)

[@link(open=overlay).button Open the reference](./reference.page.md)
```

Run `bun run dev` and open `/lesson.page`. “Read normally” navigates; “Keep this
handy” opens beside the lesson; “Open the reference” opens an overlay. Close it
to return to the invoking link. On a phone the reference uses a sheet.

The opening choice composes with button appearance. These remain normal anchors
with canonical destinations for copying, new tabs, and navigation without
JavaScript. Explicit reference links also work on touch screens.

Shift-click takes precedence over an authored overlay request. It also works
inside an open reference or overlay. Opening a reference from an overlay dismisses
the overlay so the reference is visible.

Ordinary Source links inside an opening navigate the main page. An overlay
closes on that navigation; references remain when the next layout supports them.
External links, downloads, and other modified clicks keep their normal behavior.

A Component rendering an anchor directly must provide both markers, plus a
canonical rendered URL:

```html
<a href="/practice.page#learner-prompts"
   data-pmx-source-link
   data-pmx-source-open="reference">Learner prompts</a>
```

Include `<x-source-references />` in every layout where that link appears.
`data-pmx-source-link` enrolls the anchor in Source navigation;
`data-pmx-source-open` chooses the destination. Markdown directives supply both.

When a layout lacks the requested destination, links fall back to navigation.
Direct browser API calls reject instead of navigating away.

---

## Supply the destinations

Sources is included in Core's default setup. Projects created with the Quickstart
already include both destinations in `paths/site.layout.md`; no extra package
or Plugin activation is required. Shift-click an internal Source link to try
the reference sidebar.

A layout places the reference sidebar and declares overlay availability:

```html
<div class="workspace">
  <main><x-source-content /></main>
  <x-source-references />
</div>

<x-source-overlay />
```

Use your layout's stylesheet to arrange the main content and sidebar:

```css
.workspace {
  display: flex;
  align-items: flex-start;
  gap: 2rem;
}

.workspace > main {
  flex: 1;
  min-width: 0;
}
```

The desktop panel docks to the right edge with compact, contiguous Source headers.
Drag its left edge to resize it. The focused divider also accepts Left/Right arrow
keys, Home, and End; double-click it to restore the layout’s default width.
Your chosen width lasts through enhanced navigation in the current session.
Headers and Source previews share the Source type’s icon, label, and color, with
its description available on hover. Types without a Map definition use their
resolved default label and icon. Map edits update headers already on screen.
Use a header’s chevron to collapse or expand that Source; its inputs and reading
position remain intact. Reopening a collapsed reference link expands it.

The reference component takes no space until a reference is open. Its default
width is `clamp(18rem, 32vw, 28rem)`; set `--pmx-source-reference-width` in the
layout to adjust it. On narrow screens references appear in a dismissible sheet.
An icon button reopens the sheet while references exist.

Make the reading layout respond to its own available width, rather than only
the browser width. A wide reference can leave a narrow reading pane on a desktop.
If that pane has a table-of-contents column, collapse the grid along with the
table of contents; hiding its contents alone leaves an empty column.

```css
.workspace > main {
  container: reading / inline-size;
}

@container reading (max-width: 48rem) {
  .reading-grid {
    display: block;
  }

  .reading-grid > .toc {
    display: none;
  }
}
```

Here `.reading-grid` and `.toc` are elements inside `main`. Keep the responsive
rule after the layout's desktop rules so the narrow layout wins.

At viewport widths of 48rem or less, the reference becomes a modal sheet:
the reading page keeps its full width, the resize divider is hidden, and
**Hide references** returns to the lesson without discarding the open documents.
The reopen button restores the sheet and its inputs. Closing a particular
reference removes that document. Desktop/mobile transitions keep the same
mounted documents, so resizing does not erase an in-progress exercise.

Keep reusable content presentation in the Source or its components: for example,
the [bookcase](../../examples/bookcase/index.demo) uses a card component for its
cover and notes, while its page layout supplies site navigation and a back link.
Use container queries when the content needs to adapt to a narrow reference panel.

Opened Sources show their authored content and functioning components without
their standalone site layout. The iframe requests the
[`layout=false` presentation](../site/layouts.reference) of the same Source.
Each has an independent document context, keeping
IDs, inputs, Actions, and live updates separate. Expanded references share the available dock or mobile sheet height. Each document
scrolls independently; collapsed references use only their header height. When
there are too many expanded references to read comfortably, the dock itself scrolls.

---

## Open a Source from browser code

For custom browser behavior, add a control to `paths/lesson.page.md`:

```html
<button id="open-reference" type="button">Open the reference</button>
```

Create `paths/scripts/open-reference.js` and attach it in `paths/lesson.page.md`
with `[@script]: ./scripts/open-reference.js`:

```js
import { openSource } from "@pathmx/core/sources/browser"

document.addEventListener("click", async (event) => {
  const button = event.target instanceof Element
    ? event.target.closest("#open-reference")
    : null
  if (!button) return
  try {
    const opened = await openSource("./reference.page.md", {
      in: "overlay",
      from: button,
    })
    // An owning interaction may later call opened.close().
  } catch (error) {
    console.error("The reference could not be opened.", error)
  }
})
```

The delegated listener also handles the control after a live content update.
Choose it to open the same reference through the browser API.

`in` accepts `reference` or `overlay`. `from` supplies the represented Source for
relative references and the invoking control for focus return. Without `from`,
pass a canonical Source reference or absolute URL.

The promise resolves after the Source mounts and returns an idempotent
`close()` handle. A handle for replaced overlay content cannot dismiss its
replacement. An optional `signal` cancels a pending opening:

```ts
const controller = new AbortController()
const opening = openSource("/reference.page", {
  in: "reference",
  signal: controller.signal,
})

// Cancel while loading if the owning interaction is no longer relevant.
controller.abort()
await opening.catch(() => {})
```

Load failures leave a retry action and an ordinary-link fallback in the surface.
Closing a loading surface cancels its pending opening.

---

## Navigation and lifetime

References are independent: closing one leaves the others in place. Reopening
the same Source and query reveals its existing reference; a different fragment
moves within that reference. Different query parameters can open separate
experiences.

Opening a reference or overlay does not change the main URL or add history
entries. Browser Back navigates the main page. Escape and close controls dismiss
an overlay; on narrow screens Escape also dismisses the reference sheet.

References survive enhanced navigation between layouts that support them. A
layout without the reference component closes them. Reloads and full-document
navigation do not restore references. An overlay closes when the main page
navigates.

Overlays fit their content, with a 16rem minimum height and a viewport limit.
Long Sources scroll inside the overlay; images, live edits, and width changes
update its height automatically.

For a Source that should navigate entirely within its own viewport, use
[`<x-source-embed />`](./components.reference.md#source-embeds) instead.
