Business Demo · HTML5 / Tailwind CSS / Vanilla JS

PrairiePro Book

A fully self-contained Calgary home-service booking demo — transparent pricing, professional team profiles, and a Calendly-style multi-step booking wizard. Zero dependencies. One file.

完全自包含的卡加利家居服務預約示範——透明定價、專業團隊介紹,以及 Calendly 風格的多步驟預約精靈。零依賴,單一檔案。

#HTML5 #TailwindCSS #VanillaJS #localStorage #BookingWizard #Calgary #ZeroDependencies
Visual Walkthrough

Interactive Preview

The full booking flow runs live below — try selecting a service, picking a date, and completing a reservation.

Try the Demo

Open the demo on a larger screen to experience the full interactive booking flow — or tap the link below.

互動預覽

完整預約流程在下方即時運行——試著選擇服務、選取日期,完成一次預約。

體驗示範

在較大螢幕上開啟示範,體驗完整互動預約流程——或點擊下方連結。

01 · Context & Problem

Local home-service businesses lose clients the moment they say "call us to book."

本地家居服務業者一說「請來電預約」,就已流失了客戶。

Most Calgary tradespeople — electricians, plumbers, cleaners, HVAC technicians — have no real online booking presence. Their digital footprint ranges from a Facebook page to a static website with a phone number buried in the footer. When a homeowner needs a service at 10pm on a Sunday, "we'll get back to you next business day" is a conversion killer. The competitor with a real booking flow wins, every time.

大多數卡加利的技工——電工、水喉匠、清潔工、暖通空調技術人員——沒有真正的線上預約管道。他們的數位存在從 Facebook 頁面到只有電話號碼的靜態網站不等。當屋主在週日晚上 10 點需要服務時,「我們下個工作日回覆您」是拒客的殺手。擁有真實預約流程的競爭對手每次都會勝出。

Off-the-shelf solutions like Calendly or Square Appointments work, but they strip away brand entirely and offer no control over how pricing or team credentials are presented. A bespoke demo gives a business owner a tangible vision — something they can click through, share with clients, and say "this is exactly what I want."

Calendly 或 Square Appointments 等現成方案有效,但它們完全剝奪了品牌,且無法控制定價或團隊資質的呈現方式。一個客製化示範為業主提供了切實的願景——他們可以親自點擊體驗、向客戶展示,並說:「這正是我想要的。」

The Brief
設計要求

Build a conversion-first home-service booking demo that: (1) surfaces transparent, per-service pricing — no "call for a quote" friction; (2) builds trust with professional team profiles and star ratings before the first visit; (3) delivers a booking UX that matches Calendly quality — multi-step, mobile-first, with visual progress tracking; (4) persists confirmed bookings client-side so appointment confirmations survive page reloads; and (5) ships as a single, self-contained HTML file — instantly shareable, zero infrastructure required.

建立一個以轉化為首要目標的家居服務預約示範:(1) 顯示透明的按服務定價——消除「請來電查詢」的摩擦;(2) 通過專業團隊介紹和星級評分,在首次上門前建立信任;(3) 提供與 Calendly 媲美的預約體驗——多步驟、手機優先、帶視覺進度追蹤;(4) 在客戶端持久化確認預約,確認信息在頁面刷新後仍然存在;(5) 以單一自包含 HTML 檔案交付——即時可共享,零基礎設施。

02 · Tech Deep Dive

Zero dependencies. Full Calendly-style UX. One file.

零依賴。完整 Calendly 體驗。單一檔案。

The entire application — pricing grid, team profiles, interactive booking wizard, appointment persistence, and confirmation screen — ships as a single HTML file. No npm install, no build step, no server. Tailwind's utility classes are loaded via CDN, and every interaction is handled by vanilla JavaScript.

整個應用——定價網格、團隊介紹、互動預約精靈、預約持久化和確認畫面——以單一 HTML 檔案交付。無需 npm install,無構建步驟,無伺服器。Tailwind 工具類通過 CDN 加載,所有互動均由原生 JavaScript 處理。

// Multi-Step Booking Wizard
// 多步驟預約精靈

A 4-step flow: Service Selection → Date & Time → Contact Details → Confirmation. Each step is a distinct view rendered by a central JS state object. The progress stepper reflects the active step; clicking a completed step jumps back to it; the Back button correctly restores all previous selections.

4 步驟流程:服務選擇 → 日期與時間 → 聯絡資料 → 確認。每個步驟都是由中央 JS 狀態物件渲染的獨立視圖。進度步進器反映當前步驟;點擊已完成步驟可跳回;返回按鈕正確還原之前所有選擇。

// Simulated Availability Engine
// 模擬可用時段引擎

A seeded pseudo-random function — keyed to the selected date's numeric value — deterministically marks 30–40% of time slots as unavailable. The same date always shows the same availability across all page reloads and sessions, without any backend or API call.

以選定日期數值為種子的偽隨機函數,確定性地將 30–40% 的時段標記為不可用。同一日期在所有頁面刷新和會話之間始終顯示相同的可用性,無需任何後端或 API 調用。

// localStorage Persistence
// localStorage 持久化

Every confirmed booking is serialised to localStorage under a unique booking ID. The confirmation screen — service type, technician name, date, time, and reference number — survives page reloads. A returning visitor sees their prior booking until they explicitly cancel it.

每個確認的預約均以唯一預約 ID 序列化到 localStorage。確認畫面——服務類型、技術人員姓名、日期、時間和參考號碼——在頁面刷新後仍然存在。回訪者將看到之前的預約,直到明確取消。

// Selectable Card Pattern
// 可選卡片模式

Services, date cells, time slots, and team members all use a consistent "selectable card" pattern: styled <div>s with data-value attributes, a toggled .selected class on click, and keyboard-accessible focus rings — eliminating the cross-browser styling inconsistencies of native radio inputs.

服務、日期格、時段和團隊成員均使用一致的「可選卡片」模式:帶 data-value 屬性的樣式 <div>,點擊時切換 .selected 類,以及鍵盤可訪問的焦點環——消除原生 radio 輸入的跨瀏覽器樣式不一致。

// Tailwind via CDN
// Tailwind CDN 部署

The full design system — spacing, typography, colour palette, shadows, and responsive breakpoints — is driven by Tailwind utility classes loaded from a single CDN <script> tag. No PostCSS, no purging, no pipeline. The tradeoff (~400 KB stylesheet) is acceptable for a demo that prioritises shareability over raw performance.

整個設計系統——間距、排版、色彩、陰影和響應式斷點——由單一 CDN <script> 標籤加載的 Tailwind 工具類驅動。無 PostCSS,無清除,無構建流程。權衡(約 400 KB 樣式表)對於優先考慮可共享性的示範是可接受的。

// Demo Mode Banner
// 示範模式橫幅

A sticky teal banner at the top of the page explicitly labels the site as a portfolio demo, prevents confusion about whether it is a live business, and links back to the portfolio. Standard practice for client-facing demos that may be shared publicly without context.

頁面頂部的固定青色橫幅明確標示網站為作品集示範,防止對其是否為真實業務的混淆,並連結返回作品集。這是可能在無背景情況下公開分享的示範的標準做法。

HTML5 Tailwind CSS Vanilla JS localStorage API Seeded LCG Multi-step Wizard CSS Grid Responsive Design
Hardest Part — Making simulated availability feel deterministically real
最難的部分——讓模擬時段在每次刷新後保持一致

Without a backend, time slot availability resets on every page load — which immediately breaks the illusion when a user refreshes the page. The naive fix, Math.random(), generates a different set of available slots every time, making the simulation obvious. The fix is a seeded Linear Congruential Generator (LCG) — a deterministic pseudo-random function that produces the same sequence of numbers for any given seed:

沒有後端,時間段可用性在每次頁面加載時重置——用戶刷新時立即打破幻覺。簡單的修復方法 Math.random() 每次生成不同的可用時段,使模擬顯而易見。解決方案是帶種子的線性同餘生成器(LCG)——一種確定性偽隨機函數,對任何給定種子都能生成相同的數字序列:

function seededRandom(seed) { let s = seed; return () => { s = (s * 1664525 + 1013904223) & 0xffffffff; return (s >>> 0) / 0xffffffff; }; } // Seed = selected date as a single integer const seed = year * 10000 + month * 100 + day; const rand = seededRandom(seed); // Same date → same "booked" slots, every reload slots.forEach(slot => { slot.available = rand() > 0.35; // ~65% available });

The seed is derived from the date itself: year × 10000 + month × 100 + day. This means 20 May always shows the exact same availability pattern, while 22 May shows a different-but-equally-stable one — consistent across every reload and session, without a single API call. The calendar looks live, plausible, and completely convincing to a first-time visitor.

種子來自日期本身:year × 10000 + month × 100 + day。這意味著 5 月 20 日始終顯示完全相同的可用性模式,而 5 月 22 日則顯示不同但同樣穩定的模式——在每次刷新和會話中保持一致,無需任何 API 調用。日曆看起來真實、合理,對首次訪客完全具說服力。

Want to see it live?

想親自體驗?

The demo runs entirely in your browser — no backend, no signup required. Try the full 4-step booking flow, explore the simulated calendar, and complete a reservation. Want this adapted for your own business? Let's talk.

示範完全在瀏覽器中運行——無後端,無需註冊。試用完整的 4 步驟預約流程,探索模擬日曆,完成一次預約。想將此定制為您的業務?歡迎聯絡。