---
id: seed:archetypes/changelog
title: Changelog / product updates section archetypes
source_type: seed_archetype
priority: cold
---

A changelog section surfaces your product's evolution. It builds trust by showing active development and gives users a reason to return. Four working archetypes:

---

**Timeline changelog.** Vertical timeline with entries, each entry expands on click.

*Animation & Visual Details:*
- *Entrance*: Entries stagger in from translateX(-20px) with fade, 0.08s between each
- *Hover*: Entry row highlights with gradient left border; expand icon rotates
- *Gradients*: Expanded entry has gradient left border (accent colors); version badge uses gradient
- *Radial circles*: Small radial dot on the timeline connector points
- *Scroll trigger*: IntersectionObserver at 15% threshold
- *JS interaction*: Entries expand/collapse with smooth height animation

```html
<section data-amb-section="changelog" class="changelog-section">
  <div class="container">
    <div class="changelog-header">
      <h2 class="changelog-title">What's new</h2>
      <p class="changelog-subtitle">Product updates and improvements</p>
    </div>
    <div class="changelog-timeline">
      <article class="changelog-entry">
        <div class="changelog-entry-marker">
          <div class="changelog-entry-dot"></div>
          <div class="changelog-entry-line"></div>
        </div>
        <div class="changelog-entry-content">
          <div class="changelog-entry-header">
            <span class="changelog-version-badge changelog-version-badge--feature">New</span>
            <time class="changelog-entry-date" datetime="2026-05-10">May 10, 2026</time>
          </div>
          <button class="changelog-entry-title" aria-expanded="false">
            <h3>Advanced analytics dashboard</h3>
            <svg class="changelog-entry-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>
          </button>
          <div class="changelog-entry-body">
            <p>We've rebuilt the analytics dashboard from scratch with real-time metrics, customizable widgets, and exportable reports. You can now track deployment frequency, lead time, and change failure rate in one place.</p>
            <ul class="changelog-entry-changes">
              <li>Real-time deployment metrics with 30-second refresh</li>
              <li>Customizable widget layout with drag-and-drop</li>
              <li>PDF and CSV export for stakeholder reports</li>
              <li>Team-wide baseline comparison</li>
            </ul>
          </div>
        </div>
      </article>
      <article class="changelog-entry">
        <div class="changelog-entry-marker">
          <div class="changelog-entry-dot"></div>
          <div class="changelog-entry-line"></div>
        </div>
        <div class="changelog-entry-content">
          <div class="changelog-entry-header">
            <span class="changelog-version-badge changelog-version-badge--improvement">Improved</span>
            <time class="changelog-entry-date" datetime="2026-04-28">April 28, 2026</time>
          </div>
          <button class="changelog-entry-title" aria-expanded="false">
            <h3>Webhook delivery reliability</h3>
            <svg class="changelog-entry-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>
          </button>
          <div class="changelog-entry-body">
            <p>Webhooks now retry with exponential backoff and deliver within 99.9% of deployments. We've added delivery status webhooks so your systems always know what happened.</p>
          </div>
        </div>
      </article>
      <article class="changelog-entry">
        <div class="changelog-entry-marker">
          <div class="changelog-entry-dot"></div>
          <div class="changelog-entry-line"></div>
        </div>
        <div class="changelog-entry-content">
          <div class="changelog-entry-header">
            <span class="changelog-version-badge changelog-version-badge--fix">Fixed</span>
            <time class="changelog-entry-date" datetime="2026-04-15">April 15, 2026</time>
          </div>
          <button class="changelog-entry-title" aria-expanded="false">
            <h3>Rollback timing in high-latency environments</h3>
            <svg class="changelog-entry-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>
          </button>
          <div class="changelog-entry-body">
            <p>Fixed an issue where automatic rollbacks could take up to 90 seconds in regions with >300ms latency to our control plane. Now triggers within 5 seconds regardless of region.</p>
          </div>
        </div>
      </article>
    </div>
  </div>
</section>
```

```css
:root {
  --changelog-enter-duration: 0.4s;
  --changelog-stagger: 0.08s;
}

.changelog-section {
  padding: clamp(64px, 8vw, 100px) 0;
}

.changelog-header {
  text-align: center;
  max-width: 480px;
  margin: 0 auto clamp(40px, 5vw, 60px);
}

.changelog-title {
  font-size: clamp(1.75rem, 3.5vw, 2.5rem);
  font-weight: 700;
  color: var(--color-text, #1e293b);
}

.changelog-subtitle {
  font-size: 1rem;
  color: var(--color-text-muted, #64748b);
  margin-top: 8px;
}

.changelog-timeline {
  max-width: 720px;
  margin: 0 auto;
}

.changelog-entry {
  display: grid;
  grid-template-columns: 24px 1fr;
  gap: 20px;
  opacity: 0;
  transform: translateX(-16px);
}

.changelog-entry.is-visible {
  animation: changelog-enter var(--changelog-enter-duration) ease-out forwards;
}

.changelog-entry:nth-child(1).is-visible { animation-delay: 0s; }
.changelog-entry:nth-child(2).is-visible { animation-delay: var(--changelog-stagger); }
.changelog-entry:nth-child(3).is-visible { animation-delay: calc(var(--changelog-stagger) * 2); }
.changelog-entry:nth-child(4).is-visible { animation-delay: calc(var(--changelog-stagger) * 3); }

@keyframes changelog-enter {
  to { opacity: 1; transform: translateX(0); }
}

.changelog-entry-marker {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: 8px;
}

.changelog-entry-dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: radial-gradient(circle, var(--color-accent, #6366f1) 0%, rgba(99,102,241,0.5) 100%);
  flex-shrink: 0;
}

.changelog-entry-line {
  width: 2px;
  flex: 1;
  background: linear-gradient(to bottom, #e2e8f0, #f1f5f9);
  margin-top: 8px;
}

.changelog-entry:last-child .changelog-entry-line {
  display: none;
}

.changelog-entry-content {
  background: #fff;
  border-radius: 16px;
  border: 1px solid #f1f5f9;
  padding: 24px;
  margin-bottom: 16px;
  transition: border-color 0.2s ease;
}

.changelog-entry:hover .changelog-entry-content {
  border-color: rgba(99,102,241,0.3);
}

.changelog-entry-header {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
}

.changelog-version-badge {
  display: inline-flex;
  padding: 4px 12px;
  border-radius: 20px;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.changelog-version-badge--feature {
  background: linear-gradient(135deg, rgba(34,197,94,0.1), rgba(22,163,74,0.1));
  color: #16a34a;
}

.changelog-version-badge--improvement {
  background: linear-gradient(135deg, rgba(99,102,241,0.1), rgba(124,58,237,0.1));
  color: var(--color-accent, #6366f1);
}

.changelog-version-badge--fix {
  background: linear-gradient(135deg, rgba(245,158,11,0.1), rgba(202,138,4,0.1));
  color: #ca8a04;
}

.changelog-entry-date {
  font-size: 0.8rem;
  color: var(--color-text-muted, #64748b);
}

.changelog-entry-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  padding: 0;
}

.changelog-entry-title h3 {
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--color-text, #1e293b);
  margin: 0;
}

.changelog-entry-icon {
  width: 20px;
  height: 20px;
  color: var(--color-text-muted, #64748b);
  flex-shrink: 0;
  transition: transform 0.3s ease;
}

button[aria-expanded="true"] .changelog-entry-icon {
  transform: rotate(180deg);
}

.changelog-entry-body {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.3s ease;
  margin-top: 0;
}

button[aria-expanded="true"] + .changelog-entry-body {
  grid-template-rows: 1fr;
  margin-top: 16px;
}

.changelog-entry-body > * {
  overflow: hidden;
}

.changelog-entry-body p {
  font-size: 0.9rem;
  color: var(--color-text-muted, #64748b);
  line-height: 1.7;
  margin: 0 0 12px;
}

.changelog-entry-changes {
  padding-left: 20px;
  margin: 0;
}

.changelog-entry-changes li {
  font-size: 0.9rem;
  color: var(--color-text-muted, #64748b);
  line-height: 1.6;
  margin-bottom: 6px;
}

.changelog-entry-changes li::marker {
  color: var(--color-accent, #6366f1);
}

@media (prefers-reduced-motion: reduce) {
  .changelog-entry {
    opacity: 1;
    transform: none;
  }
  .changelog-entry-body {
    transition: none;
  }
}
```

*JS behavior:*
```javascript
const changelogEntries = document.querySelectorAll('.changelog-entry');
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('is-visible');
      observer.unobserve(entry.target);
    }
  });
}, { threshold: 0.15 });
changelogEntries.forEach(entry => observer.observe(entry));

const toggles = document.querySelectorAll('.changelog-entry-title');
toggles.forEach(toggle => {
  toggle.addEventListener('click', () => {
    const expanded = toggle.getAttribute('aria-expanded') === 'true';
    toggle.setAttribute('aria-expanded', !expanded);
  });
});
```

---

**Version badge changelog.** Grouped by version number, radial badge decorations.

*Animation & Visual Details:*
- *Entrance*: Version badges fade in with scale; entries follow at 0.1s stagger
- *Hover*: Version badge has pulsing radial ring; entries highlight on hover
- *Gradients*: Version badge uses gradient fill; type badges use gradient backgrounds
- *Radial circles*: Large radial ring around the version number badge
- *Scroll trigger*: IntersectionObserver at 20% threshold

```html
<section data-amb-section="changelog" class="changelog-section changelog-section--versions">
  <div class="container">
    <div class="changelog-version-group">
      <div class="changelog-version-header">
        <div class="changelog-version-badge-wrapper">
          <div class="changelog-version-ring"></div>
          <span class="changelog-version-number">v2.4.0</span>
        </div>
        <time class="changelog-version-date">May 8, 2026</time>
      </div>
      <div class="changelog-version-entries">
        <div class="changelog-version-entry">
          <span class="changelog-type-tag changelog-type-tag--feature">Feature</span>
          <p>Added SSO with SAML 2.0 support for enterprise accounts</p>
        </div>
        <div class="changelog-version-entry">
          <span class="changelog-type-tag changelog-type-tag--improvement">Improvement</span>
          <p>Reduced average deploy time from 45s to 18s for standard deployments</p>
        </div>
      </div>
    </div>
    <div class="changelog-version-group">
      <div class="changelog-version-header">
        <div class="changelog-version-badge-wrapper">
          <div class="changelog-version-ring"></div>
          <span class="changelog-version-number">v2.3.0</span>
        </div>
        <time class="changelog-version-date">April 22, 2026</time>
      </div>
      <div class="changelog-version-entries">
        <div class="changelog-version-entry">
          <span class="changelog-type-tag changelog-type-tag--feature">Feature</span>
          <p>Launched preview deployments for pull requests</p>
        </div>
        <div class="changelog-version-entry">
          <span class="changelog-type-tag changelog-type-tag--fix">Fix</span>
          <p>Fixed memory leak in the build cache cleanup job</p>
        </div>
      </div>
    </div>
  </div>
</section>
```

```css
.changelog-section--versions {
  background: var(--color-bg-alt, #f8fafc);
}

.changelog-version-group {
  max-width: 720px;
  margin: 0 auto 40px;
}

.changelog-version-header {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 20px;
}

.changelog-version-badge-wrapper {
  position: relative;
}

.changelog-version-ring {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 80px;
  height: 80px;
  border-radius: 50%;
  border: 1px solid rgba(99,102,241,0.2);
  animation: version-ring-pulse 2s ease-in-out infinite;
  pointer-events: none;
}

@keyframes version-ring-pulse {
  0%, 100% { transform: translate(-50%, -50%) scale(1); opacity: 0.5; }
  50% { transform: translate(-50%, -50%) scale(1.15); opacity: 1; }
}

.changelog-version-number {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  background: linear-gradient(135deg, #1e1e2e, #2d1b4e);
  color: #fff;
  font-size: 0.9rem;
  font-weight: 700;
  border-radius: 12px;
}

.changelog-version-date {
  font-size: 0.85rem;
  color: var(--color-text-muted, #64748b);
}

.changelog-version-entries {
  background: #fff;
  border-radius: 16px;
  border: 1px solid #f1f5f9;
  overflow: hidden;
}

.changelog-version-entry {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 16px 20px;
  border-bottom: 1px solid #f1f5f9;
  opacity: 0;
  transform: translateY(10px);
}

.changelog-version-entry:last-child {
  border-bottom: none;
}

.changelog-version-entry.is-visible {
  animation: version-entry-enter 0.4s ease-out forwards;
}

@keyframes version-entry-enter {
  to { opacity: 1; transform: translateY(0); }
}

.changelog-type-tag {
  flex-shrink: 0;
  padding: 4px 10px;
  border-radius: 6px;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.changelog-type-tag--feature {
  background: rgba(34,197,94,0.1);
  color: #16a34a;
}

.changelog-type-tag--improvement {
  background: rgba(99,102,241,0.1);
  color: var(--color-accent, #6366f1);
}

.changelog-type-tag--fix {
  background: rgba(245,158,11,0.1);
  color: #ca8a04;
}

.changelog-version-entry p {
  font-size: 0.9rem;
  color: var(--color-text-muted, #64748b);
  line-height: 1.5;
  margin: 0;
}

@media (prefers-reduced-motion: reduce) {
  .changelog-version-entry {
    opacity: 1;
    transform: none;
  }
  .changelog-version-ring {
    animation: none;
  }
}
```

---

**Scroll-triggered entry reveal.** Entries animate in as user scrolls down timeline.

*Animation & Visual Details:*
- *Entrance*: Each entry fades in from below as it enters the viewport; timeline connector animates
- *Hover*: Entry row gets gradient left border
- *Gradients*: Entry type badges use gradient fills
- *Radial circles*: Timeline dots pulse when entry enters viewport
- *Scroll trigger*: Each entry triggers independently on IntersectionObserver

```html
<section data-amb-section="changelog" class="changelog-section changelog-section--scroll">
  <div class="container">
    <div class="changelog-scroll-timeline">
      <article class="changelog-scroll-entry" data-type="feature">
        <div class="changelog-scroll-marker">
          <div class="changelog-scroll-dot"></div>
          <div class="changelog-scroll-line"></div>
        </div>
        <div class="changelog-scroll-card">
          <div class="changelog-scroll-header">
            <span class="changelog-scroll-tag">Feature</span>
            <time>May 10, 2026</time>
          </div>
          <h3>Advanced analytics dashboard</h3>
          <p>Rebuilt analytics with real-time metrics, customizable widgets, and exportable reports.</p>
        </div>
      </article>
      <article class="changelog-scroll-entry" data-type="improvement">
        <div class="changelog-scroll-marker">
          <div class="changelog-scroll-dot"></div>
          <div class="changelog-scroll-line"></div>
        </div>
        <div class="changelog-scroll-card">
          <div class="changelog-scroll-header">
            <span class="changelog-scroll-tag">Improvement</span>
            <time>April 28, 2026</time>
          </div>
          <h3>Webhook delivery reliability</h3>
          <p>Webhooks now retry with exponential backoff. 99.9% delivery rate.</p>
        </div>
      </article>
    </div>
  </div>
</section>
```

```css
.changelog-scroll-timeline {
  max-width: 640px;
  margin: 0 auto;
}

.changelog-scroll-entry {
  display: grid;
  grid-template-columns: 40px 1fr;
  gap: 24px;
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 0.5s ease, transform 0.5s ease;
}

.changelog-scroll-entry.is-visible {
  opacity: 1;
  transform: translateY(0);
}

.changelog-scroll-entry:nth-child(2).is-visible { transition-delay: 0.15s; }

.changelog-scroll-marker {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.changelog-scroll-dot {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--color-accent, #6366f1);
  flex-shrink: 0;
  transition: transform 0.3s ease;
}

.changelog-scroll-entry.is-visible .changelog-scroll-dot {
  animation: dot-pop 0.3s ease-out;
}

@keyframes dot-pop {
  0% { transform: scale(0); }
  60% { transform: scale(1.3); }
  100% { transform: scale(1); }
}

.changelog-scroll-line {
  width: 2px;
  flex: 1;
  background: linear-gradient(to bottom, var(--color-accent, #6366f1), #e2e8f0);
  margin-top: 8px;
}

.changelog-scroll-card {
  background: #fff;
  border-radius: 16px;
  border: 1px solid #f1f5f9;
  padding: 24px;
  margin-bottom: 24px;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.changelog-scroll-card:hover {
  border-color: rgba(99,102,241,0.3);
  box-shadow: 0 8px 24px rgba(0,0,0,0.04);
}

.changelog-scroll-header {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
}

.changelog-scroll-tag {
  padding: 4px 12px;
  background: linear-gradient(135deg, rgba(99,102,241,0.1), rgba(124,58,237,0.1));
  color: var(--color-accent, #6366f1);
  font-size: 0.75rem;
  font-weight: 600;
  border-radius: 6px;
}

.changelog-scroll-card time {
  font-size: 0.8rem;
  color: var(--color-text-muted, #64748b);
}

.changelog-scroll-card h3 {
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--color-text, #1e293b);
  margin-bottom: 8px;
}

.changelog-scroll-card p {
  font-size: 0.9rem;
  color: var(--color-text-muted, #64748b);
  line-height: 1.6;
  margin: 0;
}

@media (prefers-reduced-motion: reduce) {
  .changelog-scroll-entry {
    opacity: 1;
    transform: none;
    transition: none;
  }
  .changelog-scroll-dot {
    animation: none;
  }
}
```

---

**Filter by type.** Entries tagged as "Feature", "Fix", "Improvement" with filter toggles.

*Animation & Visual Details:*
- *Entrance*: Filter buttons fade in first; entries stagger in at 0.06s intervals
- *Hover*: Filter buttons highlight with gradient; entries lift on hover
- *Gradients*: Active filter has gradient background; type tags use gradient fills
- *Radial circles*: Small radial dot in filter buttons
- *Scroll trigger*: Not applicable (filter interaction)
- *JS interaction*: Filter toggles show/hide entries with fade animation

```html
<section data-amb-section="changelog" class="changelog-section changelog-section--filterable">
  <div class="container">
    <h2 class="changelog-title">Changelog</h2>
    <div class="changelog-filter-bar">
      <button class="changelog-filter changelog-filter--active" data-filter="all">
        <span class="changelog-filter-dot"></span>
        All updates
      </button>
      <button class="changelog-filter" data-filter="feature">
        <span class="changelog-filter-dot"></span>
        Features
      </button>
      <button class="changelog-filter" data-filter="improvement">
        <span class="changelog-filter-dot"></span>
        Improvements
      </button>
      <button class="changelog-filter" data-filter="fix">
        <span class="changelog-filter-dot"></span>
        Fixes
      </button>
    </div>
    <div class="changelog-filter-grid">
      <article class="changelog-card" data-type="feature">
        <span class="changelog-card-type">Feature</span>
        <h3>Advanced analytics dashboard</h3>
        <p>Rebuilt analytics with real-time metrics, customizable widgets, and exportable reports.</p>
        <time class="changelog-card-date">May 10, 2026</time>
      </article>
      <article class="changelog-card" data-type="improvement">
        <span class="changelog-card-type">Improvement</span>
        <h3>Webhook delivery reliability</h3>
        <p>Webhooks now retry with exponential backoff. 99.9% delivery rate.</p>
        <time class="changelog-card-date">April 28, 2026</time>
      </article>
      <article class="changelog-card" data-type="fix">
        <span class="changelog-card-type">Fix</span>
        <h3>Rollback timing in high-latency environments</h3>
        <p>Automatic rollbacks now trigger within 5 seconds regardless of region.</p>
        <time class="changelog-card-date">April 15, 2026</time>
      </article>
    </div>
  </div>
</section>
```

```css
.changelog-filter-bar {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  margin-bottom: 32px;
}

.changelog-filter {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 20px;
  border-radius: 10px;
  border: 1px solid #e2e8f0;
  background: #fff;
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--color-text-muted, #64748b);
  cursor: pointer;
  transition: all 0.2s ease;
}

.changelog-filter:hover {
  border-color: var(--color-accent, #6366f1);
  color: var(--color-text, #1e293b);
}

.changelog-filter--active {
  background: linear-gradient(135deg, rgba(99,102,241,0.08), rgba(124,58,237,0.05));
  border-color: var(--color-accent, #6366f1);
  color: var(--color-accent, #6366f1);
}

.changelog-filter-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: radial-gradient(circle, currentColor 0%, transparent 70%);
  opacity: 0.5;
}

.changelog-filter--active .changelog-filter-dot {
  opacity: 1;
}

.changelog-filter-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 16px;
}

.changelog-card {
  background: #fff;
  border-radius: 16px;
  border: 1px solid #f1f5f9;
  padding: 24px;
  transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.3s ease;
}

.changelog-card[data-hidden="true"] {
  display: none;
}

.changelog-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(0,0,0,0.06);
}

.changelog-card-type {
  display: inline-block;
  padding: 4px 12px;
  border-radius: 6px;
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  margin-bottom: 12px;
}

.changelog-card[data-type="feature"] .changelog-card-type {
  background: rgba(34,197,94,0.1);
  color: #16a34a;
}

.changelog-card[data-type="improvement"] .changelog-card-type {
  background: rgba(99,102,241,0.1);
  color: var(--color-accent, #6366f1);
}

.changelog-card[data-type="fix"] .changelog-card-type {
  background: rgba(245,158,11,0.1);
  color: #ca8a04;
}

.changelog-card h3 {
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-text, #1e293b);
  margin-bottom: 8px;
}

.changelog-card p {
  font-size: 0.875rem;
  color: var(--color-text-muted, #64748b);
  line-height: 1.6;
  margin-bottom: 16px;
}

.changelog-card-date {
  font-size: 0.8rem;
  color: var(--color-text-muted, #64748b);
}
```

*JS behavior:*
```javascript
const filterBtns = document.querySelectorAll('.changelog-filter');
const cards = document.querySelectorAll('.changelog-card');

filterBtns.forEach(btn => {
  btn.addEventListener('click', () => {
    const filter = btn.dataset.filter;

    filterBtns.forEach(b => b.classList.remove('changelog-filter--active'));
    btn.classList.add('changelog-filter--active');

    cards.forEach(card => {
      const type = card.dataset.type;
      const visible = filter === 'all' || type === filter;
      card.dataset.hidden = visible ? 'false' : 'true';
      if (visible) {
        card.style.animation = 'card-filter-in 0.3s ease-out forwards';
      }
    });
  });
});
```

```css
@keyframes card-filter-in {
  0% { opacity: 0; transform: scale(0.95); }
  100% { opacity: 1; transform: scale(1); }
}
```

---

Copy rules: keep entries to one paragraph plus a bulleted list of specific changes. Never say "various improvements" — name them. Dates should be full ("May 10, 2026") not relative ("2 weeks ago") because visitors land on changelogs from search.

Layout rules: newest entries first. Group by version only if you ship with formal version numbers that your users track. Include type tags (Feature, Fix, Improvement) so users can filter to what matters to them.

CSS custom property summary:
```css
:root {
  --changelog-enter-duration: 0.4s;
  --changelog-stagger: 0.08s;
  --color-accent: #6366f1;
  --color-text: #1e293b;
  --color-text-muted: #64748b;
}
```