/*
Section visual and header JSON Schema fragments for RFC-0101/RFC-0102: visual sub-schemas and header sub-schemas applied at section root.
- Do not introduce app-specific runtime composition or deployment behavior into this reusable package source file.
- RFC-0303 Phase 3: extracted from shared-section-props/index.ts as part of the domain split.
- RFC-1289: x-text marker capability — section-header.astro resolves
{price:…} in heading (string and segment text) and subheading; eyebrow stays
plain.
*/
import { EFFECT_STACK_ITEM_SCHEMA, ALIGN_ENUM, type JsonSchemaFragment } from "./common.ts";
/** RFC-0101 — section visual contract (background / effects / density / tone / containerVariant / motion). */
export const SECTION_VISUAL_FRAGMENT: JsonSchemaFragment = {
properties: {
background: {
oneOf: [
{
type: "object",
additionalProperties: false,
required: ["kind"],
properties: {
kind: { const: "color" },
color: { type: "string", minLength: 1 },
},
},
{
type: "object",
additionalProperties: false,
required: ["kind", "imageName"],
properties: {
kind: { const: "image" },
imageName: { type: "string", minLength: 1 },
fit: { enum: ["cover", "tile", "stretch-width", "stretch-height"] },
quality: { enum: ["low", "mid", "high", "max"] },
tintOpacity: { type: "number", minimum: 0, maximum: 1 },
},
},
{
type: "object",
additionalProperties: false,
required: ["kind", "texture"],
properties: {
kind: { const: "texture" },
texture: { type: "string", minLength: 1 },
},
},
{
type: "object",
additionalProperties: false,
required: ["kind"],
properties: { kind: { const: "transparent" } },
},
{
type: "object",
additionalProperties: false,
required: ["kind", "direction"],
properties: {
kind: { const: "fade" },
direction: { enum: ["vertical", "horizontal"] },
from: { type: "string", minLength: 1 },
to: { type: "string", minLength: 1 },
startOpacity: { type: "number", minimum: 0, maximum: 1 },
endOpacity: { type: "number", minimum: 0, maximum: 1 },
inset: { type: "number", minimum: 0, maximum: 0.5 },
noStartFade: { type: "boolean" },
noEndFade: { type: "boolean" },
},
},
],
},
effects: {
type: "array",
minItems: 1,
items: {
type: "object",
additionalProperties: false,
required: ["target", "stack"],
properties: {
target: { type: "string", minLength: 1 },
stack: {
type: "array",
minItems: 1,
items: EFFECT_STACK_ITEM_SCHEMA,
},
},
},
},
density: { enum: ["compact", "normal", "spacious", "flush"] },
tone: { enum: ["default", "warning", "success", "muted"] },
containerVariant: { enum: ["default", "narrow", "full"] },
motion: {
type: "object",
additionalProperties: false,
properties: {
reveal: {
type: "object",
additionalProperties: false,
properties: {
variant: { enum: ["fade", "fade-up", "fade-up-stagger"] },
once: { type: "boolean" },
threshold: { type: "number", minimum: 0, maximum: 1 },
},
},
parallax: {
type: "object",
additionalProperties: false,
properties: {
variant: { enum: ["subtle", "balanced", "dramatic"] },
speed: { type: "number", minimum: 0, maximum: 2 },
},
},
stagger: {
type: "object",
additionalProperties: false,
properties: {
delay: { type: "number", minimum: 0, maximum: 2 },
childSelector: { type: "string" },
},
},
off: { type: "boolean" },
},
},
},
};
/** RFC-0102 — section header (tone-segmented heading + optional subheading + alignment + level). */
export const SECTION_HEADER_FRAGMENT: JsonSchemaFragment = {
properties: {
header: {
type: "object",
additionalProperties: false,
required: ["heading"],
properties: {
heading: {
// RFC-1289: section-header.astro resolves {price:…} markers in both
// the plain heading string and each tone-segment text.
oneOf: [
{ type: "string", minLength: 1, "x-text": { markers: ["price"] } },
{
type: "array",
minItems: 1,
items: {
type: "object",
additionalProperties: false,
required: ["text"],
properties: {
text: { type: "string", minLength: 1, "x-text": { markers: ["price"] } },
tone: { enum: ["default", "primary", "accent", "muted", "inverse"] },
},
},
},
],
},
eyebrow: { type: "string", minLength: 1 },
subheading: { type: "string", minLength: 1, "x-text": { markers: ["price"] } },
align: ALIGN_ENUM,
level: { enum: [1, 2] },
hideSectionNumber: { type: "boolean" },
},
},
},
};