<script type="module" src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.esm.js"></script>
<script nomodule src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.js"></script>
<div class="container">
<div class="chart-card">
<div class="chart-header">
<div class="chart-title">
<ion-icon name="bar-chart-outline"></ion-icon>
<h2>Sales Performance</h2>
</div>
<div class="chart-controls">
<button class="btn" onclick="animateChart()">
<ion-icon name="refresh-outline"></ion-icon>
Animate
</button>
<button class="btn" onclick="randomizeData()">
<ion-icon name="shuffle-outline"></ion-icon>
Randomize
</button>
</div>
</div>
<p class="chart-subtitle">Q4 2024 Performance by Product Category</p>
<div class="chart-content" id="chartContent">
<!-- Bars will be inserted here -->
</div>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-value" id="totalValue">0</div>
<div class="stat-label">Total Sales</div>
</div>
<div class="stat-card">
<div class="stat-value" id="avgValue">0</div>
<div class="stat-label">Average</div>
</div>
<div class="stat-card">
<div class="stat-value" id="maxValue">0</div>
<div class="stat-label">Highest</div>
</div>
</div>
<div class="legend">
<div class="legend-color"></div>
<span>Sales in thousands (K)</span>
</div>
</div>
</div>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 2rem;
display: flex;
justify-content: center;
align-items: center;
}
.container {
width: 100%;
max-width: 900px;
}
.chart-card {
background: white;
padding: 2.5rem;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(58, 1, 92, 0.3);
}
.chart-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
flex-wrap: wrap;
gap: 1rem;
}
.chart-title {
display: flex;
align-items: center;
gap: 0.75rem;
}
.chart-title ion-icon {
font-size: 2rem;
color: #3a015c;
}
.chart-title h2 {
color: #3a015c;
font-size: 1.75rem;
font-weight: 600;
}
.chart-controls {
display: flex;
gap: 0.5rem;
}
.btn {
background: #3a015c;
color: white;
border: none;
padding: 0.625rem 1.25rem;
border-radius: 8px;
font-size: 0.875rem;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.5rem;
}
.btn:hover {
background: #5a0190;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(58, 1, 92, 0.3);
}
.btn ion-icon {
font-size: 1.125rem;
}
.chart-subtitle {
color: #6c757d;
font-size: 0.925rem;
margin-bottom: 2rem;
}
.chart-content {
margin-top: 2rem;
}
.bar-item {
margin-bottom: 2rem;
}
.bar-label {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.label-name {
font-weight: 500;
color: #2c3e50;
font-size: 0.95rem;
}
.label-value {
font-weight: 600;
color: #3a015c;
font-size: 0.95rem;
}
.bar-track {
background: #f0f0f0;
height: 32px;
border-radius: 16px;
overflow: hidden;
position: relative;
}
.bar-fill {
height: 100%;
background: linear-gradient(90deg, #3a015c, #6a2c91);
border-radius: 16px;
width: 0;
transition: width 1.5s cubic-bezier(0.4, 0, 0.2, 1);
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 1rem;
color: white;
font-size: 0.875rem;
font-weight: 600;
box-shadow: 0 2px 8px rgba(58, 1, 92, 0.3);
}
.bar-fill.animated {
animation: pulse 2s ease-in-out;
}
@keyframes pulse {
0%, 100% {
box-shadow: 0 2px 8px rgba(58, 1, 92, 0.3);
}
50% {
box-shadow: 0 4px 16px rgba(58, 1, 92, 0.5);
}
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 1rem;
margin-top: 2rem;
padding-top: 2rem;
border-top: 2px solid #f0f0f0;
}
.stat-card {
text-align: center;
padding: 1rem;
background: #f8f9fa;
border-radius: 12px;
}
.stat-value {
font-size: 1.75rem;
font-weight: 700;
color: #3a015c;
margin-bottom: 0.25rem;
}
.stat-label {
font-size: 0.825rem;
color: #6c757d;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.legend {
display: flex;
align-items: center;
gap: 0.5rem;
color: #6c757d;
font-size: 0.875rem;
margin-top: 1.5rem;
}
.legend-color {
width: 20px;
height: 20px;
background: linear-gradient(90deg, #3a015c, #6a2c91);
border-radius: 4px;
}
</style>
const chartData = [
{ name: 'Electronics', value: 85 },
{ name: 'Clothing', value: 72 },
{ name: 'Home & Garden', value: 64 },
{ name: 'Sports', value: 58 },
{ name: 'Books', value: 45 },
{ name: 'Toys', value: 38 }
];
function renderChart(animate = true) {
const chartContent = document.getElementById('chartContent');
const maxValue = Math.max(...chartData.map(d => d.value));
chartContent.innerHTML = '';
chartData.forEach((item, index) => {
const barItem = document.createElement('div');
barItem.className = 'bar-item';
const percentage = (item.value / maxValue) * 100;
barItem.innerHTML = `
<div class="bar-label">
<span class="label-name">${item.name}</span>
<span class="label-value">${item.value}K</span>
</div>
<div class="bar-track">
<div class="bar-fill ${animate ? 'animated' : ''}" style="width: 0%"></div>
</div>
`;
chartContent.appendChild(barItem);
if (animate) {
setTimeout(() => {
const barFill = barItem.querySelector('.bar-fill');
barFill.style.width = percentage + '%';
}, index * 150);
}
});
updateStats();
}
function updateStats() {
const total = chartData.reduce((sum, item) => sum + item.value, 0);
const avg = Math.round(total / chartData.length);
const max = Math.max(...chartData.map(d => d.value));
animateValue('totalValue', 0, total, 1500);
animateValue('avgValue', 0, avg, 1500);
animateValue('maxValue', 0, max, 1500);
}
function animateValue(id, start, end, duration) {
const element = document.getElementById(id);
const range = end - start;
const increment = range / (duration / 16);
let current = start;
const timer = setInterval(() => {
current += increment;
if (current >= end) {
current = end;
clearInterval(timer);
}
element.textContent = Math.round(current) + 'K';
}, 16);
}
function animateChart() {
renderChart(true);
}
function randomizeData() {
chartData.forEach(item => {
item.value = Math.floor(Math.random() * 70) + 30;
});
chartData.sort((a, b) => b.value - a.value);
renderChart(true);
}
// Initial render
renderChart(true);
No comments yet. Be the first!