Skip to main content

Capabilities

Capabilities are the things a component can do. Which ones a component has depends on its type: videos play, text can be rewritten, anything visible can be shown, hidden, or switched between states.

Each capability is a namespace on the component. It exists only if the component carries it — otherwise the property is undefined.

CapabilityOnMethods
mediacml-videoplay(), pause()
textcml-textsetText(text), getText()
statesAny visible elementactivate(name), deactivate(name), toggle(name), list(), current()
visibilityAny visible elementshow(), hide(), isHidden()
pagesThe experiencelist(), current(), goTo(page), next(), previous()

"Any visible element" means cml-section, cml-group, cml-oval, cml-rectangle, cml-polygon, cml-image, cml-video, cml-svg, cml-text, and cml-embed.

Check before using, or use optional chaining:

const hero = experience.findByLocator('hero').only()

if (hero.has('media')) hero.media.play()
hero.text?.setText('Hi') // does nothing if it isn't text

On a ComponentSet these same namespaces reach every member that carries the capability, and skip the rest.


media​

interface MediaCapability {
play(): void
pause(): void
}

Video playback, on cml-video.

const video = experience.findByLocator('hero-video').only()

video.media.play()
video.media.pause()

experience.findByTag('cml-video').media.pause() // pause every video
  • play() behaves like a viewer tapping play. If the browser blocks autoplay it does nothing rather than raising an error, so an unattended page stays quiet. Browsers generally allow playback that follows a real user gesture — starting a video from inside a component.click handler is the reliable pattern.
  • Before the video has finished rendering, both methods do nothing.

text​

interface TextCapability {
setText(text: string): void
getText(): string
}

Read and replace the text of a cml-text component.

const headline = experience.findByLocator('headline').only()

headline.text.setText('Welcome back')
headline.text.getText() // 'Welcome back'
  • setText replaces the text and keeps the styling you designed.
  • getText returns the plain text with no markup, or '' before the component has rendered.
  • Always use setText rather than writing to the element's textContent, which can corrupt what Ceros renders.
  • getText is a read, so on a set it runs on the first match — see how calls fan out.

states​

interface StatesCapability {
activate(name: string): void
deactivate(name: string): void
toggle(name: string): void
list(): string[]
current(): string | null
}

Switch a component between the states set up in Flex — an expanded card, a selected option, a hover treatment. Carried by every visible element.

const card = experience.findByLocator('product-card').only()

card.states.list() // ['expanded', 'minimized'] — what this component declares
card.states.current() // 'expanded', or null when it's in its default state
card.states.activate('expanded') // turn on
card.states.deactivate('expanded') // turn off
card.states.toggle('expanded') // flip whichever way it currently is

// toggle a state across every match
experience.findByLocator('faq-item').states.toggle('open')
  • name is the state's name as it appears in Flex.
  • toggle(name) is a genuine toggle — it takes no arguments beyond the name. Use activate / deactivate when you want to force a direction.
  • A name the component doesn't have does nothing and logs the names it does have.
  • list() and current() are reads, so on a set they run on the first match.

visibility​

interface VisibilityCapability {
show(): void
hide(): void
isHidden(): boolean
}

Show and hide elements at runtime. Carried by every visible element.

const banner = experience.findByLocator('promo-banner').only()

banner.visibility.hide()
banner.visibility.isHidden() // true
banner.visibility.show()

// reveal everything the design hides by default
experience.findByLocator('hidden-on-load').visibility.show()
  • show() and hide() override what you published for this page view. They pull the same lever as a show/hide interaction authored in Flex.
  • isHidden() reports what's actually true right now: your override if you've set one, otherwise the published visibility.
  • show() / hide() apply to every member of a set; isHidden() reads the first.
note

show(), hide(), and isHidden() are available today, but the VisibilityCapability TypeScript type isn't exported yet. Use the methods freely — just don't import that one type name until it lands.


pages​

interface PageInfo {
slug: string // the page's URL-safe identifier — what goTo() takes
label: string // its display name (falls back to the slug when unnamed)
}

interface PagesCapability {
list(): PageInfo[]
current(): PageInfo | undefined
goTo(page: string | number): void
next(): void
previous(): void
}

Navigate a multi-page experience. Carried by the experience itself, so you reach it as experience.pages — or, from any component, component.experience.pages.

const experience = await connect()

experience.pages.list() // [{ slug: 'intro', label: 'Intro' }, …] in order
experience.pages.current() // { slug: 'intro', label: 'Intro' }
experience.pages.goTo('pricing') // by slug
experience.pages.goTo(2) // or by index, matching list()
experience.pages.next()
experience.pages.previous()
  • A slug that doesn't exist, or an index out of range, does nothing and logs the pages that are available.
  • next() on the last page and previous() on the first do nothing.
  • On an embedded experience (Flex Inline or SSR) navigation happens in place, and your handle needs re-connecting afterwards — see Delivery modes.

fullscreen​

interface FullscreenCapability {
isFullscreen(): boolean
enter(): void
exit(): void
toggle(): void
}

Put the experience into browser fullscreen, or take it out. Carried by the experience itself, so you reach it as experience.fullscreen.

const experience = await connect()

document.querySelector('#present').addEventListener('click', () => {
experience.fullscreen.enter()
})

experience.fullscreen.isFullscreen() // true while THIS experience is fullscreen
experience.fullscreen.toggle()
experience.fullscreen.exit()
  • Call enter() and toggle() from a user gesture. Browsers only grant fullscreen while the activation from a real click or keypress is still live, and only to code running synchronously inside it. Calling it from a setTimeout, a .then(), or after an await is refused by the browser. This is a browser rule, not an SDK one.
  • What goes fullscreen depends on the delivery mode, and the SDK picks correctly for you: a standalone or iframe-embedded experience takes the whole screen, while an inline or SSR embed fullscreens the experience itself rather than your surrounding page.
  • Some platforms can't do it. Safari on iPhone allows fullscreen for video only, so the call does nothing there rather than failing — check isFullscreen() afterwards if you need to branch.
  • In an iframe embed, the frame needs the permission. Ceros embed code grants it; a hand-rolled iframe needs allow="fullscreen".
  • isFullscreen() reports the Fullscreen API's state. A viewer who went fullscreen with F11 or the OS window control isn't visible to the page and reads as false.
  • It answers "is this experience fullscreen", not "is anything fullscreen". A video embed inside the experience that the viewer fullscreens with the player's own button reads as false, because what went fullscreen is nested inside the experience rather than being it. exit() is scoped the same way and will not close someone else's fullscreen.
  • To react to the state changing by any route — your call, an authored interaction, or the viewer pressing Escape — listen for the flex.fullscreen.change event Ceros fires on the window. Note this event is document-level and does not make that distinction: it fires with fullscreen: true for a nested embed too, so the two can legitimately disagree. Use the event to know something changed, then read isFullscreen() to know whether it was yours:
window.addEventListener('flex.fullscreen.change', (event) => {
const { fullscreen } = event.detail
myChart?.resize()
})

Anything you change is runtime only​

Text, states, and visibility all change the live page and nothing more. None of it is written back to your experience, and a reload restores exactly what was published. See Limits.