Ironvale Heating & Air
A call-first website for a heating and cooling contractor.
Concept project · Fictional business demonstration Ironvale Heating & Air is a fictional company. The site was designed and built to demonstrate HVAC and local-service web design and development; its reviews, credentials, arrival targets, founding year and warranty are demonstration content.
An 11-page site for a heating and cooling contractor in Columbus, Ohio. Heating and cooling run as two parallel tracks, the arrival-time target leads the page with its qualifier, and calling is the primary action on every screen.
The demo is a fictional business. It is set to noindex and shows a concept disclosure in the footer of every page.
- Type
- 11-page local-service website: 5 main pages, 4 service pages, warranty terms, privacy
- Stack
- Eleventy 3 · Nunjucks · hand-written CSS; WordPress classic theme (local only)
- Hosting
- Cloudflare Pages (static build)
- Built
- September 2026
- Status
- Live demo, noindex
- Live demo
- ironvale-heating.pages.dev
Brief
The problem
A heating and cooling contractor in Columbus, Ohio. Most visitors arrive on a phone, in the middle of a problem: no heat in January, no cooling in July.
The site has two jobs:
- Get the visitor whose system has stopped to call.
- Give the visitor who is still comparing two or three companies a reason to ask for a quote.
They arrive uncomfortable, short on time and wary of being overcharged, so the page answers who comes, how soon and how the price is set before it asks for anything.
Conversion
Conversion strategy
Calling is the primary action on every screen. The quote form is the secondary path for people who are not ready to talk.
The arrival target leads the page
The hero opens with the target: 60 minutes inside Columbus, 90 in the surrounding suburbs, followed on every appearance by the note that actual times vary with weather, traffic and call volume. The call button comes first, the quote request second.
Eight ways to call from the home page
The home page carries 8 phone links, all reachable by keyboard. The quote form is a demonstration: it validates, confirms, and says that nothing was sent.
Plans sold on what they include, not on price
Three maintenance plans differ by what is included. Pricing stays in the quote, and the annual visit is also the condition that keeps the workmanship warranty active.
Screenshots from the live demo. Phone number, targets, reviews and warranty belong to the fictional business.
Design
Design: two tracks, two temperatures
Visitors arrive with one problem: heat or cooling. So the services are two parallel tracks on different surfaces, warm cream for heating and cool grey-blue for cooling, instead of a six-card grid. With the logo removed, the tracks, the thermostat-style panels and the arrival numbers still say HVAC.
- Brand blue
#0B3D64 - Amber (conversion only)
#F0A400 - Heat ink
#A63A0B - Warm cream
#FBF3EC - Cool grey
#F2F5F8
- Type
- Archivo 700 / 600 for headings; Source Sans 3 400 / 600 for body text
- Numbers
- Phone numbers, arrival times and years set in tabular numerals; the arrival number is set at display size
- Layout
- Asymmetric 7/5 hero and feature sections; an even split is used only for the two tracks
- Colour
- Amber appears only on the primary call button, the emergency-band number and the mobile call bar
- Focus
- Two focus-ring colours, one for light sections and one for dark, after a single ring measured 1.6:1 on the dark band
Mobile
Responsive and mobile strategy
- Widths tested
- 11 pages at 360, 390, 768, 1024, 1440 and 1920px in both the static and WordPress builds: 0 horizontal overflow (local, 9 Sep 2026). Re-checked on the live demo at 375, 768 and 1440px on 10 Sep 2026: no overflow.
- Call button
- The header keeps its call button at every width; below 640px the label shortens to "Call now".
- Mobile call bar
- A call bar appears after 40% of the page is scrolled, on every page.
- Menu drawer
- Focus moves into the drawer, Esc closes it, and focus returns to the menu button.
- Tap targets
- Breadcrumb links (20px) and area-table city links (22px) were raised to 44px; no non-inline target below 44px.
Local service
Local-service architecture
- Service areas
- One page for five cities (Columbus, Dublin, Westerville, Gahanna, Hilliard), each with its own copy, housing stock, most common work and arrival target. Adding a city adds a section, not a thin duplicate page.
- Location model
- Business address city, primary service city and search market are separate settings. With the street address marked private, it appears 0 times on 12 static and 11 WordPress routes, schema included.
- One source per fact
- Phone, hours, arrival targets, founding year, warranty length and the city list resolve from one config. Changing the phone, targets, city, founding year and warranty and adding a city left 0 old values on 11 routes in both builds.
- Claim states
- Each trust claim carries a verification state. A client-production build is blocked while any claim is still a demonstration value.
Build
Technical implementation
- Static build
- Eleventy 3 and Nunjucks with hand-written CSS, no framework. At the 9 Sep 2026 QA: CSS 11.5 KB, JavaScript 1.8 KB, four font files 32 KB (gzipped).
- Strict CSP
- Inline style attributes reduced from 241 to 0, so the live policy runs style-src 'self' without 'unsafe-inline': 0 inline styles and 0 CSP violations on the 11 live routes.
- Images
- AVIF with JPG fallback in <picture>, preloaded hero image; largest hero 38 KB, largest content image 24 KB.
- SEO
- Unique titles (50–60 characters) and descriptions (140–158) on 11 pages, JSON-LD, canonical URLs, an 11-entry sitemap and a custom 404. Noindex in the meta tag, robots.txt and response header, because the business is fictional.
- Hosting
- Cloudflare Pages: HTML revalidates on every request, hashed assets are cached for a year, AVIF is served as image/avif.
Measured
Measured performance
Live demo
Lighthouse 13.4.1, mobile (simulated throttling) unless noted, on ironvale-heating.pages.dev, 11 Sep 2026 (UTC). Home mobile is the median of 3 runs; other rows are single runs. Recorded in the portfolio hub QA.
| Page | Performance | Accessibility | LCP | CLS |
|---|---|---|---|---|
| Home, mobile | 100 | 100 | 1.37 s | 0 |
| Home, desktop | 100 | 100 | 0.39 s | 0.003 |
| Services, mobile | 100 | 100 | 1.04 s | 0 |
| Service areas, mobile | 100 | 100 | 1.04 s | 0.015 |
Project QA
Ironvale QA, 9 Sep 2026: Lighthouse 13.4.1 mobile on a local serve of each build; home median of 3 runs, other pages single runs.
| Build | Performance | Accessibility | LCP | CLS |
|---|---|---|---|---|
| Static | 99–100 | 100 (10 pages) | 1.50–2.10 s | 0 (area table 0.015) |
| WordPress (local) | 99–100 | 100 (10 pages) | 1.22–1.82 s | 0 |
Also verified on production
- 10 Sep 2026 (project acceptance): 11 of 11 routes return 200, a missing page returns 404, 0 CSP violations, 0 console errors, AVIF served as image/avif.
- 11 Sep 2026 (UTC, hub re-check): 11 routes 200, 404 for a missing page, 11 sitemap entries, 0 inline styles, noindex in meta, robots.txt and header.
- Lighthouse SEO 66–69 is intentional: the demo of a fictional business is noindex.
Not claimed
- Business results of any kind: the business is fictional.
- Real-device, screen-reader or axe testing (not done for this project).
- Performance of the WordPress build on real hosting: it runs on a local instance only.
Accessibility
Accessibility testing performed
What was tested, and how. Nothing here is a conformance certification.
- Lighthouse accessibility 100 on 10 pages in both the static and WordPress builds (local, 9 Sep 2026), and 100 on the three live pages re-checked on 11 Sep 2026 (UTC).
- Contrast: two failures found by measurement and fixed before launch: footer secondary text (2.98:1, now 8.97:1) and the track panel labels (1.71–2.75:1).
- Keyboard: the primary call button is reached in 3 Tab presses with a visible focus ring; all 8 phone links on the home page are focusable; the menu drawer traps focus, closes on Esc and returns focus.
- Structure: landmarks, one H1 per page, skip link, no image without alt text.
- Reduced motion: covered by a global rule over the site's 6 remaining transitions; verified by reading the CSS, not with the operating-system setting.
Not done axe was not run on this project, and there has been no screen-reader or real-device pass.
Platform
Static and WordPress
The same content model also runs as a classic WordPress theme using Secure Custom Fields for an options page and repeatable content. Phone, hours, address, service areas, arrival targets, warranty terms, plans, FAQs, reviews and SEO fields are editable in the admin.
- Tested as an Editor-role user: changing the phone number, arrival times, warranty length and service-area list updated every rendered instance.
- Page headings match the static build on every page; Contact differs by design, because the WordPress edition has no form.
- Lighthouse on the local WordPress instance: Performance 99–100, Accessibility 100.
Scope The WordPress edition runs on a local instance (WordPress 7.1, PHP 8.2) and is not deployed. A real deployment would need a form plugin and SMTP.
Credits
Credits and disclosure
- Concept project. The business, its reviews, credentials, arrival targets and warranty are fictional.
- Photography: AI-generated for this project (OpenAI GPT image model), with the model and date recorded per file.
- Fonts: Archivo and Source Sans 3 (SIL Open Font License 1.1). Icons: Lucide (ISC).