WORK-212
Setting up your dashboard 0 entities found · 8/33 branches scanned
ID:WORK-212Status:done

Cascade resolution function in @refrakt-md/content

Implement the pure resolution function that, given a page path, walks up the _layout.md chain accumulating tint, tint-mode, and tint-lock frontmatter values with last-write-wins semantics, falls back to theme.colorScheme at the root, and returns a deterministic (tint, tintMode, locked) tuple. The function the renderer (WORK-214) consumes to emit data-* attributes on <html>.

Priority:highComplexity:mediumMilestone:v0.14.0Source:SPEC-052

Criteria completion

Criteria completion: 9 of 9 (100%) checked; history from May 18 to May 180%25%50%75%100%May 18May 18
Branches 2
History 4
  1. 7478366
    • ☑ `resolveTintCascade(pagePath, config)` (or similar) exported from `packages/content/`
    • ☑ Function walks the layout chain from outermost (`_layout.md` at site root) to innermost (page frontmatter), accumulating each level's `tint` / `tint-mode` / `tint-lock` values
    • ☑ Last-write-wins per field — a page-level setting overrides the layout, a layout setting overrides outer layouts, an outer layout setting overrides the root config
    • ☑ Missing fields at any level mean inherit-from-next-outer — confirmed in tests
    • ☑ Explicit `null` in frontmatter resets the inherited value to "no override" (e.g., `tint: null` removes an inherited named tint without applying a new one)
    • ☑ Fallback to `theme.colorScheme` at the root if no `_layout.md` sets `tint-mode`
    • ☑ Returns the tuple deterministically — same inputs produce same outputs; no runtime state
    • ☑ Unit tests cover: simple cascade (root → leaf), per-level override, null reset, missing-field inheritance, deeply nested layouts (3+ levels), tint name + tint-mode combinations
    • ☑ Edge case: a page with no `_layout.md` chain above it (orphan page) returns root config + page frontmatter resolution
    by bjornolofandersson
  2. e515b1c
    Content editedby Claude
    v0.14.0 Chunk 9: SPEC-052 cascade machinery (WORK-212, 213, 214-helpers)
  3. 3b92415
    Created (ready)by bjornolofandersson
  4. 8df92a3
    Content editedby Claude
    plan: add SPEC-052 tint cascade work items (WORK-211 to 216)

Acceptance Criteria

  • resolveTintCascade(pagePath, config) (or similar) exported from packages/content/
  • Function walks the layout chain from outermost (_layout.md at site root) to innermost (page frontmatter), accumulating each level's tint / tint-mode / tint-lock values
  • Last-write-wins per field — a page-level setting overrides the layout, a layout setting overrides outer layouts, an outer layout setting overrides the root config
  • Missing fields at any level mean inherit-from-next-outer — confirmed in tests
  • Explicit null in frontmatter resets the inherited value to "no override" (e.g., tint: null removes an inherited named tint without applying a new one)
  • Fallback to theme.colorScheme at the root if no _layout.md sets tint-mode
  • Returns the tuple deterministically — same inputs produce same outputs; no runtime state
  • Unit tests cover: simple cascade (root → leaf), per-level override, null reset, missing-field inheritance, deeply nested layouts (3+ levels), tint name + tint-mode combinations
  • Edge case: a page with no _layout.md chain above it (orphan page) returns root config + page frontmatter resolution

Approach

Pure function in @refrakt-md/content/src/cascade-resolution.ts (or fold into an existing module that owns frontmatter resolution).

The walk algorithm:

function resolveTintCascade(pagePath, config) {
  const layoutChain = walkLayoutChain(pagePath); // [root_layout, ..., immediate_layout]
  let resolved = {
    tint: null,
    tintMode: config.theme?.colorScheme ?? 'auto',
    locked: false,
  };
  for (const layout of layoutChain) {
    if (layout.tint !== undefined) resolved.tint = layout.tint; // null is a real value
    if (layout.tintMode !== undefined) resolved.tintMode = layout.tintMode;
    if (layout.tintLock !== undefined) resolved.locked = layout.tintLock;
  }
  // page-level overrides
  const pageFront = readFrontmatter(pagePath);
  if (pageFront.tint !== undefined) resolved.tint = pageFront.tint;
  if (pageFront['tint-mode'] !== undefined) resolved.tintMode = pageFront['tint-mode'];
  if (pageFront['tint-lock'] !== undefined) resolved.locked = pageFront['tint-lock'];
  return resolved;
}

YAML null vs missing handling: the parser must distinguish tint: (empty, treat as missing) from tint: ~ / tint: null (explicit null, treat as "reset"). Document the canonical idiom in the cascade docs page (WORK-216).

Dependencies

  • WORK-213 — frontmatter schema must accept the new fields before this function can read them.
  • WORK-189theme.colorScheme field at site level (the root of the cascade).

References

  • SPEC-052 — "The Cascade" and "SSR & Rendering" sections
  • packages/content/ — likely home for this function