Preview projected media with a caption, exact literal text, an isolated document, a live CEM view, or a message. Media retains its native alternative text. Use cem-avatar for a compact named identity.
Full examples and variation matrix · Property playground · Automated stories
Media is the default mode. Its default slot and caption slot both fall back to label. Text, document, live, and message modes read value. Artifact-tree reads artifact-path and resource-set. For artifact-tree select an artifact-path and supply an artifacts array in resource-set; links emit cem-preview-navigate for the owning application. For live mode enter CEM-ML such as {p | Hello}; source-uri names its owner, and resource-set supplies explicit resource text. Text preserves whitespace and markup literally; truncated adds “(truncated)” to its accessible name without changing the value. Attribute presence enables truncated, including the text “false”. Document previews keep a fixed empty sandbox and deny scripts, styling, and network access.
{slice @name=label | Policy preview}
{slice @name=mode | media}{slice @name=fit | content}
{slice @name=value | Preview content}
{slice @name=sourceUri | preview://gallery/edited.cem}
{slice @name=resources | ```{"resources":[],"artifacts":[{"path":"index.html","contentType":"text/html","text":"\u003clink rel=\"stylesheet\" href=\"assets/site.css\"\u003e\u003ch2\u003eBuilt site\u003c/h2\u003e\u003cimg alt=\"Blue marker\" src=\"assets/marker.svg\"\u003e\u003ca href=\"posts/intro.html#intro\"\u003eRead intro\u003c/a\u003e"},{"path":"posts/intro.html","contentType":"text/html","text":"\u003clink rel=\"stylesheet\" href=\"../assets/site.css\"\u003e\u003ch2 id=\"intro\"\u003eIntro article\u003c/h2\u003e\u003cimg alt=\"Blue marker\" src=\"../assets/marker.svg\"\u003e"},{"path":"assets/site.css","contentType":"text/css","text":"h2 { color: #175a8c; } img { width: 32px; height: 32px; }"},{"path":"assets/marker.svg","contentType":"image/svg+xml","text":"\u003csvg xmlns=\"http://www.w3.org/2000/svg\" width=\"32\" height=\"32\"\u003e\u003ccircle cx=\"16\" cy=\"16\" r=\"14\" fill=\"#175a8c\"/\u003e\u003c/svg\u003e"}]}```}
{slice @name=artifactPath | index.html}
{slice @name=truncated | false}
{slice @name=class}
{slice @name=dir | ltr}
{slice @name=hidden | absent}
{slice @name=content | fallback}
{cem-field @label=label @slice=label @slice-event=input @slice-value="$target.value"
@value="{$datadom.slices.label}"}
{fieldset @aria-label=mode @role=radiogroup @slice=mode @slice-event=change
@slice-value="$target.value" |
{legend | mode}
{cem-radio @checked={if datadom.slices.mode == "media" {true} else {null}} @label=media @name=property-mode @value=media}
{cem-radio @checked={if datadom.slices.mode == "text" {true} else {null}} @label=text @name=property-mode @value=text}
{cem-radio @checked={if datadom.slices.mode == "document" {true} else {null}} @label=document @name=property-mode @value=document}
{cem-radio @checked={if datadom.slices.mode == "live" {true} else {null}} @label=live @name=property-mode @value=live}
{cem-radio @checked={if datadom.slices.mode == "artifact-tree" {true} else {null}} @label=artifact-tree @name=property-mode @value=artifact-tree}
{cem-radio @checked={if datadom.slices.mode == "message" {true} else {null}} @label=message @name=property-mode @value=message}
}
{cem-textarea @label=value @rows=4 @slice=value @slice-event=input @slice-value="$target.value"
@value="{$datadom.slices.value}"}
{cem-field @label=artifact-path @slice=artifactPath @slice-event=input @slice-value="$target.value" @value="{$datadom.slices.artifactPath}"}
{cem-field @label=source-uri @slice=sourceUri @slice-event=input @slice-value="$target.value"
@value="{$datadom.slices.sourceUri}"}
{cem-textarea @label=resource-set @rows=3 @slice=resources @slice-event=input @slice-value="$target.value"
@value="{$datadom.slices.resources}"}
{cem-checkbox @checked={if datadom.slices.truncated { true } else { null }} @label=truncated @slice=truncated @slice-event=change
@slice-value="$target.checked"}
{cem-field @label=class @slice=class @slice-event=input @slice-value="$target.value"
@value="{$datadom.slices.class}"}
{fieldset @aria-label=dir @role=radiogroup @slice=dir @slice-event=change @slice-value="$target.value" |
{legend | dir}
{cem-radio @checked={if datadom.slices.dir == "ltr" {true} else {null}} @label=ltr @name=property-dir @value=ltr}
{cem-radio @checked={if datadom.slices.dir == "rtl" {true} else {null}} @label=rtl @name=property-dir @value=rtl}
}
{fieldset @aria-label=hidden @role=radiogroup @slice=hidden @slice-event=change
@slice-value="$target.value" |
{legend | hidden}
{cem-radio @checked={if datadom.slices.hidden == "absent" {true} else {null}} @label=absent @name=property-hidden @value=absent}
{cem-radio @checked={if datadom.slices.hidden == "present" {true} else {null}} @label=present @name=property-hidden @value=present}
}
{fieldset @aria-label=content @role=radiogroup @slice=content @slice-event=change
@slice-value="$target.value" |
{legend | content}
{cem-radio @checked={if datadom.slices.content == "fallback" {true} else {null}} @label=fallback @name=property-content @value=fallback}
{cem-radio @checked={if datadom.slices.content == "projected" {true} else {null}} @label=projected @name=property-content @value=projected}
}
{fieldset @aria-label=fit @role=radiogroup @slice=fit @slice-event=change @slice-value="$target.value" |
{legend | fit}
{cem-radio @checked={if datadom.slices.fit == "content" {true} else {null}} @label=content @name=property-fit @value=content}
{cem-radio @checked={if datadom.slices.fit == "fill" {true} else {null}} @label=fill @name=property-fit @value=fill}
}
{div @style="block-size:60vb" |
{cem-media-preview @fit={datadom.slices.fit} @source-uri="{$datadom.slices.sourceUri}" @resource-set="{$datadom.slices.resources}" @artifact-path="{$datadom.slices.artifactPath}" @class="{$datadom.slices.class}" @dir="{$datadom.slices.dir}" @hidden={if datadom.slices.hidden == "present" {true} else {null}}
@label="{$datadom.slices.label}" @mode="{$datadom.slices.mode}" @truncated={if datadom.slices.truncated { true } else { null }}
@value="{$datadom.slices.value}" |
{cem:if @test='datadom.slices.content == "projected"' |
{span | Media content}
{strong @slot=caption | Projected caption}
}}
}
{cem-module-url @slice=sourceUrl @src="@epa-wg/cem-components/components/cem-media-preview/cem-media-preview.xhtml"}{cem:if @test="datadom.slices.sourceUrl" | {p | {a @href="{$datadom.slices.sourceUrl}" | Open canonical XHTML}}{cem-demo-element @legend="Canonical source" @src="{$datadom.slices.sourceUrl}" @type=xml @demo=false}}