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.
| Capability | On | Methods |
|---|---|---|
media | cml-video | play(), pause() |
text | cml-text | setText(text), getText() |
states | Any visible element | activate(name), deactivate(name), toggle(name), list(), current() |
visibility | Any visible element | show(), hide(), isHidden() |
pages | The experience | list(), 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 acomponent.clickhandler 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'
setTextreplaces the text and keeps the styling you designed.getTextreturns the plain text with no markup, or''before the component has rendered.- Always use
setTextrather than writing to the element'stextContent, which can corrupt what Ceros renders. getTextis 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')
nameis the state's name as it appears in Flex.toggle(name)is a genuine toggle — it takes no arguments beyond the name. Useactivate/deactivatewhen you want to force a direction.- A name the component doesn't have does nothing and logs the names it does have.
list()andcurrent()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()andhide()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.
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 andprevious()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()andtoggle()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 asetTimeout, a.then(), or after anawaitis 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 asfalse.- 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.changeevent Ceros fires on the window. Note this event is document-level and does not make that distinction: it fires withfullscreen: truefor a nested embed too, so the two can legitimately disagree. Use the event to know something changed, then readisFullscreen()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.