You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
svelte-kit-vice/audit/components/date-range-field.md

9.8 KiB

Audit: date-range-field

audit-version: 1 audited-at: 2026-06-26 scope: method: adversarially-verified workflow (analyze → refute); HIGH/CRITICAL lead-verified. Clean-check pseudo-findings dropped. B4 ground-truth: segmented date/time/color-field register inputId via $effect (SYS-A30-EFFECT, no loop); number-field is the direct-assignment reference. provider: G:\dev\svelte\vicen\src\uix\soma\components\date-range-field\date-range-field-provider.svelte.ts field-family (A13/A24-26/A30): A13: NOT APPLICABLE - no hidden-input parts. A24: MISSING warning for readonly+undefined. A25: IMPLEMENTED split segments, test coverage gap. A26: INHERITED from DateField composition. A30: CLEAN - labelNode direct callback, inputId uses untrack()

Summary

Counts (post-verification): CRITICAL 0 · HIGH 0 · MEDIUM 2 · LOW 3.

Findings

MEDIUM: FIELD FONT RULE: field LABEL is ONE typographic step BELOW the input; input font is 1:1 wi — date-range-field-002

  • dimension: E-bis
  • rule: FIELD FONT RULE: field LABEL is ONE typographic step BELOW the input; input font is 1:1 with type scale
  • location: G:\dev\svelte\vicen\src\uix\eidos\components\date-range-field\date-range-field.css lines 54-55
  • evidence: [data-date-range-field-label] { font-size: var(--date-field-label-font-size, var(--font-size-sm)); } hardcodes label to sm for all input sizes. Compare to DateField which uses calc(1em - (var(--font-size-md) - var(--font-size-sm))). Correct only at md size.
  • impact: Label font size violates typographic hierarchy at sizes xs/sm/lg/xl
  • proposed-fix: Use: font-size: calc(1em - (var(--font-size-md) - var(--font-size-sm))); and ensure it inherits 1em from the input's dynamic --_date-field-font-size
  • verify: [confirmed] Confirmed MEDIUM. date-range-field.css:51-55 sets [data-date-range-field-label] { font-size: var(--date-field-label-font-size, var(--font-size-sm)); } — fixed at sm for every input size, and notably no font: inherit. The reference DateField (date-field.css:104-111) does it correctly: font: inherit; then font-size: calc(1em - (var(--font-size-md) - var(--font-size-sm))); with the comment 'Label one typographic step below the input ... Scales.' So DateRangeField's label is correct only at md and violates the FIELD FONT RULE (label one step below input, scaling) at xs/sm/lg/xl. Note: systemic — time-range-field.css:52-56 has the identical hardcoded-sm bug.
  • fix-status: open

MEDIUM: Tests must cover high-risk paths including split readonly segments per endpoint (A25) — date-range-field-005

  • dimension: F
  • rule: Tests must cover high-risk paths including split readonly segments per endpoint (A25)
  • location: G:\dev\svelte\vicen\src\uix\soma\components\date-range-field\date-range-field-provider.svelte.test.ts lines 73-74
  • evidence: Test creates startReadonlySegments and endReadonlySegments but does not verify they work independently. No test case sets different readonly segments for start vs end.
  • impact: Missing test coverage for A25 (split start/endReadonlySegments) — divergent readonly settings may not work correctly without verification
  • proposed-fix: Add test case where startReadonlySegments=['month'] and endReadonlySegments=['day'] to verify each endpoint respects its own readonly configuration
  • verify: [confirmed] Confirmed but lower confidence. The provider test (date-range-field-provider.svelte.test.ts:73-74) seeds startReadonlySegments/endReadonlySegments as empty state<EditableSegmentPart[]>([]) and never exercises divergent start-vs-end values; no test asserts A25 split behavior. The split-readonly wiring actually lives in components/date-range-field-input.svelte:73-77 (start→startReadonlySegments, end→endReadonlySegments), which this provider-level test cannot reach anyway — so the coverage gap is real but the untested code is trivial, type-checked forwarding. Genuine F-gap on a path the rubric calls high-risk (A25 says a single symmetric readonlySegments is wrong), so MEDIUM stands; the missing assertion would belong to a component/DOM test, not this provider test.
  • fix-status: open

LOW: Parts registered via runtime.part('x') must exist in morfo & vice-versa. 2-of-3 rule. — date-range-field-001

  • dimension: A
  • rule: Parts registered via runtime.part('x') must exist in morfo & vice-versa. 2-of-3 rule.
  • location: G:\dev\svelte\vicen\src\uix\soma\components\date-range-field\date-range-field-provider.svelte.ts lines 1-30
  • evidence: Morfo declares 3 parts: Provider (line 20), Label (line 46), Input (line 59). Provider calls runtime.part('provider', ...) at line 110. Label calls runtime.part('label', ...) at line 192. Input part is NOT registered via runtime.part. DateField by contrast registers 'input' with runtime.part.
  • impact: Input part attrs not enforced by runtime; contract violation breaks 2-of-3 semantic guarantee
  • proposed-fix: DateRangeFieldInputProvider should call runtime.part('input', {...}) in its constructor to register the Input part, matching DateField's pattern
  • verify: [downgraded] Downgraded HIGH→LOW. The claim that the morfo 'input' part is unregistered and 'breaks the 2-of-3 guarantee' is not supported. The part IS materialized: components/date-range-field-input.svelte:107-109 overlays [dateRangeFieldAttrs.input]: '' (from createAttrs(dateRangeFieldMorfo)) plus 'data-endpoint': type, and role=group/aria come from the composed DateField input provider (date-field-provider.svelte.ts:913 runtime.part('input',...)). The 2-of-3 rule is satisfied — soma emits data-date-range-field-input+data-endpoint, eidos recipe targets them (date-range-field.css:60,65-71). Pattern is deliberate, documented (CSS header lines 12-16, README lines 72-74) and identical to TimeRangeField. No behavioral/a11y break; the part renders correctly. Real residue is only LOW: the DateRangeField-specific marker attrs are emitted via createAttrs rather than routed through runtime.part('input')/assert, so the DateRangeField runtime doesn't itself enforce that part's contract on the element — a consistency nit, not a contract violation. The cited line numbers in the evidence ('Provider line 110','Label line 192') are real, but the conclusion (Input part missing) is wrong because the part is emitted by the .svelte component, which the candidate did not read.
  • fix-status: open

LOW: Recipe font-size/icon-size tokens MUST reference --font-size-/--icon-size- (a literal px — date-range-field-003

  • dimension: E-bis
  • rule: Recipe font-size/icon-size tokens MUST reference --font-size-/--icon-size- (a literal px/rem/em/% is drift)
  • location: G:\dev\svelte\vicen\src\uix\eidos\components\date-range-field\date-range-field.css line 83
  • evidence: font-size: 0.9em; /* literal: separator scales with the field font-size */
  • impact: Magic literal bypasses coherence guard; separator scaling not tokenized
  • proposed-fix: Extract to token --date-range-field-separator-font-scale: 0.9; in recipes/base.ts
  • verify: [downgraded] Kept LOW but flagged as defensible/style. date-range-field.css:83 font-size: 0.9em; /* literal: separator scales with the field font-size */ is a relative multiplier on a decorative ::before em-dash, not an absolute px/rem drift, and is explicitly commented as scaling with the inherited field font. There is no canonical token expressing '0.9× inherited size' (the --font-size-* scale is absolute steps, not multipliers), so tokenizing it as proposed would not improve coherence. Identical 0.9em appears in time-range-field.css:84. Borderline taste — not a meaningful magic-literal defect.
  • fix-status: open

LOW: Bare opacity decimal -> --opacity-* (0.05 step) — date-range-field-004

  • dimension: E-bis
  • rule: Bare opacity decimal -> --opacity-* (0.05 step)
  • location: G:\dev\svelte\vicen\src\uix\eidos\components\date-range-field\date-range-field.css line 89
  • evidence: opacity: var(--date-field-disabled-opacity, 0.55); fallback 0.55 is hardcoded, not a --opacity-* token
  • impact: Magic opacity literal in CSS; inconsistent with token-based opacity scale
  • proposed-fix: Use: opacity: var(--date-range-field-disabled-opacity, var(--opacity-disabled));
  • verify: [confirmed] Confirmed LOW (kept). date-range-field.css:89 opacity: var(--date-field-disabled-opacity, 0.55);. The fallback 0.55 is DEAD: the date-field recipe defines 'disabled-opacity': 'var(--opacity-disabled)' (base.ts:1222) → --date-field-disabled-opacity (= --opacity-disabled = 0.4, generated/base.css:296) is always defined on [data-date-field], and the range root overlays data-date-field='' (date-range-field.svelte:44), so the token resolves and the literal never renders. So this is not a live magic-opacity number affecting rendering — it's a misleading dead fallback whose value (0.55) also disagrees with the token it shadows (0.4). LOW token-hygiene nit, not a rendering defect. Same dead 0.55 in time-range-field.css:90.
  • fix-status: open

No-findings dimensions

C, D, G

Theming facts (E-bis)

  • magic z-index: none
  • magic literals: 0.9em (separator font-size) | 0.55 (opacity fallback)
  • undeclared parts: none
  • roles clean: true · variants clean: true
  • label-font (one step below input?): VIOLATED: hardcoded to --font-size-sm instead of dynamic calc(); correct at md, wrong at xs/sm/lg/xl

Tests (F)

  • exists: true · env: jsdom
  • covers: validation ordering; min/max bounds; custom validator; label click focus
  • untested: split readonly independence; readonly-without-value warning; shared placeholder constraint; endpoint validation delegation

Style observations (non-blocking)

  • Separator comment notes that literal em is intentional for scaling but should be tokenized
  • Label font-family references date-field-label-font-family (correct layering)
  • Grid layout uses space-* tokens consistently

Powered by TurnKey Linux.