···11+---
22+maudit: patch
33+---
44+55+Add a new RenderWithAlt trait for rendering images, enforcing the usage of alt when trying to render to a String
+1-1
crates/maudit/src/assets.rs
···1010mod script;
1111mod style;
1212mod tailwind;
1313-pub use image::{Image, ImageFormat, ImageOptions};
1313+pub use image::{Image, ImageFormat, ImageOptions, ImagePlaceholder, RenderWithAlt};
1414pub use script::Script;
1515pub use style::{Style, StyleOptions};
1616pub use tailwind::TailwindPlugin;
+16
crates/maudit/src/assets/image.rs
···432432433433 bytes
434434}
435435+436436+/// Trait to render an image with an alt text.
437437+pub trait RenderWithAlt {
438438+ fn render(&self, alt: &str) -> String;
439439+}
440440+441441+impl RenderWithAlt for Image {
442442+ fn render(&self, alt: &str) -> String {
443443+ let (width, height) = self.dimensions();
444444+445445+ format!(
446446+ r#"<img src="{}" width="{}" height="{}" loading="lazy" decoding="async" alt="{}"/>"#,
447447+ self.url, width, height, alt
448448+ )
449449+ }
450450+}
···99In a `main.rs` file, import the `coronate` function and call it to build your project. Here is an example of a simple Maudit project:
10101111```rs
1212-use maudit::{coronate, routes, BuildOptions, BuildOutput};
1212+use maudit::{coronate, routes, BuildOptions, BuildOutput, content_sources};
1313use routes::Index;
14141515fn main() -> Result<BuildOutput, Box<dyn std::error::Error>> {
1616- coronate(routes![Index], vec![].into(), BuildOptions::default())
1616+ coronate(routes![Index], content_sources![], BuildOptions::default())
1717}
1818```
1919···2424The first argument to the `coronate` function is a `Vec` of all the routes that should be built. For the sake of ergonomics, the `routes!` macro can be used to create this list.
25252626```rs
2727+use maudit::{coronate, routes, BuildOptions};
2728use routes::Index;
28292929-coronate(
3030- routes![Index],
3131- vec![].into(),
3232- BuildOptions::default()
3333-)
3030+fn main() {
3131+ coronate(
3232+ routes![Index],
3333+ content_sources![],
3434+ BuildOptions::default()
3535+ )
3636+}
3437```
35383639See the [Routing](/docs/routing) documentation for more information on how to define routes.
+32-1
website/content/docs/images.md
···16161717```rs
1818use maudit::route::prelude::*;
1919+use maud::{html};
19202021#[route("/blog")]
2122pub struct Blog;
···2425 fn render(&self, ctx: &mut PageContext) -> impl Into<RenderResult> {
2526 let image = ctx.assets.add_image("logo.png");
26272727- format!("", image.url)
2828+ let (width, height) = image.dimensions();
2929+ format!("<img src=\"{}\" alt=\"My logo\" width=\"{}\" height=\"{}\" />", image.url(), width, height);
3030+3131+ // A more convenient way to render an image is to use the `render()` method, which generates an img tag for you and enforces accessibility by requiring an alt text.
3232+ format!("{}", image.render("The logo of my project, a stylized crown"))
2833 }
2934}
3035```
···6873```
69747075Processing images in Markdown files using the standard syntax is currently not supported, but can be achieved using a custom [shortcode](/docs/content/#shortcodes) or [component](/docs/content/#components).
7676+7777+## Placeholders
7878+7979+Maudit supports generating low-quality image placeholders (LQIP) for images. This can be useful to improve the perceived performance of your site by showing a blurred preview of an image while the full image is loading.
8080+8181+To generate a placeholder, use the `placeholder()` method on an [Image](https://docs.rs/maudit/latest/maudit/assets/struct.Image.html) instance, for example returned by `ctx.assets.add_image()` or `ctx.assets.add_image_with_options()`.
8282+8383+```rs
8484+use maudit::route::prelude::*;
8585+8686+#[route("/image")]
8787+pub struct ImagePage;
8888+8989+impl Route for ImagePage {
9090+ fn render(&self, ctx: &mut PageContext) -> impl Into<RenderResult> {
9191+ let image = ctx.assets.add_image("path/to/image.jpg");
9292+ let placeholder = image.placeholder();
9393+9494+ format!("<img src=\"{}\" alt=\"Image with placeholder\" style=\"background-image: url('{}'); background-size: cover;\" />", image.url(), placeholder.data_uri())
9595+ }
9696+}
9797+```
9898+9999+Alternatively, it is possible to get the dominant colors of an image using the `average_rgba()` method. This method will return a tuple of four `u8` values representing the red, green, blue, and alpha channels of the average color of the image.
100100+101101+The generation of placeholders is powered by the [ThumbHash](https://evanw.github.io/thumbhash/) library.