Skip to content

Style becomes a declaration block (TrackStyle / SectionStyle) #132

Description

@puckey

Summary

Presentation stops accreting as flat fields in two competing shapes — core style strings (Section.style: 'list' | 'grid' | 'rail', Track.style / Track.childrenStyle) and platform-prefixed hints (carPlay*) — and becomes one CSS-adjacent declaration block:

interface TrackStyle {
  display?: 'list' | 'grid'                      // positional: this holder's own children (never inherited)
  accessorySymbol?: string                       // ┐ inherited item properties:
  artworkRendering?: 'original' | 'stencil'      // │ track ?? section ?? page ?? default
  imageShape?: 'circular' | 'rounded-rectangle'  // │
  cardTint?: string                              // │
  cardImage?: 'normal' | 'background'            // ┘
}
interface SectionStyle extends TrackStyle {
  gridWrap?: boolean                             // ┐ container properties (scope override):
  gridTile?: 'plain' | 'card' | 'condensed'      // ┘ section ?? page ?? default
}

Section.style?: SectionStyle
Track.style?: TrackStyle
ResolvedTrack.style?: SectionStyle   // a page is a Track that is also the container

Full specification: docs/section-styling-design.html (schema with JSDocs, resolution rules, property ledger, rendering matrix, rejected-shapes log). Decision record: ADR 0011. SDK evidence: docs/carplay-sdk-audit.html.

The rules, compressed: declarations are aspirational (each surface renders what it understands, the rest is inert — never an error); item properties are inherited within a page, never across resolution (a handle's block styles the handle); container properties resolve by scope override (page declares for its scope, a section overrides for its own children); display is positional; style is presentation-only — no style property affects queue scope, playback, or navigation; degradation drops decorations before layout.

Breaking

String styles, 'rail', and Track.childrenStyle are deleted — no string shorthand, no dialect normalization. Nothing has shipped, so there is no migration; the wire is flat nested structs.

Checklist (design doc §7)

  • Spec: TrackStyle / SectionStyle extends TrackStyle as Nitro structs (named exported unions — inline string unions don't codegen); ResolvedTrack.style widens to SectionStyle; codegen + web-stub mirrors. Ship with the flip: display, gridWrap; other properties land with their features (no dead fields).
  • Resolution: one resolver per platform — item properties track ?? section ?? page ?? default; container properties section ?? page ?? default; display on an explicit positional deny-list.
  • Spec-level tests: inheritance-completeness (every TrackStyle key read by every resolver, in precedence order; positional keys provably excluded — Nitro erases extends, so types can't enforce it).
  • Dev-mode inert-declaration diagnostic (warn when a declaration can never render — gridWrap on a list section, cardTint with no card treatment): InertStyleDiagnostic on both platforms, called from each resolveUncached — structural inertness only, judged per declaring level; the narrower promise-less all-grid page warning (Android) stays as its own check.
  • iOS: section presentation keys off display + gridWrap (+ gridTile when it lands); pre-26 degradation — treatment drops, layout survives (gridWrap: false → legacy image row; wrapping grid → list).
  • Android: tile detection from resolved display; delete childrenStyle and re-source the parent-level content-style hint from the browsable handle's declared display (the page-layout promise: declared or it doesn't exist, plus a targeted dev warning when a served grid page sits under a promise-less parent). Per-item hints derive from the section resolution.
  • Tolerant decoding in both native decoders (decode-to-nil on mismatch — a stale payload must never kill a page or persisted playback state); persisted queues keep style and disabled.
  • Track.disabled content fact (see Track.disabled — unavailable items (grayed where drawable, hidden elsewhere) #126): never plays anywhere; grayed where the surface can draw it, hidden where it can't.
  • ResolvedTrack → Track projection drops container properties explicitly.
  • Docs/guides updated to block authoring; example phone UI may honor gridWrap: false as a horizontal scroller (intended usage).

Follow-ups that land with their features (not this issue)

Metadata

Metadata

Assignees

No one assigned

    Labels

    android-autoAndroid Auto / AAOS surfaceappIn-app UI surface (the consumer's own screens)car-playCarPlay surfaceenhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions