File Structure

Rasuke is an App Router Next.js app. npm run build emits the static export into out/ (and Next's intermediate output into .next/). Day-to-day editing happens under src/.


src/app/layout.tsx (root, abridged)

The root layout owns document metadata, the font links, the icon, and the early color-scheme script. React 19 does not allow rendering <script> in the tree, so color-modes.js is registered with preinit().

import type { Metadata } from "next";
import { preinit } from "react-dom";
import site from "@/data/site.json";
import "@/app/globals.scss";
import "glightbox/dist/css/glightbox.min.css";

export const metadata: Metadata = {
  metadataBase: new URL(site.url || "https://alithemes.com"),
  title: { default: site.name, template: "%s — " + site.name },
  description: site.description,
  authors: [{ name: site.author }],
  icons: { icon: "/assets/imgs/favicon.svg" },
  openGraph: { type: "website", siteName: site.name, title: site.name },
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
  preinit("/assets/js/vendors/color-modes.js", { as: "script" });

  return (
    <html lang="en" data-color-scheme="dark" suppressHydrationWarning>
      <head>
        <link rel="preconnect" href="https://fonts.googleapis.com" />
        <link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
        <link href="https://fonts.googleapis.com/css2?family=Bonheur+Royale&family=DM+Sans:opsz,wght@9..40,400;9..40,500&family=Host+Grotesk:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
      </head>
      <body suppressHydrationWarning>{children}</body>
    </html>
  );
}
src/app/globals.scss
// Rasuke global stylesheet — mirrors the HTML master main.scss (no CSS framework).
@use "../styles/main.scss";
src/styles/main.scss

The SCSS entry imports everything in a fixed order: tokens and mixins, base layers, layout chrome, shared components, then section partials. New section groups must be added here.

@use "abstracts/css-vars";

@use "base/reset";
@use "base/typography";
@use "base/utilities";

@use "layout/container";
@use "layout/smooth-scroll";
@use "layout/offcanvas-menu";
@use "layout/nav-primary";
@use "layout/header-1";
@use "layout/header-2";
@use "layout/header-3";
@use "layout/header-4";
@use "layout/main-navigation";
@use "layout/footer-1";
@use "layout/footer-2";
@use "layout/footer-3";
@use "layout/site-chrome";

@use "components/grid-deform";
@use "components/hover-deform";
@use "components/preloader";
@use "components/transition";
@use "components/lightbox";
@use "components/back-to-top";
@use "components/cursor";

@use "sections/hero";
@use "sections/about";
@use "sections/projects";
@use "sections/services";
@use "sections/testimonials";
@use "sections/team";
@use "sections/other";
@use "sections/blog";
@use "sections/faqs";
src/components/layout/SiteShell.tsx

Pages wrap their sections in SiteShell. It renders the offcanvas menu, the chosen header and footer, the optional preloader / page transition / cursor, the back-to-top button, and mounts client motion through EffectsLoader.

export type SiteShellProps = {
  children: React.ReactNode;
  headerName?: "header-1" | "header-2" | "header-3" | "header-4" | "none";
  footerName?: "footer-1" | "footer-2" | "footer-3" | "none";
  pageName?: string;
  enablePreLoader?: boolean;   // defaults to preloader.json → enabled
  cursorEnabled?: boolean;     // defaults to cursor.json → enabled
  transitionEnabled?: boolean; // defaults to transition.json → enabled
  bodyClass?: string;
  colorScheme?: "light" | "dark";
};

Typical page usage (home 2):

export default function Index2Page() {
  return (
    <SiteShell
      pageName="index-2"
      bodyClass="page-home-2"
      colorScheme="dark"
      headerName="header-2"
      footerName="footer-2"
      enablePreLoader={true}
    >
      <Hero2 />
      <Projects2 />
      <Testimonials2 />
      <Img1 />
      <Services2 />
    </SiteShell>
  );
}
src/components/layout/PageChrome.tsx

PageChrome is a small client component used inside SiteShell. On every route change it writes the body classes (is-preloading plus bodyClass) and pins data-color-scheme on <html> when colorScheme is provided.

const classes: string[] = [];
if (enablePreLoader) classes.push("is-preloading");
if (bodyClass) classes.push(...bodyClass.split(/\s+/).filter(Boolean));
document.body.className = classes.join(" ");

if (colorScheme) {
  html.setAttribute("data-color-scheme", colorScheme);
}
src/components/effects/EffectsLoader.tsx

Client boundary for all motion. EffectsBootstrap is imported with ssr: false, so module-level browser code never runs on the server.

"use client";

import dynamic from "next/dynamic";

const EffectsBootstrap = dynamic(
  () => import("@/components/effects/EffectsBootstrap").then((m) => m.EffectsBootstrap),
  { ssr: false },
);

export function EffectsLoader() {
  return <EffectsBootstrap />;
}
src/components/effects/EffectsBootstrap.tsx

The single client entry for GSAP and Three.js. On mount (and on every pathname change) it:

  • registers GSAP plugins through registerGsap() and exposes window.gsap, window.ScrollTrigger, window.ScrollSmoother;
  • creates ScrollSmoother on #smooth-wrapper / #smooth-content (unless reduced motion or window.rasukeCore disables it);
  • calls every effect initialiser in the HTML master's order, collecting teardown functions;
  • gates text / project reveals on the preloader's reveal / done promise;
  • resets scroll to the top on route change and refreshes ScrollTrigger;
  • runs all cleanups on unmount, then kills remaining ScrollTriggers.
src/lib/gsap.ts
'use client';
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
import { ScrollSmoother } from 'gsap/ScrollSmoother';
import { ScrollToPlugin } from 'gsap/ScrollToPlugin';

let registered = false;
export function registerGsap() {
  if (registered || typeof window === 'undefined') return;
  gsap.registerPlugin(ScrollTrigger, ScrollSmoother, ScrollToPlugin);
  registered = true;
}

export { gsap, ScrollTrigger, ScrollSmoother, ScrollToPlugin };
src/lib/assets.ts

Two helpers bridge the HTML master's paths to App Router URLs:

  • asset(path) — turns assets/imgs/… into /assets/imgs/…; leaves http, /, data:, and blob: paths untouched.
  • pageHref(href) — maps index.html to / and about.html to /about; keeps anchors, http, mailto:, and tel: as-is.

The same module exports imgHeightAuto, imgWidthAuto, imgAutoSize, and imgCover presets for next/image.

Data and styles
  • src/data/*.json — section copy and lists. Components import their JSON directly, so content edits never touch TSX.
  • src/styles/abstracts/_tokens.generated.scss — generated token maps. Do not edit.
  • src/styles/abstracts/_css-vars.scss — emits :root, [data-color-scheme="dark"], and the prefers-color-scheme fallback.
  • src/styles/abstracts/_mixins.scss — media-up(), container(), grid-auto(), grid-columns(), section-space(), visually-hidden().
  • src/styles/sections/** — one partial per section plus an _index.scss that forwards it.
Key paths
  • src/app/**/page.tsx — routes
  • src/components/sections/** — section components
  • src/components/layout/** — site chrome
  • src/data/*.json — copy and lists
  • src/styles/** — SCSS (mirrors the HTML master)
  • src/effects/** — GSAP / Three.js modules
  • tokens/tokens.json — design tokens → CSS variables via npm run tokens
  • public/assets/** — static images, fonts, video, vendor scripts