Add resource-tiles shortcode
Displays a page's file resources as a responsive grid of cards with a thumbnail (explicit param, name-matched image, or a generic file icon), title, size and download link. Meant for a downloads page offering a handful of PDFs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,61 @@ first line of a page's content to get a hero flush against the navbar.
|
||||
Optional params: `title`, `height` (a CSS length such as `60vh`, which
|
||||
crops the image with `object-fit: cover`) and `class`.
|
||||
|
||||
### `resource-tiles`
|
||||
|
||||
Displays a page's file resources as a responsive grid of cards
|
||||
("tiles"), each with a thumbnail, a title, the file size and a download
|
||||
link. Meant for a downloads page that offers a handful of PDFs,
|
||||
spreadsheets, archives, …
|
||||
|
||||
```
|
||||
{{< resource-tiles >}}
|
||||
```
|
||||
|
||||
Params:
|
||||
|
||||
| Param | Default | Meaning |
|
||||
|---------|---------|--------------------------------------------------------|
|
||||
| `match` | `*.pdf` | glob selecting which page resources to show |
|
||||
| `cols` | `3` | number of columns on large screens |
|
||||
| `sort` | — | resource field to order by (`title`, `name`, `weight`) |
|
||||
| `class` | — | extra CSS classes for the wrapping row |
|
||||
|
||||
Thumbnails are resolved per resource, in order of preference:
|
||||
|
||||
1. a `thumbnail` param on the resource, pointing at an image in the same
|
||||
page bundle;
|
||||
2. an image resource whose name matches the file, e.g. `report.png` or
|
||||
`report.pdf.png` next to `report.pdf`;
|
||||
3. a generic file icon showing the upper-cased extension.
|
||||
|
||||
Hugo cannot rasterise PDFs itself, so first-page previews have to be
|
||||
produced as a build step. With poppler-utils installed, generating a
|
||||
`<name>.png` next to every PDF under `content/` is a one-liner:
|
||||
|
||||
```sh
|
||||
find content -name '*.pdf' -exec sh -c \
|
||||
'pdftocairo -png -singlefile -scale-to-x 600 -scale-to-y -1 "$1" "${1%.pdf}"' _ {} \;
|
||||
```
|
||||
|
||||
Run it before `hugo`; the tiles then use these as thumbnails
|
||||
automatically. (`pdftoppm` takes the same flags; `mutool draw` is an
|
||||
alternative.)
|
||||
|
||||
Per-resource title, description and thumbnail come from the page's front
|
||||
matter:
|
||||
|
||||
```yaml
|
||||
resources:
|
||||
- src: report.pdf
|
||||
title: Annual report
|
||||
params:
|
||||
description: Everything that happened last year.
|
||||
thumbnail: report-cover.png
|
||||
```
|
||||
|
||||
The whole tile is a click target (Bootstrap `.stretched-link`).
|
||||
|
||||
|
||||
## Navigation
|
||||
|
||||
|
||||
@@ -87,3 +87,36 @@ h1, h2, h3, h4, h5 {
|
||||
margin-top: -3rem;
|
||||
}
|
||||
|
||||
// Resource tiles (see the `resource-tiles` shortcode): a grid of cards,
|
||||
// one per downloadable file, each with a thumbnail or a generic file
|
||||
// icon on top.
|
||||
.resource-tile {
|
||||
&__thumb,
|
||||
&__icon {
|
||||
aspect-ratio: 3 / 2;
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
background-color: var(--bs-light);
|
||||
}
|
||||
|
||||
&__icon {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--bs-primary);
|
||||
}
|
||||
|
||||
&__ext {
|
||||
position: absolute;
|
||||
bottom: 0.9rem;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
{{- /*
|
||||
resource-tiles — display a page's file resources as a responsive grid
|
||||
of cards ("tiles"), each with a thumbnail, a title, the file size and a
|
||||
download link. Handy for a downloads page that offers a handful of
|
||||
PDFs, spreadsheets, archives, …
|
||||
|
||||
Params:
|
||||
match glob selecting which page resources to show
|
||||
(default "*.pdf")
|
||||
cols number of columns on large screens (default 3)
|
||||
sort resource field to order by: "title", "name" or "weight"
|
||||
(default: the order they appear in front matter)
|
||||
class extra CSS classes for the wrapping row
|
||||
|
||||
Thumbnails are resolved per resource, in order of preference:
|
||||
1. a `thumbnail` param on the resource, pointing at an image in the
|
||||
same page bundle
|
||||
2. an image resource whose name matches the file, e.g. `report.png`
|
||||
or `report.pdf.png` next to `report.pdf`
|
||||
3. a generic file icon showing the upper-cased extension
|
||||
|
||||
Per-resource metadata is read from the page's front matter:
|
||||
|
||||
resources:
|
||||
- src: report.pdf
|
||||
title: Annual report
|
||||
params:
|
||||
description: Everything that happened last year.
|
||||
thumbnail: report-cover.png
|
||||
*/ -}}
|
||||
|
||||
{{- $page := .Page -}}
|
||||
{{- $match := or (.Get "match") "*.pdf" -}}
|
||||
{{- $cols := or (.Get "cols") 3 -}}
|
||||
{{- $class := .Get "class" -}}
|
||||
{{- $imgExts := slice "png" "jpg" "jpeg" "webp" "gif" "avif" -}}
|
||||
|
||||
{{- $resources := $page.Resources.Match $match -}}
|
||||
{{- with .Get "sort" -}}
|
||||
{{- $resources = sort $resources . -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- with $resources -}}
|
||||
<div class="row row-cols-1 row-cols-sm-2 row-cols-lg-{{ $cols }} g-4 my-4{{ with $class }} {{ . }}{{ end }}">
|
||||
{{- range . }}
|
||||
{{- $res := . -}}
|
||||
{{- $ext := strings.TrimPrefix "." (path.Ext .Name) -}}
|
||||
|
||||
{{- /* Title: front-matter `title`, else the humanized file name.
|
||||
Hugo falls back .Title to .Name, so treat that as unset. */ -}}
|
||||
{{- $title := .Title -}}
|
||||
{{- if or (not $title) (eq $title .Name) -}}
|
||||
{{- $title = humanize (path.BaseName .Name) -}}
|
||||
{{- end -}}
|
||||
|
||||
{{- /* Resolve a thumbnail image (see the doc comment above). */ -}}
|
||||
{{- $thumb := false -}}
|
||||
{{- with .Params.thumbnail -}}
|
||||
{{- $thumb = $page.Resources.GetMatch . -}}
|
||||
{{- end -}}
|
||||
{{- if not $thumb -}}
|
||||
{{- $base := path.BaseName $res.Name -}}
|
||||
{{- range $imgExts -}}
|
||||
{{- if not $thumb -}}
|
||||
{{- $thumb = or ($page.Resources.GetMatch (printf "%s.%s" $base .)) ($page.Resources.GetMatch (printf "%s.%s" $res.Name .)) -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- if and $thumb (eq $thumb.ResourceType "image") -}}
|
||||
{{- $thumb = $thumb.Fill "600x400 Center" -}}
|
||||
{{- end -}}
|
||||
|
||||
<div class="col">
|
||||
<div class="card resource-tile h-100">
|
||||
{{- if $thumb }}
|
||||
<img class="card-img-top resource-tile__thumb" src="{{ $thumb.RelPermalink }}" alt="{{ $title }}" loading="lazy">
|
||||
{{- else }}
|
||||
<div class="card-img-top resource-tile__icon" aria-hidden="true">
|
||||
<svg width="72" height="72" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 2h8l4 4v15a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1Z"
|
||||
fill="currentColor" fill-opacity=".08" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/>
|
||||
<path d="M14 2v4h4" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<span class="resource-tile__ext">{{ $ext | upper }}</span>
|
||||
</div>
|
||||
{{- end }}
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ $title }}</h5>
|
||||
{{- with $res.Params.description }}
|
||||
<p class="card-text">{{ $page.RenderString . }}</p>
|
||||
{{- end }}
|
||||
</div>
|
||||
<div class="card-footer bg-transparent d-flex align-items-center justify-content-between gap-2">
|
||||
<span class="text-body-secondary small text-uppercase">
|
||||
{{- $ext }}{{ with $res.Content }} · {{ partial "human_file_size" . }}{{ end -}}
|
||||
</span>
|
||||
<a class="btn btn-primary btn-sm stretched-link" href="{{ $res.RelPermalink }}" download>Download</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{- end }}
|
||||
</div>
|
||||
{{- end -}}
|
||||
Reference in New Issue
Block a user