Free
Demo
Kindhire Demo
A demo block extracted from the Kindhire template, available in Tailwind CSS and Bootstrap.
0
1
A demo block extracted from the Kindhire template, available in Tailwind CSS and Bootstrap.
<!DOCTYPE html>
<html lang="en">
<head>
<script>
window.FontAwesomeConfig = { autoReplaceSvg: 'nest' };
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kindhire Demo | Tailwind CSS UI Block</title>
<meta name="description" content="A demo block extracted from the Kindhire template, available in Tailwind CSS and Bootstrap.">
<meta name="keywords" content="Kindhire, Tailwind CSS template, HTML website template, business template, inclusive hiring, accessible job board, disability employment, recruitment platform, remote jobs website, employer page">
<meta name="author" content="Let's templates">
<link rel="canonical" href="">
<meta property="og:type" content="product">
<meta property="og:site_name" content="Let's templates">
<meta property="og:locale" content="en_US">
<meta property="og:url" content="">
<meta property="og:title" content="Kindhire | Tailwind CSS Business Template">
<meta property="og:description" content="A warm, accessible Tailwind CSS business template for inclusive hiring platforms, with arch portraits, a story carousel and a live job matcher.">
<meta property="og:image" content="">
<meta property="og:image:alt" content="Kindhire template preview">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:url" content="">
<meta name="twitter:title" content="Kindhire | Tailwind CSS Business Template">
<meta name="twitter:description" content="A warm, accessible Tailwind CSS business template for inclusive hiring platforms, with arch portraits, a story carousel and a live job matcher.">
<meta name="twitter:image" content="">
<meta name="twitter:image:alt" content="Kindhire template preview">
<script src="https://cdn.tailwindcss.com/3.4.17"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,700;9..144,800&family=Urbanist:wght@400;500;600;700&display=swap" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
colors: { paper: '#f4f4f2', ink: '#141414', royal: { DEFAULT: '#0b4ea2', dark: '#083a7a', soft: '#dfe9f7' }, coral: { DEFAULT: '#f4846a', soft: '#fde3dc' }, leaf: { DEFAULT: '#3aa52a', soft: '#dff1db' }, sun: '#ffc928' },
fontFamily: { sans: ['Urbanist', 'sans-serif'], serif: ['Fraunces', 'serif'] }
}
}
}
</script>
<style>
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: 180ms ease-out both page-fade-out; }
::view-transition-new(root) { animation: 220ms ease-in both page-fade-in; }
@keyframes page-fade-out { to { opacity: 0; } }
@keyframes page-fade-in { from { opacity: 0; } }
html { scroll-behavior: smooth; }
[hidden] { display: none !important; }
.wiggle { animation: wiggle 5s ease-in-out infinite; transform-origin: center; }
@keyframes wiggle { 50% { rotate: 6deg; } }
.logos { animation: logos 34s linear infinite; }
.logos:hover { animation-play-state: paused; }
@keyframes logos { to { transform: translateX(-50%); } }
.ring-fill { transition: stroke-dashoffset .7s ease; }
.timeline-fill { transform-origin: top; }
.story { transition: opacity .4s ease; }
.reveal-ready [data-reveal] { opacity: 0; transform: translateY(24px); transition: opacity .6s ease, transform .6s ease; transition-delay: var(--reveal-delay, 0ms); }
.reveal-ready [data-reveal].is-visible { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
.wiggle, .logos { animation: none; }
.ring-fill, .story { transition: none; }
::view-transition-old(root), ::view-transition-new(root) { animation: none; }
}
</style>
</head>
<body class="bg-paper font-sans text-ink antialiased">
<% active_page = "index" %>
<% nav = { "platform" => "Platform", "jobs" => "Jobs", "employers" => "For employers", "stories" => "Stories", "about" => "About", "contact" => "Contact" } %>
<% { "index" => "Home" }.merge(nav).each do |slug, label| %>
<% active = active_page == slug %>
<% end %>
<% arches = [["1507003211169-0a1dd7228f2d", "Smiling man in a white T-shirt", "Marco", "QA tester", "bg-royal", "halo", "lg:mt-0"], ["1580489944761-15a19d654956", "Smiling woman with her hair tied up", "Lina", "web designer", "bg-coral", "burst", "lg:mt-36"], ["1599566150163-29194dcaad36", "Young man with glasses and a beard", "Sam", "content moderator", "bg-leaf", "zigzag", "lg:mt-0"]] %>
<% arches.each_with_index do |(photo, alt, name, role, color, doodle, offset), index| %>
<% if index == 1 %>
<% end %>
<% if doodle == "halo" %>
<% elsif doodle == "burst" %>
<% else %>
<% end %>
<% unless index == 1 %>
<% else %>
<% end %>
<% end %>
<% stories = [["Elena Ruiz", "38, works from Lisbon, uses a wheelchair", "Moderating content is calm, focused work, and I can split my hours however my body needs. Two hours in the morning, two in the evening. My team checks in, never checks up.", "1573497019940-1c28c88b4f3e", "bg-royal"], ["Daniel Okafor", "29, works from Leeds, hard of hearing", "Every meeting has live captions and everything important is written down. For the first time I'm judged on my work, not on how fast I catch what people say.", "1500648767791-00dcc994a43e", "bg-coral"], ["Mei Tanaka", "34, works from Osaka, chronic fatigue", "Flexible deadlines changed everything. I design on good days and rest on hard ones, and nobody makes me explain myself.", "1573497019236-17f8177b81e8", "bg-leaf"]] %>
<% stories.each_with_index do |(name, meta, quote, photo, color), index| %>
<% end %>
<% stories.each_with_index do |story, index| %>
<% end %>
<% skills = [["design", "Design"], ["writing", "Writing"], ["support", "Customer support"], ["testing", "Software testing"], ["data", "Data entry"], ["moderation", "Moderation"]] %>
<% needs = [["remote", "Fully remote", "fa-house-laptop"], ["flexible", "Flexible hours", "fa-clock"], ["captions", "Captioned meetings", "fa-closed-captioning"], ["reader", "Screen-reader friendly", "fa-universal-access"]] %>
<% jobs = [["Junior UI designer", "Brightfield Studio", "design", "remote flexible reader", "$38–44K", "bg-coral-soft"], ["Support specialist", "Parcelpoint", "support", "remote captions flexible", "$32–36K", "bg-royal-soft"], ["QA tester", "Northgate Apps", "testing", "remote reader captions flexible", "$40–48K", "bg-leaf-soft"], ["Content moderator", "Openstream", "moderation", "remote flexible", "$30–34K", "bg-royal-soft"], ["Copywriter", "Hearthline", "writing", "remote flexible captions", "$36–42K", "bg-coral-soft"], ["Data assistant", "Clearpath Health", "data", "flexible reader", "$29–33K", "bg-leaf-soft"]] %>
<!-- block:start demo -->
<section id="matcher" class="bg-royal py-24 text-white" data-matcher>
<div class="mx-auto max-w-7xl px-5 md:px-10">
<div class="grid gap-12 lg:grid-cols-[1fr_1.3fr]">
<div>
<p class="font-semibold text-sun">Job matcher</p>
<h2 class="mt-2 font-serif text-4xl font-bold leading-tight tracking-tight md:text-6xl">Tell us what works for you</h2>
<p class="mt-5 max-w-md text-lg text-white/80">Pick your skills and the conditions you need. We only show roles that fit both.</p>
<fieldset class="mt-10">
<legend class="font-semibold">I'm good at</legend>
<div class="mt-3 flex flex-wrap gap-2">
<% skills.each_with_index do |(id, label), index| %>
<button type="button" data-skill="<%= id %>" aria-pressed="<%= index < 2 %>" class="rounded-full border-2 border-white/30 px-4 py-2 font-medium transition hover:border-white aria-pressed:border-sun aria-pressed:bg-sun aria-pressed:text-ink"><%= label %></button>
<% end %>
</div>
</fieldset>
<fieldset class="mt-8">
<legend class="font-semibold">I need</legend>
<div class="mt-3 grid gap-2 sm:grid-cols-2">
<% needs.each_with_index do |(id, label, icon), index| %>
<label class="flex cursor-pointer items-center gap-3 rounded-2xl bg-white/10 px-4 py-3 transition hover:bg-white/15 has-[:checked]:bg-white has-[:checked]:text-royal">
<input type="checkbox" data-need="<%= id %>" class="h-5 w-5 accent-coral" <%= 'checked' if index.zero? %>>
<i class="fa-solid <%= icon %>" aria-hidden="true"></i><span class="font-medium"><%= label %></span>
</label>
<% end %>
</div>
</fieldset>
</div>
<div aria-live="polite">
<p class="text-lg"><span class="font-serif text-3xl font-bold text-sun" data-match-count><%= jobs.size %></span> roles match right now</p>
<ul class="mt-6 grid gap-3">
<% jobs.each do |title, company, skill, tags, salary, tone| %>
<li data-job data-skill="<%= skill %>" data-needs="<%= tags %>" class="flex flex-wrap items-center gap-4 rounded-3xl bg-white p-5 text-ink">
<span class="grid h-12 w-12 place-items-center rounded-2xl <%= tone %> text-lg font-bold text-royal"><%= company[0] %></span>
<span class="min-w-0 flex-1"><span class="block text-lg font-bold"><%= title %></span><span class="text-ink/60"><%= company %> · <%= salary %></span></span>
<span class="flex gap-1.5 text-royal">
<% needs.each do |id, label, icon| %><% if tags.include?(id) %><i class="fa-solid <%= icon %>" title="<%= label %>" aria-label="<%= label %>"></i><% end %><% end %>
</span>
</li>
<% end %>
</ul>
<p data-match-empty hidden class="mt-6 rounded-3xl border-2 border-dashed border-white/40 p-6 text-center text-lg">No exact match yet. <a href="contact.html" class="font-semibold underline">Tell us what you need</a> and we'll find it.</p>
<a href="jobs.html" class="mt-6 inline-flex items-center gap-2 rounded-full bg-sun px-7 py-3.5 font-semibold text-ink transition hover:bg-white">See all jobs <i class="fa-solid fa-arrow-right" aria-hidden="true"></i></a>
</div>
</div>
</div>
</section>
<!-- block:end demo -->
<script>
const reducedMotion = matchMedia('(prefers-reduced-motion: reduce)').matches;
const watchMotion = !reducedMotion && !navigator.webdriver && 'IntersectionObserver' in window;
document.querySelectorAll('[data-menu-toggle]').forEach((button) => {
const menu = document.getElementById(button.getAttribute('aria-controls'));
const close = menu.querySelector('[data-menu-close]');
const set = (open) => { menu.hidden = !open; button.setAttribute('aria-expanded', String(open)); document.body.style.overflow = open ? 'hidden' : ''; (open ? close : button).focus(); };
button.addEventListener('click', () => set(menu.hidden));
close.addEventListener('click', () => set(false));
document.addEventListener('keydown', (event) => { if (event.key === 'Escape' && !menu.hidden) set(false); });
});
document.querySelectorAll('[data-carousel]').forEach((root) => {
const slides = [...root.querySelectorAll('[data-slide]')];
const dots = [...root.querySelectorAll('[data-carousel-dot]')];
let current = 0;
const show = (index) => {
current = (index + slides.length) % slides.length;
slides.forEach((slide, i) => { slide.hidden = i !== current; });
dots.forEach((dot, i) => dot.setAttribute('aria-current', String(i === current)));
};
root.querySelectorAll('[data-carousel-prev]').forEach((button) => button.addEventListener('click', () => show(current - 1)));
root.querySelectorAll('[data-carousel-next]').forEach((button) => button.addEventListener('click', () => show(current + 1)));
dots.forEach((dot, i) => dot.addEventListener('click', () => show(i)));
root.addEventListener('keydown', (event) => { if (event.key === 'ArrowLeft') show(current - 1); if (event.key === 'ArrowRight') show(current + 1); });
});
document.querySelectorAll('[data-matcher]').forEach((root) => {
const jobs = [...root.querySelectorAll('[data-job]')];
const isOn = (el) => el.getAttribute('aria-pressed') === 'true' || el.selected === true || el.checked === true;
const paint = () => {
const skills = [...root.querySelectorAll('[data-skill]:not([data-job])')].filter(isOn).map((el) => el.dataset.skill);
const needs = [...root.querySelectorAll('[data-need]')].filter(isOn).map((el) => el.dataset.need);
let shown = 0;
jobs.forEach((job) => {
const match = (!skills.length || skills.includes(job.dataset.skill)) && needs.every((need) => job.dataset.needs.split(' ').includes(need));
job.hidden = !match;
if (match) shown += 1;
});
root.querySelector('[data-match-count]').textContent = shown;
root.querySelector('[data-match-empty]').hidden = shown > 0;
};
root.querySelectorAll('[data-skill]:not([data-job])').forEach((chip) => chip.addEventListener('click', () => { if (chip.hasAttribute('aria-pressed')) chip.setAttribute('aria-pressed', String(chip.getAttribute('aria-pressed') !== 'true')); requestAnimationFrame(paint); }));
root.addEventListener('change', paint);
if (window.customElements) customElements.whenDefined('md-filter-chip').then(() => requestAnimationFrame(paint));
paint();
});
document.querySelectorAll('[data-timeline]').forEach((root) => {
const fill = root.querySelector('[data-timeline-fill]');
if (!watchMotion) return;
const update = () => {
const box = root.getBoundingClientRect();
fill.style.transform = `scaleY(${Math.min(1, Math.max(0, (innerHeight * 0.6 - box.top) / box.height))})`;
};
update();
addEventListener('scroll', update, { passive: true });
});
document.querySelectorAll('[data-rings]').forEach((root) => {
const rings = root.querySelectorAll('[data-ring]');
if (!watchMotion) return;
rings.forEach((ring) => { ring.style.strokeDashoffset = 326.73; });
const observer = new IntersectionObserver((entries) => {
if (!entries.some((entry) => entry.isIntersecting)) return;
rings.forEach((ring) => { ring.style.strokeDashoffset = 326.73 * (1 - Number(ring.dataset.ring) / 100); });
observer.disconnect();
}, { threshold: 0.3 });
observer.observe(root);
});
document.querySelectorAll('[data-chat]').forEach((root) => {
const log = root.querySelector('[data-chat-log]');
const add = (text, mine) => {
const item = document.createElement('li');
item.className = mine ? 'ml-auto max-w-[85%] rounded-3xl rounded-br-md bg-royal px-5 py-3 text-white' : 'max-w-[85%] rounded-3xl rounded-bl-md bg-paper px-5 py-3';
item.textContent = text;
log.append(item);
log.scrollTop = log.scrollHeight;
};
root.querySelectorAll('[data-chat-question]').forEach((button) => button.addEventListener('click', () => {
add(button.dataset.label || button.textContent.trim(), true);
setTimeout(() => add(button.dataset.answer, false), reducedMotion ? 0 : 450);
}));
});
document.querySelectorAll('[data-hover-list]').forEach((root) => {
const image = root.querySelector('[data-hover-image]');
root.querySelectorAll('[data-hover-row]').forEach((row) => {
const show = () => { image.src = row.dataset.image; image.alt = row.dataset.alt; root.querySelectorAll('[data-hover-row]').forEach((other) => other.setAttribute('aria-current', String(other === row))); };
row.addEventListener('mouseenter', show);
row.addEventListener('focus', show);
row.addEventListener('click', show);
});
});
document.querySelectorAll('[data-job-board]').forEach((root) => {
const list = root.querySelector('[data-board-list]');
const items = [...list.children];
const search = root.querySelector('[data-board-search]');
const sort = root.querySelector('[data-board-sort]');
const apply = () => {
const query = String(search.value || '').trim().toLowerCase();
const sorted = [...items].sort((a, b) => sort.value === 'salary' ? b.dataset.salary - a.dataset.salary : sort.value === 'hours' ? a.dataset.hours - b.dataset.hours : b.dataset.date - a.dataset.date);
let shown = 0;
sorted.forEach((item) => { const match = !query || item.textContent.toLowerCase().includes(query); item.hidden = !match; if (match) shown += 1; list.append(item); });
root.querySelector('[data-board-count]').textContent = shown;
root.querySelector('[data-board-empty]').hidden = shown > 0;
};
search.addEventListener('input', apply);
sort.addEventListener('change', apply);
const initial = new URLSearchParams(location.search).get('q');
if (initial) search.value = initial;
apply();
});
document.querySelectorAll('[data-calculator]').forEach((root) => {
const money = (value) => `$${Math.round(value).toLocaleString('en-US')}`;
const paint = () => {
const hires = Number(root.querySelector('[data-hires]').value);
const salary = Number(root.querySelector('[data-salary]').value);
const fee = hires * salary * 0.08;
const credit = hires * Math.min(salary * 0.25, 9600);
root.querySelector('[data-hires-label]').textContent = hires;
root.querySelector('[data-salary-label]').textContent = money(salary);
root.querySelector('[data-fee]').textContent = money(fee);
root.querySelector('[data-credit]').textContent = `−${money(credit)}`;
root.querySelector('[data-net]').textContent = money(Math.max(0, fee - credit));
};
root.addEventListener('input', paint);
paint();
});
document.querySelectorAll('[data-chat-form]').forEach((form) => {
const log = form.querySelector('[data-chat-log]');
const steps = [...form.querySelectorAll('[data-chat-step]')];
const status = form.querySelector('[data-form-status]');
let current = 0;
const say = (text, mine) => {
const bubble = document.createElement('li');
bubble.className = mine ? 'ml-auto max-w-[85%] rounded-3xl rounded-br-md bg-royal px-5 py-3 text-white' : 'max-w-[85%] rounded-3xl rounded-bl-md bg-paper px-5 py-3';
bubble.textContent = text;
log.append(bubble);
log.scrollTop = log.scrollHeight;
};
const show = (index) => steps.forEach((step, i) => { step.hidden = i !== index; });
const field = (step) => step.querySelector('input, select, textarea, md-outlined-text-field, md-outlined-select');
form.addEventListener('submit', (event) => {
event.preventDefault();
const input = field(steps[current]);
if (!input.checkValidity() || !String(input.value || '').trim()) { status.textContent = form.dataset.error; input.focus(); return; }
status.textContent = '';
say(String(input.value), true);
current += 1;
if (current < steps.length) { say(steps[current].dataset.question, false); show(current); field(steps[current]).focus(); }
else { say(form.dataset.success, false); show(-1); }
});
show(0);
});
document.querySelectorAll('[data-contact-form]').forEach((form) => {
form.addEventListener('submit', (event) => {
event.preventDefault();
const status = form.querySelector('[data-form-status]');
if (!form.checkValidity()) {
status.textContent = form.dataset.error;
form.querySelector(':invalid')?.focus();
return;
}
status.textContent = form.dataset.success;
form.reset();
});
});
document.querySelectorAll('[data-current-year]').forEach((el) => { el.textContent = new Date().getFullYear(); });
(() => {
const items = document.querySelectorAll('[data-reveal]');
if (!items.length || !watchMotion) return;
document.documentElement.classList.add('reveal-ready');
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
entry.target.classList.add('is-visible');
observer.unobserve(entry.target);
});
}, { rootMargin: '0px 0px -10% 0px' });
items.forEach((item) => observer.observe(item));
})();
</script>
</body>
</html>
Business
Unlock the entire library of premium templates and blocks. Renews automatically, cancel anytime.