How to Build a StockTicker Marquee with HTML, CSS & JavaScript
Overview
Create a lightweight, accessible, and responsive scrolling stock ticker that fetches real-time or simulated prices and displays symbol, price, change, and change color. This guide assumes you’ll fetch data via an API or WebSocket; if not available, it uses simulated updates.
Files
- index.html
- styles.css
- app.js
HTML (structure)
<!doctype html>
<html lang=“en”>
<head>
<meta charset=“utf-8” />
<meta name=“viewport” content=“width=device-width,initial-scale=1” />
<title>StockTicker Marquee</title>
<link rel=“stylesheet” href=“styles.css” />
</head>
<body>
<div class=“ticker” role=“region” aria-label=“Stock ticker”>
<div class=“tickertrack” id=“tickerTrack” aria-live=“polite”></div>
</div>
<script src=“app.js”></script>
</body>
</html>
CSS (styles.css)
:root{
–bg:#0b1220;
–text:#e6eef8;
–up:#28a745;
–down:#d93636;
–muted:#95a3b8;
–height:48px;
–gap:32px;
}
*{box-sizing:border-box}
body{margin:0;font-family:Inter,system-ui,-apple-system,Segoe UI,Roboto,“Helvetica Neue”,Arial;background:var(–bg);color:var(–text);display:flex;align-items:center;justify-content:center;height:100vh}
.ticker{
width:100%;
max-width:1100px;
background:linear-gradient(90deg, rgba(255,255,255,0.02), rgba(255,255,255,0.01));
border-radius:8px;
padding:6px 12px;
overflow:hidden;
height:var(–height);
display:flex;
align-items:center;
}
.tickertrack{
display:flex;
gap:var(–gap);
align-items:center;
will-change:transform;
white-space:nowrap;
animation:scroll 25s linear infinite;
}
.tickeritem{
display:inline-flex;
align-items:center;
gap:8px;
padding:6px 10px;
border-radius:6px;
background:transparent;
color:var(–text);
font-size:14px;
}
.tickersymbol{font-weight:700}
.tickerprice{min-width:70px;text-align:right;font-variant-numeric:tabular-nums}
.tickerchange{padding-left:8px;font-variant-numeric:tabular-nums}
.tickerup{color:var(–up)}
.tickerdown{color:var(–down)}
.tickermuted{color:var(–muted);font-weight:600}
@keyframes scroll{
0%{transform:translateX(0)}
100%{transform:translateX(-50%)}
}
/* Pause on hover for accessibility */
.ticker:hover .tickertrack, .ticker:focus-within .tickertrack{
animation-play-state:paused;
}
JavaScript (app.js)
// Config
const symbols = [‘AAPL’,‘MSFT’,‘GOOGL’,‘AMZN’,‘TSLA’,‘NVDA’,‘META’,‘BABA’];
const track = document.getElementById(‘tickerTrack’);
const updateInterval = 3000; // ms for simulated updates
// Utility: format price & change
const fmt = num => Number(num).toLocaleString(undefined, {minimumFractionDigits:2, maximumFractionDigits:2});
const sign = n => (n>0?’+’:(n<0?’-’:“));
// Create item element
function createItem(sym, price, change){
const item = document.createElement(‘div’);
item.className = ‘tickeritem’;
item.innerHTML = </span><span class="token template-string" style="color: rgb(163, 21, 21);"> </span><span class="token template-string" style="color: rgb(163, 21, 21);"> <span class="ticker__symbol"></span><span class="token template-string interpolation interpolation-punctuation" style="color: rgb(57, 58, 52);">${</span><span class="token template-string interpolation">sym</span><span class="token template-string interpolation interpolation-punctuation" style="color: rgb(57, 58, 52);">}</span><span class="token template-string" style="color: rgb(163, 21, 21);"></span> </span><span class="token template-string" style="color: rgb(163, 21, 21);"> <span class="ticker__price">$</span><span class="token template-string interpolation interpolation-punctuation" style="color: rgb(57, 58, 52);">${</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">fmt</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">(</span><span class="token template-string interpolation">price</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">)</span><span class="token template-string interpolation interpolation-punctuation" style="color: rgb(57, 58, 52);">}</span><span class="token template-string" style="color: rgb(163, 21, 21);"></span> </span><span class="token template-string" style="color: rgb(163, 21, 21);"> <span class="ticker__change </span><span class="token template-string interpolation interpolation-punctuation" style="color: rgb(57, 58, 52);">${</span><span class="token template-string interpolation">change</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">></span><span class="token template-string interpolation" style="color: rgb(54, 172, 170);">0</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">?</span><span class="token template-string interpolation" style="color: rgb(163, 21, 21);">'ticker__up'</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">:</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">(</span><span class="token template-string interpolation">change</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);"><</span><span class="token template-string interpolation" style="color: rgb(54, 172, 170);">0</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">?</span><span class="token template-string interpolation" style="color: rgb(163, 21, 21);">'ticker__down'</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">:</span><span class="token template-string interpolation" style="color: rgb(163, 21, 21);">'ticker__muted'</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">)</span><span class="token template-string interpolation interpolation-punctuation" style="color: rgb(57, 58, 52);">}</span><span class="token template-string" style="color: rgb(163, 21, 21);">"> </span><span class="token template-string" style="color: rgb(163, 21, 21);"> </span><span class="token template-string interpolation interpolation-punctuation" style="color: rgb(57, 58, 52);">${</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">sign</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">(</span><span class="token template-string interpolation">change</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">)</span><span class="token template-string interpolation interpolation-punctuation" style="color: rgb(57, 58, 52);">}</span><span class="token template-string interpolation interpolation-punctuation" style="color: rgb(57, 58, 52);">${</span><span class="token template-string interpolation">Math</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">.</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">abs</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">(</span><span class="token template-string interpolation">change</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">)</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">.</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">toFixed</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">(</span><span class="token template-string interpolation" style="color: rgb(54, 172, 170);">2</span><span class="token template-string interpolation" style="color: rgb(57, 58, 52);">)</span><span class="token template-string interpolation interpolation-punctuation" style="color: rgb(57, 58, 52);">}</span><span class="token template-string" style="color: rgb(163, 21, 21);"> </span><span class="token template-string" style="color: rgb(163, 21, 21);"> </span></span><span class="token template-string template-punctuation" style="color: rgb(163, 21, 21);">;
return item;
}
// Simulate initial prices
const state = {};
symbols.forEach(s=>{
state[s] = {price: Math.random()150+50};
});
// Render items twice for seamless loop
function render(){
track.innerHTML = ”;
const fragment = document.createDocumentFragment();
symbols.forEach(s=>{
fragment.appendChild(createItem(s, state[s].price, state[s].change || 0));
});
// duplicate
const frag2 = fragment.cloneNode(true);
track.appendChild(fragment);
track.appendChild(frag2);
}
render();
// Simulate updates (replace with real API/WebSocket)
function randomWalk(){
symbols.forEach(s=>{
const p = state[s].price;
const delta = (Math.random()-0.48) (p 0.01); // ~1% moves
const newP = Math.max(0.01, p + delta);
state[s].change = newP - p;
state[s].price = newP;
});
render();
}
// Example of integrating a real API (pseudo):
// fetch(‘/api/prices’).then(r=>r.json()).then(data => { / update state and render */ });
// Start updates
setInterval(randomWalk, updateInterval);
Accessibility & Performance Notes
- Use aria-live or role=region to announce updates; pause animation on hover/focus.
- For real-time data, prefer WebSocket for low-latency updates; fall back to polling.
- Duplicate the track contents for smooth infinite scrolling, or use CSS scroll snapping for different effects.
- Keep DOM updates minimal: update text nodes rather than reconstructing whole DOM for high-frequency updates.
Deployment tips
- Host only necessary assets; minify CSS/JS.
- Cache static assets and use rate limits for API calls.
- If using third-party market data, check usage limits and display disclaimers if required.
If you want, I can convert this into a single-file example or show a WebSocket integration example.