Executive summary.
FindLaborJobs.com is a national job board for the 12 skilled trades, positioned as the trades-first alternative to white-collar career sites. Workers list and apply for free; employers pay a small per-contact fee to reach vetted candidates, with optional human vetting tiers and featured listings.
This document is the master design specification, authored before build, organised as the artefacts a full-service UX engagement ships: a Flow Strategist brief (who, why, what), a Wireframe Architect blueprint set (14 screens, mobile-first, grayscale), a Hi-Fi Prototype pass (key screens rendered in the product's real visual language), a Content & SEO Strategy (the editorial engine that drives organic growth), and a Spec Engineer handoff (tokens, atomic inventory, accessibility, Developer Deployment Code).
It defines every decision the implementation team is being asked to honour — and is intentionally written so a third-party developer, marketer, or future hire can pick it up and execute without a single clarifying meeting.
- 01Map the thinking. JTBD personas, empathy maps, behavioural analysis and a Mermaid flow that names every screen and every decision node.
- 02Validate the layout. Grayscale wireframes at 375 px with desktop expansion notes, Nielsen heuristics annotated inline.
- 03Show the real thing. Key screens rendered hi-fi in the product's actual colour, type and imagery language — what a stakeholder or investor sees.
- 04Plan the growth engine. A content and SEO strategy that turns the site's information architecture into a compounding organic-traffic asset.
- 05Hand it off. Atomic Design inventory, design tokens, component states, A11y checklist and copy-paste Deployment Code.
Tom (owner/operator), next-session designers & developers, contracted reviewers, and any future hire.
Stakeholder brief + competitor scan (Indeed, ZipRecruiter, niche trades boards) + cross-reference of all eight UX Designer references. Deep mode.
[VALIDATED] on live behaviour, DB schema, business model. [INFERRED] on user motivations (no formal research). [ASSUMED] tagged where applicable.
Visual brand refresh, paid-marketing creative, content / SEO calendar, and PHP/JS implementation details (those belong in the developer handoff that follows).
Flow strategist.
Before any screen exists, this section answers who the product is for, what job they hire it to do, and which behavioural levers the experience pulls. The output is a problem statement and a master flowchart that every wireframe in Mode 2 traces back to.
Strategy check (Lean Canvas, 1-screen).
Business context is well-defined from the founder brief, so the canvas is filled rather than diagnosed. [VALIDATED] with the product owner.
General job boards bury blue-collar postings under tech and office roles. Workers waste hours filtering. Local contractors can't find vetted help fast.
Trade-first listings (12 trades × 50 states). Free for workers. Pay-per-contact for employers. Optional human vetting.
The Job Board Built for Trades. Not Office Workers.
Aggregated supply (JSearch + Adzuna feeds) avoids the cold-start that kills most niche marketplaces — critical-mass listing volume is available from day one.
Solo-owner unit economics. SEO moat: programmatic landing pages for every trade × state combo (~600 URLs in GSC).
Programmatic SEO landing pages (trade × state, ~600 URLs), weekly blog (3 posts / week cadence), Reddit Ads on trade subreddits, word of mouth.
Skilled tradesperson (worker). Hiring contractor / GC office manager (employer).
Worker registrations, employer contact-unlocks, vetting purchases, organic landing-page traffic.
RapidAPI / Adzuna feeds, WP hosting, Reddit ad spend, occasional dev contractor.
$5 contact unlock · $5 featured · $29 / $149 mo plans · $29 / $49 / $149 vetting tiers.
// BUSINESS-CONTEXT.md § Lean Canvas (Maurya, 2010). Filled in deep mode.
JTBD personas — two-sided marketplace.
Per Christensen, archetypes are framed by the job hired, not demographics. Two archetypes drive the marketplace; a third (Admin) is operational and covered briefly. [INFERRED] from founder domain knowledge and trade-subreddit reading; recommended to validate with 5–8 user interviews using the JTBD switch-interview method (USER-RESEARCH § 1.6) before build.
When: Current job is wrapping up, weather slowed the site, or a buddy mentioned better pay 90 minutes away.
Where: On a phone, in a truck cab, on a lunch break. Spotty signal. One thumb.
Frequency: 2–6× per year actively, weekly browse-check in between.
When my current gig is winding down, I want to scan trade-specific openings in my state without sifting through office jobs, so I can line up the next paycheck before this one ends.
- Functional: Find paying openings near me, in my trade, this week.
- Emotional: Feel respected — not lectured by a "career platform."
- Social: Be seen as a working pro by the GCs who'll hire him.
Today: Indeed (too broad), Facebook trade groups (gatekept), word of mouth.
Pain: Office-job spam, "easy apply" black holes, recruiters with no idea what a journeyman card is.
Workarounds: Texts buddies; calls union hall; drives by job sites looking for trailers.
Phone rings within 72 hours of applying. Pay rate visible before he applies. Drive time under one hour.
Time: 5-minute window before the next pour.
Skills: Confident on phone, allergic to forms.
Access: Mid-tier Android, data-capped plan, gloves on half the time.
When: Crew member quit Friday. New project starts Monday. The job is bid, not budgeted for delays.
Where: Desk in a job-trailer or kitchen table, laptop. Sometimes phone.
Frequency: Cyclical — 3–10× per year, clustered around project starts.
When I lose a crew member or land a new project, I want to talk to a real, qualified tradesperson today, so I can keep the schedule and not eat liquidated damages on Monday.
- Functional: A short-list of contactable, vetted, in-trade workers.
- Emotional: Confidence the person who picks up isn't a no-show.
- Social: Look organised to the project owner who's watching.
Today: ZipRecruiter ($$$), staffing agencies (markup), referrals (slow).
Pain: Subscription bait-and-switch, fake resumés, ghosting.
Workarounds: Pays day-labor cash; poaches from rival jobsites.
Worker on site within a week. Spend under $200. No hidden subscriptions.
Money: Per-hire budget, not retainer. Trust: Won't pay for a "lead" without seeing something concrete. Time: Will not do a sales call.
Archetype C · Operational — Tom (admin)
Empathy maps.
Per Gray (XPLANE, 2010). One map per archetype. The gap between Says and Does is where most marketplace design insights live.
- "I'm not paying to apply for a job."
- "Just put the pay rate on it."
- "If they want me, they'll call."
- This site looks built by a recruiter, not a worker.
- If I have to upload my licence, this is a scam.
- One-hour drive max, two is for emergencies.
- Skims 8–12 cards in 90 seconds, bails on long pages.
- Calls before applying when a phone number is visible.
- Saves jobs to "look later" but rarely returns.
- Tired — long day, small screen.
- Skeptical — burned by Indeed inbox spam.
- Pragmatic — money is the metric.
- Office jobs cluttering trade searches
- Forms that demand a resume PDF
- "Sign up to see pay rate" gates
- Pay-rate visible on card
- Phone call from a real foreman
- Saved profile that reuses on apply
Marcus will trade more profile detail for fewer office-job impostors. Specificity is the value, not features.
- "I just need someone who shows up."
- "How much is it to actually talk to them?"
- "No, I don't want a monthly plan."
- If I'm paying, this person better be real.
- Vetting is worth it on a $30K bid, not a $300 patch.
- If I unlock and they ghost, I'm done with this site.
- Sorts candidates by vetted first, then closest.
- Reads photos and resume before name.
- Buys vetting only on key roles.
- Squeezed — schedule pressure from the GC above.
- Wary — burned by paid resume-databases.
- Decisive — once he sees a fit, he calls.
- "Premium plan required" walls
- Hidden contact info after payment
- Stale profiles, candidates not really looking
- Phone number revealed instantly post-payment
- Vetting badge meaning something concrete
- One-click re-list of a popular job
Tony is willing to pay per outcome (each contact) but allergic to pay per access (subscription). Pricing model is the product.
Problem statements.
Marcus needs a way to see real trade openings in his state on his phone without paying, signing up, or filtering past office jobs — because every minute spent searching is a minute not earning.
Tony needs a way to reach a vetted tradesperson within hours, paying only for the contact he uses — because subscription job boards are over-priced for his cadence and under-trusted for his trades.
Behavioural analysis.
Applied stack: Fogg B = MAP for diagnosing each key action, Eyal Hook Model for engagement loops, Thaler & Sunstein for the few defensible nudges. Cialdini patterns called out where used.
a · Fogg B = MAP per key action
| Action | Motivation | Ability barrier | Prompt type | Design response |
|---|---|---|---|---|
| Worker registers | Anticipation (next paycheck) | Form length, social deviance ("am I on a corporate platform?") | Spark — value-prop hero, Reddit ad | 3-field signup, then progressive profile. Trade-coded language. |
| Worker applies | Anticipation + Pleasure (close to outcome) | Effort — re-typing on a phone | Signal — Apply button on detail | Reuse profile fields, optional resume re-upload, single-screen form. |
| Employer posts a job | Pain avoidance (project deadline) | Mental effort — writing the post | Facilitator — AI job description writer | Anthropic-generated draft, all fields pre-filled, document upload optional. |
| Employer unlocks contact | Pain avoidance + Pleasure (hire close at hand) | Money — $5; Time — paypal redirect | Signal — locked contact strip on profile | Reveal everything except contact. One CTA. Receipt + redirect back to profile. |
| Employer buys vetting | Anticipation (low-risk hire) | Money — $29–$149; Trust in deliverable | Facilitator — tier comparison on profile | Three tiers visible at decision point, report sample, "always active" caption. |
// BEHAVIORAL-DESIGN § 1 (Fogg, 2009 / "Tiny Habits" 2020)
b · Hook cycle (Eyal, 2014)
External: Reddit ad in r/Construction, SEO landing for "electrician jobs texas 2026".
Internal: "Job's ending." (worker) · "Crew is short." (employer)
Single-tap on a job card. Single CTA on hero. Apply / Unlock buttons designed as the only obvious action above the fold.
Hunt: fresh jobs every 30 min (cron). Tribe: employer call-backs. Self: profile completion %.
Profile fields, photos, resume, saved jobs. Each one increases switching cost and quality of future hunt-rewards.
// BEHAVIORAL-DESIGN § 2. Manipulation matrix: Facilitator quadrant — engagement design is ethically grounded because the product genuinely improves both users' lives.
c · Nudge inventory
| Nudge | Where | Purpose | Ethical check |
|---|---|---|---|
| Default trade = "All" | Browse jobs | Reduce empty-state on landing | ✓ Easily overridden |
| Smart default: state from IP | Browse / SEO landing | Local relevance, System 1 | ✓ Editable, visible |
| Social proof: "17,983 jobs" | Home hero, footer | Marketplace liquidity signal | ✓ Real count (Cialdini) |
| Scarcity: "Expires in 5 days" | Job card, detail | Real expiry from DB | ✓ Real, not fabricated |
| Progress: "Profile 60% complete" | Seeker dashboard | Endowed-progress (Nunes/Dreze) | ✓ Honest % |
| Anchoring: $149 tier first | Vetting modal | Make $29 feel modest | ⚠ Test post-purchase satisfaction |
| Loss framing: "Don't lose your spot" | Not used | — | Avoid; tradespeople distrust urgency copy |
// BEHAVIORAL-DESIGN § 3 (Thaler & Sunstein) + UX-PATTERNS § 3 (Cialdini)
d · Dual-process (Kahneman) targeting
Job browse, card scanning, hero CTA, trade-icon recognition, blog reading. Design: large type, familiar conventions, high-contrast trade chips.
Vetting tier choice, post-a-job form, profile completion. Design: labels + helper text, comparison tables, autosave, no time pressure.
Journey maps.
Two journeys cover ~80% of the marketplace volume. Phase library per USER-RESEARCH § 3.4. Emotional curve is the design priority signal: invest where the dip is steepest.
| Phase | Aware | Browse | Register | Apply | Wait | Call-back |
|---|---|---|---|---|---|---|
| Touchpoint | Reddit ad, Google "[trade] jobs [state]" | /#/jobs |
/#/register |
/#/apply?id= |
Inbox / phone | Phone call |
| Action | Tap ad on phone | Filter trade + state, scroll cards | Pick role = Worker, 3 fields | Tap Apply, confirm | Refreshes occasionally | Answers, agrees to interview |
| Thought | "Real trades site?" | "Are these actual jobs near me?" | "Why do they need my photo?" | "Should be quick…" | "They never call back." | "OK, this works." |
| Emotion | Curious · 3/5 | Hopeful · 4/5 | Wary · 2/5 | Determined · 3/5 | Doubtful · 2/5 | Relieved · 5/5 |
| Pain | — | Pay-rate sometimes missing | Resume upload feels heavy | Field count uncertainty | No status / SMS | — |
| Opportunity | Pin Reddit moderator AMA | Default state by geo-IP; show "pay TBD" honestly | Defer resume to post-signup | Progress bar (1 of 2) | "Employer viewed your application" event email | Encourage rate review |
| Phase | Aware | Search | Evaluate | Pay | Contact | Hire |
|---|---|---|---|---|---|---|
| Touchpoint | SEO landing for "find a welder [state]" | /#/search-candidates | Worker profile (locked) | PayPal redirect | Worker profile (unlocked) | Phone, jobsite |
| Action | Clicks "Hire a [trade]" | Filters trade + state, sorts vetted-first | Reads photos, resume, work history | $5 to TAGrafx, returns to site | Calls, texts, emails | Books worker |
| Thought | "Specialised, hmm." | "Plenty of options." | "Real person?" | "$5 only? Sure." | "Phone is live." | "Good crew." |
| Emotion | Skeptical · 3/5 | Engaged · 4/5 | Discerning · 3/5 | Cautious · 2/5 | Pleased · 4/5 | Relieved · 5/5 |
| Pain | — | "Are these stale?" | Locked contact strip | PayPal exit + manual reconcile | None if worker answers | — |
| Opportunity | Trade-specific landing pages (done) | "Last seen" badge on profile | Sample vetting report preview | Stripe webhook → auto-unlock (parked) | "Did they pick up?" 1-tap NPS | Re-list / vet again CTA |
status = 'paid'). This is the single highest-leverage technical investment on the roadmap — and the strongest argument for prioritising the Stripe / PayPal webhook work that's currently parked.Information architecture.
Two surfaces: a hash-routed SPA for logged-in app behaviour, and a server-rendered SEO layer for discovery. They co-exist on the same WordPress and are intentionally distinct so Divi/JS conflicts can't break either one.
/#/home Hero · trades · how-it-works · blog /#/jobs Filterable list (1,000 init, fallback fetch) /#/job-detail?id= Detail · apply CTA · save /#/apply?id= Single-screen application /#/about Pricing & how-it-works /#/register Role chooser → worker | employer /#/login Async login, async logout (WPS guard) /#/profile Seeker dashboard /#/employer-dashboard Employer home /#/post-job Post / edit a job /#/search-candidates Candidate browser /#/worker-profile?id=Worker detail (locked / unlocked) /#/saved-jobs /#/applications /#/admin Admin shortcuts /#/blog Blog index (delegates to WP)
/ SPA root
/jobs/{trade}/ Trade landing (12)
/jobs/{state}/ State landing (50)
/jobs/{trade}/{state}/ Trade × state (600+)
/job-directory/ HTML sitemap, all combos
/sitemap-jobs.xml XML sitemap (~662 URLs)
/sitemap-jobs/ Non-extension fallback
/blog/ WP posts page
/blog/{slug}/ WP post
/about, /contact, … Divi-managed
/flj-admin-2026 WP admin (WPS Hide Login)
// Architecture corresponds to Garrett's 5-planes — surface (Divi + SPA), skeleton (hash + REST), structure (role-gated routes), scope (pricing + vetting features), strategy (two-sided marketplace).
Master flowchart.
Double-Diamond Deliver output. Entry points (Reddit / SEO / direct), the two main loops (apply, unlock), all decision nodes (auth, role, unlock fee toggle, vetting tier), and success / failure terminals. Each rounded node maps 1:1 to a wireframe in §2.
flowchart TD
%% Entries
R([Reddit ad]):::ent --> H
G([Google / SEO]):::ent --> L[/SEO landing/]
D([Direct]):::ent --> H[Home]
L -->|"CTA: View jobs"| H
%% Public browse
H --> J[Browse jobs]
J -->|filters| J
J --> JD[Job detail]
JD -->|Save| AUTH1{Logged in?}
JD -->|Apply| AUTH1
AUTH1 -- No --> RG[Register]
AUTH1 -- Yes --> APP[Apply form]
RG --> ROLE{Role?}
ROLE -- Worker --> WP1[Seeker profile setup]
ROLE -- Employer --> EP1[Employer profile setup]
WP1 --> APP
%% Worker apply loop
APP --> APPOK[/Applied · toast/]:::ok
APPOK --> DASH_S[Seeker dashboard]
DASH_S -->|Saved jobs| J
DASH_S -->|Edit profile| PROF_S[Worker profile editor]
%% Employer demand loop
EP1 --> DASH_E[Employer dashboard]
DASH_E --> POST[Post a job]
POST -->|AI assist| POST
POST --> POSTOK[/Job live/]:::ok
DASH_E --> SC[Search candidates]
SC -->|Filters| SC
SC --> WPROF[Worker profile · LOCKED]
WPROF --> FEE{Contact unlock fee?}
FEE -- ON --> PAY[PayPal $5]
FEE -- OFF --> UNLOCKED[Worker profile · UNLOCKED]
PAY --> ADMIN_REC{Admin marks paid}
ADMIN_REC --> UNLOCKED
UNLOCKED --> VET{Buy vetting?}
VET -- No --> CALL([Call / text / hire]):::end_
VET -- Yes --> VTIER[Choose tier $29/$49/$149]
VTIER --> PAY2[PayPal]
PAY2 --> REPORT[/Vetting report PDF/]:::ok
REPORT --> CALL
%% Admin
SET([Admin · Settings]):::ent --> TOGGLE[Toggle: Contact Unlock Fee]
TOGGLE --> FEE
%% Failures
AUTH1 -- "session expired" --> LOGIN[Login]
LOGIN --> AUTH1
classDef ent fill:#fff,stroke:#1a1a1a,stroke-width:2px;
classDef ok fill:#f1ede4,stroke:#1a1a1a,stroke-width:2px,color:#1a1a1a;
classDef end_ fill:#efece5,stroke:#1a1a1a,stroke-width:2px;
// UX-FOUNDATIONS § 3.1 (Design Council Double Diamond). Auto-rendered with Mermaid 11.
Wireframe architect.
14 screens, mobile-first at 375 px, strict grayscale (slate-100 → slate-900), borders define hierarchy, no shadows, no real imagery. Each frame is annotated with pattern callouts in monospace and flagged against Nielsen's 10. Microcopy intent is in [brackets]; final copy is finalised in Mode 3.
Home — SPA landing.
/#/home · page-jll-app.php · pages-public.js · renderHome()
Purpose. Within 5 seconds, a worker on a phone should understand "this is for me" and tap into Browse Jobs; an employer should see "Hire" path and pricing. Hero, trade chips, social proof count, blog teaser, footer.
[HEADLINE — value prop, 6–9 words]
[Subhead · 1 line of clarification]
★ Join [17,000+] tradespeople hiring this week
- 01.[Step copy · free for workers]
- 02.[Step copy · search by trade + state]
- 03.[Step copy · employer pays only to talk]
/jobs/{trade}/ server-rendered landing — System 1 scan path.<a>. The site's global CSS overrides anchor backgrounds, so anchor cards render without their surface fill.PageVisit on every load. SignUp on register success.Desktop expansion (≥ 768 px)
- Header: logo · Browse · Hire · Pricing · Blog · Login · Sign Up (anchor-tags only, never
<button>). - Hero: 2-column with placeholder portrait (slate-300 box "[WORKER PHOTO]") right.
- Trade grid: 6 columns × 2 rows.
- Blog: featured-left + 3 sidebar-right (matches current implementation).
Heuristics check
- H1 Visibility: ✓ jobs count is honest live data
- H2 Match real world: ✓ "trade" not "industry"
- H6 Recognition: ✓ trade chips use familiar trade names
- H8 Minimalist: ⚠ resist adding a fourth hero CTA — already on the line
Browse jobs.
/#/jobs · initial fetch 1,000 · fallback GET /jobs/{id}
Purpose. Marcus scans 8–12 cards in 90 seconds. Trade + state filters above the fold; card density tuned for thumb scroll. Empty / loading / "no results" states all spec'd.
localStorage so refresh keeps it.jll_jobs.expires_at. Real, not theatrical.Empty & error states
No results for filters: Title: "No [trade] jobs in [state] right now" Body: "Pull-up to refresh. We're adding ~140 jobs a week." Action: "Clear filters" + "Notify me when posted" Network error: Toast: "Couldn't load jobs. Try again." Retry: Auto after 3s, 5s, then manual. Aggregator stale (> 24 h): Banner: "Showing cached jobs."
Desktop expansion
- Two-column: filter sidebar (240 px) + card list. Map preview on right (future).
- Cards keep single-column max-w-2xl — easier scan than grid for trade work.
Job detail.
/#/job-detail?id= · falls back to GET /jobs/{id} if not in SPA cache
[Master Electrician — Commercial fit-out]
[Lorem ipsum dolor sit amet, 4–6 lines of job description. AI-generated when employer used the assistant.]
- [Master licence required]
- [5+ years commercial]
- [OSHA 30]
[Free to apply · Never pay to apply]
history.back() when referrer = jobs list, else navigates to /#/jobs.document_path from jll_jobs. View link opens in new tab.Authenticated variants
- Anon: Apply → Register modal → Apply.
- Worker, applied: CTA = "✓ Applied · view status".
- Employer: CTA = "Edit this job" if owner; otherwise hidden.
Apply.
/#/apply?id= · pages-public.js · renderApply()
[Master Electrician — ACME · Newark NJ]
jllRedditEvent('Lead') fires here.Error states (Nielsen H9)
Phone format wrong "Enter a 10-digit phone, e.g. 555-123-4567" Email format wrong "We can't reach you at this address. Try again?" File > 5 MB "PDF is too big — keep it under 5 MB" Server 500 "Couldn't send. We'll retry — keep this tab open."
Register — role chooser then split flow.
/#/register · pages-auth.js · renderRegister()
Two-step: (1) pick role, (2) minimum-viable form per role. Reddit pixel SignUp splits into SeekerSignUp / EmployerSignUp.
[What brings you here?]
[Subhead · we'll customize the rest]
[Have an account? Log in]
[Create your worker profile]
[Tradespeople list free. No credit card.]
[By signing up you agree to terms · we never sell data]
[Set up your hiring account]
[No subscription. Pay $5 per contact you unlock.]
[Post your first job free for 30 days]
jllRedditEvent('SignUp', {type:'Seeker'|'Employer', email:<hash>}).Login.
/#/login · async fetch · 1200 ms cookie delay · then redirect with ?nocache=…
[Welcome back.]
[Sign in to see new jobs in your trade.]
[Forgot password? · Create account]
window.location.replace(siteUrl + '?nocache=' + Date.now() + '#/route') to bypass page caching, then strip nocache with history.replaceState.CFG.logoutUrl + '?action=logout&_wpnonce='. Never /wp-login.php — WPS Hide Login blocks that endpoint by design.Seeker dashboard / my profile.
/#/profile · pages-dashboard.js · renderSeekerDashboard()
[Add 2 photos and a resume to unlock employer messages.]
Employer dashboard.
/#/employer-dashboard · pages-dashboard.js · renderEmployerDashboard()
jll_worker_unlocks + jll_vetting_requests.Search candidates.
/#/search-candidates · ORDER BY is_demo DESC, then vetted DESC
is_vetted column returns from MySQL as a string ("0"/"1"), so callers must parseInt() before truth-testing. Dashed = unvetted.ORDER BY is_demo DESC. Lets a first-time employer feel marketplace density. Honest because they're labelled — never hide demo state from admins.Worker profile — the unlock moment.
/#/worker-profile?id= · jll_worker_unlocks.status = 'paid' gates everything
The marketplace's single most important screen. Two states (Locked / Unlocked) drive 100% of paying-employer revenue. Plus a third (Free mode, admin toggle off) for the public-relations / demo configuration.
[One-time · no subscription]
[5 years residential service plumbing, NJ master in progress]
jll_contact_unlock_free = 1]$contact_free flag must be threaded through any array_map closure that reads workers (PHP closures need it in use()); otherwise free-mode silently fails to unlock. Free-mode renders both demo seekers identically with the green strip — a single admin toggle flips the whole site for PR demos.Post a job.
/#/post-job · optional editId · AI description via class-jll-ai.php
class-jll-ai.php (Anthropic). Produces a 4-paragraph draft from trade + city + pay range. User edits before publish; never auto-publishes./jll/v1/jobs/{id}/upload → re-save the job with the returned doc_path. This keeps draft jobs valid before any upload attempt.editId URL param arrives as a string and must be parseInt()'d before comparing to numeric job IDs; loaded record fills the same form.Pricing / About.
/#/about · WP page · pricing-table organism
Closes the loop on the employer empathy map: "I don't want a monthly plan." Lead with per-contact; show monthly plans as opt-in upsells.
[Pay only when you talk to a worker.]
[Workers list free. Forever. Employers pay per contact unlock or upgrade if hiring weekly.]
- · [List free]
- · [Apply free]
- · [Get vetted optional]
- · [Post jobs free]
- · [Unlock workers à la carte]
- · [No subscription]
- · [10 contacts incl.]
- · [Featured listings]
- · [Priority support]
SEO landing — /jobs/{trade}/{state}/.
page-jll-landing.php · NO wp_head / wp_footer (Divi breaks it)
600+ programmatic pages indexed in GSC. Goal: rank, convert a tap into Browse or Register. Static-HTML-only — no Divi navigation hijack.
[Electrician Jobs in Texas — 2026]
[2–3 sentence intro · pay range, trend, "post a job" CTA]
[Q · How much do electricians make in Texas?]
[A · range, sources, link to salary blog post]
[Q · What licence do I need in Texas?]
<a>. The parent theme's JS would otherwise hijack link clicks on pages that load it, so landing templates intentionally omit wp_head / wp_footer.rel="next" / rel="prev" for ?page=2, to be lit up once a trade/state combo crosses ~50 listings.Admin — Settings.
/flj-admin-2026 → LaborLink → Settings · class-jll-admin.php
jll_contact_unlock_free) and writes on change — deliberately no "Save Changes" button. Toast on success.Heuristics audit — Nielsen 10.
Severity: 0 = none · 1 = cosmetic · 2 = minor · 3 = major · 4 = catastrophe. Background-checked across all 14 wireframes.
| # | Heuristic | Status | Sev. | Notes / where |
|---|---|---|---|---|
| H1 | Visibility of system status | ✓ Good | 1 | Skeleton loaders on browse. Add toast on apply send. Add unlock-receipt timestamp (done in 2.10). |
| H2 | Match with real world | ✓ Strong | 0 | "Trade" not "industry," "Vetted" not "verified," real city names. Avoid recruiting jargon entirely. |
| H3 | User control & freedom | ⚠ Mixed | 2 | SPA back works; browser back inside hash routing can drop user to /. Spec explicit back affordance on detail screens. |
| H4 | Consistency & standards | ✓ Good | 1 | Card pattern reused across browse / candidates / dashboards. Keep CTA hierarchy (filled / outline / link) global. |
| H5 | Error prevention | ⚠ Watch | 2 | Confirm before PayPal redirect at unlock. Confirm before deactivating a posted job. |
| H6 | Recognition over recall | ✓ Good | 0 | Trade chips, locked-contact glyph, vetted badge — recognisable in < 1 s. |
| H7 | Flexibility & efficiency | ○ Could improve | 2 | Keyboard shortcuts on dashboard (J/K to move through applicants). Admin "Jump to job ID" — spec'd as a Phase 1 quality-of-life feature. |
| H8 | Aesthetic & minimalist | ✓ Good | 1 | Resist a third hero CTA. Cards cap at 6 data points; do not add more. |
| H9 | Help recover from errors | ⚠ Watch | 3 | Login error must not leak existence. Apply 500 should auto-retry, not lose form. PayPal failure path needs "contact support" surface. |
| H10 | Help & documentation | ○ Add | 2 | Help link in footer → consolidated FAQ. Tooltip on vetting tiers. "What's vetted?" caption on candidate cards. |
// NIELSENS-HEURISTICS.md (Nielsen, 1994). Background evaluation per skill rule.
Hi-fi prototype.
Every screen validated in grayscale in §2 is promoted here to full visual fidelity — real colour, real type, real photography direction (placeholder blocks stand in until location photography is shot). Backend administration gets extra depth beyond the single Settings screen wireframed in §2.14, because the tools an owner-operator uses daily deserve the same design rigor as the public product. Tokens applied here are formalised in §5.1; nothing in this section is decorative guesswork.
Every consumer-facing screen from §2 (Home through SEO Landing) is rendered here 1:1 in colour. The admin suite (§3.11) goes further than the single wireframed Settings screen — Jobs Management, Seeker/Employer records, the Vetting Requests queue, the Aggregator monitor, and Transaction History are all specified, because a one-person operation lives or dies on how fast its owner can operate these screens.
Home & browse.
Find work in the trades. Today.
Free for tradespeople. No resume required to start.
★ 17,000+ tradespeople hiring this week
Job detail.
Master Electrician — Commercial Fit-Out
Commercial electrical crew seeking a licensed journeyman for a 6-month build-out in downtown Newark. Day shift, weekly pay, tools provided.
- Journeyman or master licence
- 5+ years commercial experience
- OSHA 30 certified
Free to apply. Always.
Worker profile — the unlock moment.
The highest-revenue screen in the product, rendered hi-fi in both states. Every colour choice here was tested against the behavioural intent set in §1.5 and the states table in §5.3.
One-time. No subscription.
Employer dashboard.
Apply.
Register & login.
What brings you here?
Create your profile
Welcome back.
Forgot password? · Create account
Seeker dashboard.
Search candidates.
Post a job.
Pricing / About.
Pay only when you talk to a worker.
Workers list free, forever.
SEO landing page.
Electrician Jobs in Texas — 2026
Average pay, licensing notes, and open roles across Texas, updated weekly.
Admin suite — backend administration.
The wireframes in §2.14 spec a single Settings screen. In hi-fi, the full admin suite is designed: the operator's daily workspace across jobs, people, vetting, and data pipelines. A one-person operation lives or dies on how fast these screens work — they get the same design attention as the public product, not an afterthought template.
| ☐ | Title | Trade | Status | Expires |
|---|---|---|---|---|
| ☐ | Master Electrician — Newark | Electrical | Active | 5d |
| ☐ | Apprentice Plumber — Jersey City | Plumbing | Active | 12d |
| ☐ | Welder — Camden | Welding | Expired | — |
Content & SEO strategy.
The IA in §1.7 splits the product into an app layer and a discovery layer. This section is the plan for the discovery layer: the editorial engine that turns 600+ programmatic landing pages into a compounding organic-traffic asset, without a paid-media budget doing the heavy lifting.
Content pillars.
Three pillars, each answering a different stage of the worker's job statement (§1.2). No pillar exists to "have a blog" — each maps to a real search intent and a real internal link target.
Answers "how much do I make." Highest search volume, highest commercial intent.
Career-changers and apprentices. Builds top-of-funnel trust and shareability.
Workforce shortage coverage, notable figures, trend pieces. Earns backlinks and repeat visits.
Programmatic SEO architecture.
Hub-and-spoke model. The trade × state landing page (§2.13) is the hub built to convert; blog posts are spokes built to rank on long-tail queries the hub itself can't target, then funnel authority and clicks back to it.
flowchart LR
subgraph Spokes["Blog spokes · long-tail intent"]
B1["[trade] salary [state] [year]"]
B2["[audience] to [trade] career change"]
B3["[trend] in the trades"]
end
HUB["/jobs/[trade]/[state]/ — landing hub · converts"]
SITEMAP["/job-directory/ + sitemap-jobs.xml"]
REG(["Register / Browse jobs"]):::ok
B1 -->|internal link + CTA| HUB
B2 -->|internal link + CTA| HUB
B3 -->|internal link + CTA| HUB
HUB --> REG
HUB -.indexed via.-> SITEMAP
classDef ok fill:#f1ede4,stroke:#1a1a1a,stroke-width:2px;
// Cross-reference §1.7 Information Architecture. Every blog post ships with a mandatory internal link to its matching trade/state hub — not optional, not "when relevant."
Editorial cadence & calendar.
3 posts / week, 600–900 words, rotating across 12 trades × 50 states without repeating a trade+state combination. Sample four-week calendar below — a template to fill, not a fixed schedule.
| Week | Mon | Wed | Fri |
|---|---|---|---|
| 1 | [Electrician] salary in [Texas] — [2026] | [Office worker] to [trades] career change | [General labor] jobs in [New Jersey] |
| 2 | [Plumber] salary in [New Jersey] — [2026] | [Welding] job, no experience needed | [HVAC] technician pay in [Florida] |
| 3 | [Carpenter] jobs in [California] — [2026] | Women in skilled trades — [2026] salary data | [Roofing] jobs in [Florida] — [2026] |
| 4 | [HVAC] jobs in [Ohio] — [2026] | [Apprentice] to [$100K] — five year plan | [Plumbing] jobs in [Georgia] |
// Categories: Salary Guides · Career Advice · Industry News. Track covered trade+state pairs in a single shared log so no combination repeats.
Voice & style guide.
- Conversational, direct, jobsite-plain
- Mix sentence lengths deliberately
- Write for tradespeople, not corporate readers
- Every post ends with a register-link CTA
- 600–900 words. Say the useful thing, then stop.
- No em-dashes — the single most reliable tell of AI-written copy
- No "leverage," "seamlessly," "delve," "robust," "holistic"
- No invented statistics or unsourced salary claims
- No corporate-recruiter tone ("passionate self-starter")
- No semicolons
Distribution & promotion.
| Channel | Role | Notes |
|---|---|---|
| Organic search | Primary, compounding | Long-tail blog + hub landing pages target the full trade × state matrix. |
| Reddit ads | Paid amplification, worker acquisition | Targeted to trade-specific subreddits (r/Construction, r/Electricians, r/HVAC, etc). Conversions objective once signup volume supports it. |
| Internal linking | Crawl depth, authority flow | Every post links to its matching hub; every hub links sideways to adjacent trades/states. |
| Email digest | Retention (future) | Weekly new-jobs-in-your-trade digest once list size justifies build cost. |
KPIs & success metrics.
Month-over-month growth to landing hubs + blog.
Position tracking across the trade × state matrix.
Signups where landing session included a blog referral.
Backlinks earned by Industry News pillar content.
Spec engineer.
Wireframes converted into actionable specs. Atomic inventory (Frost), tokens, component states, A11y, responsive rules and a copy-paste Developer Deployment Code block sized for the existing WP / SPA codebase.
Design tokens.
All values are CSS-custom-property ready — drop into :root. Aesthetic intent: workmanlike, slate-led, hi-vis warm orange used sparingly. Final stylesheet lives at assets/css/styles.css.
a · Colour palette
| Token | Hex | Swatch | Usage |
|---|---|---|---|
| --color-ink | #0F172A | Primary text, primary button bg | |
| --color-ink-2 | #334155 | Body text, captions | |
| --color-muted | #64748B | Secondary text, helper labels | |
| --color-rule | #CBD5E1 | Borders, dividers | |
| --color-surface | #FFFFFF | Card surfaces | |
| --color-surface-2 | #F1F5F9 | Page background | |
| --color-accent | #E07A2D | Hi-vis accent — links, CTA highlights, brand | |
| --color-success | #15803D | Free-mode unlocked strip, verified badges | |
| --color-warning | #B45309 | Expiring soon, vetting tier badges | |
| --color-error | #B91C1C | Validation, destructive actions |
All foreground/background pairs verified ≥ 4.5:1 (WCAG AA body). Ink-on-surface = 16.7:1.
b · Typography
| Role | Family | Size · weight · line-height | Token |
|---|---|---|---|
| Display / H1 (hero) | Inter / system-ui | 40 px · 700 · 1.1 | --font-display |
| H2 | Inter / system-ui | 28 px · 700 · 1.2 | --font-h2 |
| H3 | Inter / system-ui | 20 px · 600 · 1.3 | --font-h3 |
| Body | Inter / system-ui | 16 px · 400 · 1.55 | --font-body |
| Caption / eyebrow | Inter / system-ui | 12 px · 500 · 1.4 · letter-spacing 0.08em uppercase | --font-caption |
| Mono (codes, timestamps) | JetBrains Mono / SF Mono | 13 px · 500 · 1.4 | --font-mono |
Stack: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif. No web-font fetch on landing pages — system stack only, to keep LCP < 1.8 s for SEO.
c · Spacing
| Token | Value | Used for |
|---|---|---|
| --space-1 | 4 px | Tight icon/text gaps |
| --space-2 | 8 px | Form field internal padding |
| --space-3 | 12 px | Card inner gap |
| --space-4 | 16 px | Component padding |
| --space-6 | 24 px | Section gaps mobile |
| --space-8 | 32 px | Major breaks mobile |
| --space-12 | 48 px | Page sections desktop |
d · Borders, radii, elevation
| Token | Value | Notes |
|---|---|---|
| --border-thin | 1px solid var(--color-rule) | Default card border |
| --border-strong | 2px solid var(--color-ink) | Selected state, sticky CTA top edge |
| --radius-sm | 2 px | Buttons, inputs (trade-paperback feel) |
| --radius-md | 6 px | Cards (optional) |
| --shadow-1 | 0 1px 0 #00000010 | Sticky CTA edge only |
| --shadow-2 | 0 6px 16px #00000018 | Modal & toast |
Atomic component inventory.
Per Brad Frost. Component tree flows up: atoms → molecules → organisms → templates → pages. Every wireframe is built from this set, no exceptions.
Button— primary · secondary · tertiary · destructiveInput— text · email · phone · password · numberSelect— trade · state · pay-rangeTextarea— cover note · job descriptionCheckbox·RadioGroupBadge— Vetted · Featured · Expiring · Free ModeChip— Trade selectorAvatar— Worker photo placeholderIcon— Heroicons outline 24 px (defined incore.js · Icons)Skeleton— line · blockSpinner— used only in modal/blocking contextsLink— inline · footer · breadcrumbTag— Trade · State (mono caps)Toggle— admin contact-unlock-fee switch
FormField= Label + Input + Helper + ErrorFilterBar= TradeSelect + StateSelect + SearchInputJobCard= TradeBadge + Title + Employer + Location/Pay + ScarcityStrip + SaveButtonCandidateCard= Avatar + Name + TradeMeta + VettedBadge + LastActive + CTAPricingTier= Header + Price + Bullets + Footer-CTAContactLockedStrip·ContactRevealedStripProgressBar= Track + Fill + % + captionPhotoGrid= up to 3 slots + "+ Add"DocumentRow= Icon + Filename + ViewFAQItem(details/summary, schema.org-FAQPage compatible)Toast·Banner·ModalRoleCard(register step 1)
Header— logo + nav + auth stateFooter— anchors only, never<button>Hero— title + sub + dual CTA + social-countTradeGrid— 12 trade chipsJobList= FilterBar + JobCard[] + LoadMore + SkeletonJobDetailBody+ sticky ApplyBarApplyFormSeekerDashboard= ProgressBar + 4 cardsEmployerDashboard= CTAs + KPIs + JobsTableCandidateBrowserWorkerProfile(Locked / Unlocked / Free states)PostJobForm+ AI helperPricingTable+ VettingRowSeoLandingBody(server-rendered)AdminSettings
- SpaTemplate = Header + RouterOutlet + Footer
- SeoLandingTemplate = MinimalHeader + Body + MinimalFooter (no Divi · no wp_head)
- AdminTemplate = WP admin chrome + LaborLink sub-nav
- Pages: Home · Jobs · JobDetail · Apply · Register · Login · SeekerDashboard · EmployerDashboard · SearchCandidates · WorkerProfile · PostJob · Pricing · SeoLanding(trade·state·t×s) · AdminSettings · Blog · 404
Component states.
a · Buttons
| State | Primary | Secondary | Destructive |
|---|---|---|---|
| Default | bg ink, text white | bg surface, border rule, text ink-2 | bg error, text white |
| Hover | bg ink darken 8 % | border ink, text ink | bg error darken 8 % |
| Active | scale 0.98 | scale 0.98 | scale 0.98 |
| Focus | 2 px accent outline, offset 2 px, on all variants. Never remove (H3). | ||
| Disabled | bg muted, text rule, cursor not-allowed | border rule, text muted | opacity 0.5 |
| Loading | Inline spinner left of label, label = "Working…", button disabled. | ||
b · Inputs
| State | Border | Label | Helper |
|---|---|---|---|
| Default | rule | muted | muted |
| Focus | ink, 2 px | ink | muted |
| Valid | success | ink | "✓ Looks good" |
| Error | error | error | error · pattern: "[What] · [How to fix]" |
| Disabled | rule | muted | muted |
c · ContactStrip (key)
| State | Trigger | Visual | CTA |
|---|---|---|---|
| Locked | fee=ON, no row in jll_worker_unlocks | surface-2 bg, masked dots, 2 px ink top border | "Unlock contact — $5" |
| Pending | status='pending_payment' | warning border, helper "Payment received — confirming…" | disabled, "Awaiting confirmation" |
| Unlocked | status='paid' | ink bg, white text, phone/email links | Call · Text · Email (3 atomic buttons) |
| Free mode | jll_contact_unlock_free=1 | success-50 bg, 2 px success border | Direct phone/email visible |
| Demo (Alex) | is_demo=1 AND is_vetted=1 | Treat as Unlocked regardless of toggle | Same as Unlocked |
Accessibility checklist (WCAG 2.2 AA).
- ☐ Contrast. Body 4.5:1, large 3:1. Hi-vis accent
#E07A2Donly on ink (8.3:1) or as decorative. - ☐ Focus indicators. 2 px accent outline + 2 px offset on every interactive element. Never
outline:nonewithout a replacement. - ☐ Touch targets. Mobile minimum 44 × 44 px. JobCard tap-area = whole card. Save button = 44 px.
- ☐ Heading order. Single H1 per page, no skipping levels.
- ☐ Form labels. Every input has a visible label; placeholder is hint, not label.
- ☐ Error association.
aria-describedbylinking input → helper / error. - ☐ Status live regions. Toasts use
role="status"(polite). Modal errorsrole="alert".
- ☐ Keyboard nav. All flows completable with Tab/Shift-Tab/Enter/Esc; Esc closes modals; J/K (optional) on dashboards.
- ☐ Skip links. "Skip to content" first focusable on every page.
- ☐ SPA route changes. Fire
document.titleupdate +aria-liveannouncement on hash change. - ☐ Images. Worker photos:
alt=""(decorative) until name is co-located; otherwise alt = "[Name] · [Trade]". - ☐ Locked contact. Masked dots must include
aria-label="Contact info — pay to reveal"; never expose real value in DOM until paid. - ☐ Motion. Honour
prefers-reduced-motion; sticky-CTA shadow fade only. - ☐ Language.
<html lang="en">on every template incl. landing pages.
Responsive behaviour.
| Breakpoint | Width | Layout shifts |
|---|---|---|
| Mobile (default) | < 640 px | Single column. Sticky bottom CTA bars. Filter bar sticky-top. Card density: 1 / row. |
| sm | ≥ 640 px | Trade grid 4-col. Hero CTAs side-by-side. JobCard expands but stays 1-col. |
| md | ≥ 768 px | Sidebar filters reveal (240 px). Header switches from hamburger to anchor row. Hero gets right-side imagery. |
| lg | ≥ 1024 px | Dashboards become 2-col grid. PricingTable 3-up. Trade grid 6-col. |
| xl | ≥ 1280 px | Max content width 1200 px, centred. No further reflow. |
Mobile-first. Most blue-collar traffic arrives via phone (Reddit ad clicks confirm this).
Microcopy library.
House voice: direct, jobsite-plain, no em-dashes per blog rules. Active verbs. Pay-rates and prices stated upfront. No "leverage / seamlessly / unlock the power of" — banned per skill rules + product voice.
CTAs
| Hero · worker | Find Jobs Near Me |
| Hero · employer | Hire a Tradesperson |
| Apply | Send Application |
| Unlock | Unlock Contact — $5 |
| Post job | Publish Job |
| Vetting | Order Vetting · $49 |
| Signup · worker | Create Worker Account |
| Signup · employer | Create Hiring Account |
Errors (Nielsen H9)
| Bad login | That email and password combination didn't work. |
| Empty filter | No [trade] jobs in [state] right now. We add ~140 a week. |
| Phone invalid | Enter a 10-digit phone, e.g. 555-123-4567. |
| Upload too big | PDF is too big — keep it under 5 MB. |
| Pay missing | Pay TBD shown when employer left it blank — never invent a range. |
| Server 500 | Couldn't reach us. We'll retry — keep this tab open. |
Empty states
| No applications | Apply to your first job. Free, always. |
| No saved jobs | Tap ♡ on a job to come back to it later. |
| No candidates | No vetted workers match yet. Try widening state or unchecking "vetted only." |
| First post | Post your first job. 30 days free, no credit card. |
Success / confirmation
| Applied | Application sent. Employer has been notified. |
| Saved | Saved · view in My Profile. |
| Unlocked | Contact unlocked · $5 charged · receipt sent. |
| Job published | Job is live. We'll email you when applications arrive. |
| Vetting ordered | Vetting ordered. Report typically takes 24–72 hours. |
Developer deployment code.
Copy-paste-able brief for the front-end developer (or for an LLM agent operating in the codebase). Pre-loaded with the existing tech stack (WordPress + Divi + custom plugin SPA) so no environment-discovery round-trip is needed.
---
DEVELOPER DEPLOYMENT CODE
---
You are the Developer. I am the Designer.
Here are the approved wireframes and design tokens for FindLaborJobs.com.
Implement against the existing stack:
WordPress + Divi (parent) · child theme jersey-laborlink-child
jersey-laborlink-plugin (custom)
SPA at / with hash routing
REST: /wp-json/jll/v1/
CSS in assets/css/styles.css ONLY (no inline, no <style>)
JS in assets/js/ (core, wp-bridge, pages-public, pages-dashboard, pages-auth, targeted-ads)
PHP via FTP (plugin editor server-side disabled)
ZIP delivery only. node --check all JS. Version bump +1 every ZIP.
## DESIGN CONTEXT
National job board for the 12 skilled trades. Two-sided marketplace.
Workers free. Employers pay $5 per contact unlock. Optional vetting tiers.
Solo-operator economics; no support team for runtime errors.
## USER FLOW REFERENCE
See §1.8 master flowchart. Two core loops:
WORKER · home → browse → detail → register → apply → dashboard
EMPLOYER · landing → register → post job → search → profile (locked) → PayPal → profile (unlocked) → vetting → call
## DESIGN TOKENS
Colour (CSS custom properties on :root)
--color-ink: #0F172A
--color-ink-2: #334155
--color-muted: #64748B
--color-rule: #CBD5E1
--color-surface: #FFFFFF
--color-surface-2: #F1F5F9
--color-accent: #E07A2D
--color-success: #15803D
--color-warning: #B45309
--color-error: #B91C1C
Type
--font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif
--font-mono: "JetBrains Mono", "SF Mono", ui-monospace, monospace
H1 40/700/1.1 · H2 28/700/1.2 · H3 20/600/1.3
Body 16/400/1.55 · Caption 12/500/1.4 uppercase 0.08em
Spacing (base 4px): 4 8 12 16 24 32 48
Radius: 2px (sm) · 6px (md)
Borders: 1px rule (default) · 2px ink (selected/sticky-edge)
## COMPONENT REQUIREMENTS
### JobCard (molecule)
- Structure: TradeBadge · Title · Employer · "City, ST · $low–$high or Pay TBD" · ScarcityStrip · SaveButton
- States: default · saved · viewed (visited-link grey)
- A11y: whole card focusable, aria-label "{title} in {city} – pay {range or unspecified}"
### ContactStrip (organism · KEY COMPONENT)
- States enumerated in §3.3.c
- DOM must NOT contain real phone/email until paid. Render dots only.
- Free-mode + demo Alex always render Unlocked.
- Confirm modal before PayPal redirect (Nielsen H5).
- Persist unlock via REST POST /worker_unlock; reflect status from
jll_worker_unlocks.status === 'paid'.
### ApplyForm (organism)
- Single screen, sticky CTA, 5 fields prefilled from profile.
- Validate on blur; never block submit on async; fire Reddit Lead on 200.
- Edge: file > 5MB → inline error, do not strip silently.
### PostJobForm (organism)
- Autosave 10s pause to draft endpoint.
- "Help me write" → POST /ai/job-description with {trade, city, pay}
→ 4-paragraph draft; user edits before publish.
- 2-phase document upload (save → REST nonce → POST file → re-save).
### AdminSettings (organism)
- Top card: Contact Unlock Fee toggle. Self-saves on change.
Persists to WP option jll_contact_unlock_free.
- $contact_free MUST appear in array_map closure use() list when
reading workers — otherwise free-mode silently fails to unlock.
## RESPONSIVE BEHAVIOUR (mobile-first)
- sm 640 · md 768 · lg 1024 · xl 1280 (Tailwind defaults)
- Hero 1-col → 2-col at md.
- Filter bar sticky-top mobile · sidebar-left md+.
- Sticky-bottom CTAs disappear when soft keyboard is open (visualViewport API).
- Trade grid 3 → 4 → 6 col.
## ACCESSIBILITY CHECKLIST
- [ ] WCAG 2.2 AA contrast on every token pair
- [ ] 2px accent focus outline + 2px offset, on every interactive
- [ ] 44×44 mobile touch targets
- [ ] Single H1 per page; logical heading order
- [ ] Form labels visible; aria-describedby on errors
- [ ] Toast role="status", critical alerts role="alert"
- [ ] Hash route change → update document.title + announce via aria-live
- [ ] prefers-reduced-motion respected
- [ ] lang="en" on every template
## NON-NEGOTIABLE PROJECT RULES
1. CSS only in assets/css/styles.css. No inline, no <style>.
2. Footer + nav use <a> tags, not <button>. Global CSS overrides
button backgrounds.
3. Blog cards on homepage are <div onclick>, not <a> — global
CSS overrides anchor backgrounds.
4. Landing pages MUST NOT include wp_head/wp_footer — Divi breaks them.
5. Logout uses CFG.logoutUrl, never /wp-login.php (WPS Hide Login).
6. Login is async fetch with 1200ms cookie delay then
window.location.replace(siteUrl + '?nocache=' + Date.now() + '#/route').
7. is_vetted from MySQL is string "0"/"1" — always parseInt().
8. editId from URL params is string — always parseInt() before
comparing to job IDs.
9. .xml requests intercepted by LiteSpeed before WP rewrite — sitemap
handler uses the parse_request hook to intercept early.
10. No em-dashes in user-facing copy (house voice / blog rule).
## IMPLEMENTATION NOTES
- Reddit pixel events to keep firing:
PageVisit (every load)
SignUp (split SeekerSignUp / EmployerSignUp; pass email for advanced
matching)
Lead (post-apply)
Purchase (post-PayPal unlock click)
Helper: window.jllRedditEvent(name, data)
- SPA load only fetches newest 1,000 jobs; job-detail falls back to
GET /jobs/{id} for misses. Maintain App.store.jobsTotal vs jobs.length.
- Demo accounts (Alex 190, Jordan 191) are seeded server-side and pinned
via ORDER BY is_demo DESC. Never delete; never hide is_demo from admins.
---
END DEPLOYMENT CODE
---
Hand this block to the Frontend Developer skill or to the next-session agent. It replaces the first 30 minutes of environment-discovery questions.
Blog hero templates.
§4 establishes the editorial engine; this section designs where its output actually surfaces on the homepage. Five directions for merchandising the latest post directly above the How FindLaborJobs.com Works section — the highest-traffic real estate on the site outside the hero itself.
Placement & context.
Every direction below is evaluated against the same sample post — "The 5 Highest-Paying Trades in 2026 — And Where They're Hiring Right Now" — and shown with the site nav above and the How-It-Works section immediately below, so visual weight reads accurately in situ rather than in isolation.
Five directions.
New BLS data shows skilled trade wages jumped 8.4% this year. We broke down which trades pay the most and how to land an interview this week.
New BLS data shows skilled trade wages jumped 8.4% this year. We broke down which trades pay the most.
New BLS data shows skilled trade wages jumped 8.4% this year.
New BLS data shows skilled trade wages jumped 8.4% this year. We broke down which trades pay the most, the cities with the biggest shortages, and how to land an interview this week.
Recommendation.
| Direction | Visual weight | Content density | Build cost | Best fit |
|---|---|---|---|---|
| 01 Editorial | Medium | Low | Low | Calm, trust-building default |
| 02 Magazine | Medium-high | High | Medium | Once 3+ posts/week cadence is established (§4.3) |
| 03 Banner | High | Medium | Medium | Launch week / major data-driven story |
| 04 Strip | Low | Low | Low | If How-It-Works must stay the visual anchor |
| 05 Split | Medium-high | High | Medium-high | Salary Guide pillar posts specifically (§4.1) |
Per Nielsen H8 (minimalist design) and the empathy map in §1.3, a first-time visitor's attention budget on the homepage is spent on the hero and trade grid — the blog teaser should support, not compete. Editorial gives the post real presence without visually outranking How-It-Works. Once the Salary Guides pillar (§4.1) has enough data-rich posts to fill it convincingly, promote to Split — its leaderboard format is a direct proof point of the content strategy's value and doubles as a soft trust signal for the pay-rate transparency workers care about most (§1.3).
Appendix.
Framework crosswalk.
Every framework loaded from the UX Designer reference library and where it lives in this dossier.
| Framework | Originator | Where applied |
|---|---|---|
| Jobs to Be Done | Christensen, 2016 | §1.2 Personas (Marcus, Tony) |
| ODI / Job Map | Ulwick, 2005 / 2016 | Implicit in §1.2 "Current state / Success" rows; recommend deep mode interviews |
| Empathy Map | Gray (XPLANE) 2010 | §1.3 Both archetypes |
| Customer Journey Map | [AGGREGATED] · NN/g + service design | §1.6 Worker & Employer journeys |
| Double Diamond | Design Council, 2005 | §1.7 IA + §1.8 Flowchart (Deliver output) |
| Krug's 3 Laws | Krug, 2000 | §2.1, §2.2 (omit needless words, conventions) |
| Norman — Affordances / Mapping / Feedback | Norman, 1988 | §2.3 sticky apply, §2.10 unlock reveal, §3.3 button states |
| Garrett 5 Planes | Garrett, 2002 | §1.7 IA note |
| Nielsen 10 Heuristics | Nielsen, 1994 | §2.15 audit + per-wireframe annotations |
| Atomic Design | Frost, 2016 | §3.2 inventory |
| Fogg B = MAP | Fogg, 2009 / 2020 | §1.5a per-action table |
| Hook Model | Eyal, 2014 | §1.5b cycle + manipulation matrix call-out |
| Nudge / Choice Architecture | Thaler & Sunstein, 2008 | §1.5c nudge inventory |
| Dual Process | Kahneman, 2011 | §1.5d System 1 / System 2 split |
| Cialdini 6 Principles | Cialdini, 1984 | §1.5c (proof, scarcity, anchoring), §2.10 |
| Endowed Progress | Nunes & Dreze, 2006 | §2.7 seeker dashboard progress bar |
| Wroblewski form patterns | 2008–2009 | §2.4, §2.5, §2.11 (inline validate on blur) |
| INVEST + Gherkin | Wake, 2003 + North, 2006 | Implicit in §3.7 acceptance language |
Changelog.
| Version | Change |
|---|---|
| v1.1 | Added Mode 06 — Blog Hero Templates (five homepage merchandising directions with a design recommendation), merged from the standalone concept exploration. Renumbered Spec Engineer subsections (5.1–5.7) for consistency. |
| v1.0 | Initial specification — pre-launch. Deep mode — all eight UX Designer references loaded. Personas, empathy maps, journeys, behavioural analysis, IA, 14 wireframes, hi-fi prototype (12 screens + expanded admin suite), content & SEO strategy, full developer handoff. |
Designer's oath.
I do not decorate. I architect decisions.
I do not guess user needs. I map their jobs.
I do not hand off ambiguity. I deliver specifications.
Every wireframe is a hypothesis. Every spec is a contract.
Design the thinking. Let developers build the thing.