JavaScript & Shortcodes in HBStack

Hands-on examples of implementing JavaScript functionality in HBStack using Hugo shortcodes, including a working Hello World demonstration troubleshooting tips and real-world integration patterns.

This guide provides practical, working examples of JavaScript implementation in HBStack. From a simple Hello World demonstration to advanced integration patterns, you'll see exactly how to build interactive features using Hugo shortcodes and the HBStack framework.


Practical Example: Hello World Script

Let’s start with a simple example to demonstrate how custom JavaScript works in HBStack. Below is a live demonstration of a basic JavaScript file being loaded using Hugo shortcodes.

Demo: Hello World JavaScript

The script assets/js/helloworld.js is loaded on this page using a Hugo shortcode. Here’s how the complete implementation works:

1. JavaScript File (assets/js/helloworld.js)

 1document.addEventListener('DOMContentLoaded', () => {
 2  console.log('Hello from custom.js')
 3
 4  // Create a visible notification to show the script is working
 5  showHelloWorldNotification()
 6
 7  // Add click handler for demo button if it exists
 8  const demoButton = document.getElementById('hello-demo-btn')
 9  if (demoButton) {
10    demoButton.addEventListener('click', function () {
11      alert('Hello World! The JavaScript is working correctly.')
12      console.log('Demo button clicked at:', new Date().toLocaleTimeString())
13    })
14  }
15})
16
17function showHelloWorldNotification() {
18  // Create a temporary notification
19  const notification = document.createElement('div')
20  notification.className = 'helloworld-notification'
21
22  notification.innerHTML = `
23    <strong>✓ Hello World Script Loaded!</strong><br>
24    <small>Check the console for more details</small>
25  `
26
27  document.body.appendChild(notification)
28
29  // Remove notification after 4 seconds
30  setTimeout(() => {
31    notification.classList.add('slide-out')
32    setTimeout(() => {
33      if (notification.parentNode) {
34        notification.parentNode.removeChild(notification)
35      }
36    }, 300)
37  }, 4000)
38}

2. SCSS Styles (assets/scss/helloworld.scss)

 1// Hello World notification styles
 2.helloworld-notification {
 3  position: fixed;
 4  top: 20px;
 5  right: 20px;
 6  background: #28a745;
 7  color: white;
 8  padding: 15px 20px;
 9  border-radius: 5px;
10  z-index: 9999;
11  font-family:
12    system-ui,
13    -apple-system,
14    sans-serif;
15  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
16  animation: slideIn 0.3s ease-out;
17
18  strong {
19    display: block;
20    margin-bottom: 4px;
21  }
22
23  small {
24    color: rgba(255, 255, 255, 0.9);
25  }
26}
27
28// Animation keyframes
29@keyframes slideIn {
30  from {
31    transform: translateX(100%);
32    opacity: 0;
33  }
34  to {
35    transform: translateX(0);
36    opacity: 1;
37  }
38}
39
40// Animation for hiding notification
41.helloworld-notification.slide-out {
42  animation: slideIn 0.3s ease-out reverse;
43}

3. Hugo Shortcode (layouts/shortcodes/helloworld.html)

1{{ partial "helloworld.html" . }}

4. Hugo Partial (layouts/partials/helloworld.html)

1{{- /* Process SCSS file */ -}} {{- $customSCSS := resources.Get
2"scss/helloworld.scss" -}} {{- if $customSCSS -}} {{- $customSCSS = $customSCSS
3| toCSS | minify -}}
4<link rel="stylesheet" href="{{ $customSCSS.RelPermalink }}" />
5{{- end -}} {{- /* Process JavaScript file */ -}} {{- $customJS := resources.Get
6"js/helloworld.js" -}} {{- if $customJS -}} {{- $customJS = $customJS | js.Build
7| minify -}}
8<script src="{{ $customJS.RelPermalink }}" defer></script>
9{{- end -}}

5. Including in Markdown Content

1{{< helloworld >}}

Why This Approach Works

  1. Hugo Shortcodes provide a clean interface for including dynamic content in markdown
  2. Hugo Partials contain the actual implementation logic, promoting reusability
  3. Separation of Concerns - shortcodes act as simple interfaces that call partials
  4. Separated Assets - JavaScript logic and CSS styles are kept in separate files for better maintainability
  5. SCSS Processing - Hugo’s asset pipeline compiles SCSS to CSS and minifies it
  6. Asset Pipeline processes both JavaScript and SCSS files through Hugo’s build system
  7. Resource.Get loads files from the assets/ directory
  8. js.Build processes the JavaScript (handles imports, transpilation if needed)
  9. toCSS compiles SCSS to CSS
  10. minify optimizes both CSS and JavaScript files for production
  11. defer attribute ensures the script loads after the HTML is parsed

Hugo Best Practices: Shortcodes vs Partials

✅ Recommended Pattern:

  • Shortcode (layouts/shortcodes/helloworld.html): Simple interface that calls a partial
  • Partial (layouts/partials/helloworld.html): Contains the actual implementation logic

❌ Avoid:

  • Putting complex logic directly in shortcodes
  • Calling partials directly from markdown content

Benefits of This Pattern:

  1. Reusability: Partials can be used in layouts, other partials, and multiple shortcodes
  2. Maintainability: Logic is centralized in partials, styles in SCSS files
  3. Flexibility: Easy to modify implementation without changing shortcode interface
  4. Testing: Partials can be tested independently
  5. Asset Separation: Keeps JavaScript logic and CSS styles in separate, dedicated files
  6. Performance: Hugo’s asset pipeline optimizes and minifies both CSS and JavaScript

Asset Separation Best Practices

File Structure:

1assets/
2├── js/
3│   └── helloworld.js       # JavaScript logic only
4└── scss/
5    └── helloworld.scss     # Styles only

Benefits of Asset Separation:

  1. Cleaner Code: JavaScript focuses on behavior, SCSS focuses on presentation
  2. Better Caching: Browser can cache CSS and JS separately
  3. Easier Debugging: Styles and scripts are in dedicated files
  4. Team Collaboration: Designers can work on SCSS, developers on JS
  5. Maintainability: Each file has a single responsibility
  6. Hugo Pipeline: Takes advantage of Hugo’s built-in SCSS and JS processing

Hugo Asset Processing:

  • SCSS: toCSS filter compiles SCSS to CSS
  • JavaScript: js.Build processes imports and modern JS features
  • Minification: Both assets are minified for production
  • Source Maps: Available for debugging (in development mode)

Interactive Demo

Click the button below to test the JavaScript interaction:

What You Should See

  1. On Page Load: A green notification appears briefly in the top-right corner
  2. In Console: “Hello from custom.js” message
  3. On Button Click: An alert dialog and another console message

Troubleshooting Common Issues

Issue 1: “template for shortcode ‘partial’ not found”

Problem: Using {{< partial "filename.html" . >}} in markdown content.

Solution: Create a proper Hugo shortcode that calls a partial instead of trying to call partials directly from markdown.

1# Create the shortcode file
2mkdir -p layouts/shortcodes
3mkdir -p layouts/partials

File: layouts/shortcodes/your-script.html

1{{ partial "your-script.html" . }}

File: layouts/partials/your-script.html

1{{- /* Process SCSS file */ -}} {{- $customSCSS := resources.Get
2"scss/your-script.scss" -}} {{- if $customSCSS -}} {{- $customSCSS = $customSCSS
3| toCSS | minify -}}
4<link rel="stylesheet" href="{{ $customSCSS.RelPermalink }}" />
5{{- end -}} {{- /* Process JavaScript file */ -}} {{- $customJS := resources.Get
6"js/your-script.js" -}} {{- if $customJS -}} {{- $customJS = $customJS |
7js.Build | minify -}}
8<script src="{{ $customJS.RelPermalink }}" defer></script>
9{{- end -}}

Then use in markdown:

1{{< your-script >}}

Issue 2: JavaScript Not Loading

Symptoms: No console messages, no visual effects.

Debugging Steps:

  1. Check browser Developer Tools → Network tab
  2. Look for your JavaScript file in the network requests
  3. Verify the file path in assets/js/
  4. Ensure the shortcode is properly included
  5. Check for JavaScript errors in the Console tab

Issue 3: Script Loads But Functions Don’t Work

Common Causes:

  • DOM elements don’t exist when script runs
  • CSS selectors are incorrect
  • Event listeners aren’t properly attached

Solution: Always wrap your code in DOMContentLoaded:

1document.addEventListener('DOMContentLoaded', () => {
2  // Your code here
3})

Issue 4: Hugo Build Errors

Problem: “failed to extract shortcode” or similar build errors.

Solutions:

  1. Check shortcode syntax - use the following format:
1{{< shortcode-name >}}
  1. Verify file paths are correct
  2. Ensure shortcode files are in layouts/shortcodes/
  3. Restart Hugo server after creating new shortcodes

Issue 5: SCSS/CSS Not Loading

Symptoms: JavaScript works but styles don’t apply, elements appear unstyled.

Debugging Steps:

  1. Check Network Tab: Look for your CSS file in browser Developer Tools → Network tab
  2. Verify File Path: Ensure SCSS file is in assets/scss/ directory
  3. Check Hugo Logs: Look for SCSS compilation errors in Hugo server output
  4. Validate SCSS Syntax: Ensure your SCSS syntax is correct

Common SCSS Issues:

 1// ❌ Wrong - missing semicolon
 2.notification {
 3  position: fixed
 4  top: 20px;
 5}
 6
 7// ✅ Correct
 8.notification {
 9  position: fixed;
10  top: 20px;
11}

Hugo SCSS Processing:

  • Hugo uses LibSass/Dart Sass for SCSS compilation
  • Check for SCSS syntax errors in Hugo terminal output
  • Ensure the toCSS filter is applied in your partial

Issue 6: CSS Classes Not Applied in JavaScript

Problem: JavaScript creates elements but CSS classes don’t work.

Debugging Steps:

  1. Check Class Names: Ensure JavaScript class names match SCSS selectors
  2. Inspect Elements: Use browser DevTools to verify classes are added
  3. Check CSS Loading Order: Ensure CSS loads before JavaScript runs

Example Debug Pattern:

1// Add debugging to verify classes
2const notification = document.createElement('div')
3notification.className = 'helloworld-notification'
4
5// Debug: Check if element has the class
6console.log('Element classes:', notification.className)
7console.log('Element in DOM:', document.contains(notification))

Advanced Integration Examples

Example 1: Custom Search Enhancement

 1// Enhance HBStack's search functionality
 2import { utils } from './utils'
 3
 4interface SearchResult {
 5  title: string
 6  url: string
 7  excerpt: string
 8}
 9
10class CustomSearch {
11  private searchIndex: SearchResult[] = []
12
13  async init() {
14    try {
15      const response = await fetch('/search-index.json')
16      this.searchIndex = await response.json()
17      this.setupSearch()
18    } catch (error) {
19      console.error('Failed to load search index:', error)
20    }
21  }
22
23  private setupSearch() {
24    const searchInput = document.querySelector(
25      '#search-input'
26    ) as HTMLInputElement
27    if (searchInput) {
28      searchInput.addEventListener(
29        'input',
30        utils.throttle(this.performSearch.bind(this), 300)
31      )
32    }
33  }
34
35  private performSearch(event: Event) {
36    const query = (event.target as HTMLInputElement).value.toLowerCase()
37    const results = this.searchIndex.filter(
38      item =>
39        item.title.toLowerCase().includes(query) ||
40        item.excerpt.toLowerCase().includes(query)
41    )
42    this.displayResults(results)
43  }
44
45  private displayResults(results: SearchResult[]) {
46    const container = document.querySelector('#search-results')
47    if (container) {
48      container.innerHTML = results
49        .map(
50          result => `
51                    <div class="search-result">
52                        <h3><a href="${result.url}">${result.title}</a></h3>
53                        <p>${result.excerpt}</p>
54                    </div>
55                `
56        )
57        .join('')
58    }
59  }
60}
61
62// Initialize when DOM is ready
63document.addEventListener('DOMContentLoaded', () => {
64  new CustomSearch().init()
65})

Example 2: Custom Analytics Integration

  1// Custom analytics tracking for HBStack
  2interface AnalyticsEvent {
  3  category: string
  4  action: string
  5  label?: string
  6  value?: number
  7}
  8
  9class CustomAnalytics {
 10  private trackingId: string
 11
 12  constructor(trackingId: string) {
 13    this.trackingId = trackingId
 14    this.init()
 15  }
 16
 17  private init() {
 18    // Track page views
 19    this.trackPageView()
 20
 21    // Track scroll depth
 22    this.trackScrollDepth()
 23
 24    // Track click events
 25    this.trackClicks()
 26  }
 27
 28  private trackEvent(event: AnalyticsEvent) {
 29    // Send to your analytics service
 30    console.log('Analytics Event:', event)
 31
 32    // Example: Google Analytics 4
 33    if (typeof gtag !== 'undefined') {
 34      gtag('event', event.action, {
 35        event_category: event.category,
 36        event_label: event.label,
 37        value: event.value
 38      })
 39    }
 40  }
 41
 42  private trackPageView() {
 43    this.trackEvent({
 44      category: 'page',
 45      action: 'view',
 46      label: window.location.pathname
 47    })
 48  }
 49
 50  private trackScrollDepth() {
 51    let maxScroll = 0
 52    const milestones = [25, 50, 75, 100]
 53
 54    window.addEventListener(
 55      'scroll',
 56      utils.throttle(() => {
 57        const scrollPercent = Math.round(
 58          (window.scrollY / (document.body.scrollHeight - window.innerHeight)) *
 59            100
 60        )
 61
 62        if (scrollPercent > maxScroll) {
 63          maxScroll = scrollPercent
 64
 65          milestones.forEach(milestone => {
 66            if (scrollPercent >= milestone && maxScroll < milestone + 5) {
 67              this.trackEvent({
 68                category: 'scroll',
 69                action: 'depth',
 70                label: `${milestone}%`,
 71                value: milestone
 72              })
 73            }
 74          })
 75        }
 76      }, 1000)
 77    )
 78  }
 79
 80  private trackClicks() {
 81    document.addEventListener('click', e => {
 82      const target = e.target as HTMLElement
 83
 84      // Track external links
 85      if (
 86        target.tagName === 'A' &&
 87        target.getAttribute('href')?.startsWith('http')
 88      ) {
 89        this.trackEvent({
 90          category: 'link',
 91          action: 'external_click',
 92          label: target.getAttribute('href') || ''
 93        })
 94      }
 95
 96      // Track button clicks
 97      if (target.tagName === 'BUTTON' || target.classList.contains('btn')) {
 98        this.trackEvent({
 99          category: 'button',
100          action: 'click',
101          label: target.textContent?.trim() || ''
102        })
103      }
104    })
105  }
106}
107
108// Initialize analytics
109document.addEventListener('DOMContentLoaded', () => {
110  new CustomAnalytics('YOUR_TRACKING_ID')
111})

Example 3: Progressive Enhancement Pattern

 1// Progressive enhancement example
 2class ProgressiveFeature {
 3  constructor() {
 4    this.init()
 5  }
 6
 7  init() {
 8    // Check for required features before enhancing
 9    if (this.isSupported()) {
10      this.enhance()
11    } else {
12      this.fallback()
13    }
14  }
15
16  isSupported() {
17    return (
18      'IntersectionObserver' in window &&
19      'fetch' in window &&
20      'classList' in document.documentElement
21    )
22  }
23
24  enhance() {
25    console.log('Enhanced features available')
26
27    // Modern functionality
28    this.setupIntersectionObserver()
29    this.setupModernFeatures()
30  }
31
32  fallback() {
33    console.log('Using fallback functionality')
34
35    // Basic functionality for older browsers
36    this.setupBasicFeatures()
37  }
38
39  setupIntersectionObserver() {
40    const observer = new IntersectionObserver(entries => {
41      entries.forEach(entry => {
42        if (entry.isIntersecting) {
43          entry.target.classList.add('visible')
44        }
45      })
46    })
47
48    document.querySelectorAll('.animate-on-scroll').forEach(el => {
49      observer.observe(el)
50    })
51  }
52
53  setupModernFeatures() {
54    // Use modern JavaScript features
55    document.querySelectorAll('[data-enhance]').forEach(element => {
56      element.addEventListener('click', this.handleModernClick.bind(this))
57    })
58  }
59
60  setupBasicFeatures() {
61    // Basic functionality without modern APIs
62    const elements = document.querySelectorAll('[data-enhance]')
63    for (let i = 0; i < elements.length; i++) {
64      elements[i].addEventListener('click', this.handleBasicClick.bind(this))
65    }
66  }
67
68  handleModernClick(event) {
69    event.preventDefault()
70    // Modern implementation
71    console.log('Modern click handler')
72  }
73
74  handleBasicClick(event) {
75    event.preventDefault()
76    // Basic implementation
77    console.log('Basic click handler')
78  }
79}
80
81// Initialize
82document.addEventListener('DOMContentLoaded', () => {
83  new ProgressiveFeature()
84})

Performance Optimization Examples

Lazy Loading Implementation

 1// Efficient lazy loading with Intersection Observer
 2class LazyLoader {
 3  constructor() {
 4    this.imageObserver = null
 5    this.config = {
 6      rootMargin: '50px 0px',
 7      threshold: 0.01
 8    }
 9    this.init()
10  }
11
12  init() {
13    if ('IntersectionObserver' in window) {
14      this.setupObserver()
15    } else {
16      this.loadAllImages()
17    }
18  }
19
20  setupObserver() {
21    this.imageObserver = new IntersectionObserver(entries => {
22      entries.forEach(entry => {
23        if (entry.isIntersecting) {
24          this.loadImage(entry.target)
25          this.imageObserver.unobserve(entry.target)
26        }
27      })
28    }, this.config)
29
30    this.observeImages()
31  }
32
33  observeImages() {
34    const images = document.querySelectorAll('img[data-src]')
35    images.forEach(img => this.imageObserver.observe(img))
36  }
37
38  loadImage(img) {
39    img.src = img.dataset.src
40    img.classList.remove('lazy')
41    img.classList.add('loaded')
42  }
43
44  loadAllImages() {
45    // Fallback for browsers without Intersection Observer
46    const images = document.querySelectorAll('img[data-src]')
47    images.forEach(img => this.loadImage(img))
48  }
49}
50
51// Initialize lazy loading
52document.addEventListener('DOMContentLoaded', () => {
53  new LazyLoader()
54})

Creating Custom Shortcodes

Advanced Shortcode Example

Create layouts/shortcodes/interactive-demo.html:

 1{{- $id := .Get "id" | default (printf "demo-%d" now.Unix) -}} {{- $type := .Get
 2"type" | default "button" -}} {{- $message := .Get "message" | default "Demo
 3activated!" -}}
 4
 5<div class="interactive-demo mb-4" data-demo-id="{{ $id }}">
 6  <div class="demo-controls mb-3">
 7    {{- if eq $type "button" -}}
 8    <button
 9      id="{{ $id }}-btn"
10      class="btn btn-primary demo-trigger"
11      data-message="{{ $message }}"
12    >
13      <i class="fas fa-play me-2"></i>Run Demo
14    </button>
15    {{- else if eq $type "form" -}}
16    <form class="demo-form" data-demo-id="{{ $id }}">
17      <div class="input-group">
18        <input
19          type="text"
20          class="form-control"
21          placeholder="Enter some text..."
22          id="{{ $id }}-input"
23        />
24        <button class="btn btn-primary" type="submit">Test</button>
25      </div>
26    </form>
27    {{- end -}}
28  </div>
29
30  <div id="{{ $id }}-output" class="demo-output border p-3 bg-light d-none">
31    <strong>Demo Output:</strong>
32    <div class="output-content mt-2"></div>
33  </div>
34</div>
35
36{{- $demoJS := resources.Get "js/interactive-demo.js" -}} {{- if $demoJS -}} {{-
37$demoJS = $demoJS | js.Build | minify -}}
38<script src="{{ $demoJS.RelPermalink }}" defer></script>
39{{- end -}}

And the corresponding JavaScript file assets/js/interactive-demo.js:

 1class InteractiveDemo {
 2  constructor() {
 3    this.init()
 4  }
 5
 6  init() {
 7    document.addEventListener('click', this.handleButtonDemo.bind(this))
 8    document.addEventListener('submit', this.handleFormDemo.bind(this))
 9  }
10
11  handleButtonDemo(event) {
12    if (event.target.matches('.demo-trigger')) {
13      event.preventDefault()
14      const button = event.target
15      const demoId = button.id.replace('-btn', '')
16      const message = button.dataset.message
17
18      this.showOutput(demoId, `Button clicked! Message: ${message}`)
19    }
20  }
21
22  handleFormDemo(event) {
23    if (event.target.matches('.demo-form')) {
24      event.preventDefault()
25      const form = event.target
26      const demoId = form.dataset.demoId
27      const input = form.querySelector('input')
28      const value = input.value
29
30      this.showOutput(demoId, `Form submitted with value: "${value}"`)
31    }
32  }
33
34  showOutput(demoId, content) {
35    const outputDiv = document.getElementById(`${demoId}-output`)
36    const contentDiv = outputDiv.querySelector('.output-content')
37
38    contentDiv.innerHTML = content
39    outputDiv.classList.remove('d-none')
40
41    // Auto-hide after 5 seconds
42    setTimeout(() => {
43      outputDiv.classList.add('d-none')
44    }, 5000)
45  }
46}
47
48// Initialize
49document.addEventListener('DOMContentLoaded', () => {
50  new InteractiveDemo()
51})

Using the Custom Shortcode

FAQs

Common issues include:

  • File placement: Ensure JS files are in /assets/js/ directory
  • Module conflicts: Check browser console for errors
  • Bootstrap conflicts: Use data-bs- attributes instead of data-
  • Timing issues: Wrap code in DOMContentLoaded or use Hugo’s page loading events

  1. Create shortcode in /layouts/shortcodes/yourname.html
  2. Include necessary JavaScript inline or reference external files
  3. Use Hugo’s .Page.Scratch for data passing
  4. Test with different content types and page structures

Example:

1<!-- layouts/shortcodes/interactive.html -->
2<div id="interactive-{{ .Get 0 }}" class="interactive-widget">
3  {{ .Inner }}
4</div>
5<script>
6  document.addEventListener('DOMContentLoaded', function() {
7    // Your JavaScript here
8  });
9</script>

Option 1: Hugo Modules (Recommended)

1# hugo.yaml
2module:
3  imports:
4    - path: github.com/user/library-module

Option 2: CDN with SRI

1<!-- layouts/partials/head/custom.html -->
2<script src="https://cdn.example.com/library.js"
3        integrity="sha384-..."
4        crossorigin="anonymous"></script>

Option 3: Local Assets

  • Place in /assets/js/vendor/
  • Use Hugo’s asset pipeline for bundling

Development Setup:

  1. Enable Hugo’s development mode: hugo server -D --disableFastRender
  2. Open browser developer tools (F12)
  3. Check Console tab for errors
  4. Use Network tab to verify file loading

Common Debug Steps:

  • Verify file paths in page source
  • Check for JavaScript syntax errors
  • Test with browser’s JavaScript console
  • Temporarily disable other scripts to isolate issues

Yes! HBStack supports modern JavaScript through Hugo’s asset pipeline:

Configuration:

1# hugo.yaml
2build:
3  buildStats:
4    enable: true
5params:
6  hb:
7    js:
8      transpiler: esbuild  # or babel

Features Available:

  • Arrow functions, template literals
  • Classes, modules (with bundling)
  • Async/await, Promises
  • Destructuring, spread operator

Bundle and Minify:

1<!-- layouts/partials/footer/custom.html -->
2{{ $js := resources.Get "js/main.js" | js.Build | minify }}
3<script src="{{ $js.RelPermalink }}"></script>

Lazy Loading:

  • Use Intersection Observer for viewport-based loading
  • Implement progressive enhancement patterns
  • Load non-critical scripts asynchronously

Caching Strategy:

  • Leverage Hugo’s fingerprinting for cache busting
  • Use service workers for offline functionality

HBStack provides several custom events you can listen for:

 1// Page navigation events
 2document.addEventListener('hb:page:loaded', function(e) {
 3  // Triggered when page content loads
 4});
 5
 6// Theme switching
 7document.addEventListener('hb:theme:changed', function(e) {
 8  // Triggered when dark/light theme changes
 9  console.log('New theme:', e.detail.theme);
10});
11
12// Search events
13document.addEventListener('hb:search:opened', function(e) {
14  // Triggered when search modal opens
15});

Language-Specific Scripts:

1<!-- layouts/partials/head/custom.html -->
2{{ if eq .Site.Language.Lang "en" }}
3  {{ $js := resources.Get "js/en-specific.js" }}
4  <script src="{{ $js.RelPermalink }}"></script>
5{{ end }}

Internationalization in JS:

1// Access Hugo's language data
2const lang = document.documentElement.lang;
3const translations = {
4  'en': { 'hello': 'Hello' },
5  'es': { 'hello': 'Hola' }
6};

Limited Integration: HBStack is primarily a Hugo theme, but you can:

  1. Mount frameworks to specific elements:
1<!-- In your content or shortcode -->
2<div id="react-component"></div>
3<script>
4  // Mount React component to #react-component
5</script>
  1. Use for specific interactive components:
  • Charts, interactive widgets
  • Form handling, validation
  • Dynamic content areas

Note: Full SPA integration isn’t recommended as it conflicts with Hugo’s static nature.

Core Principle: Ensure content works without JavaScript, then enhance with JS:

 1<!-- Basic HTML structure -->
 2<div class="accordion" data-enhance="accordion">
 3  <details open>
 4    <summary>Section 1</summary>
 5    <p>Content that works without JS</p>
 6  </details>
 7</div>
 8
 9<script>
10// Progressive enhancement
11document.querySelectorAll('[data-enhance="accordion"]').forEach(el => {
12  // Convert details/summary to fancy accordion
13  enhanceAccordion(el);
14});
15</script>