Horizon is a no-dependency Webflow template that works 100% with native features. Any custom code listed below is optional and safe to remove.
Pages: Home, Apartments (Listing), Property Details (CMS), About, Contact, 404, Password, Licenses, Changelog, Instructions, Cookie, Privacy Policy, Terms, Style Guide.
CMS Collections: Apartments, Agents, Bedrooms, Bathrooms, Garages, Views, 3D Tours (demo items included).
/en
).Horizon ships with an optional Global Styles Component that defines a fluid type scale using pure CSS (no JS, no <html>
font-size hacks).
How to use
<body>
on each page.CSS (paste in an Embed inside the “Global Styles” Component):
<style id="global-styles">
/* ===== Fluid type — compact defaults (no JS, no html hacks) ===== */
:root{
/* steps: tighten maxima ~20–25% vs previous scale */
--step--2: clamp(0.8125rem, 0.79rem + 0.20vw, 0.875rem); /* 13–14px */
--step--1: clamp(0.9rem, 0.88rem + 0.20vw, 0.95rem); /* 14.4–15.2px */
--step-0: clamp(1rem, 0.98rem + 0.20vw, 1.0625rem); /* 16–17px (body) */
--step-1: clamp(1.125rem, 1.05rem + 0.35vw, 1.25rem); /* 18–20px */
--step-2: clamp(1.25rem, 1.10rem + 0.60vw, 1.5rem); /* 20–24px */
--step-3: clamp(1.5rem, 1.20rem + 1.00vw, 1.875rem); /* 24–30px */
--step-4: clamp(1.875rem, 1.40rem + 1.40vw, 2.5rem); /* 30–40px */
--step-5: clamp(2.25rem, 1.60rem + 2.00vw, 3rem); /* 36–48px (display) */
}
html{ font-size:16px; -webkit-text-size-adjust:100%; }
body{ font-size:var(--step-0); line-height:1.6; }
/* Headings — smaller by default; use .display-* for hero titles */
h1,.h1{ font-size:var(--step-4); line-height:1.12; }
h2,.h2{ font-size:var(--step-3); line-height:1.18; }
h3,.h3{ font-size:var(--step-2); line-height:1.24; }
h4,.h4{ font-size:var(--step-1); line-height:1.3; }
h5,.h5{ font-size:var(--step-0); line-height:1.4; }
small,.text-small{ font-size:var(--step--1); }
/* Hero/marketing displays (opt-in only) */
.display-lg{ font-size:var(--step-5); line-height:1.06; }
.display-md{ font-size:var(--step-4); line-height:1.10; }
/* ===== Utilities ===== */
*,*::before,*::after{ box-sizing:border-box; }
:focus-visible{ outline:2px solid currentColor; outline-offset:2px; }
.sr-only{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); border:0; }
/* Text sizing helpers */
.text-xs{ font-size:var(--step--2); }
.text-sm{ font-size:var(--step--1); }
.text-md{ font-size:var(--step-0); }
.text-lg{ font-size:var(--step-1); }
/* Table/compact contexts — shrink one step without changing markup */
.is-compact{
--step-5: var(--step-4);
--step-4: var(--step-3);
--step-3: var(--step-2);
--step-2: var(--step-1);
--step-1: var(--step-0);
--step-0: var(--step--1);
}
.no-wrap{ white-space:nowrap; }
/* Optional: tighten headings inside tables only */
.table th, .table .th, .table .heading { font-size:var(--step-1); line-height:1.2; }
</style>
These features are not required for the template to work. Remove if not needed.
No-code option: swap the Tabs section for a Webflow Slider with Auto-rotate.
Light JS option (page-scoped): add this snippet only on pages that use Tabs. It auto-detects .w-tabs
, respects prefers-reduced-motion
, pauses on user interaction, and pauses if the mobile menu is open.
Where to place: Page Settings → Before </body>
(this page only), or in an Embed right after the Tabs component.
Script:
<!-- Horizon – Tabs Auto-Rotate (Optional, page-scoped, no dependencies)
Attribute-free variant: auto-detects any .w-tabs on this page.
- No data attributes required (defaults below)
- Respects prefers-reduced-motion
- Pauses on user interaction and when the mobile menu is open
- You can still override per-instance via data-rotate-ms / data-initial-delay if desired
-->
<script>
window.Webflow = window.Webflow || [];
window.Webflow.push(function () {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
const DEFAULT_ROTATE_MS = 3000;
const DEFAULT_INITIAL_DELAY = 1000;
const wrappers = Array.from(document.querySelectorAll('.w-tabs'));
if (!wrappers.length) return;
wrappers.forEach((root) => {
const menu = root.querySelector('.tabs-menu, .w-tab-menu'); if (!menu) return;
const links = Array.from(menu.querySelectorAll('.tab-link, .w-tab-link')); if (links.length < 2) return;
const ROTATE_MS = Number(root.getAttribute('data-rotate-ms') || DEFAULT_ROTATE_MS);
const INITIAL_DELAY = Number(root.getAttribute('data-initial-delay') || DEFAULT_INITIAL_DELAY);
let i = links.findIndex(l => l.classList.contains('w--current')); if (i < 0) i = 0;
let timer = null, paused = false;
const isMenuOpen = () => !!document.querySelector('.menu-button.w--open');
function step(){ if (paused || isMenuOpen()) return; i = (i + 1) % links.length;
links[i].dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true })); }
function start(){ stop(); timer = setInterval(step, ROTATE_MS); }
function stop(){ if (timer) clearInterval(timer); }
['mouseenter','focusin','pointerdown','touchstart'].forEach(evt =>
root.addEventListener(evt, () => { paused = true; stop(); }, { passive: true }));
['mouseleave','focusout'].forEach(evt =>
root.addEventListener(evt, () => { paused = false; start(); }, { passive: true }));
links.forEach(l => l.addEventListener('click', () => { paused = false; start(); }, { passive: true }));
setTimeout(step, INITIAL_DELAY); start();
});
});
</script>
Notes: On pages with multiple Tab groups, all will auto-rotate. To target a single Tabs block, you can add data-rotate-ms
/data-initial-delay
on that wrapper (optional).
Load the 3D embed only on click to keep performance and accessibility high.
Markup + script (place in the CMS Property Template or a dedicated 3D Tours page):
<div class="vr-placeholder" data-embed="https://theviewer.co/embed?galleryId=YOUR_GALLERY_ID&fullScreen=true&thumbnail=false">
<button class="btn vr-load" aria-label="Launch 3D property tour">Launch 3D Tour</button>
</div>
<script>
document.querySelectorAll('.vr-placeholder .vr-load').forEach(b=>b.addEventListener('click',()=>{
const wrap=b.closest('.vr-placeholder'); const src=wrap.dataset.embed;
const ifr=document.createElement('iframe'); ifr.src=src; ifr.loading='lazy';
ifr.setAttribute('title','3D property tour');
ifr.setAttribute('allow','xr-spatial-tracking; gyroscope; accelerometer; fullscreen');
ifr.style.width='100%'; ifr.style.height='60vh'; wrap.innerHTML=''; wrap.appendChild(ifr);
},{passive:true}));
</script>
Production note: your 3D provider may require an account. If you don’t need 3D, remove the placeholder block entirely.
noindex
).For questions or help customizing Horizon, contact: your-email@domain.com.