diff --git a/README.md b/README.md
index 5cb9f0f..1f12375 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,37 @@
Simple Bootstrap-based theme for Hugo.
+## Shortcodes
+
+### `lead`
+
+Wraps its content in a Bootstrap `.lead` paragraph (the Markdown
+attribute syntax `{.lead}` cannot target a paragraph):
+
+```
+{{< lead >}}
+An introductory paragraph that stands out.
+{{< /lead >}}
+```
+
+Pass extra classes with `class`, e.g. `{{< lead class="mb-5" >}}`.
+
+### `banner`
+
+A full-bleed banner / hero image: it spans the entire viewport width
+while the rest of the page stays within `.container`. Put it on the
+first line of a page's content to get a hero flush against the navbar.
+
+```
+{{< banner src="hero.png" alt="…" >}}
+```
+
+`src` is resolved as a page resource first, then a global resource
+(`assets/`), then as a plain link respecting the site's base URL.
+Optional params: `title`, `height` (a CSS length such as `60vh`, which
+crops the image with `object-fit: cover`) and `class`.
+
+
## Custom error page
The theme has a custom 404 error page.
diff --git a/assets/scss/hugo_bootstrap_theme.scss b/assets/scss/hugo_bootstrap_theme.scss
index 88261d2..7aa1c40 100644
--- a/assets/scss/hugo_bootstrap_theme.scss
+++ b/assets/scss/hugo_bootstrap_theme.scss
@@ -15,6 +15,12 @@
--bs-body-line-height: 1.5;
}
+// Keep the full-bleed `.banner` from adding a horizontal scrollbar
+// (100vw is wider than the viewport when a vertical scrollbar is shown).
+html {
+ overflow-x: hidden;
+}
+
body {
font-family: var(--bs-font-sans-serif);
line-height: var(--bs-body-line-height);
@@ -61,8 +67,23 @@ h1, h2, h3, h4, h5 {
}
}
-// Images
+// Full-bleed banner / hero image: spans the viewport width while the
+// surrounding page content stays within `.container`. Works from inside
+// any centred `.container` because the horizontal margins cancel out the
+// container offset (50% of the parent - 50% of the viewport).
.banner {
+ display: block;
+ width: 100vw;
+ max-width: 100vw;
+ margin-left: calc(50% - 50vw);
+ margin-right: calc(50% - 50vw);
margin-bottom: 2rem;
}
+// When the banner is the first thing in the page body, pull it flush
+// against the navbar by cancelling the layout's top padding (`py-5`).
+.content-body > .banner:first-child,
+.content-body > p:first-child > .banner:only-child {
+ margin-top: -3rem;
+}
+
diff --git a/layouts/_shortcodes/banner.html b/layouts/_shortcodes/banner.html
index b800b3b..985f602 100644
--- a/layouts/_shortcodes/banner.html
+++ b/layouts/_shortcodes/banner.html
@@ -1,4 +1,36 @@
-{{ $src := .Get "src" }}
-{{ $alt := .Get "alt" }}
-{{ $title := .Get "title" }}
-
+{{- /*
+ Full-bleed banner / hero image.
+
+ The image spans the entire viewport width while the surrounding page
+ content stays within `.container` (see the `.banner` rules in
+ hugo_bootstrap_theme.scss). Place the shortcode as the first line of a
+ page's content to get a hero that sits flush against the navbar.
+
+ Params:
+ src image path - resolved as a page resource first, then a global
+ resource (assets/), then treated as a link/URL respecting the
+ site's base URL
+ alt alternative text (default: empty)
+ title optional title attribute
+ height optional CSS length (e.g. "60vh"); crops the image with
+ object-fit: cover
+ class optional extra CSS classes
+*/ -}}
+{{- $src := .Get "src" -}}
+{{- $alt := or (.Get "alt") "" -}}
+{{- $title := .Get "title" -}}
+{{- $height := .Get "height" -}}
+{{- $class := .Get "class" -}}
+{{- $url := $src -}}
+{{- with $.Page.Resources.GetMatch $src -}}
+ {{- $url = .RelPermalink -}}
+{{- else -}}
+ {{- with resources.Get $src -}}
+ {{- $url = .RelPermalink -}}
+ {{- else -}}
+ {{- $url = $src | relURL -}}
+ {{- end -}}
+{{- end -}}
+
diff --git a/layouts/_shortcodes/lead.html b/layouts/_shortcodes/lead.html
new file mode 100644
index 0000000..7587d71
--- /dev/null
+++ b/layouts/_shortcodes/lead.html
@@ -0,0 +1,2 @@
+{{ $additional_classes := .Get "class" }}
+
{{ .Inner | safeHTML }}