🍜 Sarawak Laksa — Source Code

File structure and key code explained

📁 File Structure

projects/sarawak-laksa/
├── index.html          ← Main app (all content embedded)
├── css/
│   └── style.css       ← All styles, variables, animations, responsive
├── js/
│   └── app.js          ← Scroll nav, fade-in observer, smooth scroll
└── knowledge/
    ├── what-is-sarawak-laksa.md
    ├── ingredients.md
    ├── recipe.md
    ├── variations.md
    ├── where-to-eat.md
    ├── food-culture.md
    └── people.md
Design principle: All visible content is embedded directly in index.html. The knowledge/ folder contains the Markdown source documents used to author the HTML content — they are not loaded at runtime.

🔑 Key Code: Fade-in on Scroll

Cards fade in as they enter the viewport using the IntersectionObserver API. Each observed element gets a fade-in class immediately, then visible once it enters the viewport.

js/app.js — Fade-in observer
const fadeEls = document.querySelectorAll(
  '.ingredient-card, .variation-card, .stall-card, ' +
  '.culture-item, .person-card, .recipe-phase'
);

fadeEls.forEach(el => el.classList.add('fade-in'));

const observer = new IntersectionObserver(entries => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('visible');
    }
  });
}, { threshold: 0.1, rootMargin: '0px 0px -40px 0px' });

fadeEls.forEach(el => observer.observe(el));
css/style.css — Fade-in transition
.fade-in {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 0.5s ease, transform 0.5s ease;
}

.fade-in.visible {
  opacity: 1;
  transform: translateY(0);
}

🔑 Key Code: Active Nav on Scroll

The sticky navigation highlights the current section as the user scrolls. It tracks which section[id] is currently in view and adds an active class to the matching nav link.

js/app.js — Active nav tracking
const sections = document.querySelectorAll('section[id]');
const navLinks = document.querySelectorAll('.nav-links a');

function updateActiveNav() {
  let current = '';
  sections.forEach(section => {
    const sectionTop = section.offsetTop - 100;
    if (window.scrollY >= sectionTop) {
      current = section.getAttribute('id');
    }
  });
  navLinks.forEach(link => {
    link.classList.remove('active');
    if (link.getAttribute('href') === '#' + current) {
      link.classList.add('active');
    }
  });
}

window.addEventListener('scroll', updateActiveNav, { passive: true });

🔑 Key Code: CSS Design Tokens

The site uses CSS custom properties for a consistent colour scheme and spacing across all sections.

css/style.css — Root variables
:root {
  --brown:       #5C3D2E;
  --brown-light: #7A5243;
  --cream:       #FDF6EC;
  --cream-mid:   #F5ECD7;
  --gold:        #C8973E;
  --gold-light:  #E8B96A;
  --text-dark:   #2C1810;
  --text-mid:    #4A3728;
  --text-light:  #7A6558;
  --section-light: #FDF6EC;
  --section-mid:   #F5ECD7;
  --section-dark:  #3D2415;
  --radius:      8px;
  --shadow-sm:   0 2px 8px rgba(0,0,0,0.08);
  --shadow-md:   0 4px 16px rgba(0,0,0,0.12);
  --transition:  0.3s ease;
}

📄 Knowledge Source Files

Each section of the site was authored from a corresponding Markdown file in knowledge/. These files contain the raw facts, quotes, and structured data that were used to write the HTML content.

Example: knowledge/where-to-eat.md

knowledge/where-to-eat.md (excerpt)
---
title: "Where to Eat Sarawak Laksa"
tags: ["stalls", "kuching", "hawker", "halal"]
---

## Top Picks

### Chong Choon Cafe
- Location: Jalan Wan Alwi, Kuching
- Hours: 7:00am – 11:30am
- Halal: No
- One of Kuching's most celebrated laksa stalls.
  Queue starts at 7am. Usually sold out by 10am.