Modern SaaS landing page
rails app:template LOCATION='https://perron.railsdesigner.com/library/saas-lander/template.rb'
This template helps you launch fast and start validating your idea. It's built with a bold gradient hero that grabs attention, clear feature highlights that explain your value, and a sticky email form that stays visible as people scroll.
Perfect for getting your SaaS in front of people and building your waitlist without overthinking the design.
Template source
gem "tailwindcss-rails" after_bundle do rails_command "tailwindcss:install" rails_command "generate content page show" file "app/assets/tailwind/application.css" do <<~"_" @import "tailwindcss"; @layer utilities { .animate-zoom-in { animation: zoom-in 1.1s ease forwards; } .animate-fade-up { animation: fade-up 1s ease forwards; } .animate-fade-down { animation: fade-down 1s ease forwards; } .noise::after { content: ""; position: absolute; inset: 0; opacity: .15; background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 300 300' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.2' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E"); pointer-events: none; mask-image: linear-gradient(to bottom, black 0%, black 40%, transparent 100%); } @keyframes zoom-in { 0% { opacity: 0; transform: scale(.97); filter: blur(5px); } 100% { opacity: 1; transform: scale(1); filter: blur(0); } } @keyframes fade-up { 0% { opacity: 0; transform: translateY(2.5rem); } 100% { opacity: 1; transform: translateY(0); } } @keyframes fade-down { 0% { opacity: 0; transform: translateY(-2.5rem); } 100% { opacity: 1; transform: translateY(0); } } } _ end file "app/content/data/features.yml" do <<~"_" - name: Automate The Tedious description: Transform complex tasks into one-click wonders. Let the robots handle the boring stuff. - name: Stay Fresh Without Effort description: Set it and forget it. Real-time updates keep everything current while you focus on what matters. - name: Make It Yours description: Personalization that goes beyond color schemes. Your brand, your rules, your way. - name: Highlight What Matters description: Draw attention to what's important. Guide your audience with crystal-clear visual communication. - name: Own Your Presence description: Your space, your domain. Present a professional image that's completely yours. - name: Learn From The Journey description: Track progress, spot trends, and celebrate how far you've come with comprehensive historical insights. _ end file "app/content/pages/root.erb" do <<~"_" --- title: Simplicity meets power dscription: Because great things happen when you have the right tools to bring your vision to life. --- <div class="flex flex-col w-full bg-gradient-to-b from-blue-950 via-blue-500 to-white noise"> <section class="w-full max-w-3xl mx-auto pt-8 px-3 pb-32 md:pt-48"> <h1 class="text-4xl font-extrabold tracking-tight text-center text-balance text-white animate-zoom-in md:text-6xl"> Simplicity meets power </h1> <p class="max-w-2xl mx-auto mt-4 text-2xl font-extralight text-center text-cyan-100 animate-zoom-in delay-200 md:text-3xl md:mt-8"> Because great things happen when you have the right tools to bring your vision to life. No complexity, just clarity. </p> </section> <img src="https://placehold.co/1280x720/dbeafe/172554?text=Screenshot" alt="Preview of my great new SaaS" class="w-full max-w-5xl mx-auto rounded-2xl border-10 border-white/40 ring ring-offset-0 ring-white/60 animate-zoom-in delay-300" /> </div> <%= render partial: "shared/features", locals: { features: Perron::Site.data("features") } %> <%= render partial: "shared/email_form" %> _ end file "app/content/pages/success.erb" do <<~"_" --- title: You are on the list --- <div class="relative flex flex-col w-full md:h-[80dvh] bg-gradient-to-b from-blue-950 via-blue-500 to-white noise"> <section class="w-full max-w-3xl mx-auto pt-8 px-3 pb-32 md:pt-48"> <h1 class="text-4xl font-extrabold tracking-tight text-center text-balance text-white animate-zoom-in md:text-6xl"> Awesome. You are on the list! </h1> <p class="max-w-2xl mx-auto mt-4 text-2xl font-extralight text-center text-cyan-100 animate-zoom-in delay-200 md:text-3xl md:mt-8"> Thanks for being interested in my new SaaS. You have been added to the list and I will be in touch soon. </p> </section> </div> _ end file "app/controllers/content/pages_controller.rb" do <<~"_" class Content::PagesController < ApplicationController include Perron::Root def show @resource = Content::Page.find(params[:id]) end end _ end file "app/views/content/pages/show.html.erb" do <<~"_" <%= @resource.content %> _ end file "app/views/layouts/application.html.erb" do <<~"_" <!DOCTYPE html> <html> <head> <%= meta_tags %> <meta name="viewport" content="width=device-width,initial-scale=1"> <%= yield :head %> <link rel="icon" href="/icon.png" type="image/png"> <link rel="icon" href="/icon.svg" type="image/svg+xml"> <link rel="apple-touch-icon" href="/icon.png"> <%= stylesheet_link_tag :app %> </head> <body class="antialiased"> <%= yield %> </body> </html> _ end file "app/views/shared/_email_form.html.erb" do <<~"_" <div class="fixed inset-x-0 bottom-4 z-10 flex items-center justify-center animate-fade-up md:bottom-12"> <div class="w-full max-w-2xl px-3 lg:px-10"> <div class="-mx-1.5 w-[calc(100%+0.625rem)] rounded-2xl border border-gray-200/60 bg-white/60 p-1.5 backdrop-blur"> <form action="<%= page_path("success") %>" name="email" class="relative flex items-center w-full p-2 overflow-hidden bg-white shadow-2xl rounded-xl outline outline-1 outline-gray-300/60"> <div class="w-full"> <label for="email" class="sr-only">Email</label> <input type="email" name="email" id="email" required placeholder="you@company.com" class="w-full px-2 text-sm font-light tracking-wide border-0 text-gray-800 placeholder:text-gray-400 focus:ring-0 focus-visible:outline-none"> </div> <input type="submit" value="Subscribe" class="px-3.5 py-2 text-xs font-medium text-white cursor-pointer flex-shrink-0 bg-blue-600 rounded-md transition-colors duration-200 hover:bg-blue-500"> </form> </div> </div> </div> _ end file "app/views/shared/_features.html.erb" do <<~"_" <%# locals: (features:, container_css: "relative px-3 py-16 md:py-24 lg:py-40") -%> <%= tag.section class: container_css do %> <ul class="max-w-6xl mx-auto grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-8 md:gap-6 lg:gap-10"> <% features.each do |feature| %> <li class="relative"> <span class="absolute w-16 h-0.25 bg-blue-200 rounded-sm"></span> <div class="mt-3"> <h3 class="inline font-medium tracking-tight text-gray-950 after:content-['—']"> <%= feature.name %> </h3> <p class="inline ml-0.5 font-light text-gray-800"> <%= feature.description %> </p> </div> </li> <% end %> </ul> <% end %> _ end end