/** * Placement Validator * * Structural checks on WHERE an Eddie component sits, as opposed to what it * is. Every other markup validator judges an element on its own attributes * (slot names, link text, knockout); this one judges an element by its * parent and siblings. It exists because guidance held only as prose does * not stop a generator: eddie-brain already said "not for single paragraphs" * and "the description goes in the header slot", a composer that copied both * lines into its catalog still emitted four bare `ed-text-passage`s at the * page root (#1890, Brad-Frost-Web/bf-brain#1154). * * Scope: * - `.html` files (consumer projects, boilerplates, generated pages) * - Lit `html`...`` template regions inside `.ts` / `.tsx` / `.js` / `.mjs` / `.jsx` * - the product analyzer's tag stream (`findFloatingProse` on a tree it * builds from `scanHtml`), so `eddie_analyze_product` reports the same * finding on a consumer's built pages. * * Checks: * 1. FLOATING PROSE (error) — an `ed-text-passage`, `ed-button`, * `ed-button-group`, or bare `

` that is a direct child of a * page-level rhythm owner (`ed-page`, `ed-main`, `ed-stack`, * `ed-layout-section`, `ed-layout-container`, `ed-band`) with no heading * beside it and no enclosing content container (`ed-section`, `ed-card`, * `ed-page-header`, a recipe, a landmark…). A sentence with no container * has no place on a page: the neighbouring section's header slot (as its * description), `ed-page-header`'s description, or the card it belongs * to is where it goes. The uniform section/region gap of the owner turns * each stray line into its own 64px-padded region (the measured symptom). * * Conservatism (the repo's validator philosophy, #1015/#1530): false * positives are worse than false negatives. Every exemption below is a * legitimate page shape, not a loophole: * - a heading sibling (`ed-heading`, `

`–`

`, `ed-page-header`, or an * `ed-text-passage` carrying its own heading) gives every sibling context. * That is the issue's own definition, and it is also the cheapest route * around the rule — one heading at the top of a root stack exempts every * stray line under it. Whether those lines are the same granularity as * their siblings is #1891's question, not this check's; * - an `ed-text-passage` that carries its own heading is editorial prose * (an article body), not a stray line; * - any ancestor that is not a page-level owner or a bare `
` is a * content container that owns its children — the chain stops there; * - a `slot=` attribute means the element addresses a slot contract, which * the slot-contract validator judges, not this one; * - a `${…}` expression among the siblings (Lit) means the siblings are * unknowable statically — skip rather than guess. * * 2. MIXED-GRANULARITY STACK (warning, #1891) — an `ed-stack` with * `gap="section"` or `gap="region"` whose direct children are not all * regions. The gap is uniform, so a stack's children must be the same * granularity: bf-brain's root `` put a 20px * one-line passage and a 52px button group between sections and each got * 64px above and below — "a monstrous amount of spacing between these * little things". A one-line note belongs inside the neighbouring section * (its header description or body), not between sections; the paved path * for an app page is `ed-main` owning the sequence of `ed-section`s, with * `ed-stack` reserved for same-granularity siblings inside a region. * Conservative: only the tags that are unambiguously line-scale are * counted as small (a passage, a button, a link, a badge…); anything the * rule cannot classify — a `
`, a recipe, a custom element — is taken * to be a region. This is a STACK-level diagnosis ("this stack is the * wrong construct"), so it fires even when check 1 has already reported * each stray line: the incident's root stack gets four floating-prose * errors AND one warning saying to let ed-main own the sections. * * Every rule here is measured against the whole repo before it lands (the * sweep is in the PR that adds it); a rule that fires on Eddie's own pages, * recipes, stories or boilerplates is either wrong or has found a bug to fix * in the same PR. */ import type { HealthIssue } from '../types.js'; import type { MarkupNode } from './markup.js'; export interface MixedGranularityFinding { /** The stack's gap value (`section` | `region`). */ gap: string; /** Tags of the children that are not region-scale, in document order. */ offenders: string[]; /** Total direct children considered. */ childCount: number; /** 0-based line of the stack's open tag within the region. */ line: number; /** 0-based line of the first offending child. */ firstOffenderLine: number; message: string; suggestion: string; } export interface FloatingProseFinding { tag: string; parentTag: string; attrs: string; /** 0-based line within the region (or the token's own line when built from a stream). */ line: number; message: string; suggestion: string; } /** * Walk a parsed tree and report every floating-prose occurrence. Pure — the * validator and the product analyzer both call this. */ export declare function findFloatingProse(roots: MarkupNode[]): FloatingProseFinding[]; /** * Walk a parsed tree and report every section/region-gapped `ed-stack` whose * direct children mix a line-scale element with regions (#1891). One finding * per stack, independent of `findFloatingProse`: that check says a line has * no home, this one says the stack is the wrong construct. */ export declare function findMixedGranularity(roots: MarkupNode[]): MixedGranularityFinding[]; export declare class PlacementValidator { validateFile(filePath: string): Promise; private scanRegion; } //# sourceMappingURL=placement-validator.d.ts.map