Dataface Tasks

M1 pilot chart batch

IDGRAPH_LIBRARY-M1_PILOT_CHART_BATCH
Statuscompleted
Priorityp0
Milestonem1-ft-analytics-analyst-pilot
Ownerdata-viz-designer-engineer
Completed byrj
Completed2026-03-27

Problem

The current workstream has prototype momentum and scattered later-stage tasks, but it does not yet define the small, concrete chart batch that makes an M1 analyst pilot real. Without an explicit batch of supported basic charts, the team risks two failure modes: either trying to support too many chart types too early, or repeatedly doing bespoke work for each pilot use case. M1 needs a deliberate "enough to be useful" set of charts with predictable behavior across repeated analyst workflows.

Context

  • The chart system is an extended Vega-Lite grammar, so M1 should prefer chart types that map mechanically onto native Vega-Lite concepts.
  • The render philosophy keeps data meaning in queries and presentation in charts; M1 should avoid chart types that require hidden semantic reshaping in the viz layer.
  • For pilot readiness, a chart batch should support summary, comparison, trend, part-to-whole, and tabular inspection.

Possible Solutions

  • Minimal six-chart batch: table, bar, column, pie, donut, line. Simple scope, but it misses KPI and makes dashboard composition weaker.
  • Broad first batch: add area, scatter, heatmap, waterfall, funnel, and more. Richer surface area, but it spreads effort too thin for M1.
  • Recommended: pilot batch centered on common analytic workflows: table, KPI, bar, column, line, pie, donut. Add area and scatter only if they are mechanically cheap extensions once the core batch is solid.

Plan

  • Define the required M1 chart batch and explicit non-goals.
  • Verify each chart type can render with stable defaults and without bespoke per-case intervention.
  • Capture a small corpus of representative examples that exercise the batch across realistic analyst questions.
  • Record gaps that should stay out of M1 and roll into later depth-expansion work.

Execution Checklist

1. Foundations Already Landed

  • [x] Primary sans decision complete: Inter selected as the default chart/UI sans.
  • [x] Primary sans implementation complete in chart and export paths.
  • [x] Narrative serif decision complete: Source Serif 4 selected and --font-serif added for future narrative surfaces.
  • [x] Tonal foundation implementation complete for neutral defaults.
  • [x] Chart-family docs and navigation restructured so the chart surface is easier to inspect and explain.

2. M1 Scope Decisions Already Made

  • [x] M1 success is a stable, reusable basic chart batch for real analyst workflows, not full chart-system maturity.
  • [x] Required M1 chart batch locked for execution: table, KPI, bar, column, line, pie, donut.
  • [x] Stretch-only chart types: area and scatter, only if they are mechanically cheap extensions.
  • [x] Explicit non-goals for M1 include specialized chart types such as heatmap, waterfall, funnel, treemap, and similar deeper-expansion work.
  • [x] Style packages, evaluation ownership, design assertions, terminology standards, and broader exposed-property work are sequenced after the basic batch unless a specific pilot blocker forces escalation.

3. Work To Prove And Ship For M1

  • [x] Confirm the canonical representative example set for the required batch.
  • Required batch: table, KPI, bar, column, line, pie, donut.
  • Stretch (mechanically cheap): area, scatter.
  • Non-goals: heatmap, waterfall, funnel, treemap, and specialized types.
  • Corpus of working examples is an M2 example-corpus task; the set itself is locked here.
  • [x] Verify table works reliably for pilot-ready tabular inspection.
  • Custom SVG renderer in table.py; full test coverage passes.
  • [x] Verify KPI works reliably for summary readouts.
  • Custom SVG renderer in kpi.py; enforces exactly-1-row validation; tests pass.
  • [x] Verify bar works reliably for categorical comparison.
  • type: bar maps to Vega-Lite bar mark; horizontal orientation via _apply_horizontal_bar_orientation; tests pass.
  • [x] Verify column works reliably for categorical comparison.
  • Vertical bar is type: bar with categorical x and quantitative y; default orientation in Vega-Lite; tests pass.
  • [x] Verify line works reliably for trend analysis.
  • type: line maps to Vega-Lite line mark; single-layer spec; tests pass.
  • [x] Verify pie works reliably for simple part-to-whole cases.
  • type: pie maps to Vega-Lite arc mark via _generate_arc_spec; tests pass.
  • [x] Verify donut works reliably for simple part-to-whole cases.
  • type: donut maps to Vega-Lite arc with innerRadius; tests pass.
  • [x] Decide whether area qualifies as a cheap M1 stretch extension.
  • YES. type: area maps mechanically to Vega-Lite area; no extra renderer logic needed.
  • [x] Decide whether scatter qualifies as a cheap M1 stretch extension.
  • YES. type: scatter aliases to Vega-Lite point; no extra renderer logic needed.
  • [x] Capture explicit non-goals and known gaps that should stay out of M1.
  • Non-goals confirmed: heatmap, waterfall, funnel, treemap, specialized chart types.
  • Cross-chart category color consistency is M2 (assignment logic on top of palettes).
  • Style packages and full exposed-property surface are post-M1.
  • [ ] Record QA evidence and example references for each chart type.
  • M2 deferral: requires the example corpus. Deferred to example-corpus task.
  • [ ] Prove at least one weekly analyst workflow can reuse this batch without chart-by-chart engineering help.
  • M2 deferral: requires actual pilot usage. Deferred to pilot follow-on.

4. Decisions Made But Not Yet Fully Operationalized In M1

  • [ ] Turn the basic-batch decision into a concrete example corpus rather than a planning-only recommendation.
  • M2 deferral: example corpus is a follow-on task; the batch decision is locked here.
  • [ ] Turn completed typography and tonal decisions into pass/fail review criteria for the M1 chart examples.
  • M2 deferral: requires the example corpus to exist first. Deferred to example-corpus task.
  • [x] The serif choice no longer stays fully dormant in M1: top chart titles are now the first intentional narrative surface using the serif family, while axis titles, legend titles, and other chart text remain on the sans system for now.
  • Implemented: set_chart_title() in spec_builders.py applies config.style.title_font_family (Source Serif 4) to chart title and config.style.font_family (Inter) to subtitles.
  • [x] Reconcile the planning tension between "dashboard composition belongs in M1":
  • Decision: dashboard composition (multi-chart layout) is explicitly M2. M1 scope is the single-chart batch only.

5. High-Priority Decision-Architecture Items

  • [x] Decide whether consistent category color mapping across charts is required for M1 pilot trust or can remain a planned follow-on.
  • Decision: explicit M2 deferral. The palette is defined; per-dashboard assignment logic is a follow-on.
  • [x] Identify context-sensitive style decisions that Vega-Lite alone will not settle cleanly for the pilot batch.
  • Identified: x-axis label posture (angle/rotation by density), horizontal bar label gutter, arc chart tooltip assembly.
  • All three are already handled by mechanical policy code in standard_renderer.py and presentation.py.
  • [x] For each context-sensitive item, choose one disposition.
  • x-axis label posture: fixed M1 default rule (handled by _apply_bar_axis_defaults, config-driven thresholds in editorial structure).
  • Horizontal bar label gutter: fixed M1 default rule (handled by _apply_horizontal_bar_label_gutter).
  • Cross-chart category color consistency: explicit M2 deferral.

6. Candidate Context-Sensitive Defaults To Review

  • [ ] Cross-chart category color consistency for repeated semantic values.
  • [ ] Legend versus direct labeling posture when categories are few and repeated.
  • [ ] Axis label density / rotation / abbreviation behavior for compact surfaces.
  • [ ] Numeric notation posture across axes, tooltips, KPIs, and tables when the same metric appears in different contexts.
  • [ ] Title / subtitle / annotation defaults for composed analytic surfaces.
  • [ ] Empty-state, overflow, and small-multiple fallback behavior where raw Vega-Lite defaults may not be product-ready.
  • [ ] Lock the representative example corpus for the required seven-chart batch.
  • M2 deferral: chart types locked here; example YAML files are a follow-on task.
  • [x] Run a proof pass on each required chart type and record pass/fail gaps.
  • All seven required types verified with 2864 passing core tests (see Section 3).
  • [ ] Review the open context-sensitive items against those examples instead of deciding them abstractly.
  • M2 deferral: depends on the example corpus; deferred to example-corpus task.
  • [x] Make an explicit M1 scope call for category color mapping and any other pilot-critical decision architecture.
  • Category color mapping: explicit M2 deferral.
  • Dashboard composition: explicit M2 deferral.
  • Context-sensitive defaults not resolved by config: handled by mechanical policy in standard_renderer.py / presentation.py.
  • [x] Convert anything non-critical into M2 follow-on tasks rather than letting it quietly expand M1.
  • Non-critical context-sensitive items (Phase 3 review, tooltip styling, empty-state behavior, legend suppression, numeric format posture) are recorded as M2 follow-on items in this task file.

Missing Decisions Inventory

A. Core visual paint decisions still needing explicit direction

  • [ ] Primary categorical palette for the M1 chart batch: default palette, ordering logic, and whether the current defaults should be replaced.
  • [ ] Neutral texture system for chart scaffolding: whether axes, grid lines, labels, titles, legends, and guide lines all share one gray or use a stepped neutral hierarchy for a more textured feel.
  • [ ] Chart title / subtitle / description styling: size, weight, color, anchor, and whether subtitles/descriptions matter in M1.
  • [ ] Axis paint defaults: label color, title color, tick color, grid color, domain color, and line weights.
  • [ ] Legend paint defaults: label color, title color, symbol sizing, spacing, and visual quietness relative to marks.
  • [ ] Mark paint defaults by family: line stroke weights, point sizes/fills, bar corner treatment, arc separators, area opacity.
  • [ ] Background/surface posture: transparent chart on board canvas versus explicit chart panel/surface treatment.
  • [ ] Tooltip visual styling: whether current defaults are acceptable for M1 or need chart-library-owned polish.

B. Structural/default-behavior decisions still needing explicit direction

  • [ ] Canonical structure for M1: keep editorial as the default structure or define a new M1-specific structure preset.
  • [ ] Y-axis side posture: when right-oriented y-axes are default, and when left-oriented axes should win.
  • [ ] X-axis label posture rules: horizontal / medium / dense thresholds, rotation angles, truncation, and padding.
  • [ ] Grid philosophy: when grid lines appear, whether domains are visible, and how different that should be across bar/line/arc/table surfaces.
  • [ ] Legend placement rules: right, bottom, hidden, or direct labels by default for different chart families.
  • [ ] Title anchoring and chart padding rules for compact versus standard charts.
  • [ ] Empty-state and overflow behavior for labels, legends, and dense categories.

C. Cross-chart consistency and semantic decisions

  • [ ] Category color consistency across charts for repeated semantic values.
  • [ ] Whether repeated semantic categories should use explicit named mappings, palette position rules, or both.
  • [ ] Numeric-format posture by surface: axes, tooltips, KPI values, tables, annotations.
  • [ ] Where M1 needs chart-family-specific defaults versus global defaults.
  • [ ] Whether any annotation / narrative-text rules belong in M1 at all.

D. M1 scope-call decisions

  • [ ] Which of the above are required to make the pilot trustworthy now.
  • [ ] Which need lightweight architecture in M1 because they recur across examples.
  • [ ] Which are real design work, but should be explicitly deferred to M2.

Implementation Map

1. Structure defaults

Use structure config when the decision is about scaffold, placement, posture, or whether a guide exists at all.

  • Files:
  • dataface/core/compile/chart_structures/*.yml
  • dataface/core/compile/default_config.yml (structure, theme_structures)
  • dataface/core/render/chart/presentation.py
  • dataface/core/render/chart/standard_renderer.py
  • Decisions implemented here:
  • axis side/orient defaults
  • legend placement
  • title anchor
  • grid on/off
  • domain on/off
  • label posture defaults
  • scale padding / band behavior
  • Likely M1 move:
  • either refine editorial or introduce a dedicated M1 structure preset that captures the intended scaffold cleanly.

2. Theme / paint defaults

Use theme or chart default config when the decision is about color, font, weight, stroke, fill, opacity, or size.

  • Files:
  • dataface/core/compile/chart_defaults.yml
  • dataface/core/compile/default_config.yml
  • dataface/core/compile/chart_themes/*.yml
  • Decisions implemented here:
  • categorical palette
  • neutral hierarchy for grid/ticks/labels/titles
  • mark stroke/fill defaults
  • title/legend/axis paint
  • chart background
  • type sizes and weights
  • Likely M1 move:
  • define one chart-library-owned M1 paint system rather than inheriting accidental mixed defaults from existing generic themes.

3. Mechanical renderer wiring

Use Python renderer code when the behavior is a mechanical translation layer, a smart default policy already owned by Dataface, or a seam Vega-Lite cannot express directly from config alone.

  • Files:
  • dataface/core/render/chart/presentation.py
  • dataface/core/render/chart/standard_renderer.py
  • dataface/core/render/chart/decisions.py
  • Decisions implemented here:
  • smart x-axis label posture heuristics
  • categorical y-axis side defaults for bar charts
  • tooltip field assembly
  • pass-through versus Dataface-owned default logic
  • any lightweight decision architecture for context-sensitive defaults
  • Likely M1 move:
  • keep this layer minimal and mechanical; only add policy code where config alone cannot carry the decision cleanly.

4. Specialized renderer surfaces

Use specialized renderers where the chart bypasses standard Vega-Lite config or needs custom SVG/HTML treatment.

  • Files:
  • dataface/core/render/chart/kpi.py
  • dataface/core/render/chart/table.py
  • Decisions implemented here:
  • KPI card background, title/value hierarchy, muted/supporting text colors
  • table header paint, row striping, borders, numeric text treatment
  • table-specific texture decisions that generic Vega config does not reach
  • Likely M1 move:
  • define KPI and table texture intentionally, because they are part of the required seven-chart batch and currently carry their own default logic.

5. Cross-chart semantic mapping architecture

Use a dedicated chart-library architecture seam when the decision is about reusing meaning across charts, not just styling one chart in isolation.

  • Likely files/seams:
  • chart schema/types for authored config shape
  • chart rendering pipeline / presentation helpers for resolution logic
  • chart defaults for global fallback palette behavior
  • Decisions implemented here:
  • repeated-category color mapping
  • shared palette ordering rules
  • other context-sensitive cross-chart defaults that need Dataface-owned resolution
  • Likely M1 move:
  • only build this if the pilot examples prove it is necessary for trust and repeat use.

Prioritized Decision Agenda

Phase 1. Lock one M1 paint system first

These choices will affect every required chart type immediately and should be decided before deeper architecture work.

  • [ ] Decide the neutral hierarchy for chart scaffolding.
  • Recommended framing: use multiple neutrals, not one gray everywhere.
  • Target outcome: one darkest ink for titles/primary labels, one mid neutral for secondary labels/legend text, one light neutral for grids/rules, one very light neutral for fills or panels when needed.
  • [ ] Decide the primary categorical palette for the M1 batch.
  • Target outcome: one default ordered palette for bar/column/line/point/arc categorical series.
  • [ ] Decide chart text hierarchy.
  • Target outcome: chart title color/weight/size, axis title posture, axis label quietness, legend text quietness.
  • [ ] Decide guide-line texture.
  • Target outcome: grid visibility, domain visibility, tick presence, and relative contrast between guides and data marks.
  • [ ] Decide default mark texture by family.
  • Target outcome: line weight, point size/fill, bar corner treatment, area opacity, arc separators.
  • [ ] Decide chart surface posture.
  • Target outcome: transparent chart backgrounds by default versus subtle panel treatment for KPI/table or other exceptions.

Phase 1C. Categorical palette direction

Current working direction:

  • [x] M1 should choose one universal default categorical palette.
  • [x] That universal default palette should be assigned to and selected by the default theme.
  • [x] M1 should also support a second categorical palette: a "hero" palette whose first color stands out strongly from the remaining colors.
  • [x] Semantic meaning for categorical colors is explicitly out of scope for M1 palette selection.
  • [x] Cross-chart category consistency across an entire dashboard is a desired downstream behavior, but that logic should not block standing up the first categorical palettes.

Clarification to preserve:

  • [x] The immediate palette job is: define the first categorical palettes.
  • [x] The follow-on architecture job is: preserve category-to-color assignments across charts within a dashboard once those palettes exist.

Research guidance to preserve for palette selection:

  • [x] Categorical palettes should read as unordered identities, not as a light-to-dark ranking or value scale.
  • [x] Palette review must happen in actual chart use, not only as isolated swatches: against the background, at small mark sizes, and in chart families with high adjacency pressure such as pies and donuts.
  • [x] Lightness separation matters alongside hue separation: if categories collapse toward the same gray, the palette is too fragile.
  • [x] Colorblind distinguishability is part of the acceptance bar, not a polish pass.
  • [x] Category count and labeling strategy are part of the palette decision: once category count rises, direct labels, dashes, symbols, patterns, or selective gray may need to share the burden.
  • [x] Preserve the design-corpus source for these rules in docs/docs/guides/chart-design-notes.md Note 55 rather than duplicating the full research memo in this task.
  • [x] Preserve the competitive sample reference in /Users/rjandrews/Documents/color-inspo/Color Palette Engineering - ref2.csv and the DFT synthesis in docs/docs/guides/chart-design-notes.md Note 56.
  • [x] Preserve the local pairwise palette-audit workflow in scripts/palette_deltae_checker.py and docs/docs/guides/chart-design-notes.md Note 57.

Empirical palette-engineering takeaways from the comparative BI / OSS sample:

  • [x] The first-position convention is overwhelmingly blue: 14 of 17 sampled palettes start with a blue around hue 204-224; only two start with green and one starts with red.
  • [x] Palette length clusters around ten colors: the sample ranges from 6 to 12 colors, with median 10 and seven palettes at exactly 10 colors.
  • [x] Many competitive palettes let later colors get weak on bright backgrounds: in the sample, 33 of 164 colors fall below 2:1 contrast and 86 of 164 fall below 3:1 against #FAFAFA.
  • [x] Some competitors include gray or near-gray entries inside their ordinary categorical sets, which suggests not every product palette is optimized for equal-salience category identity.
  • [x] The competitive opportunity for DFT is likely: stay blue-first and conventionally BI-legible if desired, but be stricter than many peers about later-color readability, distinctiveness, and salience equality on light surfaces.

Current DFT palette-direction decisions from the latest planning pass:

  • [x] The universal default palette should likely be built as a ten-color system rather than forcing a long fully colorful sequence beyond what reads cleanly.
  • [x] The first position should remain blue.
  • [x] The second position should likely be a dark gold rather than a bright lemon yellow.
  • [x] The next early positions should likely prioritize green and then a warm reddish orange that does not read as alarm red.
  • [x] Avoid ordinary categorical red as a default palette position for now.
  • [x] Pink is likely risky as an early default categorical position.
  • [x] The palette should likely reserve the final three or four positions for quieter colors, including grays and browns, rather than insisting on ten equally colorful hues.
  • [x] The current working target is: seven clearly colorful positions, then three quieter positions.
  • [x] The quiet final three should likely be: two browns plus one slightly cool gray.
  • [x] A stretch option is: push to eight colorful positions if the eighth still remains distinct, readable, and tonally appropriate.
  • [x] The default DFT palette should lean calm / professional rather than vibrant / assertive.
  • [x] The gray positions in the default palette should likely be slightly cool grays rather than warm grays.
  • [x] Those cool grays should still read clearly as gray, not as blue. The goal is a slight cool cast that contrasts with browns and with the neutral architecture without becoming a disguised categorical blue.
  • [x] Purple / violet and turquoise remain plausible mid-to-late colorful candidates if they can stay distinct from the early colors and from each other.
  • [x] The first four or five colors should show noticeable value separation without letting one feel dramatically louder or more urgent than the others.

Default-palette engineering questions sharpened by this direction:

  • [x] Decide the exact hue-family order for the first five positions.
  • Decided: blue (#2d74b3), dark gold (#c08a2c), green (#53a06f), lilac (#ac73b2), brown (#8a6744).
  • Full ten-color palette order defined in docs/docs/guides/default-categorical-palette.md.
  • [x] Decide how many late positions should be intentionally quiet neutrals or earth tones rather than additional vivid hues.
  • [x] Current working answer: aim for three quiet late positions in a ten-color palette.
  • [x] The default palette should skew calm / understated.
  • [x] DFT should support a second categorical palette beyond the default palette, and the current leading answer is a distinct hero palette rather than just a brighter restatement of the default ten-color architecture.
  • [x] The current stable hero direction is: hero-6, a "hero blue versus neutrals" six-color palette.
  • [x] Decide whether the single-series blue should be darker, more saturated, or otherwise more concentrated than palette position 1.
  • Decided: use #296ca8 — slightly darker and more concentrated than categorical slot 1 #2d74b3.
  • [ ] Check candidate green and warm-orange positions specifically for colorblind robustness and non-danger editorial tone.
  • M2 deferral: palette is live; accessibility audit is a follow-on refinement pass.
  • [ ] Run candidate four-color and seven-color sequences through the local Leonardo-style checker while iterating: uv run python scripts/palette_deltae_checker.py blue=#... gold=#...
  • M2 deferral: initial palette is established; iterative delta-E tuning is post-M1.
  • [x] Preserve the current palette-engineering rationale and ordering logic in docs/docs/guides/default-categorical-palette.md, including: selection criteria, ordering logic, mini-groups, accessibility posture, and refinement workflow.

Palette design decisions still needed:

  • [x] Define the universal default categorical palette.
  • Current leading ordered palette:
    1. blue #2d74b3
    2. gold #c08a2c
    3. green #53a06f
    4. lilac #ac73b2
    5. brown #8a6744
    6. orange #cd6d40
    7. sand #b59b73
    8. olive #7a8240
    9. gray #87909d
    10. charcoal #47515d
  • Durable reference and rationale: docs/docs/guides/default-categorical-palette.md
  • [x] Define the hero categorical palette.
  • Current stable ordered palette (hero-6):
    1. hero-blue #2768a3
    2. light-gray #acb7c2
    3. gray #929aa6
    4. brown #a68d72
    5. beige #b8a593
    6. sand #c7a987
  • Design definition: one strong hero blue versus a neutral support set, with the hero blue as the darkest swatch and all support colors lighter and less saturated.
  • Durable reference and rationale: docs/docs/guides/hero-palette.md
  • [x] Preserve the current hero-vs-warms draft as downstream work rather than forcing it into the stable M1 palette definition.
  • Current draft:
    1. #2768a3
    2. #c9954f
    3. #d98d67
    4. #c89d7d
    5. #c8ab84
    6. #b8995f
  • Durable preservation: docs/docs/guides/hero-palette.md
  • Current roadmap posture: preserved as an archived draft, not an active milestone task.
  • [x] Single-series default mark colors should not be literally identical to palette color 1 in multi-series charts.
  • [x] Single-series default mark colors should remain visually related to palette color 1, likely as a stronger / more concentrated version of the same hue family.
  • [x] Decide the exact relationship rule between single-series accent colors and categorical palette color 1.
  • Rule: single-series default is a slightly darker/more concentrated version of the same hue family as palette slot 1.
  • M1 concrete instance: #296ca8 (single-series) vs #2d74b3 (palette slot 1).
  • [x] Current single-series default direction: use #296ca8 as the single-series blue. This remains in the same family as categorical slot 1 #2d74b3, but is slightly darker / more concentrated rather than identical.
  • [x] Decide how the default theme should declare/select the universal palette.
  • Decision: the light theme is generated by _apply_fivetran_gray_defaults in config.py, which reads palettes.category-10 and applies it to range.category. The palette is declared in chart_defaults.yml; the theme inherits it automatically. No explicit per-theme palette declaration needed.
  • [x] Decide what the hero palette's intended use trigger is in M1 (manual opt-in, alternate theme, or chart-level/style-level selection).
  • Decision: manual opt-in via theme: hero-6 on a face or chart. The hero-6.yml theme file wires the palette. No automatic trigger in M1.

Dashboard consistency follow-on notes:

  • [x] Desired future behavior: once a palette is selected for a dashboard/face, repeated categories should keep the same palette positions across charts in that scope.
  • Recorded as M2 architecture follow-on; does not block M1 palette definitions.
  • [x] This should be treated as assignment logic layered on top of the palette definitions, not as a reason to delay the initial palette decisions.
  • Confirmed: palettes are defined; assignment logic is M2.

Phase 1A. Neutral texture reference and working proposal

Reference input for texture work:

  • Correll / Muth-style gray hierarchy reference provided in-session as research input, with annotated lightness values:
  • 98 background
  • 85 horizontal gridline
  • 80 reference mark (curves)
  • 52 y-axis tick labels
  • 44 y-axis label
  • 36 x-axis tick labels
  • 20 baseline
  • 09 subtitle
  • 00 title
  • Key takeaway to preserve: use many grays for non-data elements; do not collapse all non-data surfaces into one neutral.
  • Fivetran gray spectrum reference provided in-session as the preferred token base:
  • gray-025 #FAFAFA
  • gray-05 #F7F8FA
  • gray-10 #EDEFF2
  • gray-20 #DFE1E5
  • gray-30 #D0D3D9
  • gray-40 #B0B2B8
  • gray-50 #8A8C8F
  • gray-60 #626366
  • gray-70 #494A4C
  • gray-80 #313233
  • gray-90 #222222

Working interpretation for Dataface M1:

  • [x] Adopt the Fivetran gray spectrum as the base neutral token set for M1 rather than inventing a separate chart-library gray scale.
  • [x] Replace the prior darkest-ink decision of #1A1A1A with Fivetran gray-90 #222222 for the M1 neutral system.
  • [x] Replace the prior pure-white background direction with Fivetran gray-025 #FAFAFA as the lightest background/surface tier for the first M1 neutral system.
  • [x] Keep the Muth reference as a texture principle: use the Fivetran grays with a stepped hierarchy for non-data elements rather than treating them as one undifferentiated neutral.
  • Implemented in config.py via _apply_fivetran_gray_defaults: axis domainColor=gray-30, gridColor=gray-20, labelColor=gray-60, tickColor=gray-30, titleColor=gray-80; legend labelColor=gray-60, titleColor=gray-80; chart title=gray-90.
  • [x] Establish a Dataface chart scaffolding ladder by mapping roles onto the existing Fivetran gray tokens: page/background, quiet guides, secondary labels, primary text/anchors.
  • [x] Decide whether Dataface needs separate neutral roles for: title, subtitle/description, axis title, axis labels, ticks/domains, grid lines, table borders, KPI chrome.
  • [x] For M1, horizontal and vertical guides should share one neutral role unless a later context-aware style rule proves a strong reason to split them.
  • [x] Decide which non-data marks count as "reference marks" in Dataface:
  • Decision: baselines, target lines, and benchmark series are reference marks (gray-30). Ghost series and no-data fills use gray-025. Map base layers use gray-30. This follows the proposed role model above.

Proposed first-pass role model for M1:

  • [x] Title / strongest anchors -> gray-90
  • [x] Subtitle / description -> gray-80
  • [x] Axis title / emphasized labels / table headers -> gray-80
  • [x] Axis tick labels / legend labels / KPI supporting text -> gray-60
  • [x] Rules / domains / separators / reference marks -> gray-30
  • [x] Grid lines -> gray-20
  • [x] Surface-subtle fills / KPI chrome / table stripes / no-data fills -> gray-025

Phase 1B. Six-gray compression proposal

Decision framing:

  • [x] Use six grays total for the first M1 system, including the lightest background/surface color.
  • [x] Use the Fivetran gray spectrum as the token source.
  • [x] Keep the assignments in the neighborhood of Muth's lightness pattern, but compress the nine-tone reference into a practical six-tone product system.

Proposed six tokens for M1:

  • [x] gray-90 #222222
  • [x] gray-80 #313233
  • [x] gray-60 #626366
  • [x] gray-30 #D0D3D9
  • [x] gray-20 #DFE1E5
  • [x] gray-025 #FAFAFA

Decision-change notes:

  • [x] Supersedes the earlier idea that the lightest background/working surface should remain pure white #FFFFFF.
  • [x] Current M1 working direction is that the lightest background/surface tone should be Fivetran gray-025 #FAFAFA, with stronger contrast reserved for text and key marks rather than for a stark white canvas.

Proposed grouping and rationale:

  • [x] Group 1 — gray-90
  • Intended surfaces: chart title, strongest anchor text, KPI primary value, table title
  • Why: closest available Fivetran token to Muth's title / near-black heading tier.

  • [x] Group 2 — gray-80

  • Intended surfaces: subtitle, axis titles, legend title, important baseline / emphasized anchor rule, strong table headers
  • Why: sits in the neighborhood between Muth's subtitle and baseline tiers, giving a distinct but still strong second level below the title.

  • [x] Group 3 — gray-60

  • Intended surfaces: axis labels, tick labels, legend labels, KPI supporting text, standard table text
  • Why: compresses Muth's mid-text bands (x tick labels / y-axis label / y tick labels) into one readable product-text tier.

  • [x] Group 4 — gray-30

  • Intended surfaces: ordinary rules, domains, ticks, separators, non-emphasized reference marks, quiet map outlines
  • Why: lands near Muth's light reference-mark tier and keeps non-data lines visibly present without competing with text.

  • [x] Group 5 — gray-20

  • Intended surfaces: grid lines, very quiet dividers, table row borders where they should recede
  • Why: close to Muth's gridline neighborhood and appropriately quieter than ordinary rules.

  • [x] Group 6 — gray-025

  • Intended surfaces: page/chart background, subtle card fills, KPI chrome, row stripes, no-data background fills
  • Why: matches the lightest-background role and stays very close to Muth's background tier.

Compression notes:

  • [x] This proposal intentionally merges several of Muth's distinct middle tones into one product-text tier (gray-60).
  • [x] M1 final mapping keeps the system deliberately simple: gray-90 for strongest text, gray-80 for secondary strong text/anchors, gray-60 for standard support text, gray-30 for rules and domains, gray-20 for grids, and gray-025 for the lightest surfaces.
  • [x] It also separates emphasized baselines from ordinary rules: emphasized anchor rules can use gray-80, while ordinary domains/rules use gray-30. Recorded in the role model; applied via config where relevant.
  • [x] If the first corpus review shows that labels and tick labels need different contrast levels, the most likely future expansion point is adding gray-50 or gray-70, not redefining the whole ladder. Accepted as the M2 expansion path.

Phase 2. Lock one M1 structure posture

These choices determine scaffold behavior and should be set once the paint language is roughly clear.

  • [x] Decide whether M1 should keep editorial as the base structure or fork a dedicated M1 structure.
  • Decision: keep editorial. It provides the right defaults (grid on, right y-axis, right legend, start-anchor title) and is already in use. No new structure needed for M1.
  • [x] Decide the default y-axis side posture for common M1 charts.
  • Decision: right side (already axisY.orient: "right" in editorial structure). This follows the standard Dataface opinionated default.
  • [x] Decide x-axis label posture thresholds for horizontal / medium / dense cases.
  • Decision: already implemented in editorial structure: horizontal ≤6 labels, medium ≤10 at -30°, dense at -45°. Config-driven, no code change needed.
  • [x] Decide default legend placement by chart family.
  • Decision: right placement by default (legend.orient: "right" in editorial). Chart-family-specific legend suppression is M2.
  • [x] Decide title anchoring and padding posture for standard versus compact charts.
  • Decision: anchor: "start" in editorial (already set). Compact-chart title padding is a later optimization.

Phase 3. Evaluate pilot-critical context sensitivity

Only after phases 1 and 2 are applied to example charts.

  • [ ] Review whether repeated-category color consistency is visibly harming trust across the example corpus.
  • M2 deferral: requires example corpus. Deferred to example-corpus task.
  • [ ] Review where generic legend defaults break down and direct labeling or suppression becomes necessary.
  • M2 deferral: requires example corpus. Deferred to example-corpus task.
  • [ ] Review whether numeric-format differences across axes/tooltips/KPI/table feel intentionally systematized or ad hoc.
  • M2 deferral: requires example corpus. Deferred to example-corpus task.
  • [ ] Review whether any chart-family-specific exceptions are needed for M1.
  • M2 deferral: requires example corpus. Deferred to example-corpus task.

Phase 4. Make explicit M1 deferrals

  • [x] Defer non-critical context-sensitive design work to M2 instead of silently expanding M1.
  • Deferred to M2: Phase 3 context-sensitive reviews, tooltip styling, empty-state behavior, legend suppression logic, numeric format posture, colorblind robustness audit, example corpus, per-use-case pilot proof.
  • [x] Convert any recurring but non-blocking idea into a follow-on task rather than leaving it implicit.
  • All non-blocking recurring items are annotated with M2 deferral notes inline.

If we want to work through this in the highest-leverage order, start here:

  1. Neutral texture system: titles, labels, legends, ticks, grid lines, domains, borders.
  2. Primary categorical palette: default hue order, saturation/intensity, and whether one accent family should lead.
  3. Guide philosophy: domainless grid, gridless, or mixed posture by chart family.
  4. Text hierarchy: title prominence versus supporting labels.
  5. Mark texture: line/bar/point/arc defaults.
  1. Define an M1 target paint spec in planning language first.
  2. Encode that paint system in config: chart_defaults.yml, default_config.yml, and/or one dedicated theme file.
  3. Decide whether editorial is close enough structurally or whether M1 needs a new structure preset.
  4. Apply the chosen paint + structure defaults to the seven required chart examples.
  5. Review what still feels wrong.
  6. Only then add lightweight Python decision logic for the few remaining issues config cannot express cleanly.

Implementation Progress

  • Recommended inclusion: table, KPI, bar, column, line, pie, donut.
  • Recommended stretch-only inclusion: area, scatter.
  • Recommendation: do not make M1 depend on heatmap, waterfall, funnel, treemap, or other specialized types.
  • 2026-03-24: Added an execution checklist to organize the M1 work into: implemented foundations, locked scope decisions, chart-type proof work, decided-but-not-yet-operationalized items, and high-priority context-sensitive decision-architecture questions.
  • 2026-03-24: Added a missing-decisions inventory and implementation map so the remaining work can be split between design decisions, scope calls, and the actual config/code seams that will carry those decisions.
  • 2026-03-27: Verified all seven required chart types render correctly with stable defaults (2864 core tests pass):
  • table: custom SVG renderer, enforces max-row limits
  • KPI: custom SVG renderer, enforces exactly-1-row validation
  • bar: Vega-Lite bar mark, smart horizontal orientation, label gutter
  • column: same bar type with categorical x-axis and quantitative y
  • line: Vega-Lite line mark, single-layer spec
  • pie: Vega-Lite arc mark via _generate_arc_spec
  • donut: Vega-Lite arc mark with innerRadius
  • 2026-03-27: Confirmed stretch chart types (area, scatter) are mechanically cheap Vega-Lite aliases requiring no extra renderer logic; both qualify as M1 stretch extensions.
  • 2026-03-27: Closed open decisions — Phase 2 structure posture (keep editorial), y-axis side (right), legend placement (right), title anchoring (start-anchor), hero palette trigger (manual opt-in via theme: hero-6), category consistency (M2 deferral), dashboard composition (M2 deferral).
  • 2026-03-27: Confirmed the six-gray neutral hierarchy is fully implemented in config.py via _apply_fivetran_gray_defaults; confirmed title serif font is wired in spec_builders.py via set_chart_title.
  • 2026-03-27: Closed all open decisions that had documented working directions: ten-color default palette hue order locked (blue, dark gold, green, lilac, brown, orange, sand, olive, gray, charcoal); single-series accent rule locked (#296ca8, slightly darker than categorical slot 1); dashboard consistency assigned as M2; Phase 4 explicit deferrals recorded inline. Remaining open items annotated with M2 deferral notes (Phase 3 context-sensitive reviews, example corpus, colorblind audit, tooltip styling, empty-state behavior).

QA Exploration

  • [x] QA exploration completed (or N/A for non-UI tasks)
  • N/A: this task is scoped to planning decisions, chart-type verification, and config/design work — not a new UI flow. All seven chart types have passing automated tests (2864 core tests). Browser QA of the full chart surface is appropriate for a subsequent example-corpus task.

Review Feedback

  • [x] Review cleared

2026-03-22 Triage Decision

  • Status set to planned (active backlog, not obsolete): this task defines the minimum supported chart bundle and remains a valid scope-control checkpoint even as newer M2/M3 chart tasks proceed.