hds-feminine-cycle-ui
Custom UI for cervical-fluid / cycle-tracking events in the HDS ecosystem — render the same data as FEMM, Billings (BOM), Creighton Model, or Mira charts. Layout-agnostic: the same cell renderer is reused as a timeline marker, a form picker button, a diary-card glyph, or a calendar-grid cell. Hosts position it.
Also ships CervixPositionMarker — a standalone glyph for body-vulva-cervix-position 3-D vector events (height / firmness / openness).
Visual vocabulary
Section titled “Visual vocabulary”The grids below show every option key each method renders, with English labels. They are auto-generated from the live spec by npm run docs:images (script: scripts/render-method-images.mjs) — run after editing a spec to keep docs in sync.
FEMM — dot-circle
Section titled “FEMM — dot-circle”Billings (BOM) — stamp-square
Section titled “Billings (BOM) — stamp-square”Creighton Model — stamp-square
Section titled “Creighton Model — stamp-square”The full 33-code grid:
Mira — dot-circle
Section titled “Mira — dot-circle”Cervical-position glyph
Section titled “Cervical-position glyph”Standalone React component — three dimensions encoded in one 28-px glyph following the SHOW mnemonic (Soft, High, Open, Wet):
- Height → horizontal “horizon” bar y-position. Low = bar at top of cell (shallow reach), High = bar at bottom (deep reach).
- Firmness → ring stroke width. Soft = thin (fertile), Firm = thick.
- Openness → inner-hole radius. Open = large hole, Closed = solid + center dot.
Mean of the three signals tints the ring slate (infertile) → teal (fertile).
Public API
Section titled “Public API”import { RepresentationCell, // cycle-fluid cells (dot-circle | stamp-square) CervixPositionMarker, // 3-D vector → SHOW glyph composeCellInput, // events → CellInput (half-and-half on overlap) detectFertilityWindow, // sliding-window peak/fertile detection registry, // built-in + user-extensible registrations samplePreviewEvents, // shared 7-day fixture for previews femmSpec, billingsSpec, creightonSpec, miraSpec} from 'hds-feminine-cycle-ui';Force-conversion across methods
Section titled “Force-conversion across methods”When a stored event’s source method differs from the chosen rep’s bound method (e.g. Mira-imported data displayed under FEMM), pass a closestOption callback that wraps the host’s EuclidianDistanceEngine.fromVector:
const closestOption = (methodId, vectors) => { const engine = model.converters.getEngine('cervical-fluid'); return String(engine.fromVector(methodId, vectors).data ?? '');};const input = composeCellInput(events, rep, { closestOption });The host typically pre-loads the cervical-fluid engine via model.converters.ensureEngine('cervical-fluid') so the first render can force-convert synchronously.
Package info
Section titled “Package info”- ESM,
"type": "module", Node ≥ 24, peer-dep React 19. - TypeScript source in
ts/, builtjs/is not in git (built viaprepare: tsc). - No runtime dependency on
hds-libordata-model— the package builds standalone.