content/auto-draft
needs attentionviewing older commit5c9de97 · incrementalPR #304reviewed 2026-07-14 17:02 UTC0H · 5M · 7L · 2I- Purpose
- Auto-drafted blog post on the GDMTH electricity tariff — part of the CFE informational content cluster targeting Mexican energy managers and solar installers.
- Goal
- Publish tarifa-gdmth.mdx with all visual components live (replacing TODO placeholders) and editorial copy accuracy-checked.
- Sub-goals
- SG-1: Content accuracy — voltage range precision ('más de 1 kV y menos de 69 kV'), tariff reform date ('2017-2018'), component definitions clarified
- SG-2: Visual components — activate GdmthBillCallouts hero and add BillCostBreakdownChart overview section
- SG-3: Chart legibility — shared TOU color palette (GREEN/AMBER/HELIOTROPE), dashed block separators, improved captions
- SG-4: Accessibility — WCAG AA link contrast fix in blog-content.css
- What
- Activated the GdmthBillCallouts hero component (previously a TODO comment), added BillCostBreakdownChart as an overview section, introduced a shared TOU color palette, refactored DemandVsConsumptionChart / TouBlocksChart / SolarBillImpactChart with better color consistency and captions, fixed blog link contrast (foreground text + brand underline).
- Why
- The blog post was drafted with TODO placeholders for the hero visual; this commit makes it publication-ready by activating all components and correcting factual details caught in an adversarial copy review.
- Areas
- apps/web/content/blog/tarifa-gdmth.mdx+200−0apps/web/src/components/blog/charts.tsx+438−46apps/web/src/app/(marketing)/blog/[slug]/page.tsx+2−2apps/web/src/styles/blog-content.css+8−2apps/web/content/calendar.yml+1−1
- Blast
- 5 files, +649/-51 across apps/web content + components. No backend, no API, no auth. Pure marketing site content change.
Findings · 14
correctness3
GdmthBillCallouts: GREEN callout anchors to wrong row (kW intermedia, not kW punta)
apps/web/src/components/blog/charts.tsx:602
The GREEN callout for 'Demanda facturable' uses `cardY: rowMid(4)` — the 'kW intermedia' row (244 kW). But the callout text reads 'la de punta alimenta el cargo de capacidad.' The capacidad charge is driven by the punta-block demand, which is row index 5 ('kW punta', 44 kW). The connector dot visually points to the wrong line, directly contradicting the label it introduces. Fix: change `rowMid(4)` to `rowMid(5)`.
DemandVsConsumptionChart: last ToU band ends at hour 23 (scale mismatch with TouBlocksChart)
apps/web/src/components/blog/charts.tsx:100
The x-scale uses `(hour / 23) * plotW`, so x(23) is the right edge and the last band `{ from: 22, to: 23 }` covers only 1/23 of the chart. TouBlocksChart uses `(hour / 24) * plotW` and shows the intermedia tail from 22:00 to 24:00 correctly. The two sibling charts are visually inconsistent about the same block schedule. Extend the last band to `to: 24` and adopt `(hour / 24) * plotW` to match.
GdmthBillCallouts: CYAN callout anchors to kWh intermedia row as representative for all three energy lines
apps/web/src/components/blog/charts.tsx:601
The CYAN connector points to row 1 (kWh intermedia) while the callout describes 'Tres líneas, una por bloque'. Anchoring to the middle of a three-row group is a common callout convention and not strictly wrong, but may look imprecise since the CYAN tint already spans rows 0–2. Consider anchoring to rowMid(1) as-is (group midpoint) or adding a bracket spanning all three rows for clarity.
conventions3
Multi-line docstrings on 6 chart components violate one-line comment rule
apps/web/src/components/blog/charts.tsx
CLAUDE.md: 'Never write multi-paragraph docstrings or multi-line comment blocks — one short line max.' Six components have multi-line block comments added or expanded in this diff: TOU constant (4 lines), SolarBillImpactChart (4 lines), DemandaFacturableSolarChart (5 lines), BillCostBreakdownChart (3 lines), GdmthBillCallouts (5 lines), DemandVsConsumptionChart (4 lines). Each should be collapsed to a single-line comment or dropped — the component name and aria-label already carry the semantic meaning.
Inline comments describe WHAT, not WHY (numerous instances)
apps/web/src/components/blog/charts.tsx
CLAUDE.md: 'Only add a comment when the WHY is non-obvious.' Multiple inline structural comments added in this diff describe structure already visible in the JSX: '// ToU bands behind the curve', '// faint separators at block boundaries', '// energy annotation', '// billable-demand envelope (what you actually pay)', '// measured peak (flat)', '// energy-derived demand (declining)', '// region annotations', '// bill card', '// group tints', '// table header', '// rows', '// callouts + connectors', '// sub-labels for the demand split', '// family labels + brackets', '// the bar', '// legend'. Remove them.
peakOf() sentinel returns {hour:0, kw:-1} when no hours match — renders marker above chart
apps/web/src/components/blog/charts.tsx
If the period predicate matches no LOAD_KW slot (impossible with the current 24-slot fixed schedule but fragile if the schedule changes), `peakOf` returns `kw: -1` and renders a demand dot above the chart area at y(-1). This is not a TypeScript error. A defensive guard or a return type of `{hour: number; kw: number} | null` with a null-skip in the render path would make the intent explicit.
tests2
Blog route /blog/tarifa-gdmth missing from E2E smoke suite
e2e/web/marketing.spec.ts:17
The ROUTES array still omits /blog and /blog/tarifa-gdmth with a stale comment 'not built yet'. With this diff activating GdmthBillCallouts (previously a TODO) and registering two new chart components in the MDX map, a crash in the React tree (broken import, MDX parse error) would go undetected. Adding `{ path: '/blog/tarifa-gdmth', name: 'tarifa-gdmth blog post' }` to ROUTES is a one-liner that closes the gap.
No snapshot/visual-regression tests for new SVG chart components
apps/web/src/components/blog/charts.tsx
BillCostBreakdownChart and GdmthBillCallouts are purely presentational static SVGs with no state, data fetching, or user interaction. TypeScript catches type mismatches at build time. The project has no pre-existing snapshot baseline for any blog chart, so there is no bar being raised. Not a gap relative to project norms.
improvement6
Duplicated segment-bar rendering logic between BillCostBreakdownChart and SolarBillImpactChart
apps/web/src/components/blog/charts.tsx
Both components define the same three-segment array (Energía/VIOLET/60%, Capacidad/GREEN/30%, Distribución/CYAN/10%) and the same `start(i)` / `center(i)` helper pattern. The bar rendering loop (rect + percentage text + optional SunMark) is structurally identical, differing only in bar height and whether a SunMark is rendered. A shared internal `SegmentBar` primitive accepting segs + barY + barH + an `isImpacted` predicate would make a future composition change (e.g. adding a fourth charge) a single-source edit.
GdmthBillCallouts: boxMid magic number (co.y + 29) silently coupled to undeclared box height 58
apps/web/src/components/blog/charts.tsx:635
The callout box height is `height={58}` in the rect and the accent bar, while `const boxMid = co.y + 29` hard-codes half of 58 without naming the constant. Changing the box height requires updating three values. Extract `const CALLOUT_H = 58` and derive `boxMid = co.y + CALLOUT_H / 2`.
GdmthBillCallouts: callout y-coordinates (96, 190, 272) are opaque magic numbers
apps/web/src/components/blog/charts.tsx:601
The three `y` offsets are manually chosen with no derivation from card geometry (cardX=24, y0=102, rowH=28). When card dimensions or row count change, these silently misalign. Deriving them from `rowMid(n) - CALLOUT_H / 2` clamped to the card's vertical range would make them self-correcting.
segCenter / center helpers use optional chaining that is unreachable
apps/web/src/components/blog/charts.tsx
`segs[i]?.pct ?? 0` in `segCenter` and `center` helpers is only called with valid indices from a .map() callback — the optional access is dead code that adds visual noise. With `noUncheckedIndexedAccess` the guard is required by the compiler, but a typed helper or explicit cast would express the intent more clearly.
TouBlocksChart label 'Intermedio' vs TOU object key 'intermedia' — minor lexical mismatch
apps/web/src/components/blog/charts.tsx
The TOU palette key is `intermedia` but the block label displayed in TouBlocksChart is `'Intermedio'`. DemandVsConsumptionChart also uses `Intermedio` in the axis label. The CFE convention uses 'intermedia' for the period name and 'intermedio' for the adjectival form — both are used in the same file, which could confuse future maintainers mapping color keys to displayed text.
CSS: blog link hover color change has no transition — flashes abruptly
apps/web/src/styles/blog-content.css
`.blog-content a:hover` switches `text-decoration-color` from brand-heliotrope to foreground instantly. Adding `transition: text-decoration-color 0.15s ease` to `.blog-content a` makes the interaction feel intentional with minimal cost.
History · 14 commits
- c27ea57needs attentionincremental3H · 5M · 4L2026-08-04 17:46
- ff69645needs attentionincremental0H · 6M · 8L2026-07-28 18:21
- 0257d74safeincremental0H · 0M · 0L2026-07-20 23:00
- 0795a9dsafeincremental0H · 0M · 2L2026-07-20 22:42
- 5c57414needs attentionincremental2H · 7M · 10L2026-07-20 22:29
- 5a57a8fneeds attentionincremental0H · 5M · 8L2026-07-20 17:14
- cc9056eneeds attentionfull1H · 3M · 9L2026-07-20 16:40
- 8b79d33needs attentionincremental5H · 7M · 11L2026-07-14 18:10
- f0c337cneeds attentionincremental1H · 1M · 5L2026-07-14 17:15
- 5c9de97needs attentionincremental0H · 5M · 7L2026-07-14 17:02current
- 92cce08needs attentionincremental1H · 4M · 3L2026-07-14 02:00
- b7dd610needs attentionincremental0H · 6M · 7L2026-07-14 01:43
- 8c43554needs attentionfull0H · 2M · 1L2026-07-14 01:15
- 2149391blockedfull8H · 6M · 7L2026-07-10 17:30