@waveso/docs 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # @waveso/docs
2
2
 
3
+ ## 0.7.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 43af6e9: The back-to-top link appears when there is something to go back to.
8
+
9
+ It sat at the foot of the table of contents on every page, including at the top
10
+ of one, offering to return a reader to where they already were. It now fades in
11
+ between 25dvh and 35dvh of scroll and fades back out on the way up.
12
+
13
+ No JavaScript was added to do it. The reveal is a scroll-driven animation, so
14
+ scroll position alone drives it — no listener, no state, no re-render per
15
+ frame, and correct before the component has hydrated. `DocsToc` is the smallest
16
+ client component this package ships and it has not grown by a byte.
17
+
18
+ `visibility` moves with the fade, so the link leaves the tab order while it is
19
+ invisible rather than sitting there as a focus target nobody can see — and it
20
+ rejoins only once it is legible, not at the first pixel of the fade.
21
+
22
+ Where the timeline cannot run the link is simply always present, exactly as it
23
+ was: Firefox has not shipped scroll-driven animations, a page too short to
24
+ scroll leaves the timeline inactive, and so does a host that scrolls an inner
25
+ pane rather than the document. Nothing hides a control on the strength of a
26
+ feature the engine did not run.
27
+
3
28
  ## 0.7.0
4
29
 
5
30
  ### Minor Changes
package/README.md CHANGED
@@ -212,7 +212,7 @@ Every component takes data as props, and every module that imports from `next/*`
212
212
  | `SkipLink` | `react/skip-link` | Targets `docs.Page`'s `<main>`; `DOCS_CONTENT_ID` is that id. `docs.Layout` renders one |
213
213
  | `createMarkdownComponents` | `react/markdown-components` | The element → component map. `defaultMarkdownComponents` is the unwired one |
214
214
 
215
- `DocsToc`'s `rootMargin` is the `IntersectionObserver` margin that decides how far above the viewport a heading counts as current; the default keeps the highlight on the section you are reading rather than the one about to arrive. `topLabel` is the back-to-top link at the end.
215
+ `DocsToc`'s `rootMargin` is the `IntersectionObserver` margin that decides how far above the viewport a heading counts as current; the default keeps the highlight on the section you are reading rather than the one about to arrive. `topLabel` is the back-to-top link at the end — it fades in once the reader is about a third of a screen down and fades out again on the way back, on a scroll timeline rather than a scroll listener, so the component ships no extra bytes to do it. Where that timeline cannot run — an engine without scroll-driven animations, a page too short to scroll, or a host that scrolls an inner pane rather than the document — the link is simply always there.
216
216
 
217
217
  The two components the adapter injects take a little more than an `<a>` and an `<img>`. `DocsLinkProps` adds `prefetch` — passed straight to `next/link`, where `false` disables the hover and viewport paths both, so it is a stronger switch in the App Router than the name suggests. `DocsImageProps` carries `src`, `alt`, `width` and `height` — the four `next/image` refuses to render without — and adds `sizes`, `loading`, `decoding` and `fetchPriority`, forwarded to it; markdown carries none of them, so they come from your `imageResolver` or from a `components` override. `decoding` defaults to `async`, and `loading` to `lazy` — except on an image the author marked `eager`, which is usually the page's largest element.
218
218
 
package/dist/styles.css CHANGED
@@ -1725,6 +1725,89 @@
1725
1725
  color: var(--wave-docs-fg);
1726
1726
  }
1727
1727
 
1728
+ /*
1729
+ * ⚠️ IT ONLY APPEARS ONCE THERE IS SOMETHING TO GO BACK TO, AND NOTHING IN
1730
+ * JAVASCRIPT DECIDES THAT.
1731
+ *
1732
+ * A scroll-driven animation, so scroll position alone drives it: no listener,
1733
+ * no state, no re-render on every frame of a scroll, and it is right before
1734
+ * hydration rather than after it. `toc` is the smallest client component this
1735
+ * package ships — 900 bytes, and its budget note exists to keep it that way —
1736
+ * so the alternative was to grow the one bundle that should never grow, in
1737
+ * order to recompute a number the compositor already has.
1738
+ *
1739
+ * ⚠️ `root`, NOT `nearest`. Above 80rem `.wave-docs-layout__toc` is itself a
1740
+ * scroll container (`overflow-y: auto`, so a long column of headings scrolls
1741
+ * in place), and `nearest` resolves to it. The animation would then track how
1742
+ * far the reader had scrolled the *table of contents*, which on almost every
1743
+ * page never scrolls at all — an inactive timeline, and a link that never
1744
+ * appears. Same shape as the `nearest`/`self` trap on the table shadow above,
1745
+ * arrived at from the opposite direction.
1746
+ *
1747
+ * ⚠️ AND THE RULE ABOVE IS THE FALLBACK, WHICH IS WHY NOTHING HERE OVERRIDES
1748
+ * IT. Three cases collapse into one: Firefox does not match this `@supports`
1749
+ * yet; a page too short to scroll leaves the timeline inactive; and so does a
1750
+ * host that scrolls an inner pane rather than the document. In all three the
1751
+ * link is simply always present, exactly as it was before this block existed.
1752
+ * Nothing hides a control on the strength of a feature the engine did not run.
1753
+ *
1754
+ * `dvh` rather than `px`: the link is redundant while the top of the document
1755
+ * is still on screen, and "still on screen" is a fraction of the viewport
1756
+ * rather than a constant. On a 900px window it fades in between 225px and
1757
+ * 315px of scroll. `dvh` because this sheet uses no `vh` anywhere, and the
1758
+ * reason it does not — the retracted mobile URL bar — costs nothing here: the
1759
+ * column this link lives in is `display: none` below 80rem.
1760
+ *
1761
+ * No `prefers-reduced-motion` gate, deliberately. That setting is about things
1762
+ * that move under a reader who did not ask them to; this is opacity, and the
1763
+ * reader's own scrolling is its clock.
1764
+ */
1765
+ @supports (animation-timeline: scroll()) {
1766
+ .wave-docs-toc__top {
1767
+ animation-timeline: scroll(root block);
1768
+ animation-timing-function: linear;
1769
+ animation-fill-mode: both;
1770
+ animation-name: wave-docs-toc-top-reveal;
1771
+ animation-range: 25dvh 35dvh;
1772
+ }
1773
+ }
1774
+
1775
+ /*
1776
+ * ⚠️ `visibility` KEEPS A LINK NOBODY CAN SEE OUT OF THE TAB ORDER, AND THE
1777
+ * MIDDLE FRAME IS WHAT DECIDES WHEN IT REJOINS.
1778
+ *
1779
+ * `opacity` alone leaves a fully focusable, fully clickable link sitting
1780
+ * invisibly at the foot of the rail: Tab reaches it and the focus ring is
1781
+ * drawn around nothing.
1782
+ *
1783
+ * `visibility` interpolates as a step with one exception — across an interval
1784
+ * where *either* endpoint is `visible` it is `visible` the whole way. Two
1785
+ * frames would therefore flip it one pixel past the threshold and hand the
1786
+ * link back to the tab order at an opacity of almost zero, which is the same
1787
+ * defect a frame later. Two adjacent `hidden` frames have no `visible`
1788
+ * endpoint between them, the exception does not apply, and the step lands at
1789
+ * 60% — where the link is legible.
1790
+ *
1791
+ * That middle frame sits exactly on the linear ramp, so it changes the fade
1792
+ * by nothing at all. Being the second `hidden` is its whole job.
1793
+ */
1794
+ @keyframes wave-docs-toc-top-reveal {
1795
+ 0% {
1796
+ opacity: 0;
1797
+ visibility: hidden;
1798
+ }
1799
+
1800
+ 60% {
1801
+ opacity: 0.6;
1802
+ visibility: hidden;
1803
+ }
1804
+
1805
+ 100% {
1806
+ opacity: 1;
1807
+ visibility: visible;
1808
+ }
1809
+ }
1810
+
1728
1811
  .wave-docs-sidebar__external {
1729
1812
  /* Baseline-ish against the label, and never a flex item that grows. */
1730
1813
  margin-inline-start: 0.25rem;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@waveso/docs",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Zero parser bytes in the browser: markdown docs for Next.js, built to hast in Node and rendered as your components",
5
5
  "type": "module",
6
6
  "sideEffects": [