scavold 0.2.1 → 0.4.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/CHANGELOG.md +64 -1
- package/COMPONENTS.md +263 -0
- package/components/ScavoldCard.vue +51 -0
- package/components/ScavoldDetails.vue +23 -0
- package/components/ScavoldGrid.vue +53 -0
- package/components/ScavoldImage.vue +13 -4
- package/composables/useCard.js +45 -0
- package/composables/useDetails.js +43 -0
- package/composables/useGrid.js +40 -0
- package/composables/useImageSizes.js +42 -0
- package/icons/float-left.svg +1 -0
- package/icons/float-right.svg +1 -0
- package/icons/split-left.svg +1 -0
- package/icons/split-right.svg +1 -0
- package/icons/stacked.svg +1 -0
- package/l10n/de.json +6 -0
- package/l10n/en.json +6 -0
- package/lib/config.js +15 -22
- package/lib/containers.js +6 -0
- package/lib/figures.js +99 -0
- package/lib/icons.js +197 -0
- package/lib/index.d.ts +11 -1
- package/lib/index.js +10 -1
- package/lib/sectionManifest.js +488 -74
- package/package.json +4 -1
- package/scripts/build-icons.js +27 -0
- package/scripts/check-fixture.js +101 -3
- package/styles/layouts.css +64 -0
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,67 @@ one offered; a patch release does not.
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [0.4.0] — 2026-10-04
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Layout variants for `section`, `article` and `aside`: `layout=float-left` or
|
|
17
|
+
`float-right` lets the content flow around the first block, an image usually;
|
|
18
|
+
`split-left` or `split-right` sets block and content side by side. The editor offers
|
|
19
|
+
them as buttons with icons Scavold brings along, drawn after Font Awesome's solid
|
|
20
|
+
style; a site replaces one by keeping an icon of the same name in `.cratly/icons/`.
|
|
21
|
+
- The section kinds Scavold brings are described to the editor in English and German —
|
|
22
|
+
their names, hints and the choices of their options — so a German editor no longer
|
|
23
|
+
reads "Preload" or "metadata". A site's `.cratly.config.yaml` may give labels and
|
|
24
|
+
hints in several languages the same way, as a map of locale → text.
|
|
25
|
+
- Each section kind Scavold brings explains itself where the editor offers the types to
|
|
26
|
+
choose from, and links its description on a new page for authors at scavold.io — the
|
|
27
|
+
first of the documentation in German as well as English. A site's own containers do
|
|
28
|
+
the same with `hint` and `docs` in `.cratly.config.yaml`.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- The section-type manifest carries the texts of the built-in section kinds as maps of
|
|
33
|
+
locale → text, and their `docs` as maps of locale → address. A cratly editor deployed
|
|
34
|
+
before these were supported shows such a text as "[object Object]": update the editor
|
|
35
|
+
before a site moves to this version.
|
|
36
|
+
|
|
37
|
+
## [0.3.0] — 2026-10-04
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- A `card` container: a unit of its own, such as an image, a heading and a few lines.
|
|
42
|
+
`link` names a page and makes the whole card clickable, its "Read more" naming the
|
|
43
|
+
page for screen readers; links written into the card stay clickable.
|
|
44
|
+
- A `details` container: content collapsed under a title (`summary`) until the reader
|
|
45
|
+
expands it, rendered as a native `<details>`. `open` starts it expanded, and blocks
|
|
46
|
+
sharing a `group` close each other, so only one of them is open at a time.
|
|
47
|
+
- A `grid` container: the blocks written inside it side by side, at most `columns` (2,
|
|
48
|
+
3 or 4) in a row, continuing in the next. A cell never gets narrower than
|
|
49
|
+
`--scavold-grid-min`, so a narrow screen drops columns by itself without breakpoints.
|
|
50
|
+
A theme declares the `sizes` of images in a grid by column count through
|
|
51
|
+
`enhanceApp( context, { gridImageSizes } )`.
|
|
52
|
+
- `provideImageSizes()`: a layout component gives the images inside it a `sizes`
|
|
53
|
+
value, replacing the site's default but not a per-image override.
|
|
54
|
+
- Image captions. The title of a Markdown image is its caption: an image standing alone
|
|
55
|
+
in its paragraph becomes a `<figure>` with a `<figcaption>`, one amid text shows it as
|
|
56
|
+
its tooltip. A grid of captioned images makes a gallery.
|
|
57
|
+
- Icons for section controls. A `boolean` prop and the choices of an `enum` prop in
|
|
58
|
+
`.cratly.config.yaml` may name an `icon`, kept as `.cratly/icons/<name>.svg` in the
|
|
59
|
+
site. The section-type manifest embeds the icons it refers to, so the cratly editor
|
|
60
|
+
can offer a toggle button or a group of buttons instead of a checkbox or a select. A
|
|
61
|
+
reference to a missing icon is reported during the build.
|
|
62
|
+
|
|
63
|
+
### Changed
|
|
64
|
+
|
|
65
|
+
- The title of a Markdown image is shown: below an image standing alone, as its tooltip
|
|
66
|
+
amid text. A site that used image titles for something else will see them now.
|
|
67
|
+
|
|
68
|
+
### Fixed
|
|
69
|
+
|
|
70
|
+
- A `sizes=` override in an image title no longer shows up as a tooltip
|
|
71
|
+
("sizes=100vw") on the published image.
|
|
72
|
+
|
|
12
73
|
## [0.2.1] — 2026-10-04
|
|
13
74
|
|
|
14
75
|
### Fixed
|
|
@@ -369,7 +430,9 @@ First published release. Version `0.1.0` existed in-tree only.
|
|
|
369
430
|
- Only images are processed out of `media_folder`; other file types (video, documents)
|
|
370
431
|
are never copied into the build output and have to live in the static folder.
|
|
371
432
|
|
|
372
|
-
[Unreleased]: https://gitlab.com/cratly/scavold/-/compare/v0.
|
|
433
|
+
[Unreleased]: https://gitlab.com/cratly/scavold/-/compare/v0.4.0...main
|
|
434
|
+
[0.4.0]: https://gitlab.com/cratly/scavold/-/compare/v0.3.0...v0.4.0
|
|
435
|
+
[0.3.0]: https://gitlab.com/cratly/scavold/-/compare/v0.2.1...v0.3.0
|
|
373
436
|
[0.2.1]: https://gitlab.com/cratly/scavold/-/compare/v0.2.0...v0.2.1
|
|
374
437
|
[0.2.0]: https://gitlab.com/cratly/scavold/-/compare/v0.2.0-rc.12...v0.2.0
|
|
375
438
|
[0.2.0-rc.12]: https://gitlab.com/cratly/scavold/-/compare/v0.2.0-rc.11...v0.2.0-rc.12
|
package/COMPONENTS.md
CHANGED
|
@@ -69,6 +69,48 @@ A folder on the way that has no `index.md` is named as text, a `<span>` in place
|
|
|
69
69
|
|
|
70
70
|
---
|
|
71
71
|
|
|
72
|
+
### `<ScavoldCard>`
|
|
73
|
+
|
|
74
|
+
A unit of its own — an image, a heading and a few lines, say — set apart from the text
|
|
75
|
+
around it. Cards side by side go into a [grid](#scavoldgrid):
|
|
76
|
+
|
|
77
|
+
```markdown
|
|
78
|
+
:::: grid columns=3
|
|
79
|
+
::: card link=about/team.md
|
|
80
|
+

|
|
81
|
+
|
|
82
|
+
### Who we are
|
|
83
|
+
|
|
84
|
+
Twelve people, one pond.
|
|
85
|
+
:::
|
|
86
|
+
::::
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
| Argument | Type | Description |
|
|
90
|
+
|---|---|---|
|
|
91
|
+
| `link` | `page-ref` | Page the card links to. Makes the whole card clickable; a reference to a page that does not exist leaves the card unlinked. |
|
|
92
|
+
|
|
93
|
+
```html
|
|
94
|
+
<div class="scavold-card scavold-card--linked">
|
|
95
|
+
…
|
|
96
|
+
<a class="scavold-card__link" href="/about/team.html" aria-label="Read more: Team">Read more</a>
|
|
97
|
+
</div>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The link's text is the same on every card, translated; its accessible name adds the
|
|
101
|
+
title of the page it leads to, so a list of links read out by a screen reader tells the
|
|
102
|
+
cards apart. Its click area is stretched over the whole card, while links written into
|
|
103
|
+
the card stay clickable on top.
|
|
104
|
+
|
|
105
|
+
Scavold styles only what keeps a card working: images stay within it, and the link
|
|
106
|
+
covers it. Its frame, padding and where its image sits are the theme's, through
|
|
107
|
+
`scavold-card` and `scavold-card--linked`.
|
|
108
|
+
|
|
109
|
+
Register a component under the name `ScavoldCard` before calling `scavoldEnhanceApp` to
|
|
110
|
+
render it differently, and resolve the arguments with [`useCard`](#usecard-props).
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
72
114
|
### `<ScavoldContainer>`
|
|
73
115
|
|
|
74
116
|
Fallback component for Markdown container blocks whose name has no dedicated
|
|
@@ -249,6 +291,94 @@ not promise.
|
|
|
249
291
|
|
|
250
292
|
---
|
|
251
293
|
|
|
294
|
+
### `<ScavoldDetails>`
|
|
295
|
+
|
|
296
|
+
Content collapsed under a title until the reader expands it. Written as a container
|
|
297
|
+
block and rendered as a native `<details>` element, so keyboard operation, the expanded
|
|
298
|
+
state for screen readers and revealing the content on find in page come with it.
|
|
299
|
+
|
|
300
|
+
```markdown
|
|
301
|
+
::: details summary="Opening hours" group=faq
|
|
302
|
+
Monday to Friday, 9 am to 5 pm.
|
|
303
|
+
:::
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
| Argument | Type | Description |
|
|
307
|
+
|---|---|---|
|
|
308
|
+
| `summary` | `string` | Title, always visible; clicking it expands or collapses the content. Left out, a translated "Details" stands in. |
|
|
309
|
+
| `open` | flag | Starts expanded. |
|
|
310
|
+
| `group` | `string` | Blocks with the same group close each other, so only one is expanded at a time. Rendered as the `name` attribute of `<details>`. |
|
|
311
|
+
|
|
312
|
+
```html
|
|
313
|
+
<details class="scavold-details" name="faq">
|
|
314
|
+
<summary class="scavold-details__summary">Opening hours</summary>
|
|
315
|
+
<div class="scavold-details__content">…</div>
|
|
316
|
+
</details>
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
Scavold adds no styles of its own; the browser's disclosure marker stays until a theme
|
|
320
|
+
replaces it. Register a component under the name `ScavoldDetails` before calling
|
|
321
|
+
`scavoldEnhanceApp` to render it differently, and resolve the arguments with
|
|
322
|
+
[`useDetails`](#usedetails-props).
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
### `<ScavoldGrid>`
|
|
327
|
+
|
|
328
|
+
Puts the blocks written inside it side by side, at most `columns` of them in a row; a
|
|
329
|
+
full row continues in the next. Each block is one cell — a paragraph, an image, or an
|
|
330
|
+
embedded container, which is how several blocks share a cell:
|
|
331
|
+
|
|
332
|
+
```markdown
|
|
333
|
+
:::: grid columns=3
|
|
334
|
+

|
|
335
|
+
|
|
336
|
+

|
|
337
|
+
|
|
338
|
+
::: section
|
|
339
|
+
### Opening hours
|
|
340
|
+
Monday to Friday.
|
|
341
|
+
:::
|
|
342
|
+
::::
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
The grid needs the longer fence when it contains a container; the cratly editor writes
|
|
346
|
+
it that way by itself.
|
|
347
|
+
|
|
348
|
+
| Argument | Type | Description |
|
|
349
|
+
|---|---|---|
|
|
350
|
+
| `columns` | `2` \| `3` \| `4` | Most cells side by side. Defaults to `2`. |
|
|
351
|
+
|
|
352
|
+
```html
|
|
353
|
+
<div class="scavold-grid scavold-grid--3" style="--scavold-grid-columns: 3">…</div>
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
A cell never gets narrower than `--scavold-grid-min` (default `14rem`), so on a narrow
|
|
357
|
+
screen the grid drops columns by itself, down to one, without breakpoints. Themes set
|
|
358
|
+
that variable and `--scavold-grid-gap` (default `1.5rem`), or replace the layout
|
|
359
|
+
through the `scavold-grid--<columns>` class. Margins of the cells themselves —
|
|
360
|
+
including the indent browsers give a `<figure>` — are removed; the gap spaces them.
|
|
361
|
+
|
|
362
|
+
Images in a grid take the `sizes` the theme declares for its column count, since only
|
|
363
|
+
the theme knows how wide a cell is at which screen width. A column count it leaves out
|
|
364
|
+
keeps the site's `image_sizes` of `.cratly.config.yaml` for its images: sharp in every column, but on a wide screen
|
|
365
|
+
a larger variant than a cell needs. An image with a `sizes=` override of its own keeps
|
|
366
|
+
it, and images nested deeper — in a card in a cell — take the grid's value as well.
|
|
367
|
+
|
|
368
|
+
```js
|
|
369
|
+
// .vitepress/theme/index.js — cells 22rem wide in a 72rem column, two columns from 48rem
|
|
370
|
+
await scavoldEnhanceApp( context, {
|
|
371
|
+
gridImageSizes: {
|
|
372
|
+
3: "(min-width: 72rem) 22rem, (min-width: 48rem) 50vw, 100vw",
|
|
373
|
+
},
|
|
374
|
+
} );
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
Register a component under the name `ScavoldGrid` before calling `scavoldEnhanceApp`
|
|
378
|
+
to render it differently, and resolve the arguments with [`useGrid`](#usegrid-props).
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
252
382
|
### `<ScavoldImage>`
|
|
253
383
|
|
|
254
384
|
Renders a responsive `<picture>` element for local images. Not intended for direct
|
|
@@ -264,6 +394,7 @@ frontmatter field can use it directly.
|
|
|
264
394
|
| `srcset` | `string` | `""` | `srcset` string for the original format (JPEG/PNG) |
|
|
265
395
|
| `webp-srcset` | `string` | `""` | `srcset` string for the WebP variants |
|
|
266
396
|
| `sizes` | `string` | `"100vw"` | CSS `sizes` attribute applied to all sources |
|
|
397
|
+
| `sizes-fallback` | `boolean` | `false` | Marks `sizes` as the site's default only, which a layout around the image may replace (see [`provideImageSizes`](#useimagesizes--provideimagesizes-sizes)). Set by the Markdown renderer. |
|
|
267
398
|
| `alt` | `string` | `""` | Alt text for the `<img>` element |
|
|
268
399
|
| `loading` | `string` | `"lazy"` | `loading` attribute of the `<img>`; `"eager"` for an image at the top of a page |
|
|
269
400
|
| `fetchpriority` | `string` | — | `fetchpriority` attribute of the `<img>`, e.g. `"high"` for the largest image above the fold |
|
|
@@ -278,6 +409,30 @@ frontmatter field can use it directly.
|
|
|
278
409
|
</picture>
|
|
279
410
|
```
|
|
280
411
|
|
|
412
|
+
#### Captions
|
|
413
|
+
|
|
414
|
+
The title of a Markdown image is its caption. An image standing alone in its paragraph
|
|
415
|
+
is rendered as a figure with the caption below it; amid text, where no figure fits, the
|
|
416
|
+
caption is the image's tooltip. A `sizes=` override in the title is taken out first and
|
|
417
|
+
never reaches the page:
|
|
418
|
+
|
|
419
|
+
```markdown
|
|
420
|
+

|
|
421
|
+

|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
```html
|
|
425
|
+
<figure class="scavold-figure">
|
|
426
|
+
<picture>…</picture>
|
|
427
|
+
<figcaption class="scavold-figure__caption">The pond in spring</figcaption>
|
|
428
|
+
</figure>
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
The alt text says what the image shows, for those who cannot see it; the caption adds
|
|
432
|
+
to it for everyone. Scavold adds no styles to a figure, so the indent browsers give
|
|
433
|
+
`<figure>` stays until a theme sets its own margins. A [grid](#scavoldgrid) of
|
|
434
|
+
captioned images is a gallery.
|
|
435
|
+
|
|
281
436
|
---
|
|
282
437
|
|
|
283
438
|
### `<ScavoldLayout>`
|
|
@@ -834,6 +989,35 @@ async function enhanceApp( context ) {
|
|
|
834
989
|
}
|
|
835
990
|
```
|
|
836
991
|
|
|
992
|
+
#### Layout
|
|
993
|
+
|
|
994
|
+
`section`, `article` and `aside` take a `layout` arranging their first block — an image,
|
|
995
|
+
usually — and the content after it. Left out, the block stands above the content.
|
|
996
|
+
|
|
997
|
+
```markdown
|
|
998
|
+
::: section layout=float-left
|
|
999
|
+

|
|
1000
|
+
|
|
1001
|
+
Text flowing around the image, and on below it.
|
|
1002
|
+
:::
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
| `layout` | Arrangement |
|
|
1006
|
+
|---|---|
|
|
1007
|
+
| `float-left`, `float-right` | The content flows around the block and on below it. |
|
|
1008
|
+
| `split-left`, `split-right` | Block and content side by side; the content does not run below the block. |
|
|
1009
|
+
|
|
1010
|
+
The element carries the choice as `data-layout`, and `scavold/styles/layouts.css`, which
|
|
1011
|
+
`enhanceApp` brings along, implements it. A section narrower than 30rem stacks its block
|
|
1012
|
+
above the content again, whatever its layout. Themes set the block's width through
|
|
1013
|
+
`--scavold-layout-width` (default `40%`) and the space beside and below it through
|
|
1014
|
+
`--scavold-layout-gap` (default `1.5rem`), or replace the rules.
|
|
1015
|
+
|
|
1016
|
+
The cratly editor offers the choices as buttons with Scavold's own icons, drawn after
|
|
1017
|
+
Font Awesome's solid style. A site replaces one by keeping an icon of the same name —
|
|
1018
|
+
`stacked`, `float-left`, `float-right`, `split-left`, `split-right` — in its
|
|
1019
|
+
`.cratly/icons/`.
|
|
1020
|
+
|
|
837
1021
|
---
|
|
838
1022
|
|
|
839
1023
|
## Composables
|
|
@@ -884,6 +1068,24 @@ Leave the `autoplay` attribute off the element; it would start the video regardl
|
|
|
884
1068
|
|
|
885
1069
|
---
|
|
886
1070
|
|
|
1071
|
+
### `useCard( props )`
|
|
1072
|
+
|
|
1073
|
+
```js
|
|
1074
|
+
import { useCard, cardProps } from "scavold/composables/useCard.js";
|
|
1075
|
+
```
|
|
1076
|
+
|
|
1077
|
+
Resolves the arguments of a `:::card` block for a site's own card component:
|
|
1078
|
+
|
|
1079
|
+
```js
|
|
1080
|
+
const props = defineProps( { ...cardProps } );
|
|
1081
|
+
const { href, title } = useCard( props );
|
|
1082
|
+
```
|
|
1083
|
+
|
|
1084
|
+
`href` is the URL of the linked page and `title` its title, both empty when the card
|
|
1085
|
+
links nowhere or to a page that does not exist.
|
|
1086
|
+
|
|
1087
|
+
---
|
|
1088
|
+
|
|
887
1089
|
### `useContainer( props )`
|
|
888
1090
|
|
|
889
1091
|
```js
|
|
@@ -988,6 +1190,41 @@ are exported as well.
|
|
|
988
1190
|
|
|
989
1191
|
---
|
|
990
1192
|
|
|
1193
|
+
### `useDetails( props )`
|
|
1194
|
+
|
|
1195
|
+
```js
|
|
1196
|
+
import { useDetails, detailsProps } from "scavold/composables/useDetails.js";
|
|
1197
|
+
```
|
|
1198
|
+
|
|
1199
|
+
Resolves the arguments of a `:::details` block for a site's own details component:
|
|
1200
|
+
|
|
1201
|
+
```js
|
|
1202
|
+
const props = defineProps( { ...detailsProps } );
|
|
1203
|
+
const { summary, open, group } = useDetails( props );
|
|
1204
|
+
```
|
|
1205
|
+
|
|
1206
|
+
`summary` is the trimmed title or an empty string, `open` a boolean, and `group` the
|
|
1207
|
+
group name or `undefined`.
|
|
1208
|
+
|
|
1209
|
+
---
|
|
1210
|
+
|
|
1211
|
+
### `useGrid( props )`
|
|
1212
|
+
|
|
1213
|
+
```js
|
|
1214
|
+
import { useGrid, gridProps, GRID_COLUMNS } from "scavold/composables/useGrid.js";
|
|
1215
|
+
```
|
|
1216
|
+
|
|
1217
|
+
Resolves the arguments of a `:::grid` block for a site's own grid component:
|
|
1218
|
+
|
|
1219
|
+
```js
|
|
1220
|
+
const props = defineProps( { ...gridProps } );
|
|
1221
|
+
const { columns } = useGrid( props );
|
|
1222
|
+
```
|
|
1223
|
+
|
|
1224
|
+
`columns` is one of `GRID_COLUMNS` (`2`, `3`, `4`), falling back to the first.
|
|
1225
|
+
|
|
1226
|
+
---
|
|
1227
|
+
|
|
991
1228
|
### `useHierarchy()`
|
|
992
1229
|
|
|
993
1230
|
```js
|
|
@@ -1070,6 +1307,29 @@ const items = computed( () =>
|
|
|
1070
1307
|
|
|
1071
1308
|
---
|
|
1072
1309
|
|
|
1310
|
+
### `useImageSizes()` / `provideImageSizes( sizes )`
|
|
1311
|
+
|
|
1312
|
+
```js
|
|
1313
|
+
import { provideImageSizes, useImageSizes } from "scavold/composables/useImageSizes.js";
|
|
1314
|
+
```
|
|
1315
|
+
|
|
1316
|
+
A layout component that knows how wide it draws its content provides a `sizes` value
|
|
1317
|
+
for the images inside it — a string or a ref:
|
|
1318
|
+
|
|
1319
|
+
```js
|
|
1320
|
+
// SiteSidebar.vue — content 18rem wide from 64rem, full width below
|
|
1321
|
+
provideImageSizes( "(min-width: 64rem) 18rem, 100vw" );
|
|
1322
|
+
```
|
|
1323
|
+
|
|
1324
|
+
Every `<ScavoldImage>` inside that carries only the site's default takes this value
|
|
1325
|
+
instead; one with a `sizes=` override, or given its `sizes` by a component like the
|
|
1326
|
+
cover, keeps its own. An empty value leaves the images with what the layout around
|
|
1327
|
+
provides. The value is resolved while rendering, so it is in the markup the server
|
|
1328
|
+
sends. `useImageSizes()` reads it, for a component rendering images of its own;
|
|
1329
|
+
`<ScavoldGrid>` provides one from `gridImageSizes`.
|
|
1330
|
+
|
|
1331
|
+
---
|
|
1332
|
+
|
|
1073
1333
|
### `usePageList( props )`
|
|
1074
1334
|
|
|
1075
1335
|
```js
|
|
@@ -1323,6 +1583,9 @@ reserved), loses browser preload scanning (images load later), and produces a fl
|
|
|
1323
1583
|
of empty space on every navigation during hydration. The `sizes` + `srcset` approach
|
|
1324
1584
|
was specifically designed to avoid all of these.
|
|
1325
1585
|
|
|
1586
|
+
A layout wrapping Markdown content rather than rendering images itself — a sidebar, a
|
|
1587
|
+
grid — reaches the images inside through [`provideImageSizes`](#useimagesizes--provideimagesizes-sizes).
|
|
1588
|
+
|
|
1326
1589
|
The global `image_sizes` in `.cratly.config.yaml` is a fallback for images that have
|
|
1327
1590
|
no layout-aware component wrapping them. Set it to the most common case in the
|
|
1328
1591
|
theme (typically the prose column width) and override via dedicated components for
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { useCard, cardProps } from "../composables/useCard.js";
|
|
4
|
+
import { useScavoldI18n } from "../composables/useI18n.js";
|
|
5
|
+
|
|
6
|
+
const props = defineProps( cardProps );
|
|
7
|
+
|
|
8
|
+
const { href, title } = useCard( props );
|
|
9
|
+
|
|
10
|
+
const { t } = useScavoldI18n();
|
|
11
|
+
const more = t( "card.more" );
|
|
12
|
+
|
|
13
|
+
// The visible text is the same on every card; the accessible name says where each
|
|
14
|
+
// one leads, starting with the visible text as WCAG 2.5.3 asks.
|
|
15
|
+
const linkLabel = computed( () => ( title.value ? `${more.value}: ${title.value}` : more.value ) );
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<div class="scavold-card" :class="[ props.class, { 'scavold-card--linked': href } ]">
|
|
20
|
+
<slot />
|
|
21
|
+
<a v-if="href" class="scavold-card__link" :href="href" :aria-label="linkLabel">{{ more }}</a>
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<style scoped>
|
|
26
|
+
/*
|
|
27
|
+
* Functional only: how a card looks — its frame, its padding, where its image sits —
|
|
28
|
+
* is the theme's. What is set here keeps it working: images stay within the card,
|
|
29
|
+
* and the link of a linked card covers all of it, so the whole card can be clicked.
|
|
30
|
+
* Links written into the card stay clickable on top of that.
|
|
31
|
+
*/
|
|
32
|
+
.scavold-card :deep(img) {
|
|
33
|
+
max-width: 100%;
|
|
34
|
+
height: auto;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.scavold-card--linked {
|
|
38
|
+
position: relative;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.scavold-card__link::after {
|
|
42
|
+
content: "";
|
|
43
|
+
position: absolute;
|
|
44
|
+
inset: 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.scavold-card--linked :deep(a:not(.scavold-card__link)) {
|
|
48
|
+
position: relative;
|
|
49
|
+
z-index: 1;
|
|
50
|
+
}
|
|
51
|
+
</style>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { useDetails, detailsProps } from "../composables/useDetails.js";
|
|
3
|
+
import { useScavoldI18n } from "../composables/useI18n.js";
|
|
4
|
+
|
|
5
|
+
const props = defineProps( detailsProps );
|
|
6
|
+
|
|
7
|
+
const { summary, open, group } = useDetails( props );
|
|
8
|
+
|
|
9
|
+
const { t } = useScavoldI18n();
|
|
10
|
+
const fallbackSummary = t( "details.summary" );
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<!-- Native <details>: keyboard operation, the expanded state for screen readers and
|
|
15
|
+
revealing the content on find in page come with the element. `name` makes
|
|
16
|
+
blocks sharing a group close each other. -->
|
|
17
|
+
<details class="scavold-details" :class="props.class" :open="open" :name="group">
|
|
18
|
+
<summary class="scavold-details__summary">{{ summary || fallbackSummary }}</summary>
|
|
19
|
+
<div class="scavold-details__content">
|
|
20
|
+
<slot />
|
|
21
|
+
</div>
|
|
22
|
+
</details>
|
|
23
|
+
</template>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed, inject } from "vue";
|
|
3
|
+
import { useGrid, gridProps } from "../composables/useGrid.js";
|
|
4
|
+
import { GRID_IMAGE_SIZES, provideImageSizes } from "../composables/useImageSizes.js";
|
|
5
|
+
|
|
6
|
+
const props = defineProps( gridProps );
|
|
7
|
+
|
|
8
|
+
const { columns } = useGrid( props );
|
|
9
|
+
|
|
10
|
+
// The theme knows how wide a cell is at which screen width; without a value for this
|
|
11
|
+
// column count, images keep what applies around the grid.
|
|
12
|
+
const imageSizes = inject( GRID_IMAGE_SIZES, {} );
|
|
13
|
+
provideImageSizes( computed( () => imageSizes[columns.value] ?? "" ) );
|
|
14
|
+
|
|
15
|
+
const style = computed( () => ( { "--scavold-grid-columns": columns.value } ) );
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<div class="scavold-grid" :class="[ props.class, `scavold-grid--${columns}` ]" :style="style">
|
|
20
|
+
<slot />
|
|
21
|
+
</div>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<style scoped>
|
|
25
|
+
/*
|
|
26
|
+
* Functional layout: every block written in the grid is a cell, at most
|
|
27
|
+
* --scavold-grid-columns of them side by side. A cell never gets narrower than
|
|
28
|
+
* --scavold-grid-min, so on a narrow screen the grid drops columns by itself,
|
|
29
|
+
* down to one, without breakpoints. Themes adjust both variables and the gap, or
|
|
30
|
+
* replace the rule entirely.
|
|
31
|
+
*/
|
|
32
|
+
.scavold-grid {
|
|
33
|
+
--_gap: var(--scavold-grid-gap, 1.5rem);
|
|
34
|
+
--_min: var(--scavold-grid-min, 14rem);
|
|
35
|
+
--_fraction: calc((100% - (var(--scavold-grid-columns) - 1) * var(--_gap)) / var(--scavold-grid-columns));
|
|
36
|
+
|
|
37
|
+
display: grid;
|
|
38
|
+
gap: var(--_gap);
|
|
39
|
+
grid-template-columns: repeat(auto-fill, minmax(min(100%, max(var(--_min), var(--_fraction))), 1fr));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* The gap spaces the cells; margins of their own would only put them out of line. */
|
|
43
|
+
.scavold-grid > :slotted(*) {
|
|
44
|
+
min-width: 0;
|
|
45
|
+
margin: 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* An image never reaches into the next cell, whatever the theme does elsewhere. */
|
|
49
|
+
.scavold-grid :deep(img) {
|
|
50
|
+
max-width: 100%;
|
|
51
|
+
height: auto;
|
|
52
|
+
}
|
|
53
|
+
</style>
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
|
|
2
|
+
import { computed } from "vue";
|
|
3
|
+
import { useImageSizes } from "../composables/useImageSizes.js";
|
|
4
|
+
|
|
5
|
+
const props = defineProps( {
|
|
3
6
|
src: { type: String, required: true },
|
|
4
7
|
srcset: { type: String, default: "" },
|
|
5
8
|
webpSrcset: { type: String, default: "" },
|
|
6
9
|
sizes: { type: String, default: "100vw" },
|
|
10
|
+
// Set by the Markdown renderer when `sizes` is only the site's default, which a
|
|
11
|
+
// layout around the image may replace with a better fit (see useImageSizes).
|
|
12
|
+
sizesFallback: { type: Boolean, default: false },
|
|
7
13
|
alt: { type: String, default: "" },
|
|
8
14
|
// An image above the fold — a cover — is wanted at once, not when it scrolls into view.
|
|
9
15
|
loading: { type: String, default: "lazy" },
|
|
10
16
|
fetchpriority: { type: String, default: undefined },
|
|
11
17
|
} );
|
|
18
|
+
|
|
19
|
+
const provided = useImageSizes();
|
|
20
|
+
const effectiveSizes = computed( () => ( props.sizesFallback && provided.value ) || props.sizes );
|
|
12
21
|
</script>
|
|
13
22
|
|
|
14
23
|
<template>
|
|
@@ -17,18 +26,18 @@ defineProps( {
|
|
|
17
26
|
v-if="webpSrcset"
|
|
18
27
|
type="image/webp"
|
|
19
28
|
:srcset="webpSrcset"
|
|
20
|
-
:sizes="
|
|
29
|
+
:sizes="effectiveSizes"
|
|
21
30
|
/>
|
|
22
31
|
<source
|
|
23
32
|
v-if="srcset"
|
|
24
33
|
:srcset="srcset"
|
|
25
|
-
:sizes="
|
|
34
|
+
:sizes="effectiveSizes"
|
|
26
35
|
/>
|
|
27
36
|
<img
|
|
28
37
|
:src="src"
|
|
29
38
|
:alt="alt"
|
|
30
39
|
:role="alt ? undefined : 'presentation'"
|
|
31
|
-
:sizes="
|
|
40
|
+
:sizes="effectiveSizes"
|
|
32
41
|
:loading="loading"
|
|
33
42
|
:fetchpriority="fetchpriority"
|
|
34
43
|
decoding="async"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { computed } from "vue";
|
|
2
|
+
import { containerProps } from "./useContainer.js";
|
|
3
|
+
import { useHierarchy } from "./hierarchy";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Props definition for card container components. Spread into `defineProps`
|
|
7
|
+
* alongside any component-specific props.
|
|
8
|
+
*/
|
|
9
|
+
export const cardProps = {
|
|
10
|
+
...containerProps,
|
|
11
|
+
|
|
12
|
+
/** Page the card links to, as a path in the pages folder. */
|
|
13
|
+
dataLink: { type: String, default: null },
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Composable for card container components.
|
|
18
|
+
*
|
|
19
|
+
* Parses the props emitted by the markdown-it container renderer for a
|
|
20
|
+
* `:::card` block and exposes derived values ready for binding:
|
|
21
|
+
*
|
|
22
|
+
* - `href` — URL of the linked page, or an empty string when the card links
|
|
23
|
+
* nowhere or to a page that does not exist
|
|
24
|
+
* - `title` — title of the linked page, or an empty string
|
|
25
|
+
*
|
|
26
|
+
* Custom card components should call this composable so they share identical
|
|
27
|
+
* attribute resolution without duplicating the logic.
|
|
28
|
+
*
|
|
29
|
+
* @param {object} props reactive props from defineProps
|
|
30
|
+
* @returns {{ href, title }}
|
|
31
|
+
*/
|
|
32
|
+
export function useCard( props ) {
|
|
33
|
+
const { resolveByPath, nodeHref, nodeTitle } = useHierarchy();
|
|
34
|
+
|
|
35
|
+
const target = computed( () => {
|
|
36
|
+
const path = props.dataLink?.trim();
|
|
37
|
+
|
|
38
|
+
return path ? resolveByPath( path ) : undefined;
|
|
39
|
+
} );
|
|
40
|
+
|
|
41
|
+
const href = computed( () => ( target.value ? nodeHref( target.value ) : "" ) );
|
|
42
|
+
const title = computed( () => ( target.value ? nodeTitle( target.value ) : "" ) );
|
|
43
|
+
|
|
44
|
+
return { href, title };
|
|
45
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { computed } from "vue";
|
|
2
|
+
import { containerProps } from "./useContainer.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Props definition for details container components. Spread into `defineProps`
|
|
6
|
+
* alongside any component-specific props.
|
|
7
|
+
*/
|
|
8
|
+
export const detailsProps = {
|
|
9
|
+
...containerProps,
|
|
10
|
+
|
|
11
|
+
/** Title shown while the content is collapsed, and clicked to expand it. */
|
|
12
|
+
dataSummary: { type: String, default: null },
|
|
13
|
+
|
|
14
|
+
/** Start expanded. */
|
|
15
|
+
dataOpen: { type: String, default: null },
|
|
16
|
+
|
|
17
|
+
/** Name shared by details blocks of which only one at a time may be expanded. */
|
|
18
|
+
dataGroup: { type: String, default: null },
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Composable for details container components.
|
|
23
|
+
*
|
|
24
|
+
* Parses the props emitted by the markdown-it container renderer for a
|
|
25
|
+
* `:::details` block and exposes derived values ready for binding:
|
|
26
|
+
*
|
|
27
|
+
* - `summary` — title of the block, or an empty string when none is given
|
|
28
|
+
* - `open` — boolean, whether the block starts expanded
|
|
29
|
+
* - `group` — name of the group the block belongs to, or undefined
|
|
30
|
+
*
|
|
31
|
+
* Custom details components should call this composable so they share identical
|
|
32
|
+
* attribute resolution without duplicating the logic.
|
|
33
|
+
*
|
|
34
|
+
* @param {object} props reactive props from defineProps
|
|
35
|
+
* @returns {{ summary, open, group }}
|
|
36
|
+
*/
|
|
37
|
+
export function useDetails( props ) {
|
|
38
|
+
const summary = computed( () => ( props.dataSummary ?? "" ).trim() );
|
|
39
|
+
const open = computed( () => props.dataOpen != null );
|
|
40
|
+
const group = computed( () => props.dataGroup?.trim() || undefined );
|
|
41
|
+
|
|
42
|
+
return { summary, open, group };
|
|
43
|
+
}
|