@netlify/spark-ui 1.26.0-alpha.6 → 1.26.0-alpha.8

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.
@@ -0,0 +1,403 @@
1
+ ---
2
+ import type { FilterableGridProps } from './filter-types';
3
+ import Button from '../Button.astro';
4
+
5
+ const { filters, results, totalCount, matchMode, id } = Astro.props as FilterableGridProps;
6
+
7
+ const filterModeAttrs = Object.fromEntries(
8
+ filters
9
+ .filter(category => category.matchMode)
10
+ .map(category => [`filter-mode-${category.key}`, category.matchMode])
11
+ );
12
+
13
+ const initialResultsLabel = `${totalCount} ${totalCount === 1 ? results.singular : results.plural}`;
14
+ ---
15
+
16
+ <div id={id} data-filter-root>
17
+ {
18
+ filters.length > 0 && (
19
+ <filter-container oninit filter-mode={matchMode} {...filterModeAttrs}>
20
+ <form class="n-filter-form" data-filter-form>
21
+ <details-utils
22
+ class="n-filter-dropdowns"
23
+ force-close="(min-width: 37.5em)"
24
+ force-restore
25
+ close-click-outside="(min-width: 37.5em)"
26
+ animate
27
+ >
28
+ {filters.map(category => (
29
+ <details class="n-filter-facet">
30
+ <summary>
31
+ <span class="n-filter-label">
32
+ {category.label}
33
+ <span class="n-filter-count" data-filter-count-badge data-filter-category={category.key}>
34
+ 0
35
+ </span>
36
+ </span>
37
+ <svg
38
+ class="n-filter-chevron-icon"
39
+ viewBox="0 0 512 512"
40
+ fill="none"
41
+ role="presentation"
42
+ >
43
+ <path
44
+ d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"
45
+ fill="currentcolor"
46
+ />
47
+ </svg>
48
+ </summary>
49
+ <fieldset class="n-filter-menu">
50
+ <legend class="n-filter-legend">{category.legend ?? `${category.label}:`}</legend>
51
+ {category.control === 'radio' && category.anyOption && (
52
+ <label>
53
+ <input
54
+ type="radio"
55
+ name={category.key}
56
+ value=""
57
+ data-filter-key={category.key}
58
+ checked
59
+ />
60
+ {category.anyOption.label}
61
+ </label>
62
+ )}
63
+ {category.options.map(option => (
64
+ <label>
65
+ <input
66
+ type={category.control === 'radio' ? 'radio' : 'checkbox'}
67
+ name={category.key}
68
+ value={option.value}
69
+ data-filter-key={category.key}
70
+ />
71
+ {option.label}
72
+ </label>
73
+ ))}
74
+ </fieldset>
75
+ </details>
76
+ ))}
77
+ </details-utils>
78
+ </form>
79
+
80
+ <div class="n-filter-meta">
81
+ <p
82
+ class="n-filter-results"
83
+ data-filter-results={`${results.singular}/${results.plural}`}
84
+ aria-live="polite"
85
+ >
86
+ {initialResultsLabel}
87
+ </p>
88
+ <ul class="n-filter-tags" role="list" data-filter-tags>
89
+ {filters.flatMap(category =>
90
+ category.options.map(option => (
91
+ <li
92
+ class="n-filter-tag"
93
+ data-tag-key={category.key}
94
+ data-tag-value={option.value}
95
+ hidden
96
+ >
97
+ <Button
98
+ type="button"
99
+ variant="outline"
100
+ data-tag-key={category.key}
101
+ data-tag-value={option.value}
102
+ >
103
+ {option.label}
104
+ <svg class="n-close-icon" viewBox="0 0 512 512" fill="none" role="presentation">
105
+ <path
106
+ d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3l105.4 105.3c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256z"
107
+ fill="currentcolor"
108
+ />
109
+ </svg>
110
+ </Button>
111
+ </li>
112
+ ))
113
+ )}
114
+ </ul>
115
+ </div>
116
+
117
+ <slot />
118
+ </filter-container>
119
+ )
120
+ }
121
+ {filters.length === 0 && <slot />}
122
+ </div>
123
+
124
+ <script src="@zachleat/filter-container/filter-container.js"></script>
125
+ <script src="@zachleat/details-utils/details-utils.js"></script>
126
+ <script>
127
+ function initFilterableGrid(root: HTMLElement): void {
128
+ const form = root.querySelector<HTMLFormElement>('[data-filter-form]');
129
+ if (!form) return;
130
+
131
+ const tagsList = root.querySelector<HTMLUListElement>('[data-filter-tags]');
132
+
133
+ const updateCount = (key: string) => {
134
+ const count = Array.from(
135
+ form.querySelectorAll<HTMLInputElement>(`[data-filter-key="${CSS.escape(key)}"]:checked`)
136
+ ).filter(entry => !!entry.value).length;
137
+ const counter = root.querySelector<HTMLElement>(
138
+ `[data-filter-count-badge][data-filter-category="${CSS.escape(key)}"]`
139
+ );
140
+ if (counter) {
141
+ counter.textContent = String(count);
142
+ counter.classList.toggle('is-visible', count > 0);
143
+ }
144
+ };
145
+
146
+ const showChip = (chipLi: HTMLLIElement) => {
147
+ if (!tagsList) return;
148
+ chipLi.hidden = false;
149
+ tagsList.appendChild(chipLi);
150
+ };
151
+
152
+ const hideChip = (chipLi: HTMLLIElement) => {
153
+ chipLi.hidden = true;
154
+ };
155
+
156
+ const findChip = (key: string, value: string): HTMLLIElement | null =>
157
+ tagsList?.querySelector<HTMLLIElement>(
158
+ `li[data-tag-key="${CSS.escape(key)}"][data-tag-value="${CSS.escape(value)}"]`
159
+ ) ?? null;
160
+
161
+ const onCheckboxInput = (key: string, value: string, checked: boolean) => {
162
+ const chip = findChip(key, value);
163
+ if (!chip) return;
164
+ if (checked) showChip(chip);
165
+ else hideChip(chip);
166
+ };
167
+
168
+ const onRadioInput = (key: string, value: string) => {
169
+ if (!tagsList) return;
170
+ tagsList
171
+ .querySelectorAll<HTMLLIElement>(`li[data-tag-key="${CSS.escape(key)}"]`)
172
+ .forEach(chip => {
173
+ if (value && chip.dataset.tagValue === value) showChip(chip);
174
+ else hideChip(chip);
175
+ });
176
+ };
177
+
178
+ const onTagInput = (input: HTMLInputElement) => {
179
+ if (!tagsList) return;
180
+ const key = input.dataset.filterKey;
181
+ if (!key) return;
182
+ if (input.type === 'radio') onRadioInput(key, input.value);
183
+ else onCheckboxInput(key, input.value, input.checked);
184
+ };
185
+
186
+ form.addEventListener('input', event => {
187
+ if (!(event.target instanceof HTMLInputElement)) return;
188
+ const key = event.target.dataset.filterKey;
189
+ if (key) updateCount(key);
190
+ if (tagsList) onTagInput(event.target);
191
+ });
192
+
193
+ tagsList?.addEventListener('click', event => {
194
+ const button = (event.target as HTMLElement).closest('button');
195
+ if (!button || !tagsList.contains(button)) return;
196
+ const key = button.dataset.tagKey;
197
+ const value = button.dataset.tagValue;
198
+ if (!key || value === undefined) return;
199
+ const input = form.querySelector<HTMLInputElement>(
200
+ `input[name="${CSS.escape(key)}"][value="${CSS.escape(value)}"]`
201
+ );
202
+ if (!input) return;
203
+ if (input.type === 'radio') {
204
+ const resetRadio = form.querySelector<HTMLInputElement>(
205
+ `input[type="radio"][name="${CSS.escape(key)}"][value=""]`
206
+ );
207
+ resetRadio?.dispatchEvent(
208
+ new MouseEvent('click', { view: window, bubbles: true, cancelable: true })
209
+ );
210
+ } else {
211
+ input.dispatchEvent(
212
+ new MouseEvent('click', { view: window, bubbles: true, cancelable: true })
213
+ );
214
+ }
215
+ });
216
+
217
+ // Seed counts/chips from state restored by filter-container's `oninit` (e.g. from URL params).
218
+ window.addEventListener('load', () => {
219
+ const checked = form.querySelectorAll<HTMLInputElement>('[data-filter-key]:checked');
220
+ checked.forEach(input => {
221
+ const key = input.dataset.filterKey;
222
+ if (key) updateCount(key);
223
+ if (tagsList) onTagInput(input);
224
+ });
225
+ });
226
+ }
227
+
228
+ const filterRoot = document.querySelector<HTMLElement>('[data-filter-root]');
229
+ if (filterRoot) initFilterableGrid(filterRoot);
230
+ </script>
231
+
232
+ <style>
233
+ .n-filter-form {
234
+ display: flex;
235
+ justify-content: center;
236
+ margin-block-end: var(--ntl-space-s);
237
+ }
238
+
239
+ .n-filter-dropdowns {
240
+ display: flex;
241
+ flex-wrap: wrap;
242
+ justify-content: center;
243
+ width: 100%;
244
+ border-radius: var(--ntl-radius-m);
245
+ }
246
+
247
+ @media (min-width: 37.5em) {
248
+ .n-filter-dropdowns {
249
+ display: inline-flex;
250
+ width: auto;
251
+ gap: var(--ntl-space-s);
252
+ padding: var(--ntl-space-s);
253
+ background-color: var(--ntl-color-bg-2);
254
+ }
255
+ }
256
+
257
+ .n-filter-facet {
258
+ width: 100%;
259
+ border-top: 1px solid var(--ntl-color-border-strong);
260
+ }
261
+
262
+ .n-filter-facet:last-child {
263
+ border-bottom: 1px solid var(--ntl-color-border-strong);
264
+ }
265
+
266
+ @media (min-width: 37.5em) {
267
+ .n-filter-facet,
268
+ .n-filter-facet:last-child {
269
+ width: auto;
270
+ border: none;
271
+ }
272
+ }
273
+
274
+ .n-filter-facet > summary {
275
+ position: relative;
276
+ display: flex;
277
+ align-items: center;
278
+ justify-content: space-between;
279
+ gap: var(--ntl-space-2xs);
280
+ padding-block: var(--ntl-space-2xs);
281
+ color: var(--ntl-heading-text-color);
282
+ cursor: pointer;
283
+ list-style: none;
284
+ }
285
+
286
+ .n-filter-facet > summary::-webkit-details-marker {
287
+ display: none;
288
+ }
289
+
290
+ @media (min-width: 37.5em) {
291
+ .n-filter-facet > summary {
292
+ padding: var(--ntl-space-2xs) var(--ntl-space-s);
293
+ border: 1px solid var(--ntl-color-border-strong);
294
+ border-radius: var(--ntl-radius-m);
295
+ background-color: var(--ntl-color-bg);
296
+ }
297
+
298
+ .n-filter-facet[open] > summary {
299
+ border-color: var(--ntl-blue-700);
300
+ }
301
+ }
302
+
303
+ .n-filter-label {
304
+ display: flex;
305
+ align-items: center;
306
+ gap: var(--ntl-space-3xs);
307
+ }
308
+
309
+ .n-filter-chevron-icon {
310
+ width: 0.7em;
311
+ }
312
+
313
+ .n-filter-count {
314
+ display: none;
315
+ align-items: center;
316
+ justify-content: center;
317
+ width: 1.5em;
318
+ height: 1.5em;
319
+ border-radius: 50%;
320
+ background: var(--ntl-blue-700);
321
+ color: var(--ntl-neutral-000);
322
+ font-size: 0.75em;
323
+ }
324
+
325
+ .n-filter-facet[open] .n-filter-count,
326
+ .n-filter-facet:not([open]) .n-filter-count.is-visible {
327
+ display: inline-flex;
328
+ }
329
+
330
+ @media (min-width: 37.5em) {
331
+ .n-filter-count {
332
+ position: absolute;
333
+ top: -0.5em;
334
+ right: -0.75em;
335
+ }
336
+ }
337
+
338
+ .n-filter-menu {
339
+ display: flex;
340
+ flex-direction: column;
341
+ padding-block: var(--ntl-space-2xs) var(--ntl-space-s);
342
+ border: none;
343
+ }
344
+
345
+ .n-filter-menu label {
346
+ display: flex;
347
+ align-items: center;
348
+ white-space: nowrap;
349
+ }
350
+
351
+ .n-filter-menu label:not(:last-child) {
352
+ margin-block-end: var(--ntl-space-2xs);
353
+ }
354
+
355
+ .n-filter-menu input[type='checkbox'],
356
+ .n-filter-menu input[type='radio'] {
357
+ margin-inline-end: var(--ntl-space-2xs);
358
+ accent-color: var(--ntl-blue-700);
359
+ }
360
+
361
+ .n-filter-legend {
362
+ position: absolute;
363
+ width: 1px;
364
+ height: 1px;
365
+ padding: 0;
366
+ margin: -1px;
367
+ overflow: hidden;
368
+ clip: rect(0, 0, 0, 0);
369
+ white-space: nowrap;
370
+ border: 0;
371
+ }
372
+
373
+ @media (min-width: 37.5em) {
374
+ .n-filter-menu {
375
+ position: absolute;
376
+ z-index: 1;
377
+ padding: var(--ntl-space-s);
378
+ border-radius: var(--ntl-radius-m);
379
+ background-color: var(--ntl-color-bg);
380
+ box-shadow: var(--ntl-shadow-deep, 0 0.5rem 1.5rem rgba(0, 0, 0, 0.2));
381
+ }
382
+ }
383
+
384
+ .n-filter-meta {
385
+ display: flex;
386
+ flex-direction: column;
387
+ align-items: center;
388
+ gap: var(--ntl-space-s);
389
+ margin-block-end: var(--ntl-space-l);
390
+ }
391
+
392
+ .n-filter-results {
393
+ color: var(--ntl-color-text-weak);
394
+ }
395
+
396
+ .n-filter-tags {
397
+ display: flex;
398
+ flex-wrap: wrap;
399
+ justify-content: center;
400
+ gap: var(--ntl-space-s);
401
+ list-style: none;
402
+ }
403
+ </style>
@@ -0,0 +1,48 @@
1
+ export interface FilterOption {
2
+ value: string;
3
+ label: string;
4
+ }
5
+
6
+ export interface FilterCategory {
7
+ key: string;
8
+ label: string;
9
+ legend?: string;
10
+ control?: 'checkbox' | 'radio';
11
+ /** Radio-only: prepend a checked `value=""` reset row. */
12
+ anyOption?: { label: string };
13
+ matchMode?: 'any' | 'all';
14
+ options: FilterOption[];
15
+ }
16
+
17
+ export interface FilterableGridProps {
18
+ filters: FilterCategory[];
19
+ results: { singular: string; plural: string };
20
+ /** Server-rendered initial count, shown before any client JS runs. */
21
+ totalCount: number;
22
+ /** Global match mode; per-category `matchMode` overrides it for that category. */
23
+ matchMode: 'any' | 'all';
24
+ id?: string;
25
+ }
26
+
27
+ /**
28
+ * Builds the `data-filter-{key}` attributes a slotted grid item needs so
29
+ * `<filter-container>` can match it against the active facets. Spread the
30
+ * result onto whatever bespoke element you render inside <FilterableGrid>.
31
+ *
32
+ * @example
33
+ * <article {...getFilterAttrs({ topic: ['react', 'astro'], level: 'beginner' })}>
34
+ * ...
35
+ * </article>
36
+ * // -> data-filter-topic="react,astro" data-filter-level="beginner"
37
+ */
38
+ export function getFilterAttrs(
39
+ values: Record<string, string | string[]>,
40
+ delimiter = ','
41
+ ): Record<`data-filter-${string}`, string> {
42
+ return Object.fromEntries(
43
+ Object.entries(values).map(([key, value]) => [
44
+ `data-filter-${key}`,
45
+ Array.isArray(value) ? value.join(delimiter) : value,
46
+ ])
47
+ ) as Record<`data-filter-${string}`, string>;
48
+ }
@@ -0,0 +1,2 @@
1
+ export { default as FilterableGrid } from './FilterableGrid.astro';
2
+ export * from './filter-types';
@@ -1,87 +1,95 @@
1
1
  @import url('../../../styles/animations.css');
2
2
 
3
3
  /* ----------------------------- */
4
- /* Container */
4
+ /* Container (invisible layer) */
5
5
  /* ----------------------------- */
6
+ /* The zone wraps arbitrary content and stays out of the way until a drag
7
+ enters it. Only a subtle dashed border hints that it's a drop target. */
6
8
  .n-dropzone {
7
9
  --n-dropzone-accent: var(--color-brand-primary, #0e7fe1);
8
10
  --n-dropzone-radius: var(--radius-2xl, 1rem);
9
11
 
10
12
  box-sizing: border-box;
11
13
  position: relative;
12
- display: flex;
13
- flex-direction: column;
14
- align-items: center;
15
- justify-content: center;
16
- gap: var(--space-s);
17
- width: 100%;
18
- height: 100%;
19
- min-height: 18rem;
20
- padding: var(--space-2xl, 2.5rem) var(--space-l, 1.5rem);
21
- text-align: center;
14
+ min-height: 4rem;
22
15
  color: var(--color-text-default, #14161a);
23
- background: var(--color-bg, #fff);
24
16
  border: 2px dashed var(--color-border-default, rgba(0, 0, 0, 0.15));
25
17
  border-radius: var(--n-dropzone-radius);
18
+ outline: none;
26
19
  transition:
27
20
  border-color 200ms ease-out,
28
21
  background-color 200ms ease-out,
29
22
  box-shadow 200ms ease-out;
30
23
  }
31
24
 
25
+ .n-dropzone:focus-visible {
26
+ border-color: var(--n-dropzone-accent);
27
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--n-dropzone-accent) 40%, transparent);
28
+ }
29
+
32
30
  /* Drag-over: the target glows without resizing. */
33
31
  .n-dropzone.is-active {
34
32
  border-color: var(--n-dropzone-accent);
35
- background: var(--color-bg-2, #f6f8fa);
36
33
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--n-dropzone-accent) 18%, transparent);
37
34
  }
38
35
 
36
+ /* While deploying, the dashes settle into a solid, quieter frame. */
39
37
  .n-dropzone.is-busy {
40
38
  border-style: solid;
41
39
  border-color: var(--color-border-subtle, rgba(0, 0, 0, 0.08));
42
40
  }
43
41
 
44
- /* A soft accent wash that fades in while dragging. */
45
- .n-dropzone::before {
46
- content: '';
47
- position: absolute;
48
- inset: 0;
49
- border-radius: inherit;
50
- background: radial-gradient(
51
- 120% 120% at 50% 0%,
52
- color-mix(in srgb, var(--n-dropzone-accent) 8%, transparent),
53
- transparent 60%
54
- );
55
- opacity: 0;
56
- transition: opacity 200ms ease-out;
57
- pointer-events: none;
42
+ /* ----------------------------- */
43
+ /* Wrapped content */
44
+ /* ----------------------------- */
45
+ /* Kept in flow (so the zone doesn't collapse) but faded out of the way once a
46
+ drag enters or a deploy starts, letting the overlay take over. */
47
+ .n-dropzone-content {
48
+ transition: opacity 150ms ease-out;
58
49
  }
59
50
 
60
- .n-dropzone.is-active::before {
61
- opacity: 1;
51
+ /* `.is-bare` (showStatus={false}) keeps content visible so a custom animation
52
+ can play in place of the built-in overlay. */
53
+ .n-dropzone.is-active:not(.is-bare) .n-dropzone-content,
54
+ .n-dropzone.is-busy:not(.is-bare) .n-dropzone-content {
55
+ opacity: 0;
56
+ pointer-events: none;
62
57
  }
63
58
 
64
59
  /* ----------------------------- */
65
- /* State panel (keyed → replays) */
60
+ /* Overlay (drag tint / status) */
66
61
  /* ----------------------------- */
67
- .n-dropzone-panel {
62
+ .n-dropzone-overlay {
63
+ position: absolute;
64
+ inset: 0;
65
+ z-index: 1;
68
66
  display: flex;
69
67
  flex-direction: column;
70
68
  align-items: center;
71
- gap: var(--space-xs, 0.75rem);
72
- width: 100%;
73
- animation: dropPanelIn 320ms cubic-bezier(0.22, 1, 0.36, 1) both;
69
+ justify-content: center;
70
+ gap: var(--space-s, 1rem);
71
+ padding: var(--space-l, 1.5rem);
72
+ text-align: center;
73
+ border-radius: inherit;
74
+ /* During a hover-only drag the overlay is a translucent wash and lets the
75
+ drop event fall through to the container beneath it. */
76
+ pointer-events: none;
77
+ background: color-mix(in srgb, var(--n-dropzone-accent) 10%, transparent);
74
78
  }
75
79
 
76
- @keyframes dropPanelIn {
77
- from {
78
- opacity: 0;
79
- transform: translateY(8px) scale(0.98);
80
- }
81
- to {
82
- opacity: 1;
83
- transform: none;
84
- }
80
+ /* Deploying / error: opaque scrim that captures interaction. */
81
+ .n-dropzone.is-busy .n-dropzone-overlay,
82
+ .n-dropzone[data-state='error'] .n-dropzone-overlay {
83
+ pointer-events: auto;
84
+ background: color-mix(in srgb, var(--color-bg, #fff) 88%, transparent);
85
+ backdrop-filter: blur(2px);
86
+ }
87
+
88
+ .n-dropzone-hint {
89
+ margin: 0;
90
+ font-size: 1.0625rem;
91
+ font-weight: var(--font-weight-semibold, 600);
92
+ color: var(--n-dropzone-accent);
85
93
  }
86
94
 
87
95
  .n-dropzone-title {
@@ -103,41 +111,6 @@
103
111
  line-height: var(--line-height-text, 1.5);
104
112
  }
105
113
 
106
- /* ----------------------------- */
107
- /* Idle upload glyph */
108
- /* ----------------------------- */
109
- .n-dropzone-glyph {
110
- display: grid;
111
- place-items: center;
112
- width: 3.5rem;
113
- height: 3.5rem;
114
- color: var(--n-dropzone-accent);
115
- }
116
-
117
- .n-dropzone-glyph svg {
118
- width: 100%;
119
- height: 100%;
120
- }
121
-
122
- /* The arrow gently bobs to invite a drop; the tray stays put. */
123
- .n-dropzone-glyph-arrow {
124
- animation: dropArrowBob 2.4s ease-in-out infinite;
125
- }
126
-
127
- .n-dropzone.is-active .n-dropzone-glyph-arrow {
128
- animation-duration: 0.9s;
129
- }
130
-
131
- @keyframes dropArrowBob {
132
- 0%,
133
- 100% {
134
- transform: translateY(0);
135
- }
136
- 50% {
137
- transform: translateY(-3px);
138
- }
139
- }
140
-
141
114
  /* ----------------------------- */
142
115
  /* Loading / spinner */
143
116
  /* ----------------------------- */
@@ -222,24 +195,13 @@
222
195
  /* Warning banner */
223
196
  /* ----------------------------- */
224
197
  .n-dropzone-warning {
225
- display: flex;
226
- align-items: center;
227
- gap: var(--space-2xs, 0.5rem);
228
198
  margin: 0;
229
- margin-block-start: var(--space-xs, 0.75rem);
230
199
  padding: var(--space-2xs, 0.5rem) var(--space-s, 1rem);
231
200
  max-width: 32rem;
232
201
  border-radius: var(--radius-m, 0.5rem);
233
202
  background: var(--color-warning-bg, #fdf3d7);
234
203
  color: var(--color-warning-text, #7a5b00);
235
204
  font-size: 0.8125rem;
236
- text-align: left;
237
- }
238
-
239
- .n-dropzone-warning-icon {
240
- flex: none;
241
- width: 1rem;
242
- height: 1rem;
243
205
  }
244
206
 
245
207
  /* ----------------------------- */
@@ -247,8 +209,7 @@
247
209
  /* ----------------------------- */
248
210
  @media (prefers-reduced-motion: reduce) {
249
211
  .n-dropzone,
250
- .n-dropzone-panel,
251
- .n-dropzone-glyph-arrow,
212
+ .n-dropzone-overlay,
252
213
  .n-dropzone-spinner,
253
214
  .n-dropzone-progress-bar {
254
215
  animation: none !important;