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 exposeswindow.gsap,window.ScrollTrigger,window.ScrollSmoother; - creates ScrollSmoother on
#smooth-wrapper/#smooth-content(unless reduced motion orwindow.rasukeCoredisables it); - calls every effect initialiser in the HTML master's order, collecting teardown functions;
- gates text / project reveals on the preloader's
reveal/donepromise; - 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)— turnsassets/imgs/…into/assets/imgs/…; leaveshttp,/,data:, andblob:paths untouched.pageHref(href)— mapsindex.htmlto/andabout.htmlto/about; keeps anchors,http,mailto:, andtel: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 theprefers-color-schemefallback.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.scssthat forwards it.
Key paths
src/app/**/page.tsx— routessrc/components/sections/**— section componentssrc/components/layout/**— site chromesrc/data/*.json— copy and listssrc/styles/**— SCSS (mirrors the HTML master)src/effects/**— GSAP / Three.js modulestokens/tokens.json— design tokens → CSS variables vianpm run tokenspublic/assets/**— static images, fonts, video, vendor scripts