DICT-ILCDB Webdev — Take-Home Reference
Everything you need at your desk, in one file. Compiled from the daily Pocket Guides + Glossary + FAQ + course map. Last regenerated: 2026-05-06
DICT-ILCDB Web Dev Foundations — Course Map
4 days · 19 sessions · ~28 hours of teaching + workshop time
Daily shape: 09:00 start · 12:00–13:00 lunch · 15:00–15:15 break · 17:00 end.
Each day ends with a 10-q quiz + 1 take-home assignment.
Day 1 — HTML & Accessibility
"We hand-code real HTML so that when the framework generates it for us, we know if it's wrong."
| # | Time | Type | Title | Files |
|---|---|---|---|---|
| 1.1 | 09:00–09:45 | Lecture | Welcome + how the web works + pretest | slides |
| 1.2 | 09:45–11:00 | Workshop | Semantic HTML (headings, landmarks, links, lists) | slides · workshop |
| 1.3 | 11:00–12:00 | Workshop | Forms + inputs + labels | slides · workshop |
| 1.4 | 13:00–14:30 | Workshop | Tables, images, audio/video + alt text | slides · workshop |
| 1.5 | 14:30–16:30 | Workshop | Accessibility (keyboard, ARIA, contrast, screen-readers) | slides · workshop |
| — | 16:30–17:00 | Recap | Day-1 quiz + assignment | quiz · assignment · cheatsheet |
Day 2 — CSS & Tailwind
"CSS Grid + Flexbox handle 95% of layouts. Tailwind handles 95% of styling. We learn the why first, then the shortcut."
| # | Time | Type | Title | Files |
|---|---|---|---|---|
| 2.1 | 09:00–10:30 | Workshop | CSS basics (selectors, cascade, specificity, box model) | slides · workshop |
| 2.2 | 10:30–12:00 | Workshop | Layout: Flexbox + Grid | slides · workshop |
| 2.3 | 13:00–14:30 | Workshop | Responsive design + media queries (mobile-first) | slides · workshop |
| 2.4 | 14:30–16:00 | Workshop | Tailwind CSS utilities + components | slides · workshop |
| 2.5 | 16:00–16:30 | Workshop | Dark mode + design tokens | slides · workshop |
| — | 16:30–17:00 | Recap | Day-2 quiz + assignment | quiz · assignment · cheatsheet |
Day 3 — JavaScript & DOM
"Vanilla JS first. When you understand the DOM, every framework becomes 'oh, that's how it works.'"
| # | Time | Type | Title | Files |
|---|---|---|---|---|
| 3.1 | 09:00–10:30 | Workshop | JavaScript basics (let/const, types, fn, arrays, objects) | slides · workshop |
| 3.2 | 10:30–12:00 | Workshop | ES2020+ (arrow, destructure, spread, modules, optional chaining) | slides · workshop |
| 3.3 | 13:00–14:30 | Workshop | DOM + events | slides · workshop |
| 3.4 | 14:30–16:00 | Workshop | fetch + async/await + JSON | slides · workshop |
| 3.5 | 16:00–16:30 | Workshop | Form validation + Alpine.js intro | slides · workshop |
| — | 16:30–17:00 | Recap | Day-3 quiz + assignment | quiz · assignment · cheatsheet |
Day 4 — Git, HTTP & Deploy
"Code that's not in git doesn't exist. Code that's not deployed doesn't help anyone."
| # | Time | Type | Title | Files |
|---|---|---|---|---|
| 4.1 | 09:00–11:00 | Workshop | Git + GitHub day-to-day flow | slides · workshop |
| 4.2 | 11:00–12:00 | Workshop | HTTP request/response + DevTools Network | slides · workshop |
| 4.3 | 13:00–15:00 | Workshop | Deploy to Netlify / Vercel / GitHub Pages | slides · workshop |
| 4.4 | 15:00–16:00 | Brief | Capstone walk-through + Q&A | slides · brief |
| 4.5 | 16:00–17:00 | Closing | Reflection + post-test + evaluation + photos | slides · post-test · eval |
After the training
- Capstone due 1 week post-training (Sunday 23:59 PHT). See capstone/brief.md.
- Bridge to a framework: codeigniter-bridge.md or laravel-bridge.md.
- Stay engaged: post-graduation-track.md.
Reference materials (always-available)
- setup-guide.md — install everything (Day 0)
- pre-training-checklist.md — what to do before Day 1
- glossary.md / glossary-tagalog.md
- faq.md
- quick-reference-card.md — 1-page cheat
- workshop-checkpoints.md — pass criteria per workshop
- take-home-reference.md — auto-compiled mega-doc
Day 1 Cheatsheet — HTML & Accessibility
Print this. Refer during all Day-1 workshops.
Boilerplate
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Short page summary">
<title>Page Title — Site Name</title>
<link rel="icon" href="/favicon.ico">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header>
<nav aria-label="Primary">
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
</header>
<main>
<h1>Page heading</h1>
<p>Intro copy.</p>
<section>
<h2>Section heading</h2>
<p>...</p>
</section>
</main>
<footer>
<p>© 2025 DICT-ILCDB</p>
</footer>
<script src="app.js" defer></script>
</body>
</html>
Semantic landmarks
| Tag | Use |
|---|---|
<header> |
Top of page or section (logo, title, nav) |
<nav> |
Major navigation (give aria-label if multiple) |
<main> |
Primary content; only one per page |
<aside> |
Sidebar, related links, asides |
<article> |
Self-contained content (post, comment) |
<section> |
Thematic grouping with a heading |
<footer> |
Bottom of page or section (copyright, contact) |
Headings rule: one <h1>, then <h2> for sections, <h3> inside <h2> content. Don't skip levels.
Text content
<p>Paragraph.</p>
<strong>Important</strong> <!-- semantic emphasis -->
<em>Emphasized</em> <!-- semantic stress -->
<small>Fine print</small>
<mark>Highlighted</mark>
<code>inline code</code>
<pre><code>block code</code></pre>
<blockquote cite="https://...">Quote text</blockquote>
<abbr title="Department of Information and Communications Technology">DICT</abbr>
<time datetime="2025-01-15">Jan 15, 2025</time>
Lists
<ul>
<li>Unordered item</li>
</ul>
<ol>
<li>Ordered item</li>
</ol>
<dl>
<dt>Term</dt>
<dd>Definition</dd>
</dl>
Links
<a href="/about">Internal link</a>
<a href="https://dict.gov.ph">External</a>
<a href="https://dict.gov.ph" target="_blank" rel="noopener">External, new tab</a>
<a href="mailto:hello@dict.gov.ph">Email me</a>
<a href="tel:+639171234567">Call me</a>
<a href="#section-2">Jump to section</a>
<a href="/file.pdf" download>Download PDF</a>
Always add rel="noopener" to target="_blank" links — security.
Images
<img src="logo.png" alt="DICT logo"> <!-- meaningful -->
<img src="decor.svg" alt=""> <!-- decorative -->
<img src="hero.jpg" alt="Beach in Palawan"
width="1200" height="600" loading="lazy">
<figure>
<img src="chart.png" alt="2024 ICT readiness scores by region">
<figcaption>Source: DICT ICT Readiness Survey 2024</figcaption>
</figure>
<picture>
<source srcset="hero-mobile.jpg" media="(max-width: 768px)">
<source srcset="hero-desktop.jpg">
<img src="hero-desktop.jpg" alt="Hero">
</picture>
Audio / Video
<audio controls src="podcast.mp3">
<p>Your browser doesn't support audio. <a href="podcast.mp3">Download</a>.</p>
</audio>
<video controls width="640" poster="thumb.jpg">
<source src="vid.mp4" type="video/mp4">
<source src="vid.webm" type="video/webm">
<track src="vid.vtt" kind="captions" srclang="en" label="English">
</video>
Tables
<table>
<caption>2024 Q4 LGU portal launches</caption>
<thead>
<tr><th scope="col">Region</th><th scope="col">Sites</th></tr>
</thead>
<tbody>
<tr><th scope="row">NCR</th><td>14</td></tr>
<tr><th scope="row">CALABARZON</th><td>22</td></tr>
</tbody>
<tfoot>
<tr><th scope="row">Total</th><td>36</td></tr>
</tfoot>
</table>
Forms
<form action="/submit" method="post" novalidate>
<fieldset>
<legend>Contact details</legend>
<div>
<label for="name">Full name</label>
<input id="name" name="name" type="text" required autocomplete="name">
</div>
<div>
<label for="email">Email</label>
<input id="email" name="email" type="email" required autocomplete="email"
aria-describedby="email-hint">
<small id="email-hint">We won't spam you.</small>
</div>
<div>
<label for="msg">Message</label>
<textarea id="msg" name="msg" rows="5" required></textarea>
</div>
<fieldset>
<legend>Notify me about</legend>
<label><input type="checkbox" name="topics[]" value="news"> News</label>
<label><input type="checkbox" name="topics[]" value="events"> Events</label>
</fieldset>
<button type="submit">Send</button>
</fieldset>
</form>
Input types
text · email · password · number · tel · url · date · time · datetime-local · checkbox · radio · file · search · color · range · hidden
Useful attributes
required · placeholder · pattern="..." · minlength · maxlength · min · max · step · autocomplete · readonly · disabled · multiple
Accessibility (a11y) checklist
- Page
<title>is descriptive -
<html lang="en">set - One
<h1>per page - Headings in order (no skipping h1 → h3)
- Every
<img>hasalt(oralt=""if decorative) - Every form
<input>has a<label> - All buttons have visible labels (no icon-only without
aria-label) - Color contrast ≥ 4.5:1 for body text, ≥ 3:1 for large text
- Focus visible (
:focus-visible { outline: 2px solid ... }) - Tab order is logical
- No keyboard trap (Tab + Esc work everywhere)
- Skip-to-content link first inside
<body>
<a href="#main" class="skip-link">Skip to main content</a>
<!-- ... -->
<main id="main" tabindex="-1">...</main>
ARIA quick guide
ARIA fills gaps native HTML can't:
<button aria-expanded="false" aria-controls="menu">Menu</button>
<ul id="menu" hidden>...</ul>
<div role="alert">Save failed</div> <!-- announced live -->
<div aria-live="polite">Loading…</div> <!-- announced when changes -->
<input aria-label="Search"> <!-- when no visible label -->
<input aria-describedby="hint"> <!-- additional info -->
<button aria-pressed="false">Toggle</button>
Rule: if HTML can do it, don't use ARIA. (e.g. use
<button>not<div role="button">.)
Meta tags worth knowing
<meta name="description" content="120-char summary for SEO">
<meta name="theme-color" content="#003566">
<meta property="og:title" content="Page Title">
<meta property="og:description" content="Summary">
<meta property="og:image" content="https://example.com/preview.png">
<meta property="og:url" content="https://example.com/page">
<meta name="twitter:card" content="summary_large_image">
Validation tools
- Lighthouse: Chrome DevTools → Lighthouse tab → Generate report
- HTML validator: https://validator.w3.org
- A11y check: Lighthouse + https://wave.webaim.org
- Color contrast: https://contrast-ratio.com
§… Recipes & One-liners (Day 1)
ðŸ—ï¸ Boilerplate snippets
<!-- Skip link (accessibility) -->
<a href="#main" class="skip">Skip to main content</a>
<!-- Responsive image -->
<img src="hero-800.jpg" srcset="hero-400.jpg 400w, hero-800.jpg 800w, hero-1600.jpg 1600w"
sizes="(min-width: 768px) 50vw, 100vw" alt="Team photo" width="800" height="450" loading="lazy">
<!-- Picture: art direction -->
<picture>
<source media="(min-width: 768px)" srcset="hero-wide.webp" type="image/webp">
<source srcset="hero-tall.webp" type="image/webp">
<img src="hero-tall.jpg" alt="">
</picture>
<!-- Lazy iframe -->
<iframe src="https://www.youtube.com/embed/X" loading="lazy" title="Demo"
width="560" height="315" allowfullscreen></iframe>
<!-- Native modal (no JS) -->
<dialog id="contact">
<form method="dialog">
<p>Email me?</p>
<button value="yes">Yes</button>
<button value="no">No</button>
</form>
</dialog>
<button onclick="contact.showModal()">Contact</button>
<!-- Native FAQ accordion (no JS) -->
<details>
<summary>What is DICT-ILCDB?</summary>
<p>Department of Information & Communications Technology — Information & Communications Technology Literacy and Competency Development Bureau.</p>
</details>
✅ Form quick-reference
<label>Email <input type="email" name="email" required
autocomplete="email" inputmode="email"></label>
<label>Phone <input type="tel" name="phone"
pattern="09\d{9}" placeholder="09171234567" required></label>
<label>Birthdate <input type="date" max="2010-01-01"></label>
<label>Quantity <input type="number" min="1" max="99" step="1"></label>
<label>Message <textarea minlength="20" maxlength="500" required></textarea></label>
<!-- Datalist autocomplete -->
<input list="barangays" name="barangay">
<datalist id="barangays">
<option value="Bagong Silangan">
<option value="Batasan Hills">
</datalist>
♿ Accessibility one-liners
| Need | Snippet |
|---|---|
| Decorative image | <img alt=""> (empty alt!) |
| Icon-only button | <button aria-label="Close">×</button> |
| Live announcement | <div role="status" aria-live="polite">Saved.</div> |
| Hide visually but readable to SR | class="sr-only" (CSS clip-path trick) |
| Current nav item | <a aria-current="page" href="..."> |
| Form error link | <input aria-invalid="true" aria-describedby="err"> |
| Modal trap | <dialog> (built-in focus trap) |
| Hidden text for SR only | <span class="sr-only">opens in new tab</span> |
/* sr-only utility class */
.sr-only {
position: absolute; width: 1px; height: 1px;
padding: 0; margin: -1px; overflow: hidden;
clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
🔗 SEO / Open Graph cheat block
<meta name="description" content="Personal portfolio of Juan dela Cruz.">
<meta property="og:title" content="Juan dela Cruz — Frontend Developer">
<meta property="og:description" content="Portfolio.">
<meta property="og:image" content="https://example.com/og.png">
<meta property="og:url" content="https://juan.example.com">
<meta name="twitter:card" content="summary_large_image">
<link rel="canonical" href="https://juan.example.com">
Day-1 checklist before going home:
- All workshops 1.2–1.5 checkpoints met
- Day-1 quiz submitted
- Day-1 assignment understood (do tonight)
Day 2 Cheatsheet — CSS & Tailwind
Print this. Refer during all Day-2 workshops.
CSS link & inline
<!-- External (preferred) -->
<link rel="stylesheet" href="styles.css">
<!-- Internal -->
<style>
body { font-family: system-ui, sans-serif; }
</style>
<!-- Inline (avoid except quick test) -->
<p style="color: red;">Hi</p>
Selectors
* { } /* universal */
h1 { } /* tag */
.card { } /* class */
#header { } /* id */
[type="email"] { } /* attribute */
/* Combinators */
nav a { } /* descendant */
nav > a { } /* direct child */
h2 + p { } /* adjacent sibling */
h2 ~ p { } /* general sibling */
/* States */
a:hover { }
a:focus-visible{ }
input:disabled { }
li:nth-child(odd) { }
li:first-child { }
li:last-of-type { }
/* Pseudo-elements */
p::first-letter { }
p::before { content: "→ "; }
::placeholder { color: gray; }
Specificity
inline > id > class > tag
Higher specificity wins. Don't use !important.
Variables
:root {
--brand: #003566;
--gold: #ffc300;
--space-1: 0.25rem;
--space-2: 0.5rem;
--space-4: 1rem;
--radius: 0.5rem;
}
.btn {
background: var(--brand);
padding: var(--space-2) var(--space-4);
border-radius: var(--radius);
}
Box model
*, *::before, *::after { box-sizing: border-box; } /* always */
.box {
margin: 1rem; /* outside */
border: 1px solid #ccc;
padding: 1rem; /* inside */
/* width / height refer to content + padding + border with border-box */
}
/* Shorthands */
margin: 1rem; /* all 4 */
margin: 1rem 0; /* y x */
margin: 1rem 0 2rem; /* top x bottom */
margin: 1rem 0 2rem 0.5rem; /* top right bottom left */
Typography
body {
font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
font-size: 1rem;
line-height: 1.6;
color: #1f2937;
}
h1 { font-size: clamp(1.75rem, 4vw, 2.5rem); }
.lead { font-size: 1.25rem; line-height: 1.5; }
.text-muted { color: #6b7280; }
Colors
color: #003566;
color: rgb(0 53 102); /* modern syntax */
color: rgb(0 53 102 / 0.8); /* with alpha */
color: hsl(210 100% 20%);
background: linear-gradient(135deg, #003566, #0077b6);
Flexbox (1D)
.row {
display: flex;
flex-direction: row; /* row | row-reverse | column | column-reverse */
flex-wrap: wrap; /* nowrap | wrap | wrap-reverse */
gap: 1rem;
justify-content: space-between; /* main axis */
align-items: center; /* cross axis */
}
.row > * {
flex: 1 1 200px; /* grow shrink basis */
}
.center { display: flex; place-items: center; min-height: 100vh; }
Grid (2D)
.layout {
display: grid;
grid-template-columns: 200px 1fr;
grid-template-rows: auto 1fr auto;
grid-template-areas:
"sidebar header"
"sidebar main"
"sidebar footer";
gap: 1rem;
min-height: 100vh;
}
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
/* Responsive cards */
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 1rem;
}
/* Span columns */
.featured { grid-column: span 2; }
.full { grid-column: 1 / -1; }
Responsive (mobile-first)
/* Base = mobile */
.hero { padding: 1rem; font-size: 1.25rem; }
/* Tablet+ */
@media (min-width: 640px) {
.hero { padding: 2rem; font-size: 1.5rem; }
}
/* Desktop+ */
@media (min-width: 1024px) {
.hero { padding: 4rem; font-size: 2rem; }
}
/* Print */
@media print {
nav, footer { display: none; }
}
/* Dark mode preference */
@media (prefers-color-scheme: dark) {
:root { --bg: #0b1220; --fg: #e5e7eb; }
}
Common patterns
/* Container */
.container { max-width: 64rem; margin-inline: auto; padding-inline: 1rem; }
/* Centered card */
.card {
background: white;
border-radius: 0.75rem;
padding: 1.5rem;
box-shadow: 0 1px 3px rgb(0 0 0 / 0.1), 0 4px 12px rgb(0 0 0 / 0.05);
}
/* Visually hidden but for screen readers */
.sr-only {
position: absolute; width: 1px; height: 1px; overflow: hidden;
clip: rect(0 0 0 0); white-space: nowrap;
}
/* Truncate text */
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Aspect ratio */
.video { aspect-ratio: 16/9; }
Tailwind cheats
Setup (CDN — fast for prototyping)
<script src="https://cdn.tailwindcss.com"></script>
Setup (CLI — production)
npm install -D tailwindcss
npx tailwindcss init
# Edit tailwind.config.js content paths
# Build: npx tailwindcss -i ./src/in.css -o ./dist/out.css --watch
Most-used utilities
<!-- Layout -->
<div class="container mx-auto max-w-4xl px-4">
<!-- Flexbox -->
<div class="flex items-center justify-between gap-4 flex-wrap">
<!-- Grid -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<!-- Spacing -->
<div class="p-4 px-6 py-2 m-4 mt-8 -mx-2 space-y-4 space-x-2">
<!-- Sizing -->
<div class="w-full h-screen min-h-screen max-w-prose">
<!-- Typography -->
<h1 class="text-2xl md:text-4xl font-bold leading-tight tracking-tight">
<p class="text-base text-gray-700 leading-relaxed">
<!-- Colors -->
<div class="bg-blue-600 text-white hover:bg-blue-700 border border-gray-300">
<!-- Effects -->
<div class="rounded-lg shadow-md transition hover:scale-105 hover:shadow-xl">
Responsive prefixes
sm: ≥ 640px
md: ≥ 768px
lg: ≥ 1024px
xl: ≥ 1280px
2xl: ≥ 1536px
State prefixes
hover: focus: focus-visible: active: disabled:
group-hover: peer-checked: dark:
Dark mode
<html class="dark">
<body class="bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100">
Toggle via JS (see Day 2 workshop 2.5).
Custom theme
// tailwind.config.js
module.exports = {
content: ['./*.html', './src/**/*.{js,html}'],
darkMode: 'class',
theme: {
extend: {
colors: {
brand: { DEFAULT: '#003566', light: '#0077b6' },
},
},
},
};
Usage:
<div class="bg-brand text-white">
Component patterns
Card
<article class="bg-white dark:bg-gray-800 rounded-xl shadow p-6 hover:shadow-lg transition">
<h3 class="text-lg font-semibold mb-2">Title</h3>
<p class="text-gray-600 dark:text-gray-300">Body</p>
<a href="#" class="mt-4 inline-block text-blue-600 hover:underline">Read more →</a>
</article>
Button
<button class="inline-flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-md
hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500
disabled:opacity-50 disabled:cursor-not-allowed transition">
Save
</button>
Form input
<label class="block">
<span class="text-sm font-medium text-gray-700 dark:text-gray-200">Email</span>
<input type="email" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm
focus:border-blue-500 focus:ring-blue-500" required>
</label>
Alert
<div role="alert" class="border-l-4 border-red-500 bg-red-50 text-red-900 p-4 rounded">
<strong class="font-semibold">Error:</strong> something failed.
</div>
§… Recipes & One-liners (Day 2)
🎨 CSS variables theme system
:root {
--brand: #003566;
--accent: #ffc300;
--bg: #ffffff;
--fg: #1a1a1a;
--gap: clamp(1rem, 2vw, 2rem);
--radius: 0.5rem;
--shadow: 0 2px 8px rgb(0 0 0 / .1);
}
[data-theme="dark"] {
--bg: #0d1117; --fg: #e6edf3; --shadow: 0 2px 8px rgb(0 0 0 / .5);
}
body { background: var(--bg); color: var(--fg); }
📠Layout one-liners
/* Auto-balancing card grid — NO media query needed */
.cards { display: grid; gap: 1rem;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr)); }
/* Stack with consistent gap */
.stack > * + * { margin-block-start: 1rem; }
/* Center anything */
.center { display: grid; place-items: center; min-height: 100vh; }
/* Holy Grail */
.app { display: grid; min-height: 100vh;
grid-template:
"header header header" auto
"nav main aside" 1fr
"footer footer footer" auto / 200px 1fr 200px; }
/* Sticky footer (flexbox) */
body { min-height: 100vh; display: flex; flex-direction: column; }
main { flex: 1; }
/* Aspect ratio */
.thumb { aspect-ratio: 16/9; object-fit: cover; }
/* Hide scrollbar but keep scroll */
.scroll { overflow: auto; scrollbar-width: none; }
.scroll::-webkit-scrollbar { display: none; }
📱 Fluid typography (no media queries)
h1 { font-size: clamp(1.75rem, 1.2rem + 2.5vw, 3rem); }
p { font-size: clamp(1rem, 0.9rem + 0.5vw, 1.125rem); }
.container { width: min(95%, 1200px); margin-inline: auto; }
🌙 Dark mode — production-ready (no flash)
<head>
<script>
// Run BEFORE CSS to avoid FOUC
(function() {
var t = localStorage.getItem('theme') ||
(matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
document.documentElement.dataset.theme = t;
})();
</script>
</head>
// Toggle button
document.querySelector('#theme-toggle').addEventListener('click', () => {
const root = document.documentElement;
const next = root.dataset.theme === 'dark' ? 'light' : 'dark';
root.dataset.theme = next;
localStorage.setItem('theme', next);
});
âš¡ Tailwind utilities cheat-cheat
| Want | Class |
|---|---|
| Container | container mx-auto px-4 |
| Auto-fit grid | grid grid-cols-[repeat(auto-fit,minmax(280px,1fr))] gap-4 |
| Center vertically | flex items-center justify-center min-h-screen |
| Card | bg-white rounded-lg shadow p-4 |
| Primary button | bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded |
| Sticky header | sticky top-0 z-10 bg-white/80 backdrop-blur |
| Truncate | truncate (1 line) or line-clamp-3 |
| Visually-hidden | sr-only |
| Dark variant | dark:bg-slate-900 dark:text-slate-100 |
| Responsive | md:grid-cols-2 lg:grid-cols-3 |
Day-2 checklist:
- All workshops 2.1–2.5 checkpoints met
- Day-2 quiz submitted
- Day-2 assignment understood
Day 3 Cheatsheet — JavaScript & DOM
Print this. Refer during all Day-3 workshops.
Loading JS
<!-- Defer (recommended) — runs after HTML parsed, in order -->
<script src="app.js" defer></script>
<!-- Module — modern, scoped, supports import/export -->
<script type="module" src="app.js"></script>
<!-- Inline -->
<script>
console.log('hi');
</script>
Variables
const PI = 3.14; // immutable binding
let count = 0; // mutable
count = count + 1;
// var: legacy, never use
Primitives & types
// number, string, boolean, null, undefined, bigint, symbol
let n = 42;
let s = 'hello'; // single or double quotes
let t = `Hi, ${name}!`; // template literal
let b = true;
let nothing = null; // intentional empty
let undef; // declared but unset
typeof 42; // "number"
typeof 'a'; // "string"
typeof null; // "object" ↠JS quirk
typeof []; // "object"
Array.isArray([]);// true
Comparison
// Always ===
1 === '1' // false
1 == '1' // true (coercion — avoid)
null == undefined // true (only loose case worth knowing)
null === undefined // false
// Truthy / falsy
// Falsy: false, 0, '', null, undefined, NaN
if (value) { /* truthy */ }
Numbers
Math.round(2.5); // 3
Math.floor(2.9); // 2
Math.ceil(2.1); // 3
Math.abs(-5); // 5
Math.max(1, 2, 3); // 3
Math.min(1, 2, 3); // 1
Math.random(); // 0..1
Math.PI;
parseInt('42', 10);
parseFloat('3.14');
Number('42'); // 42
(123.456).toFixed(2); // "123.46"
Strings
const s = 'Hello, World';
s.length; // 12
s.toLowerCase();
s.toUpperCase();
s.includes('World');
s.startsWith('Hello');
s.endsWith('!');
s.indexOf('o');
s.slice(0, 5); // "Hello"
s.replace('World', 'Juan');
s.replaceAll(',', ';');
s.split(', ');
s.trim();
'foo'.repeat(3); // "foofoofoo"
' '.padStart(5, '0'); // "000 "
Arrays
const arr = [1, 2, 3];
// Read
arr[0]; // 1
arr.length; // 3
arr.at(-1); // 3 (last item)
// Mutate
arr.push(4); // add to end → [1,2,3,4]
arr.pop(); // remove last
arr.unshift(0); // add to start
arr.shift(); // remove first
arr.splice(1, 1); // remove 1 item at index 1
// Read-only (return new)
arr.concat([5,6]);
arr.slice(0, 2);
[...arr, 4]; // spread
[...arr].sort();
[...arr].reverse();
// Iteration
arr.forEach((x, i) => console.log(i, x));
arr.map(x => x * 2); // [2, 4, 6]
arr.filter(x => x > 1); // [2, 3]
arr.reduce((sum, x) => sum + x, 0); // 6
arr.find(x => x === 2); // 2
arr.findIndex(x => x === 2); // 1
arr.some(x => x > 2); // true
arr.every(x => x > 0); // true
arr.includes(2); // true
// for…of
for (const x of arr) { console.log(x); }
for (const [i, x] of arr.entries()) { console.log(i, x); }
Objects
const user = { name: 'Juan', age: 30, address: { city: 'Manila' } };
// Access
user.name;
user['name'];
user.address?.city; // optional chaining (undefined if no address)
user.foo ?? 'default'; // nullish coalescing
// Mutate
user.email = 'j@example.com';
delete user.age;
// Iterate
Object.keys(user);
Object.values(user);
Object.entries(user);
for (const [k, v] of Object.entries(user)) { ... }
// Copy / merge
const copy = { ...user };
const merged = { ...user, ...overrides };
// Destructure
const { name, age = 0 } = user;
const { address: { city } } = user;
Functions
// Declarations (hoisted)
function add(a, b) { return a + b; }
// Expressions
const add = function (a, b) { return a + b; };
// Arrow (no `this`, no `arguments`)
const add = (a, b) => a + b;
const square = x => x * x;
const log = () => console.log('hi');
// Defaults & rest
function greet(name = 'friend', ...others) {
console.log('hi', name, ...others);
}
// Async
async function loadUser(id) {
const r = await fetch(`/api/users/${id}`);
if (!r.ok) throw new Error(`HTTP ${r.status}`);
return r.json();
}
Modules
// math.js
export const PI = 3.14;
export function add(a, b) { return a + b; }
export default function mul(a, b) { return a * b; }
// app.js
import mul, { PI, add } from './math.js';
import * as math from './math.js';
In HTML: <script type="module" src="app.js"></script>.
DOM — selecting
document.getElementById('hero'); // single, by id
document.querySelector('.card'); // first match (CSS selector)
document.querySelectorAll('.card'); // NodeList of all matches
const el = document.querySelector('.card');
el.parentElement;
el.children;
el.nextElementSibling;
el.previousElementSibling;
el.closest('.card-list'); // walk up
DOM — reading & writing
// Text
el.textContent = 'Hi'; // safe
el.innerHTML = '<b>Hi</b>'; // unsafe with user input → XSS
// Attributes
el.getAttribute('href');
el.setAttribute('href', '/about');
el.removeAttribute('disabled');
// Properties (faster than attributes for common ones)
el.value; // <input>
el.checked; // checkbox/radio
el.disabled;
el.hidden = true;
// Classes
el.classList.add('active');
el.classList.remove('hidden');
el.classList.toggle('dark');
el.classList.contains('open');
// Inline styles
el.style.color = 'red';
el.style.setProperty('--brand', '#003566');
// Data attributes
// HTML: <div data-user-id="42">
el.dataset.userId; // "42"
el.dataset.userId = '99';
DOM — creating & inserting
const li = document.createElement('li');
li.textContent = 'New item';
li.classList.add('item');
const ul = document.querySelector('ul');
ul.append(li); // at end (multiple OK)
ul.prepend(li); // at start
li.before(otherEl);
li.after(otherEl);
li.remove();
li.replaceWith(otherEl);
// Bulk via fragments (perf)
const frag = document.createDocumentFragment();
items.forEach(x => {
const li = document.createElement('li');
li.textContent = x;
frag.append(li);
});
ul.append(frag);
// Or template literals (with sanitization!)
ul.insertAdjacentHTML('beforeend', '<li>safe text</li>');
Events
// Listen
btn.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
console.log(event.target);
});
// Common events
'click' 'dblclick' 'submit' 'input' 'change' 'focus' 'blur'
'keydown' 'keyup' 'keypress'
'mouseenter' 'mouseleave' 'mouseover' 'mouseout'
'scroll' 'resize' 'load' 'DOMContentLoaded'
// Form values
form.addEventListener('submit', (e) => {
e.preventDefault();
const data = Object.fromEntries(new FormData(e.target));
console.log(data);
});
// Delegation (one listener for many children)
list.addEventListener('click', (e) => {
const item = e.target.closest('.item');
if (item) console.log(item.dataset.id);
});
// Once
btn.addEventListener('click', handler, { once: true });
// Remove
btn.removeEventListener('click', handler);
Promises & async
// Promise basics
fetch('/api')
.then(r => r.json())
.then(data => console.log(data))
.catch(err => console.error(err))
.finally(() => console.log('done'));
// Async/await — same thing, cleaner
async function main() {
try {
const r = await fetch('/api');
if (!r.ok) throw new Error(`HTTP ${r.status}`);
const data = await r.json();
console.log(data);
} catch (err) {
console.error(err);
} finally {
console.log('done');
}
}
main();
// Parallel
const [users, posts] = await Promise.all([
fetch('/users').then(r => r.json()),
fetch('/posts').then(r => r.json()),
]);
fetch — request types
// GET (default)
const r = await fetch('https://jsonplaceholder.typicode.com/posts/1');
const post = await r.json();
// POST JSON
const r = await fetch('https://api.example.com/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: 'Hi', body: '...' }),
});
// POST form data
const formData = new FormData(formEl);
await fetch('/upload', { method: 'POST', body: formData });
// Auth header
await fetch('/api', { headers: { Authorization: `Bearer ${token}` } });
// Inspect response
r.ok; // true if status 200–299
r.status; // 200
r.statusText; // "OK"
r.headers.get('content-type');
localStorage
localStorage.setItem('theme', 'dark');
localStorage.getItem('theme'); // "dark" or null
localStorage.removeItem('theme');
localStorage.clear();
// Save objects (JSON)
localStorage.setItem('user', JSON.stringify({ name: 'Juan' }));
const user = JSON.parse(localStorage.getItem('user') || 'null');
Alpine.js (intro)
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<!-- Counter -->
<div x-data="{ count: 0 }">
<button @click="count++">+</button>
<span x-text="count"></span>
</div>
<!-- Toggle -->
<div x-data="{ open: false }">
<button @click="open = !open" :aria-expanded="open">Menu</button>
<ul x-show="open" @click.outside="open = false">
<li>Home</li><li>About</li>
</ul>
</div>
<!-- Form binding -->
<div x-data="{ name: '' }">
<input x-model="name">
<p x-show="name">Hi, <span x-text="name"></span>!</p>
</div>
Common gotchas
// "Cannot read property 'X' of null"
// → element didn't exist when JS ran. Use defer or DOMContentLoaded.
// "X is not a function"
// → typo or wrong import.
// CORS error
// → server didn't allow your origin. Use a CORS-enabled API.
// "Unexpected token < in JSON"
// → server returned HTML (often 404 or 500), not JSON. Check r.ok first.
§… Recipes & One-liners (Day 3)
📜 Modern JS one-liners
// Default values + destructuring
const { name = 'Anon', tags = [] } = user ?? {};
// Optional chaining
const city = user?.address?.barangay ?? 'Unknown';
// Spread / merge objects
const merged = { ...defaults, ...overrides };
// Group by key (ES2024)
const byBarangay = Object.groupBy(residents, r => r.barangay);
// Unique array
const uniq = [...new Set(arr)];
// Sort objects
residents.sort((a, b) => a.lastName.localeCompare(b.lastName));
// Sum / max / min
const total = items.reduce((s, x) => s + x.qty, 0);
const max = Math.max(...nums);
// Pick keys from object
const { id, name } = user; // pick
const { password, ...safe } = user; // omit
// Async sleep
const sleep = ms => new Promise(r => setTimeout(r, ms));
// Format PHP currency
const peso = n => new Intl.NumberFormat('en-PH',
{ style: 'currency', currency: 'PHP' }).format(n);
// Format date Tagalog
const fmt = d => new Intl.DateTimeFormat('fil-PH',
{ dateStyle: 'long' }).format(d);
// Copy to clipboard
await navigator.clipboard.writeText('hello');
ðŸ–±ï¸ DOM event recipes
// Event delegation (1 listener for many buttons)
document.querySelector('.list').addEventListener('click', (e) => {
const btn = e.target.closest('[data-id]');
if (!btn) return;
console.log('clicked', btn.dataset.id);
});
// Once-only listener
btn.addEventListener('click', go, { once: true });
// Bulk-removable group
const ctrl = new AbortController();
btn.addEventListener('click', go, { signal: ctrl.signal });
win.addEventListener('resize', adjust, { signal: ctrl.signal });
ctrl.abort(); // removes BOTH listeners
// Debounce (lodash-free)
const debounce = (fn, ms = 300) => {
let t; return (...a) => { clearTimeout(t); t = setTimeout(() => fn(...a), ms); };
};
searchInput.addEventListener('input', debounce(search, 300));
// Throttle (max 1 per N ms)
const throttle = (fn, ms = 100) => {
let last = 0; return (...a) => {
const now = Date.now();
if (now - last >= ms) { last = now; fn(...a); }
};
};
// IntersectionObserver lazy-load
const io = new IntersectionObserver((entries) => {
entries.forEach(e => { if (e.isIntersecting) { e.target.src = e.target.dataset.src; io.unobserve(e.target); } });
});
document.querySelectorAll('img[data-src]').forEach(img => io.observe(img));
🌠Fetch patterns
// GET JSON with error handling
async function getJSON(url, signal) {
const r = await fetch(url, { signal });
if (!r.ok) throw new Error(`HTTP ${r.status}`);
return r.json();
}
// POST JSON
await fetch('/api/residents', {
method: 'POST',
headers: { 'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token },
body: JSON.stringify(payload),
});
// Cancel previous request when typing
let ctrl;
searchInput.addEventListener('input', debounce(async (e) => {
ctrl?.abort();
ctrl = new AbortController();
try {
const data = await getJSON('/api/search?q=' + encodeURIComponent(e.target.value), ctrl.signal);
render(data);
} catch (err) { if (err.name !== 'AbortError') showError(err); }
}, 300));
// Parallel fetches
const [users, posts] = await Promise.all([
getJSON('/api/users'), getJSON('/api/posts'),
]);
// Retry with backoff
async function retry(fn, tries = 3) {
for (let i = 0; i < tries; i++) {
try { return await fn(); }
catch (e) { if (i === tries - 1) throw e; await sleep(2 ** i * 500); }
}
}
// Upload file
const fd = new FormData();
fd.append('photo', fileInput.files[0]);
await fetch('/upload', { method: 'POST', body: fd });
// Server-Sent Events
const es = new EventSource('/api/stream');
es.onmessage = (e) => console.log(JSON.parse(e.data));
✅ Form validation snippets
// Cross-field check (password match)
confirmPw.addEventListener('input', () => {
confirmPw.setCustomValidity(
confirmPw.value === pw.value ? '' : 'Passwords do not match'
);
});
// Show all errors on submit
form.addEventListener('submit', (e) => {
if (!form.checkValidity()) {
e.preventDefault();
form.querySelectorAll(':invalid').forEach(el => {
el.setAttribute('aria-invalid', 'true');
});
form.querySelector(':invalid')?.focus();
}
});
// Live announce
const live = document.querySelector('#live');
function announce(msg) { live.textContent = ''; setTimeout(() => live.textContent = msg, 50); }
Day-3 checklist:
- All workshops 3.1–3.5 checkpoints met
- Day-3 quiz submitted
- Day-3 assignment understood
Day 4 Cheatsheet — Git, HTTP & Deploy
Print this. Refer during all Day-4 workshops + capstone week.
Git — one-time setup
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git config --global init.defaultBranch main
git config --global pull.rebase false
git config --global core.autocrlf input # macOS/Linux
git config --global core.autocrlf true # Windows
git config --global core.editor "code --wait"
Git — daily flow
git status # what changed
git diff # see unstaged changes
git diff --staged # see staged changes
git add . # stage all
git add path/to/file # stage one file
git restore file # discard unstaged change
git restore --staged file # unstage
git commit -m "Add nav" # save snapshot
git commit -am "msg" # add tracked + commit (skip add)
git log --oneline -10
git log --graph --all --oneline
Git — push / pull
git push # push current branch to its tracked remote
git push -u origin main # first push (set upstream)
git pull # fetch + merge
git fetch # fetch only (no merge)
git remote -v # list remotes
git remote add origin URL
git remote set-url origin URL
Git — branches
git branch # list local
git branch -a # list all (incl. remote)
git checkout -b feature-x # create + switch (or: git switch -c feature-x)
git checkout main
git switch main # modern equivalent
git branch -d feature-x # delete merged branch
git branch -D feature-x # force delete
git push origin --delete feature-x # delete on remote
Git — merging
git checkout main
git pull
git merge feature-x # merge feature-x into main
# Conflicts? → edit files (resolve <<<<<<< markers) → git add . → git commit
git push
Git — undo
# Discard unstaged changes
git restore file
git restore .
# Unstage
git restore --staged file
# Undo last commit, KEEP changes (they go back to staged)
git reset --soft HEAD~1
# Undo last commit, KEEP changes (unstaged)
git reset HEAD~1
# Undo last commit, DROP changes (DANGER)
git reset --hard HEAD~1
# Make a NEW commit that reverses an old one (safe for shared branches)
git revert HEAD
git revert <commit-hash>
# I committed but didn't push — fix the message
git commit --amend -m "better message"
# I committed but didn't push — add a forgotten file
git add forgotten.js
git commit --amend --no-edit
Git — inspect
git log --oneline --graph --all
git show HEAD # last commit details
git show <hash>
git show HEAD:file.txt # see file at last commit
git blame file.txt # who wrote each line
git diff main..feature-x # difference between branches
Git — stash
git stash # set aside uncommitted changes
git stash pop # bring them back
git stash list
git stash drop
Git — tags & releases
git tag v1.0.0
git tag -a v1.0.0 -m "First release"
git push origin v1.0.0
git push --tags
git tag -d v1.0.0 # local delete
git push origin --delete v1.0.0
.gitignore (frontend defaults)
# Dependencies
node_modules/
.pnp/
# Build output
dist/
build/
.cache/
.parcel-cache/
# Env
.env
.env.local
.env.*.local
# OS
.DS_Store
Thumbs.db
# IDE
.vscode/
!.vscode/extensions.json
.idea/
# Logs
npm-debug.log*
yarn-error.log*
*.log
GitHub PR flow
git checkout -b feature-nav
# ...edit, commit...
git push -u origin feature-nav
# Open browser → GitHub → "Compare & pull request" → fill in description → Create
# Review → click "Merge pull request" → confirm
git checkout main
git pull
git branch -d feature-nav
Resolving a merge conflict
When git pull or git merge says "CONFLICT":
git status # shows conflicted files
# Open conflict file in VS Code:
# <<<<<<< HEAD
# your version
# =======
# their version
# >>>>>>> branch-name
# Pick what to keep, delete the markers, save.
git add conflicted-file
git commit # default merge message OK
VS Code shows "Accept Current / Incoming / Both" buttons — click them.
HTTP — request anatomy
GET /api/posts/1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer eyJhbGc...
User-Agent: Mozilla/5.0
HTTP — response anatomy
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: max-age=3600
Set-Cookie: session=abc123; HttpOnly; Secure
{ "id": 1, "title": "Hi" }
HTTP methods
| Method | Use | Idempotent | Body |
|---|---|---|---|
| GET | Read | yes | no |
| POST | Create | no | yes |
| PUT | Replace | yes | yes |
| PATCH | Partial update | no* | yes |
| DELETE | Remove | yes | optional |
| HEAD | Headers only | yes | no |
| OPTIONS | Capabilities (CORS preflight) | yes | no |
HTTP status codes (memorize these)
| Code | Meaning | Common cause |
|---|---|---|
| 200 OK | Success | Normal response |
| 201 Created | POST success | Resource created |
| 204 No Content | Success, empty body | DELETE success |
| 301 Moved Permanently | Permanent redirect | URL changed |
| 302 Found | Temporary redirect | Login redirect |
| 304 Not Modified | Cached | Browser already has it |
| 400 Bad Request | Client error | Malformed JSON |
| 401 Unauthorized | Auth required | No / expired token |
| 403 Forbidden | Auth ok, not allowed | Wrong role |
| 404 Not Found | URL doesn't exist | Typo or deleted |
| 422 Unprocessable | Validation failed | Server validation rules |
| 429 Too Many Requests | Rate limited | Slow down |
| 500 Server Error | Server bug | Check server logs |
| 502 Bad Gateway | Upstream down | Proxy issue |
| 503 Service Unavailable | Maintenance | Try again |
DevTools — Network tab
- Open DevTools (F12) → Network tab
- Reload page (
Ctrl+R) - See every request: name, status, type, size, time
- Click a row → Headers (request + response), Preview (rendered), Response (raw)
- Throttling dropdown → "Slow 3G" to test mobile speeds
- Disable cache checkbox → simulate first visit
- Filter input —
status:404,mime-type:application/json,larger-than:100k
Lighthouse
DevTools → Lighthouse tab → tick all categories → Analyze page load.
Targets:
- Performance ≥ 90
- Accessibility ≥ 90
- Best Practices ≥ 90
- SEO ≥ 90
Common fixes:
- Performance:
<img loading="lazy">, compress images, defer non-critical JS - A11y: alt text, label inputs, contrast, landmark regions
- Best Practices: HTTPS, no console errors, viewport meta
- SEO: title, meta description, indexable
Deploy — GitHub Pages
# 1. Push your repo to GitHub
git push -u origin main
# 2. Repo → Settings → Pages
# Source: "Deploy from branch"
# Branch: main, folder: / (root) or /docs
# Save
# Wait ~1-2 min, then visit:
# https://USERNAME.github.io/REPO-NAME/
For projects with npm run build, push the dist folder to a gh-pages branch:
npm install -D gh-pages
# package.json: "deploy": "gh-pages -d dist"
npm run build
npm run deploy
Deploy — Netlify (drag & drop)
- https://app.netlify.com → drag your folder onto the dashboard
- Done. URL appears.
Deploy — Netlify (git-integrated)
- Push repo to GitHub
- Netlify → "Add new site" → "Import from Git"
- Authorize → pick repo → branch
main - Build command: empty (static) OR
npm run build(Tailwind CLI) - Publish directory:
.ORdist - Deploy
Every push to main auto-deploys.
Deploy — Vercel
npm i -g vercel
vercel # interactive setup, deploys instantly
vercel --prod # promote to production
Or web UI: https://vercel.com → Import Git Repository.
Custom domain (any host)
- Buy domain from Namecheap / Porkbun (~$10/yr)
- Host dashboard → Domains → Add custom domain → enter
yoursite.com - Domain registrar dashboard → DNS records → add the CNAME / A records the host shows
- Wait 5-60 min for DNS propagation
- Host issues HTTPS cert automatically
Day-4 checklist:
- Capstone repo on GitHub
§… Recipes & One-liners (Day 4)
🔀 Git command sheet
# First-time setup
git config --global user.name "Juan dela Cruz"
git config --global user.email "juan@example.com"
git config --global init.defaultBranch main
git config --global pull.rebase false
# Daily flow
git status # what changed
git add . # stage all
git add -p # stage by hunk (interactive)
git commit -m "feat: add hero section"
git push # to current branch's upstream
# Branching
git switch -c feat/new-form # create + switch (modern)
git switch main # switch back
git branch -d feat/new-form # delete merged branch
git branch -D feat/x # force delete unmerged
# Sync
git fetch --all --prune # update refs, prune deleted
git pull --rebase # pull + rebase local commits on top
git merge --no-ff feat/x # merge keeping branch history
# Inspect
git log --oneline --graph --all -20
git log -p -- README.md # changes per commit for one file
git blame README.md # who wrote each line
git diff main..feat/x # diff between branches
# Undo / fix
git restore README.md # discard unstaged changes
git restore --staged README.md # unstage
git commit --amend # fix LAST commit message/files
git reset --soft HEAD~1 # undo last commit, keep changes
git revert <sha> # safe undo (creates new commit)
# Stash
git stash push -m "WIP form"
git stash list
git stash pop # apply + remove top
git stash apply stash@{2} # apply specific
# Tags & releases
git tag v1.0.0
git push origin v1.0.0
git tag -a v1.1.0 -m "Bug fixes" # annotated
# Pretty log alias (run once)
git config --global alias.lg "log --graph --oneline --all --decorate"
🌠HTTP / fetch debugging
# Inspect headers
curl -I https://example.com
# Verbose with body
curl -v https://api.example.com/users
# POST JSON
curl -X POST https://api.example.com/users \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"name":"Juan"}'
# Save response
curl -o page.html https://example.com
# Follow redirects
curl -L https://example.com
# Time the request
curl -w "@curl-format.txt" -o /dev/null -s https://example.com
# httpbin for testing
curl https://httpbin.org/get
curl https://httpbin.org/headers
curl -X POST -d '{"x":1}' https://httpbin.org/post
🚀 Deployment quick recipes
# GitHub Pages — from a /docs folder
mkdir docs && cp -r dist/* docs/
git add docs && git commit -m "deploy" && git push
# then in repo Settings → Pages → Source: main /docs
# Netlify CLI
npm i -g netlify-cli
netlify deploy --prod --dir=dist
# Vercel CLI
npm i -g vercel
vercel --prod
# Surge.sh — fastest static deploy
npm i -g surge
surge ./dist juan.surge.sh
# Cloudflare Pages
npx wrangler pages deploy dist --project-name=juan-portfolio
# Add a 404 page
echo "<h1>Not found</h1>" > 404.html # GitHub Pages auto-uses this
âš¡ Performance recipes
| Want | Do |
|---|---|
| Lighthouse CLI | npx lighthouse https://x.com --view |
| Image to WebP | upload to Squoosh.app or cwebp in.jpg -o out.webp -q 80 |
| Strip unused CSS | PurgeCSS or Tailwind built-in JIT |
| Inline critical CSS | npx critical https://x.com -i index.html |
| Compress JSON | gzip / brotli on the server (Cloudflare auto) |
| Find biggest assets | DevTools Network → sort by Size column |
| Self-host fonts | download from google-webfonts-helper |
| Defer 3rd-party scripts | <script defer src="analytics.js"> |
✅ Pre-launch checklist
-
HTTPS green padlock
-
Lighthouse Perf / A11y / BP / SEO ≥ 90
-
All images have
alt,width,height -
No console errors on landing
-
Mobile portrait + landscape both look right
-
Tested on iOS Safari + Android Chrome
-
robots.txt+sitemap.xmldeployed -
<meta description>+ Open Graph tags present -
Custom 404 page works
-
Form submissions actually deliver (test once!)
-
Tag
v1.0.0pushed to GitHub -
Site deployed live (HTTPS green padlock)
-
Lighthouse all ≥ 90
-
Tag
v1.0.0pushed -
Post-test submitted
-
Evaluation form submitted
Quick Reference Card — DICT-ILCDB Web Dev Foundations
Fold this. Tape it to your monitor. One page.
HTML skeleton
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="...">
<title>Page Title</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header><nav>...</nav></header>
<main>...</main>
<footer>...</footer>
<script src="app.js" defer></script>
</body>
</html>
Common tags
| Tag | Use |
|---|---|
<h1>–<h6> |
Headings (1 per page for <h1>) |
<p> |
Paragraph |
<a href="..."> |
Link |
<img src="..." alt="..."> |
Image (always alt!) |
<ul><li>...</li></ul> |
Unordered list |
<ol><li>...</li></ol> |
Ordered list |
<button type="button"> |
Button (default submit inside forms!) |
<form>...</form> |
Form |
<input>, <textarea>, <select> |
Inputs |
<header>, <nav>, <main>, <aside>, <footer>, <article>, <section> |
Semantic landmarks |
Form input types
<input type="text"> <input type="email"> <input type="password">
<input type="number"> <input type="tel"> <input type="url">
<input type="date"> <input type="time"> <input type="datetime-local">
<input type="checkbox"> <input type="radio"> <input type="file">
<input type="hidden"> <input type="search"> <input type="color">
CSS basics
/* Selectors */
h1 { } /* tag */
.card { } /* class */
#header { } /* id (use sparingly) */
nav > a { } /* direct child */
nav a { } /* descendant */
a:hover { } /* state */
input:focus { }
[type="email"] { } /* attribute */
/* Box model */
.box {
margin: 1rem 0; /* outside */
padding: 0.5rem 1rem; /* inside */
border: 1px solid #ccc;
border-radius: 0.5rem;
box-sizing: border-box; /* width includes padding+border */
}
/* Flexbox (1D) */
.row {
display: flex;
gap: 1rem;
align-items: center; /* cross-axis */
justify-content: space-between; /* main-axis */
flex-wrap: wrap;
}
/* Grid (2D) */
.layout {
display: grid;
grid-template-columns: 200px 1fr;
gap: 1rem;
}
.gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 1rem;
}
/* Responsive */
@media (min-width: 768px) { .row { flex-direction: row; } }
@media (min-width: 1024px) { ... }
Tailwind essentials
<!-- Layout -->
<div class="container mx-auto px-4 max-w-4xl"></div>
<div class="flex items-center justify-between gap-4"></div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4"></div>
<!-- Spacing -->
class="p-4 px-6 py-2 m-4 mt-8 -mx-2 space-y-4"
<!-- Sizing -->
class="w-full h-screen min-h-screen max-w-prose"
<!-- Type -->
class="text-lg font-bold text-gray-700 dark:text-gray-200 leading-relaxed"
<!-- Colors -->
class="bg-blue-600 text-white hover:bg-blue-700 border border-gray-300"
<!-- Effects -->
class="rounded-lg shadow-md transition hover:scale-105"
<!-- Responsive prefixes -->
sm: 640px+ md: 768px+ lg: 1024px+ xl: 1280px+ 2xl: 1536px+
<!-- State prefixes -->
hover: focus: active: disabled: dark: group-hover: peer-checked:
JS daily set
// Selecting
const el = document.getElementById('x');
const els = document.querySelectorAll('.card');
// Read/write
el.textContent = 'Hi';
el.innerHTML = '<b>Hi</b>'; // beware XSS
el.value = 'foo';
el.classList.add('active');
el.classList.toggle('dark');
el.dataset.userId = '42';
// Events
btn.addEventListener('click', (e) => { e.preventDefault(); ... });
form.addEventListener('submit', async (e) => {
e.preventDefault();
const data = Object.fromEntries(new FormData(e.target));
});
// Loops
arr.forEach((x, i) => ...);
const doubled = arr.map(x => x * 2);
const evens = arr.filter(x => x % 2 === 0);
const total = arr.reduce((sum, x) => sum + x, 0);
// Async
async function load() {
const res = await fetch(url);
if (!res.ok) throw new Error(res.statusText);
const data = await res.json();
return data;
}
// Storage
localStorage.setItem('theme', 'dark');
const theme = localStorage.getItem('theme'); // string or null
localStorage.removeItem('theme');
Git daily set
# Setup once
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
# Daily flow
git status # what changed
git add . # stage everything
git add path/to/file # stage one
git commit -m "Add nav links" # save
git push # send to GitHub
# Branches
git checkout -b feature-x # create + switch
git checkout main # switch back
git merge feature-x # merge a branch into current
git branch -d feature-x # delete merged branch
# Sync with remote
git pull # fetch + merge
git fetch # fetch only
# Inspect
git log --oneline -10
git diff
git diff --staged
git show HEAD
# Undo
git restore <file> # discard unstaged changes
git restore --staged <file> # unstage
git reset --soft HEAD~1 # undo last commit, keep changes
git revert HEAD # new commit that undoes last
HTTP cheat
GET /resource read
POST /resource create
PUT /resource/1 replace
PATCH /resource/1 partial update
DELETE /resource/1 remove
200 OK 201 Created 204 No Content
301 Moved 302 Found 304 Not Modified
400 Bad Request 401 Unauthorized 403 Forbidden 404 Not Found
422 Unprocessable 429 Too Many
500 Server Error 502 Bad Gateway 503 Unavailable
DevTools shortcuts
| Action | Shortcut |
|---|---|
| Open DevTools | F12 / Ctrl+Shift+I |
| Inspect element | Ctrl+Shift+C, then click |
| Hard reload (no cache) | Ctrl+Shift+R |
| Toggle device toolbar (mobile) | Ctrl+Shift+M |
| Run Lighthouse | DevTools → Lighthouse → Generate |
| Console focus | Ctrl+` (grave) |
Useful free APIs (CORS-friendly)
- https://jsonplaceholder.typicode.com — fake REST API
- https://api.github.com — public GitHub data
- https://api.openweathermap.org — weather (free key)
- https://restcountries.com — country info
- https://dog.ceo/dog-api — random dog images :)
Deploy in 60 seconds
# Netlify (drag-drop)
1. Build/zip your folder
2. https://app.netlify.com → drag onto the dashboard
3. Live URL appears
# Netlify (git)
1. Push repo to GitHub
2. https://app.netlify.com → "Import from Git"
3. Pick repo, branch=main, build cmd=(blank), publish dir=. → Deploy
# GitHub Pages
1. Push repo to GitHub
2. Repo → Settings → Pages → Source = main branch / root
3. URL: https://USERNAME.github.io/REPO-NAME/
Print → fold → tape to monitor. Pull out for daily reference.
Glossary — DICT-ILCDB Web Dev Foundations (English)
Quick lookup. For Tagalog explanations see glossary-tagalog.md.
A
Accessibility (a11y) — Designing pages so people with disabilities (vision, hearing, motor, cognitive) can use them.
Alpine.js — A 15 KB JavaScript library for sprinkling interactivity into HTML using x-data, x-show, @click attributes. Bridge between vanilla JS and full frameworks.
ARIA — Accessible Rich Internet Applications. HTML attributes (role, aria-label, aria-expanded) that tell screen-readers what an element is when HTML alone isn't enough.
Async / await — JavaScript syntax to wait for a Promise without blocking. Reads top-to-bottom even though it's asynchronous.
Attribute — Extra info on an HTML tag: <a href="..." target="_blank">. The href is an attribute.
B
Box model — Every element is a rectangle: content + padding + border + margin. CSS sizing rules apply to all four.
Branch (git) — A parallel line of work. Default branch is main. Feature branches let you work without affecting main.
Browser — Chrome, Edge, Firefox, Safari. The program that interprets HTML/CSS/JS.
C
Cache — Browser-stored copy of a file to avoid re-downloading. Ctrl+Shift+R does a hard refresh, bypassing cache.
Cascade (CSS) — How conflicting CSS rules are resolved. Specificity > order > origin.
Class (CSS / HTML) — <div class="card">. Style targeting by .card { ... }. Reusable.
CDN — Content Delivery Network. A geographically distributed cache for static files (Tailwind via CDN, font files, etc.).
Clone (git) — Download a remote repo to your machine: git clone <url>.
Commit (git) — A saved snapshot of your changes with a message: git commit -m "Add nav".
CSS — Cascading Style Sheets. Tells the browser how HTML should look.
CSS Grid — Two-dimensional layout system (rows AND columns). Best for page-level layouts.
D
Dark mode — Alternate color scheme with light text on dark backgrounds. Implemented via Tailwind's dark: prefix + a class="dark" toggle.
Deploy — Putting your code on a server so the public can visit it.
DevTools — Browser developer tools (F12). Inspect HTML, edit CSS live, see network requests, run JavaScript.
DOM (Document Object Model) — The browser's in-memory tree of your HTML. JavaScript reads and modifies the DOM.
DOMContentLoaded — Event fired when HTML is fully parsed (but before images finish loading). Standard place to start your JS.
E
Element — A piece of an HTML page: <h1>, <p>, <button> — each is an element.
ES2020+ — Recent JavaScript features: arrow functions, destructuring, optional chaining (?.), nullish coalescing (??), modules.
Event — Something that happens in the browser: click, keypress, scroll, form submit. JavaScript "listens" with addEventListener.
Event delegation — Attaching one listener to a parent and using event.target to handle children. Saves memory; works for elements added later.
F
fetch — JavaScript built-in for making HTTP requests. Returns a Promise that resolves to a Response.
Flexbox — One-dimensional layout (row OR column). Best for navbars, button groups, cards in a row.
Form — <form> element holding <input>, <select>, <textarea> for user data entry.
Frontend — The part of a web app that runs in the user's browser (HTML/CSS/JS). Opposite of backend (server).
G
Git — A version control system. Tracks every change. git status, git add, git commit, git push.
GitHub — A website that hosts git repositories. Plus PRs, issues, Actions, Pages hosting.
GitHub Pages — Free static site hosting from a GitHub repo (username.github.io/repo-name).
H
HTML — HyperText Markup Language. The skeleton of every web page.
HTTPS — HTTP over TLS. Encrypted connection to the server. Required by every modern host.
HTTP status codes — 200 OK, 301/302 Redirect, 401 Unauthorized, 403 Forbidden, 404 Not Found, 500 Server Error.
I
ID (HTML) — Unique element identifier: <h1 id="page-title">. Targeted by #page-title in CSS, getElementById('page-title') in JS. Use once per page.
Inline element — Flows in a line of text: <span>, <a>, <strong>. Opposite of block.
J
JavaScript (JS) — The programming language of browsers. Adds behavior to a page.
JSON — JavaScript Object Notation. Text format for data: {"name": "Juan", "age": 30}. Universal API format.
L
Lighthouse — A Chrome DevTools tool that grades your page on Performance, Accessibility, Best Practices, SEO. Targets ≥ 90 for capstone.
Live Server — A VS Code extension that serves your folder at http://127.0.0.1:5500 and reloads the browser on save.
localStorage — Browser-persistent key/value store, ~5 MB per origin. Survives page reload and browser restart.
M
Media query — @media (min-width: 768px) { ... }. Apply CSS only when the viewport matches.
Merge (git) — Combine commits from one branch into another: git merge feature-x.
Mobile-first — Design + write CSS for phones first; add min-width: media queries for larger screens. Modern best practice.
Module (JS) — A .js file with export / import. Loaded in HTML via <script type="module" src="...">.
N
Netlify — Free static hosting service. Drag-and-drop or git-integrated.
Node.js — JavaScript outside the browser. Used for npm, Tailwind CLI, dev servers.
npm — Node Package Manager. Install JS libraries: npm install tailwindcss.
P
PR (Pull Request) — On GitHub: a request to merge your branch into another. The standard way to review code.
Promise — A JavaScript object representing a future value. fetch() returns one. Use .then() or await.
Prettier — Auto-formatter. Saves arguments about indentation.
Q
Query string — ?key=value&key2=value2 part of a URL. Read in JS with new URLSearchParams(location.search).
R
Repository (repo) — A folder tracked by git, often hosted on GitHub.
Responsive — A page that looks good on phones, tablets, and desktops. Achieved via flex/grid + media queries.
Root (CSS) — :root { --brand: navy; }. The <html> element. Where you declare CSS custom properties.
S
Selector (CSS) — Pattern that picks elements: h1, .card, #nav, nav > a:hover.
Semantic HTML — Using <header>, <nav>, <main>, <article>, <footer> instead of <div> everywhere. Helps a11y + SEO.
SEO — Search Engine Optimization. Semantic HTML + meta tags + fast load + accessibility = better Google ranking.
Specificity (CSS) — How specific a selector is. ID > class > tag. Higher specificity wins.
SSH key — A cryptographic key pair for password-less GitHub access.
T
Tailwind CSS — A utility-first CSS framework: <div class="flex p-4 bg-gray-100 rounded-lg">. No more naming classes.
Tag (HTML) — <p>, </p>. The angle-bracketed thing.
U
URL — Uniform Resource Locator. https://dict.gov.ph/path?q=1#section.
V
Vercel — Free static & SSR hosting. Git-integrated deploys.
Viewport — The visible browser area. <meta name="viewport" content="width=device-width"> is mandatory for responsive design.
W
Web Storage — Umbrella for localStorage and sessionStorage.
X
XSS (Cross-Site Scripting) — Injecting hostile JS through user input that gets rendered as HTML. Defense: escape user input.
See also: glossary-tagalog.md, quick-reference-card.md, faq.md.
Glossary (Tagalog) — DICT-ILCDB Web Dev Foundations
Para sa mga termino sa English at lookup-style, basahin ang glossary.md. Ito ay paliwanag sa Tagalog ng mga karaniwang pinipiglingan ng mga bagong trainee.
"Ano ba ang HTML, CSS, at JavaScript?"
- HTML ang kalansay — itong nagsasabi kung ano ang nasa page (heading, paragraph, button).
- CSS ang damit — itong nagbibigay ng kulay, hugis, layout.
- JavaScript ang utak — itong nagsasabi kung ano ang gagawin pag pinindot ang button.
Lahat ng webpage sa mundo ay kumbinasyon nitong tatlo, kahit gaano kakomplikado.
"Bakit kailangan ng semantic HTML?"
Imagine na ang HTML mo eh puro <div>. Para sa nakikita: ok lang. Pero para sa:
- Screen reader ng bulag na user — di nya alam kung saan ang nav, ang main content, ang footer.
- Search engine — di alam kung alin ang title, alin ang importante.
- Other devs — pagdating ng bagong dev, mahirap basahin ang puro div.
Gamitin ang <header>, <nav>, <main>, <article>, <footer> — meron silang KAHULUGAN.
"Ano ang difference ng class at id?"
-
id— unique sa buong page (parang ang ID number mo). Ginagamit isang beses.<h1 id="page-title">Mga Anunsyo</h1>CSS:
#page-title { ... }. JS:document.getElementById('page-title'). -
class— pwedeng paulit-ulit. Pang-grupo.<div class="card">...</div> <div class="card">...</div>CSS:
.card { ... }.
Rule: Para sa styling, gumamit lagi ng
class. Mas flexible.iday para sa anchor links (#section) at para sa JavaScript hooks.
"Bakit display: flex walang ginagawa?"
- Ang
display: flexay sa parent mo, hindi sa items. - Ang mga direktang anak lang ang naapektuhan, hindi mga apo.
Halimbawa:
<div class="row"> <!-- ito ang flex container -->
<div>A</div> <!-- flex item -->
<div>B</div> <!-- flex item -->
</div>
CSS:
.row { display: flex; gap: 1rem; }
"Ano ang flex vs grid?"
- Flexbox — para sa isang dimension: row o column lang. Pang-navbar, pang-button group, pang-card list na sunod-sunod.
- Grid — para sa dalawang dimensions: row AT column. Pang-page layout (sidebar + main + footer).
Pareho silang pangkasalukuyan; di na need ng floats o tables for layout.
"Bakit kailangan ng mobile-first?"
Mahigit 60% ng PH internet users ay nasa mobile. Kung disenyuhan mo muna sa desktop tapos mag-fix ka pa para sa mobile, mas mahirap. Disenyuhan mo muna sa mobile, tapos @media (min-width: 768px) lang ang gamitin para sa mas malaking screens.
"Ano ang Tailwind? Anong difference sa regular CSS?"
Sa regular CSS, gagawa ka ng class:
.btn { padding: 0.5rem 1rem; background: blue; color: white; border-radius: 0.25rem; }
Tapos:
<button class="btn">Save</button>
Sa Tailwind, walang class file — utility classes na agad sa HTML:
<button class="px-4 py-2 bg-blue-600 text-white rounded">Save</button>
Bakit? Walang naming dilemma ("ano tatawag ko sa class?"), walang dead CSS, walang specificity wars. Sandali lang sasanayan ka.
"Ano ang let, const, var?"
const— paulit-ulit gamitin, di mababago. Default mo dapat ito.let— pwede magbago ang value (counters, accumulators).var— luma. Wag na gamitin. May weird scoping rules.
const NAME = 'Juan'; // di na mababago
let count = 0; // pwede mag-increment
count = count + 1;
"Ano ang DOM?"
Ang DOM ay ang tree representation ng HTML mo, nasa memory ng browser. Pag binago mo ang DOM gamit ng JavaScript, agad nag-uupdate ang screen.
document.getElementById('msg').textContent = 'Hello';
// → ang <div id="msg"> ay agad nakikitang "Hello"
"Ano ang event?"
Event = nangyari sa page.
- User pinindot ang button →
clickevent. - User nag-type sa input →
inputevent. - User nag-submit ng form →
submitevent.
Event listener = JS na naghihintay ng event:
document.getElementById('btn').addEventListener('click', function () {
alert('Pinindot mo ako!');
});
"Ano ang async/await?"
Para sa mga mabagal na operasyon (fetch from API, file read, etc.). Bago, ang code mo eh:
fetch('/api').then(r => r.json()).then(data => console.log(data));
Mas malinis sa async/await:
const r = await fetch('/api');
const data = await r.json();
console.log(data);
Pareho lang ang ibig sabihin, mas readable.
"Ano ang git commit vs git push?"
git commit= save sa local repo mo. Naka-record ang change pero nasa laptop mo lang.git push= ipadala ang commits mo sa GitHub (remote).
Daily flow:
git add . # i-stage ang changes
git commit -m "message" # save sa local
git push # i-upload sa GitHub
"Ano ang Pull Request (PR)?"
Sa GitHub, ang PR ay pormal na paghingi ng review at merge ng iyong branch sa main branch. Mga teammates magko-comment, mag-aapprove, tapos mag-merge.
Sa solo capstone, gawin mo pa rin — para sa practice at para sa rubric.
"Ano ang HTTPS? Ano ang difference sa HTTP?"
- HTTP — naka-clear text. Pwedeng makita ng iba ang data mo (passwords, etc.).
- HTTPS — encrypted via TLS. Ligtas.
Lahat ng modern browser nagbabantay na: HTTPS may green padlock, HTTP may "Not Secure" warning.
Ang mga free hosts (Netlify, Vercel, GitHub Pages) — auto-HTTPS na lahat. Wag mag-deploy ng walang HTTPS.
"Bakit failed ang Lighthouse score ko?"
Common reasons:
- Performance — Malaking image (gamitin
<img loading="lazy">, i-compress). - Accessibility — Walang
altsa images, missing<label>sa inputs, low contrast. - Best Practices — May console errors, walang
<meta viewport>, walang HTTPS. - SEO — Walang
<title>, walang<meta description>.
Patakbuhin sa Chrome DevTools → Lighthouse tab → "Generate report." Makikita mo agad ang fixes.
May iba pang Tagalog questions? I-add sa faq.md at i-message ang trainer.
FAQ — DICT-ILCDB Web Dev Foundations
Top 30 questions trainees ask. Add yours via Slack and we'll fold them in.
Setup & tools
Q1. Why VS Code, not Sublime / Atom / Notepad++?
VS Code is free, has the largest extension ecosystem, and matches industry use. The "Live Server" extension alone saves you 30 min/day. Use any editor you want at home — but for the training, VS Code keeps everyone on the same page.
Q2. Do I need internet for everything?
You need it for: installing software (Day 0), fetching Tailwind from CDN (Day 2), git push (Day 4), deploy (Day 4), and looking up MDN docs. Local coding works offline.
Q3. Can I use Windows / Mac / Linux?
All three. Setup commands differ slightly — see setup-guide.md.
Q4. My laptop is potato (4 GB RAM, old). Will I survive?
Yes. The whole training is text + browser. No heavy IDEs, no Docker, no databases. Close Chrome tabs aggressively and you'll be fine.
HTML
Q5. <div> vs <section> vs <article> — which do I use?
<div>— meaningless container. Last resort.<section>— a thematic grouping with a heading.<article>— self-contained content (blog post, news item, comment).
If unsure, ask: "Could this stand alone in an RSS feed?" If yes → <article>. Has a heading and grouping → <section>. Just for layout? → <div>.
Q6. <b> vs <strong>? <i> vs <em>?
<strong>/<em>carry meaning (importance, emphasis). Screen readers announce them.<b>/<i>are purely visual. Use only when the visual style matters but the meaning doesn't.
99% of the time, use <strong> and <em>.
Q7. Why must inputs have <label>?
Screen readers announce the label when the user focuses the input. Clicking the label also focuses the input (great for checkboxes). Without it, your form is useless to anyone using a keyboard or screen reader.
<label for="email">Email</label>
<input id="email" type="email">
OR wrap:
<label>
Email
<input type="email">
</label>
Q8. Do I need <!DOCTYPE html>?
Yes, every page. It tells the browser "this is HTML5, don't render in quirks mode." Without it, you'll get weird CSS bugs.
CSS
Q9. Why is my CSS not applying?
- Wrong selector (
.cardvs#card) - CSS file not linked: check
<link rel="stylesheet" href="styles.css"> - Browser cached the old CSS —
Ctrl+Shift+Rfor hard refresh - Specificity issue — open DevTools → Elements → Computed → see which rule wins
- Typo in property name (
colourvscolor)
Q10. padding vs margin?
- padding — space inside the element, between content and border. Has the element's background color.
- margin — space outside the element, between this element and its neighbors. Transparent.
Q11. position: absolute vs fixed vs sticky?
absolute— positioned relative to nearest positioned ancestor. Removed from flow.fixed— positioned relative to viewport. Stays in place on scroll.sticky— acts likestaticuntil you scroll past, then "sticks" likefixed.
Q12. When do I use Flex vs Grid?
- 1 dimension (a row of buttons, a vertical menu) → Flex.
- 2 dimensions (a page with sidebar + content + footer rows) → Grid.
- Card list that wraps to N rows automatically → either works; Grid with
repeat(auto-fit, minmax(...))is elegant.
Q13. Why is the layout broken on mobile?
Likely missing the viewport meta tag in <head>:
<meta name="viewport" content="width=device-width, initial-scale=1">
Without it, mobile browsers render at 980px wide and shrink, causing tiny illegible text.
Q14. Should I use Tailwind from Day 1?
We start with regular CSS so you understand what Tailwind classes do. Day 2 afternoon switches to Tailwind. After that, use Tailwind unless you have a specific reason not to.
JavaScript
Q15. == vs ===?
Always use === (strict equality). == does type coercion and gives surprising results: '1' == 1 is true, '' == false is true. === checks both type and value.
Q16. const vs let vs var?
const— default. Doesn't allow reassignment.let— when you need to reassign (loop counter, accumulator).var— never. Old, has weird scoping.
(Note: const arr = [] doesn't prevent arr.push(x). It prevents arr = somethingElse.)
Q17. Why is my document.getElementById('x') returning null?
Your script ran before the HTML parsed. Either:
- Move
<script>to the bottom of<body>, OR - Add
deferattribute:<script src="app.js" defer></script>, OR - Wrap in
DOMContentLoaded:document.addEventListener('DOMContentLoaded', () => { /* your code */ });
Q18. Why does await not work?
You can only await inside an async function (or at module top-level if <script type="module">). Wrap in:
async function init() {
const r = await fetch('/api');
// ...
}
init();
Q19. CORS error from fetch?
The remote API needs to allow your origin via the Access-Control-Allow-Origin header. You can't fix this client-side. Either:
- Use an API that allows
*(most public APIs do) - Proxy through your own server
- Use JSONP (legacy)
For training, we use APIs that allow CORS: https://jsonplaceholder.typicode.com, https://api.github.com.
Q20. What's a Promise?
An object representing a value that's not ready yet. fetch() returns one. You either:
.then(value => ...), orawait fetch(...)to get the value when ready.
Git & GitHub
Q21. git status is overwhelming. How do I read it?
- Changes not staged = files you modified but haven't
git add-ed. - Untracked files = brand-new files git doesn't know about.
- Changes to be committed = files you
git add-ed; ready togit commit.
Daily flow: edit → git add . → git commit -m "msg" → git push.
Q22. I committed something I shouldn't have. Help!
- If not pushed yet:
git reset --soft HEAD~1(undoes the commit, keeps changes). - If pushed and it's secrets: rotate the secrets immediately, then follow GitHub's secret-removal guide.
Q23. Merge conflict on git pull. Now what?
git statusshows the conflicted file.- Open it in VS Code — you'll see
<<<<<<< HEAD ... ======= ... >>>>>>>markers. - Pick which version to keep, delete the markers.
git add <file>thengit commit.
VS Code has a built-in merge conflict UI — use it.
Q24. I'm scared to push. What if I break something?
You can't break GitHub. Worst case, your branch is wrong → delete it and re-create. The main branch on a solo capstone is yours alone. Push freely.
Deploy
Q25. Netlify vs Vercel vs GitHub Pages — which is best?
- GitHub Pages — easiest if your repo is already on GitHub. Free. Static only.
- Netlify — drag-and-drop or git-integrated. Free tier generous. Forms, redirects, env vars.
- Vercel — git-integrated. Best for Next.js but works for any static site. Free tier generous.
For capstone, any of the three is fine. We demo Netlify because of the drag-and-drop fallback.
Q26. My site loads on localhost but 404 on Netlify. Why?
- Did you push the latest commit?
git log -1then check Netlify deploy log. - Is the build folder set correctly? (For static sites it's the repo root or
/distif using Tailwind CLI.) - Are filenames case-sensitive? Linux servers are case-sensitive; Windows is not.
Logo.pngâ‰logo.pngon Netlify.
Q27. Can I deploy with a custom domain?
Yes, all three hosts support custom domains for free. You buy the domain elsewhere (Namecheap ~$10/yr) and point DNS records to the host.
Capstone
Q28. Can my capstone be a non-portfolio site?
Discuss with the trainer first. If you have a specific LGU need (barangay info site, school events page), bring it up Day 4. Default is portfolio because it's something you benefit from immediately.
Q29. How long does the capstone take?
Strong students: 8–12 hrs across the week. Beginners: 15–20 hrs. Start Monday morning. Don't deploy on Sunday at 22:00.
Q30. Can I use a template (Tailwind UI, Bootstrap example)?
You can use templates as inspiration but the HTML/CSS/JS in your repo must be your own. Copy-pasted templates that you can't explain in the demo video = auto-fail. Build it yourself.
Question not answered? Search glossary.md, then ask in trainer Slack.
Laravel Bridge — From WebDev to Laravel 11
You completed WebDev Foundations. Here's what carries over to the Laravel track and what's new.
What carries straight over
| WebDev concept | Laravel use |
|---|---|
| Semantic HTML | Blade views (.blade.php) — same HTML |
| Forms + labels + accessibility | Same; Blade adds @csrf, @error, old() helpers |
| Tailwind CSS | Laravel ships with Tailwind out of the box (Vite-built) |
| JavaScript + DOM + fetch | Same; Laravel ships with Alpine.js too |
| Git daily flow | Same |
| HTTP status codes | Laravel returns them; you read in DevTools |
| DevTools Network tab | Same |
| Deploy with HTTPS | Same idea (Forge / VPS) |
What's new
| New concept | What it is |
|---|---|
| PHP 8.2 | Server-side language. Modern, typed, OOP |
| Composer | PHP's npm |
| Routing | routes/web.php, routes/api.php map URLs to controllers |
| Controllers | Class methods that handle requests |
| Eloquent ORM | PHP classes representing DB tables — User::find(1) |
| Migrations + Seeders + Factories | PHP code that builds/populates your DB |
| Form Requests | Dedicated classes for validation rules |
| Breeze | Laravel's auth scaffolding — login/register in 5 min |
| Policies + Gates | Authorization (who can do what) |
| Sanctum | API token auth |
| Pest | The default testing framework |
| Vite | Asset bundler (already wired) |
| Forge / Vapor | Hosted deploy services |
Mental model shifts
"How does HTML get built?"
- WebDev: you wrote
index.htmldirectly - Laravel: Blade templates —
.blade.phpwith{{ $variable }}syntax. Server renders HTML before sending.
"Where does data live?"
- WebDev: hard-coded or
localStorage - Laravel: MySQL/Postgres/SQLite via Eloquent
"How do I save user input?"
- WebDev:
localStorage.setItem(...) - Laravel: form posts → controller → request validation → Eloquent
create()→ DB
"Where are my JS/CSS files?"
- WebDev:
<link>and<script>tags directly - Laravel: written in
resources/css&resources/js, Vite compiles them, you reference via@vite()
Things you can do that classmates without WebDev cannot
- ✅ Build the Blade views beautifully (you know HTML+CSS+Tailwind)
- ✅ Customize Breeze's login/register UI confidently
- ✅ Add JS interactivity (Alpine + vanilla) without panic
- ✅ Read & debug Vite errors
- ✅ Use git fearlessly
- ✅ Deploy your capstone live
Day-1 of Laravel preview
You'll spend morning learning the project structure (app/, routes/, resources/, etc.) and the request lifecycle (URL → route → controller → view). Afternoon is hands-on routing.
By Day 1 evening you'll have a 5-route Laravel app running locally. You won't write much HTML on Day 1 — that's Day 1 of WebDev work that you've already done.
Recommended pre-reading
Before Day 1 of Laravel:
- Read Laravel intro docs (~45 min)
- Watch Laracasts free intro series episodes 1–3
- Skim the Laravel README
Don't relearn
- HTML semantics
- Tailwind classes (Laravel uses the same)
- JS basics
- DOM, fetch
- Git
- HTTP / DevTools
Laravel trainers expect this. Your edge.
Common surprises
- "Why is the page blank on
npm run devnot running?" — Laravel + Vite needsnpm run devrunning in another terminal for hot reload. Ornpm run buildonce for production. - "Why CSRF on every form?" — Laravel requires
@csrfinside every form. Forget it → 419 error. - "Why is my controller saying 'view not found'?" — Check
resources/views/foo/bar.blade.phpmatchesview('foo.bar')(dot = slash). - "Why does my JS fail with 'Alpine is not defined'?" — Make sure
@vite(['resources/js/app.js'])is in your layout ANDnpm run devis running. - "Why does my data come back as
[Object]?" —console.log(user)shows it;<p>{{ user }}</p>will say[object Object]. Use{{ $user->name }}.
Tailwind specifics
Laravel ships Tailwind 3 wired through Vite. Edit resources/css/app.css:
@tailwind base;
@tailwind components;
@tailwind utilities;
tailwind.config.js contains the content paths:
content: [
'./resources/**/*.blade.php',
'./resources/**/*.js',
],
If your Tailwind classes don't apply, check the content paths and re-run npm run dev.
Bridge complete
You're set. Laravel will feel like a turbo-charged version of what you already know.
CodeIgniter Bridge — From WebDev to CodeIgniter 4
You completed WebDev Foundations. Here's what carries over to the CodeIgniter track and what's new.
What carries straight over
| WebDev concept | CI4 use |
|---|---|
| Semantic HTML | Views (.php files in app/Views/) — write the same HTML |
| Forms + labels + accessibility | CI4 Form Helper renders the same <form>, <input> tags |
| Tailwind CSS | Drop into your views, works identically |
| JavaScript + DOM + fetch | Same — used for client-side enhancement |
| Git daily flow | Same — git add . && git commit && git push |
| HTTP status codes | CI4 controllers return them; you'll read them in DevTools |
| DevTools Network tab | Same — debug both your front-end AND server responses |
| Deploy with HTTPS | Same idea, different host (VPS, not Netlify) |
What's new
| New concept | What it is |
|---|---|
| PHP | Server-side language. Looks similar to JS but runs on the server before HTML reaches the browser |
| MVC | Models (data), Views (HTML), Controllers (logic) — code organization pattern |
| Routing | URL → Controller mapping |
| Database | MySQL/MariaDB — persistent storage, replaces localStorage |
| Migrations & seeders | PHP files that build/populate your DB |
| Models | PHP classes that read/write DB tables |
| Form validation | Server-side rules (re-validate even if you have JS validation) |
| Sessions & auth | Server-stored login state, cookies, password hashing |
| Composer | PHP's npm — installs CI4 itself + libraries |
| VPS deploy | A Linux server you SSH into, not drag-and-drop |
Mental model shifts
"Where does the HTML come from?"
- WebDev: you wrote static
index.htmlfiles - CI4: PHP runs on the server, generates HTML, sends to browser
"Where does the data live?"
- WebDev: hard-coded in HTML or in
localStorage - CI4: in MySQL tables, queried per request
"How do I save user input?"
- WebDev:
localStorage.setItem(...)(this user's browser only) - CI4: form posts to controller → controller validates → model writes to DB → all users see it
"How do I see errors?"
- WebDev: DevTools Console
- CI4: DevTools Console for JS errors, plus server logs (
writable/logs/log-YYYY-MM-DD.php) for PHP errors
Day-1 of CI4 will feel familiar
You'll spend the first hour writing… HTML and CSS. Same skills. The only new step is "PHP wrapped my HTML in a controller."
Things you can do that classmates without WebDev cannot
- ✅ Build the views beautifully, fast (you know HTML+CSS already)
- ✅ Style with Tailwind from Day 1
- ✅ Write JS validation that complements server-side
- ✅ Read DevTools Network tab to debug AJAX
- ✅ Push to GitHub fearlessly
- ✅ Deploy with HTTPS confidently
Recommended pre-reading
Before Day 1 of CI4:
- Read CodeIgniter docs intro (~30 min)
- Watch a "PHP in 60 minutes" YouTube video (any reputable one)
- Skim the CI4 README
Don't relearn
You don't need to relearn:
- HTML semantics
- Tailwind classes
- JS basics
- Git
- HTTP
CI4 trainers will assume you have these. Your edge.
Common surprises
- "Why doesn't my JS run?" — On a CI4 view,
<script>runs as expected. No difference. - "Why is the page slow?" — Server-side rendering means each request hits the DB. Day 4 of CI4 covers caching.
- "Why does my form re-fetch on submit?" — Default form behavior. Use
e.preventDefault()+ fetch for SPA feel (covered in CI4 Day 3). - "Why CSRF token?" — CI4 demands it on POST. Add
<?= csrf_field() ?>inside every form.
Bridge complete
You're ready. CI4 will feel like adding a server-side superpower to skills you already have.
Post-Graduation Track — DICT-ILCDB Web Dev Foundations
You finished the 4 days. You shipped the capstone. Now what?
Week 1 after training
- Re-deploy your capstone with a custom domain (Namecheap ~$10/yr) or a
*.netlify.appsubdomain you actually like - Submit the capstone (deadline: Sunday 23:59 PHT)
- Add the live URL to your LinkedIn / FB profile
- Bookmark MDN Web Docs — your real teacher from now on
Month 1
Pick one of these tracks and commit:
Track A — Go deeper into front-end
- Build a 2-page admin dashboard from a Figma mockup
- Learn one component lib (DaisyUI, shadcn/ui pattern, Flowbite)
- Watch JavaScript: The Hard Parts (paid but worth it)
- Read JavaScript.info front to back
Track B — Add a backend (DICT-ILCDB framework tracks!)
- Enroll in the CodeIgniter or Laravel tracks (next batch)
- Read your chosen bridge file:
Track C — Open source contribution
- Find a "good first issue" on a Tailwind / Alpine repo
- Submit a PR (even a docs typo fix counts)
- Add it to your portfolio
Month 3
You should have shipped at least 2 more sites by now:
- Replicate a real LGU site (your office, a school, a barangay)
- Build a tool you actually use (todo, expense tracker, address book)
Both should be in your GitHub and deployed.
Month 6
Pick a stack to specialize:
| Stack | Why pick it |
|---|---|
| Vanilla + Alpine + 11ty/Astro | Simple, fast, great for content sites |
| Tailwind + Vue/Nuxt | Approachable component framework |
| Tailwind + React/Next | Largest job market |
| Tailwind + Svelte/SvelteKit | Best DX, growing |
| Laravel Blade + Alpine + Tailwind ("TALL") | If you took the Laravel track |
Year 1
By 12 months post-training, target:
- 5+ deployed projects in your portfolio
- Comfortable with a JS framework
- Comfortable with a CSS-in-JS or component library
- Have shipped at least one project for someone other than yourself
- (For job-seekers) Active on LinkedIn, GitHub, contributing or blogging monthly
Lifelong habits
- Read MDN before Googling. The signal-to-noise is much higher.
- Git commit daily. Even a typo fix.
- Learn in public: blog the bug you fixed yesterday.
- Pair-code monthly. You learn 10× faster.
- Re-build, don't re-watch. After watching a tutorial, close it and rebuild from memory.
Communities
- 😠#PHWebDev on Mastodon / Bluesky
- 📘 PH Web Devs Facebook group (active)
- 💬 DICT-ILCDB alumni Slack (link from your trainer)
- 🇵🇠DevCon Philippines — meetups, hackathons
- 🌎 #100DaysOfCode on Twitter / X — accountability
- 📺 The Coding Train — Daniel Shiffman, fun JS
Free learning resources (in order of value)
- MDN Web Docs — definitive reference
- JavaScript.info — best modern JS book, free
- web.dev — Google's web platform guides
- CSS Tricks — searchable CSS recipes
- The Odin Project — full free curriculum
- Frontend Masters bootcamp — free intro
- LinkedIn Learning — free with library card in some cities
When to take the framework tracks
You are ready for CodeIgniter or Laravel if you can:
- ✅ Hand-code an HTML form with proper labels
- ✅ Style it responsively without copy-pasting
- ✅ Validate it in JS before submission
- ✅ Push to GitHub and deploy
- ✅ Read DevTools Network tab and explain what you see
If yes — sign up for the next batch. Tell the registrar you completed WebDev so they can fast-track your enrollment.
Don't quit
The first 6 months feel hopeless. Everyone goes through it. Then suddenly things click. Show up daily.