buttons.php

I’ll build this as a reusable inc/shortcodes/buttons.php file for your RX theme, with secure shortcode attributes, multiple button styles, icons, tracking attributes, accessibility labels, grouped buttons, and helper rendering.

Yes, my friend. Create this file:

rx-theme/inc/shortcodes/buttons.php

Then paste the full code below. This file uses WordPress Shortcode API with add_shortcode(). WordPress officially says shortcode callbacks receive attributes, content, and shortcode tag name, and only one callback can exist for each shortcode name, so I used unique rx_ shortcode names to avoid conflict.

<?php
/**
 * RX Theme Button Shortcodes
 *
 * File: inc/shortcodes/buttons.php
 *
 * Shortcodes:
 * [rx_button]
 * [rx_buttons]
 * [rx_button_group]
 * [rx_cta_button]
 * [rx_download_button]
 * [rx_whatsapp_button]
 * [rx_phone_button]
 * [rx_email_button]
 * [rx_copy_button]
 *
 * @package RX_Theme
 */

defined( 'ABSPATH' ) || exit;

if ( ! class_exists( 'RX_Theme_Button_Shortcodes' ) ) :

final class RX_Theme_Button_Shortcodes {

	/**
	 * CSS printed once.
	 *
	 * @var bool
	 */
	private static $css_printed = false;

	/**
	 * JS printed once.
	 *
	 * @var bool
	 */
	private static $js_printed = false;

	/**
	 * Bootstrap.
	 */
	public static function init() {
		add_action( 'init', array( __CLASS__, 'register_shortcodes' ) );
		add_action( 'wp_footer', array( __CLASS__, 'print_inline_assets' ), 30 );
	}

	/**
	 * Register shortcodes.
	 */
	public static function register_shortcodes() {
		add_shortcode( 'rx_button', array( __CLASS__, 'button_shortcode' ) );
		add_shortcode( 'rx_btn', array( __CLASS__, 'button_shortcode' ) );

		add_shortcode( 'rx_buttons', array( __CLASS__, 'buttons_shortcode' ) );
		add_shortcode( 'rx_button_group', array( __CLASS__, 'buttons_shortcode' ) );

		add_shortcode( 'rx_cta_button', array( __CLASS__, 'cta_button_shortcode' ) );
		add_shortcode( 'rx_download_button', array( __CLASS__, 'download_button_shortcode' ) );
		add_shortcode( 'rx_whatsapp_button', array( __CLASS__, 'whatsapp_button_shortcode' ) );
		add_shortcode( 'rx_phone_button', array( __CLASS__, 'phone_button_shortcode' ) );
		add_shortcode( 'rx_email_button', array( __CLASS__, 'email_button_shortcode' ) );
		add_shortcode( 'rx_copy_button', array( __CLASS__, 'copy_button_shortcode' ) );
	}

	/**
	 * Main button shortcode.
	 *
	 * Example:
	 * [rx_button text="Read More" url="/about" style="primary" size="lg" icon="→"]
	 */
	public static function button_shortcode( $atts, $content = null, $tag = '' ) {
		$atts = shortcode_atts(
			array(
				'text'             => '',
				'url'              => '#',
				'type'             => 'link', // link, button, submit.
				'style'            => 'primary', // primary, secondary, success, danger, warning, info, light, dark, outline, ghost, gradient.
				'size'             => 'md', // xs, sm, md, lg, xl.
				'shape'            => 'rounded', // square, rounded, pill, circle.
				'align'            => '', // left, center, right, full.
				'width'            => '', // auto, full, custom.
				'custom_width'     => '',
				'icon'             => '',
				'icon_position'    => 'left', // left, right.
				'target'           => '',
				'rel'              => '',
				'title'            => '',
				'aria_label'       => '',
				'id'               => '',
				'class'            => '',
				'download'         => '',
				'disabled'         => 'false',
				'nofollow'         => 'false',
				'sponsored'        => 'false',
				'ugc'              => 'false',
				'noopener'         => 'true',
				'noreferrer'       => 'false',
				'data_event'       => '',
				'data_category'    => '',
				'data_label'       => '',
				'data_value'       => '',
				'data_location'    => '',
				'data_rx'          => '',
				'bg'               => '',
				'color'            => '',
				'border'           => '',
				'hover_bg'         => '',
				'hover_color'      => '',
				'tooltip'          => '',
				'loading'          => 'false',
				'loading_text'     => 'Loading...',
				'confirm'          => '',
				'extra_attrs'      => '',
			),
			$atts,
			$tag
		);

		$text = trim( $atts['text'] );

		if ( '' === $text && null !== $content ) {
			$text = trim( wp_strip_all_tags( do_shortcode( $content ) ) );
		}

		if ( '' === $text ) {
			$text = __( 'Button', 'rx-theme' );
		}

		return self::render_button( $atts, $text );
	}

	/**
	 * Button group shortcode.
	 *
	 * Example:
	 * [rx_buttons align="center" gap="md"]
	 *   [rx_button text="Start" url="/start"]
	 *   [rx_button text="Learn" url="/learn" style="outline"]
	 * [/rx_buttons]
	 */
	public static function buttons_shortcode( $atts, $content = null, $tag = '' ) {
		$atts = shortcode_atts(
			array(
				'align'       => 'center',
				'gap'         => 'md',
				'stack'       => 'mobile',
				'class'       => '',
				'id'          => '',
				'role'        => '',
				'aria_label'  => '',
			),
			$atts,
			$tag
		);

		$classes = array(
			'rx-button-group',
			'rx-button-group--align-' . sanitize_html_class( $atts['align'] ),
			'rx-button-group--gap-' . sanitize_html_class( $atts['gap'] ),
			'rx-button-group--stack-' . sanitize_html_class( $atts['stack'] ),
		);

		if ( ! empty( $atts['class'] ) ) {
			$classes[] = sanitize_html_class( $atts['class'] );
		}

		$attrs = array(
			'class' => implode( ' ', array_filter( $classes ) ),
		);

		if ( ! empty( $atts['id'] ) ) {
			$attrs['id'] = sanitize_html_class( $atts['id'] );
		}

		if ( ! empty( $atts['role'] ) ) {
			$attrs['role'] = sanitize_key( $atts['role'] );
		}

		if ( ! empty( $atts['aria_label'] ) ) {
			$attrs['aria-label'] = sanitize_text_field( $atts['aria_label'] );
		}

		return '<div ' . self::build_attrs( $attrs ) . '>' . do_shortcode( $content ) . '</div>';
	}

	/**
	 * CTA button shortcode.
	 */
	public static function cta_button_shortcode( $atts, $content = null, $tag = '' ) {
		$atts = shortcode_atts(
			array(
				'text'          => 'Get Started',
				'url'           => '#',
				'subtext'       => '',
				'style'         => 'gradient',
				'size'          => 'lg',
				'icon'          => '→',
				'icon_position' => 'right',
				'target'        => '',
				'class'         => '',
			),
			$atts,
			$tag
		);

		$atts['class'] .= ' rx-button--cta';

		$text = sanitize_text_field( $atts['text'] );

		if ( ! empty( $atts['subtext'] ) ) {
			$text .= '<span class="rx-button__subtext">' . esc_html( $atts['subtext'] ) . '</span>';
		}

		return self::render_button( $atts, $text, true );
	}

	/**
	 * Download button shortcode.
	 */
	public static function download_button_shortcode( $atts, $content = null, $tag = '' ) {
		$atts = shortcode_atts(
			array(
				'text'      => 'Download',
				'url'       => '#',
				'file_name' => '',
				'style'     => 'success',
				'size'      => 'md',
				'icon'      => '⬇',
				'target'    => '',
				'class'     => '',
			),
			$atts,
			$tag
		);

		$atts['download'] = ! empty( $atts['file_name'] ) ? sanitize_file_name( $atts['file_name'] ) : 'true';
		$atts['class']   .= ' rx-button--download';

		return self::render_button( $atts, sanitize_text_field( $atts['text'] ) );
	}

	/**
	 * WhatsApp button shortcode.
	 */
	public static function whatsapp_button_shortcode( $atts, $content = null, $tag = '' ) {
		$atts = shortcode_atts(
			array(
				'text'    => 'Chat on WhatsApp',
				'phone'   => '',
				'message' => '',
				'style'   => 'success',
				'size'    => 'md',
				'icon'    => '💬',
				'target'  => '_blank',
				'class'   => '',
			),
			$atts,
			$tag
		);

		$phone   = preg_replace( '/[^0-9]/', '', $atts['phone'] );
		$message = rawurlencode( sanitize_text_field( $atts['message'] ) );

		if ( empty( $phone ) ) {
			return '';
		}

		$atts['url']   = 'https://wa.me/' . $phone . ( $message ? '?text=' . $message : '' );
		$atts['class'] .= ' rx-button--whatsapp';

		return self::render_button( $atts, sanitize_text_field( $atts['text'] ) );
	}

	/**
	 * Phone button shortcode.
	 */
	public static function phone_button_shortcode( $atts, $content = null, $tag = '' ) {
		$atts = shortcode_atts(
			array(
				'text'  => 'Call Now',
				'phone' => '',
				'style' => 'primary',
				'size'  => 'md',
				'icon'  => '☎',
				'class' => '',
			),
			$atts,
			$tag
		);

		$phone = preg_replace( '/[^0-9+]/', '', $atts['phone'] );

		if ( empty( $phone ) ) {
			return '';
		}

		$atts['url']   = 'tel:' . $phone;
		$atts['class'] .= ' rx-button--phone';

		return self::render_button( $atts, sanitize_text_field( $atts['text'] ) );
	}

	/**
	 * Email button shortcode.
	 */
	public static function email_button_shortcode( $atts, $content = null, $tag = '' ) {
		$atts = shortcode_atts(
			array(
				'text'    => 'Email Us',
				'email'   => '',
				'subject' => '',
				'body'    => '',
				'style'   => 'secondary',
				'size'    => 'md',
				'icon'    => '✉',
				'class'   => '',
			),
			$atts,
			$tag
		);

		$email = sanitize_email( $atts['email'] );

		if ( ! is_email( $email ) ) {
			return '';
		}

		$args = array();

		if ( ! empty( $atts['subject'] ) ) {
			$args['subject'] = sanitize_text_field( $atts['subject'] );
		}

		if ( ! empty( $atts['body'] ) ) {
			$args['body'] = sanitize_textarea_field( $atts['body'] );
		}

		$query       = $args ? '?' . http_build_query( $args ) : '';
		$atts['url'] = 'mailto:' . antispambot( $email ) . $query;

		$atts['class'] .= ' rx-button--email';

		return self::render_button( $atts, sanitize_text_field( $atts['text'] ) );
	}

	/**
	 * Copy button shortcode.
	 */
	public static function copy_button_shortcode( $atts, $content = null, $tag = '' ) {
		$atts = shortcode_atts(
			array(
				'text'        => 'Copy',
				'copy'        => '',
				'copied_text' => 'Copied!',
				'style'       => 'dark',
				'size'        => 'sm',
				'icon'        => '📋',
				'class'       => '',
			),
			$atts,
			$tag
		);

		$copy_text = $atts['copy'];

		if ( '' === $copy_text && null !== $content ) {
			$copy_text = wp_strip_all_tags( do_shortcode( $content ) );
		}

		if ( '' === trim( $copy_text ) ) {
			return '';
		}

		$atts['type']          = 'button';
		$atts['url']           = '#';
		$atts['class']        .= ' rx-button--copy';
		$atts['data_rx']       = 'copy';
		$atts['data_label']    = sanitize_text_field( $copy_text );
		$atts['data_event']    = sanitize_text_field( $atts['copied_text'] );
		$atts['aria_label']    = sanitize_text_field( $atts['text'] );

		return self::render_button( $atts, sanitize_text_field( $atts['text'] ) );
	}

	/**
	 * Render button.
	 */
	private static function render_button( $atts, $text, $allow_html_text = false ) {
		$type     = sanitize_key( $atts['type'] ?? 'link' );
		$url      = isset( $atts['url'] ) ? trim( $atts['url'] ) : '#';
		$disabled = self::to_bool( $atts['disabled'] ?? false );

		$classes = array(
			'rx-button',
			'rx-button--' . sanitize_html_class( $atts['style'] ?? 'primary' ),
			'rx-button--size-' . sanitize_html_class( $atts['size'] ?? 'md' ),
			'rx-button--shape-' . sanitize_html_class( $atts['shape'] ?? 'rounded' ),
		);

		if ( ! empty( $atts['align'] ) ) {
			$classes[] = 'rx-button--align-' . sanitize_html_class( $atts['align'] );
		}

		if ( ! empty( $atts['width'] ) ) {
			$classes[] = 'rx-button--width-' . sanitize_html_class( $atts['width'] );
		}

		if ( self::to_bool( $atts['loading'] ?? false ) ) {
			$classes[] = 'is-loading';
		}

		if ( $disabled ) {
			$classes[] = 'is-disabled';
		}

		if ( ! empty( $atts['class'] ) ) {
			$extra_classes = explode( ' ', $atts['class'] );
			foreach ( $extra_classes as $extra_class ) {
				$classes[] = sanitize_html_class( $extra_class );
			}
		}

		$style = self::build_inline_style( $atts );

		$attrs = array(
			'class' => implode( ' ', array_filter( array_unique( $classes ) ) ),
		);

		if ( ! empty( $style ) ) {
			$attrs['style'] = $style;
		}

		if ( ! empty( $atts['id'] ) ) {
			$attrs['id'] = sanitize_html_class( $atts['id'] );
		}

		if ( ! empty( $atts['title'] ) ) {
			$attrs['title'] = sanitize_text_field( $atts['title'] );
		}

		if ( ! empty( $atts['tooltip'] ) ) {
			$attrs['data-tooltip'] = sanitize_text_field( $atts['tooltip'] );
		}

		if ( ! empty( $atts['aria_label'] ) ) {
			$attrs['aria-label'] = sanitize_text_field( $atts['aria_label'] );
		}

		if ( ! empty( $atts['data_event'] ) ) {
			$attrs['data-event'] = sanitize_text_field( $atts['data_event'] );
		}

		if ( ! empty( $atts['data_category'] ) ) {
			$attrs['data-category'] = sanitize_text_field( $atts['data_category'] );
		}

		if ( ! empty( $atts['data_label'] ) ) {
			$attrs['data-label'] = sanitize_text_field( $atts['data_label'] );
		}

		if ( ! empty( $atts['data_value'] ) ) {
			$attrs['data-value'] = sanitize_text_field( $atts['data_value'] );
		}

		if ( ! empty( $atts['data_location'] ) ) {
			$attrs['data-location'] = sanitize_text_field( $atts['data_location'] );
		}

		if ( ! empty( $atts['data_rx'] ) ) {
			$attrs['data-rx'] = sanitize_key( $atts['data_rx'] );
		}

		if ( ! empty( $atts['loading_text'] ) ) {
			$attrs['data-loading-text'] = sanitize_text_field( $atts['loading_text'] );
		}

		if ( ! empty( $atts['confirm'] ) ) {
			$attrs['data-confirm'] = sanitize_text_field( $atts['confirm'] );
		}

		$label = $allow_html_text ? wp_kses_post( $text ) : esc_html( $text );
		$icon  = self::render_icon( $atts['icon'] ?? '', $atts['icon_position'] ?? 'left' );

		if ( ! empty( $icon ) && 'right' === sanitize_key( $atts['icon_position'] ?? 'left' ) ) {
			$inner = '<span class="rx-button__text">' . $label . '</span>' . $icon;
		} elseif ( ! empty( $icon ) ) {
			$inner = $icon . '<span class="rx-button__text">' . $label . '</span>';
		} else {
			$inner = '<span class="rx-button__text">' . $label . '</span>';
		}

		if ( 'button' === $type || 'submit' === $type ) {
			$attrs['type'] = 'submit' === $type ? 'submit' : 'button';

			if ( $disabled ) {
				$attrs['disabled'] = 'disabled';
				$attrs['aria-disabled'] = 'true';
			}

			return '<button ' . self::build_attrs( $attrs ) . '>' . $inner . '</button>';
		}

		$attrs['href'] = esc_url( $url );

		if ( ! empty( $atts['target'] ) ) {
			$attrs['target'] = sanitize_key( $atts['target'] );
		}

		$rel = self::build_rel( $atts );

		if ( ! empty( $rel ) ) {
			$attrs['rel'] = $rel;
		}

		if ( ! empty( $atts['download'] ) ) {
			$attrs['download'] = 'true' === $atts['download'] ? '' : sanitize_file_name( $atts['download'] );
		}

		if ( $disabled ) {
			$attrs['href']          = '#';
			$attrs['aria-disabled'] = 'true';
			$attrs['tabindex']      = '-1';
		}

		return '<a ' . self::build_attrs( $attrs ) . '>' . $inner . '</a>';
	}

	/**
	 * Render icon.
	 */
	private static function render_icon( $icon, $position = 'left' ) {
		$icon = trim( wp_kses_post( $icon ) );

		if ( '' === $icon ) {
			return '';
		}

		$class = 'rx-button__icon rx-button__icon--' . sanitize_html_class( $position );

		return '<span class="' . esc_attr( $class ) . '" aria-hidden="true">' . $icon . '</span>';
	}

	/**
	 * Build rel attribute.
	 */
	private static function build_rel( $atts ) {
		$rel = array();

		if ( ! empty( $atts['rel'] ) ) {
			$custom_rel = explode( ' ', $atts['rel'] );

			foreach ( $custom_rel as $item ) {
				$rel[] = sanitize_key( $item );
			}
		}

		if ( '_blank' === ( $atts['target'] ?? '' ) && self::to_bool( $atts['noopener'] ?? true ) ) {
			$rel[] = 'noopener';
		}

		if ( self::to_bool( $atts['noreferrer'] ?? false ) ) {
			$rel[] = 'noreferrer';
		}

		if ( self::to_bool( $atts['nofollow'] ?? false ) ) {
			$rel[] = 'nofollow';
		}

		if ( self::to_bool( $atts['sponsored'] ?? false ) ) {
			$rel[] = 'sponsored';
		}

		if ( self::to_bool( $atts['ugc'] ?? false ) ) {
			$rel[] = 'ugc';
		}

		return implode( ' ', array_filter( array_unique( $rel ) ) );
	}

	/**
	 * Build inline style.
	 */
	private static function build_inline_style( $atts ) {
		$styles = array();

		if ( ! empty( $atts['bg'] ) ) {
			$styles[] = '--rx-btn-bg:' . sanitize_hex_color( $atts['bg'] );
		}

		if ( ! empty( $atts['color'] ) ) {
			$styles[] = '--rx-btn-color:' . sanitize_hex_color( $atts['color'] );
		}

		if ( ! empty( $atts['border'] ) ) {
			$styles[] = '--rx-btn-border:' . sanitize_hex_color( $atts['border'] );
		}

		if ( ! empty( $atts['hover_bg'] ) ) {
			$styles[] = '--rx-btn-hover-bg:' . sanitize_hex_color( $atts['hover_bg'] );
		}

		if ( ! empty( $atts['hover_color'] ) ) {
			$styles[] = '--rx-btn-hover-color:' . sanitize_hex_color( $atts['hover_color'] );
		}

		if ( ! empty( $atts['custom_width'] ) ) {
			$width = preg_replace( '/[^0-9a-zA-Z.%\-]/', '', $atts['custom_width'] );
			$styles[] = '--rx-btn-custom-width:' . $width;
		}

		return implode( ';', array_filter( $styles ) );
	}

	/**
	 * Build HTML attributes.
	 */
	private static function build_attrs( $attrs ) {
		$html = '';

		foreach ( $attrs as $name => $value ) {
			if ( null === $value || false === $value ) {
				continue;
			}

			$name = sanitize_key( $name );

			if ( '' === $value ) {
				$html .= ' ' . esc_attr( $name );
			} else {
				$html .= ' ' . esc_attr( $name ) . '="' . esc_attr( $value ) . '"';
			}
		}

		return trim( $html );
	}

	/**
	 * Convert shortcode bool.
	 */
	private static function to_bool( $value ) {
		if ( is_bool( $value ) ) {
			return $value;
		}

		$value = strtolower( trim( (string) $value ) );

		return in_array( $value, array( '1', 'true', 'yes', 'on' ), true );
	}

	/**
	 * Print inline CSS and JS once.
	 */
	public static function print_inline_assets() {
		self::print_css();
		self::print_js();
	}

	/**
	 * Print CSS.
	 */
	private static function print_css() {
		if ( self::$css_printed ) {
			return;
		}

		self::$css_printed = true;
		?>
		<style id="rx-button-shortcodes-css">
			.rx-button {
				--rx-btn-bg: #2563eb;
				--rx-btn-color: #ffffff;
				--rx-btn-border: #2563eb;
				--rx-btn-hover-bg: #1d4ed8;
				--rx-btn-hover-color: #ffffff;
				--rx-btn-radius: 10px;
				--rx-btn-custom-width: auto;
				display: inline-flex;
				align-items: center;
				justify-content: center;
				gap: .5em;
				width: var(--rx-btn-custom-width);
				padding: .75em 1.15em;
				border: 1px solid var(--rx-btn-border);
				border-radius: var(--rx-btn-radius);
				background: var(--rx-btn-bg);
				color: var(--rx-btn-color);
				font-weight: 700;
				line-height: 1.2;
				text-decoration: none;
				cursor: pointer;
				transition: transform .2s ease, box-shadow .2s ease, background .2s ease, color .2s ease, border-color .2s ease;
				box-shadow: 0 8px 20px rgba(15, 23, 42, .12);
				position: relative;
				vertical-align: middle;
			}

			.rx-button:hover,
			.rx-button:focus {
				background: var(--rx-btn-hover-bg);
				color: var(--rx-btn-hover-color);
				text-decoration: none;
				transform: translateY(-1px);
				box-shadow: 0 12px 28px rgba(15, 23, 42, .18);
			}

			.rx-button:focus-visible {
				outline: 3px solid rgba(37, 99, 235, .35);
				outline-offset: 3px;
			}

			.rx-button__icon {
				display: inline-flex;
				align-items: center;
				justify-content: center;
				line-height: 1;
			}

			.rx-button__text {
				display: inline-flex;
				align-items: center;
				gap: .35em;
			}

			.rx-button__subtext {
				display: block;
				font-size: .72em;
				font-weight: 500;
				opacity: .9;
				margin-top: .15rem;
			}

			.rx-button--primary {
				--rx-btn-bg: #2563eb;
				--rx-btn-border: #2563eb;
				--rx-btn-hover-bg: #1d4ed8;
			}

			.rx-button--secondary {
				--rx-btn-bg: #475569;
				--rx-btn-border: #475569;
				--rx-btn-hover-bg: #334155;
			}

			.rx-button--success {
				--rx-btn-bg: #16a34a;
				--rx-btn-border: #16a34a;
				--rx-btn-hover-bg: #15803d;
			}

			.rx-button--danger {
				--rx-btn-bg: #dc2626;
				--rx-btn-border: #dc2626;
				--rx-btn-hover-bg: #b91c1c;
			}

			.rx-button--warning {
				--rx-btn-bg: #f59e0b;
				--rx-btn-border: #f59e0b;
				--rx-btn-color: #111827;
				--rx-btn-hover-bg: #d97706;
				--rx-btn-hover-color: #111827;
			}

			.rx-button--info {
				--rx-btn-bg: #0891b2;
				--rx-btn-border: #0891b2;
				--rx-btn-hover-bg: #0e7490;
			}

			.rx-button--light {
				--rx-btn-bg: #f8fafc;
				--rx-btn-color: #0f172a;
				--rx-btn-border: #e2e8f0;
				--rx-btn-hover-bg: #e2e8f0;
				--rx-btn-hover-color: #0f172a;
			}

			.rx-button--dark {
				--rx-btn-bg: #0f172a;
				--rx-btn-border: #0f172a;
				--rx-btn-hover-bg: #020617;
			}

			.rx-button--outline {
				--rx-btn-bg: transparent;
				--rx-btn-color: #2563eb;
				--rx-btn-border: #2563eb;
				--rx-btn-hover-bg: #2563eb;
				--rx-btn-hover-color: #ffffff;
			}

			.rx-button--ghost {
				--rx-btn-bg: transparent;
				--rx-btn-color: #2563eb;
				--rx-btn-border: transparent;
				--rx-btn-hover-bg: rgba(37, 99, 235, .08);
				--rx-btn-hover-color: #1d4ed8;
				box-shadow: none;
			}

			.rx-button--gradient {
				--rx-btn-bg: linear-gradient(135deg, #2563eb, #7c3aed);
				--rx-btn-border: transparent;
				--rx-btn-hover-bg: linear-gradient(135deg, #1d4ed8, #6d28d9);
			}

			.rx-button--size-xs {
				font-size: .75rem;
				padding: .45em .7em;
			}

			.rx-button--size-sm {
				font-size: .875rem;
				padding: .55em .85em;
			}

			.rx-button--size-md {
				font-size: 1rem;
			}

			.rx-button--size-lg {
				font-size: 1.125rem;
				padding: .9em 1.35em;
			}

			.rx-button--size-xl {
				font-size: 1.25rem;
				padding: 1em 1.6em;
			}

			.rx-button--shape-square {
				--rx-btn-radius: 0;
			}

			.rx-button--shape-rounded {
				--rx-btn-radius: 10px;
			}

			.rx-button--shape-pill {
				--rx-btn-radius: 999px;
			}

			.rx-button--shape-circle {
				--rx-btn-radius: 999px;
				aspect-ratio: 1 / 1;
				padding-left: .9em;
				padding-right: .9em;
			}

			.rx-button--width-full,
			.rx-button--align-full {
				display: flex;
				width: 100%;
			}

			.rx-button--width-custom {
				width: var(--rx-btn-custom-width);
			}

			.rx-button--align-center {
				margin-left: auto;
				margin-right: auto;
			}

			.rx-button--align-right {
				margin-left: auto;
			}

			.rx-button.is-disabled,
			.rx-button[disabled] {
				opacity: .55;
				pointer-events: none;
				cursor: not-allowed;
				transform: none;
			}

			.rx-button.is-loading {
				pointer-events: none;
				opacity: .8;
			}

			.rx-button.is-loading::after {
				content: "";
				width: 1em;
				height: 1em;
				border: 2px solid currentColor;
				border-right-color: transparent;
				border-radius: 999px;
				animation: rxButtonSpin .75s linear infinite;
			}

			.rx-button[data-tooltip]::before {
				content: attr(data-tooltip);
				position: absolute;
				left: 50%;
				bottom: calc(100% + 8px);
				transform: translateX(-50%);
				padding: .35rem .55rem;
				border-radius: 6px;
				background: #111827;
				color: #ffffff;
				font-size: .75rem;
				white-space: nowrap;
				opacity: 0;
				visibility: hidden;
				pointer-events: none;
				transition: opacity .2s ease, visibility .2s ease;
			}

			.rx-button[data-tooltip]:hover::before,
			.rx-button[data-tooltip]:focus::before {
				opacity: 1;
				visibility: visible;
			}

			.rx-button-group {
				display: flex;
				flex-wrap: wrap;
				align-items: center;
				gap: .75rem;
				margin: 1rem 0;
			}

			.rx-button-group--align-left {
				justify-content: flex-start;
			}

			.rx-button-group--align-center {
				justify-content: center;
			}

			.rx-button-group--align-right {
				justify-content: flex-end;
			}

			.rx-button-group--align-between {
				justify-content: space-between;
			}

			.rx-button-group--gap-xs {
				gap: .25rem;
			}

			.rx-button-group--gap-sm {
				gap: .5rem;
			}

			.rx-button-group--gap-md {
				gap: .75rem;
			}

			.rx-button-group--gap-lg {
				gap: 1rem;
			}

			.rx-button-group--gap-xl {
				gap: 1.5rem;
			}

			.rx-button--cta {
				min-width: 220px;
				min-height: 58px;
			}

			.rx-button--copy.is-copied {
				--rx-btn-bg: #16a34a;
				--rx-btn-border: #16a34a;
				--rx-btn-hover-bg: #15803d;
			}

			@keyframes rxButtonSpin {
				to {
					transform: rotate(360deg);
				}
			}

			@media (max-width: 640px) {
				.rx-button-group--stack-mobile {
					flex-direction: column;
					align-items: stretch;
				}

				.rx-button-group--stack-mobile .rx-button {
					width: 100%;
				}
			}
		</style>
		<?php
	}

	/**
	 * Print JS.
	 */
	private static function print_js() {
		if ( self::$js_printed ) {
			return;
		}

		self::$js_printed = true;
		?>
		<script id="rx-button-shortcodes-js">
			(function () {
				'use strict';

				document.addEventListener('click', function (event) {
					var button = event.target.closest('.rx-button');

					if (!button) {
						return;
					}

					var confirmMessage = button.getAttribute('data-confirm');

					if (confirmMessage && !window.confirm(confirmMessage)) {
						event.preventDefault();
						return;
					}

					if (button.getAttribute('data-rx') === 'copy') {
						event.preventDefault();

						var copyText = button.getAttribute('data-label') || '';
						var copiedText = button.getAttribute('data-event') || 'Copied!';
						var originalTextElement = button.querySelector('.rx-button__text');
						var originalText = originalTextElement ? originalTextElement.textContent : '';

						if (!copyText) {
							return;
						}

						function setCopiedState() {
							button.classList.add('is-copied');

							if (originalTextElement) {
								originalTextElement.textContent = copiedText;
							}

							window.setTimeout(function () {
								button.classList.remove('is-copied');

								if (originalTextElement) {
									originalTextElement.textContent = originalText;
								}
							}, 1600);
						}

						if (navigator.clipboard && navigator.clipboard.writeText) {
							navigator.clipboard.writeText(copyText).then(setCopiedState);
						} else {
							var textarea = document.createElement('textarea');
							textarea.value = copyText;
							textarea.style.position = 'fixed';
							textarea.style.left = '-9999px';
							document.body.appendChild(textarea);
							textarea.select();

							try {
								document.execCommand('copy');
								setCopiedState();
							} catch (error) {}

							document.body.removeChild(textarea);
						}
					}

					if (button.getAttribute('data-loading-text')) {
						var textElement = button.querySelector('.rx-button__text');

						if (textElement && !button.classList.contains('is-loading')) {
							button.setAttribute('data-original-text', textElement.textContent);
							textElement.textContent = button.getAttribute('data-loading-text');
						}

						button.classList.add('is-loading');
					}
				});
			})();
		</script>
		<?php
	}
}

RX_Theme_Button_Shortcodes::init();

endif;

Now include it in your functions.php:

/**
 * RX Theme Shortcodes.
 */
$rx_buttons_file = get_template_directory() . '/inc/shortcodes/buttons.php';

if ( file_exists( $rx_buttons_file ) ) {
	require_once $rx_buttons_file;
}

Usage examples:

[rx_button text="Read More" url="/about" style="primary" size="lg" icon="→" icon_position="right"]

[rx_button text="Download Report" url="/file.pdf" style="success" download="report.pdf" icon="⬇"]

[rx_buttons align="center" gap="lg"]
[rx_button text="Start Now" url="/start" style="gradient" size="lg"]
[rx_button text="Learn More" url="/learn" style="outline" size="lg"]
[/rx_buttons]

[rx_cta_button text="Join RX Health Mission" subtext="A global war against illness" url="/join"]

[rx_whatsapp_button phone="8801700000000" message="Hello RX Harun" text="Chat With Us"]

[rx_phone_button phone="+8801700000000" text="Call Doctor"]

[rx_email_button email="hello@example.com" subject="Appointment Request" text="Send Email"]

[rx_copy_button copy="RXHARUN2026" text="Copy Coupon"]

This is safe for theme use because it sanitizes shortcode input, escapes output, supports accessibility labels, and uses unique rx_ shortcode names.

Leave a Reply

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