Core API

All runtime functions, constants, classes and types below are exported from @page-sections/core. The package has no runtime dependencies, DOM requirement or rendering implementation. Svelte re-exports its types only.

Layout constants and types

ExportValue / shape
layoutTokensCanonical JSON object; see complete token reference
contentGridSizesReadonly tuple: xs, s, m, l
viewportLayoutSizesReadonly tuple: xs, s, m, l, xl, 2xl, 3xl
ContentGridSizeUnion of contentGridSizes values
ViewportLayoutSizeUnion of viewportLayoutSizes values
ContentSpannumber or Partial<Record<ContentGridSize, number>>
LayoutCardSizekeyof layoutTokens.cards: s, m, l
LayoutRectx, y, width, height: number
LayoutInsetstop, right, bottom, left: number
ResponsiveLayoutInputRequired width/height: number; optional scrollbarWidth: number, safeArea: Partial<LayoutInsets>

resolvePageLayout(input: ResponsiveLayoutInput) returns { layout, content: { x, width }, menu: { x, y, width, height } }. It accounts for a reserved scrollbar and safe-area insets while classifying the original viewport width. The host owns those measurements; native Navigation assumes a full-width containing block.

resolveMenuOffset(scrollY: number, fixed = false) returns zero until 112px, then 112 - scrollY; fixed mode always returns zero.

Layout functions

FunctionResult and behavior
resolveViewportLayout(width: number): ViewportLayoutSizeGreatest matching viewport threshold; negative/nonfinite input becomes zero
resolveContentGrid(width: number)size, width, minWidth, columns, gap, headlineSize, headlineLineHeight, bodySize, bodyLineHeight, headlineGap, columnWidth; width clamps to 0โ€“1536
resolveContentSpan(span: ContentSpan | undefined, grid: ContentGridSize): numberMobile-first inheritance, full-row default, integer validation and column-count clamp
contentSpanWidth(width: number, span: number): numberPixel width for a numeric span including internal gaps, excluding outer gutters
resolveCardLayout(containerWidth: number, cardSize: LayoutCardSize = 'm', options: CardLayoutOptions = {})Card token fields plus span, width, height, textHeight, eyebrowRows, mediaFrame and media: LayoutRect

For resolveCardLayout, s/m/l request 2/2/4 columns, except l uses 3 on the s grid. CardLayoutOptions accepts mediaAspect (3/2 default; also 4/3 or 1/1), mediaFit (contain default or cover), and reserveEyebrow (false default, ignored for S). Derive reserveEyebrow from the entire collection, as the Svelte collections do. Width comes from contentSpanWidth; mediaFrame.height is width divided by the selected ratio. Text height is (rows + eyebrowRows) * lineHeight + 2 * cardTextInset.block; total height is mediaFrame.height + textHeight. The media rectangle is inset by 8px for contain (clamped to available space), or fills mediaFrame for cover. These are default catalog measurements, not measurements of arbitrary CSS overrides or editorial copy. Observe the actual CardElements.media rectangle when attaching a renderer.

ts
import {
  resolveContentGrid,
  contentSpanWidth,
  resolveCardLayout
} from '@page-sections/core';
const grid = resolveContentGrid(840);
console.log(grid.size, grid.columns, contentSpanWidth(840, 3)); // m, 8, 293
const card = resolveCardLayout(840, 'm');
console.log(card.width, card.height, card.media);

SectionRegistry and SECTION_REGISTRY

new SectionRegistry(changed?: () => void) creates a navigation registry. SECTION_REGISTRY is its exported context-key symbol, not a singleton registry.

MemberBehavior
sections getterFresh SectionNavigationItem[] with detached item copies, in registry order
register(section: SectionDefinition)Returns update(next: SectionDefinition) and unregister() lifecycle methods
order(ids: readonly string[])Sorts by supplied IDs; unspecified registrations follow in their existing order

Registration checks nonblank titles, IDs matching ^[a-z][a-z0-9-]*$, and page-local uniqueness. Changes call the callback. Updating unchanged metadata emits nothing. Unregister is idempotent, and subsequent updates on that handle do nothing. The registry stores id/title only, not content. Create one per page/request; Svelte ContentPage handles this automatically.

ts
import { SectionRegistry } from '@page-sections/core';
const registry = new SectionRegistry();
const handle = registry.register({ id: 'intro', title: 'Introduction' });
handle.update({ id: 'intro', title: 'Start here' });
console.log(registry.sections);
handle.unregister();

Background normalization

normalizeSectionBackground(background: SectionBackground | undefined, context: SectionBackgroundContext = {}): ResolvedSectionBackground returns effective background values. context.pageKind?: 'content' | 'detail'; detail forces standard size. Svelte Section inherits pageKind from SectionSurfaceGroup and forwards it to the normalizer. Its optional onDiagnostic callback receives deduplicated diagnostics.

ResolvedSectionBackground has color: SectionBackgroundColor, size: standard/full, gradient: boolean, cssBackground: string and optional diagnostic: SectionBackgroundDiagnostic.

SectionBackgroundDiagnostic fields: code (gradient-requires-full-background), message: string, requestedColor: SectionBackgroundColor, requestedSize: standard/full, pageKind: content/detail/undefined, effectiveColor: transparent/standard/muted. Gradients with an effective standard size resolve to their end color.

createSectionBackgroundNormalizer(report?: (diagnostic: SectionBackgroundDiagnostic) => void) returns (background, context) => ResolvedSectionBackground. Unlike the direct function, the returned function's context argument is required (pass {}). It reports each requested-color/size/pageKind combination once per normalizer instance. No global state or automatic console logger is used.

ts
import { createSectionBackgroundNormalizer } from '@page-sections/core';
const normalize = createSectionBackgroundNormalizer((diagnostic) =>
  console.warn(diagnostic.message)
);
const background = normalize(
  { color: 'standardToMuted', size: 'full' },
  { pageKind: 'content' }
);
console.log(background.cssBackground);

SectionSurfaceRegistry and SECTION_SURFACES

new SectionSurfaceRegistry(changed?: () => void) tracks presentation order. SECTION_SURFACES is the exported context-key symbol.

  • register(surface: Pick<ResolvedSectionBackground, 'color' | 'size'>) returns a numeric id, update(next) and unregister(). IDs increase within that instance.
  • order(ids: readonly number[]) deduplicates known IDs and appends unspecified registrations.
  • corners returns { id: number; top: boolean; bottom: boolean }[]. Standard nontransparent neighbors join regardless of color; transparent/full surfaces break the group.
  • Updates after unregister do nothing. A changed callback runs when registration, surface values or order change.

The input alias Surface is internal and is not a named package export. Use the structural Pick type above. See Section Backgrounds and Surface Groups.

Theme and URL helpers

themeStyle(theme: PageTheme = {}): string maps recognized fields to semicolon-separated --ps-* declarations. Unknown keys or values containing ;, {, }, < or > throw. This prevents additional declaration injection but does not validate arbitrary CSS grammar. Defaults to an empty string; palette defaults live in CSS.

safeHref(href: string | undefined): string | undefined returns undefined for unsupported URI schemes; permits http, https, mailto, tel and strings without a scheme. It strips control/space characters for the scheme check but returns the original accepted string. It is not URL normalization, domain allowlisting or comprehensive untrusted-content validation.

Content type index

Every public content type is covered here or on its linked component page:

TypesReference
SectionDefinition, SectionNavigationItemSection fields; navigation item is Pick<SectionDefinition, id/title>
SectionBackground, SectionBackgroundColorBackgrounds
SectionBackgroundContext, SectionBackgroundDiagnostic, ResolvedSectionBackgroundBackground normalization above
ContentPageData, PageDataPage props and data models
SectionTypeMap, SectionData, GridSectionDataData models and extension
HeroSectionData, RichTextSectionData, ImageSectionData, VideoSectionData, CardGridSectionData, CarouselSectionDataBuilt-in model table
CardItem, CardSize, ImageData, ImageAspectCard and Image
VideoDataVideoSection
MenuItem, ButtonGroupItemNavigation and Satellites
BreadcrumbBreadcrumbs
PageThemeThemes and Styling

There is no runtime JSON schema/validator export. Applications validate externally supplied content. See CSS Variables and Layout Tokens for all token values.

Structured content types (0.2)

  • HeadingLevel: 1 through 6; inherited by nested sections and cards.
  • PageKind: content or detail; controls background normalization and large detail-pane corners.
  • CardVariant: catalog or editorial.
  • CardMediaAspect: 3/2, 4/3 or 1/1; applies to the media frame only.
  • CardMediaFit: contain or cover; independent from the frame ratio.
  • CardLayoutOptions: mediaAspect, mediaFit and reserveEyebrow for catalog measurements.
  • InlineText: string or InlineRun array. InlineRun: text/strong/emphasis/code with text, link with text/href, or break.
  • ContentAction: label, href, optional variant (primary, secondary, text).
  • RichTextContentData: either body with optional format, or blocks; the two sources are mutually exclusive.
  • RichTextBlock: paragraph (text, optional variant default/lead/muted), address (name, lines), list (items, optional ordered), panel (optional title/tone/actions, blocks). Panels can nest.
  • PanelSectionData: shared SectionDefinition, type panel, required blocks, optional tone and actions.

See structured text, panels and shared layout reference.

SurfaceTone is the shared standard | muted surface selection for Card.tone, collection cardTone, PanelSectionData.tone and structured panel blocks. The selection follows the active theme and does not change the surrounding SectionBackground.

Code models

CodeData contains source and presentation options. CodeSectionData adds section metadata and the code discriminator. CodeToken and HighlightedCode describe optional derived syntax tokens. See the complete code reference for fields, defaults and examples.

Heading models

HeadingSize is m | l | xl, independent of HeadingLevel. SectionDefinition.headingSize selects the visual size. HeadingSectionData extends SectionDefinition with type: 'heading'. It is part of SectionTypeMap and renders through Sections. Place it or a HeroSectionData record directly in PageData.sections and set headingLevel: 1 for the page title.