{"spec_id":"bar-stacked-percent","library":"pygal","language":"python","code":"\"\"\" anyplot.ai\nbar-stacked-percent: 100% Stacked Bar Chart\nLibrary: pygal 3.1.3 | Python 3.13.15\nQuality: 88/100 | Updated: 2026-08-18\n\"\"\"\n\nimport os\n\nimport pygal\nfrom pygal.style import Style\n\n\nTHEME = os.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG = \"#FAF8F1\" if THEME == \"light\" else \"#1A1A17\"\nINK = \"#1A1A17\" if THEME == \"light\" else \"#F0EFE8\"\nINK_MUTED = \"#6B6A63\" if THEME == \"light\" else \"#A8A79F\"\n\n# Sentiment semantic exception (Imprint palette + anchors) — a Likert-scale\n# response is a diverging polarity, not an abstract category, so we map it\n# green (positive) -> lime -> muted (neutral) -> amber (warning) -> red (negative)\n# instead of the canonical 1..N ordinal order. #009E73 stays series 1.\nSENTIMENT_COLORS = (\"#009E73\", \"#99B314\", INK_MUTED, \"#DDCC77\", \"#AE3030\")\n\ncustom_style = Style(\n    background=PAGE_BG,\n    plot_background=PAGE_BG,\n    foreground=INK,\n    foreground_strong=INK,\n    foreground_subtle=INK_MUTED,\n    colors=SENTIMENT_COLORS,\n    font_family=\"'Helvetica Neue', Helvetica, Arial, sans-serif\",\n    title_font_size=66,\n    label_font_size=56,\n    major_label_font_size=44,\n    legend_font_size=44,\n    value_font_size=36,\n    stroke_width=2.5,\n)\n\n# Employee engagement survey, 400 respondents per topic, response counts per\n# Likert level. Topics are listed in descending order of favorable response\n# (Agree + Strongly Agree share) so the chart reads as a ranked story from\n# the org's strongest area to its weakest.\ntopics = [\n    \"Team Collaboration\",\n    \"Management Support\",\n    \"Communication\",\n    \"Recognition\",\n    \"Career Growth\",\n    \"Work-Life Balance\",\n    \"Job Security\",\n    \"Compensation\",\n]\nstrongly_disagree = [10, 15, 20, 25, 35, 45, 50, 60]\ndisagree = [25, 35, 45, 55, 70, 80, 85, 90]\nneutral = [40, 60, 70, 80, 90, 85, 95, 100]\nagree = [180, 170, 155, 140, 125, 110, 100, 90]\nstrongly_agree = [145, 120, 110, 100, 80, 80, 70, 60]\n\npercent_strongly_agree = []\npercent_agree = []\npercent_neutral = []\npercent_disagree = []\npercent_strongly_disagree = []\n\nfor i in range(len(topics)):\n    total = strongly_disagree[i] + disagree[i] + neutral[i] + agree[i] + strongly_agree[i]\n    percent_strongly_agree.append(round(strongly_agree[i] / total * 100, 1))\n    percent_agree.append(round(agree[i] / total * 100, 1))\n    percent_neutral.append(round(neutral[i] / total * 100, 1))\n    percent_disagree.append(round(disagree[i] / total * 100, 1))\n    percent_strongly_disagree.append(round(strongly_disagree[i] / total * 100, 1))\n\nchart = pygal.StackedBar(\n    width=3200,\n    height=1800,\n    style=custom_style,\n    title=\"bar-stacked-percent · python · pygal · anyplot.ai\",\n    x_title=\"Survey Topic\",\n    y_title=\"Respondents (%)\",\n    show_y_guides=True,\n    show_x_guides=False,\n    legend_at_bottom=True,\n    print_values=True,\n    print_values_position=\"center\",\n    # Segments below ~7% are shorter than value_font_size can render without\n    # spilling outside their own colored band, so those labels are suppressed\n    # rather than printed (their share is still visible from the band + legend).\n    value_formatter=lambda x: f\"{x:.0f}%\" if x >= 7 else \"\",\n    x_label_rotation=25,\n    truncate_label=-1,\n    margin=60,\n    margin_bottom=90,\n)\n\nchart.x_labels = topics\nchart.add(\"Strongly Agree\", percent_strongly_agree)\nchart.add(\"Agree\", percent_agree)\nchart.add(\"Neutral\", percent_neutral)\nchart.add(\"Disagree\", percent_disagree)\nchart.add(\"Strongly Disagree\", percent_strongly_disagree)\n\nchart.range = (0, 100)\n\nchart.render_to_png(f\"plot-{THEME}.png\")\nwith open(f\"plot-{THEME}.html\", \"wb\") as f:\n    f.write(chart.render())\n"}