JSFiddle - React, Tailwind, and code Playground

HTML

<script src="https://www.gstatic.com/charts/loader.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div class="chart-container">
        <h1>Dynamic Bar Chart Report</h1>
        <!-- The data-json attribute holds your chart data.
             It's structured as [label, value1, tooltip1, color1, value2, tooltip2, color2, ...]
             This mock data simulates the structure from your original image. -->
        <div id="chart_div" class="complex-barchart"
             data-chart-type="bar"
             data-json='{"cols": [{"type": "string" ,"id": "PrimaryKey" ,"label": "PrimaryKey" }, {"type": "number" ,"id": "SoftwareHours" ,"label": "Software" }, {"type": "string" ,"id": "SoftwareAnnotation" ,"label": "SoftwareAnnotation" , "p": {"role" : "annotation"}}, {"type": "string" ,"id": "SoftwareToolTip" ,"label": "SoftwareToolTip" , "p": {"role" : "tooltip"}}, {"type": "string" ,"id": "SoftwareColor" ,"label": "SoftwareColor" , "p": {"role" : "style"}}, {"type": "number" ,"id": "SoftwareBudgetHours" ,"label": "SoftwareBudgetHours" }, {"type": "string" ,"id": "SoftwareBudgetAnnotation" ,"label": "SoftwareBudgetAnnotation" , "p": {"role" : "annotation"}}, {"type": "string" ,"id": "SoftwareBudgetToolTip" ,"label": "SoftwareBudgetToolTip" , "p": {"role" : "tooltip"}}, {"type": "string" ,"id": "SoftwareBudgetColor" ,"label": "SoftwareBudgetColor" , "p": {"role" : "style"}}, {"type": "number" ,"id": "ManufacturingHours" ,"label": "Manufacturing" }, {"type": "string" ,"id": "ManufacturingAnnotation" ,"label": "ManufacturingAnnotation" , "p": {"role" : "annotation"}}, {"type": "string" ,"id": "ManufacturingToolTip" ,"label": "ManufacturingToolTip" , "p": {"role" : "tooltip"}}, {"type": "string" ,"id": "ManufacturingColor" ,"label": "ManufacturingColor" , "p": {"role" : "style"}}, {"type": "number" ,"id": "ManufacturingBudgetHours" ,"label": "ManufacturingBudgetHours" }, {"type": "string" ,"id":...

CSS

body {
            font-family: 'Inter', sans-serif;
            display: flex;
            justify-content: center;
            align-items: flex-start;
            min-height: 100vh;
            background-color: #f0f2f5;
            padding: 20px;
            box-sizing: border-box;
        }
        .chart-container {
            width: 90%; /* Responsive width */
            max-width: 1200px; /* Max width for larger screens */
            background-color: #ffffff;
            border-radius: 12px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
            padding: 20px;
            box-sizing: border-box;
        }
        h1 {
            text-align: center;
            color: #333;
            margin-bottom: 20px;
        }
        /* Ensure the chart div itself can grow */
        #chart_div {
            width: 100%;
            /* Height will be set dynamically by Google Charts */
        }

JavaScript

google.charts.load('current', { packages: ['corechart', 'bar'] })
google.charts.setOnLoadCallback(GenerateComplexBarChartReports)

// Helper function to find the minimum value for hAxis, if needed.
// This is a placeholder; you'll need to implement your actual logic here.
function FindMinValue(data) {
  let minValue = 0 // Default to 0, or calculate based on your data
  // Example: iterate through data to find min value if needed
  // for (let i = 0; i < data.getNumberOfRows(); i++) {
  //     for (let j = 1; j < data.getNumberOfColumns(); j += 8) { // Adjusted to jump by 8 for each department
  //         let actualHours = data.getValue(i, j);
  //         let budgetHours = data.getValue(i, j + 4); // Budget hours are 4 columns after actual
  //         if (typeof actualHours === "number" && actualHours < minValue) {
  //             minValue = actualHours;
  //         }
  //         if (typeof budgetHours === "number" && budgetHours < minValue) {
  //             minValue = budgetHours;
  //         }
  //     }
  // }
  return minValue
}

/**
 * Calculates the appropriate chart height based on the number of bars
 * per y-axis item to prevent overlap and ensure proper spacing.
 * @param {google.visualization.DataTable} data The DataTable object for the chart.
 * @returns {number} The calculated height for the chartArea.
 */
function CalculateChartHeightBasedOnDataItems(data) {
  // Height needed for a single bar including its annotation.
  // This is the fundamental unit of vertical space.
  const heightOfOneBarAndAnnotation = 30 // Further reduced for tighter packing

  // Minimal vertical space for the category label itself, if no bars are present,
  // or as a base for very sparse categories.
  const minHeightForCategoryLabel = 15 // Further reduced for tighter packing

  // Small, consistent padding *between* different y-axis categories.
  const interCategorySpacing = 5 // Further reduced for tighter packing

  let...