feat(bar): add dumbbell as a Bar variant (#107)

Documents the dumbbell in type-bar.md's existing Variants section,
alongside grouped and stacked bars. Reference-only, matching the
precedent those two set and the one #100 landed for datalake: no
example assets, no gallery entries.

Not a new visual type. The count stays at 28 and SKILL.md is untouched
-- no selection-table row, no frontmatter hook, no manifest description
change.

Covers layout on the shared 1000x500 composition, an element pattern,
and five honesty rules: no truncated axis, one scale with both endpoints
labelled, the connector is a gap and not a trajectory, a stated row
order, and no imputed or silently dropped endpoints.

Two of those rules live in a formula rather than a drawing, so they ship
as a checker with adversarial fixtures per ADR 0005:

- Domain resolution is exhaustive over sign. A rule phrased as
  "all-positive / all-negative" silently drops data that only touches
  zero, and drops all-zero data entirely -- where floor == ceil == 0 and
  the position formula divides by zero. resolve_domain() partitions the
  space, guarantees ceil > floor, and returns a finite fallback span for
  all-zero input so every dot lands on the floor.
- Non-text contrast is carried by the boundary, not the fill. Accent on
  paper is 2.86:1 skin-wide, and shape redundancy does not waive WCAG
  1.4.11, so the solid endpoint takes a 1px ink stroke (11.82:1) and the
  connector 55% ink on light / 40% on dark (3.19:1 and 3.24:1). The
  checker reads these tokens out of the reference, so prose and
  thresholds cannot drift apart.

scripts/test-verify-dumbbell.py covers both polarities: all-zero,
zero-touching, mixed-sign, empty and non-finite input, observed-extremes
truncation, and the sub-3:1 treatments this replaces. Wired into CI and
CONTRIBUTING alongside the treemap and geometry gates.

Other things the geometry and house style forced: the focal bar pattern
(12% tint plus accent stroke) does not survive at a 6px dot, so the pair
separates by fill weight; a 64px row pitch from y=96 overflows the y=420
plot floor at seven rows, so pitch and origin are tabulated per row
count; rgb(45,49,66) IS the dark paper, so light-theme hairlines
composite to 1.000:1 and must invert; label offsets keyed to start/end
put both labels inside the pair on a decreasing row; a value on the
domain floor collided with its category label; and the value axis was
missing the Geist Mono 7px title every other cartesian type carries.

Co-authored-by: 0xDarkMatter <0xDarkMatter@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
0xDarkMatter
2026-08-18 18:49:39 -05:00
committed by GitHub
co-authored by 0xDarkMatter Claude Opus 5
parent 0947fdb6e5
commit 29917724f8
7 changed files with 564 additions and 4 deletions
+12
View File
@@ -197,6 +197,16 @@ jobs:
id: treemap_tests
run: python scripts/test-verify-treemap.py
- name: Verify dumbbell domain and contrast rules
if: always()
id: dumbbell
run: python scripts/verify-dumbbell.py
- name: Verify dumbbell checker
if: always()
id: dumbbell_tests
run: python scripts/test-verify-dumbbell.py
- name: Verify generated icon assets
if: always()
id: icons
@@ -233,4 +243,6 @@ jobs:
echo "| Label Geometry Checker | ${{ steps.geometry_tests.outcome == 'success' && '✅ Passed' || (steps.geometry_tests.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
echo "| Treemap Area & Label Fit | ${{ steps.treemap.outcome == 'success' && '✅ Passed' || (steps.treemap.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
echo "| Treemap Checker | ${{ steps.treemap_tests.outcome == 'success' && '✅ Passed' || (steps.treemap_tests.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
echo "| Dumbbell Domain & Contrast | ${{ steps.dumbbell.outcome == 'success' && '✅ Passed' || (steps.dumbbell.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
echo "| Dumbbell Checker | ${{ steps.dumbbell_tests.outcome == 'success' && '✅ Passed' || (steps.dumbbell_tests.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY
echo "| Primitive Icons & Documentation | ${{ steps.icons.outcome == 'success' && '✅ Passed' || (steps.icons.outcome == 'skipped' && '⏭️ Skipped' || '❌ Failed') }} |" >> $GITHUB_STEP_SUMMARY