Active Margin Notes

Margin notes stay in phase with the paragraph or section currently being read.

This experiment requires full-page rendering.

Open live demo

HTML

<div class="active-margin-notes">
  <aside class="active-margin-notes__rail">
    <p class="active-margin-notes__eyebrow">Margin notes</p>
    <h2>Active Margin Notes</h2>

    <article class="margin-note is-active" data-margin-note="frame">
      <p class="margin-note__eyebrow">Note 01</p>
      <h3>Frame the opening</h3>
      <p>
        The opening note clarifies why the section matters before the reader commits to the full argument.
      </p>
    </article>

    <article class="margin-note" data-margin-note="evidence">
      <p class="margin-note__eyebrow">Note 02</p>
      <h3>Surface the evidence</h3>
      <p>
        As the reader reaches the supporting material, the side note can foreground a source, a citation or a quick
        methodological reminder.
      </p>
    </article>

    <article class="margin-note" data-margin-note="takeaway">
      <p class="margin-note__eyebrow">Note 03</p>
      <h3>Land the takeaway</h3>
      <p>
        The final note condenses the point of the section so the reader exits with a clean memory of the argument.
      </p>
    </article>
  </aside>

  <article class="active-margin-notes__article">
    <section class="margin-section is-active" data-margin-section="frame">
      <p class="margin-section__eyebrow">Section 01</p>
      <h3>Lead with editorial clarity</h3>
      <p>
        Long-form editorial pages often carry layers of commentary that become useful only when they sit beside the
        paragraph currently in focus. When those notes drift too far from the active section, they stop feeling
        supportive and start feeling detached.
      </p>
      <p>
        This pattern keeps the support rail aligned with the reader's current location, without forcing a disruptive jump
        or an additional interaction.
      </p>
    </section>

    <section class="margin-section" data-margin-section="evidence">
      <p class="margin-section__eyebrow">Section 02</p>
      <h3>Keep supporting material in phase</h3>
      <p>
        Source notes, editorial glosses and research context become more useful when they appear in sync with the
        paragraph being read. The rail remains calm, but the active note updates as the narrative progresses.
      </p>
      <p>
        The result is a margin system that feels integrated into the reading act instead of floating beside it.
      </p>
    </section>

    <section class="margin-section" data-margin-section="takeaway">
      <p class="margin-section__eyebrow">Section 03</p>
      <h3>Close with a sharper memory</h3>
      <p>
        By the final section, the interface can compress the key takeaway into a side note that lingers just long enough
        to reinforce recall. The story remains primary, but the structure around it becomes easier to retain.
      </p>
    </section>
  </article>
</div>

CSS

.active-margin-notes {
  display: grid;
  gap: 22px;
  max-width: 1100px;
  margin: 0 auto;
}

.active-margin-notes__rail {
  align-self: start;
  padding: 26px;
  border-radius: 28px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  background:
    linear-gradient(180deg, rgba(250, 175, 59, 0.15), transparent 42%),
    rgba(10, 11, 14, 0.95);
}

.active-margin-notes__eyebrow,
.margin-note__eyebrow,
.margin-section__eyebrow {
  margin: 0 0 10px;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: rgba(255, 255, 255, 0.58);
}

.active-margin-notes__rail h2 {
  margin: 0 0 18px;
  font-size: clamp(2rem, 4vw, 3rem);
  line-height: 0.98;
}

.margin-note {
  opacity: 0.24;
  transform: translateY(8px);
  padding: 18px 20px;
  border-radius: 22px;
  border: 1px solid rgba(255, 255, 255, 0.06);
  background: rgba(255, 255, 255, 0.03);
  transition: opacity 220ms ease, transform 220ms ease, border-color 220ms ease;
}

.margin-note + .margin-note {
  margin-top: 12px;
}

.margin-note.is-active {
  opacity: 1;
  transform: translateY(0);
  border-color: rgba(250, 175, 59, 0.34);
}

.margin-note h3 {
  margin: 0 0 10px;
  color: #faaf3b;
}

.margin-note p:last-child {
  margin: 0;
  color: rgba(255, 255, 255, 0.8);
  line-height: 1.7;
}

.active-margin-notes__article {
  display: grid;
  gap: 18px;
}

.margin-section {
  min-height: 66vh;
  padding: 30px;
  border-radius: 28px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.05), transparent 52%),
    rgba(8, 9, 11, 0.95);
}

.margin-section.is-active {
  border-color: rgba(19, 190, 156, 0.34);
}

.margin-section h3 {
  margin: 0 0 14px;
  max-width: 16ch;
  font-size: clamp(2rem, 4vw, 3.2rem);
  line-height: 0.98;
}

.margin-section p {
  max-width: 58ch;
  margin: 0;
  color: rgba(255, 255, 255, 0.84);
  line-height: 1.78;
}

.margin-section p + p {
  margin-top: 14px;
}

@media only screen and (min-width: 980px) {
  .active-margin-notes {
    grid-template-columns: 340px minmax(0, 1fr);
    align-items: start;
  }

  .active-margin-notes__rail {
    position: sticky;
    top: 18px;
  }
}

JavaScript

(function () {
  const root = document.querySelector('.active-margin-notes');
  if (!root) return;

  const sections = Array.from(root.querySelectorAll('[data-margin-section]'));
  const notes = Array.from(root.querySelectorAll('[data-margin-note]'));
  if (!sections.length || !notes.length) return;

  function activate(id) {
    sections.forEach(section => {
      section.classList.toggle('is-active', section.dataset.marginSection === id);
    });

    notes.forEach(note => {
      note.classList.toggle('is-active', note.dataset.marginNote === id);
    });
  }

  const observer = new IntersectionObserver(
    entries => {
      let nextId = null;
      let highestRatio = 0;

      entries.forEach(entry => {
        if (entry.intersectionRatio > highestRatio) {
          highestRatio = entry.intersectionRatio;
          nextId = entry.target.dataset.marginSection;
        }
      });

      if (nextId) {
        activate(nextId);
      }
    },
    {
      threshold: [0.25, 0.45, 0.65]
    }
  );

  sections.forEach(section => observer.observe(section));
  activate(sections[0].dataset.marginSection);
})();
  • Tech used Vanilla JS, WordPress-ready, No dependencies
  • Integration level Medium (template edit)
  • Performance safe Yes

What it solves

Prevents editorial support notes from feeling detached by keeping them synchronized with the active section.

Active Margin Notes keeps the editorial support rail aligned with the section currently in focus. Instead of letting notes drift away from the reader’s actual position, the margin layer updates as the story advances.

The pattern is useful for essays, annotated case studies and research pieces where commentary, sources or editorial glosses should stay synchronized with the main narrative.