Compare commits
34 Commits
f739f87ce4
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7e6b00b21d | |||
| 4f693a66fd | |||
| 925ec7cf1e | |||
| 96351533c6 | |||
| 5738815b91 | |||
| a7e7697fec | |||
| 9e15866468 | |||
| e0572f4471 | |||
| 5e87f52e5e | |||
| b9ddb56bbe | |||
| bf76ca99d6 | |||
| 6338bc3aae | |||
|
|
3b8f90a2b7 | ||
|
|
7b9b926fcd | ||
|
|
421cd8a00a | ||
|
|
44d8ca6887 | ||
|
|
316675bd64 | ||
|
|
59df2555ce | ||
|
|
89c110e0c8 | ||
|
|
48ab0691b5 | ||
|
|
f9011f5ef4 | ||
|
|
fc0c2f5a1c | ||
|
|
9447305dea | ||
|
|
18abe47f5c | ||
| f2b47f38b4 | |||
|
|
f316a049a6 | ||
|
|
77b43080a0 | ||
| 8e19c2ee20 | |||
|
|
29de615702 | ||
|
|
b8d1c81620 | ||
|
|
4e22a6c687 | ||
| e1a8d089a9 | |||
|
|
7a8ef0fab7 | ||
| 38c707885a |
103
minify-build.js
@@ -35,10 +35,10 @@ async function createOptimizedBuild() {
|
||||
const purgeResults = await new PurgeCSS().purge({
|
||||
content: [
|
||||
'public/index.html',
|
||||
'public/about.html'
|
||||
'public/about.html',
|
||||
],
|
||||
css: [
|
||||
'public/css/pico.jade.css'
|
||||
'public/css/pico.jade.css',
|
||||
// 'public/css/pico.colors.min.css'
|
||||
],
|
||||
safelist: {
|
||||
@@ -51,7 +51,14 @@ async function createOptimizedBuild() {
|
||||
'video-container',
|
||||
'photo-gallery',
|
||||
'photo-item',
|
||||
'dark'
|
||||
'dark',
|
||||
// Responsive navigation classes
|
||||
'desktop-nav',
|
||||
'mobile-nav',
|
||||
'dropdown',
|
||||
// Button classes
|
||||
'secondary',
|
||||
'outline'
|
||||
],
|
||||
deep: [
|
||||
/^--pico-/,
|
||||
@@ -60,7 +67,12 @@ async function createOptimizedBuild() {
|
||||
/:hover/,
|
||||
/:focus/,
|
||||
/:active/,
|
||||
/:visited/
|
||||
/:visited/,
|
||||
// Dropdown and interactive states
|
||||
/\[open\]/,
|
||||
/\[role=/,
|
||||
/details/,
|
||||
/summary/
|
||||
],
|
||||
// Keep base typography selectors that are essential for proper font rendering
|
||||
greedy: [
|
||||
@@ -78,7 +90,14 @@ async function createOptimizedBuild() {
|
||||
/^a$/,
|
||||
/^a:/, // Add anchor pseudo-classes like a:hover, a:focus
|
||||
/^a\./, // Add anchor with classes like a.secondary
|
||||
/^a\[/ // Add anchor with attributes like a[role=button]
|
||||
/^a\[/, // Add anchor with attributes like a[role=button]
|
||||
// Dropdown and button elements
|
||||
/^details$/,
|
||||
/^summary$/,
|
||||
/^button$/,
|
||||
/^button:/,
|
||||
/^button\./,
|
||||
/^button\[/
|
||||
]
|
||||
},
|
||||
variables: true,
|
||||
@@ -109,20 +128,21 @@ async function createOptimizedBuild() {
|
||||
})
|
||||
]).process(combinedCSS, { from: undefined });
|
||||
|
||||
await fs.writeFile('dist/css/styles.css', minifiedCSS.css);
|
||||
|
||||
// Also minify custom site styles
|
||||
// Combine with custom site styles
|
||||
const customCSS = await fs.readFile('public/site-styles/style.css', 'utf8');
|
||||
const minifiedCustomCSS = await postcss([
|
||||
const combinedWithCustomCSS = minifiedCSS.css + '\n' + customCSS;
|
||||
|
||||
// Minify the combined CSS bundle
|
||||
const finalMinifiedCSS = await postcss([
|
||||
cssnano({
|
||||
preset: ['default', {
|
||||
discardComments: { removeAll: true },
|
||||
normalizeWhitespace: true
|
||||
}]
|
||||
})
|
||||
]).process(customCSS, { from: undefined });
|
||||
]).process(combinedWithCustomCSS, { from: undefined });
|
||||
|
||||
await fs.writeFile('dist/site-styles/style.css', minifiedCustomCSS.css);
|
||||
await fs.writeFile('dist/css/bundle.css', finalMinifiedCSS.css);
|
||||
|
||||
// Step 3: JavaScript Bundling and Minification
|
||||
console.log('📦 Step 3: Bundling and minifying JavaScript...');
|
||||
@@ -133,24 +153,29 @@ async function createOptimizedBuild() {
|
||||
await minifyHTMLFile('public/index.html', 'dist/index.html');
|
||||
await minifyHTMLFile('public/about.html', 'dist/about.html');
|
||||
|
||||
// Step 5: Copy assets
|
||||
console.log('📁 Step 5: Copying assets...');
|
||||
// Step 5: Copy assets and SEO files
|
||||
console.log('📁 Step 5: Copying assets and SEO files...');
|
||||
try {
|
||||
const assets = await fs.readdir('public/assets');
|
||||
for (const asset of assets) {
|
||||
await fs.copyFile(
|
||||
path.join('public/assets', asset),
|
||||
path.join('dist/assets', asset)
|
||||
);
|
||||
}
|
||||
await copyDirectoryRecursive('public/assets', 'dist/assets');
|
||||
console.log(' ✅ Assets directory copied recursively');
|
||||
} catch (err) {
|
||||
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
|
||||
const originalCSSSize = (await fs.stat('public/css/pico.green.min.css')).size +
|
||||
(await fs.stat('public/css/pico.colors.min.css')).size;
|
||||
const optimizedCSSSize = (await fs.stat('dist/css/styles.css')).size;
|
||||
const originalCSSSize = (await fs.stat('public/css/pico.jade.min.css')).size +
|
||||
(await fs.stat('public/css/pico.min.css')).size;
|
||||
const optimizedCSSSize = (await fs.stat('dist/css/bundle.css')).size;
|
||||
const cssReduction = ((originalCSSSize - optimizedCSSSize) / originalCSSSize * 100).toFixed(1);
|
||||
|
||||
const originalHTMLSize = (await fs.stat('public/index.html')).size +
|
||||
@@ -238,7 +263,7 @@ async function bundleAndMinifyJS() {
|
||||
hoist_vars: false,
|
||||
if_return: true,
|
||||
join_vars: true,
|
||||
cascade: true,
|
||||
|
||||
side_effects: true
|
||||
},
|
||||
mangle: {
|
||||
@@ -266,16 +291,36 @@ 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) {
|
||||
const html = await fs.readFile(inputPath, 'utf8');
|
||||
|
||||
// Update CSS references for production and add script reference
|
||||
// Update CSS references for production and update script reference
|
||||
let updatedHTML = html
|
||||
.replace(/<link rel="stylesheet" href="css\/pico\.green\.min\.css">/g, '')
|
||||
.replace(/<link rel="stylesheet" href="css\/pico\.colors\.min\.css">/g, '')
|
||||
.replace(/<link rel="stylesheet" href="css\/pico\.jade\.min\.css">/g, '')
|
||||
.replace(/<link rel="stylesheet" href="site-styles\/style\.css">/g,
|
||||
'<link rel="stylesheet" href="css/styles.css"><link rel="stylesheet" href="site-styles/style.css">')
|
||||
.replace(/<\/body>/g, '<script src="scripts/bundle.js"></script></body>');
|
||||
'<link rel="stylesheet" href="css/bundle.css">')
|
||||
.replace(/<script src="site-scripts\/theme-toggle\.js"><\/script>/g, ''); // Remove individual script references
|
||||
|
||||
const minified = await htmlMinify(updatedHTML, {
|
||||
collapseWhitespace: true,
|
||||
|
||||
32
nginx.conf
@@ -16,6 +16,7 @@ http {
|
||||
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
charset utf-8;
|
||||
|
||||
access_log /dev/stdout;
|
||||
error_log /dev/stderr;
|
||||
@@ -39,15 +40,42 @@ http {
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" 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
|
||||
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
|
||||
expires 1w;
|
||||
expires 10m;
|
||||
add_header Cache-Control "public, immutable";
|
||||
# add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
# add_header Pragma "no-cache";
|
||||
# add_header Expires "0";
|
||||
}
|
||||
|
||||
location ~* \.html$ {
|
||||
expires 1h;
|
||||
expires 10m;
|
||||
add_header Cache-Control "public";
|
||||
# add_header Cache-Control "no-cache, no-store, must-revalidate";
|
||||
# add_header Pragma "no-cache";
|
||||
# add_header Expires "0";
|
||||
}
|
||||
|
||||
location ~* \.json$ {
|
||||
expires 1d;
|
||||
add_header Cache-Control "public, must-revalidate";
|
||||
}
|
||||
|
||||
location /health {
|
||||
|
||||
@@ -4,14 +4,29 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<link rel="stylesheet" href="css/pico.green.min.css">
|
||||
<link rel="stylesheet" href="css/pico.colors.min.css">
|
||||
<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="site-styles/style.css">
|
||||
<title>Keyboard Vagabond - About</title>
|
||||
</head>
|
||||
<body>
|
||||
<header class="container">
|
||||
<nav>
|
||||
<!-- Desktop Navigation -->
|
||||
<ul class="desktop-nav">
|
||||
<li><a target="_blank" href="https://mastodon.keyboardvagabond.com/public">Mastodon</a></li>
|
||||
<li><a target="_blank" href="https://piefed.keyboardvagabond.com">Piefed</a></li>
|
||||
<li><a target="_blank" href="https://pixelfed.keyboardvagabond.com">Pixelfed</a></li>
|
||||
<li><a target="_blank" href="https://bookwyrm.keyboardvagabond.com">Bookwyrm</a></li>
|
||||
<li><a target="_blank" href="https://blog.keyboardvagabond.com">Write Freely</a></li>
|
||||
<li><a target="_blank" href="https://picsur.keyboardvagabond.com">Picsur</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- Mobile Navigation -->
|
||||
<ul class="mobile-nav">
|
||||
<li>
|
||||
<details class="dropdown">
|
||||
<summary role="button" class="secondary">Sites</summary>
|
||||
<ul>
|
||||
<li><a target="_blank" href="https://mastodon.keyboardvagabond.com/public">Mastodon</a></li>
|
||||
<li><a target="_blank" href="https://piefed.keyboardvagabond.com">Piefed</a></li>
|
||||
@@ -20,6 +35,9 @@
|
||||
<li><a target="_blank" href="https://blog.keyboardvagabond.com">Write Freely</a></li>
|
||||
<li><a target="_blank" href="https://picsur.keyboardvagabond.com">Picsur</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li>
|
||||
@@ -34,10 +52,10 @@
|
||||
|
||||
<main class="container">
|
||||
<h1>About Keyboard Vagabond</h1>
|
||||
<p>Keyboard Vagabond is a place where nomads, travelers, backpacker, whoever, can come together in a digital space that is free of advertising and the attention economy to share information and experiences. It is a place of mutual respect, courtesy, and understanding not just for the members who join, but also for those people and places we encounter on our journeys.</p>
|
||||
<p>Keyboard Vagabond is a place where nomads, travelers, backpackers, whoever, can come together in a digital space that is free of advertising and the attention economy to share information and experiences. It is a place of mutual respect, courtesy, and understanding not just for the members who join, but also for those people and places we encounter on our journeys.</p>
|
||||
|
||||
<h4>Why Keyboard Vagabond</h4>
|
||||
<p>Keyboard Vagabond was made because I saw multiple instances of people saying that, while there are travel communities on different instances, there was a space specifically for nomads, so I thought I would make it.</p>
|
||||
<p>Keyboard Vagabond was made because I saw multiple instances of people saying that, while there are travel communities on different instances, there was not a space specifically for nomads, so I thought I would make it.</p>
|
||||
|
||||
<h2>What to expect and commitments</h2>
|
||||
<p><strong>Moderation style</strong> -
|
||||
@@ -48,34 +66,57 @@
|
||||
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>
|
||||
<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">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> -
|
||||
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>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>
|
||||
</section>
|
||||
<section>
|
||||
<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">Talos</a> and Kubernetes.
|
||||
<p><strong>Storage</strong> - Longhorn ensures that there are at least 2 copies across the nodes.</p>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<p><strong>Observability and Logging</strong> - OpenObserve dashboards and log aggregation.</p>
|
||||
<p><strong>Domain</strong> - domain is provided by CloudFlare</p>
|
||||
<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>
|
||||
<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>
|
||||
<p><strong>Costs</strong><br />
|
||||
<dl>
|
||||
<dt><strong>Servers</strong></dt>
|
||||
<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>
|
||||
<dt><strong>Storage</strong></dt>
|
||||
<dd>Longhorn ensures that there are at least 2 copies across the nodes.</dd>
|
||||
<dt><strong>Backups and Content</strong></dt>
|
||||
<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>
|
||||
<dt><strong>CDN</strong></dt>
|
||||
<dd>CloudFlare provides CDN and special rules have been set up to be sure that as much as possible is cached.</dd>
|
||||
<dt><strong>Security</strong></dt>
|
||||
<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>
|
||||
<dt><strong>Observability and Logging</strong></dt>
|
||||
<dd>OpenObserve dashboards and log aggregation.</dd>
|
||||
<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</p>
|
||||
Total: ~$45/mth
|
||||
</span>
|
||||
</p>
|
||||
</main>
|
||||
|
||||
<footer class="container">
|
||||
<p>Contact: <a href="mailto:admin@keyboardvagabond.com">admin@keyboardvagabond.com</a>, any of the @sysadmin accounts on the instances</p>
|
||||
<p></p>
|
||||
<p>Copyright 2025 Keyboard Vagabond</p>
|
||||
<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>
|
||||
|
||||
<script src="scripts/bundle.js"></script>
|
||||
|
||||
1
public/assets/logos/bookwyrm-dark.svg
Normal file
|
After Width: | Height: | Size: 313 KiB |
1
public/assets/logos/bookwyrm-light.svg
Normal file
|
After Width: | Height: | Size: 153 KiB |
1
public/assets/logos/default-dark.svg
Normal file
|
After Width: | Height: | Size: 157 KiB |
1
public/assets/logos/default-light.svg
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
public/assets/logos/favicon.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
1
public/assets/logos/mastodon-dark.svg
Normal file
|
After Width: | Height: | Size: 157 KiB |
1
public/assets/logos/mastodon-light.svg
Normal file
|
After Width: | Height: | Size: 153 KiB |
1
public/assets/logos/piefed-dark.svg
Normal file
|
After Width: | Height: | Size: 160 KiB |
1
public/assets/logos/piefed-light.svg
Normal file
|
After Width: | Height: | Size: 155 KiB |
1
public/assets/logos/pixelfed-dark.svg
Normal file
|
After Width: | Height: | Size: 163 KiB |
1
public/assets/logos/pixelfed-light.svg
Normal file
|
After Width: | Height: | Size: 157 KiB |
1
public/assets/logos/primary-dark.svg
Normal file
|
After Width: | Height: | Size: 180 KiB |
1
public/assets/logos/primary-light.svg
Normal file
|
After Width: | Height: | Size: 179 KiB |
@@ -5,24 +5,90 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="color-scheme" content="light dark">
|
||||
<link rel="stylesheet" href="css/pico.green.min.css">
|
||||
<link rel="stylesheet" href="css/pico.colors.min.css">
|
||||
<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="site-styles/style.css">
|
||||
<title>Keyboard Vagabond</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Header -->
|
||||
<header class="container">
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a target="_blank"href="https://mastodon.keyboardvagabond.com/public">Mastodon</a></li>
|
||||
<li><a target="_blank" href="https://piefed.keyboardvagabond.com">Piefed</a></li>
|
||||
<li><a target="_blank" href="https://pixelfed.keyboardvagabond.com">Pixelfed</a></li>
|
||||
<li><a target="_blank" href="https://bookwyrm.keyboardvagabond.com">Bookwyrm</a></li>
|
||||
<li><a target="_blank" href="https://blog.keyboardvagabond.com">Write Freely</a></li>
|
||||
<li><a target="_blank" href="https://picsur.keyboardvagabond.com">Picsur</a></li>
|
||||
<nav role="navigation" aria-label="Main navigation">
|
||||
<!-- Desktop Navigation -->
|
||||
<ul class="desktop-nav">
|
||||
<li><a target="_blank" href="https://mastodon.keyboardvagabond.com/public" rel="noopener">Mastodon</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" rel="noopener">Pixelfed</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" rel="noopener">Write Freely</a></li>
|
||||
<li><a target="_blank" href="https://picsur.keyboardvagabond.com" rel="noopener">Picsur</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- Mobile Navigation -->
|
||||
<ul class="mobile-nav">
|
||||
<li>
|
||||
<details class="dropdown">
|
||||
<summary role="button" class="secondary">Sites</summary>
|
||||
<ul>
|
||||
<li><a target="_blank" href="https://mastodon.keyboardvagabond.com/public" rel="noopener">Mastodon</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" rel="noopener">Pixelfed</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" rel="noopener">Write Freely</a></li>
|
||||
<li><a target="_blank" href="https://picsur.keyboardvagabond.com" rel="noopener">Picsur</a></li>
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul>
|
||||
<li><a href="about.html">About</a></li>
|
||||
<li>
|
||||
@@ -34,32 +100,123 @@
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<div class="banner-container">
|
||||
<img src="https://picsur.michaeldileo.org/i/36516edf-1a67-4565-aa8e-c10dbe743fd6.jpg?width=2048" alt="Scenic mountain road with snow-capped peaks" class="banner">
|
||||
<h1 class="banner-title">Welcome to Keyboard Vagabond</h1>
|
||||
<p class="banner-subtitle">A space in the fediverse for travelers, nomads, and vagabonds of all kinds</p>
|
||||
</div>
|
||||
<section class="banner-container" role="banner" aria-labelledby="main-heading">
|
||||
<picture>
|
||||
<source media="(min-width: 1500px" srcset="https://picsur.keyboardvagabond.com/i/9334b89b-ce7a-49e7-ab85-43047e00f9ee.jpg">
|
||||
<source media="(min-width: 1000px" srcset="https://picsur.keyboardvagabond.com/i/9334b89b-ce7a-49e7-ab85-43047e00f9ee.jpg?width=1500&shrinkonly=true">
|
||||
<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 class="container">
|
||||
<section aria-labelledby="intro-heading">
|
||||
<h2 id="intro-heading" class="sr-only">About Keyboard Vagabond</h2>
|
||||
<p>We welcome you to this space to connect with others, share information, create memories, and help each other
|
||||
to travel responsibly and considerately as we explore the world.</p>
|
||||
<p><mascot here? :D >
|
||||
<strong>mascot intro and why it was chosen</strong>
|
||||
</p>
|
||||
<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 go and the people we see. This space is what we make of it.
|
||||
respectful of each other as well as the places we go and the people we see. This space is what we make of it.
|
||||
</p>
|
||||
<h2>What you'll find here</h2>
|
||||
<p>Keyboard Vagabond hosts the various fediverse alternatives to big tech and participates in the network.
|
||||
Available to you is:</p>
|
||||
|
||||
<div class="mascot-section">
|
||||
<div class="mascot-image">
|
||||
<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>
|
||||
<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.
|
||||
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>
|
||||
<li><strong><a href="https://pixelfed.keyboardvagabond.com">Pixelfed</a></strong> - An alternative to
|
||||
instagram, where you can share your photos, albums, and create stories. See more at <a
|
||||
<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
|
||||
href="https://pixelfed.org/servers">pixelfed.org</a>.</li>
|
||||
<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
|
||||
@@ -75,7 +232,25 @@
|
||||
hosting, to use for images in your blogs. You can also use Pixelfed. Contact the admin for Picsur
|
||||
signup, as there's no automated signup process.</li>
|
||||
</ul>
|
||||
<h1>What is the Fediverse</h1>
|
||||
<strong>Mobile Apps</strong>
|
||||
<section>
|
||||
<ul>
|
||||
<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
|
||||
<a href="https://vger.app/settings/install">Voyager (Android)</a>, though features like topics and communities are best served on the website.
|
||||
</li>
|
||||
<li>
|
||||
<strong><a href="https://pixelfed.org/mobile-apps" target="_blank">Pixelfed</a></strong>, <strong><a href="https://joinmastodon.org/apps" target="_blank">Mastodon</a></strong> - have native mobile apps
|
||||
</li>
|
||||
<li>
|
||||
<strong>Lemmy</strong> - many of your favorite reddit apps can be used with Lemmy
|
||||
</li>
|
||||
</ul>
|
||||
<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>
|
||||
</section>
|
||||
<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
|
||||
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
|
||||
@@ -85,64 +260,96 @@
|
||||
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>
|
||||
<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@aseachange.com.</p>
|
||||
on Mastodon by searching <a href="https://mastodon.social/@_elena@mastodon.social" target="_blank">@_elena@mastodon.social</a>.</p>
|
||||
<div class="video-container">
|
||||
<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"
|
||||
loading="lazy"></iframe>
|
||||
</div>
|
||||
<h2>How to get started in the Fediverse</h2>
|
||||
</section>
|
||||
<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
|
||||
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
|
||||
across instances.</p>
|
||||
<p>Once you choose and place and sign up, wait for your admin approval. Many instances require manual account
|
||||
approval to prevent spam.</p>
|
||||
<p><strong>Popular places to get started are</strong>:</p>
|
||||
<strong><a href="https://lemmy.zip">Lemmy.zip</a></strong> for Lemmy or <strong><a
|
||||
href="https://piefed.social">Piefed.social</a></strong> for PieFed, which is the software that this
|
||||
<strong>Popular places to get started are</strong>:<br/>
|
||||
<ul>
|
||||
<li>
|
||||
<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" rel="nofollow">Piefed.social</a></strong> for PieFed, which is the software that this
|
||||
community runs.
|
||||
</p>
|
||||
<p><strong><a href="https://mastodon.social">Mastodon.social</a></strong> - the largest mastodon instance.
|
||||
Search for hashtags or check out whats trending and follow them to create your timeline.</p>
|
||||
<p><strong><a href="https://pixelfed.social">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.</p>
|
||||
|
||||
</li>
|
||||
<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.
|
||||
</li>
|
||||
<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.
|
||||
</li>
|
||||
</ul>
|
||||
<strong>Codes of Conduct</strong><br />
|
||||
<span>
|
||||
While corporate media is primarily interested in the Terms of Service, the Fediverse cares about the
|
||||
Code of Conduct, the agreement of how we treat each other and what behavior is and is not tolerated.
|
||||
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.
|
||||
</span>
|
||||
</section>
|
||||
<h2>Creating your experience</h2>
|
||||
<section>
|
||||
<p>
|
||||
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
|
||||
href="https://mastodon.keyboardvagabond.com/tags/travel">#travel</a> or <a
|
||||
href="https://mastodon.keyboardvagabond.com/tags/travelphotography">#travelphotography</a>.</p>
|
||||
href="https://mastodon.keyboardvagabond.com/tags/travel" target="_blank">#travel</a>, <a
|
||||
href="https://mastodon.keyboardvagabond.com/tags/travelphotography" target="_blank">#travelphotography</a>, or <a
|
||||
href="https://mastodon.keyboardvagabond.com/tags/EscapeFlights" target="_blank">#EscapeFlights</a>.</p>
|
||||
<div class="photo-gallery">
|
||||
<div class="photo-item">
|
||||
<small>Check out that Explore button on the main page.</small>
|
||||
<img src="https://picsur.keyboardvagabond.com/i/48624639-e731-4e50-a166-f88cf9eccded.jpg?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" />
|
||||
</div>
|
||||
<div class="photo-item">
|
||||
<small>Visit the Posts, Hashtags, and News tabs to see what's on the server</small>
|
||||
<img src="https://picsur.keyboardvagabond.com/i/d05996c7-5c23-450e-9ca0-b7e532d1c700.jpg?width=700"
|
||||
<img src="https://picsur.keyboardvagabond.com/i/aea3c3c5-b011-4680-be4c-96fc1fdb009a.png?width=700"
|
||||
alt="Posts, Hashtags, and News tabs" loading="lazy" />
|
||||
</div>
|
||||
<div class="photo-item">
|
||||
<small>Look at the local communities to see what communities have been created on this server specifically. The rest of the communities are ones that this server is following.
|
||||
Following communities on other instances will result in them being shown here.
|
||||
</small>
|
||||
<img src="https://picsur.michaeldileo.org/i/e07dddbc-d264-4f12-8877-17eed896026a.jpg?width=700"
|
||||
<img src="https://picsur.keyboardvagabond.com/i/b5d316d9-8958-47bf-ba74-fb853e2d2be8.png?width=700"
|
||||
alt="Communities, Local Communities" loading="lazy" />
|
||||
</div>
|
||||
<div class="photo-item">
|
||||
<small>Scroll all the way to the bottom to find local topics. Topics are groups of communities. You can suggest more in the Meta community.</small>
|
||||
<img src="https://picsur.michaeldileo.org/i/5a977aa0-b9bb-4376-a348-f879c7162f63.jpg?width=700"
|
||||
<img src="https://picsur.keyboardvagabond.com/i/e5909364-3c4f-4bca-8631-5b1225781177.png?width=700"
|
||||
alt="Local topics" loading="lazy" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- 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> - Social Media</li>
|
||||
<li><a href="https://piefed.keyboardvagabond.com">Piefed</a> - Community Forum</li>
|
||||
<li><a href="https://pixelfed.keyboardvagabond.com">Pixelfed</a> - Photo Sharing</li>
|
||||
<li><a href="https://bookwyrm.keyboardvagabond.com">Bookwyrm</a> - Book Reviews</li>
|
||||
<li><a href="https://blog.keyboardvagabond.com">Write Freely</a> - Blogging</li>
|
||||
</ul>
|
||||
<p><small>© 2025 Keyboard Vagabond. Part of the decentralized fediverse network.</small></p>
|
||||
</section>
|
||||
</footer>
|
||||
|
||||
<script src="scripts/bundle.js"></script>
|
||||
</body>
|
||||
|
||||
22
public/robots.txt
Normal 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
|
||||
@@ -2,31 +2,24 @@
|
||||
const themeToggle = document.getElementById('theme-toggle');
|
||||
const themeIcon = document.getElementById('theme-icon');
|
||||
|
||||
function getStoredTheme() {
|
||||
return localStorage.getItem('picoPreferredColorScheme') || 'auto';
|
||||
}
|
||||
|
||||
function storeTheme(theme) {
|
||||
localStorage.setItem('picoPreferredColorScheme', theme);
|
||||
}
|
||||
|
||||
function getSystemTheme() {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
function getStoredTheme() {
|
||||
return localStorage.getItem('picoPreferredColorScheme');
|
||||
}
|
||||
|
||||
function applyTheme() {
|
||||
const storedTheme = getStoredTheme();
|
||||
let actualTheme;
|
||||
|
||||
if (storedTheme === 'auto') {
|
||||
actualTheme = getSystemTheme();
|
||||
} else {
|
||||
actualTheme = storedTheme;
|
||||
function getCurrentTheme() {
|
||||
return document.documentElement.getAttribute('data-theme') ||
|
||||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
||||
}
|
||||
|
||||
document.documentElement.setAttribute('data-theme', actualTheme);
|
||||
|
||||
if (actualTheme === 'dark') {
|
||||
function applyTheme(currentTheme) {
|
||||
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
||||
document.documentElement.setAttribute('data-theme', newTheme);
|
||||
if (newTheme === 'dark') {
|
||||
themeIcon.textContent = '☀️';
|
||||
themeToggle.setAttribute('aria-pressed', 'true');
|
||||
} else {
|
||||
@@ -35,34 +28,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
function toggleTheme() {
|
||||
const currentStored = getStoredTheme();
|
||||
let nextTheme;
|
||||
themeToggle.addEventListener('click', () => {
|
||||
const currentTheme = getCurrentTheme();
|
||||
|
||||
if (currentStored === 'auto') {
|
||||
nextTheme = 'light';
|
||||
} else if (currentStored === 'light') {
|
||||
nextTheme = 'dark';
|
||||
} else {
|
||||
nextTheme = 'auto';
|
||||
}
|
||||
|
||||
storeTheme(nextTheme);
|
||||
applyTheme();
|
||||
}
|
||||
applyTheme(currentTheme);
|
||||
storeTheme(currentTheme);
|
||||
});
|
||||
|
||||
function init() {
|
||||
if (themeToggle) {
|
||||
applyTheme();
|
||||
let storedTheme = getStoredTheme() || getCurrentTheme();
|
||||
|
||||
themeToggle.addEventListener('click', toggleTheme);
|
||||
applyTheme(storedTheme);
|
||||
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
||||
if (getStoredTheme() === 'auto') {
|
||||
const storedTheme = getStoredTheme();
|
||||
if (storedTheme === 'auto') {
|
||||
applyTheme();
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (document.readyState !== 'loading') {
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
.banner-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
max-height: 40vh;
|
||||
min-height: 200px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.banner-container picture {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.banner-container img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.banner {
|
||||
width: 100%;
|
||||
max-height: 40vh;
|
||||
@@ -82,8 +91,9 @@
|
||||
.banner-title {
|
||||
font-size: 1.8rem;
|
||||
top: calc(var(--pico-typography-spacing-vertical) * 0.5);
|
||||
left: var(--pico-spacing);
|
||||
right: var(--pico-spacing);
|
||||
/* left: 50%; */
|
||||
right: auto;
|
||||
/* transform: translateX(-50%); */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +137,7 @@
|
||||
width: 100%;
|
||||
max-width: 560px;
|
||||
margin: var(--pico-typography-spacing-vertical) auto;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.video-container::before {
|
||||
@@ -245,3 +256,380 @@
|
||||
display: block;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* Responsive Navigation Styles */
|
||||
/* Hide mobile nav by default (desktop first) */
|
||||
.mobile-nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Show desktop nav by default */
|
||||
.desktop-nav {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Mobile breakpoint - hide desktop nav and show mobile nav */
|
||||
@media (max-width: 767px) {
|
||||
.desktop-nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobile-nav {
|
||||
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;
|
||||
}
|
||||
|
||||
.mobile-nav .dropdown {
|
||||
width: auto;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.mobile-nav .dropdown summary {
|
||||
margin: 0;
|
||||
padding: calc(var(--pico-spacing) * 0.5) var(--pico-spacing);
|
||||
border-radius: var(--pico-border-radius);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.mobile-nav .dropdown[open] summary {
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.mobile-nav .dropdown ul {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: var(--pico-background-color);
|
||||
border: 1px solid var(--pico-muted-border-color);
|
||||
border-top: none;
|
||||
border-radius: 0 0 var(--pico-border-radius) var(--pico-border-radius);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
z-index: 1000;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.mobile-nav .dropdown ul li {
|
||||
margin: 0;
|
||||
border-bottom: 1px solid var(--pico-muted-border-color);
|
||||
}
|
||||
|
||||
.mobile-nav .dropdown ul li:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.mobile-nav .dropdown ul li a {
|
||||
display: block;
|
||||
padding: calc(var(--pico-spacing) * 0.75) var(--pico-spacing);
|
||||
text-decoration: none;
|
||||
color: var(--pico-color);
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.mobile-nav .dropdown ul li a:hover {
|
||||
background-color: var(--pico-muted-color);
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
/* Ensure proper positioning context */
|
||||
.mobile-nav li {
|
||||
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
@@ -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
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||