Snippets

SNIPPETS

These Javascript and CSS Snippets have been entered into Dashboard>HFCM and Dashboard>Code Snippets and are duplicated here, for the record. Click on a snippet name to see the code.

HFCM Snippets

Code Snippets

HFCM Snippets (For CSS)

A-Z bar

This snippet looks after the A-Z at the head of the List of People (Tablepress 88)

		Site Display:	Site Wide
		Location:	Header	
		<style>
			/* ============================================================
			   A-Z bar: centered + responsive v1.0
			   GOAL:
			   - Keep the A-Z links truly centered
			   - Scale down font on small screens
			   - Prevent ugly wrap spacing issues
			   ============================================================ */

			.azbar{
			  text-align: center;
			  font-size: 1.2rem;
			  line-height: 1.4;
			  margin: 0;
			}

			/* If it wraps, this keeps spacing tidy */
			.azbar a{
			  display: inline-block;
			  padding: 0 0.15em; /* replaces &nbsp; spacing */
			  text-decoration: underline;
			}

			/* Tablet */
			@media (max-width: 768px){
			  .azbar{ font-size: 1.05rem; }
			}

			/* Phone */
			@media (max-width: 480px){
			  .azbar{ font-size: 0.95rem; }
			}
		</style>
	

Top Hamburger

On small screens the top MenuBar is collapsed to a hamburger.

		<style id="mh-elementor-mobile-toggle-white">
		/* ============================================================
		   TITLE: MH — Elementor Mobile Menu Toggle White
		   PURPOSE:
		   Force the TOP Elementor mobile hamburger icon to white
		   on small screens.

		   WHERE TO PUT IT:
		   HFCM → Add New
		   Type: HTML
		   Display: Entire Website
		   Location: Header

		   IMPORTANT:
		   Delete/disable the earlier test snippet called:
		   "MH — Mobile Header Hamburger White"

		   THIS TARGETS:
		   div.elementor-menu-toggle
		   svg.elementor-menu-toggle__icon--open
		   svg.elementor-menu-toggle__icon--close
		   and their <path> shapes
		   ============================================================ */

		@media (max-width: 1024px) {

		  /* button shell */
		  .elementor-menu-toggle {
			color: #ffffff !important;
			border-color: #ffffff !important;
		  }

		  /* the svg icons themselves */
		  .elementor-menu-toggle svg,
		  .elementor-menu-toggle svg * ,
		  .elementor-menu-toggle__icon--open,
		  .elementor-menu-toggle__icon--open *,
		  .elementor-menu-toggle__icon--close,
		  .elementor-menu-toggle__icon--close * {
			fill: #ffffff !important;
			stroke: #ffffff !important;
			color: #ffffff !important;
		  }

		  /* specifically the 3-bar icon path and close icon path */
		  .elementor-menu-toggle svg path,
		  .elementor-menu-toggle__icon--open path,
		  .elementor-menu-toggle__icon--close path {
			fill: #ffffff !important;
			stroke: #ffffff !important;
		  }
		}
		</style>
	

Bottom Hamburger

Sets up Footer

Simple (Blue Bar, Astra footer hidden)

		Site Display:	Site Wide
		Location:	Footer
		<style>
		/* --------------------------------------------------
		   1) Hide Astra's empty footer area
		   -------------------------------------------------- */
		.site-footer {
		  display: none !important;
		}

		/* --------------------------------------------------
		   2) Custom Macanese Heritage footer
		   -------------------------------------------------- */

		/* Custom footer wrapper */
		#mh-footer {
		  padding: 1.5rem 1rem 2rem;
		  border-top: 1px solid rgba(255, 255, 255, 0.1);
		  text-align: center;
		  font-size: 0.9rem;
		  line-height: 1.6;
		  
		  /* Portuguese tile blue bar */
		background-color: #003A6B;  /* use the same hex as your menu bar */

		  color: #ffffff;
		}

		.mh-footer-inner {
		  max-width: 900px;
		  margin: 0 auto;
		}

		/* Links row */
		.mh-footer-links {
		  display: flex;
		  flex-wrap: wrap;
		  justify-content: center;
		  gap: 0.4rem;
		  margin-bottom: 0.4rem;
		}

		/* Individual links */
		.mh-footer-links a {
		  text-decoration: none;
		  border-bottom: 1px solid transparent;
		  padding: 0 0.1rem;
		  color: #ffffff; /* white links */
		}

		/* Separator dot */
		.mh-footer-sep {
		  opacity: 0.8;
		  color: #ffffff;
		}

		/* Hover/focus states */
		.mh-footer-links a:hover,
		.mh-footer-links a:focus {
		  border-bottom-color: #ffffff;
		}

		/* Copyright line */
		.mh-footer-copy {
		  margin-top: 0.2rem;
		  font-size: 0.85rem;
		  opacity: 0.9;
		  color: #ffffff;
		}

		/* Small-screen tweaks */
		@media (max-width: 480px) {
		  #mh-footer {
			font-size: 0.85rem;
			padding: 1.2rem 0.8rem 1.6rem;
		  }
		  
		  .mh-footer-links {
			gap: 0.2rem;
		  }
		}
		</style>

		<div id="mh-footer">
		  <div class="mh-footer-inner">
			
			<!-- Footer navigation links -->
			<nav class="mh-footer-links" aria-label="Footer">
			  <a href="/about/">
				<span class="eng">About</span>
				<span class="port">Sobre</span>
			  </a>
			  <span class="mh-footer-sep">•</span>
			  
			  <a href="/contact/">
				<span class="eng">Contact</span>
				<span class="port">Contacto</span>
			  </a>
			  <span class="mh-footer-sep">•</span>
			  
			  <a href="/comments/">
				<span class="eng">Visitor Feedback</span>
				<span class="port">Opinião dos Visitantes</span>
			  </a>
			  <span class="mh-footer-sep">•</span>
			  
			  <a href="/privacy/">
				<span class="eng">Privacy</span>
				<span class="port">Privacidade</span>
			  </a>
			  <span class="mh-footer-sep">•</span>
			  
			  <a href="/credits/">
				<span class="eng">Credits</span>
				<span class="port">Créditos</span>
			  </a>
			</nav>

			<!-- Copyright line -->
			<div class="mh-footer-copy">
			  <span class="eng">© 2026 Macanese Heritage. All rights reserved.</span>
			  <span class="port">© 2026 Macanese Heritage. Todos os direitos reservados.</span>
			</div>
			
		  </div>
		</div>

		</style>
	

Hide Image Captions

We do not want image captions on pages, preferring to use HTML coding to add captions appropriate to the image.

		Site Display:	Site Wide
		Location:	Header
		<style id="mh-hide-page-captions-only">
			/* Hide captions under images on pages */
			figure.wp-caption figcaption,
			figure.wp-block-image figcaption,
			.wp-caption-text {
			  display: none !important;
			}
		</style>
	

Hide Page Titles

We do not want page titles on pages, preferring to add our own.

		Site Display:	Site Wide
		Location:	Header	
		<style>
			/* Hide native WordPress/Theme page titles site-wide (but NOT blog posts) */
			body.page h1.entry-title,
			body.page .entry-title,
			body.page .page-title,
			body.page .elementor-page-title,
			body.page header.entry-header > .entry-title,
			body.page header.entry-header > h1.entry-title {
			  display: none !important;
			}
		</style>
	

Language Switching

Flipping between English and Portuguese is done by a snippet (WordPress Dashboard > HFCM > Language Switching)
		Site Display:	Site Wide
		Location:	Header		
		<style>
		  /* =====================================
			 LANGUAGE VISIBILITY (CONTENT BLOCKS)
			 ===================================== */

		  /* Default = English visible, Portuguese hidden */
		  html:not(.show-port) .port {
			display: none !important;
		  }

		  /* When Portuguese mode is ON, hide English instead */
		  html.show-port .eng {
			display: none !important;
		  }

		  /* Optional: EN / PT menu highlighting */
		  li.menu-lang-en a,
		  li.menu-lang-pt a {
			opacity: 0.75;
			transition: opacity 0.2s ease;
		  }

		  html.show-port li.menu-lang-pt a {
			opacity: 1;
		  }

		  html.show-port li.menu-lang-en a {
			opacity: 0.5;
		  }
		</style>

		<script>
		(function () {
		  var STORAGE_KEY = 'wp_lang';

		  // Don't run in wp-admin
		  if (/\/wp-admin\//.test(window.location.pathname)) return;

		  function setLang(lang, persist) {
			lang = (lang || 'en').toLowerCase();

			var html = document.documentElement;
			if (lang === 'pt') {
			  html.classList.add('show-port');
			} else {
			  html.classList.remove('show-port');
			}

			if (persist) {
			  try {
				localStorage.setItem(STORAGE_KEY, lang);
			  } catch (e) {}
			}
		  }

		  function initLang() {
			var urlLang = null;
			try {
			  var params = new URLSearchParams(window.location.search);
			  urlLang = params.get('lang');
			} catch (e) {}

			var stored = null;
			try {
			  stored = localStorage.getItem(STORAGE_KEY);
			} catch (e) {}

			// PRIORITY:
			// 1) lang from URL (and we save it)
			// 2) stored in localStorage
			// 3) default 'en' (but we do NOT force-save 'en')
			var initial = (urlLang || stored || 'en').toLowerCase();

			// Persist only if it actually came from the URL
			setLang(initial, !!urlLang);
		  }

		  // Apply language on page load
		  initLang();

		  // Handle clicks on EN / PT menu items (language flipper)
		  document.addEventListener('click', function (e) {
			var li = e.target.closest('li.menu-lang-en, li.menu-lang-pt');
			if (!li) return;

			// Stop the link doing anything; we only want to flip in-place
			e.preventDefault();

			var lang = li.classList.contains('menu-lang-pt') ? 'pt' : 'en';
			setLang(lang, true);

			// Update the URL (current page) to include ?lang=xx so refresh keeps it
			try {
			  var url = new URL(window.location.href);
			  url.searchParams.set('lang', lang);
			  window.history.replaceState({}, '', url);
			} catch (err) {
			  // ignore
			}
		  }, { passive: false });
		})();
		</script>
	

Tag Archives Remove Meta Line

Normally the Tag Archives show details like “Henry”; this snippet removes them.
		Site Display:	Site Wide
		Location:	Header	
		<style>
		  /* ============================================================
			 TAG ARCHIVES ONLY
			 ============================================================ */
		  body.tag,
		  body.archive.tag {
			/* just for clarity: these body classes should match tag archives */
		  }

		  /* ============================================================
			 1) ASTRA / WP classic meta wrappers
			 ============================================================ */
		  body.tag .entry-meta,
		  body.tag .entry-meta *,
		  body.tag .post-meta,
		  body.tag .post-meta *,
		  body.tag .ast-blog-meta-container,
		  body.tag .ast-blog-meta-container *,
		  body.tag .ast-post-meta,
		  body.tag .ast-post-meta *,
		  body.tag .ast-article-post-meta,
		  body.tag .ast-article-post-meta *,
		  body.tag .ast-entry-meta,
		  body.tag .ast-entry-meta *,
		  body.tag .ast-meta,
		  body.tag .ast-meta *,

		  body.archive.tag .entry-meta,
		  body.archive.tag .entry-meta *,
		  body.archive.tag .post-meta,
		  body.archive.tag .post-meta *,
		  body.archive.tag .ast-blog-meta-container,
		  body.archive.tag .ast-blog-meta-container *,
		  body.archive.tag .ast-post-meta,
		  body.archive.tag .ast-post-meta *,
		  body.archive.tag .ast-article-post-meta,
		  body.archive.tag .ast-article-post-meta *,
		  body.archive.tag .ast-entry-meta,
		  body.archive.tag .ast-entry-meta *,
		  body.archive.tag .ast-meta,
		  body.archive.tag .ast-meta * {
			display: none !important;
		  }

		  /* ============================================================
			 2) ELEMENTOR "Posts" widget meta wrapper
			 ============================================================ */
		  body.tag .elementor-post__meta-data,
		  body.tag .elementor-post__meta-data *,
		  body.archive.tag .elementor-post__meta-data,
		  body.archive.tag .elementor-post__meta-data * {
			display: none !important;
		  }

		  /* ============================================================
			 Optional: tighten spacing where meta used to be (safe)
			 ============================================================ */
		  body.tag .entry-header,
		  body.archive.tag .entry-header {
			margin-bottom: 0.35rem !important;
		  }
		</style>

		<script>
		(function () {
		  'use strict';

		  /* ============================================================
			 JS FALLBACK (tag archives only)
			 - If some meta line survives due to different markup, remove it
			   by detecting an author link in the "meta-ish" area of each card.
			 ============================================================ */

		  function isTagArchivePage() {
			var b = document.body;
			return b && (b.classList.contains('tag') || (b.classList.contains('archive') && b.classList.contains('tag')));
		  }

		  function removeMetaLinesFallback() {
			if (!isTagArchivePage()) return;

			// Candidate containers that often hold meta in Astra/Elementor.
			var candidates = document.querySelectorAll([
			  '.entry-meta',
			  '.post-meta',
			  '.ast-blog-meta-container',
			  '.ast-post-meta',
			  '.ast-article-post-meta',
			  '.ast-entry-meta',
			  '.ast-meta',
			  '.elementor-post__meta-data'
			].join(','));

			candidates.forEach(function (el) {
			  // If it contains an author link, nuke the whole block.
			  if (el.querySelector('a[rel="author"], .byline, .author, .ast-author')) {
				el.style.display = 'none';
			  }
			});

			// Extra fallback: sometimes meta is a plain text block with an author link nearby.
			// We remove any small "meta-looking" lines that contain an author link.
			var authorLinks = document.querySelectorAll('a[rel="author"]');
			authorLinks.forEach(function (a) {
			  // Walk up a few levels to find the likely meta line container and hide it.
			  var el = a;
			  for (var i = 0; i < 5 && el; i++) {
				if (el.classList && (
					el.classList.contains('entry-meta') ||
					el.classList.contains('ast-blog-meta-container') ||
					el.classList.contains('ast-post-meta') ||
					el.classList.contains('elementor-post__meta-data') ||
					el.tagName === 'P' ||
					el.tagName === 'DIV'
				)) {
				  el.style.display = 'none';
				  break;
				}
				el = el.parentElement;
			  }
			});
		  }

		  // Run on DOM ready
		  if (document.readyState === 'loading') {
			document.addEventListener('DOMContentLoaded', removeMetaLinesFallback);
		  } else {
			removeMetaLinesFallback();
		  }

		})();
		</script>

		
	

Code Snippets (For JavaScript)

Sets up Breadcrumbs for pages

		/* ============================================================
		   SNIPPET: MH — Breadcrumbs (MANUAL ONLY: shortcode + CSS) v2.0
		   PURPOSE
		   - Provides shortcode: 
		   - You place  manually where you want breadcrumbs to appear
		   - Styles breadcrumbs TOP LEFT
		   - Preserves language in crumb LINKS using ?lang=en|pt

		   IMPORTANT CHANGE
		   - NO auto-injection (no Astra hooks, no Elementor hooks, no the_content filters)
		   ============================================================ */

		if ( ! defined('ABSPATH') ) exit;

		/** Detect current language: 'en' or 'pt' (default 'en'). */
		function mh_breadcrumbs_get_lang() {
			if ( isset( $_GET['lang'] ) ) {
				$lang = strtolower( sanitize_text_field( $_GET['lang'] ) );
				if ( in_array( $lang, array( 'en', 'pt' ), true ) ) return $lang;
			}
			return 'en';
		}

		/** Add/overwrite ?lang=... in a URL. */
		function mh_breadcrumbs_url_with_lang( $url, $lang ) {
			return esc_url( add_query_arg( 'lang', $lang, $url ) );
		}

		function mh_get_breadcrumbs_html() {
			if ( is_front_page() ) return '';

			$lang     = mh_breadcrumbs_get_lang();
			$home_url = mh_breadcrumbs_url_with_lang( home_url( '/' ), $lang );

			$crumbs = array();
			$crumbs[] = array( 'Home', $home_url );

			if ( is_singular( 'page' ) ) {
				global $post;
				$ancestors = get_post_ancestors( $post );

				if ( ! empty( $ancestors ) ) {
					$ancestors = array_reverse( $ancestors );
					foreach ( $ancestors as $ancestor_id ) {
						$crumbs[] = array(
							get_the_title( $ancestor_id ),
							mh_breadcrumbs_url_with_lang( get_permalink( $ancestor_id ), $lang )
						);
					}
				}

				$crumbs[] = array( get_the_title(), null );
			}
			elseif ( is_singular( 'post' ) ) {
				$categories = get_the_category();
				if ( ! empty( $categories ) ) {
					$primary_cat = $categories[0];
					$crumbs[] = array(
						$primary_cat->name,
						mh_breadcrumbs_url_with_lang( get_category_link( $primary_cat->term_id ), $lang )
					);
				}
				$crumbs[] = array( get_the_title(), null );
			}
			elseif ( is_category() ) {
				$crumbs[] = array( single_cat_title( '', false ), null );
			}
			elseif ( is_tag() ) {
				$crumbs[] = array( single_tag_title( '', false ), null );
			}
			elseif ( is_search() ) {
				$crumbs[] = array( 'Search: ' . get_search_query(), null );
			}
			elseif ( is_404() ) {
				$crumbs[] = array( '404 – Page not found', null );
			}
			elseif ( is_archive() ) {
				$title = wp_strip_all_tags( get_the_archive_title() );
				$crumbs[] = array( $title, null );
			}

			$out  = '<nav class="mh-breadcrumbs" aria-label="Breadcrumb">';
			$out .= '<ol class="mh-breadcrumbs__list">';

			$total = count( $crumbs );
			foreach ( $crumbs as $i => $c ) {
				$label   = $c[0];
				$url     = $c[1];
				$is_last = ( $i === $total - 1 );

				$out .= '<li class="mh-breadcrumbs__item">';

				if ( ! $is_last && ! empty( $url ) ) {
					$out .= '<a class="mh-breadcrumbs__link" href="' . esc_url( $url ) . '">';
					$out .= esc_html( $label );
					$out .= '</a>';
				} else {
					$out .= '<span class="mh-breadcrumbs__current" aria-current="page">';
					$out .= esc_html( $label );
					$out .= '</span>';
				}

				if ( ! $is_last ) {
					$out .= '<span class="mh-breadcrumbs__sep" aria-hidden="true">&rsaquo;</span>';
				}

				$out .= '</li>';
			}

			$out .= '</ol></nav>';

			return $out;
		}

		function mh_breadcrumbs_shortcode() {
			return mh_get_breadcrumbs_html();
		}
		add_shortcode( 'mh_breadcrumbs', 'mh_breadcrumbs_shortcode' );

		function mh_breadcrumbs_inline_css() {
			if ( is_admin() ) return;

			echo "\n" . '<style id="mh-breadcrumbs-css-manual-only">
		.mh-breadcrumbs{
		  margin: 0.4rem 0 0.8rem;
		  display: flex;
		  justify-content: flex-start;
		}
		.mh-breadcrumbs__list{
		  list-style: none;
		  padding: 0;
		  margin: 0;
		  display: flex;
		  flex-wrap: wrap;
		  justify-content: flex-start;
		  align-items: baseline;
		  gap: 0.35rem;
		  font-size: 0.95rem;
		  line-height: 1.35;
		  color: var(--brand-blue, #005A9C);
		}
		.mh-breadcrumbs__item{ display: inline-flex; align-items: baseline; gap: 0.35rem; }
		.mh-breadcrumbs__link{
		  color: inherit;
		  text-decoration: none;
		  border-bottom: 1px dotted currentColor;
		  text-underline-offset: 2px;
		}
		.mh-breadcrumbs__link:hover,
		.mh-breadcrumbs__link:focus-visible{ border-bottom-style: solid; outline: none; }
		.mh-breadcrumbs__sep{ opacity: 0.6; }
		.mh-breadcrumbs__current{ font-weight: 700; color: var(--text-color, #333); }
		@media (max-width: 767px){
		  .mh-breadcrumbs__list{ font-size: 0.85rem; line-height: 1.25; }
		}
		</style>' . "\n";
		}
		add_action( 'wp_head', 'mh_breadcrumbs_inline_css', 30 );
	

Tooltip shortcode TT

Sets up shortcode for tooltips of the form [tt keyword=’NAME’]CONTENT[/tt] (instead of [tooltips keyword=’NAME’ content=’CONTENT’]) that accepts inverted commas in CONTENT
* Title: TT Shortcode Wrapper for WP Tooltips * Purpose: Allow authors to write NAME * instead of NAME. * Features: * – Normalizes curly quotes (Elementor/Word). * – Preserves nested shortcodes in content. * – Auto-chooses content=”…” vs. content=’…’ to reduce escaping issues. * – Runs on Elementor, Classic Editor, and widget text. */

Pela Patria Viewer

Sets up links to Pela Patria magazine pages
		Site Display:	Site Wide
		Location:	Footer	
		<style>
		  /* Keep the caption readable and consistent */
		  .pp-viewer-standard {
			font-weight: 600;
		  }
		</style>

		<script>
		(function () {
		  "use strict";

		  /* ------------------------------------------
			 Month name map: "08" -> "Aug"
			 ------------------------------------------ */
		  var MONTHS = {
			"01":"Jan","02":"Feb","03":"Mar","04":"Apr","05":"May","06":"Jun",
			"07":"Jul","08":"Aug","09":"Sep","10":"Oct","11":"Nov","12":"Dec"
		  };

		  /* ------------------------------------------
			 Helper: normalize separators
			 - Your caption uses a middle dot: ·
			 - But we tolerate extra spaces and different dot types.
			 ------------------------------------------ */
		  function normalizeCaption(s) {
			return (s || "")
			  .replace(/\u00A0/g, " ")            // non-breaking spaces
			  .replace(/\s+/g, " ")              // collapse whitespace
			  .replace(/\s*[•·]\s*/g, " · ")     // normalize bullet/dot to " · "
			  .trim();
		  }

		  /* ------------------------------------------
			 Parse:
			   "1940 · 08 · p04 · Scan 3 of 42"
			 Output:
			   { yy:"1940", mm:"08", pg:"p04", pos:"3", total:"42" }
			 ------------------------------------------ */
		  function parseOldFormat(text) {
			var t = normalizeCaption(text);

			// Strict pattern: YEAR · MM · pNN[A] · Scan X of Y
			var re = /^(\d{4}) · (\d{2}) · (p\d{2}[A-Za-z]*) · Scan (\d+) of (\d+)$/;
			var m = t.match(re);
			if (!m) return null;

			return {
			  yy: m[1],
			  mm: m[2],
			  pg: m[3],
			  pos: m[4],
			  total: m[5]
			};
		  }

		  /* ------------------------------------------
			 Build new caption:
			   "p04 · Aug 1940 · 3 of 42"
			 ------------------------------------------ */
		  function buildNewCaption(obj) {
			var mon = MONTHS[obj.mm] || obj.mm; // fallback to "08" if unexpected
			return obj.pg + " · " + mon + " " + obj.yy + " · " + obj.pos + " of " + obj.total;
		  }

		  /* ------------------------------------------
			 Main: find caption element and rewrite
			 ------------------------------------------ */
		  function rewriteOnce() {
			// Your working viewer uses this class (from earlier snippets)
			var el = document.querySelector(".pp-viewer-standard");
			if (!el) return;

			var original = normalizeCaption(el.textContent);
			var parsed = parseOldFormat(original);
			if (!parsed) return; // fail safely

			el.textContent = buildNewCaption(parsed);
		  }

		  /* Run after DOM is ready */
		  if (document.readyState === "loading") {
			document.addEventListener("DOMContentLoaded", rewriteOnce);
		  } else {
			rewriteOnce();
		  }
		})();
		</script>
	

Tag Archives

		/* ============================================================
		   SNIPPET: MH — Tags for Pages + Tag Archives (MASTER) v2.0
		   DATE: 2026-02-13

		   GOAL (YOUR MODEL)
		   - Use Tags as NAVIGATION for Pages.
		   - Pages must be taggable.
		   - /tag/{slug}/ must list tagged Pages (and optionally Posts).

		   THIS SINGLE SNIPPET REPLACES / MERGES:
		   - "Enable Excerpts for Pages"
		   - "MH — Tag Navigation (Pages + Tag Archives) MASTER v1.0"
		   - "MH — Tag Archives: Include Pages + Posts v1.0"

		   CONFIG (EDIT THESE)
		   - MH_TAG_ARCHIVES_MODE:
			   'pages_only'  = tag archives show ONLY Pages (best for navigation hubs)
			   'pages_posts' = tag archives show Pages + Posts
		   - MH_ENABLE_PAGE_EXCERPTS:
			   true  = enable Excerpt field for Pages (recommended)
			   false = leave unchanged

		   INSTALL
		   - WordPress Admin → Code Snippets → Add New (PHP)
		   - Run: "Run snippet everywhere"
		   - Activate
		   - Then DISABLE the older/duplicate snippets listed above.

		   HOW TO TEST
		   1) Edit a Page → assign a Tag.
		   2) Visit /tag/{tag-slug}/
		   3) Confirm the Page appears.
		============================================================ */

		if ( ! defined('ABSPATH') ) exit;

		/* =========================
		   CONFIG
		   ========================= */
		define('MH_TAG_ARCHIVES_MODE', 'pages_only'); // 'pages_only' | 'pages_posts'
		define('MH_ENABLE_PAGE_EXCERPTS', true);

		/* =========================
		   A) ENABLE TAGS + OPTIONAL EXCERPTS FOR PAGES
		   ========================= */
		add_action('init', function () {

			// Enable core WP Tags (post_tag) on Pages
			register_taxonomy_for_object_type('post_tag', 'page');

			// Optional: enable Excerpt field for Pages
			if ( MH_ENABLE_PAGE_EXCERPTS ) {
				add_post_type_support('page', 'excerpt');
			}
		});

		/* =========================
		   B) TAG ARCHIVES: INCLUDE PAGES (AND OPTIONAL POSTS)
		   ========================= */
		add_action('pre_get_posts', function ($q) {

			// Front-end only; do not affect wp-admin
			if ( is_admin() ) return;

			// Only adjust the main query
			if ( ! $q->is_main_query() ) return;

			// Only tag archives
			if ( ! $q->is_tag() ) return;

			// Choose behavior
			if ( MH_TAG_ARCHIVES_MODE === 'pages_posts' ) {
				$q->set('post_type', array('page', 'post'));
			} else {
				// Default: pages_only
				$q->set('post_type', array('page'));
			}

			// Navigation-friendly ordering
			$q->set('orderby', 'title');
			$q->set('order', 'ASC');
		});

	

	
1