initial commit
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
# Website Blueprint Template
|
||||
|
||||
Use this format when designing a website before building. Write the blueprint first, then build from it.
|
||||
|
||||
---
|
||||
|
||||
## Business Context
|
||||
- **Client:** [name]
|
||||
- **Business:** [type — HVAC, handyman, plumbing, etc.]
|
||||
- **USPs:** [2-4 unique selling propositions — 24/7, family-owned, flat-rate, fast response]
|
||||
- **Service Areas:** [city list]
|
||||
|
||||
## URL Structure
|
||||
```
|
||||
/ → Homepage
|
||||
/service-a → Service page A
|
||||
/service-a/landing → Standalone landing page (for ads)
|
||||
/service-b → Service page B
|
||||
/contact → Contact / Quote Request
|
||||
```
|
||||
|
||||
## Technology
|
||||
- Static HTML/CSS/JS — no backend
|
||||
- Form submissions via Web3Forms (free tier)
|
||||
- No frameworks, no build step
|
||||
- System font stack, CSS custom properties
|
||||
|
||||
---
|
||||
|
||||
## Per-Page Blueprint
|
||||
|
||||
For each page, list sections top-to-bottom with:
|
||||
- Section name
|
||||
- Visual elements (icons, photos, layout)
|
||||
- Copy and headlines (with SEO keywords)
|
||||
- CTAs (buttons, phone links)
|
||||
- Psychology notes (what the visitor feels, what they need to see)
|
||||
|
||||
### Global Header
|
||||
```
|
||||
Layout: Logo | Nav links | Phone | CTA button
|
||||
Mobile: Logo | Hamburger + sticky tap-to-call bar below
|
||||
```
|
||||
|
||||
### Global Footer
|
||||
```
|
||||
Layout: Brand | Service links | Contact | Service areas
|
||||
SEO: All service area cities in footer on every page
|
||||
```
|
||||
|
||||
### Page: [Name]
|
||||
#### Section 1: Hero
|
||||
- Headline: [H1 with primary keyword + city]
|
||||
- Subcopy: [1-2 lines]
|
||||
- CTAs: [primary CTA, secondary CTA]
|
||||
- Psychology: [what the visitor is thinking]
|
||||
|
||||
#### Section 2: [Name]
|
||||
- ...
|
||||
|
||||
[Repeat for all sections]
|
||||
|
||||
---
|
||||
|
||||
## Color Palette
|
||||
|
||||
| Role | Color | Usage |
|
||||
|------|-------|-------|
|
||||
| Primary | hex | primary CTAs, brand elements |
|
||||
| Secondary | hex | secondary CTAs |
|
||||
| Emergency | hex | urgency CTAs if applicable |
|
||||
| Dark | hex | body text, dark sections |
|
||||
| Light | hex | page background |
|
||||
|
||||
## Implementation Priority
|
||||
1. Global shell (header, footer, CSS, JS)
|
||||
2. [Highest-conversion page]
|
||||
3. [Contact form]
|
||||
4. [Homepage]
|
||||
5. [Remaining service pages]
|
||||
6. Polish (schema, meta, images)
|
||||
@@ -0,0 +1,135 @@
|
||||
# Contact Form Pattern (Web3Forms)
|
||||
|
||||
Static-site contact form that emails the business owner directly. No backend, no calendar, no payment.
|
||||
|
||||
## Form Fields
|
||||
|
||||
Keep it SHORT. Every extra field costs conversions.
|
||||
|
||||
| Field | Required | Type | Notes |
|
||||
|-------|----------|------|-------|
|
||||
| Name | Yes | text | `autocomplete="name"` |
|
||||
| Phone | Yes | tel | THE most important field for phone-first businesses |
|
||||
| Email | No | email | Optional — don't require it, it adds friction |
|
||||
| Service Needed | Yes | select | Dropdown with all service types + "Other" |
|
||||
| Project Details | Yes | textarea | 3-line minimum, gives context before callback |
|
||||
| Lead Source | No | select | Google/Facebook/Neighbor/Other — helps measure marketing |
|
||||
|
||||
**Deliberately missing:** address, calendar, file upload, CAPTCHA (add only if spam), budget field.
|
||||
|
||||
## HTML Structure
|
||||
|
||||
```html
|
||||
<div class="form-card" id="quote-form-wrapper">
|
||||
<div class="form-error" id="form-error" role="alert">
|
||||
Something went wrong. Please try again or call us directly.
|
||||
</div>
|
||||
|
||||
<form id="quote-form" novalidate>
|
||||
<!-- fields with .form-group > .form-label + .form-input/.form-select/.form-textarea + .field-error -->
|
||||
<button type="submit" class="btn submit-btn">
|
||||
<span class="btn-text">Get My Free Quote</span>
|
||||
<span class="spinner"></span>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<!-- Success state (hidden by default) -->
|
||||
<div class="form-success" id="form-success">
|
||||
<div class="check-icon">✓</div>
|
||||
<h3>Thanks, <span id="success-name">friend</span>!</h3>
|
||||
<p>We'll reach out within 2 hours.</p>
|
||||
<div class="emergency-note">
|
||||
<strong>🚨 Emergency?</strong> Call now: <a href="tel:...">(XXX) XXX-XXXX</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
## CSS States
|
||||
|
||||
```css
|
||||
.form-input.error, .form-select.error, .form-textarea.error {
|
||||
border-color: var(--color-emergency);
|
||||
box-shadow: 0 0 0 3px rgba(220,38,38,0.1);
|
||||
}
|
||||
.field-error { display: none; }
|
||||
.field-error.visible { display: block; color: var(--color-emergency); }
|
||||
.form-error { display: none; background: var(--color-emergency-light); }
|
||||
.form-error.visible { display: block; }
|
||||
.form-success { display: none; }
|
||||
.form-success.visible { display: block; }
|
||||
.submit-btn.loading .spinner { display: inline-block; }
|
||||
.submit-btn.loading .btn-text { display: none; }
|
||||
```
|
||||
|
||||
## JavaScript
|
||||
|
||||
```javascript
|
||||
var WEB3FORMS_KEY = 'YOUR_ACCESS_KEY'; // Replace with real key
|
||||
var WEB3FORMS_URL = 'https://api.web3forms.com/submit';
|
||||
|
||||
// Validation
|
||||
function validate() {
|
||||
var valid = true;
|
||||
if (!nameField.value.trim()) { showErr('name'); valid = false; }
|
||||
if (!phoneField.value.trim()) { showErr('phone'); valid = false; }
|
||||
if (!detailsField.value.trim()) { showErr('details'); valid = false; }
|
||||
if (!serviceField.value) { showErr('service'); valid = false; }
|
||||
return valid;
|
||||
}
|
||||
|
||||
// Clear errors on input
|
||||
inputs.forEach(function(el) {
|
||||
el.addEventListener('input', function() {
|
||||
el.classList.remove('error');
|
||||
getErrorEl(el.name).classList.remove('visible');
|
||||
});
|
||||
});
|
||||
|
||||
// Submit
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
if (!validate()) return;
|
||||
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.classList.add('loading');
|
||||
|
||||
var payload = new FormData(form);
|
||||
payload.append('access_key', WEB3FORMS_KEY);
|
||||
payload.append('subject', 'New Quote Request — Business Name');
|
||||
payload.append('from_name', 'Business Website');
|
||||
|
||||
fetch(WEB3FORMS_URL, { method: 'POST', body: payload })
|
||||
.then(function(res) { return res.json(); })
|
||||
.then(function(data) {
|
||||
if (data.success) {
|
||||
document.getElementById('success-name').textContent =
|
||||
nameField.value.trim().split(' ')[0] || 'friend';
|
||||
form.style.display = 'none';
|
||||
successDiv.classList.add('visible');
|
||||
} else {
|
||||
throw new Error(data.message || 'Submission failed');
|
||||
}
|
||||
})
|
||||
.catch(function(err) {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.classList.remove('loading');
|
||||
errorBanner.classList.add('visible');
|
||||
console.error('Form error:', err);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
1. Go to https://web3forms.com/ — create free account
|
||||
2. Copy access key
|
||||
3. Replace `YOUR_ACCESS_KEY` in the form script
|
||||
4. Configure destination email in Web3Forms dashboard
|
||||
|
||||
## Testing
|
||||
|
||||
- `file://` CORS blocks fetch() — expected. Test from a real web server.
|
||||
- Click submit with empty fields → validation errors on all required fields
|
||||
- Fill all required fields, submit → loading spinner → success card (if key valid) or error banner (if invalid key)
|
||||
- Success card shows customer's first name + emergency phone fallback
|
||||
Reference in New Issue
Block a user