PageSourceSearch

https://www.visualizing.org/assets/Heatmap-SXIHC0G9.js

js visualizing.org collected 2026-09-24 18:11:10 UTC 90,418 bytes, 128 lines download raw bytes

1import{j as e,L as r}from"./vendor-react-CVijXI0n.js";import{S as d,b as h,h as m,a as p,c as x}from"./index-D0o2Sutv.js";import{C as u}from"./ChartBreadcrumb-BOBTh6lg.js";import{C as g,b as f}from"./ChartFAQ-DwXxlpIj.js";import{S as o,D as b,a as y,C as w,A as v,B as j,T as k,b as N,R as S}from"./References-CIdDEfJg.js";const i="https://www.visualizing.org",l=[{q:"What is a heatmap?",a:"A heatmap is a data visualization that represents values as colors in a two-dimensional grid. Each cell of the grid corresponds to a combination of two categorical or ordinal variables (rows and columns), and the cell's color encodes the magnitude of a third, quantitative variable on a sequential or diverging color scale."},{q:"When should you use a heatmap?",a:"Use a heatmap when you have a dense matrix of values (dozens to thousands of cells) and want readers to spot patterns, clusters, or outliers without reading every number. It is the right tool for correlation matrices, day×hour activity grids, gene-expression matrices, and any situation where the gestalt of the matrix matters more than precise per-cell values."},{q:"When should you avoid a heatmap?",a:"Avoid a heatmap when readers need precise values — color is the least precise visual encoding. Skip it when the matrix is small (a 3×3 grid is just a confusing table), when you only have one dimension (use a bar chart), or when you can’t guarantee a colorblind-safe palette and a textual fallback."},{q:"What is the difference between a heatmap and a choropleth map?",a:"Both encode magnitude with color, but a choropleth colors geographic regions on a map (countries, states, ZIP codes) while a heatmap colors cells in an abstract row×column grid. A choropleth lives in geographic space; a heatmap lives in tabular space. Both share the same accessibility and palette rules."},{q:"What is the difference between a heatmap and a scatter plot?",a:"A scatter plot encodes two continuous variables with point position to reveal correlation; a heatmap encodes a third value (often a count or a correlation coefficient) with color across a grid of two categorical or binned variables. Scatter plots are precise per-point; heatmaps are precise about pattern."},{q:"Sequential or diverging color scale — which should a heatmap use?",a:"Use a sequential palette (light→dark in a single hue, e.g. Viridis or Blues) when values run from a meaningful low to a meaningful high. Use a diverging palette (two hues meeting at a neutral midpoint, e.g. RdBu) when values are signed around a meaningful zero, such as correlation coefficients (-1 to +1) or percent change."},{q:"Is a rainbow palette OK for a heatmap?",a:"No. Rainbow palettes (jet, hsv, spectral) are not perceptually uniform — the eye sees green steps as smaller than yellow steps, distorting your data. They also fail badly for colorblind readers. Use Viridis, Magma, Inferno, Cividis, or any ColorBrewer sequential or diverging scheme."},{q:"How do I make a heatmap accessible?",a:"Pair a colorblind-safe palette (Viridis, Cividis, ColorBrewer) with a textual data table, label cell values directly when there are fewer than ~50 cells, ensure the legend has 4.5:1 contrast for axis text, and provide an aria-label that summarizes the dominant pattern instead of just naming the chart."},{q:"Should I cluster the rows and columns?",a:"Often, yes. Re-ordering rows and columns by hierarchical clustering, by row sum, or by a meaningful sort key turns a noisy matrix into block patterns the eye can read. The same data with random row order looks chaotic; with sorted rows it tells a story."},{q:"What is a calendar heatmap?",a:"A calendar heatmap is a heatmap whose rows and columns are days of the week and weeks of the year. GitHub’s contribution graph is the canonical example. The grid layout makes seasonal patterns and individual outlier days obvious in a way a line chart of daily totals cannot."},{q:"How big can a heatmap be?",a:"A well-designed heatmap can hold thousands of cells if the cells stay at least 4–6 pixels wide and the palette is perceptually uniform. Past about 10,000 cells you start to fight Moiré patterns and rendering performance — consider downsampling, hierarchical clustering, or a focus-plus-context view instead."},{q:"What category of chart is a heatmap?",a:"A heatmap belongs to the Magnitude family of charts (and overlaps with the Correlation fam
1ily when used for correlation matrices). Charts in those families are designed to answer the same kind of question, so they often work as alternatives when one doesn’t quite fit your data."},{q:"What is the best library for building heatmaps in code?",a:"For static publication-quality heatmaps, seaborn’s sns.heatmap (Python) and ggplot2’s geom_tile (R) are the standard choices. For interactive web heatmaps, D3.js gives the most control while Observable Plot (Plot.cell), Plotly, ECharts, and Recharts are higher-level alternatives."}],T=f("/heatmap",l),M={"@type":"HowTo","@id":`${i}/heatmap/#howto-python`,name:"Make a heatmap in Python with seaborn",description:"Build a heatmap in Python by loading a 2D matrix into a pandas DataFrame, calling seaborn.heatmap with a perceptually uniform palette, and adding cell annotations.",totalTime:"PT6M",tool:["Python 3","pandas","seaborn","Matplotlib"],step:[{"@type":"HowToStep",position:1,name:"Install seaborn",text:"Run pip install seaborn pandas matplotlib so the plotting stack is available."},{"@type":"HowToStep",position:2,name:"Shape the data",text:"Pivot your long-format data into a 2D matrix (DataFrame) with rows and columns aligned to the two categorical dimensions."},{"@type":"HowToStep",position:3,name:"Pick a palette",text:"Choose cmap='viridis' for sequential data or cmap='RdBu_r' centered on zero for diverging data such as correlations."},{"@type":"HowToStep",position:4,name:"Call sns.heatmap",text:"Pass the matrix to sns.heatmap(matrix, cmap=..., annot=True, fmt='.2f', cbar_kws={'label': 'units'})."},{"@type":"HowToStep",position:5,name:"Polish",text:"Rotate tick labels, add a takeaway title, and call plt.tight_layout() before plt.show() or plt.savefig()."}]},A={"@type":"HowTo","@id":`${i}/heatmap/#howto-excel`,name:"Make a heatmap in Microsoft Excel",description:"Build a heatmap in Excel by laying out a 2D matrix and applying conditional formatting with a 3-color scale or color scale icon set.",totalTime:"PT4M",tool:["Microsoft Excel"],step:[{"@type":"HowToStep",position:1,name:"Lay out the matrix",text:"Put row labels in column A, column labels in row 1, and numeric values in the inner cells."},{"@type":"HowToStep",position:2,name:"Select the value range",text:"Highlight only the inner numeric cells — not the row or column headers."},{"@type":"HowToStep",position:3,name:"Apply a color scale",text:"Open Home → Conditional Formatting → Color Scales and pick a 3-color or 2-color scale (sequential for magnitudes, diverging for signed values)."},{"@type":"HowToStep",position:4,name:"Polish",text:"Reduce row height and column width to make the cells more square, hide cell values if you only want the pattern, and add a manual legend below the matrix."}]},c=[{title:"Pick the question the matrix has to answer",description:"A heatmap answers “where are the high and low values across these two dimensions, and what pattern do they form?” Write that question down. If you actually need precise per-cell values, a sortable table is better; if your data is one-dimensional, use a bar chart."},{title:"Reshape the data into a 2D matrix",description:"Pivot your long-format data into a wide matrix with one row per row-category, one column per column-category, and one numeric value per cell. Decide how to fill in missing cells (NA, zero, the row mean) before plotting — different fills tell different stories."},{title:"Choose sequential vs diverging",description:"If values run from low to high with no meaningful zero, use a sequential palette (light→dark in one hue: Viridis, Blues). If values are signed around a meaningful zero (correlation, percent change, deviation from mean), use a diverging palette (two hues meeting at a neutral midpoint: RdBu, BrBG)."},{title:"Pick a perceptually uniform palette",description:"Use Viridis, Cividis, Magma, Inferno, or any ColorBrewer scheme. Avoid jet, rainbow, hsv, and spectral — they are not perceptually uniform and will distort your data. Cividis is the safest default for colorblind audiences while still looking attractive."},{title:"Set the color domain explicitly",description:"Don’t let the library auto-scale silently. Decide whether the domain is [0, max], [min, max], or fixed across multiple panels. For a diverging scale, anch
1or the midpoint to zero and make the domain symmetric (e.g. [-1, +1]) so reds and blues are comparable."},{title:"Sort or cluster the rows and columns",description:"Random row order makes a heatmap look like noise. Sort rows by value, by a meaningful key (date, magnitude), or by hierarchical clustering. Re-order columns the same way. Block patterns, diagonal stripes, and clusters only emerge after sorting."},{title:"Decide whether to label cells",description:"If the grid is smaller than ~50 cells, print the numeric value inside each cell with text contrast that flips between dark and light against the cell color. Past 50 cells, hide the labels and lean on the legend; past 200 cells, hide the gridlines too."},{title:"Add a clear legend with units",description:"Every heatmap needs a labeled color bar. State the units (“correlation r”, “sessions per hour”, “%”), show the numeric range, and use 5–7 visible tick marks. Position the legend close to the plot, not floating in a corner."},{title:"Write a takeaway title and ship",description:"“Traffic by day and hour” is a label. “Weekday lunchtime drives our peak traffic — weekend evenings barely register” is a takeaway. Lead with the takeaway, put the descriptive label as a subtitle, and verify the chart still works at the size your readers will see it."}],C=x({slug:"/heatmap",id:"howto-steps",name:"How to make a heatmap",description:"Step-by-step guide to designing and shipping a heatmap that communicates your data clearly.",steps:c.map(t=>({name:t.title,text:String(t.description)}))}),D={"@context":"https://schema.org","@graph":[{"@type":"WebPage","@id":`${i}/heatmap/#webpage`,url:`${i}/heatmap`,name:"Heatmap — The Definitive Guide",description:"The complete reference guide to heatmaps: definition, anatomy, when to use, accessibility, code samples in Python, R and JavaScript, tool-specific instructions, and the most common comparisons.",isPartOf:{"@id":`${i}/#website`},breadcrumb:{"@id":`${i}/heatmap/#breadcrumb`},mainEntity:{"@id":`${i}/heatmap/#article`},inLanguage:"en"},h({slug:"/heatmap",caption:"Example heatmap",width:620,height:280}),{"@type":["Article","LearningResource"],"@id":`${i}/heatmap/#article`,mainEntityOfPage:{"@id":`${i}/heatmap/#webpage`},headline:"Heatmap: Definition, Anatomy, Code, and When to Use",description:"A complete reference guide to heatmaps — what they are, how to build one in Excel, Sheets, Python, R, JavaScript, Tableau, and Power BI, when to use them, and common mistakes to avoid.",image:{"@id":m("/heatmap")},datePublished:"2025-03-15",dateModified:"2026-04-28",publisher:{"@id":`${i}/#organization`},inLanguage:"en",educationalLevel:"intermediate",learningResourceType:"reference",teaches:["How to read and interpret heatmaps","When to use heatmaps for data visualization","How to build heatmaps in Excel, Google Sheets, Python, R, JavaScript, Tableau, and Power BI","How to make heatmaps accessible to all readers","How to choose between sequential and diverging color palettes","Common heatmap mistakes and how to avoid them"],competencyRequired:"Basic understanding of data matrices and color scales",timeRequired:"PT15M",isAccessibleForFree:!0,audience:{"@type":"EducationalAudience",educationalRole:"student"},about:{"@id":`${i}/heatmap/#term`},hasPart:[{"@id":`${i}/heatmap/#howto-steps`},{"@id":`${i}/heatmap/#howto-excel`},{"@id":`${i}/heatmap/#howto-python`},{"@id":`${i}/heatmap/#code-python`},{"@id":`${i}/heatmap/#code-r`},{"@id":`${i}/heatmap/#code-js`}],keywords:["heatmap","heat map","data visualization","color encoding","matrix visualization","correlation matrix","seaborn heatmap","ggplot2 geom_tile","d3 heatmap"]},C,{"@type":"DefinedTerm","@id":`${i}/heatmap/#term`,name:"Heatmap",description:"A data visualization chart type that uses color intensity to represent values in a two-dimensional matrix. Each cell’s color corresponds to its value on a sequential or diverging color scale, making patterns and outliers immediately visible.",inDefinedTermSet:{"@id":`${i}/chart-types/#termset`},termCode:"heatmap",url:`${i}/heatmap`,sameAs:"https://en.wikipedia.org/wiki/Heat_map"},{"@type":"DefinedTermSet","@id":`${i}/chart-types/#termset`,name:"Data Visualization Chart Types",url:`${i}/chart-types`},p("/heatmap"),A,M,{"@type":"SoftwareSourceCode","@id":`${i}/heatmap/#code-python`,name:"Heatmap with seaborn",programmingLanguage:"Python",codeRepository:`${i}/heatmap/#code-examples`,codeSampleType:"code snippet",runtimePlatform:"Python 3 with seaborn and Matplotlib"},{"@type":"SoftwareSourceCode","@id":`${i}/heatmap/#code-r`,name:"Heatmap with ggplot2",programmingLanguage:"R",codeRepository:`${i}/heatmap/#code-examples`,codeSampleType:"code snippet",runtimePlatform:"R with ggplot2"},{"@type":"SoftwareSourceCode","@id":`${i}/heatmap/#code-js`,name:"Heatmap with D3.js",programmingLanguage:"JavaScript",codeRepository:`${i}/heatmap/#code-examples`,codeSampleType:"code snippet",runtimePlatform:"Browser with D3.js v7"},T]},q=[{id:"the-chart",label:"01. The chart"},{id:"what-is-it",label:"02. What is it?"},{id:"when-to-use",label:"03. When to use it"},{id:"when-not-to-use",label:"04. When not to use it"},{id:"data-requirements",label:"05. Data requirements"},{id:"anatomy",label:"06. Anatomy"},{id:"step-by-step",label:"07. Step-by-step"},{id:"real-world-examples",label:"08. Examples"},{id:"variations",label:"09. Variations"},{id:"comparisons",label:"10. Comparisons"},{id:"common-mistakes",label:"11. Common mistakes"},{id:"accessibility",label:"12. Accessibility"},{id:"best-practices",label:"13. Best practices"},{id:"related-charts",label:"14. Alternatives"},{id:"tool-instructions",label:"15. Tool instructions"},{id:"code-examples",label:"16. Code examples"},{id:"faqs",label:"17. FAQs"},{id:"references",label:"18. References"}],B=[{name:"row",type:"string / categorical",description:"The first dimension of the matrix — days of the week, gene IDs, products, customer segments, hours of the day. Each unique value becomes one row of the grid. Order is significant: re-sorting rows often reveals patterns that random order hides.",required:!0},{name:"column",type:"string / categorical",description:"The second dimension of the matrix — hours, time periods, experiment conditions, regions. Each unique value becomes one column. Like rows, the order of columns can be sorted, clustered, or kept in a natural sequence (months, ordinal scales).",required:!0},{name:"value",type:"number (continuous)",description:"The quantity each cell encodes. Must share a single unit across cells (count, percent, dollars, correlation coefficient). For diverging palettes the value should have a meaningful zero point (e.g. signed change, correlation in [-1, +1]).",required:!0},{name:"annotation",type:"string (optional)",description:"Optional cell text used to print the numeric value (or a symbol such as * for significance) directly inside the cell. Useful for small grids; turn off for grids larger than ~50 cells where text becomes unreadable."}],H=[{title:"Use a colorblind-safe, perceptually uniform palette",wcag:"1.4.1",description:"Cividis, Viridis, and ColorBrewer’s sequential and diverging schemes are perceptually uniform and have been tested against the major forms of color-vision deficiency. Avoid red–green diverging palettes (Spectral, RdYlGn) for any audie
1nce that has not been screened."},{title:"Match the palette type to the data type",wcag:"1.4.1",description:"Sequential palettes for unsigned magnitudes (counts, durations). Diverging palettes for signed values around a meaningful midpoint (correlations in [-1, +1], year-over-year change). A diverging palette on unsigned data invents a midpoint that doesn’t exist and misleads readers."},{title:"Provide a textual data table fallback",wcag:"1.1.1",description:"Place the underlying matrix in a screen-reader-friendly HTML table beneath or beside the chart, or expose it via a hidden table. Many readers cannot perceive color encoding at all and will navigate the data via the table or copy it to their own tools."},{title:"Write an aria-label that summarizes the pattern",wcag:"1.1.1",description:'Set the SVG’s role="img" and an aria-label that names the takeaway, not the chart type. “Heatmap of website traffic” is weak; “Traffic peaks Tuesday–Thursday between 10am and 2pm; weekend mornings are nearly empty” is strong.'},{title:"Meet contrast for legend text and cell labels",wcag:"1.4.3",description:"Axis text, legend ticks, and cell annotations must reach 4.5:1 contrast for body text or 3:1 for large text. When cells are labeled, switch label color between dark and light depending on the cell’s luminance so labels stay readable across the whole palette."},{title:"Encode meaning programmatically",wcag:"1.3.1",description:"Don’t encode information only in color. Add gridlines, group separators, or printed values for the most important cells so the matrix structure is conveyed in the DOM (and to assistive technology) and not only as pixels."},{title:"Make the chart resizable and zoomable",wcag:"1.4.4",description:"Render the heatmap in a responsive SVG with a viewBox so it stays legible at 200% browser zoom and on small screens. Avoid baking cell pixel sizes into the markup — the cell aspect ratio should adapt to the container."},{title:"Keep cell tooltips keyboard-reachable",wcag:"2.1.1",description:"If cells expose tooltips with the precise value, every cell should be reachable with the Tab key, the tooltip should appear on focus (not only hover), and the cell should have a visible focus ring. Tooltip text must be selectable so screen readers can read it."},{title:"Respect prefers-reduced-motion",wcag:"2.3.3",description:"If cells fade or recolor on load or on filter, gate the animation behind a prefers-reduced-motion: no-preference media query so motion-sensitive readers see the final state immediately."}],R=[{kind:"do",title:"Use Viridis, Cividis, or ColorBrewer",description:"Default to a perceptually uniform palette designed for data: Viridis or Cividis for sequential, RdBu or BrBG for diverging. They were engineered by visualization researchers specifically to keep equal data steps perceptually equal across the whole range."},{kind:"dont",title:"Use rainbow / jet / spectral palettes",description:"Rainbow palettes look colorful but are not perceptually uniform: equal steps in the data become unequal steps in the eye. They also fail badly for the ~8% of men and ~0.5% of women with color-vision deficiency. Stop using them."},{kind:"do",title:"Sort or cluster rows and columns",description:"Re-order rows and columns by value, by a meaningful key, or by hierarchical clustering. The same data with sorted rows tells a story; with random rows it looks like noise. Sorting is often the single biggest improvement you can make."},{kind:"dont",title:"Use a heatmap for small data",description:"Below about 5×5 cells a heatmap is just a confusingly colored table. Use a small-multiple bar chart, a labeled table, or a dot plot instead — they communicate the same information far more precisely."},{kind:"do",title:"Anchor diverging scales at zero",description:"When the value has a meaningful zero (correlations, signed change), set the diverging palette’s midpoint to exactly zero and make the domain symmetric (e.g. [−max, +max]) so positive and negative values are visually comparable."},{kind:"dont",title:"Hide the legend",description:"Without a labeled color bar showing units and the numeric range, the heatmap is unreadable. Always include the legend close to the plot, with 5–7 visible tick marks and a units label."},{kind:"do",title:"Provide the data as a table too",description:"Pair every heatmap with the underlying values in an accessible HTML table. Readers will copy from it for analysis, screen readers will navigate it, and it gives anyone who can’t see the colors a precise fallback."},{kind:"dont",title:"Cram thousands of unlabeled cells",description:"Past about 10,000 cells you start fighting Moiré patterns and rendering performance, and the matrix becomes impossible to navigate cell-by-cell. Down-sample, cluster, or build a focus-plus-context view instead."}],z=[{id:"vs-choropleth",heading:"Heatmap vs choropleth (geographic)",takeaway:"Both encode magnitude with color, but they live in different c
1oordinate systems. A choropleth colors geographic regions on a map; a heatmap colors cells in an abstract row×column grid. Use whichever coordinate system actually carries the meaning of your data.",left:{name:"Heatmap (row × column)",summary:"Encodes a value with color across a grid of two non-spatial dimensions (day×hour, gene×sample, product×region as labels, not as map polygons).",bullets:["Rows and columns are categorical or ordinal","Order can be sorted or clustered","No geographic interpretation"]},right:{name:"Choropleth map",summary:"Encodes a value with color across geographic polygons (countries, states, ZIPs, hex bins on a map). Spatial position is part of the meaning.",bullets:["Polygons follow real geography","Must normalize by area (rate, not count)","Often misleading without a cartogram alternative"]}},{id:"vs-scatter",heading:"Heatmap vs scatter plot (correlation)",takeaway:"When the question is “how do these two variables relate?” a scatter plot answers it precisely for a few hundred points. When the question is “how do these many variables correlate with each other?” a correlation heatmap is the right tool.",left:{name:"Heatmap (correlation matrix)",summary:"Each cell is the correlation between two variables. Color encodes r in [-1, +1], usually with a diverging palette anchored at zero. Reveals clusters of correlated variables at a glance.",bullets:["Compares many pairs at once","Loses individual point detail","Diverging palette anchored at 0"]},right:{name:"Scatter plot",summary:"Each point is one observation; position encodes two continuous variables. Reveals shape (linear, non-linear), clusters, and outliers at the row level. Best for one or a small number of variable pairs.",bullets:["Precise per-point","Hard to scan many variable pairs","Use scatter matrix for several pairs"]}},{id:"vs-table",heading:"Heatmap vs sortable data table",takeaway:"A table gives you precise per-cell values and unlimited interaction (sort, filter, copy). A heatmap gives you the gestalt — the pattern of highs and lows across the whole matrix. Pair them: heatmap above, table beneath.",left:{
1name:"Heatmap",summary:"Color encodes value, sacrificing precision for pattern recognition. Best when the audience needs to see structure, clusters, or outliers in a large matrix at once.",bullets:["Pattern-first, not value-first","Scales to thousands of cells","Color is the encoding"]},right:{name:"Sortable table",summary:"Numbers in cells, no color encoding. Best when readers will read or copy individual values, sort by columns, or filter to a subset.",bullets:["Value-first, not pattern-first","Hard to scan past ~30 rows","Always pair with a heatmap if matrix is large"]}},{id:"vs-calendar",heading:"Heatmap vs calendar heatmap",takeaway:"A calendar heatmap is a heatmap with the rows and columns fixed to days-of-week and weeks-of-year. Use it when the time dimension is the story; use a generic heatmap when rows and columns are anything else.",left:{name:"Generic heatmap",summary:"Rows and columns are arbitrary categorical or ordinal dimensions. Order can be sorted or clustered to reveal blocks.",bullets:["Flexible row/column meaning","Sorting reveals structure","Use for matrices, correlations, day×hour grids"]},right:{name:"Calendar heatmap",summary:"Rows are days of the week (or months), columns are weeks of the year. Layout mirrors a wall calendar so seasonal patterns and individual outlier days are obvious.",bullets:["Fixed calendar layout","Best for daily activity over months/years","GitHub contribution graph is the canonical example"]}}],P=[{id:"excel",tool:"Microsoft Excel",tagline:"Spreadsheet — ~4 min",steps:["Lay out the matrix with row labels in column A, column labels in row 1, and numeric values in the inner cells.","Highlight only the inner numeric cells — do not include the row or column header text.","Open Home → Conditional Formatting → Color Scales and pick a 3-color scale (sequential) or a Red–White–Blue scale (diverging) anchored at zero.","Open Manage Rules → Edit Rule and set Minimum to Number → 0 (or your true min) and Maximum to a fixed number so the scale stays comparable across panels.","Reduce row height and column width to make cells roughly square; turn off cell borders for a cleaner grid.","Cluster or sort the rows by adding a helper column with the row sum, then sorting the table by that helper before re-applying the color scale."],caption:"Tip: Excel’s default 3-color scale is green–yellow–red — swap to a colorblind-safe Red–White–Blue or White–Blue scale before sharing."},{id:"google-sheets",tool:"Google Sheets",tagline:"Spreadsheet — ~3 min",steps:["Lay out the matrix in the same shape as Excel: row labels, column labels, numeric inner cells.","Select the inner numeric range, then choose Format → Conditional formatting → Color scale.","Pick a preset (the White→Blue preset is the closest to Viridis available natively) or set custom Min/Mid/Max colors.","For a diverging palette, set Midpoint to Number → 0 and choose a neutral midpoint color so positive and negative values are visually comparable.","Resize rows and columns to be roughly square so the grid reads as a chart, not a table.","Add a manual legend in cells beneath the matrix using the same color scale on a small reference range."],caption:"Sheets has no built-in clustering. If row/column order matters, sort the helper column first, then re-apply the conditional format."},{id:"python",tool:"Python (seaborn)",tagline:"Code — ~5 min",steps:["Install the stack with pip install pandas seaborn matplotlib if it isn’t already in your environment.","Pivot your long-format data into a 2D DataFrame with df.pivot(index='row', columns='col', values='value').","Choose a palette: cmap='viridis' for sequential magnitudes, cmap='RdBu_r' centered on zero for diverging signed data.","Call sns.heatmap(matrix, cmap=..., annot=True, fmt='.0f', linewidths=0.5, cbar_kws={'label': 'units'}) for small matrices, or annot=False for large ones.","For diverging palettes, pass center=0 and vmin=-vmax, vmax=+vmax so the midpoint is anchored on zero with a symmetric domain.","Sort rows and columns by sum, by hierarchical clustering (sns.clustermap), or by a meaningful key before plotting.","Add plt.title() with a takeaway sentence, rotate ylabels with plt.yticks(rotation=0), and call plt.tight_layout() before plt.savefig()."],caption:"Use sns.clustermap() instead of sns.heatmap() to get hierarchical clustering on rows and columns for free — the dendrograms appear automatically on the margins."},{id:"r",tool:"R (ggplot2)",tagline:"Code — ~5 min",steps:["Install ggplot2 and viridisLite with install.packages(c('ggplot2', 'viridisLite')) and load them.","Reshape data into long format with three columns: row, column, value (use tidyr::pivot_longer if needed).","Build the plot with ggplot(df, aes(x = col, y = row, fill = value)) + geom_tile().","Add scale_fill_viridis_c(option = 'D', name = 'units') for sequential, or scale_fill_gradient2(low = '#2c7bb6', mid = '#ffffff', high = '#d7191c', midpoint = 0) for diverging.","Reorder factors with reorder(row, value) (or with hclust + cutree for true clustering) so blocks emerge.","Add coord_fixed() so cells are square, and theme_minimal() + theme(panel.grid = element_blank()) to drop default gridlines.","Polish with labs() for the takeaway title and axis labels, then ggsave() to a PNG or SVG."],caption:"Use the heatmaply package for an interactive ggplot-flavored heatmap with built-in row/column clustering and zoom — great for exploration before exporting a static publication chart."},{id:"javascript",tool:"JavaScript (D3.js)",tagline:"Code — ~10 min",steps:["Install D3 with npm i d3, or include the CDN script tag in your HTML.","Create an SVG container with a viewBox, set role='img', and add an aria-label that summarizes the dominant pattern.","Build d3.scaleBand() for the row and column axes, padded slightly so cell borders are visible.","Build the color scale with d3.scaleSequential(d3.interpolateViridis) for sequential or d3.scaleDiverging(d3.interpolateRdBu) anchored at zero.","Bind data with svg.selectAll('rect').data(data).join('rect') and set x, y, width, height from the band scales and fill from the color scale.","Render the axes with d3.axisBottom() and d3.axisLeft(), and append a color-bar legend using d3-color-legend or by stamping a gradient <rect>
1 with tick text.","For interactivity, add focus rings, keyboard navigation across cells, and tooltips that appear on focus (not only hover) so the chart works for keyboard and screen-reader users."],caption:"If you don’t need full control, Observable Plot’s Plot.cell() mark gets you to a publication-quality D3 heatmap in about 10 lines."},{id:"tableau",tool:"Tableau",tagline:"BI — ~4 min",steps:["Drag the row dimension to Rows and the column dimension to Columns. Drag the value measure to the Color shelf inside the Marks card.","Change the Marks type to Square so the cells fill the grid (rather than the default circles).","Click the Color shelf and choose Edit Colors. Pick the Viridis palette (or Blue–Teal Sequential) for sequential data, or Red–Blue Diverging anchored at zero for signed data.","Tick “Use Full Color Range” and, for diverging palettes, set the Center value to 0 to keep red and blue comparable.","Drag the value measure onto Label so each cell prints its value, then format the label so dark-cell text is white and light-cell text is dark.","Sort rows and columns by total or by a chosen field (right-click → Sort) so blocks of similar cells emerge instead of random colors."],caption:"Tableau’s default “Automatic” color scale is Orange–Blue diverging — it’s pretty but it implies a midpoint even when your data has none. Switch to a sequential palette when in doubt."},{id:"power-bi",tool:"Power BI",tagline:"BI — ~4 min",steps:["Use the built-in Matrix visual: drag the row dimension to Rows, the column dimension to Columns, and the value to Values.","In the Format pane, expand Cell elements, set Apply to values = the measure, and toggle Background color on.","Click the fx button next to Background color, choose Format style = Gradient, and set Minimum, Center (for diverging), and Maximum colors using a colorblind-safe palette.","For diverging palettes set Center to a fixed Number = 0 so positive and negative values are visually comparable across slicers.","Reduce the column width and row height to make cells more square, and toggle off totals if they would dwarf the per-cell color scale.","For richer heatmaps, install the Custom Visual “Heatmap” or “Correlation plot” from AppSource — they support clustering and dedicated legends out of the box."],caption:"Power BI’s default conditional formatting palette is red–yellow–green — the worst possible choice for colorblind users. Always swap to Cividis or a Red–White–Blue diverging scheme."}],O=[{id:"python",language:"python",label:"Python — seaborn",filename:"heatmap.py",runner:"python heatmap.py",code:`import numpy as np
2import pandas as pd
3import seaborn as sns
4import matplotlib.pyplot as plt
5
6# Hours of the day x days of the week, with synthetic traffic counts.
7days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
8hours = ["6am", "8am", "10am", "12pm", "2pm", "4pm", "6pm", "8pm", "10pm"]
9
10# rows = days, cols = hours, values = sessions
11values = np.array([
12    [ 40, 220, 880, 940, 820, 360, 120,  90,  40],
13    [ 60, 360, 920, 1180, 980, 540, 200, 110,  50],
14    [ 50, 280, 860, 1020, 940, 420, 230, 120,  60],
15    [ 30, 300, 880, 1080, 920, 580, 260, 130,  70],
16    [ 50, 320, 720,  920, 720, 420, 320, 200,  90],
17    [ 30,  80, 200,  340, 220, 180, 360, 540, 280],
18    [ 30,  60, 180,  220, 200, 160, 280, 480, 200],
19])
20
21matrix = pd.DataFrame(values, index=days, columns=hours)
22
23fig, ax = plt.subplots(figsize=(9, 4.5))
24sns.heatmap(
25    matrix,
26    cmap="viridis",            # perceptually uniform sequential palette
27    linewidths=0.5,
28    linecolor="#fff",
29    annot=False,
30    cbar_kws={"label": "Sessions"},
31    ax=ax,
32)
33ax.set_title("Weekday lunchtime drives our peak traffic", loc="left", fontsize=14)
34ax.set_xlabel("Hour of day")
35ax.set_ylabel("Day of week")
36plt.yticks(rotation=0)
37plt.tight_layout()
38plt.savefig("heatmap.png", dpi=200)
39plt.show()
40`},{id:"r",language:"r",label:"R — ggplot2",filename:"heatmap.R",runner:"Rscript heatmap.R",code:`library(ggplot2)
41library(viridisLite)
42library(tidyr)
43
44days  <- c("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")
45hours <- c("6am", "8am", "10am", "12pm", "2pm", "4pm", "6pm", "8pm", "10pm")
46
47values <- matrix(c(
48   40, 220, 880,  940, 820, 360, 120,  90,  40,
49   60, 360, 920, 1180, 980, 540, 200, 110,  50,
50   50, 280, 860, 1020, 940, 420, 230, 120,  60,
51   30, 300, 880, 1080, 920, 580, 260, 130,  70,
52   50, 320, 720,  920, 720, 420, 320, 200,  90,
53   30,  80, 200,  340, 220, 180, 360, 540, 280,
54   30,  60, 180,  220, 200, 160, 280, 480, 200
55), nrow = 7, byrow = TRUE)
56
57df <- as.data.frame(values)
58colnames(df) <- hours
59df$day <- factor(days, levels = days)
60
61long <- pivot_longer(df, -day, names_to = "hour", values_to = "sessions")
62long$hour <- factor(long$hour, levels = hours)
63
64ggplot(long, aes(x = hour, y = day, fill = sessions)) +
65  geom_tile(colour = "white", linewidth = 0.6) +
66  scale_fill_viridis_c(option = "D", name = "Sessions") +
67  coord_fixed() +
68  labs(title = "Weekday lunchtime drives our peak traffic",
69       subtitle = "Sessions per hour, last 7 days",
70       x = "Hour of day", y = NULL) +
71  theme_minimal(base_size = 12) +
72  theme(panel.grid = element_blank(),
73        plot.title.position = "plot")
74`},{id:"js",language:"javascript",label:"JavaScript — D3.js",filename:"heatmap.js",runner:"open index.html",code:`import * as d3 from "d3";
75
76const days  = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
77const hours = ["6am", "8am", "10am", "12pm", "2pm", "4pm", "6pm", "8pm", "10pm"];
78
79const raw = [
80  [ 40, 220, 880,  940, 820, 360, 120,  90,  40],
81  [ 60, 360, 920, 1180, 980, 540, 200, 110,  50],
82  [ 50, 280, 860, 1020, 940, 420, 230, 120,  60],
83  [ 30, 300, 880, 1080, 920, 580, 260, 130,  70],
84  [ 50, 320, 720,  920, 720, 420, 320, 200,  90],
85  [ 30,  80, 200,  340, 220, 180, 360, 540, 280],
86  [ 30,  60, 180,  220, 200, 160, 280, 480, 200],
87];
88
89const data = raw.flatMap((row, i) =>
90  row.map((value, j) => ({ day: days[i], hour: hours[j], value }))
91);
92
93const width = 720;
94const height = 320;
95const margin = { top: 36, right: 24, bottom: 32, left: 56 };
96
97const svg = d3.select("#chart")
98  .append("svg")
99  .attr("viewBox", \`0 0 \${width} \${height}\`)
100  .attr("role", "img")
101  .attr("aria-label",
102    "Heatmap of website sessions by day and hour: weekday traffic peaks Tuesday through Thursday around noon; weekend mornings are quietest.");
103
104const x = d3.scaleBand().domain(hours).range([margin.left, width - margin.right]).padding(0.05);
105const y = d3.scaleBand().domain(days).range([margin.top, height - margin.bottom]).padding(0.05);
106
107const color = d3.scaleSequential(d3.interpolateViridis)
108  .domain([0, d3.max(data, d => d.value)]);
109
110svg.append("g").selectAll("rect")
111  .data(data).join("rect")
112    .attr("x", d => x(d.hour))
113    .attr("y", d => y(d.day))
114    .attr("width", x.bandwidth())
115    .attr("height", y.bandwidth())
116    .attr("fill", d => color(d.value))
117    .attr("rx", 2)
118    .append("title")
119      .text(d => \`\${d.day} \${d.hour}: \${d.value.toLocaleString()} sessions\`);
120
121svg.append("g")
122    .attr("transform", \`translate(0,\${margin.top})\`)
123    .call(d3.axisTop(x).tickSizeOuter(0));
124
125svg.append("g")
126    .attr("transform", \`translate(\${margin.left},0)\`)
127    .call(d3.axisLeft(y).tickSizeOuter(0));
128`}],F=[{source:"Wikipedia — Heat map",href:"https://en.wikipedia.org/wiki/Heat_map",description:"Encyclopedia entry covering the history, variants, and visual encoding of heatmaps. A solid neutral starting point with citations to primary sources.",kind:"Reference"},{source:"Toussaint Loua — Atlas statistique de la population de Paris (1873)",href:"https://gallica.bnf.fr/ark:/12148/bpt6k844290",description:"Loua’s 1873 statistical atlas, where shaded matrices to encode social data first appeared. Hosted by the Bibliothèque nationale de France.",kind:"Primary source"},{source:"Cynthia Brewer — ColorBrewer 2.0",href:"https://colorbrewer2.org/",description:"Cartographer Cynthia Brewer’s online tool for choosing colorblind-safe sequential, diverging, and qualitative palettes. The de facto standard for choropleth and heatmap palettes.",kind:"Tool"},{source:"Stefan van der Walt & Nathaniel Smith — A Better Default Colormap for Matplotlib (Viridis, SciPy 2015)",href:"https://bids.github.io/colormap/",description:"The talk and write-up that introduced the Viridis family of perceptually uniform colormaps and explained why jet is so harmful for data visualization.",kind:"Primary source"},{source:"Datawrapper Academy — What to consider when creating a heatmap",href:"https://academy.datawrapper.de/article/197-how-to-create-a-heatmap-in-datawrapper",description:"Hands-on tutorial with real published examples. Especially useful for sequential vs diverging palette choice and for handling missing cells.",kind:"Tutorial"},{source:"Edward Tufte — The Visual Display of Quantitative Information",href:"https://www.edwardtufte.com/book/the-visual-display-of-quantitative-information/",description:"Tufte’s foun
128dational text on data graphics. The chapters on data-ink ratio and small multiples explain why a sorted heatmap of small multiples often beats a single dense matrix.",kind:"Book"},{source:"Financial Times — Visual Vocabulary",href:"https://github.com/Financial-Times/chart-doctor/tree/main/visual-vocabulary",description:"Open-source poster categorizing chart types by intent. Heatmaps appear in both the Magnitude and Correlation families.",kind:"Reference"},{source:"WAI — Complex Images: Charts and Graphs",href:"https://www.w3.org/WAI/tutorials/images/complex/",description:"Web Accessibility Initiative guidance on making charts accessible: text alternatives, long descriptions, and data tables. Use this when building the accessibility checklist for your heatmap.",kind:"Accessibility"},{source:"seaborn documentation — sns.heatmap",href:"https://seaborn.pydata.org/generated/seaborn.heatmap.html",description:"Official API reference for seaborn’s heatmap helper used in this guide’s Python sample. Includes guidance on cmap, center, vmin/vmax and annotations.",kind:"Docs"},{source:"ggplot2 reference — geom_tile",href:"https://ggplot2.tidyverse.org/reference/geom_tile.html",description:"Tidyverse documentation for the ggplot2 tile geometry. The standard way to draw a heatmap in R.",kind:"Docs"},{source:"Observable D3 — Heatmap",href:"https://observablehq.com/@d3/heatmap",description:"Maintained Observable notebook from the D3 community that mirrors the JavaScript code sample in this guide.",kind:"Docs"},{source:"Mike Bostock — Calendar View",href:"https://observablehq.com/@d3/calendar-view",description:"The classic D3 calendar heatmap notebook. Useful template for adapting heatmaps to time-series data with year/month/week structure.",kind:"Tutorial"}];function _(){return e.jsxs(e.Fragment,{children:[e.jsx(d,{title:"Heatmap: Definition, Best Practices & Examples",description:"Use a heatmap to compare data clearly: definition, when to use it, real-world examples, common mistakes, and tools (Excel, D3, Tableau).",path:"/heatmap",ogType:"article",jsonLd:[D]}),e.jsx(u,{slug:"/heatmap"}),e.jsxs("div",{className:"max-w-[1100px] mx-auto px-6 md:px-12",children:[e.jsxs("div",{className:"py-[56px] pb-[48px] border-b border-[var(--border-color)]",children:[e.jsxs("div",{className:"flex items-center gap-3 mb-5",children:[e.jsx("span",{className:"font-mono text-[10px] tracking-[0.12em] uppercase bg-[var(--accent-light)] text-[var(--accent-color)] px-[10px] py-[4px] rounded-[3px] font-medium",children:"Magnitude"}),e.jsx("span",{className:"font-mono text-[10px] tracking-[0.10em] uppercase bg-[var(--bg-alt)] text-[var(--ink-muted)] px-[10px] py-[4px] rounded-[3px]",children:"Intermediate"})]}),e.jsx("h1",{className:"font-serif text-[42px] md:text-[52px] font-normal tracking-[-0.025em] leading-[1.1] text-[var(--ink)] mb-4",children:"Heatmap"}),e.jsx("p",{className:"text-[17px] md:text-[19px] font-light text-[var(--ink-muted)] leading-[1.6] max-w-[680px]",children:"A grid of colored cells where color intensity encodes magnitude. Heatmaps turn dense data matrices into instantly scannable patterns — the darker the cell, the higher the value — and have been used to spot structure in everything from social statistics to gene expression."})]}),e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-[1fr_280px] gap-16 pt-12 items-start",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsxs("section",{className:"py-14 border-b border-[var(--border-color)]",id:"the-chart",children:[e.jsx(o,{number:"01",label:"The chart",title:"What it looks like"}),e.jsxs("div",{className:"bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[12px] p-6 md:p-10 mt-8 mb-2",children:[e.jsxs("div",{className:"font-mono text-[10px] tracking-[0.10em] uppercase text-[var(--ink-faint)] mb-6 flex justify-between items-center",children:[e.jsxs("span",{children:["Example — ",e.jsx("em",{className:"not-italic text-[var(--accent-color)] font-medium",children:"Website traffic by day & hour"})]}),e.jsx("span",{children:"One week"})]}),e.jsxs("svg",{viewBox:"0 0 620 320",xmlns:"http://www.w3.org/2000/svg",className:"w-full block",role:"img","aria-label":"Heatmap of website sessions by day and hour: weekday traffic peaks Tuesday through Thursday around noon, with a darker red cell at Tuesday 12pm; weekend mornings are quietest.",children:[e.jsx("text",{x:"62",y:"48",textAnchor:"end",fill:"#6b6b67",fontSize:"11",fontFamily:"DM Mono, monospace",children:"Mon"}),e.jsx("text",{x:"62",y:"88",textAnchor:"end",fill:"#6b6b67",fontSize:"11",fontFamily:"DM Mono, monospace",children:"Tue"}),e.jsx("text",{x:"62",y:"128",textAnchor:"end",fill:"#6b6b67",fontSize:"11",fontFamily:"DM Mono, monospace",children:"Wed"}),e.jsx("text",{x:"62",y:"168",textAnchor:"end",fill:"#6b6b67",fontSize:"11",fontFamily:"DM Mono, monospace",children:"Thu"}),e.jsx("text",{x:"62",y:"208",textAnchor:"end",fill:"#6b6b67",fontSize:"11",fontFamily:"DM Mono, monospace",children:"Fri"}),e.jsx("text",{x:"62",y:"248",textAnchor:"end",fill:"#6b6b67",fontSize:"11",fontFamily:"DM Mono, monospace",children:"Sat"}),e.jsx("text",{x:"62",y:"288",textAnchor:"end",fill:"#6b6b67",fontSize:"11",fontFamily:"DM Mono, monospace",children:"Sun"}),e.jsx("text",{x:"100",y:"20",textAnchor:"middle",fill:"#b0b0aa",fontSize:"10",fontFamily:"DM Mono, monospace",children:"6am"}),e.jsx("text",{x:"147",y:"20",textAnchor:"middle",fill:"#b0b0aa",fontSize:"10",fontFamily:"DM Mono, monospace",children:"8am"}),e.jsx("text",{x:"194",y:"20",textAnchor:"middle",fill:"#b0b0aa",fontSize:"10",fontFamily:"DM Mono, monospace",children:"10am"}),e.jsx("text",{x:"241",y:"20",textAnchor:"middle",fill:"#b0b0aa",fontSize:"10",fontFamily:"DM Mono, monospace",children:"12pm"}),e.jsx("text",{x:"288",y:"20",textAnchor:"middle",fill:"#b0b0aa",fontSize:"10",fontFamily:"DM Mono, monospace",children:"2pm"}),e.jsx("text",{x:"335",y:"20",textAnchor:"middle",fill:"#b0b0aa",fontSize:"10",fontFamily:"DM Mono, monospace",children:"4pm"}),e.jsx("text",{x:"382",y:"20",textAnchor:"middle",fill:"#b0b0aa",fontSize:"10",fontFamily:"DM Mono, monospace",children:"6pm"}),e.jsx("text",{x:"429",y:"20",textAnchor:"middle",fill:"#b0b0aa",fontSize:"10",fontFamily:"DM Mono, monospace",children:"8pm"}),e.jsx("text",{x:"476",y:"20",textAnchor:"middle",fill:"#b0b0aa",fontSize:"10",fontFamily:"DM Mono, monospace",children:"10pm"}),e.jsx("rect",{x:"78",y:"30",width:"44",height:"32",rx:"3",fill:"#f5ede9"}),e.jsx("rect",{x:"125",y:"30",width:"44",height:"32",rx:"3",fill:"#e8c4b8"}),e.jsx("rect",{x:"172",y:"30",width:"44",height:"32",rx:"3",fill:"#c94a2e"}),e.jsx("rect",{x:"219",y:"30",width:"44",height:"32",rx:"3",fill:"#c94a2e"}),e.jsx("rect",{x:"266",y:"30",width:"44",height:"32",rx:"3",fill:"#c94a2e",fillOpacity:"0.85"}),e.jsx("rect",{x:"313",y:"30",width:"44",height:"32",rx:"3",fill:"#e8c4b8"}),e.jsx("rect",{x:"360",y:"30",width:"44",height:"32",rx:"3",fill:"#f5ede9"}),e.jsx("rect",{x:"407",y:"30",width:"44",height:"32",rx:"3",fill:"#f5ede9"}),e.jsx("rect",{x:"454",y:"30",width:"44",height:"32",rx:"3",fill:"#f5ede9",fillOpacity:"0.6"}),e.jsx("rect",{x:"78",y:"70",width:"44",height:"32",rx:"3",fill:"#f5ede9"}),e.jsx("rect",{x:"125",y:"70",width:"44",height:"32",rx:"3",fill:"#c94a2e",fillOpacity:"0.7"}),e.jsx("rect",{x:"172",y:"70",width:"44",height:"32",rx:"3",fill:"#c94a2e"}),e.jsx("rect",{x:"219",y:"70",width:"44",height:"32",rx:"3",fill:"#9e3522"}),e.jsx("rect",{x:"266",y:"70",width:"44",height:"32",rx:"3",fill:"#c94a2e"}),e.jsx("rect",{x:"313",y:"70",width:"44",height:"32",rx:"3",fill:"#c94a2e",fillOpacity:"0.7"}),e.jsx("rect",{x:"360",y:"70",width:"44",height:"32",rx:"3",fill:"#e8c4b8"}),e.jsx("rect",{x:"407",y:"70",width:"44",height:"32",rx:"3",fill:"#f5ede9"}),e.jsx("rect",{x:"454",y:"70",width:"44",height:"32",rx:"3",fill:"#f5ede9",fillOpacity:"0.6"}),e.jsx("rect",{x:"78",y:"110",width:"44",height:"32",rx:"3",fill:"#f5ede9"}),e.jsx("rect",{x:"125",y:"110",width:"44",height:"32",rx:"3",fill:"#e8c4b8"}),e.jsx("rect",{x:"172",y:"110",width:"44",height:"32",rx:"3",fill:"#c94a2e",fillOpacity:"0.85"}),e.jsx("rect",{x:"219",y:"110",width:"44",height:"32",rx:"3",fill:"#c94a2e"}),e.jsx("rect",{x:"266",y:"110",width:"44",height:"32",rx:"3",fill:"#c94a2e"}),e.jsx("rect",{x:"313",y:"110",width:"44",height:"32",rx:"3",fill:"#e8c4b8"}),e.jsx("rect",{x:"360",y:"110",width:"44",height:"32",rx:"3",fill:"#e8c4b8"}),e.jsx("rect",{x:"407",y:"110",width:"44",height:"32",rx:"3",fill:"#f5ede9"}),e.jsx("rect",{x:"454",y:"110",width:"44",height:"32",rx:"3",fill:"#f5ede9",fillOpacity:"0.6"}),e.jsx("rect",{x:"78",y:"150",width:"44",height:"32",rx:"3",fill:"#f5ede9",fillOpacity:"0.6"}),e.jsx("rect",{x:"125",y:"150",width:"44",height:"32",rx:"3",fill:"#e8c4b8"}),e.jsx("rect",{x:"172",y:"150",width:"44",height:"32",rx:"3",fill:"#c94a2e"}),e.jsx("rect",{x:"219",y:"150",width:"44",height:"32",rx:"3",fill:"#c94a2e"}),e.jsx("rect",{x:"266",y:"150",width:"44",height:"32",rx:"3",fill:"#c94a2e",fillOpacity:"0.85"}),e.jsx("rect",{x:"313",y:"150",width:"44",height:"32",rx:"3",fill:"#c94a2e",fillOpacity:"0.7"}),e.jsx("rect",{x:"360",y:"150",width:"44",height:"32",rx:"3",fill:"#e8c4b8"}),e.jsx("rect",{x:"407",y:"150",width:"44",height:"32",rx:"3",fill:"#f5ede9"}),e.jsx("rect",{x:"454",y:"150",width:"44",height:"32",rx:"3",fill:"#f5ede9",fillOpacity:"0.6"}),e.jsx("rect",{x:"78",y:"190",width:"44",height:"32",rx:"3",fill:"#f5ede9"}),e.jsx("rect",{x:"125",y:"190",width:"44",height:"32",rx:"3",fill:"#e8c4b8"}),e.jsx("rect",{x:"172",y:"190",width:"44",height:"32",rx:"3",fill:"#c94a2e",fillOpacity:"0.7"}),e.jsx("rect",{x:"219",y:"190",width:"44",height:"32",rx:"3",fill:"#c94a2e",fillOpacity:"0.85"}),e.jsx("rect",{x:"266",y:"190",width:"44",height:"32",rx:"3",fill:"#c94a2e",fillOpacity:"0.7"}),e.jsx("rect",{x:"313",y:"190",width:"44",height:"32",rx:"3",fill:"#e8c4b8"}),e.jsx("rect",{x:"360",y:"190",width:"44",height:"32",rx:"3",fill:"#e8c4b8"}),e.jsx("rect",{x:"407",y:"190",width:"44",height:"32",rx:"3",fill:"#e8c4b8"}),e.jsx("rect",{x:"454",y:"190",width:"44",height:"32",rx:"3",fill:"#f5ede9"}),e.jsx("rect",{x:"78",y:"230",width:"44",height:"32",rx:"3",fill:"#f5ede9",fillOpacity:"0.6"}),e.jsx("rect",{x:"125",y:"230",width:"44",height:"32",rx:"3",fill:"#f5ede9",fillOpacity:"0.6"}),e.jsx("rect",{x:"172",y:"230",width:"44",height:"32",rx:"3",fill:"#f5ede9"}),e.jsx("rect",{x:"219",y:"230",width:"44",height:"32",rx:"3",fill:"#e8c4b8"}),e.jsx("rect",{x:"266",y:"230",width:"44",height:"32",rx:"3",fill:"#f5ede9"}),e.jsx("rect",{x:"313",y:"230",width:"44",height:"32",rx:"3",fill:"#f5ede9"}),e.jsx("rect",{x:"360",y:"230",width:"44",height:"32",rx:"3",fill:"#e8c4b8"}),e.jsx("rect",{x:"407",y:"230",width:"44",height:"32",rx:"3",fill:"#c94a2e",fillOpacity:"0.7"}),e.jsx("rect",{x:"454",y:"230",width:"44",height:"32",rx:"3",fill:"#e8c4b8"}),e.jsx("rect",{x:"78",y:"270",width:"44",height:"32",rx:"3",fill:"#f5ede9",fillOpacity:"0.6"}),e.jsx("rect",{x:"125",y:"270",width:"44",height:"32",rx:"3",fill:"#f5ede9",fillOpacity:"0.6"}),e.jsx("rect",{x:"172",y:"270",width:"44",height:"32",rx:"3",fill:"#f5ede9"}),e.jsx("rect",{x:"219",y:"270",width:"44",height:"32",rx:"3",fill:"#f5ede9"}),e.jsx("rect",{x:"266",y:"270",width:"44",height:"32",rx:"3",fill:"#f5ede9"}),e.jsx("rect",{x:"313",y:"270",width:"44",height:"32",rx:"3",fill:"#f5ede9"}),e.jsx("rect",{x:"360",y:"270",width:"44",height:"32",rx:"3",fill:"#e8c4b8"}),e.jsx("rect",{x:"407",y:"270",width:"44",height:"32",rx:"3",fill:"#c94a2e",fillOpacity:"0.5"}),e.jsx("rect",{x:"454",y:"270",width:"44",height:"32",rx:"3",fill:"#f5ede9"}),e.jsx("rect",{x:"219",y:"70",width:"44",height:"32",rx:"3",fill:"none",stroke:"#1a1a18",strokeWidth:"2"}),e.jsx("line",{x1:"270",y1:"78",x2:"510",y2:"50",stroke:"#c94a2e",strokeWidth:"1",strokeDasharray:"3,2"}),e.jsx("rect",{x:"512",y:"38",width:"92",height:"22",rx:"3",fill:"#c94a2e"}),e.jsx("text",{x:"558",y:"53",textAnchor:"middle",fill:"#fff",fontSize:"10",fontFamily:"DM Mono, monospace",children:"Peak: Tue 12pm"}),e.jsx("text",{x:"78",y:"318",fill:"#b0b0aa",fontSize:"10",fontFamily:"DM Mono, monospace",children:"Low"}),e.jsx("rect",{x:"100",y:"308",width:"20",height:"12",rx:"2",fill:"#f5ede9",fillOpacity:"0.6"}),e.jsx("rect",{x:"122",y:"308",width:"20",height:"12",rx:"2",fill:"#f5ede9"}),e.jsx("rect",{x:"144",y:"308",width:"20",height:"12",rx:"2",fill:"#e8c4b8"}),e.jsx("rect",{x:"166",y:"308",width:"20",height:"12",rx:"2",fill:"#c94a2e",fillOpacity:"0.7"}),e.jsx("rect",{x:"188",y:"308",width:"20",height:"12",rx:"2",fill:"#c94a2e"}),e.jsx("rect",{x:"210",y:"308",width:"20",height:"12",rx:"2",fill:"#9e3522"}),e.jsx("text",{x:"240",y:"318",fill:"#b0b0aa",fontSize:"10",fontFamily:"DM Mono, monospace",children:"High"})]}),e.jsx("p",{className:"text-[13px] text-[var(--ink-muted)] text-center italic mt-4",children:"A heatmap showing website traffic patterns across days and hours. Darker cells indicate higher traffic — the pattern clearly shows weekday business hours as the peak period."})]})]}),e.jsxs("section",{className:"py-14 border-b border-[var(--border-color)]",id:"what-is-it",children:[e.jsx(o,{number:"02",label:"Definition",title:"What is a heatmap?"}),e.jsxs("div",{className:"text-[16px] font-light text-[var(--ink)] leading-[1.75]",children:[e.jsxs("p",{children:["A heatmap is a data visualization that represents values as ",e.jsx("strong",{className:"font-medium",children:"colors in a two-dimensional grid"}),". Each cell in the grid corresponds to a combination of two categorical or ordinal variables (rows and columns), and the cell’s color encodes the magnitude of a third, quantitative variable on a sequential or diverging color scale. The result is a matrix where the structure of the data — cluster
128s, gradients, outliers, blocks — jumps out at a glance even when there are thousands of cells."]}),e.jsxs("p",{className:"mt-[18px]",children:["The power of a heatmap lies in ",e.jsx("strong",{className:"font-medium",children:"pattern recognition at scale"}),". The human visual system is unusually good at picking out regions of consistent color and noticing where they break, but unusually bad at reading exact values from color. Heatmaps lean into that asymmetry: they sacrifice per-cell precision (color is less accurate than position or length) in exchange for the ability to see the whole matrix at once. A dark patch jumps out immediately; the same extreme value buried in row 47, column 12 of a spreadsheet does not."]}),e.jsxs("p",{className:"mt-[18px]",children:["Two design choices dominate every heatmap. The first is the ",e.jsx("strong",{className:"font-medium",children:"palette"}),": sequential (light to dark in a single hue) for unsigned magnitudes, or diverging (two hues meeting at a neutral midpoint) for signed values around a meaningful zero such as a correlation coefficient or year-over-year change. The second is the ",e.jsx("strong",{className:"font-medium",children:"row and column order"}),": random order looks like noise, but sorting by value, by a meaningful key, or by hierarchical clustering can transform the same data into clearly readable blocks and stripes."]}),e.jsxs("p",{className:"mt-[18px]",children:["Heatmaps live in two related families. As a ",e.jsx("strong",{className:"font-medium",children:"Magnitude"})," chart they are an alternative to bar charts and stacked bars when the data has two categorical dimensions. As a ",e.jsx("strong",{className:"font-medium",children:"Correlation"})," chart they are the standard way to display correlation matrices, distance matrices, and confusion matrices. The rest of this guide is about how to live in the heatmap’s sweet spot — and how to recognize when a sortable table, a small multiple of bar charts, or a choropleth map would do the job better."]})]}),e.jsx("div",{className:"bg-[var(--accent-light)] border-l-[3px] border-l-[var(--accent-color)] rounded-r-[8px] px-5 py-4 mt-6",children:e.jsxs("p",{className:"text-[14px] text-[var(--ink)] leading-[1.65] font-light",children:[e.jsx("strong",{className:"font-medium text-[var(--accent-color)]",children:"Origin:"})," The idea of using shading to encode values in a matrix dates back to French statistician Toussaint Loua’s 1873 ",e.jsx("em",{children:"Atlas statistique de la population de Paris"}),", which used shaded cells to compare social statistics across Parisian arrondissements. The modern term “heat map” was trademarked by software designer Cormac Kinney in 1991 to describe a real-time financial market display. Heatmaps then exploded in genomics in the 1990s with hierarchical clustering of gene-expression matrices, and today they are everywhere from GitHub contribution graphs to UX click maps."]})})]}),e.jsxs("section",{className:"py-14 border-b border-[var(--border-color)]",id:"when-to-use",children:[e.jsx(o,{number:"03",label:"When to use",title:"When a heatmap is the right call",intro:"Reach for a heatmap when the question is about pattern across two dimensions and the answer is encoded in a third numeric value per cell. Below are the situations where a heatmap consistently wins against the alternatives."}),e.jsxs("div",{className:"bg-[var(--green-light)] border border-[var(--green-border)] rounded-[10px] p-6 mt-7",children:[e.jsxs("div",{className:"flex items-center gap-2 font-medium text-[14px] text-[var(--green)] mb-4",children:[e.jsx("span",{className:"w-[22px] h-[22px] rounded-full bg-[var(--green)] text-white flex items-center justify-center text-[12px] shrink-0",children:"&check;"}),"Use a heatmap when…"]}),e.jsx("ul",{className:"flex flex-col gap-[10px] list-none",children:["You have a dense matrix (dozens to thousands of cells) and want readers to spot patterns and clusters","Showing a correlation matrix between many variables in a single chart","Displaying activity patterns across two dimensions (day × hour, product × region, week × day-of-week)","Visualizing genomics, machine-learning confusion matrices, or other naturally matrix-shaped data","The audience needs to see the gestalt rather than every exact value (they can hover or read a table for precision)","Rows and columns can be sorted or clustered to reveal block structure","You want a layout that scales from a small dashboard t
128ile to a wall-sized poster without redesign"].map((t,a)=>e.jsx("li",{className:"text-[14px] text-[var(--ink)] leading-[1.5] pl-4 relative font-light before:content-['—'] before:absolute before:left-0 before:text-[var(--ink-faint)]",children:t},a))})]})]}),e.jsxs("section",{className:"py-14 border-b border-[var(--border-color)]",id:"when-not-to-use",children:[e.jsx(o,{number:"04",label:"When not to use",title:"When a heatmap is the wrong call",intro:"Heatmaps can technically display almost any 2D matrix, but “technically possible” is not the same as “good idea.” Below are the cases where a heatmap actively hides information you need to communicate."}),e.jsxs("div",{className:"bg-[var(--red-light)] border border-[var(--red-border)] rounded-[10px] p-6 mt-7",children:[e.jsxs("div",{className:"flex items-center gap-2 font-medium text-[14px] text-[var(--red)] mb-4",children:[e.jsx("span",{className:"w-[22px] h-[22px] rounded-full bg-[var(--red)] text-white flex items-center justify-center text-[12px] shrink-0",children:"×"}),"Avoid a heatmap when…"]}),e.jsx("ul",{className:"flex flex-col gap-[10px] list-none",children:["Readers need precise per-cell values — color is the least precise visual encoding","Your matrix is small (under ~5×5) — a labeled table or small-multiple bar chart is clearer","Your data is one-dimensional — use a bar chart or histogram instead","Your data is geographic — use a choropleth, hexbin map, or proportional symbol map","Rows and columns have no meaningful order and can’t be clustered — the matrix will look like noise","You can’t guarantee a colorblind-safe palette and a textual fallback","The values span many orders of magnitude — use a log color scale, or split the data into panels","You’re showing parts of a whole — use a stacked bar, marimekko, or 100% stacked variant"].map((t,a)=>e.jsx("li",{className:"text-[14px] text-[var(--ink)] leading-[1.5] pl-4 relative font-light before:content-['—'] before:absolute before:left-0 before:text-[var(--ink-faint)]",children:t},a))})]})]}),e.jsx(b,{number:"05",id:"data-requirements",fields:B,shape:"One row per (row, column) pair, with a numeric value column. Or, equivalently, a 2D matrix with row labels on the index and column labels on the header.",minRows:"At least 5×5 cells (~25 rows in long format). Below that, a labeled table communicates more clearly.",maxRows:"Up to ~10,000 cells before rendering and Moiré patterns become a problem. Past that, downsample or cluster.",exampleHeaders:["day","hour","sessions"],exampleRows:[["Mon","10am","880"],["Mon","12pm","940"],["Tue","12pm","1180"],["Tue","2pm","980"],["Sat","8pm","540"],["Sun","6am","30"]],notes:e.jsxs(e.Fragment,{children:[e.jsx("strong",{className:"font-medium text-[var(--accent-color)]",children:"Tip:"})," heatmaps want a ",e.jsx("em",{children:"complete"})," matrix — every (row, column) pair filled in. If your raw data is sparse, decide explicitly how to fill the gaps (zero, NA, the row mean, the global mean) before plotting. Tools like pandas ",e.jsx("code",{className:"font-mono text-[12px] bg-[var(--bg-card)] px-[4px] py-[1px] rounded",children:".pivot()"})," + ",e.jsx("code",{className:"font-mono text-[12px] bg-[var(--bg-card)] px-[4px] py-[1px] rounded",children:".fillna()"})," or SQL ",e.jsx("code",{className:"font-mono text-[12px] bg-[var(--bg-card)] px-[4px] py-[1px] rounded",children:"CROSS JOIN"})," are how you get there."]})}),e.jsxs("section",{className:"py-14 border-b border-[var(--border-color)]",id:"anatomy",children:[e.jsx(o,{number:"06",label:"Anatomy",title:"Parts of a heatmap",intro:"Every heatmap is built from the same handful of parts. Knowing the names makes it easier to talk about what to keep, what to drop, and which knob to turn when the chart isn’t doing its job."}),e.jsxs("div",{className:"bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[12px] p-6 md:p-10 mt-8",children:[e.jsxs("svg",{viewBox:"0 0 580 260",xmlns:"http://www.w3.org/2000/svg",className:"w-full block",role:"img","aria-label":"Annotated diagram showing the parts of a heatmap: row labels, column labels, cell color encoding, highlighted cell, and color legend.",children:[e.jsx("text",{x:"92",y:"65",textAnchor:"end",fill:"#6b6b67",fontSize:"11",fontFamily:"DM Mono, monospace",children:"Row 1"}),e.jsx("text",{x:"92",y:"110",textAnchor:"end",fill:"#6b6b67",fontSize:"11",fontFamily:"DM Mono, monospace",children:"Row 2"}),e.jsx("text",{x:"92",y:"155",textAnchor:"end",fill:"#6b6b67",fontSize:"11",fontFamily:"DM Mono, monospace",children:"Row 3"}),e.jsx("text",{x:"92",y:"200",textAnchor:"end",fill:"#6b6b67",fontSize:"11",fontFamily:"DM Mono, monospace",children:"Row 4"}),e.jsx("text",{x:"140",y:"32",textAnchor:"middle",fill:"#6b6b67",fontSize:"11",fontFamily:"DM Mono, monospace",children:"A"}),e.jsx("text",{x:"200",y:"32",textAnchor:"middle",fill:"#6b6b67",fontSize:"11",fontFamily:"DM Mono, monospace",children:"B"}),e.jsx("text",{x:"260",y:"32",textAnchor:"middle",fill:"#6b6b67",fontSize:"11",fontFamily:"DM Mono, monospace",children:"C"}),e.jsx("text",{x:"320",y:"32",textAnchor:"middle",fill:"#6b6b67",fontSize:"11",fontFamily:"DM Mono, monospace",children:"D"}),e.jsx("rect",{x:"110",y:"44",width:"56",height:"36",rx:"4",fill:"#f5ede9"}),e.jsx("rect",{x:"170",y:"44",width:"56",height:"36",rx:"4",fill:"#e8c4b8"}),e.jsx("rect",{x:"230",y:"44",width:"56",height:"36",rx:"4",fill:"#c94a2e"}),e.jsx("rect",{x:"290",y:"44",width:"56",height:"36",rx:"4",fill:"#e8c4b8"}),e.jsx("rect",{x:"110",y:"89",width:"56",height:"36",rx:"4",fill:"#e8c4b8"}),e.jsx("rect",{x:"170",y:"89",width:"56",height:"36",rx:"4",fill:"#9e3522"}),e.jsx("rect",{x:"230",y:"89",width:"56",height:"36",rx:"4",fill:"#c94a2e"}),e.jsx("rect",{x:"290",y:"89",width:"56",height:"36",rx:"4",fill:"#c94a2e",fillOpacity:"0.7"}),e.jsx("rect",{x:"110",y:"134",width:"56",height:"36",rx:"4",fill:"#f5ede9",fillOpacity:"0.6"}),e.jsx("rect",{x:"170",y:"134",width:"56",height:"36",rx:"4",fill:"#f5ede9"}),e.jsx("rect",{x:"230",y:"134",width:"56",height:"36",rx:"4",fill:"#e8c4b8"}),e.jsx("rect",{x:"290",y:"134",width:"56",height:"36",rx:"4",fill:"#f5ede9"}),e.jsx("rect",{x:"110",y:"179",width:"56",height:"36",rx:"4",fill:"#f5ede9"}),e.jsx("rect",{x:"170",y:"179",width:"56",height:"36",rx:"4",fill:"#e8c4b8"}),e.jsx("rect",{x:"230",y:"179",width:"56",height:"36",rx:"4",fill:"#f5ede9"}),e.jsx("rect",{x:"290",y:"179",width:"56",height:"36",rx:"4",fill:"#c94a2e"}),e.jsx("line",{x1:"55",y1:"130",x2:"90",y2:"130",stroke:"#2e7d52",strokeWidth:"1.2",strokeDasharray:"3,2"}),e.jsx("circle",{cx:"52",cy:"130",r:"4",fill:"#2e7d52"}),e.jsx("text",{x:"48",y:"118",textAnchor:"middle",fill:"#2e7d52",fontSize:"10",fontFamily:"DM Mono, monospace",children:"A"}),e.jsx("line",{x1:"220",y1:"240",x2:"220",y2:"220",stroke:"#2e7d52",strokeWidth:"1.2",strokeDasharray:"3,2"}),e.jsx("circle",{cx:"220",cy:"242",r:"4",fill:"#2e7d52"}),e.jsx("text",{x:"220",y:"256",textAnchor:"middle",fill:"#2e7d52",fontSize:"10",fontFamily:"DM Mono, monospace",children:"B"}),e.jsx("rect",{x:"170",y:"89",width:"56",height:"36",rx:"4",fill:"none",stroke:"#1a1a18",strokeWidth:"2"}),e.jsx("line",{x1:"230",y1:"100",x2:"370",y2:"80",stroke:"#c94a2e",strokeWidth:"1.2"}),e.jsx("circle",{cx:"372",cy:"78",r:"4",fill:"#c94a2e"}),e.jsx("text",{x:"382",y:"75",fill:"#c94a2e",fontSize:"10",fontFamily:"DM Mono, monospace",children:"C"}),e.jsx("rect",{x:"380",y:"160",width:"24",height:"14",rx:"3",fill:"#f5ede9"}),e.jsx("rect",{x:"406",y:"160",width:"24",height:"14",rx:"3",fill:"#e8c4b8"}),e.jsx("rect",{x:"432",y:"160",width:"24",height:"14",rx:"3",fill:"#c94a2e"}),e.jsx("rect",{x:"458",y:"160",width:"24",height:"14",rx:"3",fill:"#9e3522"}),e.jsx("line",{x1:"440",y1:"180",x2:"440",y2:"196",stroke:"#6b6b67",strokeWidth:"1.2"}),e.jsx("circle",{cx:"440",cy:"198",r:"4",fill:"#6b6b67"}),e.jsx("text",{x:"440",y:"214",textAnchor:"middle",fill:"#6b6b67",fontSize:"10",fontFamily:"DM Mono, monospace",children:"D"})]}),e.jsx("div",{className:"flex flex-wrap gap-3 mt-7",children:[{color:"#2e7d52",label:"A — Row labels:",desc:"One categorical or ordinal dimension; can be sorted or clustered to reveal blocks."},{color:"#2e7d52",label:"B — Column labels:",desc:"The second categorical or ordinal dimension; same sorting rules as rows."},{color:"#c94a2e",label:"C — Cell color:",desc:"The core encoding — color intensity maps to the value at each row×column intersection."},{color:"#6b6b67",label:"D — Color legend:",desc:"A labeled color bar showing the palette and numeric range; essential for accurate reading."}].map((t,a)=>e.jsxs("div",{className:"flex items-center gap-2 bg-[var(--bg-alt)] rounded-[4px] px-[14px] py-2 text-[13px]",children:[e.jsx("span",{className:"w-[10px] h-[10px] rounded-full shrink-0",style:{background:t.color}}),e.jsxs("span",{className:"text-[var(--ink-muted)]",children:[e.jsx("strong",{className:"text-[var(--ink)] font-medium",children:t.label})," ",t.desc]})]},a))})]})]}),e.jsx(y,{number:"07",id:"step-by-step",title:"Step-by-step: how to build a good heatmap",intro:"A nine-step recipe that works regardless of the tool. Walk through it the first few times and the moves become automatic; skip steps and the chart usually shows it.",steps:c}),e.jsxs("section",{className:"py-14 border-b border-[var(--border-color)]",id:"real-world-examples",children:[e.jsx(o,{number:"08",label:"Real-world examples",title:"Where you’ll see heatmaps used",intro:"Heatmaps show up most often in four places: software product analytics, scientific research, financial markets, and operational dashboards. Each context has its own conventions, but they all reward the same fundamentals."}),e.jsx("div",{className:"flex flex-col gap-5 mt-7",children:[{num:"01",title:"Software: GitHub contribution calendar",desc:"GitHub’s contribution graph is one of the most widely recognized heatmaps in tech. It shows your coding activity across 365 days as a grid of green squares (rows = days of the week, columns = weeks of the year). Darker greens mean more commits. At a glance you can see work patterns, vacations, and productivity streaks — information no line chart of daily commits would surface as fast.",tag:"Product Analytics"},{num:"02",title:"Genomics: Gene-expression heatmaps",desc:"Bioinformatics researchers use heatmaps to visualize expression of thousands of genes across dozens of samples simultaneously. Red cells mean high expression, blue means low, and hierarchical clustering on both axes groups similar genes and similar samples together. The resulting block patterns are often the headline finding of a paper.",tag:"Science"},{num:"03",title:"UX research: Click and scroll heatmaps",desc:"Tools like Hotjar, Crazy Egg, and Microsoft Clarity overlay heatmaps on web pages to show where users click, move their mouse, or stop scrolling. Hot spots reveal which elements get attention; cold spots reveal what the design failed to surface. Product teams use them to prioritize layout changes.",tag:"UX Design"},{num:"04",title:"Operations: Day × hour traffic dashboards",de
128sc:"Operations teams at retail, transit, and SaaS companies pin a day-of-week × hour-of-day heatmap of demand to the wall of their war room. The matrix instantly tells the staffing manager where peaks form and where troughs sit, in a way a list of 168 hourly numbers never would.",tag:"Operations"}].map(t=>e.jsxs("div",{className:"bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[10px] p-6 grid grid-cols-[48px_1fr] gap-4 items-start",children:[e.jsx("span",{className:"font-serif text-[32px] font-semibold text-[var(--accent-mid)] leading-none mt-1",children:t.num}),e.jsxs("div",{children:[e.jsx("h4",{className:"text-[15px] font-medium text-[var(--ink)] mb-[6px]",children:t.title}),e.jsx("p",{className:"text-[14px] font-light text-[var(--ink-muted)] leading-[1.65]",children:t.desc}),e.jsx("span",{className:"inline-block mt-[10px] font-mono text-[9px] tracking-[0.1em] uppercase bg-[var(--bg-alt)] text-[var(--ink-muted)] px-2 py-[3px] rounded-[3px]",children:t.tag})]})]},t.num))})]}),e.jsxs("section",{className:"py-14 border-b border-[var(--border-color)]",id:"variations",children:[e.jsx(o,{number:"09",label:"Variations",title:"Types of heatmaps",intro:"The basic heatmap has several specialized variants, each tuned for a particular data shape. The headline rule is the same: pick the variant whose strengths match your question."}),e.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-2 gap-4 mt-7",children:[e.jsxs("div",{className:"bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[10px] p-5",children:[e.jsx("div",{className:"h-[60px] mb-3 flex items-center justify-center",children:e.jsx("svg",{viewBox:"0 0 120 40",width:"120",height:"40",children:[0,1,2,3,4,5,6,7,8,9,10,11].map(t=>[0,1,2,3].map(a=>e.jsx("rect",{x:t*10+2,y:a*10+2,width:"8",height:"8",rx:"1.5",fill:["#f5ede9","#e8c4b8","#c94a2e","#f5ede9","#e8c4b8","#c94a2e","#9e3522","#e8c4b8","#f5ede9","#c94a2e","#e8c4b8","#f5ede9","#c94a2e","#f5ede9","#e8c4b8","#c94a2e","#f5ede9","#e8c4b8","#f5ede9","#9e3522","#e8c4b8","#c94a2e","#f5ede9","#e8c4b8","#f5ede9","#f5ede9","#e8c4b8","#c94a2e","#c94a2e","#e8c4b8","#f5ede9","#f5ede9","#e8c4b8","#9e3522","#c94a2e","#e8c4b8","#f5ede9","#e8c4b8","#f5ede9","#f5ede9","#c94a2e","#f5ede9","#e8c4b8","#c94a2e","#f5ede9","#e8c4b8","#c94a2e","#9e3522"][t*4+a]},`${t}-${a}`)))})}),e.jsx("p",{className:"text-[13px] font-medium text-[var(--ink)] mb-1",children:"Calendar heatmap"}),e.jsx("p",{className:"text-[12px] text-[var(--ink-muted)] font-light leading-[1.5]",children:"Maps daily values to a calendar grid (weeks × days). GitHub’s contribution graph is the canonical example."})]}),e.jsxs("div",{className:"bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[10px] p-5",children:[e.jsx("div",{className:"h-[60px] mb-3 flex items-center justify-center",children:e.jsx("svg",{viewBox:"0 0 120 60",width:"120",height:"60",children:[0,1,2,3,4].map(t=>[0,1,2,3,4].map(a=>{const s=Math.abs(t-a),n=["#9e3522","#c94a2e","#e8c4b8","#f5ede9","#f5ede9"];return e.jsx("rect",{x:t*22+6,y:a*11+2,width:"20",height:"9",rx:"1",fill:n[s]},`${t}-${a}`)}))})}),e.jsx("p",{className:"text-[13px] font-medium text-[var(--ink)] mb-1",children:"Correlation matrix"}),e.jsx("p",{className:"text-[12px] text-[var(--ink-muted)] font-light leading-[1.5]",children:"Symmetric heatmap of pairwise correlations between many variables. Diverging palette anchored at zero."})]}),e.jsxs("div",{className:"bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[10px] p-5",children:[e.jsx("div",{className:"h-[60px] mb-3 flex items-center justify-center",children:e.jsxs("svg",{viewBox:"0 0 120 60",width:"120",height:"60",children:[e.jsx("line",{x1:"6",y1:"2",x2:"6",y2:"14",stroke:"#6b6b67",strokeWidth:"0.6"}),e.jsx("line",{x1:"6",y1:"14",x2:"60",y2:"14",stroke:"#6b6b67",strokeWidth:"0.6"}),e.jsx("line",{x1:"60",y1:"2",x2:"60",y2:"14",stroke:"#6b6b67",strokeWidth:"0.6"}),e.jsx("line",{x1:"60",y1:"14",x2:"114",y2:"14",stroke:"#6b6b67",strokeWidth:"0.6"}),e.jsx("line",{x1:"114",y1:"2",x2:"114",y2:"14",stroke:"#6b6b67",strokeWidth:"0.6"}),[0,1,2,3,4,5,6,7,8,9].map(t=>[0,1,2,3].map(a=>e.jsx("rect",{x:t*11+5,y:a*10+18,width:"9",height:"8",rx:"1",fill:["#f5ede9","#e8c4b8","#c94a2e","#9e3522","#c94a2e","#f5ede9","#e8c4b8","#c94a2e","#9e3522","#e8c4b8","#c94a2e","#f5ede9","#9e3522","#c94a2e","#e8c4b8","#f5ede9","#c94a2e","#e8c4b8","#9e3522","#c94a2e","#f5ede9","#e8c4b8","#c94a2e","#f5ede9","#9e3522","#c94a2e","#e8c4b8","#f5ede9","#c94a2e","#9e3522","#e8c4b8","#f5ede9","#c94a2e","#9e3522","#e8c4b8","#c94a2e","#f5ede9","#e8c4b8","#c94a2e","#9e3522"][t*4+a]},`${t}-${a}`)))]})}),e.jsx("p",{className:"text-[13px] font-medium text-[var(--ink)] mb-1",children:"Clustered heatmap (clustermap)"}),e.jsx("p",{className:"text-[12px] text-[var(--ink-muted)] font-light leading-[1.5]",children:"A heatmap with hierarchical-clustering dendrograms on the row and column margins. Standard in genomics."})]}),e.jsxs("div",{className:"bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[10px] p-5",children:[e.jsx("div",{className:"h-[60px] mb-3 flex items-center justify-center",children:e.jsxs("svg",{viewBox:"0 0 120 60",width:"120",height:"60",children:[e.jsx("defs",{children:e.jsxs("radialGradient",{id:"heat-blob",cx:"0.5",cy:"0.5",r:"0.5",children:[e.jsx("stop",{offset:"0",stopColor:"#9e3522"}),e.jsx("stop",{offset:"1",stopColor:"#9e3522",stopOpacity:"0"})]})}),e.jsx("rect",{x:"0",y:"0",width:"120",height:"60",fill:"#1a1a18",fillOpacity:"0.04"}),e.jsx("circle",{cx:"40",cy:"22",r:"22",fill:"url(#heat-blob)"}),e.jsx("circle",{cx:"80",cy:"38",r:"18",fill:"url(#heat-blob)"}),e.jsx("circle",{cx:"22",cy:"46",r:"14",fill:"url(#heat-blob)"})]})}),e.jsx("p",{className:"text-[13px] font-medium text-[var(--ink)] mb-1",children:"Density heatmap (KDE / hexbin)"}),e.jsx("p",{className:"text-[12px] text-[var(--ink-muted)] font-light leading-[1.5]",children:"A continuous heatmap built from kernel density or hexagonal binning of point data. Useful for over-plotted scatters."})]})]})]}),e.jsx(w,{number:"10",id:"comparisons",title:"Heatmap vs other chart types",intro:"Heatmaps get confused with several other chart types because they all use color to encode magnitude. The differences matter — picking the wrong one changes what your reader is allowed to conclude.",items:z}),e.jsxs("section",{className:"py-14 border-b border-[var(--border-color)]",id:"common-mistakes",children:[e.jsx(o,{number:"11",label:"Common mistakes",title:"Mistakes to wat
128ch out for",intro:"Almost every broken heatmap in the wild fails the same handful of ways. If you only memorize a few rules, make them these."}),e.jsx("div",{className:"flex flex-col gap-4 mt-7",children:[{title:"Using a rainbow palette",desc:"Rainbow palettes (jet, hsv, spectral) look colorful, but they’re not perceptually uniform: equal data steps look like unequal color steps to the eye, especially around the green band. They also fail badly for the ~8% of men with red–green color-vision deficiency. Switch to Viridis, Cividis, or any ColorBrewer scheme — they were engineered specifically to avoid this."},{title:"Missing or unlabeled legend",desc:"Without a labeled color bar, readers have no way to map colors back to numbers. The legend should sit close to the plot, show the units (“sessions”, “r”, “%”), and have 5–7 visible tick marks. A heatmap without a legend is just decorative coloring."},{title:"Random row and column order",desc:"The same data with random rows looks like noise; with sorted rows it tells a story. Sort by row sum, by a meaningful key (date, magnitude), or by hierarchical clustering before plotting. This is often the single biggest improvement you can make to a heatmap."},{title:"Diverging palette on unsigned data",desc:"Diverging palettes (red–white–blue, orange–white–purple) imply a meaningful midpoint. Using one for unsigned magnitudes invents a midpoint that doesn’t exist and biases the reader toward seeing the median as a special boundary. Use sequential palettes for unsigned data and reserve diverging for signed values around zero."},{title:"Auto-scaled domains across panels",desc:"When you small-multiple several heatmaps and let each auto-scale its color domain, the same color means a different value in each panel. Fix vmin and vmax explicitly so colors are comparable across panels and across time."},{title:"Heatmap on a tiny matrix",desc:"Below about 5×5 cells, a heatmap is just a confusingly colored table. Use a labeled table, a small-multiple bar chart, or a dot plot instead. Heatmaps earn their keep when the matrix is too dense to read cell-by-cell."},{title:"Encoding everything in color, nothing in text",desc:"If color is the only encoding, anyone who can’t see it (printed in grayscale, screen-reader users, colorblind viewers, low-contrast monitors) loses the entire chart. Always pair the heatmap with the underlying data table, and label the most important cells directly when the grid is small enough."}].map((t,a)=>e.jsxs("div",{className:"bg-[var(--bg-card)] border border-[var(--border-color)] border-l-[3px] border-l-[var(--accent-color)] rounded-r-[8px] px-5 py-[18px]",children:[e.jsx("h4",{className:"text-[14px] font-medium text-[var(--ink)] mb-[6px]",children:t.title}),e.jsx("p",{className:"text-[13px] font-light text-[var(--ink-muted)] leading-[1.6]",children:t.desc})]},a))})]}),e.jsx(v,{number:"12",id:"accessibility",items:H}),e.jsx(j,{number:"13",id:"best-practices",intro:"The mistakes section above tells you what to avoid. The list below is the positive version: the small set of habits that separate a good heatmap from a passable one.",items:R}),e.jsxs("section",{className:"py-14 border-b border-[var(--border-color)]",id:"related-charts",children:[e.jsx(o,{number:"14",label:"Alternatives",title:"Related and alternative charts",intro:"When a heatmap isn’t quite right, one of these neighbors usually is. They all live in or next to the Magnitude and Correlation families and trade off the same handful of properties."}),e.jsxs("div",{className:"grid grid-cols-1 sm:grid-cols-3 gap-4 mt-7",children:[e.jsxs(r,{href:"/choropleth-map",className:"bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[10px] p-6 px-5 hover:border-[var(--accent-border-hover)] hover:-translate-y-[2px] hover:shadow-[var(--shadow-card)] transition-all duration-150 no-underline block",children:[e.jsx("div",{className:"h-[72px] mb-4 flex items-center justify-center",children:e.jsxs("svg",{viewBox:"0 0 100 60",width:"100",height:"60",children:[e.jsx("path",{d:"M10,12 L34,8 L46,18 L40,32 L24,38 Z",fill:"#c94a2e"}),e.jsx("path",{d:"M46,18 L70,12 L80,28 L64,40 L40,32 Z",fill:"#e8c4b8"}),e.jsx("path",{d:"M64,40 L80,28 L92,40 L82,52 L60,52 Z",fill:"#9e3522"}),e.jsx("path",{d:"M24,38 L40,32 L60,52 L36,54 Z",fill:"#f5ede9",stroke:"#c94a2e",strokeWidth:"0.5"})]})}),e.jsx("p",{className:"text-[14px] font-medium text-[var(--ink)] mb-1",children:"Choropleth map"}),e.jsx("p",{className:"text-[12px] text-[var(--ink-muted)] font-light leading-[1.5]",children:"A heatmap whose “cells” are geographic polygons. Use when the row×column meaning is real geography."}),e.jsx("span",{className:"inline-block mt-[10px] font-mono text-[9px] tracking-[0.1em] uppercase text-[var(--accent-color)] bg-[var(--accent-light)] px-2 py-[3px] rounded-[3px]",children:"Geospatial"})]}),e.jsxs(r,{href:"/calendar-heatmap",className:"bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[10px] p-6 px-5 hover:border-[var(--accent-border-hover)] hover:-translate-y-[2px] hover:shadow-[var(--shadow-card)] transition-all duration-150 no-underline block",children:[e.jsx("div",{className:"h-[72px] mb-4 flex items-center justify-center",children:e.jsx("svg",{viewBox:"0 0 100 60",width:"100",height:"60",children:[0,1,2,3,4,5,6,7,8,9,10,11].map(t=>[0,1,2,3,4,5,6].map(a=>e.jsx("rect",{x:t*8+2,y:a*7+2,width:"6",height:"5",rx:"1",fill:["#f5ede9","#e8c4b8","#c94a2e","#9e3522"][(t*7+a*3)%4]},`${t}-${a}`)))})}),e.jsx("p",{className:"text-[14px] font-medium text-[var(--ink)] mb-1",children:"Calendar heatmap"}),e.jsx("p",{className:"text-[12px] text-[var(--ink-muted)] font-light leading-[1.5]",children:"When the rows and columns are days-of-week and weeks-of-year. The GitHub-contribution-graph layout."}),e.jsx("span",{className:"inline-block mt-[10px] font-mono text-[9px] tracking-[0.1em] uppercase text-[var(--accent-color)] bg-[var(--accent-light)] px-2 py-[3px] rounded-[3px]",children:"Time series"})]}),e.jsxs(r,{href:"/correlogram",className:"bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[10px] p-6 px-5 hover:border-[var(--accent-border-hover)] hover:-translate-y-[2px] hover:shadow-[var(--shadow-card)] transition-all duration-150 no-underline block",children:[e.jsx("div",{className:"h-[72px] mb-4 flex items-center justify-center",children:e.jsx("svg",{viewBox:"0 0 100 60",width:"100",height:"60",children:[0,1,2,3,4].map(t=>[0,1,2,3,4].map(a=>{const s=Math.abs(t-a),n=["#9e3522","#c94a2e","#e8c4b8","#f5ede9","#f5ede9"];return e.jsx("rect",{x:t*18+6,y:a*11+2,width:"16",height:"9",rx:"1",fill:n[s]},`${t}-${a}`)}))})}),e.jsx("p",{className:"text-[14px] font-medium text-[var(--ink)] mb-1",children:"Correlogram"}),e.jsx("p",{className:"text-[12px] text-[var(--ink-muted)] font-light leading-[1.5]",children:"A heatmap specifically of a correlation matrix, usually with a diverging palette and a symmetric domain."}),e.jsx("span",{className:"inline-block mt-[10px] font-mono text-[9px] tracking-[0.1em] uppercase text-[var(--accent-color)] bg-[var(--accent-light)] px-2 py-[3px] rounded-[3px]",children:"Correlation"})]}),e.jsxs(r,{href:"/treemap",className:"bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[10px] p-6 px-5 hover:border-[var(--accent-border-hover)] hover:-translate-y-[2px] hover:shadow-[var(--shadow-card)] transition-all duration-150 no-underline block",children:[e.jsx("div",{className:"h-[72px] mb-4 flex items-center justify-center",children:e.jsxs("svg",{viewBox:"0 0 100 60",width:"100",height:"60",children:[e.jsx("rect",{x:"0",y:"0",width:"55",height:"40",fill:"#c94a2e"}),e.jsx("rect",{x:"55",y:"0",width:"45",height:"22",fill:"#e8c4b8"}),e.jsx("rect",{x:"55",y:"22",width:"45",height:"18",fill:"#9e3522"}),e.jsx("rect",{x:"0",y:"40",width:"35",height:"20",fill:"#f5ede9",stroke:"#c94a2e",strokeWidth:"0.5"}),e.jsx("rect",{x:"35",y:"40",width:"30",height:"20",fill:"#e8c4b8"}),e.jsx("rect",{x:"65",y:"40",width:"35",height:"20",fill:"#c94a2e",fillOpacity:"0.7"})]})}),e.jsx("p",{className:"text-[14px] font-medium text-[var(--ink)] mb-1",children:"Treemap"}),e.jsx("p",{className:"text-[12px] text-[var(--ink-muted)] font-light leading-[1.5]",children:"When the data is hierarchical and you need to show parts-of-a-whole as well as magnitude."}),e.jsx("span",{className:"inline-block mt-[10px] font-mono text-[9px] tracking-[0.1em] uppercase text-[var(--accent-color)] bg-[var(--accent-light)] px-2 py-[3px] rounded-[3px]",children:"Part-to-whole"})]}),e.jsxs(r,{href:"/density-plot",className:"bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[10px] p-6 px-5 hover:border-[var(--accent-border-hover)] hover:-translate-y-[2px] hover:shadow-[var(--shadow-card)] transition-all duration-150 no-underline block",children:[e.jsx("div",{className:"h-[72px] mb-4 flex items-center justify-center",children:e.jsxs("svg",{viewBox:"0 0 100 60",width:"100",height:"60",children:[e.jsx("path",{d:"M0,55 C20,50 30,10 50,10 C70,10 80,50 100,55 L100,60 
128L0,60 Z",fill:"#c94a2e",fillOpacity:"0.6"}),e.jsx("line",{x1:"0",y1:"55",x2:"100",y2:"55",stroke:"#1a1a18",strokeWidth:"0.5",strokeOpacity:"0.2"})]})}),e.jsx("p",{className:"text-[14px] font-medium text-[var(--ink)] mb-1",children:"Density plot"}),e.jsx("p",{className:"text-[12px] text-[var(--ink-muted)] font-light leading-[1.5]",children:"When you want a smooth view of a single distribution rather than a binned 2D heatmap."}),e.jsx("span",{className:"inline-block mt-[10px] font-mono text-[9px] tracking-[0.1em] uppercase text-[var(--accent-color)] bg-[var(--accent-light)] px-2 py-[3px] rounded-[3px]",children:"Distribution"})]}),e.jsxs(r,{href:"/hexbin-plot",className:"bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[10px] p-6 px-5 hover:border-[var(--accent-border-hover)] hover:-translate-y-[2px] hover:shadow-[var(--shadow-card)] transition-all duration-150 no-underline block",children:[e.jsx("div",{className:"h-[72px] mb-4 flex items-center justify-center",children:e.jsx("svg",{viewBox:"0 0 100 60",width:"100",height:"60",children:[{x:20,y:20,c:"#e8c4b8"},{x:38,y:20,c:"#c94a2e"},{x:56,y:20,c:"#9e3522"},{x:74,y:20,c:"#e8c4b8"},{x:29,y:36,c:"#c94a2e"},{x:47,y:36,c:"#c94a2e"},{x:65,y:36,c:"#e8c4b8"},{x:38,y:52,c:"#f5ede9"},{x:56,y:52,c:"#e8c4b8"}].map((t,a)=>e.jsx("polygon",{points:`${t.x},${t.y-8} ${t.x+7},${t.y-4} ${t.x+7},${t.y+4} ${t.x},${t.y+8} ${t.x-7},${t.y+4} ${t.x-7},${t.y-4}`,fill:t.c},a))})}),e.jsx("p",{className:"text-[14px] font-medium text-[var(--ink)] mb-1",children:"Hexbin plot"}),e.jsx("p",{className:"text-[12px] text-[var(--ink-muted)] font-light leading-[1.5]",children:"A heatmap built from hexagonal bins of point data. Cures over-plotting in dense scatter plots."}),e.jsx("span",{className:"inline-block mt-[10px] font-mono text-[9px] tracking-[0.1em] uppercase text-[var(--accent-color)] bg-[var(--accent-light)] px-2 py-[3px] rounded-[3px]",children:"Correlation"})]})]})]}),e.jsx(k,{number:"15",id:"tool-instructions",intro:"Heatmaps live in every modern data tool, but the path to a clean, sorted, colorblind-safe heatmap differs from one to the next. The recipes below get you to a publication-ready chart in each of the most common platforms.",tools:P}),e.jsx(N,{number:"16",id:"code-examples",intro:"Three runnable snippets that produce the same chart — a sequential Viridis heatmap of weekly website traffic by day and hour. Copy, paste, and replace the data with yours.",samples:O}),e.jsx(g,{faqs:l,sectionNumber:"17"}),e.jsx(S,{number:"18",id:"references",intro:"Primary sources, reference texts, and the official documentation for the libraries and tools referenced throughout this guide.",items:F})]}),e.jsxs("aside",{className:"hidden md:block sticky top-[80px]",children:[e.jsxs("div",{className:"mt-12 mb-7",children:[e.jsx("p",{className:"font-mono text-[10px] tracking-[0.12em] uppercase text-[var(--ink-faint)] mb-[14px] pb-[10px] border-b border-[var(--border-color)]",children:"On this page"}),e.jsx("ul",{className:"list-none",children:q.map(t=>e.jsx("li",{className:"mb-[2px]",children:e.jsx("a",{href:`#${t.id}`,className:"block text-[12px] text-[var(--ink-muted)] no-underline py-[5px] pl-3 -ml-3 border-l-2 border-transparent hover:text-[var(--ink)] hover:border-l-[var(--accent-color)] transition-all duration-150 leading-[1.4]",children:t.label})},t.id))})]}),e.jsxs("div",{className:"bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[10px] overflow-hidden mb-5",children:[e.jsx("div",{className:"bg-[var(--ink)] px-[18px] py-[14px] font-mono text-[10px] tracking-[0.12em] uppercase text-[var(--white-muted)]",children:"// At a glance"}),e.jsx("div",{children:[{label:"Category",val:"Magnitude / Correlation",color:""},{label:"Difficulty",val:"Intermediate",color:"text-[var(--caution)]"},{label:"Encoding",val:"Color intensity per cell",color:""},{label:"Best palette",val:"Viridis / Cividis",color:""},{label:"Best matrix size",val:"5×5 – ~10,000 cells",color:""},{label:"Invented",val:"1873 (Loua) / 1991 (term)",color:""},{label:"Read time",val:"~15 min",color:""}].map((t,a)=>e.jsxs("div",{className:"px-[18px] py-[14px] border-b border-[var(--border-color)] last:border-b-0 flex flex-col gap-[2px]",children:[e.jsx("span",{className:"text-[11px] text-[var(--ink-faint)] font-mono tracking-[0.05em]",children:t.label}),e.jsx("span",{className:`text-[14px] text-[var(--ink)] ${t.color}`,children:t.val})]},a))})]}),e.jsxs("div",{className:"bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[10px] overflow-hidden mb-5",children:[e.jsx("div",{className:"bg-[var(--ink)] px-[18px] py-[14px] font-mono text-[10px] tracking-[0.12em] uppercase text-[var(--white-muted)]",children:"// Tags"}),e.jsx("div",{className:"px-[18px] py-4",children:e.jsx("div",{className:"flex flex-wrap gap-[6px]",children:["Magnitude","Correlation","Matrix","Color encoding","Pattern","Genomics","UX","Calendar"].map(t=>e.jsx("span",{className:"font-mono text-[10px] tracking-[0.06em] uppercase bg-[var(--bg-alt)] text-[var(--ink-muted)] px-[10px] py-1 rounded-[3px]",children:t},t))})})]}),e.jsxs("div",{className:"bg-[var(--bg-card)] border border-[var(--border-color)] rounded-[10px] overflow-hidden mb-5",children:[e.jsx("div",{className:"bg-[var(--ink)] px-[18px] py-[14px] font-mono text-[10px] tracking-[0.12em] uppercase text-[var(--white-muted)]",children:"// Similar charts"}),e.jsx("div",{className:"py-1",children:[{name:"Choropleth map",href:"/choropleth-map"},{name:"Calendar heatmap",href:"/calendar-heatmap"},{name:"Correlation matrix",href:"/correlogram"},{name:"Treemap",href:"/treemap"},{name:"Density plot",href:"/density-plot"}].map((t,a,s)=>e.jsxs(r,{href:t.href,className:`flex items-center justify-between px-[18px] py-3 text-[13px] text-[var(--ink-muted)] hover:text-[var(--accent-color)] transition-colors duration-150 cursor-pointer no-underline ${a<s.length-1?"border-b border-[var(--border-color)]":""}`,children:[t.name," ",e.jsx("span",{children:"→"})]},t.name))})]})]})]})]}),e.jsxs("div",{className:"border-t border-[var(--border-color)] bg-[var(--bg-card)] px-6 md:px-12 py-5 flex items-center justify-between",children:[e.jsx("div",{className:"flex gap-8",children:e.jsxs(r,{href:"/histogram",className:"text-[13px] text-[var(--ink-muted)] flex items-center gap-[6px] hover:text-[var(--ink)] transition-colors duration-150 no-underline",children:[e.jsx("span",{className:"text-[var(--accent-color)]",children:"←"})," Previous: Histogram"]})}),e.jsx("span",{className:"font-mono text-[11px] text-[var(--ink-faint)] hidden md:block",children:"1 of 80+ chart types"}),e.jsx("div",{className:"flex gap-8",children:e.jsxs(r,{href:"/choropleth-map",className:"text-[13px] text-[var(--ink-muted)] flex items-center gap-[6px] hover:text-[var(--ink)] transition-colors duration-150 no-underline",children:["Next: Choropleth map ",e.jsx("span",{className:"text-[var(--accent-color)]",children:"→"})]})})]})]})}export{_ as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.