npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@meetelise/studio-components

v0.1.80

Published

Elise Studio component library — renderer, layout primitives, and data-fetching components for property websites

Readme

elise-studio-components

Custom button events

Existing Button nodes can opt into click tracking with eventName:

{
  "type": "Button",
  "props": {
    "content": "Explore floor plans",
    "action": "navigate",
    "target": "/floorplans",
    "eventName": "floorplans_cta_click",
    "eventCategory": "engagement",
    "eventParameters": { "placement": "hero", "rank": 1, "featured": true }
  }
}

Event names use lowercase snake_case and may be shared by several buttons. Omit eventName to disable custom tracking; retained category and parameter settings do not block publishing while tracking is disabled. Category defaults to engagement; parameters are optional string, finite number, or boolean values. The keys event, gtm/gtm.*, ecommerce, event_category, property_id, type, asset, and prototype keys are reserved. Studio treats type as a node marker and asset as an asset reference; use names such as button_type and asset_name instead. Property ID comes from the nearest property scope, falling back to the site's property; it is omitted when unavailable. Parameter dictionaries are literal data: binding-like keys such as $content are preserved rather than resolved as Studio directives.

The shared analytics dispatcher sends the event before the button action, including native external links and document downloads. This measures the click, not a completed download or booking. Native disabled buttons do not track. Existing link and download activation behavior is unchanged. Phone and email links send only their existing automatic phone_click or email_click event; custom event settings do not apply to these links. Downloads use custom tracking, even when a phone or email target is also configured. Configuration errors are reported during authoring and blocked by the compiler before publishing. Click handlers trust validated configuration; a failing customer tag does not interrupt the custom-tracked button action. Analytics mode, destination routing, and consent handling use the existing site configuration; this feature does not grant consent or install tags.

Schema version 41 adds Button tracking fields using the existing object kind for event parameters. Release this package first, then update the exact @meetelise/studio-components version and lockfiles in both the CRM editor and Studio compiler. The corresponding editor adds tracking controls; the compiler blocks invalid custom tracking at publish and refuses tracked buttons when its installed package lacks this contract.

Nav button links and Footer actions

Nav button links (including buttons in dropdowns) and Footer actions accept the same eventName, eventCategory, and eventParameters and send the same event when clicked. Plain Nav links do not send custom events. Schema version 43 declares the fields on Nav links and schema version 44 on Footer actions; the compiler blocks their tracking errors at publish like a Button's.

Apply Now and Schedule a Tour buttons

ApplyNowButton and ScheduleTourButton are Buttons with a conversion event prefilled. They take Button presentation props, a content label ("Apply Now" / "Schedule a Tour"), an optional propertyId that overrides the nearest property scope for the event, and the custom tracking fields of a Button (eventName, eventCategory, eventParameters). The schema defaults eventName to apply_click / tour_cta_click and eventCategory to conversion, and the editor writes them when the button is inserted; authors can rename the event, add parameters, or turn tracking off by removing eventName, as on a Button. A missing eventCategory means conversion.

  • ApplyNowButton's default apply_click matches the payload unit cards send (event_category: "conversion" and property_id). It opens its application link in a new tab unless openInNewTab is false. The link is the property's CRM application_link, read from the site properties request (attributes only, shared by every apply button on the site). An authored target (Application URL) overrides it.
  • ScheduleTourButton's event also carries tour_type from tourType (guided, sgt, or virtual, matching ScheduleTour; the schema labels them In-Person, Self-Guided, and Virtual). Without a target it opens ScheduleTour for its property in a modal, with tourType selected when the property offers it; with one it follows action and target like a Button (a tour page, a modal, or a section). tour_booking still means a completed ScheduleTour booking.

ApplyNowButton without a URL or CRM link, and ScheduleTourButton without a destination or a property, render nothing on published sites and show a placeholder in the editor. Nav button links and Footer actions can't hold these components; give them the same events with custom tracking (eventName: "apply_click" or "tour_cta_click" and eventCategory: "conversion").

Button shapes and icons

Button.radius (none, sm, md, lg, xl, full) rounds one button on the theme's radius scale without changing radius.base, so pill-shaped calls to action can sit beside cards, maps, and modals that keep the theme radius. Button.icon names an Icon glyph drawn beside the label; iconPosition places it at the start (default) or end. Unknown names render no icon, and document buttons keep their download glyph. Nav button links, Footer actions, ContactForm submit buttons, and FloorplanCatalog actions forward these fields through the shared Button presentation contract. Omitting them preserves the theme radius and text-only label.

Icon adds kitchen, lounge, sun, window, bus, car, phone, mail, map-pin, and arrow-right glyphs. Its optional color accepts the same validated colors and {"$token": "color.primary"} bindings as other color props; omitted icons inherit the surrounding text color.

Schema version 42 adds these fields with the Nav, Gallery, SpecialPromotion, and FloorplanExplorer options below. Release this package first, then update the exact @meetelise/studio-components version and lockfiles in both the CRM editor and Studio compiler before authoring them in production.

Leasing office hours

OfficeHours reads attributes.leasing_office_hours_schedule from the public property API. Each interval contains day_of_week, property-local start_time and end_time (HH:MM), and by_appointment_only. For a non-empty structured schedule, both layouts render Monday through Sunday, preserving multiple intervals per day and displaying missing days as Closed, matching CRM's enabled-day storage. Table and inline layouts format these records directly without parsing human-readable schedules or shifting times to the visitor's timezone.

When structured hours are absent or empty, the existing leasing_office_hours string takes precedence over authored hours; an empty schedule does not imply that all seven days are closed. Both text sources remain intact as text, including when table layout is selected. Deploy the additive API field before updating component-package pins to enable structured tables in consumers.

Thumbnail carousel galleries

Set Gallery.layout to thumbnail-carousel for a large photo with square previous/next controls and a strip of five visible thumbnails below it. At desktop widths (1024px and up), arrows appear on photo hover or keyboard focus when the device has a fine, hover-capable pointer. Smaller tablet/mobile viewports and touch-only devices hide the arrows; tappable thumbnails remain available alongside swipes. Swipe left/right on the main photo to navigate on touch screens; vertical scrolling and pinch zoom remain available, and a tap opens the lightbox. Clicking a thumbnail selects that photo; the active border and strip stay synchronized with the arrows. Selecting a photo smoothly shifts its thumbnail to the left edge of the strip, stopping at the end so the last five thumbnails stay filled. Overflow supports touch scrolling; only the strip moves, not the surrounding page. Reduced-motion preferences disable the smooth animation. Focused thumbnails support Left/Right arrows and Home/End as well as native Enter/Space activation.

This layout supports category filters and independent section galleries, static images and live property media. carouselAccentColor controls the arrows and selected border, defaulting to the site's primary theme color. Existing tile height, image fit, radius, and responsive settings still apply; the default stage aspect ratio is 4:3. Clicking the large photo opens the existing lightbox; lightboxThumbnails controls only the overlay's optional thumbnail strip.

The editor and compiler receive the new layout through the generated component schema (version 37). Release the package and update the consuming editor/compiler versions before selecting this layout in production.

Scrolling photo strips

Set Gallery.layout to strip for a single row of square photos that scrolls horizontally with snap points. tileHeight (with responsiveTileHeight) sets each tile's height and width, defaulting to 16rem; columnGap, tileRadius, tileFit, and edgeBleed keep their existing meanings. Tiles open the existing lightbox. Touch, trackpad, and keyboard focus scroll the row; on wide screens with a fine, hover-capable pointer, previous/next buttons appear on hover or focus whenever more photos are available in that direction. Category filters and section grouping work as in the other layouts.

Floor plan rent display

FloorplanExplorer.rentDisplay: "startingAt" shows the lowest rent with a "Starting at" label on standard cards instead of the full rent range. The default range keeps the existing output; marketplace cards already show their starting rent and are unaffected.

Floor plan visitor filters

FloorplanExplorer.showFilters: true adds a visitor filter bar like UnitExplorer's: bedroom chips, bathroom chips, and minimum and maximum rent. Filters run on the server: the floorplans endpoint receives min_bedrooms/ max_bedrooms, min_bathrooms/max_bathrooms, and min_rent/max_rent, and a plan matches a rent bound when it has an available unit in range. Its card then shows only those units. Rent input is debounced, and the list shows loading cards until the filtered results arrive, then keeps paging with Load More. Chips list the values in the property's floor plans, plus any value the visitor already picked. They come from the unfiltered first page, plus one request for every plan when the property has more than one page. The rent inputs follow showRentRange. When showFilters is on it replaces showBedroomFilter; when it is off, the existing output is unchanged. Rent bounds need the Studio API that accepts min_rent/max_rent.

The bar's chips wrap on narrow screens, and unselected chips and inputs have a visible border on light themes. UnitExplorer shares the controls and keeps its released look by default; UnitExplorer.filterStyle: "outlined" opts it into the same visible, wrapping style.

Curated floor plan catalogs

FloorplanCatalog is a grouped, selector-and-accordion presentation of live Elise floor plans. Each authored plan is a reference to an Elise floor plan id; references missing from the authenticated property API are not rendered. Authored JSON controls group order, marketing copy, actions, optional image fallbacks, and an optional square-footage marketing label. API records remain the source of identity, bed/bath values, availability, rent, and preferred media. The component follows all cursor pages so a curated id is not lost beyond the first response page, and stops safely if the API repeats a cursor.

The catalog uses Studio's keyed sm through 2xl responsive contract and a semantic collapseAt breakpoint. Actions reuse the canonical Button contract, navigation stays router-aware, analytics emit canonical API floor plan ids, and images use the shared accessible Lightbox. FloorplanExplorer remains unchanged for uncurated listing grids and lists. A validated compactPresentation object controls accordion typography, rhythm, panel insets, and action alignment below the collapse breakpoint without relying on arbitrary classes or site templates.

The opt-in desktopPresentation: "workspace" mode integrates a bounded, scrollable grouped selector with a stable summary-and-drawing canvas for larger catalogs. Selector headings, counts, and API-backed plan details are separately configurable. Below collapseAt, compactDisclosure: "single" turns the same catalog into a globally single-open inline accordion; responsive detail columns and connected panel styling remain authored presentation choices. The default split/perGroup behavior and API contract are unchanged.

Motion and background media

BackgroundMedia is a generic frame for decorative image or muted video backgrounds. It accepts normal Studio children, so headings, buttons, and data components remain independently editable. The optional parallax motion moves only the media layer; overlay content and document layout stay fixed. Video is paused outside the viewport and replaced by its poster or image when the visitor requests reduced motion. If no still is authored, a paused first video frame is used instead.

{
  "type": "BackgroundMedia",
  "props": {
    "video": { "$content": "hero.video" },
    "poster": { "$content": "hero.poster" },
    "minHeight": "90vh",
    "contentPosition": "bottom-left",
    "motion": "parallax"
  },
  "children": [
    {
      "type": "Text",
      "props": { "content": { "$content": "hero.headline" }, "variant": "display" }
    }
  ]
}

Parallax applies the same bounded, request-animation-frame scroll transform to any child subtree. StickyFrame pins a subtree within its parent scroll range and defaults to normal flow on mobile. Together with Grid, Container, and ScrollReveal, these frames support sticky storytelling, independently moving media, and layered editorial sections without site-specific JavaScript or CSS. All motion is progressive enhancement: server output remains visible, unsupported browsers retain a static layout, and operating-system reduced-motion preferences disable continuous translation.

Atlas authoring contract

The exported component schema includes optional authoring metadata for Atlas and semantic validators. It describes component roles, breakpoint-aware props, composition constraints, required context, relative performance cost, stable repair hints, and recommended recipe IDs. Runtime rendering ignores this metadata.

The generated dist/componentSchema.json versions the component schema, authoring contract, and diagnostic envelope independently. Diagnostics use a shared shape and may include existing Studio design operations for Atlas to apply:

{
  "code": "STICKY_MEDIA_OVERLAP",
  "severity": "error",
  "nodeId": "hero-media",
  "breakpoint": "sm",
  "message": "The transformed media can leave its sticky boundary.",
  "suggestedOperations": [
    {
      "type": "set_node_prop",
      "node_id": "hero-media",
      "prop_path": ["motion"],
      "value": "none"
    }
  ]
}

validateAuthoringTree is the shared, side-effect-free semantic validator used by Atlas, preview, and compiler consumers. It checks required props, component nesting, authoring contexts, incompatibilities, deprecations, and an optional normalized performance budget without resolving or rendering the tree:

const { diagnostics, performanceCost } = validateAuthoringTree(page.tree, {
  availableContexts: ["property"],
  performanceBudget: 24,
  path: "pages[0].tree",
});

Errors represent invalid authoring decisions; warnings cover deprecations, unknown props, and budget pressure. When a safe default exists, a diagnostic may include a typed Studio design operation that Atlas can apply as a repair.

Atlas composition recipes

The @meetelise/studio-components/recipes export and generated dist/authoringRecipes.json provide versioned, validated section blueprints. Each recipe declares its content fields, required site contexts, normalized performance budget, and a normal Studio component tree using $content bindings. The initial catalog includes editorial hero, split-media story, sticky-media story, and availability/conversion compositions.

Recipes are build-validated against the canonical component schema and semantic validator. A build fails if a recipe references an unknown component, an undeclared content key, or a missing recipe ID. Atlas can therefore select a recipe, create its declared content fields, and insert the tree through ordinary typed Studio operations.

Amenity presentations

AmenityList keeps layout and ordering independent. Existing sites continue to use the alphabetized icon-grid default, while order="source" preserves the first occurrence of each amenity from either authored content or the Elise property API. The opt-in bulleted layout emits semantic ul/li markup and uses theme body typography. Its indentation, item spacing, and typography can be adjusted through the validated bulletPresentation object without baking a property's measurements into the shared component.

Overlay navigation below preceding content

Use Container display="grid" overlapChildren to layer navigation over page content while earlier content, such as a specials banner, stays in document flow:

<Container maxWidth="none" paddingX="none" paddingY="none">
  <SpecialPromotion presentation="banner" />
  <Container display="grid" overlapChildren align="start"
    maxWidth="none" paddingX="none" paddingY="none">
    <Nav overlay positioning="layout" sticky />
    <Container maxWidth="none" paddingX="none" paddingY="none">
      <main>{/* Hero and all subsequent page sections */}</main>
      <footer>{/* Footer content */}</footer>
    </Container>
  </Container>
</Container>

overlapChildren applies only to grid containers. Keep all page content, including the footer, in one child so sticky navigation follows the full page, and leave ancestor overflow visible. Shared layouts can place their page Slot in that content child. This composition is opt-in: overlapChildren defaults to false and Nav's positioning defaults to "auto".

Navigation presentations

Nav keeps the inline, solid header and dropdown menu as its default. Sites may opt into stacked or overlay layouts, compact links or equal-width tabs, separate primary and utility actions, per-link desktop/mobile visibility, a scrolled logo, an optional compact scroll effect, and a modal drawer. The existing sticky flag controls viewport following while overlay controls whether the initial header occupies document flow. collapseAt accepts a boolean or a shared sm/md/lg/xl/2xl breakpoint (default: md). A breakpoint shows the hamburger below that width and expands navigation at or above it. Set collapseAt: true to use the hamburger at every width, or collapseAt: false to keep navigation expanded at every width. Booleans select the layout mode; users can still open and close the hamburger menu normally. Use JSON booleans, not the strings "true" or "false". Changing to false closes any open menu and releases the drawer's scroll lock. Optional validated lengths control logo bounds, link rhythm, content width, section padding, tab borders, and drawer width without embedding a site template in the component. For sticky compact navigation, optional topStyle and scrolledStyle objects can independently control validated logo height, frame padding and gap, background color/opacity, divider, shadow, backdrop blur, and foreground scheme. A bounded pixel threshold and whitelisted transition timing provide a two-state CSS transition with an optional logo crossfade; reduced-motion preferences disable that motion. Button-style links consume the canonical Button presentation contract. Omitting the presentation fields preserves the existing inline/dropdown behavior.

utilityCollapseAt takes the same values as collapseAt and lets utility links (such as a phone number or tour and apply buttons) stay in the bar after primary links move into the menu; for example, collapseAt: true with utilityCollapseAt: "lg" keeps those actions visible from 1024px up. While they are in the bar, the menu omits them. A value that would collapse utility links later than collapseAt is ignored so every link stays reachable. menuButtonPosition (start or end) renders the menu toggle inline in the bar, next to the logo or after the utility links, instead of the default placement. drawerShowLogo repeats the logo beside the drawer's close button. A dropdown item with triggerStyle: "button" renders its desktop trigger with that item's Button presentation (variant, colors, radius, sizing) and aligns its panel to the trigger's end; mobile menus keep the standard disclosure row.

Specials announcement bar

SpecialPromotion.presentation: "bar" renders a slim, full-width announcement on the primary color: the heading (when set), the special's name and description on one wrapping line, a text call to action with an arrow, and the dismiss control. It uses the same live or manual content, frequency, delay, and analytics as the other presentations. Set heading to an empty string to show only the special itself.

Footer presentations

Footer keeps its columns-and-copyright output as the default. The optional property presentation composes structured brand, address, contact, action, badge, and legal content inside one semantic footer. Sections and responsive grids follow the content that is present; actions reuse Button's canonical presentation and routing behavior. propertyLayout and keyed responsivePropertyLayout overrides use Studio's shared breakpoints and validated CSS lengths, while colors come from the active theme.

Responsive layout overrides

Container and Stack keep their token spacing as the default design language. For layouts that need exact authored geometry, Container also accepts optional base edge padding values and keyed responsivePadding overrides. Studio uses the same mobile-first scale as Tailwind: sm (40rem), md (48rem), lg (64rem), xl (80rem), and 2xl (96rem). Stack and Grid use the same duplicate-free keyed contract for authored gaps. Values are limited to simple CSS lengths with finite, unit-specific bounds; malformed and extreme declarations are ignored. Container bottom margin is signed so layout composition can intentionally overlap sections.

Button supports the same opt-in pattern for base/responsive font size, weight, line-height, tracking, horizontal/vertical padding, border width, inset outline, and bounded shadow presets. responsiveTypography can override font size, line height, or signed letter spacing at the shared breakpoints. Focus presentation is site-wide rather than per button: token themes may define focus.ring.width, focus.ring.offset, and focus.ring.color. Unsafe values are ignored, and the native focus indicator remains the fallback. Omitting presentation fields preserves the existing variant/size output.

Background-image sections can opt into bgAttachment and responsiveBgAttachment (scroll or fixed). Omitting both preserves the browser default while allowing authored fixed-background behavior at any shared breakpoint without runtime-authored CSS classes. Background position supports edges and all four corners. bgOverlayOpacity controls the overlay layer independently from its color and is bounded to the CSS opacity range.

Coordinate-aware property maps

PropertyMap renders a property coordinate on the same map runtime used by Studio's unit marketplace. Its default MapLibre style is served by Elise; an installation may instead configure Mapbox or an alternate style through the existing Studio runtime configuration. Provider credentials are platform configuration and are never authored into site JSON.

Authors control center, zoom, responsive height, the property marker, and an optional list of nearby-place markers. Every marker can use an image or safe custom/category color, a structured information card, and a generated or custom Google Maps view/directions action. When nearby places are present the map fits the property and visible places by default; authors may opt back into manual center and zoom. Only one card opens at a time and it remains attached to its coordinate while the visitor pans or zooms, flips and clamps inside the map viewport, supports phone, email, and Google Maps actions, and can be dismissed and reopened. Responsive values use Studio's keyed sm through 2xl contract. MapEmbed remains the lightweight iframe option and retains its existing output and TypeScript markers input.

Contact form presentations

ContactForm keeps its existing comfortable, stacked lead form as the default and continues to submit through Elise's authenticated /v1/prospect endpoint. Opt-in grid layout, one-to-three columns, compact density, grouped names, configurable fields, labels, and requiredness are independent authoring choices. The native date input remains the default; usText accepts editable numeric or punctuated input, formats it on blur, validates the calendar date, and sends the existing ISO move_in_date payload.

Base and keyed responsive values can control row, column, and label-to-control gaps. A bounded control-presentation object covers minimum heights, padding, border, radius, and label typography without embedding a site template. Submit buttons consume the canonical Button presentation contract and add independent base/responsive alignment. Invalid authored CSS values are ignored, fields keep theme colors and visible focus, and validation errors identify and focus the affected control.

Property SEO template tokens

A building_template: true page may use the following compile-time tokens in plain string fields. Resolution is recursive, so the same tokens work in the page path, search/social metadata, and nested seo.structuredData strings.

| Token | Merged content source | | --- | --- | | {{property.name}} | building.name | | {{property.slug}} | vars.slug | | {{property.city}} | building.city | | {{property.state}} | building.state | | {{property.neighborhood}} | building.neighborhood | | {{property.address}} | building.address | | {{property.shortDescription}} | building.short_description, then building.tagline | | {{property.amenitySummary}} | building.amenity_summary | | {{property.bedroomRange}} | building.bedroom_range | | {{site.name}} | site.name, then merged meta.name / meta.title, then raw site name metadata |

The existing {{slug}} syntax remains an alias for vars.slug. A token whose source is missing stays visibly unresolved so publish-time SEO checks can report the missing data. Runtime {$data: "..."} bindings are never interpolated.

Property hero images are intentionally not string tokens. Author an image or social-image field with the building-scoped object binding {$content: "building.image"}; the resolver converts its AssetValue to that building's CDN URL.

Typography theme tokens

Typography is configured by role so a bounded token set can cover every Text variant. All tokens are optional. Without an authored role override, each variant retains its built-in responsive size, weight, line-height, and tracking.

| Role | Family | Scale | Weight | Line height | Tracking | | --- | --- | --- | --- | --- | --- | | Heading (display, h1–h3) | font.heading | font.heading.scale | font.heading.weight | font.heading.line-height | font.heading.tracking | | Body (subtitle, body, body-lg, caption, overline) | font.body | font.body.scale | font.body.weight | font.body.line-height | font.body.tracking |

Scale is a unitless multiplier such as "1.1". Weight and line-height accept CSS values such as "600" and "1.4"; tracking accepts a CSS letter-spacing value such as "-0.02em". All values may use the existing {$token} alias shape, and building-mode overrides inherit through ThemeScope.

Individual Text nodes can override base font size, weight, line height, letter spacing, transform, and alignment. The keyed responsiveTypography object uses the same sm through 2xl contract as layout and Button, with optional font-size, line-height, and signed letter-spacing values at each minimum viewport. Values are validated and bounded before rendering; invalid authored values are ignored without removing the variant defaults.

Body Google fonts include the regular italic face in addition to the upright weight range, allowing semantic emphasis to use the selected family without requesting every italic weight. Duplicate stylesheet requests are suppressed.

Centralized accessibility presentation

Built-in site components use semantic CSS colors for content, surfaces, actions, control borders, and status messages. Themes can set color.surface, color.surface-muted, color.surface-inverse, color.on-inverse, color.on-dark, color.on-primary, color.on-secondary, color.on-accent, color.overlay, color.on-overlay, color.border, color.border-strong, color.link, and paired color.success, color.warning, color.danger, and color.info / color.*-surface tokens. Keep foreground and background pairs readable in every state; an arbitrary authored palette is not a guarantee of WCAG contrast. Existing configurations retain their published color bindings, individual Tailwind shades, opacity, hover states, and text metrics. New author role overrides are opt-in: missing roles do not replace distinct legacy surfaces or status shades with one shared value. The internal --studio-fg-*, --studio-bg-* and --studio-border-* palette variables preserve those values while allowing visitor transforms and component overrides. General component color, background, and border overrides leave status palettes intact; customize status colors through their paired theme roles. Transparent components with colorScheme="dark" use color.on-dark (white by default) for an externally supplied dark surface; this role follows visitor palette changes. color.on-inverse belongs on a painted inverse surface. Photographs, videos, and authored photographic overlays retain their pixels. With inversion enabled, overlaid content uses a local light contrast palette and a backing surface so text and controls remain paired against unchanged media. An explicit visitor contrast choice takes precedence over that local palette. An explicit component text color overrides its scheme; a background override alone does not select a different foreground.

ThemeProvider accepts runtime accessibility preferences independently of saved author themes: textScale (1–2), contrast (none, light, dark), inverted, monochrome, saturation (0–2), textSpacing, and reducedMotion. Nested building themes and their overlays retain those preferences. This is the presentation API for a future visitor widget; the package does not add that widget or persist a visitor's choices. Reduced motion also stops slideshow timers and autoplaying videos and removes map camera animation. VideoHero exposes an opt-in showPlaybackControl author setting; its default keeps the existing video chrome. Native browser zoom, forced colors, and reduced motion continue to work.

Button, Link, Accordion, AmenityList, ApartmentAmenity, ContactForm, UnitExplorer, and FloorplanExplorer expose optional color, backgroundColor, borderColor, and fontSize in the shared authoring schema. Omitting a property restores its theme default. Nested navigation/footer actions and ContactForm's submitPresentation use the same Button color fields. Complex component font sizes set a base while preserving the relative sizes of internal labels; em/% bases resolve against the theme body size once. Text, Button, gallery headings, amenity bullets, navigation labels, form labels, and floorplan catalog typography include visitor enlargement when the author supplies an explicit or responsive size. Fixed authored line heights enlarge with their text; unitless line heights remain proportional. Absolute sizes retain their authored units, including pixels at non-16px document roots. Visitor scaling does not expand the set of components controlled by preexisting font.body.* tokens. Font-only nested scopes preserve inherited CSS colors and typography, and focus styling is activated by explicit focus tokens.

When adding a component, use semantic variables for built-in styles and useStudioPresentation for author-provided solid colors and sizes. Validate solid colors with safeCssColor before transforming them. Use color(value, role) for CSS color/background properties and background(value) for background-image. Theme palette transforms support hex, named colors, numeric RGB/HSL (including alpha and HSL angle units), and var(--token) aliases. Known color stops in gradients and color-mix() follow the same transforms. Inversion, monochrome, and saturation also support numeric oklch() colors used by the published Tailwind palette; unresolved custom properties and other advanced functions are preserved. Do not use a page-wide CSS filter: palette changes should preserve photographs and videos. Media overlay/background pairs remain together through palette changes using color.overlay and color.on-overlay. Use minimum heights for text controls and allow text to wrap while preserving default dimensions. The existing Accordion height animation is retained; collapsed panels are inert and hidden from assistive technology. Mount fixed overlays outside transformed or clipped ancestors and carry the hook's effective variables and preferences onto a dedicated portal theme root. SpecialPromotion demonstrates a body-level portal that preserves the nearest building theme without adding a full-page canvas.

Use structured author controls or semantic utilities for colors and typography. Arbitrary className literals and third-party embed or WebGL basemap styling are outside the shared color contract; custom integrations must apply equivalent preferences through their own APIs. Studio-owned map controls, markers, and information cards use the shared presentation contract. Media itself is preserved.

Release verification should include browser checks for 200% text enlargement, 320 CSS-pixel reflow, WCAG text-spacing overrides, keyboard focus, forced colors, and open overlays with both a root theme and a nested building theme. Unit tests verify presentation/state contracts but cannot establish visual reflow or contrast.