sliderpro-agentic-skills-etch 0.1.2 → 0.2.1
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.
- package/bin/cli.js +151 -41
- package/lib/skills-source.js +65 -0
- package/package.json +2 -1
- package/skills-package/components/dwc-slider-pagination.md +38 -0
- package/skills-package/manifest.json +47 -0
- package/skills-package/slider-skills/slider-pro-skills-build.md +438 -0
- package/skills-package/slider-skills/slider-pro-skills-reference.md +8 -3
- package/skills-package/slider-skills/slider-pro-skills.md +302 -308
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
---
|
|
2
|
+
icon: hammer
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# AI Skills Reference: Building
|
|
6
|
+
|
|
7
|
+
Read this when you are building a slider, a card deck or a template **from scratch**. For changing
|
|
8
|
+
a prop, adjusting CSS or fixing an existing build, `slider-pro-skills.md` (same folder) is enough.
|
|
9
|
+
|
|
10
|
+
Everything in `slider-pro-skills.md` still applies here: the native-first gate, the one-extra-brace
|
|
11
|
+
rule, the class mechanisms, the CSS conventions and the gotchas. This file adds only what building
|
|
12
|
+
from nothing needs. Read it after that file, never instead of it.
|
|
13
|
+
|
|
14
|
+
***
|
|
15
|
+
|
|
16
|
+
## Block JSON
|
|
17
|
+
|
|
18
|
+
### Component instances are the unit of work
|
|
19
|
+
|
|
20
|
+
A Slider Pro build is a tree of `etch/component` blocks, each naming a component and carrying a
|
|
21
|
+
flat `attributes` map of prop values. **Two different shapes describe the same tree, and mixing
|
|
22
|
+
them up is the first thing that goes wrong in a session.**
|
|
23
|
+
|
|
24
|
+
**The live shape**, which `etch.blocks.getTree()` returns and `etch.blocks.create()` accepts:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"type": "etch/component",
|
|
29
|
+
"version": 1,
|
|
30
|
+
"context": {},
|
|
31
|
+
"children": [],
|
|
32
|
+
"componentId": 6455,
|
|
33
|
+
"attributes": { "wrapperHeight": "350px", "spaceBetweenSliders": "50px" }
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`version`, `context` and `children` are required. Block ids are strings (`"ot2wnef"`), not numbers.
|
|
38
|
+
|
|
39
|
+
**The export shape**, which you only ever see inside a premade template `.json` file, uses
|
|
40
|
+
`blockName`, nests under `attrs`, and calls the component id `ref`:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{ "blockName": "etch/component", "attrs": { "ref": 6455, "attributes": {} }, "innerBlocks": [] }
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Never pass the export shape to `create()`. It is rejected with
|
|
47
|
+
`Invalid block JSON: expected string, received undefined` at `path: ["type"]`. To put a template on
|
|
48
|
+
the page, hand its whole `gutenbergBlock` to `etch.blocks.pasteAsync()` instead.
|
|
49
|
+
|
|
50
|
+
### Children go in a slot, never directly on the component
|
|
51
|
+
|
|
52
|
+
A component's children must be wrapped in an `etch/slot-content` node naming the slot they fill.
|
|
53
|
+
Putting a Slider straight inside a Wrapper's `children` renders nothing.
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{ "type": "etch/slot-content", "version": 1, "context": {}, "children": [], "slotName": "Slides" }
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
| Component | Slots |
|
|
60
|
+
| --- | --- |
|
|
61
|
+
| DWC Slider Wrapper | `Sliders_and_Controls` |
|
|
62
|
+
| DWC Slider | `Top__Controls`, `Slides`, `Bottom__Controls` |
|
|
63
|
+
| DWC Slide | `Content` |
|
|
64
|
+
| DWC Slider Pagination | `PaginationButtons` |
|
|
65
|
+
| DWC Slider Nav Button | `Nav_Btn_Content` |
|
|
66
|
+
| DWC Slider Progress, DWC Slider Play-Pause | none |
|
|
67
|
+
|
|
68
|
+
Controls are siblings of the Slider inside `Sliders_and_Controls`, or children of the Slider's own
|
|
69
|
+
`Top__Controls` / `Bottom__Controls`. Both work, because controls find their own slider.
|
|
70
|
+
|
|
71
|
+
***
|
|
72
|
+
|
|
73
|
+
## Design recipes
|
|
74
|
+
|
|
75
|
+
Each of these is a shipped template reduced to the settings that produce it.
|
|
76
|
+
|
|
77
|
+
### Cover-flow carousel (Slider Flow): no script, no CSS
|
|
78
|
+
|
|
79
|
+
The whole effect is the `slides.*` group, which applies transforms to inactive slides and separate
|
|
80
|
+
`-Active` values to the current one.
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
setGroup(sliderId, 'layout', { slidesPerPage: '3 lg:1', gapBetweenSlides: '20px',
|
|
84
|
+
sliderEdgeOffset: 'lg:20% md:18%' });
|
|
85
|
+
setGroup(sliderId, 'motion', { focus: 'center', loop: '{true}', speed: '800',
|
|
86
|
+
updateOnMove: '{true}' });
|
|
87
|
+
setGroup(sliderId, 'slides', { perspective: '950px', opacity: '0.3', scale: '0.9',
|
|
88
|
+
borderRadius: '1rem',
|
|
89
|
+
transition: 'transform 0.5s ease, opacity 0.5s ease' });
|
|
90
|
+
setGroup(sliderId, 'dimensions', { sliderHeight: '350px md:300px sm:180px' });
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
`motion.focus: 'center'` is what puts the active slide in the middle. The neighbours are dimmed and
|
|
94
|
+
shrunk purely by `slides.opacity` and `slides.scale`, which apply to inactive slides only.
|
|
95
|
+
|
|
96
|
+
For an angled flip, add `slides.rotateY` and turn on `slides.flipNextRotateY` so the slide on the
|
|
97
|
+
other side mirrors the angle instead of repeating it. Same for `translateX` with
|
|
98
|
+
`flipNextTranslateX`.
|
|
99
|
+
|
|
100
|
+
### Logo marquee (Slider Marquee): no script
|
|
101
|
+
|
|
102
|
+
Two Sliders in one Wrapper, running in opposite directions.
|
|
103
|
+
|
|
104
|
+
```js
|
|
105
|
+
// both rows
|
|
106
|
+
setGroup(sliderId, 'dimensions', { slideAutoWidth: '{true}' }); // slides size to content
|
|
107
|
+
setGroup(sliderId, 'motion', { loop: '{true}' });
|
|
108
|
+
setGroup(sliderId, 'autoscroll', { infiniteScroll: '{true}', scrollSpeed: '1',
|
|
109
|
+
pauseOnHover: '{false}', pauseOnFocus: '{false}' });
|
|
110
|
+
setGroup(sliderId, 'layout', { gapBetweenSlides: '2.4rem' });
|
|
111
|
+
|
|
112
|
+
// second row only
|
|
113
|
+
setGroup(secondId, 'sliderSetup', { slldeDirection: 'rtl' }); // note the spelling
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Edge Fade goes on the **Wrapper** (`props.edgeFade`), not the sliders, so both rows fade as one
|
|
117
|
+
unit. `props.pauseSlidersOnHover` on the Wrapper pauses both rows together.
|
|
118
|
+
|
|
119
|
+
Infinite Scroll needs the Auto-Scroll extension enabled in admin settings, which is the default.
|
|
120
|
+
|
|
121
|
+
### Synced elements (Team, Stack)
|
|
122
|
+
|
|
123
|
+
Driving arbitrary elements on the page from the slider is one prop.
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
setGroup(sliderId, 'sliderSetup', { syncCustomElement: '.timeline-node',
|
|
127
|
+
syncCustomElementNav: '{true}' });
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The plugin then moves `is-active`, `is-prev` and `is-next` across those elements as the slider
|
|
131
|
+
moves, and with nav on, clicking one jumps the slider to it. **You write the CSS for the three
|
|
132
|
+
states.** The elements do not need to be inside the slider and do not need to match the slide
|
|
133
|
+
count.
|
|
134
|
+
|
|
135
|
+
The selector takes a comma-separated list, so one slider can drive several groups at once. Team
|
|
136
|
+
uses `'.slider-team__sync, .slider-team__sync-heading'` to move a portrait and a heading together.
|
|
137
|
+
|
|
138
|
+
Grep `## 3. Sync Custom Element` in the reference for multiple selectors and the overlap caveat.
|
|
139
|
+
|
|
140
|
+
### Main and thumbnails in one wrapper (Zeon)
|
|
141
|
+
|
|
142
|
+
A full-bleed hero with a thumbnail strip needs no sync prop at all, just roles:
|
|
143
|
+
|
|
144
|
+
```js
|
|
145
|
+
setGroup(mainId, 'sliderSetup', { sliderRole: 'main', transitionType: 'Fade' });
|
|
146
|
+
setGroup(thumbId, 'sliderSetup', { sliderRole: 'thumbnails', transitionType: 'Loop' });
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Both inside the same Wrapper and they pair automatically. A Wrapper can hold more than one
|
|
150
|
+
thumbnail slider, which is how Zeon runs a background layer and a strip off the same main.
|
|
151
|
+
|
|
152
|
+
### Animating slide content: no script
|
|
153
|
+
|
|
154
|
+
Splide puts `is-active` on the current `.splide__slide`, which is all you need to animate anything
|
|
155
|
+
inside it. Give the element its resting state, then reveal it from the active slide:
|
|
156
|
+
|
|
157
|
+
```css
|
|
158
|
+
/* in .slider-<name>__figure's own entry */
|
|
159
|
+
opacity: 0;
|
|
160
|
+
transform: translateY(30px);
|
|
161
|
+
transition: opacity 820ms cubic-bezier(0.22, 0.68, 0.24, 1),
|
|
162
|
+
transform 820ms cubic-bezier(0.22, 0.68, 0.24, 1);
|
|
163
|
+
|
|
164
|
+
.splide__slide.is-active & { opacity: 1; transform: translateY(0); }
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Stagger a caption by putting the same pair on each line with a growing `transition-delay`
|
|
168
|
+
(thumbnail 140ms, status 200ms, title 260ms, body 320ms) so the block assembles rather than
|
|
169
|
+
appearing at once.
|
|
170
|
+
|
|
171
|
+
Two things that bite:
|
|
172
|
+
|
|
173
|
+
* **Keep layout transforms in the animated transform.** An element centred with
|
|
174
|
+
`translateX(-50%)` must animate as `translateX(-50%) translateY(30px)`, or it jumps sideways on
|
|
175
|
+
every transition. Where a breakpoint drops the centring, restate the transform there too.
|
|
176
|
+
* **A slow drift on the backdrop** (`scale(1.06)` to `scale(1)` over several seconds) makes a
|
|
177
|
+
change read as movement rather than a cut. Put it on the image, not the slide, so it does not
|
|
178
|
+
fight the transition.
|
|
179
|
+
|
|
180
|
+
This works with the Fade transition, which crossfades the slides underneath while the contents
|
|
181
|
+
move independently.
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
### Numbered steps beside a slider: no script
|
|
185
|
+
|
|
186
|
+
A vertical list of steps next to an image, where the number highlights as the image changes, uses
|
|
187
|
+
**two mechanisms on one slider**, split by whether the content repeats:
|
|
188
|
+
|
|
189
|
+
* **What repeats goes in the pagination.** The numbered markers are identical except for the digit,
|
|
190
|
+
which is exactly what `customPaginationMode: 'Template'` does: put one item in the
|
|
191
|
+
`PaginationButtons` slot with a literal `1` in it and the engine clones it per slide, substituting
|
|
192
|
+
the number. Add a slide later and the marker appears by itself. The active marker gets
|
|
193
|
+
`is-active`, and each is a real button, so clicking navigates.
|
|
194
|
+
* **What differs goes in a synced set.** Titles and descriptions are per step, and a clone cannot
|
|
195
|
+
carry them, so those are your own elements driven by
|
|
196
|
+
`sliderSetup.syncCustomElement` with `syncCustomElementNav` on.
|
|
197
|
+
|
|
198
|
+
```js
|
|
199
|
+
setGroup(sliderId, 'sliderSetup', { transitionType: 'Fade',
|
|
200
|
+
syncCustomElement: '.step',
|
|
201
|
+
syncCustomElementNav: '{true}' });
|
|
202
|
+
etch.blocks.setAttribute(pagId, 'customPaginationMode', 'Template');
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Do not force one mechanism to do both. Template mode cannot give each clone its own text, and
|
|
206
|
+
faking it with a script to fill the clones is the kind of work the native features already cover.
|
|
207
|
+
|
|
208
|
+
**Give the template two levels.** The cloned root has to stretch to its row so it can carry the
|
|
209
|
+
connecting line, while the circle stays a fixed square at the top:
|
|
210
|
+
|
|
211
|
+
```
|
|
212
|
+
.mark <- the cloned root; receives is-active, fills the row, draws the line
|
|
213
|
+
.mark-dot <- the circle and the number
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
State travels from the root to the dot through custom properties, so no descendant selector has to
|
|
217
|
+
fight the plugin for the same box:
|
|
218
|
+
|
|
219
|
+
```css
|
|
220
|
+
.mark { --dot-bg: #e5e7eb; --dot-fg: #4b5563; }
|
|
221
|
+
.mark.is-active { --dot-bg: #45bf55; --dot-fg: #fff; }
|
|
222
|
+
.mark-dot { background: var(--dot-bg); color: var(--dot-fg); }
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**Let the row count come from the slides, not the stylesheet.** Put both columns in one parent row
|
|
226
|
+
so they are the same height, then give each `grid-auto-rows`. Marker *n* then lines up with card
|
|
227
|
+
*n* whatever the copy length, and adding a slide needs no CSS change:
|
|
228
|
+
|
|
229
|
+
```css
|
|
230
|
+
.rail { display: grid; grid-template-columns: auto 1fr; gap: 0 to-rem(24px); }
|
|
231
|
+
.marks,
|
|
232
|
+
.steps { display: grid; grid-auto-rows: minmax(0, 1fr); }
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Hang the connecting line off each marker, not the container.** A single line drawn on the
|
|
236
|
+
container has to be told where to stop, which means hardcoding the count. Per marker it is
|
|
237
|
+
self-terminating:
|
|
238
|
+
|
|
239
|
+
```css
|
|
240
|
+
.mark:not(:last-child)::after {
|
|
241
|
+
content: ""; position: absolute;
|
|
242
|
+
inset-inline-start: 50%; transform: translateX(-50%);
|
|
243
|
+
inset-block-start: calc(var(--dot-top) + var(--dot-size));
|
|
244
|
+
inset-block-end: 0;
|
|
245
|
+
inline-size: to-rem(2px); background: #e5e7eb;
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
The last marker gets no line for free, at any slide count.
|
|
250
|
+
|
|
251
|
+
Three traps, all silent:
|
|
252
|
+
|
|
253
|
+
* **Use `minmax(0, 1fr)`, not `1fr`.** `1fr` means `minmax(auto, 1fr)`, so a longer description
|
|
254
|
+
expands its own row and the two columns drift apart by a few pixels per row.
|
|
255
|
+
* **Match `gap` on both columns.** The pagination carries a flex `gap` of its own, which shortens
|
|
256
|
+
every row on that side only. Set it explicitly, including `0`.
|
|
257
|
+
* **The pagination will not accept your layout without a co-class.** It sets `display: flex` and
|
|
258
|
+
`block-size: fit-content` on `.dwc-slider-pagination-wrapper`, which ties with a single class of
|
|
259
|
+
yours and wins on order. Write the override **inside the entry its class prop already points at**,
|
|
260
|
+
so it stays findable from the element:
|
|
261
|
+
|
|
262
|
+
```css
|
|
263
|
+
/* the .marks entry itself */
|
|
264
|
+
position: relative;
|
|
265
|
+
|
|
266
|
+
&.dwc-slider-pagination-wrapper {
|
|
267
|
+
display: grid; grid-auto-rows: minmax(0, 1fr); block-size: 100%; gap: 0;
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
### Carousel on mobile, grid on desktop
|
|
273
|
+
|
|
274
|
+
```js
|
|
275
|
+
setGroup(sliderId, 'layout', { layoutMode: 'static md:slider', gridColumns: '3 sm:1' });
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Above `md` it is a plain CSS grid; at `md` and below it is a real carousel. The slider is built and
|
|
279
|
+
torn down live as the viewport crosses the breakpoint. **Controls are hidden in static mode**,
|
|
280
|
+
because there is no live slider to drive.
|
|
281
|
+
|
|
282
|
+
### Card decks with no slider (Deck, Fall): the one place script is allowed
|
|
283
|
+
|
|
284
|
+
Structure: a Wrapper with `sliderlessSync.customElement` pointing at your cards, no Slider inside,
|
|
285
|
+
cards nested three deep as `card > lift > content` so the 3-D survives.
|
|
286
|
+
|
|
287
|
+
```js
|
|
288
|
+
setGroup(wrapperId, 'sliderlessSync', {
|
|
289
|
+
customElement: '.deck-card', customElementNav: '{true}',
|
|
290
|
+
loop: '{true}', autoplay: '{true}', autoplayInterval: '4000'
|
|
291
|
+
});
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
CSS handles the front card and its two neighbours from `is-active` / `is-prev` / `is-next`. What
|
|
295
|
+
CSS cannot do is know that a given card is *three* back, so the rows behind, the z-index ladder and
|
|
296
|
+
the click targets need a script. That is case 1 of the native-first gate.
|
|
297
|
+
|
|
298
|
+
The contract between that script and the CSS is one attribute, **`data-pos`**, which the script
|
|
299
|
+
derives from `is-active` and writes on every card. It is a **signed** offset from the active card,
|
|
300
|
+
so the CSS can fan the left and right sides in opposite directions:
|
|
301
|
+
|
|
302
|
+
```css
|
|
303
|
+
.deck-stack-featured &:is([data-pos='2'], [data-pos='-2']) { /* ring 2, both sides */ }
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
The stack container carries **`data-tiers`**, how many rings are actually in play. It is computed,
|
|
307
|
+
not fixed: a looping deck needs spare cards to hide the wrap behind, so the script reduces the
|
|
308
|
+
tiers when there are too few cards.
|
|
309
|
+
|
|
310
|
+
Writing an unsigned rank, or naming the attribute anything else, produces a flat pile: the rules
|
|
311
|
+
above simply never match. The shipped script does this:
|
|
312
|
+
|
|
313
|
+
```js
|
|
314
|
+
// 1. make a custom property interpolable, so a blur can animate (case 2)
|
|
315
|
+
CSS.registerProperty({ name: '--stack-focus', syntax: '<number>',
|
|
316
|
+
inherits: true, initialValue: '0' });
|
|
317
|
+
|
|
318
|
+
// 2. how many rings this deck can afford, given looping and the card count
|
|
319
|
+
var mayLoop = wrap.dataset.loop === 'true' && total >= MIN_LOOP;
|
|
320
|
+
stack.dataset.tiers = mayLoop ? Math.min(MAX_TIERS, Math.floor((total - 3) / 2)) : MAX_TIERS;
|
|
321
|
+
|
|
322
|
+
// 3. per card: SIGNED offset from the active card. On a looping deck the offset
|
|
323
|
+
// wraps, so the far end of the list reads as the near side, not as distance 8.
|
|
324
|
+
var d = n - active;
|
|
325
|
+
if (mayLoop) {
|
|
326
|
+
if (d > total / 2) d -= total;
|
|
327
|
+
if (d < -total / 2) d += total;
|
|
328
|
+
}
|
|
329
|
+
card.dataset.pos = d;
|
|
330
|
+
|
|
331
|
+
// 4. hit-testing: stack order by distance, and clicks off beyond the clickable rings
|
|
332
|
+
var rank = Math.abs(d);
|
|
333
|
+
card.style.zIndex = String(total - rank);
|
|
334
|
+
lift.style.pointerEvents = rank <= NAV_RINGS ? 'auto' : 'none';
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Do not clamp `data-pos` to the tier count. Cards past the deepest ring should match no rule and
|
|
338
|
+
stay stacked at the back; clamping piles them onto the last visible ring instead.
|
|
339
|
+
|
|
340
|
+
Both decks also set `--i` on each line of card content so the CSS can stagger it:
|
|
341
|
+
|
|
342
|
+
```js
|
|
343
|
+
card.querySelectorAll('.deck-card-featured__content > *')
|
|
344
|
+
.forEach(function (line, j) { line.style.setProperty('--i', j); });
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
Step 4 is Deck only. **Fall's script is much smaller**: it sets `--i` and a signed `data-pos`, and
|
|
348
|
+
nothing else. Fall never loops, so there is no wrap to fold and no z-index ladder to maintain,
|
|
349
|
+
because the cards fall past each other rather than fanning around a front card.
|
|
350
|
+
|
|
351
|
+
Recompute on every change: watch the stack with a `MutationObserver` filtered to `class`, since
|
|
352
|
+
`is-active` moving is the only signal you get.
|
|
353
|
+
|
|
354
|
+
### Where a script goes
|
|
355
|
+
|
|
356
|
+
**Not in a `<script>` element.** Etch silently drops one from the render, so the page looks right
|
|
357
|
+
in the builder and ships with no behaviour at all.
|
|
358
|
+
|
|
359
|
+
Every block has an optional `script` field (`EtchBlockScript`), which is Etch's JavaScript code
|
|
360
|
+
block. Put the code on the **component instance that owns the markup**, which for a card stack is
|
|
361
|
+
the Wrapper:
|
|
362
|
+
|
|
363
|
+
```js
|
|
364
|
+
const wrapperId = etch.blocks.create({
|
|
365
|
+
type: 'etch/component', version: 1, context: {}, children: [ /* ... */ ],
|
|
366
|
+
componentId: C['DWC Slider Wrapper'], attributes: {},
|
|
367
|
+
script: { code: "(function () { /* ... */ })();" }
|
|
368
|
+
});
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
`code` is **plain source** over the API. The base64 you see in a premade template `.json` is only
|
|
372
|
+
how the export serialises it; do not encode it yourself.
|
|
373
|
+
|
|
374
|
+
Etch renders it as `<script type="module" defer>`, so it runs after parsing and top-level `await`
|
|
375
|
+
is available. Read it back with `etch.blocks.getJson(id).script`.
|
|
376
|
+
|
|
377
|
+
Component scripts are also the reason a card deck keeps working: the bridge leaves Sync Without
|
|
378
|
+
Slider wrappers unreconstructed precisely so an author's script keeps observing the original nodes.
|
|
379
|
+
|
|
380
|
+
Card counts for a looping deck: three are always visible (front plus two), and a loop needs a
|
|
381
|
+
hidden slot at each end, so **five is the minimum** and nine gives the full three-row fan. Fall
|
|
382
|
+
never loops, so it has no minimum.
|
|
383
|
+
|
|
384
|
+
***
|
|
385
|
+
|
|
386
|
+
## Block builders
|
|
387
|
+
|
|
388
|
+
These extend the four helpers in `slider-pro-skills.md`. Include both sets.
|
|
389
|
+
|
|
390
|
+
```js
|
|
391
|
+
function node(extra, children) {
|
|
392
|
+
return Object.assign({ version: 1, context: {}, children: children || [] }, extra);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function component(componentId, attributes, children) {
|
|
396
|
+
return node({ type: 'etch/component', componentId: componentId,
|
|
397
|
+
attributes: attributes || {} }, children);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function slot(slotName, children) {
|
|
401
|
+
return node({ type: 'etch/slot-content', slotName: slotName }, children);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function el(tag, attributes, children) {
|
|
405
|
+
return node({ type: 'etch/element', tag: tag, attributes: attributes || {} }, children);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function text(t) { return node({ type: 'etch/text', text: t }); }
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### Build a wrapper with N slides
|
|
412
|
+
|
|
413
|
+
```js
|
|
414
|
+
const C = comps();
|
|
415
|
+
const slides = [];
|
|
416
|
+
for (let i = 0; i < 5; i++) {
|
|
417
|
+
slides.push(component(C['DWC Slide'], {}, [
|
|
418
|
+
slot('Content', [ el('div', {}, [ text('Slide ' + (i + 1)) ]) ])
|
|
419
|
+
]));
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const id = etch.blocks.create(
|
|
423
|
+
component(C['DWC Slider Wrapper'], {}, [
|
|
424
|
+
slot('Sliders_and_Controls', [
|
|
425
|
+
component(C['DWC Slider'], {}, [ slot('Slides', slides) ]),
|
|
426
|
+
component(C['DWC Slider Nav Button'], {}),
|
|
427
|
+
component(C['DWC Slider Pagination'], {})
|
|
428
|
+
])
|
|
429
|
+
])
|
|
430
|
+
);
|
|
431
|
+
return id;
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
An empty Slide has no content and so no height. Give slides something to size before wondering why
|
|
435
|
+
the slider is a flat line.
|
|
436
|
+
|
|
437
|
+
Build **one** first, screenshot it, then scale to the full count. Do not generate twenty slides
|
|
438
|
+
before you have seen one render.
|
|
@@ -189,6 +189,7 @@ matches the components installed on the site. Do not hand-edit this section.
|
|
|
189
189
|
| Background Blur | `props.style.backgroundBlur` | `style` | `8px` | Shown when `props.progressType === "counter" \|\| (props.progressType === "circular" && props.circularCounter)` |
|
|
190
190
|
| Progress Fill Color | `props.style.progressFillColor` | `style` | `#ff4d6a` | Shown when `props.progressType === "bar" \|\| props.progressType === "circular"` |
|
|
191
191
|
| Progress Fill Color 2 | `props.style.progressFillColor2` | `style` | `#ff8fa3` | Shown when `props.progressType === "bar"` |
|
|
192
|
+
| Progress Height | `props.style.progressHeight` | `style` | `3px` | Shown when `props.progressType === "bar"` |
|
|
192
193
|
| Progress Track Color | `props.style.progressTrackColor` | `style` | `rgba(255, 255, 255, 0.1)` | Shown when `props.progressType === "bar" \|\| props.progressType === "circular"` |
|
|
193
194
|
| Background Color | `props.style.backgroundColor` | `style` | `rgba(0, 0, 0, 0.4)` | Shown when `props.progressType === "counter"` |
|
|
194
195
|
| Border Radius | `props.style.borderRadius` | `style` | `20px` | Shown when `props.progressType === "counter"` |
|
|
@@ -280,9 +281,13 @@ Arrows, dots, pagination, progress, play/pause and the lightbox are styled with
|
|
|
280
281
|
properties, not props. The template class is **`.slider-navigation-vars`**, holding 94 variables.
|
|
281
282
|
|
|
282
283
|
**Per-slider styling.** Each Slider's **Slider Class** prop (`props.sliderClass`) names the class
|
|
283
|
-
its variables are read from, so two sliders on one page style independently. The plugin
|
|
284
|
-
|
|
285
|
-
|
|
284
|
+
its variables are read from, so two sliders on one page style independently. The plugin gives each
|
|
285
|
+
slider its own minted `.dwc-slider-vars-*` copy of that class, and mints a fresh one whenever a
|
|
286
|
+
slider is duplicated. So never style directly on `.slider-navigation-vars`, and never write that
|
|
287
|
+
shared class into the prop: doing so stops the minting and makes every duplicate share one set of
|
|
288
|
+
variables. Add your own class (e.g. `.my-slider`) alongside the minted one and style that. Classes
|
|
289
|
+
you add yourself are never touched. See "`sliderClass` carries a minted per-instance class" in
|
|
290
|
+
[slider-pro-skills.md](slider-pro-skills.md).
|
|
286
291
|
|
|
287
292
|
### Arrows
|
|
288
293
|
|