Rust library to generate static websites
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: docs improvements

+82 -61
+30 -19
website/assets/docs-sidebar.ts
··· 30 30 function toggleLeftSidebar() { 31 31 leftOpen = !leftOpen; 32 32 33 - leftSidebar.classList.toggle("-translate-x-full", !leftOpen); 34 - leftSidebar.classList.toggle("translate-x-0", leftOpen); 33 + const leftSidebarContent = leftSidebar.querySelector("div") as HTMLElement; 34 + 35 + // Toggle overlay opacity 35 36 leftSidebar.classList.toggle("opacity-0", !leftOpen); 36 37 leftSidebar.classList.toggle("opacity-100", leftOpen); 37 38 leftSidebar.classList.toggle("pointer-events-none", !leftOpen); 38 39 40 + // Toggle sidebar content transform 41 + if (leftSidebarContent) { 42 + leftSidebarContent.classList.toggle("-translate-x-full", !leftOpen); 43 + leftSidebarContent.classList.toggle("translate-x-0", leftOpen); 44 + } 45 + 39 46 if (leftOpen) { 40 47 document.body.style.overflow = "hidden"; 41 48 } else if (!rightOpen) { ··· 46 53 function toggleRightSidebar() { 47 54 rightOpen = !rightOpen; 48 55 49 - rightSidebar.classList.toggle("translate-x-full", !rightOpen); 50 - rightSidebar.classList.toggle("translate-x-0", rightOpen); 56 + const rightSidebarContent = rightSidebar.querySelector( 57 + "div" 58 + ) as HTMLElement; 59 + 60 + // Toggle overlay opacity 51 61 rightSidebar.classList.toggle("opacity-0", !rightOpen); 52 62 rightSidebar.classList.toggle("opacity-100", rightOpen); 53 63 rightSidebar.classList.toggle("pointer-events-none", !rightOpen); 54 64 65 + // Toggle sidebar content transform 66 + if (rightSidebarContent) { 67 + rightSidebarContent.classList.toggle("translate-x-full", !rightOpen); 68 + rightSidebarContent.classList.toggle("translate-x-0", rightOpen); 69 + } 70 + 55 71 if (rightOpen) { 56 72 document.body.style.overflow = "hidden"; 57 73 } else if (!leftOpen) { ··· 59 75 } 60 76 } 61 77 62 - // Close sidebars when clicking outside 78 + // Close sidebars when clicking on the dark overlay 63 79 function closeSidebars(event: MouseEvent) { 64 - const target = event.target; 65 - if ( 66 - leftOpen && 67 - target && 68 - !leftSidebar.contains(target as Node) && 69 - !leftSidebarToggle.contains(target as Node) 70 - ) { 80 + const target = event.target as HTMLElement; 81 + 82 + // Check if clicking on the dark overlay (the outer div with bg-black) 83 + if (leftOpen && target && target.id === "mobile-left-sidebar") { 71 84 toggleLeftSidebar(); 72 85 } 73 - if ( 74 - rightOpen && 75 - target && 76 - !rightSidebar.contains(target as Node) && 77 - !rightSidebarToggle.contains(target as Node) 78 - ) { 86 + if (rightOpen && target && target.id === "mobile-right-sidebar") { 79 87 toggleRightSidebar(); 80 88 } 81 89 } 82 90 83 91 leftSidebarToggle.addEventListener("click", toggleLeftSidebar); 84 92 rightSidebarToggle.addEventListener("click", toggleRightSidebar); 85 - document.addEventListener("click", closeSidebars); 93 + 94 + // Add click listeners to the overlay divs 95 + leftSidebar.addEventListener("click", closeSidebars); 96 + rightSidebar.addEventListener("click", closeSidebars); 86 97 87 98 // Close right sidebar when clicking on table of contents links 88 99 rightSidebar.addEventListener("click", function (event) {
+9 -5
website/assets/prin.css
··· 136 136 line-height: 1.75; 137 137 } 138 138 139 - .prose code { 139 + .prose code, 140 + .intro-code code { 140 141 font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, 141 142 Consolas, "DejaVu Sans Mono", monospace; 142 143 font-size: 0.888889em; 143 144 } 144 145 145 146 .prose code:not(pre code) { 146 - font-weight: 600; 147 + font-weight: bold; 147 148 } 148 149 149 150 .prose code:not(pre code):before { ··· 157 158 .prose pre { 158 159 margin-left: -1.5rem; 159 160 margin-right: -1.5rem; 161 + } 162 + 163 + .prose pre, 164 + .intro-code pre { 160 165 background-color: var(--color-darker-black); 161 166 overflow-x: auto; 162 167 padding-top: 1em; ··· 182 187 } 183 188 184 189 @media (max-width: 768px) { 185 - .prose pre { 190 + .prose pre, 191 + .intro-code pre { 186 192 border-radius: 0; 187 - margin-left: -1rem; 188 - margin-right: -1rem; 189 193 } 190 194 }
+6 -4
website/content/docs/images.md
··· 12 12 13 13 ### In pages 14 14 15 - To use an image in a page, add it anywhere in your project's directory, and use the `ctx.assets.add_image()` method to add it to a page's assets. 15 + To use an image in a page, add it anywhere in your project's directory, and use the [`ctx.assets.add_image()`](https://docs.rs/maudit/latest/maudit/assets/struct.RouteAssets.html#method.add_image) method to add it to a page's assets. 16 16 17 17 ```rs 18 18 use maudit::route::prelude::*; ··· 48 48 49 49 ## Processing images 50 50 51 - Images added to pages can be transformed by using `ctx.assets.add_image_with_options()`. 51 + Images added to pages can be transformed by using [`ctx.assets.add_image_with_options()`](https://docs.rs/maudit/latest/maudit/assets/struct.RouteAssets.html#method.add_image_with_options), which takes an additional [`ImageOptions`](https://docs.rs/maudit/latest/maudit/assets/struct.ImageOptions.html) struct to specify how the image should be processed. 52 52 53 53 ```rs 54 54 use maudit::route::prelude::*; ··· 78 78 79 79 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. 80 80 81 - 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()`. 81 + To generate a placeholder, use the [`placeholder()`](https://docs.rs/maudit/latest/maudit/assets/struct.Image.html#method.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()`. 82 + 83 + It can then be included into the page by using the [`data_uri()`](https://docs.rs/maudit/latest/maudit/assets/struct.ImagePlaceholder.html#method.data_uri) method on the returned [`ImagePlaceholder`](https://docs.rs/maudit/latest/maudit/assets/struct.ImagePlaceholder.html) instance. 82 84 83 85 ```rs 84 86 use maudit::route::prelude::*; ··· 96 98 } 97 99 ``` 98 100 99 - 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. 101 + Alternatively, it is possible to get the dominant colors of an image using the [`average_rgba()`](https://docs.rs/maudit/latest/maudit/assets/struct.ImagePlaceholder.html#method.average_rgba) method on the placeholder. 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. 100 102 101 103 The generation of placeholders is powered by the [ThumbHash](https://evanw.github.io/thumbhash/) library.
+2 -2
website/content/docs/javascript.md
··· 6 6 7 7 Maudit supports adding JavaScript and TypeScript files to your site. 8 8 9 - To import a script, add it anywhere in your project's directory, and use the `ctx.assets.add_script()` method to add it to a page's assets. Paths are relative to the project's current working directory, not the file where the method is called. 9 + To import a script, add it anywhere in your project's directory, and use the [`ctx.assets.add_script()`](https://docs.rs/maudit/latest/maudit/assets/struct.RouteAssets.html#method.add_script) method to add it to a page's assets. Paths are relative to the project's current working directory, not the file where the method is called. 10 10 11 11 ```rs 12 12 use maudit::route::prelude::*; ··· 34 34 } 35 35 ``` 36 36 37 - Alternatively, the `include_script()` method can be used to automatically include the script in the page, without needing to manually add it to the template. This can be useful when a layout or component need to include their own scripts. 37 + Alternatively, the [`include_script()`](https://docs.rs/maudit/latest/maudit/assets/struct.RouteAssets.html#method.include_script) method can be used to automatically include the script in the page, without needing to manually add it to the template. This can be useful when a layout or component need to include their own scripts. 38 38 39 39 ```rs 40 40 fn render(&self, ctx: &mut PageContext) -> impl Into<RenderResult> {
+2 -2
website/content/docs/library.md
··· 103 103 104 104 ## Handling content 105 105 106 - In the current implementation, trying to use content will result in an empty list of entries. Despite what the syntax might suggest, content sources are not automatically initialized when creating a `ContentSources` instance through the `content_sources![]` macro. 106 + In the current implementation, trying to use content will result in an empty list of entries. Despite what the syntax might suggest, content sources are not automatically initialized when creating a [`ContentSources`](https://docs.rs/maudit/latest/maudit/content/struct.ContentSources.html) instance through the [`content_sources![]`](https://docs.rs/maudit/latest/maudit/macro.content_sources.html) macro. 107 107 108 108 If you've copied the previous snippets, you might have noticed that Rust has been complaining about `content_sources` being mutable but never mutated. 109 109 ··· 162 162 163 163 ## Conclusion 164 164 165 - And with that, you've succesfully rebuilt Maudit at home! There's a few more things that can be done to improve this implementation, like adding logging, copying static assets (from `options.static_dir`), asset processing, better error handling, parallelization, caching, etc, etc. 165 + And with that, you've succesfully rebuilt Maudit at home! There's a few more things that can be done to improve this implementation, like adding logging, copying static assets (from [`options.static_dir`](https://docs.rs/maudit/latest/maudit/struct.BuildOptions.html#structfield.static_dir)), asset processing, better error handling, parallelization, caching, etc, etc. 166 166 167 167 But, this is a fully functional implementation that can be used as a starting point for more advanced use cases... or just as a learning exercise to understand how Maudit works under the hood.
+1 -1
website/content/docs/performance.md
··· 23 23 24 24 ### Disabling heavy features during development 25 25 26 - When running through `maudit dev`, the `is_dev()` function will return `true`, allowing you to conditionally disable features that are slow to build or run during development. 26 + When running through `maudit dev` or by using the `MAUDIT_DEV=true` env variable, the [`is_dev()`](https://docs.rs/maudit/latest/maudit/fn.is_dev.html) function will return `true`, allowing you to conditionally disable features that are slow to build or run during development. 27 27 28 28 ```rs 29 29 use maudit::is_dev;
+6 -6
website/content/docs/routing.md
··· 25 25 26 26 ## Static Routes 27 27 28 - To create a new page in your Maudit project, create a struct and implement the `Route` trait for it, adding the `#[route]` attribute to the struct definition with the path of the route as an argument. The path can be any Rust expression, as long as its value can be converted to String. (i.e. `.to_string()` will be called on it) 28 + To create a new page in your Maudit project, create a struct and implement [the `Route` trait](https://docs.rs/maudit/latest/maudit/route/trait.Route.html) for it, adding the `#[route]` attribute to the struct definition with the path of the route as an argument. The path can be any Rust expression, as long as its value can be converted to String. (i.e. `.to_string()` will be called on it) 29 29 30 30 ```rs 31 31 use maudit::route::prelude::*; ··· 40 40 } 41 41 ``` 42 42 43 - The `Route` trait requires the implementation of a `render` method that returns any types that can be converted into `RenderResult`. This method is called when the page is built and should return the content that will be displayed. In most cases, you'll be [using a templating library](/docs/templating/) to create HTML content. 43 + The `Route` trait requires the implementation of a [`render` method](https://docs.rs/maudit/latest/maudit/route/trait.Route.html#tymethod.render) that returns any types that can be converted into [`RenderResult`](https://docs.rs/maudit/latest/maudit/route/enum.RenderResult.html). This method is called when the page is built and should return the content that will be displayed. In most cases, you'll be [using a templating library](/docs/templating/) to create HTML content. 44 44 45 - Maudit implements `Into<RenderResult>` for the following types: 45 + Maudit implements [`Into<RenderResult>`](https://docs.rs/maudit/latest/maudit/route/enum.RenderResult.html#trait-implementations) for the following types: 46 46 47 47 - `String`, `Vec<u8>`, `&str`, `&[u8]` 48 48 - `Result<T, E> where T: Into<RenderResult> and E: std::error::Error` (see [Handling Errors](#handling-errors) for more information) ··· 56 56 57 57 To create a dynamic route, export a struct using the `route` attribute and add parameters by enclosing them in square brackets (ex: `/posts/[slug]`) in the route's path. 58 58 59 - In addition to the `render` method, dynamic routes must implement a `pages` method for Route. The `pages` method returns a list of all the possible values for each parameter in the route's path, so that Maudit can generate all the necessary pages. 59 + In addition to the `render` method, dynamic routes must implement a [`pages` method](https://docs.rs/maudit/latest/maudit/route/trait.Route.html#method.pages) for Route. The `pages` method returns a list of all the possible values for each parameter in the route's path, so that Maudit can generate all the necessary pages. 60 60 61 61 ```rs 62 62 use maudit::route::prelude::*; ··· 84 84 } 85 85 ``` 86 86 87 - The route parameters are automatically extracted from the URL and made available through the `ctx.params::<T>()` method in the `PageContext` struct, providing type-safe access to the values. 87 + The route parameters are automatically extracted from the URL and made available through the [`ctx.params::<T>()`](https://docs.rs/maudit/latest/maudit/route/struct.PageContext.html#method.params) method in the [`PageContext`](https://docs.rs/maudit/latest/maudit/route/struct.PageContext.html) struct passed to the render method, providing type-safe access to the values. 88 88 89 89 ```rs 90 90 use maudit::route::prelude::*; ··· 111 111 } 112 112 ``` 113 113 114 - The struct used for the parameters must implement `Into<PageParams>`, which can be done automatically by deriving the `Params` trait. The fields of the struct must implement the `Display` trait, as they will be converted to strings to be used in the final URLs and file paths. 114 + The struct used for the parameters must implement `Into<PageParams>`, which can be done automatically by deriving the `Params` trait. The fields of the struct must implement the `Display` trait, as they will be converted to strings to be used in the final URLs and file paths. For ergonomy, it is recommended to derive the `Clone` trait as well, or the params will only be accessible by reference through [`ctx.params_ref()`](https://docs.rs/maudit/latest/maudit/route/struct.PageContext.html#method.params_ref). 115 115 116 116 Like static routes, dynamic routes must be [registered](#registering-routes) in the `coronate` function in order for them to be built. 117 117
+13 -9
website/src/layout.rs
··· 80 80 layout( 81 81 html! { 82 82 // Second header for docs navigation (mobile only) 83 - header.bg-our-white.border-b.border-borders.md:hidden.bg-linear-to-b."from-darker-white" { 83 + header.sticky.top-0.z-40.bg-our-white.border-b.border-borders.md:hidden.bg-linear-to-b."from-darker-white" { 84 84 div.flex.items-center.justify-between { 85 85 button id="left-sidebar-toggle" .px-4.py-3.flex.items-center.gap-x-2.text-base.font-medium.text-our-black aria-label="Toggle navigation menu" { 86 86 (PreEscaped(include_str!("../assets/side-menu.svg"))) ··· 94 94 } 95 95 96 96 // Mobile left sidebar overlay 97 - div id="mobile-left-sidebar" .fixed.left-0.w-full.bg-our-white.transform."-translate-x-full".transition-all.opacity-0.pointer-events-none.z-50.overflow-y-auto style="top: 116px; bottom: 0;" { 98 - div.px-6.py-4 { 99 - (left_sidebar(ctx)) 97 + div id="mobile-left-sidebar" .fixed."inset-0 bg-black/50".transition-opacity.opacity-0.pointer-events-none.z-50 { 98 + div.w-80.max-w-sm.h-full.bg-our-white.overflow-y-auto.transform."-translate-x-full".transition-transform { 99 + div.px-4.py-4 { 100 + (left_sidebar(ctx)) 101 + } 100 102 } 101 103 } 102 104 103 105 // Mobile right sidebar overlay 104 - div id="mobile-right-sidebar" .fixed.right-0.w-full.bg-our-white.transform."translate-x-full".transition-all.opacity-0.pointer-events-none.z-50.overflow-y-auto style="top: 116px; bottom: 0;" { 105 - div.px-6.py-4 { 106 - (right_sidebar(headings)) 106 + div id="mobile-right-sidebar" .fixed."inset-0 bg-black/50".transition-opacity.opacity-0.pointer-events-none.z-50.flex.justify-end { 107 + div.w-80.max-w-sm.h-full.bg-our-white.overflow-y-auto.transform."translate-x-full".transition-transform { 108 + div.px-4.py-4 { 109 + (right_sidebar(headings)) 110 + } 107 111 } 108 112 } 109 113 110 - div.container.mx-auto."md:grid-cols-(--docs-columns)".md:grid."min-h-[calc(100%-64px)]".px-4.md:px-0.pt-2.md:pt-0 { 114 + div.container.mx-auto."md:grid-cols-(--docs-columns)".md:grid."min-h-[calc(100%-64px)]".px-6.md:px-0.pt-2.md:pt-0 { 111 115 aside.bg-linear-to-l."from-darker-white"."py-8"."h-full".border-r.border-r-borders.hidden.md:block { 112 116 (left_sidebar(ctx)) 113 117 } ··· 149 153 (seo_data.render(&ctx.base_url)) 150 154 } 151 155 body { 152 - div.bg-our-white { 156 + div.relative.bg-our-white { 153 157 (header(ctx, bottom_border)) 154 158 (main) 155 159 footer.bg-our-black.text-white {
+8 -8
website/src/layout/docs_sidebars.rs
··· 89 89 while i < headings.len() { 90 90 let heading = &headings[i]; 91 91 let (pad, border) = match heading.level { 92 - 2 => ("pl-0", ""), // h2 93 - 3 => ("pl-4", "border-l-2 border-borders"), // h3 94 - 4 => ("pl-8", "border-l-2 border-borders"), // h4 95 - 5 => ("pl-12", "border-l-2 border-borders"), // h5 96 - 6 => ("pl-16", "border-l-2 border-borders"), // h6 97 - _ => ("pl-0", ""), // fallback 92 + 2 => ("pl-0", ""), // h2 93 + 3 => ("pl-4", "sm:border-l-2 sm:border-borders"), // h3 94 + 4 => ("pl-8", "sm:border-l-2 sm:border-borders"), // h4 95 + 5 => ("pl-12", "sm:border-l-2 sm:border-borders"), // h5 96 + 6 => ("pl-16", "sm:border-l-2 sm:border-borders"), // h6 97 + _ => ("pl-0", ""), // fallback 98 98 }; 99 99 let next_level = if i + 1 < headings.len() { 100 100 headings[i + 1].level ··· 110 110 seen_h2 = true; 111 111 } 112 112 html_headings.push(html! { 113 - li.(pad).(border).(margin_top) { 114 - a.block.py-1.px-3.sm:px-0.sm:py-0.text-lg.sm:text-base.transition-colors."hover:bg-gray-50".sm:hover:bg-transparent."hover:text-brand-red" href=(format!("#{}", heading.id)) { 113 + li.(border).(margin_top) { 114 + a class=(format!("block py-1 px-3 sm:px-0 sm:py-0 text-lg sm:text-base transition-colors hover:bg-gray-50 sm:hover:bg-transparent hover:text-brand-red border-b border-borders sm:border-b-0 {}", pad)) href=(format!("#{}", heading.id)) { 115 115 (heading.title) 116 116 } 117 117 }
+5 -5
website/src/routes/index.rs
··· 1 - use maud::html; 2 1 use maud::PreEscaped; 3 - use maudit::content::highlight_code; 2 + use maud::html; 4 3 use maudit::content::HighlightOptions; 4 + use maudit::content::highlight_code; 5 5 use maudit::route::prelude::*; 6 6 7 + use crate::layout::SeoMeta; 7 8 use crate::layout::layout; 8 - use crate::layout::SeoMeta; 9 9 10 10 const CODE_EXAMPLE: &str = r#"use maudit::prelude::*; 11 11 ··· 99 99 a.underline href="/docs/philosophy/#maudit-is-a-library-not-a-framework" { "Maudit is a library, not a framework." } " A Maudit site is a normal Rust program that you have full control over. Hook into the build process, customize the output, and use any libraries you want." 100 100 } 101 101 } 102 - div { 103 - pre.bg-gray-900.p-4.rounded-lg.overflow-x-auto.sm:text-base.text-sm { 102 + div.intro-code { 103 + pre { 104 104 code { 105 105 (PreEscaped(code_example)) 106 106 }