responsive navbar #14

Merged
michael_dileo merged 8 commits from dev into main 2025-08-14 19:43:31 +00:00
5 changed files with 172 additions and 28 deletions

View File

@@ -51,7 +51,14 @@ async function createOptimizedBuild() {
'video-container', 'video-container',
'photo-gallery', 'photo-gallery',
'photo-item', 'photo-item',
'dark' 'dark',
// Responsive navigation classes
'desktop-nav',
'mobile-nav',
'dropdown',
// Button classes
'secondary',
'outline'
], ],
deep: [ deep: [
/^--pico-/, /^--pico-/,
@@ -60,7 +67,12 @@ async function createOptimizedBuild() {
/:hover/, /:hover/,
/:focus/, /:focus/,
/:active/, /:active/,
/:visited/ /:visited/,
// Dropdown and interactive states
/\[open\]/,
/\[role=/,
/details/,
/summary/
], ],
// Keep base typography selectors that are essential for proper font rendering // Keep base typography selectors that are essential for proper font rendering
greedy: [ greedy: [
@@ -78,7 +90,14 @@ async function createOptimizedBuild() {
/^a$/, /^a$/,
/^a:/, // Add anchor pseudo-classes like a:hover, a:focus /^a:/, // Add anchor pseudo-classes like a:hover, a:focus
/^a\./, // Add anchor with classes like a.secondary /^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, variables: true,
@@ -109,20 +128,21 @@ async function createOptimizedBuild() {
}) })
]).process(combinedCSS, { from: undefined }); ]).process(combinedCSS, { from: undefined });
await fs.writeFile('dist/css/styles.css', minifiedCSS.css); // Combine with custom site styles
// Also minify custom site styles
const customCSS = await fs.readFile('public/site-styles/style.css', 'utf8'); 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({ cssnano({
preset: ['default', { preset: ['default', {
discardComments: { removeAll: true }, discardComments: { removeAll: true },
normalizeWhitespace: 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 // Step 3: JavaScript Bundling and Minification
console.log('📦 Step 3: Bundling and minifying JavaScript...'); console.log('📦 Step 3: Bundling and minifying JavaScript...');
@@ -148,9 +168,9 @@ async function createOptimizedBuild() {
} }
// Calculate compression results // Calculate compression results
const originalCSSSize = (await fs.stat('public/css/pico.green.min.css')).size + const originalCSSSize = (await fs.stat('public/css/pico.jade.min.css')).size +
(await fs.stat('public/css/pico.colors.min.css')).size; (await fs.stat('public/css/pico.min.css')).size;
const optimizedCSSSize = (await fs.stat('dist/css/styles.css')).size; const optimizedCSSSize = (await fs.stat('dist/css/bundle.css')).size;
const cssReduction = ((originalCSSSize - optimizedCSSSize) / originalCSSSize * 100).toFixed(1); const cssReduction = ((originalCSSSize - optimizedCSSSize) / originalCSSSize * 100).toFixed(1);
const originalHTMLSize = (await fs.stat('public/index.html')).size + const originalHTMLSize = (await fs.stat('public/index.html')).size +
@@ -238,7 +258,7 @@ async function bundleAndMinifyJS() {
hoist_vars: false, hoist_vars: false,
if_return: true, if_return: true,
join_vars: true, join_vars: true,
cascade: true,
side_effects: true side_effects: true
}, },
mangle: { mangle: {
@@ -271,10 +291,9 @@ async function minifyHTMLFile(inputPath, outputPath) {
// Update CSS references for production and add script reference // Update CSS references for production and add script reference
let updatedHTML = html let updatedHTML = html
.replace(/<link rel="stylesheet" href="css\/pico\.green\.min\.css">/g, '') .replace(/<link rel="stylesheet" href="css\/pico\.jade\.min\.css">/g, '')
.replace(/<link rel="stylesheet" href="css\/pico\.colors\.min\.css">/g, '')
.replace(/<link rel="stylesheet" href="site-styles\/style\.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">') '<link rel="stylesheet" href="css/bundle.css">')
.replace(/<\/body>/g, '<script src="scripts/bundle.js"></script></body>'); .replace(/<\/body>/g, '<script src="scripts/bundle.js"></script></body>');
const minified = await htmlMinify(updatedHTML, { const minified = await htmlMinify(updatedHTML, {

View File

@@ -40,14 +40,21 @@ http {
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;
# 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)$ {
expires 1w; # expires 1w;
add_header Cache-Control "public, immutable"; # 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$ { location ~* \.html$ {
expires 1h; # expires 1h;
add_header Cache-Control "public"; # 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 /health { location /health {

View File

@@ -4,22 +4,39 @@
<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">
<link rel="stylesheet" href="css/pico.green.min.css"> <link rel="stylesheet" href="css/pico.jade.min.css">
<link rel="stylesheet" href="css/pico.colors.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>
</head> </head>
<body> <body>
<header class="container"> <header class="container">
<nav> <nav>
<ul> <!-- Desktop Navigation -->
<li><a target="_blank"href="https://mastodon.keyboardvagabond.com/public">Mastodon</a></li> <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://piefed.keyboardvagabond.com">Piefed</a></li>
<li><a target="_blank" href="https://pixelfed.keyboardvagabond.com">Pixelfed</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://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://blog.keyboardvagabond.com">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">Picsur</a></li>
</ul> </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>
<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>
</details>
</li>
</ul>
<ul> <ul>
<li><a href="index.html">Home</a></li> <li><a href="index.html">Home</a></li>
<li> <li>

View File

@@ -5,8 +5,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">
<link rel="stylesheet" href="css/pico.green.min.css"> <link rel="stylesheet" href="css/pico.jade.min.css">
<link rel="stylesheet" href="css/pico.colors.min.css">
<link rel="stylesheet" href="site-styles/style.css"> <link rel="stylesheet" href="site-styles/style.css">
<title>Keyboard Vagabond</title> <title>Keyboard Vagabond</title>
</head> </head>
@@ -15,14 +14,33 @@
<!-- Header --> <!-- Header -->
<header class="container"> <header class="container">
<nav> <nav>
<ul> <!-- Desktop Navigation -->
<li><a target="_blank"href="https://mastodon.keyboardvagabond.com/public">Mastodon</a></li> <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://piefed.keyboardvagabond.com">Piefed</a></li>
<li><a target="_blank" href="https://pixelfed.keyboardvagabond.com">Pixelfed</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://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://blog.keyboardvagabond.com">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">Picsur</a></li>
</ul> </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>
<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>
</details>
</li>
</ul>
<ul> <ul>
<li><a href="about.html">About</a></li> <li><a href="about.html">About</a></li>
<li> <li>

View File

@@ -245,3 +245,86 @@
display: block; display: block;
line-height: 1; 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: 768px) {
.desktop-nav {
display: none;
}
.mobile-nav {
display: flex;
}
/* Style the dropdown for mobile */
.mobile-nav .dropdown {
width: 100%;
}
.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;
}
}