Workspace snapshots
Capture a workspace state, compare later changes, and restore the version you need.
Automatic history One-click restore
Animate the expander part with the --opening and --closing states. Keep grid-template-rows: 1fr during closing when the keyframes do not animate rows.
Capture a workspace state, compare later changes, and restore the version you need.
The following example assumes that you have imported the component and set up the theme.
<div class="custom-animation-example"> <sv-flyout class="update-flyout" position-order="bottom center center, top center center"> <button type="button" class="update-trigger" slot="trigger"> <span aria-hidden="true">✨</span> See what’s new </button>
<article class="update-card">12 collapsed lines
<span class="update-version">Version 3.2</span> <h2>Workspace snapshots</h2> <p>Capture a workspace state, compare later changes, and restore the version you need.</p>
<div class="update-details"> <span>Automatic history</span> <span>One-click restore</span> </div> </article> </sv-flyout></div>
<style>19 collapsed lines
.custom-animation-example { display: grid; place-items: center; min-block-size: 320px;
.update-trigger { display: flex; gap: 0.5em; align-items: center; padding: 0.65em 1em; font: inherit; font-weight: 650; color: var(--sv-gray-1); cursor: pointer; background: var(--sv-gray-6); border: solid var(--sv-bdr-sz-sm) var(--sv-gray-5); border-radius: var(--sv-bdr-rad-md); }
.update-flyout { /* Override default durations. */ --sv-open-dur: 400ms; --sv-close-dur: 300ms;
/* --ease-*() syntax is replaced with linear() at build time you can use your own easing functions. */ --update-enter-ease: --ease-out-wobble(2.4); --update-exit-ease: --ease-in-back(6);
&::part(content) { inline-size: min(22rem, calc(100vw - 2rem)); min-block-size: 200px; padding: 0; }
&:state(--opening)::part(expander) { /* Apply the custom animation for the opening phase. */ animation: update-card-enter var(--sv-open-dur) var(--update-enter-ease); }
&:state(--closing)::part(expander) { /* Keep the expander laid out while the visual exit runs. The component hides the popover after this animation ends. */ grid-template-rows: 1fr;
/* Apply the custom animation for the closing phase. */ animation: update-card-exit var(--sv-close-dur) var(--update-exit-ease); }
/* Custom animations must explicitly honor the user’s motion preference. */ @media (prefers-reduced-motion: reduce) { --sv-open-dur: 0s; --sv-close-dur: 0s; } }
40 collapsed lines
.update-card { padding: 1.25em; color: var(--sv-gray-1); background: radial-gradient(circle at top right, color-mix(in srgb, var(--sv-accent) 22%, transparent), transparent 45%), var(--sv-gray-7);
.update-version { font-size: 0.75rem; font-weight: 750; color: var(--sv-accent); text-transform: uppercase; letter-spacing: 0.08em; }
h2 { margin: 0.35em 0 0; font-size: 1.25rem; }
p { margin: 0.65em 0 1em; line-height: 1.5; color: var(--sv-gray-2); }
.update-details { display: flex; flex-wrap: wrap; gap: 0.5em;
span { padding: 0.35em 0.6em; font-size: 0.8rem; background: var(--sv-gray-6); border-radius: 100vmax; } } } }
@keyframes update-card-enter { from { opacity: 0; transform: perspective(650px) translateY(1.25rem) rotateX(-24deg) rotateZ(-2deg); scale: 0.78; }
to { opacity: 1; transform: perspective(650px) translateY(0) rotateX(0) rotateZ(0); scale: 1; } }
@keyframes update-card-exit { from { opacity: 1; transform: perspective(650px) translateY(0) rotateX(0) rotateZ(0); scale: 1; }
to { opacity: 0; transform: perspective(650px) translateY(-1.25rem) rotateX(16deg) rotateZ(-1deg); scale: 0.9; } }</style>