21 CSS Tooltips19 / 21
Event Timeline
A horizontal seven-event company timeline (seed → IPO) with alternating labels above and below the track. Hovering any dot reveals an event card with quarter, headline, body prose, and a metric badge (valuation, ARR, market cap). For about pages, investor pages, retrospectives.
Published
The code
<div class="tp-19">
<div class="tp-19__track">
<div class="tp-19__events">
<div class="tp-19__event">
<div class="tp-19__label">Seed Round</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2018</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q3 · 2018</div><div class="tp-19__card-title">Seed Funding Closed</div><span class="tp-19__badge tp-19__badge-mile">Milestone</span></div>
<div class="tp-19__body">$2.4M raised from 12 angel investors. The round was led by two former founders from the enterprise SaaS sector, providing both capital and key industry connections.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">Runway</span><span class="tp-19__impact-val">18 months secured</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">Public Beta</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2019</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q1 · 2019</div><div class="tp-19__card-title">Public Beta Launch</div><span class="tp-19__badge tp-19__badge-prod">Product</span></div>
<div class="tp-19__body">First 1,000 paying customers acquired within 90 days. Net Promoter Score of 72 in initial surveys. Product-market fit validated across legal, healthcare, fintech.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">ARR at launch</span><span class="tp-19__impact-val">$480K</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">Series A</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2020</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q2 · 2020</div><div class="tp-19__card-title">Series A — $12M</div><span class="tp-19__badge tp-19__badge-mile">Milestone</span></div>
<div class="tp-19__body">$12M Series A led by Sequoia Capital. Round closed despite pandemic uncertainty, reflecting strong retention. Funds used to double engineering headcount.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">Post-money</span><span class="tp-19__impact-val">$68M</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">EU Expansion</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2021</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q3 · 2021</div><div class="tp-19__card-title">100K Users · EU Launch</div><span class="tp-19__badge tp-19__badge-prod">Growth</span></div>
<div class="tp-19__body">Crossed 100,000 active users and launched GDPR-compliant EU data centres in Frankfurt and Dublin. Opened London office with 24 hires. MRR surpassed $1M.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">EU share</span><span class="tp-19__impact-val">31% of new ARR</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">Series B</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2022</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q1 · 2022</div><div class="tp-19__card-title">Series B — $45M · Profitable</div><span class="tp-19__badge tp-19__badge-mile">Milestone</span></div>
<div class="tp-19__body">$45M Series B led by Andreessen Horowitz. Achieved first profitable quarter ahead of schedule. Capital allocated to enterprise sales infrastructure.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">Valuation</span><span class="tp-19__impact-val">$320M post-money</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">Acquisition</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2023</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q2 · 2023</div><div class="tp-19__card-title">Acquired Synapse Labs</div><span class="tp-19__badge tp-19__badge-acq">M&A</span></div>
<div class="tp-19__body">Acquired Synapse Labs, a 22-person AI workflow automation startup, for $280M. The deal added Synapse's LLM orchestration layer, accelerating the roadmap.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">ARR added</span><span class="tp-19__impact-val">$8.4M</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">IPO Filed</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2024</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q3 · 2024</div><div class="tp-19__card-title">S-1 Filed · NASDAQ</div><span class="tp-19__badge tp-19__badge-ipo">Public Markets</span></div>
<div class="tp-19__body">Filed S-1 registration with the SEC. IPO priced at $18/share, valuing the company at $1.2B. Shares began trading on NASDAQ under the ticker SNYP, closing up 38% on day one.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">Market cap</span><span class="tp-19__impact-val">$1.65B (day 1)</span></div>
</div>
</div>
</div>
</div>
</div><div class="tp-19">
<div class="tp-19__track">
<div class="tp-19__events">
<div class="tp-19__event">
<div class="tp-19__label">Seed Round</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2018</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q3 · 2018</div><div class="tp-19__card-title">Seed Funding Closed</div><span class="tp-19__badge tp-19__badge-mile">Milestone</span></div>
<div class="tp-19__body">$2.4M raised from 12 angel investors. The round was led by two former founders from the enterprise SaaS sector, providing both capital and key industry connections.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">Runway</span><span class="tp-19__impact-val">18 months secured</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">Public Beta</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2019</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q1 · 2019</div><div class="tp-19__card-title">Public Beta Launch</div><span class="tp-19__badge tp-19__badge-prod">Product</span></div>
<div class="tp-19__body">First 1,000 paying customers acquired within 90 days. Net Promoter Score of 72 in initial surveys. Product-market fit validated across legal, healthcare, fintech.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">ARR at launch</span><span class="tp-19__impact-val">$480K</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">Series A</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2020</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q2 · 2020</div><div class="tp-19__card-title">Series A — $12M</div><span class="tp-19__badge tp-19__badge-mile">Milestone</span></div>
<div class="tp-19__body">$12M Series A led by Sequoia Capital. Round closed despite pandemic uncertainty, reflecting strong retention. Funds used to double engineering headcount.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">Post-money</span><span class="tp-19__impact-val">$68M</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">EU Expansion</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2021</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q3 · 2021</div><div class="tp-19__card-title">100K Users · EU Launch</div><span class="tp-19__badge tp-19__badge-prod">Growth</span></div>
<div class="tp-19__body">Crossed 100,000 active users and launched GDPR-compliant EU data centres in Frankfurt and Dublin. Opened London office with 24 hires. MRR surpassed $1M.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">EU share</span><span class="tp-19__impact-val">31% of new ARR</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">Series B</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2022</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q1 · 2022</div><div class="tp-19__card-title">Series B — $45M · Profitable</div><span class="tp-19__badge tp-19__badge-mile">Milestone</span></div>
<div class="tp-19__body">$45M Series B led by Andreessen Horowitz. Achieved first profitable quarter ahead of schedule. Capital allocated to enterprise sales infrastructure.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">Valuation</span><span class="tp-19__impact-val">$320M post-money</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">Acquisition</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2023</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q2 · 2023</div><div class="tp-19__card-title">Acquired Synapse Labs</div><span class="tp-19__badge tp-19__badge-acq">M&A</span></div>
<div class="tp-19__body">Acquired Synapse Labs, a 22-person AI workflow automation startup, for $280M. The deal added Synapse's LLM orchestration layer, accelerating the roadmap.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">ARR added</span><span class="tp-19__impact-val">$8.4M</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">IPO Filed</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2024</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q3 · 2024</div><div class="tp-19__card-title">S-1 Filed · NASDAQ</div><span class="tp-19__badge tp-19__badge-ipo">Public Markets</span></div>
<div class="tp-19__body">Filed S-1 registration with the SEC. IPO priced at $18/share, valuing the company at $1.2B. Shares began trading on NASDAQ under the ticker SNYP, closing up 38% on day one.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">Market cap</span><span class="tp-19__impact-val">$1.65B (day 1)</span></div>
</div>
</div>
</div>
</div>
</div>.tp-19 {
width: 100%;
min-height: 100vh;
background: #0e0c14;
/* Top + bottom room — event cards alternate above and below the
track. Odd events flip column-reverse so the label lands above
the dot and the card pops up. Cards are ~220px tall. */
padding: 240px 24px;
font-family: 'Inter', system-ui, sans-serif;
position: relative;
overflow: hidden;
}
.tp-19::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(ellipse 60% 50% at 50% 50%, rgba(80, 40, 120, 0.08) 0%, transparent 70%);
pointer-events: none;
}
.tp-19__track {
max-width: 760px;
margin: 0 auto;
position: relative;
padding: 40px 0;
}
.tp-19__track::before {
content: '';
position: absolute;
top: 50%;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(to right, transparent, rgba(120, 80, 200, 0.3) 10%, rgba(120, 80, 200, 0.3) 90%, transparent);
transform: translateY(-50%);
}
.tp-19__events {
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
}
.tp-19__event {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
cursor: help;
}
.tp-19__event:nth-child(odd) {
flex-direction: column-reverse;
}
.tp-19__label {
font-size: 9px;
color: rgba(180, 140, 240, 0.5);
text-align: center;
margin: 11px 0;
letter-spacing: 0.08em;
font-family: 'JetBrains Mono', ui-monospace, monospace;
}
.tp-19__year {
font-size: 11px;
font-weight: 700;
color: rgba(180, 140, 240, 0.7);
text-align: center;
margin: 11px 0;
font-family: 'JetBrains Mono', ui-monospace, monospace;
letter-spacing: 0.04em;
}
.tp-19__dot {
width: 11px;
height: 11px;
border-radius: 50%;
background: rgba(120, 80, 200, 0.6);
border: 2px solid rgba(120, 80, 200, 0.3);
transition: background .15s, box-shadow .15s, transform .15s;
position: relative;
z-index: 2;
flex-shrink: 0;
}
.tp-19__event:hover .tp-19__dot {
background: rgba(160, 110, 240, 0.9);
box-shadow: 0 0 14px 4px rgba(120, 80, 200, 0.25);
transform: scale(1.3);
}
.tp-19__card {
position: absolute;
left: 50%;
transform: translateX(-50%) translateY(-6px);
z-index: 200;
width: 250px;
background: rgba(14, 12, 22, 0.97);
border: 1px solid rgba(120, 80, 200, 0.2);
border-radius: 10px;
opacity: 0;
visibility: hidden;
pointer-events: none;
transition: opacity .2s, transform .2s cubic-bezier(0.22, 1, 0.36, 1), visibility 0s linear .2s;
box-shadow: 0 16px 44px rgba(0, 0, 0, 0.9), 0 0 0 1px rgba(120, 80, 200, 0.08);
overflow: hidden;
text-align: left;
}
.tp-19__event:nth-child(odd) .tp-19__card {
bottom: calc(100% + 10px);
top: auto;
}
.tp-19__event:nth-child(even) .tp-19__card {
top: calc(100% + 10px);
bottom: auto;
}
.tp-19__event:hover .tp-19__card {
opacity: 1;
visibility: visible;
transform: translateX(-50%) translateY(0);
transition-delay: 0s;
}
.tp-19__card-top {
padding: 12px 13px 10px;
border-bottom: 1px solid rgba(120, 80, 200, 0.1);
}
.tp-19__card-year {
font-size: 8.5px;
font-family: 'JetBrains Mono', ui-monospace, monospace;
color: rgba(160, 110, 240, 0.5);
letter-spacing: 0.1em;
margin-bottom: 4px;
}
.tp-19__card-title {
font-size: 13px;
color: rgba(255, 255, 255, 0.88);
line-height: 1.3;
font-family: Georgia, serif;
}
.tp-19__badge {
display: inline-block;
font-size: 7.5px;
font-family: 'JetBrains Mono', ui-monospace, monospace;
padding: 2px 7px;
border-radius: 3px;
margin-top: 7px;
letter-spacing: 0.07em;
text-transform: uppercase;
}
.tp-19__badge-mile {
background: rgba(80, 160, 80, 0.15);
color: rgba(120, 200, 120, 0.8);
border: 1px solid rgba(80, 160, 80, 0.2);
}
.tp-19__badge-prod {
background: rgba(80, 120, 200, 0.15);
color: rgba(120, 160, 240, 0.8);
border: 1px solid rgba(80, 120, 200, 0.2);
}
.tp-19__badge-acq {
background: rgba(200, 120, 40, 0.15);
color: rgba(240, 160, 80, 0.8);
border: 1px solid rgba(200, 120, 40, 0.2);
}
.tp-19__badge-ipo {
background: rgba(160, 80, 200, 0.15);
color: rgba(200, 120, 240, 0.8);
border: 1px solid rgba(160, 80, 200, 0.2);
}
.tp-19__body {
padding: 9px 13px;
font-size: 11px;
color: rgba(255, 255, 255, 0.5);
line-height: 1.55;
border-bottom: 1px solid rgba(120, 80, 200, 0.06);
font-style: italic;
font-family: Georgia, serif;
}
.tp-19__impact {
padding: 9px 13px;
display: flex;
justify-content: space-between;
font-size: 9.5px;
font-family: 'JetBrains Mono', ui-monospace, monospace;
}
.tp-19__impact-label {
color: rgba(120, 80, 200, 0.5);
}
.tp-19__impact-val {
color: rgba(200, 160, 255, 0.7);
}.tp-19 {
width: 100%;
min-height: 100vh;
background: #0e0c14;
/* Top + bottom room — event cards alternate above and below the
track. Odd events flip column-reverse so the label lands above
the dot and the card pops up. Cards are ~220px tall. */
padding: 240px 24px;
font-family: 'Inter', system-ui, sans-serif;
position: relative;
overflow: hidden;
}
.tp-19::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(ellipse 60% 50% at 50% 50%, rgba(80, 40, 120, 0.08) 0%, transparent 70%);
pointer-events: none;
}
.tp-19__track {
max-width: 760px;
margin: 0 auto;
position: relative;
padding: 40px 0;
}
.tp-19__track::before {
content: '';
position: absolute;
top: 50%;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(to right, transparent, rgba(120, 80, 200, 0.3) 10%, rgba(120, 80, 200, 0.3) 90%, transparent);
transform: translateY(-50%);
}
.tp-19__events {
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
}
.tp-19__event {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
cursor: help;
}
.tp-19__event:nth-child(odd) {
flex-direction: column-reverse;
}
.tp-19__label {
font-size: 9px;
color: rgba(180, 140, 240, 0.5);
text-align: center;
margin: 11px 0;
letter-spacing: 0.08em;
font-family: 'JetBrains Mono', ui-monospace, monospace;
}
.tp-19__year {
font-size: 11px;
font-weight: 700;
color: rgba(180, 140, 240, 0.7);
text-align: center;
margin: 11px 0;
font-family: 'JetBrains Mono', ui-monospace, monospace;
letter-spacing: 0.04em;
}
.tp-19__dot {
width: 11px;
height: 11px;
border-radius: 50%;
background: rgba(120, 80, 200, 0.6);
border: 2px solid rgba(120, 80, 200, 0.3);
transition: background .15s, box-shadow .15s, transform .15s;
position: relative;
z-index: 2;
flex-shrink: 0;
}
.tp-19__event:hover .tp-19__dot {
background: rgba(160, 110, 240, 0.9);
box-shadow: 0 0 14px 4px rgba(120, 80, 200, 0.25);
transform: scale(1.3);
}
.tp-19__card {
position: absolute;
left: 50%;
transform: translateX(-50%) translateY(-6px);
z-index: 200;
width: 250px;
background: rgba(14, 12, 22, 0.97);
border: 1px solid rgba(120, 80, 200, 0.2);
border-radius: 10px;
opacity: 0;
visibility: hidden;
pointer-events: none;
transition: opacity .2s, transform .2s cubic-bezier(0.22, 1, 0.36, 1), visibility 0s linear .2s;
box-shadow: 0 16px 44px rgba(0, 0, 0, 0.9), 0 0 0 1px rgba(120, 80, 200, 0.08);
overflow: hidden;
text-align: left;
}
.tp-19__event:nth-child(odd) .tp-19__card {
bottom: calc(100% + 10px);
top: auto;
}
.tp-19__event:nth-child(even) .tp-19__card {
top: calc(100% + 10px);
bottom: auto;
}
.tp-19__event:hover .tp-19__card {
opacity: 1;
visibility: visible;
transform: translateX(-50%) translateY(0);
transition-delay: 0s;
}
.tp-19__card-top {
padding: 12px 13px 10px;
border-bottom: 1px solid rgba(120, 80, 200, 0.1);
}
.tp-19__card-year {
font-size: 8.5px;
font-family: 'JetBrains Mono', ui-monospace, monospace;
color: rgba(160, 110, 240, 0.5);
letter-spacing: 0.1em;
margin-bottom: 4px;
}
.tp-19__card-title {
font-size: 13px;
color: rgba(255, 255, 255, 0.88);
line-height: 1.3;
font-family: Georgia, serif;
}
.tp-19__badge {
display: inline-block;
font-size: 7.5px;
font-family: 'JetBrains Mono', ui-monospace, monospace;
padding: 2px 7px;
border-radius: 3px;
margin-top: 7px;
letter-spacing: 0.07em;
text-transform: uppercase;
}
.tp-19__badge-mile {
background: rgba(80, 160, 80, 0.15);
color: rgba(120, 200, 120, 0.8);
border: 1px solid rgba(80, 160, 80, 0.2);
}
.tp-19__badge-prod {
background: rgba(80, 120, 200, 0.15);
color: rgba(120, 160, 240, 0.8);
border: 1px solid rgba(80, 120, 200, 0.2);
}
.tp-19__badge-acq {
background: rgba(200, 120, 40, 0.15);
color: rgba(240, 160, 80, 0.8);
border: 1px solid rgba(200, 120, 40, 0.2);
}
.tp-19__badge-ipo {
background: rgba(160, 80, 200, 0.15);
color: rgba(200, 120, 240, 0.8);
border: 1px solid rgba(160, 80, 200, 0.2);
}
.tp-19__body {
padding: 9px 13px;
font-size: 11px;
color: rgba(255, 255, 255, 0.5);
line-height: 1.55;
border-bottom: 1px solid rgba(120, 80, 200, 0.06);
font-style: italic;
font-family: Georgia, serif;
}
.tp-19__impact {
padding: 9px 13px;
display: flex;
justify-content: space-between;
font-size: 9.5px;
font-family: 'JetBrains Mono', ui-monospace, monospace;
}
.tp-19__impact-label {
color: rgba(120, 80, 200, 0.5);
}
.tp-19__impact-val {
color: rgba(200, 160, 255, 0.7);
}Here's a working CSS Tooltip from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Event Timeline
Source: https://codefronts.com/snippets/css-tooltips/event-timeline/
A horizontal seven-event company timeline (seed → IPO) with alternating labels above and below the track. Hovering any dot reveals an event card with quarter, headline, body prose, and a metric badge (valuation, ARR, market cap). For about pages, investor pages, retrospectives.
## HTML
```html
<div class="tp-19">
<div class="tp-19__track">
<div class="tp-19__events">
<div class="tp-19__event">
<div class="tp-19__label">Seed Round</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2018</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q3 · 2018</div><div class="tp-19__card-title">Seed Funding Closed</div><span class="tp-19__badge tp-19__badge-mile">Milestone</span></div>
<div class="tp-19__body">$2.4M raised from 12 angel investors. The round was led by two former founders from the enterprise SaaS sector, providing both capital and key industry connections.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">Runway</span><span class="tp-19__impact-val">18 months secured</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">Public Beta</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2019</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q1 · 2019</div><div class="tp-19__card-title">Public Beta Launch</div><span class="tp-19__badge tp-19__badge-prod">Product</span></div>
<div class="tp-19__body">First 1,000 paying customers acquired within 90 days. Net Promoter Score of 72 in initial surveys. Product-market fit validated across legal, healthcare, fintech.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">ARR at launch</span><span class="tp-19__impact-val">$480K</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">Series A</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2020</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q2 · 2020</div><div class="tp-19__card-title">Series A — $12M</div><span class="tp-19__badge tp-19__badge-mile">Milestone</span></div>
<div class="tp-19__body">$12M Series A led by Sequoia Capital. Round closed despite pandemic uncertainty, reflecting strong retention. Funds used to double engineering headcount.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">Post-money</span><span class="tp-19__impact-val">$68M</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">EU Expansion</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2021</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q3 · 2021</div><div class="tp-19__card-title">100K Users · EU Launch</div><span class="tp-19__badge tp-19__badge-prod">Growth</span></div>
<div class="tp-19__body">Crossed 100,000 active users and launched GDPR-compliant EU data centres in Frankfurt and Dublin. Opened London office with 24 hires. MRR surpassed $1M.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">EU share</span><span class="tp-19__impact-val">31% of new ARR</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">Series B</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2022</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q1 · 2022</div><div class="tp-19__card-title">Series B — $45M · Profitable</div><span class="tp-19__badge tp-19__badge-mile">Milestone</span></div>
<div class="tp-19__body">$45M Series B led by Andreessen Horowitz. Achieved first profitable quarter ahead of schedule. Capital allocated to enterprise sales infrastructure.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">Valuation</span><span class="tp-19__impact-val">$320M post-money</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">Acquisition</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2023</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q2 · 2023</div><div class="tp-19__card-title">Acquired Synapse Labs</div><span class="tp-19__badge tp-19__badge-acq">M&A</span></div>
<div class="tp-19__body">Acquired Synapse Labs, a 22-person AI workflow automation startup, for $280M. The deal added Synapse's LLM orchestration layer, accelerating the roadmap.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">ARR added</span><span class="tp-19__impact-val">$8.4M</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">IPO Filed</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2024</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q3 · 2024</div><div class="tp-19__card-title">S-1 Filed · NASDAQ</div><span class="tp-19__badge tp-19__badge-ipo">Public Markets</span></div>
<div class="tp-19__body">Filed S-1 registration with the SEC. IPO priced at $18/share, valuing the company at $1.2B. Shares began trading on NASDAQ under the ticker SNYP, closing up 38% on day one.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">Market cap</span><span class="tp-19__impact-val">$1.65B (day 1)</span></div>
</div>
</div>
</div>
</div>
</div>
```
## CSS
```css
.tp-19 {
width: 100%;
min-height: 100vh;
background: #0e0c14;
/* Top + bottom room — event cards alternate above and below the
track. Odd events flip column-reverse so the label lands above
the dot and the card pops up. Cards are ~220px tall. */
padding: 240px 24px;
font-family: 'Inter', system-ui, sans-serif;
position: relative;
overflow: hidden;
}
.tp-19::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(ellipse 60% 50% at 50% 50%, rgba(80, 40, 120, 0.08) 0%, transparent 70%);
pointer-events: none;
}
.tp-19__track {
max-width: 760px;
margin: 0 auto;
position: relative;
padding: 40px 0;
}
.tp-19__track::before {
content: '';
position: absolute;
top: 50%;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(to right, transparent, rgba(120, 80, 200, 0.3) 10%, rgba(120, 80, 200, 0.3) 90%, transparent);
transform: translateY(-50%);
}
.tp-19__events {
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
}
.tp-19__event {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
cursor: help;
}
.tp-19__event:nth-child(odd) {
flex-direction: column-reverse;
}
.tp-19__label {
font-size: 9px;
color: rgba(180, 140, 240, 0.5);
text-align: center;
margin: 11px 0;
letter-spacing: 0.08em;
font-family: 'JetBrains Mono', ui-monospace, monospace;
}
.tp-19__year {
font-size: 11px;
font-weight: 700;
color: rgba(180, 140, 240, 0.7);
text-align: center;
margin: 11px 0;
font-family: 'JetBrains Mono', ui-monospace, monospace;
letter-spacing: 0.04em;
}
.tp-19__dot {
width: 11px;
height: 11px;
border-radius: 50%;
background: rgba(120, 80, 200, 0.6);
border: 2px solid rgba(120, 80, 200, 0.3);
transition: background .15s, box-shadow .15s, transform .15s;
position: relative;
z-index: 2;
flex-shrink: 0;
}
.tp-19__event:hover .tp-19__dot {
background: rgba(160, 110, 240, 0.9);
box-shadow: 0 0 14px 4px rgba(120, 80, 200, 0.25);
transform: scale(1.3);
}
.tp-19__card {
position: absolute;
left: 50%;
transform: translateX(-50%) translateY(-6px);
z-index: 200;
width: 250px;
background: rgba(14, 12, 22, 0.97);
border: 1px solid rgba(120, 80, 200, 0.2);
border-radius: 10px;
opacity: 0;
visibility: hidden;
pointer-events: none;
transition: opacity .2s, transform .2s cubic-bezier(0.22, 1, 0.36, 1), visibility 0s linear .2s;
box-shadow: 0 16px 44px rgba(0, 0, 0, 0.9), 0 0 0 1px rgba(120, 80, 200, 0.08);
overflow: hidden;
text-align: left;
}
.tp-19__event:nth-child(odd) .tp-19__card {
bottom: calc(100% + 10px);
top: auto;
}
.tp-19__event:nth-child(even) .tp-19__card {
top: calc(100% + 10px);
bottom: auto;
}
.tp-19__event:hover .tp-19__card {
opacity: 1;
visibility: visible;
transform: translateX(-50%) translateY(0);
transition-delay: 0s;
}
.tp-19__card-top {
padding: 12px 13px 10px;
border-bottom: 1px solid rgba(120, 80, 200, 0.1);
}
.tp-19__card-year {
font-size: 8.5px;
font-family: 'JetBrains Mono', ui-monospace, monospace;
color: rgba(160, 110, 240, 0.5);
letter-spacing: 0.1em;
margin-bottom: 4px;
}
.tp-19__card-title {
font-size: 13px;
color: rgba(255, 255, 255, 0.88);
line-height: 1.3;
font-family: Georgia, serif;
}
.tp-19__badge {
display: inline-block;
font-size: 7.5px;
font-family: 'JetBrains Mono', ui-monospace, monospace;
padding: 2px 7px;
border-radius: 3px;
margin-top: 7px;
letter-spacing: 0.07em;
text-transform: uppercase;
}
.tp-19__badge-mile {
background: rgba(80, 160, 80, 0.15);
color: rgba(120, 200, 120, 0.8);
border: 1px solid rgba(80, 160, 80, 0.2);
}
.tp-19__badge-prod {
background: rgba(80, 120, 200, 0.15);
color: rgba(120, 160, 240, 0.8);
border: 1px solid rgba(80, 120, 200, 0.2);
}
.tp-19__badge-acq {
background: rgba(200, 120, 40, 0.15);
color: rgba(240, 160, 80, 0.8);
border: 1px solid rgba(200, 120, 40, 0.2);
}
.tp-19__badge-ipo {
background: rgba(160, 80, 200, 0.15);
color: rgba(200, 120, 240, 0.8);
border: 1px solid rgba(160, 80, 200, 0.2);
}
.tp-19__body {
padding: 9px 13px;
font-size: 11px;
color: rgba(255, 255, 255, 0.5);
line-height: 1.55;
border-bottom: 1px solid rgba(120, 80, 200, 0.06);
font-style: italic;
font-family: Georgia, serif;
}
.tp-19__impact {
padding: 9px 13px;
display: flex;
justify-content: space-between;
font-size: 9.5px;
font-family: 'JetBrains Mono', ui-monospace, monospace;
}
.tp-19__impact-label {
color: rgba(120, 80, 200, 0.5);
}
.tp-19__impact-val {
color: rgba(200, 160, 255, 0.7);
}
```Here's a working CSS Tooltip from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Event Timeline
Source: https://codefronts.com/snippets/css-tooltips/event-timeline/
A horizontal seven-event company timeline (seed → IPO) with alternating labels above and below the track. Hovering any dot reveals an event card with quarter, headline, body prose, and a metric badge (valuation, ARR, market cap). For about pages, investor pages, retrospectives.
## HTML
```html
<div class="tp-19">
<div class="tp-19__track">
<div class="tp-19__events">
<div class="tp-19__event">
<div class="tp-19__label">Seed Round</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2018</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q3 · 2018</div><div class="tp-19__card-title">Seed Funding Closed</div><span class="tp-19__badge tp-19__badge-mile">Milestone</span></div>
<div class="tp-19__body">$2.4M raised from 12 angel investors. The round was led by two former founders from the enterprise SaaS sector, providing both capital and key industry connections.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">Runway</span><span class="tp-19__impact-val">18 months secured</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">Public Beta</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2019</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q1 · 2019</div><div class="tp-19__card-title">Public Beta Launch</div><span class="tp-19__badge tp-19__badge-prod">Product</span></div>
<div class="tp-19__body">First 1,000 paying customers acquired within 90 days. Net Promoter Score of 72 in initial surveys. Product-market fit validated across legal, healthcare, fintech.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">ARR at launch</span><span class="tp-19__impact-val">$480K</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">Series A</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2020</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q2 · 2020</div><div class="tp-19__card-title">Series A — $12M</div><span class="tp-19__badge tp-19__badge-mile">Milestone</span></div>
<div class="tp-19__body">$12M Series A led by Sequoia Capital. Round closed despite pandemic uncertainty, reflecting strong retention. Funds used to double engineering headcount.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">Post-money</span><span class="tp-19__impact-val">$68M</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">EU Expansion</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2021</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q3 · 2021</div><div class="tp-19__card-title">100K Users · EU Launch</div><span class="tp-19__badge tp-19__badge-prod">Growth</span></div>
<div class="tp-19__body">Crossed 100,000 active users and launched GDPR-compliant EU data centres in Frankfurt and Dublin. Opened London office with 24 hires. MRR surpassed $1M.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">EU share</span><span class="tp-19__impact-val">31% of new ARR</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">Series B</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2022</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q1 · 2022</div><div class="tp-19__card-title">Series B — $45M · Profitable</div><span class="tp-19__badge tp-19__badge-mile">Milestone</span></div>
<div class="tp-19__body">$45M Series B led by Andreessen Horowitz. Achieved first profitable quarter ahead of schedule. Capital allocated to enterprise sales infrastructure.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">Valuation</span><span class="tp-19__impact-val">$320M post-money</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">Acquisition</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2023</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q2 · 2023</div><div class="tp-19__card-title">Acquired Synapse Labs</div><span class="tp-19__badge tp-19__badge-acq">M&A</span></div>
<div class="tp-19__body">Acquired Synapse Labs, a 22-person AI workflow automation startup, for $280M. The deal added Synapse's LLM orchestration layer, accelerating the roadmap.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">ARR added</span><span class="tp-19__impact-val">$8.4M</span></div>
</div>
</div>
<div class="tp-19__event">
<div class="tp-19__label">IPO Filed</div>
<div class="tp-19__dot"></div>
<div class="tp-19__year">2024</div>
<div class="tp-19__card">
<div class="tp-19__card-top"><div class="tp-19__card-year">Q3 · 2024</div><div class="tp-19__card-title">S-1 Filed · NASDAQ</div><span class="tp-19__badge tp-19__badge-ipo">Public Markets</span></div>
<div class="tp-19__body">Filed S-1 registration with the SEC. IPO priced at $18/share, valuing the company at $1.2B. Shares began trading on NASDAQ under the ticker SNYP, closing up 38% on day one.</div>
<div class="tp-19__impact"><span class="tp-19__impact-label">Market cap</span><span class="tp-19__impact-val">$1.65B (day 1)</span></div>
</div>
</div>
</div>
</div>
</div>
```
## CSS
```css
.tp-19 {
width: 100%;
min-height: 100vh;
background: #0e0c14;
/* Top + bottom room — event cards alternate above and below the
track. Odd events flip column-reverse so the label lands above
the dot and the card pops up. Cards are ~220px tall. */
padding: 240px 24px;
font-family: 'Inter', system-ui, sans-serif;
position: relative;
overflow: hidden;
}
.tp-19::before {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(ellipse 60% 50% at 50% 50%, rgba(80, 40, 120, 0.08) 0%, transparent 70%);
pointer-events: none;
}
.tp-19__track {
max-width: 760px;
margin: 0 auto;
position: relative;
padding: 40px 0;
}
.tp-19__track::before {
content: '';
position: absolute;
top: 50%;
left: 0;
right: 0;
height: 1px;
background: linear-gradient(to right, transparent, rgba(120, 80, 200, 0.3) 10%, rgba(120, 80, 200, 0.3) 90%, transparent);
transform: translateY(-50%);
}
.tp-19__events {
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
}
.tp-19__event {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
cursor: help;
}
.tp-19__event:nth-child(odd) {
flex-direction: column-reverse;
}
.tp-19__label {
font-size: 9px;
color: rgba(180, 140, 240, 0.5);
text-align: center;
margin: 11px 0;
letter-spacing: 0.08em;
font-family: 'JetBrains Mono', ui-monospace, monospace;
}
.tp-19__year {
font-size: 11px;
font-weight: 700;
color: rgba(180, 140, 240, 0.7);
text-align: center;
margin: 11px 0;
font-family: 'JetBrains Mono', ui-monospace, monospace;
letter-spacing: 0.04em;
}
.tp-19__dot {
width: 11px;
height: 11px;
border-radius: 50%;
background: rgba(120, 80, 200, 0.6);
border: 2px solid rgba(120, 80, 200, 0.3);
transition: background .15s, box-shadow .15s, transform .15s;
position: relative;
z-index: 2;
flex-shrink: 0;
}
.tp-19__event:hover .tp-19__dot {
background: rgba(160, 110, 240, 0.9);
box-shadow: 0 0 14px 4px rgba(120, 80, 200, 0.25);
transform: scale(1.3);
}
.tp-19__card {
position: absolute;
left: 50%;
transform: translateX(-50%) translateY(-6px);
z-index: 200;
width: 250px;
background: rgba(14, 12, 22, 0.97);
border: 1px solid rgba(120, 80, 200, 0.2);
border-radius: 10px;
opacity: 0;
visibility: hidden;
pointer-events: none;
transition: opacity .2s, transform .2s cubic-bezier(0.22, 1, 0.36, 1), visibility 0s linear .2s;
box-shadow: 0 16px 44px rgba(0, 0, 0, 0.9), 0 0 0 1px rgba(120, 80, 200, 0.08);
overflow: hidden;
text-align: left;
}
.tp-19__event:nth-child(odd) .tp-19__card {
bottom: calc(100% + 10px);
top: auto;
}
.tp-19__event:nth-child(even) .tp-19__card {
top: calc(100% + 10px);
bottom: auto;
}
.tp-19__event:hover .tp-19__card {
opacity: 1;
visibility: visible;
transform: translateX(-50%) translateY(0);
transition-delay: 0s;
}
.tp-19__card-top {
padding: 12px 13px 10px;
border-bottom: 1px solid rgba(120, 80, 200, 0.1);
}
.tp-19__card-year {
font-size: 8.5px;
font-family: 'JetBrains Mono', ui-monospace, monospace;
color: rgba(160, 110, 240, 0.5);
letter-spacing: 0.1em;
margin-bottom: 4px;
}
.tp-19__card-title {
font-size: 13px;
color: rgba(255, 255, 255, 0.88);
line-height: 1.3;
font-family: Georgia, serif;
}
.tp-19__badge {
display: inline-block;
font-size: 7.5px;
font-family: 'JetBrains Mono', ui-monospace, monospace;
padding: 2px 7px;
border-radius: 3px;
margin-top: 7px;
letter-spacing: 0.07em;
text-transform: uppercase;
}
.tp-19__badge-mile {
background: rgba(80, 160, 80, 0.15);
color: rgba(120, 200, 120, 0.8);
border: 1px solid rgba(80, 160, 80, 0.2);
}
.tp-19__badge-prod {
background: rgba(80, 120, 200, 0.15);
color: rgba(120, 160, 240, 0.8);
border: 1px solid rgba(80, 120, 200, 0.2);
}
.tp-19__badge-acq {
background: rgba(200, 120, 40, 0.15);
color: rgba(240, 160, 80, 0.8);
border: 1px solid rgba(200, 120, 40, 0.2);
}
.tp-19__badge-ipo {
background: rgba(160, 80, 200, 0.15);
color: rgba(200, 120, 240, 0.8);
border: 1px solid rgba(160, 80, 200, 0.2);
}
.tp-19__body {
padding: 9px 13px;
font-size: 11px;
color: rgba(255, 255, 255, 0.5);
line-height: 1.55;
border-bottom: 1px solid rgba(120, 80, 200, 0.06);
font-style: italic;
font-family: Georgia, serif;
}
.tp-19__impact {
padding: 9px 13px;
display: flex;
justify-content: space-between;
font-size: 9.5px;
font-family: 'JetBrains Mono', ui-monospace, monospace;
}
.tp-19__impact-label {
color: rgba(120, 80, 200, 0.5);
}
.tp-19__impact-val {
color: rgba(200, 160, 255, 0.7);
}
```How this works
The tp-19 timeline is a horizontal flex row of seven events strung along a 1px gradient rail rendered by the ::before pseudo-element on tp-19__track. Each tp-19__event is a flex column that stacks a label, a dot, and a year; the :nth-child(odd) selector flips events to column-reverse so labels alternate above and below the rail. The dot is an 11px circle with a translucent purple ring; on hover it snaps to scale(1.3) with a 14px glow via box-shadow, marking the active event without shifting the track.
The card is tp-19__card, a 250px popover that flips its anchor based on which side of the rail the event sits on. Odd events show their card above the rail (bottom: calc(100% + 10px)); even events show theirs below (top: calc(100% + 10px)). Both use left: 50%; translateX(-50%) for horizontal centring, and the enter transition combines opacity, a 6px translate-Y, and delayed visibility. Because odd and even events use different Y anchors, the transition remains symmetrical without extra CSS on the hover side.
Inside the card, tp-19__card-top stacks a monospaced quarter/year, a Georgia-serif headline, and a tp-19__badge chip — one of four modifiers (tp-19__badge-mile, tp-19__badge-prod, tp-19__badge-acq, tp-19__badge-ipo) each tinted for milestone, product/growth, mergers, or IPO categories. The body is set in italic Georgia at 11px for that Substack-timeline-editorial cadence, and the tp-19__impact footer flexes a monospaced label/value pair (Runway, Post-money, Market cap) across the bottom.
Make it yours
- Add an event by dropping another
tp-19__eventintotp-19__events. The parent'sjustify-content: space-betweenauto-distributes eight events as cleanly as seven; the odd/even card flip continues on the new node. - Retint the whole timeline by editing the four badge modifiers and the two
rgba(120, 80, 200, ...)tokens on the track and dot. The purple scheme is the only colour identity; every accent flows from those two hues. - Swap Georgia for a display serif like Playfair Display or Fraunces on
tp-19__card-titleandtp-19__body. Keep the monospaced JetBrains Mono on year/badge/impact so the editorial-meets-technical rhythm survives. - Turn the timeline vertical for mobile by adding an
@media (max-width: 720px)block that switchestp-19__eventstoflex-direction: column. The card anchors then need a translateY/translateX swap to open sideways — a one-block override. - Attach a category tint to the dot itself by adding modifiers like
tp-19__dot--acqthat inherit the badge's colour token. That surfaces category information without requiring the user to hover the card.
Gotchas — read before shipping
- The 240px top and bottom padding on the container is the load-bearing part. Cards are ~220px tall and pop equally above and below the rail. Trim the padding and the outermost events clip against the container edges — a classic mistake when embedding a timeline inside a shorter shell.
- Georgia italic renders differently on Windows vs macOS; the 11px italic body can drop below 4.5:1 against
rgba(14, 12, 22, 0.97)on Windows because Cleartype smooths differently. Verify with a real contrast checker to stay above SC 1.4.3 Contrast (Minimum). - Hover-only reveal fails SC 2.1.1 for keyboard users. Promote
.tp-19__eventto a button, add:focus-visible .tp-19__cardselectors alongside the hover ones, and ensure the dot'sscale(1.3)hover state also triggers on focus so the tab position is visible on the rail. - The overlay radial gradient on
tp-19::beforeusespointer-events: none. Without that rule, the gradient would swallow pointer events across the entire container and no dot would ever fire hover — always double-check this bit before layering additional visual overlays. - The card uses
rgba(14, 12, 22, 0.97), not fully opaque. On top of the radial gradient overlay the residual 3% transparency reads fine, but if you nest the timeline inside a page with a busy hero image, the card copy loses legibility. Bump the alpha to 1.0 for those contexts. filterplustransform: scaleon hover promotes each dot to its own layer. Multiplying this timeline to 40+ events on a scroll-linked animation on Chrome Android will pin the compositor at 100% and destroy INP. Keep the event count under 12 per row, or debounce the box-shadow viawill-change.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 90+ | 15+ | 88+ | 90+ |
Mixed monospaced/serif font stacks, radial-gradient overlays, and nth-child column-reverse flip are all native. The rgba-heavy card background and box-shadow glow render identically to Chromium. Cubic-bezier transitions, aspect-ratio-free layout, and the alternating column-reverse rule all work without prefixes.