Brassmoor UI Template - Let's templates
Brassmoor preview 1
Free
0 2

Brassmoor

By Luca Moretti

Brassmoor is a dark, masculine business template for barbershops and grooming studios, with a hero slider, cut styles, locations, services, a price list with opening hours, a team slider, testimonials, a gallery with lightbox and an appointment form.

Live Demo
brassmoor-tailwind.html
<%# letstemplates: access=premium category=business %>
<!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 | Tailwind CSS Business Template</title>
  <meta name="description" content="Brassmoor is a dark, masculine Tailwind CSS business template for barbershops and grooming studios, with a hero slider, cut styles, locations, services, a price list with opening hours, a team slider, testimonials, a gallery with lightbox and an appointment form.">
  <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" } %>
  <!-- block:start navbar -->
  <header class="relative z-40">
    <div class="mx-auto flex max-w-7xl flex-wrap items-center justify-between gap-3 px-5 pt-4 text-xs text-white/70 md:px-10">
      <p class="flex flex-wrap gap-6"><span><i class="fa-solid fa-clock mr-2 text-ember" aria-hidden="true"></i>09:00 – 19:00</span><span class="hidden sm:inline"><i class="fa-solid fa-calendar-days mr-2 text-ember" aria-hidden="true"></i>Tuesday – Saturday</span></p>
      <a href="tel:+16175550142" class="hover:text-ember"><i class="fa-solid fa-phone mr-2 text-ember" aria-hidden="true"></i>(617) 555-0142</a>
    </div>
    <nav class="mx-auto flex max-w-7xl items-center justify-between gap-4 px-5 py-5 md:px-10" aria-label="Main">
      <a href="index.html" class="font-display text-3xl font-semibold uppercase leading-none tracking-wide">Brass<span class="text-ember">moor</span>.</a>
      <ul class="hidden items-center gap-8 text-sm md:flex">
        <% nav.each do |slug, label| %>
          <% active = active_page == slug %>
          <li><a href="<%= slug %>.html" <%= 'aria-current="page"'.html_safe if active %> class="transition <%= active ? 'font-semibold text-white' : 'text-white/70 hover:text-ember' %>"><%= label %></a></li>
        <% end %>
      </ul>
      <button type="button" data-menu-toggle aria-controls="mobile-menu" aria-expanded="false" class="grid h-10 w-10 place-items-center border border-white/30 md:hidden" aria-label="Open menu"><i class="fa-solid fa-bars" aria-hidden="true"></i></button>
    </nav>
    <div id="mobile-menu" class="hidden border-y border-coal-line bg-coal-card px-5 py-4 md:hidden">
      <ul class="grid gap-1 font-display text-3xl">
        <% nav.each do |slug, label| %>
          <% active = active_page == slug %>
          <li><a href="<%= slug %>.html" <%= 'aria-current="page"'.html_safe if active %> class="block py-1 <%= active ? 'text-ember' : 'hover:text-ember' %>"><%= label %></a></li>
        <% end %>
      </ul>
    </div>
  </header>
  <!-- block:end navbar -->

  <% 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 -->

  <!-- block:start stats -->
  <section class="mx-auto max-w-7xl px-5 py-20 md:px-10 md:py-24">
    <div class="grid gap-12 lg:grid-cols-[1.4fr_1fr] lg:items-center">
      <div data-reveal>
        <h2 class="font-display text-5xl font-medium leading-[1.05] md:text-6xl">A barbershop is more than a trade, it's our <span class="mark">calling!</span></h2>
        <p class="mt-6 max-w-lg text-white/70">Three generations of barbers taught us the same lesson: a good cut is a craft, and a good chair is a place where people feel heard.</p>
      </div>
      <dl class="grid gap-6">
        <% [["18", "Years in the chair"], ["07", "Master barbers"]].each_with_index do |(value, label), index| %>
          <div class="flex items-center gap-5" data-reveal style="--reveal-delay: <%= index * 100 %>ms"><dt class="order-2 font-display text-2xl"><%= label %></dt><dd class="font-display text-7xl font-semibold leading-none text-ember"><span data-count="<%= value %>"><%= value %></span></dd></div>
        <% end %>
      </dl>
    </div>
    <div class="mt-16 flex items-center gap-6">
      <span class="h-px flex-1 bg-white/15"></span>
      <div class="text-center">
        <p class="flex justify-center gap-2 text-3xl text-gold"><% 5.times do %><i class="fa-solid fa-star" aria-hidden="true"></i><% end %><span class="sr-only">Rated 5 out of 5</span></p>
        <p class="mt-2 font-display text-xl text-white/70"><span data-count="4280">4280</span> reviews</p>
      </div>
      <span class="h-px flex-1 bg-white/15"></span>
    </div>
  </section>
  <!-- block:end stats -->

  <% shops = [["Downtown chair", "212 Hanover St, Boston, MA 02113"], ["Southside studio", "48 Dorchester Ave, Boston, MA 02127"], ["Harbor corner", "9 Seaport Blvd, Boston, MA 02210"]] %>
  <!-- block:start about -->
  <section class="mx-auto max-w-7xl px-5 md:px-10">
    <div class="relative isolate overflow-hidden px-6 pb-8 pt-20 text-center md:px-12 md:pt-28">
      <img src="https://images.unsplash.com/photo-1503951914875-452162b0f3f1?auto=format&fit=crop&w=1600&h=900&q=75" alt="Barber giving a straight-razor shave in a dim shop" class="absolute inset-0 -z-10 h-full w-full object-cover opacity-50">
      <div class="absolute inset-0 -z-10 bg-gradient-to-b from-coal/40 to-coal/80"></div>
      <h2 class="font-display text-5xl font-medium">Since 2008</h2>
      <p class="mx-auto mt-3 max-w-xl text-white/80">What started as one chair in a borrowed basement is now three shops across the city — same clippers, same coffee, same welcome.</p>
      <a href="about.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">Read our story</a>
      <ul class="mt-16 grid gap-4 md:grid-cols-3">
        <% shops.each_with_index do |(name, address), index| %>
          <li class="bg-coal-soft/90 p-6 text-left backdrop-blur" data-reveal style="--reveal-delay: <%= index * 100 %>ms"><p class="flex items-center gap-3 font-display text-2xl"><i class="fa-solid fa-location-dot text-ember" aria-hidden="true"></i><%= name %></p><p class="mt-2 text-sm text-white/70"><%= address %></p></li>
        <% end %>
      </ul>
    </div>
  </section>
  <!-- block:end about -->

  <% services = [["Shaving", "Hot towel, straight razor and a cool balm to finish.", "fa-pump-soap"], ["Styling", "Blow-dry, texture and the right product for your hair.", "fa-wind"], ["Trimming", "Beard shaping and line-ups to keep the edges clean.", "fa-scissors"], ["Haircut", "From skin fades to long scissor cuts, done properly.", "fa-user"]] %>
  <!-- block:start features -->
  <section class="mx-auto max-w-7xl px-5 py-20 md:px-10 md:py-28">
    <h2 class="text-center font-display text-5xl font-medium md:text-6xl">Service beyond <span class="mark">expectations</span></h2>
    <ul class="mt-14 grid gap-4 sm:grid-cols-2 lg:grid-cols-4 lg:items-center">
      <% services.each_with_index do |(name, text, icon), index| %>
        <li class="group border border-transparent bg-coal-card p-8 text-center transition hover:-translate-y-2 hover:border-ember <%= 'lg:-translate-y-6 border-ember' if index == 1 %>" data-reveal style="--reveal-delay: <%= index * 80 %>ms">
          <i class="fa-solid <%= icon %> text-4xl text-ember" aria-hidden="true"></i>
          <h3 class="mt-6 font-display text-3xl uppercase"><%= name %></h3>
          <p class="mt-2 text-sm text-white/70"><%= text %></p>
          <a href="services.html" class="mt-6 inline-block border border-ember px-4 py-2 text-xs font-semibold uppercase tracking-widest transition hover:bg-ember <%= 'bg-ember' if index == 1 %>">Read more</a>
        </li>
      <% end %>
    </ul>
  </section>
  <!-- block:end features -->

  <% prices = [["Vintage cut", "$32"], ["Beard trim", "$18"], ["Cut + hot shave", "$48"], ["Modern fade", "$36"]] %>
  <!-- block:start pricing -->
  <section class="mx-auto grid max-w-7xl gap-12 px-5 pb-20 md:px-10 md:pb-28 lg:grid-cols-[1fr_1.2fr] lg:items-center">
    <div>
      <h2 class="font-display text-5xl font-medium md:text-6xl">Price list</h2>
      <ul class="mt-10 grid gap-2">
        <% prices.each_with_index do |(name, price), index| %>
          <li class="flex items-baseline gap-3 py-3" data-reveal style="--reveal-delay: <%= index * 80 %>ms"><span class="font-display text-2xl"><%= name %></span><span class="flex-1 border-b-2 border-dashed border-white/20"></span><span class="font-display text-2xl text-ember"><%= price %></span></li>
        <% end %>
      </ul>
      <a href="services.html" class="mt-10 inline-block bg-ember px-7 py-4 text-xs font-semibold uppercase tracking-widest transition hover:bg-white hover:text-coal">View all</a>
    </div>
    <div class="relative pb-24 md:pb-0">
      <img src="https://images.unsplash.com/photo-1585747860715-2ba37e788b70?auto=format&fit=crop&w=1000&h=750&q=80" alt="Row of leather barber chairs under warm lights" class="ml-auto aspect-[4/3] w-full object-cover md:w-[78%]">
      <div class="absolute bottom-0 left-4 w-56 bg-coal-soft/95 p-7 text-center backdrop-blur md:bottom-auto md:left-0 md:top-1/4 md:w-60">
        <h3 class="font-display text-4xl">Opening hours</h3>
        <p class="mt-6 text-sm text-white/70">Tuesday – Saturday</p>
        <p class="mt-1 border-b border-white/15 pb-5 text-sm">09:00 – 19:00</p>
        <p class="mt-5 text-sm text-white/70">Sunday &amp; Monday closed</p>
      </div>
    </div>
  </section>
  <!-- block:end pricing -->

  <% team = [["Marcus Hale", "Master barber", "Twenty years with the clippers and still the first one in every morning. Ask him about pompadours.", "1534297635766-a262cdcb8ee4", "Barber with glasses cutting hair"], ["Dario Vance", "Beard specialist", "Turns any beard into a plan. Known for hot towels and long, honest chats.", "1622287162716-f311baa1a2b8", "Bearded barber working in a colourful shop"], ["Leo Park", "Fade expert", "Clean lines and zero-to-two fades in under thirty minutes, without rushing a thing.", "1605497788044-5a32c7078486", "Barber blow-drying a smiling client"]] %>
  <% strip = [["1517832606299-7ae9b720a186", "Beard trim in black and white"], ["1532710093739-9470acff878f", "Barber brushing shaving foam onto a beard"], ["1596728325488-58c87691e9af", "Barber shaving a client with a straight razor"]] %>
  <!-- block:start team -->
  <section class="pb-20 md:pb-28" data-team>
    <h2 class="px-5 text-center font-display text-5xl font-medium md:text-6xl">Meet our master <span class="mark">barbers</span></h2>
    <div class="mx-auto mt-14 grid max-w-7xl gap-8 px-5 md:px-10 lg:grid-cols-[1.3fr_1fr] lg:items-center">
      <div aria-live="polite">
        <% team.each_with_index do |(name, role, bio), index| %>
          <div data-member="<%= index %>" <%= 'hidden' unless index.zero? %>>
            <p class="font-display text-xl text-white/60"><%= role %></p>
            <h3 class="font-display text-5xl"><%= name %></h3>
            <p class="mt-4 max-w-xl text-white/70"><%= bio %></p>
            <div class="mt-6 flex gap-4 text-white/70">
              <a href="contact.html" class="hover:text-ember" aria-label="<%= name %> on Instagram"><i class="fa-brands fa-instagram" aria-hidden="true"></i></a>
              <a href="contact.html" class="hover:text-ember" aria-label="<%= name %> on Facebook"><i class="fa-brands fa-facebook-f" aria-hidden="true"></i></a>
            </div>
          </div>
        <% end %>
        <div class="mt-8 flex gap-3">
          <button type="button" data-member-prev class="grid h-11 w-11 place-items-center bg-ember transition hover:bg-white hover:text-coal" aria-label="Previous barber"><i class="fa-solid fa-chevron-left" aria-hidden="true"></i></button>
          <button type="button" data-member-next class="grid h-11 w-11 place-items-center bg-ember transition hover:bg-white hover:text-coal" aria-label="Next barber"><i class="fa-solid fa-chevron-right" aria-hidden="true"></i></button>
        </div>
      </div>
      <div class="relative aspect-[4/5] overflow-hidden">
        <% team.each_with_index do |(name, role, bio, photo, alt), index| %>
          <img src="https://images.unsplash.com/photo-<%= photo %>?auto=format&fit=crop&w=800&h=1000&q=80" alt="<%= alt %>" class="slide absolute inset-0 h-full w-full object-cover <%= index.zero? ? 'opacity-100' : 'opacity-0' %>" data-member-photo="<%= index %>">
        <% end %>
      </div>
    </div>
    <ul class="mx-auto mt-6 grid max-w-7xl grid-cols-3 gap-3 px-5 md:px-10 lg:w-[60%] lg:max-w-none lg:pr-0">
      <% strip.each do |(photo, alt)| %>
        <li><img src="https://images.unsplash.com/photo-<%= photo %>?auto=format&fit=crop&w=400&h=420&q=70" alt="<%= alt %>" class="aspect-square w-full object-cover" loading="lazy"></li>
      <% end %>
    </ul>
  </section>
  <!-- block:end team -->

  <% quotes = [["The best cut I've had in years — relaxed, sharp and the hot shave was quick too.", "Alex Moreno"], ["They listened, suggested something better and nailed it. I'm not going anywhere else.", "Jordan Blake"], ["Proper old-school service with a modern eye. The beard trim alone is worth it.", "Sam Whitfield"]] %>
  <!-- block:start reviews -->
  <section class="mx-auto max-w-7xl px-5 pb-20 md:px-10 md:pb-28" data-carousel>
    <div class="relative isolate overflow-hidden px-6 py-20 text-center md:px-24 md:py-28">
      <img src="https://images.unsplash.com/photo-1560066984-138dadb4c035?auto=format&fit=crop&w=1600&h=900&q=75" alt="" class="absolute inset-0 -z-10 h-full w-full object-cover">
      <div class="absolute inset-0 -z-10 bg-coal/70"></div>
      <i class="fa-solid fa-quote-left absolute left-8 top-4 text-7xl text-white/80 md:left-16 md:text-8xl" aria-hidden="true"></i>
      <% quotes.each_with_index do |(quote, name), index| %>
        <figure data-slide <%= 'hidden' unless index.zero? %>>
          <blockquote class="font-display text-3xl font-medium leading-tight md:text-5xl"><%= quote %></blockquote>
          <figcaption class="mt-6"><span class="block font-display text-xl"><%= name %></span><span class="text-gold"><% 5.times do %><i class="fa-solid fa-star" aria-hidden="true"></i><% end %></span></figcaption>
        </figure>
      <% end %>
      <button type="button" data-carousel-prev class="absolute left-4 top-1/2 grid h-10 w-10 -translate-y-1/2 place-items-center bg-ember transition hover:bg-white hover:text-coal md:left-8" aria-label="Previous review"><i class="fa-solid fa-chevron-left" aria-hidden="true"></i></button>
      <button type="button" data-carousel-next class="absolute right-4 top-1/2 grid h-10 w-10 -translate-y-1/2 place-items-center bg-ember transition hover:bg-white hover:text-coal md:right-8" aria-label="Next review"><i class="fa-solid fa-chevron-right" aria-hidden="true"></i></button>
      <div class="mt-8 flex justify-center gap-2">
        <% quotes.each_with_index do |quote, index| %>
          <button type="button" data-carousel-dot aria-current="<%= index.zero? %>" class="h-2 w-6 bg-white/30 transition aria-[current=true]:w-10 aria-[current=true]:bg-ember" aria-label="Show review <%= index + 1 %>"></button>
        <% end %>
      </div>
    </div>
  </section>
  <!-- block:end reviews -->

  <% gallery = [["1585747860715-2ba37e788b70", "Long row of barber chairs and mirrors", "row-span-2"], ["1605497788044-5a32c7078486", "Barber blow-drying a client's hair", "row-span-2"], ["1512690459411-b9245aed614b", "Vintage leather barber chair", ""], ["1622287162716-f311baa1a2b8", "Barber at work in a colourful shop", "row-span-2"], ["1503951914875-452162b0f3f1", "Straight-razor shave in a dim room", ""], ["1596728325488-58c87691e9af", "Close-up of a beard being shaved", ""]] %>
  <!-- block:start gallery -->
  <section class="mx-auto max-w-7xl px-5 pb-20 md:px-10 md:pb-28" data-lightbox-root>
    <h2 class="font-display text-5xl font-medium md:text-6xl">Barbershop gallery</h2>
    <ul class="mt-10 grid auto-rows-[10rem] grid-cols-2 gap-3 md:auto-rows-[12rem] md:grid-cols-3">
      <% gallery.each_with_index do |(photo, alt, span), index| %>
        <li class="<%= span %>"><button type="button" class="group relative block h-full w-full overflow-hidden" data-lightbox-open="<%= photo %>" data-alt="<%= alt %>" aria-label="Open photo: <%= alt %>"><img src="https://images.unsplash.com/photo-<%= photo %>?auto=format&fit=crop&w=700&h=800&q=70" alt="<%= alt %>" class="h-full w-full object-cover transition duration-500 group-hover:scale-105" loading="lazy"><span class="absolute inset-0 grid place-items-center bg-coal/60 opacity-0 transition group-hover:opacity-100"><i class="fa-solid fa-plus grid h-12 w-12 place-items-center bg-ember text-lg" aria-hidden="true"></i></span></button></li>
      <% end %>
    </ul>
    <dialog data-lightbox class="w-[min(60rem,92vw)] bg-coal-card p-0 text-white backdrop:bg-black/80">
      <img data-lightbox-image src="https://images.unsplash.com/photo-1585747860715-2ba37e788b70?auto=format&fit=crop&w=1400&h=900&q=80" alt="" class="aspect-[14/9] w-full object-cover">
      <div class="flex items-center justify-between gap-4 p-4"><p class="text-sm text-white/70" data-lightbox-caption></p><button type="button" data-lightbox-close class="grid h-10 w-10 place-items-center bg-ember" aria-label="Close"><i class="fa-solid fa-xmark" aria-hidden="true"></i></button></div>
    </dialog>
  </section>
  <!-- block:end gallery -->

  <!-- block:start contact -->
  <section class="relative isolate overflow-hidden py-20 md:py-28">
    <img src="https://images.unsplash.com/photo-1512690459411-b9245aed614b?auto=format&fit=crop&w=1800&h=900&q=70" alt="" class="absolute inset-0 -z-10 h-full w-full object-cover opacity-30">
    <div class="mx-auto max-w-7xl px-5 md:px-10">
      <h2 class="font-display text-5xl font-medium md:text-6xl">Book an appointment</h2>
      <p class="mt-3 max-w-lg text-white/70">Pick a day and we'll hold a chair for you. We'll text to confirm within the hour.</p>
      <form class="mt-10 grid gap-3 md:grid-cols-[1fr_1fr_1fr_auto]" data-booking novalidate>
        <label class="sr-only" for="book-name">Your name</label>
        <input id="book-name" name="name" required autocomplete="name" placeholder="Your name" class="bg-white px-5 py-4 text-coal placeholder:text-coal/50 focus:outline-none focus:ring-4 focus:ring-ember/50">
        <label class="sr-only" for="book-phone">Phone</label>
        <input id="book-phone" name="phone" type="tel" required autocomplete="tel" placeholder="Phone number" class="bg-white px-5 py-4 text-coal placeholder:text-coal/50 focus:outline-none focus:ring-4 focus:ring-ember/50">
        <label class="sr-only" for="book-date">Date</label>
        <input id="book-date" name="date" type="date" required class="bg-white px-5 py-4 text-coal focus:outline-none focus:ring-4 focus:ring-ember/50">
        <button type="submit" class="bg-ember px-8 py-4 text-xs font-semibold uppercase tracking-widest transition hover:bg-white hover:text-coal">Book now</button>
      </form>
      <p class="mt-4 text-sm text-white/80" data-form-status role="status"></p>
    </div>
  </section>
  <!-- block:end contact -->

  <!-- block:start footer -->
  <footer class="bg-coal">
    <div class="mx-auto max-w-7xl px-5 pt-16 md:px-10">
      <a href="index.html" class="block text-center font-display text-4xl font-semibold uppercase">Brass<span class="text-ember">moor</span>.</a>
      <div class="mt-12 grid gap-10 sm:grid-cols-2 lg:grid-cols-4">
        <div>
          <h2 class="font-display text-2xl">Pages</h2>
          <ul class="mt-4 grid gap-2 text-sm text-white/70">
            <% nav.each do |slug, label| %><li><a href="<%= slug %>.html" class="hover:text-ember"><%= label %></a></li><% end %>
          </ul>
        </div>
        <div>
          <h2 class="font-display text-2xl">Services</h2>
          <ul class="mt-4 grid gap-2 text-sm text-white/70">
            <% services.each do |(name)| %><li><a href="services.html" class="hover:text-ember"><%= name %></a></li><% end %>
          </ul>
        </div>
        <div>
          <h2 class="font-display text-2xl">Contact</h2>
          <ul class="mt-4 grid gap-2 text-sm text-white/70">
            <li><i class="fa-solid fa-phone mr-2 text-ember" aria-hidden="true"></i>(617) 555-0142</li>
            <li><i class="fa-solid fa-envelope mr-2 text-ember" aria-hidden="true"></i><a href="mailto:chair@brassmoor.com" class="hover:text-ember">chair@brassmoor.com</a></li>
            <li><i class="fa-solid fa-clock mr-2 text-ember" aria-hidden="true"></i>Tue – Sat, 09:00 – 19:00</li>
          </ul>
        </div>
        <div>
          <h2 class="font-display text-2xl">Stay in the loop</h2>
          <p class="mt-4 text-sm text-white/70">Offers and new barbers, once a month.</p>
          <form class="mt-4 flex" data-subscribe novalidate>
            <label for="footer-email" class="sr-only">Email</label>
            <input id="footer-email" name="email" type="email" required placeholder="Email address" class="min-w-0 flex-1 bg-coal-soft px-4 py-3 text-sm focus:outline-none focus:ring-2 focus:ring-ember">
            <button type="submit" class="grid w-12 place-items-center bg-ember transition hover:bg-white hover:text-coal" aria-label="Subscribe"><i class="fa-solid fa-paper-plane" aria-hidden="true"></i></button>
          </form>
          <p class="mt-2 text-xs text-white/70" data-subscribe-status role="status"></p>
        </div>
      </div>
      <ul class="mt-14 grid gap-6 border-t border-coal-line pt-10 text-sm text-white/70 md:grid-cols-3">
        <% shops.each do |(name, address)| %>
          <li class="flex gap-3"><i class="fa-solid fa-location-dot mt-1 text-ember" aria-hidden="true"></i><span><span class="block font-display text-xl text-white"><%= name %></span><%= address %></span></li>
        <% end %>
      </ul>
    </div>
    <p class="mt-10 border-t border-coal-line px-5 py-6 text-center text-xs text-white/50">&copy; 2026 Brassmoor Barbers. All rights reserved.</p>
  </footer>
  <!-- block:end footer -->

  <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>

Unlimited access to all templates

Unlock our entire library of premium UI kits, dashboards, and landing pages. Renews automatically, cancel anytime.

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

More Business Templates