Revolution Documentation

01 · Overview

Introduction

Everything you need to know before you open your editor — what’s in the box, what it’s built with, and what it expects from your machine.

Welcome

Thanks for picking up Revolution, a creative-agency website template for Astro. This guide walks through installing the template, understanding how the project is organized, customizing content and styling, and shipping it to production. It’s written for developers who are comfortable with the command line but new to this specific codebase — if you already know Astro well, feel free to skip ahead using the sidebar.

What’s Included

Source files

The full project source in the revolution-astro/ folder — components, pages, data files, blog posts, scripts, styles, and configuration.

Production build

Not pre-built — no dist/ folder ships in the package. Run npm run build to generate the static site (see Build & Preview).

Design assets

Demo images (logos, photos, client logos) in public/images/, the Satoshi font files in public/fonts/, and an inline SVG icon set in src/data/icons.js. No Figma or other design source files are included.

This documentation

Hosted online — the link is in documentation.txt, next to the template folder in the download.

Features

  • Agency home page with a large hero, services slider, work marquee and grid, awards band, and testimonials
  • About, Service, and Contact pages, plus a standalone 404 page
  • Seven portfolio grid layouts (2, 3, 4, 5, and 6 columns, 3-column masonry, 4-column metro) with category filtering, category pages, and 12 single-project page variants
  • Markdown blog with five post formats (standard, gallery, video, audio, quote), pagination, and category, tag, author, and format archives
  • GSAP entrance and scroll animations that respect prefers-reduced-motion, plus Swiper sliders, a lightbox, masonry, parallax, and magnetic buttons
  • All page copy kept in plain JavaScript data files under src/data/
  • Canonical, Open Graph, and Twitter card tags on every page, and an automatic sitemap-index.xml
  • Self-hosted fonts — no requests to Google Fonts

Technology Stack

Revolution is built with plain .astro components, vanilla JavaScript, and plain CSS — no UI framework, no TypeScript, and no CSS preprocessor or utility framework.

Astro 7 Vite JavaScript CSS GSAP Swiper Fontsource

Requirements

ToolMinimum versionNotes
Node.js22.12.0 or laterSet in package.json under engines; Astro 7 requires it
Package managernpm (bundled with Node.js)A package-lock.json is included, so npm is the default; pnpm or Yarn also work
Code editor—VS Code with the Astro extension (recommended in .vscode/extensions.json) is a comfortable default
Git2.xOnly required if you plan to version-control your customized copy

Browser Support

The built site targets the last two versions of all evergreen browsers:

Chrome Firefox Safari Edge
Internet Explorer

Internet Explorer 11 is not supported. Revolution ships no polyfills.

02 · Setup

Getting Started

From a downloaded ZIP to a running dev server in five steps.

Installation & Extraction

  1. Download revolution-astro-package.zip from your purchase confirmation email or account downloads page.
  2. Extract it to a folder on your machine — avoid folder names with spaces or special characters.
  3. Inside you’ll find the revolution-astro/ template folder and documentation.txt. Open revolution-astro/ in your terminal and in your code editor.
bash
# Extract on macOS / Linux
unzip revolution-astro-package.zip
cd revolution-astro-package/revolution-astro

Node.js Setup

Check which version of Node.js is currently active:

bash
node --version
# v22.12.0 or later

If you manage multiple Node versions, tools like nvm or fnm make switching painless:

bash
nvm install 22
nvm use 22

Install Dependencies

From the project root, install packages with your preferred package manager:

npm
npm install
pnpm
pnpm install
yarn
yarn install

This reads package.json and downloads every dependency into a local node_modules/ folder. It only needs to be re-run when dependencies change.

Run the Development Server

npm
npm run dev
pnpm
pnpm dev

This starts Vite’s local dev server with hot module reloading — edits to components, pages, and styles appear in the browser immediately without a manual refresh. By default it’s available at http://localhost:4321; the terminal output will confirm the exact address.

Build for Production & Preview

bash
# Compile an optimized production build into dist/
npm run build

# Serve that build locally to sanity-check it
npm run preview

build validates the blog content against its schema, renders every page to static HTML, bundles and minifies CSS/JS, and writes the output to dist/. preview serves that exact output locally so you can verify it before deploying — it does not rebuild automatically, so re-run build after further changes.

Which command do I actually need?

Use dev while you’re customizing the template. Use build, then preview, once before you deploy — it catches issues (like invalid blog front matter or a missing import on a page you didn’t open) that the dev server tolerates but production won’t.

03 · Reference

Project Structure

A tour of the folders you’ll actually touch, and the ones you generally shouldn’t.

PathPurpose
public/Files here are copied to the output root untouched — no optimization or hashing. Revolution keeps all its images here (public/images/) along with the Satoshi fonts and favicons.
src/components/Header, Footer, and Icon at the top level; page sections in sections/<page>/ (home, about, service, contact, portfolio, blog, post, archive, error) and sections/shared/; small building blocks in ui/.
src/layouts/Wrapper templates that define the common page shell — <head> tags, header, footer — so individual pages only supply their unique content.
src/pages/Every file here becomes a route automatically. See Pages for details.
src/content/blog/One Markdown file per blog post; the front matter schema lives in src/content.config.js. See Content Editing.
src/scripts/Small vanilla JavaScript modules for client-side behavior: reveal.js, magnetic.js, parallax.js, lightbox.js, masonry.js, cursor-link.js, share-links.js.
src/styles/A single global.css with the font declarations, CSS variables, and base styles. See Styling.
src/data/Page copy and settings as plain JavaScript exports — site.js, navigation.js, footer.js, home.js, about.js, service.js, contact.js, portfolio*.js, blog.js, and more — kept separate from markup so it’s easy to edit without touching Astro templates.
Files not shown above

.vscode/ (recommends the Astro extension) and .gitignore also sit at the project root. node_modules/, .astro/, and dist/ are created by npm install, npm run dev, and npm run build — don’t edit them by hand.

04 · Reference

Configuration

The files that control how the project builds, and where to set site-wide values.

package.json

Defines dependencies and the scripts you run from the terminal (dev, build, preview). Add your own script here if you introduce a new tool.

jsonpackage.json
{
  "name": "revolution-astro",
  "type": "module",
  "version": "0.0.1",
  "engines": { "node": ">=22.12.0" },
  "scripts": {
    "dev": "astro dev",
    "build": "astro build",
    "preview": "astro preview",
    "astro": "astro"
  },
  "dependencies": {
    "@astrojs/sitemap": "^3.7.4",
    "@fontsource-variable/outfit": "^5.3.0",
    "@fontsource/manrope": "^5.3.0",
    "@fontsource/playfair-display": "^5.3.0",
    "astro": "^7.3.8",
    "gsap": "^3.15.0",
    "swiper": "^14.3.0"
  }
}

astro.config.mjs

Controls integrations, the deployed site URL, and build behavior. Revolution uses one integration, @astrojs/sitemap, and renders every page statically. Replace site with your real domain before deploying.

javascriptastro.config.mjs
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';

export default defineConfig({
  // Replace with the deployed URL: used for canonical links, Open Graph images and the sitemap.
  site: 'https://example.com',
  // The dev toolbar overlays the page and would show up in screenshot comparisons.
  devToolbar: { enabled: false },
  integrations: [sitemap()],
});

tsconfig.json

Revolution contains no TypeScript code — this is Astro’s default file, kept so your editor understands .astro files and Astro’s generated types. No path aliases are defined, so imports use relative paths (e.g. ../components/Header.astro).

jsontsconfig.json
{
  "extends": "astro/tsconfigs/strict",
  "include": [".astro/types.d.ts", "**/*"],
  "exclude": ["dist"]
}

Environment Variables (.env)

Revolution doesn’t read any environment variables out of the box, so no .env file is needed to run it. If you add an integration that needs one, create a .env file in the project root for values that differ between environments (API URLs, keys) or shouldn’t be committed to Git. Variables must be prefixed PUBLIC_ to be readable in client-side code.

env.env
# Exposed to the browser — never put secrets here
PUBLIC_SITE_URL=https://example.com

# Server-only — safe for secrets, not readable in browser code
API_SECRET_KEY=your_secret_key
Never commit .env

Add .env to .gitignore (it usually is by default). Anything without the PUBLIC_ prefix should stay off the client entirely.

05 · Guide

Pages

How routes are created, and which file builds which URL.

Creating a New Page

Astro uses file-based routing: any .astro or .md file placed in src/pages/ automatically becomes a route matching its file path. Revolution’s routes:

text
src/pages/index.astro                      →  /
src/pages/about.astro                      →  /about
src/pages/service.astro                    →  /service
src/pages/contact.astro                    →  /contact
src/pages/portfolio-3-column.astro         →  /portfolio-3-column  (+ 2, 4, 5, 6-column, masonry, metro)
src/pages/portfolio/[slug].astro           →  /portfolio/:slug  (12 project pages)
src/pages/portfolio-category/[slug].astro  →  /portfolio-category/:slug
src/pages/blog/[...page].astro             →  /blog, /blog/page/2 …
src/pages/[slug].astro                     →  /:post-slug  (single blog posts)
src/pages/category/[slug]/[...page].astro  →  /category/:slug
src/pages/tag/[slug]/[...page].astro       →  /tag/:slug
src/pages/author/[slug]/[...page].astro    →  /author/:slug
src/pages/type/[slug]/[...page].astro      →  /type/:format  (gallery, video, audio, quote)
src/pages/404.astro                        →  any unmatched URL

To add a page, create a new file in src/pages/ using the layout and structure of an existing, similar page as a starting point:

astrosrc/pages/services.astro
---
import Layout from '../layouts/Layout.astro';
import Testimonials from '../components/sections/shared/Testimonials.astro';
import { testimonials } from '../data/testimonials.js';
---
<Layout title="Services – Revolution – Agency" description="What we do.">
  <Testimonials {...testimonials} />
</Layout>

Editing an Existing Page

Revolution’s pages are thin: each file under src/pages/ imports its sections and passes them data from src/data/. To change what a page says, edit its data file (e.g. src/data/about.js); to change how a section looks or is structured, edit the component in src/components/sections/; to add, remove, or reorder sections, edit the page file itself.

Dynamic Routes

Files wrapped in square brackets, such as [slug].astro, generate multiple pages from a single template — commonly used for blog posts or product pages. Astro requires a getStaticPaths() function for these on statically-generated sites. Revolution’s project pages are built from the list in src/data/portfolio-projects.js:

astrosrc/pages/portfolio/[slug].astro
---
import { projectPages } from '../../data/portfolio-projects.js';

export function getStaticPaths() {
  return projectPages.map((page) => ({ params: { slug: page.slug }, props: { page } }));
}
---

Layouts

Layouts (in src/layouts/) wrap page content with shared structure — the <html> shell, <head> metadata, header, and footer. Revolution has three: Layout.astro (used by every page), PostLayout.astro (single blog posts), and ArchiveLayout.astro (category, tag, author, and format archives), and the last two render inside Layout. Page content is injected via a <slot />:

astrosrc/layouts/Layout.astro (simplified)
---
const {
  title = site.name,
  description = '',
  image,                  // Open Graph image
  headerScheme = 'dark',   // 'dark' or 'light' header logo/text
  footerMarquee = true,  // show the "Talk to us" marquee in the footer
  background,             // page background color
  chrome = true,         // false hides header and footer (404)
  noindex = false,
} = Astro.props;
---
<html lang="en">
  <head><title>{title}</title></head>
  <body>
    {chrome && <Header scheme={headerScheme} />}
    <main class="site-main"><slot /></main> <!-- page content is injected here -->
    {chrome && <Footer marquee={footerMarquee} />}
  </body>
</html>

Page Metadata & the 404 Page

Pass a title and description into your layout’s props on every page for correct browser tabs and search results (see also SEO). A file named 404.astro in src/pages/ is served automatically whenever no route matches; Revolution’s renders without header and footer (chrome={false}), is marked noindex, and takes its copy from src/data/not-found.js.

06 · Guide

Components

The building blocks pages are assembled from, and how data flows into them.

Reusable Components

Components live in src/components/ and encapsulate one piece of UI — a button, a pricing card, a navigation bar — so it can be reused across pages without copy-pasting markup. Every component in Revolution is a plain .astro file, organized as:

  • Header.astro, Footer.astro — site chrome, including the menus
  • Icon.astro — renders an SVG from src/data/icons.js by name
  • sections/<page>/ — sections used by one page type (home/Hero.astro, about/AboutTeam.astro, contact/ContactMain.astro, portfolio/CaseHero.astro…)
  • sections/shared/ — sections reused across pages (Testimonials, PortfolioGrid, PostCard, FeaturedVideo…)
  • ui/ — small building blocks: CtaCircle, LineLink, Marquee

Props

Props are the inputs a component accepts, similar to HTML attributes. Revolution uses plain JavaScript (no TypeScript interfaces), so each component documents its props by destructuring them from Astro.props at the top of the file, with defaults where a prop is optional:

astrosrc/components/ui/CtaCircle.astro
---
// Round call-to-action: a colour fill rises from the top on hover,
// and the button drifts toward the cursor.
const {
  href,
  label,
  size = 136,
  background = 'var(--color-primary)',
  fill = 'var(--color-accent)',
  color = 'var(--color-white)',
  hoverColor = color,
  fontSize = 15,
  letterSpacing = '0',
  class: className,
} = Astro.props;
---

In plain language: href and label are expected on every use; everything else falls back to its default, so <CtaCircle href="/contact/" label="Let’s Talk" /> renders a 136 px black circle with a green hover fill. Nothing checks prop types at build time, so a misspelled prop name is simply ignored — compare against the destructuring block if a prop seems to have no effect.

Component Composition

Larger sections are usually composed from smaller components rather than written as one long block of markup:

astrosrc/pages/index.astro
<Layout title={page.title} description={page.description}>
  <Hero {...hero} />
  <ServicesSlider {...services} />
  <GlobalPresence {...presence} />
  <FeaturedVideo {...featuredVideo} />
  <WorkMarquee {...workMarquee} />
  <WorkGrid {...workGrid} />
  <AwardsBand {...awards} />
  <Testimonials {...testimonials} />
</Layout>

Sections, in turn, compose the ui/ pieces — Hero uses CtaCircle, and many sections use Icon and LineLink.

Passing Data to Components

In Astro, props are passed the same way as HTML attributes and read via Astro.props:

astro
---
import LineLink from '../components/ui/LineLink.astro';
---
<LineLink href="/service/" label="Discover" />

Page sections usually receive a whole object from a data file via the spread syntax (<Hero {...hero} />), so each key of the hero export in src/data/home.js becomes a prop.

Where the interactivity lives

Components render to static HTML. Interactive behavior — the menus, sliders, portfolio filtering, form validation, animations — comes from a <script> block at the bottom of the relevant .astro file (importing GSAP or Swiper as needed) or from a shared module in src/scripts/. Astro bundles these automatically; no client: directives or UI framework are involved.

07 · Guide

Styling

Where to change colors, type, and spacing without fighting the build.

Global Styles & CSS Variables

Site-wide values — color palette, font families, type scale, container widths — are defined once as CSS custom properties at the top of src/styles/global.css (imported by Layout.astro) and referenced everywhere else. Change a value here and it updates across the whole site.

csssrc/styles/global.css
:root {
  /* Colors */
  --color-primary: #000000;
  --color-secondary: #202020;
  --color-text: #48495d;
  --color-accent: #00a57d;
  --color-yellow: #ffca50;
  --color-mint: #eefff8;
  --color-sky: #ecf6ff;
  --color-blush: #fff3f8;
  --color-peach: #fff4f3;
  --color-lavender: #f0e8fd;
  --color-white: #ffffff;

  /* Fonts */
  --font-heading: 'Outfit Variable', 'Outfit', sans-serif;
  --font-body: 'Satoshi', sans-serif;

  /* Layout */
  --container: 1200px;
  --container-wide: 1220px;
  --header-height: 111px;
}

Component Styles

Astro components support scoped <style> blocks — CSS written there only applies to that component, so class names never collide across files.

astro
<style>
  .line-link { color: var(--color-primary); }
  .line-link__text { font-family: var(--font-heading); }
</style>

Every Revolution component keeps its own styles in such a block, so to restyle a section, open its .astro file rather than searching global.css.

Responsive Design, Typography & Spacing

The type scale (--text-display, --text-marquee, and their line heights) and container widths are tokens in global.css, and are re-declared at each breakpoint there. The breakpoints used throughout the components are: mobile ≤767px, mobile landscape ≤880px, tablet ≤1024px, tablet landscape ≤1200px, and laptop ≤1366px. Prefer adjusting these shared values over overriding sizes on individual elements, so the site keeps a consistent rhythm as you edit it.

Avoid editing dist/

The dist/ folder is generated output — any change made there is overwritten on the next build. Always edit files under src/.

08 · Guide

Images & Assets

Where Revolution keeps its images, and how to swap them.

LocationUse forBehavior
public/images/Every image in the template, in folders per page: home/, about/, service/, portfolio/, blog/, shared/ (logos, client logos, menu image)Copied as-is, no processing; referenced by URL, e.g. /images/home/hero-bg.jpg
public/ rootfavicon.svg and favicon.icoCopied as-is, no processing
src/assets/Not used by Revolution — create it if you want Astro’s optimized <Image> pipeline for new imagesOptimized, resized, and content-hashed by the build

Replacing Images

Swap the file in public/images/ in place (keeping the same filename), or drop in a new file and update the path where it’s referenced — almost always in a src/data/*.js file or in a blog post’s front matter. Keep the width/height values in step with the new image so the layout doesn’t shift while it loads:

javascriptsrc/data/home.js
export const hero = {
  background: '/images/home/hero-bg.jpg',
  lineOne: { word: 'Creative', image: { src: '/images/home/hero-portrait-right.png', width: 147, height: 176 } },
  // …
};

The logo is set in src/data/site.js (logo.dark and logo.light, both SVGs in public/images/shared/).

Supported Formats & SVGs

Because images are served from public/ through plain <img> tags and CSS backgrounds, any format the browser supports works — the demo uses JPEG, PNG, and SVG. The logos are SVG files referenced as <img> sources; UI icons are inline SVG paths rendered by Icon.astro so they inherit color via currentColor (see Icons).

Image Optimization

Files in public/ are not optimized by the build, so resize and compress images before adding them. The demo ships pre-cut sizes for listings — e.g. a blog post has a full image, a 1200×790 thumbnail, and a 650×544 card crop — and it’s worth keeping that pattern for your own images. Images below the fold use loading="lazy".

Rule of thumb

To stay consistent with the rest of the template, put new images in the matching public/images/<page>/ folder and reference them by root-relative URL. Only move to src/assets/ + astro:assets if you want build-time optimization, which also means changing the component that renders the image.

09 · Guide

Fonts & Icons

Swapping typefaces and icon sets without touching every component.

Google Fonts (via Fontsource)

Revolution doesn’t link to the Google Fonts CDN. Its Google-hosted typefaces are installed as npm packages from Fontsource and bundled with the site: Outfit (headings, imported in global.css), Manrope (blog post body, imported in PostLayout.astro), and Playfair Display (the quote in QuoteSplit.astro). To swap one, install the new Fontsource package, change the import, then update the matching --font-* variable:

csssrc/styles/global.css
@import '@fontsource-variable/outfit';

:root {
  --font-heading: 'Outfit Variable', 'Outfit', sans-serif;
}

Local Fonts

The body font, Satoshi (weights 500 and 700), is self-hosted in public/fonts/ and declared with @font-face at the top of global.css. Layout.astro also preloads satoshi-500.woff2, so update that <link rel="preload"> if you replace it:

csssrc/styles/global.css
@font-face {
  font-family: 'Satoshi';
  src: url('/fonts/satoshi-500.woff2') format('woff2'), url('/fonts/satoshi-500.woff') format('woff');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

Icon Libraries

Revolution uses no external icon library. Its icons (arrows, close, social logos, and so on) are stored as SVG path data in src/data/icons.js and rendered by src/components/Icon.astro, which takes a name, an optional size, and an optional class, and fills with currentColor:

astro
<Icon name="arrow-up-right" size={13} />

Replacing & Adding Icons

To change an icon’s color, set color on its parent in CSS. To add a new icon, add an entry to src/data/icons.js with the SVG’s viewBox and the d attribute of its single <path>, then use it by name. Icons made of several paths need to be merged into one path first (most SVG editors can do this). Social icons are picked by name in src/data/navigation.js and src/data/footer.js.

javascriptsrc/data/icons.js
export const icons = {
  "arrow-right": {
    "viewBox": "0 0 17 17",
    "d": "M12.8112 7.37197L7.44725 2.00796…"
  },
  // …
};
10 · Beginner Friendly

Content Editing

The fastest, lowest-risk way to make the site say what you want it to say.

New to code?

Everything in this section is plain text or attribute changes — no logic to break. Save the file, and your dev server (from Getting Started) will refresh the browser automatically so you can see the change right away.

Headings, Paragraphs & Buttons

In Revolution, page text lives in the data file named after the page — src/data/home.js, about.js, service.js, contact.js, not-found.js, and portfolio-pages.js / portfolio-projects.js for the portfolio. Each section has its own export; edit the text values between the quotes:

javascriptsrc/data/home.js
// before
export const hero = {
  intro: 'Hello, we’re Revolution, an award winning..',
  lineOne: { word: 'Creative', /* … */ },
  lineTwo: { word: 'Agency', /* … */ },
  cta: { label: 'Work', href: '/portfolio-3-column/' },
};

// after
export const hero = {
  intro: 'Hello, we’re Riverside, an independent..',
  lineOne: { word: 'Design', /* … */ },
  lineTwo: { word: 'Studio', /* … */ },
  cta: { label: 'Projects', href: '/portfolio-3-column/' },
};

Each page’s browser-tab title and meta description sit in the same file, in its page export (e.g. page.title in home.js).

Menu links, the header “Let’s Talk” button, the featured project in the menu, and social links live in src/data/navigation.js; the footer marquee, menu, social icons, and copyright line (© 2026 RNN Studio) live in src/data/footer.js. Edit the label and URL once and every page updates. See Navigation for the full pattern.

Cards, Testimonials & Pricing

Repeating content blocks — service cards, team members, portfolio projects, testimonials, client logos — are driven by data arrays rather than hand-written per card, so adding or removing an item is a matter of editing the list. The testimonials band (shown on the home and service pages) is a good example:

javascriptsrc/data/testimonials.js
export const testimonials = {
  eyebrow: '96.3 Customer Satisfaction',
  titleLines: ['Hear from our', 'happy clients'],
  testimonials: [
    { quote, avatar, name: 'Linda Miller', role: 'COO of Paytime' },
    { quote, avatar, name: 'Linda Miller', role: 'COO of Paytime' },
  ],
  logos: [logo(0, 100), logo(1, 147), logo(2, 100), logo(3, 119), logo(4, 112)],
};

Portfolio projects work the same way: the cards in every grid come from projects in src/data/portfolio.js, and each project page’s sections are listed in src/data/portfolio-projects.js.

Blog Content & Contact Information

Blog posts are Markdown files in src/content/blog/ — one file per post. Edit the front matter and body directly, or copy an existing post to add a new one. Fields are checked against the schema in src/content.config.js; format switches between the standard, gallery, video, audio, and quote layouts:

markdownsrc/content/blog/a-closer-look-at-connected-living.md
---
title: "A closer look at connected living"
slug: "a-closer-look-at-connected-living"
date: "2022-05-20T12:24:49"
excerpt: "Our most successful engagements have been…"
author: "golothemes"
categories: ["Article", "Digital", "Technology"]
tags: ["Art", "Mobile Apps", "Photography", "Web"]
image: "/images/blog/girl-with-phone-1.jpg"
thumbnail: "/images/blog/girl-with-phone-1-1200x790.jpg"
card: "/images/blog/girl-with-phone-1-650x544.jpg"
---

Which posts appear in the slider at the top of /blog, and how many posts show per page, is set in blogPage in src/data/blog.js; author bios are in the same file. The demo comment thread under each post comes from src/data/comments.js. The site name, logo, phone number, and email address are set once in src/data/site.js and reused by the menu, footer, and contact page.

12 · Guide

Forms

What ships out of the box, and what still needs a backend.

Forms need somewhere to go

Revolution has two forms — the contact form on /contact and the “Leave a Reply” comment form under each blog post. Both render the full UI, but neither is connected to a working service: their action is the placeholder https://formspree.io/f/your-form-id. Until you replace it, submissions fail.

Contact Forms

The contact form’s fields, button text, messages, and endpoint are all set in src/data/contact.js. Replace action with your endpoint:

javascriptsrc/data/contact.js
form: {
  // Wire this up to your form service (Formspree, Web3Forms, Netlify Forms…)
  action: 'https://formspree.io/f/your-form-id',
  method: 'POST',
  fields: [
    { name: 'name', type: 'text', placeholder: 'Name', required: true },
    { name: 'email', type: 'email', placeholder: 'Email', required: true },
    { name: 'message', type: 'textarea', placeholder: 'Message', required: true },
  ],
  submit: 'Submit',
},

The script in src/components/sections/contact/ContactMain.astro submits the form with fetch (sending FormData with an Accept: application/json header), then shows the “sent” or “failed” message from contact.js without leaving the page. The endpoint must therefore accept a cross-origin POST and return a 2xx status on success. The comment form’s endpoint is commentForm.action in src/data/comments.js; it is a plain HTML form post with no script.

Newsletter Forms

Revolution doesn’t include a newsletter signup form, and no email provider is pre-wired. To add one, use your provider’s embed form in a new section, following the same pattern as the contact form.

Form Validation

The contact form uses novalidate to switch off the browser’s own pop-ups and validates with a small script instead: empty required fields and invalid type="email" values get an inline message under the field (with aria-invalid and aria-describedby set), and nothing is sent until every field passes. The message texts are in messages in src/data/contact.js.

Third-Party Form Services

If you’d rather not run your own backend, services like Formspree, Getform, or Basin accept form submissions directly from static HTML and forward them to your email — typically a one-line change to the form’s action attribute.

13 · Guide

API & Data

Only relevant if your template fetches data from an external source — skip this section otherwise.

Revolution fetches no external data

Every page is built from local files: the JavaScript exports in src/data/ and the Markdown blog collection in src/content/blog/. There are no API calls, API keys, or environment variables. The only network request the site makes is the form submission described in Forms. The subsections below are general guidance in case you connect a CMS or API later.

API Configuration

API URLs and keys are read from environment variables (see Configuration) rather than hard-coded, so the same code can point at a staging or production API depending on environment:

env.env
PUBLIC_API_URL=https://example.com/api
PUBLIC_API_KEY=your_api_key
Never expose real credentials

The values above are placeholders. Keep real API keys out of version control, and prefer server-only variables (without the PUBLIC_ prefix) for anything sensitive.

Fetch Requests

javascript
const res = await fetch(`${import.meta.env.PUBLIC_API_URL}/products`, {
  headers: { Authorization: `Bearer ${import.meta.env.PUBLIC_API_KEY}` },
});
const data = await res.json();

Data Structures

The one structured data source with a formal schema is the blog collection, defined in src/content.config.js. If you move posts to a CMS, map its fields to this shape so the existing components keep working:

FieldTypeNotes
title, slugstringRequired; slug becomes the post URL (/<slug>)
datedateRequired; read as UTC when no time zone is given. updated is optional
excerpt, authorstringOptional
categories, tagsstring[]Default to empty; drive the category and tag archives
image, thumbnail, cardstringOptional image URLs: full image, 1200×790 listing crop, 650×544 related-posts crop
formatenumstandard (default), gallery, video, audio, or quote
gallery, video, audio, quotevariesOptional media for the matching format; sticky is an optional boolean

Error Handling

javascript
try {
  const res = await fetch(apiUrl);
  if (!res.ok) throw new Error(`Request failed: ${res.status}`);
  const data = await res.json();
} catch (err) {
  // show a fallback UI state instead of a blank page
  console.error(err);
}
14 · Guide

SEO

Per-page metadata, social previews, and the machine-readable files search engines expect.

Page Title & Meta Description

Pass a unique title and description into Layout on every page — these drive both the browser tab and the snippet shown in search results. In Revolution these values come from each page’s data file (the page export), blog posts use their title and excerpt, and project pages use the title and description in portfolio-projects.js:

astrosrc/pages/contact.astro
<Layout title={contactPage.title} description={contactPage.description}>
<!-- contactPage (src/data/contact.js):
     title: 'Contact – Revolution – Agency'
     description: 'We’d love to hear from you! …' -->

Add an image prop (a root-relative path such as /images/home/hero-bg.jpg) to give a page its own Open Graph image.

Canonical URL, Open Graph & Twitter/X Cards

Already set inside Layout.astro’s <head>, using the site value from astro.config.mjs to build absolute URLs. og:image is only output when the page passes an image prop (blog posts and project pages do), and is turned into an absolute URL automatically:

astrosrc/layouts/Layout.astro
{canonical && <link rel="canonical" href={canonical} />}
<meta property="og:type" content="website" />
<meta property="og:site_name" content={site.name} />
<meta property="og:title" content={title} />
{description && <meta property="og:description" content={description} />}
{ogImage && <meta property="og:image" content={ogImage} />}
<meta name="twitter:card" content="summary_large_image" />
{noindex && <meta name="robots" content="noindex" />}

Sitemap & Robots.txt

Astro’s official sitemap integration (@astrojs/sitemap, already installed and registered in astro.config.mjs) generates sitemap-index.xml automatically at build time once the site value is set. Revolution doesn’t ship a robots.txt; to add one, create public/robots.txt pointing to that sitemap:

textpublic/robots.txt
User-agent: *
Allow: /
Sitemap: https://example.com/sitemap-index.xml

Structured Data & Semantic HTML

Use landmark elements (<header>, <nav>, <main>, <article>) and a single <h1> per page so both search engines and screen readers can parse the page’s structure. Revolution ships no JSON-LD structured data; add it in Layout.astro’s <head> for rich results (e.g. Organization schema site-wide, or Article schema in PostLayout.astro) where relevant to your content.

15 · Guide

Deployment

Platform-by-platform steps. Confirm your rendering mode first — it changes the build output you deploy.

Static vs. SSR — check this first

A static Astro build (the default) produces plain HTML/CSS/JS in dist/ that any static host can serve. An Astro site using server-side rendering (SSR via an adapter) needs a Node-capable host or the platform’s matching adapter. Revolution is fully static — no adapter is installed — so the static instructions below apply. Using the wrong instructions for your rendering mode is the most common deployment mistake.

Vercel

  1. Push the project to a GitHub, GitLab, or Bitbucket repository.
  2. In Vercel, choose Add New → Project and import that repository.
  3. Build command: npm run build · Output directory: dist (Astro static) — Vercel’s Astro preset detects this automatically in most cases.
  4. Revolution needs no environment variables; if you’ve added any in a .env file, set them under Project Settings → Environment Variables.
  5. Click Deploy.

Netlify

tomlnetlify.toml
[build]
  command = "npm run build"
  publish = "dist"

Connect the repository in the Netlify dashboard, confirm these build settings, add environment variables under Site configuration → Environment variables, and deploy.

Cloudflare Pages

Connect the repository, set the build command to npm run build and the output directory to dist. For SSR Astro sites, use the official @astrojs/cloudflare adapter instead of the static preset.

GitHub Pages

Set site and base in astro.config.mjs to match your repository URL, then build and publish the dist/ folder to the gh-pages branch (via the official astro build + a deploy action, or manually). GitHub Pages only serves static output — it cannot run an SSR adapter.

Traditional Hosting

For a static build, upload the contents of dist/ to your host’s public web root via FTP/SFTP or your host’s file manager. For an SSR build (if you later add an adapter), the host must support running node continuously (a VPS or a Node-compatible host) — plain shared hosting without Node support cannot run it.

16 · Guide

Build & Production

What happens when you run build, and a checklist before you ship.

bash
npm run build     # render every page, bundle, and write dist/
npm run preview   # serve dist/ locally, exactly as it will run in production

Build Output

The build writes one static HTML file per route, hashed CSS/JS bundles, the bundled font files, sitemap-index.xml, and everything from public/ (copied as-is) to dist/. This folder is disposable and regenerated by every build — it’s conventional to keep it out of Git via .gitignore.

Environment Variables in Production

Revolution uses none out of the box. If you add some, note that variables from your local .env are not deployed automatically — each hosting platform requires you to set them again in its dashboard (see Deployment). A missing production variable is one of the most common causes of a build that works locally but fails, or behaves unexpectedly, once deployed.

Production Checklist

  • Run npm run build locally and confirm it completes with no errors.
  • Run npm run preview and click through the main pages.
  • Confirm the site URL in astro.config.mjs matches your real domain.
  • If you added a .env file, set each of its variables in your hosting platform’s dashboard.
  • Replace the placeholder https://formspree.io/f/your-form-id in src/data/contact.js and src/data/comments.js with a real endpoint, and send a test message.
  • Replace the demo phone and email in src/data/site.js, the social URLs in navigation.js / footer.js, and the copyright line in footer.js.
  • Verify the favicon, logo, Open Graph images, and meta descriptions are your own, not the template defaults.
  • Replace demo images and blog posts you don’t have the rights to use.
17 · Reference

Troubleshooting

The most common problems, in the order you’re likely to hit them.

npm install fails

Cause
Node.js version too old, a corrupted local cache, or a stale lockfile from a different package manager.
Solution
Confirm node --version meets the requirement in Requirements, then run rm -rf node_modules package-lock.json && npm install.

Port already in use

Cause
Another process (often a previous dev server that didn’t shut down cleanly) is already listening on the default port.
Solution
Stop the other process, or start this one on a different port: npm run dev -- --port 4322.

Module not found

Cause
A missing dependency, or a typo in a relative import path (often after moving a component to a different folder). Revolution defines no path aliases.
Solution
Re-run npm install; check the import path casing matches the file exactly (Linux build servers are case-sensitive) and that the number of ../ segments matches the file’s new location.

Images not loading

Cause
A path that doesn’t match the file in public/images/ (wrong folder, case, or extension), or a path that includes public/.
Solution
Review Images & Assets — public/ files are referenced with a root-relative URL that leaves out public, like /images/home/hero-bg.jpg.

Styles not loading

Cause
A new page doesn’t render inside Layout (which imports global.css), or a style in a component’s scoped <style> block is being targeted at markup from a different component.
Solution
Wrap new pages in Layout from src/layouts/Layout.astro; put styles in the same .astro file as the markup they target, or in global.css if they’re shared.

Blog post front matter errors

Cause
A post’s front matter doesn’t match the schema in src/content.config.js — a missing title, slug, or date, a format other than the five allowed values, or categories/tags written as a string instead of a list.
Solution
The error names the file and field; compare it with an existing post (see Content Editing) and fix the front matter.

Build fails (but dev server works)

Cause
The dev server only renders the pages you open, while build renders every route — an error on a page you never visited in dev (a project page, a blog archive) only shows up in the build.
Solution
Read the full terminal output for the first error (later errors are often side effects of it); fix issues top to bottom and re-run.

Environment variables not working

Cause
Only applies if you’ve added your own — Revolution reads none. Usually a missing PUBLIC_ prefix for a client-side value, or the dev server wasn’t restarted after editing .env.
Solution
Rename the variable with the PUBLIC_ prefix if it’s used in browser code, then stop and restart npm run dev — env changes aren’t hot-reloaded.

Deployment problems

Cause
Wrong output directory configured on the host, a Node.js version older than 22.12.0 on the build server, or (if you added any) environment variables set locally but not in the platform’s dashboard.
Solution
Re-check the build command and output folder against Deployment for your platform, set the platform’s Node.js version to 22 or later, and confirm any .env variables are duplicated there.
18 · Reference

FAQ

Quick answers to the questions we hear most.

How do I install the template?

Extract revolution-astro-package.zip, open the revolution-astro/ folder, run npm install, then npm run dev. The full walkthrough is in Getting Started.

Which Node.js version should I use?

Node 22.12.0 or later — see Requirements. The current LTS release is a safe default.

Can I use JavaScript instead of TypeScript?

Revolution is already plain JavaScript — there are no .ts files and no type annotations to remove. The tsconfig.json is only Astro’s default editor configuration. You can add TypeScript later if you want it; Astro supports it without extra setup.

How do I change colors?

Update the --color-* custom properties at the top of src/styles/global.css — e.g. --color-accent for the green hover fill — see Styling. A few one-off colors (like the blog post background) are passed as props or set in a component’s own <style> block.

How do I change fonts?

Swap the Fontsource import or the Satoshi @font-face files, and update --font-heading / --font-body in global.css — see Fonts & Icons.

How do I replace images?

Swap the file in public/images/ or update its path in the matching src/data/ file or post front matter — see Images & Assets.

How do I add a new page?

Add a file to src/pages/; the route is created automatically — see Pages. New blog posts go in src/content/blog/ instead, and new portfolio projects in src/data/portfolio.js and portfolio-projects.js.

How do I deploy the template?

Platform-specific steps for Vercel, Netlify, Cloudflare Pages, GitHub Pages, and traditional hosts are in Deployment.

Where can I get support?

See the Support section for contact details and what to include in a request.

19 · Reference

Changelog

Every notable change, newest first. Add a new entry above the last one for each release.

Version 0.0.1
  • AddedInitial release: home, about, service, contact, and 404 pages.
  • AddedSeven portfolio grid layouts, portfolio category pages, and 12 single-project page variants.
  • AddedMarkdown blog with five post formats, pagination, and category, tag, author, and format archives.
  • AddedGSAP animations, Swiper sliders, self-hosted fonts, and an automatic sitemap.
Keeping this current

Copy the markup for one entry above, update the version number, date, and bullet list, and place it at the top of this section for each new release.

20 · Reference

Support

Stuck on something this guide didn’t cover? Here’s how to reach us.

Documentation

Most questions are answered somewhere in this guide — try the search (Ctrl K) before reaching out.

Back to top

Contact support

Email hello@rnnstudio.com or reach us through our contact form.

Contact support

Support hours

We’re based in India and available Monday–Friday, 10am–7pm IST. Expect a response within 24 hours.

What to Include in a Support Request

The fastest way to get a useful answer is to include the details below in your first message:

text
When contacting support, please include:

- Template name and version
- Browser (and version)
- Operating system
- Node.js version (node --version)
- The exact error message
- Steps to reproduce the problem