studio-engine 0.129.0 → 0.130.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +41 -0
- data/README.md +5 -0
- data/app/assets/tailwind/studio_engine/engine-motion.css +148 -56
- data/app/assets/tailwind/studio_engine/engine.css +150 -19
- data/app/helpers/studio/fizz_helper.rb +5 -0
- data/app/javascript/studio/controllers/style_section_nav_controller.js +4 -4
- data/app/javascript/studio/pinned_stack.js +112 -4
- data/app/views/studio/_fizz.html.erb +26 -0
- data/app/views/studio/_fizz_layer.html.erb +4 -1
- data/app/views/studio/_hold_button.html.erb +1 -2
- data/app/views/studio/_page_header.html.erb +3 -2
- data/app/views/studio/_section_header.html.erb +23 -0
- data/app/views/style/_components.html.erb +3 -5
- data/app/views/style/_host.html.erb +3 -6
- data/app/views/style/_modal_specimen.html.erb +5 -5
- data/app/views/style/_modals.html.erb +3 -5
- data/app/views/style/_site_footer.html.erb +1 -1
- data/app/views/style/_specimen.html.erb +4 -0
- data/app/views/style/_tasks.html.erb +3 -5
- data/app/views/style/_theme.html.erb +2 -2
- data/app/views/style/_tricks.html.erb +155 -216
- data/app/views/style/index.html.erb +7 -3
- data/lib/studio/version.rb +1 -1
- metadata +3 -1
|
@@ -1,20 +1,10 @@
|
|
|
1
|
-
<%# Tricks section of the living style guide (admin/style).
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
specimens for real.
|
|
9
|
-
|
|
10
|
-
The Leveling group renders the REAL .level-badge tiers ported into the engine
|
|
11
|
-
from Turf Monster, capability-gated by Studio.feature?(:leveling). When a host
|
|
12
|
-
has leveling OFF (McRitchie Studio's default), its specimens render "disabled
|
|
13
|
-
but present" via the _specimen `disabled:` local - visible and previewable,
|
|
14
|
-
but greyed and flagged, never hidden.
|
|
15
|
-
|
|
16
|
-
The color tokens/swatches now live in the Theme section (the landing section
|
|
17
|
-
owns "the colors"); this section is the trick board. %>
|
|
1
|
+
<%# Tricks section of the living style guide (admin/style). Buttons come first,
|
|
2
|
+
then what you can add to a button, then the other primitives. Each specimen
|
|
3
|
+
(the shared _specimen partial) shows the real engine class live, with its name
|
|
4
|
+
and a usage snippet to copy.
|
|
5
|
+
|
|
6
|
+
The Leveling group is gated by Studio.feature?(:leveling). With it off, its
|
|
7
|
+
specimens render "disabled but present": visible, greyed and flagged, never hidden. %>
|
|
18
8
|
<%
|
|
19
9
|
# Keyed by the WHOLE class, never "btn-" + a name: Tailwind compiles only the
|
|
20
10
|
# @utility names it can read in source.
|
|
@@ -30,26 +20,22 @@
|
|
|
30
20
|
}
|
|
31
21
|
|
|
32
22
|
leveling_on = Studio.feature?(:leveling)
|
|
23
|
+
|
|
24
|
+
# An ERB snippet cannot be written literally inside an ERB template: the scanner
|
|
25
|
+
# closes the tag at the first `%` + `>` it sees, whatever Ruby string it sits in.
|
|
26
|
+
# So assemble the markers: the raw template never contains one.
|
|
27
|
+
erb = ->(code) { "<" + "%= " + code + " %" + ">" }
|
|
28
|
+
erb_end = "<" + "% end %" + ">"
|
|
33
29
|
%>
|
|
34
|
-
<section id="tricks" class="space-y-12"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
<p class="text-muted text-sm max-w-2xl">
|
|
38
|
-
A board of style tricks you can see and hand to an agent to use out of the box.
|
|
39
|
-
Each specimen surfaces the rendered primitive, its <strong>class name (one-click
|
|
40
|
-
copy)</strong>, and a copyable usage snippet - the button, surface, scrim, motion,
|
|
41
|
-
effect, and leveling primitives. The color tokens live in <a href="#theme" class="underline">Theme</a>.
|
|
42
|
-
</p>
|
|
43
|
-
</div>
|
|
30
|
+
<section id="tricks" class="section-target space-y-12">
|
|
31
|
+
<%= render "studio/section_header", title: "Tricks" %>
|
|
32
|
+
<p class="text-muted text-sm max-w-2xl">Buttons first, then what you can add to them.</p>
|
|
44
33
|
|
|
45
34
|
<!-- Buttons -->
|
|
46
35
|
<section class="space-y-5">
|
|
47
36
|
<div class="space-y-1">
|
|
48
37
|
<h3 class="type-h4 text-heading">Buttons</h3>
|
|
49
|
-
<p class="text-muted text-sm">
|
|
50
|
-
<code class="font-mono text-2xs">.btn</code> base plus one role variant, and the
|
|
51
|
-
<code class="font-mono text-2xs">.btn-sm</code> / <code class="font-mono text-2xs">.btn-lg</code> sizes.
|
|
52
|
-
</p>
|
|
38
|
+
<p class="text-muted text-sm"><code class="font-mono text-2xs">.btn</code> plus one role and one size.</p>
|
|
53
39
|
</div>
|
|
54
40
|
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
55
41
|
<% button_variants.each do |name, label| %>
|
|
@@ -72,16 +58,117 @@
|
|
|
72
58
|
</div>
|
|
73
59
|
</section>
|
|
74
60
|
|
|
61
|
+
<!-- Button tricks -->
|
|
62
|
+
<section id="button-tricks" class="section-subtarget space-y-5">
|
|
63
|
+
<div class="space-y-1">
|
|
64
|
+
<h3 class="type-h4 text-heading">Button tricks</h3>
|
|
65
|
+
<p class="text-muted text-sm">Add any of these to a <code class="font-mono text-2xs">.btn</code>.</p>
|
|
66
|
+
</div>
|
|
67
|
+
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
68
|
+
<%# The CARD shape: the button carries its own background and the ring still
|
|
69
|
+
rings, because the primitive punches the host's box out of both wedge
|
|
70
|
+
layers. The WRAPPER shape (bare host + opaque child) is staged by the
|
|
71
|
+
Modals section's glow cards. engine-motion.css section 9b has both. %>
|
|
72
|
+
<%= render layout: "style/specimen",
|
|
73
|
+
locals: { klass: ".edge-rotate-glow (knobs: --edge-rotate-glow-color / -color-b)",
|
|
74
|
+
note: "Adds a glow that rotates around the button's edge.",
|
|
75
|
+
usage: %(<button class="btn btn-primary edge-rotate-glow">Save</button>\n<button class="btn btn-primary edge-rotate-glow"\n style="--edge-rotate-glow-color: #F08030; --edge-rotate-glow-color-b: #6890F0">Two colors</button>) } do %>
|
|
76
|
+
<button type="button" class="btn btn-primary edge-rotate-glow">Save</button>
|
|
77
|
+
<button type="button" class="btn btn-primary edge-rotate-glow"
|
|
78
|
+
style="--edge-rotate-glow-color: #F08030; --edge-rotate-glow-color-b: #6890F0">Two colors</button>
|
|
79
|
+
<% end %>
|
|
80
|
+
|
|
81
|
+
<%= render layout: "style/specimen",
|
|
82
|
+
locals: { klass: ".studio-border-glow",
|
|
83
|
+
note: "Adds a moving rainbow border with a soft halo.",
|
|
84
|
+
usage: %(<button class="btn btn-primary studio-border-glow">Save</button>) } do %>
|
|
85
|
+
<button type="button" class="btn btn-primary studio-border-glow">Save</button>
|
|
86
|
+
<% end %>
|
|
87
|
+
|
|
88
|
+
<%= render layout: "style/specimen",
|
|
89
|
+
locals: { klass: ".sheen .sheen--loop",
|
|
90
|
+
note: "Adds a band of light that sweeps across the button.",
|
|
91
|
+
usage: %(<button class="btn btn-primary sheen">Once</button>\n<button class="btn btn-primary sheen sheen--loop">Loop</button>) } do %>
|
|
92
|
+
<button type="button" class="btn btn-primary sheen">Once</button>
|
|
93
|
+
<button type="button" class="btn btn-primary sheen sheen--loop">Loop</button>
|
|
94
|
+
<% end %>
|
|
95
|
+
|
|
96
|
+
<%= render layout: "style/specimen",
|
|
97
|
+
locals: { klass: ".pulse-cta (knobs: --pulse-cta-color / -speed / -scale)",
|
|
98
|
+
note: "Adds a slow beat with a ring that spreads from the edge.",
|
|
99
|
+
usage: %(<button class="btn btn-primary pulse-cta">Save</button>\n<button class="btn btn-success pulse-cta" style="--pulse-cta-color: var(--color-success)">Go</button>) } do %>
|
|
100
|
+
<button type="button" class="btn btn-primary pulse-cta">Save</button>
|
|
101
|
+
<button type="button" class="btn btn-success pulse-cta" style="--pulse-cta-color: var(--color-success)">Go</button>
|
|
102
|
+
<% end %>
|
|
103
|
+
|
|
104
|
+
<%= render layout: "style/specimen",
|
|
105
|
+
locals: { klass: ".fizz-stack (studio/fizz)",
|
|
106
|
+
note: "Adds bubbles that rise from behind the button.",
|
|
107
|
+
usage: [erb.call(%(render layout: "studio/fizz", locals: { seed: "save" } do)),
|
|
108
|
+
%( <button class="btn btn-primary">Save</button>),
|
|
109
|
+
erb_end].join("\n") } do %>
|
|
110
|
+
<%= render layout: "studio/fizz", locals: { seed: "style-fizz" } do %>
|
|
111
|
+
<button type="button" class="btn btn-primary">Save</button>
|
|
112
|
+
<% end %>
|
|
113
|
+
<%= render layout: "studio/fizz", locals: { seed: "style-fizz-colors", colors: %w[#8b5cf6 #8b5cf6 #8b5cf6 #22d3ee #22d3ee #22d3ee #f59e0b #f59e0b #f59e0b #ef4444 #ef4444 #ef4444 #10b981 #10b981 #10b981 #ec4899 #ec4899 #ec4899] } do %>
|
|
114
|
+
<button type="button" class="btn btn-neutral">Six colors</button>
|
|
115
|
+
<% end %>
|
|
116
|
+
<% end %>
|
|
117
|
+
|
|
118
|
+
<%# The live button: press and hold for two seconds. The card clips, so this one
|
|
119
|
+
sets fizz_portal and its bubbles rise past the card's edge. %>
|
|
120
|
+
<%= render layout: "style/specimen",
|
|
121
|
+
locals: { klass: ".hold-btn (studio/hold_button)",
|
|
122
|
+
note: "Makes the button confirm only after a two-second hold.",
|
|
123
|
+
usage: %(render "studio/hold_button", hold_id: "confirm", duration: 2000, fizz_portal: true) } do %>
|
|
124
|
+
<div class="w-full max-w-xs" x-data="{ submitting: false }">
|
|
125
|
+
<%= render "studio/hold_button", hold_id: "style-hold", duration: 2000, fizz_portal: true,
|
|
126
|
+
default_text: "Hold to Confirm", hold_text: "Almost there…", success_text: "Confirmed!" %>
|
|
127
|
+
<button type="button" class="btn btn-neutral btn-sm mt-3 mx-auto block"
|
|
128
|
+
@click="document.querySelector('.hold-btn[data-hold-id=style-hold]').classList.remove('process','success','error','loading')">Reset</button>
|
|
129
|
+
</div>
|
|
130
|
+
<% end %>
|
|
131
|
+
|
|
132
|
+
<%# The same button, re-themed from an ancestor: no new CSS. %>
|
|
133
|
+
<%= render layout: "style/specimen",
|
|
134
|
+
locals: { klass: ".hold-btn (--hold-* overrides)",
|
|
135
|
+
note: "Recolors a hold button from any parent element.",
|
|
136
|
+
usage: %(<div style="--hold-bg-from:#f0abfc;--hold-success-to:#4c1d95">…</div>) } do %>
|
|
137
|
+
<div class="w-full max-w-xs" x-data="{ submitting: false }"
|
|
138
|
+
style="--hold-bg-from:#f0abfc;--hold-bg-mid:#a855f7;--hold-bg-to:#6b21a8;
|
|
139
|
+
--hold-success-from:#c4b5fd;--hold-success-to:#4c1d95;--hold-success-glow-rgb:139 92 246">
|
|
140
|
+
<%= render "studio/hold_button", hold_id: "style-hold-custom", duration: 2000,
|
|
141
|
+
default_text: "Hold to Confirm", success_text: "Confirmed!",
|
|
142
|
+
fizz_colors: %w[#f0abfc #6b21a8 #a855f7 #a5b4fc #312e81 #6366f1 #fbcfe8 #831843 #ec4899
|
|
143
|
+
#bae6fd #0c4a6e #0ea5e9 #ddd6fe #4c1d95 #8b5cf6 #fde68a #78350f #f59e0b] %>
|
|
144
|
+
<button type="button" class="btn btn-neutral btn-sm mt-3 mx-auto block"
|
|
145
|
+
@click="document.querySelector('.hold-btn[data-hold-id=style-hold-custom]').classList.remove('process','success','error','loading')">Reset</button>
|
|
146
|
+
</div>
|
|
147
|
+
<% end %>
|
|
148
|
+
|
|
149
|
+
<%= render layout: "style/specimen",
|
|
150
|
+
locals: { klass: "window.studioConfetti.burst(el)",
|
|
151
|
+
note: "Fires confetti out from behind the button.",
|
|
152
|
+
usage: %(<button class="btn btn-primary" x-data @click="window.studioConfetti.burst($el)">Save</button>) } do %>
|
|
153
|
+
<button type="button" class="btn btn-primary" x-data
|
|
154
|
+
@click="window.studioConfetti && window.studioConfetti.burst($el)">Burst</button>
|
|
155
|
+
<% end %>
|
|
156
|
+
|
|
157
|
+
<%= render layout: "style/specimen",
|
|
158
|
+
locals: { klass: "window.studioConfetti.cannons()",
|
|
159
|
+
note: "Fires confetti in from both sides of the screen.",
|
|
160
|
+
usage: %(<button class="btn btn-success" x-data @click="window.studioConfetti.cannons()">Done</button>) } do %>
|
|
161
|
+
<button type="button" class="btn btn-success" x-data
|
|
162
|
+
@click="window.studioConfetti && window.studioConfetti.cannons()">Cannons</button>
|
|
163
|
+
<% end %>
|
|
164
|
+
</div>
|
|
165
|
+
</section>
|
|
166
|
+
|
|
75
167
|
<!-- Page header -->
|
|
76
168
|
<section class="space-y-5" data-page-header-specimen>
|
|
77
169
|
<div class="space-y-1">
|
|
78
170
|
<h3 class="type-h4 text-heading">Page header</h3>
|
|
79
|
-
<p class="text-muted text-sm">
|
|
80
|
-
<code class="font-mono text-2xs">studio/_page_header</code>: the title, optional meta beside
|
|
81
|
-
it and optional tabs on the right. This page's own title row is one: scroll to see it take the bar.
|
|
82
|
-
It joins the pinned stack, so a table's sticky header sits under it: navbar, page row, table
|
|
83
|
-
header, each below the last. The rule is in <code class="font-mono text-2xs">docs/PAGE_HEADER.md</code>.
|
|
84
|
-
</p>
|
|
171
|
+
<p class="text-muted text-sm"><code class="font-mono text-2xs">studio/_page_header</code> is the title row, with optional meta and tabs.</p>
|
|
85
172
|
</div>
|
|
86
173
|
<div class="grid gap-4 lg:grid-cols-2">
|
|
87
174
|
<%# The classes alone, not the partial: the partial's row is sticky, joins the
|
|
@@ -107,11 +194,33 @@
|
|
|
107
194
|
</div>
|
|
108
195
|
</section>
|
|
109
196
|
|
|
197
|
+
<!-- Section header -->
|
|
198
|
+
<section class="space-y-5" data-section-header-specimen>
|
|
199
|
+
<div class="space-y-1">
|
|
200
|
+
<h3 class="type-h4 text-heading">Section header</h3>
|
|
201
|
+
<p class="text-muted text-sm">
|
|
202
|
+
<code class="font-mono text-2xs">studio/_section_header</code>: a section's title that sticks under
|
|
203
|
+
the page row until the next section's takes its place. This page's own section titles are these.
|
|
204
|
+
The rule is in <code class="font-mono text-2xs">docs/PAGE_HEADER.md</code>.
|
|
205
|
+
</p>
|
|
206
|
+
</div>
|
|
207
|
+
<div class="grid gap-4 lg:grid-cols-2">
|
|
208
|
+
<%# The classes alone, not the partial: the partial's header sticks and joins the
|
|
209
|
+
pinned stack, and a specimen must do neither. %>
|
|
210
|
+
<% [["At rest", {}], ["Stuck", { stuck: "" }]].each do |state, data| %>
|
|
211
|
+
<%= render layout: "style/specimen",
|
|
212
|
+
locals: { klass: "studio/_section_header (#{state.downcase})",
|
|
213
|
+
usage: %(<section id="modals" class="section-target">\n <%= render "studio/section_header", title: "Modals" %\>\n ...\n</section>) } do %>
|
|
214
|
+
<%= tag.p("Modals", class: "section-header px-4", data: data) %>
|
|
215
|
+
<% end %>
|
|
216
|
+
<% end %>
|
|
217
|
+
</div>
|
|
218
|
+
</section>
|
|
219
|
+
|
|
110
220
|
<!-- Surfaces, text and form -->
|
|
111
221
|
<section class="space-y-5">
|
|
112
222
|
<div class="space-y-1">
|
|
113
223
|
<h3 class="type-h4 text-heading">Surfaces, text & form</h3>
|
|
114
|
-
<p class="text-muted text-sm">Cards, badges, inputs, the empty state, and the uppercase meta label.</p>
|
|
115
224
|
</div>
|
|
116
225
|
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
117
226
|
<%= render layout: "style/specimen",
|
|
@@ -160,17 +269,8 @@
|
|
|
160
269
|
<section class="space-y-5">
|
|
161
270
|
<div class="space-y-1">
|
|
162
271
|
<h3 class="type-h4 text-heading">Time stamps — the “at” format</h3>
|
|
163
|
-
<p class="text-muted text-sm">
|
|
164
|
-
<code class="font-mono text-2xs">at_time_tag</code> stamps WHEN something happened, on
|
|
165
|
-
the reader’s own clock. The stamps below are live: the server rendered them in this
|
|
166
|
-
app’s timezone and <code class="font-mono text-2xs">studio/at_time_script</code>
|
|
167
|
-
re-stamped them in yours. Hover one for the relative time and your zone.
|
|
168
|
-
</p>
|
|
272
|
+
<p class="text-muted text-sm"><code class="font-mono text-2xs">at_time_tag</code> stamps when something happened, on the reader’s own clock.</p>
|
|
169
273
|
</div>
|
|
170
|
-
<%# An ERB snippet cannot be written literally inside an ERB template: the
|
|
171
|
-
scanner closes the tag at the first `%` + `>` it sees, whatever Ruby string
|
|
172
|
-
it sits in. So assemble the markers — the raw template never contains one. %>
|
|
173
|
-
<% erb = ->(code) { "<" + "%= " + code + " %" + ">" } %>
|
|
174
274
|
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
175
275
|
<%= render layout: "style/specimen",
|
|
176
276
|
locals: { klass: "at_time_tag(time)",
|
|
@@ -225,26 +325,19 @@
|
|
|
225
325
|
<!-- Branded loader -->
|
|
226
326
|
<%
|
|
227
327
|
loader_config = Studio.loader
|
|
228
|
-
# An ERB tag as text, built so this file never holds a closing tag inside one.
|
|
229
|
-
erb_tag = ->(code) { "<" + "%= #{code} %" + ">" }
|
|
230
328
|
loader_art_styles = Studio::Loader::ART_STYLES.keys.select do |style|
|
|
231
329
|
loader_config[:art] && Studio::Loader.art(loader_config[:art], style)
|
|
232
330
|
end
|
|
233
331
|
%>
|
|
234
|
-
<section id="loader" class="space-y-5"
|
|
332
|
+
<section id="loader" class="section-subtarget space-y-5">
|
|
235
333
|
<div class="space-y-1">
|
|
236
334
|
<h3 class="type-h4 text-heading">Branded loader</h3>
|
|
237
|
-
<p class="text-muted text-sm max-w-2xl">
|
|
238
|
-
This app's own icon in its theme colours, in the style <code class="font-mono text-2xs">config.loader</code>
|
|
239
|
-
names (<strong><%= loader_config[:style].to_s.tr("_", " ") %></strong>). Page loads, long waits and pending
|
|
240
|
-
buttons all draw it. One class, <code class="font-mono text-2xs">.studio-loader</code>, sized by
|
|
241
|
-
<code class="font-mono text-2xs">--studio-loader-size</code>. studio-engine docs/LOADER.md.
|
|
242
|
-
</p>
|
|
335
|
+
<p class="text-muted text-sm max-w-2xl">This app's icon in its theme colors, in the <strong><%= loader_config[:style].to_s.tr("_", " ") %></strong> style.</p>
|
|
243
336
|
</div>
|
|
244
337
|
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
245
338
|
<%= render layout: "style/specimen",
|
|
246
339
|
locals: { klass: ".studio-loader (this app's style)",
|
|
247
|
-
usage: [
|
|
340
|
+
usage: [erb.call("studio_loader"), erb.call('studio_loader size: 20, label: "Saving"')].join("\n") } do %>
|
|
248
341
|
<%= studio_loader %>
|
|
249
342
|
<%= studio_loader size: 32 %>
|
|
250
343
|
<%= studio_loader size: 20 %>
|
|
@@ -252,7 +345,7 @@
|
|
|
252
345
|
|
|
253
346
|
<%= render layout: "style/specimen",
|
|
254
347
|
locals: { klass: "studio_loader_pending (inside a busy button)",
|
|
255
|
-
usage: ["<button class=\"btn btn-primary\">", " <span data-loader-idle>Save</span>", " #{
|
|
348
|
+
usage: ["<button class=\"btn btn-primary\">", " <span data-loader-idle>Save</span>", " #{erb.call('studio_loader_pending')}", "</button>"].join("\n") } do %>
|
|
256
349
|
<button type="button" class="btn btn-primary" aria-busy="true">
|
|
257
350
|
<%= studio_loader_pending %><span class="ml-2">Saving…</span>
|
|
258
351
|
</button>
|
|
@@ -260,7 +353,7 @@
|
|
|
260
353
|
|
|
261
354
|
<%= render layout: "style/specimen",
|
|
262
355
|
locals: { klass: "studio_loader_overlay (the page-load overlay)",
|
|
263
|
-
usage: ["#{
|
|
356
|
+
usage: ["#{erb.call('studio_loader_overlay')} (once, in the layout body)", 'window.StudioLoader.show("Caption") / .hide()'].join("\n") } do %>
|
|
264
357
|
<button type="button" class="btn btn-secondary" data-test="style-show-loader-overlay"
|
|
265
358
|
x-data x-on:click="window.StudioLoader && window.StudioLoader.show('Loading your page…'); setTimeout(() => window.StudioLoader && window.StudioLoader.hide(), 3000)">
|
|
266
359
|
Show the overlay for 3 seconds
|
|
@@ -285,18 +378,9 @@
|
|
|
285
378
|
<section class="space-y-5">
|
|
286
379
|
<div class="space-y-1">
|
|
287
380
|
<h3 class="type-h4 text-heading">Motion primitives</h3>
|
|
288
|
-
<p class="text-muted text-sm">
|
|
289
|
-
The seven <code class="font-mono text-2xs">engine-motion.css</code> primitives. Each knob is a themeable
|
|
290
|
-
CSS variable - tune it inline with <code class="font-mono text-2xs">style="--knob: value"</code>.
|
|
291
|
-
</p>
|
|
381
|
+
<p class="text-muted text-sm">Tune any knob inline with <code class="font-mono text-2xs">style="--knob: value"</code>.</p>
|
|
292
382
|
</div>
|
|
293
383
|
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
294
|
-
<%= render layout: "style/specimen",
|
|
295
|
-
locals: { klass: ".studio-border-glow (rounded-xl wrapper)",
|
|
296
|
-
usage: %(<div class="studio-border-glow rounded-xl">...</div>) } do %>
|
|
297
|
-
<div class="studio-border-glow rounded-xl p-5 bg-surface text-center text-sm text-heading">Border glow</div>
|
|
298
|
-
<% end %>
|
|
299
|
-
|
|
300
384
|
<%= render layout: "style/specimen",
|
|
301
385
|
locals: { klass: ".spinner (knob: --spinner-size)",
|
|
302
386
|
usage: %(<span class="spinner"></span>\n<span class="spinner" style="--spinner-size: 2rem"></span>) } do %>
|
|
@@ -316,13 +400,6 @@
|
|
|
316
400
|
<span class="loading-dots" style="--loading-dots-size: 12px; --loading-dots-color: var(--color-cta)"><span></span><span></span><span></span></span>
|
|
317
401
|
<% end %>
|
|
318
402
|
|
|
319
|
-
<%= render layout: "style/specimen",
|
|
320
|
-
locals: { klass: ".sheen .sheen--loop",
|
|
321
|
-
usage: %(<span class="sheen sheen--loop">...</span>) } do %>
|
|
322
|
-
<span class="sheen sheen--loop badge text-body">Level 5 unlocked</span>
|
|
323
|
-
<span class="sheen sheen--loop card px-4 py-2 text-sm text-body" style="--sheen-duration: 3s">Sheen sweep</span>
|
|
324
|
-
<% end %>
|
|
325
|
-
|
|
326
403
|
<%= render layout: "style/specimen",
|
|
327
404
|
locals: { klass: ".ping .ping--dot (knob: --ping-color)",
|
|
328
405
|
usage: %(<span class="ping ping--dot"></span>) } do %>
|
|
@@ -372,12 +449,6 @@
|
|
|
372
449
|
<section class="space-y-5">
|
|
373
450
|
<div class="space-y-1">
|
|
374
451
|
<h3 class="type-h4 text-heading">Effects</h3>
|
|
375
|
-
<p class="text-muted text-sm">
|
|
376
|
-
The <code class="font-mono text-2xs">engine-motion.css</code> visual-effect primitives beyond motion -
|
|
377
|
-
gradient text, a soft glow halo, a one- or two-color rotating <strong>selection glow</strong>
|
|
378
|
-
(ported from Turf Monster's selected-team-card ring), frosted glass, and a conic wash. Each is
|
|
379
|
-
themed through the role tokens and tunable via CSS-var knobs.
|
|
380
|
-
</p>
|
|
381
452
|
</div>
|
|
382
453
|
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
383
454
|
<%= render layout: "style/specimen",
|
|
@@ -399,21 +470,6 @@
|
|
|
399
470
|
</div>
|
|
400
471
|
<% end %>
|
|
401
472
|
|
|
402
|
-
<%# The CARD shape: the host carries its own background and the ring still
|
|
403
|
-
rings, because the primitive punches the host's box out of both wedge
|
|
404
|
-
layers. The WRAPPER shape (bare host + opaque child) is staged by the
|
|
405
|
-
Modals section's glow cards — both are documented in engine-motion.css
|
|
406
|
-
§9b. Two wedges travel the edge, so a pair of colors reads as a pair. %>
|
|
407
|
-
<%= render layout: "style/specimen",
|
|
408
|
-
locals: { klass: ".studio-team-glow (knobs: --studio-team-glow-color / -color-b)",
|
|
409
|
-
usage: %(<div class="studio-team-glow rounded-xl bg-surface p-5">Selected</div>\n<div class="studio-team-glow rounded-xl bg-surface p-5"\n style="--studio-team-glow-color: #F08030; --studio-team-glow-color-b: #6890F0">Fire / Flying</div>) } do %>
|
|
410
|
-
<div class="flex flex-wrap items-center justify-center gap-6">
|
|
411
|
-
<div class="studio-team-glow rounded-xl bg-surface border border-subtle px-5 py-4 text-sm font-semibold text-heading text-center">Selected</div>
|
|
412
|
-
<div class="studio-team-glow rounded-xl bg-surface border border-subtle px-5 py-4 text-sm font-semibold text-heading text-center"
|
|
413
|
-
style="--studio-team-glow-color: #F08030; --studio-team-glow-color-b: #6890F0">Fire / Flying</div>
|
|
414
|
-
</div>
|
|
415
|
-
<% end %>
|
|
416
|
-
|
|
417
473
|
<%= render layout: "style/specimen",
|
|
418
474
|
locals: { klass: ".surface-glass (knobs: --surface-glass-opacity / -blur)",
|
|
419
475
|
usage: %(<div class="surface-glass rounded-xl p-4">...</div>) } do %>
|
|
@@ -433,115 +489,6 @@
|
|
|
433
489
|
</div>
|
|
434
490
|
</section>
|
|
435
491
|
|
|
436
|
-
<!-- Hold to confirm -->
|
|
437
|
-
<section class="space-y-5">
|
|
438
|
-
<div class="space-y-1">
|
|
439
|
-
<h3 class="type-h4 text-heading">Hold to confirm</h3>
|
|
440
|
-
<p class="text-muted text-sm max-w-2xl">
|
|
441
|
-
A press-and-hold CTA for an action you do not want taken by accident, and the
|
|
442
|
-
carbonation behind it. Ported from Turf Monster, where it confirms a contest entry.
|
|
443
|
-
The bubbles sit <em>behind</em> the button and show only where they escape its edges;
|
|
444
|
-
they simmer at rest, boil on hover and through the hold, then burst outward on
|
|
445
|
-
success. The whole thing themes off the primary role token, so it wears this app’s
|
|
446
|
-
brand — and every color also reads a <code class="font-mono text-2xs">--hold-*</code>
|
|
447
|
-
input, so one instance can be re-themed from any ancestor.
|
|
448
|
-
<code class="font-mono text-2xs">prefers-reduced-motion</code> switches the bubbles off.
|
|
449
|
-
Inside a card that clips, pass <code class="font-mono text-2xs">fizz_portal: true</code>
|
|
450
|
-
and the bubbles rise past the card’s edge instead of stopping at it.
|
|
451
|
-
</p>
|
|
452
|
-
</div>
|
|
453
|
-
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
454
|
-
<%# 1. The fizz on its own: no button, just the layers. %>
|
|
455
|
-
<%= render layout: "style/specimen",
|
|
456
|
-
locals: { klass: ".hold-stack > .hold-fizz (.fizz-bit)",
|
|
457
|
-
usage: %(render "studio/fizz_layer", seed: "demo") } do %>
|
|
458
|
-
<div class="w-full max-w-xs">
|
|
459
|
-
<span class="hold-stack fizz-lively" style="height:3rem;--fizz-c-1:#8b5cf6;--fizz-c-4:#22d3ee;--fizz-c-7:#f59e0b;--fizz-c-10:#ef4444;--fizz-c-13:#10b981;--fizz-c-16:#ec4899">
|
|
460
|
-
<%= render "studio/fizz_layer", seed: "style-fizz" %>
|
|
461
|
-
<%= render "studio/fizz_layer", seed: "style-fizz~extra", extra: true %>
|
|
462
|
-
<span class="flex items-center justify-center h-12 text-xs text-muted">six zones, six colors</span>
|
|
463
|
-
</span>
|
|
464
|
-
</div>
|
|
465
|
-
<% end %>
|
|
466
|
-
|
|
467
|
-
<%# 2. The button itself, live: press and hold for two seconds. The card
|
|
468
|
-
clips, so this one sets fizz_portal and its bubbles rise past the
|
|
469
|
-
card's edge. %>
|
|
470
|
-
<%= render layout: "style/specimen",
|
|
471
|
-
locals: { klass: ".hold-btn (studio/hold_button)",
|
|
472
|
-
usage: %(render "studio/hold_button", hold_id: "confirm", duration: 2000, fizz_portal: true) } do %>
|
|
473
|
-
<div class="w-full max-w-xs" x-data="{ submitting: false }">
|
|
474
|
-
<%= render "studio/hold_button", hold_id: "style-hold", duration: 2000, fizz_portal: true,
|
|
475
|
-
default_text: "Hold to Confirm", hold_text: "Almost there…", success_text: "Confirmed!" %>
|
|
476
|
-
<button type="button" class="btn btn-neutral btn-sm mt-3 mx-auto block"
|
|
477
|
-
@click="document.querySelector('.hold-btn[data-hold-id=style-hold]').classList.remove('process','success','error','loading')">Reset</button>
|
|
478
|
-
</div>
|
|
479
|
-
<% end %>
|
|
480
|
-
|
|
481
|
-
<%# 3. The same button, re-themed from an ancestor — no new CSS. %>
|
|
482
|
-
<%= render layout: "style/specimen",
|
|
483
|
-
locals: { klass: ".hold-btn (--hold-* overrides)",
|
|
484
|
-
usage: %(<div style="--hold-bg-from:#f0abfc;--hold-success-to:#4c1d95">…</div>) } do %>
|
|
485
|
-
<div class="w-full max-w-xs" x-data="{ submitting: false }"
|
|
486
|
-
style="--hold-bg-from:#f0abfc;--hold-bg-mid:#a855f7;--hold-bg-to:#6b21a8;
|
|
487
|
-
--hold-success-from:#c4b5fd;--hold-success-to:#4c1d95;--hold-success-glow-rgb:139 92 246">
|
|
488
|
-
<%= render "studio/hold_button", hold_id: "style-hold-custom", duration: 2000,
|
|
489
|
-
default_text: "Hold to Confirm", success_text: "Confirmed!",
|
|
490
|
-
fizz_colors: %w[#f0abfc #6b21a8 #a855f7 #a5b4fc #312e81 #6366f1 #fbcfe8 #831843 #ec4899
|
|
491
|
-
#bae6fd #0c4a6e #0ea5e9 #ddd6fe #4c1d95 #8b5cf6 #fde68a #78350f #f59e0b] %>
|
|
492
|
-
<button type="button" class="btn btn-neutral btn-sm mt-3 mx-auto block"
|
|
493
|
-
@click="document.querySelector('.hold-btn[data-hold-id=style-hold-custom]').classList.remove('process','success','error','loading')">Reset</button>
|
|
494
|
-
</div>
|
|
495
|
-
<% end %>
|
|
496
|
-
</div>
|
|
497
|
-
</section>
|
|
498
|
-
|
|
499
|
-
<!-- Confetti & pulse -->
|
|
500
|
-
<section class="space-y-5">
|
|
501
|
-
<div class="space-y-1">
|
|
502
|
-
<h3 class="type-h4 text-heading">Confetti & pulse</h3>
|
|
503
|
-
<p class="text-muted text-sm max-w-2xl">
|
|
504
|
-
Celebration effects lifted from Turf Monster into the engine. Confetti is
|
|
505
|
-
<code class="font-mono text-2xs">window.studioConfetti</code> - a zero-dependency,
|
|
506
|
-
no-CDN effect (canvas-confetti is vendored into the engine and shipped through the
|
|
507
|
-
asset pipeline). Two callable variants, both honoring
|
|
508
|
-
<code class="font-mono text-2xs">prefers-reduced-motion</code>. The pulse is the
|
|
509
|
-
<code class="font-mono text-2xs">.pulse-cta</code> attention beat. Fire the confetti
|
|
510
|
-
with the buttons below.
|
|
511
|
-
</p>
|
|
512
|
-
</div>
|
|
513
|
-
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
514
|
-
<%= render layout: "style/specimen",
|
|
515
|
-
locals: { klass: "window.studioConfetti.burst(el)",
|
|
516
|
-
usage: %(window.studioConfetti.burst(cardEl)\n// radial burst from behind a card/modal, from its center) } do %>
|
|
517
|
-
<div x-data="{}" class="flex flex-col items-center gap-3 py-2">
|
|
518
|
-
<div x-ref="burstCard" class="card px-5 py-4 text-sm font-semibold text-heading text-center">Success!</div>
|
|
519
|
-
<button type="button" class="btn btn-primary btn-sm"
|
|
520
|
-
@click="window.studioConfetti && window.studioConfetti.burst($refs.burstCard)">Fire burst</button>
|
|
521
|
-
</div>
|
|
522
|
-
<% end %>
|
|
523
|
-
|
|
524
|
-
<%= render layout: "style/specimen",
|
|
525
|
-
locals: { klass: "window.studioConfetti.cannons()",
|
|
526
|
-
usage: %(window.studioConfetti.cannons()\n// left + right side-cannons, full-screen celebration) } do %>
|
|
527
|
-
<div x-data="{}" class="flex flex-col items-center gap-3 py-2">
|
|
528
|
-
<p class="text-sm font-semibold text-heading text-center">You joined a contest!</p>
|
|
529
|
-
<button type="button" class="btn btn-success btn-sm"
|
|
530
|
-
@click="window.studioConfetti && window.studioConfetti.cannons()">Fire cannons</button>
|
|
531
|
-
</div>
|
|
532
|
-
<% end %>
|
|
533
|
-
|
|
534
|
-
<%= render layout: "style/specimen",
|
|
535
|
-
locals: { klass: ".pulse-cta (knobs: --pulse-cta-color / -speed / -scale)",
|
|
536
|
-
usage: %(<button class="btn btn-primary pulse-cta">Change Username</button>\n<button class="btn btn-success pulse-cta" style="--pulse-cta-color: var(--color-success)">Go</button>) } do %>
|
|
537
|
-
<div class="flex flex-wrap items-center justify-center gap-4">
|
|
538
|
-
<button type="button" class="btn btn-primary pulse-cta">Change Username</button>
|
|
539
|
-
<button type="button" class="btn btn-success pulse-cta" style="--pulse-cta-color: var(--color-success)">Go</button>
|
|
540
|
-
</div>
|
|
541
|
-
<% end %>
|
|
542
|
-
</div>
|
|
543
|
-
</section>
|
|
544
|
-
|
|
545
492
|
<!-- Leveling (capability-gated: Studio.feature?(:leveling)) -->
|
|
546
493
|
<section class="space-y-5">
|
|
547
494
|
<div class="space-y-1">
|
|
@@ -553,15 +500,7 @@
|
|
|
553
500
|
<span class="badge" style="color: var(--color-warning); border-color: var(--color-warning)">disabled on this app</span>
|
|
554
501
|
<% end %>
|
|
555
502
|
</div>
|
|
556
|
-
<p class="text-muted text-sm max-w-2xl">
|
|
557
|
-
The real <code class="font-mono text-2xs">.level-badge</code> tiers, gated by
|
|
558
|
-
<code class="font-mono text-2xs">Studio.feature?(:leveling)</code>. Ported into the engine
|
|
559
|
-
(<code class="font-mono text-2xs">engine-motion.css</code>) from Turf Monster's leveling - the
|
|
560
|
-
outline starter through the holographic Level 10 - so any consumer with leveling on ships the
|
|
561
|
-
polished ladder. The tiers theme off <code class="font-mono text-2xs">--color-primary-*</code>, so
|
|
562
|
-
they restyle per app. Leveling is <strong><%= leveling_on ? "on" : "off" %></strong> here, so these
|
|
563
|
-
render <%= leveling_on ? "live" : "disabled-but-present - visible and previewable, but greyed and flagged" %>.
|
|
564
|
-
</p>
|
|
503
|
+
<p class="text-muted text-sm max-w-2xl">Level pills, gated by <code class="font-mono text-2xs">Studio.feature?(:leveling)</code>.</p>
|
|
565
504
|
</div>
|
|
566
505
|
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
567
506
|
<%= render layout: "style/specimen",
|
|
@@ -22,7 +22,8 @@
|
|
|
22
22
|
|
|
23
23
|
HEADINGS wear the type ladder, on trial on this page only (style/_type_ladder): the one
|
|
24
24
|
<h1> is studio/_page_header's, at type-h2; a section is an <h2> at type-h3; a subsection
|
|
25
|
-
is an <h3> at type-h4.
|
|
25
|
+
is an <h3> at type-h4. The section's <h2> is studio/_section_header's, in type-h3's numbers,
|
|
26
|
+
which sticks under the page row until the next section's takes its place.
|
|
26
27
|
|
|
27
28
|
Specimens use ONLY the engine's own classes (engine.css component layer plus
|
|
28
29
|
engine-motion.css motion/effect/leveling layer), so a consumer that imports
|
|
@@ -59,7 +60,7 @@
|
|
|
59
60
|
%>
|
|
60
61
|
<div class="max-w-6xl mx-auto px-4 pt-6 pb-16" data-type-ladder>
|
|
61
62
|
<%# The page's title and its section tabs: studio/_page_header, the row of the
|
|
62
|
-
|
|
63
|
+
pinned stack (docs/PAGE_HEADER.md). It sits at --z-sticky, above --z-raised,
|
|
63
64
|
so the live hold button's portal bubbles scroll under it rather than over it.
|
|
64
65
|
|
|
65
66
|
The style-section-nav controller moves the highlight as the page scrolls
|
|
@@ -77,7 +78,10 @@
|
|
|
77
78
|
data: { style_section_nav_target: "link" } }
|
|
78
79
|
} %>
|
|
79
80
|
|
|
80
|
-
|
|
81
|
+
<%# 6rem between sections: a first jump from the top of the page lands by the navbar's
|
|
82
|
+
collapse below the line, and the section above must be gone from the bar by then
|
|
83
|
+
(76px on the hub at phone width). %>
|
|
84
|
+
<div class="space-y-24 pt-10">
|
|
81
85
|
<%= render "style/theme" %>
|
|
82
86
|
<%= render "style/modals" %>
|
|
83
87
|
<% if host_modals %>
|
data/lib/studio/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: studio-engine
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.130.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex McRitchie
|
|
@@ -640,11 +640,13 @@ files:
|
|
|
640
640
|
- app/views/studio/_board_assets.html.erb
|
|
641
641
|
- app/views/studio/_confirm_interstitial.html.erb
|
|
642
642
|
- app/views/studio/_cropper_assets.html.erb
|
|
643
|
+
- app/views/studio/_fizz.html.erb
|
|
643
644
|
- app/views/studio/_fizz_layer.html.erb
|
|
644
645
|
- app/views/studio/_hold_button.html.erb
|
|
645
646
|
- app/views/studio/_hold_button_guard.html.erb
|
|
646
647
|
- app/views/studio/_leveling_activity_assets.html.erb
|
|
647
648
|
- app/views/studio/_page_header.html.erb
|
|
649
|
+
- app/views/studio/_section_header.html.erb
|
|
648
650
|
- app/views/studio/_session_stamp.html.erb
|
|
649
651
|
- app/views/studio/admin_models/_arenas_table.html.erb
|
|
650
652
|
- app/views/studio/admin_models/_sessions_table.html.erb
|