Compare commits

14 Commits
dev ... main

Author SHA1 Message Date
7e6b00b21d retarget banner to correct source 2025-12-10 00:51:48 +01:00
4f693a66fd fix favicon, cloudflare script blocking, error in js on startup 2025-12-07 18:48:48 +01:00
925ec7cf1e change logo images to use css 2025-12-07 14:22:54 +01:00
96351533c6 iframe adjustments 2025-12-07 14:18:50 +01:00
5738815b91 add seymour icons 2025-12-07 14:15:26 +01:00
a7e7697fec fix nav bar being stacked in mobile. fixed header and subtitle positioning in mobile views 2025-10-27 10:56:24 +00:00
9e15866468 fix typo 2025-10-05 11:34:25 +01:00
e0572f4471 fix-typo (#18)
add SEO adjustments, apply html segments

Reviewed-on: #18
Co-authored-by: Michael DiLeo <michael_dileo@proton.me>
Co-committed-by: Michael DiLeo <michael_dileo@proton.me>
2025-09-29 18:57:15 +00:00
5e87f52e5e seo (#17)
SEO implementation, adds a structured data response and robots.txt, fixes page structure, adds meta tags, add elements to UI for better SEO as well.

Reviewed-on: #17
Co-authored-by: Michael DiLeo <michael_dileo@proton.me>
Co-committed-by: Michael DiLeo <michael_dileo@proton.me>
2025-09-28 18:00:58 +00:00
b9ddb56bbe Merge remote-tracking branch 'origin/dev' 2025-09-01 11:56:14 -05:00
bf76ca99d6 add that pixelfed is like old instagram 2025-09-01 11:20:48 -05:00
6338bc3aae text - restructure the specs into section and dl tags 2025-09-01 11:01:29 -05:00
f2b47f38b4 responsive navbar (#14)
collapse navbar on smaller screens. #9

Co-authored-by: Michael DiLeo <michael.dileo@oakstreethealth.com>
Reviewed-on: #14
2025-08-14 19:43:30 +00:00
8e19c2ee20 dev (#13)
switch to bundle.css

Co-authored-by: Michael DiLeo <michael.dileo@oakstreethealth.com>
Reviewed-on: #13
2025-08-14 14:55:21 +00:00
22 changed files with 847 additions and 86 deletions

View File

@@ -35,10 +35,10 @@ async function createOptimizedBuild() {
const purgeResults = await new PurgeCSS().purge({ const purgeResults = await new PurgeCSS().purge({
content: [ content: [
'public/index.html', 'public/index.html',
'public/about.html' 'public/about.html',
], ],
css: [ css: [
'public/css/pico.jade.css' 'public/css/pico.jade.css',
// 'public/css/pico.colors.min.css' // 'public/css/pico.colors.min.css'
], ],
safelist: { safelist: {
@@ -153,20 +153,25 @@ async function createOptimizedBuild() {
await minifyHTMLFile('public/index.html', 'dist/index.html'); await minifyHTMLFile('public/index.html', 'dist/index.html');
await minifyHTMLFile('public/about.html', 'dist/about.html'); await minifyHTMLFile('public/about.html', 'dist/about.html');
// Step 5: Copy assets // Step 5: Copy assets and SEO files
console.log('📁 Step 5: Copying assets...'); console.log('📁 Step 5: Copying assets and SEO files...');
try { try {
const assets = await fs.readdir('public/assets'); await copyDirectoryRecursive('public/assets', 'dist/assets');
for (const asset of assets) { console.log(' ✅ Assets directory copied recursively');
await fs.copyFile(
path.join('public/assets', asset),
path.join('dist/assets', asset)
);
}
} catch (err) { } catch (err) {
console.log(' No assets directory found, skipping...'); console.log(' No assets directory found, skipping...');
} }
// Copy SEO files
try {
await fs.copyFile('public/structured-data.json', 'dist/structured-data.json');
await fs.copyFile('public/sitemap.xml', 'dist/sitemap.xml');
await fs.copyFile('public/robots.txt', 'dist/robots.txt');
console.log(' ✅ SEO files copied (structured-data.json, sitemap.xml, robots.txt)');
} catch (err) {
console.log(' ⚠️ Some SEO files not found, skipping...');
}
// Calculate compression results // Calculate compression results
const originalCSSSize = (await fs.stat('public/css/pico.jade.min.css')).size + const originalCSSSize = (await fs.stat('public/css/pico.jade.min.css')).size +
(await fs.stat('public/css/pico.min.css')).size; (await fs.stat('public/css/pico.min.css')).size;
@@ -286,6 +291,27 @@ async function bundleAndMinifyJS() {
} }
} }
async function copyDirectoryRecursive(src, dest) {
// Create destination directory if it doesn't exist
await fs.mkdir(dest, { recursive: true });
// Read all items in source directory
const entries = await fs.readdir(src, { withFileTypes: true });
for (const entry of entries) {
const srcPath = path.join(src, entry.name);
const destPath = path.join(dest, entry.name);
if (entry.isDirectory()) {
// Recursively copy subdirectories
await copyDirectoryRecursive(srcPath, destPath);
} else {
// Copy files
await fs.copyFile(srcPath, destPath);
}
}
}
async function minifyHTMLFile(inputPath, outputPath) { async function minifyHTMLFile(inputPath, outputPath) {
const html = await fs.readFile(inputPath, 'utf8'); const html = await fs.readFile(inputPath, 'utf8');

View File

@@ -16,6 +16,7 @@ http {
include /etc/nginx/mime.types; include /etc/nginx/mime.types;
default_type application/octet-stream; default_type application/octet-stream;
charset utf-8;
access_log /dev/stdout; access_log /dev/stdout;
error_log /dev/stderr; error_log /dev/stderr;
@@ -39,6 +40,21 @@ http {
add_header X-Content-Type-Options "nosniff" always; add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always; add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "script-src 'self' 'unsafe-inline' https://static.cloudflareinsights.com; object-src 'none';" always;
# Favicon: serve at standard /favicon.ico location
location = /favicon.ico {
alias /usr/share/nginx/html/assets/logos/favicon.png;
expires 7d;
add_header Cache-Control "public, max-age=604800";
access_log off;
}
# Logo assets: 1 hour cache
location ~* ^/assets/logos/ {
expires 1h;
add_header Cache-Control "public, max-age=3600";
}
# Temporary: Disable caching during development # Temporary: Disable caching during development
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
@@ -56,6 +72,11 @@ http {
# add_header Pragma "no-cache"; # add_header Pragma "no-cache";
# add_header Expires "0"; # add_header Expires "0";
} }
location ~* \.json$ {
expires 1d;
add_header Cache-Control "public, must-revalidate";
}
location /health { location /health {
access_log off; access_log off;

View File

@@ -4,6 +4,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark"> <meta name="color-scheme" content="light dark">
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline' https://static.cloudflareinsights.com; object-src 'none';">
<link rel="stylesheet" href="css/pico.jade.min.css"> <link rel="stylesheet" href="css/pico.jade.min.css">
<link rel="stylesheet" href="site-styles/style.css"> <link rel="stylesheet" href="site-styles/style.css">
<title>Keyboard Vagabond - About</title> <title>Keyboard Vagabond - About</title>
@@ -65,33 +66,57 @@
Your data is yours and you can download it at any time through the apps. Your data is yours and you can download it at any time through the apps.
The servers are run in a cluster with data redundancy across nodes + nightly and weekly backups to offline storage.</p> The servers are run in a cluster with data redundancy across nodes + nightly and weekly backups to offline storage.</p>
<p><strong>Should shutdown happen</strong> - <p><strong>Should shutdown happen</strong> -
There will be a 3 month announcement in advance, in accordance with the <a href="https://joinmastodon.org/covenant" target="_blank">Mastodon Server Covenant</a>.</p> There will be a 3 month announcement in advance, in accordance with the <a href="https://joinmastodon.org/covenant" rel="nofollow" target="_blank">Mastodon Server Covenant</a>.</p>
<p><strong>Funding</strong> - <p><strong>Funding</strong> -
Keyboard Mastodon is currently funded by the admin, for a cost of ~$40 - $45 per month. Donations may be opened in the future, but have not been set up at this time.</p> Keyboard Mastodon is currently funded by the admin, for a cost of ~$40 - $45 per month. Donations may be opened in the future, but have not been set up at this time.</p>
<h1>The Dirty Technicals</h1>
<section>
<h2>The Dirty Technicals</h2>
<p>If you're not a mega-nerd, turn back now.</p> <p>If you're not a mega-nerd, turn back now.</p>
<p>I warned you.</p> <p>I warned you.</p>
<p>Keyboard Vagabond is run on a 3 node Kubernetes cluster running on 3x Arm VPSs hosted by NetCup in Amsterdam. I chose Amsterdam because I thought that Europe would be more centrally located for people who are traveling the world.</p> <p>Keyboard Vagabond is run on a 3 node Kubernetes cluster running on 3x Arm VPSs hosted by NetCup in Amsterdam. I chose Amsterdam because I thought that Europe would be more centrally located for people who are traveling the world.</p>
</section>
<section>
<h4>The Specs</h4> <h4>The Specs</h4>
<p><strong>Servers</strong> - 3x 10 ARM vCPUs, 16GB Ram, 500GB (~50GB for Talos and the rest for Longhorn) storage running <a href="https://www.talos.dev" target="_blank">Talos</a> and Kubernetes. <dl>
<p><strong>Storage</strong> - Longhorn ensures that there are at least 2 copies across the nodes.</p> <dt><strong>Servers</strong></dt>
<p><strong>Backups and Content</strong> - Backups and content are stored in S3 storage hosted by BackBlaze with CloudFlare providing CDN. I've already run through disaster recovery and restored database backups from S3.</p> <dd>3x 10 ARM vCPUs, 16GB Ram, 500GB (~50GB for Talos and the rest for Longhorn) storage running <a href="https://www.talos.dev" rel="nofollow">Talos</a> and Kubernetes.</dd>
<p><strong>CDN</strong> - CloudFlare provides CDN and special rules have been set up to be sure that as much as possible is cached.</p> <dt><strong>Storage</strong></dt>
<p><strong>Security</strong> - ports are closed off to the world and secured with CloudFlare tunnels and TailScale as the only means of access outside of website access.</p> <dd>Longhorn ensures that there are at least 2 copies across the nodes.</dd>
<p><strong>Observability and Logging</strong> - OpenObserve dashboards and log aggregation.</p> <dt><strong>Backups and Content</strong></dt>
<p><strong>Domain</strong> - domain is provided by CloudFlare</p> <dd>Stored in S3 storage hosted by BackBlaze with CloudFlare providing CDN. I've already run through disaster recovery and restored database backups from S3.</dd>
<p><strong>Services</strong> - Typical arrangement for services is that web services get 2 instances and workers get 1 instance with autoscaling. Web pods scale horizontally and workers scale vertically, then horizontally.</p> <dt><strong>CDN</strong></dt>
<p><strong>Source Code</strong> - If I get the source code to where I'm comfortable sharing, I'll post a link here. And if you're experienced in k8s, I'd always appreciate a review. :)</p> <dd>CloudFlare provides CDN and special rules have been set up to be sure that as much as possible is cached.</dd>
<p><strong>Costs</strong><br /> <dt><strong>Security</strong></dt>
VPS servers - 3x ~$13 / mth = $40/mth<br /> <dd>Ports are closed off to the world and secured with CloudFlare tunnels and secure VPN as the only means of access outside of website access.</dd>
Domain name - $12/year<br /> <dt><strong>Observability and Logging</strong></dt>
Backblaze - $6/TB/mth = ~$2/mth<br /> <dd>OpenObserve dashboards and log aggregation.</dd>
Total: ~$45/mth</p> <dt><strong>Domain</strong></dt>
<dd>Domain is provided by CloudFlare</dd>
<dt><strong>Services</strong></dt>
<dd>Typical arrangement for services is that web services get 2 instances and workers get 1 instance with autoscaling. Web pods scale horizontally and workers scale vertically, then horizontally.</dd>
<dt><strong>Source Code</strong></dt>
<dd>If I get the source code to where I'm comfortable sharing, I'll post a link here. And if you're experienced in k8s, I'd always appreciate a review. :)</dd>
</dl>
</section>
<p>
<span><strong>Costs</strong></span><br />
<span>
VPS servers - 3x ~$13 / mth = $40/mth<br />
Domain name - $12/year<br />
Backblaze - $6/TB/mth = ~$2/mth<br />
Total: ~$45/mth
</span>
</p>
</main> </main>
<footer class="container"> <footer class="container">
<p>Contact: <a href="mailto:sysadmin@mailkeyboardvagabond.com">sysadmin@mail.keyboardvagabond.com</a>, any of the @sysadmin accounts on the instances</p> <p>
<p>Copyright 2025 Keyboard Vagabond</p> <span>Contact: <a href="mailto:admin@mail.keyboardvagabond.com">admin@mail.keyboardvagabond.com</a>, any of the @sysadmin accounts on the instances</span></br>
<span>Copyright 2025 Keyboard Vagabond</span>
</p>
</footer> </footer>
<script src="scripts/bundle.js"></script> <script src="scripts/bundle.js"></script>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 313 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 153 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 157 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 157 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 153 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 160 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 155 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 163 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 157 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 180 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 179 KiB

View File

@@ -5,23 +5,71 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="light dark"> <meta name="color-scheme" content="light dark">
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline' https://static.cloudflareinsights.com; object-src 'none';">
<!-- Primary SEO Meta Tags -->
<title>Keyboard Vagabond - Fediverse for Digital Nomads</title>
<meta name="title" content="Keyboard Vagabond - Fediverse for Digital Nomads">
<meta name="description" content="Join Keyboard Vagabond's fediverse community for digital nomads, remote workers, and travelers on Mastodon, Piefed, Pixelfed, Bookwyrm, and Write Freely.">
<meta name="keywords" content="fediverse, digital nomad, remote work, travel, mastodon, piefed, pixelfed, bookwyrm, write freely, social media alternative, keyboard vagabond">
<meta name="author" content="Keyboard Vagabond">
<meta name="robots" content="index, follow">
<link rel="canonical" href="https://www.keyboardvagabond.com/">
<link rel="sitemap" type="application/xml" title="Sitemap" href="/sitemap.xml">
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://www.keyboardvagabond.com/">
<meta property="og:title" content="Keyboard Vagabond - Fediverse for Digital Nomads">
<meta property="og:description" content="Join Keyboard Vagabond's fediverse community for digital nomads, remote workers, and travelers on Mastodon, Piefed, Pixelfed, Bookwyrm, and Write Freely.">
<meta property="og:image" content="https://picsur.keyboardvagabond.com/i/076a5b88-20d3-426e-ad7f-f24a68d3fa70.jpg?width=1200">
<meta property="og:site_name" content="Keyboard Vagabond">
<meta property="og:locale" content="en_US">
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:url" content="https://www.keyboardvagabond.com/">
<meta property="twitter:title" content="Keyboard Vagabond - Fediverse for Digital Nomads">
<meta property="twitter:description" content="Join Keyboard Vagabond's fediverse community for digital nomads, remote workers, and travelers on Mastodon, Piefed, Pixelfed, Bookwyrm, and Write Freely.">
<meta property="twitter:image" content="https://picsur.keyboardvagabond.com/i/076a5b88-20d3-426e-ad7f-f24a68d3fa70.jpg?width=1200">
<!-- Additional SEO -->
<meta name="theme-color" content="#2d5a27">
<meta name="msapplication-TileColor" content="#2d5a27">
<meta name="application-name" content="Keyboard Vagabond">
<!-- Additional SEO for sitelinks -->
<meta name="format-detection" content="telephone=no">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="Keyboard Vagabond">
<!-- Help search engines understand site structure -->
<link rel="home" href="https://www.keyboardvagabond.com/">
<link rel="index" href="https://www.keyboardvagabond.com/">
<!-- Structured Data -->
<script type="application/ld+json" src="/structured-data.json"></script>
<link rel="stylesheet" href="css/pico.jade.min.css"> <link rel="stylesheet" href="css/pico.jade.min.css">
<link rel="stylesheet" href="site-styles/style.css"> <link rel="stylesheet" href="site-styles/style.css">
<title>Keyboard Vagabond</title>
</head> </head>
<body> <body>
<!-- Header --> <!-- Header -->
<header class="container"> <header class="container">
<nav> <nav role="navigation" aria-label="Main navigation">
<!-- Desktop Navigation --> <!-- Desktop Navigation -->
<ul class="desktop-nav"> <ul class="desktop-nav">
<li><a target="_blank" href="https://mastodon.keyboardvagabond.com/public">Mastodon</a></li> <li><a target="_blank" href="https://mastodon.keyboardvagabond.com/public" rel="noopener">Mastodon</a></li>
<li><a target="_blank" href="https://piefed.keyboardvagabond.com">Piefed</a></li> <li><a target="_blank" href="https://piefed.keyboardvagabond.com" rel="noopener">Piefed</a></li>
<li><a target="_blank" href="https://pixelfed.keyboardvagabond.com">Pixelfed</a></li> <li><a target="_blank" href="https://pixelfed.keyboardvagabond.com" rel="noopener">Pixelfed</a></li>
<li><a target="_blank" href="https://bookwyrm.keyboardvagabond.com">Bookwyrm</a></li> <li><a target="_blank" href="https://bookwyrm.keyboardvagabond.com" rel="noopener">Bookwyrm</a></li>
<li><a target="_blank" href="https://blog.keyboardvagabond.com">Write Freely</a></li> <li><a target="_blank" href="https://blog.keyboardvagabond.com" rel="noopener">Write Freely</a></li>
<li><a target="_blank" href="https://picsur.keyboardvagabond.com">Picsur</a></li> <li><a target="_blank" href="https://picsur.keyboardvagabond.com" rel="noopener">Picsur</a></li>
</ul> </ul>
<!-- Mobile Navigation --> <!-- Mobile Navigation -->
@@ -30,12 +78,12 @@
<details class="dropdown"> <details class="dropdown">
<summary role="button" class="secondary">Sites</summary> <summary role="button" class="secondary">Sites</summary>
<ul> <ul>
<li><a target="_blank" href="https://mastodon.keyboardvagabond.com/public">Mastodon</a></li> <li><a target="_blank" href="https://mastodon.keyboardvagabond.com/public" rel="noopener">Mastodon</a></li>
<li><a target="_blank" href="https://piefed.keyboardvagabond.com">Piefed</a></li> <li><a target="_blank" href="https://piefed.keyboardvagabond.com" rel="noopener">Piefed</a></li>
<li><a target="_blank" href="https://pixelfed.keyboardvagabond.com">Pixelfed</a></li> <li><a target="_blank" href="https://pixelfed.keyboardvagabond.com" rel="noopener">Pixelfed</a></li>
<li><a target="_blank" href="https://bookwyrm.keyboardvagabond.com">Bookwyrm</a></li> <li><a target="_blank" href="https://bookwyrm.keyboardvagabond.com" rel="noopener">Bookwyrm</a></li>
<li><a target="_blank" href="https://blog.keyboardvagabond.com">Write Freely</a></li> <li><a target="_blank" href="https://blog.keyboardvagabond.com" rel="noopener">Write Freely</a></li>
<li><a target="_blank" href="https://picsur.keyboardvagabond.com">Picsur</a></li> <li><a target="_blank" href="https://picsur.keyboardvagabond.com" rel="noopener">Picsur</a></li>
</ul> </ul>
</details> </details>
</li> </li>
@@ -52,32 +100,123 @@
</nav> </nav>
</header> </header>
<div class="banner-container"> <section class="banner-container" role="banner" aria-labelledby="main-heading">
<img src="https://picsur.keyboardvagabond.com/i/076a5b88-20d3-426e-ad7f-f24a68d3fa70.jpg?width=2048" alt="Scenic mountain road with snow-capped peaks" class="banner"> <picture>
<h1 class="banner-title">Welcome to Keyboard Vagabond</h1> <source media="(min-width: 1500px" srcset="https://picsur.keyboardvagabond.com/i/9334b89b-ce7a-49e7-ab85-43047e00f9ee.jpg">
<p class="banner-subtitle">A space in the fediverse for travelers, nomads, and vagabonds of all kinds</p> <source media="(min-width: 1000px" srcset="https://picsur.keyboardvagabond.com/i/9334b89b-ce7a-49e7-ab85-43047e00f9ee.jpg?width=1500&shrinkonly=true">
</div> <source media="(min-width: 500px" srcset="https://picsur.keyboardvagabond.com/i/9334b89b-ce7a-49e7-ab85-43047e00f9ee.jpg?width=1000&shrinkonly=true">
<img src="https://picsur.keyboardvagabond.com/i/9334b89b-ce7a-49e7-ab85-43047e00f9ee.jpg" alt="Cartoon view of mountains with a dirt trail in the center. A wooden sign reads Keyboard Vagabond.">
</picture>
<p class="banner-subtitle">A fediverse community for digital nomads, remote workers, and travel enthusiasts</p>
</section>
<!-- Main content --> <!-- Main content -->
<main class="container"> <main class="container">
<p>We welcome you to this space to connect with others, share information, create memories, and help each other <section aria-labelledby="intro-heading">
to travel responsibly and considerately as we explore the world.</p> <h2 id="intro-heading" class="sr-only">About Keyboard Vagabond</h2>
<p>&lt;mascot here? :D &gt; <p>We welcome you to this space to connect with others, share information, create memories, and help each other
<strong>mascot intro and why it was chosen</strong> to travel responsibly and considerately as we explore the world.</p>
</p> <p><strong>Here, you are a member, not just a user</strong> - We want to create community in this space, being
<p><strong>Here, you are a member, not just a user</strong> - We want to create community in this space, being respectful of each other as well as the places we go and the people we see. This space is what we make of it.
respectful of each other as well as the places go and the people we see. This space is what we make of it. </p>
</p>
<h2>What you'll find here</h2> <div class="mascot-section">
<p>Keyboard Vagabond hosts the various fediverse alternatives to big tech and participates in the network. <div class="mascot-image">
Available to you is:</p> <picture>
<source srcset="assets/logos/primary-dark.svg" media="(prefers-color-scheme: dark)" type="image/svg+xml" />
<source srcset="assets/logos/primary-light.svg" media="(prefers-color-scheme: light)" type="image/svg+xml" />
<img src="assets/logos/primary-light.svg" alt="Otter mascot holding a laptop with the words Keyboard Vagabond circling it" width="200" type="image/svg+xml" />
</picture>
</div>
<span class="mascot-text">
Seymour is the official mascot of Keyboard Vagabond and a nomadic otter who always wants to see more!
He makes his home wherever he goes and adapts to his new environments.
Let our adorable friend guide you through the fediverse and give you a break from the
algorithms and attention grabbing of corporate social media.
He was lovingly created by nomad and designer Lori Kendall at <a href="https://branddesigncreate.com" target="_blank" rel="nofollow">branddesigncreate.com</a>,
Instagram at <a target="_blank" rel="nofollow" href="https://www.instagram.com/branddesigncreate/">@branddesigncreate</a>.
</span>
</div>
</section>
<section aria-labelledby="services-heading">
<!-- Services Summary for SEO -->
<div class="services-summary" role="region" aria-label="Quick access to all services">
<h3>Our Fediverse Services</h3>
<div class="service-links">
<a href="https://mastodon.keyboardvagabond.com" target="_blank" class="service-link" aria-label="Mastodon - Social Media Alternative">
<picture class="service-icon">
<source srcset="assets/logos/mastodon-dark.svg" media="(prefers-color-scheme: dark)" type="image/svg+xml"/>
<source srcset="assets/logos/mastodon-light.svg" media="(prefers-color-scheme: light)" type="image/svg+xml"/>
<img src="assets/logos/mastodon-light.svg" alt="Mastodon mascot holding a phone with mastodon mascot" type="image/svg+xml"/>
</picture>
<span class="service-text">
<strong>Mastodon</strong> - Microblogging
</span>
</a>
<a href="https://piefed.keyboardvagabond.com" target="_blank" class="service-link" aria-label="Piefed - Community Forum">
<picture class="service-icon">
<source srcset="assets/logos/piefed-dark.svg" media="(prefers-color-scheme: dark)" type="image/svg+xml" />
<source srcset="assets/logos/piefed-light.svg" media="(prefers-color-scheme: light)" type="image/svg+xml" />
<img src="assets/logos/piefed-light.svg" alt="Piefed mascot holding a pie" type="image/svg+xml" />
</picture>
<span class="service-text">
<strong>Piefed</strong> - Community Forum
</span>
</a>
<a href="https://pixelfed.keyboardvagabond.com" target="_blank" class="service-link" aria-label="Pixelfed - Photo Sharing">
<picture class="service-icon">
<source srcset="assets/logos/pixelfed-dark.svg" media="(prefers-color-scheme: dark)" type="image/svg+xml"/>
<source srcset="assets/logos/pixelfed-light.svg" media="(prefers-color-scheme: light)" type="image/svg+xml"/>
<img src="assets/logos/pixelfed-light.svg" alt="Pixelfed mascot holding camera" type="image/svg+xml"/>
</picture>
<span class="service-text">
<strong>Pixelfed</strong> - Photo Sharing
</span>
</a>
<a href="https://bookwyrm.keyboardvagabond.com" target="_blank" class="service-link" aria-label="Bookwyrm - Book Reviews">
<picture class="service-icon">
<source srcset="assets/logos/bookwyrm-dark.svg" media="(prefers-color-scheme: dark)" type="image/svg+xml"/>
<source srcset="assets/logos/bookwyrm-light.svg" media="(prefers-color-scheme: light)" type="image/svg+xml"/>
<img src="assets/logos/bookwyrm-light.svg" alt="Bookwyrm mascot holding book" type="image/svg+xml"/>
</picture>
<span class="service-text">
<strong>Bookwyrm</strong> - Book Reviews
</span>
</a>
<a href="https://blog.keyboardvagabond.com" target="_blank" class="service-link" aria-label="Write Freely - Blogging">
<picture class="service-icon">
<source srcset="assets/logos/default-dark.svg" media="(prefers-color-scheme: dark)" type="image/svg+xml"/>
<source srcset="assets/logos/default-light.svg" media="(prefers-color-scheme: light)" type="image/svg+xml"/>
<img src="assets/logos/default-light.svg" alt="Default mascot holding laptop" type="image/svg+xml"/>
</picture>
<span class="service-text">
<strong>Write Freely</strong> - Blogging
</span>
</a>
<a href="https://picsur.keyboardvagabond.com" target="_blank" class="service-link" aria-label="Picsur - Image Hosting">
<picture class="service-icon">
<source srcset="assets/logos/default-dark.svg" media="(prefers-color-scheme: dark)" type="image/svg+xml"/>
<source srcset="assets/logos/default-light.svg" media="(prefers-color-scheme: light)" type="image/svg+xml"/>
<img src="assets/logos/default-light.svg" alt="Default mascot holding laptop" type="image/svg+xml"/>
</picture>
<span class="service-text">
<strong>Picsur</strong> - Image Hosting
</span>
</a>
</div>
</div>
</section>
<h3>Detailed Service Information</h3>
<ul> <ul>
<li><strong><a href="https://piefed.keyboardvagabond.com">Piefed</a></strong> - Similar to and compatible <li><strong><a href="https://piefed.keyboardvagabond.com">Piefed</a></strong> - An alternative to Reddit and similar to and compatible
with <a href="https://join-lemmy.org">Lemmy</a>, but with extra features for topics and communities. with <a href="https://join-lemmy.org">Lemmy</a>, but with extra features for topics and communities.
Both are an alternative to Reddit. See more at <a href="https://join-lemmy.org">join-lemmy.org</a> and See more at <a href="https://join-lemmy.org">join-lemmy.org</a> and
<a href="https://join.piefed.social/try/">join.piefed.social</a>.</li> <a href="https://join.piefed.social/try/">join.piefed.social</a>.</li>
<li><strong><a href="https://pixelfed.keyboardvagabond.com">Pixelfed</a></strong> - An alternative to <li><strong><a href="https://pixelfed.keyboardvagabond.com">Pixelfed</a></strong> - Like old instagram, where you can share your photos, albums, and create stories. See more at <a
instagram, where you can share your photos, albums, and create stories. See more at <a
href="https://pixelfed.org/servers">pixelfed.org</a>.</li> href="https://pixelfed.org/servers">pixelfed.org</a>.</li>
<li><strong><a href="https://mastodon.keyboardvagabond.com">Mastodon</a></strong> - An alternative to <li><strong><a href="https://mastodon.keyboardvagabond.com">Mastodon</a></strong> - An alternative to
twitter. Create your timeline by following users and hashtags. See more at <a twitter. Create your timeline by following users and hashtags. See more at <a
@@ -94,7 +233,7 @@
signup, as there's no automated signup process.</li> signup, as there's no automated signup process.</li>
</ul> </ul>
<strong>Mobile Apps</strong> <strong>Mobile Apps</strong>
<p> <section>
<ul> <ul>
<li> <li>
<strong>Piefed</strong> - Piefed has support in <a href="https://www.lemmyapps.com/Interstellar" target="_blank">Interstellar (cross platform)</a> and experimental support in <strong>Piefed</strong> - Piefed has support in <a href="https://www.lemmyapps.com/Interstellar" target="_blank">Interstellar (cross platform)</a> and experimental support in
@@ -109,8 +248,9 @@
</ul> </ul>
<span>The rest of the applications are accessible on the websites and can be added to your homescreen.</span><br/> <span>The rest of the applications are accessible on the websites and can be added to your homescreen.</span><br/>
<span><strong>Signing in</strong> - you'll search the server that you want to join, such as mastodon.social or mastodon.keyboardvagabond.com, or use the provided default instances.</span> <span><strong>Signing in</strong> - you'll search the server that you want to join, such as mastodon.social or mastodon.keyboardvagabond.com, or use the provided default instances.</span>
</p> </section>
<h1>What is the Fediverse</h1> <h2>What is the Fediverse</h2>
<section>
<p>The fediverse is a collection of big-tech alternative social media that all communicate with each other using <p>The fediverse is a collection of big-tech alternative social media that all communicate with each other using
the same protocol, called ActivityPub. This means that not only can different “instances,” such as this the same protocol, called ActivityPub. This means that not only can different “instances,” such as this
community, participate with discussions on other servers, but also with the different applications. You can community, participate with discussions on other servers, but also with the different applications. You can
@@ -120,14 +260,16 @@
make a video on PeerTube (Youtube alternative) and discuss on Mastodon. Your Write Freely blog posts will, make a video on PeerTube (Youtube alternative) and discuss on Mastodon. Your Write Freely blog posts will,
if you enable it, be visible on Mastodon and people can follow your blog account.</p> if you enable it, be visible on Mastodon and people can follow your blog account.</p>
<p>Check out this amazing four minute video by <a href="https://news.elenarossini.com">Elena Rossini</a>. You can follow her <p>Check out this amazing four minute video by <a href="https://news.elenarossini.com">Elena Rossini</a>. You can follow her
on Mastodon by searching @_elena@mastodon.social.</p> on Mastodon by searching <a href="https://mastodon.social/@_elena@mastodon.social" target="_blank">@_elena@mastodon.social</a>.</p>
<div class="video-container"> <div class="video-container">
<iframe title="Introducing the Fediverse: a New Era of Social Media" <iframe title="Introducing the Fediverse: a New Era of Social Media"
src="https://videos.elenarossini.com/videos/embed/64VuNCccZNrP4u9MfgbhkN" frameborder="0" allowfullscreen="" src="https://videos.elenarossini.com/videos/embed/64VuNCccZNrP4u9MfgbhkN" allowfullscreen=""
sandbox="allow-same-origin allow-scripts allow-popups allow-forms" sandbox="allow-same-origin allow-scripts allow-popups allow-forms"
loading="lazy"></iframe> loading="lazy"></iframe>
</div> </div>
</section>
<h2>Getting started in the Fediverse</h2> <h2>Getting started in the Fediverse</h2>
<section>
<p>The best way to see what's available in the fediverse is to start off on a larger instance, which will be <p>The best way to see what's available in the fediverse is to start off on a larger instance, which will be
following the most content. From there you can follow communities and members on Keyboard Vagabond, or following the most content. From there you can follow communities and members on Keyboard Vagabond, or
export your profiles, follows, etc over to Keyboard Vagabond. You can, of course, have multiple profiles export your profiles, follows, etc over to Keyboard Vagabond. You can, of course, have multiple profiles
@@ -137,14 +279,14 @@
<strong>Popular places to get started are</strong>:<br/> <strong>Popular places to get started are</strong>:<br/>
<ul> <ul>
<li> <li>
<strong><a href="https://lemmy.zip" target="_blank">Lemmy.zip</a></strong> for Lemmy or <strong><a <strong><a href="https://lemmy.zip" target="_blank" rel="nofollow">Lemmy.zip</a></strong> for Lemmy or <strong><a
href="https://piefed.social" target="_blank">Piefed.social</a></strong> for PieFed, which is the software that this href="https://piefed.social" target="_blank" rel="nofollow">Piefed.social</a></strong> for PieFed, which is the software that this
community runs. community runs.
</li> </li>
<li><strong><a href="https://mastodon.social" target="_blank">Mastodon.social</a></strong> - the largest mastodon instance. <li><strong><a href="https://mastodon.social" target="_blank" rel="nofollow">Mastodon.social</a></strong> - the largest mastodon instance.
Search for hashtags or check out whats trending and follow them to create your timeline. Search for hashtags or check out whats trending and follow them to create your timeline.
</li> </li>
<li><strong><a href="https://pixelfed.social" target="_blank">Pixelfed.social</a></strong> - the largest Pixelfed instance. Also <li><strong><a href="https://pixelfed.social" target="_blank" rel="nofollow">Pixelfed.social</a></strong> - the largest Pixelfed instance. Also
search for timelines and hashtags of things that you're interested in or what may be trending. search for timelines and hashtags of things that you're interested in or what may be trending.
</li> </li>
</ul> </ul>
@@ -155,9 +297,9 @@
Each instance should have one, including those of Keyboard Vagabond. These agreements help us to Each instance should have one, including those of Keyboard Vagabond. These agreements help us to
create welcoming spaces that are free of harassment and bigotry. create welcoming spaces that are free of harassment and bigotry.
</span> </span>
</p> </section>
<h2>Creating your experience</h2> <h2>Creating your experience</h2>
<section>
<p> <p>
In the fediverse, there are no algorithms. No one is trying to harvest your data or monetize your attention. In the fediverse, there are no algorithms. No one is trying to harvest your data or monetize your attention.
No one is trying to push anything in front of you. Try searching for hashtags like <a No one is trying to push anything in front of you. Try searching for hashtags like <a
@@ -167,7 +309,7 @@
<div class="photo-gallery"> <div class="photo-gallery">
<div class="photo-item"> <div class="photo-item">
<small>Check out that Explore button on the main page.</small> <small>Check out that Explore button on the main page.</small>
<img src="https://picsur.keyboardvagabond.com/i/e8ab899f-5bb4-4cf1-b531-8621ac93670e.png?width=400" <img src="https://picsur.keyboardvagabond.com/i/e8ab899f-5bb4-4cf1-b531-8621ac93670e.png?width=600"
alt="Explore button on main page" loading="lazy" /> alt="Explore button on main page" loading="lazy" />
</div> </div>
<div class="photo-item"> <div class="photo-item">
@@ -188,10 +330,26 @@
alt="Local topics" loading="lazy" /> alt="Local topics" loading="lazy" />
</div> </div>
</div> </div>
</section>
</main> </main>
<!-- Footer --> <!-- Footer -->
<footer></footer> <footer class="container" role="contentinfo">
<hr>
<section aria-labelledby="footer-heading">
<h2 id="footer-heading" class="sr-only">Footer Information</h2>
<p><strong>Keyboard Vagabond</strong> - A fediverse community for digital nomads, remote workers, and travel enthusiasts.</p>
<span>Connect with us across the fediverse:</span><br/>
<ul>
<li><a href="https://mastodon.keyboardvagabond.com">Mastodon</a>&nbsp;- Social Media</li>
<li><a href="https://piefed.keyboardvagabond.com">Piefed</a>&nbsp;- Community Forum</li>
<li><a href="https://pixelfed.keyboardvagabond.com">Pixelfed</a>&nbsp;- Photo Sharing</li>
<li><a href="https://bookwyrm.keyboardvagabond.com">Bookwyrm</a>&nbsp;- Book Reviews</li>
<li><a href="https://blog.keyboardvagabond.com">Write Freely</a>&nbsp;- Blogging</li>
</ul>
<p><small>&copy; 2025 Keyboard Vagabond. Part of the decentralized fediverse network.</small></p>
</section>
</footer>
<script src="scripts/bundle.js"></script> <script src="scripts/bundle.js"></script>
</body> </body>

22
public/robots.txt Normal file
View File

@@ -0,0 +1,22 @@
User-agent: *
Allow: /
# Sitemap location
Sitemap: https://www.keyboardvagabond.com/sitemap.xml
# Disallow crawling of fediverse subdomains to focus on main site
Disallow: /mastodon/
Disallow: /piefed/
Disallow: /pixelfed/
Disallow: /bookwyrm/
Disallow: /blog/
Disallow: /picsur/
# Allow important pages
Allow: /about.html
Allow: /css/
Allow: /scripts/
Allow: /site-styles/
# Crawl delay to be respectful
Crawl-delay: 1

View File

@@ -40,10 +40,7 @@
applyTheme(storedTheme); applyTheme(storedTheme);
themeToggle.addEventListener('click', toggleTheme);
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
// Only apply theme if user hasn't made an explicit choice
const storedTheme = getStoredTheme(); const storedTheme = getStoredTheme();
if (storedTheme === 'auto') { if (storedTheme === 'auto') {
applyTheme(); applyTheme();

View File

@@ -1,11 +1,20 @@
.banner-container { .banner-container {
position: relative; position: relative;
width: 100%; width: 100%;
max-height: 40vh;
min-height: 200px;
overflow: hidden; overflow: hidden;
} }
.banner-container picture {
width: 100%;
display: block;
}
.banner-container img {
width: 100%;
height: auto;
display: block;
}
.banner { .banner {
width: 100%; width: 100%;
max-height: 40vh; max-height: 40vh;
@@ -82,8 +91,9 @@
.banner-title { .banner-title {
font-size: 1.8rem; font-size: 1.8rem;
top: calc(var(--pico-typography-spacing-vertical) * 0.5); top: calc(var(--pico-typography-spacing-vertical) * 0.5);
left: var(--pico-spacing); /* left: 50%; */
right: var(--pico-spacing); right: auto;
/* transform: translateX(-50%); */
} }
} }
@@ -127,6 +137,7 @@
width: 100%; width: 100%;
max-width: 560px; max-width: 560px;
margin: var(--pico-typography-spacing-vertical) auto; margin: var(--pico-typography-spacing-vertical) auto;
border: 0;
} }
.video-container::before { .video-container::before {
@@ -258,18 +269,44 @@
} }
/* Mobile breakpoint - hide desktop nav and show mobile nav */ /* Mobile breakpoint - hide desktop nav and show mobile nav */
@media (max-width: 768px) { @media (max-width: 767px) {
.desktop-nav { .desktop-nav {
display: none; display: none;
} }
.mobile-nav { .mobile-nav {
display: flex; display: flex;
flex: 0 0 auto;
}
/* Arrange mobile nav items horizontally */
header nav {
flex-direction: row !important;
justify-content: space-between !important;
align-items: center;
gap: 0;
}
/* Make the About/theme toggle ul display as flex */
header nav > ul:not(.desktop-nav):not(.mobile-nav) {
display: flex !important;
gap: calc(var(--pico-spacing) * 0.5);
align-items: center;
flex: 0 0 auto;
margin: 0;
}
header nav > ul:not(.desktop-nav):not(.mobile-nav) {
padding: 0;
}
header nav > ul:not(.desktop-nav):not(.mobile-nav) li {
margin: 0;
} }
/* Style the dropdown for mobile */
.mobile-nav .dropdown { .mobile-nav .dropdown {
width: 100%; width: auto;
min-width: 120px;
} }
.mobile-nav .dropdown summary { .mobile-nav .dropdown summary {
@@ -327,4 +364,272 @@
.mobile-nav li { .mobile-nav li {
position: relative; position: relative;
} }
}
/* Services Summary Styling */
.services-summary {
background: var(--pico-muted-color);
background: rgba(var(--pico-muted-color-rgb, 0, 0, 0), 0.05);
border: 1px solid var(--pico-muted-border-color);
border-radius: var(--pico-border-radius);
padding: var(--pico-spacing);
margin: var(--pico-typography-spacing-vertical) 0;
}
.services-summary h3 {
margin-top: 0;
margin-bottom: var(--pico-typography-spacing-vertical);
color: var(--pico-primary);
}
.service-links {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: calc(var(--pico-spacing) * 0.75);
margin: 0;
}
.service-link {
display: flex;
flex-direction: column;
align-items: center;
gap: calc(var(--pico-spacing) * 0.75);
padding: calc(var(--pico-spacing) * 0.75);
background: var(--pico-background-color);
border: 1px solid var(--pico-muted-border-color);
border-radius: var(--pico-border-radius);
text-decoration: none;
color: var(--pico-color);
transition: all 0.2s ease;
text-align: center;
}
.service-link:hover {
background: var(--pico-primary);
color: var(--pico-primary-inverse);
border-color: var(--pico-primary);
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.service-icon {
flex-shrink: 0;
display: block;
width: 60px;
height: 60px;
}
.service-icon img {
width: 60px;
height: 60px;
object-fit: contain;
display: block;
}
.service-text {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
.service-link strong {
display: block;
font-size: 1.1em;
margin-bottom: calc(var(--pico-spacing) * 0.25);
}
/* Screen reader only class */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* Main Navigation Structure */
.main-nav {
display: flex;
align-items: center;
gap: calc(var(--pico-spacing) * 0.5);
margin: 0;
padding: 0;
list-style: none;
}
.main-nav li {
margin: 0;
}
.main-nav a[aria-current="page"] {
font-weight: bold;
color: var(--pico-primary);
}
/* Services Navigation */
.services-nav {
margin-top: calc(var(--pico-spacing) * 0.5);
padding-top: calc(var(--pico-spacing) * 0.5);
border-top: 1px solid var(--pico-muted-border-color);
}
.services-nav h2 {
margin: 0 0 calc(var(--pico-spacing) * 0.5) 0;
font-size: 1em;
color: var(--pico-muted-color);
}
/* Navigation Layout */
header nav {
display: flex;
flex-direction: column;
gap: calc(var(--pico-spacing) * 0.5);
}
/* Desktop navigation layout */
@media (min-width: 768px) {
header nav {
flex-direction: row;
justify-content: space-between;
align-items: flex-start;
flex-wrap: wrap;
}
.main-nav {
order: 1;
flex: 0 0 auto;
}
.services-nav {
order: 2;
margin-top: 0;
padding-top: 0;
border-top: none;
flex: 1 1 100%;
margin-top: calc(var(--pico-spacing) * 0.5);
}
}
/* Mobile navigation layout */
@media (max-width: 767px) {
.services-nav {
margin-top: calc(var(--pico-spacing) * 0.5);
padding-top: calc(var(--pico-spacing) * 0.5);
border-top: 1px solid var(--pico-muted-border-color);
}
}
/* Responsive service links */
@media (max-width: 768px) {
.service-links {
grid-template-columns: 1fr;
gap: calc(var(--pico-spacing) * 0.5);
}
.service-link {
padding: calc(var(--pico-spacing) * 0.5);
}
}
footer ul {
display: flex;
flex-wrap: wrap;
gap: calc(var(--pico-spacing) * 0.5);
list-style: none;
padding: 0;
margin: 0;
align-items: center;
}
footer li {
margin: 0;
display: flex;
align-items: center;
}
footer li:not(:last-child)::after {
content: "|";
margin: 0 calc(var(--pico-spacing) * 0.5);
color: var(--pico-muted-color);
font-weight: normal;
}
@media (max-width: 480px) {
footer ul {
flex-direction: column;
align-items: center;
gap: calc(var(--pico-spacing) * 0.5);
}
footer li:not(:last-child)::after {
content: none; /* Remove delimiters on mobile */
}
}
/* Mascot section styling */
.mascot-section {
display: flex;
align-items: center;
gap: calc(var(--pico-spacing) * 2);
margin: var(--pico-typography-spacing-vertical) 0;
}
.mascot-image {
flex-shrink: 0;
min-width: 120px; /* Prevent image from disappearing on thin screens */
}
.mascot-image picture,
.mascot-image img {
display: block;
max-width: 100%;
height: auto;
}
.mascot-image img {
width: 200px;
max-width: min(200px, 30vw);
min-width: 120px; /* Ensure image stays visible on very thin screens */
}
.mascot-text {
flex: 1;
min-width: 0; /* Allows text to shrink properly */
}
/* Stack on small devices */
@media (max-width: 576px) {
.mascot-section {
flex-direction: column;
align-items: center;
gap: var(--pico-spacing);
}
.mascot-image {
min-width: auto; /* Reset min-width when stacked */
}
.mascot-image img {
width: 100%;
max-width: 200px;
min-width: 120px; /* Keep minimum size even when stacked */
}
.mascot-text {
text-align: center;
}
}
/* Very thin screens - ensure image stays visible */
@media (max-width: 400px) {
.mascot-image img {
min-width: 100px; /* Smaller but still visible on very thin screens */
}
} }

52
public/sitemap.xml Normal file
View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://www.keyboardvagabond.com/</loc>
<lastmod>2025-09-28</lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<url>
<loc>https://www.keyboardvagabond.com/about.html</loc>
<lastmod>2025-09-28</lastmod>
<changefreq>monthly</changefreq>
<priority>0.8</priority>
</url>
<!-- Fediverse Services - These are external but part of our ecosystem -->
<url>
<loc>https://mastodon.keyboardvagabond.com/</loc>
<lastmod>2025-09-28</lastmod>
<changefreq>daily</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://piefed.keyboardvagabond.com/</loc>
<lastmod>2025-09-28</lastmod>
<changefreq>daily</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://pixelfed.keyboardvagabond.com/</loc>
<lastmod>2025-09-28</lastmod>
<changefreq>daily</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://bookwyrm.keyboardvagabond.com/</loc>
<lastmod>2025-09-28</lastmod>
<changefreq>daily</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://blog.keyboardvagabond.com/</loc>
<lastmod>2025-09-28</lastmod>
<changefreq>daily</changefreq>
<priority>0.7</priority>
</url>
<url>
<loc>https://picsur.keyboardvagabond.com/</loc>
<lastmod>2025-09-28</lastmod>
<changefreq>weekly</changefreq>
<priority>0.6</priority>
</url>
</urlset>

143
public/structured-data.json Normal file
View File

@@ -0,0 +1,143 @@
[
{
"@context": "https://schema.org",
"@type": "WebSite",
"name": "Keyboard Vagabond",
"url": "https://www.keyboardvagabond.com",
"description": "A fediverse community for digital nomads, remote workers, and travel enthusiasts offering multiple social media alternatives including Mastodon, Piefed, Pixelfed, Bookwyrm, and Write Freely.",
"publisher": {
"@type": "Organization",
"name": "Keyboard Vagabond",
"logo": "https://picsur.michaeldileo.org/i/b4579222-367e-4601-b634-87e6d66e3a99.png?width=400",
"foundingDate": "2025",
"areaServed": "Global",
"knowsAbout": ["Fediverse", "Digital Nomadism", "Remote Work", "Travel", "Social Media Alternatives"]
},
"mainEntity": {
"@type": "ItemList",
"name": "Keyboard Vagabond Services",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"url": "https://www.keyboardvagabond.com/",
"description": "Keyboard Vagabond main landing page"
},
{
"@type": "ListItem",
"position": 2,
"name": "About",
"url": "https://www.keyboardvagabond.com/about.html",
"description": "Learn more about Keyboard Vagabond community"
},
{
"@type": "ListItem",
"position": 3,
"name": "Mastodon",
"url": "https://mastodon.keyboardvagabond.com",
"description": "Social media alternative to Twitter"
},
{
"@type": "ListItem",
"position": 4,
"name": "Piefed",
"url": "https://piefed.keyboardvagabond.com",
"description": "Community forum alternative to Reddit"
},
{
"@type": "ListItem",
"position": 5,
"name": "Pixelfed",
"url": "https://pixelfed.keyboardvagabond.com",
"description": "Photo sharing platform alternative to Instagram"
},
{
"@type": "ListItem",
"position": 6,
"name": "Bookwyrm",
"url": "https://bookwyrm.keyboardvagabond.com",
"description": "Book discussion and review platform"
},
{
"@type": "ListItem",
"position": 7,
"name": "Write Freely",
"url": "https://blog.keyboardvagabond.com",
"description": "Minimalist federated blogging platform"
},
{
"@type": "ListItem",
"position": 8,
"name": "Picsur",
"url": "https://picsur.keyboardvagabond.com",
"description": "Image hosting service"
}
]
}
},
{
"@context": "https://schema.org",
"@type": "Organization",
"name": "Keyboard Vagabond",
"url": "https://www.keyboardvagabond.com",
"logo": "https://picsur.michaeldileo.org/i/b4579222-367e-4601-b634-87e6d66e3a99.png?width=400",
"description": "A fediverse community for digital nomads, remote workers, and travel enthusiasts offering multiple social media alternatives including Mastodon, Piefed, Pixelfed, Bookwyrm, and Write Freely.",
"sameAs": [
"https://mastodon.keyboardvagabond.com",
"https://piefed.keyboardvagabond.com",
"https://pixelfed.keyboardvagabond.com",
"https://bookwyrm.keyboardvagabond.com",
"https://blog.keyboardvagabond.com"
],
"foundingDate": "2025",
"areaServed": "Global",
"knowsAbout": ["Fediverse", "Digital Nomadism", "Remote Work", "Travel", "Social Media Alternatives"],
"hasOfferCatalog": {
"@type": "OfferCatalog",
"name": "Fediverse Services",
"itemListElement": [
{
"@type": "Offer",
"itemOffered": {
"@type": "Service",
"name": "Mastodon",
"description": "Decentralized social media alternative to Twitter"
}
},
{
"@type": "Offer",
"itemOffered": {
"@type": "Service",
"name": "Piefed",
"description": "Community forum alternative to Reddit with enhanced features"
}
},
{
"@type": "Offer",
"itemOffered": {
"@type": "Service",
"name": "Pixelfed",
"description": "Photo sharing platform alternative to Instagram"
}
},
{
"@type": "Offer",
"itemOffered": {
"@type": "Service",
"name": "Bookwyrm",
"description": "Book discussion and review platform"
}
},
{
"@type": "Offer",
"itemOffered": {
"@type": "Service",
"name": "Write Freely",
"description": "Minimalist federated blogging platform"
}
}
]
}
}
]