diff --git a/modules/blox-tailwind/archetypes/faq.md b/modules/blox-tailwind/archetypes/faq.md
new file mode 100644
index 00000000..7735148c
--- /dev/null
+++ b/modules/blox-tailwind/archetypes/faq.md
@@ -0,0 +1,45 @@
+---
+title: "{{ replace .Name "-" " " | title }}"
+date: {{ .Date }}
+draft: false
+
+# Summary for SEO
+summary: ""
+
+# Mark this as an FAQ page to enable FAQPage structured data
+faq_page: true
+
+# Categories and tags
+categories: []
+tags: []
+
+# Pagefind search metadata (automatically indexed)
+# type: faq (auto-added by layout)
+# category: first category from categories array above
+
+# Option 1: Define FAQs inline
+faqs:
+ - question: "What is your question here?"
+ answer: "Your answer here. Can use **Markdown** formatting."
+
+ - question: "Another question?"
+ answer: "Another answer with more details."
+
+ # Add more Q&As as needed
+
+# Option 2: Alternatively, create child pages under this FAQ section
+# and they will automatically be included
+
+# SEO settings
+seo:
+ title: ""
+ description: ""
+
+# Show breadcrumb navigation
+show_breadcrumb: true
+---
+
+Add an introductory text here that will appear before the FAQ accordion.
+
+This is a great place to provide context about the FAQ section.
+
diff --git a/modules/blox-tailwind/archetypes/questions.md b/modules/blox-tailwind/archetypes/questions.md
new file mode 100644
index 00000000..78023ab5
--- /dev/null
+++ b/modules/blox-tailwind/archetypes/questions.md
@@ -0,0 +1,62 @@
+---
+title: "{{ replace .Name "-" " " | title }}"
+date: {{ .Date }}
+draft: false
+
+# The question (can also use title)
+question: "{{ replace .Name "-" " " | title }}"
+
+# Short answer (optional - can also use content below)
+answer: ""
+
+# Summary for SEO and previews
+summary: ""
+
+# Difficulty level (optional)
+difficulty: "" # e.g., "Beginner", "Intermediate", "Advanced"
+
+# Categories and tags for organization
+categories: []
+tags: []
+
+# Pagefind search metadata (automatically indexed)
+# type: questions (auto-added by layout)
+# category: first category from categories array above
+# difficulty: value from difficulty field below
+
+# Vote counts (optional - for display and structured data)
+upvote_count: 0
+downvote_count: 0
+
+# Additional/suggested answers (optional)
+# suggested_answer:
+# - text: "Alternative answer text here..."
+# author: "Author Name"
+# date: 2024-01-15
+# upvote_count: 5
+
+# Related questions (optional - can also be auto-generated)
+# related:
+# - link-to-related-question
+
+# SEO settings
+seo:
+ title: ""
+ description: ""
+
+# Show breadcrumb navigation
+show_breadcrumb: true
+---
+
+Write your detailed answer here using Markdown.
+
+You can include:
+- Code blocks
+- Images
+- Lists
+- Links
+- And more!
+
+## Additional Details
+
+Add sections as needed to provide comprehensive information.
diff --git a/modules/blox-tailwind/assets/css/config/safelist.css b/modules/blox-tailwind/assets/css/config/safelist.css
index 971a8707..308ca68f 100644
--- a/modules/blox-tailwind/assets/css/config/safelist.css
+++ b/modules/blox-tailwind/assets/css/config/safelist.css
@@ -3,7 +3,8 @@
/* This ensures Tailwind generates these classes even if not found in templates */
/* Inline safelist for classes used in dynamic content and YAML configuration */
-@source inline("
");
+/* Includes search modal Quick Actions dynamic color classes */
+@source inline("");
/* Force generation of all CSS custom properties for dynamic usage in YAML front matter */
/* Hidden utility class that forces all color variables to be generated */
diff --git a/modules/blox-tailwind/assets/css/framework/base.css b/modules/blox-tailwind/assets/css/framework/base.css
index e111e024..cd3ea9fa 100644
--- a/modules/blox-tailwind/assets/css/framework/base.css
+++ b/modules/blox-tailwind/assets/css/framework/base.css
@@ -11,6 +11,39 @@
}
}
+/* Gradient Mesh Animations */
+@keyframes float {
+ 0%, 100% {
+ transform: translateY(0) translateX(0);
+ }
+ 25% {
+ transform: translateY(-20px) translateX(10px);
+ }
+ 50% {
+ transform: translateY(-10px) translateX(-10px);
+ }
+ 75% {
+ transform: translateY(-30px) translateX(5px);
+ }
+}
+
+@keyframes rotate-slow {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+.animate-float {
+ animation: float 8s ease-in-out infinite;
+}
+
+.animate-rotate-slow {
+ animation: rotate-slow 20s linear infinite;
+}
+
/* Custom utilities and components */
@utility task-list {
list-style: none;
diff --git a/modules/blox-tailwind/assets/css/hb-search.css b/modules/blox-tailwind/assets/css/hb-search.css
new file mode 100644
index 00000000..39266725
--- /dev/null
+++ b/modules/blox-tailwind/assets/css/hb-search.css
@@ -0,0 +1,88 @@
+/* Hugo Blox Search - Custom Pagefind Implementation */
+
+/* Animations */
+@keyframes spin {
+ to { transform: rotate(360deg); }
+}
+
+.animate-spin {
+ animation: spin 1s linear infinite;
+}
+
+/* Search result highlights */
+.search-result mark {
+ background-color: rgb(254 240 138);
+ color: rgb(17 24 39);
+ font-weight: 600;
+ padding: 0.05rem 0.2rem;
+ border-radius: 0.125rem;
+}
+
+.dark .search-result mark {
+ background-color: rgb(133 77 14);
+ color: rgb(254 240 138);
+}
+
+/* Result hover state */
+.search-result:hover mark {
+ background-color: rgb(253 224 71);
+}
+
+.dark .search-result:hover mark {
+ background-color: rgb(161 98 7);
+}
+
+/* Smooth transitions */
+.search-modal-enter {
+ animation: slideInUp 0.2s ease-out;
+}
+
+@keyframes slideInUp {
+ from {
+ opacity: 0;
+ transform: translateY(1rem) scale(0.95);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0) scale(1);
+ }
+}
+
+/* Loading skeleton */
+.search-skeleton {
+ background: linear-gradient(
+ 90deg,
+ rgb(229 231 235) 25%,
+ rgb(243 244 246) 50%,
+ rgb(229 231 235) 75%
+ );
+ background-size: 200% 100%;
+ animation: shimmer 1.5s infinite;
+}
+
+.dark .search-skeleton {
+ background: linear-gradient(
+ 90deg,
+ rgb(55 65 81) 25%,
+ rgb(75 85 99) 50%,
+ rgb(55 65 81) 75%
+ );
+ background-size: 200% 100%;
+}
+
+@keyframes shimmer {
+ 0% { background-position: 200% 0; }
+ 100% { background-position: -200% 0; }
+}
+
+/* Keyboard navigation highlight - handled by Alpine :class binding */
+/* Smooth transition for selection state */
+.search-result {
+ transition: all 0.15s ease;
+}
+
+/* Additional hover effects for keyboard-selected items */
+.search-result:focus-visible {
+ outline: 2px solid rgb(59 130 246);
+ outline-offset: -2px;
+}
\ No newline at end of file
diff --git a/modules/blox-tailwind/assets/css/main.css b/modules/blox-tailwind/assets/css/main.css
index 77dbcaf6..9e917a5e 100644
--- a/modules/blox-tailwind/assets/css/main.css
+++ b/modules/blox-tailwind/assets/css/main.css
@@ -35,5 +35,8 @@
@import "views/all.css";
@import "chroma.css";
+/* Hugo Blox Search */
+@import "hb-search.css";
+
/* Safelist for dynamic classes */
@import "config/safelist.css";
diff --git a/modules/blox-tailwind/assets/js/hb-search.js b/modules/blox-tailwind/assets/js/hb-search.js
index 199f5982..d6b55cac 100644
--- a/modules/blox-tailwind/assets/js/hb-search.js
+++ b/modules/blox-tailwind/assets/js/hb-search.js
@@ -1,69 +1,32 @@
-// Hugo Blox Search functionality
-// Initialize search wrapper element globally
-let searchWrapper = null;
+// Hugo Blox Search - Custom Pagefind API Implementation
+// Search is handled by Alpine.js component in search-modal.html
-window.addEventListener("DOMContentLoaded", () => {
- const configScript = document.getElementById("search-config");
- if (!configScript) {
- // No search on this page.
- return;
- }
-
- const searchConfig = JSON.parse(configScript.textContent);
- if (!searchConfig || typeof searchConfig.baseUrl === "undefined") {
- console.error("Pagefind search config is missing or invalid.", searchConfig);
- return;
- }
-
- try {
- new PagefindUI({
- element: "#search",
- showSubResults: true,
- baseUrl: searchConfig.baseUrl,
- bundlePath: searchConfig.baseUrl + "pagefind/",
+// Additional keyboard shortcuts and utilities
+document.addEventListener('DOMContentLoaded', () => {
+ console.log('✓ Hugo Blox Search initialized (Pagefind Headless API)');
+
+ // Handle search toggle buttons
+ const searchButtons = document.querySelectorAll('[data-search-toggle]');
+ searchButtons.forEach(button => {
+ button.addEventListener('click', (e) => {
+ const prefilledQuery = button.dataset.searchQuery;
+
+ if (window.Alpine && Alpine.store('search')) {
+ Alpine.store('search').open = true;
+
+ // Pre-fill query if specified
+ if (prefilledQuery) {
+ setTimeout(() => {
+ const searchInput = document.querySelector('[x-ref="searchInput"]');
+ if (searchInput) {
+ searchInput.value = prefilledQuery;
+ searchInput.dispatchEvent(new Event('input'));
+ }
+ }, 100);
+ }
+ }
});
- } catch (e) {
- console.error("Failed to initialize PagefindUI:", e);
- return;
- }
-
- // Get search wrapper element
- searchWrapper = document.getElementById("search-wrapper");
-
- // Add click handlers to triggers
- let triggers = document.querySelectorAll("[data-search-toggle]");
- triggers.forEach((trigger) => trigger.addEventListener("click", handleSearchToggle));
-});
-
-function handleSearchToggle() {
- if (!searchWrapper) return;
-
- const isHidden = searchWrapper.classList.contains("hidden");
- searchWrapper.classList.toggle("hidden");
- document.body.style.overflow = isHidden ? "hidden" : "";
-
- const searchInput = searchWrapper.querySelector("input");
- if (searchInput) {
- searchInput.value = "";
- searchInput.focus();
- }
-
- if (!searchWrapper.classList.contains("hidden")) {
- let clearTrigger = document.querySelector(".pagefind-ui__search-clear");
-
- if (clearTrigger && !clearTrigger.hasAttribute("listenerOnClick")) {
- clearTrigger.setAttribute("listenerOnClick", "true");
-
- clearTrigger.addEventListener("click", () => {
- searchInput.focus();
- });
- }
- }
-}
-
-// Add keyboard shortcut to close search modal with Escape key
-document.addEventListener("keydown", (event) => {
- if (event.key === "Escape" && searchWrapper && !searchWrapper.classList.contains("hidden")) {
- searchWrapper.classList.add("hidden");
- }
-});
+ });
+
+ // Note: Keyboard navigation (arrow keys, Enter) is handled by Alpine.js in search-modal.html
+});
\ No newline at end of file
diff --git a/modules/blox-tailwind/blox/faq/README.md b/modules/blox-tailwind/blox/faq/README.md
new file mode 100644
index 00000000..4889adc3
--- /dev/null
+++ b/modules/blox-tailwind/blox/faq/README.md
@@ -0,0 +1,58 @@
+# FAQ Block
+
+Display frequently asked questions in an interactive accordion format with automatic FAQPage structured data for SEO.
+
+## Features
+
+- ✅ Interactive accordion with Alpine.js
+- ✅ FAQPage Schema.org structured data for Google rich results
+- ✅ Responsive design with Tailwind CSS
+- ✅ Dark mode support
+- ✅ Markdown support in answers
+
+## Usage
+
+Add to your page's front matter:
+
+```yaml
+sections:
+ - block: faq
+ content:
+ title: Frequently Asked Questions
+ subtitle: Find answers to common questions
+ text: Can't find what you're looking for? [Contact us](/contact)
+ items:
+ - question: What is Hugo Blox Builder?
+ answer: Hugo Blox Builder is a no-code website builder framework powered by Hugo and Tailwind CSS.
+
+ - question: Is it free to use?
+ answer: Yes! Hugo Blox Builder is open source and free to use under the MIT license.
+
+ - question: How do I get started?
+ answer: |
+ Getting started is easy:
+ 1. Choose a template
+ 2. Click "Use Template"
+ 3. Customize your content
+ 4. Deploy to GitHub Pages or Netlify
+
+ button:
+ text: View All FAQs
+ url: /faq/
+ icon: arrow-right
+ design:
+ spacing:
+ padding: ["6rem", "0", "6rem", "0"]
+```
+
+## Structured Data
+
+This block automatically generates FAQPage structured data for better SEO and eligibility for Google's FAQ rich results.
+
+## Customization
+
+- **Accordion behavior**: One item open at a time (default)
+- **Colors**: Automatically adapts to your theme
+- **Spacing**: Customizable via `design.spacing`
+- **Background**: Customizable via `design.background.color`
+
diff --git a/modules/blox-tailwind/blox/faq/block.html b/modules/blox-tailwind/blox/faq/block.html
new file mode 100644
index 00000000..0a7c448b
--- /dev/null
+++ b/modules/blox-tailwind/blox/faq/block.html
@@ -0,0 +1,97 @@
+{{- $wcPage := .wcPage -}}
+{{- $wcBlock := .wcBlock -}}
+{{- $wcIdentifier := .wcIdentifier -}}
+
+{{/* Enable Alpine.js for interactive accordion */}}
+{{ $wcPage.Page.Store.Set "has_alpine" true }}
+
+{{- $st := $wcBlock.design.spacing | default dict -}}
+{{- $pad := $st.padding | default (slice "20px" "0" "20px" "0") -}}
+{{- $pad_sm := slice "3rem" "0" -}}
+{{- $mar := $st.margin | default (slice "0" "0" "0" "0") -}}
+
+{{- $bg := $wcBlock.design.background | default dict -}}
+{{- $bg_color_val := $bg.color | default "" -}}
+
+
+
+
+ {{- with $wcBlock.content.title -}}
+
+
+ {{ . | markdownify | emojify }}
+
+ {{- with $wcBlock.content.subtitle -}}
+
+ {{ . | markdownify | emojify }}
+
+ {{- end -}}
+
+ {{- end -}}
+
+ {{- with $wcBlock.content.text -}}
+
+
+
+
+
\ No newline at end of file
diff --git a/modules/blox-tailwind/layouts/_partials/docs_layout.html b/modules/blox-tailwind/layouts/_partials/docs_layout.html
index 80c0031b..3d4a4359 100644
--- a/modules/blox-tailwind/layouts/_partials/docs_layout.html
+++ b/modules/blox-tailwind/layouts/_partials/docs_layout.html
@@ -9,9 +9,16 @@
{{ end }}
-
+ {{/* Pagefind metadata for search filtering */}}
+
{{ .Title }}
{{ .Content }}
+
+ {{/* Use hidden spans for filter values - best practice per Pagefind docs */}}
+ docs
+ {{ with .Params.categories }}
+ {{ index . 0 }}
+ {{ end }}
+
+{{- end -}}
+
diff --git a/modules/blox-tailwind/layouts/faq/single.html b/modules/blox-tailwind/layouts/faq/single.html
new file mode 100644
index 00000000..16b17223
--- /dev/null
+++ b/modules/blox-tailwind/layouts/faq/single.html
@@ -0,0 +1,141 @@
+{{- define "main" -}}
+
+{{/* Enable Alpine.js for interactive FAQ accordion */}}
+{{ .Page.Store.Set "has_alpine" true }}
+
+{{/* Pagefind metadata for search filtering */}}
+
+ {{/* Use hidden spans for filter values - best practice per Pagefind docs */}}
+ faq
+ {{ with .Params.categories }}
+ {{ index . 0 }}
+ {{ end }}
+
+
+ {{ end }}
+
+ {{/* FAQ Accordion */}}
+ {{ with .Params.faqs }}
+
+ {{ range $index, $item := . }}
+
+
+
+
+ {{ $item.answer | markdownify }}
+
+
+
+ {{ end }}
+
+ {{ end }}
+
+ {{/* If no explicit FAQs, show child pages as FAQ items */}}
+ {{ if not .Params.faqs }}
+ {{ $child_pages := .Pages }}
+ {{ if $child_pages }}
+
+
+{{- end -}}
diff --git a/modules/blox-tailwind/layouts/questions/single.html b/modules/blox-tailwind/layouts/questions/single.html
new file mode 100644
index 00000000..20649aef
--- /dev/null
+++ b/modules/blox-tailwind/layouts/questions/single.html
@@ -0,0 +1,151 @@
+{{- define "main" -}}
+
+{{/* Pagefind metadata for search filtering */}}
+
+ {{/* Use hidden spans for filter values - best practice per Pagefind docs */}}
+ questions
+ {{ with .Params.categories }}
+ {{ index . 0 }}
+ {{ end }}
+ {{ with .Params.difficulty }}
+ {{ . }}
+ {{ end }}
+
+