studio-engine 0.132.0 → 0.133.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 +77 -0
- data/README.md +52 -24
- data/app/assets/tailwind/studio_engine/engine.css +141 -0
- data/app/javascript/studio/application.js +1 -0
- data/app/javascript/studio/carousel.js +66 -1
- data/app/javascript/studio/controllers/carousel_controller.js +139 -8
- data/app/javascript/studio/controllers/page_header_controller.js +64 -1
- data/app/javascript/studio/controllers/style_section_nav_controller.js +26 -0
- data/app/javascript/studio/in_page_links.js +308 -0
- data/app/javascript/studio/page_header.js +17 -0
- data/app/views/studio/_carousel.html.erb +33 -5
- data/app/views/studio/_page_header.html.erb +64 -12
- data/app/views/studio/auth/_sign_in_body.html.erb +236 -0
- data/app/views/studio/auth/_sign_in_card.html.erb +50 -0
- data/app/views/studio/auth/_sign_in_modal.html.erb +36 -0
- data/app/views/studio/modals/auth/_resend_footer.html.erb +6 -0
- data/app/views/style/_glass.html.erb +4 -8
- data/app/views/style/_loaders.html.erb +11 -35
- data/app/views/style/_modals.html.erb +29 -39
- data/app/views/style/_scrims.html.erb +10 -20
- data/app/views/style/_site_footer.html.erb +10 -18
- data/app/views/style/_specimen.html.erb +13 -41
- data/app/views/style/_tasks.html.erb +4 -22
- data/app/views/style/_trick_label.html.erb +33 -0
- data/app/views/style/_tricks.html.erb +127 -207
- data/app/views/style/index.html.erb +2 -2
- data/app/views/style/modals/_wallet_connect.html.erb +2 -1
- data/config/studio_tricks.yml +470 -0
- data/lib/studio/page_header.rb +53 -0
- data/lib/studio/tricks.rb +110 -0
- data/lib/studio/version.rb +1 -1
- data/lib/studio.rb +6 -4
- metadata +9 -3
- data/app/views/style/modals/_ds_email_field.html.erb +0 -58
- data/app/views/style/modals/_ds_resend_footer.html.erb +0 -70
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
<%# Tricks section of the living style guide (admin/style). Buttons come first,
|
|
2
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
|
|
4
|
-
|
|
3
|
+
(the shared _specimen partial) shows the real engine class live under its
|
|
4
|
+
trick name from config/studio_tricks.yml ("Sheen", "Border Glow · clear"), the
|
|
5
|
+
name Alex McRitchie says to an agent. Variants of one primitive share a card
|
|
6
|
+
and stand side by side on its stage. The glossary is docs/TRICKS.md.
|
|
5
7
|
|
|
6
8
|
The Leveling group is gated by Studio.feature?(:leveling). With it off, its
|
|
7
9
|
specimens render "disabled but present": visible, greyed and flagged, never hidden. %>
|
|
@@ -20,12 +22,6 @@
|
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
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 %" + ">"
|
|
29
25
|
%>
|
|
30
26
|
<section id="tricks" class="section-target space-y-12">
|
|
31
27
|
<%= render "studio/section_header", title: "Tricks" %>
|
|
@@ -37,18 +33,14 @@
|
|
|
37
33
|
<h3 class="type-h4 text-heading">Buttons</h3>
|
|
38
34
|
<p class="text-muted text-sm"><code class="font-mono text-2xs">.btn</code> plus one role and one size.</p>
|
|
39
35
|
</div>
|
|
40
|
-
<div class="grid gap-4 sm:grid-cols-2
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
locals: { klass: ".btn .#{name}",
|
|
44
|
-
usage: %(<button class="btn #{name}">#{label}</button>) } do %>
|
|
36
|
+
<div class="grid gap-4 sm:grid-cols-2">
|
|
37
|
+
<%= render layout: "style/specimen", locals: { trick: "button" } do %>
|
|
38
|
+
<% button_variants.each do |name, label| %>
|
|
45
39
|
<button type="button" class="btn <%= name %>"><%= label %></button>
|
|
46
40
|
<% end %>
|
|
47
41
|
<% end %>
|
|
48
42
|
|
|
49
|
-
<%= render layout: "style/specimen",
|
|
50
|
-
locals: { klass: ".btn-sm / .btn (default) / .btn-lg",
|
|
51
|
-
usage: %(<button class="btn btn-primary btn-sm">Small</button>\n<button class="btn btn-primary">Default</button>\n<button class="btn btn-primary btn-lg">Large</button>) } do %>
|
|
43
|
+
<%= render layout: "style/specimen", locals: { trick: "button", variant: "size" } do %>
|
|
52
44
|
<div class="flex flex-wrap items-center justify-center gap-3">
|
|
53
45
|
<button type="button" class="btn btn-primary btn-sm">Small</button>
|
|
54
46
|
<button type="button" class="btn btn-primary">Default</button>
|
|
@@ -69,51 +61,28 @@
|
|
|
69
61
|
rings, because the primitive punches the host's box out of both wedge
|
|
70
62
|
layers. The WRAPPER shape (bare host + opaque child) is staged by the
|
|
71
63
|
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 %>
|
|
64
|
+
<%= render layout: "style/specimen", locals: { trick: "edge_glow" } do %>
|
|
76
65
|
<button type="button" class="btn btn-primary edge-rotate-glow">Save</button>
|
|
77
66
|
<button type="button" class="btn btn-primary edge-rotate-glow"
|
|
78
67
|
style="--edge-rotate-glow-color: #F08030; --edge-rotate-glow-color-b: #6890F0">Two colors</button>
|
|
79
68
|
<% end %>
|
|
80
69
|
|
|
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 %>
|
|
70
|
+
<%= render layout: "style/specimen", locals: { trick: "border_glow" } do %>
|
|
85
71
|
<button type="button" class="btn btn-primary studio-border-glow">Save</button>
|
|
72
|
+
<button type="button" class="btn studio-border-glow studio-border-glow--clear" data-border-glow-clear>Clear</button>
|
|
86
73
|
<% end %>
|
|
87
74
|
|
|
88
|
-
<%= render layout: "style/specimen",
|
|
89
|
-
locals: { klass: ".studio-border-glow--clear",
|
|
90
|
-
note: "The same rainbow ring and halo on a clear button.",
|
|
91
|
-
usage: %(<button class="btn studio-border-glow studio-border-glow--clear">Save</button>) } do %>
|
|
92
|
-
<button type="button" class="btn studio-border-glow studio-border-glow--clear" data-border-glow-clear>Save</button>
|
|
93
|
-
<% end %>
|
|
94
|
-
|
|
95
|
-
<%= render layout: "style/specimen",
|
|
96
|
-
locals: { klass: ".sheen .sheen--loop",
|
|
97
|
-
note: "Adds a band of light that sweeps across the button.",
|
|
98
|
-
usage: %(<button class="btn btn-primary sheen">Once</button>\n<button class="btn btn-primary sheen sheen--loop">Loop</button>) } do %>
|
|
75
|
+
<%= render layout: "style/specimen", locals: { trick: "sheen" } do %>
|
|
99
76
|
<button type="button" class="btn btn-primary sheen">Once</button>
|
|
100
77
|
<button type="button" class="btn btn-primary sheen sheen--loop">Loop</button>
|
|
101
78
|
<% end %>
|
|
102
79
|
|
|
103
|
-
<%= render layout: "style/specimen",
|
|
104
|
-
locals: { klass: ".pulse-cta (knobs: --pulse-cta-color / -speed / -scale)",
|
|
105
|
-
note: "Adds a slow beat with a ring that spreads from the edge.",
|
|
106
|
-
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 %>
|
|
80
|
+
<%= render layout: "style/specimen", locals: { trick: "pulse" } do %>
|
|
107
81
|
<button type="button" class="btn btn-primary pulse-cta">Save</button>
|
|
108
82
|
<button type="button" class="btn btn-success pulse-cta" style="--pulse-cta-color: var(--color-success)">Go</button>
|
|
109
83
|
<% end %>
|
|
110
84
|
|
|
111
|
-
<%= render layout: "style/specimen",
|
|
112
|
-
locals: { klass: ".fizz-stack (studio/fizz)",
|
|
113
|
-
note: "Adds bubbles that rise from behind the button.",
|
|
114
|
-
usage: [erb.call(%(render layout: "studio/fizz", locals: { seed: "save" } do)),
|
|
115
|
-
%( <button class="btn btn-primary">Save</button>),
|
|
116
|
-
erb_end].join("\n") } do %>
|
|
85
|
+
<%= render layout: "style/specimen", locals: { trick: "fizz" } do %>
|
|
117
86
|
<%= render layout: "studio/fizz", locals: { seed: "style-fizz" } do %>
|
|
118
87
|
<button type="button" class="btn btn-primary">Save</button>
|
|
119
88
|
<% end %>
|
|
@@ -122,25 +91,17 @@
|
|
|
122
91
|
<% end %>
|
|
123
92
|
<% end %>
|
|
124
93
|
|
|
125
|
-
<%# The live button: press and hold for two
|
|
126
|
-
sets fizz_portal and its bubbles rise past the
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
usage: %(render "studio/hold_button", hold_id: "confirm", duration: 2000, fizz_portal: true) } do %>
|
|
94
|
+
<%# Hold and its recolor, side by side. The live button: press and hold for two
|
|
95
|
+
seconds. The card clips, so it sets fizz_portal and its bubbles rise past the
|
|
96
|
+
card's edge. The recolor is the same button re-themed from an ancestor: no
|
|
97
|
+
new CSS. %>
|
|
98
|
+
<%= render layout: "style/specimen", locals: { trick: "hold" } do %>
|
|
131
99
|
<div class="w-full max-w-xs" x-data="{ submitting: false }">
|
|
132
100
|
<%= render "studio/hold_button", hold_id: "style-hold", duration: 2000, fizz_portal: true,
|
|
133
101
|
default_text: "Hold to Confirm", hold_text: "Almost there…", success_text: "Confirmed!" %>
|
|
134
102
|
<button type="button" class="btn btn-neutral btn-sm mt-3 mx-auto block"
|
|
135
103
|
@click="document.querySelector('.hold-btn[data-hold-id=style-hold]').classList.remove('process','success','error','loading')">Reset</button>
|
|
136
104
|
</div>
|
|
137
|
-
<% end %>
|
|
138
|
-
|
|
139
|
-
<%# The same button, re-themed from an ancestor: no new CSS. %>
|
|
140
|
-
<%= render layout: "style/specimen",
|
|
141
|
-
locals: { klass: ".hold-btn (--hold-* overrides)",
|
|
142
|
-
note: "Recolors a hold button from any parent element.",
|
|
143
|
-
usage: %(<div style="--hold-bg-from:#f0abfc;--hold-success-to:#4c1d95">…</div>) } do %>
|
|
144
105
|
<div class="w-full max-w-xs" x-data="{ submitting: false }"
|
|
145
106
|
style="--hold-bg-from:#f0abfc;--hold-bg-mid:#a855f7;--hold-bg-to:#6b21a8;
|
|
146
107
|
--hold-success-from:#c4b5fd;--hold-success-to:#4c1d95;--hold-success-glow-rgb:139 92 246">
|
|
@@ -153,18 +114,9 @@
|
|
|
153
114
|
</div>
|
|
154
115
|
<% end %>
|
|
155
116
|
|
|
156
|
-
<%= render layout: "style/specimen",
|
|
157
|
-
locals: { klass: "window.studioConfetti.burst(el)",
|
|
158
|
-
note: "Fires confetti out from behind the button.",
|
|
159
|
-
usage: %(<button class="btn btn-primary" x-data @click="window.studioConfetti.burst($el)">Save</button>) } do %>
|
|
117
|
+
<%= render layout: "style/specimen", locals: { trick: "burst" } do %>
|
|
160
118
|
<button type="button" class="btn btn-primary" x-data
|
|
161
119
|
@click="window.studioConfetti && window.studioConfetti.burst($el)">Burst</button>
|
|
162
|
-
<% end %>
|
|
163
|
-
|
|
164
|
-
<%= render layout: "style/specimen",
|
|
165
|
-
locals: { klass: "window.studioConfetti.cannons()",
|
|
166
|
-
note: "Fires confetti in from both sides of the screen.",
|
|
167
|
-
usage: %(<button class="btn btn-success" x-data @click="window.studioConfetti.cannons()">Done</button>) } do %>
|
|
168
120
|
<button type="button" class="btn btn-success" x-data
|
|
169
121
|
@click="window.studioConfetti && window.studioConfetti.cannons()">Cannons</button>
|
|
170
122
|
<% end %>
|
|
@@ -175,15 +127,16 @@
|
|
|
175
127
|
<section class="space-y-5" data-page-header-specimen>
|
|
176
128
|
<div class="space-y-1">
|
|
177
129
|
<h3 class="type-h4 text-heading">Page header</h3>
|
|
178
|
-
<p class="text-muted text-sm"><code class="font-mono text-2xs">studio/_page_header</code>: crumbs above the title
|
|
130
|
+
<p class="text-muted text-sm"><code class="font-mono text-2xs">studio/_page_header</code>: crumbs above the title; tabs or anchors, a Links menu, one action, then tools.</p>
|
|
179
131
|
</div>
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
132
|
+
<%# The classes alone, not the partial: the partial's row is sticky, joins the
|
|
133
|
+
pinned stack and holds the page's h1, and a page has one of each. One card,
|
|
134
|
+
the row at rest above the row stuck (Page Header · stuck). %>
|
|
135
|
+
<%= render layout: "style/specimen", locals: { trick: "page_header" } do %>
|
|
136
|
+
<div class="w-full space-y-4">
|
|
137
|
+
<% [["at rest", {}], ["stuck", { stuck: "" }]].each do |state, data| %>
|
|
138
|
+
<div class="space-y-1">
|
|
139
|
+
<p class="label-upper"><%= state %></p>
|
|
187
140
|
<%= tag.div(class: "page-header w-full px-4", data: data) do %>
|
|
188
141
|
<div class="page-header-trail">
|
|
189
142
|
<%# A div, not the partial's <nav>: a specimen adds no landmark to the page. %>
|
|
@@ -210,7 +163,49 @@
|
|
|
210
163
|
<a href="#tricks" class="page-header-link">Invites</a>
|
|
211
164
|
</div>
|
|
212
165
|
<% end %>
|
|
213
|
-
|
|
166
|
+
</div>
|
|
167
|
+
<% end %>
|
|
168
|
+
</div>
|
|
169
|
+
<% end %>
|
|
170
|
+
<%# The four kinds of right-side control, each with its one look: tabs (views of the
|
|
171
|
+
same thing) or anchors (places on this page) in the pill group, the Links menu
|
|
172
|
+
(pages elsewhere), the one primary action, then the muted tools. The menu is the
|
|
173
|
+
browser's popover, so it opens here with no script (Page Header · controls). %>
|
|
174
|
+
<div data-page-header-kinds-specimen>
|
|
175
|
+
<%= render layout: "style/specimen", locals: { trick: "page_header", variant: "controls" } do %>
|
|
176
|
+
<div class="w-full space-y-4">
|
|
177
|
+
<% [["tabs", "Deployments", [["Deployments", true], ["Triage", false]]],
|
|
178
|
+
["anchors", "Style", [["Colors", false], ["Buttons", false], ["Forms", false]]]].each do |kind, title, pills| %>
|
|
179
|
+
<%= tag.div(class: "page-header w-full px-4", data: { stuck: "" }) do %>
|
|
180
|
+
<div class="page-header-lead">
|
|
181
|
+
<p class="page-header-title"><%= title %></p>
|
|
182
|
+
</div>
|
|
183
|
+
<div class="page-header-tabs" data-page-header-kind="<%= kind %>">
|
|
184
|
+
<div class="page-header-track">
|
|
185
|
+
<% pills.each do |label, current| %>
|
|
186
|
+
<a href="#tricks" class="page-header-tab btn btn-sm <%= current ? "btn-primary" : "btn-neutral" %>"><span class="page-header-tab-label"><%= label %></span></a>
|
|
187
|
+
<% end %>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
<div class="page-header-actions">
|
|
191
|
+
<div class="page-header-menu" data-page-header-kind="menu">
|
|
192
|
+
<button type="button" class="page-header-menu-button btn btn-sm btn-neutral"
|
|
193
|
+
popovertarget="style-page-header-menu-<%= kind %>" aria-controls="style-page-header-menu-<%= kind %>">Links<span class="page-header-menu-caret" aria-hidden="true"></span></button>
|
|
194
|
+
<div id="style-page-header-menu-<%= kind %>" class="page-header-menu-list" popover="auto">
|
|
195
|
+
<ul>
|
|
196
|
+
<li><a href="#tricks" class="page-header-menu-item">Stages</a></li>
|
|
197
|
+
<li><a href="#tricks" class="page-header-menu-item">Docs</a></li>
|
|
198
|
+
</ul>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
<a href="#tricks" class="page-header-action btn btn-sm btn-primary" data-page-header-kind="action">New Task</a>
|
|
202
|
+
<div class="page-header-tools" data-page-header-kind="tools">
|
|
203
|
+
<button type="button" class="btn btn-sm btn-neutral">Seed</button>
|
|
204
|
+
</div>
|
|
205
|
+
</div>
|
|
206
|
+
<% end %>
|
|
207
|
+
<% end %>
|
|
208
|
+
</div>
|
|
214
209
|
<% end %>
|
|
215
210
|
</div>
|
|
216
211
|
</section>
|
|
@@ -225,17 +220,18 @@
|
|
|
225
220
|
The rule is in <code class="font-mono text-2xs">docs/PAGE_HEADER.md</code>.
|
|
226
221
|
</p>
|
|
227
222
|
</div>
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
223
|
+
<%# The classes alone, not the partial: the partial's header sticks and joins the
|
|
224
|
+
pinned stack, and a specimen must do neither. %>
|
|
225
|
+
<%= render layout: "style/specimen", locals: { trick: "section_header" } do %>
|
|
226
|
+
<div class="w-full grid gap-4 sm:grid-cols-2">
|
|
227
|
+
<% [["at rest", {}], ["stuck", { stuck: "" }]].each do |state, data| %>
|
|
228
|
+
<div class="space-y-1">
|
|
229
|
+
<p class="label-upper"><%= state %></p>
|
|
230
|
+
<%= tag.p("Modals", class: "section-header px-4", data: data) %>
|
|
231
|
+
</div>
|
|
236
232
|
<% end %>
|
|
237
|
-
|
|
238
|
-
|
|
233
|
+
</div>
|
|
234
|
+
<% end %>
|
|
239
235
|
</section>
|
|
240
236
|
|
|
241
237
|
<!-- Carousel -->
|
|
@@ -247,10 +243,7 @@
|
|
|
247
243
|
<%# The live partial: a carousel joins no stack and holds no title, so the
|
|
248
244
|
specimen is the real thing. Swipe it, press its arrows, or focus it and
|
|
249
245
|
use the arrow keys. %>
|
|
250
|
-
<%= render layout: "style/specimen",
|
|
251
|
-
locals: { klass: "studio/_carousel",
|
|
252
|
-
note: "Shows a set too long to grid, a few slides at a time.",
|
|
253
|
-
usage: %(<%= render layout: "studio/carousel",\n locals: { label: "Portfolio", items: @apps, per_view: 3 } do |app| %\>\n <%= render "apps/card", app: app %\>\n<% end %\>) } do %>
|
|
246
|
+
<%= render layout: "style/specimen", locals: { trick: "carousel" } do %>
|
|
254
247
|
<div class="w-full min-w-0">
|
|
255
248
|
<%= render layout: "studio/carousel",
|
|
256
249
|
locals: { label: "Specimen slides", items: (1..7).to_a, per_view: 3, id: "style-carousel-specimen" } do |n| %>
|
|
@@ -262,10 +255,7 @@
|
|
|
262
255
|
</div>
|
|
263
256
|
<% end %>
|
|
264
257
|
|
|
265
|
-
<%= render layout: "style/specimen",
|
|
266
|
-
locals: { klass: "studio/_carousel (style: :spotlight)",
|
|
267
|
-
note: "Centres one slide at full size and colour, with its caption, and shrinks, fades and greys its neighbours as they scroll by. It opens on its middle slide; start: takes an index instead.",
|
|
268
|
-
usage: %(<%= render layout: "studio/carousel",\n locals: { label: "Team", items: @people, style: :spotlight,\n caption: ->(person) { person.title } } do |person| %\>\n <%= render "people/card", person: person %\>\n<% end %\>) } do %>
|
|
258
|
+
<%= render layout: "style/specimen", locals: { trick: "spotlight" } do %>
|
|
269
259
|
<div class="w-full min-w-0" data-carousel-spotlight-specimen>
|
|
270
260
|
<%= render layout: "studio/carousel",
|
|
271
261
|
locals: { label: "Spotlight specimen", items: (1..7).to_a, per_view: 3, style: :spotlight,
|
|
@@ -285,18 +275,13 @@
|
|
|
285
275
|
<h3 class="type-h4 text-heading">Surfaces, text & form</h3>
|
|
286
276
|
</div>
|
|
287
277
|
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
288
|
-
<%= render layout: "style/specimen",
|
|
289
|
-
|
|
290
|
-
<div class="card p-5 text-sm text-body
|
|
291
|
-
<% end %>
|
|
292
|
-
|
|
293
|
-
<%= render layout: "style/specimen",
|
|
294
|
-
locals: { klass: ".card-hover", usage: %(<div class="card-hover p-5">...</div>) } do %>
|
|
295
|
-
<div class="card-hover p-5 text-sm text-body w-full max-w-xs text-center">Hover me</div>
|
|
278
|
+
<%= render layout: "style/specimen", locals: { trick: "card" } do %>
|
|
279
|
+
<div class="card p-5 text-sm text-body text-center">Surface card</div>
|
|
280
|
+
<div class="card-hover p-5 text-sm text-body text-center">Hover me</div>
|
|
296
281
|
<% end %>
|
|
297
282
|
|
|
298
283
|
<%= render layout: "style/specimen",
|
|
299
|
-
locals: {
|
|
284
|
+
locals: { trick: "badge" } do %>
|
|
300
285
|
<div class="flex flex-wrap items-center justify-center gap-2">
|
|
301
286
|
<span class="badge text-body">Neutral</span>
|
|
302
287
|
<span class="badge" style="color: var(--color-success-ink); border-color: var(--color-success)">Success</span>
|
|
@@ -306,17 +291,17 @@
|
|
|
306
291
|
<% end %>
|
|
307
292
|
|
|
308
293
|
<%= render layout: "style/specimen",
|
|
309
|
-
locals: {
|
|
294
|
+
locals: { trick: "input" } do %>
|
|
310
295
|
<input type="text" class="input-field" placeholder="you@example.com" aria-label="Sample input">
|
|
311
296
|
<% end %>
|
|
312
297
|
|
|
313
298
|
<%= render layout: "style/specimen",
|
|
314
|
-
locals: {
|
|
299
|
+
locals: { trick: "label" } do %>
|
|
315
300
|
<p class="label-upper">Section label</p>
|
|
316
301
|
<% end %>
|
|
317
302
|
|
|
318
303
|
<%= render layout: "style/specimen",
|
|
319
|
-
locals: {
|
|
304
|
+
locals: { trick: "empty_state" } do %>
|
|
320
305
|
<div class="empty-state w-full max-w-xs text-sm text-muted">Nothing here yet</div>
|
|
321
306
|
<% end %>
|
|
322
307
|
</div>
|
|
@@ -334,38 +319,28 @@
|
|
|
334
319
|
<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>
|
|
335
320
|
</div>
|
|
336
321
|
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
usage: erb.call("at_time_tag(old_record.created_at)") + "\n# the year joins only when it differs" } do %>
|
|
354
|
-
<span class="text-sm text-body"><%= at_time_tag(1.year.ago) %></span>
|
|
355
|
-
<% end %>
|
|
356
|
-
|
|
357
|
-
<%= render layout: "style/specimen",
|
|
358
|
-
locals: { klass: "at_time_tag(time, prefix: nil)",
|
|
359
|
-
usage: erb.call("at_time_tag(time, prefix: nil)") + %(\n# bare clock, for a cell that already says "at") } do %>
|
|
360
|
-
<span class="text-sm text-body"><%= at_time_tag(Time.current, prefix: nil) %></span>
|
|
322
|
+
<%# The date joins only when the stamp is not today, and the year only when it
|
|
323
|
+
differs: the rule reads best as the stamps one under another, each beside
|
|
324
|
+
what it shows. The last row is the first caller's shape, a status badge. %>
|
|
325
|
+
<%= render layout: "style/specimen", locals: { trick: "time_stamp" } do %>
|
|
326
|
+
<dl class="grid grid-cols-[auto_1fr] items-center gap-x-4 gap-y-2 text-sm">
|
|
327
|
+
<dt class="text-muted">today</dt>
|
|
328
|
+
<dd class="text-body"><%= at_time_tag(Time.current) %></dd>
|
|
329
|
+
<dt class="text-muted">not today</dt>
|
|
330
|
+
<dd class="text-body"><%= at_time_tag(3.days.ago) %></dd>
|
|
331
|
+
<dt class="text-muted">another year</dt>
|
|
332
|
+
<dd class="text-body"><%= at_time_tag(1.year.ago) %></dd>
|
|
333
|
+
<dt class="text-muted">bare</dt>
|
|
334
|
+
<dd class="text-body"><%= at_time_tag(Time.current, prefix: nil) %></dd>
|
|
335
|
+
<dt class="text-muted">in a badge</dt>
|
|
336
|
+
<dd><span class="badge" style="color: var(--color-success-ink); border-color: var(--color-success)">Shipped <%= at_time_tag(42.minutes.ago) %></span></dd>
|
|
337
|
+
</dl>
|
|
361
338
|
<% end %>
|
|
362
339
|
|
|
363
340
|
<%# STATIC ILLUSTRATION, not the primitive: a reader inside the US never sees
|
|
364
341
|
the flag, so a live specimen cannot show them what it looks like. Hand-built
|
|
365
342
|
to match the hydrated markup exactly (clock, then ml-2, then the flag). %>
|
|
366
|
-
<%= render layout: "style/specimen",
|
|
367
|
-
locals: { klass: "outside the US — the flag trails",
|
|
368
|
-
usage: %(# The flag is added CLIENT-side from the browser's IANA zone.\n# Inside the US there is no flag at all, and its margin collapses with it.) } do %>
|
|
343
|
+
<%= render layout: "style/specimen", locals: { trick: "time_stamp", variant: "abroad" } do %>
|
|
369
344
|
<div class="flex flex-col items-start gap-1.5 text-sm text-body">
|
|
370
345
|
<span class="whitespace-nowrap"><span>at 6:50a</span><span class="ml-2">🇯🇵</span></span>
|
|
371
346
|
<span class="whitespace-nowrap"><span>at 10:50p</span><span class="ml-2">🇬🇧</span></span>
|
|
@@ -373,14 +348,6 @@
|
|
|
373
348
|
<span class="whitespace-nowrap text-muted"><span>at 4:50p</span></span>
|
|
374
349
|
</div>
|
|
375
350
|
<% end %>
|
|
376
|
-
|
|
377
|
-
<%# The first caller's shape, so a host sees the primitive in a real pill
|
|
378
|
-
rather than only as bare text. %>
|
|
379
|
-
<%= render layout: "style/specimen",
|
|
380
|
-
locals: { klass: "in a status badge",
|
|
381
|
-
usage: %(<span class="badge">Shipped ) + erb.call("at_time_tag(release.shipped_at)") + "</span>" } do %>
|
|
382
|
-
<span class="badge" style="color: var(--color-success-ink); border-color: var(--color-success)">Shipped <%= at_time_tag(42.minutes.ago) %></span>
|
|
383
|
-
<% end %>
|
|
384
351
|
</div>
|
|
385
352
|
</section>
|
|
386
353
|
|
|
@@ -397,37 +364,18 @@
|
|
|
397
364
|
<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>
|
|
398
365
|
</div>
|
|
399
366
|
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
367
|
+
<%# This app's own loader. Its busy button and its overlay are the Loaders
|
|
368
|
+
section's Busy Button and Overlay; this group is the styles. %>
|
|
369
|
+
<%= render layout: "style/specimen", locals: { trick: "loader" } do %>
|
|
403
370
|
<%= studio_loader %>
|
|
404
371
|
<%= studio_loader size: 32 %>
|
|
405
372
|
<%= studio_loader size: 20 %>
|
|
406
373
|
<% end %>
|
|
407
374
|
|
|
408
|
-
<%= render layout: "style/specimen",
|
|
409
|
-
locals: { klass: "studio_loader_pending (inside a busy button)",
|
|
410
|
-
usage: ["<button class=\"btn btn-primary\">", " <span data-loader-idle>Save</span>", " #{erb.call('studio_loader_pending')}", "</button>"].join("\n") } do %>
|
|
411
|
-
<button type="button" class="btn btn-primary" aria-busy="true">
|
|
412
|
-
<%= studio_loader_pending %><span class="ml-2">Saving…</span>
|
|
413
|
-
</button>
|
|
414
|
-
<% end %>
|
|
415
|
-
|
|
416
|
-
<%= render layout: "style/specimen",
|
|
417
|
-
locals: { klass: "studio_loader_overlay (the page-load overlay)",
|
|
418
|
-
usage: ["#{erb.call('studio_loader_overlay')} (once, in the layout body)", 'window.StudioLoader.show("Caption") / .hide()'].join("\n") } do %>
|
|
419
|
-
<button type="button" class="btn btn-secondary" data-test="style-show-loader-overlay"
|
|
420
|
-
x-data x-on:click="window.StudioLoader && window.StudioLoader.show('Loading your page…'); setTimeout(() => window.StudioLoader && window.StudioLoader.hide(), 3000)">
|
|
421
|
-
Show the overlay for 3 seconds
|
|
422
|
-
</button>
|
|
423
|
-
<% end %>
|
|
424
|
-
|
|
425
375
|
<% Studio::Loader::STYLES.each do |style| %>
|
|
426
|
-
<%
|
|
427
|
-
<% unavailable = signature && !loader_art_styles.include?(style) %>
|
|
376
|
+
<% unavailable = Studio::Loader.art_style?(style) && !loader_art_styles.include?(style) %>
|
|
428
377
|
<%= render layout: "style/specimen",
|
|
429
|
-
locals: {
|
|
430
|
-
usage: %(config.loader = #{signature ? "{ style: :#{style}, art: \"/brand/loader-#{style}.svg\" }" : ":#{style}"}),
|
|
378
|
+
locals: { trick: "loader", variant: style.to_s,
|
|
431
379
|
disabled: unavailable, disabled_label: "needs this app's art file" } do %>
|
|
432
380
|
<%= studio_loader style: style, size: 72 %>
|
|
433
381
|
<%= studio_loader style: style, size: 28 %>
|
|
@@ -443,9 +391,8 @@
|
|
|
443
391
|
<p class="text-muted text-sm">Tune any knob inline with <code class="font-mono text-2xs">style="--knob: value"</code>.</p>
|
|
444
392
|
</div>
|
|
445
393
|
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
usage: %(<span class="spinner"></span>\n<span class="spinner" style="--spinner-size: 2rem"></span>) } do %>
|
|
394
|
+
<%# The bare classes under the Loaders section's helpers, tuned inline. %>
|
|
395
|
+
<%= render layout: "style/specimen", locals: { trick: "spinner", variant: "css" } do %>
|
|
449
396
|
<span class="spinner"></span>
|
|
450
397
|
<span class="spinner" style="--spinner-size: 1.6rem"></span>
|
|
451
398
|
<span class="spinner" style="--spinner-size: 2.4rem; --spinner-thickness: 3px"></span>
|
|
@@ -455,24 +402,18 @@
|
|
|
455
402
|
</button>
|
|
456
403
|
<% end %>
|
|
457
404
|
|
|
458
|
-
<%= render layout: "style/specimen",
|
|
459
|
-
locals: { klass: ".loading-dots (3 span children)",
|
|
460
|
-
usage: %(<span class="loading-dots"><span></span><span></span><span></span></span>) } do %>
|
|
405
|
+
<%= render layout: "style/specimen", locals: { trick: "typing_dots", variant: "css" } do %>
|
|
461
406
|
<span class="loading-dots"><span></span><span></span><span></span></span>
|
|
462
407
|
<span class="loading-dots" style="--loading-dots-size: 12px; --loading-dots-color: var(--color-cta)"><span></span><span></span><span></span></span>
|
|
463
408
|
<% end %>
|
|
464
409
|
|
|
465
|
-
<%= render layout: "style/specimen",
|
|
466
|
-
locals: { klass: ".ping .ping--dot (knob: --ping-color)",
|
|
467
|
-
usage: %(<span class="ping ping--dot"></span>) } do %>
|
|
410
|
+
<%= render layout: "style/specimen", locals: { trick: "ping" } do %>
|
|
468
411
|
<span class="ping ping--dot"></span>
|
|
469
412
|
<span class="ping ping--dot" style="--ping-color: var(--color-success)"></span>
|
|
470
413
|
<span class="ping ping--dot" style="--ping-color: var(--color-danger); --ping-size: 0.9rem"></span>
|
|
471
414
|
<% end %>
|
|
472
415
|
|
|
473
|
-
<%= render layout: "style/specimen",
|
|
474
|
-
locals: { klass: ".fade-edge-{t,b,l,r,x,y} (knob: --fade-edge-size)",
|
|
475
|
-
usage: %(<div class="fade-edge-y overflow-y-auto">...long content...</div>) } do %>
|
|
416
|
+
<%= render layout: "style/specimen", locals: { trick: "edge_fade" } do %>
|
|
476
417
|
<div class="fade-edge-y overflow-y-auto h-32 w-full max-w-xs bg-surface rounded-lg p-3 text-sm text-body space-y-2">
|
|
477
418
|
<p>Scroll me - the top and bottom edges dissolve.</p>
|
|
478
419
|
<p>The mask softens content near either end of the container.</p>
|
|
@@ -482,9 +423,7 @@
|
|
|
482
423
|
</div>
|
|
483
424
|
<% end %>
|
|
484
425
|
|
|
485
|
-
<%= render layout: "style/specimen",
|
|
486
|
-
locals: { klass: ".progress-meter (knob: --progress)",
|
|
487
|
-
usage: %(<div class="progress-meter" style="--progress: 62%">\n <div class="progress-meter-track"><span class="progress-meter-label">62%</span></div>\n <div class="progress-meter-fill"><span class="progress-meter-label">62%</span></div>\n</div>) } do %>
|
|
426
|
+
<%= render layout: "style/specimen", locals: { trick: "progress_bar", variant: "css" } do %>
|
|
488
427
|
<div class="w-full max-w-xs space-y-3">
|
|
489
428
|
<div class="progress-meter" style="--progress: 25%">
|
|
490
429
|
<div class="progress-meter-track"><span class="progress-meter-label">25%</span></div>
|
|
@@ -513,18 +452,14 @@
|
|
|
513
452
|
<h3 class="type-h4 text-heading">Effects</h3>
|
|
514
453
|
</div>
|
|
515
454
|
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
516
|
-
<%= render layout: "style/specimen",
|
|
517
|
-
locals: { klass: ".text-gradient (knobs: --text-gradient-from / -to / -angle)",
|
|
518
|
-
usage: %(<h2 class="text-gradient text-3xl font-extrabold">Level up</h2>) } do %>
|
|
455
|
+
<%= render layout: "style/specimen", locals: { trick: "gradient_text" } do %>
|
|
519
456
|
<div class="text-center space-y-2">
|
|
520
457
|
<p class="text-gradient text-3xl font-extrabold">Level up</p>
|
|
521
458
|
<p class="text-gradient text-xl font-bold" style="--text-gradient-from: var(--color-success); --text-gradient-to: var(--color-cta)">Season complete</p>
|
|
522
459
|
</div>
|
|
523
460
|
<% end %>
|
|
524
461
|
|
|
525
|
-
<%= render layout: "style/specimen",
|
|
526
|
-
locals: { klass: ".studio-glow / .studio-glow--pulse (knob: --studio-glow-color)",
|
|
527
|
-
usage: %(<button class="btn btn-primary studio-glow">Launch</button>) } do %>
|
|
462
|
+
<%= render layout: "style/specimen", locals: { trick: "glow" } do %>
|
|
528
463
|
<div class="flex flex-wrap items-center justify-center gap-6">
|
|
529
464
|
<button type="button" class="btn btn-primary studio-glow">Launch</button>
|
|
530
465
|
<span class="studio-glow studio-glow--pulse inline-flex h-12 w-12 items-center justify-center rounded-full text-white font-extrabold"
|
|
@@ -532,17 +467,13 @@
|
|
|
532
467
|
</div>
|
|
533
468
|
<% end %>
|
|
534
469
|
|
|
535
|
-
<%= render layout: "style/specimen",
|
|
536
|
-
locals: { klass: ".surface-glass (knobs: --surface-glass-opacity / -blur)",
|
|
537
|
-
usage: %(<div class="surface-glass rounded-xl p-4">...</div>) } do %>
|
|
470
|
+
<%= render layout: "style/specimen", locals: { trick: "frost" } do %>
|
|
538
471
|
<div class="conic-surface w-full max-w-xs rounded-2xl p-4" style="--conic-surface-duration: 22s">
|
|
539
472
|
<div class="surface-glass rounded-xl p-4 text-center text-sm text-heading">Frosted panel over a live backdrop</div>
|
|
540
473
|
</div>
|
|
541
474
|
<% end %>
|
|
542
475
|
|
|
543
|
-
<%= render layout: "style/specimen",
|
|
544
|
-
locals: { klass: ".conic-surface (knob: --conic-surface-duration)",
|
|
545
|
-
usage: %(<div class="conic-surface rounded-2xl p-8">...</div>) } do %>
|
|
476
|
+
<%= render layout: "style/specimen", locals: { trick: "conic_wash" } do %>
|
|
546
477
|
<div class="conic-surface w-full max-w-xs rounded-2xl p-8 text-center">
|
|
547
478
|
<p class="text-heading font-bold">Ambient wash</p>
|
|
548
479
|
<p class="text-body text-xs mt-1">Content stays crisp above the rotating gradient.</p>
|
|
@@ -565,21 +496,13 @@
|
|
|
565
496
|
<p class="text-muted text-sm max-w-2xl">Level pills, gated by <code class="font-mono text-2xs">Studio.feature?(:leveling)</code>.</p>
|
|
566
497
|
</div>
|
|
567
498
|
<div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
disabled: !leveling_on,
|
|
571
|
-
usage: %(<span class="level-badge level-badge-10">Level 10</span>) } do %>
|
|
499
|
+
<%# The ten tiers, then two of them in a row, the shape a caller puts them in. %>
|
|
500
|
+
<%= render layout: "style/specimen", locals: { trick: "level_badge", disabled: !leveling_on } do %>
|
|
572
501
|
<div class="flex flex-wrap items-center justify-center gap-2">
|
|
573
502
|
<% (1..10).each do |lvl| %>
|
|
574
503
|
<span class="level-badge level-badge-<%= lvl %>">Level <%= lvl %></span>
|
|
575
504
|
<% end %>
|
|
576
505
|
</div>
|
|
577
|
-
<% end %>
|
|
578
|
-
|
|
579
|
-
<%= render layout: "style/specimen",
|
|
580
|
-
locals: { klass: ".level-badge (in a chip context)",
|
|
581
|
-
disabled: !leveling_on,
|
|
582
|
-
usage: %(<span class="text-heading font-semibold text-sm">Earn a free entry</span>\n<span class="level-badge level-badge-8">Level 8</span>) } do %>
|
|
583
506
|
<div class="w-full max-w-xs space-y-2">
|
|
584
507
|
<div class="card flex items-center justify-between gap-3 px-3 py-2">
|
|
585
508
|
<span class="text-heading font-semibold text-sm">Earn a free entry</span>
|
|
@@ -592,10 +515,7 @@
|
|
|
592
515
|
</div>
|
|
593
516
|
<% end %>
|
|
594
517
|
|
|
595
|
-
<%= render layout: "style/specimen",
|
|
596
|
-
locals: { klass: ".level-up-pop (fires the pop + glow burst)",
|
|
597
|
-
disabled: !leveling_on,
|
|
598
|
-
usage: %(<span class="level-badge level-badge-10 level-up-pop">Level 10</span>) } do %>
|
|
518
|
+
<%= render layout: "style/specimen", locals: { trick: "level_pop", disabled: !leveling_on } do %>
|
|
599
519
|
<div x-data="{ pop: true }" class="flex flex-col items-center gap-3 py-2">
|
|
600
520
|
<span class="level-badge level-badge-10" :class="pop && 'level-up-pop'">Level 10</span>
|
|
601
521
|
<button type="button" class="btn btn-neutral btn-sm"
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
]
|
|
64
64
|
%>
|
|
65
65
|
<div class="max-w-6xl mx-auto px-4 pt-6 pb-16" data-type-ladder>
|
|
66
|
-
<%# The page's title and its section
|
|
66
|
+
<%# The page's title and its section anchors: studio/_page_header, the row of the
|
|
67
67
|
pinned stack (docs/PAGE_HEADER.md). It sits at --z-sticky, above --z-raised,
|
|
68
68
|
so the live hold button's portal bubbles scroll under it rather than over it.
|
|
69
69
|
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
label: "Style guide sections",
|
|
79
79
|
data: { studio_controller: "style-section-nav" },
|
|
80
80
|
tabs_data: { style_section_nav_target: "scroller" },
|
|
81
|
-
|
|
81
|
+
anchors: sections.each_with_index.map { |(anchor, label), index|
|
|
82
82
|
{ label: label, href: "##{anchor}", current: (index.zero? ? "true" : nil),
|
|
83
83
|
data: { style_section_nav_target: "link" } }
|
|
84
84
|
} %>
|
|
@@ -86,7 +86,8 @@
|
|
|
86
86
|
onDeepLink() { this.pick('Phantom'); },
|
|
87
87
|
// A REAL APP SWAPS BACK TO ITS OWN SIGN-IN CARD HERE, reading props.backTo
|
|
88
88
|
// — that is the partial's contract and solana-studio still honours it. This
|
|
89
|
-
// GUIDE
|
|
89
|
+
// GUIDE does not: its sign-in card (ds-sign-in, the engine's standard card in
|
|
90
|
+
// demo mode) carries no wallet button, and the 'auth' branch that
|
|
90
91
|
// used to live here pointed at style/modals/_auth, the retired
|
|
91
92
|
// turf-monster mirror. An unregistered swap target renders an EMPTY card
|
|
92
93
|
// silently, so the branch was removed rather than left aimed at nothing.
|