Category · Interactive Difficulty Level · Intermediate Published on · January 15, 2024

Interactive Timeline Component

A comprehensive timeline component with interactive animations, multiple layouts, and rich content support for displaying chronological events and milestones

#timeline #animation #interactive #chronological #events #ui

Responsive Design

Yes

Dark Mode Support

No

lines

11

Browser Compatibility

No

Live Preview

Interact with the component without leaving the page.

800px

Interactive Timeline Component

A comprehensive timeline component that displays chronological events with interactive animations, multiple layout options, and rich content support. Perfect for showcasing company history, project milestones, or any sequential data.

Features

  • Multiple Layout Options: Vertical, horizontal, and alternating layouts
  • Interactive Animations: Smooth scroll-triggered animations and hover effects
  • Rich Content Support: Text, images, videos, and custom HTML content
  • Responsive Design: Adapts to all screen sizes and devices
  • Customizable Styling: Flexible theming and color schemes
  • Navigation Controls: Jump to specific timeline points
  • Progress Indicator: Visual progress tracking through timeline
  • Accessibility Support: ARIA attributes and keyboard navigation

Demo

<div class="timeline-container">
  <!-- Timeline Header -->
  <div class="timeline-header">
    <h2 class="timeline-title">Company Milestones</h2>
    <div class="timeline-controls">
      <button class="timeline-nav-btn" data-target="2020">2020</button>
      <button class="timeline-nav-btn" data-target="2021">2021</button>
      <button class="timeline-nav-btn" data-target="2022">2022</button>
      <button class="timeline-nav-btn" data-target="2023">2023</button>
      <button class="timeline-nav-btn" data-target="2024">2024</button>
    </div>
  </div>

  <!-- Timeline Progress -->
  <div class="timeline-progress">
    <div class="timeline-progress-bar"></div>
  </div>

  <!-- Vertical Timeline -->
  <div class="timeline timeline-vertical" id="main-timeline">
    <!-- Timeline Item 1 -->
    <div class="timeline-item" data-year="2020" data-date="2020-01-15">
      <div class="timeline-marker">
        <div class="timeline-icon">
          <svg viewBox="0 0 24 24" fill="currentColor">
            <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
          </svg>
        </div>
      </div>
      <div class="timeline-content">
        <div class="timeline-date">January 15, 2020</div>
        <h3 class="timeline-title">Company Founded</h3>
        <p class="timeline-description">
          Our journey began with a simple idea: to create innovative solutions that make a difference. 
          Founded by a team of passionate developers and designers.
        </p>
        <div class="timeline-media">
          <img src="https://images.unsplash.com/photo-1560472354-b33ff0c44a43?w=400&h=200&fit=crop" 
               alt="Company founding" class="timeline-image">
        </div>
        <div class="timeline-tags">
          <span class="timeline-tag">Startup</span>
          <span class="timeline-tag">Innovation</span>
        </div>
      </div>
    </div>

    <!-- Timeline Item 2 -->
    <div class="timeline-item timeline-item-right" data-year="2020" data-date="2020-06-10">
      <div class="timeline-marker">
        <div class="timeline-icon">
          <svg viewBox="0 0 24 24" fill="currentColor">
            <path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm-1 16H9V7h9v14z"/>
          </svg>
        </div>
      </div>
      <div class="timeline-content">
        <div class="timeline-date">June 10, 2020</div>
        <h3 class="timeline-title">First Product Launch</h3>
        <p class="timeline-description">
          After months of development, we launched our first product to the market. 
          The response was overwhelming and exceeded all our expectations.
        </p>
        <div class="timeline-stats">
          <div class="timeline-stat">
            <span class="stat-number">1,000+</span>
            <span class="stat-label">Users</span>
          </div>
          <div class="timeline-stat">
            <span class="stat-number">50+</span>
            <span class="stat-label">Reviews</span>
          </div>
        </div>
      </div>
    </div>

    <!-- Timeline Item 3 -->
    <div class="timeline-item" data-year="2021" data-date="2021-03-22">
      <div class="timeline-marker">
        <div class="timeline-icon">
          <svg viewBox="0 0 24 24" fill="currentColor">
            <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
          </svg>
        </div>
      </div>
      <div class="timeline-content">
        <div class="timeline-date">March 22, 2021</div>
        <h3 class="timeline-title">Series A Funding</h3>
        <p class="timeline-description">
          Secured $2M in Series A funding to accelerate growth and expand our team. 
          This milestone allowed us to scale our operations significantly.
        </p>
        <div class="timeline-achievement">
          <div class="achievement-badge">
            <span class="achievement-amount">$2M</span>
            <span class="achievement-text">Funding Raised</span>
          </div>
        </div>
      </div>
    </div>

    <!-- Timeline Item 4 -->
    <div class="timeline-item timeline-item-right" data-year="2021" data-date="2021-09-15">
      <div class="timeline-marker">
        <div class="timeline-icon">
          <svg viewBox="0 0 24 24" fill="currentColor">
            <path d="M16 4c0-1.11.89-2 2-2s2 .89 2 2-.89 2-2 2-2-.89-2-2zm4 18v-6h2.5l-2.54-7.63A2.996 2.996 0 0 0 17.06 7H16c-.8 0-1.54.37-2.01.99L12 10.5 10.01 7.99A2.996 2.996 0 0 0 8.01 7H6.94c-1.4 0-2.59.93-2.9 2.37L1.5 16H4v6h2v-6h1.5l1.7-5.1L12 14.5l2.8-3.6L16.5 16H18v6h2z"/>
          </svg>
        </div>
      </div>
      <div class="timeline-content">
        <div class="timeline-date">September 15, 2021</div>
        <h3 class="timeline-title">Team Expansion</h3>
        <p class="timeline-description">
          Grew our team from 5 to 25 talented individuals across engineering, design, and marketing. 
          Building a diverse and inclusive workplace culture.
        </p>
        <div class="timeline-team">
          <div class="team-avatars">
            <div class="team-avatar">👨‍💻</div>
            <div class="team-avatar">👩‍🎨</div>
            <div class="team-avatar">👨‍💼</div>
            <div class="team-avatar">👩‍💻</div>
            <div class="team-avatar">👨‍🎨</div>
            <div class="team-more">+20</div>
          </div>
        </div>
      </div>
    </div>

    <!-- Timeline Item 5 -->
    <div class="timeline-item" data-year="2022" data-date="2022-05-08">
      <div class="timeline-marker">
        <div class="timeline-icon">
          <svg viewBox="0 0 24 24" fill="currentColor">
            <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
          </svg>
        </div>
      </div>
      <div class="timeline-content">
        <div class="timeline-date">May 8, 2022</div>
        <h3 class="timeline-title">Product 2.0 Release</h3>
        <p class="timeline-description">
          Launched a completely redesigned version of our platform with advanced features, 
          improved performance, and enhanced user experience.
        </p>
        <div class="timeline-features">
          <div class="feature-list">
            <div class="feature-item">✨ New UI/UX Design</div>
            <div class="feature-item">🚀 50% Performance Boost</div>
            <div class="feature-item">🔧 Advanced Analytics</div>
            <div class="feature-item">📱 Mobile App</div>
          </div>
        </div>
      </div>
    </div>

    <!-- Timeline Item 6 -->
    <div class="timeline-item timeline-item-right" data-year="2023" data-date="2023-01-20">
      <div class="timeline-marker">
        <div class="timeline-icon">
          <svg viewBox="0 0 24 24" fill="currentColor">
            <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm4.59-12.42L10 14.17l-2.59-2.58L6 13l4 4 8-8-1.41-1.42z"/>
          </svg>
        </div>
      </div>
      <div class="timeline-content">
        <div class="timeline-date">January 20, 2023</div>
        <h3 class="timeline-title">Global Expansion</h3>
        <p class="timeline-description">
          Expanded operations to 15 countries across 3 continents. 
          Established regional offices and localized our platform for international markets.
        </p>
        <div class="timeline-map">
          <div class="map-regions">
            <span class="region">🇺🇸 North America</span>
            <span class="region">🇪🇺 Europe</span>
            <span class="region">🌏 Asia Pacific</span>
          </div>
        </div>
      </div>
    </div>

    <!-- Timeline Item 7 -->
    <div class="timeline-item" data-year="2024" data-date="2024-01-15">
      <div class="timeline-marker">
        <div class="timeline-icon">
          <svg viewBox="0 0 24 24" fill="currentColor">
            <path d="M9 11H7v6h2v-6zm4 0h-2v6h2v-6zm4 0h-2v6h2v-6zm2.5-9H19v2h-1.5v17c0 1.1-.9 2-2 2h-11c-1.1 0-2-.9-2-2V4H1V2h3.5c.28 0 .5.22.5.5s.22.5.5.5h9c.28 0 .5-.22.5-.5s.22-.5.5-.5z"/>
          </svg>
        </div>
      </div>
      <div class="timeline-content">
        <div class="timeline-date">January 15, 2024</div>
        <h3 class="timeline-title">Looking Forward</h3>
        <p class="timeline-description">
          As we celebrate our 4th anniversary, we're excited about the future. 
          New innovations, partnerships, and opportunities await us in 2024 and beyond.
        </p>
        <div class="timeline-future">
          <div class="future-goals">
            <div class="goal-item">🎯 AI Integration</div>
            <div class="goal-item">🤝 Strategic Partnerships</div>
            <div class="goal-item">🌱 Sustainability Focus</div>
          </div>
        </div>
      </div>
    </div>
  </div>

  <!-- Horizontal Timeline -->
  <div class="timeline timeline-horizontal" id="horizontal-timeline" style="display: none;">
    <div class="timeline-track">
      <div class="timeline-line"></div>
      
      <div class="timeline-item-horizontal" data-year="2020">
        <div class="timeline-marker-horizontal">
          <div class="timeline-icon">🚀</div>
        </div>
        <div class="timeline-content-horizontal">
          <div class="timeline-year">2020</div>
          <div class="timeline-title">Founded</div>
        </div>
      </div>
      
      <div class="timeline-item-horizontal" data-year="2021">
        <div class="timeline-marker-horizontal">
          <div class="timeline-icon">💰</div>
        </div>
        <div class="timeline-content-horizontal">
          <div class="timeline-year">2021</div>
          <div class="timeline-title">Series A</div>
        </div>
      </div>
      
      <div class="timeline-item-horizontal" data-year="2022">
        <div class="timeline-marker-horizontal">
          <div class="timeline-icon">🎉</div>
        </div>
        <div class="timeline-content-horizontal">
          <div class="timeline-year">2022</div>
          <div class="timeline-title">Product 2.0</div>
        </div>
      </div>
      
      <div class="timeline-item-horizontal" data-year="2023">
        <div class="timeline-marker-horizontal">
          <div class="timeline-icon">🌍</div>
        </div>
        <div class="timeline-content-horizontal">
          <div class="timeline-year">2023</div>
          <div class="timeline-title">Global</div>
        </div>
      </div>
      
      <div class="timeline-item-horizontal" data-year="2024">
        <div class="timeline-marker-horizontal">
          <div class="timeline-icon">🔮</div>
        </div>
        <div class="timeline-content-horizontal">
          <div class="timeline-year">2024</div>
          <div class="timeline-title">Future</div>
        </div>
      </div>
    </div>
  </div>

  <!-- Timeline Layout Toggle -->
  <div class="timeline-layout-toggle">
    <button class="layout-btn active" data-layout="vertical">Vertical</button>
    <button class="layout-btn" data-layout="horizontal">Horizontal</button>
  </div>
</div>
.timeline-container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 2rem;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  min-height: 100vh;
  position: relative;
}

/* Timeline Header */
.timeline-header {
  text-align: center;
  margin-bottom: 3rem;
  background: rgba(255, 255, 255, 0.1);
  backdrop-filter: blur(10px);
  border-radius: 20px;
  padding: 2rem;
  border: 1px solid rgba(255, 255, 255, 0.2);
}

.timeline-title {
  font-size: 2.5rem;
  font-weight: 700;
  color: white;
  margin: 0 0 1.5rem 0;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.3);
}

.timeline-controls {
  display: flex;
  justify-content: center;
  gap: 1rem;
  flex-wrap: wrap;
}

.timeline-nav-btn {
  padding: 0.75rem 1.5rem;
  background: rgba(255, 255, 255, 0.2);
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 25px;
  color: white;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s ease;
  backdrop-filter: blur(5px);
}

.timeline-nav-btn:hover {
  background: rgba(255, 255, 255, 0.3);
  transform: translateY(-2px);
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
}

.timeline-nav-btn.active {
  background: white;
  color: #667eea;
}

/* Timeline Progress */
.timeline-progress {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 4px;
  background: rgba(255, 255, 255, 0.2);
  z-index: 1000;
}

.timeline-progress-bar {
  height: 100%;
  background: linear-gradient(90deg, #667eea, #764ba2);
  width: 0;
  transition: width 0.3s ease;
}

/* Vertical Timeline */
.timeline-vertical {
  position: relative;
  padding: 2rem 0;
}

.timeline-vertical::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 0;
  bottom: 0;
  width: 4px;
  background: linear-gradient(180deg, #667eea, #764ba2);
  transform: translateX(-50%);
  border-radius: 2px;
  box-shadow: 0 0 20px rgba(102, 126, 234, 0.5);
}

.timeline-item {
  position: relative;
  margin-bottom: 4rem;
  display: flex;
  align-items: center;
  opacity: 0;
  transform: translateY(50px);
  transition: all 0.6s ease;
}

.timeline-item.animate {
  opacity: 1;
  transform: translateY(0);
}

.timeline-item:nth-child(even) {
  flex-direction: row-reverse;
}

.timeline-item-right {
  flex-direction: row-reverse;
}

.timeline-marker {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  z-index: 10;
  width: 60px;
  height: 60px;
  background: white;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 0 4px #667eea, 0 0 20px rgba(102, 126, 234, 0.3);
  transition: all 0.3s ease;
}

.timeline-item:hover .timeline-marker {
  transform: translateX(-50%) scale(1.1);
  box-shadow: 0 0 0 6px #667eea, 0 0 30px rgba(102, 126, 234, 0.5);
}

.timeline-icon {
  width: 30px;
  height: 30px;
  color: #667eea;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.5rem;
}

.timeline-icon svg {
  width: 100%;
  height: 100%;
}

.timeline-content {
  background: white;
  border-radius: 16px;
  padding: 2rem;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
  width: calc(50% - 60px);
  margin: 0 30px;
  position: relative;
  border: 1px solid rgba(102, 126, 234, 0.1);
  transition: all 0.3s ease;
}

.timeline-item:hover .timeline-content {
  transform: translateY(-5px);
  box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
}

.timeline-content::before {
  content: '';
  position: absolute;
  top: 50%;
  width: 0;
  height: 0;
  border: 15px solid transparent;
  transform: translateY(-50%);
}

.timeline-item:not(.timeline-item-right) .timeline-content::before {
  right: -30px;
  border-left-color: white;
}

.timeline-item-right .timeline-content::before {
  left: -30px;
  border-right-color: white;
}

.timeline-date {
  color: #667eea;
  font-weight: 600;
  font-size: 0.875rem;
  text-transform: uppercase;
  letter-spacing: 1px;
  margin-bottom: 0.5rem;
}

.timeline-content .timeline-title {
  font-size: 1.5rem;
  font-weight: 700;
  color: #1a1a1a;
  margin: 0 0 1rem 0;
}

.timeline-description {
  color: #666;
  line-height: 1.6;
  margin-bottom: 1.5rem;
}

.timeline-media {
  margin: 1.5rem 0;
}

.timeline-image {
  width: 100%;
  height: 200px;
  object-fit: cover;
  border-radius: 12px;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}

.timeline-tags {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-top: 1rem;
}

.timeline-tag {
  background: linear-gradient(135deg, #667eea, #764ba2);
  color: white;
  padding: 0.25rem 0.75rem;
  border-radius: 20px;
  font-size: 0.75rem;
  font-weight: 600;
}

.timeline-stats {
  display: flex;
  gap: 2rem;
  margin-top: 1.5rem;
}

.timeline-stat {
  text-align: center;
}

.stat-number {
  display: block;
  font-size: 2rem;
  font-weight: 700;
  color: #667eea;
}

.stat-label {
  font-size: 0.875rem;
  color: #666;
  text-transform: uppercase;
  letter-spacing: 1px;
}

.timeline-achievement {
  margin-top: 1.5rem;
}

.achievement-badge {
  background: linear-gradient(135deg, #10b981, #059669);
  color: white;
  padding: 1rem 2rem;
  border-radius: 12px;
  text-align: center;
  display: inline-block;
}

.achievement-amount {
  display: block;
  font-size: 1.5rem;
  font-weight: 700;
}

.achievement-text {
  font-size: 0.875rem;
  opacity: 0.9;
}

.timeline-team {
  margin-top: 1.5rem;
}

.team-avatars {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.team-avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: linear-gradient(135deg, #667eea, #764ba2);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.2rem;
  border: 2px solid white;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.team-more {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: #f3f4f6;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.75rem;
  font-weight: 600;
  color: #666;
  border: 2px solid white;
}

.timeline-features {
  margin-top: 1.5rem;
}

.feature-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 0.75rem;
}

.feature-item {
  background: #f8fafc;
  padding: 0.75rem 1rem;
  border-radius: 8px;
  font-size: 0.875rem;
  font-weight: 500;
  border-left: 4px solid #667eea;
}

.timeline-map {
  margin-top: 1.5rem;
}

.map-regions {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
  justify-content: center;
}

.region {
  background: rgba(102, 126, 234, 0.1);
  color: #667eea;
  padding: 0.5rem 1rem;
  border-radius: 20px;
  font-size: 0.875rem;
  font-weight: 600;
  border: 1px solid rgba(102, 126, 234, 0.2);
}

.timeline-future {
  margin-top: 1.5rem;
}

.future-goals {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1rem;
}

.goal-item {
  background: linear-gradient(135deg, #f59e0b, #d97706);
  color: white;
  padding: 1rem;
  border-radius: 12px;
  text-align: center;
  font-weight: 600;
  font-size: 0.875rem;
}

/* Horizontal Timeline */
.timeline-horizontal {
  margin: 3rem 0;
  padding: 2rem;
  background: rgba(255, 255, 255, 0.1);
  backdrop-filter: blur(10px);
  border-radius: 20px;
  border: 1px solid rgba(255, 255, 255, 0.2);
}

.timeline-track {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 2rem 0;
  overflow-x: auto;
  min-width: 800px;
}

.timeline-line {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 4px;
  background: linear-gradient(90deg, #667eea, #764ba2);
  transform: translateY(-50%);
  border-radius: 2px;
}

.timeline-item-horizontal {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  z-index: 10;
  cursor: pointer;
  transition: all 0.3s ease;
}

.timeline-item-horizontal:hover {
  transform: translateY(-5px);
}

.timeline-marker-horizontal {
  width: 60px;
  height: 60px;
  background: white;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 0 4px #667eea, 0 5px 15px rgba(0, 0, 0, 0.2);
  margin-bottom: 1rem;
  transition: all 0.3s ease;
}

.timeline-item-horizontal:hover .timeline-marker-horizontal {
  transform: scale(1.1);
  box-shadow: 0 0 0 6px #667eea, 0 8px 25px rgba(0, 0, 0, 0.3);
}

.timeline-content-horizontal {
  text-align: center;
  background: white;
  padding: 1rem 1.5rem;
  border-radius: 12px;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
  min-width: 120px;
}

.timeline-year {
  font-size: 1.25rem;
  font-weight: 700;
  color: #667eea;
  margin-bottom: 0.25rem;
}

.timeline-content-horizontal .timeline-title {
  font-size: 0.875rem;
  font-weight: 600;
  color: #666;
  margin: 0;
}

/* Layout Toggle */
.timeline-layout-toggle {
  display: flex;
  justify-content: center;
  gap: 1rem;
  margin-top: 3rem;
}

.layout-btn {
  padding: 0.75rem 2rem;
  background: rgba(255, 255, 255, 0.2);
  border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 25px;
  color: white;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s ease;
  backdrop-filter: blur(5px);
}

.layout-btn:hover {
  background: rgba(255, 255, 255, 0.3);
  transform: translateY(-2px);
}

.layout-btn.active {
  background: white;
  color: #667eea;
}

/* Responsive Design */
@media (max-width: 768px) {
  .timeline-container {
    padding: 1rem;
  }
  
  .timeline-title {
    font-size: 2rem;
  }
  
  .timeline-controls {
    gap: 0.5rem;
  }
  
  .timeline-nav-btn {
    padding: 0.5rem 1rem;
    font-size: 0.875rem;
  }
  
  .timeline-vertical::before {
    left: 30px;
  }
  
  .timeline-item {
    flex-direction: row !important;
    margin-bottom: 2rem;
  }
  
  .timeline-marker {
    left: 30px;
    transform: translateX(-50%);
    width: 40px;
    height: 40px;
  }
  
  .timeline-icon {
    width: 20px;
    height: 20px;
    font-size: 1rem;
  }
  
  .timeline-content {
    width: calc(100% - 80px);
    margin-left: 60px;
    margin-right: 0;
  }
  
  .timeline-content::before {
    left: -15px;
    border-right-color: white;
    border-left-color: transparent;
  }
  
  .timeline-stats {
    gap: 1rem;
  }
  
  .stat-number {
    font-size: 1.5rem;
  }
  
  .feature-list {
    grid-template-columns: 1fr;
  }
  
  .timeline-track {
    min-width: 600px;
  }
  
  .timeline-marker-horizontal {
    width: 40px;
    height: 40px;
  }
  
  .timeline-content-horizontal {
    min-width: 80px;
    padding: 0.75rem 1rem;
  }
}

@media (max-width: 480px) {
  .timeline-title {
    font-size: 1.5rem;
  }
  
  .timeline-content {
    padding: 1.5rem;
  }
  
  .timeline-content .timeline-title {
    font-size: 1.25rem;
  }
  
  .timeline-image {
    height: 150px;
  }
  
  .team-avatars {
    gap: 0.25rem;
  }
  
  .team-avatar,
  .team-more {
    width: 30px;
    height: 30px;
    font-size: 0.875rem;
  }
  
  .timeline-track {
    min-width: 400px;
  }
}

/* Dark Theme */
.dark .timeline-container {
  background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
}

.dark .timeline-content {
  background: #2d3748;
  color: #e2e8f0;
  border-color: rgba(255, 255, 255, 0.1);
}

.dark .timeline-content .timeline-title {
  color: #f7fafc;
}

.dark .timeline-description {
  color: #cbd5e0;
}

.dark .timeline-content::before {
  border-left-color: #2d3748;
  border-right-color: #2d3748;
}

.dark .feature-item {
  background: #4a5568;
  color: #e2e8f0;
}

.dark .timeline-content-horizontal {
  background: #2d3748;
  color: #e2e8f0;
}

/* Reduced Motion */
@media (prefers-reduced-motion: reduce) {
  .timeline-item,
  .timeline-marker,
  .timeline-content,
  .timeline-item-horizontal,
  .timeline-marker-horizontal {
    transition: none;
    animation: none;
  }
  
  .timeline-item {
    opacity: 1;
    transform: none;
  }
}

/* High Contrast */
@media (prefers-contrast: high) {
  .timeline-vertical::before {
    background: #000;
  }
  
  .timeline-marker {
    box-shadow: 0 0 0 4px #000;
  }
  
  .timeline-content {
    border: 2px solid #000;
  }
}
class TimelineComponent {
  constructor(options = {}) {
    this.options = {
      container: '.timeline-container',
      animationOffset: 100,
      autoProgress: true,
      smoothScroll: true,
      ...options
    };
    
    this.container = document.querySelector(this.options.container);
    this.timeline = null;
    this.timelineItems = [];
    this.currentLayout = 'vertical';
    this.isAnimating = false;
    
    this.init();
  }

  init() {
    if (!this.container) {
      console.error('Timeline container not found');
      return;
    }
    
    this.timeline = this.container.querySelector('.timeline');
    this.timelineItems = Array.from(this.container.querySelectorAll('.timeline-item'));
    
    this.setupEventListeners();
    this.setupIntersectionObserver();
    this.setupProgressTracking();
    this.setupNavigation();
    this.setupLayoutToggle();
    this.setupAccessibility();
    
    // Initial animation check
    this.checkAnimations();
  }

  setupEventListeners() {
    // Scroll events
    window.addEventListener('scroll', this.throttle(() => {
      this.updateProgress();
      this.checkAnimations();
    }, 16));
    
    // Resize events
    window.addEventListener('resize', this.debounce(() => {
      this.handleResize();
    }, 250));
    
    // Timeline item hover effects
    this.timelineItems.forEach(item => {
      item.addEventListener('mouseenter', () => {
        this.handleItemHover(item, true);
      });
      
      item.addEventListener('mouseleave', () => {
        this.handleItemHover(item, false);
      });
      
      item.addEventListener('click', () => {
        this.handleItemClick(item);
      });
    });
  }

  setupIntersectionObserver() {
    const observerOptions = {
      threshold: 0.1,
      rootMargin: `${this.options.animationOffset}px 0px`
    };
    
    this.observer = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          this.animateItem(entry.target);
        }
      });
    }, observerOptions);
    
    this.timelineItems.forEach(item => {
      this.observer.observe(item);
    });
  }

  setupProgressTracking() {
    this.progressBar = this.container.querySelector('.timeline-progress-bar');
    if (this.progressBar) {
      this.updateProgress();
    }
  }

  setupNavigation() {
    const navButtons = this.container.querySelectorAll('.timeline-nav-btn');
    navButtons.forEach(btn => {
      btn.addEventListener('click', () => {
        const target = btn.dataset.target;
        this.navigateToYear(target);
        this.updateActiveNavButton(btn);
      });
    });
  }

  setupLayoutToggle() {
    const layoutButtons = this.container.querySelectorAll('.layout-btn');
    layoutButtons.forEach(btn => {
      btn.addEventListener('click', () => {
        const layout = btn.dataset.layout;
        this.switchLayout(layout);
        this.updateActiveLayoutButton(btn);
      });
    });
  }

  setupAccessibility() {
    // Add ARIA attributes
    this.timelineItems.forEach((item, index) => {
      item.setAttribute('role', 'article');
      item.setAttribute('aria-label', `Timeline item ${index + 1}`);
      
      const content = item.querySelector('.timeline-content');
      if (content) {
        content.setAttribute('tabindex', '0');
      }
    });
    
    // Keyboard navigation
    this.container.addEventListener('keydown', (e) => {
      this.handleKeyboardNavigation(e);
    });
  }

  animateItem(item) {
    if (item.classList.contains('animate')) return;
    
    item.classList.add('animate');
    
    // Trigger custom event
    this.dispatchEvent('itemAnimated', {
      item,
      year: item.dataset.year,
      date: item.dataset.date
    });
  }

  checkAnimations() {
    if (this.isAnimating) return;
    
    this.timelineItems.forEach(item => {
      if (this.isInViewport(item) && !item.classList.contains('animate')) {
        this.animateItem(item);
      }
    });
  }

  isInViewport(element) {
    const rect = element.getBoundingClientRect();
    const windowHeight = window.innerHeight || document.documentElement.clientHeight;
    
    return (
      rect.top <= windowHeight - this.options.animationOffset &&
      rect.bottom >= this.options.animationOffset
    );
  }

  updateProgress() {
    if (!this.progressBar || !this.options.autoProgress) return;
    
    const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
    const documentHeight = document.documentElement.scrollHeight - window.innerHeight;
    const progress = Math.min(scrollTop / documentHeight, 1);
    
    this.progressBar.style.width = `${progress * 100}%`;
  }

  navigateToYear(year) {
    const targetItem = this.container.querySelector(`[data-year="${year}"]`);
    if (!targetItem) return;
    
    this.isAnimating = true;
    
    if (this.options.smoothScroll) {
      targetItem.scrollIntoView({
        behavior: 'smooth',
        block: 'center'
      });
      
      setTimeout(() => {
        this.isAnimating = false;
      }, 1000);
    } else {
      targetItem.scrollIntoView({ block: 'center' });
      this.isAnimating = false;
    }
    
    // Highlight the target item
    this.highlightItem(targetItem);
    
    this.dispatchEvent('navigationClicked', {
      year,
      targetItem
    });
  }

  highlightItem(item) {
    // Remove previous highlights
    this.timelineItems.forEach(i => i.classList.remove('highlighted'));
    
    // Add highlight to target item
    item.classList.add('highlighted');
    
    // Remove highlight after animation
    setTimeout(() => {
      item.classList.remove('highlighted');
    }, 2000);
  }

  switchLayout(layout) {
    if (this.currentLayout === layout) return;
    
    const verticalTimeline = this.container.querySelector('.timeline-vertical');
    const horizontalTimeline = this.container.querySelector('.timeline-horizontal');
    
    if (layout === 'vertical') {
      verticalTimeline.style.display = 'block';
      horizontalTimeline.style.display = 'none';
    } else if (layout === 'horizontal') {
      verticalTimeline.style.display = 'none';
      horizontalTimeline.style.display = 'block';
    }
    
    this.currentLayout = layout;
    
    this.dispatchEvent('layoutChanged', {
      layout,
      previousLayout: this.currentLayout
    });
  }

  updateActiveNavButton(activeBtn) {
    const navButtons = this.container.querySelectorAll('.timeline-nav-btn');
    navButtons.forEach(btn => btn.classList.remove('active'));
    activeBtn.classList.add('active');
  }

  updateActiveLayoutButton(activeBtn) {
    const layoutButtons = this.container.querySelectorAll('.layout-btn');
    layoutButtons.forEach(btn => btn.classList.remove('active'));
    activeBtn.classList.add('active');
  }

  handleItemHover(item, isHovering) {
    const marker = item.querySelector('.timeline-marker');
    const content = item.querySelector('.timeline-content');
    
    if (isHovering) {
      marker?.classList.add('hovered');
      content?.classList.add('hovered');
    } else {
      marker?.classList.remove('hovered');
      content?.classList.remove('hovered');
    }
    
    this.dispatchEvent('itemHover', {
      item,
      isHovering,
      year: item.dataset.year
    });
  }

  handleItemClick(item) {
    const year = item.dataset.year;
    const date = item.dataset.date;
    
    this.dispatchEvent('itemClicked', {
      item,
      year,
      date
    });
  }

  handleKeyboardNavigation(e) {
    const focusedElement = document.activeElement;
    const currentIndex = this.timelineItems.indexOf(focusedElement.closest('.timeline-item'));
    
    if (currentIndex === -1) return;
    
    let nextIndex;
    
    switch (e.key) {
      case 'ArrowDown':
      case 'ArrowRight':
        e.preventDefault();
        nextIndex = Math.min(currentIndex + 1, this.timelineItems.length - 1);
        break;
      case 'ArrowUp':
      case 'ArrowLeft':
        e.preventDefault();
        nextIndex = Math.max(currentIndex - 1, 0);
        break;
      case 'Home':
        e.preventDefault();
        nextIndex = 0;
        break;
      case 'End':
        e.preventDefault();
        nextIndex = this.timelineItems.length - 1;
        break;
      default:
        return;
    }
    
    const nextItem = this.timelineItems[nextIndex];
    const nextContent = nextItem.querySelector('.timeline-content');
    if (nextContent) {
      nextContent.focus();
    }
  }

  handleResize() {
    // Recalculate positions and animations
    this.checkAnimations();
    this.updateProgress();
  }

  // Utility functions
  throttle(func, limit) {
    let inThrottle;
    return function() {
      const args = arguments;
      const context = this;
      if (!inThrottle) {
        func.apply(context, args);
        inThrottle = true;
        setTimeout(() => inThrottle = false, limit);
      }
    };
  }

  debounce(func, wait) {
    let timeout;
    return function executedFunction(...args) {
      const later = () => {
        clearTimeout(timeout);
        func(...args);
      };
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);
    };
  }

  dispatchEvent(eventName, detail) {
    const event = new CustomEvent(`timeline:${eventName}`, {
      detail,
      bubbles: true,
      cancelable: true
    });
    this.container.dispatchEvent(event);
  }

  // Public API methods
  addTimelineItem(itemData) {
    const { year, date, title, description, icon, position } = itemData;
    
    const itemHTML = `
      <div class="timeline-item" data-year="${year}" data-date="${date}">
        <div class="timeline-marker">
          <div class="timeline-icon">
            ${icon || '<svg viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="12" r="10"/></svg>'}
          </div>
        </div>
        <div class="timeline-content">
          <div class="timeline-date">${new Date(date).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}</div>
          <h3 class="timeline-title">${title}</h3>
          <p class="timeline-description">${description}</p>
        </div>
      </div>
    `;
    
    const timeline = this.container.querySelector('.timeline-vertical');
    if (position === 'start') {
      timeline.insertAdjacentHTML('afterbegin', itemHTML);
    } else {
      timeline.insertAdjacentHTML('beforeend', itemHTML);
    }
    
    // Refresh timeline items
    this.timelineItems = Array.from(this.container.querySelectorAll('.timeline-item'));
    this.setupEventListeners();
    
    this.dispatchEvent('itemAdded', { itemData });
  }

  removeTimelineItem(year) {
    const item = this.container.querySelector(`[data-year="${year}"]`);
    if (item) {
      item.remove();
      this.timelineItems = Array.from(this.container.querySelectorAll('.timeline-item'));
      this.dispatchEvent('itemRemoved', { year });
    }
  }

  updateTimelineItem(year, newData) {
    const item = this.container.querySelector(`[data-year="${year}"]`);
    if (!item) return;
    
    const content = item.querySelector('.timeline-content');
    if (newData.title) {
      const titleElement = content.querySelector('.timeline-title');
      if (titleElement) titleElement.textContent = newData.title;
    }
    
    if (newData.description) {
      const descElement = content.querySelector('.timeline-description');
      if (descElement) descElement.textContent = newData.description;
    }
    
    if (newData.date) {
      item.dataset.date = newData.date;
      const dateElement = content.querySelector('.timeline-date');
      if (dateElement) {
        dateElement.textContent = new Date(newData.date).toLocaleDateString('en-US', {
          year: 'numeric',
          month: 'long',
          day: 'numeric'
        });
      }
    }
    
    this.dispatchEvent('itemUpdated', { year, newData });
  }

  getTimelineData() {
    return this.timelineItems.map(item => ({
      year: item.dataset.year,
      date: item.dataset.date,
      title: item.querySelector('.timeline-title')?.textContent,
      description: item.querySelector('.timeline-description')?.textContent
    }));
  }

  setProgress(percentage) {
    if (this.progressBar) {
      this.progressBar.style.width = `${Math.min(Math.max(percentage, 0), 100)}%`;
    }
  }

  scrollToItem(year) {
    this.navigateToYear(year);
  }

  getCurrentLayout() {
    return this.currentLayout;
  }

  setLayout(layout) {
    this.switchLayout(layout);
    const layoutBtn = this.container.querySelector(`[data-layout="${layout}"]`);
    if (layoutBtn) {
      this.updateActiveLayoutButton(layoutBtn);
    }
  }

  destroy() {
    // Remove event listeners
    window.removeEventListener('scroll', this.updateProgress);
    window.removeEventListener('resize', this.handleResize);
    
    // Disconnect observer
    if (this.observer) {
      this.observer.disconnect();
    }
    
    // Clear references
    this.timelineItems = [];
    this.container = null;
    this.timeline = null;
    
    this.dispatchEvent('destroyed', {});
  }
}

// Auto-initialize
let timelineComponent;
document.addEventListener('DOMContentLoaded', () => {
  timelineComponent = new TimelineComponent();
});

// Export for module usage
if (typeof module !== 'undefined' && module.exports) {
  module.exports = TimelineComponent;
}

// Global API
window.TimelineComponent = TimelineComponent;

Usage Examples

Basic Timeline

// Initialize with default options
const timeline = new TimelineComponent();

// Listen to events
timeline.container.addEventListener('timeline:itemAnimated', (e) => {
  console.log('Item animated:', e.detail.year);
});

timeline.container.addEventListener('timeline:itemClicked', (e) => {
  console.log('Item clicked:', e.detail.year, e.detail.date);
});

Custom Configuration

const timeline = new TimelineComponent({
  container: '#my-timeline',
  animationOffset: 150,
  autoProgress: false,
  smoothScroll: false
});

Dynamic Timeline Management

// Add new timeline item
timeline.addTimelineItem({
  year: '2024',
  date: '2024-01-15',
  title: 'New Achievement',
  description: 'Description of the new achievement',
  icon: '<svg>...</svg>',
  position: 'end'
});

// Update existing item
timeline.updateTimelineItem('2023', {
  title: 'Updated Title',
  description: 'Updated description'
});

// Remove item
timeline.removeTimelineItem('2022');

// Get all timeline data
const data = timeline.getTimelineData();
console.log(data);

Layout Switching

// Switch to horizontal layout
timeline.setLayout('horizontal');

// Get current layout
const currentLayout = timeline.getCurrentLayout();

// Listen to layout changes
timeline.container.addEventListener('timeline:layoutChanged', (e) => {
  console.log('Layout changed from', e.detail.previousLayout, 'to', e.detail.layout);
});

Progress Control

// Set custom progress
timeline.setProgress(75); // 75%

// Navigate to specific year
timeline.scrollToItem('2023');

API Reference

Constructor Options

OptionTypeDefaultDescription
containerstring’.timeline-container’CSS selector for timeline container
animationOffsetnumber100Offset in pixels for animation trigger
autoProgressbooleantrueEnable automatic progress tracking
smoothScrollbooleantrueEnable smooth scrolling navigation

Methods

addTimelineItem(itemData)

Adds a new timeline item.

Parameters:

  • itemData (object): Item configuration
    • year (string): Year identifier
    • date (string): ISO date string
    • title (string): Item title
    • description (string): Item description
    • icon (string): SVG icon HTML
    • position (string): ‘start’ or ‘end’

removeTimelineItem(year)

Removes a timeline item by year.

Parameters:

  • year (string): Year identifier

updateTimelineItem(year, newData)

Updates an existing timeline item.

Parameters:

  • year (string): Year identifier
  • newData (object): Updated data

getTimelineData()

Returns array of all timeline items data.

Returns: Array of timeline item objects

setProgress(percentage)

Sets the progress bar percentage.

Parameters:

  • percentage (number): Progress percentage (0-100)

scrollToItem(year)

Scrolls to a specific timeline item.

Parameters:

  • year (string): Year identifier

setLayout(layout)

Switches timeline layout.

Parameters:

  • layout (string): ‘vertical’ or ‘horizontal’

getCurrentLayout()

Returns current timeline layout.

Returns: String (‘vertical’ or ‘horizontal’)

destroy()

Cleans up the timeline component.

Events

All events are dispatched on the timeline container with the timeline: prefix.

timeline:itemAnimated

Fired when a timeline item is animated into view.

Detail:

  • item (Element): The animated item
  • year (string): Item year
  • date (string): Item date

timeline:itemClicked

Fired when a timeline item is clicked.

Detail:

  • item (Element): The clicked item
  • year (string): Item year
  • date (string): Item date

timeline:itemHover

Fired when a timeline item is hovered.

Detail:

  • item (Element): The hovered item
  • isHovering (boolean): Hover state
  • year (string): Item year

timeline:navigationClicked

Fired when navigation button is clicked.

Detail:

  • year (string): Target year
  • targetItem (Element): Target timeline item

timeline:layoutChanged

Fired when layout is changed.

Detail:

  • layout (string): New layout
  • previousLayout (string): Previous layout

timeline:itemAdded

Fired when a new item is added.

Detail:

  • itemData (object): Added item data

timeline:itemRemoved

Fired when an item is removed.

Detail:

  • year (string): Removed item year

timeline:itemUpdated

Fired when an item is updated.

Detail:

  • year (string): Updated item year
  • newData (object): Updated data

timeline:destroyed

Fired when the component is destroyed.

CSS Classes

Layout Classes

  • .timeline-container - Main container
  • .timeline-vertical - Vertical timeline layout
  • .timeline-horizontal - Horizontal timeline layout
  • .timeline-item - Individual timeline item
  • .timeline-marker - Timeline marker/dot
  • .timeline-content - Item content area

State Classes

  • .animate - Applied when item is animated
  • .highlighted - Applied when item is highlighted
  • .hovered - Applied when item is hovered
  • .active - Applied to active navigation buttons
  • .timeline-navigation - Navigation container
  • .timeline-nav-btn - Navigation button
  • .layout-controls - Layout control container
  • .layout-btn - Layout toggle button

Customization

CSS Variables

:root {
  /* Colors */
  --timeline-primary-color: #3b82f6;
  --timeline-secondary-color: #64748b;
  --timeline-background-color: #ffffff;
  --timeline-border-color: #e2e8f0;
  --timeline-text-color: #1e293b;
  --timeline-muted-color: #64748b;
  
  /* Spacing */
  --timeline-item-spacing: 2rem;
  --timeline-marker-size: 1rem;
  --timeline-line-width: 2px;
  --timeline-content-padding: 1.5rem;
  
  /* Animation */
  --timeline-animation-duration: 0.6s;
  --timeline-animation-easing: cubic-bezier(0.4, 0, 0.2, 1);
  --timeline-hover-scale: 1.05;
  
  /* Typography */
  --timeline-title-size: 1.25rem;
  --timeline-description-size: 0.875rem;
  --timeline-date-size: 0.75rem;
}

Custom Animations

/* Custom slide-in animation */
@keyframes customSlideIn {
  from {
    opacity: 0;
    transform: translateX(-50px) scale(0.9);
  }
  to {
    opacity: 1;
    transform: translateX(0) scale(1);
  }
}

.timeline-item.animate .timeline-content {
  animation: customSlideIn var(--timeline-animation-duration) var(--timeline-animation-easing);
}

Theme Integration

/* Dark theme */
[data-theme="dark"] {
  --timeline-background-color: #1e293b;
  --timeline-text-color: #f1f5f9;
  --timeline-border-color: #334155;
  --timeline-muted-color: #94a3b8;
}

/* Custom brand colors */
.timeline-brand {
  --timeline-primary-color: #8b5cf6;
  --timeline-secondary-color: #a78bfa;
}

Accessibility

ARIA Support

  • Timeline items have role="article" and descriptive aria-label
  • Content areas are focusable with tabindex="0"
  • Navigation buttons have proper ARIA states
  • Progress indicators include aria-valuenow and aria-valuemax

Keyboard Navigation

  • Arrow Keys: Navigate between timeline items
  • Home/End: Jump to first/last item
  • Enter/Space: Activate focused item
  • Tab: Navigate through interactive elements

Screen Reader Support

  • Semantic HTML structure with proper headings
  • Descriptive text for all interactive elements
  • Live regions for dynamic content updates
  • Alternative text for icons and graphics

Reduced Motion

@media (prefers-reduced-motion: reduce) {
  .timeline-item {
    animation: none !important;
    transition: none !important;
  }
  
  .timeline-item.animate {
    opacity: 1;
    transform: none;
  }
}

Browser Support

  • Modern Browsers: Full support with all features
  • IE 11: Basic functionality with polyfills
  • Mobile Browsers: Optimized touch interactions

Required Polyfills for IE 11

<!-- Intersection Observer -->
<script src="https://polyfill.io/v3/polyfill.min.js?features=IntersectionObserver"></script>

<!-- Custom Events -->
<script src="https://polyfill.io/v3/polyfill.min.js?features=CustomEvent"></script>

Performance Considerations

Optimization Tips

  1. Lazy Loading: Use Intersection Observer for efficient animation triggering
  2. Throttling: Scroll events are throttled to 60fps
  3. Debouncing: Resize events are debounced to prevent excessive calculations
  4. Memory Management: Proper cleanup in destroy method

Large Datasets

// For timelines with many items, consider virtual scrolling
const timeline = new TimelineComponent({
  animationOffset: 50, // Smaller offset for better performance
  autoProgress: false  // Disable auto progress for large datasets
});

Integration Examples

React Integration

import React, { useEffect, useRef } from 'react';
import TimelineComponent from './timeline-component';

function Timeline({ data, onItemClick }) {
  const timelineRef = useRef(null);
  const componentRef = useRef(null);
  
  useEffect(() => {
    componentRef.current = new TimelineComponent({
      container: timelineRef.current
    });
    
    const handleItemClick = (e) => {
      onItemClick?.(e.detail);
    };
    
    timelineRef.current.addEventListener('timeline:itemClicked', handleItemClick);
    
    return () => {
      timelineRef.current?.removeEventListener('timeline:itemClicked', handleItemClick);
      componentRef.current?.destroy();
    };
  }, [onItemClick]);
  
  return (
    <div ref={timelineRef} className="timeline-container">
      {/* Timeline HTML structure */}
    </div>
  );
}

Vue Integration

<template>
  <div ref="timeline" class="timeline-container">
    <!-- Timeline HTML structure -->
  </div>
</template>

<script>
import TimelineComponent from './timeline-component';

export default {
  name: 'Timeline',
  props: ['data'],
  mounted() {
    this.timeline = new TimelineComponent({
      container: this.$refs.timeline
    });
    
    this.$refs.timeline.addEventListener('timeline:itemClicked', this.handleItemClick);
  },
  beforeUnmount() {
    this.timeline?.destroy();
  },
  methods: {
    handleItemClick(e) {
      this.$emit('item-click', e.detail);
    }
  }
};
</script>

Angular Integration

import { Component, ElementRef, OnInit, OnDestroy } from '@angular/core';
import { TimelineComponent } from './timeline-component';

@Component({
  selector: 'app-timeline',
  template: `
    <div class="timeline-container">
      <!-- Timeline HTML structure -->
    </div>
  `
})
export class TimelineComponentWrapper implements OnInit, OnDestroy {
  private timeline: TimelineComponent;
  
  constructor(private elementRef: ElementRef) {}
  
  ngOnInit() {
    this.timeline = new TimelineComponent({
      container: this.elementRef.nativeElement.querySelector('.timeline-container')
    });
  }
  
  ngOnDestroy() {
    this.timeline?.destroy();
  }
}

HTML

4

lines

CSS

7

lines


                <div class="timeline-container">
  <h2>Interactive Timeline Component</h2>
  <p>Comprehensive timeline with interactive animations</p>
</div>

              
4lines
142characters
HTMLLanguage

Related Code Snippets

Explore template packs

Need larger building blocks? Browse responsive landing pages and component bundles.

Open HTML Template Library →