Full design · Marketplaces
Job board
A niche job board for Australia's climate and energy workforce: solar, wind, batteries, the grid, EV charging and home electrification.
Live preview. Click around, it works.
About this template
A niche job board for Australia's climate and energy workforce: solar, wind, batteries, the grid, EV charging and home electrification. Seekers search and filter roles by location, remote or on site, salary range, type and experience, sort results, and save searches with simulated alerts. A job page covers the role, the pay, what the week looks like and the company, then lets a seeker apply with a CV kept in memory for preview and a cover note, saved through a security definer function. Company pages carry real workplace photography and open roles. Saved jobs and applications track a status timeline from Applied through to Offer. Employers post a job through a multi step form with a live preview and a demo payment, run a kanban applicant pipeline with notes, ratings and a kind templated rejection message, edit their company profile, and read insights charts from views to applications per job.
What the agents are asked
The first message sent when you use this template. You can edit the plan before anything is built.
Adapt this job board to my business. Keep both sides and every working rule: the seeker side (Jobs with search, filters, sorting and saved searches with simulated alerts, Job detail with an apply flow that keeps the CV in memory for preview and writes through the security definer function, Company pages, Saved jobs and Applications with a status timeline) and the employer side (Post a job as a multi step form with a live preview and a demo payment, Applicants pipeline as a kanban by stage with notes, ratings and a templated reject message, Company profile editor, Insights charts from views to applications per job). Change the brand, colours, industry, companies, locations, salary bands and screening questions to mine. Keep the sample data marked Sample and removable in one step, keep salaries realistic for Australia, and keep the general information line wherever the site touches employment advice. When I want real accounts and payments, wire in sign in, the tables and functions in schema.sql and Stripe Checkout as the README describes, with every secret key on the server.
A job board for Australia's climate and energy workforce: solar, wind, grid scale batteries, EV charging and home electrification. Seekers search real roles, apply with a CV kept in memory and a cover note, and track every application on a timeline. Employers ("Viewing as: Employer", acting as the sample company Meridian Solar) post roles through a multi step form with a live preview and a demo payment, run a kanban applicant pipeline, edit their company profile, and read insights charts from views to applications.
"Gridwork", Meridian Solar, Southern Cross Wind, Voltpath, Ember Retrofits and Basalt Grid Storage are invented for this template. The people (applicants, and the "you" who applies as a seeker) are invented too. Salaries are sample figures for the demo, not a market rate.
Files
| File | What it is |
|---|---|
index.html | Shell: demo ribbon with Viewing as: Seeker / Employer, top bar, employer sidebar, main, phone tab bar, one <dialog> for every sheet, toast and chart tooltip. One <script type="module">. |
styles.css | Tokens first (light; dark by prefers-color-scheme, or a manual override on <html data-theme>), then shell, components, screens, motion. Every colour is a token. |
script.js | One plain ES module, no dependencies. Sections: helpers, icons, constants, sample data, store (the data layer), rpc (the write side, named after the SQL functions), queries, shared UI, seeker screens, employer screens, insights charts, router, events. |
schema.sql | Postgres tables, row level security, grants, the three write functions, seed rows and a VERIFY block. |
media.json | The photographs, with the prompts they were generated from. |
template.json | The catalogue entry and the identity (fonts, colours, the signature moment). |
There is no build step. Serve the folder with any static server that also serves /template-media/job-board/.
Screens
Seeker (bottom tab bar on a phone, top navigation on a desktop)
| Screen | What works |
|---|---|
Home #/ | A photography led hero with a keyword search, the saved search alert strip when a saved search has new matches, every company hiring now with its own workplace photo and open role count, a "how it works" walkthrough, and the newest roles. |
Jobs #/jobs | Search and filters (location, remote or on site, salary range converted to an annual figure so an hourly rate compares fairly, type, experience), sort (newest, salary high to low, low to high), a job card per role with a save heart, and saved searches with a match count and a "new" flag that clears once viewed. / focuses the search box. |
Job detail #/jobs/<id> | The role, the pay, "what the week looks like" written by the company, the screening questions the apply form will ask, an about the company card with the company's own photo, other open roles at that company, and a sticky apply panel. |
Company #/companies/<slug> | The company's own workplace photo, its story, and every open role. |
Saved and applications #/saved | Two tabs: saved jobs, and applications with a status timeline (Applied through to an offer, or a kind, specific message if the role went another way). |
Employer (sidebar on a desktop, tab bar on a phone), acting as Meridian Solar
| Screen | What works |
|---|---|
My jobs #/employer | Open role and applicant counts, total views, new applicants this week, and a table of every role (draft, open or closed) with a quick close, reopen or delete a draft. |
Post a job #/employer/post (or /employer/post/<id> to edit or resume a draft) | A four step form (basics, description, compensation, review) with a live preview panel that updates as you type, a Save as draft action available at every step, and a demo checkout on the review step: "Job posting fee $199 (demo). Connect Stripe in project settings." |
Applicants #/employer/pipeline | A kanban board by stage (Applied, Reviewing, Interview, Offer, Rejected; Hired shows inside Offer), filterable to one role. Each card carries a star rating, a notes field, and a Move to control for the stage (the accessible, reliable primary control; the columns are the picture, the select is what actually moves the card). Choosing Rejected opens three kind, specific message templates to send, edited or sent as is, which lands on the applicant's own timeline. |
Company profile #/employer/company | Edit the company's story, industry, head office, founding year, size and website, with a live preview of the card seekers see. |
Insights #/employer/insights | Two SVG charts: views compared to applications for every role, and a funnel (views, applied, reviewing, interview, offer) for one selected role, each with a hover and keyboard tooltip and a Table view toggle. |
The data model (store in script.js, the same tables as schema.sql)
- companies: the five sample companies.
is_employer_demomarks Meridian Solar, the one the employer screens act as;owner_user_idis how a real deployment knows which signed in user runs which company (see "Going live" below). - jobs:
statusisdraft,openorclosed. Salary is stored assalary_min/salary_max/salary_period(yearorhour);salaryAnnual()in script.js converts an hourly rate to a rough yearly figure so filtering and sorting compare fairly. - job_view_events: one row per view, append only, counted rather than incremented, so nothing can be lost to two views landing at once (GENERATED-APPS.md, "money and counters are append only"). Debounced to once per job per browser session (
sessionStorage). - saved_jobs, saved_searches: a seeker's own rows.
saved_searches.queryis the same shape as the filter state in the Jobs screen;last_alerted_atis the watermark the "new matches" count and the alert strip are measured against. - applications:
statusmoves throughapplied,reviewing,interview,offer,hiredorrejected.cv_filenameandcv_summaryare what the demo keeps: the file itself is read only into the browser's memory for the preview and is never uploaded anywhere in this template (see "Going live" for where a real upload belongs). A client sideis_mineflag marks the rows the current browser actually submitted, standing in foruser_id = auth.uid()until real sign in exists. - application_events: append only, the timeline a seeker sees and the employer's history. A rejection's kind message lives here, on the
rejectedevent.
Going live: from store/rpc to Supabase
Swapping localStorage for a real backend is a change inside store and rpc only; no screen touches either directly.
- Sign in. Wire up Supabase Auth for both seekers and employers. A company becomes an employer's by setting
companies.owner_user_id = auth.uid(); row level security inschema.sqlalready scopes every employer screen to that. - Applications, never through the table.
rpc.submitApplicationbecomes a call tosupabase.rpc('submit_application', { p_job, p_name, p_email, p_cover_note, p_cv_filename, p_cv_summary }), the SECURITY DEFINER function inschema.sql(L25: a public form writes through a function, never the table, soanonnever holds an insert grant onapplications). - The CV file itself. Upload it to Supabase Storage under a path keyed by the application id, and pass the storage path as
cv_filename(or add acv_pathcolumn). Keep parsing server side if a real text preview is wanted later; the demo's in browser preview only reads plain text files, and never claims to read a PDF's contents. - Stage changes, never a bare update.
rpc.setApplicationStagebecomessupabase.rpc('set_application_stage', { p_application, p_stage, p_message }), which writes the status and the timeline event together and checks the caller owns the job's company, so a stage can never change without a record of why. - Payments. The demo checkout button calls no network. Replace it with a real Stripe Checkout session created by a server function; only mark the job
openafter the webhook confirms payment, the same way the SQL function only ever setsstatus = 'open'from server side logic, never from a value the client sends directly. - Views.
rpc.recordJobViewbecomessupabase.rpc('record_job_view', { p_job }).
Art direction
One shoot across five real Australian energy workplaces, shot in the hour around sunrise (one portrait lit by monitor glow at dusk to match its setting): low, raking side light, warm highlights against cool blue grey shadows, long real shadows, 24 to 85mm primes, fine film grain, muted natural colour, natural skin with no airbrushing. Every hi-vis vest and hard hat is plain and unbranded; no text, logos or watermarks in any frame.
| Name | Used for |
|---|---|
meridian-solar-site | Meridian Solar's workplace: Home hero, its company card and company page, poster for solar-site-loop.mp4. |
southern-cross-wind-site | Southern Cross Wind's workplace: its company card and company page. |
voltpath-depot | Voltpath's workplace: its company card and company page. |
ember-retrofit-site | Ember Retrofits' workplace: its company card and company page. |
basalt-battery-site | Basalt Grid Storage's workplace: its company card and company page. |
installer-hands | A person at work (no single company): the Home "Real crews, real work" section. |
control-room-engineer | A person at work: generated for the shoot's portrait set. |
solar-site-loop.mp4 | A calm five second loop from the Meridian Solar site, muted, looping, paused off screen or when the tab is hidden. |
Each company's photograph belongs to that company only, on every screen it appears: Home's company grid, that company's own page, and its jobs' "about the company" card all reuse the same one image rather than a second photograph of the same place.
What was deliberately left out of Phase 2
- Kanban drag and drop. Cards move stage through a Move to select, not a drag gesture. The select is keyboard accessible and cannot silently fail the way HTML5 drag and drop can in a sandboxed preview; a real drag interaction can be layered on top of the same
rpc.setApplicationStagecall later without changing the data model. - Scroll triggered reveals. The premium bar's motion vocabulary is optional, not a checklist to exhaust. This template's one orchestrated moment is the apply confirmation (below); everywhere else content is visible at rest, with a page load stagger on Home only, so there is nothing that depends on an
IntersectionObserverfiring in a hidden tab (LESSONS.md L22, L24).
The signature moment
Submitting an application: the sheet snaps shut once like a switchboard breaker (is-energising in styles.css, skipped entirely under prefers-reduced-motion), then the confirmation replaces the form. The same submission also updates the job detail page behind the sheet from "Apply" to "Applied" before the sheet even closes.
General information
Salaries, company details and every person on this template are sample data for the demo, marked is_sample in schema.sql and removable in one step from the ribbon. Nothing here is a real job offer, a real salary benchmark or real employment advice.