From 8422da814d8311d605eaba8b99bf3bc63c21241a Mon Sep 17 00:00:00 2001 From: Kam Date: Tue, 21 Jul 2026 17:22:58 +0300 Subject: [PATCH] feat(docs-infra): react with Angie in the playground minigame MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The angle-guessing minigame's result popup personifies your score with a hand-drawn stick-figure that changes expression by accuracy. Now that Angie appears across adev — the 404 page, docs search, the embedded editor, the tutorial intros and completions — give the minigame the same treatment so its result feels consistent with the rest of the docs. Replace the result reactor's stick-figure illustration with an Angie pose keyed to accuracy (seven tiers, superhero down to angry). She is revealed once the accuracy counter finishes counting up, popping in beside a speech bubble that carries the round's existing quote. The result popup is widened so Angie and the bubble sit side by side, and the share link is moved from twitter.com to x.com. This removes the previous hand-drawn stick-figure result art. NG the Angle, the interactive character in the play area, is unchanged. --- .../3-minigame/src/assets/angry.svg | 1 + .../3-minigame/src/assets/coffee.svg | 1 + .../3-minigame/src/assets/error.svg | 1 + .../3-minigame/src/assets/greeting.svg | 1 + .../3-minigame/src/assets/laughing.svg | 1 + .../playground/3-minigame/src/assets/sad.svg | 1 + .../3-minigame/src/assets/superhero.svg | 1 + .../playground/3-minigame/src/game.css | 69 ++++++++++++----- .../playground/3-minigame/src/game.html | 75 ++----------------- .../playground/3-minigame/src/main.ts | 19 ++++- 10 files changed, 83 insertions(+), 87 deletions(-) create mode 100644 adev/src/content/tutorials/playground/3-minigame/src/assets/angry.svg create mode 100644 adev/src/content/tutorials/playground/3-minigame/src/assets/coffee.svg create mode 100644 adev/src/content/tutorials/playground/3-minigame/src/assets/error.svg create mode 100644 adev/src/content/tutorials/playground/3-minigame/src/assets/greeting.svg create mode 100644 adev/src/content/tutorials/playground/3-minigame/src/assets/laughing.svg create mode 100644 adev/src/content/tutorials/playground/3-minigame/src/assets/sad.svg create mode 100644 adev/src/content/tutorials/playground/3-minigame/src/assets/superhero.svg diff --git a/adev/src/content/tutorials/playground/3-minigame/src/assets/angry.svg b/adev/src/content/tutorials/playground/3-minigame/src/assets/angry.svg new file mode 100644 index 00000000000..0f8c92a2e4e --- /dev/null +++ b/adev/src/content/tutorials/playground/3-minigame/src/assets/angry.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/adev/src/content/tutorials/playground/3-minigame/src/assets/coffee.svg b/adev/src/content/tutorials/playground/3-minigame/src/assets/coffee.svg new file mode 100644 index 00000000000..9e0f119c08d --- /dev/null +++ b/adev/src/content/tutorials/playground/3-minigame/src/assets/coffee.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/adev/src/content/tutorials/playground/3-minigame/src/assets/error.svg b/adev/src/content/tutorials/playground/3-minigame/src/assets/error.svg new file mode 100644 index 00000000000..cae8db62f54 --- /dev/null +++ b/adev/src/content/tutorials/playground/3-minigame/src/assets/error.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/adev/src/content/tutorials/playground/3-minigame/src/assets/greeting.svg b/adev/src/content/tutorials/playground/3-minigame/src/assets/greeting.svg new file mode 100644 index 00000000000..21fd754dcab --- /dev/null +++ b/adev/src/content/tutorials/playground/3-minigame/src/assets/greeting.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/adev/src/content/tutorials/playground/3-minigame/src/assets/laughing.svg b/adev/src/content/tutorials/playground/3-minigame/src/assets/laughing.svg new file mode 100644 index 00000000000..5ac679258f4 --- /dev/null +++ b/adev/src/content/tutorials/playground/3-minigame/src/assets/laughing.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/adev/src/content/tutorials/playground/3-minigame/src/assets/sad.svg b/adev/src/content/tutorials/playground/3-minigame/src/assets/sad.svg new file mode 100644 index 00000000000..2f62614f04d --- /dev/null +++ b/adev/src/content/tutorials/playground/3-minigame/src/assets/sad.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/adev/src/content/tutorials/playground/3-minigame/src/assets/superhero.svg b/adev/src/content/tutorials/playground/3-minigame/src/assets/superhero.svg new file mode 100644 index 00000000000..16f228d98bd --- /dev/null +++ b/adev/src/content/tutorials/playground/3-minigame/src/assets/superhero.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/adev/src/content/tutorials/playground/3-minigame/src/game.css b/adev/src/content/tutorials/playground/3-minigame/src/game.css index 4980150df4c..3dc8572885b 100644 --- a/adev/src/content/tutorials/playground/3-minigame/src/game.css +++ b/adev/src/content/tutorials/playground/3-minigame/src/game.css @@ -234,38 +234,73 @@ a.gradient-button:focus-visible span:nth-of-type(2) { top: 50%; left: 50%; transform: translate(-50%, -50%); - width: 50%; + width: min(90%, 480px); display: flex; flex-direction: column; - justify-content: space-around; + justify-content: center; align-items: center; + gap: 1rem; padding: 2rem; z-index: 10; } -svg.personified { - height: 125px; +.angie-reaction { + display: flex; + align-items: flex-start; + gap: 0.75rem; } -.personified .g { - fill: #fff; +.personified { + height: 160px; + width: auto; + flex: none; + animation: pop-in 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); } -.personified .h { - stroke-miterlimit: 10; - stroke-width: 4px; +@keyframes pop-in { + from { + opacity: 0; + transform: scale(0.6) translateY(10px); + } + + to { + opacity: 1; + transform: scale(1) translateY(0); + } } -.personified .h, -.personified .i { - fill: none; - stroke: #000; - stroke-linecap: round; +.result-quote { + position: relative; + margin-top: 2rem; + max-width: 32ch; + padding: 0.85rem 1.25rem; + border-radius: 1rem; + background: #f1f1f3; + text-align: center; + animation: fade-in 0.4s ease 0.15s both; } -.personified .i { - stroke-linejoin: round; - stroke-width: 3px; +.result-quote::before { + content: ''; + position: absolute; + top: 1.1rem; + left: -0.4rem; + width: 0.9rem; + height: 0.9rem; + background: inherit; + transform: rotate(45deg); +} + +@keyframes fade-in { + from { + opacity: 0; + transform: translateY(6px); + } + + to { + opacity: 1; + transform: translateY(0); + } } #close { diff --git a/adev/src/content/tutorials/playground/3-minigame/src/game.html b/adev/src/content/tutorials/playground/3-minigame/src/game.html index 2f6d646b150..3c635edc86c 100644 --- a/adev/src/content/tutorials/playground/3-minigame/src/game.html +++ b/adev/src/content/tutorials/playground/3-minigame/src/game.html @@ -170,73 +170,12 @@ {{ animatedAccuracy() | number: '1.1-1' }}% accurate - - - - - - - - - @if (animatedAccuracy() > 95) { - - - - - - - - } @else if (animatedAccuracy() > 80) { - - - - - - } @else if (animatedAccuracy() > 60) { - - - - - - - - } @else if (animatedAccuracy() > 40) { - - - - - - - - } @else { - - - - - - - - } - -
"{{ resultQuote() }}"
+ @if (showPose()) { +
+ +
{{ resultQuote() }}
+
+ }
diff --git a/adev/src/content/tutorials/playground/3-minigame/src/main.ts b/adev/src/content/tutorials/playground/3-minigame/src/main.ts index 5141fbadec9..b7f546f75c9 100644 --- a/adev/src/content/tutorials/playground/3-minigame/src/main.ts +++ b/adev/src/content/tutorials/playground/3-minigame/src/main.ts @@ -62,6 +62,7 @@ export class Playground { protected readonly rotateVal = signal(40); protected readonly goal = signal(85); protected readonly animatedAccuracy = signal(0); + protected readonly finalAccuracy = signal(0); protected readonly gameStats = signal({ level: 0, totalAccuracy: 0, @@ -100,6 +101,19 @@ export class Playground { return this.currentInteractions; }); + protected readonly resultPose = computed(() => { + const accuracy = this.finalAccuracy(); + if (accuracy > 95) return 'superhero.svg'; + if (accuracy > 80) return 'greeting.svg'; + if (accuracy > 60) return 'coffee.svg'; + if (accuracy > 40) return 'sad.svg'; + if (accuracy > 20) return 'error.svg'; + if (accuracy > 10) return 'laughing.svg'; + return 'angry.svg'; + }); + + protected readonly showPose = computed(() => this.animatedAccuracy() >= this.finalAccuracy()); + protected readonly rotation = computed(() => `rotate(${this.rotateVal()}deg)`); protected readonly indicatorStyle = computed(() => 0.487 * this.rotateVal() - 179.5); @@ -161,6 +175,7 @@ export class Playground { this.isGuessModalOpen.set(true); const calcAcc = Math.abs(100 - (Math.abs(this.goal() - this.rotateVal()) / 180) * 100); this.resultQuote.set(getResultQuote(calcAcc)); + this.finalAccuracy.set(calcAcc); this.animatedAccuracy.set(calcAcc > 20 ? calcAcc - 20 : 0); this.powerUpAccuracy(calcAcc); this.gameStats.update(({level, totalAccuracy}) => ({ @@ -203,9 +218,9 @@ export class Playground { emojiAccuracy += roundedAcc >= 20 * (i + 1) ? '🟩' : '⬜️'; } return encodeURI( - `📐 ${emojiAccuracy} \n My angles are ${roundedAcc}% accurate on level ${ + `📐 ${emojiAccuracy}\n\nI'm ${roundedAcc}% accurate at angles on level ${ this.gameStats().level - }. \n\nHow @Angular are you? \nhttps://angular.dev/playground`, + }.\n\nHow @Angular are you?\nhttps://angular.dev/playground`, ); }