Site Update: Hero Images

Published on , 698 words, 3 minutes to read

An image of the secret to life, the universe and everything, concept art
the secret to life, the universe and everything, concept art - Midjourney

For a while I've been wondering how I can add dramatic flair to my website with so-called "hero images". These images are tools that let you describe the mood a website wants to evoke. I've been unsure how to best implement these on my website for a while, but with the advent of MidJourney and other image generation APIs/algorithms I think I have found a way to create these without too much effort on my part and the results are pretty fantastic.

I have generated a bunch of other images that I'm going to use for my other posts. I'll give out a desktop wallpaper sized version of each of these images on my Patreon.

Under the hood this is powered by lol_html and Maud. The magic is mostly contained in a function that generates a <figure> HTML element (which I just learned exists today). I use a function that looks like this for generating the <XeblogHero> snippets:

pub fn xeblog_hero(file: String, prompt: Option<String>) -> Markup {
    html! {
        figure.hero style="margin:0" {
            picture style="margin:0" {
                source type="image/avif" srcset={"https://cdn.xeiaso.net/file/christine-static/hero/" (file) ".avif"};
                source type="image/webp" srcset={"https://cdn.xeiaso.net/file/christine-static/hero/" (file) ".webp"};
                img style="padding:0" alt={"hero image " (file)} src={"https://cdn.xeiaso.net/file/christine-static/hero/" (file) "-smol.png"};
            }
            figcaption { "Image generated by MidJourney" @if let Some(prompt) = prompt { " -- " (prompt) } }
        }
    }
}

I have it wired up with lol_html like this:

lol_html::element!("XeblogHero", |el| {
    let file = el.get_attribute("file").expect("wanted XeblogHero to contain file");
    el.replace(&crate::tmpl::xeblog_hero(file, el.get_attribute("prompt")).0, ContentType::Html);
    Ok(())
})

The result is that I can declare hero images with HTML fragments like this:

<XeblogHero
  file="miku-dark-souls"
  prompt="hatsune miku, elden ring, dark souls, concept art, crowbar"
></XeblogHero>

And I get this:

An image of hatsune miku, elden ring, dark souls, concept art, crowbar
hatsune miku, elden ring, dark souls, concept art, crowbar
Mara is hacker
<Mara> This is powered by the <figure> tag, which is a new discovery to us. This is probably one of the most useful tags we never knew about and removed the need to write a bunch of annoying CSS and HTML.

The webp and AVIF versions of the hero images have a higher resolution version so that it looks nicer on retina screens. However, the png versions of these are locked to a resolution of 800x356 pixels because I was unable to crush them below a size of half a megabyte at full resolution. Realistically, this should only affect older browsers on slower hardware, so I don't expect this to have too much impact on most users.

Cadey is coffee
<Cadey>

If you don't want to see these hero images, you can remove them with a userstyle like this:

figure.hero {
    display: none;
}

I'm likely going to convert over most of my website templates to use Maud. I'm very happy with it and I think it is incredibly useful to express your HTML in Rust instead of something that has to be compiled to Rust. In practice it reminds me of the Nim library emerald, which lets you write HTML using Nim functions similar to how you use Maud.

Here's a few more examples of hero images I have generated:

An image of the forbidden shape
the forbidden shape
An image of the great wave off of kanagawa, cyberpunk, hanzi inscription
the great wave off of kanagawa, cyberpunk, hanzi inscription

Normally I will only have one image per post and it will usually be after the introduction paragraph. The prompt will usually be related to the article topic, but sometimes I will take artistic liberty. If you have suggestions for prompts, please contact me with those ideas.

I hope these updates on how I've been messing with my site are interesting. I'm trying to capture the spirit of how I'm implementing these changes as well as details of how everything fits together.


Facts and circumstances may have changed since publication. Please contact me before jumping to conclusions if something seems wrong or unclear.

Tags: