frogoe 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/bin/frogoe.mjs +11 -0
  2. package/dist/cli.js +3124 -0
  3. package/dist/contract/contract.js +236 -0
  4. package/dist/registry/blocks/coin-counter/coin-counter.html +101 -0
  5. package/dist/registry/blocks/coin-counter/registry-item.json +27 -0
  6. package/dist/registry/blocks/combo-counter/combo-counter.html +106 -0
  7. package/dist/registry/blocks/combo-counter/registry-item.json +27 -0
  8. package/dist/registry/blocks/fuel-gauge/fuel-gauge.html +130 -0
  9. package/dist/registry/blocks/fuel-gauge/registry-item.json +29 -0
  10. package/dist/registry/blocks/game-over-card/game-over-card.html +185 -0
  11. package/dist/registry/blocks/game-over-card/registry-item.json +30 -0
  12. package/dist/registry/blocks/hearts-row/hearts-row.html +92 -0
  13. package/dist/registry/blocks/hearts-row/registry-item.json +27 -0
  14. package/dist/registry/blocks/mini-board/mini-board.html +125 -0
  15. package/dist/registry/blocks/mini-board/registry-item.json +28 -0
  16. package/dist/registry/blocks/objective-chip/objective-chip.html +101 -0
  17. package/dist/registry/blocks/objective-chip/registry-item.json +29 -0
  18. package/dist/registry/blocks/ready-gate/ready-gate.html +124 -0
  19. package/dist/registry/blocks/ready-gate/registry-item.json +28 -0
  20. package/dist/registry/blocks/ready-hint/ready-hint.html +104 -0
  21. package/dist/registry/blocks/ready-hint/registry-item.json +29 -0
  22. package/dist/registry/blocks/score-card/registry-item.json +27 -0
  23. package/dist/registry/blocks/score-card/score-card.html +92 -0
  24. package/dist/registry/blocks/timer-ring/registry-item.json +28 -0
  25. package/dist/registry/blocks/timer-ring/timer-ring.html +123 -0
  26. package/dist/registry/registry.json +51 -0
  27. package/dist/registry/schema/registry-item.schema.json +36 -0
  28. package/dist/registry/schema/registry.schema.json +22 -0
  29. package/dist/runtimeVersion.js +10 -0
  30. package/dist/templates/_shared/AGENTS.md +105 -0
  31. package/dist/templates/_shared/CLAUDE.md +105 -0
  32. package/package.json +42 -0
@@ -0,0 +1,185 @@
1
+ <!doctype html>
2
+ <!-- frogoe block source: game-over-card — this file IS viewable in a browser (default
3
+ state on a neutral backdrop). Copy ONLY between the markers into your
4
+ game's blocks/ layer; the interactive playground is demo.html. -->
5
+ <html lang="en">
6
+ <head>
7
+ <meta charset="utf-8" />
8
+ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
9
+ <title>frogoe block — game-over-card</title>
10
+ <style>
11
+ body {
12
+ margin: 0;
13
+ min-height: 100vh;
14
+ display: grid;
15
+ place-items: center;
16
+ background: radial-gradient(120% 120% at 50% 0%, #182031 0%, #0d1220 60%, #080b13 100%);
17
+ }
18
+ </style>
19
+ </head>
20
+ <body>
21
+ <!-- ===================== COPY FROM HERE ===================== -->
22
+ <!--
23
+ frogoe block: game-over-card
24
+ End-of-run card: title, score, best, one chunky retry button. Hidden by
25
+ default; the game shows it on death and wires the button to restart.
26
+ Entrance pops in; NEW BEST state is a data attribute that flips the
27
+ subtitle and tints the accent.
28
+
29
+ Bind from game.js (vanilla, no helper):
30
+ const card = document.querySelector("[data-block-gameover]");
31
+ card.querySelector("[data-block-final]").textContent = score;
32
+ card.querySelector("[data-block-best]").textContent = best;
33
+ card.toggleAttribute("data-new-best", isNewBest);
34
+ card.showModal === undefined; // it is a plain overlay:
35
+ card.toggleAttribute("data-open", true);
36
+ card.querySelector("[data-block-retry]").addEventListener("click", () => {
37
+ location.reload(); // or your own reset
38
+ });
39
+
40
+ Theme: --block-card, --block-accent, --block-cta, --block-cta-label, --block-font.
41
+ -->
42
+ <style>
43
+ .block-game-over {
44
+ --block-accent: #ffd166;
45
+ --block-card: #171d2e;
46
+ --block-cta: #ffcf3f;
47
+ --block-cta-label: #241a06;
48
+ --block-font: "Fredoka", "Baloo 2", system-ui, sans-serif;
49
+ --block-outline: #0a0e1a;
50
+
51
+ position: absolute;
52
+ inset: 0;
53
+ display: grid;
54
+ place-items: center;
55
+ background: rgba(4, 6, 12, 0.55);
56
+ font-family: var(--block-font);
57
+ color: #fffdf7;
58
+ opacity: 0;
59
+ pointer-events: none;
60
+ transition: opacity 220ms ease;
61
+ -webkit-user-select: none;
62
+ user-select: none;
63
+ }
64
+ .block-game-over[data-open] {
65
+ opacity: 1;
66
+ pointer-events: auto;
67
+ }
68
+ .block-game-over__card {
69
+ display: grid;
70
+ justify-items: center;
71
+ gap: 0.35em;
72
+ min-width: min(78vw, 20rem);
73
+ padding: 2rem 2.4rem 2.2rem;
74
+ background: var(--block-card);
75
+ border: 2px solid rgba(255, 255, 255, 0.12);
76
+ border-radius: 1.6rem;
77
+ box-shadow: 0 1.2rem 3rem rgba(0, 0, 0, 0.45);
78
+ text-align: center;
79
+ }
80
+ .block-game-over[data-open] .block-game-over__card {
81
+ animation: block-card-in 320ms cubic-bezier(0.34, 1.56, 0.5, 1);
82
+ }
83
+ @keyframes block-card-in {
84
+ from {
85
+ transform: translateY(1.2rem) scale(0.92);
86
+ opacity: 0;
87
+ }
88
+ }
89
+ .block-game-over__title {
90
+ font-size: clamp(1.6rem, 7vmin, 2.4rem);
91
+ font-weight: 700;
92
+ letter-spacing: 0.06em;
93
+ text-shadow: 0 3px 0 var(--block-outline);
94
+ }
95
+ .block-game-over__subtitle {
96
+ font-size: 0.9rem;
97
+ font-weight: 600;
98
+ letter-spacing: 0.28em;
99
+ text-transform: uppercase;
100
+ opacity: 0.6;
101
+ }
102
+ .block-game-over[data-new-best] .block-game-over__subtitle {
103
+ color: var(--block-accent);
104
+ opacity: 1;
105
+ animation: block-best-pulse 800ms ease-in-out infinite alternate;
106
+ }
107
+ @keyframes block-best-pulse {
108
+ from {
109
+ opacity: 1;
110
+ }
111
+ to {
112
+ opacity: 0.55;
113
+ }
114
+ }
115
+ .block-game-over__score {
116
+ font-size: clamp(2.6rem, 12vmin, 4rem);
117
+ font-weight: 700;
118
+ font-variant-numeric: tabular-nums;
119
+ line-height: 1.1;
120
+ text-shadow: 0 4px 0 var(--block-outline);
121
+ }
122
+ .block-game-over__best {
123
+ font-size: 0.95rem;
124
+ font-weight: 600;
125
+ opacity: 0.75;
126
+ }
127
+ .block-game-over__retry {
128
+ margin-top: 0.9em;
129
+ font-family: inherit;
130
+ font-size: 1.05rem;
131
+ font-weight: 700;
132
+ letter-spacing: 0.12em;
133
+ text-transform: uppercase;
134
+ color: var(--block-cta-label);
135
+ background: var(--block-cta);
136
+ border: none;
137
+ border-radius: 999px;
138
+ padding: 0.85em 2.6em;
139
+ cursor: pointer;
140
+ /* chunky button: hard extrusion under, push-in on press */
141
+ box-shadow: 0 5px 0 color-mix(in srgb, var(--block-cta) 62%, black);
142
+ transition:
143
+ transform 80ms ease,
144
+ box-shadow 80ms ease;
145
+ }
146
+ .block-game-over__retry:active {
147
+ transform: translateY(4px);
148
+ box-shadow: 0 1px 0 color-mix(in srgb, var(--block-cta) 62%, black);
149
+ }
150
+ @media (prefers-reduced-motion: reduce) {
151
+ .block-game-over[data-open] .block-game-over__card {
152
+ animation: none;
153
+ }
154
+ .block-game-over[data-new-best] .block-game-over__subtitle {
155
+ animation: none;
156
+ }
157
+ .block-game-over {
158
+ transition: none;
159
+ }
160
+ }
161
+ </style>
162
+
163
+ <div class="block-game-over" data-block-gameover>
164
+ <div class="block-game-over__card">
165
+ <span class="block-game-over__title">Run Over</span>
166
+ <span class="block-game-over__subtitle" data-block-subtitle>Score</span>
167
+ <span class="block-game-over__score" data-block-final>0</span>
168
+ <span class="block-game-over__best">Best <b data-block-best>0</b></span>
169
+ <button class="block-game-over__retry" type="button" data-block-retry>Retry</button>
170
+ </div>
171
+ </div>
172
+ <!-- ===================== COPY TO HERE ======================= -->
173
+
174
+ <!-- preview-only: games toggle data-open themselves; this script exists so
175
+ the source file shows its open state when opened directly -->
176
+ <script type="module">
177
+ const card = document.querySelector("[data-block-gameover]");
178
+ card.toggleAttribute("data-open", true);
179
+ card.querySelector("[data-block-retry]").addEventListener("click", () => {
180
+ card.toggleAttribute("data-open", false);
181
+ setTimeout(() => card.toggleAttribute("data-open", true), 700);
182
+ });
183
+ </script>
184
+ </body>
185
+ </html>
@@ -0,0 +1,30 @@
1
+ {
2
+ "$schema": "../../schema/registry-item.schema.json",
3
+ "name": "game-over-card",
4
+ "type": "frogoe:block",
5
+ "title": "Game Over Card",
6
+ "description": "End-of-run overlay: title, score, best, NEW BEST state, chunky retry button with press feedback; themeable via --block-*",
7
+ "tags": [
8
+ "hud",
9
+ "game-over",
10
+ "results",
11
+ "retry"
12
+ ],
13
+ "files": [
14
+ {
15
+ "path": "game-over-card.html",
16
+ "target": "blocks/game-over-card.html",
17
+ "type": "frogoe:markup"
18
+ }
19
+ ],
20
+ "bindings": [
21
+ "data-block-gameover",
22
+ "data-block-final",
23
+ "data-block-best",
24
+ "data-block-retry"
25
+ ],
26
+ "placement": "overlay-fullscreen",
27
+ "preview": {
28
+ "demo": "demo.html"
29
+ }
30
+ }
@@ -0,0 +1,92 @@
1
+ <!doctype html>
2
+ <!-- frogoe block source: hearts-row — this file IS viewable in a browser (default
3
+ state on a neutral backdrop). Copy ONLY between the markers into your
4
+ game's blocks/ layer; the interactive playground is demo.html. -->
5
+ <html lang="en">
6
+ <head>
7
+ <meta charset="utf-8" />
8
+ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
9
+ <title>frogoe block — hearts-row</title>
10
+ <style>
11
+ body {
12
+ margin: 0;
13
+ min-height: 100vh;
14
+ display: grid;
15
+ place-items: center;
16
+ background: radial-gradient(120% 120% at 50% 0%, #182031 0%, #0d1220 60%, #080b13 100%);
17
+ }
18
+ </style>
19
+ </head>
20
+ <body>
21
+ <!-- ===================== COPY FROM HERE ===================== -->
22
+ <!--
23
+ frogoe block: hearts-row
24
+ Lives / health as a row of hearts. Filled hearts on the left, lost hearts
25
+ ghost on the right; a lost heart pops as it empties.
26
+
27
+ Bind from game.js (vanilla, no helper):
28
+ const row = document.querySelector("[data-block-hearts]");
29
+ row.dataset.value = String(lives); // hearts shown as full
30
+ // data-block-hearts element carries: data-value (full), data-max (slots)
31
+
32
+ Theme: --block-heart (fill), --block-heart-empty (ghost), --block-outline.
33
+ -->
34
+ <style>
35
+ .block-hearts-row {
36
+ --block-gap: 0.5em;
37
+ --block-heart: #ff4d6d;
38
+ --block-heart-empty: rgba(255, 255, 255, 0.18);
39
+ --block-outline: #33101d;
40
+ --block-size: clamp(1.4rem, 5vmin, 2rem);
41
+
42
+ display: flex;
43
+ gap: var(--block-gap);
44
+ pointer-events: none;
45
+ -webkit-user-select: none;
46
+ user-select: none;
47
+ }
48
+ .block-hearts-row__heart {
49
+ width: var(--block-size);
50
+ height: var(--block-size);
51
+ background: var(--block-heart);
52
+ /* heart silhouette — no icon font, no image */
53
+ -webkit-mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z'/></svg>")
54
+ center / contain no-repeat;
55
+ mask: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z'/></svg>")
56
+ center / contain no-repeat;
57
+ filter: drop-shadow(0 2px 0 var(--block-outline)) drop-shadow(0 4px 6px rgba(0, 0, 0, 0.3));
58
+ transition:
59
+ opacity 160ms ease,
60
+ transform 160ms ease;
61
+ }
62
+ .block-hearts-row__heart[aria-hidden="true"] {
63
+ background: var(--block-heart-empty);
64
+ filter: drop-shadow(0 2px 0 rgba(0, 0, 0, 0.25));
65
+ transform: scale(0.86);
66
+ animation: block-heart-pop 320ms cubic-bezier(0.34, 1.8, 0.5, 1);
67
+ }
68
+ @keyframes block-heart-pop {
69
+ from {
70
+ transform: scale(1.35) rotate(-8deg);
71
+ }
72
+ }
73
+ @media (prefers-reduced-motion: reduce) {
74
+ .block-hearts-row__heart,
75
+ .block-hearts-row__heart[aria-hidden="true"] {
76
+ animation: none;
77
+ transition: none;
78
+ }
79
+ }
80
+ </style>
81
+
82
+ <div class="block-hearts-row" data-block-hearts data-value="3" data-max="3">
83
+ <span class="block-hearts-row__heart"></span>
84
+ <span class="block-hearts-row__heart"></span>
85
+ <span class="block-hearts-row__heart"></span>
86
+ <!-- hearts are managed by the game script: set data-value, then update
87
+ each child's aria-hidden (full: remove; lost: set). Demo shows the
88
+ six-line binder. More slots? Add more <span> children. -->
89
+ </div>
90
+ <!-- ===================== COPY TO HERE ======================= -->
91
+ </body>
92
+ </html>
@@ -0,0 +1,27 @@
1
+ {
2
+ "$schema": "../../schema/registry-item.schema.json",
3
+ "name": "hearts-row",
4
+ "type": "frogoe:block",
5
+ "title": "Hearts Row",
6
+ "description": "Lives as a row of hearts (clip-path silhouettes); lost hearts ghost with a pop, reduced-motion safe; themeable via --block-*",
7
+ "tags": [
8
+ "hud",
9
+ "lives",
10
+ "health",
11
+ "hearts"
12
+ ],
13
+ "files": [
14
+ {
15
+ "path": "hearts-row.html",
16
+ "target": "blocks/hearts-row.html",
17
+ "type": "frogoe:markup"
18
+ }
19
+ ],
20
+ "bindings": [
21
+ "data-block-hearts"
22
+ ],
23
+ "placement": "overlay",
24
+ "preview": {
25
+ "demo": "demo.html"
26
+ }
27
+ }
@@ -0,0 +1,125 @@
1
+ <!doctype html>
2
+ <!-- frogoe block source: mini-board — this file IS viewable in a browser (default
3
+ state on a neutral backdrop). Copy ONLY between the markers into your
4
+ game's blocks/ layer; the interactive playground is demo.html. -->
5
+ <html lang="en">
6
+ <head>
7
+ <meta charset="utf-8" />
8
+ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
9
+ <title>frogoe block — mini-board</title>
10
+ <style>
11
+ body {
12
+ margin: 0;
13
+ min-height: 100vh;
14
+ display: grid;
15
+ place-items: center;
16
+ background: radial-gradient(120% 120% at 50% 0%, #182031 0%, #0d1220 60%, #080b13 100%);
17
+ }
18
+ </style>
19
+ </head>
20
+ <body>
21
+ <!-- ===================== COPY FROM HERE ===================== -->
22
+ <!--
23
+ frogoe block: mini-board
24
+ Top-3 scores in a compact board — the "one more run" social proof. Seeds
25
+ are ORGANIC numbers by doctrine: never round slop (1234567), never
26
+ fake-perfect (99.99%). The player's row is marked; new bests glow once.
27
+
28
+ Bind from game.js (vanilla, no helper):
29
+ const board = document.querySelector("[data-block-board]");
30
+ board.querySelector("[data-block-board-r1-score]").textContent = "1,283";
31
+ // rows r1..r3: name + score; [data-block-board-you] marks the player row.
32
+ board.toggleAttribute("data-hidden", false);
33
+
34
+ Theme: --block-accent, --block-fg, --block-outline, --block-chip.
35
+ -->
36
+ <style>
37
+ .block-board {
38
+ --block-accent: #ffd166;
39
+ --block-chip: rgba(12, 16, 30, 0.55);
40
+ --block-fg: #fffdf7;
41
+ --block-font: "Fredoka", "Baloo 2", system-ui, sans-serif;
42
+ --block-outline: #26180a;
43
+
44
+ display: grid;
45
+ gap: 2px; /* razor rows: contrast gap, not borders */
46
+ min-width: 11rem;
47
+ padding: 0.7em 0.9em;
48
+ background: var(--block-chip);
49
+ border: 2px solid rgba(255, 255, 255, 0.14);
50
+ border-radius: 1rem;
51
+ color: var(--block-fg);
52
+ font-family: var(--block-font);
53
+ pointer-events: none;
54
+ -webkit-user-select: none;
55
+ user-select: none;
56
+ }
57
+ .block-board__row {
58
+ display: grid;
59
+ grid-template-columns: auto 1fr auto;
60
+ align-items: baseline;
61
+ gap: 0.6em;
62
+ padding: 0.25em 0.3em;
63
+ font-size: 0.95rem;
64
+ }
65
+ .block-board__row + .block-board__row {
66
+ background: rgba(255, 255, 255, 0.05);
67
+ }
68
+ .block-board__rank {
69
+ font-weight: 700;
70
+ opacity: 0.6;
71
+ font-variant-numeric: tabular-nums;
72
+ }
73
+ .block-board__name {
74
+ font-weight: 600;
75
+ overflow: hidden;
76
+ text-overflow: ellipsis;
77
+ white-space: nowrap;
78
+ }
79
+ .block-board__score {
80
+ font-weight: 700;
81
+ font-variant-numeric: tabular-nums;
82
+ text-shadow: 0 1px 0 var(--block-outline);
83
+ }
84
+ .block-board__row[data-block-board-you] {
85
+ background: color-mix(in srgb, var(--block-accent) 16%, transparent);
86
+ }
87
+ .block-board__row[data-block-board-you] .block-board__name {
88
+ color: var(--block-accent);
89
+ }
90
+ .block-board[data-fresh-best] .block-board__row[data-block-board-you] .block-board__score {
91
+ animation: block-board-best 700ms cubic-bezier(0.34, 1.8, 0.5, 1);
92
+ }
93
+ @keyframes block-board-best {
94
+ from {
95
+ transform: scale(1.35);
96
+ color: var(--block-accent);
97
+ }
98
+ }
99
+ @media (prefers-reduced-motion: reduce) {
100
+ .block-board[data-fresh-best] .block-board__row[data-block-board-you] .block-board__score {
101
+ animation: none;
102
+ }
103
+ }
104
+ </style>
105
+
106
+ <div class="block-board" data-block-board>
107
+ <div class="block-board__row">
108
+ <span class="block-board__rank">1</span>
109
+ <span class="block-board__name" data-block-board-r1-name>muja</span>
110
+ <span class="block-board__score" data-block-board-r1-score>1,283</span>
111
+ </div>
112
+ <div class="block-board__row">
113
+ <span class="block-board__rank">2</span>
114
+ <span class="block-board__name" data-block-board-r2-name>kiri</span>
115
+ <span class="block-board__score" data-block-board-r2-score>947</span>
116
+ </div>
117
+ <div class="block-board__row" data-block-board-you>
118
+ <span class="block-board__rank">3</span>
119
+ <span class="block-board__name" data-block-board-r3-name>you</span>
120
+ <span class="block-board__score" data-block-board-r3-score>512</span>
121
+ </div>
122
+ </div>
123
+ <!-- ===================== COPY TO HERE ======================= -->
124
+ </body>
125
+ </html>
@@ -0,0 +1,28 @@
1
+ {
2
+ "$schema": "../../schema/registry-item.schema.json",
3
+ "name": "mini-board",
4
+ "type": "frogoe:block",
5
+ "title": "Mini Board",
6
+ "description": "Top-3 leaderboard chip with organic seed numbers, player-row highlight, and a fresh-best pop; themeable via --block-*",
7
+ "tags": [
8
+ "hud",
9
+ "leaderboard",
10
+ "scores",
11
+ "social-proof"
12
+ ],
13
+ "files": [
14
+ {
15
+ "path": "mini-board.html",
16
+ "target": "blocks/mini-board.html",
17
+ "type": "frogoe:markup"
18
+ }
19
+ ],
20
+ "bindings": [
21
+ "data-block-board",
22
+ "data-block-board-you"
23
+ ],
24
+ "placement": "overlay",
25
+ "preview": {
26
+ "demo": "demo.html"
27
+ }
28
+ }
@@ -0,0 +1,101 @@
1
+ <!doctype html>
2
+ <!-- frogoe block source: objective-chip — this file IS viewable in a browser (default
3
+ state on a neutral backdrop). Copy ONLY between the markers into your
4
+ game's blocks/ layer; the interactive playground is demo.html. -->
5
+ <html lang="en">
6
+ <head>
7
+ <meta charset="utf-8" />
8
+ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
9
+ <title>frogoe block — objective-chip</title>
10
+ <style>
11
+ body {
12
+ margin: 0;
13
+ min-height: 100vh;
14
+ display: grid;
15
+ place-items: center;
16
+ background: radial-gradient(120% 120% at 50% 0%, #182031 0%, #0d1220 60%, #080b13 100%);
17
+ }
18
+ </style>
19
+ </head>
20
+ <body>
21
+ <!-- ===================== COPY FROM HERE ===================== -->
22
+ <!--
23
+ frogoe block: objective-chip
24
+ Compact corner chip for one resource that must be WATCHED: moves left,
25
+ balls left, retries left. Big tabular number + label; warning state at
26
+ data-low. For anything less critical, use coin-counter.
27
+
28
+ Bind from game.js (vanilla, no helper):
29
+ const chip = document.querySelector("[data-block-objective]");
30
+ chip.querySelector("[data-block-objective-value]").textContent = String(moves);
31
+ chip.toggleAttribute("data-low", moves <= 5);
32
+
33
+ Theme: --block-accent, --block-danger, --block-fg, --block-outline, --block-chip (bg).
34
+ -->
35
+ <style>
36
+ .block-objective {
37
+ --block-accent: #ffd166;
38
+ --block-chip: rgba(12, 16, 30, 0.55);
39
+ --block-danger: #ff5d5d;
40
+ --block-fg: #fffdf7;
41
+ --block-font: "Fredoka", "Baloo 2", system-ui, sans-serif;
42
+ --block-outline: #26180a;
43
+ --block-value-color: var(--block-fg);
44
+
45
+ display: inline-flex;
46
+ align-items: baseline;
47
+ gap: 0.5em;
48
+ padding: 0.5em 1em;
49
+ background: var(--block-chip);
50
+ border: 2px solid rgba(255, 255, 255, 0.14);
51
+ border-radius: 999px;
52
+ color: var(--block-fg);
53
+ font-family: var(--block-font);
54
+ pointer-events: none;
55
+ -webkit-user-select: none;
56
+ user-select: none;
57
+ }
58
+ .block-objective__value {
59
+ font-size: clamp(1.4rem, 6vmin, 2rem);
60
+ font-weight: 700;
61
+ font-variant-numeric: tabular-nums;
62
+ line-height: 1;
63
+ color: var(--block-value-color);
64
+ text-shadow: 0 2px 0 var(--block-outline);
65
+ }
66
+ .block-objective__label {
67
+ font-size: 0.78rem;
68
+ font-weight: 700;
69
+ letter-spacing: 0.22em;
70
+ text-transform: uppercase;
71
+ opacity: 0.8;
72
+ }
73
+ .block-objective[data-low] {
74
+ --block-value-color: var(--block-danger);
75
+ border-color: color-mix(in srgb, var(--block-danger) 55%, transparent);
76
+ }
77
+ .block-objective[data-low] .block-objective__value {
78
+ animation: block-objective-low 640ms ease-in-out infinite alternate;
79
+ }
80
+ @keyframes block-objective-low {
81
+ from {
82
+ opacity: 1;
83
+ }
84
+ to {
85
+ opacity: 0.6;
86
+ }
87
+ }
88
+ @media (prefers-reduced-motion: reduce) {
89
+ .block-objective[data-low] .block-objective__value {
90
+ animation: none;
91
+ }
92
+ }
93
+ </style>
94
+
95
+ <div class="block-objective" data-block-objective>
96
+ <span class="block-objective__value" data-block-objective-value>25</span>
97
+ <span class="block-objective__label">moves</span>
98
+ </div>
99
+ <!-- ===================== COPY TO HERE ======================= -->
100
+ </body>
101
+ </html>
@@ -0,0 +1,29 @@
1
+ {
2
+ "$schema": "../../schema/registry-item.schema.json",
3
+ "name": "objective-chip",
4
+ "type": "frogoe:block",
5
+ "title": "Objective Chip",
6
+ "description": "Corner chip for one watched resource (moves/balls/retries): big tabular number, label, data-low warning state; themeable via --block-*",
7
+ "tags": [
8
+ "hud",
9
+ "objective",
10
+ "moves",
11
+ "resource",
12
+ "chip"
13
+ ],
14
+ "files": [
15
+ {
16
+ "path": "objective-chip.html",
17
+ "target": "blocks/objective-chip.html",
18
+ "type": "frogoe:markup"
19
+ }
20
+ ],
21
+ "bindings": [
22
+ "data-block-objective",
23
+ "data-block-objective-value"
24
+ ],
25
+ "placement": "overlay",
26
+ "preview": {
27
+ "demo": "demo.html"
28
+ }
29
+ }