@excom/spa-route 0.1.0
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/.rush/temp/chunked-rush-logs/spa-route.apply-exports.chunks.jsonl +1 -0
- package/.rush/temp/chunked-rush-logs/spa-route.build_docs.chunks.jsonl +1 -0
- package/.rush/temp/chunked-rush-logs/spa-route.build_package-metas.chunks.jsonl +1 -0
- package/.rush/temp/operation/apply-exports/all.log +1 -0
- package/.rush/temp/operation/apply-exports/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/apply-exports/state.json +3 -0
- package/.rush/temp/operation/build_docs/all.log +1 -0
- package/.rush/temp/operation/build_docs/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/build_docs/state.json +3 -0
- package/.rush/temp/operation/build_package-metas/all.log +1 -0
- package/.rush/temp/operation/build_package-metas/log-chunks.jsonl +1 -0
- package/.rush/temp/operation/build_package-metas/state.json +3 -0
- package/.rush/temp/shrinkwrap-deps.json +3 -0
- package/config/rig.json +5 -0
- package/index.css +5 -0
- package/index.ts +29 -0
- package/package.json +48 -0
- package/rush-logs/spa-route.apply-exports.cache.log +1 -0
- package/rush-logs/spa-route.apply-exports.log +1 -0
- package/rush-logs/spa-route.build_docs.cache.log +1 -0
- package/rush-logs/spa-route.build_docs.log +1 -0
- package/rush-logs/spa-route.build_package-metas.cache.log +1 -0
- package/rush-logs/spa-route.build_package-metas.log +1 -0
- package/spa-a.ts +124 -0
- package/spa-manager.ts +605 -0
- package/spa-route.ts +322 -0
- package/src/spa-route.css +24 -0
- package/src/utils.ts +31 -0
- package/support/custom-elements.json +953 -0
- package/support/dist-docs/spa-a.md +40 -0
- package/support/dist-docs/spa-manager.md +66 -0
- package/support/dist-docs/spa-route.md +408 -0
- package/support/docs/README.md +314 -0
- package/support/package-meta.json +789 -0
- package/support/tests/spa-navigation.test.ts +1076 -0
- package/support/tests/spa-route.test.ts +766 -0
- package/support/tests/spa-title.test.ts +225 -0
- package/tsconfig.json +5 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# spa-route
|
|
2
|
+
|
|
3
|
+
Build a full SPA from HTML alone — screens, links, and view transitions.
|
|
4
|
+
|
|
5
|
+
> This site is a live demo... inspect its HTML! Other live demos of spa-route coming soon.
|
|
6
|
+
|
|
7
|
+
```html
|
|
8
|
+
<spa-manager>
|
|
9
|
+
<spa-route route-href="/" template-ref="/views/home.html"></spa-route>
|
|
10
|
+
<spa-route route-href="/about" template-ref="/views/about.html"></spa-route>
|
|
11
|
+
</spa-manager>
|
|
12
|
+
|
|
13
|
+
<nav>
|
|
14
|
+
<spa-a route-href="/">Home</spa-a>
|
|
15
|
+
<spa-a route-href="/about">About</spa-a>
|
|
16
|
+
</nav>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- **Pure CSS View Transitions** Write CSS, get beautiful animations between routes
|
|
22
|
+
- **Active / was-active** Style current and outgoing links & screens (nav chrome, card expansion)
|
|
23
|
+
- **Same-route reuse / refresh** Keep or rebuild the view when only params change
|
|
24
|
+
- **Scroll reset / restore** Per-axis control for push, replace, back, forward
|
|
25
|
+
- **Nested layouts** Keep a parent route mounted under child paths
|
|
26
|
+
- **404 fallbacks** Catch-alls that only fire when nothing else matched
|
|
27
|
+
- **Per-route document title** `document.title` follows the active route
|
|
28
|
+
- **History actions** Push, replace, back, forward from a link
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
<include-content is-active template-ref="/views/install-section/install-section.html"></include-content>
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
Wrap screens in `<spa-manager>`, give each `<spa-route>` a `route-href`, and link with `<spa-a>`.
|
|
37
|
+
|
|
38
|
+
```html
|
|
39
|
+
<!-- Optional SPA manager for View Transitions and batched router config -->
|
|
40
|
+
<spa-manager>
|
|
41
|
+
<!-- SPA routing -->
|
|
42
|
+
<spa-route route-href="/" template-ref="/views/home.html"></spa-route>
|
|
43
|
+
<spa-route route-href="/about" template-ref="/views/about.html"></spa-route>
|
|
44
|
+
</spa-manager>
|
|
45
|
+
<nav>
|
|
46
|
+
<!-- SPA links -->
|
|
47
|
+
<spa-a route-href="/">Home</spa-a>
|
|
48
|
+
<spa-a route-href="/about">About</spa-a>
|
|
49
|
+
</nav>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### API Reference
|
|
53
|
+
|
|
54
|
+
<include-content is-active template-ref="/views/api-reference/api-reference.html"></include-content>
|
|
55
|
+
|
|
56
|
+
### Examples
|
|
57
|
+
|
|
58
|
+
#### Minimal SPA
|
|
59
|
+
|
|
60
|
+
Three routes, three links. Active links style via `spa-a[is-active]`.
|
|
61
|
+
|
|
62
|
+
```html
|
|
63
|
+
<spa-manager>
|
|
64
|
+
<nav>
|
|
65
|
+
<spa-a route-href="/home">Home</spa-a>
|
|
66
|
+
<spa-a route-href="/users">Users</spa-a>
|
|
67
|
+
<spa-a route-href="/about">About</spa-a>
|
|
68
|
+
<spa-a route-href="/contact">Contact</spa-a>
|
|
69
|
+
</nav>
|
|
70
|
+
<spa-route route-href="/home">
|
|
71
|
+
<template>
|
|
72
|
+
<h3>Welcome</h3>
|
|
73
|
+
<p>Mounted because the URL matched <code>/home</code>.</p>
|
|
74
|
+
</template>
|
|
75
|
+
</spa-route>
|
|
76
|
+
<spa-route route-href="/users">
|
|
77
|
+
<template>
|
|
78
|
+
<h3>Users</h3>
|
|
79
|
+
<ul>
|
|
80
|
+
<li>Adam</li>
|
|
81
|
+
<li>Linus</li>
|
|
82
|
+
<li>Grace</li>
|
|
83
|
+
</ul>
|
|
84
|
+
</template>
|
|
85
|
+
</spa-route>
|
|
86
|
+
<spa-route route-href="/about" template-ref="/this/view/is/remote.html"></spa-route>
|
|
87
|
+
<spa-route route-href="/contact" template-ref="#this-view-is-dom-selected"></spa-route>
|
|
88
|
+
</spa-manager>
|
|
89
|
+
|
|
90
|
+
<template id="this-view-is-dom-selected">foo@bar.com</template>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
```css
|
|
94
|
+
spa-a[is-active] {
|
|
95
|
+
font-weight: bold;
|
|
96
|
+
pointer-events: none;
|
|
97
|
+
text-decoration: none;
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
#### Nested layout & 404
|
|
102
|
+
|
|
103
|
+
`match-nested` keeps a layout mounted under child paths. `is-fallback` with `route-regex=".*"` is a 404 that only activates when no preceding sibling matched.
|
|
104
|
+
|
|
105
|
+
```html
|
|
106
|
+
<spa-manager>
|
|
107
|
+
<nav>
|
|
108
|
+
<spa-a route-href="/users">Users list</spa-a>
|
|
109
|
+
<spa-a route-href="/users/42">User 42</spa-a>
|
|
110
|
+
<spa-a route-href="/missing">Missing page</spa-a>
|
|
111
|
+
</nav>
|
|
112
|
+
<spa-route route-href="/users" match-nested>
|
|
113
|
+
<template>
|
|
114
|
+
<section>
|
|
115
|
+
<h3>Users layout</h3>
|
|
116
|
+
<spa-manager>
|
|
117
|
+
<spa-route route-href="/users">
|
|
118
|
+
<template><p>List of users.</p></template>
|
|
119
|
+
</spa-route>
|
|
120
|
+
<spa-route route-href="/users/:id">
|
|
121
|
+
<template><p>Detail for a single user.</p></template>
|
|
122
|
+
</spa-route>
|
|
123
|
+
</spa-manager>
|
|
124
|
+
</section>
|
|
125
|
+
</template>
|
|
126
|
+
</spa-route>
|
|
127
|
+
<spa-route route-regex=".*?view=admin.*" template-ref="/views/admin-sidebar.html"></spa-route>
|
|
128
|
+
<spa-route route-regex=".*" is-fallback>
|
|
129
|
+
<template>
|
|
130
|
+
<section>
|
|
131
|
+
<h3>404</h3>
|
|
132
|
+
<p>Catch-all — only when no preceding sibling matched.</p>
|
|
133
|
+
</section>
|
|
134
|
+
</template>
|
|
135
|
+
</spa-route>
|
|
136
|
+
</spa-manager>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
#### Document title
|
|
140
|
+
|
|
141
|
+
`document-title` sets `document.title` while its route is active. It keys off
|
|
142
|
+
activation, not clicks, so cold loads and back / forward retitle too. The
|
|
143
|
+
outermost `<spa-manager>` applies the last active route carrying one — a
|
|
144
|
+
nested route beats its ancestor — and restores the page's own `<title>` once
|
|
145
|
+
no active route has a title.
|
|
146
|
+
|
|
147
|
+
```html
|
|
148
|
+
<title>Nucleus · docs</title>
|
|
149
|
+
|
|
150
|
+
<spa-manager>
|
|
151
|
+
<spa-route route-href="/" document-title="My company">
|
|
152
|
+
<template><p>The company page.</p></template>
|
|
153
|
+
</spa-route>
|
|
154
|
+
<!-- untitled: the page's own <title> comes back -->
|
|
155
|
+
<spa-route route-href="/docs">
|
|
156
|
+
<template><p>The docs.</p></template>
|
|
157
|
+
</spa-route>
|
|
158
|
+
</spa-manager>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### History actions
|
|
162
|
+
|
|
163
|
+
`route-action="back"` / `"forward"` walk history; `"replace"` swaps the current entry instead of pushing.
|
|
164
|
+
|
|
165
|
+
```html
|
|
166
|
+
<spa-manager>
|
|
167
|
+
<nav>
|
|
168
|
+
<spa-a route-action="back">‹ Back</spa-a>
|
|
169
|
+
<spa-a route-action="forward">Forward ›</spa-a>
|
|
170
|
+
<spa-a route-href="/one">Push /one</spa-a>
|
|
171
|
+
<spa-a route-href="/two">Push /two</spa-a>
|
|
172
|
+
<spa-a route-href="/login" route-action="replace">
|
|
173
|
+
Replace with /login
|
|
174
|
+
</spa-a>
|
|
175
|
+
</nav>
|
|
176
|
+
<spa-route route-href="/one">
|
|
177
|
+
<template><p>You're on <code>/one</code>.</p></template>
|
|
178
|
+
</spa-route>
|
|
179
|
+
<spa-route route-href="/two">
|
|
180
|
+
<template><p>You're on <code>/two</code>.</p></template>
|
|
181
|
+
</spa-route>
|
|
182
|
+
<spa-route route-href="/login">
|
|
183
|
+
<template>
|
|
184
|
+
<p>You're on <code>/login</code> — this entry replaced the
|
|
185
|
+
previous one in history.</p>
|
|
186
|
+
</template>
|
|
187
|
+
</spa-route>
|
|
188
|
+
</spa-manager>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
#### View Transitions
|
|
192
|
+
|
|
193
|
+
`<spa-manager>` wraps each navigation in `document.startViewTransition()` (when supported). Style with `::view-transition-*`; set per-link types via `transition-types` (e.g. card expansion); opt a route out with `no-transition`.
|
|
194
|
+
|
|
195
|
+
```css
|
|
196
|
+
::view-transition-old(root),
|
|
197
|
+
::view-transition-new(root) {
|
|
198
|
+
animation-duration: 0.25s;
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
#### View Transitions - Localized
|
|
203
|
+
If you had a list of cards, and clicking on one expanded it to the detail view (and vice versa, contracting), you would achieve it similarly to the code example below. This technique relies on styling the `<spa-a>` with its `[is-active]` (incoming view) and `[was-active]` (outgoing view).
|
|
204
|
+
|
|
205
|
+
```html
|
|
206
|
+
<spa-manager>
|
|
207
|
+
<spa-route id="route-list" route-href="/list">
|
|
208
|
+
<template>
|
|
209
|
+
<spa-a route-href="/detail/123" transition-types="card-morph" class="mini-card">
|
|
210
|
+
Go to detail
|
|
211
|
+
</spa-a>
|
|
212
|
+
</template>
|
|
213
|
+
</spa-route>
|
|
214
|
+
<spa-route id="route-detail" route-href="/detail/:id">
|
|
215
|
+
<template>
|
|
216
|
+
<article id="detail-card" class="card">
|
|
217
|
+
<!-- other content here -->
|
|
218
|
+
</article>
|
|
219
|
+
</template>
|
|
220
|
+
</spa-route>
|
|
221
|
+
</spa-manager>
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
```css
|
|
225
|
+
html:active-view-transition-type(card-morph) {
|
|
226
|
+
#route-list spa-a[transition-types="card-morph"][was-active], /* outgoing list card (forward) */
|
|
227
|
+
#route-list spa-a[transition-types="card-morph"][is-active], /* incoming list card (back) */
|
|
228
|
+
#detail-card /* detail card (forward & back) */ {
|
|
229
|
+
contain: layout;
|
|
230
|
+
height: fit-content;
|
|
231
|
+
view-transition-name: card-morph;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
::view-transition-old(card-morph),
|
|
235
|
+
::view-transition-new(card-morph) {
|
|
236
|
+
mix-blend-mode: normal;
|
|
237
|
+
height: 100%;
|
|
238
|
+
width: 100%;
|
|
239
|
+
will-change: opacity;
|
|
240
|
+
animation-fill-mode: both;
|
|
241
|
+
}
|
|
242
|
+
::view-transition-old(card-morph) {
|
|
243
|
+
animation-name: fade-out 1s ease;
|
|
244
|
+
}
|
|
245
|
+
::view-transition-new(card-morph) {
|
|
246
|
+
animation-name: fade-in 1s ease;
|
|
247
|
+
}
|
|
248
|
+
@keyframes fade-in {
|
|
249
|
+
from { opacity: 0; }
|
|
250
|
+
to { opacity: 1; }
|
|
251
|
+
}
|
|
252
|
+
@keyframes fade-out {
|
|
253
|
+
from { opacity: 1; }
|
|
254
|
+
to { opacity: 0; }
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
#### Touch edge-swipe
|
|
259
|
+
On touch devices, horizontal drags from within `overscroll-x-threshold` of an edge trigger back / forward. Use `"none"` to block overscroll without navigating. Useful for preventing native swipes in Safari, which visually break SPAs.
|
|
260
|
+
|
|
261
|
+
```html
|
|
262
|
+
<spa-manager overscroll-behavior-x="navigate"></spa-manager>
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
#### Scroll reset / restore
|
|
267
|
+
|
|
268
|
+
By default, `<spa-route>`:
|
|
269
|
+
- resets scroll to top-left on `push` / `replace`
|
|
270
|
+
- restores the saved scroll position on `back` / `forward`
|
|
271
|
+
|
|
272
|
+
Override per axis with `scroll-reset-y` / `scroll-reset-x` — space-separated moves that should reset to `0` (omitted moves restore instead):
|
|
273
|
+
|
|
274
|
+
```html
|
|
275
|
+
<!-- also reset Y when the user hits back -->
|
|
276
|
+
<spa-route
|
|
277
|
+
route-href="/article/:id"
|
|
278
|
+
scroll-reset-y="push replace back"
|
|
279
|
+
></spa-route>
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Animate with `scroll-reset-behavior="smooth"`. Disable all scroll handling with `scroll-set-disabled`.
|
|
283
|
+
|
|
284
|
+
#### Same-route params
|
|
285
|
+
|
|
286
|
+
When the matched route stays the same but params change (e.g. `/users/1` → `/users/2`):
|
|
287
|
+
|
|
288
|
+
- `same-route="reuse"` (default) — keep the rendered tree, update route data, and let `<spa-manager>` run a View Transition
|
|
289
|
+
- `same-route="refresh"` — tear down and re-render the view
|
|
290
|
+
|
|
291
|
+
```html
|
|
292
|
+
<spa-route route-href="/users/:id" same-route="refresh">
|
|
293
|
+
<template><!-- fresh tree per user id --></template>
|
|
294
|
+
</spa-route>
|
|
295
|
+
|
|
296
|
+
<spa-route
|
|
297
|
+
route-href="/logs/:view"
|
|
298
|
+
same-route="reuse"
|
|
299
|
+
scroll-set-disabled
|
|
300
|
+
>
|
|
301
|
+
<template><!-- preserve content + scroll across view tabs --></template>
|
|
302
|
+
</spa-route>
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
#### Transition delay
|
|
306
|
+
|
|
307
|
+
`transition-delay` on `<spa-manager>` waits N ms before starting the batched View Transition — useful when sibling routes need a beat to queue their render/unrender callbacks, or for last-second DOM work.
|
|
308
|
+
|
|
309
|
+
```html
|
|
310
|
+
<spa-manager transition-delay="50">
|
|
311
|
+
<!-- routes -->
|
|
312
|
+
</spa-manager>
|
|
313
|
+
```
|
|
314
|
+
|