I’ll build this as a theme shortcode file you can drop into inc/shortcodes/faq.php, with FAQ accordion output, schema JSON-LD, categories, search/filter UI, icons, accessibility, styling hooks, and safe sanitization.
Below is an advanced inc/shortcodes/faq.php file for your RX Theme. It includes:
FAQ accordion, FAQ schema JSON-LD, manual FAQ items, custom post type support, category filter, live search, expand/collapse all, deep-link anchor support, responsive grid/accordion layout, accessible ARIA controls, copy-link button, clean CSS/JS, and safe sanitization.
Create this file:
<?php
/**
* RX Theme FAQ Shortcodes
*
* File: inc/shortcodes/faq.php
*
* Shortcodes:
* [rx_faq]
* [rx_faq source="manual"] [rx_faq_item question="What is RX Theme?"]Answer here[/rx_faq_item] [/rx_faq]
* [rx_faq_item question="Question"]Answer[/rx_faq_item]
*
* Recommended include from functions.php:
* require_once get_template_directory() . '/inc/shortcodes/faq.php';
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'RX_Theme_FAQ_Shortcodes' ) ) :
final class RX_Theme_FAQ_Shortcodes {
private static $instance = null;
private static $assets_loaded = false;
private static $schema_items = array();
public static function instance() {
if ( null === self::$instance ) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
add_shortcode( 'rx_faq', array( $this, 'render_faq_shortcode' ) );
add_shortcode( 'rx_faq_item', array( $this, 'render_faq_item_shortcode' ) );
add_action( 'wp_footer', array( $this, 'print_schema_json_ld' ), 99 );
}
public function render_faq_shortcode( $atts, $content = null ) {
$atts = shortcode_atts(
array(
'source' => 'manual', // manual, post_type, current_post_meta.
'title' => 'Frequently Asked Questions',
'subtitle' => '',
'description' => '',
'post_type' => 'rx_faq',
'taxonomy' => 'faq_category',
'category' => '',
'limit' => 20,
'orderby' => 'menu_order',
'order' => 'ASC',
'include' => '',
'exclude' => '',
'layout' => 'accordion', // accordion, grid.
'columns' => 2,
'open_first' => 'yes',
'multiple_open' => 'yes',
'search' => 'yes',
'filter' => 'yes',
'expand_all' => 'yes',
'copy_link' => 'yes',
'numbering' => 'no',
'icon' => 'plus',
'schema' => 'yes',
'class' => '',
'id' => '',
'empty_text' => 'No FAQs found.',
),
$atts,
'rx_faq'
);
self::enqueue_assets();
$uid = ! empty( $atts['id'] ) ? sanitize_html_class( $atts['id'] ) : 'rx-faq-' . wp_generate_uuid4();
$items = array();
if ( 'post_type' === $atts['source'] ) {
$items = $this->get_faqs_from_post_type( $atts );
} elseif ( 'current_post_meta' === $atts['source'] ) {
$items = $this->get_faqs_from_current_post_meta();
} else {
$items = $this->get_faqs_from_manual_content( $content );
}
$items = apply_filters( 'rx_theme_faq_items', $items, $atts );
if ( empty( $items ) ) {
return sprintf(
'<div class="rx-faq-empty">%s</div>',
esc_html( $atts['empty_text'] )
);
}
if ( 'yes' === $atts['schema'] ) {
self::$schema_items = array_merge( self::$schema_items, $items );
}
$categories = $this->collect_categories( $items );
$wrapper_classes = array(
'rx-faq-wrap',
'rx-faq-layout-' . sanitize_html_class( $atts['layout'] ),
'rx-faq-icon-' . sanitize_html_class( $atts['icon'] ),
);
if ( ! empty( $atts['class'] ) ) {
$wrapper_classes[] = sanitize_html_class( $atts['class'] );
}
$columns = absint( $atts['columns'] );
if ( $columns < 1 ) {
$columns = 1;
}
if ( $columns > 4 ) {
$columns = 4;
}
ob_start();
?>
<section
id="<?php echo esc_attr( $uid ); ?>"
class="<?php echo esc_attr( implode( ' ', $wrapper_classes ) ); ?>"
data-multiple-open="<?php echo esc_attr( $atts['multiple_open'] ); ?>"
style="--rx-faq-columns: <?php echo esc_attr( $columns ); ?>;"
>
<?php if ( ! empty( $atts['title'] ) || ! empty( $atts['subtitle'] ) || ! empty( $atts['description'] ) ) : ?>
<div class="rx-faq-header">
<?php if ( ! empty( $atts['subtitle'] ) ) : ?>
<p class="rx-faq-subtitle"><?php echo esc_html( $atts['subtitle'] ); ?></p>
<?php endif; ?>
<?php if ( ! empty( $atts['title'] ) ) : ?>
<h2 class="rx-faq-title"><?php echo esc_html( $atts['title'] ); ?></h2>
<?php endif; ?>
<?php if ( ! empty( $atts['description'] ) ) : ?>
<div class="rx-faq-description"><?php echo wp_kses_post( wpautop( $atts['description'] ) ); ?></div>
<?php endif; ?>
</div>
<?php endif; ?>
<?php if ( 'yes' === $atts['search'] || 'yes' === $atts['filter'] || 'yes' === $atts['expand_all'] ) : ?>
<div class="rx-faq-toolbar">
<?php if ( 'yes' === $atts['search'] ) : ?>
<label class="rx-faq-search-label">
<span class="screen-reader-text"><?php esc_html_e( 'Search FAQ', 'rx-theme' ); ?></span>
<input
type="search"
class="rx-faq-search"
placeholder="<?php esc_attr_e( 'Search questions...', 'rx-theme' ); ?>"
autocomplete="off"
>
</label>
<?php endif; ?>
<?php if ( 'yes' === $atts['filter'] && count( $categories ) > 1 ) : ?>
<select class="rx-faq-filter" aria-label="<?php esc_attr_e( 'Filter FAQ category', 'rx-theme' ); ?>">
<option value=""><?php esc_html_e( 'All Categories', 'rx-theme' ); ?></option>
<?php foreach ( $categories as $cat_key => $cat_label ) : ?>
<option value="<?php echo esc_attr( $cat_key ); ?>">
<?php echo esc_html( $cat_label ); ?>
</option>
<?php endforeach; ?>
</select>
<?php endif; ?>
<?php if ( 'yes' === $atts['expand_all'] ) : ?>
<div class="rx-faq-actions">
<button type="button" class="rx-faq-expand-all">
<?php esc_html_e( 'Expand All', 'rx-theme' ); ?>
</button>
<button type="button" class="rx-faq-collapse-all">
<?php esc_html_e( 'Collapse All', 'rx-theme' ); ?>
</button>
</div>
<?php endif; ?>
</div>
<?php endif; ?>
<div class="rx-faq-list">
<?php
$count = 0;
foreach ( $items as $index => $item ) :
$count++;
$question = isset( $item['question'] ) ? $item['question'] : '';
$answer = isset( $item['answer'] ) ? $item['answer'] : '';
$category = isset( $item['category'] ) ? $item['category'] : '';
$cat_key = sanitize_title( $category );
if ( empty( $question ) || empty( $answer ) ) {
continue;
}
$item_id = $uid . '-item-' . $count;
$panel_id = $item_id . '-panel';
$button_id = $item_id . '-button';
$is_open = ( 1 === $count && 'yes' === $atts['open_first'] );
$anchor_slug = sanitize_title( $question );
?>
<article
id="<?php echo esc_attr( $anchor_slug ); ?>"
class="rx-faq-item <?php echo $is_open ? 'is-open' : ''; ?>"
data-category="<?php echo esc_attr( $cat_key ); ?>"
data-search="<?php echo esc_attr( strtolower( wp_strip_all_tags( $question . ' ' . $answer . ' ' . $category ) ) ); ?>"
>
<h3 class="rx-faq-question-wrap">
<button
id="<?php echo esc_attr( $button_id ); ?>"
class="rx-faq-question"
type="button"
aria-expanded="<?php echo $is_open ? 'true' : 'false'; ?>"
aria-controls="<?php echo esc_attr( $panel_id ); ?>"
>
<span class="rx-faq-question-text">
<?php if ( 'yes' === $atts['numbering'] ) : ?>
<span class="rx-faq-number"><?php echo esc_html( str_pad( $count, 2, '0', STR_PAD_LEFT ) ); ?></span>
<?php endif; ?>
<?php echo esc_html( $question ); ?>
</span>
<span class="rx-faq-toggle-icon" aria-hidden="true"></span>
</button>
</h3>
<div
id="<?php echo esc_attr( $panel_id ); ?>"
class="rx-faq-answer"
role="region"
aria-labelledby="<?php echo esc_attr( $button_id ); ?>"
<?php echo $is_open ? '' : 'hidden'; ?>
>
<div class="rx-faq-answer-inner">
<?php if ( ! empty( $category ) ) : ?>
<span class="rx-faq-category"><?php echo esc_html( $category ); ?></span>
<?php endif; ?>
<?php echo wp_kses_post( wpautop( do_shortcode( $answer ) ) ); ?>
<?php if ( 'yes' === $atts['copy_link'] ) : ?>
<button type="button" class="rx-faq-copy-link" data-anchor="<?php echo esc_attr( $anchor_slug ); ?>">
<?php esc_html_e( 'Copy FAQ link', 'rx-theme' ); ?>
</button>
<?php endif; ?>
</div>
</div>
</article>
<?php endforeach; ?>
</div>
<div class="rx-faq-no-results" hidden>
<?php esc_html_e( 'No matching FAQ found.', 'rx-theme' ); ?>
</div>
</section>
<?php
return ob_get_clean();
}
public function render_faq_item_shortcode( $atts, $content = null ) {
$atts = shortcode_atts(
array(
'question' => '',
'category' => '',
),
$atts,
'rx_faq_item'
);
$data = array(
'question' => sanitize_text_field( $atts['question'] ),
'category' => sanitize_text_field( $atts['category'] ),
'answer' => wp_kses_post( $content ),
);
return '<!--RX_FAQ_ITEM:' . esc_html( base64_encode( wp_json_encode( $data ) ) ) . '-->';
}
private function get_faqs_from_manual_content( $content ) {
$items = array();
if ( empty( $content ) ) {
return $items;
}
$processed = do_shortcode( $content );
preg_match_all( '/<!--RX_FAQ_ITEM:(.*?)-->/s', $processed, $matches );
if ( empty( $matches[1] ) ) {
return $items;
}
foreach ( $matches[1] as $encoded ) {
$json = base64_decode( $encoded );
$data = json_decode( $json, true );
if ( ! is_array( $data ) ) {
continue;
}
$items[] = array(
'question' => isset( $data['question'] ) ? sanitize_text_field( $data['question'] ) : '',
'answer' => isset( $data['answer'] ) ? wp_kses_post( $data['answer'] ) : '',
'category' => isset( $data['category'] ) ? sanitize_text_field( $data['category'] ) : '',
);
}
return $items;
}
private function get_faqs_from_post_type( $atts ) {
$items = array();
$args = array(
'post_type' => sanitize_key( $atts['post_type'] ),
'post_status' => 'publish',
'posts_per_page' => absint( $atts['limit'] ),
'orderby' => sanitize_key( $atts['orderby'] ),
'order' => 'DESC' === strtoupper( $atts['order'] ) ? 'DESC' : 'ASC',
'ignore_sticky_posts' => true,
'no_found_rows' => true,
);
if ( ! empty( $atts['include'] ) ) {
$args['post__in'] = array_map( 'absint', explode( ',', $atts['include'] ) );
$args['orderby'] = 'post__in';
}
if ( ! empty( $atts['exclude'] ) ) {
$args['post__not_in'] = array_map( 'absint', explode( ',', $atts['exclude'] ) );
}
if ( ! empty( $atts['category'] ) && taxonomy_exists( $atts['taxonomy'] ) ) {
$args['tax_query'] = array(
array(
'taxonomy' => sanitize_key( $atts['taxonomy'] ),
'field' => 'slug',
'terms' => array_map( 'sanitize_title', explode( ',', $atts['category'] ) ),
),
);
}
$query = new WP_Query( $args );
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
$post_id = get_the_ID();
$category_name = '';
if ( taxonomy_exists( $atts['taxonomy'] ) ) {
$terms = get_the_terms( $post_id, sanitize_key( $atts['taxonomy'] ) );
if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) {
$category_name = $terms[0]->name;
}
}
$items[] = array(
'question' => get_the_title(),
'answer' => apply_filters( 'the_content', get_the_content() ),
'category' => $category_name,
);
}
wp_reset_postdata();
}
return $items;
}
private function get_faqs_from_current_post_meta() {
$items = array();
if ( ! is_singular() ) {
return $items;
}
$post_id = get_the_ID();
/**
* Supported meta format:
* rx_faq_items = array(
* array(
* 'question' => 'Question?',
* 'answer' => 'Answer',
* 'category' => 'General'
* )
* )
*/
$meta_items = get_post_meta( $post_id, 'rx_faq_items', true );
if ( empty( $meta_items ) || ! is_array( $meta_items ) ) {
return $items;
}
foreach ( $meta_items as $item ) {
$items[] = array(
'question' => isset( $item['question'] ) ? sanitize_text_field( $item['question'] ) : '',
'answer' => isset( $item['answer'] ) ? wp_kses_post( $item['answer'] ) : '',
'category' => isset( $item['category'] ) ? sanitize_text_field( $item['category'] ) : '',
);
}
return $items;
}
private function collect_categories( $items ) {
$categories = array();
foreach ( $items as $item ) {
if ( empty( $item['category'] ) ) {
continue;
}
$key = sanitize_title( $item['category'] );
$categories[ $key ] = $item['category'];
}
return $categories;
}
public static function enqueue_assets() {
if ( self::$assets_loaded ) {
return;
}
self::$assets_loaded = true;
wp_register_style( 'rx-theme-faq', false, array(), '1.0.0' );
wp_enqueue_style( 'rx-theme-faq' );
wp_add_inline_style(
'rx-theme-faq',
'
.rx-faq-wrap {
margin: 32px 0;
--rx-faq-border: #e5e7eb;
--rx-faq-bg: #ffffff;
--rx-faq-soft: #f8fafc;
--rx-faq-text: #111827;
--rx-faq-muted: #6b7280;
--rx-faq-primary: #0f766e;
--rx-faq-radius: 16px;
--rx-faq-shadow: 0 10px 30px rgba(15, 23, 42, 0.08);
}
.rx-faq-header {
margin-bottom: 24px;
text-align: center;
}
.rx-faq-subtitle {
margin: 0 0 8px;
color: var(--rx-faq-primary);
font-weight: 700;
letter-spacing: .04em;
text-transform: uppercase;
font-size: 13px;
}
.rx-faq-title {
margin: 0;
color: var(--rx-faq-text);
font-size: clamp(26px, 4vw, 42px);
line-height: 1.15;
}
.rx-faq-description {
max-width: 760px;
margin: 14px auto 0;
color: var(--rx-faq-muted);
font-size: 16px;
line-height: 1.7;
}
.rx-faq-toolbar {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 12px;
margin-bottom: 18px;
padding: 14px;
background: var(--rx-faq-soft);
border: 1px solid var(--rx-faq-border);
border-radius: var(--rx-faq-radius);
}
.rx-faq-search-label {
flex: 1 1 260px;
margin: 0;
}
.rx-faq-search,
.rx-faq-filter {
width: 100%;
min-height: 44px;
border: 1px solid var(--rx-faq-border);
border-radius: 12px;
padding: 10px 14px;
background: #fff;
color: var(--rx-faq-text);
font-size: 15px;
outline: none;
}
.rx-faq-filter {
flex: 0 1 220px;
}
.rx-faq-search:focus,
.rx-faq-filter:focus {
border-color: var(--rx-faq-primary);
box-shadow: 0 0 0 3px rgba(15, 118, 110, .15);
}
.rx-faq-actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.rx-faq-expand-all,
.rx-faq-collapse-all,
.rx-faq-copy-link {
border: 0;
border-radius: 999px;
padding: 10px 14px;
background: var(--rx-faq-primary);
color: #fff;
font-size: 14px;
font-weight: 700;
cursor: pointer;
}
.rx-faq-collapse-all {
background: #334155;
}
.rx-faq-copy-link {
margin-top: 12px;
background: #0f172a;
font-size: 13px;
padding: 8px 12px;
}
.rx-faq-list {
display: grid;
gap: 14px;
}
.rx-faq-layout-grid .rx-faq-list {
grid-template-columns: repeat(var(--rx-faq-columns), minmax(0, 1fr));
}
.rx-faq-item {
background: var(--rx-faq-bg);
border: 1px solid var(--rx-faq-border);
border-radius: var(--rx-faq-radius);
box-shadow: var(--rx-faq-shadow);
overflow: hidden;
transition: border-color .2s ease, transform .2s ease;
}
.rx-faq-item:hover {
border-color: rgba(15, 118, 110, .45);
}
.rx-faq-question-wrap {
margin: 0;
font-size: 18px;
line-height: 1.4;
}
.rx-faq-question {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
border: 0;
background: transparent;
padding: 18px 20px;
color: var(--rx-faq-text);
text-align: left;
font: inherit;
font-weight: 800;
cursor: pointer;
}
.rx-faq-question:focus {
outline: 3px solid rgba(15, 118, 110, .25);
outline-offset: -3px;
}
.rx-faq-question-text {
display: flex;
align-items: center;
gap: 10px;
}
.rx-faq-number {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 34px;
height: 34px;
border-radius: 50%;
background: rgba(15, 118, 110, .12);
color: var(--rx-faq-primary);
font-size: 13px;
font-weight: 900;
}
.rx-faq-toggle-icon {
width: 22px;
height: 22px;
position: relative;
flex: 0 0 22px;
}
.rx-faq-toggle-icon::before,
.rx-faq-toggle-icon::after {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 16px;
height: 2px;
background: var(--rx-faq-primary);
transform: translate(-50%, -50%);
transition: transform .2s ease;
}
.rx-faq-toggle-icon::after {
transform: translate(-50%, -50%) rotate(90deg);
}
.rx-faq-item.is-open .rx-faq-toggle-icon::after {
transform: translate(-50%, -50%) rotate(0deg);
}
.rx-faq-answer {
border-top: 1px solid var(--rx-faq-border);
background: linear-gradient(180deg, #fff, var(--rx-faq-soft));
}
.rx-faq-answer-inner {
padding: 18px 20px 22px;
color: #374151;
font-size: 16px;
line-height: 1.8;
}
.rx-faq-answer-inner p:first-child {
margin-top: 0;
}
.rx-faq-answer-inner p:last-child {
margin-bottom: 0;
}
.rx-faq-category {
display: inline-flex;
margin-bottom: 10px;
padding: 4px 10px;
border-radius: 999px;
background: rgba(15, 118, 110, .1);
color: var(--rx-faq-primary);
font-size: 12px;
font-weight: 800;
}
.rx-faq-no-results,
.rx-faq-empty {
padding: 20px;
border: 1px dashed var(--rx-faq-border);
border-radius: var(--rx-faq-radius);
color: var(--rx-faq-muted);
text-align: center;
background: var(--rx-faq-soft);
}
@media (max-width: 768px) {
.rx-faq-layout-grid .rx-faq-list {
grid-template-columns: 1fr;
}
.rx-faq-toolbar {
align-items: stretch;
}
.rx-faq-actions,
.rx-faq-expand-all,
.rx-faq-collapse-all {
width: 100%;
}
.rx-faq-actions button {
flex: 1;
}
}
'
);
wp_register_script( 'rx-theme-faq', false, array(), '1.0.0', true );
wp_enqueue_script( 'rx-theme-faq' );
wp_add_inline_script(
'rx-theme-faq',
'
(function(){
"use strict";
function openItem(item) {
var btn = item.querySelector(".rx-faq-question");
var panel = item.querySelector(".rx-faq-answer");
if (!btn || !panel) return;
item.classList.add("is-open");
btn.setAttribute("aria-expanded", "true");
panel.hidden = false;
}
function closeItem(item) {
var btn = item.querySelector(".rx-faq-question");
var panel = item.querySelector(".rx-faq-answer");
if (!btn || !panel) return;
item.classList.remove("is-open");
btn.setAttribute("aria-expanded", "false");
panel.hidden = true;
}
function normalizeText(text) {
return String(text || "").toLowerCase().trim();
}
function filterFaq(wrap) {
var search = wrap.querySelector(".rx-faq-search");
var filter = wrap.querySelector(".rx-faq-filter");
var items = wrap.querySelectorAll(".rx-faq-item");
var noResults = wrap.querySelector(".rx-faq-no-results");
var searchValue = search ? normalizeText(search.value) : "";
var filterValue = filter ? filter.value : "";
var visible = 0;
items.forEach(function(item){
var haystack = normalizeText(item.getAttribute("data-search"));
var category = item.getAttribute("data-category") || "";
var matchSearch = !searchValue || haystack.indexOf(searchValue) !== -1;
var matchFilter = !filterValue || category === filterValue;
if (matchSearch && matchFilter) {
item.hidden = false;
visible++;
} else {
item.hidden = true;
}
});
if (noResults) {
noResults.hidden = visible !== 0;
}
}
function initFaq(wrap) {
var multipleOpen = wrap.getAttribute("data-multiple-open") === "yes";
var buttons = wrap.querySelectorAll(".rx-faq-question");
buttons.forEach(function(button){
button.addEventListener("click", function(){
var item = button.closest(".rx-faq-item");
if (!item) return;
var isOpen = item.classList.contains("is-open");
if (!multipleOpen) {
wrap.querySelectorAll(".rx-faq-item.is-open").forEach(closeItem);
}
if (isOpen) {
closeItem(item);
} else {
openItem(item);
}
});
});
var search = wrap.querySelector(".rx-faq-search");
var filter = wrap.querySelector(".rx-faq-filter");
if (search) {
search.addEventListener("input", function(){
filterFaq(wrap);
});
}
if (filter) {
filter.addEventListener("change", function(){
filterFaq(wrap);
});
}
var expandAll = wrap.querySelector(".rx-faq-expand-all");
var collapseAll = wrap.querySelector(".rx-faq-collapse-all");
if (expandAll) {
expandAll.addEventListener("click", function(){
wrap.querySelectorAll(".rx-faq-item:not([hidden])").forEach(openItem);
});
}
if (collapseAll) {
collapseAll.addEventListener("click", function(){
wrap.querySelectorAll(".rx-faq-item").forEach(closeItem);
});
}
wrap.querySelectorAll(".rx-faq-copy-link").forEach(function(btn){
btn.addEventListener("click", function(){
var anchor = btn.getAttribute("data-anchor");
var url = window.location.origin + window.location.pathname + "#" + anchor;
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(url).then(function(){
var oldText = btn.textContent;
btn.textContent = "Copied!";
setTimeout(function(){
btn.textContent = oldText;
}, 1300);
});
}
});
});
}
document.addEventListener("DOMContentLoaded", function(){
document.querySelectorAll(".rx-faq-wrap").forEach(initFaq);
if (window.location.hash) {
var target = document.querySelector(window.location.hash);
if (target && target.classList.contains("rx-faq-item")) {
openItem(target);
setTimeout(function(){
target.scrollIntoView({ behavior: "smooth", block: "start" });
}, 200);
}
}
});
})();
'
);
}
public function print_schema_json_ld() {
if ( empty( self::$schema_items ) ) {
return;
}
$main_entity = array();
foreach ( self::$schema_items as $item ) {
if ( empty( $item['question'] ) || empty( $item['answer'] ) ) {
continue;
}
$main_entity[] = array(
'@type' => 'Question',
'name' => wp_strip_all_tags( $item['question'] ),
'acceptedAnswer' => array(
'@type' => 'Answer',
'text' => wp_strip_all_tags( $item['answer'] ),
),
);
}
if ( empty( $main_entity ) ) {
return;
}
$schema = array(
'@context' => 'https://schema.org',
'@type' => 'FAQPage',
'mainEntity' => $main_entity,
);
echo "\n" . '<script type="application/ld+json" class="rx-faq-schema">' . wp_json_encode( $schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ) . '</script>' . "\n";
}
}
RX_Theme_FAQ_Shortcodes::instance();
endif;
Then include it in your theme functions.php:
require_once get_template_directory() . '/inc/shortcodes/faq.php';
Manual FAQ usage
[rx_faq title="Medical FAQ" subtitle="RX HARUN" search="yes" filter="yes" numbering="yes" schema="yes"]
[rx_faq_item question="What is RX Theme?" category="General"]
RX Theme is a custom WordPress theme for medical, health, blog, and professional websites.
[/rx_faq_item]
[rx_faq_item question="Does this FAQ support Google FAQ schema?" category="SEO"]
Yes. It automatically outputs FAQPage JSON-LD schema when schema="yes".
[/rx_faq_item]
[/rx_faq]
Custom post type FAQ usage
[rx_faq source="post_type" post_type="rx_faq" taxonomy="faq_category" limit="50" search="yes" filter="yes" layout="accordion"]
Grid layout usage
[rx_faq title="Website FAQ" layout="grid" columns="2" numbering="yes" search="yes"]
[rx_faq_item question="Is RX Theme responsive?" category="Design"]
Yes. It works on desktop, tablet, and mobile devices.
[/rx_faq_item]
[/rx_faq]
This file is safe for theme use and does not require a plugin.