Incoming signal
Kepler relay / A–17
- Origin
- Kepler-186f
- Distance
- 582 ly
- Integrity
- 96.4%
Animate the expander part with the --opening and --closing states. Keep grid-template-rows: 1fr during closing when the keyframes do not animate rows.
The following example assumes that you have imported the component and set up the theme.
<div class="signal-console-example"> <button type="button" class="signal-trigger" aria-controls="signal-transmission" aria-expanded="false"> <span class="signal-pulse" aria-hidden="true"></span> Decode transmission <span class="signal-code" aria-hidden="true">A–17</span> </button>
<sv-collapsible id="signal-transmission" class="signal-collapsible">84 collapsed lines
<article class="signal-panel"> <header> <div> <span class="eyebrow">Incoming signal</span> <h2>Kepler relay / A–17</h2> </div> </header>
<p class="signal-message">“The quietest route is clear. Follow the blue markers until the second sunrise.”</p>
<dl class="signal-data"> <div> <dt>Origin</dt> <dd>Kepler-186f</dd> </div> <div> <dt>Distance</dt> <dd>582 ly</dd> </div> <div> <dt>Integrity</dt> <dd>96.4%</dd> </div> </dl> </article> </sv-collapsible></div>
<script> const container = document.querySelector(".signal-console-example"); const trigger = container.querySelector(".signal-trigger"); const collapsible = container.querySelector("sv-collapsible");
trigger.addEventListener("click", () => { collapsible.toggle(); trigger.ariaExpanded = String(collapsible.opened); });</script>
<style> .signal-console-example { inline-size: min(34rem, 100%); padding: 1rem; margin: auto; overflow: hidden; font-family: ui-monospace, "Cascadia Code", monospace; background: linear-gradient(color-mix(in srgb, var(--sv-accent) 5%, transparent) 1px, transparent 1px), linear-gradient(90deg, color-mix(in srgb, var(--sv-accent) 5%, transparent) 1px, transparent 1px), var(--sv-gray-8); background-size: 1.25rem 1.25rem;
.signal-trigger { display: grid; grid-template-columns: auto 1fr auto; gap: 0.75em; align-items: center; inline-size: 100%; padding: 0.8em 1em; font: inherit; font-weight: 700; color: var(--sv-gray-1); text-align: start; cursor: pointer; background: color-mix(in srgb, var(--sv-gray-7) 88%, transparent); border: solid var(--sv-bdr-sz-sm) var(--sv-gray-5); border-radius: var(--sv-bdr-rad-md); transition: border-color 350ms ease;
.signal-pulse { inline-size: 0.65em; block-size: 0.65em; background: var(--sv-accent); border-radius: 50%; box-shadow: 0 0 0.75em var(--sv-accent); }
.signal-code { font-size: 0.75em; font-weight: 500; color: var(--sv-gray-3); letter-spacing: 0.12em; } }
&:has(.signal-collapsible:state(--open)) .signal-trigger { border-color: var(--sv-accent); }
/* Phase states allow each direction to use its own timing and keyframes. */ .signal-collapsible { --sv-open-dur: 350ms; --sv-close-dur: 450ms; --signal-enter-ease: --ease-out-back(2); --signal-exit-ease: cubic-bezier(0.16, 1, 0.3, 1);
&:state(--opening)::part(expander) { animation: signal-unfold var(--sv-open-dur) var(--signal-enter-ease); }
&:state(--closing)::part(expander) { animation: signal-fold var(--sv-close-dur) var(--signal-exit-ease); }
/* Custom animations must explicitly honor reduced-motion preferences. */ @media (prefers-reduced-motion: reduce) { --sv-open-dur: 0s; --sv-close-dur: 0s; } }
85 collapsed lines
.signal-panel { position: relative; padding: 1.25em; margin-block-start: 0.65rem; color: var(--sv-gray-2); background: linear-gradient(135deg, color-mix(in srgb, var(--sv-accent) 13%, transparent), transparent 45%), color-mix(in srgb, var(--sv-gray-7) 94%, transparent); border: solid var(--sv-bdr-sz-sm) color-mix(in srgb, var(--sv-accent) 38%, var(--sv-gray-5)); border-radius: var(--sv-bdr-rad-md);
&::before { position: absolute; inset-block: 0; inset-inline-start: 1.25em; inline-size: 1px; content: ""; background: linear-gradient(transparent, var(--sv-accent), transparent); opacity: 0.35; }
header { display: flex; gap: 1em; align-items: start; justify-content: space-between; padding-inline-start: 1em; }
.eyebrow, dt { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.12em; }
.eyebrow { color: var(--sv-accent); }
h2 { margin: 0.25em 0 0; font-size: 1.05rem; color: var(--sv-gray-1); }
.signal-message { padding: 1em; margin-block: 1em; font-family: system-ui, sans-serif; font-size: 1.05rem; font-style: italic; line-height: 1.55; background: color-mix(in srgb, var(--sv-gray-8) 55%, transparent); border-inline-start: solid 2px var(--sv-accent); }
.signal-data { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5em; margin: 0;
div { min-inline-size: 0; padding: 0.65em; background: color-mix(in srgb, var(--sv-gray-8) 45%, transparent); border-radius: var(--sv-bdr-rad-sm); }
dt { color: var(--sv-gray-3); }
dd { margin: 0.25em 0 0; overflow: hidden; text-overflow: ellipsis; font-size: 0.8rem; color: var(--sv-gray-1); white-space: nowrap; } } } }
/* Grid rows animate the occupied height; the other properties shape the visual reveal. */ @keyframes signal-unfold { from { grid-template-rows: 0fr; opacity: 0; filter: blur(10px); transform: translateY(-1rem) scale(0.9); }
to { grid-template-rows: 1fr; opacity: 1; filter: blur(0); transform: translateY(0) scale(1); } }
@keyframes signal-fold { from { grid-template-rows: 1fr; opacity: 1; filter: blur(0); clip-path: inset(0); transform: translateY(0) scaleY(1); }
to { grid-template-rows: 0fr; opacity: 0; filter: blur(8px); clip-path: inset(0 0 100%); transform: translateY(-0.5rem) scaleY(0.94); } }</style>