Robert Smith 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 Robert Smith, a bold, single-page creative portfolio theme for designers, art directors and motion artists, built with Astro. Oversized typography, looping video work thumbnails, infinite marquees and a scroll-driven colour wash give it a studio feel, while project and category pages are generated from a single data file. 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, uncompiled project source — components, pages, styles, and configuration.

Production build

Run npm run build to generate a static dist/ folder you can deploy anywhere. dist/ is generated output and is not included in the download.

Design assets

Placeholder project images in public/images/projects/, looping preview videos (MP4 + WebM with poster frames) in public/videos/, SVG logos, icons and background patterns in public/images/, and the self-hosted General Sans variable font in public/fonts/. No Figma or other design source files are included.

This documentation

A single offline HTML file — open it directly in any browser, no server required.

Features

  • Single-page home: hero with a rotating badge, featured project video, selected-work list with a cursor-following “View” bubble, client logo marquee, about section with image reveal, services accordion, experience and awards lists, and a contact form
  • Project pages (/project/[slug]) and category pages (/category/[slug]) generated automatically from one data file, src/data/projects.js
  • Keyboard-accessible lightbox gallery on every project page, built with Swiper
  • GSAP + ScrollTrigger animations (scroll reveals, marquees, colour wash, hover states), all disabled for prefers-reduced-motion
  • Responsive at desktop, tablet and mobile, with an off-canvas mobile menu
  • SEO meta and Open Graph tags on every page, plus an automatic sitemap via @astrojs/sitemap
  • Plain JavaScript and .astro components — no UI framework and no TypeScript required

Technology Stack

Robert Smith is a plain Astro project — .astro components, vanilla JavaScript and plain CSS, with GSAP for animation and Swiper for the lightbox gallery:

Astro 7 Vite GSAP + ScrollTrigger Swiper @astrojs/sitemap Plain CSS

Requirements

ToolMinimum versionNotes
Node.js22.12.0 or laterSet in package.json under engines; required by Astro 7
Package managernpm (bundled with Node), pnpm, or YarnA package-lock.json is included for npm; pnpm and Yarn also work
Code editor—VS Code with the official Astro extension (recommended in .vscode/extensions.json)
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, and the template ships no polyfills.

02 · Setup

Getting Started

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

Installation & Extraction

  1. Download the template 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. Open that folder in your terminal and in your code editor.
bash
# Extract on macOS / Linux
unzip robert-smith-astro-package.zip
cd robert-smith-astro-package/robert-smith-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 Astro’s local dev server (powered by Vite) 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 renders every page (including one page per project and category), bundles and minifies CSS and JavaScript, and writes static 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 a missing import or a project entry with a broken field) that only surface when every page is rendered, and lets you check the final animations and assets.

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. In this template it holds every image, video and font, plus favicon.svg and favicon.ico. See Images & Assets.
src/components/One component per home-page section (Hero, Featured, WorkList, Clients, About, Expertise), the site chrome (Navbar, Footer), and shared pieces (Button, Marquee, Lightbox).
src/layouts/BaseLayout.astro defines the common page shell — <head> and SEO tags, stylesheets, navbar, footer — so individual pages only supply their unique content.
src/pages/Every file here becomes a route automatically. See Pages for details.
src/scripts/animations.js registers GSAP’s ScrollTrigger and exports shared helpers (initHover, initReveal, initMasks, initLinkMovement, prefersReducedMotion) used by the components.
src/styles/Plain CSS: normalize.css, base-framework.css, the theme stylesheet robert-smith.css (colour variables live at its top), and small additions in global.css. See Styling.
src/data/site.js (site name, SEO defaults, email, phone, nav and social links, copyright) and projects.js (projects, categories, and the featured project), kept separate from markup so they’re easy to edit without touching Astro templates.
Files not shown above

The project root also contains package-lock.json, .gitignore, a .vscode/ folder recommending the Astro extension, and the downloaded package also includes a LICENSE file with the license terms. There is no src/assets/ or src/content/ folder, and no Tailwind or other CSS tooling config.

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": "robert-smith-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",
    "astro": "^7.3.5",
    "gsap": "^3.15.0",
    "swiper": "^14.2.0"
  }
}

astro.config.mjs

Controls integrations, the deployed site URL, and build behavior. Robert Smith registers a single integration, @astrojs/sitemap, and uses Astro’s default static output. Replace the placeholder site value with your real domain — it’s used for canonical URLs, Open Graph image URLs, and the sitemap.

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

export default defineConfig({
  // Set this to your deployed URL: used for canonical/OG URLs and the sitemap.
  site: 'https://example.com',
  integrations: [sitemap()],
});

tsconfig.json

The template is written in plain JavaScript, so you never need to write TypeScript. tsconfig.json simply extends Astro’s strict preset so your editor gets autocomplete and Astro’s generated types. No path aliases are configured — imports use relative paths such as ../components/Button.astro.

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

Environment Variables (.env)

Robert Smith doesn’t read any environment variables out of the box, and no .env file is included. If you later add values that differ between environments (API URLs, keys) or shouldn’t be committed to Git, create a .env file in the project root. 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

.env and .env.production are already listed in the template’s .gitignore. Anything without the PUBLIC_ prefix should stay off the client entirely.

05 · Guide

Pages

How routes are created, and how project and category pages are generated from data.

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. Robert Smith ships three route files:

text
src/pages/index.astro            →  /                 (single-page home)
src/pages/project/[slug].astro   →  /project/:slug    (one page per project)
src/pages/category/[slug].astro  →  /category/:slug   (one page per category)

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 BaseLayout from '../layouts/BaseLayout.astro';
---
<BaseLayout title="Services - Robert Smith" description="Branding, motion and video services.">
  <h1>Services</h1>
</BaseLayout>

Editing an Existing Page

The home page (src/pages/index.astro) is just a list of section components — Hero, Featured, WorkList, Clients, About, Expertise — so to change a section’s copy, edit the matching file in src/components/. To reorder or remove a section, edit the list in index.astro. The navbar and the footer (with the contact form) come from BaseLayout.

Dynamic Routes

Files wrapped in square brackets, such as [slug].astro, generate multiple pages from a single template — here, one page per project and one per category, both read from src/data/projects.js. Astro requires a getStaticPaths() function for these on statically-generated sites:

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

export function getStaticPaths() {
  return projects.map((project) => ({ params: { slug: project.slug }, props: { project } }));
}

const { project } = Astro.props;
---

Layouts

Robert Smith has one layout, src/layouts/BaseLayout.astro. It wraps page content with the <html> shell, <head> metadata, all four stylesheets, the navbar, and the footer. Every page renders inside it via a <slot />. All props are optional: title, description and image fall back to the defaults in src/data/site.js, and footer={false} hides the footer and contact form:

astrosrc/layouts/BaseLayout.astro
---
import { site } from '../data/site.js';
const {
  title = site.title,
  description = site.description,
  image = site.ogImage,
  footer = true,
} = Astro.props;
---
<html lang="en">
  <head><title>{title}</title> <!-- + meta, canonical, Open Graph, font preload --></head>
  <body>
    <Navbar />
    <main>
      <slot /> <!-- page content is injected here -->
    </main>
    {footer && <Footer />}
  </body>
</html>

Page Metadata & the 404 Page

Pass a title and description into BaseLayout on every new page for correct browser tabs and search results (see also SEO). Project and category pages already do this, using the project title and description. The template doesn’t ship a custom 404 page. If you want one, add a 404.astro file to src/pages/, and most static hosts will serve it automatically whenever no route matches.

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 so it can be reused without copy-pasting markup. Every component is a plain .astro file:

ComponentWhat it renders
NavbarLogo, nav links, email/phone and social links, off-canvas mobile menu, home-page scroll-spy
Hero“Hello, I’m Robert” heading, rotating circular badge, intro paragraph
FeaturedFull-width featured project video with the cursor-following “View” bubble
WorkList“Selected work” marquee, project list with video thumbnails, call-to-action button (reused on category pages)
ClientsInfinite marquee of client logos
AboutAbout heading, portrait with mask reveal, bio paragraph
ExpertiseServices accordion, work experience and awards lists inside the scroll-driven colour wash
Footer“Talk to me” marquee, contact form, footer links, copyright, back-to-top link
ButtonText button with rotating arrow and underline sweep
MarqueeInfinite right-to-left text marquee
LightboxSwiper-powered modal image gallery on project pages

Props

Props are the inputs a component accepts, similar to HTML attributes. In this template they’re read by destructuring Astro.props in the component’s frontmatter, with default values written inline — there are no TypeScript interfaces. Here is the real Button component:

astrosrc/components/Button.astro
---
const { href, label, arrow = true, external = false, reveal = true } = Astro.props;
---
<div class="button-wrapper" data-reveal={reveal ? '' : undefined}>
  <a href={href} class="button" data-hover="arrow line"
     target={external ? '_blank' : undefined} rel={external ? 'noopener' : undefined}>
    <div class="button-text">{label}</div>
    {arrow && <img src="/images/button-arrow-white.svg" alt="" class="button-arrow target-arrow" />}
  </a>
</div>

In plain language: href and label are the link target and text, and you should always pass both. arrow shows the rotating arrow icon (on by default). external opens the link in a new tab with rel="noopener" (off by default). reveal fades the button in on scroll (on by default). Usage: <Button href="#about" label="About Me" />.

Component Composition

Pages are composed from section components, and sections in turn reuse smaller ones — WorkList and Footer both use Marquee, and Hero, About and WorkList all use Button. The whole home page is:

astrosrc/pages/index.astro
<BaseLayout>
  <Hero />
  <Featured />
  <WorkList projects={projects} />
  <Clients />
  <About />
  <Expertise />
</BaseLayout>

Passing Data to Components

In Astro, props are passed the same way as HTML attributes and read via Astro.props. The category page reuses WorkList with a filtered project list, a marquee lead word, and an h1 heading:

astro
---
import WorkList from '../../components/WorkList.astro';
const items = projects.filter((p) => p.categories.includes(category.slug));
---
<WorkList projects={items} lead={category.name} headingTag="h1" />
Where the interactivity lives

There is no UI framework and no client: directive in this template. Interactive behavior (mobile menu, accordion, marquees, lightbox, form success state) is written in plain JavaScript inside each component’s <script> tag. Astro bundles that script automatically, and it imports shared GSAP helpers from src/scripts/animations.js.

07 · Guide

Styling

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

Global Styles & CSS Variables

The template uses plain CSS — no Tailwind, Sass, or CSS-in-JS. BaseLayout.astro imports four stylesheets in order: normalize.css, base-framework.css (ported base framework: containers, grid, forms, nav), robert-smith.css (the theme itself), and global.css (small additions: screen-reader utility, focus rings, scroll-reveal defaults). The colour palette is defined once as CSS custom properties at the top of robert-smith.css. Change a value there and it updates across the whole site.

csssrc/styles/robert-smith.css
:root {
  --color--carbon-black: #101010;  /* page background */
  --color--white: white;         /* main text */
  --color--black: #000;
  --_sizes---container-padding: 30px;
  --_sizes---section-padding: 200px;
  --color--pink: #ffa9e0;
  --color--green: #bff4a2;
  --color--yellow: #ffe88e;
  --color--off-white: #faf9f6;
}

Component Styles

Astro components support scoped <style> blocks — CSS written there only applies to that component, so class names never collide across files. Lightbox.astro uses a scoped block, while Navbar.astro and Footer.astro use <style is:global> for small overrides of the theme stylesheet. Most styling lives in robert-smith.css.

astro
<style>
  .lightbox-button { color: var(--color--white); opacity: 0.7; transition: opacity 0.3s; }
</style>

Responsive Design, Typography & Spacing

The layout is desktop-first, with max-width media queries in robert-smith.css at 991px (tablet), 767px (mobile landscape) and 479px (mobile portrait). Heading sizes are set on h1–h6 and utility classes such as .display, .section-title-big and .paragraph-giant, with overrides inside each breakpoint. Prefer adjusting these shared classes over overriding sizes on individual elements, so the site keeps a consistent rhythm as you edit it. The template is a single dark theme with no light/dark toggle.

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 every image and video lives, and how to swap in your own work.

LocationUse forBehavior
public/images/UI icons, client logos, background patterns, the hero hand image, and the About portrait (about-image.avif, also the default Open Graph image)Copied as-is, referenced by root-relative paths like /images/down-icon.svg
public/images/projects/<slug>/Each project’s cover (main.avif) and gallery imagesCopied as-is, listed per project in src/data/projects.js
public/videos/Looping preview videos: <name>-transcode.mp4, <name>-transcode.webm, and a <name>-poster-00001.jpg poster frameCopied as-is, referenced through the video() helper in projects.js
src/assets/Not used by the template — available if you want build-time optimizationOptimized, resized, and content-hashed by the build when used with <Image />

Replacing Images

The quickest route is to swap a file in place, keeping the same filename. For project work, edit the project’s entry in src/data/projects.js and point it at your own files. The video() helper expects the MP4, WebM and poster files to share a base name in public/videos/:

javascriptsrc/data/projects.js
{
  slug: 'canvas',
  title: 'Canvas',
  video: video('02'), // /videos/02-transcode.mp4, .webm, 02-poster-00001.jpg
  mainImage: '/images/projects/canvas/main.avif',
  gallery: gallery('canvas', ['gallery-1.avif', 'gallery-2.avif', 'gallery-3.jpg']),
}

Supported Formats & SVGs

The demo uses AVIF and JPEG for photos, PNG for the hero hand image, and SVG for icons, logos and patterns, all rendered with plain <img> tags. Any format the browser supports will work. The gallery grid on project pages has seven fixed slots, so a project shows up to seven gallery images.

Image Optimization

Because every image lives in public/, the build copies it as-is — it is not resized or re-encoded. Compress images (AVIF or WebP work well) and videos before adding them. Gallery and thumbnail images use loading="lazy". If you’d rather have Astro optimize an image, move it into src/assets/ and render it with the built-in Image component:

astro
---
import { Image } from 'astro:assets';
import portrait from '../assets/about-image.avif';
---
<Image src={portrait} alt="Portrait of the designer" class="about-image" />
Rule of thumb

To stay consistent with the template, keep images in public/ and reference them with root-relative paths (/images/…). Filenames are case-sensitive on most hosts, so BG-pattern.svg and bg-pattern.svg are different files.

09 · Guide

Fonts & Icons

Swapping typefaces and icon sets without touching every component.

Google Fonts

Robert Smith doesn’t load any Google Fonts — its only typeface is self-hosted (see below). To switch to a Google Font, add its embed code to the <head> in BaseLayout.astro, then replace 'General Sans Variable' in the body rule’s font-family in src/styles/robert-smith.css:

htmlsrc/layouts/BaseLayout.astro
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet">

Local Fonts

The template uses General Sans by Indian Type Foundry (ITF Free Font License) as a single variable font, public/fonts/GeneralSans-Variable.ttf. It’s declared with @font-face at the top of src/styles/robert-smith.css and preloaded by a <link rel="preload"> in BaseLayout.astro. If you replace the file, update both references:

csssrc/styles/robert-smith.css
@font-face {
  font-family: 'General Sans Variable';
  src: url('/fonts/GeneralSans-Variable.ttf') format("truetype");
  font-weight: 200 700;
  font-style: normal;
  font-display: swap;
}

Icon Libraries

The template doesn’t use an icon library. Icons are individual SVG files in public/images/: button-arrow-white.svg (button and link arrows), service-arrow.svg (accordion and submit arrow), Marqee-Arrow.svg (marquee separator), down-icon.svg and Circular-Text.svg (the hero’s rotating badge). A few icons are inline SVGs: the menu, social-link arrows in Navbar.astro, the back-to-top arrow in Footer.astro, and the previous/next/close buttons in Lightbox.astro.

Replacing & Adding Icons

To replace a file-based icon, overwrite the SVG in public/images/ with the same filename, or point the component’s <img src> at a new file. The inline SVGs use stroke="currentColor", so they follow the surrounding text colour. Edit their paths directly in the component. To add a new icon, drop an SVG into public/images/ and reference it with <img src="/images/your-icon.svg" alt="">.

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

Home-page copy lives in the section components in src/components/ (Hero.astro, About.astro, Expertise.astro, …). Locate the text between the tags and edit it directly:

astro
<!-- before (src/components/Hero.astro) -->
<div class="h2">I’m Robert</div>
<span class="display hero-display-line">Creative</span>
<Button href="#about" label="About Me" />

<!-- after -->
<div class="h2">I’m Alex</div>
<span class="display hero-display-line">Motion</span>
<Button href="#about" label="My Story" />

Header nav links (navLinks), the two navbar social links (navSocials) and the footer social links (footerSocials) are arrays in src/data/site.js. The footer’s quick links (Home, Work, Contact) are written directly in src/components/Footer.astro. The logo text (“Robert - Smith”) is written in Navbar.astro. See Navigation for the full pattern.

Cards, Testimonials & Pricing

The template has no testimonials or pricing blocks. Its repeating content is driven by data arrays, so adding or removing an item means editing a list. Projects and categories live in src/data/projects.js (see Data Structures). Services, work experience and awards are arrays at the top of src/components/Expertise.astro. Client logos are the five client-logo-N.svg files listed in Clients.astro:

javascriptsrc/components/Expertise.astro
const services = ['Branding', 'Motion', 'Video', 'Designing', 'Development'];

const experience = [
  { dates: 'Jan 2015  —  Dec 2018', role: 'Creative Designer @ Green Owl' },
  { dates: 'Jan 2015  —  Dec 2018', role: 'UX Designer @ Blossom Studio' },
];

const awards = [
  { source: 'Web Awards', title: 'Site of the Month  —  Paytime' },
];

Blog Content & Contact Information

The template has no blog. Contact details live in one place, src/data/site.js: email, phone, phoneHref (the tel: link), and copyright, alongside the site name, default title, description and ogImage. The navbar and footer both read from it, so each value only needs updating once.

12 · Guide

Forms

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

Forms need somewhere to go

The contact form in the footer captures input in the browser, but it isn’t connected to any service. Its submit handler cancels the submission and simply shows the “Thank you!” message, so nothing is sent anywhere. Delivering messages to an inbox requires a backend endpoint or a third-party form service.

Contact Forms

The contact form (Name, Email, Message) is part of src/components/Footer.astro, so it appears at the bottom of every page that renders the footer. The demo handler at the bottom of that file looks like this:

javascriptsrc/components/Footer.astro
// Demo form handler: no backend is wired up.
form.addEventListener('submit', (e) => {
  e.preventDefault();
  form.style.display = 'none';
  wrapper.querySelector('.w-form-done').style.display = 'block';
});

To deliver submissions, post the form data to your endpoint and keep the existing success (.w-form-done) and error (.w-form-fail) states:

javascriptsrc/components/Footer.astro
form.addEventListener('submit', async (e) => {
  e.preventDefault();
  const res = await fetch('YOUR_FORM_ENDPOINT', { method: 'POST', body: new FormData(form) });
  form.style.display = 'none';
  wrapper.querySelector(res.ok ? '.w-form-done' : '.w-form-fail').style.display = 'block';
});

Newsletter Forms

The template doesn’t include a newsletter form, and it isn’t pre-wired to any email provider. If you add one, the contact form in Footer.astro is a good starting point for the markup and success state.

Form Validation

Validation uses standard HTML attributes only, with no custom validation script: Name and Email are required, Email uses type="email", and maxlength caps Name and Email at 256 characters and Message at 5000. Message is optional. The browser checks these before the submit handler runs, giving users immediate feedback without a round trip to a server.

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

Robert Smith doesn’t fetch data from any external API. All content is static JavaScript data read at build time.

API Configuration

There is no API configuration in the template: no endpoints, keys, or environment variables. If you connect one later (for example a headless CMS), read URLs and keys from environment variables (see Configuration) rather than hard-coding them, so the same code can point at a staging or production API:

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

The template makes no fetch requests. If you add an API, fetching in a page’s frontmatter runs once at build time, so the static output stays API-free for visitors:

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

All portfolio content lives in src/data/projects.js. It exports categories (each { slug, name }), projects, featured (which project and video the home page’s Featured section promotes), and the helpers getProject(slug) and getCategory(slug). Each project object has this shape:

javascriptsrc/data/projects.js
{
  slug: 'think',                  // URL: /project/think
  title: 'Think',
  tag: 'Branding',                // label shown in the work list
  bgColor: '#0f003b',             // card background colour
  textColor: '#ffffff',           // card text colour
  description,                    // project page paragraph + meta description
  client: { name: 'Studio Creatives', url: 'https://example.com' },
  categories: ['branding', …],     // category slugs
  video: video('01'),              // { mp4, webm, poster }
  mainImage: '/images/projects/think/main.avif',
  gallery: gallery('think', ['gallery-1.avif', …]),
}

Add an object to projects and its page appears at /project/<slug> on the next build. It also shows up in the home work list and on every category page whose slug it lists. Site-wide settings live separately in src/data/site.js (see Content Editing).

Error Handling

With no network requests there’s nothing to fail at runtime. The only runtime error states are the contact form’s success and failure messages (see Forms). If you add an API, handle failed requests so the build or page degrades gracefully:

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 BaseLayout on every page — these drive both the browser tab and the snippet shown in search results. When omitted (as on the home page), the defaults come from site.title and site.description in src/data/site.js. Project pages use `${project.title} - Robert Smith` and the project description:

astro
<BaseLayout
  title="Services - Robert Smith"
  description="Branding, motion and video services."
>

Canonical URL, Open Graph & Twitter/X Cards

Already set in BaseLayout.astro’s <head> for every page, using the site value from astro.config.mjs to build absolute URLs. The Open Graph and Twitter image defaults to site.ogImage (/images/about-image.avif), and project pages pass their mainImage instead:

astrosrc/layouts/BaseLayout.astro
const canonical = new URL(Astro.url.pathname, Astro.site ?? Astro.url);
const ogImage = new URL(image, Astro.site ?? Astro.url);

<link rel="canonical" href={canonical} />
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:image" content={ogImage} />
<meta property="og:url" content={canonical} />
<meta name="twitter:card" content="summary_large_image" />

Sitemap & Robots.txt

The template already includes Astro’s official @astrojs/sitemap integration, which generates sitemap-index.xml at build time. Every project and category page is included. Make sure site in astro.config.mjs is your real domain, not the placeholder https://example.com. The template doesn’t ship a robots.txt. To add one, create public/robots.txt pointing to the 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. Robert Smith follows this already: every page has one <h1> (the hero title, the project title, or the category name) inside <main>. The template doesn’t include JSON-LD structured data. If you want rich results, add a <script type="application/ld+json"> with Person or CreativeWork schema to BaseLayout.astro’s <head>.

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

Robert Smith is a fully static site with no SSR adapter configured. npm run build produces plain HTML/CSS/JS in dist/ that any static host can serve, with no Node server needed. If you later add an SSR adapter, you’ll need a Node-capable host or the platform’s matching adapter instead.

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. The template needs no environment variables. If you’ve added a .env file, copy its values 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 any environment variables you’ve introduced 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. The static preset is all this template needs.

GitHub Pages

Set site in astro.config.mjs to your Pages URL, then build and publish the dist/ folder (via the official Astro deploy action, or manually). The template references images, videos, fonts and links with root-relative paths like /images/… and /project/…. Deploy it at the root of a domain: a custom domain or a username.github.io user site. A project sub-path (/repo-name/) would need base set and every hard-coded path updated.

Traditional Hosting

Upload the contents of dist/ to your host’s public web root via FTP/SFTP or your host’s file manager. Because the build is fully static, any plain shared host works — no Node.js support is needed on the server. Upload to the domain root rather than a sub-folder, for the root-relative path reason above.

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 static HTML (the home page plus one page per project and per category), hashed CSS/JS bundles, sitemap-index.xml, and a copy of everything in public/ to dist/. Images and videos are copied as-is, not optimized. This folder is disposable and regenerated by every build, and it’s already excluded in the template’s .gitignore.

Environment Variables in Production

The template uses no environment variables, so there’s nothing to configure by default. If you add some, remember 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.
  • Replace the placeholder content in src/data/site.js and src/data/projects.js, and the demo images and videos in public/, with your own.
  • If you’ve added environment variables, set each one in your hosting platform’s dashboard.
  • Connect the contact form in Footer.astro to a real endpoint — out of the box it only shows a success message.
  • Verify favicon, Open Graph image, and meta descriptions are your own, not the template defaults.
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 (such as gsap or swiper), or a typo in a relative import path.
Solution
Re-run npm install; check the import path and its casing match the file exactly. The template uses relative imports only, with no path aliases.

Images not loading

Cause
A path in projects.js or a component that doesn’t match the file in public/, filename casing that differs (fine on Windows/macOS, broken on Linux hosts), or the site deployed under a sub-path.
Solution
Review Images & Assets — every file in public/ needs a root-relative URL like /images/projects/think/main.avif with exact casing. For videos, check that all three files (-transcode.mp4, -transcode.webm, -poster-00001.jpg) exist. Deploy at a domain root (see GitHub Pages).

Styles not loading

Cause
A new page that doesn’t render inside BaseLayout, or a stylesheet import removed or reordered in the layout’s frontmatter.
Solution
Wrap the page in <BaseLayout>, and confirm BaseLayout.astro still imports normalize.css, base-framework.css, robert-smith.css and global.css in that order.

Animations don’t play

Cause
“Reduce motion” is turned on in your operating system (the template deliberately disables its GSAP animations for prefers-reduced-motion), or a JavaScript error stopped the component scripts.
Solution
Check your OS accessibility settings, then open the browser console for errors — a common one is an element a script expects (e.g. [data-navbar]) that was removed from the markup. Compare against the component’s props and markup (see Props).

Build fails (but dev server works)

Cause
The dev server renders pages on demand, while build renders every page up front — including every project and category from getStaticPaths(). A broken project entry (e.g. a category slug that doesn’t exist in categories) only fails when its page is built.
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 a .env file (the template uses none): missing the 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, or 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, and confirm every variable from .env is duplicated there.
18 · Reference

FAQ

Quick answers to the questions we hear most.

How do I install the template?

Extract the ZIP, run your package manager’s install command, then start the dev server. The full walkthrough is in Getting Started.

Which Node.js version should I use?

Node 22.12.0 or later, as required by Astro 7 and set in package.json — see Requirements.

Can I use JavaScript instead of TypeScript?

You already are — Robert Smith is written in plain JavaScript and .astro components, with no .ts files. tsconfig.json is there only for editor autocomplete. You can add TypeScript later if you want type checking.

How do I change colors?

Update the CSS custom properties at the top of src/styles/robert-smith.css — see Styling. Each project’s card colours are its bgColor and textColor in src/data/projects.js. The colour-wash sequence (pink → green → yellow) is set in the script in Expertise.astro.

How do I change fonts?

Replace the @font-face rule and the body font-family in robert-smith.css, and the font preload in BaseLayout.astro — see Fonts & Icons.

How do I replace images?

Swap the file in public/images/ or update its path in src/data/projects.js — see Images & Assets.

How do I add a new page?

Add a file to src/pages/; the route is created automatically — see Pages. To add a portfolio project, add an entry to src/data/projects.js instead, and its page is generated for you.

How do I deploy the template?

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

Can I use the template commercially?

The demo photos and videos come from Unsplash and Pexels, and the General Sans font is under the ITF Free Font License. Replace the placeholder content with your own work before publishing. [State your license terms here — e.g. standard vs. extended license — and link to the full license text.]

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