@hagicode/hagilight-core 0.6.1 → 0.6.2-dev.78.1.6cba3ad

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,21 @@ const { fallback, locale = Astro.currentLocale ?? 'en' } = Astro.props;
42
57
  </script>
43
58
 
44
59
  <style is:global>
60
+ /*
61
+ * Layout model: the card is content on top, one action row on the bottom.
62
+ * The call to action sits bottom-left; pager and pause share that row at the
63
+ * bottom-right, so controls never take a column or a row of their own. Dismiss
64
+ * is a small corner mark. Wide: media beside the copy. Portrait phones: a small
65
+ * thumbnail beside the title only. Short landscape viewports: no thumbnail and a
66
+ * one-line description, so the card stays a thin strip.
67
+ */
45
68
  .hagilight-promoto {
46
69
  display: block;
70
+ --promoto-control-size: 1.75rem;
71
+ --promoto-cta-height: 2.25rem;
72
+ --promoto-pad: 0.875rem;
73
+ --promoto-pager-width: calc(3 * var(--promoto-control-size) + 2.25rem + 0.375rem);
74
+ --promoto-muted: var(--sl-color-gray-2, #4b5563);
47
75
  }
48
76
 
49
77
  .hagilight-promoto[hidden],
@@ -57,13 +85,13 @@ const { fallback, locale = Astro.currentLocale ?? 'en' } = Astro.props;
57
85
  z-index: 40;
58
86
  inset-inline: 0;
59
87
  bottom: max(1rem, env(safe-area-inset-bottom));
60
- width: min(72rem, calc(100vw - 1rem));
88
+ width: min(42rem, 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;
@@ -88,14 +116,27 @@ const { fallback, locale = Astro.currentLocale ?? 'en' } = Astro.props;
88
116
  transition: transform 0.34s ease;
89
117
  }
90
118
 
119
+ /* Slide: media beside copy and cta, so a tall image never opens a gap above the cta. The pager overlays the end of the cta row. */
91
120
  .hagilight-promoto__slide {
92
121
  display: grid;
93
- grid-template-columns: minmax(0, 1fr) auto auto;
94
- align-items: center;
95
- gap: 1.25rem;
122
+ grid-template-columns: auto minmax(0, 1fr);
123
+ grid-template-areas:
124
+ 'media copy'
125
+ 'media cta';
126
+ align-items: start;
127
+ column-gap: 1rem;
128
+ row-gap: 0.75rem;
96
129
  width: 100%;
97
130
  min-width: 100%;
98
- padding: 1.25rem 4rem 1.25rem 1.25rem;
131
+ padding: var(--promoto-pad);
132
+ }
133
+
134
+ .hagilight-promoto__slide:not([data-has-image='true']) {
135
+ grid-template-columns: minmax(0, 1fr);
136
+ grid-template-areas:
137
+ 'copy'
138
+ 'cta';
139
+ padding-inline-start: 1.25rem;
99
140
  }
100
141
 
101
142
  .hagilight-promoto__slide[aria-hidden='true'] {
@@ -103,10 +144,9 @@ const { fallback, locale = Astro.currentLocale ?? 'en' } = Astro.props;
103
144
  }
104
145
 
105
146
  .hagilight-promoto__copy {
147
+ grid-area: copy;
106
148
  display: grid;
107
- grid-template-columns: 1fr;
108
- align-items: start;
109
- gap: 0.4rem;
149
+ gap: 0.25rem;
110
150
  min-width: 0;
111
151
  }
112
152
 
@@ -115,28 +155,37 @@ const { fallback, locale = Astro.currentLocale ?? 'en' } = Astro.props;
115
155
  display: -webkit-box;
116
156
  overflow: hidden;
117
157
  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
158
  -webkit-box-orient: vertical;
123
159
  -webkit-line-clamp: 2;
124
160
  }
125
161
 
162
+ .hagilight-promoto__title {
163
+ padding-inline-end: 2rem;
164
+ color: var(--sl-color-text-high, var(--sl-color-gray-1, #111827));
165
+ font-size: var(--sl-text-base, 1rem);
166
+ font-weight: 700;
167
+ line-height: 1.3;
168
+ text-wrap: balance;
169
+ }
170
+
126
171
  .hagilight-promoto__description {
127
- color: var(--sl-color-text, #1f2937);
172
+ color: var(--promoto-muted);
173
+ font-size: var(--sl-text-sm, 0.875rem);
174
+ font-weight: 400;
175
+ line-height: 1.4;
128
176
  }
129
177
 
130
178
  .hagilight-promoto__media {
179
+ grid-area: media;
131
180
  display: inline-flex;
132
181
  align-items: center;
133
182
  justify-content: center;
134
- width: clamp(4.5rem, 8vw, 6.25rem);
135
- min-width: clamp(4.5rem, 8vw, 6.25rem);
183
+ width: 4.5rem;
184
+ min-width: 4.5rem;
136
185
  aspect-ratio: 1;
137
186
  overflow: hidden;
138
187
  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;
188
+ border-radius: 0.75rem;
140
189
  background: color-mix(in srgb, var(--sl-color-bg-sidebar, #f8fafc) 82%, transparent);
141
190
  }
142
191
 
@@ -148,66 +197,119 @@ const { fallback, locale = Astro.currentLocale ?? 'en' } = Astro.props;
148
197
  }
149
198
 
150
199
  .hagilight-promoto__cta {
200
+ grid-area: cta;
151
201
  display: inline-flex;
152
202
  align-items: center;
153
203
  justify-content: center;
154
- min-width: 7.5rem;
155
- min-height: 2.6rem;
204
+ justify-self: start;
205
+ max-width: min(100%, 16rem);
206
+ min-height: var(--promoto-cta-height);
207
+ padding: 0.4rem 1rem;
156
208
  border: 1px solid color-mix(in srgb, var(--sl-color-accent-high, #1d4ed8) 30%, transparent);
157
209
  border-radius: 999px;
158
210
  background: var(--sl-color-accent, #2563eb);
159
211
  color: #fff;
160
- font-size: var(--sl-text-xs);
161
- font-weight: 700;
162
- padding-inline: 1rem;
212
+ font-size: var(--sl-text-sm, 0.875rem);
213
+ font-weight: 600;
214
+ line-height: 1.2;
163
215
  text-align: center;
164
216
  text-decoration: none;
165
217
  }
166
218
 
219
+ .hagilight-promoto[data-multiple] .hagilight-promoto__cta {
220
+ max-width: min(16rem, calc(100% - var(--promoto-pager-width)));
221
+ }
222
+
167
223
  .hagilight-promoto__cta:hover,
168
224
  .hagilight-promoto__cta:focus-visible {
169
225
  background: var(--sl-color-accent-high, #1d4ed8);
170
226
  }
171
227
 
228
+ /* Pager + pause: bottom-right, on the same row as the call to action. */
229
+ .hagilight-promoto__controls {
230
+ position: absolute;
231
+ inset-block-end: var(--promoto-pad);
232
+ inset-inline-end: 0.5rem;
233
+ display: flex;
234
+ align-items: center;
235
+ height: var(--promoto-cta-height);
236
+ gap: 0.125rem;
237
+ }
238
+
172
239
  .hagilight-promoto__dismiss,
173
240
  .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));
241
+ position: relative;
242
+ display: inline-grid;
243
+ place-items: center;
244
+ flex: none;
245
+ width: var(--promoto-control-size);
246
+ height: var(--promoto-control-size);
247
+ padding: 0;
248
+ border: 0;
180
249
  border-radius: 999px;
181
- background: var(--sl-color-bg-nav, #f8fafc);
182
- color: var(--sl-color-text-high, #111827);
250
+ background: transparent;
251
+ color: var(--promoto-muted);
183
252
  cursor: pointer;
253
+ transition: background-color 0.15s ease, color 0.15s ease;
184
254
  }
185
255
 
256
+ /* Keep the tap target larger than the visible mark. */
257
+ .hagilight-promoto__dismiss::before,
258
+ .hagilight-promoto__control::before {
259
+ content: '';
260
+ position: absolute;
261
+ inset-block: -0.375rem;
262
+ inset-inline: 0;
263
+ }
264
+
265
+ /* Dismiss: a small corner mark, out of the content flow. */
186
266
  .hagilight-promoto__dismiss {
187
267
  position: absolute;
188
- z-index: 1;
189
- inset-block-start: 0.6rem;
190
- inset-inline-end: 0.6rem;
268
+ inset-block-start: 0.375rem;
269
+ inset-inline-end: 0.375rem;
191
270
  }
192
271
 
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;
272
+ .hagilight-promoto__dismiss::before {
273
+ inset-inline: -0.25rem;
274
+ }
275
+
276
+ .hagilight-promoto__icon {
277
+ width: 1rem;
278
+ height: 1rem;
279
+ fill: none;
280
+ stroke: currentColor;
281
+ stroke-width: 2;
282
+ stroke-linecap: round;
283
+ stroke-linejoin: round;
284
+ pointer-events: none;
285
+ }
286
+
287
+ .hagilight-promoto__icon--play {
288
+ display: none;
289
+ fill: currentColor;
290
+ }
291
+
292
+ .hagilight-promoto__control[aria-pressed='true'] .hagilight-promoto__icon--pause {
293
+ display: none;
294
+ }
295
+
296
+ .hagilight-promoto__control[aria-pressed='true'] .hagilight-promoto__icon--play {
297
+ display: block;
199
298
  }
200
299
 
201
300
  .hagilight-promoto__count {
202
- min-width: 3.5rem;
203
- text-align: center;
204
- font-size: var(--sl-text-xs);
301
+ min-width: 2.25rem;
302
+ color: var(--promoto-muted);
303
+ font-size: var(--sl-text-xs, 0.75rem);
205
304
  font-variant-numeric: tabular-nums;
305
+ line-height: 1;
306
+ text-align: center;
307
+ white-space: nowrap;
206
308
  }
207
309
 
208
310
  .hagilight-promoto__control:disabled {
209
311
  cursor: default;
210
- opacity: 0.6;
312
+ opacity: 0.45;
211
313
  }
212
314
 
213
315
  .hagilight-promoto__status {
@@ -219,128 +321,135 @@ const { fallback, locale = Astro.currentLocale ?? 'en' } = Astro.props;
219
321
  white-space: nowrap;
220
322
  }
221
323
 
324
+ @media (hover: hover) {
325
+ .hagilight-promoto__dismiss:hover,
326
+ .hagilight-promoto__control:hover:not(:disabled) {
327
+ background: color-mix(in srgb, var(--sl-color-accent, #2563eb) 12%, transparent);
328
+ color: var(--sl-color-text-high, #111827);
329
+ }
330
+ }
331
+
222
332
  .hagilight-promoto__dismiss:focus-visible,
223
333
  .hagilight-promoto__control:focus-visible,
224
334
  .hagilight-promoto__cta:focus-visible {
225
- outline: 3px solid var(--sl-color-accent-high, #1d4ed8);
226
- outline-offset: 3px;
335
+ outline: 2px solid var(--sl-color-accent-high, #1d4ed8);
336
+ outline-offset: 2px;
227
337
  }
228
338
 
229
- @media (max-width: 960px) {
230
- .hagilight-promoto__slide {
231
- grid-template-columns: 1fr;
232
- gap: 0.75rem;
233
- padding-inline-end: 3.7rem;
234
- }
235
-
236
- .hagilight-promoto__slide[data-has-image='true'] {
237
- grid-template-columns: 1fr;
238
- }
239
-
240
- .hagilight-promoto__copy {
241
- grid-template-columns: 1fr;
242
- gap: 0.45rem;
243
- }
244
-
245
- .hagilight-promoto__media {
246
- width: min(100%, 8rem);
247
- min-width: 0;
248
- }
249
-
250
- .hagilight-promoto__cta {
251
- justify-self: start;
252
- white-space: normal;
339
+ /* Touch: bigger marks, still compact. */
340
+ @media (pointer: coarse) {
341
+ .hagilight-promoto {
342
+ --promoto-control-size: 2rem;
253
343
  }
344
+ }
254
345
 
255
- .hagilight-promoto__controls {
256
- justify-content: flex-start;
257
- padding-top: 0;
346
+ /* Portrait phones: thumbnail beside the title only; the description and the action row use the full width. */
347
+ @media (max-width: 36rem) and (min-height: 30rem) {
348
+ .hagilight-promoto {
349
+ --promoto-control-size: 2rem;
350
+ --promoto-pad: 0.75rem;
258
351
  }
259
- }
260
352
 
261
- @media (max-width: 720px) {
262
353
  .hagilight-promoto__shell {
263
354
  bottom: calc(env(safe-area-inset-bottom) + 0.5rem);
264
- width: min(calc(100vw - 1rem), 24rem);
265
- border-radius: 1rem;
355
+ width: min(calc(100vw - 1rem - 2 * max(env(safe-area-inset-left, 0px), env(safe-area-inset-right, 0px))), 28rem);
356
+ border-radius: 0.875rem;
266
357
  }
267
358
 
268
- .hagilight-promoto__slide {
269
- grid-template-columns: 1fr;
270
- gap: 0.7rem;
271
- padding: 0.85rem 4.8rem 0.85rem 0.85rem;
359
+ .hagilight-promoto__slide[data-has-image] {
360
+ grid-template-columns: minmax(0, 1fr);
361
+ grid-template-areas: 'title' 'desc' 'cta';
362
+ gap: 0.375rem;
363
+ padding: var(--promoto-pad);
272
364
  }
273
365
 
274
366
  .hagilight-promoto__slide[data-has-image='true'] {
275
- grid-template-columns: minmax(0, 1fr) 4.5rem;
276
- align-items: start;
367
+ grid-template-columns: 3rem minmax(0, 1fr);
368
+ grid-template-areas: 'media title' 'desc desc' 'cta cta';
369
+ column-gap: 0.625rem;
277
370
  }
278
371
 
372
+ /* Flatten copy so the thumbnail sits beside the title only. */
279
373
  .hagilight-promoto__copy {
280
- grid-column: 1;
281
- grid-row: 1;
282
- gap: 0.45rem;
374
+ display: contents;
283
375
  }
284
376
 
285
377
  .hagilight-promoto__title {
286
- font-size: 0.92rem;
378
+ grid-area: title;
379
+ align-self: center;
380
+ font-size: 0.95rem;
287
381
  }
288
382
 
289
383
  .hagilight-promoto__description {
290
- font-size: 0.84rem;
291
- font-weight: 500;
292
- line-height: 1.3;
384
+ grid-area: desc;
385
+ font-size: 0.85rem;
386
+ line-height: 1.35;
293
387
  }
294
388
 
295
389
  .hagilight-promoto__media {
296
- width: 4.5rem;
297
- min-width: 4.5rem;
298
- align-self: start;
299
- grid-column: 2;
300
- grid-row: 1;
390
+ width: 3rem;
391
+ min-width: 3rem;
392
+ border-radius: 0.625rem;
301
393
  }
302
394
 
303
395
  .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;
396
+ margin-block-start: 0.25rem;
397
+ padding-inline: 0.875rem;
311
398
  }
399
+ }
312
400
 
313
- .hagilight-promoto__control {
314
- min-width: 2.2rem;
315
- min-height: 2.2rem;
401
+ @media (max-width: 22.5rem) and (min-height: 30rem) {
402
+ .hagilight-promoto {
403
+ --promoto-pager-width: 6rem;
316
404
  }
317
405
 
318
406
  .hagilight-promoto__count {
319
- min-width: 2.75rem;
320
- font-size: 0.75rem;
407
+ display: none;
321
408
  }
322
409
 
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;
410
+ .hagilight-promoto__cta {
411
+ padding-inline: 0.625rem;
412
+ }
413
+ }
414
+
415
+ /* Short landscape viewports: thin strip, no thumbnail, one-line description. */
416
+ @media (max-height: 30rem) {
417
+ .hagilight-promoto {
418
+ --promoto-cta-height: 2rem;
419
+ --promoto-pad: 0.5rem;
331
420
  }
332
421
 
333
- .hagilight-promoto__controls {
334
- flex-wrap: wrap;
335
- gap: 0.35rem;
336
- padding: 0 0.85rem 0.9rem;
422
+ .hagilight-promoto__shell {
423
+ bottom: max(0.5rem, env(safe-area-inset-bottom));
424
+ }
425
+
426
+ .hagilight-promoto__media {
427
+ display: none;
428
+ }
429
+
430
+ .hagilight-promoto__slide[data-has-image] {
431
+ grid-template-columns: minmax(0, 1fr);
432
+ grid-template-areas: 'copy' 'cta';
433
+ row-gap: 0.375rem;
434
+ padding: 0.5rem 0.75rem 0.5rem 1rem;
435
+ }
436
+
437
+ .hagilight-promoto__description {
438
+ -webkit-line-clamp: 1;
439
+ }
440
+
441
+ .hagilight-promoto__dismiss {
442
+ inset-block-start: 0.25rem;
443
+ inset-inline-end: 0.25rem;
337
444
  }
338
445
  }
339
446
 
340
447
  @media (prefers-reduced-motion: reduce) {
341
448
  .hagilight-promoto__shell,
342
- .hagilight-promoto__track {
449
+ .hagilight-promoto__track,
450
+ .hagilight-promoto__dismiss,
451
+ .hagilight-promoto__control {
343
452
  transition: none;
344
453
  }
345
454
  }
346
- </style>
455
+ </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",
3
+ "version": "0.6.2-dev.78.1.6cba3ad",
4
4
  "type": "module",
5
5
  "description": "Shared TypeScript utilities, components, and assets for HagiCode Astro and Starlight sites",
6
6
  "repository": {