UNIT 17 • STAGE 7 OF 7

Today

Complete the site: IntersectionObserver scroll animations, Today section, and era series footer

UNIT
STEP 1

Stage 7: Today

Stage 7 completes the Termination Era site. You will add the Today section with three cards and a reflection card, the site footer with the era series navigation, and your first look at the IntersectionObserver API: JavaScript that watches elements and triggers an animation when they scroll into view.

The IntersectionObserver is this unit's new JavaScript concept. It is a browser API that lets you know when an element enters or leaves the visible portion of the screen. You have used addEventListener for user-triggered events (clicks, form submissions). The IntersectionObserver handles something different: a scroll-triggered event that fires automatically based on what the browser can see.

How IntersectionObserver works

You create an observer object with new IntersectionObserver(callback, options). The callback runs whenever a watched element enters or exits the viewport. Inside the callback, you check entry.isIntersecting to know if the element just became visible. You then add a class, and call observer.unobserve(entry.target) so the animation only triggers once. You register elements to watch with observer.observe(el).

STEP 2

The Fade-In CSS

Before writing the JavaScript, set up the CSS states. Elements start invisible and slightly shifted down. When JavaScript adds the .visible class, the CSS transition animates them in.

// Default state: hidden and shifted. .visible state: fully shown, in position. [data-fade] {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.6s ease, transform 0.6s ease;
}
[data-fade].visible {
  opacity: 1;
  transform: translateY(0);
}
  • [data-fade]: An attribute selector. It targets any element that has a data-fade HTML attribute, regardless of the element's tag or class. This is more flexible than a class: you can add data-fade to any element without changing its class list, and the selector applies automatically.
  • transform: translateY(24px): Shifts the element 24 pixels downward from its normal position. Combined with opacity: 0, the element is both invisible and slightly below where it will land. When it transitions to translateY(0), it slides up as it fades in.
  • transition: opacity 0.6s ease, transform 0.6s ease: Animates both properties simultaneously over 0.6 seconds. The ease timing function starts fast and decelerates, which feels natural for a scroll reveal.
STEP 3

The IntersectionObserver JavaScript

The script goes at the bottom of your HTML, before the closing </body> tag. It creates an observer, tells it what to do when an element becomes visible, and registers all [data-fade] elements.

// Create the observer. callback fires when watched elements enter/exit the viewport. // threshold: 0.15 means the callback fires when 15% of the element is visible.
var fadeObserver = new IntersectionObserver(function(entries) {
  entries.forEach(function(entry) {
    if (entry.isIntersecting) {
      entry.target.classList.add('visible');
      fadeObserver.unobserve(entry.target);
    }
  });
}, { threshold: 0.15 });

document.querySelectorAll('[data-fade]').forEach(function(el) {
  fadeObserver.observe(el);
});
  • new IntersectionObserver(callback, options): Creates the observer. The callback is a function that receives an array of entries, one per element that changed visibility. The options object configures the observer; threshold: 0.15 means "fire when at least 15% of the element is visible."
  • entry.isIntersecting: A boolean. true means the element just entered the viewport; false means it just left. You only want to add .visible on entry, so you check if (entry.isIntersecting).
  • entry.target: The specific DOM element that triggered this entry. Since the observer watches multiple elements, entry.target tells you which one just became visible.
  • fadeObserver.unobserve(entry.target): Stops watching this element after it has been seen once. Without this, the observer would keep firing every time the element scrolled in and out, toggling the class on and off. One-time animations should call unobserve.
  • document.querySelectorAll('[data-fade]'): Selects all elements with the data-fade attribute. The .forEach loop then registers each one with observer.observe(el). Any element you add data-fade to in the HTML will automatically be watched.
STEP 4

Today Cards and the Footer

The Today section grid and reflection card follow the same patterns as Units 13, 14, and 15. The footer uses a 2fr 1fr 1fr grid with the era series pill navigation in the footer-bottom row. Add data-fade to each .today-card and the .reflection-card to activate the scroll animation.

// Today section: three-column grid + fade animation on each card. .today-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; margin-bottom: 48px; }
.today-card { background: var(--bg-card); border: 1px solid var(--gold-border); border-radius: 12px; padding: 32px 28px; }
.today-tag { font-size: 0.625rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em; color: var(--gold); margin-bottom: 12px; }
.today-card h3 { font-family: 'Playfair Display',Georgia,serif; font-size: 1.25rem; font-weight: 700; line-height: 1.3; margin-bottom: 12px; }
.today-card p { font-size: 0.9375rem; line-height: 1.7; color: var(--text-dim); }
// Reflection card: left border accent. Footer: 2fr 1fr 1fr grid, two border-top uses. .reflection-card { border-left: 4px solid var(--gold); background: var(--bg-card); border-radius: 0 12px 12px 0; padding: 36px 32px; }
.site-footer { background: var(--bg-section); border-top: 1px solid var(--gold-border); padding: 64px 48px 48px; }
.footer-inner { max-width: 1160px; margin: 0 auto; display: grid; grid-template-columns: 2fr 1fr 1fr; gap: 48px; margin-bottom: 48px; }
.footer-bottom { max-width: 1160px; margin: 0 auto; padding-top: 24px; border-top: 1px solid var(--gold-border); display: flex; justify-content: space-between; align-items: center; }
  • border-left: 4px solid var(--gold) with border-radius: 0 12px 12px 0: The left side is flat (0) because the border-left creates a visual edge there. The right side is rounded. This is the same pattern as the sovereignty callout in Stage 5: a left-border accent card where the flat left aligns with the accent line.
  • Two border-top usages at different layout levels: .site-footer uses border-top to separate the footer from the section above it. .footer-bottom uses a second border-top to separate the copyright row from the footer columns above. Same property, different purpose, different level of the layout.
  • grid-template-columns: 2fr 1fr 1fr: The brand/description column gets twice the space of each link column. 2 + 1 + 1 = 4 parts. The brand column is 50%, each link column is 25%. The proportional split gives the description room for a few sentences without cramping the link lists.
STEP 5

Complete Your Site

The Today section and footer are pre-built. Add data-fade to the three .today-card elements and the .reflection-card in the HTML, then verify in the preview that the cards start invisible and animate in as you scroll down to them. The section intro has a REPLACE comment.

  • To test the animation: scroll to the bottom of the preview. The Today section cards should be invisible at first and fade in as they enter view. If they are already visible when the page loads, check that your [data-fade] CSS is in the stylesheet and that you added data-fade to the HTML elements.
  • The era series footer pills link to all six era unit landing pages. If you want to navigate between era sites, open each unit's index.html page in a new tab and use the pill navigation.

Unit 17 Complete!

Your Termination Era research site is done. You have covered the policy (HCR 108, PL 280, Dillon Myer), the scale (109 nations, 1.36 million acres), the relocation program and AIM's founding, the Menominee and Klamath nation profiles, your own nation research, and the present-day record. You have also learned IntersectionObserver, your first browser observation API.

This site will become one of seven era sections in your Unit 18 capstone project. Save it and keep your sources.

Code Editor
Live Preview