Make it your own
Change the text, the flavours and their 3D cans, the colours, the images, the fonts and the page title.
Where everything is
| What | Where |
|---|---|
| Every text, in both languages | messages/en.json and messages/ar.json |
| Page title and description | the metadata keys in the same two files |
| The flavours and their 3D cans | src/features/carousel/constants/juiceCans.ts, models in public/assets/3d/can/ |
| The colours of each flavour | src/features/product-showcase/constants/themes.ts |
| Lighting on the cans | public/assets/hdri/forest_slope_1k.hdr, named in src/features/carousel/constants/environment.ts |
| Fruit images | public/assets/images/fruit/, listed in src/features/floating-fruits/constants/fruitImages.ts |
| Ice cubes, leaves, water drop | public/assets/images/ice/, public/assets/images/leaf/, public/assets/images/drop.png |
| Cart sample items | src/features/navigation/constants/sampleCartItems.ts, images in public/assets/images/can/ |
| Bottle sizes | src/features/product-showcase/constants/sizes.ts |
| Contact links | src/features/product-showcase/constants/contactLinks.ts |
| Menu items | src/features/navigation/constants/navItems.ts |
| Fonts | src/app/[locale]/layout.tsx and src/app/globals.css |
| Favicon | src/app/favicon.ico |
Run yarn dev while you work: the page reloads on every save.
Text
Every word on the page comes from messages/en.json and messages/ar.json. Change the value, never the key, and keep the same keys in both files.
| Key | What it is |
|---|---|
brand.name | The name at the start of the menu bar |
brand.wordmark | The giant word behind the cans |
juices.<id>.name, juices.<id>.description | The title and the text of each flavour |
nav.items | The menu labels |
cart.items | The product names in the cart panel |
scroll.firstLine, scroll.secondLine | The two words on the round button at the bottom, which opens the contact menu |
contact | The contact menu's title and the labels of its links |
metadata | The page title and description |
The giant word is drawn in the Thunder font, in capitals, and is JUICY in both languages. Keep it in Latin letters, or change its font too.
Swap a flavour
A flavour is an id that ties together a 3D can, a colour theme, a fruit image and its text. The steps below replace Mango Burst with a new flavour, Peach Fizz, with the id peachFizz. Do the same for any of the four.
The wheel holds exactly four cans: four positions in src/features/carousel/constants/positionConfigs.ts, and a quarter turn per step. Replace a flavour rather than add a fifth.
Rename the id
In
src/types/juice.ts, replace the old id with the new one. From here on, TypeScript points at every file that still uses the old id.src/types/juice.tsexport type JuiceId = | "lemonGinger" | "blueberryAcai" | "peachFizz" | "raspberryRose";Make the can model
Each can is a
.glbfile with one mesh and two materials:M_Aluminum, the metal body, andM_Cola, the label. The label is one image wrapped around the can, 2048 by 958 pixels, stored inside the.glb. To make your can, open an existing one in Blender (File > Import > glTF 2.0), replace the image in theM_Colamaterial with your own label at the same size, and export it with File > Export > glTF 2.0, format glTF Binary (.glb). Keep the model's size and position so it sits on the wheel like the others.Save it as
public/assets/3d/can/peach.glb. The code makes every material shiny metal when it loads the model (metalnessandroughnessinsrc/features/carousel/hooks/useCanModel.ts), so change the finish there rather than in Blender.Point the wheel at the model
In
src/features/carousel/constants/juiceCans.ts, change the entry. The order of this list is the order on the wheel, and the first can is the one the page opens on.src/features/carousel/constants/juiceCans.ts{ id: "peachFizz", model: "/assets/3d/can/peach.glb" },Give it its colours
In
src/features/product-showcase/constants/themes.ts, rename the entry and set its colours. What each one paints is in Colours.src/features/product-showcase/constants/themes.tspeachFizz: { mainBgColor: "#F4A27B", blurColor: "#FFE1D2", textColor: "white", accentColor: "rgba(255, 255, 255, 0.9)", buttonBgColor: "white", buttonTextColor: "#F4A27B", },Add its fruit image
Put a PNG with a transparent background in
public/assets/images/fruit/, at 400 by 600 pixels like the others, and name it insrc/features/floating-fruits/constants/fruitImages.ts. The page shows it four times around the can, at different sizes and angles.src/features/floating-fruits/constants/fruitImages.tspeachFizz: "peach.png",Write its text
In both
messages/en.jsonandmessages/ar.json, rename the flavour's keys underjuicesandcart.itemsand write the new text.messages/en.json"juices": { "peachFizz": { "name": "Peach Fizz", "description": "Ripe peaches and a light sparkle, cold from the can." } }Update the cart, if the flavour is in it
The cart panel shows the items in
src/features/navigation/constants/sampleCartItems.ts: Lemon Ginger and Blueberry Açai. If yours is one of them, change itsjuiceId, itspriceand itsimage, a picture of the can inpublic/assets/images/can/.Check
Terminalinjuicyyarn typecheck yarn devExpected result:
Having trouble?yarn typecheckreports no errors, and the new can turns into place with its colours, its fruit and its text.
When you replace the first flavour, Lemon Ginger, also change DEFAULT_JUICE_ID at the end of themes.ts, and the background of body in src/app/globals.css to its mainBgColor. That colour shows for a moment before the page has loaded.
Colours
Each flavour has a theme in src/features/product-showcase/constants/themes.ts. The page eases from one theme to the next as the wheel turns.
| Field | What it paints |
|---|---|
mainBgColor | The page background, the size buttons that are not selected, and the buttons in the cart, account and contact panels |
blurColor | The soft glow behind the can |
buttonBgColor | The fill of the See More button and of the selected size |
buttonTextColor | The text of the See More button |
textColor and accentColor are part of each theme but no component reads them. The text on the page is white.
While the page runs, useBodyBackground in src/features/landing/hooks/ paints the page background in the active flavour's mainBgColor. The body background in src/app/globals.css, #82AF38, only covers the moment before the page loads, so change it together with the first flavour.
The light reflected on the cans comes from public/assets/hdri/forest_slope_1k.hdr, Poly Haven's Forest Slope image (CC0). To change it, put another .hdr file in that folder and name it in src/features/carousel/constants/environment.ts.
Images
Replace a file with your own under the same name, or change the name where the table says.
| Image | File | Named in |
|---|---|---|
| Fruits | public/assets/images/fruit/*.png | src/features/floating-fruits/constants/fruitImages.ts |
| Ice cube | public/assets/images/ice/cube.webp | src/features/ice-cubes/utils/getImagePath.ts |
| Leaves | public/assets/images/leaf/1.png, 2.png | src/features/ice-cubes/utils/getImagePath.ts |
| Water ripple | public/assets/images/drop.png | src/features/landing/constants/stage.ts |
| Cans in the cart | public/assets/images/can/*.webp | src/features/navigation/constants/sampleCartItems.ts |
The number of ice cubes and leaves, on phones and on larger screens, is set in src/features/ice-cubes/constants/layout.ts.
Fonts
- Thunder, the giant word: its files are in
src/app/fonts/andsrc/app/[locale]/layout.tsxloads them withlocalFont. To use another font, put its files there and change thesrcpaths. - Cairo, the Arabic text: loaded from Google Fonts in the same file with
next/font/google. ReplaceCairowith another Google font that has thearabicsubset. - The Latin text uses Arial, set on
bodyinsrc/app/globals.css.
Parts of the page
The page is one file, src/app/[locale]/page.tsx, which places each part. To remove one, delete its element there.
| Element | What it is |
|---|---|
<IceCubes> | The ice cubes and leaves at the sides |
<NavBar> | The menu bar, with the language switch, the account and the cart |
<WaterWaveStage> | The water ripple under the pointer, around everything else |
<ProductLogo> | The giant word behind the cans |
<JuiceCarousel> | The 3D wheel, its arrows and the floating fruits |
<SizeSelector> | The round size buttons |
<ProductInfo> | The flavour's title, text and See More button |
<ScrollDownButton> | The round button at the bottom that opens the contact menu |
Links and buttons
The contact menu's links are in src/features/product-showcase/constants/contactLinks.ts. Each has an id, a url and an icon from react-icons, and its label is under contact.links.<id> in the message files. Replace the two sample links with your own.
import { FaInstagram } from "react-icons/fa6";
{ id: "instagram", url: "https://www.instagram.com/your-brand", icon: FaInstagram },- Menu items are listed by id in
src/features/navigation/constants/navItems.ts, with their labels undernav.items. They are labels only and lead nowhere yet: to link one, render an<a>with your address insrc/features/navigation/components/NavLinks.tsx. - See More, Checkout and View Cart have no action yet. Add an
onClickor turn them into links where they are drawn:src/features/product-showcase/components/ProductInfo.tsxandsrc/features/navigation/components/CartSummary.tsx. - The sign-in form checks its fields and then clears them. To sign people in, send the values from
onSubmitinsrc/features/navigation/hooks/useSignInForm.tsto your own back-end.
Bottle sizes
The round size buttons come from src/features/product-showcase/constants/sizes.ts. Pressing one selects it; selected: true marks the one chosen when the page opens. The unit's label is under units in the message files.
{ value: "355", unit: "ml", selected: true },Page title, description and search
The browser tab's title and the description search engines show are metadata.title and metadata.description in each message file, so each language has its own. The tab icon is src/app/favicon.ico: replace it with your own under the same name.
The template sets no site address, social sharing image, sitemap or robots.txt. Once you know your domain, add them in generateMetadata in src/app/[locale]/layout.tsx, for example:
return {
metadataBase: new URL("https://www.your-domain.com"),
title: t("title"),
description: t("description"),
openGraph: { images: "/og.png" },
};Put the sharing image at public/og.png, 1200 by 630 pixels.