adp-web-components 0.3.18 → 0.3.20

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 (364) hide show
  1. package/dist/cjs/{branch-slot-dropdown_15.cjs.entry.js → branch-date-dropdown_17.cjs.entry.js} +958 -68
  2. package/dist/cjs/dead-stock-lookup_3.cjs.entry.js +9 -9
  3. package/dist/cjs/flexible-container.cjs.entry.js +3 -3
  4. package/dist/cjs/form-checkbox.cjs.entry.js +2 -2
  5. package/dist/cjs/form-date-picker.cjs.entry.js +5 -5
  6. package/dist/cjs/form-input_7.cjs.entry.js +21 -19
  7. package/dist/cjs/form-switch.cjs.entry.js +2 -2
  8. package/dist/cjs/form-time-picker.cjs.entry.js +6 -6
  9. package/dist/cjs/{functions-C9XsBzrL.js → functions-BAUJUMt-.js} +44 -19
  10. package/dist/cjs/general-form.cjs.entry.js +6 -6
  11. package/dist/cjs/general-inquiry-form.cjs.entry.js +6 -6
  12. package/dist/cjs/{get-local-language-BnUadlVY.js → get-local-language-DI_UK6D5.js} +1 -1
  13. package/dist/cjs/{get-mock-files-Dr5dpq1N.js → get-mock-files-B1tNGb9m.js} +1 -1
  14. package/dist/cjs/information-table.cjs.entry.js +1 -1
  15. package/dist/cjs/loader.cjs.js +1 -1
  16. package/dist/cjs/loading-spinner.cjs.entry.js +2 -2
  17. package/dist/cjs/manufacturer-part-lookup.cjs.entry.js +3 -3
  18. package/dist/cjs/{mappers-BlH-TAo7.js → mappers-DnWgECCv.js} +54 -18
  19. package/dist/cjs/part-lookup.cjs.entry.js +2 -2
  20. package/dist/cjs/service-booking-form.cjs.entry.js +6 -6
  21. package/dist/cjs/shift-accordion.cjs.entry.js +2 -2
  22. package/dist/cjs/shift-checkbox.cjs.entry.js +2 -2
  23. package/dist/cjs/shift-components.cjs.js +1 -1
  24. package/dist/cjs/shift-slider.cjs.entry.js +1 -1
  25. package/dist/cjs/shift-switch.cjs.entry.js +2 -2
  26. package/dist/cjs/shift-tab-content.cjs.entry.js +2 -2
  27. package/dist/cjs/shift-tabs_2.cjs.entry.js +92 -34
  28. package/dist/cjs/ssc-lookup-form.cjs.entry.js +6 -6
  29. package/dist/cjs/test-drive-form.cjs.entry.js +6 -6
  30. package/dist/cjs/triangle-alert-CTvU1eGY.js +248 -0
  31. package/dist/cjs/vehicle-accessories_8.cjs.entry.js +177 -117
  32. package/dist/cjs/vehicle-lookup.cjs.entry.js +3 -3
  33. package/dist/cjs/vehicle-quotation-form.cjs.entry.js +17 -18
  34. package/dist/collection/collection-manifest.json +2 -0
  35. package/dist/collection/components/components/empty.css +1 -1
  36. package/dist/collection/components/components/flexible-container.css +1 -1
  37. package/dist/collection/components/components/flexible-container.js +2 -2
  38. package/dist/collection/components/components/information-table.css +1 -1
  39. package/dist/collection/components/components/loading-spinner.css +1 -1
  40. package/dist/collection/components/components/loading-spinner.js +1 -1
  41. package/dist/collection/components/components/shift-accordion.css +1 -1
  42. package/dist/collection/components/components/shift-accordion.js +1 -1
  43. package/dist/collection/components/components/shift-checkbox.js +1 -1
  44. package/dist/collection/components/components/shift-portal.css +1 -1
  45. package/dist/collection/components/components/shift-select-dropdown.css +1 -1
  46. package/dist/collection/components/components/shift-select-dropdown.js +2 -2
  47. package/dist/collection/components/components/shift-select.css +1 -1
  48. package/dist/collection/components/components/shift-select.js +3 -3
  49. package/dist/collection/components/components/shift-slider.js +1 -1
  50. package/dist/collection/components/components/shift-switch.js +1 -1
  51. package/dist/collection/components/components/shift-tab-content.css +1 -1
  52. package/dist/collection/components/components/shift-tab-content.js +1 -1
  53. package/dist/collection/components/components/shift-tabs.css +1 -1
  54. package/dist/collection/components/components/shift-tabs.js +2 -2
  55. package/dist/collection/components/form-elements/branch-date-dropdown.css +1 -0
  56. package/dist/collection/components/form-elements/branch-date-dropdown.js +682 -0
  57. package/dist/collection/components/form-elements/branch-date-picker.css +1 -0
  58. package/dist/collection/components/form-elements/branch-date-picker.js +1197 -0
  59. package/dist/collection/components/form-elements/branch-slot-dropdown.css +1 -1
  60. package/dist/collection/components/form-elements/branch-slot-dropdown.js +11 -6
  61. package/dist/collection/components/form-elements/branch-slot-picker.css +1 -1
  62. package/dist/collection/components/form-elements/branch-slot-picker.js +166 -18
  63. package/dist/collection/components/form-elements/form-checkbox.js +1 -1
  64. package/dist/collection/components/form-elements/form-date-picker-legacy.js +4 -4
  65. package/dist/collection/components/form-elements/form-dialog.css +1 -1
  66. package/dist/collection/components/form-elements/form-dialog.js +1 -1
  67. package/dist/collection/components/form-elements/form-file.js +2 -2
  68. package/dist/collection/components/form-elements/form-input.js +6 -4
  69. package/dist/collection/components/form-elements/form-inputs.css +1 -1
  70. package/dist/collection/components/form-elements/form-phone-number.js +4 -2
  71. package/dist/collection/components/form-elements/form-picker-input.js +6 -4
  72. package/dist/collection/components/form-elements/form-shadow-input.js +1 -1
  73. package/dist/collection/components/form-elements/form-stepper-control.js +2 -2
  74. package/dist/collection/components/form-elements/form-stepper-submit.js +2 -2
  75. package/dist/collection/components/form-elements/form-stepper.js +1 -1
  76. package/dist/collection/components/form-elements/form-structure-error.js +1 -1
  77. package/dist/collection/components/form-elements/form-submit.js +2 -2
  78. package/dist/collection/components/form-elements/form-switch.js +1 -1
  79. package/dist/collection/components/form-elements/form-text-area.js +6 -4
  80. package/dist/collection/components/form-elements/form-time-picker-legacy.js +5 -5
  81. package/dist/collection/components/form-elements/form-vin-input.js +6 -4
  82. package/dist/collection/components/forms/defaults/mappers.js +54 -18
  83. package/dist/collection/components/forms/defaults/style.css +1 -1
  84. package/dist/collection/components/forms/general-form.js +2 -2
  85. package/dist/collection/components/forms/general-inquiry.js +2 -2
  86. package/dist/collection/components/forms/service-booking.js +2 -2
  87. package/dist/collection/components/forms/ssc-lookup.js +2 -2
  88. package/dist/collection/components/forms/test-drive.js +2 -2
  89. package/dist/collection/components/forms/vehicle-quotation/themes.css +1 -1
  90. package/dist/collection/components/forms/vehicle-quotation.js +15 -16
  91. package/dist/collection/components/part-lookup/dead-stock-lookup.css +1 -1
  92. package/dist/collection/components/part-lookup/dead-stock-lookup.js +2 -2
  93. package/dist/collection/components/part-lookup/distributor-lookup.css +1 -1
  94. package/dist/collection/components/part-lookup/distributor-lookup.js +2 -2
  95. package/dist/collection/components/part-lookup/manufacturer-lookup.css +1 -1
  96. package/dist/collection/components/part-lookup/manufacturer-part-lookup.css +1 -1
  97. package/dist/collection/components/part-lookup/manufacturer-part-lookup.js +1 -1
  98. package/dist/collection/components/part-lookup/part-lookup.css +1 -1
  99. package/dist/collection/components/vehicle-lookup/components/claimable-item-popover.js +37 -29
  100. package/dist/collection/components/vehicle-lookup/components/claimable-trace-modal.js +6 -2
  101. package/dist/collection/components/vehicle-lookup/components/service-history-sub-row.js +3 -3
  102. package/dist/collection/components/vehicle-lookup/vehicle-accessories.css +1 -1
  103. package/dist/collection/components/vehicle-lookup/vehicle-accessories.js +2 -2
  104. package/dist/collection/components/vehicle-lookup/vehicle-claimable-items.css +1 -1
  105. package/dist/collection/components/vehicle-lookup/vehicle-claimable-items.js +121 -70
  106. package/dist/collection/components/vehicle-lookup/vehicle-item-claim-form.css +1 -1
  107. package/dist/collection/components/vehicle-lookup/vehicle-item-claim-form.js +89 -29
  108. package/dist/collection/components/vehicle-lookup/vehicle-lookup.css +1 -1
  109. package/dist/collection/components/vehicle-lookup/vehicle-paint-thickness.css +1 -1
  110. package/dist/collection/components/vehicle-lookup/vehicle-sale-information.css +1 -1
  111. package/dist/collection/components/vehicle-lookup/vehicle-sale-information.js +2 -2
  112. package/dist/collection/components/vehicle-lookup/vehicle-service-history.css +1 -1
  113. package/dist/collection/components/vehicle-lookup/vehicle-service-history.js +1 -1
  114. package/dist/collection/components/vehicle-lookup/vehicle-specification.css +1 -1
  115. package/dist/collection/components/vehicle-lookup/vehicle-specification.js +1 -1
  116. package/dist/collection/components/vehicle-lookup/vehicle-warranty-details.css +1 -1
  117. package/dist/collection/components/vehicle-lookup/vehicle-warranty-timeline.css +1 -1
  118. package/dist/collection/components/vehicle-lookup/vehicle-warranty-timeline.js +1 -1
  119. package/dist/collection/components/vin-extractor/vin-extractor.css +1 -1
  120. package/dist/collection/components/vin-extractor/vin-extractor.js +1 -1
  121. package/dist/collection/features/form-hook/functions.js +42 -18
  122. package/dist/collection/global/lib/overlay.js +154 -0
  123. package/dist/collection/global/lib/slot-day-rules.js +65 -0
  124. package/dist/collection/templates/assets/adp-wordmark.svg +1 -0
  125. package/dist/collection/templates/assets/favicon.svg +17 -0
  126. package/dist/collection/templates/forms/docs-content/params.js +1199 -0
  127. package/dist/collection/templates/forms/docs-content/structure.js +1153 -0
  128. package/dist/collection/templates/forms/docs-content/theming.js +1240 -0
  129. package/dist/collection/templates/harness-theme.js +44 -0
  130. package/dist/collection/templates/harness.js +775 -0
  131. package/dist/collection/templates/highlight.js +194 -0
  132. package/dist/collection/templates/nav.js +320 -0
  133. package/dist/collection/templates/site-header.js +642 -0
  134. package/dist/collection/templates/site-locales.js +771 -0
  135. package/dist/components/branch-date-dropdown.d.ts +11 -0
  136. package/dist/components/branch-date-dropdown.js +5 -0
  137. package/dist/components/branch-date-picker.d.ts +11 -0
  138. package/dist/components/branch-date-picker.js +5 -0
  139. package/dist/components/branch-slot-dropdown.js +1 -1
  140. package/dist/components/branch-slot-picker.js +1 -1
  141. package/dist/components/dead-stock-lookup.js +1 -1
  142. package/dist/components/distributor-lookup.js +1 -1
  143. package/dist/components/flexible-container.js +1 -1
  144. package/dist/components/form-checkbox.js +1 -1
  145. package/dist/components/form-date-picker.js +1 -1
  146. package/dist/components/form-dialog.js +1 -1
  147. package/dist/components/form-file.js +1 -1
  148. package/dist/components/form-input-preview.js +1 -1
  149. package/dist/components/form-input.js +1 -1
  150. package/dist/components/form-phone-number.js +1 -1
  151. package/dist/components/form-picker-input.js +1 -1
  152. package/dist/components/form-select.js +1 -1
  153. package/dist/components/form-shadow-input.js +1 -1
  154. package/dist/components/form-stepper-control.js +1 -1
  155. package/dist/components/form-stepper-submit.js +1 -1
  156. package/dist/components/form-stepper.js +1 -1
  157. package/dist/components/form-structure-error.js +1 -1
  158. package/dist/components/form-structure.js +1 -1
  159. package/dist/components/form-submit.js +1 -1
  160. package/dist/components/form-switch.js +1 -1
  161. package/dist/components/form-text-area.js +1 -1
  162. package/dist/components/form-time-picker.js +1 -1
  163. package/dist/components/form-vin-input.js +1 -1
  164. package/dist/components/general-form.js +1 -1
  165. package/dist/components/general-inquiry-form.js +1 -1
  166. package/dist/components/information-table.js +1 -1
  167. package/dist/components/loading-spinner.js +1 -1
  168. package/dist/components/manufacturer-lookup.js +1 -1
  169. package/dist/components/manufacturer-part-lookup.js +1 -1
  170. package/dist/components/p--lk4OUJg.js +5 -0
  171. package/dist/components/p-38ox9qxm.js +5 -0
  172. package/dist/components/p-5OD_mwiD.js +5 -0
  173. package/dist/components/{p-D7wRHBb3.js → p-68bjv9gT.js} +1 -1
  174. package/dist/components/p-B0_5_VvS.js +5 -0
  175. package/dist/components/p-B1xVBTqj.js +5 -0
  176. package/dist/components/{p-B0lK-5c_.js → p-B2bD-55K.js} +1 -1
  177. package/dist/components/{p-Bs5AIefE.js → p-BI5NL1Rh.js} +1 -1
  178. package/dist/components/{p-Be01Kv65.js → p-BPXRokES.js} +1 -1
  179. package/dist/components/{p-oZMLGlUq.js → p-BULuujHY.js} +1 -1
  180. package/dist/components/{p-B6EgLMus.js → p-BWlX-Nz-.js} +1 -1
  181. package/dist/components/p-Bc5HIbcj.js +5 -0
  182. package/dist/components/p-BcWJAP8j.js +5 -0
  183. package/dist/components/p-BpB_86n0.js +5 -0
  184. package/dist/components/p-BpHkd1L9.js +5 -0
  185. package/dist/components/{p-B0tcNl8W.js → p-BsnGFZeT.js} +1 -1
  186. package/dist/components/{p-B_jDD5xI.js → p-BvLVCp-j.js} +1 -1
  187. package/dist/components/p-C3KLOUbk.js +5 -0
  188. package/dist/components/{p-DgJ9CCPI.js → p-CJtTHErI.js} +1 -1
  189. package/dist/components/{p-DcB3e3j1.js → p-CMAmzdM7.js} +1 -1
  190. package/dist/components/p-CVpbr1tR.js +5 -0
  191. package/dist/components/{p-BVdlf8rx.js → p-CXbi5iPZ.js} +1 -1
  192. package/dist/components/p-C_1_eN4N.js +5 -0
  193. package/dist/components/{p-TtDq4UV5.js → p-Ce2mazYo.js} +1 -1
  194. package/dist/components/p-Cg6Prz81.js +5 -0
  195. package/dist/components/{p-l738xIxs.js → p-Cpjvk1pk.js} +1 -1
  196. package/dist/components/p-CqJiXtPb.js +5 -0
  197. package/dist/components/p-CrPyvAdX.js +5 -0
  198. package/dist/components/{p-C31A3MHr.js → p-CsU_c7eX.js} +1 -1
  199. package/dist/components/{p-Ci6k5mSb.js → p-D42YuMNZ.js} +1 -1
  200. package/dist/components/{p-Dhi7g1w3.js → p-DCpRGVqe.js} +1 -1
  201. package/dist/components/{p-D9pKibY5.js → p-DCsMDWbS.js} +1 -1
  202. package/dist/components/{p-BMnQwtNn.js → p-DQ0uTCi2.js} +1 -1
  203. package/dist/components/p-DTmI_Yx0.js +5 -0
  204. package/dist/components/p-DnspXuVN.js +5 -0
  205. package/dist/components/p-DqSdjh2n.js +5 -0
  206. package/dist/components/p-Dv2j7fhS.js +5 -0
  207. package/dist/components/{p-BUczNscD.js → p-K-OF4DG-.js} +1 -1
  208. package/dist/components/p-KSvRVGk-.js +5 -0
  209. package/dist/components/p-KbiYvxzP.js +5 -0
  210. package/dist/components/{p-DG-IgRFW.js → p-OY4Ixaeq.js} +1 -1
  211. package/dist/components/{p-BiaT4YFg.js → p-SIS25ChD.js} +1 -1
  212. package/dist/components/p-Wgb4y0H3.js +5 -0
  213. package/dist/components/p-X3AzJ0KD.js +5 -0
  214. package/dist/components/{p-gzdpekMI.js → p-bCeQwUKM.js} +1 -1
  215. package/dist/components/p-d3ZIPeEC.js +5 -0
  216. package/dist/components/{p-DlqfD7uh.js → p-ikaUQvOO.js} +1 -1
  217. package/dist/components/p-mbVTvSJF.js +5 -0
  218. package/dist/components/{p-DTdm5I7a.js → p-rQ3Z9EnS.js} +1 -1
  219. package/dist/components/{p-BmQrqnnJ.js → p-uYkn8a0m.js} +1 -1
  220. package/dist/components/part-lookup.js +1 -1
  221. package/dist/components/service-booking-form.js +1 -1
  222. package/dist/components/shift-accordion.js +1 -1
  223. package/dist/components/shift-checkbox.js +1 -1
  224. package/dist/components/shift-portal.js +1 -1
  225. package/dist/components/shift-select-dropdown.js +1 -1
  226. package/dist/components/shift-select.js +1 -1
  227. package/dist/components/shift-slider.js +1 -1
  228. package/dist/components/shift-switch.js +1 -1
  229. package/dist/components/shift-tab-content.js +1 -1
  230. package/dist/components/shift-tabs.js +1 -1
  231. package/dist/components/ssc-lookup-form.js +1 -1
  232. package/dist/components/test-drive-form.js +1 -1
  233. package/dist/components/vehicle-accessories.js +1 -1
  234. package/dist/components/vehicle-claimable-items.js +1 -1
  235. package/dist/components/vehicle-item-claim-form.js +1 -1
  236. package/dist/components/vehicle-lookup.js +1 -1
  237. package/dist/components/vehicle-paint-thickness.js +1 -1
  238. package/dist/components/vehicle-quotation-form.js +1 -1
  239. package/dist/components/vehicle-sale-information.js +1 -1
  240. package/dist/components/vehicle-service-history.js +1 -1
  241. package/dist/components/vehicle-specification.js +1 -1
  242. package/dist/components/vehicle-warranty-details.js +1 -1
  243. package/dist/components/vehicle-warranty-timeline.js +1 -1
  244. package/dist/components/vin-extractor.js +1 -1
  245. package/dist/esm/{branch-slot-dropdown_15.entry.js → branch-date-dropdown_17.entry.js} +957 -69
  246. package/dist/esm/dead-stock-lookup_3.entry.js +9 -9
  247. package/dist/esm/flexible-container.entry.js +3 -3
  248. package/dist/esm/form-checkbox.entry.js +2 -2
  249. package/dist/esm/form-date-picker.entry.js +5 -5
  250. package/dist/esm/form-input_7.entry.js +21 -19
  251. package/dist/esm/form-switch.entry.js +2 -2
  252. package/dist/esm/form-time-picker.entry.js +6 -6
  253. package/dist/esm/{functions-COLiDjnd.js → functions-DSwc1YVL.js} +44 -20
  254. package/dist/esm/general-form.entry.js +6 -6
  255. package/dist/esm/general-inquiry-form.entry.js +6 -6
  256. package/dist/esm/{get-local-language-CACck9dv.js → get-local-language-DdSAPSCV.js} +1 -1
  257. package/dist/esm/{get-mock-files-BEIYZlS_.js → get-mock-files-D4naDetb.js} +1 -1
  258. package/dist/esm/information-table.entry.js +1 -1
  259. package/dist/esm/loader.js +1 -1
  260. package/dist/esm/loading-spinner.entry.js +2 -2
  261. package/dist/esm/manufacturer-part-lookup.entry.js +3 -3
  262. package/dist/esm/{mappers-9IKbvPiR.js → mappers-CZs2R7k6.js} +54 -18
  263. package/dist/esm/part-lookup.entry.js +2 -2
  264. package/dist/esm/service-booking-form.entry.js +6 -6
  265. package/dist/esm/shift-accordion.entry.js +2 -2
  266. package/dist/esm/shift-checkbox.entry.js +2 -2
  267. package/dist/esm/shift-components.js +1 -1
  268. package/dist/esm/shift-slider.entry.js +1 -1
  269. package/dist/esm/shift-switch.entry.js +2 -2
  270. package/dist/esm/shift-tab-content.entry.js +2 -2
  271. package/dist/esm/shift-tabs_2.entry.js +92 -34
  272. package/dist/esm/ssc-lookup-form.entry.js +6 -6
  273. package/dist/esm/test-drive-form.entry.js +6 -6
  274. package/dist/esm/triangle-alert-BlnfPzWh.js +237 -0
  275. package/dist/esm/vehicle-accessories_8.entry.js +177 -117
  276. package/dist/esm/vehicle-lookup.entry.js +3 -3
  277. package/dist/esm/vehicle-quotation-form.entry.js +17 -18
  278. package/dist/integration-manifest.json +1 -1
  279. package/dist/locales/vehicleLookup/serviceHistory/en.json +2 -2
  280. package/dist/shift-components/{p-a91ed0fc.entry.js → p-0b576888.entry.js} +1 -1
  281. package/dist/shift-components/p-11ffa908.entry.js +5 -0
  282. package/dist/shift-components/{p-b2048d87.entry.js → p-152057de.entry.js} +1 -1
  283. package/dist/shift-components/p-198333fa.entry.js +5 -0
  284. package/dist/shift-components/{p-537507ee.entry.js → p-247ac2db.entry.js} +1 -1
  285. package/dist/shift-components/{p-1923fdb9.entry.js → p-295e445f.entry.js} +1 -1
  286. package/dist/shift-components/p-431522f2.entry.js +5 -0
  287. package/dist/shift-components/{p-b1270d98.entry.js → p-49dc6d73.entry.js} +1 -1
  288. package/dist/shift-components/{p-ac91d4ce.entry.js → p-4b6f801a.entry.js} +1 -1
  289. package/dist/shift-components/{p-6263bc0c.entry.js → p-4cdeab63.entry.js} +1 -1
  290. package/dist/shift-components/{p-829f24b9.entry.js → p-52530fba.entry.js} +1 -1
  291. package/dist/shift-components/p-5ad4d072.entry.js +5 -0
  292. package/dist/shift-components/{p-a0d13479.entry.js → p-5e8e5ed7.entry.js} +1 -1
  293. package/dist/shift-components/{p-65aad39b.entry.js → p-68e41217.entry.js} +1 -1
  294. package/dist/shift-components/{p-f2b86186.entry.js → p-76b0ea8c.entry.js} +1 -1
  295. package/dist/shift-components/{p-fdf94f65.entry.js → p-85d43a41.entry.js} +1 -1
  296. package/dist/shift-components/p-B9INqdFT.js +5 -0
  297. package/dist/shift-components/p-BkVfhjgg.js +5 -0
  298. package/dist/shift-components/{p--JyU3LyU.js → p-D2Ofzg_i.js} +1 -1
  299. package/dist/shift-components/p-DDtDRGFL.js +5 -0
  300. package/dist/shift-components/{p-jmNTa9tM.js → p-OfPuHUYc.js} +1 -1
  301. package/dist/shift-components/p-b44b2c00.entry.js +5 -0
  302. package/dist/shift-components/{p-48a9feb9.entry.js → p-b4544d10.entry.js} +1 -1
  303. package/dist/shift-components/{p-853f1b8b.entry.js → p-bfe0661c.entry.js} +1 -1
  304. package/dist/shift-components/{p-e7a99998.entry.js → p-c7fc8d5f.entry.js} +1 -1
  305. package/dist/shift-components/{p-bb172fc4.entry.js → p-ca0683e9.entry.js} +1 -1
  306. package/dist/shift-components/{p-cfcfdb20.entry.js → p-cbdc1c2e.entry.js} +1 -1
  307. package/dist/shift-components/p-d3bbba07.entry.js +5 -0
  308. package/dist/shift-components/{p-ec2a7d65.entry.js → p-df0c7ac1.entry.js} +1 -1
  309. package/dist/shift-components/p-f4679148.entry.js +5 -0
  310. package/dist/shift-components/{p-c4cf0a18.entry.js → p-feb443ec.entry.js} +1 -1
  311. package/dist/shift-components/shift-components.esm.js +1 -1
  312. package/dist/stencil-docs.json +1677 -12
  313. package/dist/templates/production-host/vehicle-service-history.html +14 -0
  314. package/dist/types/components/form-elements/branch-date-dropdown.d.ts +74 -0
  315. package/dist/types/components/form-elements/branch-date-picker.d.ts +174 -0
  316. package/dist/types/components/form-elements/branch-slot-dropdown.d.ts +6 -0
  317. package/dist/types/components/form-elements/branch-slot-picker.d.ts +27 -0
  318. package/dist/types/components/forms/defaults/mappers.d.ts +15 -9
  319. package/dist/types/components/vehicle-lookup/components/claimable-item-popover.d.ts +10 -1
  320. package/dist/types/components/vehicle-lookup/components/claimable-trace-modal.d.ts +3 -0
  321. package/dist/types/components/vehicle-lookup/vehicle-claimable-items.d.ts +14 -4
  322. package/dist/types/components/vehicle-lookup/vehicle-item-claim-form.d.ts +23 -3
  323. package/dist/types/components.d.ts +533 -6
  324. package/dist/types/features/form-hook/functions.d.ts +14 -0
  325. package/dist/types/global/lib/overlay.d.ts +61 -0
  326. package/dist/types/global/lib/slot-day-rules.d.ts +25 -0
  327. package/dist/types/locales/vehicleLookup/serviceHistory/type.d.ts +2 -2
  328. package/dist/types/locales/vehicleLookup/warrantyTimeline/type.d.ts +4 -4
  329. package/dist/types/locales/vehicleLookup/wrapper-type.d.ts +2 -2
  330. package/package.json +15 -3
  331. package/dist/cjs/triangle-alert-BjQBwsRv.js +0 -92
  332. package/dist/components/p-BQHywBX6.js +0 -5
  333. package/dist/components/p-Bb4mauUH.js +0 -5
  334. package/dist/components/p-Bl_BMBad.js +0 -5
  335. package/dist/components/p-BogCz_yJ.js +0 -5
  336. package/dist/components/p-BvFDjUZZ.js +0 -5
  337. package/dist/components/p-C2trAVtF.js +0 -5
  338. package/dist/components/p-CCuYl9VQ.js +0 -5
  339. package/dist/components/p-CNqcMKLv.js +0 -5
  340. package/dist/components/p-CZWmJ1vV.js +0 -5
  341. package/dist/components/p-Cg4l7ICV.js +0 -5
  342. package/dist/components/p-Ci6LbIzY.js +0 -5
  343. package/dist/components/p-CuTemmgW.js +0 -5
  344. package/dist/components/p-D0oXezbg.js +0 -5
  345. package/dist/components/p-DBKG_o1-.js +0 -5
  346. package/dist/components/p-Dlm9wHfQ.js +0 -5
  347. package/dist/components/p-DsMy6H9e.js +0 -5
  348. package/dist/components/p-Dseg250d.js +0 -5
  349. package/dist/components/p-EVz84xxv.js +0 -5
  350. package/dist/components/p-SnXklPq9.js +0 -5
  351. package/dist/components/p-k8G1hnHC.js +0 -5
  352. package/dist/components/p-uPiGNvhQ.js +0 -5
  353. package/dist/esm/triangle-alert-Brtqhx2d.js +0 -86
  354. package/dist/shift-components/p-34dffc00.entry.js +0 -5
  355. package/dist/shift-components/p-6fa1121c.entry.js +0 -5
  356. package/dist/shift-components/p-782fe0e1.entry.js +0 -5
  357. package/dist/shift-components/p-9a6450f6.entry.js +0 -5
  358. package/dist/shift-components/p-B9kqtQjS.js +0 -5
  359. package/dist/shift-components/p-D0Md1Xf7.js +0 -5
  360. package/dist/shift-components/p-c66b18c9.entry.js +0 -5
  361. package/dist/shift-components/p-c9a7e5ab.entry.js +0 -5
  362. package/dist/shift-components/p-fa7a961c.entry.js +0 -5
  363. package/dist/shift-components/p-hABirfUj.js +0 -5
  364. /package/dist/collection/templates/{vehicle-lookup → prototypes}/prototype-assets/extended-warranty/adp-logo.svg +0 -0
@@ -0,0 +1,1240 @@
1
+ /**
2
+ * Theming reference for the ticket-form components.
3
+ *
4
+ * Plain data. No DOM, no framework — the docs page renders it.
5
+ *
6
+ * Everything here was read off the source, not off the demos:
7
+ *
8
+ * src/components/forms/defaults/style.css the host reset
9
+ * src/components/form-elements/form-inputs.css every form-element style
10
+ * src/components/components/shift-portal.tsx the light-DOM escape hatch
11
+ * src/global/lib/get-custom-classes-for-portal.ts what travels with it
12
+ * src/global/lib/middleware.ts the Google Fonts injection
13
+ * src/features/form-hook/render-structure.tsx how a structure node names itself
14
+ * every `part=` under src/components/forms and src/components/form-elements
15
+ *
16
+ * The one sentence to read first: `::part()` is the entire theming surface.
17
+ * There are no CSS custom properties to set, no theme tokens, no Sass entry
18
+ * point. You get a list of named elements and ordinary CSS on each of them.
19
+ */
20
+
21
+ export const layers = [
22
+ {
23
+ id: 'global',
24
+ title: 'The whole form, through the shift-form part',
25
+ blurb:
26
+ 'Every form component renders one outer div carrying part="shift-form". It is the only part guaranteed to exist whichever form tag you used, so it is where the frame lives: the font, the max width, the rhythm between fields. It is also the first token copied onto the panels that leave the shadow root, which is why the portaled overlays can be reached by the same class you put on the form element.',
27
+ examples: [
28
+ {
29
+ label: 'The frame',
30
+ css: `general-form::part(shift-form) {
31
+ display: flex;
32
+ flex-direction: column;
33
+ gap: 20px;
34
+ max-width: 640px;
35
+ font-family: 'Nunito', system-ui, sans-serif !important;
36
+ }`,
37
+ note: 'The !important on font-family is not decoration — see the Fonts layer and the "The form does not inherit your page font" limit. Everything else here is an ordinary declaration.',
38
+ },
39
+ {
40
+ label: 'Every field at once',
41
+ css: `/* These aliases are carried by every field of their kind, so one rule
42
+ reaches all of them regardless of what the structure named the field. */
43
+ general-form::part(form-input),
44
+ general-form::part(form-input-select),
45
+ general-form::part(form-input-textarea),
46
+ general-form::part(form-file-trigger) {
47
+ border: 1px solid #cbd5e1;
48
+ border-radius: 8px;
49
+ padding: 10px 12px;
50
+ background: #fff;
51
+ }
52
+
53
+ general-form::part(form-input):focus,
54
+ general-form::part(form-input-select):focus,
55
+ general-form::part(form-input-textarea):focus {
56
+ border-color: #475569;
57
+ outline: 2px solid #cbd5e1;
58
+ outline-offset: 1px;
59
+ }`,
60
+ note: 'form-file-trigger also carries form-input, so the first selector already covers the upload button; list it separately only when the button should differ from the text fields.',
61
+ },
62
+ {
63
+ label: 'Two forms on one page, styled differently',
64
+ css: `<!-- Put the class on the element, not on a wrapper. -->
65
+ <general-form class="enquiry-theme"></general-form>
66
+ <service-booking-form class="booking-theme"></service-booking-form>
67
+
68
+ /* A class on the host is the only hook that reaches BOTH the shadow tree and
69
+ the panels the form portals to <body>. A wrapper around the element
70
+ reaches neither. */
71
+ .enquiry-theme::part(shift-form) {
72
+ max-width: 560px;
73
+ }
74
+
75
+ .booking-theme::part(shift-form) {
76
+ max-width: 820px;
77
+ }`,
78
+ note: 'Prefer the host class over the bare tag name in anything you ship. general-form::part(...) is fine while there is one form on the page and becomes a bug the day there are two.',
79
+ },
80
+ {
81
+ label: 'The parts around shift-form',
82
+ css: `/* form-container is the positioned box holding the loader and the form.
83
+ form-loader-container is the spinner covering it until the structure has
84
+ rendered; it fades out on its own. */
85
+ general-form::part(form-container) {
86
+ min-height: 220px;
87
+ }
88
+
89
+ general-form::part(form-loader-container) {
90
+ background: #f8fafc;
91
+ }
92
+
93
+ general-form::part(form-loader-icon) {
94
+ width: 28px;
95
+ height: 28px;
96
+ }`,
97
+ note: 'form-structure-form-container sits one level in and wraps the rendered structure; form-stepper-container becomes the direct wrapper instead when the structure declares steps.',
98
+ },
99
+ ],
100
+ },
101
+
102
+ {
103
+ id: 'theme-identifier',
104
+ title: 'The theme identifier: data.theme and the theme prop',
105
+ blurb:
106
+ 'The shift-form div is rendered as part={cn("shift-form", structure.data.theme, this.theme)}. Two independent inputs — a theme key in the structure JSON and a theme attribute on the element — become extra part tokens on that same div. Both then travel to the portaled panels as classes, so one identifier can style the form and its overlays together. This is the mechanism a deployment uses when the structure, not the page, decides which look applies.',
107
+ examples: [
108
+ {
109
+ label: 'From the structure JSON',
110
+ css: `// structure.json
111
+ {
112
+ "data": { "theme": "night" },
113
+ "children": [ /* ... */ ]
114
+ }
115
+
116
+ /* The token lands on the same div as shift-form. */
117
+ general-form::part(night) {
118
+ background: #0f172a;
119
+ padding: 28px;
120
+ border-radius: 14px;
121
+ }`,
122
+ note: 'The structure is fetched, so this lets one page render a different look per form without a deploy. It is also the identifier a page author cannot see in the markup — worth knowing when a theme rule appears to fire from nowhere.',
123
+ },
124
+ {
125
+ label: 'From the element',
126
+ css: `<general-form theme="night"></general-form>
127
+
128
+ general-form::part(night) {
129
+ background: #0f172a;
130
+ }`,
131
+ note: 'The theme prop and structure.data.theme are additive, not alternatives: set both and the div carries both tokens.',
132
+ },
133
+ {
134
+ label: 'The same identifier reaches the portaled panels',
135
+ css: `/* The token is copied onto the portaled elements as a CLASS, not a part —
136
+ so on the form it is ::part(night), and on the overlays it is .night. */
137
+ .night::part(dialog-wrapper) {
138
+ background: #0f172a;
139
+ color: #e2e8f0;
140
+ }
141
+
142
+ .night::part(shift-select-container) {
143
+ background: #1e293b;
144
+ border-color: #334155;
145
+ }`,
146
+ note: 'This is the one asymmetry worth memorising. Inside the form the identifier is a part; outside it is a class, because the portal copies part tokens and classes together into the copy’s className.',
147
+ },
148
+ {
149
+ label: 'The host class is an identifier too',
150
+ css: `<general-form class="docs-form"></general-form>
151
+
152
+ /* A class on the element is copied to the portals the same way, and is the
153
+ hook the four shipped presets use for exactly that reason. */
154
+ .docs-form::part(shift-form) { /* the form */ }
155
+ .docs-form::part(dialog-wrapper) { /* the dialog, at the end of <body> */ }`,
156
+ note: 'Practical advice: use the host class as the theme hook and keep data.theme for cases where the structure has to choose. From CSS they behave identically.',
157
+ },
158
+ {
159
+ label: 'vehicle-quotation-form is the exception',
160
+ css: `/* This one form composes its token differently — it ignores the theme prop
161
+ and prefixes the structure key: */
162
+ vehicle-quotation-form::part(vehicle-quotation-night) {
163
+ background: #0f172a;
164
+ }`,
165
+ note: 'Source: vehicle-quotation.tsx renders part="shift-form vehicle-quotation-<data.theme>". Every other form uses cn("shift-form", data.theme, theme).',
166
+ },
167
+ ],
168
+ },
169
+
170
+ {
171
+ id: 'structure-parts',
172
+ title: 'Part names the structure author writes',
173
+ blurb:
174
+ 'A structure node with a "tag" is rendered as that HTML tag with part={cn(id, class, "element-" + tag, tag)}. Four tokens: the id you wrote, the class you wrote, a generic element-<tag>, and the bare tag. This is how layout gets named — wrappers, section headings, column groups. None of these names come from the components, which is why they are the flexible half of theming and the half that breaks when the structure changes.',
175
+ examples: [
176
+ {
177
+ label: 'One node, four part names',
178
+ css: `// structure.json
179
+ { "tag": "div", "id": "inputs_wrapper", "class": "stack", "children": [ /* ... */ ] }
180
+
181
+ /* All four of these match that same div: */
182
+ general-form::part(inputs_wrapper) { } /* the id */
183
+ general-form::part(stack) { } /* the class */
184
+ general-form::part(element-div) { } /* generic, every div node */
185
+ general-form::part(div) { } /* generic, every div node */`,
186
+ note: 'Use the id. element-div and div match every div node in the structure and are almost never what you want.',
187
+ },
188
+ {
189
+ label: 'Layout, which is what these names are for',
190
+ css: `/* Single column first — a form squeezed into two columns on a phone is
191
+ worse than one that never tried. */
192
+ .docs-form::part(inputs_wrapper) {
193
+ display: flex;
194
+ flex-direction: column;
195
+ gap: 20px;
196
+ }
197
+
198
+ @media (min-width: 640px) {
199
+ .docs-form::part(inputs_wrapper) {
200
+ display: grid;
201
+ grid-template-columns: repeat(2, minmax(0, 1fr));
202
+ gap: 20px 24px;
203
+ }
204
+
205
+ /* Field part names come from the field name, so they hold whichever
206
+ wrapper the structure put them in. */
207
+ .docs-form::part(message),
208
+ .docs-form::part(submit-button) {
209
+ grid-column: 1 / -1;
210
+ }
211
+ }`,
212
+ note: 'This is what themes/two-column.css does. Note the mix: the wrapper is an authored name, the exceptions are derived names.',
213
+ },
214
+ {
215
+ label: 'A heading node',
216
+ css: `// structure.json
217
+ { "tag": "h1", "class": "section-title", "children": { "en": "Your details", "ar": "بياناتك" } }
218
+
219
+ .docs-form::part(section-title) {
220
+ font-size: 20px;
221
+ font-weight: 700;
222
+ color: #0f172a;
223
+ }`,
224
+ note: 'A tag node whose children is an object keyed by language renders the entry for the current language. vehicle-quotation-form also emits part="section-title" from its own element mapper for three built-in headings.',
225
+ },
226
+ {
227
+ label: 'What happens when the structure changes',
228
+ css: `/* Rename the node and the rule stops matching. Nothing warns you.
229
+ { "tag": "div", "id": "inputs_wrapper" }
230
+ → { "tag": "div", "id": "fields" } */
231
+
232
+ .docs-form::part(inputs_wrapper) { /* now matches nothing */ }
233
+ .docs-form::part(fields) { /* the replacement */ }`,
234
+ note: 'Treat the ids in a structure as part of its public surface once a theme depends on them, the same way you would treat a field name.',
235
+ },
236
+ ],
237
+ },
238
+
239
+ {
240
+ id: 'field-parts',
241
+ title: 'Per-field parts derived from name',
242
+ blurb:
243
+ 'Every field element builds its parts from its own name — the key the structure used, which is also the key the mapper matched and the key the value is submitted under. A field called email gives you email on the wrapper, email-container on the box, email-input on the control, email-label on the label, email-error-message on the message. These names are stable: they do not care where the layout put the field, and they change only if the field is renamed.',
244
+ examples: [
245
+ {
246
+ label: 'The five parts of a text field',
247
+ css: `/* For a field named "email" in the structure. */
248
+ .docs-form::part(email) { /* <label> wrapper */ }
249
+ .docs-form::part(email-label) { /* the label text */ }
250
+ .docs-form::part(email-container) { /* the positioned box */ }
251
+ .docs-form::part(email-input) { /* the <input> itself */ }
252
+ .docs-form::part(email-error-message) { /* the validation message */ }
253
+
254
+ /* Every one of those also carries a generic alias, so the same rule written
255
+ against the alias reaches every field of that kind: */
256
+ .docs-form::part(form-input) { /* every text input in the form */ }`,
257
+ note: 'The generic alias and the derived name are two tokens on one element. Style with the alias, override with the name.',
258
+ },
259
+ {
260
+ label: 'One field, differently',
261
+ css: `/* A grid where one field spans both columns and one is narrow. */
262
+ .docs-form::part(message) {
263
+ grid-column: 1 / -1;
264
+ }
265
+
266
+ .docs-form::part(postcode-input) {
267
+ max-width: 10ch;
268
+ }`,
269
+ note: 'part(message) is the field wrapper; part(message-textarea) is the <textarea>. Sizing usually belongs on the wrapper, appearance on the control.',
270
+ },
271
+ {
272
+ label: 'The required star and the error state',
273
+ css: `.docs-form::part(form-input-label-required-star) {
274
+ color: #b91c1c;
275
+ }
276
+
277
+ /* Only the field that is failing: */
278
+ .docs-form::part(email-label-required-star) {
279
+ color: #dc2626;
280
+ }
281
+
282
+ /* The error message element carries a third token, display-error-message,
283
+ while the message is actually shown. */
284
+ .docs-form::part(form-error-message) {
285
+ font-size: 12px;
286
+ color: #b91c1c;
287
+ }
288
+
289
+ .docs-form::part(display-error-message) {
290
+ font-weight: 600;
291
+ }`,
292
+ note: 'The message element is always in the DOM; display-error-message is the token that reveals it. Do not display:none the form-error-message-container — it is what reserves the space the message animates into.',
293
+ },
294
+ {
295
+ label: 'Fields whose label carries no name',
296
+ css: `/* form-input, form-picker-input, form-vin-input, form-date-picker and
297
+ form-time-picker call the label component WITHOUT a name, so their labels
298
+ emit form-input-label but NOT <name>-label. */
299
+
300
+ .docs-form::part(form-input-label) { /* works everywhere */ }
301
+ .docs-form::part(email-label) { /* matches nothing for a form-input field */ }
302
+
303
+ /* If one of those labels must differ, give the field a distinct wrapper in
304
+ the structure and style the wrapper — ::part() cannot be used as an
305
+ ancestor, so there is no descendant route to it. */`,
306
+ note: 'The full list is in the limits, under "Some labels have no per-field part". The affected controls are exactly the ones whose label component is called without the name prop.',
307
+ },
308
+ {
309
+ label: 'A select and its trigger',
310
+ css: `/* A select field named "city": wrapper and label come from form-select,
311
+ the box and the input from the shift-select inside it. */
312
+ .docs-form::part(city) { /* <label> wrapper */ }
313
+ .docs-form::part(city-label) { /* label text (form-select DOES pass name) */ }
314
+ .docs-form::part(city-container) { /* the box */ }
315
+ .docs-form::part(city-input-select) { /* the readonly / search input */ }
316
+ .docs-form::part(city-arrow-icon) { /* the chevron */ }
317
+ .docs-form::part(city-cross-icon) { /* the clear button, when clearable */ }
318
+
319
+ /* The options list is NOT here — it is portaled. See the next layer. */`,
320
+ note: 'branch-slot-picker and branch-date-picker expose the same trigger shape: <name>, <name>-container, <name>-input-select, <name>-arrow-icon.',
321
+ },
322
+ ],
323
+ },
324
+
325
+ {
326
+ id: 'portal',
327
+ title: 'The overlays that leave the shadow root',
328
+ blurb:
329
+ 'Four components are not rendered where they are declared. shift-portal creates the element with document.createElement and appends it to document.body, so it escapes any overflow:hidden or stacking context the host page puts around the form. They are form-dialog, shift-select-dropdown, branch-slot-dropdown and branch-date-dropdown — and unlike everything else in the form, all four are shadow:true themselves. general-form::part(x) cannot reach them: they are no longer in that shadow tree. What reaches them is the class shift-portal copies onto them.',
330
+ examples: [
331
+ {
332
+ label: 'What travels, exactly',
333
+ css: `<!-- You write: -->
334
+ <general-form class="docs-form" theme="night"></general-form>
335
+
336
+ <!-- At the end of <body>, at runtime: -->
337
+ <form-dialog class="shift-form night docs-form"></form-dialog>
338
+ <shift-select-dropdown class="city-select shift-form night docs-form"></shift-select-dropdown>
339
+
340
+ /* So all of these work: */
341
+ .docs-form::part(dialog-wrapper) { }
342
+ .night::part(shift-select-container) { }
343
+ .shift-form::part(dialog-wrapper) { /* every form on the page */ }`,
344
+ note: 'getCustomClassesForPortal walks up from the field, collects part tokens and classes from the first ancestor carrying shift-form or shift-component, crosses the shadow boundary to the host, and adds the host’s classes too. That is the whole contract.',
345
+ },
346
+ {
347
+ label: 'A dark form needs this or the dropdown stays white',
348
+ css: `.docs-form::part(shift-form) {
349
+ background: #0f172a;
350
+ }
351
+
352
+ /* Without the rules below, a dark form opens a white dropdown and a white
353
+ dialog, because neither is inside the form any more. */
354
+ .docs-form::part(shift-select-container) {
355
+ background: #1e293b;
356
+ border: 1px solid #334155;
357
+ border-radius: 10px;
358
+ }
359
+
360
+ .docs-form::part(shift-select-option) {
361
+ color: #e2e8f0;
362
+ padding: 10px 14px;
363
+ }
364
+
365
+ .docs-form::part(shift-select-option-selected) {
366
+ background: #334155;
367
+ }
368
+
369
+ .docs-form::part(dialog-drop-container) {
370
+ background: rgb(0 0 0 / 0.65);
371
+ }
372
+
373
+ .docs-form::part(dialog-wrapper) {
374
+ background: #0f172a;
375
+ color: #e2e8f0;
376
+ }`,
377
+ note: 'This is the entire point of themes/dark-panel.css. Any theme that changes the background is incomplete until the portaled panels are covered.',
378
+ },
379
+ {
380
+ label: 'Per-field hooks on the panels',
381
+ css: `/* The portal class starts with a field-specific token, so one field's panel
382
+ can differ from the rest:
383
+ shift-select-dropdown → <name>-select
384
+ branch-slot-dropdown → <name>-slot-picker
385
+ branch-date-dropdown → <name>-date-picker */
386
+
387
+ .city-select::part(shift-select-container) {
388
+ max-height: 420px;
389
+ }
390
+
391
+ .pickupSlot-slot-picker::part(branch-slot-container) {
392
+ min-width: 340px;
393
+ }
394
+
395
+ .pickupDate-date-picker::part(branch-date-container) {
396
+ border-radius: 16px;
397
+ }`,
398
+ note: 'These tokens are classes on the portaled element, so they combine with the theme class: .docs-form.city-select::part(shift-select-container).',
399
+ },
400
+ {
401
+ label: 'Do not target the tag',
402
+ css: `/* Wrong: hits every form on the page, including ones with another theme. */
403
+ form-dialog::part(dialog-wrapper) {
404
+ background: #0f172a;
405
+ }
406
+
407
+ /* Right: scoped to the form that owns it. */
408
+ .docs-form::part(dialog-wrapper) {
409
+ background: #0f172a;
410
+ }`,
411
+ note: 'Every form instance portals its own dialog, and every select portals its own dropdown; they all pile up at the end of <body>. The theme class is the only thing distinguishing them.',
412
+ },
413
+ {
414
+ label: 'The branch pickers, both panels',
415
+ css: `.docs-form::part(branch-slot-container),
416
+ .docs-form::part(branch-date-container) {
417
+ background: #1e293b;
418
+ border: 1px solid #334155;
419
+ color: #e2e8f0;
420
+ }
421
+
422
+ .docs-form::part(branch-slot-day-selected),
423
+ .docs-form::part(branch-slot-time-selected),
424
+ .docs-form::part(branch-date-cell-selected),
425
+ .docs-form::part(branch-date-time-selected) {
426
+ background: #e2e8f0;
427
+ color: #0f172a;
428
+ }
429
+
430
+ .docs-form::part(branch-date-cell-blocked) {
431
+ opacity: 0.35;
432
+ }
433
+
434
+ /* Below 600px both panels become a bottom sheet and the scrim appears. */
435
+ .docs-form::part(branch-slot-backdrop),
436
+ .docs-form::part(branch-date-backdrop) {
437
+ background: rgb(0 0 0 / 0.55);
438
+ }`,
439
+ note: 'The panels position themselves through --branch-slot-* / --branch-date-* custom properties written by JS on every reposition. Do not set those from CSS; see the limits.',
440
+ },
441
+ ],
442
+ },
443
+
444
+ {
445
+ id: 'fonts',
446
+ title: 'Fonts',
447
+ blurb:
448
+ 'The package injects one Google Fonts stylesheet into document.head on load — Noto Kufi Arabic and Nunito — from src/global/lib/middleware.ts, which Stencil runs as the global script. That is a fixed behaviour of the bundle, not a setting. On top of that the form host carries all: initial !important, so the page typography does not cross the boundary by inheritance. The consequence is simple: declare the font on ::part(shift-form), and mark it !important.',
449
+ examples: [
450
+ {
451
+ label: 'Set the form font',
452
+ css: `.docs-form::part(shift-form) {
453
+ font-family: 'Nunito', system-ui, sans-serif !important;
454
+ }`,
455
+ note: 'All four shipped presets do exactly this and nothing else about fonts. Everything inside the form inherits from that div — including inputs, buttons and textareas, which the bundled preflight already gives font-family: inherit.',
456
+ },
457
+ {
458
+ label: 'Why the host itself is not the place',
459
+ css: `/* Neither of these does anything. Both component stylesheets that land in
460
+ the form's shadow root put all: initial !important on :host, and an
461
+ important declaration in an inner shadow tree beats an important one from
462
+ the outer page. */
463
+ general-form { font-family: 'Nunito', sans-serif; }
464
+ general-form { font-family: 'Nunito', sans-serif !important; }
465
+
466
+ /* This works. */
467
+ general-form::part(shift-form) { font-family: 'Nunito', sans-serif !important; }`,
468
+ note: 'Same reason a wrapper div with a font on it has no effect: the reset cuts inheritance before the wrapper font reaches the form.',
469
+ },
470
+ {
471
+ label: 'Self-hosting instead of the injected sheet',
472
+ css: `/* The injected <link> stays — it is unconditional — but nothing forces you
473
+ to use those families. Declare your own on the page and point the form at
474
+ it; the font file is fetched by the page, not by the component. */
475
+ @font-face {
476
+ font-family: 'Brand Sans';
477
+ src: url('/assets/fonts/brand-sans.woff2') format('woff2');
478
+ font-weight: 100 900;
479
+ font-display: swap;
480
+ }
481
+
482
+ .docs-form::part(shift-form) {
483
+ font-family: 'Brand Sans', 'Noto Kufi Arabic', system-ui, sans-serif !important;
484
+ }`,
485
+ note: 'Keep an Arabic-capable family in the stack. The components ship in en, ar, ku and ru, and the Arabic and Kurdish forms render right-to-left in whatever face you leave them.',
486
+ },
487
+ {
488
+ label: 'The portaled panels keep their own font',
489
+ css: `/* No form of this works. The panels declare their font with !important from
490
+ inside their own shadow tree, twice — on :host and again on *, :host *. */
491
+ .docs-form::part(shift-select-container) { font-family: 'Brand Sans' !important; }
492
+ shift-select-dropdown { font-family: 'Brand Sans' !important; }
493
+
494
+ /* Everything else about them is themeable. Only the typeface is fixed. */
495
+ .docs-form::part(shift-select-container) {
496
+ background: #1e293b;
497
+ border-radius: 10px;
498
+ font-size: 15px; /* size, weight, colour, spacing: all fine */
499
+ }`,
500
+ note: 'Documented in full under the limits. If the mismatch matters, the fix is a source change to those four stylesheets, not a theme.',
501
+ },
502
+ ],
503
+ },
504
+ ];
505
+
506
+ /**
507
+ * The inventory.
508
+ *
509
+ * `scope: 'form'` means the element is inside the form's single shadow root and
510
+ * is reached with `<form-tag>::part(x)` or `.your-class::part(x)`.
511
+ *
512
+ * `scope: 'portal'` means the element lives in a shadow root of its own on an
513
+ * element appended to document.body. `<form-tag>::part(x)` will never match it;
514
+ * use the class that shift-portal copied across — see the portal layer.
515
+ *
516
+ * `<name>` is the field's name from the structure. Where a row lists two names
517
+ * separated by a space, both tokens are on the same element: the derived one
518
+ * and the generic alias.
519
+ */
520
+ export const parts = [
521
+ // ── The form shell ───────────────────────────────────────────────────────
522
+ {
523
+ part: 'shift-form',
524
+ scope: 'form',
525
+ appliesTo: 'The outer div of every form component',
526
+ note: 'Always present. The frame goes here: font, width, gap, background. Also the first class copied onto the portaled panels.',
527
+ },
528
+ {
529
+ part: '<data.theme>',
530
+ scope: 'form',
531
+ appliesTo: 'The same div as shift-form',
532
+ note: 'The theme key from the structure JSON, verbatim. Absent if the structure declares none.',
533
+ },
534
+ {
535
+ part: '<theme prop>',
536
+ scope: 'form',
537
+ appliesTo: 'The same div as shift-form',
538
+ note: 'The theme attribute on the element, verbatim. Additive with data.theme. Not read by vehicle-quotation-form.',
539
+ },
540
+ {
541
+ part: 'vehicle-quotation-<data.theme>',
542
+ scope: 'form',
543
+ appliesTo: 'vehicle-quotation-form only',
544
+ note: 'That form prefixes the structure key instead of emitting it bare, and ignores the theme prop.',
545
+ },
546
+ {
547
+ part: 'form-container',
548
+ scope: 'form',
549
+ appliesTo: 'The positioned box inside shift-form',
550
+ note: 'Holds the loader overlay and the form. Carries a min-height so the loader has somewhere to sit.',
551
+ },
552
+ {
553
+ part: 'form-loader-container',
554
+ scope: 'form',
555
+ appliesTo: 'The full-cover spinner overlay',
556
+ note: 'Fades to opacity 0 once the structure has rendered. Style the background here if a white flash is wrong on your page.',
557
+ },
558
+ {
559
+ part: 'form-loader-icon',
560
+ scope: 'form',
561
+ appliesTo: 'The spinner image inside the loader overlay',
562
+ note: 'Sized 32px by default; it spins from a keyframe you cannot reach, but width/height/opacity are yours.',
563
+ },
564
+ { part: 'form-structure-form-container', scope: 'form', appliesTo: 'The wrapper around the rendered structure', note: 'Present on every form. One level inside form-container.' },
565
+ {
566
+ part: 'form-stepper-container',
567
+ scope: 'form',
568
+ appliesTo: 'Replaces the plain wrapper when the structure declares steps',
569
+ note: 'Carries overflow:hidden so the step panes can slide. Changing that breaks the step transition.',
570
+ },
571
+ {
572
+ part: 'form-structure-error-container',
573
+ scope: 'form',
574
+ appliesTo: 'Shown instead of the form when no structure could be loaded',
575
+ note: 'Worth theming: it is what an integrator sees when the structure URL is wrong.',
576
+ },
577
+ { part: 'form-structure-error-content', scope: 'form', appliesTo: 'The message box inside the structure-error container', note: 'Default is a red-tinted card at 20px.' },
578
+
579
+ // ── Shared field chrome ──────────────────────────────────────────────────
580
+ {
581
+ part: 'form-input-label',
582
+ scope: 'form',
583
+ appliesTo: 'The label text of every field that renders one',
584
+ note: 'Always present when the field has a label; the per-field twin is not — see the limits.',
585
+ },
586
+ {
587
+ part: '<name>-label',
588
+ scope: 'form',
589
+ appliesTo: 'The same label, when the component passes its name',
590
+ note: 'Emitted by form-select, form-text-area, form-file, form-phone-number, branch-slot-picker and branch-date-picker only.',
591
+ },
592
+ { part: 'form-input-label-required-star', scope: 'form', appliesTo: 'The asterisk after a required field label', note: 'A span inside the label. Red by default.' },
593
+ { part: '<name>-label-required-star', scope: 'form', appliesTo: 'The same asterisk, when the component passes its name', note: 'Same six components as <name>-label.' },
594
+ {
595
+ part: 'form-error-message-container',
596
+ scope: 'form',
597
+ appliesTo: 'The clipping box below every field',
598
+ note: 'Reserves the space the message slides into. Do not hide it — hide the message instead.',
599
+ },
600
+ { part: '<name>-error-message-container', scope: 'form', appliesTo: 'The same box, per field', note: 'Every field component passes its name here, unlike the label.' },
601
+ { part: 'form-error-message', scope: 'form', appliesTo: 'The validation message text', note: 'Always rendered, at opacity 0 until there is an error.' },
602
+ { part: '<name>-error-message', scope: 'form', appliesTo: 'The same message, per field', note: 'Use this to give one field a different error treatment.' },
603
+ {
604
+ part: 'display-error-message',
605
+ scope: 'form',
606
+ appliesTo: 'Added to the message element while an error is showing',
607
+ note: 'A state token, not an element. Pair it with form-error-message to style only the visible state.',
608
+ },
609
+ {
610
+ part: 'form-input-prefix',
611
+ scope: 'form',
612
+ appliesTo: 'The static prefix inside an input (form-input, form-picker-input, form-vin-input, form-phone-number)',
613
+ note: 'Absolutely positioned and pointer-events:none. The input padding is set from JS off its measured width.',
614
+ },
615
+ { part: '<name>-prefix', scope: 'form', appliesTo: 'The same prefix, per field', note: 'All four prefix-capable components pass their name.' },
616
+
617
+ // ── form-input ───────────────────────────────────────────────────────────
618
+ { part: '<name>', scope: 'form', appliesTo: 'The <label> wrapper of every field', note: 'The outermost per-field element. Grid placement and width belong here.' },
619
+ {
620
+ part: '<name>-container form-input-container',
621
+ scope: 'form',
622
+ appliesTo: 'The positioned box holding the control, prefix and icons',
623
+ note: 'Gains an "open" or "disabled" class internally, but those are classes, not parts.',
624
+ },
625
+ {
626
+ part: '<name>-input form-input',
627
+ scope: 'form',
628
+ appliesTo: 'The <input> of form-input, form-vin-input, form-phone-number and the legacy pickers',
629
+ note: 'The main text-field selector. form-input is the alias that reaches all of them at once.',
630
+ },
631
+ {
632
+ part: '<name>-icon form-input-icon',
633
+ scope: 'form',
634
+ appliesTo: 'The trailing or leading icon of form-input and form-picker-input',
635
+ note: 'Rendered as a button when the field defines an icon action, otherwise a span. Both carry the same parts.',
636
+ },
637
+
638
+ // ── form-text-area ───────────────────────────────────────────────────────
639
+ {
640
+ part: 'form-textarea <name>',
641
+ scope: 'form',
642
+ appliesTo: 'The <label> wrapper of a textarea field',
643
+ note: 'Note the shape: form-textarea is on the WRAPPER, not on the textarea element.',
644
+ },
645
+ {
646
+ part: '<name>-textarea form-input-textarea',
647
+ scope: 'form',
648
+ appliesTo: 'The <textarea> itself',
649
+ note: 'This is the one to give borders, padding and font-size. Default height is 200px with resize disabled.',
650
+ },
651
+
652
+ // ── form-select / shift-select ───────────────────────────────────────────
653
+ {
654
+ part: '<name>-input-select form-input-select',
655
+ scope: 'form',
656
+ appliesTo: 'The trigger input of a select and of both branch pickers',
657
+ note: 'Readonly unless the select is searchable. Style alongside form-input for a consistent field look.',
658
+ },
659
+ {
660
+ part: '<name>-select-icon-container form-input-select-icon-container',
661
+ scope: 'form',
662
+ appliesTo: 'The 36px box at the trailing edge holding the chevron or the clear button',
663
+ note: 'Pointer events are toggled from a class depending on whether a value can be cleared.',
664
+ },
665
+ {
666
+ part: '<name>-arrow-icon select-arrow',
667
+ scope: 'form',
668
+ appliesTo: 'The chevron on a select or branch picker',
669
+ note: 'Rotates when the panel opens, via an internal class. Colour and size are yours.',
670
+ },
671
+ {
672
+ part: '<name>-cross-icon',
673
+ scope: 'form',
674
+ appliesTo: 'The clear button, on a clearable select with a value',
675
+ note: 'It is the add icon rotated 45 degrees. There is no generic alias for it.',
676
+ },
677
+
678
+ // ── form-picker-input ────────────────────────────────────────────────────
679
+ {
680
+ part: 'form-input',
681
+ scope: 'form',
682
+ appliesTo: 'The visible readonly display input of form-picker-input',
683
+ note: 'Deliberate: this element carries ONLY the alias, no <name> token. Target one picker through <name>-container instead.',
684
+ },
685
+ {
686
+ part: '<name>-input form-picker-<name>',
687
+ scope: 'form',
688
+ appliesTo: 'The real (transparent, overlaid) date/time input of form-picker-input',
689
+ note: 'This is the element the native picker opens from. It sits at opacity 0 over the display input.',
690
+ },
691
+ {
692
+ part: '<name>-container form-input-container relative',
693
+ scope: 'form',
694
+ appliesTo: 'form-picker-input adds a third token to its container',
695
+ note: 'Harmless duplication of the positioning class; match on <name>-container or form-input-container.',
696
+ },
697
+
698
+ // ── form-file ────────────────────────────────────────────────────────────
699
+ { part: 'form-file <name>', scope: 'form', appliesTo: 'The <label> wrapper of an upload field', note: 'Same shape as the textarea wrapper: the type alias sits on the wrapper.' },
700
+ { part: '<name>-file-input form-file-input', scope: 'form', appliesTo: 'The real <input type="file">', note: 'display:none. Styling it is pointless; style the trigger.' },
701
+ {
702
+ part: '<name>-file-trigger form-file-trigger form-input',
703
+ scope: 'form',
704
+ appliesTo: 'The button the user actually clicks',
705
+ note: 'Carries form-input too, so a generic field rule already covers it.',
706
+ },
707
+ {
708
+ part: '<name>-file-text form-file-text',
709
+ scope: 'form',
710
+ appliesTo: 'The filename / prompt text inside the trigger',
711
+ note: 'Truncates. Give it a max-width if your filenames are long.',
712
+ },
713
+ {
714
+ part: '<name>-file-icon form-file-icon',
715
+ scope: 'form',
716
+ appliesTo: 'The spinner or tick at the trailing edge of the trigger',
717
+ note: 'Holds the upload spinner during transfer and a green tick after success.',
718
+ },
719
+
720
+ // ── form-phone-number ────────────────────────────────────────────────────
721
+ {
722
+ part: '<name>-container form-input-container form-phone-container',
723
+ scope: 'form',
724
+ appliesTo: 'The phone field box',
725
+ note: 'Gains form-input-container-country as a fourth token when more than one country code is configured.',
726
+ },
727
+ {
728
+ part: '<name>-input-country-selection form-input form-input-country-selection',
729
+ scope: 'form',
730
+ appliesTo: 'The country-code select sitting flush against the number input',
731
+ note: 'Fixed 75px wide inside a phone field. Its inner border radii are flattened by component CSS.',
732
+ },
733
+ { part: 'form-input-container-wrapper', scope: 'form', appliesTo: 'The flex child holding the number input', note: 'No per-field token on this one.' },
734
+ {
735
+ part: '<name>-input-with-country form-input-with-country',
736
+ scope: 'form',
737
+ appliesTo: 'Added to the number input only when a country selector is present',
738
+ note: 'Use it to adjust the leading radius without affecting single-country phone fields.',
739
+ },
740
+
741
+ // ── form-checkbox / form-switch ──────────────────────────────────────────
742
+ {
743
+ part: '<name>',
744
+ scope: 'form',
745
+ appliesTo: 'The wrapper div of a checkbox or switch field',
746
+ note: 'A div, not a label, on these two — the label is inside the inner component.',
747
+ },
748
+ {
749
+ part: 'shift-checkbox',
750
+ scope: 'form',
751
+ appliesTo: 'The clickable label of BOTH shift-checkbox and shift-switch',
752
+ note: 'Not a typo: the switch reuses the checkbox part name. The box, tick, track and knob expose no parts at all.',
753
+ },
754
+
755
+ // ── Legacy date / time pickers ───────────────────────────────────────────
756
+ {
757
+ part: '<name>-icon form-date-picker-icon',
758
+ scope: 'form',
759
+ appliesTo: 'The calendar glyph on the legacy date field',
760
+ note: 'form-date-picker is the legacy control; new structures usually use the picker-input or the branch pickers.',
761
+ },
762
+ {
763
+ part: '<name>-dropdown form-date-picker-dropdown',
764
+ scope: 'form',
765
+ appliesTo: 'The legacy calendar panel',
766
+ note: 'This panel is NOT portaled — it renders inside the form and is clipped by any overflow around it.',
767
+ },
768
+ {
769
+ part: '<name>-prev form-date-picker-nav / <name>-next form-date-picker-nav',
770
+ scope: 'form',
771
+ appliesTo: 'The month arrows of the legacy calendar',
772
+ note: 'Both arrows share the nav alias; the prev/next tokens separate them.',
773
+ },
774
+ { part: '<name>-title form-date-picker-title', scope: 'form', appliesTo: 'The month label of the legacy calendar', note: '' },
775
+ {
776
+ part: 'form-date-picker-day',
777
+ scope: 'form',
778
+ appliesTo: 'Every day cell of the legacy calendar',
779
+ note: 'Gains form-date-picker-day-selected and form-date-picker-day-today as extra tokens. No per-field token.',
780
+ },
781
+ { part: '<name>-icon form-time-picker-icon', scope: 'form', appliesTo: 'The clock glyph on the legacy time field', note: '' },
782
+ { part: '<name>-dropdown form-time-picker-dropdown', scope: 'form', appliesTo: 'The legacy hour/minute panel', note: 'Also not portaled.' },
783
+ {
784
+ part: 'form-time-picker-option',
785
+ scope: 'form',
786
+ appliesTo: 'Every hour and minute cell of the legacy time panel',
787
+ note: 'Gains form-time-picker-option-selected. No per-field token.',
788
+ },
789
+
790
+ // ── Submit and stepper ───────────────────────────────────────────────────
791
+ {
792
+ part: 'submit-button',
793
+ scope: 'form',
794
+ appliesTo: 'The submit button of form-submit, and of form-stepper-submit',
795
+ note: 'One selector covers both. form-stepper-submit adds stepper-submit-button so the two can still be told apart.',
796
+ },
797
+ { part: 'stepper-submit-button', scope: 'form', appliesTo: 'The submit button on a stepped form only', note: '' },
798
+ {
799
+ part: 'form-submit-text',
800
+ scope: 'form',
801
+ appliesTo: 'The label inside a submit button — rendered twice',
802
+ note: 'Two elements share this part: one invisible copy that sets the width, one that slides out on submit.',
803
+ },
804
+ {
805
+ part: 'form-submit-loading-container',
806
+ scope: 'form',
807
+ appliesTo: 'The spinner layer that slides in while submitting',
808
+ note: 'Translated out of view until the button enters its loading state.',
809
+ },
810
+ {
811
+ part: 'form-submit-loading-icon',
812
+ scope: 'form',
813
+ appliesTo: 'The spinner image itself',
814
+ note: 'A white loader by default; replace the colour by restyling the container background instead.',
815
+ },
816
+ {
817
+ part: 'stepper-control stepper-control-button',
818
+ scope: 'form',
819
+ appliesTo: 'The back button on a stepped form',
820
+ note: 'Also carries stepper-control-<ltr|rtl> and stepper-control-step-<n>, so it can be styled per direction and per step.',
821
+ },
822
+ {
823
+ part: 'stepper-control-icon',
824
+ scope: 'form',
825
+ appliesTo: 'The chevron inside the back button',
826
+ note: 'Also carries stepper-control-icon-<ltr|rtl>. The glyph itself flips with direction.',
827
+ },
828
+ { part: 'form-stepper-line', scope: 'form', appliesTo: 'The stepper rail wrapper', note: 'Also carries the stepper node name from the structure, when one was given.' },
829
+ {
830
+ part: 'form-stepper-linecontainer',
831
+ scope: 'form',
832
+ appliesTo: 'The flex row holding the step cells',
833
+ note: 'The missing hyphen is real — the component concatenates the id with "container".',
834
+ },
835
+ {
836
+ part: 'form-stepper-line-line',
837
+ scope: 'form',
838
+ appliesTo: 'The dashed rule running behind the step indicators',
839
+ note: 'Its width is measured and set from JS; its background is an inline style, so override needs !important.',
840
+ },
841
+ {
842
+ part: 'form-stepper-line-step',
843
+ scope: 'form',
844
+ appliesTo: 'One step cell',
845
+ note: 'Also carries -step-<i>, and -step-active / -step-active<i> / -step-done / -step-done<i> as state.',
846
+ },
847
+ {
848
+ part: 'form-stepper-line-step-indicator',
849
+ scope: 'form',
850
+ appliesTo: 'The numbered circle of a step',
851
+ note: 'Same suffix family: -<i>, -active, -active<i>, -done, -done<i>. This is the main stepper colour hook.',
852
+ },
853
+ { part: 'form-stepper-line-step-indicator-text', scope: 'form', appliesTo: 'The number inside the circle', note: 'Same suffix family.' },
854
+ { part: 'form-stepper-line-step-title', scope: 'form', appliesTo: 'The caption under a step circle', note: 'Same suffix family.' },
855
+
856
+ // ── Vehicle image viewer ─────────────────────────────────────────────────
857
+ {
858
+ part: 'vehicle-image-wrapper',
859
+ scope: 'form',
860
+ appliesTo: 'The image frame of the vehicle preview element',
861
+ note: 'Rendered by the vehicleImage mapper entry; present only if the structure uses it.',
862
+ },
863
+ {
864
+ part: 'vehicle-image-loading-wrapper',
865
+ scope: 'form',
866
+ appliesTo: 'The loading layer over the vehicle image',
867
+ note: 'Gains vehicle-image-active-loading-wrapper while fetching.',
868
+ },
869
+ { part: 'vehicle-image-loader-icon', scope: 'form', appliesTo: 'The spinner in the vehicle image loading layer', note: '' },
870
+ { part: 'vehicle-image', scope: 'form', appliesTo: 'The <img> itself', note: 'Radius and opacity are set inline by the component, so overriding them needs !important.' },
871
+ {
872
+ part: 'section-title',
873
+ scope: 'form',
874
+ appliesTo: 'The three built-in headings of vehicle-quotation-form',
875
+ note: 'Also the conventional class to put on a heading tag node in any structure — see the structure layer.',
876
+ },
877
+
878
+ // ── Portal: form-dialog ──────────────────────────────────────────────────
879
+ {
880
+ part: 'form-dialog-modal dialog-drop-container',
881
+ scope: 'portal',
882
+ appliesTo: 'The fixed full-screen scrim behind the dialog',
883
+ note: 'Gains dialog-drop-container-opened and dialog-drop-container-error. Default is black/50 with a blur.',
884
+ },
885
+ { part: 'dialog-wrapper', scope: 'portal', appliesTo: 'The dialog card', note: 'Gains dialog-wrapper-opened while shown. Background, radius and padding all belong here.' },
886
+ { part: 'dialog-content', scope: 'portal', appliesTo: 'The message area of the dialog', note: 'Holds the error text, or the success block below.' },
887
+ { part: 'form-success-container', scope: 'portal', appliesTo: 'The success block, shown when there is no error', note: 'A column with an icon above the success message.' },
888
+ {
889
+ part: 'form-success-container-icon',
890
+ scope: 'portal',
891
+ appliesTo: 'The success tick illustration',
892
+ note: 'An inline SVG using currentColor, so colour it through the container or here.',
893
+ },
894
+ { part: 'dialog-close-icon-button', scope: 'portal', appliesTo: 'The small dismiss button in the dialog corner', note: '' },
895
+ { part: 'dialog-close-icon-button-icon', scope: 'portal', appliesTo: 'The glyph inside that dismiss button', note: 'The add icon rotated to a cross.' },
896
+ {
897
+ part: 'dialog-close-button',
898
+ scope: 'portal',
899
+ appliesTo: 'The full-width close button at the bottom of the dialog',
900
+ note: 'Gains dialog-close-button-error when the dialog is showing an error, so success and failure can differ.',
901
+ },
902
+
903
+ // ── Portal: shift-select-dropdown ────────────────────────────────────────
904
+ {
905
+ part: '<name>-select-container shift-select-container',
906
+ scope: 'portal',
907
+ appliesTo: 'The options panel of a select',
908
+ note: 'Fixed-positioned from JS-written variables. Height caps and colours are yours; top/left/width are not.',
909
+ },
910
+ { part: 'shift-select-container-open', scope: 'portal', appliesTo: 'Added to the panel while it is open', note: 'A state token. The open/closed transition is opacity only.' },
911
+ { part: '<name>-select-option shift-select-option', scope: 'portal', appliesTo: 'One option row', note: 'The main padding and hover hook for a dropdown.' },
912
+ { part: 'shift-select-option-selected', scope: 'portal', appliesTo: 'Added to the currently selected option', note: '' },
913
+ {
914
+ part: '<name>-select-option-label shift-select-option-label',
915
+ scope: 'portal',
916
+ appliesTo: 'The text of a default-rendered option',
917
+ note: 'Absent when the field supplies a custom option renderer.',
918
+ },
919
+ {
920
+ part: '<name>-tick-icon shift-select-option-tick',
921
+ scope: 'portal',
922
+ appliesTo: 'The tick at the end of an option row',
923
+ note: 'Gains shift-select-option-tick-selected. Hidden by opacity, so it always occupies its space.',
924
+ },
925
+ {
926
+ part: '<name>-select-empty-container shift-select-empty-container',
927
+ scope: 'portal',
928
+ appliesTo: 'The panel body when there are no options',
929
+ note: 'Gains shift-select-empty-container-error when the option fetch failed.',
930
+ },
931
+ { part: '<name>-select-spinner shift-select-spinner', scope: 'portal', appliesTo: 'The spinner shown while options are loading', note: '' },
932
+ {
933
+ part: 'custom-shift-select-option custom-shift-select-country-option',
934
+ scope: 'portal',
935
+ appliesTo: 'A country row in a phone field dropdown',
936
+ note: 'Gains custom-shift-select-option-selected and shift-select-option-selected together.',
937
+ },
938
+ { part: 'shift-select-country-code-label', scope: 'portal', appliesTo: 'The country code text in that row', note: '' },
939
+ { part: 'shift-select-country-number', scope: 'portal', appliesTo: 'The dialling number in that row', note: '' },
940
+
941
+ // ── Portal: branch-slot-dropdown ─────────────────────────────────────────
942
+ {
943
+ part: '<name>-slot-container branch-slot-container',
944
+ scope: 'portal',
945
+ appliesTo: 'The day-strip booking panel',
946
+ note: 'Gains branch-slot-container-open. Has a 288px floor and a JS-measured max-height.',
947
+ },
948
+ {
949
+ part: '<name>-slot-backdrop branch-slot-backdrop',
950
+ scope: 'portal',
951
+ appliesTo: 'The sheet scrim, visible only below 600px',
952
+ note: 'Gains branch-slot-backdrop-open. Hidden entirely on wider viewports.',
953
+ },
954
+ {
955
+ part: '<name>-slot-day branch-slot-day',
956
+ scope: 'portal',
957
+ appliesTo: 'One day chip in the strip',
958
+ note: 'Gains branch-slot-day-selected and branch-slot-day-disabled. Closed days are greyed, not removed.',
959
+ },
960
+ { part: '<name>-slot-time branch-slot-time', scope: 'portal', appliesTo: 'One time chip', note: 'Gains branch-slot-time-selected.' },
961
+ {
962
+ part: '<name>-slot-empty-container branch-slot-empty-container',
963
+ scope: 'portal',
964
+ appliesTo: 'The idle, empty and error states of the panel',
965
+ note: 'Gains branch-slot-empty-container-error on a failed fetch.',
966
+ },
967
+ { part: '<name>-slot-retry branch-slot-retry', scope: 'portal', appliesTo: 'The retry button in the error state', note: '' },
968
+
969
+ // ── Portal: branch-date-dropdown ─────────────────────────────────────────
970
+ {
971
+ part: '<name>-date-container branch-date-container',
972
+ scope: 'portal',
973
+ appliesTo: 'The month-calendar booking panel',
974
+ note: 'Gains branch-date-container-open. 300px floor, JS-measured max-height, flips upward via a bottom variable.',
975
+ },
976
+ { part: '<name>-date-backdrop branch-date-backdrop', scope: 'portal', appliesTo: 'The sheet scrim, visible only below 600px', note: 'Gains branch-date-backdrop-open.' },
977
+ {
978
+ part: '<name>-date-cell branch-date-cell',
979
+ scope: 'portal',
980
+ appliesTo: 'One day cell in the calendar grid',
981
+ note: 'Gains -open, -blocked, -off, -today and -selected. Blocked means returned but not bookable; off means never offered.',
982
+ },
983
+ {
984
+ part: '<name>-date-time branch-date-time',
985
+ scope: 'portal',
986
+ appliesTo: 'One time chip on the second pane',
987
+ note: 'Gains branch-date-time-selected. Entry is staggered by an inline animation-delay.',
988
+ },
989
+ { part: '<name>-date-prev branch-date-nav / <name>-date-next branch-date-nav', scope: 'portal', appliesTo: 'The month arrows', note: '' },
990
+ { part: '<name>-date-back branch-date-back', scope: 'portal', appliesTo: 'The button returning from the time pane to the calendar', note: '' },
991
+ {
992
+ part: '<name>-date-empty-container branch-date-empty-container',
993
+ scope: 'portal',
994
+ appliesTo: 'The idle, empty and error states of the panel',
995
+ note: 'Gains branch-date-empty-container-error.',
996
+ },
997
+ { part: '<name>-date-retry branch-date-retry', scope: 'portal', appliesTo: 'The retry button in the error state', note: '' },
998
+ ];
999
+
1000
+ export const limits = [
1001
+ {
1002
+ id: 'no-custom-properties',
1003
+ title: 'There are no themeable CSS custom properties',
1004
+ detail:
1005
+ 'Not one. Every var(--...) in the ticket-form components is a positioning channel written by JavaScript on the portaled panels — --shift-select-top/left/width, --branch-slot-top/left/width/max-height, --branch-date-top/bottom/left/width/max-height — and nothing else declares or reads a custom property. There is no --primary, no --radius, no font token. If a snippet you have been given sets one, it is doing nothing.',
1006
+ example: `/* Does nothing. No component reads any of these. */
1007
+ general-form {
1008
+ --primary: #b91c1c;
1009
+ --form-radius: 12px;
1010
+ --form-font: 'Nunito';
1011
+ }
1012
+
1013
+ /* The equivalent that works. */
1014
+ .docs-form::part(submit-button) { background: #b91c1c; }
1015
+ .docs-form::part(form-input) { border-radius: 12px; }
1016
+ .docs-form::part(shift-form) { font-family: 'Nunito', sans-serif !important; }`,
1017
+ workaround:
1018
+ 'Declare your own custom properties on the page and use them in your ::part() rules — the variables live in your stylesheet, the components never see them. That gives you a token layer without the components needing one.',
1019
+ },
1020
+ {
1021
+ id: 'part-is-a-leaf',
1022
+ title: 'You cannot select through a part',
1023
+ detail:
1024
+ '::part() is a terminal pseudo-element: nothing may follow it except a pseudo-class such as :hover, :focus or :disabled. An element with no part= of its own cannot be reached at all, from anywhere. Several elements in the tree are in exactly that position — the skeleton placeholders in both branch panels, the weekday row of the calendar, the day-of-week and month spans inside a day chip, the animated step panes, the <form> element itself.',
1025
+ example: `/* Invalid CSS. The whole rule is dropped, silently. */
1026
+ .docs-form::part(shift-form) .form-input-label { color: #334155; }
1027
+ .docs-form::part(form-input-container) input { border: none; }
1028
+
1029
+ /* Valid: a pseudo-class may follow a part. */
1030
+ .docs-form::part(form-input):focus { border-color: #475569; }
1031
+ .docs-form::part(form-input):disabled { opacity: 0.5; }
1032
+ .docs-form::part(submit-button):hover { background: #1e293b; }`,
1033
+ workaround:
1034
+ 'Find the nearest element that does have a part and style that instead. If none exists, the only route is a source change adding a part= — this is the reason to file an issue rather than to reach for a workaround.',
1035
+ },
1036
+ {
1037
+ id: 'portal-font',
1038
+ title: 'The portaled panels’ font-family cannot be overridden',
1039
+ detail:
1040
+ 'form-dialog, shift-select-dropdown, branch-slot-dropdown and branch-date-dropdown each declare their typeface twice inside their own shadow tree: once on :host with !important, and again as "*, :host * { font-family: inherit !important }". In the cascade, an important declaration from an inner shadow tree beats an important declaration from the outer page — so neither a ::part() rule nor a rule on the tag itself can change it, with or without !important. Every other property on those panels is themeable; only the typeface is fixed.',
1041
+ example: `/* All three have no effect. */
1042
+ .docs-form::part(shift-select-container) { font-family: 'Brand Sans' !important; }
1043
+ .docs-form::part(dialog-wrapper) { font-family: 'Brand Sans' !important; }
1044
+ shift-select-dropdown { font-family: 'Brand Sans' !important; }
1045
+
1046
+ /* These do work — size, weight, colour, spacing, background, radius. */
1047
+ .docs-form::part(shift-select-container) {
1048
+ font-size: 15px;
1049
+ background: #1e293b;
1050
+ border-radius: 10px;
1051
+ }
1052
+
1053
+ .docs-form::part(shift-select-option) {
1054
+ font-weight: 600;
1055
+ letter-spacing: 0.01em;
1056
+ color: #e2e8f0;
1057
+ }`,
1058
+ workaround:
1059
+ 'None from CSS. If your brand face matters more than the built-in Arabic fallback, the change is to those four stylesheets in the package. In practice the mismatch is only visible when a dropdown is open, and the shipped face already covers Latin, Arabic and Kurdish.',
1060
+ },
1061
+ {
1062
+ id: 'host-reset',
1063
+ title: 'The form does not inherit your page font',
1064
+ detail:
1065
+ 'Both stylesheets that land in the form’s shadow root — forms/defaults/style.css and form-elements/form-inputs.css — open with :host { all: initial !important }. That reset applies to the form element itself, and an important declaration from the inner tree beats an important one from the outer page, so the host’s own typography cannot be set from outside at all. Page-level font rules, and font rules on a wrapper around the form, therefore do not reach it.',
1066
+ example: `/* None of these reach the form. */
1067
+ body { font-family: 'Brand Sans', sans-serif; }
1068
+ .form-wrapper { font-family: 'Brand Sans', sans-serif; }
1069
+ general-form { font-family: 'Brand Sans', sans-serif !important; }
1070
+
1071
+ /* This does. Everything inside the form inherits from this div. */
1072
+ general-form::part(shift-form) {
1073
+ font-family: 'Brand Sans', 'Noto Kufi Arabic', sans-serif !important;
1074
+ }`,
1075
+ workaround:
1076
+ 'Always declare the font on ::part(shift-form), and keep the !important — it is what the four shipped presets do, and it keeps the rule immune to anything the components later declare inside the tree. Remember the portaled panels are a separate case and stay on their own face.',
1077
+ },
1078
+ {
1079
+ id: 'nested-shadow',
1080
+ title: 'The VIN scanner is a nested shadow root and is closed to you',
1081
+ detail:
1082
+ 'Everything in the in-page form is shadow:false and therefore lives in the form’s single shadow root — with one exception. vin-extractor, the camera overlay opened from a VIN field, is shadow:true and is rendered inside the form rather than portaled out. Its two internal parts are not re-exported: Stencil emits no exportparts attribute anywhere in this package, and parts do not cross a second boundary on their own.',
1083
+ example: `/* Reaches the field and its scan button — both are in the form's tree. */
1084
+ .docs-form::part(vin-input) { text-transform: uppercase; }
1085
+ .docs-form::part(vin-validator) { color: #0f172a; }
1086
+
1087
+ /* Reaches nothing: these parts are inside vin-extractor's own shadow root. */
1088
+ .docs-form::part(vin-extractor-capture-button) { background: #b91c1c; }
1089
+ vin-extractor::part(vin-extractor-capture-button) { background: #b91c1c; }`,
1090
+ workaround:
1091
+ 'The scanner overlay is a full-screen camera surface with its own dark treatment; in practice it does not need to match the form. If it must, that is an exportparts change in the package.',
1092
+ },
1093
+ {
1094
+ id: 'unnamed-labels',
1095
+ title: 'Some labels have no per-field part',
1096
+ detail:
1097
+ 'The label component emits <name>-label only when it is given the field name. Five components call it without: form-input, form-picker-input, form-vin-input, form-date-picker and form-time-picker. For a field rendered by any of those, ::part(<name>-label) and ::part(<name>-label-required-star) match nothing — only the generic form-input-label and form-input-label-required-star exist. The six that do pass the name are form-select, form-text-area, form-file, form-phone-number, branch-slot-picker and branch-date-picker.',
1098
+ example: `/* "email" is a form-input field: */
1099
+ .docs-form::part(form-input-label) { font-weight: 600; } /* applies */
1100
+ .docs-form::part(email-label) { font-weight: 800; } /* matches nothing */
1101
+
1102
+ /* "city" is a form-select field: */
1103
+ .docs-form::part(city-label) { font-weight: 800; } /* applies */
1104
+
1105
+ /* Note the error message is unaffected — every field passes its name there. */
1106
+ .docs-form::part(email-error-message) { color: #dc2626; } /* applies */`,
1107
+ workaround:
1108
+ 'Wrap the field in a named tag node in the structure and style that wrapper, or accept the generic label rule for those field types. There is no CSS-side fix, because a part cannot be used as an ancestor.',
1109
+ },
1110
+ {
1111
+ id: 'checkbox-switch-internals',
1112
+ title: 'Checkboxes and switches expose one part between them',
1113
+ detail:
1114
+ 'shift-checkbox and shift-switch both put part="shift-checkbox" on their clickable label and nothing else. The checkbox square, the tick, the switch track and the switch knob carry classes only. A single selector therefore hits both control types, and neither can be recoloured through ::part() — the default blue is baked into component CSS.',
1115
+ example: `/* All you can reach. Both controls answer to it. */
1116
+ .docs-form::part(shift-checkbox) {
1117
+ gap: 10px;
1118
+ font-size: 15px;
1119
+ color: #334155;
1120
+ }
1121
+
1122
+ /* No equivalent exists for the box, the tick, the track or the knob. */
1123
+
1124
+ /* The field wrapper and its error message are still per-field: */
1125
+ .docs-form::part(consent) { margin-top: 8px; }
1126
+ .docs-form::part(consent-error-message) { color: #b91c1c; }`,
1127
+ workaround:
1128
+ 'Keep these two controls on their default palette, or replace them in the structure with a select of yes/no options where the visual matters. Adding parts to their internals is a small source change if it becomes worth it.',
1129
+ },
1130
+ {
1131
+ id: 'picker-display-input',
1132
+ title: 'One picker’s display box cannot be named',
1133
+ detail:
1134
+ 'form-picker-input renders two inputs: a visible readonly one showing the formatted value and a transparent real one over it that opens the native picker. The visible one carries only part="form-input" — no <name> token — so a rule for it necessarily hits every text field in the form.',
1135
+ example: `/* Hits every field, not just the date picker. */
1136
+ .docs-form::part(form-input) { background: #f8fafc; }
1137
+
1138
+ /* Scope to the one picker through its container instead. */
1139
+ .docs-form::part(collectionDate-container) {
1140
+ background: #f8fafc;
1141
+ border-radius: 8px;
1142
+ }
1143
+
1144
+ /* The transparent overlay is separately named, if you need it. */
1145
+ .docs-form::part(collectionDate-input) { cursor: pointer; }`,
1146
+ workaround: 'Style the field’s container, which does carry the name, and let the display input inherit background and colour from it.',
1147
+ },
1148
+ {
1149
+ id: 'positioning-variables',
1150
+ title: 'The positioning variables belong to JavaScript',
1151
+ detail:
1152
+ 'The three portaled panels are positioned by setting --shift-select-*, --branch-slot-* and --branch-date-* as inline styles on the panel element, recomputed on open, on resize and on every scroll. An inline style already beats a stylesheet rule, and even a matching !important would be recomputed away on the next reposition.',
1153
+ example: `/* Both pointless. Overwritten within a frame. */
1154
+ .docs-form::part(shift-select-container) { --shift-select-top: 40px; }
1155
+ .docs-form::part(branch-date-container) { --branch-date-width: 420px !important; }
1156
+
1157
+ /* What you can control: the floors and caps the panel respects. */
1158
+ .docs-form::part(shift-select-container) { max-height: 320px; }
1159
+ .docs-form::part(branch-slot-container) { min-width: 340px; }`,
1160
+ workaround:
1161
+ 'Set min-width and max-height, which the panels honour, and leave placement to the component. It measures the trigger and the viewport, and flips the panel upward when there is no room below.',
1162
+ },
1163
+ {
1164
+ id: 'unnamed-structural-elements',
1165
+ title: 'The form element and the step panes have no parts',
1166
+ detail:
1167
+ 'The <form> tag itself, and the sliding panes that hold each step of a multi-step form, are rendered with classes only. So the submit-time form behaviour cannot be styled directly, and the step transition — a translate plus opacity over 700ms — cannot be retimed or disabled from a theme.',
1168
+ example: `/* No such part. */
1169
+ .docs-form::part(form) { padding: 24px; }
1170
+
1171
+ /* Put the padding on the shell instead. */
1172
+ .docs-form::part(shift-form) { padding: 24px; }
1173
+
1174
+ /* And style the step content through the wrapper the structure named. */
1175
+ .docs-form::part(step-one),
1176
+ .docs-form::part(step-two) {
1177
+ display: flex;
1178
+ flex-direction: column;
1179
+ gap: 20px;
1180
+ }`,
1181
+ workaround:
1182
+ 'Everything a theme normally wants from the form element — padding, background, max width — belongs on shift-form. For the step content, give each step a wrapper tag node with an id in the structure.',
1183
+ },
1184
+ {
1185
+ id: 'authored-names-are-not-a-contract',
1186
+ title: 'Structure-authored part names are not a contract',
1187
+ detail:
1188
+ 'Names coming from a structure node’s id or class exist only because that structure says so; rename the node and every rule using it stops matching, with no error. They also pass through the same class-merging helper the components use for real classes, which de-duplicates tokens that look like conflicting utility classes — so an id or class shaped like a utility can be dropped from the part list before it reaches the DOM.',
1189
+ example: `/* Fragile: depends on a structure you may not own. */
1190
+ .docs-form::part(inputs_wrapper) { display: grid; }
1191
+
1192
+ /* Stable: field names change only when the submitted payload changes. */
1193
+ .docs-form::part(message) { grid-column: 1 / -1; }
1194
+ .docs-form::part(submit-button) { grid-column: 1 / -1; }
1195
+
1196
+ /* Avoid ids and classes shaped like utility classes on tag nodes. */
1197
+ { "tag": "div", "id": "p-4" } /* may not survive as a part name */
1198
+ { "tag": "div", "id": "inputs_row" } /* safe */`,
1199
+ workaround:
1200
+ 'Anchor layout to field names where you can, keep authored ids descriptive rather than utility-shaped, and version a theme alongside the structure it was written against.',
1201
+ },
1202
+ ];
1203
+
1204
+ export const partNameRules = {
1205
+ summary:
1206
+ 'Part names come from three places, and knowing which one you are looking at tells you how stable it is. (1) Fixed aliases the components always emit — shift-form, form-input, form-input-label, submit-button, dialog-wrapper. These never change. (2) Derived names, built by prefixing the field’s name from the structure: <name>, <name>-container, <name>-input, <name>-error-message. These change only if the field is renamed, which also changes the submitted payload. (3) Authored names, taken from the id and class you write on a tag node in the structure, alongside a generic element-<tag> and the bare tag. These are yours, and they are only as stable as the structure file. Most elements carry two tokens at once — one derived and one fixed — so you can style broadly with the alias and override narrowly with the name.',
1207
+ example: `// A slice of a structure, and everything it names.
1208
+ {
1209
+ "tag": "div",
1210
+ "id": "contact_block", // → ::part(contact_block)
1211
+ "class": "stack", // → ::part(stack)
1212
+ // → ::part(element-div), ::part(div)
1213
+ "children": [
1214
+ { "name": "email" }, // a field, rendered by the mapper
1215
+ { "name": "message" }
1216
+ ]
1217
+ }
1218
+
1219
+ /* The layout node — an authored name. */
1220
+ .docs-form::part(contact_block) {
1221
+ display: grid;
1222
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1223
+ gap: 20px;
1224
+ }
1225
+
1226
+ /* The field — a derived name, plus the fixed aliases on the same elements. */
1227
+ .docs-form::part(email) { grid-column: 1; }
1228
+ .docs-form::part(email-container) { position: relative; }
1229
+ .docs-form::part(email-input) { border-radius: 8px; }
1230
+ .docs-form::part(email-error-message) { color: #b91c1c; }
1231
+
1232
+ .docs-form::part(message) { grid-column: 1 / -1; }
1233
+ .docs-form::part(message-textarea) { min-height: 140px; }
1234
+
1235
+ /* The fixed aliases, reaching every field of a kind at once. */
1236
+ .docs-form::part(form-input) { border: 1px solid #cbd5e1; }
1237
+ .docs-form::part(form-input-textarea) { border: 1px solid #cbd5e1; }
1238
+ .docs-form::part(form-input-label) { font-weight: 600; }`,
1239
+ note: 'Two gaps to keep in mind while reading a name. First, a token is a token: part="a b c" means all three of ::part(a), ::part(b) and ::part(c) match that one element, and a state suffix such as -selected, -active, -done or -open is just another token on the same element, not a separate element. Second, ::part() is a leaf — nothing may follow it but a pseudo-class — so if the element you want carries no part of its own, no name gets you there.',
1240
+ };