block-faq.php

I’ll make this as a reusable advanced FAQ block template for RX Theme, with safe escaping, schema JSON-LD, accordion behavior, block attributes, ACF fallback, accessibility, and theme-friendly class naming.

Yes dear friend. Create this file:

/template-parts/blocks/block-faq.php

This is an advanced reusable FAQ block for RX Theme. It supports:

Advanced features included: $args support, ACF repeater support, post meta fallback, FAQ schema JSON-LD, accessible accordion, search/filter inside FAQ, expand/collapse all, copy answer button, deep-link support, RTL-friendly markup, safe escaping, unique block IDs, lazy JS, no jQuery dependency.

<?php
/**
 * Template Part: Advanced FAQ Block
 *
 * File: /template-parts/blocks/block-faq.php
 *
 * Usage examples:
 *
 * 1. Basic:
 * get_template_part( 'template-parts/blocks/block', 'faq' );
 *
 * 2. With custom args:
 * get_template_part(
 *     'template-parts/blocks/block',
 *     'faq',
 *     array(
 *         'title'       => 'Frequently Asked Questions',
 *         'subtitle'    => 'Helpful answers for readers.',
 *         'description' => 'Find common questions and simple answers.',
 *         'faqs'        => array(
 *             array(
 *                 'question' => 'What is RX Theme?',
 *                 'answer'   => 'RX Theme is a custom WordPress theme.',
 *             ),
 *         ),
 *     )
 * );
 *
 * 3. ACF supported field names:
 * - rx_faq_title
 * - rx_faq_subtitle
 * - rx_faq_description
 * - rx_faq_items repeater
 *   - question
 *   - answer
 *
 * @package RX_Theme
 */

defined( 'ABSPATH' ) || exit;

/**
 * ---------------------------------------------------------
 * 1. Safe default args
 * ---------------------------------------------------------
 */

$rx_faq_defaults = array(
	'id'                    => '',
	'title'                 => '',
	'subtitle'              => '',
	'description'           => '',
	'faqs'                  => array(),
	'layout'                => 'accordion', // accordion, list.
	'style'                 => 'default',   // default, card, minimal.
	'columns'               => 1,
	'show_search'           => true,
	'show_expand_all'       => true,
	'show_copy_button'      => false,
	'open_first'            => false,
	'allow_multiple_open'   => true,
	'enable_schema'         => true,
	'enable_deep_link'      => true,
	'enable_count'          => true,
	'heading_level'         => 2,
	'item_heading_level'    => 3,
	'empty_message'         => __( 'No frequently asked questions found.', 'rx-theme' ),
	'search_placeholder'    => __( 'Search questions...', 'rx-theme' ),
	'expand_all_text'       => __( 'Expand all', 'rx-theme' ),
	'collapse_all_text'     => __( 'Collapse all', 'rx-theme' ),
	'copy_text'             => __( 'Copy', 'rx-theme' ),
	'copied_text'           => __( 'Copied!', 'rx-theme' ),
	'no_result_text'        => __( 'No matching questions found.', 'rx-theme' ),
	'custom_class'          => '',
	'anchor'                => '',
	'background'            => '',
	'text_align'            => '',
	'container'             => true,
	'aria_label'            => __( 'Frequently asked questions', 'rx-theme' ),
);

$rx_faq_args = isset( $args ) && is_array( $args ) ? wp_parse_args( $args, $rx_faq_defaults ) : $rx_faq_defaults;

/**
 * ---------------------------------------------------------
 * 2. Gutenberg block support when passed by render callback
 * ---------------------------------------------------------
 */

if ( isset( $rx_faq_args['block'] ) && is_array( $rx_faq_args['block'] ) ) {
	$rx_block = $rx_faq_args['block'];

	if ( empty( $rx_faq_args['id'] ) && ! empty( $rx_block['id'] ) ) {
		$rx_faq_args['id'] = 'rx-faq-' . sanitize_html_class( $rx_block['id'] );
	}

	if ( empty( $rx_faq_args['anchor'] ) && ! empty( $rx_block['anchor'] ) ) {
		$rx_faq_args['anchor'] = sanitize_title( $rx_block['anchor'] );
	}

	if ( ! empty( $rx_block['className'] ) ) {
		$rx_faq_args['custom_class'] .= ' ' . sanitize_html_class( $rx_block['className'] );
	}

	if ( ! empty( $rx_block['align'] ) ) {
		$rx_faq_args['custom_class'] .= ' align' . sanitize_html_class( $rx_block['align'] );
	}
}

/**
 * ---------------------------------------------------------
 * 3. ACF field fallback
 * ---------------------------------------------------------
 */

if ( function_exists( 'get_field' ) ) {
	$rx_acf_title       = get_field( 'rx_faq_title' );
	$rx_acf_subtitle    = get_field( 'rx_faq_subtitle' );
	$rx_acf_description = get_field( 'rx_faq_description' );
	$rx_acf_items       = get_field( 'rx_faq_items' );

	if ( empty( $rx_faq_args['title'] ) && ! empty( $rx_acf_title ) ) {
		$rx_faq_args['title'] = $rx_acf_title;
	}

	if ( empty( $rx_faq_args['subtitle'] ) && ! empty( $rx_acf_subtitle ) ) {
		$rx_faq_args['subtitle'] = $rx_acf_subtitle;
	}

	if ( empty( $rx_faq_args['description'] ) && ! empty( $rx_acf_description ) ) {
		$rx_faq_args['description'] = $rx_acf_description;
	}

	if ( empty( $rx_faq_args['faqs'] ) && is_array( $rx_acf_items ) ) {
		$rx_faq_args['faqs'] = $rx_acf_items;
	}
}

/**
 * ---------------------------------------------------------
 * 4. Post meta fallback
 * ---------------------------------------------------------
 *
 * Supported meta:
 * - rx_faq_items as array
 * - rx_faq_items as JSON string
 */

if ( empty( $rx_faq_args['faqs'] ) ) {
	$rx_meta_faqs = get_post_meta( get_the_ID(), 'rx_faq_items', true );

	if ( is_string( $rx_meta_faqs ) ) {
		$rx_decoded = json_decode( $rx_meta_faqs, true );

		if ( json_last_error() === JSON_ERROR_NONE && is_array( $rx_decoded ) ) {
			$rx_meta_faqs = $rx_decoded;
		}
	}

	if ( is_array( $rx_meta_faqs ) && ! empty( $rx_meta_faqs ) ) {
		$rx_faq_args['faqs'] = $rx_meta_faqs;
	}
}

/**
 * ---------------------------------------------------------
 * 5. Demo fallback only for Customizer preview/admin preview
 * ---------------------------------------------------------
 */

if ( empty( $rx_faq_args['faqs'] ) && ( is_customize_preview() || is_admin() ) ) {
	$rx_faq_args['faqs'] = array(
		array(
			'question' => __( 'What is this FAQ block?', 'rx-theme' ),
			'answer'   => __( 'This is an advanced reusable FAQ block for RX Theme. You can pass FAQ items by args, ACF, or post meta.', 'rx-theme' ),
		),
		array(
			'question' => __( 'Does this block support FAQ schema?', 'rx-theme' ),
			'answer'   => __( 'Yes. It can automatically print FAQPage JSON-LD schema for better structured data support.', 'rx-theme' ),
		),
	);
}

/**
 * ---------------------------------------------------------
 * 6. Normalize FAQ data
 * ---------------------------------------------------------
 */

$rx_normalized_faqs = array();

if ( is_array( $rx_faq_args['faqs'] ) ) {
	foreach ( $rx_faq_args['faqs'] as $rx_index => $rx_item ) {
		if ( ! is_array( $rx_item ) ) {
			continue;
		}

		$rx_question = '';

		if ( isset( $rx_item['question'] ) ) {
			$rx_question = $rx_item['question'];
		} elseif ( isset( $rx_item['title'] ) ) {
			$rx_question = $rx_item['title'];
		} elseif ( isset( $rx_item['q'] ) ) {
			$rx_question = $rx_item['q'];
		}

		$rx_answer = '';

		if ( isset( $rx_item['answer'] ) ) {
			$rx_answer = $rx_item['answer'];
		} elseif ( isset( $rx_item['content'] ) ) {
			$rx_answer = $rx_item['content'];
		} elseif ( isset( $rx_item['a'] ) ) {
			$rx_answer = $rx_item['a'];
		}

		$rx_category = isset( $rx_item['category'] ) ? $rx_item['category'] : '';
		$rx_icon     = isset( $rx_item['icon'] ) ? $rx_item['icon'] : '';
		$rx_open     = isset( $rx_item['open'] ) ? (bool) $rx_item['open'] : false;

		$rx_question = wp_strip_all_tags( $rx_question );
		$rx_answer   = trim( (string) $rx_answer );

		if ( '' === $rx_question || '' === $rx_answer ) {
			continue;
		}

		$rx_normalized_faqs[] = array(
			'question' => $rx_question,
			'answer'   => $rx_answer,
			'category' => sanitize_text_field( $rx_category ),
			'icon'     => sanitize_text_field( $rx_icon ),
			'open'     => $rx_open,
		);
	}
}

$rx_faq_args['faqs'] = $rx_normalized_faqs;

/**
 * ---------------------------------------------------------
 * 7. Helper values
 * ---------------------------------------------------------
 */

$rx_block_id = ! empty( $rx_faq_args['id'] )
	? sanitize_html_class( $rx_faq_args['id'] )
	: 'rx-faq-' . wp_unique_id();

$rx_anchor_id = ! empty( $rx_faq_args['anchor'] )
	? sanitize_title( $rx_faq_args['anchor'] )
	: $rx_block_id;

$rx_heading_level = absint( $rx_faq_args['heading_level'] );
$rx_heading_level = $rx_heading_level >= 1 && $rx_heading_level <= 6 ? $rx_heading_level : 2;

$rx_item_heading_level = absint( $rx_faq_args['item_heading_level'] );
$rx_item_heading_level = $rx_item_heading_level >= 2 && $rx_item_heading_level <= 6 ? $rx_item_heading_level : 3;

$rx_faq_count = count( $rx_faq_args['faqs'] );

$rx_classes = array(
	'rx-block',
	'rx-block-faq',
	'rx-faq',
	'rx-faq--layout-' . sanitize_html_class( $rx_faq_args['layout'] ),
	'rx-faq--style-' . sanitize_html_class( $rx_faq_args['style'] ),
	'rx-faq--columns-' . absint( $rx_faq_args['columns'] ),
);

if ( ! empty( $rx_faq_args['custom_class'] ) ) {
	$rx_classes[] = sanitize_html_class( trim( $rx_faq_args['custom_class'] ) );
}

if ( ! empty( $rx_faq_args['background'] ) ) {
	$rx_classes[] = 'has-background';
	$rx_classes[] = 'has-' . sanitize_html_class( $rx_faq_args['background'] ) . '-background';
}

if ( ! empty( $rx_faq_args['text_align'] ) ) {
	$rx_classes[] = 'has-text-align-' . sanitize_html_class( $rx_faq_args['text_align'] );
}

$rx_wrapper_classes = implode( ' ', array_filter( $rx_classes ) );

$rx_data_settings = array(
	'allowMultipleOpen' => (bool) $rx_faq_args['allow_multiple_open'],
	'enableDeepLink'   => (bool) $rx_faq_args['enable_deep_link'],
	'copyText'         => (string) $rx_faq_args['copy_text'],
	'copiedText'       => (string) $rx_faq_args['copied_text'],
	'noResultText'     => (string) $rx_faq_args['no_result_text'],
);

/**
 * ---------------------------------------------------------
 * 8. Schema JSON-LD
 * ---------------------------------------------------------
 */

$rx_schema = array();

if ( ! empty( $rx_faq_args['enable_schema'] ) && ! empty( $rx_faq_args['faqs'] ) ) {
	$rx_schema = array(
		'@context'   => 'https://schema.org',
		'@type'      => 'FAQPage',
		'mainEntity' => array(),
	);

	foreach ( $rx_faq_args['faqs'] as $rx_schema_item ) {
		$rx_schema['mainEntity'][] = array(
			'@type'          => 'Question',
			'name'           => wp_strip_all_tags( $rx_schema_item['question'] ),
			'acceptedAnswer' => array(
				'@type' => 'Answer',
				'text'  => wp_strip_all_tags( $rx_schema_item['answer'] ),
			),
		);
	}
}

?>

<section
	id="<?php echo esc_attr( $rx_anchor_id ); ?>"
	class="<?php echo esc_attr( $rx_wrapper_classes ); ?>"
	aria-label="<?php echo esc_attr( $rx_faq_args['aria_label'] ); ?>"
	data-rx-faq="<?php echo esc_attr( wp_json_encode( $rx_data_settings ) ); ?>"
>
	<?php if ( ! empty( $rx_faq_args['container'] ) ) : ?>
		<div class="rx-container rx-faq__container">
	<?php endif; ?>

		<?php if ( ! empty( $rx_faq_args['subtitle'] ) || ! empty( $rx_faq_args['title'] ) || ! empty( $rx_faq_args['description'] ) ) : ?>
			<header class="rx-faq__header">
				<?php if ( ! empty( $rx_faq_args['subtitle'] ) ) : ?>
					<p class="rx-faq__subtitle">
						<?php echo esc_html( $rx_faq_args['subtitle'] ); ?>
					</p>
				<?php endif; ?>

				<?php if ( ! empty( $rx_faq_args['title'] ) ) : ?>
					<<?php echo esc_attr( 'h' . $rx_heading_level ); ?> class="rx-faq__title">
						<?php echo esc_html( $rx_faq_args['title'] ); ?>

						<?php if ( ! empty( $rx_faq_args['enable_count'] ) && $rx_faq_count > 0 ) : ?>
							<span class="rx-faq__count" aria-label="<?php echo esc_attr( sprintf( _n( '%s question', '%s questions', $rx_faq_count, 'rx-theme' ), number_format_i18n( $rx_faq_count ) ) ); ?>">
								<?php echo esc_html( number_format_i18n( $rx_faq_count ) ); ?>
							</span>
						<?php endif; ?>
					</<?php echo esc_attr( 'h' . $rx_heading_level ); ?>>
				<?php endif; ?>

				<?php if ( ! empty( $rx_faq_args['description'] ) ) : ?>
					<div class="rx-faq__description">
						<?php echo wp_kses_post( wpautop( $rx_faq_args['description'] ) ); ?>
					</div>
				<?php endif; ?>
			</header>
		<?php endif; ?>

		<?php if ( ! empty( $rx_faq_args['faqs'] ) ) : ?>

			<?php if ( ! empty( $rx_faq_args['show_search'] ) || ! empty( $rx_faq_args['show_expand_all'] ) ) : ?>
				<div class="rx-faq__toolbar">
					<?php if ( ! empty( $rx_faq_args['show_search'] ) ) : ?>
						<div class="rx-faq__search-wrap">
							<label class="screen-reader-text" for="<?php echo esc_attr( $rx_block_id ); ?>-search">
								<?php esc_html_e( 'Search FAQ questions', 'rx-theme' ); ?>
							</label>

							<input
								id="<?php echo esc_attr( $rx_block_id ); ?>-search"
								class="rx-faq__search"
								type="search"
								placeholder="<?php echo esc_attr( $rx_faq_args['search_placeholder'] ); ?>"
								autocomplete="off"
								data-rx-faq-search
							/>
						</div>
					<?php endif; ?>

					<?php if ( ! empty( $rx_faq_args['show_expand_all'] ) ) : ?>
						<div class="rx-faq__actions">
							<button
								type="button"
								class="rx-faq__action rx-faq__expand-all"
								data-rx-faq-expand-all
							>
								<?php echo esc_html( $rx_faq_args['expand_all_text'] ); ?>
							</button>

							<button
								type="button"
								class="rx-faq__action rx-faq__collapse-all"
								data-rx-faq-collapse-all
							>
								<?php echo esc_html( $rx_faq_args['collapse_all_text'] ); ?>
							</button>
						</div>
					<?php endif; ?>
				</div>
			<?php endif; ?>

			<div class="rx-faq__items" data-rx-faq-items>
				<?php foreach ( $rx_faq_args['faqs'] as $rx_index => $rx_faq ) : ?>
					<?php
					$rx_item_number = $rx_index + 1;
					$rx_item_id     = $rx_block_id . '-item-' . $rx_item_number;
					$rx_button_id   = $rx_item_id . '-button';
					$rx_panel_id    = $rx_item_id . '-panel';

					$rx_is_open = false;

					if ( ! empty( $rx_faq['open'] ) ) {
						$rx_is_open = true;
					}

					if ( ! empty( $rx_faq_args['open_first'] ) && 0 === $rx_index ) {
						$rx_is_open = true;
					}

					$rx_question_plain = wp_strip_all_tags( $rx_faq['question'] );
					$rx_answer_plain   = wp_strip_all_tags( $rx_faq['answer'] );
					$rx_search_text    = strtolower( $rx_question_plain . ' ' . $rx_answer_plain . ' ' . $rx_faq['category'] );
					?>

					<article
						id="<?php echo esc_attr( $rx_item_id ); ?>"
						class="rx-faq__item<?php echo $rx_is_open ? ' is-open' : ''; ?>"
						data-rx-faq-item
						data-rx-faq-text="<?php echo esc_attr( $rx_search_text ); ?>"
						<?php if ( ! empty( $rx_faq['category'] ) ) : ?>
							data-rx-faq-category="<?php echo esc_attr( $rx_faq['category'] ); ?>"
						<?php endif; ?>
					>
						<<?php echo esc_attr( 'h' . $rx_item_heading_level ); ?> class="rx-faq__question-heading">
							<button
								id="<?php echo esc_attr( $rx_button_id ); ?>"
								class="rx-faq__question"
								type="button"
								aria-expanded="<?php echo $rx_is_open ? 'true' : 'false'; ?>"
								aria-controls="<?php echo esc_attr( $rx_panel_id ); ?>"
								data-rx-faq-button
							>
								<span class="rx-faq__question-inner">
									<?php if ( ! empty( $rx_faq['icon'] ) ) : ?>
										<span class="rx-faq__icon" aria-hidden="true">
											<?php echo esc_html( $rx_faq['icon'] ); ?>
										</span>
									<?php endif; ?>

									<span class="rx-faq__number" aria-hidden="true">
										<?php echo esc_html( sprintf( '%02d', $rx_item_number ) ); ?>
									</span>

									<span class="rx-faq__question-text">
										<?php echo esc_html( $rx_faq['question'] ); ?>
									</span>

									<?php if ( ! empty( $rx_faq['category'] ) ) : ?>
										<span class="rx-faq__category">
											<?php echo esc_html( $rx_faq['category'] ); ?>
										</span>
									<?php endif; ?>
								</span>

								<span class="rx-faq__toggle-icon" aria-hidden="true">
									<span class="rx-faq__toggle-line rx-faq__toggle-line--horizontal"></span>
									<span class="rx-faq__toggle-line rx-faq__toggle-line--vertical"></span>
								</span>
							</button>
						</<?php echo esc_attr( 'h' . $rx_item_heading_level ); ?>>

						<div
							id="<?php echo esc_attr( $rx_panel_id ); ?>"
							class="rx-faq__answer"
							role="region"
							aria-labelledby="<?php echo esc_attr( $rx_button_id ); ?>"
							<?php echo $rx_is_open ? '' : 'hidden'; ?>
							data-rx-faq-panel
						>
							<div class="rx-faq__answer-inner">
								<?php echo wp_kses_post( wpautop( do_shortcode( $rx_faq['answer'] ) ) ); ?>

								<?php if ( ! empty( $rx_faq_args['show_copy_button'] ) ) : ?>
									<button
										type="button"
										class="rx-faq__copy"
										data-rx-faq-copy
										data-rx-faq-copy-text="<?php echo esc_attr( $rx_answer_plain ); ?>"
									>
										<?php echo esc_html( $rx_faq_args['copy_text'] ); ?>
									</button>
								<?php endif; ?>
							</div>
						</div>
					</article>
				<?php endforeach; ?>
			</div>

			<p class="rx-faq__no-results" hidden data-rx-faq-no-results>
				<?php echo esc_html( $rx_faq_args['no_result_text'] ); ?>
			</p>

		<?php else : ?>

			<p class="rx-faq__empty">
				<?php echo esc_html( $rx_faq_args['empty_message'] ); ?>
			</p>

		<?php endif; ?>

	<?php if ( ! empty( $rx_faq_args['container'] ) ) : ?>
		</div>
	<?php endif; ?>
</section>

<?php if ( ! empty( $rx_schema ) ) : ?>
	<script type="application/ld+json">
		<?php echo wp_json_encode( $rx_schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT ); ?>
	</script>
<?php endif; ?>

<?php
/**
 * ---------------------------------------------------------
 * 9. Inline script printed once
 * ---------------------------------------------------------
 */

if ( ! defined( 'RX_THEME_FAQ_BLOCK_SCRIPT_PRINTED' ) ) :
	define( 'RX_THEME_FAQ_BLOCK_SCRIPT_PRINTED', true );
	?>
	<script>
		(function () {
			'use strict';

			const faqBlocks = document.querySelectorAll('[data-rx-faq]');

			if (!faqBlocks.length) {
				return;
			}

			const safeJSON = function (value) {
				try {
					return JSON.parse(value || '{}');
				} catch (error) {
					return {};
				}
			};

			const normalize = function (value) {
				return String(value || '').toLowerCase().trim();
			};

			const openItem = function (item) {
				const button = item.querySelector('[data-rx-faq-button]');
				const panel = item.querySelector('[data-rx-faq-panel]');

				if (!button || !panel) {
					return;
				}

				item.classList.add('is-open');
				button.setAttribute('aria-expanded', 'true');
				panel.hidden = false;
			};

			const closeItem = function (item) {
				const button = item.querySelector('[data-rx-faq-button]');
				const panel = item.querySelector('[data-rx-faq-panel]');

				if (!button || !panel) {
					return;
				}

				item.classList.remove('is-open');
				button.setAttribute('aria-expanded', 'false');
				panel.hidden = true;
			};

			const copyText = async function (text) {
				if (!navigator.clipboard || !text) {
					return false;
				}

				try {
					await navigator.clipboard.writeText(text);
					return true;
				} catch (error) {
					return false;
				}
			};

			faqBlocks.forEach(function (block) {
				const settings = safeJSON(block.getAttribute('data-rx-faq'));
				const items = Array.from(block.querySelectorAll('[data-rx-faq-item]'));
				const searchInput = block.querySelector('[data-rx-faq-search]');
				const expandAllButton = block.querySelector('[data-rx-faq-expand-all]');
				const collapseAllButton = block.querySelector('[data-rx-faq-collapse-all]');
				const noResults = block.querySelector('[data-rx-faq-no-results]');

				items.forEach(function (item) {
					const button = item.querySelector('[data-rx-faq-button]');

					if (!button) {
						return;
					}

					button.addEventListener('click', function () {
						const isOpen = button.getAttribute('aria-expanded') === 'true';

						if (!settings.allowMultipleOpen) {
							items.forEach(function (otherItem) {
								if (otherItem !== item) {
									closeItem(otherItem);
								}
							});
						}

						if (isOpen) {
							closeItem(item);
						} else {
							openItem(item);

							if (settings.enableDeepLink && item.id) {
								const newUrl = window.location.pathname + window.location.search + '#' + item.id;
								window.history.replaceState(null, '', newUrl);
							}
						}
					});
				});

				if (searchInput) {
					searchInput.addEventListener('input', function () {
						const query = normalize(searchInput.value);
						let visibleCount = 0;

						items.forEach(function (item) {
							const text = normalize(item.getAttribute('data-rx-faq-text'));
							const isMatch = !query || text.indexOf(query) !== -1;

							item.hidden = !isMatch;

							if (isMatch) {
								visibleCount++;
							}
						});

						if (noResults) {
							noResults.hidden = visibleCount !== 0;
						}
					});
				}

				if (expandAllButton) {
					expandAllButton.addEventListener('click', function () {
						items.forEach(function (item) {
							if (!item.hidden) {
								openItem(item);
							}
						});
					});
				}

				if (collapseAllButton) {
					collapseAllButton.addEventListener('click', function () {
						items.forEach(function (item) {
							closeItem(item);
						});
					});
				}

				block.addEventListener('click', function (event) {
					const copyButton = event.target.closest('[data-rx-faq-copy]');

					if (!copyButton) {
						return;
					}

					const originalText = copyButton.textContent;
					const text = copyButton.getAttribute('data-rx-faq-copy-text');

					copyText(text).then(function (success) {
						if (!success) {
							return;
						}

						copyButton.textContent = settings.copiedText || 'Copied!';

						window.setTimeout(function () {
							copyButton.textContent = originalText || settings.copyText || 'Copy';
						}, 1600);
					});
				});

				if (settings.enableDeepLink && window.location.hash) {
					const target = block.querySelector(window.location.hash);

					if (target && target.matches('[data-rx-faq-item]')) {
						openItem(target);

						window.setTimeout(function () {
							target.scrollIntoView({
								behavior: 'smooth',
								block: 'start'
							});
						}, 100);
					}
				}
			});
		})();
	</script>
<?php endif; ?>

Use it like this in any template:

<?php
get_template_part(
	'template-parts/blocks/block',
	'faq',
	array(
		'title'            => 'Frequently Asked Questions',
		'subtitle'         => 'RX Theme Help Center',
		'description'      => 'Common questions and answers for visitors.',
		'show_search'      => true,
		'show_expand_all'  => true,
		'show_copy_button' => true,
		'open_first'       => true,
		'enable_schema'    => true,
		'faqs'             => array(
			array(
				'question' => 'What is RX Theme?',
				'answer'   => 'RX Theme is a custom WordPress theme built for medical content, performance, SEO, and advanced design.',
				'category' => 'Theme',
				'icon'     => '❓',
			),
			array(
				'question' => 'Does RX Theme support FAQ schema?',
				'answer'   => 'Yes. This FAQ block automatically creates FAQPage JSON-LD schema when schema is enabled.',
				'category' => 'SEO',
				'icon'     => '✅',
			),
		),
	)
);
?>

Recommended CSS file name for this block:

/assets/css/blocks/block-faq.css

Recommended JS file name if you later separate inline JavaScript:

/assets/js/blocks/block-faq.js

Leave a Reply

Your email address will not be published. Required fields are marked *