.dashboard-container {
max-width: 900px;
width: 100%;
}
/* Typography */
.main-title {
text-align: center;
font-size: 22px;
font-weight: bold;
margin-bottom: 50px;
color: #111;
}
/* Grid Layout */
.charts-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
row-gap: 50px;
column-gap: 30px;
margin-bottom: 30px;
}
/* Individual Chart Container */
.chart-item {
display: flex;
flex-direction: column;
align-items: center;
}
.chart-header {
font-size: 14px;
text-align: center;
text-transform: uppercase;
margin-bottom: 5px;
height: 40px; /* Fixed height for title alignment */
display: flex;
align-items: flex-end;
justify-content: center;
line-height: 1.15;
font-weight: 400;
color: #111;
}
/* Wrapper for the canvas to control its size */
.canvas-wrapper {
position: relative;
width: 120px;
height: 180px;
}
/* Footer Notes */
.footer-notes {
font-size: 14px;
color: #666;
line-height: 1.5;
padding-top: 20px;
margin-top: 20px;
}
/* Responsive Design */
@media (max-width: 768px) {
.charts-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 480px) {
.charts-grid {
grid-template-columns: 1fr;
}
}
Cost of inpatient procedures
// 1. Registrasi Plugin Datalabels agar angka muncul di atas bar
Chart.register(ChartDataLabels);
// 2. Data prosedur
const medicalData = [
{ id: 'chart0', title: "TOTAL HIP
REPLACEMENT", peerValue: 10, usValue: 29 },
{ id: 'chart1', title: "TOTAL KNEE
REPLACEMENT", peerValue: 11, usValue: 26 },
{ id: 'chart2', title: "PROSTATECTOMY
(ROBOTIC)", peerValue: 11, usValue: 24 },
{ id: 'chart3', title: "SLEEVE
GASTRECTOMY", peerValue: 9, usValue: 23 },
{ id: 'chart4', title: "SPINAL
DECOMPRESSION", peerValue: 7, usValue: 21 },
{ id: 'chart5', title: "CHOLECYSTECTOMY", peerValue: 5, usValue: 18 },
{ id: 'chart6', title: "CHILDBIRTH
(C-SECTION)", peerValue: 4, usValue: 14 },
{ id: 'chart7', title: "APPENDECTOMY", peerValue: 4, usValue: 14 }
];
const container = document.getElementById('charts-container');
// 3. Buat kerangka HTML (canvas) untuk setiap grafik
medicalData.forEach(item => {
container.innerHTML += `
`;
});
// 4. Inisialisasi Chart.js untuk masing-masing canvas
medicalData.forEach(item => {
const ctx = document.getElementById(item.id).getContext('2d');
new Chart(ctx, {
type: 'bar',
data: {
labels: ['Peer', 'U.S.'],
datasets: [{
data: [item.peerValue, item.usValue],
backgroundColor: [
'#9FD0D7', // Light blue untuk Peer
'#31737B' // Dark teal untuk U.S.
],
// Mengatur ketebalan batang agar mirip desain asli
barPercentage: 0.7,
categoryPercentage: 1.0
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
layout: {
padding: {
top: 25 // Ruang kosong di atas agar teks angka tidak terpotong
}
},
scales: {
y: {
display: false, // Sembunyikan sumbu Y sepenuhnya
max: 32 // Set maksimal sedikit di atas nilai tertinggi (29)
},
x: {
grid: {
display: false,
drawBorder: false // Menghilangkan garis sumbu X
},
ticks: {
font: {
size: 16,
family: "'Helvetica Neue', Helvetica, Arial, sans-serif"
},
color: '#111'
}
}
},
plugins: {
legend: {
display: false // Sembunyikan legenda
},
tooltip: {
enabled: false // Sembunyikan tooltip saat dihover (opsional, karena WSJ statis)
},
datalabels: {
anchor: 'end',
align: 'end',
offset: 4,
formatter: (value) => '$' + value + 'k',
font: {
size: 18,
family: "'Helvetica Neue', Helvetica, Arial, sans-serif"
},
color: '#111'
}
},
// Menghilangkan interaksi hover agar terasa seperti grafik statis jurnalistik
hover: {mode: null},
animation: false // Matikan animasi agar langsung tampil (opsional)
}
});
});
Americans spend more on healthcare than anyone else in the world. Just insuring a family here costs nearly $27,000 a year, enough to buy a car.
The main cause: Prices are far higher in the U.S. for the same medical products and services, from surgeries to drugs.
American patients have also been using more care recently, including costly hospital treatment and expensive new drugs for weight loss. That has pushed up spending as well.
Here are some of the factors that make U.S. healthcare the most expensive.
.dashboard-container {
max-width: 800px;
width: 100%;
}
/* Typography */
.main-title {
font-family: Georgia, 'Times New Roman', serif;
font-size: 24px;
font-weight: bold;
margin-bottom: 12px;
color: #111;
}
.sub-title {
font-family: Georgia, 'Times New Roman', serif;
font-size: 16px;
color: #333;
margin-bottom: 40px;
margin-left: 30px;
}
.section-header {
font-size: 15px;
font-weight: bold;
margin-bottom: 10px;
color: #111;
}
/* Chart Sections */
.drug-section {
margin-bottom: 35px;
}
.drug-title {
font-size: 15px;
margin-bottom: 15px;
}
.drug-title strong {
font-weight: bold;
color: #111;
}
.drug-title span {
font-weight: normal;
color: #444;
}
/* Rows & Lollipop Layout */
.chart-area {
padding-right: 60px;
}
.chart-row {
display: flex;
align-items: center;
height: 24px;
}
.row-label {
width: 105px;
text-align: right;
padding-right: 12px;
font-size: 14px;
color: #222;
}
.row-track-area {
flex-grow: 1;
height: 100%;
border-left: 2px solid #31737B;
display: flex;
align-items: center;
position: relative;
}
/* The Static Bar (Lollipop Line & Dot) */
.track-group {
position: relative;
height: 100%;
display: flex;
align-items: center;
/* Transisi dihapus */
}
.track-line {
width: 100%;
height: 3px;
}
.track-dot {
position: absolute;
right: 0;
top: 50%;
width: 12px;
height: 12px;
border-radius: 50%;
transform: translate(50%, -50%);
}
/* Value Text */
.row-value {
position: absolute;
left: 100%;
top: 50%;
transform: translateY(-50%);
padding-left: 12px;
font-size: 14.5px;
color: #111;
/* Opacity 0 dihapus, jadi langsung terlihat */
opacity: 1;
}
/* Colors matching WSJ */
.us .track-line, .us .track-dot { background-color: #31737B; }
.peer .track-line, .peer .track-dot { background-color: #9FD0D7; }
/* Footer Notes */
.footer-notes {
font-size: 13px;
color: #666;
line-height: 1.5;
padding-top: 20px;
margin-top: 20px;
border-top: 1px solid #eee;
}
.footer-notes p { margin-bottom: 4px; }
Prescription drugs cost a lot more in the U.S.
Most other nations force drugmakers to accept lower rates, while the U.S. government generally doesn’t.
// Data obat
const drugData = [
{
name: "Herceptin",
desc: "(For cancer; 450mg injection)",
data: [
{ country: "USA", value: 6819, type: "us" },
{ country: "Germany", value: 2599, type: "peer" },
{ country: "South Africa", value: 1620, type: "peer" },
{ country: "Greece", value: 1418, type: "peer" },
{ country: "UK", value: 1360, type: "peer" },
{ country: "Spain", value: 1338, type: "peer" }
]
},
{
name: "Enbrel",
desc: "(For arthritis; 50mg/ml - 4 syringes)",
data: [
{ country: "USA", value: 6238, type: "us" },
{ country: "Germany", value: 1043, type: "peer" },
{ country: "Spain", value: 802, type: "peer" },
{ country: "Greece", value: 668, type: "peer" },
{ country: "South Africa", value: 516, type: "peer" }
]
},
{
name: "Eliquis",
desc: "(For stroke and blood clots; 5mg - 60 pills)",
data: [
{ country: "USA", value: 513, type: "us" },
{ country: "Spain", value: 96, type: "peer" },
{ country: "Germany", value: 90, type: "peer" },
{ country: "Greece", value: 72, type: "peer" },
{ country: "South Africa", value: 60, type: "peer" }
]
}
];
const GLOBAL_MAX = 7000;
// Render HTML
const lollipop = document.getElementById('lollipop-charts');
let htmlContent = '';
drugData.forEach(drug => {
htmlContent += `
${drug.name} ${drug.desc}
`;
drug.data.forEach(row => {
const targetWidth = (row.value / GLOBAL_MAX) * 100;
// Lebar (width) sekarang langsung disuntikkan ke inline style tanpa menunggu JS timeout
htmlContent += `
${row.country}
$${row.value.toLocaleString('en-US')}
`;
});
htmlContent += `
`;
});
// Tampilkan hasil akhir
lollipop.innerHTML = htmlContent;
// Tidak perlu lagi fungsi setTimeout untuk memicu animasi
.dashboard-container {
max-width: 100%;
width: 100%;
}
/* Typography */
.main-title {
font-family: Georgia, 'Times New Roman', serif;
font-size: 24px;
font-weight: bold;
color: #111;
margin-bottom: 12px;
}
.sub-title {
font-family: Georgia, 'Times New Roman', serif;
font-size: 16.5px;
color: #333;
margin-bottom: 35px;
margin-left: 35px; /* Indentasi khas WSJ */
line-height: 1.5;
}
.section-header {
font-size: 15px;
font-weight: bold;
color: #111;
margin-bottom: 25px;
}
/* Dual Column Layout */
.chart-layout {
display: flex;
gap: 20px;
width: 100%;
margin-bottom: 20px;
}
.chart-column {
width: 50%;
display: flex;
flex-direction: column;
}
.column-title {
text-align: center;
font-size: 14.5px;
text-transform: uppercase;
color: #333;
margin-bottom: 15px;
}
/* Rows & Lollipop Layout */
.chart-row {
display: flex;
align-items: center;
height: 28px; /* Jarak antar baris */
}
.row-label {
width: 130px;
text-align: right;
padding-right: 12px;
font-size: 14.5px;
color: #222;
}
.row-track-area {
flex-grow: 1;
height: 100%;
border-left: 1px solid #111; /* Garis vertikal sumbu Y */
display: flex;
align-items: center;
position: relative;
}
/* The Static Bar (Lollipop Line & Dot) */
.track-group {
position: relative;
height: 100%;
display: flex;
align-items: center;
}
.track-line {
width: 100%;
height: 4px; /* Ketebalan garis batang */
}
.track-dot {
position: absolute;
right: 0;
top: 50%;
width: 13px;
height: 13px;
border-radius: 50%;
transform: translate(50%, -50%);
}
/* Value Text */
.row-value {
position: absolute;
left: 100%;
top: 50%;
transform: translateY(-50%);
padding-left: 12px;
font-size: 14.5px;
color: #111;
}
/* WSJ Colors */
/* U.S. (Top Row) is Dark Teal */
.us .track-line, .us .track-dot { background-color: #31737B; }
/* Other countries are Light Blue */
.peer .track-line, .peer .track-dot { background-color: #9FD0D7; }
/* Customizing specific labels like United States* and United Kingdom†*/
.row-label sup {
font-size: 11px;
}
/* Footer Notes */
.footer-notes {
font-size: 13.5px;
color: #666;
line-height: 1.5;
padding-top: 10px;
margin-top: 15px;
}
.footer-notes p { margin-bottom: 2px; }
/* Responsiveness */
@media (max-width: 768px) {
.chart-layout {
flex-direction: column;
gap: 40px;
}
.chart-column {
width: 100%;
}
.row-label {
width: 110px;
}
}
Labor costs are higher
American doctors and nurses generally make more than their counterparts in other countries, another factor that can drive up the cost of care.
// 1. Data sesuai dengan gambar
const physiciansData = [
{ country: "United States*", value: 245, type: "us" },
{ country: "Netherlands", value: 157, type: "peer" },
{ country: "Sweden", value: 128, type: "peer" },
{ country: "Spain", value: 124, type: "peer" },
{ country: "United Kingdom†", value: 99, type: "peer" },
{ country: "Türkiye", value: 98, type: "peer" },
{ country: "Israel", value: 70, type: "peer" },
{ country: "Czechia", value: 70, type: "peer" },
{ country: "Portugal", value: 69, type: "peer" },
{ country: "Mexico", value: 49, type: "peer" }
];
const nursesData = [
{ country: "", value: 97, type: "us" }, // Label kosong di kolom kanan
{ country: "", value: 82, type: "peer" },
{ country: "", value: 56, type: "peer" },
{ country: "", value: 67, type: "peer" },
{ country: "", value: 53, type: "peer" },
{ country: "", value: 48, type: "peer" },
{ country: "", value: 68, type: "peer" },
{ country: "", value: 52, type: "peer" },
{ country: "", value: 35, type: "peer" },
{ country: "", value: 35, type: "peer" }
];
// 2. Skala Global
// Dalam gambar, garis USA Physicians ($245k) hampir menyentuh ujung kontainer,
// jadi kita gunakan batas skala ~270 agar proporsional dan tidak terpotong.
const G_MAX = 270;
// 3. Fungsi Render Baris (Row)
function renderRows(dataArray, containerId, hasLabels) {
const container = document.getElementById(containerId);
let htmlContent = '';
dataArray.forEach(row => {
const targetWidth = (row.value / G_MAX) * 100;
// Format tanda * dan †menjadi elemen
let formattedCountry = row.country
.replace('*', '*')
.replace('†', '†');
// Jika nilai di atas 200 (USA), tambahkan tanda dollar '$'
let valueText = row.value > 200 ? `$${row.value}` : row.value;
// Jika parameter hasLabels false, sembunyikan container label
let labelDiv = hasLabels
? `${formattedCountry}
`
: ``;
htmlContent += `
`;
});
container.innerHTML = htmlContent;
}
// 4. Eksekusi Render
// Kolom Kiri (Dengan label nama negara)
renderRows(physiciansData, 'chart-physicians', true);
// Kolom Kanan (Tanpa label nama negara, sama persis seperti gambar)
renderRows(nursesData, 'chart-nurses', false);
.chart-container {
max-width: 100%;
width: 100%;
}
/* Typography matching WSJ */
h2.title {
font-family: Georgia, 'Times New Roman', serif;
font-size: 24px;
font-weight: bold;
color: #111;
margin-bottom: 15px;
letter-spacing: -0.2px;
}
p.subtitle {
font-family: Georgia, 'Times New Roman', serif;
font-size: 16px;
margin-left: 35px;
margin-bottom: 40px;
color: #222;
}
.chart-header {
font-weight: bold;
font-size: 14.5px;
color: #111;
margin-bottom: 20px;
}
/* Canvas Wrapper */
.canvas-wrapper {
position: relative;
height: 400px; /* Tinggi yang cukup untuk menampilkan grafik garis dengan jelas */
width: 100%;
margin-bottom: 25px;
}
/* Footer */
.footer-notes {
font-size: 13.5px;
color: #666;
line-height: 1.5;
margin-top: 10px;
}
Americans are using more healthcare
Healthcare utilization has grown faster than prices in the most recent years.
// Membungkus dalam IIFE untuk mencegah konflik nama variabel dengan chart lain di dashboard
(() => {
// Registrasi plugin
Chart.register(ChartDataLabels);
// Data Ekstraksi dari Gambar
// Membuat label dari 1979 hingga 2024 untuk mengakomodasi seluruh garis waktu
const years = [];
for (let y = 1979; y <= 2024; y++) {
years.push(y);
}
// Estimasi visual data (kurang lebih 46 titik data)
const priceData = [
12.0, 12.1, 11.2, 8.5, 6.0, 5.8, 6.4, 7.8, 8.6, 8.2,
7.0, 5.2, 4.0, 3.4, 1.8, 1.8, 1.8, 2.2, 3.0, 3.4,
2.5, 3.6, 3.5, 3.0, 2.9, 3.5, 2.7, 2.7, 2.4, 1.6,
1.6, 1.3, 1.0, 0.5, 1.0, 1.4, 1.8, 2.8, 2.3, 2.0,
2.6, 2.1, 2.5
];
const qtyData = [
2.5, 4.8, 0.0, 2.8, 1.2, 2.8, 2.8, 4.0, 3.7, 0.8,
3.2, 1.6, 2.8, 0.2, 0.6, 2.0, 2.0, 2.9, 3.0, 1.4,
3.0, 4.8, 5.8, 2.5, 3.2, 3.0, 2.0, 2.2, 2.2, 2.0,
1.2, 1.2, 1.6, 0.6, 5.2, 3.6, 2.0, 2.2, 3.5, -7.5,
8.9, 4.0, 6.0, 5.2
];
// Konfigurasi Kustom Garis Tebal (Sumbu X pada nilai 0)
const zeroLinePlugin = {
id: 'zeroLinePlugin',
beforeDraw(chart) {
const ctx = chart.ctx;
const xAxis = chart.scales.x;
const yAxis = chart.scales.y;
// Cari posisi pixel untuk Y = 0
const yZero = yAxis.getPixelForValue(0);
ctx.save();
ctx.beginPath();
ctx.moveTo(xAxis.left, yZero);
ctx.lineTo(xAxis.right, yZero);
ctx.lineWidth = 1.5;
ctx.strokeStyle = '#111'; // Hitam
ctx.stroke();
ctx.restore();
}
};
const ctxUtil = document.getElementById('utilizationChart').getContext('2d');
new Chart(ctxUtil, {
type: 'line',
plugins: [zeroLinePlugin], // Masukkan plugin garis nol
data: {
labels: years,
datasets: [
{
label: 'Price',
data: priceData,
borderColor: '#A8B1B6', // Abu-abu terang (Mirip gambar)
backgroundColor: 'rgba(230, 235, 235, 0.4)', // Abu-abu transparan untuk fill
borderWidth: 2,
pointRadius: 0, // Hilangkan titik
pointHoverRadius: 0,
fill: true,
tension: 0.1 // Sedikit melengkung
},
{
label: 'Quantity',
data: qtyData,
borderColor: '#19829A', // Teal terang (Mirip gambar)
backgroundColor: 'rgba(25, 130, 154, 0.1)', // Teal transparan untuk fill
borderWidth: 2,
pointRadius: 0,
pointHoverRadius: 0,
fill: true,
tension: 0.1
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
layout: {
padding: {
right: 80, // Ruang untuk label 'Quantity' dan 'Price' di kanan
top: 40 // Ruang untuk label kotak teks panjang
}
},
scales: {
y: {
min: -10,
max: 15,
grid: {
color: '#e4e4e4', // Garis horisontal tipis
drawBorder: false, // Hilangkan garis tebal di paling kiri sumbu Y
lineWidth: 1
},
ticks: {
stepSize: 5,
font: {
size: 14,
family: "'Helvetica Neue', Helvetica, Arial, sans-serif"
},
color: '#333',
callback: function(value) {
// Tambahkan tanda % hanya di angka 15
if (value === 15) return '15%';
// Berikan spasi pada angka minus agar sejajar
if (value = 1980 && year <= 2020) {
return year;
}
return '';
}
}
}
},
plugins: {
legend: {
display: false // Sembunyikan legenda standar
},
tooltip: {
enabled: false // Grafik WSJ biasanya statis
},
datalabels: {
// Menangani penempatan label khusus di akhir garis dan callout box
display: function(context) {
// Tampilkan label hanya pada titik data terakhir yang ada nilainya
const isLastPrice = context.datasetIndex === 0 && context.dataIndex === priceData.length - 1;
const isLastQty = context.datasetIndex === 1 && context.dataIndex === qtyData.length - 1;
// Callout box "Healthcare use has grown..." kita pasang di puncak Quantity (sekitar tahun 2021)
const isCallout = context.datasetIndex === 1 && context.dataIndex === 41; // Index 41 = tahun 2020
return isLastPrice || isLastQty || isCallout;
},
formatter: function(value, context) {
if (context.datasetIndex === 0 && context.dataIndex === priceData.length - 1) return 'Price';
if (context.datasetIndex === 1 && context.dataIndex === qtyData.length - 1) return 'Quantity';
if (context.datasetIndex === 1 && context.dataIndex === 41) return 'Healthcare use has grown faster than\nprices in recent years.';
return '';
},
color: function(context) {
if (context.datasetIndex === 0) return '#777'; // Warna label Price
if (context.datasetIndex === 1 && context.dataIndex === qtyData.length - 1) return '#19829A'; // Warna label Quantity
return '#222'; // Warna kotak callout
},
font: function(context) {
const isCallout = context.dataIndex === 41;
return {
size: isCallout ? 16 : 17,
family: "'Helvetica Neue', Helvetica, Arial, sans-serif",
weight: isCallout ? 'normal' : 'normal'
};
},
textAlign: 'right', // Untuk callout box agar rata kanan
anchor: function(context) {
if (context.dataIndex === 41) return 'top';
return 'end';
},
align: function(context) {
if (context.dataIndex === 41) return 'top';
return 'right';
},
offset: function(context) {
if (context.dataIndex === 41) return 25; // Jarak kotak callout ke atas
return 10; // Jarak label akhir dari garis
}
}
},
// NONAKTIFKAN ANIMASI agar memuat secara instan seperti gambar statis
animation: false
}
});
})();