@hagicode/hagilight-core 0.6.1-dev.75.1.60c438f → 0.6.2-dev.77.1.2359fea

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.
@@ -16,21 +16,36 @@ const { fallback, locale = Astro.currentLocale ?? 'en' } = Astro.props;
16
16
  hidden
17
17
  >
18
18
  <section class="hagilight-promoto__shell" data-promoto-shell hidden aria-label="Featured promotion">
19
- <button
20
- type="button"
21
- class="hagilight-promoto__dismiss"
22
- data-promoto-dismiss
23
- aria-label="Dismiss promotion"
24
- >&times;</button>
25
19
  <div class="hagilight-promoto__viewport">
26
20
  <div class="hagilight-promoto__track" data-promoto-track></div>
27
21
  </div>
28
22
  <nav class="hagilight-promoto__controls" data-promoto-controls aria-label="Promotion controls" hidden>
29
- <button type="button" class="hagilight-promoto__control" data-promoto-previous aria-label="Previous promotion">‹</button>
23
+ <button type="button" class="hagilight-promoto__control" data-promoto-previous aria-label="Previous promotion">
24
+ <svg class="hagilight-promoto__icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M15 6l-6 6 6 6" /></svg>
25
+ </button>
30
26
  <span class="hagilight-promoto__count" data-promoto-count aria-label="Promotion progress"></span>
31
- <button type="button" class="hagilight-promoto__control" data-promoto-next aria-label="Next promotion">›</button>
32
- <button type="button" class="hagilight-promoto__control" data-promoto-pause aria-pressed="false">Pause</button>
27
+ <button type="button" class="hagilight-promoto__control" data-promoto-next aria-label="Next promotion">
28
+ <svg class="hagilight-promoto__icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6l6 6-6 6" /></svg>
29
+ </button>
30
+ <button
31
+ type="button"
32
+ class="hagilight-promoto__control"
33
+ data-promoto-pause
34
+ aria-pressed="false"
35
+ aria-label="Pause automatic promotion rotation"
36
+ >
37
+ <svg class="hagilight-promoto__icon hagilight-promoto__icon--pause" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M9 6v12M15 6v12" /></svg>
38
+ <svg class="hagilight-promoto__icon hagilight-promoto__icon--play" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M8 5.5v13l11-6.5z" /></svg>
39
+ </button>
33
40
  </nav>
41
+ <button
42
+ type="button"
43
+ class="hagilight-promoto__dismiss"
44
+ data-promoto-dismiss
45
+ aria-label="Dismiss promotion"
46
+ >
47
+ <svg class="hagilight-promoto__icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M6.5 6.5l11 11M17.5 6.5l-11 11" /></svg>
48
+ </button>
34
49
  <span class="hagilight-promoto__status" data-promoto-status aria-live="polite"></span>
35
50
  </section>
36
51
  </hagilight-promoto-banner>
@@ -42,8 +57,18 @@ const { fallback, locale = Astro.currentLocale ?? 'en' } = Astro.props;
42
57
  </script>
43
58
 
44
59
  <style is:global>
60
+ /*
61
+ * Layout model: the card leads with the campaign (media, title, description,
62
+ * call to action). Pager and dismiss are one quiet toolbar and never get their
63
+ * own row. Wide: a single row with the toolbar at the trailing edge, vertically
64
+ * centered. Narrow (portrait phones): the dismiss sits in the top corner and the
65
+ * pager shares the call-to-action row. Short landscape viewports keep the single
66
+ * row and drop the thumbnail so the card stays a thin strip.
67
+ */
45
68
  .hagilight-promoto {
46
69
  display: block;
70
+ --promoto-control-size: 1.75rem;
71
+ --promoto-muted: var(--sl-color-gray-2, #4b5563);
47
72
  }
48
73
 
49
74
  .hagilight-promoto[hidden],
@@ -57,13 +82,16 @@ const { fallback, locale = Astro.currentLocale ?? 'en' } = Astro.props;
57
82
  z-index: 40;
58
83
  inset-inline: 0;
59
84
  bottom: max(1rem, env(safe-area-inset-bottom));
60
- width: min(72rem, calc(100vw - 1rem));
85
+ display: grid;
86
+ grid-template-columns: minmax(0, 1fr) auto auto;
87
+ align-items: center;
88
+ width: min(72rem, calc(100vw - 1rem - 2 * max(env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px))));
61
89
  margin-inline: auto;
62
90
  overflow: hidden;
63
91
  border: 1px solid color-mix(in srgb, var(--sl-color-accent, #2563eb) 24%, var(--sl-color-gray-5, #cbd5e1));
64
- border-radius: 1.25rem;
92
+ border-radius: 1rem;
65
93
  background: color-mix(in srgb, var(--sl-color-bg, #fff) 96%, var(--sl-color-accent-low, #dbeafe) 4%);
66
- box-shadow: 0 18px 48px color-mix(in srgb, var(--sl-color-black, #000) 16%, transparent);
94
+ box-shadow: 0 12px 32px color-mix(in srgb, var(--sl-color-black, #000) 14%, transparent);
67
95
  color: var(--sl-color-text, #1f2937);
68
96
  opacity: 1;
69
97
  visibility: visible;
@@ -78,6 +106,9 @@ const { fallback, locale = Astro.currentLocale ?? 'en' } = Astro.props;
78
106
  }
79
107
 
80
108
  .hagilight-promoto__viewport {
109
+ grid-column: 1;
110
+ grid-row: 1;
111
+ min-width: 0;
81
112
  overflow: hidden;
82
113
  }
83
114
 
@@ -88,14 +119,22 @@ const { fallback, locale = Astro.currentLocale ?? 'en' } = Astro.props;
88
119
  transition: transform 0.34s ease;
89
120
  }
90
121
 
122
+ /* Slide: [media] [copy] [cta] in one row. */
91
123
  .hagilight-promoto__slide {
92
124
  display: grid;
93
- grid-template-columns: minmax(0, 1fr) auto auto;
125
+ grid-template-columns: auto minmax(0, 1fr) auto;
126
+ grid-template-areas: 'media copy cta';
94
127
  align-items: center;
95
- gap: 1.25rem;
128
+ column-gap: 1rem;
96
129
  width: 100%;
97
130
  min-width: 100%;
98
- padding: 1.25rem 4rem 1.25rem 1.25rem;
131
+ padding: 0.875rem 1rem 0.875rem 0.875rem;
132
+ }
133
+
134
+ .hagilight-promoto__slide:not([data-has-image='true']) {
135
+ grid-template-columns: minmax(0, 1fr) auto;
136
+ grid-template-areas: 'copy cta';
137
+ padding-inline-start: 1.25rem;
99
138
  }
100
139
 
101
140
  .hagilight-promoto__slide[aria-hidden='true'] {
@@ -103,10 +142,9 @@ const { fallback, locale = Astro.currentLocale ?? 'en' } = Astro.props;
103
142
  }
104
143
 
105
144
  .hagilight-promoto__copy {
145
+ grid-area: copy;
106
146
  display: grid;
107
- grid-template-columns: 1fr;
108
- align-items: start;
109
- gap: 0.4rem;
147
+ gap: 0.25rem;
110
148
  min-width: 0;
111
149
  }
112
150
 
@@ -115,28 +153,36 @@ const { fallback, locale = Astro.currentLocale ?? 'en' } = Astro.props;
115
153
  display: -webkit-box;
116
154
  overflow: hidden;
117
155
  margin: 0;
118
- color: var(--sl-color-text-high, var(--sl-color-gray-1, #111827));
119
- font-size: var(--sl-text-sm);
120
- font-weight: 600;
121
- line-height: 1.35;
122
156
  -webkit-box-orient: vertical;
123
157
  -webkit-line-clamp: 2;
124
158
  }
125
159
 
160
+ .hagilight-promoto__title {
161
+ color: var(--sl-color-text-high, var(--sl-color-gray-1, #111827));
162
+ font-size: var(--sl-text-base, 1rem);
163
+ font-weight: 700;
164
+ line-height: 1.3;
165
+ text-wrap: balance;
166
+ }
167
+
126
168
  .hagilight-promoto__description {
127
- color: var(--sl-color-text, #1f2937);
169
+ color: var(--promoto-muted);
170
+ font-size: var(--sl-text-sm, 0.875rem);
171
+ font-weight: 400;
172
+ line-height: 1.4;
128
173
  }
129
174
 
130
175
  .hagilight-promoto__media {
176
+ grid-area: media;
131
177
  display: inline-flex;
132
178
  align-items: center;
133
179
  justify-content: center;
134
- width: clamp(4.5rem, 8vw, 6.25rem);
135
- min-width: clamp(4.5rem, 8vw, 6.25rem);
180
+ width: 4.5rem;
181
+ min-width: 4.5rem;
136
182
  aspect-ratio: 1;
137
183
  overflow: hidden;
138
184
  border: 1px solid color-mix(in srgb, var(--sl-color-accent-high, #1d4ed8) 18%, var(--sl-color-gray-5, #cbd5e1));
139
- border-radius: 0.8rem;
185
+ border-radius: 0.75rem;
140
186
  background: color-mix(in srgb, var(--sl-color-bg-sidebar, #f8fafc) 82%, transparent);
141
187
  }
142
188
 
@@ -148,18 +194,20 @@ const { fallback, locale = Astro.currentLocale ?? 'en' } = Astro.props;
148
194
  }
149
195
 
150
196
  .hagilight-promoto__cta {
197
+ grid-area: cta;
151
198
  display: inline-flex;
152
199
  align-items: center;
153
200
  justify-content: center;
154
- min-width: 7.5rem;
155
- min-height: 2.6rem;
201
+ max-width: 14rem;
202
+ min-height: 2.25rem;
203
+ padding: 0.4rem 1rem;
156
204
  border: 1px solid color-mix(in srgb, var(--sl-color-accent-high, #1d4ed8) 30%, transparent);
157
205
  border-radius: 999px;
158
206
  background: var(--sl-color-accent, #2563eb);
159
207
  color: #fff;
160
- font-size: var(--sl-text-xs);
161
- font-weight: 700;
162
- padding-inline: 1rem;
208
+ font-size: var(--sl-text-sm, 0.875rem);
209
+ font-weight: 600;
210
+ line-height: 1.2;
163
211
  text-align: center;
164
212
  text-decoration: none;
165
213
  }
@@ -169,45 +217,88 @@ const { fallback, locale = Astro.currentLocale ?? 'en' } = Astro.props;
169
217
  background: var(--sl-color-accent-high, #1d4ed8);
170
218
  }
171
219
 
220
+ /* Toolbar: pager then dismiss, small, quiet, trailing edge. */
221
+ .hagilight-promoto__controls {
222
+ grid-column: 2;
223
+ grid-row: 1;
224
+ display: flex;
225
+ align-items: center;
226
+ gap: 0.125rem;
227
+ }
228
+
172
229
  .hagilight-promoto__dismiss,
173
230
  .hagilight-promoto__control {
174
- display: inline-flex;
175
- align-items: center;
176
- justify-content: center;
177
- min-width: 2.5rem;
178
- min-height: 2.5rem;
179
- border: 1px solid color-mix(in srgb, var(--sl-color-accent-high, #1d4ed8) 20%, var(--sl-color-gray-5, #cbd5e1));
231
+ position: relative;
232
+ display: inline-grid;
233
+ place-items: center;
234
+ flex: none;
235
+ width: var(--promoto-control-size);
236
+ height: var(--promoto-control-size);
237
+ padding: 0;
238
+ border: 0;
180
239
  border-radius: 999px;
181
- background: var(--sl-color-bg-nav, #f8fafc);
182
- color: var(--sl-color-text-high, #111827);
240
+ background: transparent;
241
+ color: var(--promoto-muted);
183
242
  cursor: pointer;
243
+ transition: background-color 0.15s ease, color 0.15s ease;
184
244
  }
185
245
 
186
- .hagilight-promoto__dismiss {
246
+ /* Keep the tap target larger than the visible mark. */
247
+ .hagilight-promoto__dismiss::before,
248
+ .hagilight-promoto__control::before {
249
+ content: '';
187
250
  position: absolute;
188
- z-index: 1;
189
- inset-block-start: 0.6rem;
190
- inset-inline-end: 0.6rem;
251
+ inset-block: -0.375rem;
252
+ inset-inline: 0;
191
253
  }
192
254
 
193
- .hagilight-promoto__controls {
194
- display: flex;
195
- align-items: center;
196
- justify-content: flex-end;
197
- gap: 0.5rem;
198
- padding: 0 1.25rem 1rem;
255
+ .hagilight-promoto__dismiss {
256
+ grid-column: 3;
257
+ grid-row: 1;
258
+ margin-inline: 0.25rem 0.625rem;
259
+ }
260
+
261
+ .hagilight-promoto__dismiss::before {
262
+ inset-inline: -0.25rem;
263
+ }
264
+
265
+ .hagilight-promoto__icon {
266
+ width: 1rem;
267
+ height: 1rem;
268
+ fill: none;
269
+ stroke: currentColor;
270
+ stroke-width: 2;
271
+ stroke-linecap: round;
272
+ stroke-linejoin: round;
273
+ pointer-events: none;
274
+ }
275
+
276
+ .hagilight-promoto__icon--play {
277
+ display: none;
278
+ fill: currentColor;
279
+ }
280
+
281
+ .hagilight-promoto__control[aria-pressed='true'] .hagilight-promoto__icon--pause {
282
+ display: none;
283
+ }
284
+
285
+ .hagilight-promoto__control[aria-pressed='true'] .hagilight-promoto__icon--play {
286
+ display: block;
199
287
  }
200
288
 
201
289
  .hagilight-promoto__count {
202
- min-width: 3.5rem;
203
- text-align: center;
204
- font-size: var(--sl-text-xs);
290
+ min-width: 2.25rem;
291
+ color: var(--promoto-muted);
292
+ font-size: var(--sl-text-xs, 0.75rem);
205
293
  font-variant-numeric: tabular-nums;
294
+ line-height: 1;
295
+ text-align: center;
296
+ white-space: nowrap;
206
297
  }
207
298
 
208
299
  .hagilight-promoto__control:disabled {
209
300
  cursor: default;
210
- opacity: 0.6;
301
+ opacity: 0.45;
211
302
  }
212
303
 
213
304
  .hagilight-promoto__status {
@@ -219,128 +310,157 @@ const { fallback, locale = Astro.currentLocale ?? 'en' } = Astro.props;
219
310
  white-space: nowrap;
220
311
  }
221
312
 
313
+ @media (hover: hover) {
314
+ .hagilight-promoto__dismiss:hover,
315
+ .hagilight-promoto__control:hover:not(:disabled) {
316
+ background: color-mix(in srgb, var(--sl-color-accent, #2563eb) 12%, transparent);
317
+ color: var(--sl-color-text-high, #111827);
318
+ }
319
+ }
320
+
222
321
  .hagilight-promoto__dismiss:focus-visible,
223
322
  .hagilight-promoto__control:focus-visible,
224
323
  .hagilight-promoto__cta:focus-visible {
225
- outline: 3px solid var(--sl-color-accent-high, #1d4ed8);
226
- outline-offset: 3px;
324
+ outline: 2px solid var(--sl-color-accent-high, #1d4ed8);
325
+ outline-offset: 2px;
227
326
  }
228
327
 
229
- @media (max-width: 960px) {
230
- .hagilight-promoto__slide {
231
- grid-template-columns: 1fr;
232
- gap: 0.75rem;
233
- padding-inline-end: 3.7rem;
328
+ /* Touch: bigger marks, still compact. */
329
+ @media (pointer: coarse) {
330
+ .hagilight-promoto {
331
+ --promoto-control-size: 2rem;
332
+ }
333
+ }
334
+
335
+ /* Portrait phones and narrow windows: stacked card, pager on the CTA row. */
336
+ @media (max-width: 45rem) and (min-height: 30rem) {
337
+ .hagilight-promoto {
338
+ --promoto-control-size: 2rem;
339
+ --promoto-pager-width: 8.25rem;
340
+ }
341
+
342
+ .hagilight-promoto__shell {
343
+ display: block;
344
+ bottom: calc(env(safe-area-inset-bottom) + 0.5rem);
345
+ width: min(calc(100vw - 1rem - 2 * max(env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px))), 24rem);
346
+ border-radius: 0.875rem;
347
+ }
348
+
349
+ .hagilight-promoto__slide,
350
+ .hagilight-promoto__slide:not([data-has-image='true']) {
351
+ grid-template-columns: minmax(0, 1fr);
352
+ grid-template-areas: 'title' 'desc' 'cta';
353
+ align-items: start;
354
+ gap: 0.375rem;
355
+ padding: 0.75rem;
234
356
  }
235
357
 
236
358
  .hagilight-promoto__slide[data-has-image='true'] {
237
- grid-template-columns: 1fr;
359
+ grid-template-columns: 3rem minmax(0, 1fr);
360
+ grid-template-areas: 'media title' 'desc desc' 'cta cta';
361
+ column-gap: 0.625rem;
238
362
  }
239
363
 
364
+ /* Flatten copy so the thumbnail sits beside the title only. */
240
365
  .hagilight-promoto__copy {
241
- grid-template-columns: 1fr;
242
- gap: 0.45rem;
366
+ display: contents;
243
367
  }
244
368
 
245
- .hagilight-promoto__media {
246
- width: min(100%, 8rem);
247
- min-width: 0;
369
+ .hagilight-promoto__title {
370
+ grid-area: title;
371
+ align-self: center;
372
+ padding-inline-end: 1.75rem;
373
+ font-size: 0.95rem;
248
374
  }
249
375
 
250
- .hagilight-promoto__cta {
251
- justify-self: start;
252
- white-space: normal;
376
+ .hagilight-promoto__description {
377
+ grid-area: desc;
378
+ font-size: 0.85rem;
379
+ line-height: 1.35;
253
380
  }
254
381
 
255
- .hagilight-promoto__controls {
256
- justify-content: flex-start;
257
- padding-top: 0;
382
+ .hagilight-promoto__media {
383
+ width: 3rem;
384
+ min-width: 3rem;
385
+ border-radius: 0.625rem;
258
386
  }
259
- }
260
387
 
261
- @media (max-width: 720px) {
262
- .hagilight-promoto__shell {
263
- bottom: calc(env(safe-area-inset-bottom) + 0.5rem);
264
- width: min(calc(100vw - 1rem), 24rem);
265
- border-radius: 1rem;
388
+ .hagilight-promoto__cta {
389
+ justify-self: start;
390
+ margin-block-start: 0.25rem;
391
+ min-height: 2.25rem;
392
+ padding-inline: 0.875rem;
266
393
  }
267
394
 
268
- .hagilight-promoto__slide {
269
- grid-template-columns: 1fr;
270
- gap: 0.7rem;
271
- padding: 0.85rem 4.8rem 0.85rem 0.85rem;
395
+ .hagilight-promoto[data-multiple] .hagilight-promoto__cta {
396
+ max-width: calc(100% - var(--promoto-pager-width));
272
397
  }
273
398
 
274
- .hagilight-promoto__slide[data-has-image='true'] {
275
- grid-template-columns: minmax(0, 1fr) 4.5rem;
276
- align-items: start;
399
+ .hagilight-promoto__dismiss {
400
+ position: absolute;
401
+ inset-block-start: 0.375rem;
402
+ inset-inline-end: 0.375rem;
403
+ margin: 0;
277
404
  }
278
405
 
279
- .hagilight-promoto__copy {
280
- grid-column: 1;
281
- grid-row: 1;
282
- gap: 0.45rem;
406
+ .hagilight-promoto__controls {
407
+ position: absolute;
408
+ inset-block-end: 0.75rem;
409
+ inset-inline-end: 0.5rem;
410
+ gap: 0;
283
411
  }
284
412
 
285
- .hagilight-promoto__title {
286
- font-size: 0.92rem;
413
+ .hagilight-promoto__count {
414
+ min-width: 2.25rem;
287
415
  }
416
+ }
288
417
 
289
- .hagilight-promoto__description {
290
- font-size: 0.84rem;
291
- font-weight: 500;
292
- line-height: 1.3;
418
+ @media (max-width: 22.5rem) and (min-height: 30rem) {
419
+ .hagilight-promoto {
420
+ --promoto-pager-width: 6rem;
293
421
  }
294
422
 
295
- .hagilight-promoto__media {
296
- width: 4.5rem;
297
- min-width: 4.5rem;
298
- align-self: start;
299
- grid-column: 2;
300
- grid-row: 1;
423
+ .hagilight-promoto__count {
424
+ display: none;
301
425
  }
302
426
 
303
427
  .hagilight-promoto__cta {
304
- grid-column: 1 / -1;
305
- justify-self: stretch;
306
- min-width: 0;
307
- min-height: 2.35rem;
308
- width: 100%;
309
- font-size: 0.82rem;
310
- white-space: normal;
428
+ padding-inline: 0.625rem;
311
429
  }
430
+ }
312
431
 
313
- .hagilight-promoto__control {
314
- min-width: 2.2rem;
315
- min-height: 2.2rem;
432
+ /* Short landscape viewports: single strip, no thumbnail, one-line description. */
433
+ @media (max-height: 30rem) {
434
+ .hagilight-promoto__shell {
435
+ bottom: max(0.5rem, env(safe-area-inset-bottom));
316
436
  }
317
437
 
318
- .hagilight-promoto__count {
319
- min-width: 2.75rem;
320
- font-size: 0.75rem;
438
+ .hagilight-promoto__media {
439
+ display: none;
321
440
  }
322
441
 
323
- .hagilight-promoto__dismiss {
324
- inset-block-start: 0.55rem;
325
- inset-inline-end: 0.55rem;
326
- width: auto;
327
- min-width: 2rem;
328
- height: 2rem;
329
- gap: 0.25rem;
330
- padding-inline: 0.55rem;
442
+ .hagilight-promoto__slide,
443
+ .hagilight-promoto__slide[data-has-image='true'] {
444
+ grid-template-columns: minmax(0, 1fr) auto;
445
+ grid-template-areas: 'copy cta';
446
+ padding: 0.5rem 0.75rem 0.5rem 1rem;
331
447
  }
332
448
 
333
- .hagilight-promoto__controls {
334
- flex-wrap: wrap;
335
- gap: 0.35rem;
336
- padding: 0 0.85rem 0.9rem;
449
+ .hagilight-promoto__description {
450
+ -webkit-line-clamp: 1;
451
+ }
452
+
453
+ .hagilight-promoto__cta {
454
+ min-height: 2rem;
337
455
  }
338
456
  }
339
457
 
340
458
  @media (prefers-reduced-motion: reduce) {
341
459
  .hagilight-promoto__shell,
342
- .hagilight-promoto__track {
460
+ .hagilight-promoto__track,
461
+ .hagilight-promoto__dismiss,
462
+ .hagilight-promoto__control {
343
463
  transition: none;
344
464
  }
345
465
  }
346
- </style>
466
+ </style>
@@ -281,6 +281,7 @@ export function definePromotoBannerElement() {
281
281
  }
282
282
  updateControls() {
283
283
  const multiple = this.cards.length > 1;
284
+ this.toggleAttribute('data-multiple', multiple);
284
285
  const controls = this.querySelector('[data-promoto-controls]');
285
286
  if (controls)
286
287
  controls.hidden = !multiple;
@@ -290,7 +291,6 @@ export function definePromotoBannerElement() {
290
291
  pause.disabled = this.reducedMotion;
291
292
  pause.setAttribute('aria-pressed', String(paused));
292
293
  pause.setAttribute('aria-label', paused ? 'Resume automatic promotion rotation' : 'Pause automatic promotion rotation');
293
- pause.textContent = paused ? 'Resume' : 'Pause';
294
294
  }
295
295
  this.updateSlides();
296
296
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hagicode/hagilight-core",
3
- "version": "0.6.1-dev.75.1.60c438f",
3
+ "version": "0.6.2-dev.77.1.2359fea",
4
4
  "type": "module",
5
5
  "description": "Shared TypeScript utilities, components, and assets for HagiCode Astro and Starlight sites",
6
6
  "repository": {