6 bottom sheet animations. Copy any short prompt for free, and open full prompt 1 to see how detailed they are. Full prompts 2–6 are in the pack.
Basics: stage 470×222px, background #13131C, all coordinates in px from the stage's top-left, everything clipped to the stage (its two top corners are rounded with radius 40px). The animation is a 4s loop that plays by itself and is seamless (the state at 4.0s equals the state at 0s; u = loop time 0–4 in seconds, and all times below are values of u). The video is 12s = three loops; T = seconds since the video start (0–12), and u = T mod 4. T is used only for idle motion: the waves of the sea picture in the background screen appBg (period 4s; used in 01, 03, 04, 05, 06) and the pulsing ring around the chosen pin in 02 (period 1s); both periods divide 4, so you may use u instead of T, and 12s → 0s is seamless too. Nothing depends on which of the three loops is playing. Finger timing: a tap at 0.5s (01, 04), plus a second tap at 2.4s in 01 and 03 and at 1.8s in 04. 02, 05 and 06 make two drags in every loop: press at 0.5s, move during 0.6–1.4s, lift at 1.5s; press again at 2.3s, move back during 2.4–3.2s, lift at 3.3s; 3.3–4.0s is still. 03 makes one drag: press at 0.5s, move during 0.6–1.2s, lift at 1.3s. Colors: violet #6E5BFF, cyan #38C6FF, pink #FF5CA8, gold #FFD166, mint #3DDBB0. "violet→cyan from x = a to b" is a horizontal linear gradient with violet at x = a and cyan at x = b (in the current drawing frame). Text: font stack "Segoe UI Variable Display","Segoe UI",sans-serif, weight and size as given (weights used: 500, 600, 700, 800); x is the left edge unless "centered" or "right-aligned"; y is the alphabetic baseline unless "baseline middle" is stated. A text's own opacity multiplies with the surrounding opacity. mix(A, B, k) = per-channel linear RGB blend (k clamped to 0–1, channels rounded). "Glow"/"shadow" = a canvas shadow (that shadowColor and shadowBlur, no offset unless stated). rr(x, y, w, h, r) = rounded rect with top-left (x, y), corner radius max(0, min(r, w/2, h/2)). roundRect(x, y, w, h, [a, b, c, d]) = rect with corner radii top-left a, top-right b, bottom-right c, bottom-left d. Lines have butt (flat) caps unless "round caps" is stated. clamp(x) = x limited to 0–1; lerp(a, b, k) = a + (b − a)·k. Easing (k is clamped to 0–1 first): ease = 1 − (1−k)³, easeIn = k³, easeIO = 4k³ for k < 0.5, else 1 − (−2k+2)³/2, easeBack = 1 + 2.9(k−1)³ + 1.9(k−1)² (0→1, overshooting to about 1.12). seg(x, a, b) = clamp((x − a)/(b − a)); seg(a, b) with two arguments applies it to u. Spring springF(ω₀, ζ)(t) = 0 for t ≤ 0, else 1 − e^(−ζω₀t)·(cos ω_d t + (ζω₀/ω_d)·sin ω_d t), ω_d = ω₀√(1−ζ²) (rises 0 → 1 with an overshoot). settle(T0, from, to, ω₀, ζ) = from for u < T0, else lerp(from, to, springF(ω₀, ζ)(u − T0)) (released at T0, it springs from "from" to "to"). For an event at time E: rel = u − E wrapped into −2…+2 (negative before it), since = u − E wrapped into 0…4. Press pressK(a, d) (a = rel to the tap): 1 for a < −0.1 or a > 0.6; for −0.1 ≤ a < 0, 1 − d·(a+0.1)/0.1; for 0 ≤ a ≤ 0.6, 1 − d·(1 − springF(22, 0.45)(a)); the pressed element scales by it around the point given. Tap indicator tapMark(x, y, a) (a = rel to the tap; drawn on top of everything): visible only for −0.42 ≤ a < 0.4. A circle of radius 22.575px, fill rgba(215,215,230,0.38) with a soft shadow (black 35%, shadowBlur 10, fill only), then a 3.5px border white 92% (no shadow); fill and border are drawn in the circle's scaled frame, so the border width scales with the circle. Scale: for a < −0.1, lerp(1.25, 1, ease((a+0.42)/0.32)); for −0.1 ≤ a < 0, lerp(1, 0.8, (a+0.1)/0.1); for a ≥ 0, lerp(0.8, 1, ease(a/0.2)). Opacity: clamp((a+0.42)/0.18) before the tap, clamp(1 − (a−0.12)/0.25) after. Ripple (drawn under the circle, not scaled, only for 0 ≤ a < 0.4): a 3px white ring of radius 22.575·(1 + 3.2a) (22.6 → 51.5px), opacity 0.7·(1 − a/0.4) (its own opacity, not multiplied by the circle's opacity); it keeps growing for its full 0.4s. Hold indicator holdMark(x, y, T0, T1) (a finger that presses at T0 and lifts at T1, drawn on top of everything at the point (x, y) given at that moment, so it moves with a drag; a = rel to T0, b = since T1): while T0 ≤ u < T1 it has opacity 1 and scale lerp(0.8, 0.86, ease(a/0.2)) (0.86 after 0.2s); else if b < 0.4 (after the lift): opacity clamp(1 − (b−0.12)/0.25) (gone at b = 0.37), scale lerp(0.86, 1, ease(b/0.2)); else if −0.42 ≤ a < 0 it approaches exactly like the tap indicator before its tap; otherwise it is hidden. Same circle as the tap indicator. Its ripple is the same as the tap's and depends only on a: it is drawn whenever u ≥ T0 and 0 ≤ a < 0.4, so it keeps growing for its full 0.4s. fingers(x, y) = holdMark(x, y, 0.5, 1.5) then holdMark(x, y, 2.3, 3.3), both at the same point. Drag value drag2(a, b) = lerp(a, b, easeIO(seg(0.6, 1.4))) for u < 1.9, else lerp(b, a, easeIO(seg(2.4, 3.2))) (a → b with the first drag, b → a with the second). Background app screen appBg(title) (a made-up app screen): fillRect(0, 0, 470, 222) #13131C; the title (800 24px #F4F4F8) at (24, 44); picture scenery(118, 108, 188, 92, 16, kind 0) (x 24–212, y 62–154) and picture scenery(352, 108, 188, 92, 16, kind 1) (x 258–446, y 62–154); then two cards k = 0, 1: rr(24 + 234k, 166, 188, 40, 12) filled #1C1B2B, rr(38 + 234k, 180, 96, 6, 3) filled #3A3852, rr(38 + 234k, 192, 60, 5, 2.5) filled #2A2840. Picture scenery(x, y, w, h, r, kind) (made-up stand-in for a photo): clipped to rr(x − w/2, y − h/2, w, h, r), in a frame translated to its top-left (x − w/2, y − h/2), with ph = T·π/2. kind 0 (sunset mountains, no motion): fillRect(0, 0, w, h) with a vertical linear gradient from y = 0 to h: #3B2E86 (0), #7E5BD6 (0.5), #F3A5C8 (1); a sun disc #FFD8A8 at (0.7w, 0.42h), radius 0.13h; mountains = filled polygon #2A2266 through (0, h), (0.32w, 0.48h), (0.55w, 0.78h), (0.75w, 0.58h), (w, 0.85h), (w, h). kind 1 (sea): fillRect with a vertical gradient #1B3F7A (y = 0) → #38C6FF (y = h); a disc rgba(255,255,255,0.85) at (0.3w, 0.3h), radius 0.08h; three wave layers m = 0, 1, 2 (back to front) filled #2C6FC0, #1F55A0, #173F80: a polygon from (0, h) through the points (j/20·w, h·(0.62 + 0.12m) + sin(0.9j + ph + m)·0.03h) for j = 0…20, then (w, h). dimAll(a): if a > 0, fillRect(0, 0, 470, 222) #05050A at opacity a. Sheet sheetBody(y, x0 = 20, x1 = 450, r = 22, hw = 40): path = roundRect(x0, y, x1 − x0, 282 − y, [r, r, 0, 0]) (it runs past the stage bottom to y = 282); fill #1F1D33 with a shadow rgba(0,0,0,0.55), shadowBlur 24, shadowOffsetY −4 (the shadow falls upward); then stroke the same path 1.5px rgba(255,255,255,0.07) (no shadow); then, if hw > 0, a handle rr((x0 + x1)/2 − hw/2, y + 9, hw, 5, 2.5) filled #5E5B7E. Sheet row sheetRow(x, y, w, label, sub, color, a = 1): nothing if a ≤ 0; at opacity × a: a disc of radius 16 in color at (x + 16, y + 18); label (600 15px #F4F4F8) at (x + 44, y + 15); sub (500 12.5px #9D9BBB) at (x + 44, y + 33); divider fillRect(x + 44, y + 43, w − 44, 1) rgba(255,255,255,0.06). Check mark check(x, y, s, p, lw, color): a round-capped, round-joined polyline through (−0.30, 0.02) → (−0.09, 0.23) → (0.32, −0.20) × s from (x, y); "drawn by p" means only the first p (0–1) of its total length is drawn. People (made-up; 06 only), each (name, message, color, time, dark color = mix(color, #1A1830, 0.45)): ("Mia Park", "See you at 7?", #8F7BFF, "9:41", #5A4EA2), ("Leo Cruz", "Sent the files", #38C6FF, "9:12", #2B78A2), ("Ava Stone", "Lunch tomorrow?", #FF5CA8, "8:30", #983D72), ("Kai Ito", "Thanks a lot!", #3DDBB0, "Mon", #2D8376), ("Zoe Lane", "Look at this 👀", #FFD166, "Sun", #987E4E). Avatar avatar(x, y, r, person): a circle of radius r filled with a linear gradient from its top-left (x − r, y − r) in the person's color to its bottom-right (x + r, y + r) in the dark color, with the name's first letter (700, 0.95·r px, white, centered, baseline middle) at (x, y + 1). Message row msgRow(x0, y0, w, h, person, bg) (06 only): S0 = h/0.28, yc = y0 + h/2; rr(x0, y0, w, h, 0.06·S0) filled bg; avatar(x0 + 0.15·S0, yc, 0.085·S0); name (600, 0.08·S0 px, #C4C2E0) at (x0 + 0.3·S0, yc − 0.012·S0); message (500, 0.066·S0 px, #77758F) at (x0 + 0.3·S0, yc + 0.078·S0); time (500, 0.058·S0 px, #6D6B88) right-aligned at (x0 + w − 0.07·S0, yc − 0.012·S0). With h = 52: S0 = 185.714, corner radius 11.143, avatar at x0 + 27.857 with radius 15.786 (letter 15px), name 14.857px at x0 + 55.714 and yc − 2.229, message 12.257px at yc + 14.486, time 10.771px at x0 + w − 13.
A settings sheet sits over a dimmed app screen; tapping a row slides a second sheet up over it while the first sheet shrinks back, moves up and darkens; tapping the back chevron drops the second sheet away.
- Background: appBg("Profile"), then dimAll(0.5).
- Second sheet top yB: 240 for u < 0.55; settle(0.55, 240, 92, ω₀ 14, ζ 0.72) for 0.55 ≤ u < 2.42 (it springs up and overshoots about 5.7px to ≈ 86.3 before resting at 92); from 2.42, lerp(92, 240, easeIn(seg(2.42, 2.72))) (it falls off the bottom). k = clamp((240 − yB)/148) (0 → 1 as the second sheet rises).
- Chevron chev(x, y, color, dir): a stroked polyline, line width 2.5, round caps and joins: (x + 4·dir, y − 7) → (x − 3·dir, y) → (x + 4·dir, y + 7) (dir = −1 points right ›, dir = 1 points left ‹).
- Toggle toggle(x, y, on): rr(x, y, 40, 24, 12) filled mix(#3A3852, violet, on) (#3A3852 when on = 0); a white disc of radius 9 at (x + 12 + 16·on, y + 12). The toggles do not animate.
- First sheet group, drawn in a frame: translate(235, 54), scale(s, s) with s = 1 − 0.06k, translate(−235, −54 − 12k) (a point (x, y) lands at (235 + s·(x − 235), 54 + s·(y − 54 − 12k)): it shrinks to 94% around (235, 54) and its top moves up 11.28px). In this frame:
- sheetBody(54); "Settings" (700 18px #F4F4F8) at (42, 94).
- Four rows j = 0–3: sheetRow(42, 106 + 48j, 386, label, sub, color) with ("Account", "Name, email, photo", violet), ("Notifications", "Sounds, badges", pink), ("Privacy", "Who can see you", cyan), ("Storage", "2.1 GB used", gold); each followed by chev(420, 124 + 48j, #6D6B88, −1). Row j = 1 (with its chevron) is scaled by pressK(rel to 0.5, 0.03) around (235, 172).
- Veil: roundRect(20, 54, 430, 240, [22, 22, 0, 0]) filled #05050A at opacity 0.45k.
- Second sheet (only if yB < 240): sheetBody(yB); back chevron chev(46, yB + 34, #C9C3F0, 1); "Notifications" (700 17px #F4F4F8) centered at (235, yB + 40); three rows j = 0–2 with (label, on) = ("Sounds", 1), ("Badges", 0), ("Previews", 1): label (600 15px #E9E8F5) at (42, yB + 82 + 42j); toggle(388, yB + 64 + 42j, on); divider fillRect(42, yB + 96 + 42j, 386, 1) rgba(255,255,255,0.06).
- Taps: tapMark(300, 172) at 0.5 (on the "Notifications" row); tapMark(46, 126) at 2.4 (on the back chevron, when yB ≈ 92).
- Draw order: background, dim, first sheet group (sheet, "Settings", rows with chevrons, veil), second sheet (sheet, chevron, title, toggle rows), tap indicator for 0.5, tap indicator for 2.4.