Background cover image using CSS

Hi, This is my first try with Hugo, I am trying to convert an HTML Theme to Hugo for the startup company. I did face some issues but found fixes for those either on YouTube, some random blog, or here but I cannot find a fix for there’s another issue as no one has even replied on that one. Hopefully, this won’t see the same faith any way I want to use the cover.jpg as background in blog–> single.html

That section uses background-image: in CSS to define a static image as background but I want to use the cover image of the posts. so far I tired

background: rgba(0, 0, 0, 0) url('{{"/img/cover.jpg" | relURL }}') no-repeat fixed center center / cover;
background: rgba(0, 0, 0, 0) url("/img/cover.jpg") no-repeat fixed center center / cover;
background: rgba(0, 0, 0, 0) url("{{ .RelPermalink }}/img/cover.jpg") no-repeat fixed center center / cover;
background: rgba(0, 0, 0, 0) url("{{ .Permalink }}/img/cover.jpg") no-repeat fixed center center / cover;
+++++++
in index.md -----> cover: /img/cover.jpg
+++++++

background: rgba(0, 0, 0, 0) url('{{ .Params.cover }}') no-repeat fixed center center / cover;

but these don’t seem to work, this {{ .RelPermalink }}/img/cover.jpg works in HTML but not in CSS so I am not sure what to do.

In order to do something like this, you’d have to process your CSS as a template using ExecuteAstemplate. In my experience, this has created some slowness, though.

I would recommend using a CSS variable as the URL and then setting the variable on the relevant HTML element.

E.g.

Stylesheet

.some-selector {
  --bg-cover: "/path/to/some/default/img.jpg";
  background: rgba(0, 0, 0, 0) url(var(--bg-cover)) no-repeat fixed center center / cover;
}

baseof.html

...
<body {{ with .Params.cover }}style="--bg-cover: '{{ . }}'"{{ end }}>
...
</body>
...

index.md

+++
cover = "/img/cover.jpg"
+++

Try this in your CSS file.

{{- $cover := .Resources.GetMatch "cover.*"}}
.test { background: rgba(0, 0, 0, 0) url('{{ $cover.RelPermalink }}') no-repeat fixed center center / cover; }

Then in your head.html file (assuming your cover images have the same base file name).

{{- $cover := resources.Get "css/test.css" | resources.ExecuteAsTemplate "css/test.css" . | minify | fingerprint }}
<link rel="stylesheet" href="{{ $cover.RelPermalink }}" {{- if hugo.IsProduction }} integrity="{{ $cover.Data.Integrity }}" {{- end }}>
``
.some-selector {
  --bg-cover: "/path/to/some/default/img.jpg";
  background: rgba(0, 0, 0, 0) url(var(--bg-cover)) no-repeat fixed center center / cover;
}

Not sure how it will be dynamic? this looks like using a normal background image but with extra steps. I want the background-image to change for every post.

    {{- $cover := resources.Get "css/style.css" | resources.ExecuteAsTemplate "css/style.css" . | minify | fingerprint }}
    <link rel="stylesheet" href="{{ $cover.RelPermalink }}" {{- if hugo.IsProduction }}
        integrity="{{ $cover.Data.Integrity }}" {{- end }}>

pasting this in the head.html gives this error

Error: error building site: render: failed to render pages: render of "home" failed: "/mnt/myData/work/mw/hugoFiles/PortV1/themes/AroxMarketing/layouts/_default/baseof.html:3:3": execute of template failed: template: home/list.html:3:3: executing "home/list.html" at <partial "common/head.html" .>: error calling partial: "/mnt/myData/work/mw/hugoFiles/PortV1/themes/AroxMarketing/layouts/partials/common/head.html:23:58": execute of template failed: template: partials/common/head.html:23:58: executing "partials/common/head.html" at <resources.ExecuteAsTemplate>: error calling ExecuteAsTemplate: type <nil> not supported in Resource transformations

Not sure how it will be dynamic?

It’s dynamic due to the use of CSS variables (or custom properties). If you’re not familiar with them yet, I’d highly recommend reading up on them. Extremely powerful tool.

While seeming like extra steps, it’s actually fewer steps for Hugo since it doesn’t need to treat your stylesheet as a template. You also don’t have to muck up your stylesheets with Hugo specific template syntax.

I don’t understand if I am going to define a path /img/cover.jpg in the CSS variable wouldn’t it look in static/img/ instead of looking into post/blog —> postfolder —> img ??

and btw it’s not working the URL shows these weird numbers

.blogarea {
    --bg-cover: "/img/cover.jpg";
    background: rgba(0, 0, 0, 0) url(var(--bg-cover))  no-repeat fixed center center / cover;
}

<section {{ with .Params.cover }}style="--bg-cover: '{{ . }}'"{{ end }} class="section blogarea overlay-dark d-flex align-items-center">


....


</section>

Does your content structure look like this?

content/
├── blog/
│   ├── blog-1/
│   │   ├── cover.jpg
│   │   └── index.md
│   └── blog-2/
│       ├── cover.jpg
│       └── index.md
└── _index.md

Or something different?

No it is like that

content/
├── blog/
│   ├── 2023/
│   │  ├── Jul/
│   │       ├──21/
│   │         ├──img/cover.jpg
│   │       ├─ index.md
│   └── index.md
└── _index.md

Try this:

git clone --single-branch -b hugo-forum-topic-45450 https://github.com/jmooring/hugo-testing hugo-forum-topic-45450
cd hugo-forum-topic-45450
hugo server
layouts/_default/baseof.html
{{ if and (eq .Type "blog") (eq .Kind "page") }}
  {{ partial "set-background-image.html" (dict "page" . "selector" "main" "image" "img/cover.jpg") }}
{{ end }}

layouts/partials/set-background-image.html
{{- /* Get page resource, fall back to global resource. */}}
{{- $r := "" }}
{{- with .page.Resources.Get .image }}
  {{- $r = . }}
{{- else }}
  {{- with resources.Get .image }}
    {{- $r = . }}
  {{- end }}
{{- end }}

{{- /* Create and publish style sheet. */}}
{{- if $r }}
  {{- $css := "{{ .selector }} {background: rgba(0, 0, 0, 0) url('{{ .url }}') no-repeat fixed center center / cover;}" }}
  {{- with resources.FromString "set-background-image.css" $css }}
    {{- $targetPath := urls.JoinPath $.page.RelPermalink (printf "background-image-%d.css" ($.selector | crypto.FNV32a)) }}
    {{- $ctx := dict "selector" $.selector "url" $r.RelPermalink }}
    {{- with . | resources.ExecuteAsTemplate $targetPath $ctx }}
      {{- with . | minify | fingerprint }}
        <link rel="stylesheet" href="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous">
      {{- end }}
    {{- end }}
  {{- end }}
{{- end }}

published site structure
public/
├── blog/
│   ├── blog-1/
│   │   ├── img/
│   │   │   └── cover.jpg
│   │   ├── background-image-3935363592.min.5eb4bdb930cd537a4fef2d3d831ed0016bac9f41b6c1fc147386eaf37a662346.css
│   │   └── index.html
│   ├── blog-2/
│   │   ├── img/
│   │   │   └── cover.jpg
│   │   ├── background-image-3935363592.min.82d8f2b42cc0d13da33ceceaf9b1cede142c21aec3810fbe95b30ac69ae4bcfe.css
│   │   └── index.html
│   ├── blog-3/
│   │   ├── background-image-3935363592.min.03a47ecc7957c81fc7529b42e171f791265f1fa13bdac553a85c8ee0c4ad17da.css
│   │   └── index.html
│   └── index.html
├── css/
│   └── style.min.bff66cbcab35b14fd5b053ae084e97c3539211ddd188eb9eae3c4146778680a3.css
├── img/
│   └── cover.jpg
├── favicon.ico
└── index.html

This approach creates a small, unique, additional style sheet for each “dynamic” background image that you add to a page. And you can use the partial multiple times on the same page with different selectors and image paths.

A web server’s Content Security Policy (CSP) may block inline styles—this approach is safer.

2 Likes

I had to do this because in the CSS it was showing main for some reason. I did a few change and after breaking the site a few this one worked

{{ partial "common/backgroundcover.html" (dict "page" . "selector" ".blogarea" "image" "img/cover.jpg") }}

Thank You So Much for helping me out.

I’m refactoring this a bit. Hold off.

1 Like

I’ve edited my initial response, and pushed a change to the repository. There was a problem with the style sheet name if the selector was a class or id (element selectors worked fine).

1 Like

One more time… now it handles complex selectors properly (e.g., div#foo > .bar.baz).

I’ve updated my initial response, and pushed a change to the repository.

2 Likes

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.