@birdapi/velinstyle 1.0.0 → 1.2.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 (321) hide show
  1. package/README.de.md +87 -158
  2. package/README.md +188 -115
  3. package/cli/blueprint.js +98 -5
  4. package/cli/blueprints/app-chrome.html +18 -0
  5. package/cli/blueprints/benefits-grid.html +9 -0
  6. package/cli/blueprints/bottom-nav-mobile.html +2 -1
  7. package/cli/blueprints/cookie-consent.html +1 -1
  8. package/cli/blueprints/cta-band.html +6 -0
  9. package/cli/blueprints/faq-section.html +18 -0
  10. package/cli/blueprints/filter-bar.html +4 -4
  11. package/cli/blueprints/form-contact.html +6 -5
  12. package/cli/blueprints/hero-section.html +3 -3
  13. package/cli/blueprints/notification-center.html +1 -1
  14. package/cli/blueprints/onboarding.html +2 -1
  15. package/cli/blueprints/ops-console.html +29 -0
  16. package/cli/blueprints/pricing-band.html +37 -0
  17. package/cli/blueprints/process-steps.html +10 -0
  18. package/cli/blueprints/services-grid.html +9 -0
  19. package/cli/blueprints/settings-panel.html +8 -6
  20. package/cli/blueprints/split-hero.html +25 -0
  21. package/cli/blueprints/testimonials.html +8 -0
  22. package/cli/blueprints/trust-bar.html +10 -0
  23. package/cli/cli-manifest.json +81 -17
  24. package/cli/contrast-utils.js +97 -0
  25. package/cli/create.js +370 -0
  26. package/cli/docgen/extract-a11y.js +4 -2
  27. package/cli/docgen/extract-attributes.js +126 -10
  28. package/cli/docgen/extract-cli.js +3 -2
  29. package/cli/docgen/extract-components.js +61 -2
  30. package/cli/docgen/extract-utilities.js +10 -10
  31. package/cli/doctor.js +119 -0
  32. package/cli/index.js +535 -46
  33. package/cli/meta.js +84 -1
  34. package/cli/perf-audit.js +15 -2
  35. package/cli/prompt-engine.js +243 -0
  36. package/cli/review.js +268 -0
  37. package/cli/scaffold.js +119 -4
  38. package/cli/scanner-rules-data.js +7 -0
  39. package/cli/scanner.js +357 -4
  40. package/cli/serve.js +81 -0
  41. package/cli/skills.js +223 -0
  42. package/cli/wc-api.js +89 -0
  43. package/cli/workflow.js +42 -0
  44. package/components/index.js +4 -0
  45. package/components/runtime/component-loaders.js +4 -0
  46. package/components/velin-accordion.js +62 -13
  47. package/components/velin-bottom-nav.js +10 -0
  48. package/components/velin-calendar.js +212 -0
  49. package/components/velin-command.js +18 -2
  50. package/components/velin-data-table.js +438 -0
  51. package/components/velin-dialog.js +4 -1
  52. package/components/velin-drawer.js +2 -4
  53. package/components/velin-file-dropzone.js +162 -0
  54. package/components/velin-form-summary.js +348 -0
  55. package/components/velin-icon.js +22 -1
  56. package/components/velin-lightbox.js +5 -3
  57. package/components/velin-modal.js +2 -4
  58. package/components/velin-search.js +1 -1
  59. package/components/velin-sheet.js +2 -4
  60. package/core/a11y/component-contracts.json +35 -1
  61. package/core/highlight/languages/go.js +28 -0
  62. package/core/highlight/languages/python.js +29 -0
  63. package/core/highlight/languages/rust.js +32 -0
  64. package/core/highlight/languages/yaml.js +23 -0
  65. package/core/highlight/registry.js +13 -2
  66. package/core/meta/build.js +126 -6
  67. package/core/meta/design-constraints/contact.json +9 -0
  68. package/core/meta/design-constraints/faq.json +9 -0
  69. package/core/meta/design-constraints/hero.json +13 -0
  70. package/core/meta/knowledge/components.json +622 -0
  71. package/core/meta/knowledge/tokens.json +282 -0
  72. package/core/meta/pages/registry.json +294 -0
  73. package/core/meta/schema.js +3 -1
  74. package/core/meta/sections/registry.json +185 -0
  75. package/dist/chunks/{attributes-TPANLMLF.js → attributes-A423NX5J.js} +3 -3
  76. package/dist/chunks/{attributes-ADOKLKA7.js → attributes-F6UPIEVP.js} +2 -2
  77. package/dist/chunks/{attributes-STYCS3O5.js → attributes-GLYXGGAL.js} +3 -3
  78. package/dist/chunks/{attributes-5Q2WXGRC.js → attributes-UFTYOSAU.js} +20 -3
  79. package/dist/chunks/attributes-XGLXLR2T.js +391 -0
  80. package/dist/chunks/attributes-YF4NI2QS.js +391 -0
  81. package/dist/chunks/browser-AUABBYUK.js +1544 -0
  82. package/dist/chunks/{chunk-SRL6EWHP.js → chunk-2LSKX57P.js} +13 -11
  83. package/dist/chunks/{chunk-RSMRR27R.js → chunk-2TGCVDUL.js} +11 -9
  84. package/dist/chunks/{chunk-XIET2VSZ.js → chunk-5JWGUAPC.js} +17 -13
  85. package/dist/chunks/chunk-7YSO2OQ2.js +113 -0
  86. package/dist/chunks/{chunk-IEHKRARD.js → chunk-BHR3ZG6U.js} +10 -8
  87. package/dist/chunks/{chunk-J472ERN2.js → chunk-ERF5YVP4.js} +15 -4
  88. package/dist/chunks/{chunk-XYLJ2GCB.js → chunk-JMSIHEYX.js} +55 -7
  89. package/dist/chunks/{chunk-MKZP2TBH.js → chunk-L7ZGUU4D.js} +2 -2
  90. package/dist/chunks/chunk-TUGIPKW5.js +113 -0
  91. package/dist/chunks/go-HZ6XTFCK.js +31 -0
  92. package/dist/chunks/{highlight-GJHHV4KM.js → highlight-IBDX4XWS.js} +1 -1
  93. package/dist/chunks/{highlight-XTHRKBA3.js → highlight-M2ELQNYK.js} +1 -1
  94. package/dist/chunks/python-BMPKDKNO.js +32 -0
  95. package/dist/chunks/runtime-entry.js +1 -1
  96. package/dist/chunks/rust-2BWKI2MN.js +35 -0
  97. package/dist/chunks/{velin-accordion-FBIPAEXG.js → velin-accordion-WSY6BMLA.js} +64 -10
  98. package/dist/chunks/{velin-bottom-nav-7GEMPIM6.js → velin-bottom-nav-5RVRNGIP.js} +25 -0
  99. package/dist/chunks/{velin-bottom-nav-2N2TYWOF.js → velin-bottom-nav-N5F4UO2I.js} +26 -1
  100. package/dist/chunks/velin-calendar-OFE6THUD.js +210 -0
  101. package/dist/chunks/velin-calendar-PGVNJTXK.js +217 -0
  102. package/dist/chunks/velin-calendar-XC7JRDSH.js +217 -0
  103. package/dist/chunks/{velin-code-block-CIAURDRJ.js → velin-code-block-ITCLIC6T.js} +1 -1
  104. package/dist/chunks/{velin-code-block-TSUKPY4C.js → velin-code-block-ZGWG7JI5.js} +12 -3
  105. package/dist/chunks/{velin-combobox-NXS4QB4R.js → velin-combobox-56V2ZWT5.js} +1 -1
  106. package/dist/chunks/{velin-command-ONKO2A3J.js → velin-command-2HO2EKR6.js} +29 -2
  107. package/dist/chunks/{velin-command-TMLSRGIU.js → velin-command-ZJSLPK7B.js} +30 -3
  108. package/dist/chunks/{velin-copy-NYDBOKXT.js → velin-copy-XDQSV2XU.js} +1 -1
  109. package/dist/chunks/{velin-countdown-4ZVDGK4R.js → velin-countdown-V4Z5MA63.js} +1 -1
  110. package/dist/chunks/velin-data-table-KK7IDGTP.js +302 -0
  111. package/dist/chunks/velin-data-table-UVR42UUM.js +372 -0
  112. package/dist/chunks/{velin-dialog-GTTZI564.js → velin-dialog-3YK2RPRV.js} +9 -2
  113. package/dist/chunks/{velin-dialog-ELFOZIXZ.js → velin-dialog-S24KAP4S.js} +3 -1
  114. package/dist/chunks/{velin-drawer-BE6TYN3H.js → velin-drawer-5PNU624J.js} +2 -4
  115. package/dist/chunks/{velin-drawer-DLL2GLT6.js → velin-drawer-CCM6S25D.js} +3 -5
  116. package/dist/chunks/{velin-email-RBX6ZR4S.js → velin-email-TNFONXPW.js} +1 -1
  117. package/dist/chunks/velin-file-dropzone-QRGF2JNA.js +154 -0
  118. package/dist/chunks/velin-file-dropzone-XIZMJ3NB.js +154 -0
  119. package/dist/chunks/velin-form-summary-XRQ7COQH.js +273 -0
  120. package/dist/chunks/{velin-icon-7M76DOPB.js → velin-icon-A6NCXGUK.js} +59 -17
  121. package/dist/chunks/{velin-icon-ZTSKUOVN.js → velin-icon-EC4B5XCM.js} +18 -5
  122. package/dist/chunks/{velin-lightbox-6E7CAATP.js → velin-lightbox-FFK4TUUP.js} +7 -6
  123. package/dist/chunks/{velin-lightbox-I75U6JF4.js → velin-lightbox-P725FLTY.js} +15 -5
  124. package/dist/chunks/{velin-menubar-J66JXRWW.js → velin-menubar-6I2LM5HL.js} +1 -1
  125. package/dist/chunks/{velin-modal-5HYNNNUI.js → velin-modal-ICTMGRSU.js} +7 -8
  126. package/dist/chunks/{velin-modal-AEOTZ67O.js → velin-modal-JBNODH7D.js} +2 -4
  127. package/dist/chunks/{velin-popover-T72HK3DF.js → velin-popover-WZG2GHBL.js} +25 -4
  128. package/dist/chunks/{velin-rating-N4DI2XNL.js → velin-rating-QBBYRRBU.js} +1 -1
  129. package/dist/chunks/{velin-search-7B4KJ6TX.js → velin-search-B3L6J2RP.js} +2 -2
  130. package/dist/chunks/{velin-search-2XRQFBCS.js → velin-search-L3UG6NDQ.js} +5 -4
  131. package/dist/chunks/{velin-secure-field-6PV7RZDJ.js → velin-secure-field-DGYMP7OK.js} +1 -1
  132. package/dist/chunks/{velin-segmented-control-GYD5BIXQ.js → velin-segmented-control-P74GJPAR.js} +1 -1
  133. package/dist/chunks/{velin-sheet-R2Y67X6H.js → velin-sheet-M5GZZD7G.js} +2 -4
  134. package/dist/chunks/{velin-sheet-OUK6572T.js → velin-sheet-X7RXIWKV.js} +3 -5
  135. package/dist/chunks/{velin-stepper-X54WGHDG.js → velin-stepper-4YQJUHPC.js} +12 -5
  136. package/dist/chunks/{velin-toast-OMMOA2DK.js → velin-toast-UG5LWTG2.js} +5 -1
  137. package/dist/chunks/{velin-tooltip-BDHGRTQB.js → velin-tooltip-ZDH5SDLU.js} +1 -1
  138. package/dist/chunks/yaml-O67LEQYT.js +26 -0
  139. package/dist/llms.txt +29 -7
  140. package/dist/search-index.json +182 -10
  141. package/dist/velin-agent.json +3997 -26
  142. package/dist/velinstyle-components.iife.js +1952 -287
  143. package/dist/velinstyle-components.js +1956 -287
  144. package/dist/velinstyle-components.min.js +267 -119
  145. package/dist/velinstyle.css +381 -67
  146. package/dist/velinstyle.d.ts +4 -0
  147. package/dist/velinstyle.min.css +1 -1
  148. package/package.json +26 -6
  149. package/packages/skill-engine/README.md +24 -0
  150. package/packages/skill-engine/package.json +7 -0
  151. package/packages/skill-engine/src/catalog.js +29 -0
  152. package/packages/skill-engine/src/index.js +5 -0
  153. package/packages/skill-engine/src/install.js +69 -0
  154. package/packages/skill-engine/src/registry.js +27 -0
  155. package/packages/skill-engine/src/resolve.js +68 -0
  156. package/packages/skill-engine/src/validate.js +40 -0
  157. package/packages/velinstyle-skills/README.md +6 -0
  158. package/packages/velinstyle-skills/adapter.json +8 -0
  159. package/packages/velinstyle-skills/bundles/landing-starter.bundle.yaml +12 -0
  160. package/packages/velinstyle-skills/bundles/review-suite.bundle.yaml +11 -0
  161. package/packages/velinstyle-skills/catalog.json +1004 -0
  162. package/packages/velinstyle-skills/graphs/component-ship.graph.yaml +14 -0
  163. package/packages/velinstyle-skills/graphs/landingpage.graph.yaml +21 -0
  164. package/packages/velinstyle-skills/graphs/release-gate.graph.yaml +15 -0
  165. package/packages/velinstyle-skills/index.js +22 -0
  166. package/packages/velinstyle-skills/package.json +7 -0
  167. package/packages/velinstyle-skills/packs/accessibility.pack.yaml +9 -0
  168. package/packages/velinstyle-skills/packs/design.pack.yaml +10 -0
  169. package/packages/velinstyle-skills/packs/documentation.pack.yaml +8 -0
  170. package/packages/velinstyle-skills/packs/enterprise.pack.yaml +10 -0
  171. package/packages/velinstyle-skills/packs/frontend.pack.yaml +10 -0
  172. package/packages/velinstyle-skills/packs/review.pack.yaml +9 -0
  173. package/packages/velinstyle-skills/projects/admin-panel.project.yaml +12 -0
  174. package/packages/velinstyle-skills/projects/blog.project.yaml +11 -0
  175. package/packages/velinstyle-skills/projects/documentation-portal.project.yaml +13 -0
  176. package/packages/velinstyle-skills/projects/ecommerce-shop.project.yaml +12 -0
  177. package/packages/velinstyle-skills/projects/landing-page.project.yaml +12 -0
  178. package/packages/velinstyle-skills/projects/laravel-dashboard.project.yaml +12 -0
  179. package/packages/velinstyle-skills/projects/marketing-website.project.yaml +14 -0
  180. package/packages/velinstyle-skills/projects/portfolio.project.yaml +12 -0
  181. package/packages/velinstyle-skills/registry.json +142 -0
  182. package/packages/velinstyle-skills/scripts/generate-assets.mjs +163 -0
  183. package/packages/velinstyle-skills/skills/velin-a11y-focus-keyboard/SKILL.md +57 -0
  184. package/packages/velinstyle-skills/skills/velin-a11y-page-checklist/SKILL.md +57 -0
  185. package/packages/velinstyle-skills/skills/velin-a11y-scan-fix/SKILL.md +57 -0
  186. package/packages/velinstyle-skills/skills/velin-ai-agent-briefing/SKILL.md +57 -0
  187. package/packages/velinstyle-skills/skills/velin-ai-plan-scaffold-review/SKILL.md +57 -0
  188. package/packages/velinstyle-skills/skills/velin-ai-skill-authoring/SKILL.md +57 -0
  189. package/packages/velinstyle-skills/skills/velin-components-accessibility-map/SKILL.md +57 -0
  190. package/packages/velinstyle-skills/skills/velin-components-api-contract/SKILL.md +57 -0
  191. package/packages/velinstyle-skills/skills/velin-components-recipe/SKILL.md +57 -0
  192. package/packages/velinstyle-skills/skills/velin-components-select-compose/SKILL.md +57 -0
  193. package/packages/velinstyle-skills/skills/velin-design-hero-compose/SKILL.md +57 -0
  194. package/packages/velinstyle-skills/skills/velin-design-section-rhythm/SKILL.md +57 -0
  195. package/packages/velinstyle-skills/skills/velin-design-surface-roles/SKILL.md +57 -0
  196. package/packages/velinstyle-skills/skills/velin-dev-component-wire/SKILL.md +57 -0
  197. package/packages/velinstyle-skills/skills/velin-dev-page-scaffold/SKILL.md +57 -0
  198. package/packages/velinstyle-skills/skills/velin-dev-plan-first/SKILL.md +57 -0
  199. package/packages/velinstyle-skills/skills/velin-docs-component-guide/SKILL.md +57 -0
  200. package/packages/velinstyle-skills/skills/velin-docs-generate-api/SKILL.md +57 -0
  201. package/packages/velinstyle-skills/skills/velin-dx-doctor-setup/SKILL.md +57 -0
  202. package/packages/velinstyle-skills/skills/velin-dx-project-init/SKILL.md +57 -0
  203. package/packages/velinstyle-skills/skills/velin-migration-legacy-wc/SKILL.md +57 -0
  204. package/packages/velinstyle-skills/skills/velin-migration-prefix-classes/SKILL.md +57 -0
  205. package/packages/velinstyle-skills/skills/velin-motion-reduced-safe/SKILL.md +57 -0
  206. package/packages/velinstyle-skills/skills/velin-motion-reveal-feedback/SKILL.md +57 -0
  207. package/packages/velinstyle-skills/skills/velin-perf-audit-fix/SKILL.md +57 -0
  208. package/packages/velinstyle-skills/skills/velin-perf-runtime-tree-shake/SKILL.md +57 -0
  209. package/packages/velinstyle-skills/skills/velin-release-changelog-sync/SKILL.md +57 -0
  210. package/packages/velinstyle-skills/skills/velin-release-meta-rebuild/SKILL.md +57 -0
  211. package/packages/velinstyle-skills/skills/velin-review-conversion-hero/SKILL.md +57 -0
  212. package/packages/velinstyle-skills/skills/velin-review-page-gate/SKILL.md +57 -0
  213. package/packages/velinstyle-skills/skills/velin-review-seo-meta/SKILL.md +57 -0
  214. package/packages/velinstyle-skills/skills/velin-security-sanitize-html/SKILL.md +57 -0
  215. package/packages/velinstyle-skills/skills/velin-security-scan-pii/SKILL.md +57 -0
  216. package/packages/velinstyle-skills/skills/velin-testing-a11y-suite/SKILL.md +57 -0
  217. package/packages/velinstyle-skills/skills/velin-testing-visual-smoke/SKILL.md +57 -0
  218. package/packages/velinstyle-skills/skills/velin-themes-apply-token/SKILL.md +57 -0
  219. package/packages/velinstyle-skills/skills/velin-themes-contrast-check/SKILL.md +57 -0
  220. package/packages/velinstyle-skills/skills/velin-utilities-layout-apply/SKILL.md +57 -0
  221. package/packages/velinstyle-skills/skills/velin-utilities-prefix-fix/SKILL.md +57 -0
  222. package/packages/velinstyle-skills/skills/velin-utilities-token-map/SKILL.md +57 -0
  223. package/packages/velinstyle-skills/templates/admin-panel.template.yaml +12 -0
  224. package/packages/velinstyle-skills/templates/blog.template.yaml +12 -0
  225. package/packages/velinstyle-skills/templates/documentation-portal.template.yaml +12 -0
  226. package/packages/velinstyle-skills/templates/ecommerce-shop.template.yaml +13 -0
  227. package/packages/velinstyle-skills/templates/landing-page.template.yaml +12 -0
  228. package/packages/velinstyle-skills/templates/laravel-dashboard.template.yaml +11 -0
  229. package/packages/velinstyle-skills/templates/marketing-website.template.yaml +13 -0
  230. package/packages/velinstyle-skills/templates/portfolio.template.yaml +12 -0
  231. package/schemas/component-knowledge.schema.json +33 -0
  232. package/schemas/design-constraint.schema.json +15 -0
  233. package/schemas/design-intelligence.schema.json +19 -0
  234. package/schemas/design-rule.schema.json +19 -0
  235. package/schemas/page-template.schema.json +24 -0
  236. package/schemas/review-report.schema.json +36 -0
  237. package/schemas/section.schema.json +22 -0
  238. package/schemas/skill-bundle.schema.json +15 -0
  239. package/schemas/skill-graph.schema.json +25 -0
  240. package/schemas/skill-pack.schema.json +14 -0
  241. package/schemas/skill-project.schema.json +16 -0
  242. package/schemas/skill-record.schema.json +27 -0
  243. package/schemas/skill-registry.schema.json +17 -0
  244. package/schemas/skill-template.schema.json +17 -0
  245. package/schemas/token-graph.schema.json +27 -0
  246. package/schemas/utility.schema.json +166 -0
  247. package/src/base/wc-placeholder.css +7 -0
  248. package/src/components/alert.css +22 -1
  249. package/src/components/calendar-dropzone.css +5 -0
  250. package/src/components/data-table.css +93 -0
  251. package/src/components/form-validation.css +40 -0
  252. package/src/layout/app-shell.css +33 -0
  253. package/src/tokens/shadow.css +13 -0
  254. package/src/utilities/border.css +2 -0
  255. package/src/utilities/display.css +1 -24
  256. package/src/utilities/responsive.css +16 -1
  257. package/src/utilities/spacing.css +32 -0
  258. package/src/velinstyle.css +3 -0
  259. package/dist/chunks/attributes-3BZLI7XF.js +0 -364
  260. package/dist/chunks/attributes-4N5JP4CG.js +0 -364
  261. package/dist/chunks/attributes-AIR7SVLK.js +0 -364
  262. package/dist/chunks/attributes-BGRN5XZO.js +0 -364
  263. package/dist/chunks/attributes-BVSQAYLR.js +0 -364
  264. package/dist/chunks/attributes-CDYFUY7Y.js +0 -364
  265. package/dist/chunks/attributes-JMYJZBPE.js +0 -364
  266. package/dist/chunks/attributes-R5JY3X4O.js +0 -374
  267. package/dist/chunks/attributes-TNSPQTHX.js +0 -374
  268. package/dist/chunks/chunk-2354NEBP.js +0 -109
  269. package/dist/chunks/chunk-2VNA7G7S.js +0 -109
  270. package/dist/chunks/chunk-AQLRJSGG.js +0 -109
  271. package/dist/chunks/chunk-EWPSDMQT.js +0 -109
  272. package/dist/chunks/chunk-LA7264K4.js +0 -85
  273. package/dist/chunks/chunk-QTJJ3DPO.js +0 -109
  274. package/dist/chunks/chunk-RZQVWFHW.js +0 -109
  275. package/dist/chunks/chunk-U4XHMZ7D.js +0 -109
  276. package/dist/chunks/chunk-V5YIUA6G.js +0 -109
  277. package/dist/chunks/chunk-ZPSPKUYD.js +0 -109
  278. package/dist/chunks/velin-bottom-nav-N3DFQR66.js +0 -86
  279. package/dist/chunks/velin-carousel-Y5VCOABZ.js +0 -213
  280. package/dist/chunks/velin-code-block-ASUBTAQV.js +0 -119
  281. package/dist/chunks/velin-code-block-Y6CM726Q.js +0 -119
  282. package/dist/chunks/velin-combobox-2KDTCQ7N.js +0 -155
  283. package/dist/chunks/velin-command-M7RNHUPR.js +0 -136
  284. package/dist/chunks/velin-copy-T5KDTDXP.js +0 -81
  285. package/dist/chunks/velin-countdown-5XSTN3BH.js +0 -101
  286. package/dist/chunks/velin-counter-ISCTEAHI.js +0 -118
  287. package/dist/chunks/velin-dialog-D2GUHSCD.js +0 -144
  288. package/dist/chunks/velin-dialog-IJLFDXAJ.js +0 -144
  289. package/dist/chunks/velin-dialog-TNS3WXMB.js +0 -144
  290. package/dist/chunks/velin-drawer-MJS6WCF7.js +0 -130
  291. package/dist/chunks/velin-email-N5B5ZYTE.js +0 -90
  292. package/dist/chunks/velin-icon-KZO3IEHG.js +0 -164
  293. package/dist/chunks/velin-lightbox-QOTB4PRL.js +0 -140
  294. package/dist/chunks/velin-lightbox-UCD6N7HF.js +0 -140
  295. package/dist/chunks/velin-live-dot-XHUZCIDZ.js +0 -67
  296. package/dist/chunks/velin-menubar-SFEXJE56.js +0 -86
  297. package/dist/chunks/velin-modal-2HOBXE6F.js +0 -176
  298. package/dist/chunks/velin-modal-GLI2XZZB.js +0 -176
  299. package/dist/chunks/velin-popover-EXINRIGJ.js +0 -145
  300. package/dist/chunks/velin-popover-OXX7MRZ6.js +0 -145
  301. package/dist/chunks/velin-popover-YKCCROJG.js +0 -145
  302. package/dist/chunks/velin-rating-XRQJV4LO.js +0 -91
  303. package/dist/chunks/velin-scroll-top-F2FCZNYD.js +0 -58
  304. package/dist/chunks/velin-search-4YHJWYSS.js +0 -556
  305. package/dist/chunks/velin-search-LWHY23Q5.js +0 -474
  306. package/dist/chunks/velin-secure-field-H7FSBUJW.js +0 -95
  307. package/dist/chunks/velin-segmented-control-PQX6LKOJ.js +0 -109
  308. package/dist/chunks/velin-sheet-3OGI4HD7.js +0 -119
  309. package/dist/chunks/velin-sparkline-GVEANJVW.js +0 -184
  310. package/dist/chunks/velin-sparkline-KDA3GQAN.js +0 -162
  311. package/dist/chunks/velin-stepper-2ZIHLKGM.js +0 -141
  312. package/dist/chunks/velin-stepper-FYEUEWE6.js +0 -141
  313. package/dist/chunks/velin-stepper-wc-QU3KRVQR.js +0 -133
  314. package/dist/chunks/velin-theme-toggle-BNNS7WLV.js +0 -285
  315. package/dist/chunks/velin-theme-toggle-NO7V4TWI.js +0 -285
  316. package/dist/chunks/velin-theme-toggle-QELCJM2Q.js +0 -291
  317. package/dist/chunks/velin-toast-RCZ4CSUC.js +0 -109
  318. package/dist/chunks/velin-toast-WEUBTJG6.js +0 -109
  319. package/dist/chunks/velin-tooltip-664IOHZA.js +0 -112
  320. package/dist/chunks/velin-tooltip-wc-LXDVT6DE.js +0 -103
  321. package/dist/chunks/worker-client-R6DBYPFT.js +0 -46
@@ -150,7 +150,10 @@ class VelinDialog extends HTMLElement {
150
150
  }
151
151
  this._resolve(value);
152
152
  this._resolve = null;
153
- this.dispatchEvent(new CustomEvent('velin-dialog-close', { bubbles: true, detail: { value } }));
153
+ const detail = { value };
154
+ this.dispatchEvent(new CustomEvent('velin-close', { bubbles: true, detail }));
155
+ // Deprecated alias — remove after deprecation window (ADR 0012)
156
+ this.dispatchEvent(new CustomEvent('velin-dialog-close', { bubbles: true, detail }));
154
157
  }
155
158
  }
156
159
 
@@ -88,7 +88,6 @@ class VelinDrawer extends HTMLElement {
88
88
  this._prev = saveFocus();
89
89
  setBackgroundInert(this);
90
90
  document.addEventListener('keydown', this._onKey);
91
- document.body.style.overflow = 'hidden';
92
91
  requestAnimationFrame(() => {
93
92
  const f = getFocusableElements(this.shadowRoot);
94
93
  if (f.length) f[0].focus();
@@ -97,8 +96,7 @@ class VelinDrawer extends HTMLElement {
97
96
 
98
97
  _close() {
99
98
  document.removeEventListener('keydown', this._onKey);
100
- document.body.style.overflow = '';
101
- clearBackgroundInert();
99
+ clearBackgroundInert(this);
102
100
  restoreFocus(this._prev);
103
101
  }
104
102
 
@@ -109,7 +107,7 @@ class VelinDrawer extends HTMLElement {
109
107
 
110
108
  disconnectedCallback() {
111
109
  document.removeEventListener('keydown', this._onKey);
112
- document.body.style.overflow = '';
110
+ clearBackgroundInert(this);
113
111
  }
114
112
  }
115
113
 
@@ -0,0 +1,162 @@
1
+ import { escapeHTML } from './sanitize.js';
2
+
3
+ const styles = `
4
+ :host { display: block; }
5
+ .zone {
6
+ border: 2px dashed var(--velin-color-border, #cbd5e1);
7
+ border-radius: var(--velin-radius-md, 0.5rem);
8
+ padding: var(--velin-space-6, 1.5rem);
9
+ background: var(--velin-color-surface-dim, var(--velin-color-bg-subtle, #f8fafc));
10
+ color: var(--velin-color-text, #111);
11
+ text-align: center;
12
+ transition: border-color 150ms ease, background 150ms ease;
13
+ }
14
+ :host([dragging]) .zone {
15
+ border-color: var(--velin-color-primary, #2563eb);
16
+ background: var(--velin-color-primary-subtle, #eff6ff);
17
+ }
18
+ .hint { margin: 0 0 var(--velin-space-3, 0.75rem); color: var(--velin-color-text-muted, #64748b); }
19
+ .browse {
20
+ display: inline-flex; align-items: center; justify-content: center;
21
+ min-block-size: 2.75rem; padding-inline: var(--velin-space-4, 1rem);
22
+ border-radius: var(--velin-radius-md, 0.5rem);
23
+ border: none; cursor: pointer;
24
+ background: var(--velin-color-primary, #2563eb);
25
+ color: var(--velin-color-on-primary, #fff);
26
+ font: inherit;
27
+ }
28
+ .browse:focus-visible { outline: 2px solid var(--velin-color-focus, #2563eb); outline-offset: 2px; }
29
+ input[type="file"] {
30
+ position: absolute; inline-size: 1px; block-size: 1px; padding: 0; margin: -1px;
31
+ overflow: hidden; clip: rect(0,0,0,0); border: 0;
32
+ }
33
+ .list { list-style: none; padding: 0; margin: var(--velin-space-4, 1rem) 0 0; text-align: start; }
34
+ .list li {
35
+ display: flex; justify-content: space-between; gap: var(--velin-space-3, 0.75rem);
36
+ padding: var(--velin-space-2, 0.5rem) 0;
37
+ border-block-end: 1px solid var(--velin-color-border, #e2e8f0);
38
+ font-size: var(--velin-text-sm, 0.875rem);
39
+ }
40
+ .progress {
41
+ margin-block-start: var(--velin-space-3, 0.75rem);
42
+ block-size: 0.5rem; border-radius: 999px;
43
+ background: var(--velin-color-border, #e2e8f0); overflow: hidden;
44
+ }
45
+ .bar {
46
+ block-size: 100%; inline-size: 0%;
47
+ background: var(--velin-color-primary, #2563eb);
48
+ transition: inline-size 150ms ease;
49
+ }
50
+ .status { margin: var(--velin-space-2, 0.5rem) 0 0; font-size: var(--velin-text-sm, 0.875rem); }
51
+ .status[data-tone="error"] { color: var(--velin-color-danger, #b91c1c); }
52
+ `;
53
+
54
+ class VelinFileDropzone extends HTMLElement {
55
+ static get observedAttributes() {
56
+ return ['accept', 'multiple', 'label', 'progress'];
57
+ }
58
+
59
+ constructor() {
60
+ super();
61
+ this.attachShadow({ mode: 'open' });
62
+ this._files = [];
63
+ }
64
+
65
+ connectedCallback() {
66
+ this._render();
67
+ }
68
+
69
+ attributeChangedCallback() {
70
+ if (this.shadowRoot?.querySelector('.zone')) this._render();
71
+ }
72
+
73
+ get files() {
74
+ return this._files.slice();
75
+ }
76
+
77
+ _emitFiles(fileList) {
78
+ const accept = this.getAttribute('accept');
79
+ const files = [...fileList];
80
+ if (accept) {
81
+ const parts = accept.split(',').map((s) => s.trim().toLowerCase());
82
+ const bad = files.find((f) => {
83
+ const name = f.name.toLowerCase();
84
+ const type = (f.type || '').toLowerCase();
85
+ return !parts.some((p) => (p.startsWith('.') ? name.endsWith(p) : type === p || type.startsWith(p.replace('/*', '/'))));
86
+ });
87
+ if (bad) {
88
+ this._setStatus(`File type not allowed: ${bad.name}`, 'error');
89
+ this.dispatchEvent(new CustomEvent('velin-error', { bubbles: true, detail: { message: 'accept', file: bad } }));
90
+ return;
91
+ }
92
+ }
93
+ this._files = files;
94
+ this._setStatus(files.length ? `${files.length} file(s) ready` : '', 'ok');
95
+ this.dispatchEvent(new CustomEvent('velin-files', { bubbles: true, detail: { files } }));
96
+ this._renderList();
97
+ }
98
+
99
+ _setStatus(text, tone = 'ok') {
100
+ const el = this.shadowRoot?.querySelector('.status');
101
+ if (!el) return;
102
+ el.textContent = text;
103
+ el.dataset.tone = tone;
104
+ }
105
+
106
+ _renderList() {
107
+ const ul = this.shadowRoot?.querySelector('.list');
108
+ if (!ul) return;
109
+ ul.innerHTML = this._files.map((f) => `<li><span>${escapeHTML(f.name)}</span><span>${Math.round(f.size / 1024)} KB</span></li>`).join('');
110
+ }
111
+
112
+ _render() {
113
+ const label = escapeHTML(this.getAttribute('label') || 'Upload files');
114
+ const accept = this.getAttribute('accept') || '';
115
+ const multiple = this.hasAttribute('multiple');
116
+ const progress = Math.max(0, Math.min(100, Number(this.getAttribute('progress') || 0)));
117
+
118
+ this.shadowRoot.innerHTML = `
119
+ <style>${styles}</style>
120
+ <div class="zone" role="group" aria-label="${label}">
121
+ <p class="hint">Drag and drop files here, or browse. Client-side only — wire <code>velin-files</code> to your upload API.</p>
122
+ <button type="button" class="browse">Browse files</button>
123
+ <input type="file" ${accept ? `accept="${escapeHTML(accept)}"` : ''} ${multiple ? 'multiple' : ''} />
124
+ <div class="progress" hidden="${progress <= 0 ? 'true' : 'false'}" aria-hidden="${progress <= 0 ? 'true' : 'false'}">
125
+ <div class="bar" style="inline-size:${progress}%"></div>
126
+ </div>
127
+ <p class="status" role="status" aria-live="polite"></p>
128
+ <ul class="list"></ul>
129
+ </div>
130
+ `;
131
+
132
+ const input = this.shadowRoot.querySelector('input');
133
+ const browse = this.shadowRoot.querySelector('.browse');
134
+ const zone = this.shadowRoot.querySelector('.zone');
135
+ browse.addEventListener('click', () => input.click());
136
+ input.addEventListener('change', () => {
137
+ if (input.files?.length) this._emitFiles(input.files);
138
+ });
139
+
140
+ ['dragenter', 'dragover'].forEach((ev) => {
141
+ zone.addEventListener(ev, (e) => {
142
+ e.preventDefault();
143
+ this.setAttribute('dragging', '');
144
+ });
145
+ });
146
+ ['dragleave', 'drop'].forEach((ev) => {
147
+ zone.addEventListener(ev, (e) => {
148
+ e.preventDefault();
149
+ this.removeAttribute('dragging');
150
+ });
151
+ });
152
+ zone.addEventListener('drop', (e) => {
153
+ const list = e.dataTransfer?.files;
154
+ if (list?.length) this._emitFiles(list);
155
+ });
156
+
157
+ this._renderList();
158
+ }
159
+ }
160
+
161
+ customElements.define('velin-file-dropzone', VelinFileDropzone);
162
+ export default VelinFileDropzone;
@@ -0,0 +1,348 @@
1
+ import { announce } from './a11y-utils.js';
2
+
3
+ /**
4
+ * Accessible error summary for a light-DOM `<form>`.
5
+ *
6
+ * Native constraint validation shows a transient browser bubble on one field at
7
+ * a time, which is unusable with a screen reader or on a long form. This element
8
+ * takes over submit handling to build a persistent, focusable summary, wire
9
+ * `aria-invalid` and `aria-describedby` per field, and announce the error count.
10
+ *
11
+ * Covers WCAG 3.3.1 Error Identification, 3.3.3 Error Suggestion and the
12
+ * `aria-describedby` half of 4.1.2 Name, Role, Value.
13
+ */
14
+
15
+ const FIELD_SELECTOR = 'input, select, textarea';
16
+ const IGNORED_TYPES = new Set(['submit', 'reset', 'button', 'image', 'hidden']);
17
+
18
+ let fieldIdCounter = 0;
19
+
20
+ /** `CSS.escape` is missing in some non-browser DOM implementations. */
21
+ function escapeSelector(value) {
22
+ const text = String(value ?? '');
23
+ if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') return CSS.escape(text);
24
+ return text.replace(/[^\w-]/g, (char) => `\\${char}`);
25
+ }
26
+
27
+ /** @param {HTMLElement} field */
28
+ function fieldLabel(field) {
29
+ const explicit = field.getAttribute('data-error-label');
30
+ if (explicit) return explicit;
31
+
32
+ const ariaLabel = field.getAttribute('aria-label');
33
+ if (ariaLabel?.trim()) return ariaLabel.trim();
34
+
35
+ const labelledBy = field.getAttribute('aria-labelledby');
36
+ if (labelledBy) {
37
+ const text = labelledBy
38
+ .split(/\s+/)
39
+ .map((id) => field.ownerDocument.getElementById(id)?.textContent?.trim() || '')
40
+ .filter(Boolean)
41
+ .join(' ');
42
+ if (text) return text;
43
+ }
44
+
45
+ if (field.id) {
46
+ const label = field.ownerDocument.querySelector(`label[for="${escapeSelector(field.id)}"]`);
47
+ if (label?.textContent.trim()) return label.textContent.trim();
48
+ }
49
+
50
+ const wrapping = field.closest('label');
51
+ if (wrapping?.textContent.trim()) return wrapping.textContent.trim();
52
+
53
+ return field.name || 'This field';
54
+ }
55
+
56
+ /** @param {HTMLElement} field */
57
+ function fieldMessage(field) {
58
+ return field.getAttribute('data-error-message')?.trim() || field.validationMessage || 'Invalid value';
59
+ }
60
+
61
+ /**
62
+ * @param {HTMLElement} field
63
+ * @param {string} id
64
+ */
65
+ function addDescribedBy(field, id) {
66
+ const ids = (field.getAttribute('aria-describedby') || '').split(/\s+/).filter(Boolean);
67
+ if (!ids.includes(id)) {
68
+ ids.push(id);
69
+ field.setAttribute('aria-describedby', ids.join(' '));
70
+ }
71
+ }
72
+
73
+ /**
74
+ * @param {HTMLElement} field
75
+ * @param {string} id
76
+ */
77
+ function removeDescribedBy(field, id) {
78
+ const ids = (field.getAttribute('aria-describedby') || '').split(/\s+/).filter(Boolean);
79
+ const next = ids.filter((value) => value !== id);
80
+ if (next.length) field.setAttribute('aria-describedby', next.join(' '));
81
+ else field.removeAttribute('aria-describedby');
82
+ }
83
+
84
+ class VelinFormSummary extends HTMLElement {
85
+ static get observedAttributes() {
86
+ return ['for', 'heading'];
87
+ }
88
+
89
+ constructor() {
90
+ super();
91
+ this._form = null;
92
+ this._panel = null;
93
+ this._errors = [];
94
+ this._onSubmit = this._onSubmit.bind(this);
95
+ this._onFieldChange = this._onFieldChange.bind(this);
96
+ this._onReset = this._onReset.bind(this);
97
+ }
98
+
99
+ connectedCallback() {
100
+ this.classList.add('velin-form-summary');
101
+ requestAnimationFrame(() => this._bindForm());
102
+ }
103
+
104
+ disconnectedCallback() {
105
+ this._unbindForm();
106
+ }
107
+
108
+ attributeChangedCallback(name, previous, next) {
109
+ if (previous === next) return;
110
+ if (name === 'for' && this.isConnected) {
111
+ this._unbindForm();
112
+ this._bindForm();
113
+ } else if (name === 'heading' && this._panel) {
114
+ const heading = this._panel.querySelector('.velin-form-summary__heading');
115
+ if (heading) heading.textContent = this.headingText;
116
+ }
117
+ }
118
+
119
+ // ── Public API ─────────────────────────────────────────────────────────────
120
+
121
+ get form() {
122
+ return this._form;
123
+ }
124
+
125
+ /** @returns {{ field: HTMLElement, label: string, message: string }[]} */
126
+ get errors() {
127
+ return this._errors.slice();
128
+ }
129
+
130
+ get headingText() {
131
+ return this.getAttribute('heading') || 'There is a problem';
132
+ }
133
+
134
+ /** Validate the form and render the summary. @returns {boolean} valid */
135
+ validate() {
136
+ if (!this._form) return true;
137
+ const errors = [];
138
+ for (const field of this._fields()) {
139
+ if (field.checkValidity()) {
140
+ this._clearFieldError(field);
141
+ continue;
142
+ }
143
+ const error = { field, label: fieldLabel(field), message: fieldMessage(field) };
144
+ this._markFieldError(field, error.message);
145
+ errors.push(error);
146
+ }
147
+ this._errors = errors;
148
+ this._render();
149
+ return errors.length === 0;
150
+ }
151
+
152
+ /** Remove the summary and all field error state. */
153
+ clear() {
154
+ for (const field of this._fields()) this._clearFieldError(field);
155
+ this._errors = [];
156
+ this._render();
157
+ }
158
+
159
+ /** Move focus to the first field with an error. */
160
+ focusFirstError() {
161
+ const first = this._errors[0];
162
+ if (first) this._focusField(first.field);
163
+ }
164
+
165
+ // ── Form wiring ────────────────────────────────────────────────────────────
166
+
167
+ _bindForm() {
168
+ const id = this.getAttribute('for');
169
+ this._form = id ? this.ownerDocument.getElementById(id) : this.closest('form');
170
+ if (!this._form) return;
171
+
172
+ // Native bubbles show one error at a time and vanish; the summary replaces them.
173
+ if (!this.hasAttribute('native-validation')) this._form.noValidate = true;
174
+
175
+ this._form.addEventListener('submit', this._onSubmit);
176
+ this._form.addEventListener('reset', this._onReset);
177
+ this._form.addEventListener('input', this._onFieldChange);
178
+ this._form.addEventListener('change', this._onFieldChange);
179
+ }
180
+
181
+ _unbindForm() {
182
+ if (!this._form) return;
183
+ this._form.removeEventListener('submit', this._onSubmit);
184
+ this._form.removeEventListener('reset', this._onReset);
185
+ this._form.removeEventListener('input', this._onFieldChange);
186
+ this._form.removeEventListener('change', this._onFieldChange);
187
+ this._form = null;
188
+ }
189
+
190
+ /** @returns {HTMLElement[]} */
191
+ _fields() {
192
+ if (!this._form) return [];
193
+ const seenRadioNames = new Set();
194
+ return [...this._form.querySelectorAll(FIELD_SELECTOR)].filter((field) => {
195
+ if (IGNORED_TYPES.has(field.type)) return false;
196
+ if (field.disabled || field.hasAttribute('data-error-ignore')) return false;
197
+ if (typeof field.checkValidity !== 'function') return false;
198
+ // One entry per radio group so the summary lists the group once.
199
+ if (field.type === 'radio' && field.name) {
200
+ if (seenRadioNames.has(field.name)) return false;
201
+ seenRadioNames.add(field.name);
202
+ }
203
+ return true;
204
+ });
205
+ }
206
+
207
+ _onSubmit(event) {
208
+ if (this.validate()) return;
209
+ event.preventDefault();
210
+ this._announceErrors();
211
+ this._focusPanel();
212
+ this.dispatchEvent(new CustomEvent('velin-form-invalid', {
213
+ bubbles: true,
214
+ detail: { errors: this.errors.map(({ label, message }) => ({ label, message })) },
215
+ }));
216
+ }
217
+
218
+ _onReset() {
219
+ requestAnimationFrame(() => this.clear());
220
+ }
221
+
222
+ /** Re-validate a single field once it already had an error, never before. */
223
+ _onFieldChange(event) {
224
+ const field = event.target;
225
+ if (!field || !this._errors.some((error) => error.field === field)) return;
226
+ if (!field.checkValidity()) return;
227
+
228
+ this._clearFieldError(field);
229
+ this._errors = this._errors.filter((error) => error.field !== field);
230
+ this._render();
231
+ if (this._errors.length === 0) {
232
+ this.dispatchEvent(new CustomEvent('velin-form-valid', { bubbles: true }));
233
+ }
234
+ }
235
+
236
+ // ── Field state ────────────────────────────────────────────────────────────
237
+
238
+ /** @param {HTMLElement} field */
239
+ _errorId(field) {
240
+ if (!field.id) field.id = `velin-field-${++fieldIdCounter}`;
241
+ return `${field.id}-error`;
242
+ }
243
+
244
+ /**
245
+ * @param {HTMLElement} field
246
+ * @param {string} message
247
+ */
248
+ _markFieldError(field, message) {
249
+ const errorId = this._errorId(field);
250
+ field.setAttribute('aria-invalid', 'true');
251
+
252
+ let holder = this.ownerDocument.getElementById(errorId);
253
+ if (!holder) {
254
+ holder = this._form.querySelector(`[data-velin-error-for="${escapeSelector(field.name || field.id)}"]`);
255
+ }
256
+ if (!holder) {
257
+ holder = this.ownerDocument.createElement('p');
258
+ holder.dataset.velinErrorGenerated = 'true';
259
+ field.insertAdjacentElement('afterend', holder);
260
+ }
261
+ holder.id = errorId;
262
+ holder.classList.add('velin-field-error');
263
+ holder.textContent = message;
264
+ addDescribedBy(field, errorId);
265
+ }
266
+
267
+ /** @param {HTMLElement} field */
268
+ _clearFieldError(field) {
269
+ if (!field.id) return;
270
+ const errorId = `${field.id}-error`;
271
+ field.removeAttribute('aria-invalid');
272
+ removeDescribedBy(field, errorId);
273
+
274
+ const holder = this.ownerDocument.getElementById(errorId);
275
+ if (!holder) return;
276
+ if (holder.dataset.velinErrorGenerated) holder.remove();
277
+ else holder.textContent = '';
278
+ }
279
+
280
+ /** @param {HTMLElement} field */
281
+ _focusField(field) {
282
+ const target = field.type === 'radio' && field.name
283
+ ? this._form.querySelector(`input[type="radio"][name="${escapeSelector(field.name)}"]`) || field
284
+ : field;
285
+ target.focus();
286
+ this.dispatchEvent(new CustomEvent('velin-form-error-focus', {
287
+ bubbles: true,
288
+ detail: { name: target.name || target.id },
289
+ }));
290
+ }
291
+
292
+ // ── Summary panel ──────────────────────────────────────────────────────────
293
+
294
+ _render() {
295
+ if (!this._errors.length) {
296
+ this._panel?.remove();
297
+ this._panel = null;
298
+ this.hidden = true;
299
+ return;
300
+ }
301
+
302
+ this.hidden = false;
303
+ if (!this._panel?.isConnected) {
304
+ const panel = this.ownerDocument.createElement('div');
305
+ panel.className = 'velin-form-summary__panel velin-alert velin-alert--danger';
306
+ panel.setAttribute('role', 'alert');
307
+ panel.tabIndex = -1;
308
+
309
+ const heading = this.ownerDocument.createElement('p');
310
+ heading.className = 'velin-form-summary__heading';
311
+ heading.textContent = this.headingText;
312
+
313
+ const list = this.ownerDocument.createElement('ul');
314
+ list.className = 'velin-form-summary__list';
315
+
316
+ panel.append(heading, list);
317
+ this.appendChild(panel);
318
+ this._panel = panel;
319
+ }
320
+
321
+ const list = this._panel.querySelector('.velin-form-summary__list');
322
+ list.textContent = '';
323
+ for (const error of this._errors) {
324
+ const item = this.ownerDocument.createElement('li');
325
+ const link = this.ownerDocument.createElement('a');
326
+ link.href = `#${this._errorId(error.field).replace(/-error$/, '')}`;
327
+ link.textContent = `${error.label}: ${error.message}`;
328
+ link.addEventListener('click', (event) => {
329
+ event.preventDefault();
330
+ this._focusField(error.field);
331
+ });
332
+ item.appendChild(link);
333
+ list.appendChild(item);
334
+ }
335
+ }
336
+
337
+ _focusPanel() {
338
+ this._panel?.focus();
339
+ }
340
+
341
+ _announceErrors() {
342
+ const count = this._errors.length;
343
+ announce(count === 1 ? '1 field needs attention' : `${count} fields need attention`, 'assertive');
344
+ }
345
+ }
346
+
347
+ customElements.define('velin-form-summary', VelinFormSummary);
348
+ export default VelinFormSummary;
@@ -32,8 +32,28 @@ function resolveProviderUrl(provider, variant) {
32
32
  }
33
33
 
34
34
  const _svgCache = new Map();
35
+ const DEFAULT_SPRITE = 'velin-icons.svg';
36
+
37
+ /**
38
+ * Resolve default sprite URL: attribute → meta/html data → VelinIcon.defaultSprite → relative fallback.
39
+ * Avoids hardcoded absolute `/dist/...` which breaks offline/vendor installs.
40
+ */
41
+ function resolveDefaultSpriteUrl() {
42
+ if (typeof document !== 'undefined') {
43
+ const meta = document.querySelector('meta[name="velin-icon-sprite"]');
44
+ const fromMeta = meta?.getAttribute('content')?.trim();
45
+ if (fromMeta) return fromMeta;
46
+ const fromHtml = document.documentElement?.getAttribute('data-velin-icon-sprite')?.trim();
47
+ if (fromHtml) return fromHtml;
48
+ }
49
+ const configured = typeof VelinIcon.defaultSprite === 'string' ? VelinIcon.defaultSprite.trim() : '';
50
+ return configured || DEFAULT_SPRITE;
51
+ }
35
52
 
36
53
  class VelinIcon extends HTMLElement {
54
+ /** @type {string} Relative or absolute sprite URL used when `sprite` attribute is omitted. */
55
+ static defaultSprite = DEFAULT_SPRITE;
56
+
37
57
  static get observedAttributes() {
38
58
  return ['name', 'size', 'label', 'provider', 'variant', 'sprite'];
39
59
  }
@@ -99,7 +119,8 @@ class VelinIcon extends HTMLElement {
99
119
  if (spriteAttr === '' || (spriteAttr == null && isLocalSymbol)) {
100
120
  href = `#${name}`;
101
121
  } else {
102
- const spriteUrl = sanitizeURL(spriteAttr || '/dist/velin-icons.svg') || '/dist/velin-icons.svg';
122
+ const fallback = resolveDefaultSpriteUrl();
123
+ const spriteUrl = sanitizeURL(spriteAttr || fallback) || fallback || DEFAULT_SPRITE;
103
124
  href = `${spriteUrl}#${name}`;
104
125
  }
105
126
  use.setAttribute('href', href);
@@ -92,7 +92,6 @@ class VelinLightbox extends HTMLElement {
92
92
  this.setAttribute('open', '');
93
93
  setBackgroundInert(this);
94
94
  this._render();
95
- document.body.style.overflow = 'hidden';
96
95
  const overlay = this.shadowRoot.querySelector('.overlay');
97
96
  overlay.removeEventListener('keydown', this._onTrapKey);
98
97
  overlay.addEventListener('keydown', this._onTrapKey);
@@ -101,8 +100,7 @@ class VelinLightbox extends HTMLElement {
101
100
 
102
101
  close() {
103
102
  this.removeAttribute('open');
104
- clearBackgroundInert();
105
- document.body.style.overflow = '';
103
+ clearBackgroundInert(this);
106
104
  if (this._previousFocus) {
107
105
  this._previousFocus.focus();
108
106
  this._previousFocus = null;
@@ -110,6 +108,10 @@ class VelinLightbox extends HTMLElement {
110
108
  this.dispatchEvent(new CustomEvent('velin-close', { bubbles: true }));
111
109
  }
112
110
 
111
+ disconnectedCallback() {
112
+ clearBackgroundInert(this);
113
+ }
114
+
113
115
  _prev() { this._index = (this._index - 1 + this._items.length) % this._items.length; this._render(); }
114
116
  _next() { this._index = (this._index + 1) % this._items.length; this._render(); }
115
117
 
@@ -146,7 +146,6 @@ class VelinModal extends HTMLElement {
146
146
  this._previouslyFocused = saveFocus();
147
147
  setBackgroundInert(this);
148
148
  document.addEventListener('keydown', this._onKeydown);
149
- document.body.style.overflow = 'hidden';
150
149
 
151
150
  requestAnimationFrame(() => {
152
151
  const focusable = getFocusableElements(this.shadowRoot);
@@ -156,8 +155,7 @@ class VelinModal extends HTMLElement {
156
155
 
157
156
  _close() {
158
157
  document.removeEventListener('keydown', this._onKeydown);
159
- document.body.style.overflow = '';
160
- clearBackgroundInert();
158
+ clearBackgroundInert(this);
161
159
  restoreFocus(this._previouslyFocused);
162
160
  }
163
161
 
@@ -171,7 +169,7 @@ class VelinModal extends HTMLElement {
171
169
 
172
170
  disconnectedCallback() {
173
171
  document.removeEventListener('keydown', this._onKeydown);
174
- document.body.style.overflow = '';
172
+ clearBackgroundInert(this);
175
173
  }
176
174
  }
177
175
 
@@ -129,7 +129,7 @@ class VelinSearch extends HTMLElement {
129
129
  return;
130
130
  }
131
131
  if (!this._indexLoaded) await this._loadIndex();
132
- const { groups } = this._search.query(q, {
132
+ const { groups } = await this._search.query(q, {
133
133
  minChars: this.minChars,
134
134
  fuzzy: this.fuzzy,
135
135
  categories: this.categories || undefined,
@@ -78,7 +78,6 @@ class VelinSheet extends HTMLElement {
78
78
  this._prev = saveFocus();
79
79
  setBackgroundInert(this);
80
80
  document.addEventListener('keydown', this._onKey);
81
- document.body.style.overflow = 'hidden';
82
81
  requestAnimationFrame(() => {
83
82
  const f = getFocusableElements(this.shadowRoot);
84
83
  if (f.length) f[0].focus();
@@ -87,8 +86,7 @@ class VelinSheet extends HTMLElement {
87
86
 
88
87
  _close() {
89
88
  document.removeEventListener('keydown', this._onKey);
90
- document.body.style.overflow = '';
91
- clearBackgroundInert();
89
+ clearBackgroundInert(this);
92
90
  restoreFocus(this._prev);
93
91
  }
94
92
 
@@ -99,7 +97,7 @@ class VelinSheet extends HTMLElement {
99
97
 
100
98
  disconnectedCallback() {
101
99
  document.removeEventListener('keydown', this._onKey);
102
- document.body.style.overflow = '';
100
+ clearBackgroundInert(this);
103
101
  }
104
102
  }
105
103