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 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
Footer Code
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)
Breadcrumbs Code
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">›</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');
});