Free
Hero
Velvoro Hero
A hero block extracted from the Velvoro template, available in Tailwind CSS and Bootstrap.
0
2
A hero block extracted from the Velvoro 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>Velvoro Hero | Tailwind CSS UI Block</title>
<meta name="description" content="A hero block extracted from the Velvoro template, available in Tailwind CSS and Bootstrap.">
<meta name="keywords" content="Velvoro, Tailwind CSS template, HTML website template, portfolio template, fashion photography portfolio, editorial magazine template, photo archive, lookbook website, creative studio">
<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="Velvoro | Tailwind CSS Portfolio Template">
<meta property="og:description" content="An editorial Tailwind CSS portfolio template for fashion photographers, with a split collage hero, a look switcher and a contact sheet you can mark up.">
<meta property="og:image" content="">
<meta property="og:image:alt" content="Velvoro template preview">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:url" content="">
<meta name="twitter:title" content="Velvoro | Tailwind CSS Portfolio Template">
<meta name="twitter:description" content="An editorial Tailwind CSS portfolio template for fashion photographers, with a split collage hero, a look switcher and a contact sheet you can mark up.">
<meta name="twitter:image" content="">
<meta name="twitter:image:alt" content="Velvoro 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=Inter:wght@300;400;500&family=Jost:wght@200;300;400;500&display=swap" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
colors: { olive: { DEFAULT: '#6f7550', deep: '#4e5338' }, rust: '#b8382c', cream: '#efe7dd', ink: '#1c1f1d', slate: '#2d3430' },
fontFamily: { sans: ['Inter', 'sans-serif'], wide: ['Jost', 'sans-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; }
.gridlines { background-image: linear-gradient(to right, rgba(255,255,255,.22) 1px, transparent 1px), linear-gradient(to bottom, rgba(255,255,255,.22) 1px, transparent 1px); background-size: 33.333% 33.333%; }
.spaced { letter-spacing: .9em; margin-right: -.9em; }
.swap-img { transition: opacity .5s ease; }
.pick-ring { stroke-dasharray: 100; stroke-dashoffset: 101; opacity: 0; transition: stroke-dashoffset .45s ease, opacity .2s; }
[aria-pressed="true"] .pick-ring { stroke-dashoffset: 0; opacity: 1; }
.menu-overlay { transition: opacity .4s ease, visibility .4s; }
.menu-overlay:not(.is-open) { opacity: 0; visibility: hidden; }
.reveal-ready [data-reveal] { opacity: 0; transform: translateY(24px); transition: opacity .7s ease, transform .7s 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; }
.swap-img, .pick-ring, .menu-overlay { transition: none; }
::view-transition-old(root), ::view-transition-new(root) { animation: none; }
}
</style>
</head>
<body class="bg-slate font-sans font-light text-cream antialiased">
<% active_page = "index" %>
<% nav = { "index" => "Home", "archive" => "Archive", "the-red-room" => "Stories", "about" => "About", "contact" => "Contact" } %>
<% nav.each_with_index do |(slug, label), index| %>
<% active = active_page == slug %>
<% end %>
<% houses = [["Maison Orrel", "1531746020798-e6953c6e8e04", "Woman with a nose ring against a dusty pink wall"], ["Castellane", "1506863530036-1efeddceb993", "Black and white portrait with freckles and short curls"], ["Ivo Brask", "1502823403499-6ccfcf4fb453", "Profile of a woman bathed in violet light"], ["Atelier Senne", "1521572267360-ee0c2909d518", "Bearded man in dramatic low light"]] %>
<!-- block:start hero -->
<section class="grid min-h-[min(100svh,62rem)] lg:grid-cols-2" data-house-swap>
<div class="relative min-h-[36rem] overflow-hidden bg-[#c9a28f]">
<img src="https://images.unsplash.com/photo-1529626455594-4ff0802cfb7e?auto=format&fit=crop&w=1400&q=80" alt="Close-up of a red-haired model looking aside" class="absolute inset-0 h-full w-full scale-125 object-cover object-[60%_80%]">
<div class="gridlines absolute inset-0" aria-hidden="true"></div>
<svg class="absolute left-[18%] top-[22%] h-[42%] w-auto text-cream/50" viewBox="0 0 200 200" aria-hidden="true"><circle cx="100" cy="100" r="98" fill="none" stroke="currentColor" stroke-width=".8"/></svg>
<p class="spaced absolute left-[12%] top-[36%] font-wide text-xs uppercase text-cream/90">Archive</p>
<div class="absolute bottom-0 left-0 h-[55%] w-[46%] bg-rust">
<img src="https://images.unsplash.com/photo-1503185912284-5271ff81b9a8?auto=format&fit=crop&w=700&q=80" alt="Model in a white off-shoulder top leaning on a carved red door" class="h-full w-full object-cover mix-blend-multiply">
</div>
<div class="absolute bottom-0 right-0 h-[33%] w-[45%] border-l border-t border-cream/40">
<img src="https://images.unsplash.com/photo-1509631179647-0177331693ae?auto=format&fit=crop&w=700&q=80" alt="Model in striped trousers posing against a teal wall" class="h-full w-full object-cover">
</div>
<span class="absolute bottom-[34%] right-[44%] font-wide text-[10px] tracking-[0.3em] text-cream/80">SS/26</span>
</div>
<div class="relative min-h-[36rem] overflow-hidden bg-olive">
<% houses.each_with_index do |(name, photo, alt), index| %>
<img src="https://images.unsplash.com/photo-<%= photo %>?auto=format&fit=crop&w=1400&q=80" alt="<%= alt %>" data-house-image="<%= index %>" class="swap-img absolute inset-0 h-full w-full object-cover <%= 'opacity-0' unless index.zero? %>" <%= 'aria-hidden="true"'.html_safe unless index.zero? %>>
<% end %>
<div class="absolute inset-0 bg-olive/35 mix-blend-multiply" aria-hidden="true"></div>
<div class="gridlines absolute inset-0 opacity-60" aria-hidden="true"></div>
<div class="relative flex h-full min-h-[36rem] flex-col justify-between px-6 pb-8 pt-28 md:px-10">
<span class="self-end text-2xl text-cream/80" aria-hidden="true">+</span>
<div class="text-center">
<h1 class="spaced font-wide text-4xl font-extralight uppercase md:text-6xl">Presents</h1>
<p class="spaced mt-3 font-wide text-[11px] uppercase text-cream/80">The archive</p>
</div>
<div>
<p class="font-wide text-sm uppercase tracking-[0.2em]">Best in show</p>
<ul class="mt-5 grid grid-cols-2 gap-3 text-sm text-cream/70 sm:flex sm:justify-between">
<% houses.each_with_index do |(name, _photo, _alt), index| %>
<li><a href="archive.html" data-house="<%= index %>" class="border-b border-transparent pb-1 transition hover:border-cream hover:text-cream focus:border-cream focus:text-cream <%= 'border-cream text-cream' if index.zero? %>"><%= name %></a></li>
<% end %>
</ul>
</div>
</div>
</div>
</section>
<!-- block:end hero -->
<script>
document.querySelectorAll('[data-overlay-toggle]').forEach((button) => {
const overlay = document.getElementById(button.getAttribute('aria-controls'));
const label = button.querySelector('[data-overlay-label]');
const set = (open) => {
button.setAttribute('aria-expanded', String(open));
overlay.classList.toggle('is-open', open);
if (label) label.textContent = open ? 'Close' : 'Menu';
button.querySelector('svg, i')?.classList.toggle('rotate-45', open);
document.body.style.overflow = open ? 'hidden' : '';
};
button.addEventListener('click', () => set(button.getAttribute('aria-expanded') !== 'true'));
document.addEventListener('keydown', (event) => { if (event.key === 'Escape') set(false); });
});
document.querySelectorAll('[data-house-swap]').forEach((root) => {
const images = [...root.querySelectorAll('[data-house-image]')];
const links = [...root.querySelectorAll('[data-house]')];
const show = (index) => {
images.forEach((image) => {
const on = image.dataset.houseImage === index;
image.classList.toggle('opacity-0', !on);
image.toggleAttribute('aria-hidden', !on);
});
links.forEach((link) => link.classList.toggle('border-cream', link.dataset.house === index));
links.forEach((link) => link.classList.toggle('text-cream', link.dataset.house === index));
};
links.forEach((link) => {
link.addEventListener('mouseenter', () => show(link.dataset.house));
link.addEventListener('focus', () => show(link.dataset.house));
});
});
document.querySelectorAll('[data-look-switch]').forEach((root) => {
root.querySelectorAll('[data-look-view]').forEach((button) => {
button.addEventListener('click', () => {
const view = button.dataset.lookView;
root.querySelectorAll('[data-look-view]').forEach((other) => other.setAttribute('aria-pressed', String(other === button)));
root.querySelectorAll('[data-look]').forEach((image) => {
const on = image.dataset.look === view;
image.classList.toggle('opacity-0', !on);
image.toggleAttribute('aria-hidden', !on);
});
});
});
});
document.querySelectorAll('[data-contact-sheet]').forEach((sheet) => {
const frames = [...sheet.querySelectorAll('[data-pick]')];
const count = sheet.querySelector('[data-pick-count]');
const update = () => { count.textContent = frames.filter((frame) => frame.getAttribute('aria-pressed') === 'true').length; };
frames.forEach((frame) => frame.addEventListener('click', () => {
frame.setAttribute('aria-pressed', String(frame.getAttribute('aria-pressed') !== 'true'));
update();
}));
sheet.querySelector('[data-pick-editors]')?.addEventListener('click', () => {
frames.forEach((frame) => frame.setAttribute('aria-pressed', frame.dataset.editor));
update();
});
sheet.querySelector('[data-pick-clear]')?.addEventListener('click', () => {
frames.forEach((frame) => frame.setAttribute('aria-pressed', 'false'));
update();
});
});
document.querySelectorAll('[data-accordion]').forEach((accordion) => {
accordion.querySelectorAll('[data-accordion-trigger]').forEach((trigger) => {
const panel = document.getElementById(trigger.getAttribute('aria-controls'));
trigger.addEventListener('click', () => {
const open = trigger.getAttribute('aria-expanded') !== 'true';
trigger.setAttribute('aria-expanded', String(open));
panel.hidden = !open;
(trigger.querySelector('svg') || trigger.querySelector('i'))?.classList.toggle('rotate-45', open);
});
});
});
document.querySelectorAll('[data-sort-table]').forEach((table) => {
const body = table.querySelector('tbody');
table.querySelectorAll('[data-sort]').forEach((button) => {
button.addEventListener('click', () => {
const key = button.dataset.sort;
const ascending = button.getAttribute('aria-sort') !== 'ascending';
table.querySelectorAll('[data-sort]').forEach((other) => other.removeAttribute('aria-sort'));
button.setAttribute('aria-sort', ascending ? 'ascending' : 'descending');
[...body.rows]
.sort((a, b) => a.dataset[key].localeCompare(b.dataset[key], undefined, { numeric: true }) * (ascending ? 1 : -1))
.forEach((row) => body.append(row));
});
});
});
document.querySelectorAll('[data-chat-form]').forEach((form) => {
const steps = [...form.querySelectorAll('[data-chat-step]')];
const log = form.querySelector('[data-chat-log]');
const status = form.querySelector('[data-form-status]');
let current = 0;
const show = (index) => steps.forEach((step, i) => { step.hidden = i !== index; });
form.addEventListener('submit', (event) => {
event.preventDefault();
const field = steps[current].querySelector('[name]');
if (!field.checkValidity() || !String(field.value || '').trim()) {
status.textContent = 'Could you answer that one first?';
field.focus();
return;
}
status.textContent = '';
const bubble = document.createElement('li');
bubble.className = 'ml-auto max-w-[80%] rounded-2xl rounded-br-sm bg-rust px-4 py-3 text-cream';
bubble.textContent = field.value;
log.append(bubble);
current += 1;
if (current < steps.length) {
const question = document.createElement('li');
question.className = 'max-w-[80%] rounded-2xl rounded-bl-sm bg-cream/10 px-4 py-3';
question.textContent = steps[current].dataset.question;
log.append(question);
show(current);
steps[current].querySelector('[name]').focus();
} else {
show(-1);
const done = document.createElement('li');
done.className = 'max-w-[80%] rounded-2xl rounded-bl-sm bg-cream/10 px-4 py-3';
done.textContent = form.dataset.success;
log.append(done);
status.textContent = form.dataset.success;
}
});
show(0);
});
document.querySelectorAll('[data-contact-form]').forEach((form) => {
form.addEventListener('submit', (event) => {
event.preventDefault();
const status = form.parentElement.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]');
const skip = navigator.webdriver || !('IntersectionObserver' in window) || matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!items.length || skip) 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>
Agency
Unlock the entire library of premium templates and blocks. Renews automatically, cancel anytime.
Tailwind CSS · HTML download