Crows keep their size; sparkles mean celebration only
- No animation scales a crow: reactions use hops, tilts and spins - Waiting is a plain "?" again; dazed crows tip and fade with no circling stars (they read as drunk) - RIVE_CONTRACT.md updated to match Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UQnSw4RRWp8uqpj3nsnnXF
This commit is contained in:
2 files changed
+10
-15
No files matched your search
+2
-2
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
The animator builds `crows.riv` against this file, and the web code drives it from the events in `EVENTS.md`. Both sides build against this file, so change it first and then the code. Each animation exists only because an `EVENTS.md` event fires it.
|
The animator builds `crows.riv` against this file, and the web code drives it from the events in `EVENTS.md`. Both sides build against this file, so change it first and then the code. Each animation exists only because an `EVENTS.md` event fires it.
|
||||||
|
|
||||||
Style: Duolingo-like behavior, pen-and-ink look: cream paper, ink linework, stipple shading (see `ui/src/style.css`). Smokey's crow art (`art/crow.svg`) is the character; keep its silhouette. Chunky, expressive crows with big squash-and-stretch reactions and readable silhouettes at 96 pixels. The approve and reject buttons stay in HTML for v1 (see "Why buttons stay in HTML").
|
Style: Duolingo-like behavior, pen-and-ink look: cream paper, ink linework, stipple shading (see `ui/src/style.css`). Smokey's crow art (`art/crow.svg`) is the character; keep its silhouette. Expressive crows that react through motion (hops, tilts, spins) and never change size, with readable silhouettes at 96 pixels. Sparkles mean celebration only. The approve and reject buttons stay in HTML for v1 (see "Why buttons stay in HTML").
|
||||||
|
|
||||||
## Rive approach: data binding, not inputs
|
## Rive approach: data binding, not inputs
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ The 8 crows tell themselves apart by `name` in v1. A `plumage` Color property fo
|
|||||||
| `perched` | yes | Entry state. Idle breathing, blinking, a head tilt every few seconds. |
|
| `perched` | yes | Entry state. Idle breathing, blinking, a head tilt every few seconds. |
|
||||||
| `flying` | yes | Wings flapping, a slight bob. It reads as "working". |
|
| `flying` | yes | Wings flapping, a slight bob. It reads as "working". |
|
||||||
| `waiting` | yes | Holds a rolled scroll up toward the viewer and taps a foot. It reads as "your turn". |
|
| `waiting` | yes | Holds a rolled scroll up toward the viewer and taps a foot. It reads as "your turn". |
|
||||||
| `grounded` | yes | Sitting dazed with stars circling. It means failure or timeout. |
|
| `grounded` | yes | Tipped over and faded. It means failure or timeout. No circling stars (they read as drunk). |
|
||||||
|
|
||||||
Transitions: Any State → each state when `pose` equals that value, with a duration of 200 milliseconds (ms). There's no Exit Time, so a pose change is instant to read.
|
Transitions: Any State → each state when `pose` equals that value, with a duration of 200 milliseconds (ms). There's no Exit Time, so a pose change is instant to read.
|
||||||
|
|
||||||
|
|||||||
+8
-13
@@ -117,15 +117,10 @@ main { max-width: 680px; margin: 0 auto; padding: 0 16px 64px; }
|
|||||||
.pose-flying .jaw { transform: rotate(-10deg); }
|
.pose-flying .jaw { transform: rotate(-10deg); }
|
||||||
.pose-waiting .crow-body { animation: tilt 1.4s ease-in-out infinite; }
|
.pose-waiting .crow-body { animation: tilt 1.4s ease-in-out infinite; }
|
||||||
.pose-waiting::after {
|
.pose-waiting::after {
|
||||||
content: ""; position: absolute; top: -14px; left: 0; width: 34px; height: 24px;
|
content: "?"; position: absolute; top: -12px; left: 4px; font: 400 26px/1 var(--display);
|
||||||
background: var(--sparkle) 0 2px / 18px 18px no-repeat, var(--glint) 22px 0 / 11px 11px no-repeat;
|
animation: pop 1.4s ease-in-out infinite;
|
||||||
animation: twinkle-sparkle 1.4s ease-in-out infinite;
|
|
||||||
}
|
}
|
||||||
.pose-grounded .crow-body { transform: rotate(-18deg) translateY(6px); opacity: 0.5; }
|
.pose-grounded .crow-body { transform: rotate(-18deg) translateY(6px); opacity: 0.5; }
|
||||||
.pose-grounded::after {
|
|
||||||
content: ""; position: absolute; top: -8px; width: 40px; height: 14px; animation: spin 1.6s linear infinite;
|
|
||||||
background: var(--sparkle) 0 2px / 10px 10px no-repeat, var(--glint) 15px 0 / 9px 9px no-repeat, var(--sparkle) 28px 3px / 9px 9px no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reaction layer: one-shots on top */
|
/* Reaction layer: one-shots on top */
|
||||||
.r-cheer .crow-body { animation: hop 0.8s ease-out; }
|
.r-cheer .crow-body { animation: hop 0.8s ease-out; }
|
||||||
@@ -140,19 +135,19 @@ main { max-width: 680px; margin: 0 auto; padding: 0 16px 64px; }
|
|||||||
.r-levelDown .crow-body { animation: shrug 1s ease-out; }
|
.r-levelDown .crow-body { animation: shrug 1s ease-out; }
|
||||||
.r-prank .crow-body { animation: prank 2.5s ease-in-out; }
|
.r-prank .crow-body { animation: prank 2.5s ease-in-out; }
|
||||||
|
|
||||||
@keyframes breathe { 50% { transform: scale(1.03, 0.98); } }
|
@keyframes breathe { 50% { transform: translateY(-2px); } }
|
||||||
@keyframes flap { from { transform: translateY(0) rotate(-3deg); } to { transform: translateY(-8px) rotate(3deg); } }
|
@keyframes flap { from { transform: translateY(0) rotate(-3deg); } to { transform: translateY(-8px) rotate(3deg); } }
|
||||||
@keyframes tilt { 50% { transform: rotate(8deg); } }
|
@keyframes tilt { 50% { transform: rotate(8deg); } }
|
||||||
@keyframes pop { 50% { transform: translateY(-4px); } }
|
@keyframes pop { 50% { transform: translateY(-4px); } }
|
||||||
@keyframes spin { to { transform: rotate(360deg); } }
|
@keyframes spin { to { transform: rotate(360deg); } }
|
||||||
@keyframes twinkle-sparkle { 50% { transform: scale(1.15) rotate(8deg); } }
|
@keyframes twinkle-sparkle { 50% { transform: scale(1.15) rotate(8deg); } }
|
||||||
@keyframes twinkle { 50% { transform: rotate(-12deg) scale(1.15); } }
|
@keyframes twinkle { 50% { transform: rotate(-12deg) scale(1.15); } }
|
||||||
@keyframes hop { 30% { transform: translateY(-22px) scale(0.95, 1.08); } 60% { transform: translateY(0) scale(1.08, 0.92); } }
|
@keyframes hop { 35% { transform: translateY(-20px); } 60% { transform: translateY(0); } }
|
||||||
@keyframes droop { 40%, 70% { transform: translateY(6px) rotate(-6deg) scale(1, 0.94); } }
|
@keyframes droop { 40%, 70% { transform: translateY(6px) rotate(-6deg); } }
|
||||||
@keyframes levelup { 20% { transform: scale(1.25) rotate(0); } 60% { transform: scale(1.25) rotate(360deg); } }
|
@keyframes levelup { 15% { transform: translateY(-14px); } 60% { transform: translateY(-14px) rotate(360deg); } }
|
||||||
@keyframes rise { from { opacity: 1; transform: translateY(0) scale(0.6); } to { opacity: 0; transform: translateY(-30px) scale(1.4); } }
|
@keyframes rise { from { opacity: 1; transform: translateY(0) scale(0.6); } to { opacity: 0; transform: translateY(-30px) scale(1.4); } }
|
||||||
@keyframes shrug { 30% { transform: scale(0.9) rotate(-4deg); } 60% { transform: scale(0.9) rotate(4deg); } }
|
@keyframes shrug { 30% { transform: rotate(-5deg); } 60% { transform: rotate(5deg); } }
|
||||||
@keyframes prank { 25% { transform: translate(-120px, -60px) rotate(-30deg); opacity: 0; } 55% { transform: translate(120px, -60px); opacity: 0; } 80% { transform: scale(1.1); opacity: 1; } }
|
@keyframes prank { 25% { transform: translate(-120px, -60px) rotate(-30deg); opacity: 0; } 55% { transform: translate(120px, -60px); opacity: 0; } 80% { transform: none; opacity: 1; } }
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.crow *, .crow::before, .crow::after { animation: none !important; transition: none !important; }
|
.crow *, .crow::before, .crow::after { animation: none !important; transition: none !important; }
|
||||||
|
|||||||
Reference in new issue
Block a user