I’ll build this as a reusable inc/shortcodes/medical-boxes.php file for your RX Theme: multiple medical content box shortcodes, safe escaping/sanitization, schema-friendly output, accessibility, responsive CSS, icon support, and optional CTA/list layouts.
Below is an advanced complete file for:
rx-theme/inc/shortcodes/medical-boxes.php
It adds many useful medical shortcodes for your RX Theme: medical boxes, alerts, symptoms, causes, treatments, diagnostic boxes, step boxes, FAQ schema, pros/cons, do/don’t boxes, stats, timeline, emergency warning, and reusable styling.
<?php
/**
* RX Theme - Medical Boxes Shortcodes
*
* File: inc/shortcodes/medical-boxes.php
*
* Features:
* - Medical content boxes
* - Alert / warning / emergency boxes
* - Symptom, cause, treatment, diagnosis, prevention boxes
* - FAQ accordion with optional schema
* - Pros and cons boxes
* - Do and don't boxes
* - Step/timeline boxes
* - Medical stat boxes
* - CTA medical box
* - Responsive inline CSS loaded only when shortcode is used
* - Accessibility-ready markup
* - Sanitized and escaped output
*
* @package RX_Theme
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! class_exists( 'RX_Medical_Boxes_Shortcodes' ) ) :
final class RX_Medical_Boxes_Shortcodes {
/**
* CSS printed once.
*
* @var bool
*/
private static $styles_printed = false;
/**
* Schema data queue.
*
* @var array
*/
private static $schema_queue = array();
/**
* Initialize shortcodes and hooks.
*/
public static function init() {
add_shortcode( 'rx_medical_box', array( __CLASS__, 'medical_box_shortcode' ) );
add_shortcode( 'rx_box', array( __CLASS__, 'medical_box_shortcode' ) );
add_shortcode( 'rx_alert', array( __CLASS__, 'alert_shortcode' ) );
add_shortcode( 'rx_emergency', array( __CLASS__, 'emergency_shortcode' ) );
add_shortcode( 'rx_medical_grid', array( __CLASS__, 'medical_grid_shortcode' ) );
add_shortcode( 'rx_medical_card', array( __CLASS__, 'medical_card_shortcode' ) );
add_shortcode( 'rx_symptoms', array( __CLASS__, 'list_box_shortcode' ) );
add_shortcode( 'rx_causes', array( __CLASS__, 'list_box_shortcode' ) );
add_shortcode( 'rx_treatments', array( __CLASS__, 'list_box_shortcode' ) );
add_shortcode( 'rx_diagnosis', array( __CLASS__, 'list_box_shortcode' ) );
add_shortcode( 'rx_prevention', array( __CLASS__, 'list_box_shortcode' ) );
add_shortcode( 'rx_steps', array( __CLASS__, 'steps_shortcode' ) );
add_shortcode( 'rx_timeline', array( __CLASS__, 'timeline_shortcode' ) );
add_shortcode( 'rx_faq', array( __CLASS__, 'faq_shortcode' ) );
add_shortcode( 'rx_faq_item', array( __CLASS__, 'faq_item_shortcode' ) );
add_shortcode( 'rx_pros_cons', array( __CLASS__, 'pros_cons_shortcode' ) );
add_shortcode( 'rx_do_dont', array( __CLASS__, 'do_dont_shortcode' ) );
add_shortcode( 'rx_stat', array( __CLASS__, 'stat_shortcode' ) );
add_shortcode( 'rx_cta_box', array( __CLASS__, 'cta_box_shortcode' ) );
add_shortcode( 'rx_disclaimer', array( __CLASS__, 'disclaimer_shortcode' ) );
add_action( 'wp_footer', array( __CLASS__, 'print_schema' ), 30 );
}
/**
* Print inline CSS only once.
*/
private static function maybe_print_styles() {
if ( self::$styles_printed ) {
return;
}
self::$styles_printed = true;
add_action(
'wp_footer',
function() {
?>
<style id="rx-medical-boxes-css">
:root {
--rx-box-bg: #ffffff;
--rx-box-text: #1f2937;
--rx-box-muted: #6b7280;
--rx-box-border: #e5e7eb;
--rx-box-primary: #2563eb;
--rx-box-success: #16a34a;
--rx-box-warning: #d97706;
--rx-box-danger: #dc2626;
--rx-box-info: #0891b2;
--rx-box-purple: #7c3aed;
--rx-box-radius: 16px;
--rx-box-shadow: 0 10px 25px rgba(15, 23, 42, 0.08);
}
.rx-medical-box,
.rx-medical-card,
.rx-alert-box,
.rx-stat-box,
.rx-cta-box,
.rx-disclaimer-box {
position: relative;
box-sizing: border-box;
margin: 1.4rem 0;
padding: 1.25rem 1.35rem;
border: 1px solid var(--rx-box-border);
border-radius: var(--rx-box-radius);
background: var(--rx-box-bg);
color: var(--rx-box-text);
box-shadow: var(--rx-box-shadow);
overflow: hidden;
}
.rx-medical-box *,
.rx-medical-card *,
.rx-alert-box *,
.rx-stat-box *,
.rx-cta-box *,
.rx-disclaimer-box * {
box-sizing: border-box;
}
.rx-medical-box::before,
.rx-alert-box::before,
.rx-cta-box::before,
.rx-disclaimer-box::before {
content: "";
position: absolute;
inset: 0 auto 0 0;
width: 6px;
background: var(--rx-box-primary);
}
.rx-type-info::before { background: var(--rx-box-info); }
.rx-type-success::before { background: var(--rx-box-success); }
.rx-type-warning::before { background: var(--rx-box-warning); }
.rx-type-danger::before,
.rx-type-emergency::before { background: var(--rx-box-danger); }
.rx-type-purple::before { background: var(--rx-box-purple); }
.rx-box-header {
display: flex;
align-items: flex-start;
gap: 0.85rem;
margin-bottom: 0.75rem;
}
.rx-box-icon {
display: inline-flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
width: 2.45rem;
height: 2.45rem;
border-radius: 999px;
background: rgba(37, 99, 235, 0.10);
color: var(--rx-box-primary);
font-size: 1.25rem;
line-height: 1;
}
.rx-type-info .rx-box-icon {
background: rgba(8, 145, 178, 0.10);
color: var(--rx-box-info);
}
.rx-type-success .rx-box-icon {
background: rgba(22, 163, 74, 0.10);
color: var(--rx-box-success);
}
.rx-type-warning .rx-box-icon {
background: rgba(217, 119, 6, 0.12);
color: var(--rx-box-warning);
}
.rx-type-danger .rx-box-icon,
.rx-type-emergency .rx-box-icon {
background: rgba(220, 38, 38, 0.10);
color: var(--rx-box-danger);
}
.rx-type-purple .rx-box-icon {
background: rgba(124, 58, 237, 0.10);
color: var(--rx-box-purple);
}
.rx-box-title {
margin: 0;
font-size: clamp(1.05rem, 2vw, 1.35rem);
line-height: 1.35;
font-weight: 800;
color: var(--rx-box-text);
}
.rx-box-subtitle {
margin: 0.2rem 0 0;
color: var(--rx-box-muted);
font-size: 0.95rem;
line-height: 1.55;
}
.rx-box-content {
color: var(--rx-box-text);
line-height: 1.75;
}
.rx-box-content p:last-child {
margin-bottom: 0;
}
.rx-box-list {
margin: 0.75rem 0 0;
padding-left: 1.25rem;
}
.rx-box-list li {
margin: 0.4rem 0;
line-height: 1.65;
}
.rx-medical-grid {
display: grid;
grid-template-columns: repeat(var(--rx-grid-columns, 3), minmax(0, 1fr));
gap: var(--rx-grid-gap, 1rem);
margin: 1.5rem 0;
}
.rx-medical-card {
height: 100%;
margin: 0;
box-shadow: 0 6px 18px rgba(15, 23, 42, 0.07);
}
.rx-card-link {
display: inline-flex;
align-items: center;
gap: 0.35rem;
margin-top: 0.85rem;
font-weight: 700;
text-decoration: none;
}
.rx-card-link:hover {
text-decoration: underline;
}
.rx-alert-box {
background: #fffdf7;
}
.rx-alert-box.rx-type-danger,
.rx-alert-box.rx-type-emergency {
background: #fff7f7;
}
.rx-alert-label {
display: inline-block;
margin-bottom: 0.4rem;
padding: 0.2rem 0.55rem;
border-radius: 999px;
background: rgba(15, 23, 42, 0.08);
font-size: 0.75rem;
font-weight: 800;
text-transform: uppercase;
letter-spacing: 0.04em;
}
.rx-steps {
counter-reset: rx-step;
margin: 1.5rem 0;
padding: 0;
list-style: none;
}
.rx-step-item {
counter-increment: rx-step;
position: relative;
margin: 0 0 1rem;
padding: 1rem 1rem 1rem 4rem;
border: 1px solid var(--rx-box-border);
border-radius: var(--rx-box-radius);
background: #fff;
box-shadow: 0 6px 18px rgba(15, 23, 42, 0.06);
}
.rx-step-item::before {
content: counter(rx-step);
position: absolute;
left: 1rem;
top: 1rem;
display: inline-flex;
align-items: center;
justify-content: center;
width: 2rem;
height: 2rem;
border-radius: 999px;
background: var(--rx-box-primary);
color: #fff;
font-weight: 800;
}
.rx-step-title {
margin: 0 0 0.35rem;
font-weight: 800;
font-size: 1.05rem;
}
.rx-faq-wrap {
margin: 1.5rem 0;
}
.rx-faq-item {
margin-bottom: 0.85rem;
border: 1px solid var(--rx-box-border);
border-radius: var(--rx-box-radius);
background: #fff;
box-shadow: 0 4px 16px rgba(15, 23, 42, 0.05);
overflow: hidden;
}
.rx-faq-question {
display: block;
width: 100%;
padding: 1rem 1.15rem;
border: 0;
background: #fff;
color: var(--rx-box-text);
font-weight: 800;
text-align: left;
cursor: pointer;
}
.rx-faq-answer {
padding: 0 1.15rem 1.15rem;
color: var(--rx-box-text);
line-height: 1.75;
}
.rx-pros-cons,
.rx-do-dont {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 1rem;
margin: 1.5rem 0;
}
.rx-pros-box,
.rx-cons-box,
.rx-do-box,
.rx-dont-box {
padding: 1.15rem;
border: 1px solid var(--rx-box-border);
border-radius: var(--rx-box-radius);
background: #fff;
box-shadow: 0 6px 18px rgba(15, 23, 42, 0.06);
}
.rx-pros-box,
.rx-do-box {
border-left: 6px solid var(--rx-box-success);
}
.rx-cons-box,
.rx-dont-box {
border-left: 6px solid var(--rx-box-danger);
}
.rx-small-heading {
margin: 0 0 0.75rem;
font-size: 1.05rem;
font-weight: 900;
}
.rx-stat-box {
display: flex;
align-items: center;
gap: 1rem;
}
.rx-stat-number {
font-size: clamp(2rem, 5vw, 3.5rem);
font-weight: 900;
line-height: 1;
color: var(--rx-box-primary);
}
.rx-stat-label {
margin: 0;
font-weight: 800;
}
.rx-stat-desc {
margin: 0.25rem 0 0;
color: var(--rx-box-muted);
line-height: 1.55;
}
.rx-cta-box {
text-align: center;
padding: 1.6rem;
background: linear-gradient(135deg, rgba(37,99,235,.08), rgba(8,145,178,.08));
}
.rx-cta-button {
display: inline-flex;
align-items: center;
justify-content: center;
margin-top: 1rem;
padding: 0.75rem 1.15rem;
border-radius: 999px;
background: var(--rx-box-primary);
color: #fff !important;
font-weight: 800;
text-decoration: none;
}
.rx-cta-button:hover {
filter: brightness(0.95);
text-decoration: none;
}
.rx-disclaimer-box {
font-size: 0.94rem;
background: #f9fafb;
color: #374151;
}
.rx-hidden {
display: none !important;
}
@media (max-width: 900px) {
.rx-medical-grid {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
@media (max-width: 640px) {
.rx-medical-grid,
.rx-pros-cons,
.rx-do-dont {
grid-template-columns: 1fr;
}
.rx-medical-box,
.rx-alert-box,
.rx-stat-box,
.rx-cta-box,
.rx-disclaimer-box {
padding: 1rem;
}
.rx-box-header {
gap: 0.65rem;
}
.rx-box-icon {
width: 2.15rem;
height: 2.15rem;
}
.rx-step-item {
padding-left: 3.5rem;
}
}
</style>
<script id="rx-medical-boxes-js">
document.addEventListener('click', function(event) {
var button = event.target.closest('.rx-faq-question');
if (!button) return;
var answerId = button.getAttribute('aria-controls');
var answer = document.getElementById(answerId);
if (!answer) return;
var expanded = button.getAttribute('aria-expanded') === 'true';
button.setAttribute('aria-expanded', expanded ? 'false' : 'true');
answer.hidden = expanded;
});
</script>
<?php
},
20
);
}
/**
* Normalize type.
*/
private static function normalize_type( $type ) {
$type = sanitize_key( $type );
$allowed = array(
'default',
'info',
'success',
'warning',
'danger',
'emergency',
'purple',
);
return in_array( $type, $allowed, true ) ? $type : 'default';
}
/**
* Icon map.
*/
private static function get_icon( $icon, $type = 'default' ) {
$icon = sanitize_text_field( $icon );
if ( ! empty( $icon ) ) {
return $icon;
}
$map = array(
'default' => '⚕️',
'info' => 'ℹ️',
'success' => '✅',
'warning' => '⚠️',
'danger' => '⛔',
'emergency' => '🚨',
'purple' => '🧬',
'symptoms' => '🩺',
'causes' => '🔎',
'treatments'=> '💊',
'diagnosis' => '🧪',
'prevention'=> '🛡️',
);
return isset( $map[ $type ] ) ? $map[ $type ] : $map['default'];
}
/**
* Safe content processor.
*/
private static function safe_content( $content ) {
$content = do_shortcode( $content );
$content = wpautop( $content );
return wp_kses_post( $content );
}
/**
* Convert pipe-separated or line-separated content to list.
*/
private static function parse_items( $items ) {
$items = trim( wp_strip_all_tags( $items ) );
if ( empty( $items ) ) {
return array();
}
$split = preg_split( '/\r\n|\r|\n|\|/', $items );
$clean = array();
foreach ( $split as $item ) {
$item = trim( $item );
if ( ! empty( $item ) ) {
$clean[] = sanitize_text_field( $item );
}
}
return $clean;
}
/**
* Render common box.
*/
private static function render_box( $args = array(), $content = '' ) {
self::maybe_print_styles();
$defaults = array(
'title' => '',
'subtitle' => '',
'type' => 'default',
'icon' => '',
'class' => '',
'id' => '',
'role' => '',
'items' => '',
'list' => 'ul',
'show_icon' => 'yes',
'aria_label' => '',
);
$args = wp_parse_args( $args, $defaults );
$type = self::normalize_type( $args['type'] );
$title = sanitize_text_field( $args['title'] );
$subtitle = sanitize_text_field( $args['subtitle'] );
$icon = self::get_icon( $args['icon'], $type );
$class = sanitize_html_class( $args['class'] );
$id = sanitize_html_class( $args['id'] );
$role = sanitize_key( $args['role'] );
$list_type = 'ol' === strtolower( $args['list'] ) ? 'ol' : 'ul';
$show_icon = 'no' !== strtolower( $args['show_icon'] );
$aria_label = sanitize_text_field( $args['aria_label'] );
$items = self::parse_items( $args['items'] );
$attrs = array();
if ( ! empty( $id ) ) {
$attrs[] = 'id="' . esc_attr( $id ) . '"';
}
if ( ! empty( $role ) ) {
$attrs[] = 'role="' . esc_attr( $role ) . '"';
}
if ( ! empty( $aria_label ) ) {
$attrs[] = 'aria-label="' . esc_attr( $aria_label ) . '"';
}
ob_start();
?>
<div class="rx-medical-box rx-type-<?php echo esc_attr( $type ); ?> <?php echo esc_attr( $class ); ?>" <?php echo implode( ' ', $attrs ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
<?php if ( $title || $subtitle || $show_icon ) : ?>
<div class="rx-box-header">
<?php if ( $show_icon ) : ?>
<span class="rx-box-icon" aria-hidden="true"><?php echo esc_html( $icon ); ?></span>
<?php endif; ?>
<div>
<?php if ( $title ) : ?>
<h3 class="rx-box-title"><?php echo esc_html( $title ); ?></h3>
<?php endif; ?>
<?php if ( $subtitle ) : ?>
<p class="rx-box-subtitle"><?php echo esc_html( $subtitle ); ?></p>
<?php endif; ?>
</div>
</div>
<?php endif; ?>
<div class="rx-box-content">
<?php echo self::safe_content( $content ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<?php if ( ! empty( $items ) ) : ?>
<<?php echo esc_attr( $list_type ); ?> class="rx-box-list">
<?php foreach ( $items as $item ) : ?>
<li><?php echo esc_html( $item ); ?></li>
<?php endforeach; ?>
</<?php echo esc_attr( $list_type ); ?>>
<?php endif; ?>
</div>
</div>
<?php
return ob_get_clean();
}
/**
* [rx_medical_box]
*/
public static function medical_box_shortcode( $atts, $content = '' ) {
$atts = shortcode_atts(
array(
'title' => '',
'subtitle' => '',
'type' => 'default',
'icon' => '',
'class' => '',
'id' => '',
'role' => '',
'items' => '',
'list' => 'ul',
'show_icon' => 'yes',
'aria_label' => '',
),
$atts,
'rx_medical_box'
);
return self::render_box( $atts, $content );
}
/**
* [rx_alert]
*/
public static function alert_shortcode( $atts, $content = '' ) {
self::maybe_print_styles();
$atts = shortcode_atts(
array(
'title' => 'Medical Alert',
'label' => 'Important',
'type' => 'warning',
'icon' => '',
'role' => 'note',
'class' => '',
),
$atts,
'rx_alert'
);
$type = self::normalize_type( $atts['type'] );
$icon = self::get_icon( $atts['icon'], $type );
$title = sanitize_text_field( $atts['title'] );
$label = sanitize_text_field( $atts['label'] );
$role = sanitize_key( $atts['role'] );
$class = sanitize_html_class( $atts['class'] );
ob_start();
?>
<div class="rx-alert-box rx-type-<?php echo esc_attr( $type ); ?> <?php echo esc_attr( $class ); ?>" role="<?php echo esc_attr( $role ); ?>">
<span class="rx-alert-label"><?php echo esc_html( $label ); ?></span>
<div class="rx-box-header">
<span class="rx-box-icon" aria-hidden="true"><?php echo esc_html( $icon ); ?></span>
<h3 class="rx-box-title"><?php echo esc_html( $title ); ?></h3>
</div>
<div class="rx-box-content">
<?php echo self::safe_content( $content ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
</div>
<?php
return ob_get_clean();
}
/**
* [rx_emergency]
*/
public static function emergency_shortcode( $atts, $content = '' ) {
$atts = shortcode_atts(
array(
'title' => 'Seek emergency medical help',
'label' => 'Emergency warning',
'icon' => '🚨',
),
$atts,
'rx_emergency'
);
return self::alert_shortcode(
array(
'title' => $atts['title'],
'label' => $atts['label'],
'icon' => $atts['icon'],
'type' => 'emergency',
'role' => 'alert',
'class' => 'rx-emergency-box',
),
$content
);
}
/**
* [rx_medical_grid]
*/
public static function medical_grid_shortcode( $atts, $content = '' ) {
self::maybe_print_styles();
$atts = shortcode_atts(
array(
'columns' => 3,
'gap' => '1rem',
'class' => '',
),
$atts,
'rx_medical_grid'
);
$columns = absint( $atts['columns'] );
if ( $columns < 1 ) {
$columns = 1;
}
if ( $columns > 4 ) {
$columns = 4;
}
$gap = sanitize_text_field( $atts['gap'] );
$class = sanitize_html_class( $atts['class'] );
ob_start();
?>
<div class="rx-medical-grid <?php echo esc_attr( $class ); ?>" style="--rx-grid-columns: <?php echo esc_attr( $columns ); ?>; --rx-grid-gap: <?php echo esc_attr( $gap ); ?>;">
<?php echo do_shortcode( $content ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<?php
return ob_get_clean();
}
/**
* [rx_medical_card]
*/
public static function medical_card_shortcode( $atts, $content = '' ) {
self::maybe_print_styles();
$atts = shortcode_atts(
array(
'title' => '',
'subtitle' => '',
'icon' => '⚕️',
'url' => '',
'linktext' => 'Read more',
'type' => 'default',
'class' => '',
),
$atts,
'rx_medical_card'
);
$type = self::normalize_type( $atts['type'] );
$title = sanitize_text_field( $atts['title'] );
$subtitle = sanitize_text_field( $atts['subtitle'] );
$icon = self::get_icon( $atts['icon'], $type );
$url = esc_url( $atts['url'] );
$linktext = sanitize_text_field( $atts['linktext'] );
$class = sanitize_html_class( $atts['class'] );
ob_start();
?>
<article class="rx-medical-card rx-type-<?php echo esc_attr( $type ); ?> <?php echo esc_attr( $class ); ?>">
<div class="rx-box-header">
<span class="rx-box-icon" aria-hidden="true"><?php echo esc_html( $icon ); ?></span>
<div>
<?php if ( $title ) : ?>
<h3 class="rx-box-title"><?php echo esc_html( $title ); ?></h3>
<?php endif; ?>
<?php if ( $subtitle ) : ?>
<p class="rx-box-subtitle"><?php echo esc_html( $subtitle ); ?></p>
<?php endif; ?>
</div>
</div>
<div class="rx-box-content">
<?php echo self::safe_content( $content ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<?php if ( $url ) : ?>
<a class="rx-card-link" href="<?php echo esc_url( $url ); ?>">
<?php echo esc_html( $linktext ); ?> <span aria-hidden="true">→</span>
</a>
<?php endif; ?>
</article>
<?php
return ob_get_clean();
}
/**
* List box shortcodes.
*
* Used by:
* [rx_symptoms]
* [rx_causes]
* [rx_treatments]
* [rx_diagnosis]
* [rx_prevention]
*/
public static function list_box_shortcode( $atts, $content = '', $tag = '' ) {
$map = array(
'rx_symptoms' => array( 'title' => 'Common Symptoms', 'icon' => '🩺', 'type' => 'info' ),
'rx_causes' => array( 'title' => 'Possible Causes', 'icon' => '🔎', 'type' => 'purple' ),
'rx_treatments' => array( 'title' => 'Treatment Options', 'icon' => '💊', 'type' => 'success' ),
'rx_diagnosis' => array( 'title' => 'Diagnostic Tests', 'icon' => '🧪', 'type' => 'info' ),
'rx_prevention' => array( 'title' => 'Prevention Tips', 'icon' => '🛡️', 'type' => 'success' ),
);
$defaults = isset( $map[ $tag ] ) ? $map[ $tag ] : $map['rx_symptoms'];
$atts = shortcode_atts(
array(
'title' => $defaults['title'],
'subtitle' => '',
'items' => '',
'list' => 'ul',
'type' => $defaults['type'],
'icon' => $defaults['icon'],
'class' => '',
),
$atts,
$tag
);
if ( empty( $atts['items'] ) && ! empty( $content ) ) {
$atts['items'] = $content;
$content = '';
}
return self::render_box( $atts, $content );
}
/**
* [rx_steps]
*/
public static function steps_shortcode( $atts, $content = '' ) {
self::maybe_print_styles();
$atts = shortcode_atts(
array(
'items' => '',
'class' => '',
),
$atts,
'rx_steps'
);
$items = self::parse_items( ! empty( $atts['items'] ) ? $atts['items'] : $content );
$class = sanitize_html_class( $atts['class'] );
if ( empty( $items ) ) {
return '';
}
ob_start();
?>
<ol class="rx-steps <?php echo esc_attr( $class ); ?>">
<?php foreach ( $items as $item ) : ?>
<li class="rx-step-item">
<div class="rx-step-title"><?php echo esc_html( $item ); ?></div>
</li>
<?php endforeach; ?>
</ol>
<?php
return ob_get_clean();
}
/**
* [rx_timeline]
*/
public static function timeline_shortcode( $atts, $content = '' ) {
$atts = shortcode_atts(
array(
'items' => '',
'class' => '',
),
$atts,
'rx_timeline'
);
return self::steps_shortcode( $atts, $content );
}
/**
* [rx_faq]
*
* Usage:
* [rx_faq schema="yes"]
* [rx_faq_item question="What is fever?"]Fever is high body temperature.[/rx_faq_item]
* [/rx_faq]
*/
public static function faq_shortcode( $atts, $content = '' ) {
self::maybe_print_styles();
$atts = shortcode_atts(
array(
'title' => '',
'schema' => 'yes',
'class' => '',
),
$atts,
'rx_faq'
);
$class = sanitize_html_class( $atts['class'] );
$title = sanitize_text_field( $atts['title'] );
ob_start();
?>
<section class="rx-faq-wrap <?php echo esc_attr( $class ); ?>">
<?php if ( $title ) : ?>
<h2><?php echo esc_html( $title ); ?></h2>
<?php endif; ?>
<?php echo do_shortcode( $content ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</section>
<?php
return ob_get_clean();
}
/**
* [rx_faq_item]
*/
public static function faq_item_shortcode( $atts, $content = '' ) {
self::maybe_print_styles();
$atts = shortcode_atts(
array(
'question' => '',
'open' => 'no',
'schema' => 'yes',
),
$atts,
'rx_faq_item'
);
$question = sanitize_text_field( $atts['question'] );
$answer = wp_strip_all_tags( do_shortcode( $content ) );
$is_open = 'yes' === strtolower( $atts['open'] );
$id = 'rx-faq-' . wp_generate_uuid4();
if ( empty( $question ) || empty( $answer ) ) {
return '';
}
if ( 'yes' === strtolower( $atts['schema'] ) ) {
self::$schema_queue[] = array(
'@type' => 'Question',
'name' => $question,
'acceptedAnswer' => array(
'@type' => 'Answer',
'text' => $answer,
),
);
}
ob_start();
?>
<div class="rx-faq-item">
<button
type="button"
class="rx-faq-question"
aria-expanded="<?php echo $is_open ? 'true' : 'false'; ?>"
aria-controls="<?php echo esc_attr( $id ); ?>"
>
<?php echo esc_html( $question ); ?>
</button>
<div
id="<?php echo esc_attr( $id ); ?>"
class="rx-faq-answer"
<?php echo $is_open ? '' : 'hidden'; ?>
>
<?php echo self::safe_content( $content ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
</div>
<?php
return ob_get_clean();
}
/**
* [rx_pros_cons]
*/
public static function pros_cons_shortcode( $atts, $content = '' ) {
self::maybe_print_styles();
$atts = shortcode_atts(
array(
'pros_title' => 'Benefits',
'cons_title' => 'Limitations',
'pros' => '',
'cons' => '',
),
$atts,
'rx_pros_cons'
);
$pros = self::parse_items( $atts['pros'] );
$cons = self::parse_items( $atts['cons'] );
ob_start();
?>
<div class="rx-pros-cons">
<div class="rx-pros-box">
<h3 class="rx-small-heading">✅ <?php echo esc_html( $atts['pros_title'] ); ?></h3>
<ul class="rx-box-list">
<?php foreach ( $pros as $item ) : ?>
<li><?php echo esc_html( $item ); ?></li>
<?php endforeach; ?>
</ul>
</div>
<div class="rx-cons-box">
<h3 class="rx-small-heading">⚠️ <?php echo esc_html( $atts['cons_title'] ); ?></h3>
<ul class="rx-box-list">
<?php foreach ( $cons as $item ) : ?>
<li><?php echo esc_html( $item ); ?></li>
<?php endforeach; ?>
</ul>
</div>
</div>
<?php
return ob_get_clean();
}
/**
* [rx_do_dont]
*/
public static function do_dont_shortcode( $atts, $content = '' ) {
self::maybe_print_styles();
$atts = shortcode_atts(
array(
'do_title' => 'What to Do',
'dont_title' => 'What to Avoid',
'do' => '',
'dont' => '',
),
$atts,
'rx_do_dont'
);
$do_items = self::parse_items( $atts['do'] );
$dont_items = self::parse_items( $atts['dont'] );
ob_start();
?>
<div class="rx-do-dont">
<div class="rx-do-box">
<h3 class="rx-small-heading">✅ <?php echo esc_html( $atts['do_title'] ); ?></h3>
<ul class="rx-box-list">
<?php foreach ( $do_items as $item ) : ?>
<li><?php echo esc_html( $item ); ?></li>
<?php endforeach; ?>
</ul>
</div>
<div class="rx-dont-box">
<h3 class="rx-small-heading">❌ <?php echo esc_html( $atts['dont_title'] ); ?></h3>
<ul class="rx-box-list">
<?php foreach ( $dont_items as $item ) : ?>
<li><?php echo esc_html( $item ); ?></li>
<?php endforeach; ?>
</ul>
</div>
</div>
<?php
return ob_get_clean();
}
/**
* [rx_stat]
*/
public static function stat_shortcode( $atts, $content = '' ) {
self::maybe_print_styles();
$atts = shortcode_atts(
array(
'number' => '',
'label' => '',
'desc' => '',
'type' => 'default',
'class' => '',
),
$atts,
'rx_stat'
);
$type = self::normalize_type( $atts['type'] );
$class = sanitize_html_class( $atts['class'] );
$number = sanitize_text_field( $atts['number'] );
$label = sanitize_text_field( $atts['label'] );
$desc = sanitize_text_field( $atts['desc'] );
if ( empty( $desc ) && ! empty( $content ) ) {
$desc = wp_strip_all_tags( $content );
}
ob_start();
?>
<div class="rx-stat-box rx-type-<?php echo esc_attr( $type ); ?> <?php echo esc_attr( $class ); ?>">
<div class="rx-stat-number"><?php echo esc_html( $number ); ?></div>
<div>
<?php if ( $label ) : ?>
<p class="rx-stat-label"><?php echo esc_html( $label ); ?></p>
<?php endif; ?>
<?php if ( $desc ) : ?>
<p class="rx-stat-desc"><?php echo esc_html( $desc ); ?></p>
<?php endif; ?>
</div>
</div>
<?php
return ob_get_clean();
}
/**
* [rx_cta_box]
*/
public static function cta_box_shortcode( $atts, $content = '' ) {
self::maybe_print_styles();
$atts = shortcode_atts(
array(
'title' => '',
'button_text' => '',
'button_url' => '',
'type' => 'info',
),
$atts,
'rx_cta_box'
);
$title = sanitize_text_field( $atts['title'] );
$button = sanitize_text_field( $atts['button_text'] );
$url = esc_url( $atts['button_url'] );
$type = self::normalize_type( $atts['type'] );
ob_start();
?>
<div class="rx-cta-box rx-type-<?php echo esc_attr( $type ); ?>">
<?php if ( $title ) : ?>
<h3 class="rx-box-title"><?php echo esc_html( $title ); ?></h3>
<?php endif; ?>
<div class="rx-box-content">
<?php echo self::safe_content( $content ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<?php if ( $button && $url ) : ?>
<a class="rx-cta-button" href="<?php echo esc_url( $url ); ?>">
<?php echo esc_html( $button ); ?>
</a>
<?php endif; ?>
</div>
<?php
return ob_get_clean();
}
/**
* [rx_disclaimer]
*/
public static function disclaimer_shortcode( $atts, $content = '' ) {
self::maybe_print_styles();
$atts = shortcode_atts(
array(
'title' => 'Medical Disclaimer',
'icon' => '⚕️',
),
$atts,
'rx_disclaimer'
);
if ( empty( trim( $content ) ) ) {
$content = 'This information is for educational purposes only and should not replace professional medical advice, diagnosis, or treatment. Always consult a qualified healthcare provider for personal medical concerns.';
}
return self::render_box(
array(
'title' => $atts['title'],
'icon' => $atts['icon'],
'type' => 'info',
'role' => 'note',
'class' => 'rx-disclaimer-box',
),
$content
);
}
/**
* Print FAQ schema.
*/
public static function print_schema() {
if ( empty( self::$schema_queue ) || ! is_singular() ) {
return;
}
$schema = array(
'@context' => 'https://schema.org',
'@type' => 'FAQPage',
'mainEntity' => self::$schema_queue,
);
echo '<script type="application/ld+json" class="rx-faq-schema">';
echo wp_json_encode( $schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
echo '</script>';
}
}
endif;
RX_Medical_Boxes_Shortcodes::init();
Now add this file in your theme loading system.
In functions.php, add:
/**
* RX Theme shortcodes.
*/
$rx_medical_boxes_file = get_template_directory() . '/inc/shortcodes/medical-boxes.php';
if ( file_exists( $rx_medical_boxes_file ) ) {
require_once $rx_medical_boxes_file;
}
Example use in WordPress post
[rx_medical_box title="What is Hypertension?" subtitle="Simple medical explanation" type="info"]
Hypertension means blood pressure stays higher than normal for a long time.
[/rx_medical_box]
[rx_symptoms items="Headache|Dizziness|Chest discomfort|Shortness of breath"]
[rx_alert title="Important Warning" type="warning"]
Do not stop prescribed medicine without talking to your doctor.
[/rx_alert]
[rx_emergency]
Call emergency help if there is severe chest pain, fainting, severe breathing difficulty, or stroke-like symptoms.
[/rx_emergency]
[rx_do_dont
do="Drink enough water|Take medicine as prescribed|Follow up with your doctor"
dont="Do not ignore severe symptoms|Do not self-medicate|Do not delay emergency care"]
[rx_faq title="Frequently Asked Questions"]
[rx_faq_item question="Is this article medical advice?"]
No. It is educational information only.
[/rx_faq_item]
[rx_faq_item question="When should I see a doctor?"]
See a doctor if symptoms are severe, persistent, worsening, or unusual.
[/rx_faq_item]
[/rx_faq]
This file is safe, reusable, SEO-friendly, and very useful for your medical article website.