random navigation
Zap!
Well that was random...
"Random note" implementation guide
This guide provides step-by-step instructions for implementing a random note navigation feature in the digital garden.
Overview
The random feature allows users to navigate to a randomly selected note via a floating button and a dedicated /random
route.
These changes need to be made to your digital garden repository, not your Obsidian vault.
Instructions
Step 1: Create the Random Route Layout
src/site/_includes/layouts/random.njk
<!DOCTYPE html>
<html lang="{{ meta.mainLanguage }}">
<head>
<title>Random Page</title>
{% include "components/pageheader.njk" %}
<script>
// Get all published notes and redirect to a random one
const publishedNotes = [
{% for note in collections.note %}
{% if note.data['dg-publish'] %}
"{{ note.url }}",
{% endif %}
{% endfor %}
];
if (publishedNotes.length > 0) {
const randomIndex = Math.floor(Math.random() * publishedNotes.length);
const randomUrl = publishedNotes[randomIndex];
window.location.href = randomUrl;
} else {
// Fallback to home if no notes found
window.location.href = '/';
}
</script>
</head>
<body class="theme-{{meta.baseTheme}} markdown-preview-view markdown-rendered markdown-preview-section {{meta.bodyClasses}}">
<main class="content cm-s-obsidian">
<header>
<h1>Redirecting to Random Page...</h1>
<p>You are being redirected to a random page on the site!</p>
</header>
<div>
<p>If you are not redirected automatically, <a href="/">click here to go home</a>.</p>
</div>
</main>
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
{% include "components/lucideIcons.njk" %}
</body>
</html>
Step 2: Create the Random Route File
src/site/random.md
---
layout: layouts/random.njk
permalink: /random/
eleventyExcludeFromCollections: true
---
Step 3: Add the Floating Random Button Styles
src/site/styles/custom-style.scss
#floating-control-random {
position: fixed;
bottom: 1vmax;
left: 1vmax; /* Positioned on bottom-left */
z-index: 999999; /* High z-index to float over sidebar */
background-color: var(--interactive-accent);
border-radius: 50%;
width: 24px;
height: 24px;
box-shadow: var(--shadow-s);
cursor: pointer;
transition: all 0.15s ease-in-out;
display: flex;
align-items: center;
justify-content: center;
color: var(--text-on-accent);
&:hover {
background-color: var(--interactive-accent-hover);
transform: scale(1.05);
}
#random-switch {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
.svg-icon {
width: 20px;
height: 20px;
stroke-width: 2px;
}
}
}
Step 4: Create the Random Button Component
src/site/_includes/components/user/common/footer/random-button.njk`
<aside id="floating-control-random">
<span id="random-switch">
<i class="svg-icon" icon-name="shuffle" aria-hidden="true"></i>
</span>
</aside>
<script>
document.getElementById('random-switch').addEventListener('click', function() {
window.location.href = '/random';
});
</script>
Step 5: Add Random Button in Your Layouts
src/site/_includes/layouts/note.njk and index.njk
<!-- Add this before the closing </body> tag -->
{% include "components/user/common/footer/random-button.njk" %}
Features Implemented
- ✅ Floating random button positioned on bottom-left
- ✅ CSS styling with hover effects and smooth transitions
- ✅ High z-index to float over sidebar
- ✅ Integration with theme system (uses CSS custom properties)
- ✅ Responsive design considerations
- ✅
/random
route implementation with proper Eleventy collection filtering - ✅ Random button component with click handler
- ✅ Proper note collection filtering (respects
dg-publish
setting) - ✅ Fallback handling when no published notes are available
How It Works
- Button Click: User clicks the floating random button
- Navigation: JavaScript redirects to
/random
route - Collection Processing: Eleventy template processes all notes in
collections.note
- Filtering: Only notes with
dg-publish: true
are included - Random Selection: JavaScript picks a random note from the filtered list
- Redirect: User is automatically redirected to the selected note
- Fallback: If no published notes exist, user is redirected to home page
File Locations
- Styles:
src/site/styles/custom-style.scss
- Random Route:
src/site/random.md
- Random Layout:
src/site/_includes/layouts/random.njk
- Button Component:
src/site/_includes/components/user/common/footer/random-button.njk
- Layout Templates: Include in
src/site/_includes/layouts/note.njk
andsrc/site/_includes/layouts/index.njk
Notes
- The button uses the "shuffle" icon from Lucide icons
- Positioning avoids conflict with existing theme switcher (bottom-right)
- The random selection respects the
dg-publish
frontmatter setting - Includes proper error handling and fallback behavior
- Uses Eleventy's
collections.note
to dynamically generate the list of available notes
Patch
for convenience
diff --git a/src/site/_includes/components/user/common/footer/random-button.njk b/src/site/_includes/components/user/common/footer/random-button.njk
new file mode 100644
index 0000000..b9b4f7f
--- /dev/null
+++ b/src/site/_includes/components/user/common/footer/random-button.njk
@@ -0,0 +1,11 @@
+<aside id="floating-control-random">
+ <span id="random-switch">
+ <i class="svg-icon" icon-name="shuffle" aria-hidden="true"></i>
+ </span>
+</aside>
+
+<script>
+ document.getElementById('random-switch').addEventListener('click', function() {
+ window.location.href = '/random';
+ });
+</script>
\ No newline at end of file
diff --git a/src/site/_includes/layouts/random.njk b/src/site/_includes/layouts/random.njk
new file mode 100644
index 0000000..f6aa44d
--- /dev/null
+++ b/src/site/_includes/layouts/random.njk
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html lang="{{ meta.mainLanguage }}">
+ <head>
+ <title>Random Page</title>
+ {% include "components/pageheader.njk" %}
+ <script>
+ // Get all published notes and redirect to a random one
+ const publishedNotes = [
+ {% for note in collections.note %}
+ {% if note.data['dg-publish'] %}
+ "{{ note.url }}",
+ {% endif %}
+ {% endfor %}
+ ];
+
+ if (publishedNotes.length > 0) {
+ const randomIndex = Math.floor(Math.random() * publishedNotes.length);
+ const randomUrl = publishedNotes[randomIndex];
+ window.location.href = randomUrl;
+ } else {
+ // Fallback to home if no notes found
+ window.location.href = '/';
+ }
+ </script>
+ </head>
+ <body class="theme-{{meta.baseTheme}} markdown-preview-view markdown-rendered markdown-preview-section {{meta.bodyClasses}}">
+ <main class="content cm-s-obsidian">
+ <header>
+ <h1>Redirecting to Random Page...</h1>
+ <p>You are being redirected to a random page on the site!</p>
+ </header>
+ <div>
+ <p>If you are not redirected automatically, <a href="/">click here to go home</a>.</p>
+ </div>
+ </main>
+ <script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
+ {% include "components/lucideIcons.njk" %}
+ </body>
+</html>
\ No newline at end of file
diff --git a/src/site/random.md b/src/site/random.md
new file mode 100644
index 0000000..a0ded8b
--- /dev/null
+++ b/src/site/random.md
@@ -0,0 +1,5 @@
+---
+layout: layouts/random.njk
+permalink: /random/
+eleventyExcludeFromCollections: true
+---
\ No newline at end of file
diff --git a/src/site/styles/custom-style.scss b/src/site/styles/custom-style.scss
index 9b08909..848525c 100644
--- a/src/site/styles/custom-style.scss
+++ b/src/site/styles/custom-style.scss
@@ -145,3 +145,40 @@ body.backlinks-note-icon .backlink[data-note-icon="chest"]::before {
}
}
}
+
+#floating-control-random {
+ position: fixed;
+ bottom: 1vmax;
+ left: 1vmax; /* Changed from right to left */
+ z-index: 999999; /* High z-index to float over sidebar */
+ background-color: var(--interactive-accent);
+ border-radius: 50%;
+ width: 24px;
+ height: 24px;
+ box-shadow: var(--shadow-s);
+ cursor: pointer;
+ transition: all 0.15s ease-in-out;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--text-on-accent);
+
+ &:hover {
+ background-color: var(--interactive-accent-hover);
+ transform: scale(1.05);
+ }
+
+ #random-switch {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ height: 100%;
+
+ .svg-icon {
+ width: 20px;
+ height: 20px;
+ stroke-width: 2px;
+ }
+ }
+}