Brassmoor Hero Hero Block - Let's templates
Unlock every premium block and template with All-Access from $20 / 3 months, cancel anytime. Get All-Access
All blocks
Free Hero

Brassmoor Hero

A hero block extracted from the Brassmoor template, available in Tailwind CSS and Bootstrap.

0 2
Brassmoor Hero preview
brassmoor.html
<!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>Brassmoor Hero | Tailwind CSS UI Block</title>
  <meta name="description" content="A hero block extracted from the Brassmoor template, available in Tailwind CSS and Bootstrap.">
  <meta name="keywords" content="Brassmoor, Tailwind CSS template, HTML website template, business template, barbershop website, barber template, grooming salon, haircut prices, booking form, men's hair 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="Brassmoor | Tailwind CSS Business Template">
  <meta property="og:description" content="A dark Tailwind CSS barbershop template with a hero slider, a price list with opening hours, a team slider, a gallery and an appointment form.">
  <meta property="og:image" content="">
  <meta property="og:image:alt" content="Brassmoor template preview">

  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:url" content="">
  <meta name="twitter:title" content="Brassmoor | Tailwind CSS Business Template">
  <meta name="twitter:description" content="A dark Tailwind CSS barbershop template with a hero slider, a price list with opening hours, a team slider, a gallery and an appointment form.">
  <meta name="twitter:image" content="">
  <meta name="twitter:image:alt" content="Brassmoor 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=Teko:wght@400;500;600&family=Hind:wght@400;500;600&display=swap" rel="stylesheet">
  <script>
    tailwind.config = {
      theme: {
        extend: {
          colors: { coal: { DEFAULT: '#121416', card: '#1e2023', soft: '#26292d', line: '#34383d' }, ember: { DEFAULT: '#e85a3d', dark: '#c9452b' }, gold: '#f2a541' },
          fontFamily: { sans: ['Hind', 'sans-serif'], display: ['Teko', '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; }
    .mark { background: linear-gradient(transparent 58%, #e85a3d 58%, #e85a3d 92%, transparent 92%); padding: 0 .1em; }
    .slide { transition: opacity .6s 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; }
      .slide { transition: none; }
      ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
    }
  </style>
</head>
<body class="bg-coal font-sans text-white antialiased">
<% active_page = "index" %>
<% nav = { "index" => "Home", "about" => "About Us", "services" => "Services", "gallery" => "Gallery", "contact" => "Contact" } %>
<% nav.each do |slug, label| %>
<% active = active_page == slug %>
<% end %>
<% nav.each do |slug, label| %>
<% active = active_page == slug %>
<% end %>
<% slides = [["Sharp cuts &amp; hot shaves for", "every gentleman", "1506794778202-cad84cf45f1d", "Man with tousled hair looking straight ahead"], ["Classic fades with a", "modern edge", "1593702275687-f8b402bf1fb5", "Barber shaping a fade with clippers"], ["Beards trimmed the", "old-school way", "1517832606299-7ae9b720a186", "Barber trimming a full beard with scissors"], ["A chair, a coffee and", "an hour for you", "1585747860715-2ba37e788b70", "Row of leather barber chairs under warm lights"], ["Walk in tired,", "walk out sharp", "1534297635766-a262cdcb8ee4", "Barber with glasses cutting a client's hair"]] %>
<% styles = [["Textured crop", "1622286342621-4bd786c2447c", "Barber styling a textured crop"], ["Skin fade", "1593702275687-f8b402bf1fb5", "Fresh skin fade from the side"], ["Side part", "1508341591423-4347099e1f19", "Young man with a neat side part"], ["Curly taper", "1567894340315-735d7c361db0", "Barber tapering curly hair"]] %>
<!-- block:start hero -->
  <section class="relative isolate -mt-32 pt-32" data-hero>
    <svg class="pointer-events-none absolute inset-0 h-full w-full text-white/10" viewBox="0 0 1440 700" preserveAspectRatio="none" fill="none" aria-hidden="true"><path d="M0 420C260 300 420 520 720 380S1180 180 1440 260" stroke="currentColor"/><path d="M0 500C300 380 460 600 760 460S1200 260 1440 340" stroke="currentColor"/><path d="M0 120C320 220 560 40 860 140S1240 60 1440 120" stroke="currentColor"/></svg>
    <div class="relative mx-auto max-w-7xl px-5 md:px-10">
      <div class="relative grid min-h-[34rem] items-center lg:min-h-[38rem]">
        <% slides.each_with_index do |(line_one, line_two, photo, alt), index| %>
          <div class="slide absolute inset-0 grid items-center <%= index.zero? ? 'opacity-100' : 'pointer-events-none opacity-0' %>" data-slide aria-hidden="<%= !index.zero? %>">
            <img src="https://images.unsplash.com/photo-<%= photo %>?auto=format&fit=crop&w=1100&h=900&q=80" alt="<%= alt %>" class="absolute bottom-0 right-0 -z-10 h-full w-full object-cover opacity-50 [mask-image:linear-gradient(to_left,black_45%,transparent)] md:w-3/4 md:opacity-80">
            <div class="max-w-xl">
              <<%= index.zero? ? 'h1' : 'p' %> class="font-display text-5xl font-medium leading-[1.05] md:text-7xl"><%= line_one.html_safe %> <span class="mark"><%= line_two %></span></<%= index.zero? ? 'h1' : 'p' %>>
              <p class="mt-6 max-w-md text-white/70">Precision cuts, hot towels and a proper conversation. Book your chair and leave looking like the best version of you.</p>
              <a href="contact.html" class="mt-8 inline-block bg-ember px-7 py-4 text-xs font-semibold uppercase tracking-widest transition hover:bg-white hover:text-coal" tabindex="<%= index.zero? ? 0 : -1 %>">Book appointment</a>
            </div>
          </div>
        <% end %>
        <p class="absolute right-16 top-1/2 hidden -translate-y-1/2 font-display text-7xl font-semibold md:block" aria-live="polite"><span data-slide-index>01</span> <span class="text-3xl text-white/50">/ 0<%= slides.size %></span></p>
        <div class="absolute right-0 top-1/2 flex -translate-y-1/2 flex-col gap-5" role="group" aria-label="Choose a slide">
          <% slides.each_with_index do |slide, index| %>
            <button type="button" data-slide-dot="<%= index %>" aria-pressed="<%= index.zero? %>" class="group grid h-5 w-5 place-items-center rounded-full transition aria-pressed:ring-2 aria-pressed:ring-white" aria-label="Show slide <%= index + 1 %>"><span class="h-2 w-2 rounded-full bg-white/50 group-aria-pressed:bg-ember"></span></button>
          <% end %>
        </div>
      </div>
      <ul class="relative grid grid-cols-2 gap-x-6 gap-y-4 bg-coal-soft/90 p-5 backdrop-blur md:grid-cols-4 md:p-6 lg:mx-10">
        <% styles.each do |(name, photo, alt)| %>
          <li><a href="gallery.html" class="group flex items-center gap-4"><img src="https://images.unsplash.com/photo-<%= photo %>?auto=format&fit=crop&w=200&h=200&q=70" alt="<%= alt %>" class="h-16 w-16 object-cover transition group-hover:ring-2 group-hover:ring-ember md:h-20 md:w-20"><span class="text-sm transition group-hover:text-ember"><%= name %></span></a></li>
        <% end %>
      </ul>
    </div>
  </section>
  <!-- block:end hero -->
<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'));
      button.addEventListener('click', () => {
        const open = button.getAttribute('aria-expanded') === 'true';
        button.setAttribute('aria-expanded', String(!open));
        menu.classList.toggle('hidden', open);
      });
    });

    document.querySelectorAll('[data-hero]').forEach((root) => {
      const slides = [...root.querySelectorAll('[data-slide]')];
      const dots = [...root.querySelectorAll('[data-slide-dot]')];
      let current = 0;
      const show = (index) => {
        current = (index + slides.length) % slides.length;
        slides.forEach((slide, i) => {
          const on = i === current;
          slide.classList.toggle('opacity-100', on); slide.classList.toggle('opacity-0', !on); slide.classList.toggle('pointer-events-none', !on);
          slide.setAttribute('aria-hidden', String(!on));
          slide.querySelectorAll('a').forEach((link) => { link.tabIndex = on ? 0 : -1; });
        });
        dots.forEach((dot, i) => dot.setAttribute('aria-pressed', String(i === current)));
        root.querySelector('[data-slide-index]').textContent = String(current + 1).padStart(2, '0');
      };
      dots.forEach((dot, i) => dot.addEventListener('click', () => show(i)));
      root.addEventListener('keydown', (event) => { if (event.key === 'ArrowDown') show(current + 1); if (event.key === 'ArrowUp') show(current - 1); });
    });

    document.querySelectorAll('[data-team]').forEach((root) => {
      const members = [...root.querySelectorAll('[data-member]')];
      const photos = [...root.querySelectorAll('[data-member-photo]')];
      let current = 0;
      const show = (index) => {
        current = (index + members.length) % members.length;
        members.forEach((member, i) => { member.hidden = i !== current; });
        photos.forEach((photo, i) => { photo.classList.toggle('opacity-100', i === current); photo.classList.toggle('opacity-0', i !== current); });
      };
      root.querySelector('[data-member-prev]').addEventListener('click', () => show(current - 1));
      root.querySelector('[data-member-next]').addEventListener('click', () => show(current + 1));
    });

    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.querySelector('[data-carousel-prev]').addEventListener('click', () => show(current - 1));
      root.querySelector('[data-carousel-next]').addEventListener('click', () => show(current + 1));
      dots.forEach((dot, i) => dot.addEventListener('click', () => show(i)));
    });

    document.querySelectorAll('[data-lightbox-root]').forEach((root) => {
      const dialog = root.querySelector('[data-lightbox]');
      root.querySelectorAll('[data-lightbox-open]').forEach((button) => button.addEventListener('click', () => {
        const image = dialog.querySelector('[data-lightbox-image]');
        image.src = `https://images.unsplash.com/photo-${button.dataset.lightboxOpen}?auto=format&fit=crop&w=1400&h=900&q=80`;
        image.alt = button.dataset.alt;
        dialog.querySelector('[data-lightbox-caption]').textContent = button.dataset.alt;
        dialog.showModal();
      }));
      dialog.querySelector('[data-lightbox-close]').addEventListener('click', () => dialog.close());
      dialog.addEventListener('click', (event) => { if (event.target === dialog) dialog.close(); });
    });

    document.querySelectorAll('[data-booking]').forEach((form) => {
      const status = form.parentElement.querySelector('[data-form-status]');
      const date = form.querySelector('[name="date"]');
      if (date) date.min = new Date().toISOString().slice(0, 10);
      form.addEventListener('submit', (event) => {
        event.preventDefault();
        const missing = [...form.querySelectorAll('[required]')].find((input) => !input.checkValidity());
        if (missing) { status.textContent = 'Please add your name, phone and a date from today onwards.'; missing.focus(); return; }
        status.textContent = `Thanks, ${form.elements.name.value}! Your chair is held for ${new Date(date.value).toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric' })}.`;
        form.reset();
      });
    });

    document.querySelectorAll('[data-subscribe]').forEach((form) => {
      const status = form.parentElement.querySelector('[data-subscribe-status]');
      form.addEventListener('submit', (event) => {
        event.preventDefault();
        if (!form.elements.email.checkValidity()) { status.textContent = 'Please enter a valid email.'; return; }
        status.textContent = 'Subscribed — see you next month.';
        form.reset();
      });
    });

    document.querySelectorAll('[data-count]').forEach((el) => {
      if (!watchMotion) return;
      const target = Number(el.dataset.count);
      const pad = el.dataset.count.length;
      const observer = new IntersectionObserver(([entry]) => {
        if (!entry.isIntersecting) return;
        observer.disconnect();
        const start = performance.now();
        const tick = (now) => { const t = Math.min(1, (now - start) / 700); el.textContent = String(Math.round(target * t)).padStart(pad > 2 ? 0 : pad, '0'); if (t < 1) requestAnimationFrame(tick); };
        requestAnimationFrame(tick);
      });
      observer.observe(el);
    });

    if (watchMotion) {
      document.documentElement.classList.add('reveal-ready');
      const observer = new IntersectionObserver((entries) => entries.forEach((entry) => {
        if (entry.isIntersecting) { entry.target.classList.add('is-visible'); observer.unobserve(entry.target); }
      }), { threshold: 0.15 });
      document.querySelectorAll('[data-reveal]').forEach((el) => observer.observe(el));
    }
  </script>
</body>
</html>

From this template

Brassmoor screenshot

Business

Brassmoor

Unlimited access to all products

Unlock the entire library of premium templates and blocks. Renews automatically, cancel anytime.

$20 / 3 months or $60 / year
Get Unlimited Access