Public
Edited
Feb 5
Insert cell
Insert cell
chart = {

// Create the SVG container.
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto; background: #006d77; color: #fff;")
.attr("class", "chart");

// setting rect width (could use band scale & bandwidth for a more flexible approach)
const rectWidth = 100

// Add title
svg.append('g')
.append('text')
.attr('class', 'chart-title')
.attr('x', width / 2)
.attr('y', margin.top/2)
.attr('text-anchor', 'middle')
.attr('fill', 'white')
.attr('style', 'font-size: 24px;')
.text("Stacked & Labeled Bar Chart");

// draw grid lines
svg.append('g').call(yGrid)

const rects = svg.append("g").selectAll().data(stack).enter()
.append("g")
.attr("fill", d => colors[d.key]);
// Add a rect for each bar.
rects.selectAll("rect")
.data(d => d)
.join("rect")
.attr("x", (d) => xScale(d.data.date) - rectWidth/2)
.attr("y", (d) => yScale(d[1]))
.attr("height", d=> yScale(d[0]) - yScale(d[1]))
.attr("width", rectWidth);

// Add text labels
rects.selectAll("text")
.data(d => d)
.join("text")
.attr("x", (d) => xScale(d.data.date))
.attr("y", (d) => yScale(d[0]) + (yScale(d[1]) - yScale(d[0])) / 2) // center text
.attr("text-anchor", "middle")
.attr("alignment-baseline", "middle")
.attr("fill", "#006d77")
.attr("opacity", (d) => yScale(d[1]) ? 1 : 0) // don't show text labels for segments that don't exist in a given month (i.e. there's no "Hold" in May so we don't want to show a text element that says "NaN")
.text((d) => (d[1] - d[0]));


// Add the x-axis and label.
svg.append("g")
.attr("transform", `translate(0,${height - margin.bottom})`)
.call(
d3.axisBottom(xScale)
.tickFormat(d3.timeFormat('%b')) // month abbr. formatting
.tickSize(0) // hide tick marks
.tickPadding(10)
)
.selectAll(".domain,.tick>line") // hide x axis line
.remove();

// Add the y-axis and label, and remove the domain line.
svg.append("g")
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(yScale)
.ticks(8)
.tickSize(0) // hide tick marks
)
.attr("font-size", "20px")
.selectAll(".domain,.tick>line") // hide y axis line
.remove();

// Add tick label styling
svg.selectAll(".tick text")
.attr("font-size", "20px")

const legendKeys = Object.keys(colors).reverse()
// Add legend
svg.selectAll("legend-marks")
.data(legendKeys)
.enter()
.append("rect")
.attr("x", 100)
.attr("y", function(d,i){ return height - 50 - i*(25)}) // 100 is where the first dot appears. 25 is the distance between dots
.attr("width", 20)
.attr("height", 20)
.style("fill", (d)=>colors[d])
// Add one dot in the legend for each name.
svg.selectAll("legend-labels")
.data(legendKeys)
.enter()
.append("text")
.attr("x", 100 + 20*1.2)
.attr("y", function(d,i){ return height - 50 - i*(20+5) + (20/2)}) // 100 is where the first dot appears. 25 is the distance between dots
.style("fill", "#fff")
.text((d)=>d)
.attr("text-anchor", "left")
.style("alignment-baseline", "middle")

// Return the SVG element.
return svg.node();
}
Insert cell
Insert cell
data = ratingData.map((d)=> ({...d, date: new Date(Date.parse(d.date))}))
Insert cell
categories = ["Group5", "Group4", "Group3", "Group2", "Group1"]
Insert cell
xScale = d3.scaleTime()
.domain([new Date("2000-01-01T05:00Z"), new Date("2000-06-01T05:00Z")])
.range([margin.left+100, width - margin.right -100])
Insert cell
yScale = d3.scaleLinear()
.domain([0, 7])
.range([height - margin.bottom, margin.top]);
Insert cell
colors = ({
"Group1": "#83c5be",
"Group2": "#edf6f9",
"Group3": "#ffddd2",
"Group4": "#e29578",
"Group5": "#fca311",
})
Insert cell
height = 700
Insert cell
width = 900
Insert cell
margin = ({ left: 50, bottom: 200, right: 30, top: 100})
Insert cell
stack = d3.stack().keys(categories.reverse())(data)
Insert cell
xGrid = (g) => g
.attr('class', 'grid-lines')
.selectAll('line')
.data(xScale.ticks())
.join('line')
.attr('x1', d => xScale(d))
.attr('x2', d => xScale(d))
.attr('y1', margin.top)
.attr('y2', height - margin.bottom)
Insert cell
yGrid = (g) => g
.attr('class', 'grid-lines')
.selectAll('line')
.data(yScale.ticks())
.join('line')
.attr('x1', margin.left)
.attr('x2', width - margin.right)
.attr('y1', d => yScale(d))
.attr('y2', d => yScale(d))
Insert cell
style = html`
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:ital@0;1&display=swap">

<style>
body, svg {
font-family: 'Roboto', serif;
}
.grid-lines line {
stroke: lightgray;
stroke-opacity: 0.3;
}
.grid-lines line:nth-child(even) {
opacity: 0
}
</style>
`
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more