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.
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.
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.