diff --git a/ENHANCEMENTS.md b/ENHANCEMENTS.md new file mode 100644 index 0000000..f144fd7 --- /dev/null +++ b/ENHANCEMENTS.md @@ -0,0 +1,420 @@ +# Blog Enhancements - Implementation Guide + +This document outlines all the enhancements made to your Hugo blog and how to configure them. + +## ๐ŸŽ‰ What's Been Added + +All **5 Quick Win Enhancements** have been successfully implemented: + +1. โœ… **Enhanced Post Cards** - Beautiful, modern post listings with thumbnails and metadata +2. โœ… **Search Functionality** - Fast client-side search with keyboard shortcuts +3. โœ… **Comment System** - Giscus integration for community engagement +4. โœ… **Related Posts** - Smart content recommendations based on tags/categories +5. โœ… **Newsletter Integration** - Email subscription forms for audience building + +--- + +## 1๏ธโƒฃ Enhanced Post Cards + +### What Changed +- Homepage now displays rich post cards instead of simple list items +- Each card shows: + - Thumbnail image (auto-detected from post images) + - Category badges + - Post title + - Description/excerpt + - Publication date + - Reading time +- Responsive grid layout (1 column on mobile, 2 on tablet, 3 on desktop) +- Hover animations and smooth transitions + +### Files Modified +- `layouts/_default/list.html` - Updated post listing template +- `assets/css/style.css` - Added post card styles +- `assets/js/main.js` - Updated loading states + +### No Configuration Needed +This feature works automatically! Your posts will automatically display with enhanced cards. + +--- + +## 2๏ธโƒฃ Search Functionality + +### What Changed +- Beautiful modal search interface (opens with keyboard shortcut) +- Searches through post titles, descriptions, content, tags, and categories +- Keyboard shortcuts: `Cmd/Ctrl + K` to open, `Escape` to close +- Real-time search as you type +- Fuzzy search algorithm for better results +- Clean, distraction-free interface + +### Files Created +- `layouts/_default/index.json` - Search index generator +- `assets/js/search.js` - Search functionality + +### Files Modified +- `hugo.toml` - Added JSON output format +- `layouts/_default/baseof.html` - Added search UI and included search.js +- `assets/css/style.css` - Added search modal styles + +### Configuration +The search works automatically! Hugo will generate `/index.json` with all your posts. + +**Test it:** Press `โŒ˜K` (Mac) or `Ctrl+K` (Windows/Linux) to open search from anywhere on your site + +--- + +## 3๏ธโƒฃ Comment System (Giscus) + +### What Changed +- Added comment section at the bottom of each blog post +- Supports multiple providers: Giscus, Utterances, or Disqus +- Default: Giscus (GitHub Discussions) +- Theme syncs with your site's dark/light mode + +### Files Created +- `layouts/partials/comments.html` - Comments component + +### Files Modified +- `layouts/_default/single.html` - Added comments section +- `hugo.toml` - Added comments configuration +- `assets/css/style.css` - Added comments styles + +### Configuration Required โš ๏ธ + +To enable Giscus comments, follow these steps: + +1. **Create a GitHub repository for comments** (or use an existing one) + - Can be a dedicated repo like `blog-comments` or your blog repo itself + - Must be **public** + +2. **Enable GitHub Discussions** + - Go to your repo โ†’ Settings โ†’ Features + - Check "Discussions" + +3. **Install Giscus App** + - Visit https://giscus.app/ + - Scroll down to "Configuration" + - Enter your repo (e.g., `RexO77/blog-comments`) + - Choose settings (recommended defaults are fine) + - Copy the generated values + +4. **Update `hugo.toml`** + ```toml + [params.comments.giscus] + repo = "YourUsername/your-repo" # UPDATE THIS! + repoId = "R_xxxxxxxxxxxxx" # Get from giscus.app + category = "General" # Discussion category + categoryId = "DIC_xxxxxxxxxx" # Get from giscus.app + ``` + +5. **Disable if not ready** + ```toml + [params.comments] + enabled = false # Set to true when configured + ``` + +--- + +## 4๏ธโƒฃ Related Posts + +### What Changed +- "You Might Also Like" section appears on blog posts +- Shows up to 3 related posts +- Smart matching based on tags, categories, and date +- Beautiful card layout with images + +### Files Created +- `layouts/partials/related-posts.html` - Related posts component + +### Files Modified +- `layouts/_default/single.html` - Added related posts section +- `hugo.toml` - Added related content configuration +- `assets/css/style.css` - Added related posts styles + +### Configuration +The feature is already configured! Hugo's built-in related content engine will automatically find similar posts. + +You can adjust the algorithm in `hugo.toml`: +```toml +[related] + threshold = 80 # Minimum match score (0-100) + includeNewer = true # Include newer posts + [[related.indices]] + name = "tags" + weight = 100 # Tags are most important + [[related.indices]] + name = "categories" + weight = 80 # Categories are important + [[related.indices]] + name = "date" + weight = 10 # Date is least important +``` + +--- + +## 5๏ธโƒฃ Newsletter Integration + +### What Changed +- Beautiful newsletter signup section on blog posts +- Eye-catching gradient design with animations +- Supports multiple providers: Mailchimp, ConvertKit, Substack, or custom +- Responsive and mobile-friendly + +### Files Created +- `layouts/partials/newsletter.html` - Newsletter component + +### Files Modified +- `layouts/_default/single.html` - Added newsletter section +- `hugo.toml` - Added newsletter configuration +- `assets/css/style.css` - Added newsletter styles + +### Configuration Required โš ๏ธ + +Choose your newsletter provider and configure: + +#### Option A: Mailchimp (Recommended) + +1. **Create a Mailchimp audience** at mailchimp.com +2. **Get your signup form code** + - Audience โ†’ Signup forms โ†’ Embedded forms + - Copy the form action URL (looks like: `https://yoursite.us21.list-manage.com/subscribe/post?u=xxx&id=xxx`) +3. **Update `hugo.toml`** + ```toml + [params.newsletter] + enabled = true + provider = "mailchimp" + + [params.newsletter.mailchimp] + action = "YOUR_MAILCHIMP_FORM_ACTION_URL" + hiddenField = "b_YOUR_USER_ID_YOUR_LIST_ID" + ``` + +#### Option B: ConvertKit + +```toml +[params.newsletter] + enabled = true + provider = "convertkit" + +[params.newsletter.convertkit] + action = "https://app.convertkit.com/forms/YOUR_FORM_ID/subscriptions" + formId = "YOUR_FORM_ID" + uid = "YOUR_UID" +``` + +#### Option C: Substack + +```toml +[params.newsletter] + enabled = true + provider = "substack" + +[params.newsletter.substack] + url = "https://yoursubstack.substack.com" +``` + +#### Option D: Custom Backend + +If you want to handle subscriptions yourself: + +```toml +[params.newsletter] + enabled = true + provider = "custom" + +[params.newsletter.custom] + action = "/api/newsletter/subscribe" # Your API endpoint +``` + +#### Customize the Text + +```toml +[params.newsletter] + title = "๐Ÿ“ฌ Your Custom Title" + description = "Your custom description about the newsletter." +``` + +#### Disable if Not Ready + +```toml +[params.newsletter] + enabled = false # Set to true when configured +``` + +--- + +## ๐Ÿš€ Testing Your Changes + +### Build and Run Locally + +```bash +# Start the Hugo dev server +hugo server -D + +# Or if you have a dev script +./dev.sh +``` + +Visit `http://localhost:1313` to see all the new features! + +### What to Test + +1. **Homepage** - Check the new post cards layout +2. **Search** - Press `โŒ˜K` or click search icon, try searching for posts +3. **Open a blog post** and scroll down to see: + - Tags section + - Related Posts section + - Newsletter signup + - Social sharing buttons + - Post navigation + - Comments section (if configured) + +--- + +## ๐Ÿ“ Next Steps + +### 1. Configure Giscus Comments +- Follow the instructions in section 3๏ธโƒฃ above +- Update `hugo.toml` with your repo details + +### 2. Setup Newsletter +- Choose a provider (Mailchimp recommended for beginners) +- Follow the instructions in section 5๏ธโƒฃ above +- Update `hugo.toml` with your provider details + +### 3. Optional: Add Google Analytics +```toml +[params] + googleAnalytics = "G-XXXXXXXXXX" # Your GA4 ID +``` + +### 4. Deploy +Once everything looks good locally: + +```bash +# Build the site +hugo + +# Or if you have a build script +./build.sh + +# Then deploy (if using Vercel, it auto-deploys on push) +git add . +git commit -m "Add blog enhancements" +git push +``` + +--- + +## ๐ŸŽจ Customization + +### Change Colors + +Edit `assets/css/style.css`: + +```css +:root { + --accent-color-blue: #0099FF; /* Primary accent */ + --accent-color-yellow: #FFD700; /* Secondary accent */ +} +``` + +### Adjust Layout + +All responsive breakpoints are in `assets/css/style.css`: +- Mobile: `< 768px` +- Tablet: `768px - 1024px` +- Desktop: `> 1024px` + +### Modify Search Behavior + +Edit `assets/js/search.js` to adjust: +- Search delay (currently 200ms) +- Number of results +- Search algorithm + +--- + +## ๐Ÿ› Troubleshooting + +### Search Not Working +- Check if `/index.json` is generated (visit it in browser) +- Check browser console for JavaScript errors +- Make sure `hugo.toml` has the correct output configuration + +### Comments Not Showing +- Ensure `enabled = true` in `hugo.toml` +- Check if Giscus repo and category IDs are correct +- Make sure GitHub Discussions is enabled on your repo + +### Related Posts Not Showing +- You need at least 2 posts with similar tags/categories +- Lower the `threshold` in `hugo.toml` if no posts appear +- Check that posts have tags and categories in frontmatter + +### Newsletter Form Not Working +- Verify the action URL is correct +- Check browser console for errors +- Test with your provider's test mode first + +--- + +## ๐Ÿ“š File Structure + +``` +blog.nischalskanda.tech/ +โ”œโ”€โ”€ layouts/ +โ”‚ โ”œโ”€โ”€ _default/ +โ”‚ โ”‚ โ”œโ”€โ”€ baseof.html # Modified: Added search modal +โ”‚ โ”‚ โ”œโ”€โ”€ list.html # Modified: Enhanced post cards +โ”‚ โ”‚ โ”œโ”€โ”€ single.html # Modified: Added all new sections +โ”‚ โ”‚ โ””โ”€โ”€ index.json # NEW: Search index +โ”‚ โ””โ”€โ”€ partials/ +โ”‚ โ”œโ”€โ”€ comments.html # NEW: Comments component +โ”‚ โ”œโ”€โ”€ related-posts.html # NEW: Related posts component +โ”‚ โ””โ”€โ”€ newsletter.html # NEW: Newsletter component +โ”œโ”€โ”€ assets/ +โ”‚ โ”œโ”€โ”€ css/ +โ”‚ โ”‚ โ””โ”€โ”€ style.css # Modified: Added all new styles +โ”‚ โ””โ”€โ”€ js/ +โ”‚ โ”œโ”€โ”€ main.js # Modified: Updated for post cards +โ”‚ โ””โ”€โ”€ search.js # NEW: Search functionality +โ”œโ”€โ”€ hugo.toml # Modified: New configurations +โ””โ”€โ”€ ENHANCEMENTS.md # NEW: This file! +``` + +--- + +## ๐ŸŽฏ What You Get + +- **Better UX**: Visitors can easily browse and find content +- **Engagement**: Comments and newsletter build your community +- **Retention**: Related posts keep readers on your site longer +- **Professional**: Modern design that matches 2025 standards +- **Performance**: All features are optimized and lightweight + +--- + +## ๐Ÿ’ก Tips + +1. **Write good descriptions** for your posts - they appear in cards and search +2. **Use tags and categories** consistently for better related posts +3. **Add images** to your posts - they automatically show in cards +4. **Respond to comments** to build community engagement +5. **Promote your newsletter** in your posts to grow your list + +--- + +## ๐Ÿค Support + +If you encounter any issues: +1. Check the troubleshooting section above +2. Verify configuration in `hugo.toml` +3. Check browser console for errors +4. Review Hugo documentation at https://gohugo.io/ + +--- + +**Enjoy your enhanced blog! ๐Ÿš€** + diff --git a/QUICK-START.md b/QUICK-START.md new file mode 100644 index 0000000..e3da6e6 --- /dev/null +++ b/QUICK-START.md @@ -0,0 +1,133 @@ +# ๐Ÿš€ Quick Start Guide + +## All 5 Enhancements Are DONE! โœ… + +Your blog now has these awesome features: + +## โœ… What Works Right Now (No Configuration Needed) + +### 1. Enhanced Post Cards +- Beautiful grid layout on homepage +- Automatic image detection +- Category badges +- Reading time + +### 2. Search Functionality +- Press `โŒ˜K` (Mac) or `Ctrl+K` (Windows/Linux) to search +- Real-time search across all posts +- Works immediately! + +### 3. Related Posts +- Smart recommendations +- Shows similar content +- Already working! + +## โš™๏ธ What Needs Configuration (5 minutes each) + +### 4. Comments (Giscus) +**Status:** Template ready, needs your GitHub repo + +**Quick Setup:** +1. Go to https://giscus.app/ +2. Enter your repo name +3. Get the IDs +4. Update `hugo.toml` lines 60-63 +5. Change `enabled = true` on line 53 + +**OR disable for now:** +```toml +[params.comments] + enabled = false # Line 53 in hugo.toml +``` + +### 5. Newsletter +**Status:** Template ready, needs your email provider + +**Quick Setup (Mailchimp):** +1. Create Mailchimp account +2. Get form action URL +3. Update `hugo.toml` line 91 +4. Change `enabled = true` on line 84 + +**OR disable for now:** +```toml +[params.newsletter] + enabled = false # Line 84 in hugo.toml +``` + +--- + +## ๐Ÿงช Test It Now! + +```bash +hugo server -D +``` + +Then visit http://localhost:1313 and check: + +1. **Homepage** - See the new beautiful post cards +2. **Press โŒ˜K or Ctrl+K** - Try the search feature +3. **Open any post** - See related posts section +4. **Scroll down** - See newsletter and comments (if enabled) + +--- + +## ๐Ÿ“ Files Changed + +**New Files Created:** +- `layouts/_default/index.json` - Search index +- `layouts/partials/comments.html` - Comments component +- `layouts/partials/related-posts.html` - Related posts +- `layouts/partials/newsletter.html` - Newsletter form +- `assets/js/search.js` - Search functionality +- `ENHANCEMENTS.md` - Full documentation +- `QUICK-START.md` - This file + +**Files Modified:** +- `layouts/_default/list.html` - New post cards +- `layouts/_default/single.html` - Added new sections +- `layouts/_default/baseof.html` - Added search UI +- `assets/css/style.css` - All new styles +- `assets/js/main.js` - Updated animations +- `hugo.toml` - New configurations + +--- + +## ๐ŸŽฏ Ready to Deploy? + +1. **Test locally first** + ```bash + hugo server -D + ``` + +2. **Configure comments & newsletter** (optional) + - See `ENHANCEMENTS.md` for detailed steps + +3. **Build** + ```bash + hugo + ``` + +4. **Deploy** + ```bash + git add . + git commit -m "Add 5 major blog enhancements" + git push + ``` + +--- + +## ๐Ÿ’ก Pro Tips + +- **Search**: It indexes all your posts automatically! +- **Related Posts**: Add more tags to your posts for better matching +- **Post Cards**: Images from your posts auto-appear +- **Comments**: Can enable later without breaking anything +- **Newsletter**: Can enable later without breaking anything + +--- + +**Read `ENHANCEMENTS.md` for detailed configuration instructions!** + +Enjoy your upgraded blog! ๐ŸŽ‰ + diff --git a/assets/css/style.css b/assets/css/style.css index e405d28..5aee516 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -654,7 +654,7 @@ a { top: 0; left: 0; width: 100%; - height: 3px; + height: 4px; background: transparent; z-index: 9999; pointer-events: none; @@ -662,10 +662,56 @@ a { .reading-progress-fill { height: 100%; - background: linear-gradient(90deg, var(--accent-color-blue), var(--accent-color-yellow)); + background: linear-gradient(90deg, + #0099FF 0%, + #00B4FF 25%, + #FFB800 75%, + #FFD700 100% + ); width: 0%; - transition: width var(--transition-fast); - border-radius: 0 3px 3px 0; + transition: width 0.1s ease-out; + box-shadow: + 0 0 12px rgba(0, 153, 255, 0.6), + 0 0 24px rgba(0, 153, 255, 0.3), + 0 2px 8px rgba(0, 153, 255, 0.4); + border-radius: 0 4px 4px 0; +} + +/* Dark mode specific glow */ +[data-theme="dark"] .reading-progress-fill { + box-shadow: + 0 0 16px rgba(0, 180, 255, 0.8), + 0 0 32px rgba(0, 180, 255, 0.4), + 0 2px 12px rgba(0, 180, 255, 0.5); +} + +/* Light mode specific glow */ +[data-theme="light"] .reading-progress-fill, +:root:not([data-theme]) .reading-progress-fill { + box-shadow: + 0 0 10px rgba(0, 153, 255, 0.5), + 0 0 20px rgba(0, 153, 255, 0.25), + 0 2px 6px rgba(0, 0, 0, 0.15); +} + +/* System preference for light mode */ +@media (prefers-color-scheme: light) { + :root:not([data-theme]) .reading-progress-fill { + box-shadow: + 0 0 10px rgba(0, 153, 255, 0.5), + 0 0 20px rgba(0, 153, 255, 0.25), + 0 2px 6px rgba(0, 0, 0, 0.15); + } +} + +/* System preference for dark mode */ +@media (prefers-color-scheme: dark) { + :root:not([data-theme]) .reading-progress-fill { + box-shadow: + 0 0 16px rgba(0, 180, 255, 0.8), + 0 0 32px rgba(0, 180, 255, 0.4), + 0 2px 12px rgba(0, 180, 255, 0.5); + } } /* Focus styles for accessibility */ @@ -1137,3 +1183,652 @@ html, body { height: 300px; } } + +/* --- Enhanced Post Cards --- */ +.post-list { + display: grid; + grid-template-columns: 1fr; + gap: var(--space-2xl); + margin: var(--space-3xl) 0; +} + +@media (min-width: 768px) { + .post-list { + grid-template-columns: repeat(2, 1fr); + gap: var(--space-xl); + } +} + +@media (min-width: 1200px) { + .post-list { + grid-template-columns: repeat(3, 1fr); + } +} + +.post-card { + background: var(--surface-color); + border-radius: 12px; + overflow: hidden; + border: 1px solid var(--border-color); + transition: transform var(--transition-normal), box-shadow var(--transition-normal); + height: 100%; + display: flex; + flex-direction: column; +} + +.post-card:hover { + transform: translateY(-4px); + box-shadow: 0 12px 24px var(--shadow-color); +} + +.post-card-link { + text-decoration: none; + color: inherit; + display: flex; + flex-direction: column; + height: 100%; +} + +.post-card-image { + width: 100%; + height: 220px; + overflow: hidden; + background: var(--glass-bg); + position: relative; +} + +.post-card-image img { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.3s ease; +} + +.post-card:hover .post-card-image img { + transform: scale(1.05); +} + +.post-card-image-placeholder { + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, var(--accent-color-blue) 0%, var(--accent-color-yellow) 100%); + opacity: 0.1; +} + +.post-card-placeholder-content { + display: flex; + align-items: center; + justify-content: center; +} + +.post-card-placeholder-icon { + font-size: 4rem; + opacity: 0.3; +} + +.post-card-content { + padding: var(--space-lg); + display: flex; + flex-direction: column; + gap: var(--space-sm); + flex: 1; +} + +.post-card-categories { + display: flex; + gap: var(--space-xs); + flex-wrap: wrap; +} + +.post-card-category { + font-size: var(--text-xs); + font-family: var(--font-heading); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + color: var(--accent-color-blue); + background: var(--glass-bg); + padding: 0.25rem 0.75rem; + border-radius: 20px; + border: 1px solid var(--accent-color-blue); +} + +.post-card-title { + font-family: var(--font-heading); + font-size: var(--text-xl); + font-weight: 700; + line-height: 1.3; + margin: 0; + color: var(--text-color); +} + +.post-card-link:hover .post-card-title { + color: var(--accent-color-blue); +} + +.post-card-description { + font-size: var(--text-base); + line-height: 1.6; + color: var(--text-color); + opacity: 0.8; + margin: 0; + flex: 1; +} + +.post-card-meta { + display: flex; + align-items: center; + gap: var(--space-xs); + font-size: var(--text-sm); + color: var(--text-color); + opacity: 0.6; + margin-top: auto; + padding-top: var(--space-sm); +} + +.post-card-date, +.post-card-reading-time { + font-family: var(--font-body); +} + +.post-card-separator { + opacity: 0.5; +} + +/* --- Search Functionality --- */ +.search-modal { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.8); + z-index: 9999; + display: flex; + align-items: flex-start; + justify-content: center; + padding-top: 10vh; + backdrop-filter: blur(8px); + animation: fadeIn 0.2s ease; +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +.search-modal-content { + background: var(--surface-color); + border-radius: 12px; + width: 90%; + max-width: 700px; + max-height: 80vh; + display: flex; + flex-direction: column; + border: 1px solid var(--border-color); + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); + animation: slideDown 0.3s ease; +} + +@keyframes slideDown { + from { + opacity: 0; + transform: translateY(-20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.search-header { + display: flex; + align-items: center; + gap: var(--space-sm); + padding: var(--space-lg); + border-bottom: 1px solid var(--border-color); +} + +.search-input { + flex: 1; + font-family: var(--font-body); + font-size: var(--text-lg); + padding: 0.75rem; + border: none; + background: transparent; + color: var(--text-color); + outline: none; +} + +.search-input::placeholder { + color: var(--text-color); + opacity: 0.5; +} + +.search-close { + background: none; + border: none; + color: var(--text-color); + cursor: pointer; + padding: 0.5rem; + display: flex; + align-items: center; + justify-content: center; + border-radius: 6px; + transition: all var(--transition-normal); +} + +.search-close:hover { + background: var(--glass-bg); + color: var(--accent-color-blue); +} + +.search-results { + overflow-y: auto; + padding: var(--space-sm); + max-height: calc(80vh - 100px); +} + +.search-result-item { + padding: var(--space-md); + border-radius: 8px; + margin-bottom: var(--space-sm); + transition: all var(--transition-normal); + cursor: pointer; + text-decoration: none; + color: inherit; + display: block; + border: 1px solid transparent; +} + +.search-result-item:hover { + background: var(--glass-bg); + border-color: var(--border-color); + transform: translateX(4px); +} + +.search-result-title { + font-family: var(--font-heading); + font-size: var(--text-lg); + font-weight: 700; + margin: 0 0 var(--space-xs) 0; + color: var(--text-color); +} + +.search-result-item:hover .search-result-title { + color: var(--accent-color-blue); +} + +.search-result-meta { + font-size: var(--text-sm); + color: var(--text-color); + opacity: 0.6; + margin-bottom: var(--space-xs); +} + +.search-result-description { + font-size: var(--text-base); + color: var(--text-color); + opacity: 0.8; + line-height: 1.5; + margin: 0; +} + +.search-result-tags { + display: flex; + gap: var(--space-xs); + flex-wrap: wrap; + margin-top: var(--space-sm); +} + +.search-result-tag { + font-size: var(--text-xs); + color: var(--accent-color-blue); + background: var(--glass-bg); + padding: 0.2rem 0.6rem; + border-radius: 12px; + border: 1px solid var(--accent-color-blue); +} + +.search-no-results { + text-align: center; + padding: var(--space-3xl); + color: var(--text-color); + opacity: 0.6; +} + +.search-no-results-icon { + font-size: 3rem; + margin-bottom: var(--space-md); +} + +/* --- Comments Section --- */ +.comments-section { + margin-top: var(--space-3xl); + width: 100%; + max-width: 100%; + overflow: hidden; +} + +.comments-title { + font-family: var(--font-heading); + font-size: var(--text-2xl); + font-weight: 700; + margin: 0 0 var(--space-lg) 0; + color: var(--text-color); +} + +/* Ensure Giscus is fully responsive */ +.giscus, .giscus-frame { + width: 100%; + max-width: 100%; +} + +.giscus iframe { + width: 100% !important; + max-width: 100% !important; +} + +/* Style comment container */ +.comments-section .giscus { + background: var(--surface-color); + border-radius: 8px; + padding: var(--space-md); + border: 1px solid var(--border-color); +} + +/* Mobile responsive adjustments */ +@media (max-width: 768px) { + .comments-section { + margin-top: var(--space-2xl); + } + + .comments-title { + font-size: var(--text-xl); + } + + .comments-section .giscus { + padding: var(--space-sm); + border-radius: 6px; + } +} + +/* --- Related Posts Section --- */ +.related-posts { + margin: var(--space-3xl) 0; +} + +.related-posts-title { + font-family: var(--font-heading); + font-size: var(--text-2xl); + font-weight: 700; + margin: 0 0 var(--space-lg) 0; + color: var(--text-color); +} + +.related-posts-grid { + display: grid; + grid-template-columns: 1fr; + gap: var(--space-lg); +} + +@media (min-width: 768px) { + .related-posts-grid { + grid-template-columns: repeat(2, 1fr); + } +} + +@media (min-width: 1024px) { + .related-posts-grid { + grid-template-columns: repeat(3, 1fr); + } +} + +.related-post-card { + background: var(--surface-color); + border-radius: 12px; + overflow: hidden; + border: 1px solid var(--border-color); + transition: transform var(--transition-normal), box-shadow var(--transition-normal); + height: 100%; + display: flex; + flex-direction: column; +} + +.related-post-card:hover { + transform: translateY(-4px); + box-shadow: 0 8px 16px var(--shadow-color); +} + +.related-post-link { + text-decoration: none; + color: inherit; + display: flex; + flex-direction: column; + height: 100%; +} + +.related-post-image { + width: 100%; + height: 180px; + overflow: hidden; + background: var(--glass-bg); + position: relative; +} + +.related-post-image img { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.3s ease; +} + +.related-post-card:hover .related-post-image img { + transform: scale(1.05); +} + +.related-post-image-placeholder { + display: flex; + align-items: center; + justify-content: center; + background: linear-gradient(135deg, var(--accent-color-blue) 0%, var(--accent-color-yellow) 100%); + opacity: 0.1; +} + +.related-post-placeholder-icon { + font-size: 3rem; + opacity: 0.3; +} + +.related-post-content { + padding: var(--space-md); + display: flex; + flex-direction: column; + gap: var(--space-xs); + flex: 1; +} + +.related-post-category { + font-size: var(--text-xs); + font-family: var(--font-heading); + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + color: var(--accent-color-blue); + background: var(--glass-bg); + padding: 0.25rem 0.75rem; + border-radius: 20px; + border: 1px solid var(--accent-color-blue); + display: inline-block; + width: fit-content; +} + +.related-post-title { + font-family: var(--font-heading); + font-size: var(--text-lg); + font-weight: 700; + line-height: 1.3; + margin: var(--space-xs) 0 0 0; + color: var(--text-color); + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.related-post-link:hover .related-post-title { + color: var(--accent-color-blue); +} + +.related-post-meta { + display: flex; + align-items: center; + gap: var(--space-xs); + font-size: var(--text-sm); + color: var(--text-color); + opacity: 0.6; + margin-top: auto; +} + +.related-post-separator { + opacity: 0.5; +} + +/* --- Newsletter Section --- */ +.newsletter-section { + margin: var(--space-3xl) 0; + background: var(--surface-color); + border: 1px solid var(--border-color); + border-radius: 12px; + padding: var(--space-2xl); + position: relative; + overflow: hidden; +} + +.newsletter-container { + text-align: center; + max-width: 600px; + margin: 0 auto; +} + +.newsletter-title { + font-family: var(--font-heading); + font-size: var(--text-2xl); + font-weight: 700; + margin: 0 0 var(--space-sm) 0; + color: var(--text-color); +} + +.newsletter-description { + font-size: var(--text-base); + line-height: 1.6; + color: var(--text-color); + opacity: 0.7; + margin: 0 0 var(--space-xl) 0; +} + +.newsletter-form { + margin-bottom: var(--space-sm); +} + +.newsletter-form-group { + display: flex; + gap: var(--space-sm); + max-width: 500px; + margin: 0 auto; +} + +@media (max-width: 600px) { + .newsletter-form-group { + flex-direction: column; + } +} + +.newsletter-input { + flex: 1; + padding: 0.875rem 1.125rem; + font-family: var(--font-body); + font-size: var(--text-base); + border: 1px solid var(--border-color); + background: var(--background-color); + color: var(--text-color); + border-radius: 8px; + outline: none; + transition: all var(--transition-normal); +} + +.newsletter-input:focus { + border-color: var(--accent-color-blue); + box-shadow: 0 0 0 3px rgba(0, 153, 255, 0.1); +} + +.newsletter-input::placeholder { + color: var(--text-color); + opacity: 0.5; +} + +.newsletter-button { + padding: 0.875rem 1.75rem; + font-family: var(--font-heading); + font-size: var(--text-base); + font-weight: 600; + background: var(--accent-color-blue); + color: #ffffff; + border: none; + border-radius: 8px; + cursor: pointer; + transition: all var(--transition-normal); + white-space: nowrap; +} + +.newsletter-button:hover { + background: #0088EE; + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(0, 153, 255, 0.3); +} + +.newsletter-button:active { + transform: translateY(0); +} + +.newsletter-button:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.newsletter-privacy { + font-size: var(--text-sm); + color: var(--text-color); + opacity: 0.6; + margin: var(--space-md) 0 0 0; +} + +.newsletter-message { + margin-top: var(--space-md); + padding: var(--space-sm) var(--space-md); + border-radius: 6px; + font-size: var(--text-sm); +} + +.newsletter-message-success { + background: rgba(76, 175, 80, 0.1); + color: #4CAF50; + border: 1px solid rgba(76, 175, 80, 0.3); +} + +[data-theme="dark"] .newsletter-message-success { + background: rgba(76, 175, 80, 0.15); + color: #66BB6A; +} + +.newsletter-message-error { + background: rgba(244, 67, 54, 0.1); + color: #F44336; + border: 1px solid rgba(244, 67, 54, 0.3); +} + +[data-theme="dark"] .newsletter-message-error { + background: rgba(244, 67, 54, 0.15); + color: #EF5350; +} diff --git a/assets/js/main.js b/assets/js/main.js index 50367ac..1bad099 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -77,7 +77,7 @@ // Performance: Add loading states function initLoadingStates() { - const elements = document.querySelectorAll('.hero-headline, .post-item'); + const elements = document.querySelectorAll('.hero-headline, .post-item, .post-card'); // Use Intersection Observer for progressive loading const observer = new IntersectionObserver((entries) => { diff --git a/assets/js/search.js b/assets/js/search.js new file mode 100644 index 0000000..0ff4013 --- /dev/null +++ b/assets/js/search.js @@ -0,0 +1,168 @@ +// Simple client-side search functionality +(function() { + 'use strict'; + + let searchIndex = []; + let searchModal, searchInput, searchResults, searchClose; + + // Simple fuzzy search function + function fuzzySearch(query, text) { + if (!query || !text) return false; + + query = query.toLowerCase(); + text = text.toLowerCase(); + + // Direct match gets highest priority + if (text.includes(query)) return true; + + // Fuzzy match - all query chars appear in order + let queryIndex = 0; + for (let i = 0; i < text.length && queryIndex < query.length; i++) { + if (text[i] === query[queryIndex]) { + queryIndex++; + } + } + return queryIndex === query.length; + } + + // Search function + function performSearch(query) { + if (!query || query.length < 2) { + searchResults.innerHTML = '
๐Ÿ”

Start typing to search posts...

'; + return; + } + + const results = searchIndex.filter(post => { + return fuzzySearch(query, post.title) || + fuzzySearch(query, post.description) || + fuzzySearch(query, post.content) || + (post.tags && post.tags.some(tag => fuzzySearch(query, tag))) || + (post.categories && post.categories.some(cat => fuzzySearch(query, cat))); + }); + + if (results.length === 0) { + searchResults.innerHTML = '
๐Ÿ˜•

No posts found matching your search.

'; + return; + } + + // Clear previous results + searchResults.innerHTML = ''; + results.forEach(post => { + const item = document.createElement('a'); + item.className = 'search-result-item'; + item.href = post.permalink; + + const title = document.createElement('h3'); + title.className = 'search-result-title'; + title.textContent = post.title; + item.appendChild(title); + + const meta = document.createElement('div'); + meta.className = 'search-result-meta'; + meta.textContent = post.date; + item.appendChild(meta); + + const desc = document.createElement('p'); + desc.className = 'search-result-description'; + desc.textContent = post.description || post.content; + item.appendChild(desc); + + if (post.tags && post.tags.length > 0) { + const tagsDiv = document.createElement('div'); + tagsDiv.className = 'search-result-tags'; + post.tags.slice(0, 3).forEach(tag => { + const tagSpan = document.createElement('span'); + tagSpan.className = 'search-result-tag'; + tagSpan.textContent = tag; + tagsDiv.appendChild(tagSpan); + }); + item.appendChild(tagsDiv); + } + + searchResults.appendChild(item); + }); + } + + // Open search modal + function openSearch() { + searchModal.style.display = 'flex'; + document.body.style.overflow = 'hidden'; + setTimeout(() => searchInput.focus(), 100); + } + + // Close search modal + function closeSearch() { + searchModal.style.display = 'none'; + document.body.style.overflow = ''; + searchInput.value = ''; + searchResults.innerHTML = ''; + } + + // Load search index + function loadSearchIndex() { + fetch('/index.json') + .then(response => response.json()) + .then(data => { + searchIndex = data; + }) + .catch(error => { + console.error('Error loading search index:', error); + }); + } + + // Initialize search + function initSearch() { + searchModal = document.getElementById('search-modal'); + searchInput = document.getElementById('search-input'); + searchResults = document.getElementById('search-results'); + searchClose = document.getElementById('search-close'); + + if (!searchModal || !searchInput || !searchResults) return; + + // Load search index + loadSearchIndex(); + + // Event listeners + searchClose.addEventListener('click', closeSearch); + + // Close on background click + searchModal.addEventListener('click', (e) => { + if (e.target === searchModal) { + closeSearch(); + } + }); + + // Close on Escape key + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && searchModal.style.display === 'flex') { + closeSearch(); + } + // Open with Cmd/Ctrl + K + if ((e.metaKey || e.ctrlKey) && e.key === 'k') { + e.preventDefault(); + openSearch(); + } + }); + + // Perform search as user types + let searchTimeout; + searchInput.addEventListener('input', (e) => { + clearTimeout(searchTimeout); + searchTimeout = setTimeout(() => { + performSearch(e.target.value); + }, 200); + }); + + // Show initial message + searchResults.innerHTML = '
๐Ÿ”

Start typing to search posts...

'; + } + + // Initialize when DOM is ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initSearch); + } else { + initSearch(); + } + +})(); + diff --git a/hugo.toml b/hugo.toml index e577214..5ca2396 100644 --- a/hugo.toml +++ b/hugo.toml @@ -5,6 +5,11 @@ enableRobotsTXT = true enableGitInfo = true canonifyURLs = true +# Output formats for search +[outputs] + home = ["HTML", "RSS", "JSON"] + section = ["HTML", "RSS"] + # SEO and Performance [sitemap] changefreq = "weekly" @@ -43,6 +48,63 @@ canonifyURLs = true googleAnalytics = "" # Add your GA4 tracking ID vercelAnalytics = true # Enable Vercel Analytics + # Comments System Configuration + [params.comments] + enabled = false + system = "giscus" # Options: "giscus", "utterances", "disqus" + + # Giscus Configuration (GitHub Discussions) + # To set up: Visit https://giscus.app/ and follow the setup guide + # You'll need to enable Discussions on your GitHub repo + [params.comments.giscus] + repo = "RexO77/blogs" # Your GitHub repo + repoId = "R_kgDOPTHrAg" # From giscus.app + category = "Ideas" # Discussion category name + categoryId = "DIC_kwDOPTHrAs4CxjGf" # From giscus.app + mapping = "pathname" # How to map posts to discussions + strict = "0" + reactionsEnabled = "1" + emitMetadata = "0" + inputPosition = "bottom" + theme = "preferred_color_scheme" + lang = "en" + + # Alternative: Utterances (GitHub Issues) + [params.comments.utterances] + repo = "" # Your GitHub username/repo + issueTerm = "pathname" + theme = "github-light" + + # Alternative: Disqus + [params.comments.disqus] + shortname = "" # Your Disqus shortname + + # Newsletter Configuration + [params.newsletter] + enabled = true + title = "Stay in the Loop" + description = "Get weekly insights on AI, tech, and career advice. Join readers who stay ahead of the curve." + provider = "mailchimp" # Options: "mailchimp", "convertkit", "substack", "custom" + + # Mailchimp Configuration + [params.newsletter.mailchimp] + action = "https://tech.us21.list-manage.com/subscribe/post?u=YOUR_USER_ID&id=YOUR_LIST_ID" # UPDATE THIS! + hiddenField = "b_YOUR_USER_ID_YOUR_LIST_ID" # Anti-bot field name + + # ConvertKit Configuration + [params.newsletter.convertkit] + action = "https://app.convertkit.com/forms/YOUR_FORM_ID/subscriptions" # UPDATE THIS! + formId = "YOUR_FORM_ID" + uid = "YOUR_UID" + + # Substack Configuration + [params.newsletter.substack] + url = "https://yoursubstack.substack.com" # UPDATE THIS! + + # Custom Configuration (for your own backend) + [params.newsletter.custom] + action = "/api/newsletter/subscribe" # Your API endpoint + # Performance lazyLoading = true @@ -110,3 +172,18 @@ canonifyURLs = true category = "categories" tag = "tags" series = "series" + +# Related content configuration +[related] + threshold = 80 + includeNewer = true + toLower = true + [[related.indices]] + name = "tags" + weight = 100 + [[related.indices]] + name = "categories" + weight = 80 + [[related.indices]] + name = "date" + weight = 10 diff --git a/hugo_stats.json b/hugo_stats.json index 728285e..13a3f50 100644 --- a/hugo_stats.json +++ b/hugo_stats.json @@ -13,9 +13,11 @@ "em", "figcaption", "figure", + "form", "h1", "h2", "h3", + "h4", "head", "header", "hr", @@ -23,6 +25,7 @@ "img", "input", "li", + "line", "link", "main", "meta", @@ -38,6 +41,7 @@ "strong", "style", "sup", + "svg", "table", "tbody", "td", @@ -69,6 +73,15 @@ "is-active", "logo", "main-site-link", + "newsletter-button", + "newsletter-container", + "newsletter-description", + "newsletter-form", + "newsletter-form-group", + "newsletter-input", + "newsletter-privacy", + "newsletter-section", + "newsletter-title", "pager", "pager-next", "pager-number", @@ -76,12 +89,26 @@ "pager-prev", "pagination", "pagination-inner", + "post-card", + "post-card-categories", + "post-card-category", + "post-card-content", + "post-card-date", + "post-card-description", + "post-card-image", + "post-card-image-placeholder", + "post-card-link", + "post-card-meta", + "post-card-placeholder-content", + "post-card-placeholder-icon", + "post-card-reading-time", + "post-card-separator", + "post-card-title", "post-category-link", "post-content", "post-description", "post-divider", "post-header", - "post-item", "post-list", "post-meta", "post-meta-separator", @@ -98,7 +125,25 @@ "post-tags-list", "post-tags-title", "post-title", + "related-post-card", + "related-post-category", + "related-post-content", + "related-post-image", + "related-post-link", + "related-post-meta", + "related-post-reading-time", + "related-post-separator", + "related-post-title", + "related-posts", + "related-posts-grid", + "related-posts-title", "responsive-image", + "search-close", + "search-header", + "search-input", + "search-modal", + "search-modal-content", + "search-results", "site-header", "social-button", "social-button-linkedin", @@ -218,6 +263,10 @@ "references--field-notes", "saving-our-brains-before-theyre-toast", "scaling-experience-not-compute", + "search-close", + "search-input", + "search-modal", + "search-results", "so-how-the-heck-do-you-protect-yourself", "so-what", "so-what-the-hell-do-we-do", diff --git a/layouts/_default/baseof.html b/layouts/_default/baseof.html index 5cafc40..849964f 100644 --- a/layouts/_default/baseof.html +++ b/layouts/_default/baseof.html @@ -50,6 +50,22 @@ Main Site + + +
{{ block "main" . }}{{ end }} @@ -59,6 +75,9 @@ {{ $js := resources.Get "js/main.js" | resources.Minify | resources.Fingerprint }} + {{ $search := resources.Get "js/search.js" | resources.Minify | resources.Fingerprint }} + + {{ if .Site.Params.vercelAnalytics }} diff --git a/layouts/_default/index.json b/layouts/_default/index.json new file mode 100644 index 0000000..eab12ef --- /dev/null +++ b/layouts/_default/index.json @@ -0,0 +1,14 @@ +{{- $.Scratch.Add "index" slice -}} +{{- range where .Site.RegularPages "Section" "posts" -}} + {{- $.Scratch.Add "index" (dict + "title" .Title + "description" .Description + "content" (.Plain | truncate 500) + "permalink" .Permalink + "date" (.Date.Format "Jan 2, 2006") + "categories" .Params.categories + "tags" .Params.tags + ) -}} +{{- end -}} +{{- $.Scratch.Get "index" | jsonify -}} + diff --git a/layouts/_default/list.html b/layouts/_default/list.html index 427d85e..e73e076 100644 --- a/layouts/_default/list.html +++ b/layouts/_default/list.html @@ -13,9 +13,72 @@

{{ end }} {{ $p := .Paginate $pages }} {{ range $p.Pages }} -
-

{{ .Title }}

- +
+ + {{- $thumbnail := "" }} + {{- if .Params.thumbnail }} + {{- $thumbnail = .Params.thumbnail }} + {{- else }} + {{- /* Look for a file with "thumbnail" in the name first */ -}} + {{- range .Resources.ByType "image" }} + {{- if or (in .Name "thumbnail") (in .Name "Thumbnail") }} + {{- $thumbnail = .RelPermalink }} + {{- break }} + {{- end }} + {{- end }} + {{- /* If no thumbnail found, use the first image */ -}} + {{- if not $thumbnail }} + {{- $images := .Resources.ByType "image" }} + {{- if $images }} + {{- with index $images 0 }} + {{- $thumbnail = .RelPermalink }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + + {{- if $thumbnail }} +
+ {{- if .Params.thumbnail }} + {{ .Title }} + {{- else }} + {{ .Title }} + {{- end }} +
+ {{- else }} +
+
+ ๐Ÿ“ +
+
+ {{- end }} + +
+ {{- if .Params.categories }} +
+ {{- range first 2 .Params.categories }} + {{ . }} + {{- end }} +
+ {{- end }} + +

{{ .Title }}

+ + {{- if .Description }} +

{{ .Description | truncate 150 }}

+ {{- else }} +

{{ .Summary | truncate 150 }}

+ {{- end }} + +
+ + โ€ข + {{ .ReadingTime }} min read +
+
+
{{ end }} diff --git a/layouts/_default/single.html b/layouts/_default/single.html index ac52481..d2f596a 100644 --- a/layouts/_default/single.html +++ b/layouts/_default/single.html @@ -44,6 +44,12 @@ {{- end }} + + {{ partial "related-posts.html" . }} + + + {{ partial "newsletter.html" . }} + + + + {{ partial "comments.html" . }}
{{ end }} diff --git a/layouts/partials/comments.html b/layouts/partials/comments.html new file mode 100644 index 0000000..94ce09f --- /dev/null +++ b/layouts/partials/comments.html @@ -0,0 +1,83 @@ +{{- if .Site.Params.comments.enabled -}} +
+

Comments

+ + {{- if eq .Site.Params.comments.system "giscus" -}} + + + + + + {{- end -}} + + {{- if eq .Site.Params.comments.system "utterances" -}} + + + {{- end -}} + + {{- if eq .Site.Params.comments.system "disqus" -}} + +
+ + + {{- end -}} +
+{{- end -}} + diff --git a/layouts/partials/newsletter.html b/layouts/partials/newsletter.html new file mode 100644 index 0000000..a46679d --- /dev/null +++ b/layouts/partials/newsletter.html @@ -0,0 +1,99 @@ +{{- if .Site.Params.newsletter.enabled -}} + +{{- end -}} + diff --git a/layouts/partials/related-posts.html b/layouts/partials/related-posts.html new file mode 100644 index 0000000..529ebd8 --- /dev/null +++ b/layouts/partials/related-posts.html @@ -0,0 +1,68 @@ +{{- $related := .Site.RegularPages.Related . | first 3 -}} +{{- if $related -}} +
+ + +
+{{- end -}} +