@neuravision/construct 1.1.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.
Files changed (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +227 -0
  3. package/components/README.md +566 -0
  4. package/components/_keyframes.css +23 -0
  5. package/components/_shared.css +120 -0
  6. package/components/accordion.css +124 -0
  7. package/components/alert.css +67 -0
  8. package/components/avatar.css +127 -0
  9. package/components/badge.css +67 -0
  10. package/components/banner.css +247 -0
  11. package/components/breadcrumbs.css +152 -0
  12. package/components/button.css +145 -0
  13. package/components/card.css +76 -0
  14. package/components/checkbox.css +120 -0
  15. package/components/chip.css +361 -0
  16. package/components/combobox.css +385 -0
  17. package/components/components.css +2 -0
  18. package/components/data-table.css +93 -0
  19. package/components/datepicker.css +268 -0
  20. package/components/divider.css +73 -0
  21. package/components/drawer.css +167 -0
  22. package/components/dropdown.css +401 -0
  23. package/components/empty-state.css +97 -0
  24. package/components/field.css +42 -0
  25. package/components/file-upload.css +111 -0
  26. package/components/icon.css +31 -0
  27. package/components/index.css +49 -0
  28. package/components/input.css +64 -0
  29. package/components/list.css +474 -0
  30. package/components/modal.css +164 -0
  31. package/components/navbar.css +587 -0
  32. package/components/pagination.css +131 -0
  33. package/components/popover.css +231 -0
  34. package/components/progress-bar.css +56 -0
  35. package/components/select-menu.css +267 -0
  36. package/components/select.css +30 -0
  37. package/components/sidebar.css +183 -0
  38. package/components/skeleton.css +38 -0
  39. package/components/skip-link.css +38 -0
  40. package/components/slider.css +305 -0
  41. package/components/spinner.css +72 -0
  42. package/components/switch.css +82 -0
  43. package/components/table.css +139 -0
  44. package/components/tabs.css +147 -0
  45. package/components/textarea.css +16 -0
  46. package/components/toast.css +71 -0
  47. package/components/toggle-group.css +196 -0
  48. package/components/toolbar.css +222 -0
  49. package/components/tooltip.css +124 -0
  50. package/docs/guidelines.md +141 -0
  51. package/foundations.css +299 -0
  52. package/package.json +66 -0
  53. package/tokens/README.md +179 -0
  54. package/tokens/tokens.css +434 -0
  55. package/tokens/tokens.js +1188 -0
  56. package/tokens/tokens.json +810 -0
  57. package/tokens/tokens.ts +1188 -0
@@ -0,0 +1,566 @@
1
+ # Construct Components v1
2
+
3
+ Framework-agnostic component styles built on Construct design tokens. Use these styles directly with HTML or wrap them in Angular, React, or Svelte components. Interactive states are controlled via data attributes and ARIA, making them easy to enhance with JavaScript.
4
+
5
+ ## Usage
6
+
7
+ Import order (recommended):
8
+ 1. Tokens (automatically included via foundations)
9
+ 2. Foundations
10
+ 3. Components
11
+
12
+ Example:
13
+ ```css
14
+ @import '@neuravision/construct/foundations.css';
15
+ @import '@neuravision/construct/components/components.css';
16
+ ```
17
+
18
+ ## State Conventions
19
+
20
+ - `data-state="open"` / `data-state="closed"` for popovers, modals, tooltips
21
+ - `aria-invalid="true"` for form errors
22
+ - `aria-current="page"` for current pagination page
23
+ - `aria-selected="true"` for active tabs and selected datepicker days
24
+ - `aria-disabled="true"` when disabled but not using native `disabled`
25
+
26
+ ## Keyboard + ARIA Requirements
27
+
28
+ When building framework wrappers, implement these keyboard patterns:
29
+
30
+ - **Tabs**: Arrow keys move focus between tabs, Home/End jumps to first/last, Enter/Space activates. Use roving tabindex (active tab `tabindex="0"`, others `tabindex="-1"`).
31
+ - **Dropdown (Action-List)**: Trigger uses `aria-expanded` + `aria-controls`. On open, move focus to first item, Esc closes and returns focus to trigger.
32
+ - **Dropdown (Role=menu)**: Only use if implementing arrow-key navigation, Home/End, typeahead, and roving tabindex.
33
+ - **Datepicker**: Arrow keys move by day, PageUp/PageDown switches months, Home/End jumps within week, Enter/Space selects, Esc closes.
34
+ - **Modal**: Focus trap, initial focus inside dialog, Esc closes, focus returns to trigger.
35
+ - **Tooltip**: Opens on hover/focus, closes on blur/Esc, uses `role="tooltip"` with `aria-describedby`.
36
+
37
+ ## Components
38
+
39
+ ### Button
40
+
41
+ Basic button with variants and sizes:
42
+
43
+ ```html
44
+ <button class="ct-button">Primary</button>
45
+ <button class="ct-button ct-button--secondary">Secondary</button>
46
+ <button class="ct-button ct-button--ghost">Ghost</button>
47
+ <button class="ct-button ct-button--outline">Outline</button>
48
+ <button class="ct-button ct-button--danger">Danger</button>
49
+ <button class="ct-button ct-button--accent">Accent</button>
50
+ <button class="ct-button ct-button--link">Link</button>
51
+
52
+ <!-- Sizes -->
53
+ <button class="ct-button ct-button--sm">Small</button>
54
+ <button class="ct-button">Medium</button>
55
+ <button class="ct-button ct-button--lg">Large</button>
56
+
57
+ <!-- With icon -->
58
+ <button class="ct-button">
59
+ <span class="ct-button__icon" aria-hidden="true">+</span>
60
+ Add item
61
+ </button>
62
+
63
+ <!-- Icon only -->
64
+ <button class="ct-button ct-button--icon" aria-label="Settings">
65
+ <span class="ct-button__icon" aria-hidden="true">⚙</span>
66
+ </button>
67
+ ```
68
+
69
+ ### Field + Input
70
+
71
+ Form field with label, input, hint, and error states:
72
+
73
+ ```html
74
+ <div class="ct-field">
75
+ <label class="ct-field__label" for="email">Email</label>
76
+ <input class="ct-input" id="email" type="email" placeholder="name@company.com" />
77
+ <div class="ct-field__hint">We will not share this.</div>
78
+ </div>
79
+
80
+ <!-- Error state -->
81
+ <div class="ct-field ct-field--error">
82
+ <label class="ct-field__label" for="name">Name</label>
83
+ <input class="ct-input" id="name" aria-invalid="true" />
84
+ <div class="ct-field__error">Name is required.</div>
85
+ </div>
86
+
87
+ <!-- With icon -->
88
+ <div class="ct-field">
89
+ <label class="ct-field__label" for="search">Search</label>
90
+ <div class="ct-input-wrap">
91
+ <span class="ct-input__icon" aria-hidden="true">🔍</span>
92
+ <input class="ct-input ct-input--with-icon" id="search" type="search" />
93
+ </div>
94
+ </div>
95
+ ```
96
+
97
+ ### Select
98
+
99
+ Native select dropdown:
100
+
101
+ ```html
102
+ <div class="ct-field">
103
+ <label class="ct-field__label" for="role">Role</label>
104
+ <select class="ct-select" id="role">
105
+ <option>Designer</option>
106
+ <option>Engineer</option>
107
+ <option>Manager</option>
108
+ </select>
109
+ </div>
110
+ ```
111
+
112
+ ### Textarea
113
+
114
+ Multi-line text input:
115
+
116
+ ```html
117
+ <div class="ct-field">
118
+ <label class="ct-field__label" for="notes">Notes</label>
119
+ <textarea class="ct-textarea" id="notes" placeholder="Add context..."></textarea>
120
+ </div>
121
+ ```
122
+
123
+ ### Checkbox
124
+
125
+ Single checkbox or checkbox group:
126
+
127
+ ```html
128
+ <label class="ct-check">
129
+ <input class="ct-check__input" type="checkbox" />
130
+ <span class="ct-check__label">Remember me</span>
131
+ </label>
132
+
133
+ <label class="ct-check">
134
+ <input class="ct-check__input" type="checkbox" checked />
135
+ <span class="ct-check__label">Send weekly reports</span>
136
+ </label>
137
+ ```
138
+
139
+ ### Radio
140
+
141
+ Radio button group:
142
+
143
+ ```html
144
+ <label class="ct-radio">
145
+ <input class="ct-radio__input" type="radio" name="plan" checked />
146
+ <span class="ct-radio__label">Standard</span>
147
+ </label>
148
+
149
+ <label class="ct-radio">
150
+ <input class="ct-radio__input" type="radio" name="plan" />
151
+ <span class="ct-radio__label">Premium</span>
152
+ </label>
153
+ ```
154
+
155
+ ### Switch
156
+
157
+ Toggle switch control:
158
+
159
+ ```html
160
+ <label class="ct-switch">
161
+ <input class="ct-switch__input" type="checkbox" role="switch" checked />
162
+ <span class="ct-switch__label">Auto renew</span>
163
+ </label>
164
+ ```
165
+
166
+ ### Card
167
+
168
+ Content card with header, body, and footer:
169
+
170
+ ```html
171
+ <section class="ct-card">
172
+ <div class="ct-card__header">
173
+ <h3>Team</h3>
174
+ <button class="ct-button ct-button--ghost">Edit</button>
175
+ </div>
176
+ <div class="ct-card__body">
177
+ <p>Shared ownership and clear permissions.</p>
178
+ <p class="ct-muted">Updated 2 days ago</p>
179
+ </div>
180
+ <div class="ct-card__footer">
181
+ <span class="ct-muted">12 members</span>
182
+ <button class="ct-button ct-button--secondary">Open</button>
183
+ </div>
184
+ </section>
185
+ ```
186
+
187
+ ### Table
188
+
189
+ Basic data table with variants:
190
+
191
+ ```html
192
+ <div class="ct-table-wrap">
193
+ <table class="ct-table ct-table--striped">
194
+ <thead>
195
+ <tr>
196
+ <th scope="col">Name</th>
197
+ <th scope="col">Status</th>
198
+ <th scope="col">Owner</th>
199
+ </tr>
200
+ </thead>
201
+ <tbody>
202
+ <tr>
203
+ <td>Alpha</td>
204
+ <td>Active</td>
205
+ <td>J. Chen</td>
206
+ </tr>
207
+ <tr>
208
+ <td>Beta</td>
209
+ <td>Paused</td>
210
+ <td>L. Hart</td>
211
+ </tr>
212
+ </tbody>
213
+ </table>
214
+ </div>
215
+ ```
216
+
217
+ Variants:
218
+ - `ct-table--striped` - Alternating row backgrounds
219
+ - `ct-table--compact` - Reduced padding
220
+ - `ct-table--hover` - Row hover effect
221
+
222
+ ### Data Table
223
+
224
+ Complex data table with header, toolbar, filters, and actions:
225
+
226
+ ```html
227
+ <div class="ct-data-table">
228
+ <div class="ct-data-table__header">
229
+ <div class="ct-data-table__title">
230
+ <h3>Projects</h3>
231
+ <span class="ct-data-table__meta">24 total</span>
232
+ </div>
233
+ <div class="ct-data-table__actions">
234
+ <button class="ct-button ct-button--secondary ct-button--sm">Export</button>
235
+ <button class="ct-button ct-button--sm">New project</button>
236
+ </div>
237
+ </div>
238
+
239
+ <div class="ct-data-table__toolbar">
240
+ <div class="ct-data-table__filters">
241
+ <input class="ct-input ct-control--sm" placeholder="Search..." aria-label="Search" />
242
+ <select class="ct-select ct-control--sm" aria-label="Filter by status">
243
+ <option>All Status</option>
244
+ <option>Active</option>
245
+ <option>Paused</option>
246
+ </select>
247
+ </div>
248
+ </div>
249
+
250
+ <div class="ct-data-table__table">
251
+ <table class="ct-table ct-table--striped">
252
+ <!-- table content -->
253
+ </table>
254
+ </div>
255
+
256
+ <div class="ct-data-table__footer">
257
+ <span class="ct-muted">Showing 1-10 of 24</span>
258
+ <!-- pagination component -->
259
+ </div>
260
+ </div>
261
+ ```
262
+
263
+ ### Modal
264
+
265
+ Dialog with backdrop:
266
+
267
+ ```html
268
+ <div class="ct-modal" data-state="open" role="dialog" aria-modal="true" aria-labelledby="modal-title">
269
+ <div class="ct-modal__dialog">
270
+ <div class="ct-modal__header">
271
+ <h2 id="modal-title">Invite team</h2>
272
+ <button class="ct-button ct-button--ghost" aria-label="Close">×</button>
273
+ </div>
274
+ <div class="ct-modal__body">
275
+ <p>Send an invite to a new team member.</p>
276
+ <!-- form content -->
277
+ </div>
278
+ <div class="ct-modal__footer">
279
+ <button class="ct-button ct-button--secondary">Cancel</button>
280
+ <button class="ct-button">Send</button>
281
+ </div>
282
+ </div>
283
+ </div>
284
+ ```
285
+
286
+ ### Toast
287
+
288
+ Notification with variants:
289
+
290
+ ```html
291
+ <div class="ct-toast-region" aria-live="polite">
292
+ <div class="ct-toast" data-variant="success" data-state="open">
293
+ <div class="ct-toast__title">Saved</div>
294
+ <div class="ct-toast__description">Your changes were saved.</div>
295
+ <button class="ct-button ct-button--ghost">Undo</button>
296
+ </div>
297
+ </div>
298
+ ```
299
+
300
+ Variants: `success`, `error`, `info`, `warning`
301
+
302
+ ### Alert / Banner
303
+
304
+ Inline feedback message with variants:
305
+
306
+ ```html
307
+ <div class="ct-alert" data-variant="warning" role="alert">
308
+ <span class="ct-alert__icon" aria-hidden="true">!</span>
309
+ <div class="ct-alert__content">
310
+ <div class="ct-alert__title">Action required</div>
311
+ <div class="ct-alert__description">Please review the pending changes.</div>
312
+ <div class="ct-alert__actions">
313
+ <button class="ct-button ct-button--secondary ct-button--sm">Review</button>
314
+ </div>
315
+ </div>
316
+ </div>
317
+ ```
318
+
319
+ Variants: `info`, `success`, `warning`, `danger`
320
+
321
+ ### Badge / Status Badge
322
+
323
+ Small status indicators with optional icon or dot:
324
+
325
+ ```html
326
+ <span class="ct-badge ct-badge--icon"><span class="ct-badge__dot" aria-hidden="true"></span>Draft</span>
327
+ <span class="ct-badge ct-badge--success ct-badge--icon">
328
+ <span class="ct-badge__icon" aria-hidden="true">+</span>
329
+ Approved
330
+ </span>
331
+ ```
332
+
333
+ ### Chip / Tag
334
+
335
+ Compact tags for filters or metadata:
336
+
337
+ ```html
338
+ <button class="ct-chip ct-chip--interactive" type="button" aria-pressed="true">
339
+ <span class="ct-chip__icon" aria-hidden="true">#</span>
340
+ Finance
341
+ </button>
342
+
343
+ <span class="ct-chip">
344
+ Uploads
345
+ <button class="ct-chip__remove" type="button" aria-label="Remove tag">x</button>
346
+ </span>
347
+ ```
348
+
349
+ ### File Upload / Drag & Drop
350
+
351
+ Drag area + file list pattern. Use `data-state="dragover"` and `aria-invalid="true"` when needed.
352
+
353
+ ```html
354
+ <div class="ct-file-upload">
355
+ <label class="ct-file-upload__dropzone" for="files">
356
+ <input class="ct-file-upload__input" id="files" type="file" multiple />
357
+ <div class="ct-file-upload__title">Drop files here or browse</div>
358
+ <div class="ct-file-upload__hint">PDF, DOCX up to 10MB</div>
359
+ <span class="ct-button ct-button--secondary ct-button--sm">Browse files</span>
360
+ </label>
361
+
362
+ <ul class="ct-file-upload__list">
363
+ <li class="ct-file-upload__item" data-status="success">
364
+ <div class="ct-file-upload__file">
365
+ <div class="ct-file-upload__name">report.pdf</div>
366
+ <div class="ct-file-upload__meta">820 KB</div>
367
+ </div>
368
+ <span class="ct-badge ct-badge--success">Uploaded</span>
369
+ </li>
370
+ </ul>
371
+ <div class="ct-file-upload__error">File is too large.</div>
372
+ </div>
373
+ ```
374
+
375
+ ### Loading Spinner + Overlay
376
+
377
+ Spinner for inline loading and overlay for blocking states:
378
+
379
+ ```html
380
+ <div class="ct-spinner ct-spinner--sm" role="status" aria-live="polite"></div>
381
+
382
+ <div class="ct-card" style="position: relative; min-height: 180px;">
383
+ <div class="ct-loading-overlay" data-state="active" aria-busy="true">
384
+ <div class="ct-loading-overlay__content">
385
+ <div class="ct-spinner ct-spinner--lg" aria-hidden="true"></div>
386
+ <div class="ct-loading-overlay__label">Uploading files...</div>
387
+ </div>
388
+ </div>
389
+ </div>
390
+ ```
391
+
392
+ ### Confirmation Dialog
393
+
394
+ Use the modal base with a confirmation layout:
395
+
396
+ ```html
397
+ <div class="ct-modal ct-modal--confirmation" data-state="open" role="dialog" aria-modal="true" aria-labelledby="confirm-title" aria-describedby="confirm-desc">
398
+ <div class="ct-modal__dialog">
399
+ <div class="ct-modal__body">
400
+ <div class="ct-confirmation" data-variant="danger">
401
+ <div class="ct-confirmation__icon" aria-hidden="true">!</div>
402
+ <div class="ct-confirmation__content">
403
+ <h2 class="ct-confirmation__title" id="confirm-title">Delete file?</h2>
404
+ <p class="ct-confirmation__description" id="confirm-desc">This action cannot be undone.</p>
405
+ </div>
406
+ </div>
407
+ </div>
408
+ <div class="ct-modal__footer">
409
+ <button class="ct-button ct-button--secondary">Cancel</button>
410
+ <button class="ct-button ct-button--danger">Delete</button>
411
+ </div>
412
+ </div>
413
+ </div>
414
+ ```
415
+
416
+ ### Skeleton / Placeholder
417
+
418
+ Use for loading placeholders:
419
+
420
+ ```html
421
+ <div class="ct-stack" style="--ct-stack-space: var(--space-3); max-width: 320px;">
422
+ <span class="ct-skeleton ct-skeleton--title"></span>
423
+ <span class="ct-skeleton ct-skeleton--text"></span>
424
+ <span class="ct-skeleton ct-skeleton--text" style="--ct-skeleton-width: 70%;"></span>
425
+ </div>
426
+ ```
427
+
428
+ ### Tabs
429
+
430
+ Tab navigation with panels:
431
+
432
+ ```html
433
+ <div class="ct-tabs">
434
+ <div class="ct-tabs__list" role="tablist">
435
+ <button class="ct-tabs__trigger" role="tab" aria-selected="true" aria-controls="panel-1" id="tab-1" tabindex="0">Overview</button>
436
+ <button class="ct-tabs__trigger" role="tab" aria-selected="false" aria-controls="panel-2" id="tab-2" tabindex="-1">Settings</button>
437
+ </div>
438
+ <div class="ct-tabs__panel" role="tabpanel" id="panel-1" aria-labelledby="tab-1">
439
+ <p>Panel content</p>
440
+ </div>
441
+ </div>
442
+ ```
443
+
444
+ ### Dropdown
445
+
446
+ Action menu:
447
+
448
+ ```html
449
+ <div class="ct-dropdown" data-state="open">
450
+ <button class="ct-button ct-button--secondary ct-dropdown__trigger" aria-expanded="true" aria-controls="menu">Actions</button>
451
+ <div class="ct-dropdown__menu" id="menu">
452
+ <button class="ct-dropdown__item" type="button">Edit</button>
453
+ <button class="ct-dropdown__item" type="button">Duplicate</button>
454
+ <div class="ct-dropdown__separator" role="separator"></div>
455
+ <button class="ct-dropdown__item" type="button">Delete</button>
456
+ </div>
457
+ </div>
458
+ ```
459
+
460
+ ### Pagination
461
+
462
+ Page navigation:
463
+
464
+ ```html
465
+ <nav class="ct-pagination" aria-label="Pagination">
466
+ <ul class="ct-pagination__list">
467
+ <li><button class="ct-pagination__link" type="button">1</button></li>
468
+ <li><button class="ct-pagination__link" aria-current="page" type="button">2</button></li>
469
+ <li><button class="ct-pagination__link" type="button">3</button></li>
470
+ </ul>
471
+ </nav>
472
+ ```
473
+
474
+ ### Breadcrumbs
475
+
476
+ Path navigation:
477
+
478
+ ```html
479
+ <nav class="ct-breadcrumbs" aria-label="Breadcrumb">
480
+ <ol class="ct-breadcrumbs__list">
481
+ <li class="ct-breadcrumbs__item">
482
+ <a class="ct-breadcrumbs__link" href="/">Home</a>
483
+ <span class="ct-breadcrumbs__separator">/</span>
484
+ </li>
485
+ <li class="ct-breadcrumbs__item">
486
+ <a class="ct-breadcrumbs__link" href="/projects">Projects</a>
487
+ <span class="ct-breadcrumbs__separator">/</span>
488
+ </li>
489
+ <li class="ct-breadcrumbs__item">
490
+ <span class="ct-breadcrumbs__current">Alpha</span>
491
+ </li>
492
+ </ol>
493
+ </nav>
494
+ ```
495
+
496
+ ### Datepicker
497
+
498
+ Calendar popup for date selection:
499
+
500
+ ```html
501
+ <div class="ct-field">
502
+ <label class="ct-field__label" for="date">Date</label>
503
+ <div class="ct-datepicker" data-state="open">
504
+ <input class="ct-input" id="date" type="text" placeholder="Select date" />
505
+ <div class="ct-datepicker__popover" role="dialog" aria-label="Choose date">
506
+ <div class="ct-datepicker__header">
507
+ <button class="ct-button ct-button--ghost ct-button--icon" aria-label="Previous month">‹</button>
508
+ <div class="ct-datepicker__title">March 2026</div>
509
+ <button class="ct-button ct-button--ghost ct-button--icon" aria-label="Next month">›</button>
510
+ </div>
511
+ <div class="ct-datepicker__grid">
512
+ <div class="ct-datepicker__weekday">Mo</div>
513
+ <!-- ... weekdays ... -->
514
+ <button class="ct-datepicker__day">1</button>
515
+ <button class="ct-datepicker__day" aria-selected="true">15</button>
516
+ <!-- ... days ... -->
517
+ </div>
518
+ </div>
519
+ </div>
520
+ </div>
521
+ ```
522
+
523
+ ### Tooltip
524
+
525
+ Hint on hover/focus:
526
+
527
+ ```html
528
+ <span class="ct-tooltip" data-state="open" data-side="top">
529
+ <button class="ct-button" aria-describedby="tip-1">Hover me</button>
530
+ <span class="ct-tooltip__content" role="tooltip" id="tip-1">Short hint</span>
531
+ </span>
532
+ ```
533
+
534
+ ## Layout Utilities
535
+
536
+ ### Stack
537
+
538
+ Vertical spacing between children:
539
+
540
+ ```html
541
+ <div class="ct-stack" style="--ct-stack-space: var(--space-4);">
542
+ <div>Item 1</div>
543
+ <div>Item 2</div>
544
+ </div>
545
+ ```
546
+
547
+ ### Cluster
548
+
549
+ Horizontal spacing with wrapping:
550
+
551
+ ```html
552
+ <div class="ct-cluster" style="--ct-cluster-space: var(--space-3);">
553
+ <button>Button 1</button>
554
+ <button>Button 2</button>
555
+ </div>
556
+ ```
557
+
558
+ ## Text Utilities
559
+
560
+ - `ct-muted` - Muted text color
561
+ - `ct-truncate` - Truncate with ellipsis
562
+ - `ct-sr-only` - Screen reader only (visually hidden)
563
+
564
+ ---
565
+
566
+ **Construct** - Build accessible design constructs
@@ -0,0 +1,23 @@
1
+ @keyframes ct-spin {
2
+ to {
3
+ transform: rotate(360deg);
4
+ }
5
+ }
6
+
7
+ @keyframes ct-skeleton {
8
+ 0% {
9
+ background-position: 200% 0;
10
+ }
11
+ 100% {
12
+ background-position: -200% 0;
13
+ }
14
+ }
15
+
16
+ @keyframes ct-progress-indeterminate {
17
+ 0% {
18
+ transform: translateX(-100%);
19
+ }
20
+ 100% {
21
+ transform: translateX(400%);
22
+ }
23
+ }