@lifesg/react-design-system 4.0.0-alpha.12 → 4.0.0-alpha.14

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 (323) hide show
  1. package/breadcrumb/breadcrumb.js +1 -1
  2. package/breadcrumb/breadcrumb.js.map +1 -1
  3. package/breadcrumb/breadcrumb.styles.d.ts +0 -8
  4. package/breadcrumb/breadcrumb.styles.js +1 -1
  5. package/breadcrumb/breadcrumb.styles.js.map +1 -1
  6. package/breadcrumb/breadcrumb.styles_1blzn8n.css +7 -0
  7. package/breadcrumb/types.d.ts +2 -13
  8. package/cjs/breadcrumb/breadcrumb.js +1 -1
  9. package/cjs/breadcrumb/breadcrumb.js.map +1 -1
  10. package/cjs/breadcrumb/breadcrumb.styles.js +1 -1
  11. package/cjs/breadcrumb/breadcrumb.styles.js.map +1 -1
  12. package/cjs/breadcrumb/breadcrumb.styles_1blzn8n.css +7 -0
  13. package/cjs/drawer/drawer.js +1 -1
  14. package/cjs/drawer/drawer.js.map +1 -1
  15. package/cjs/drawer/drawer.styles.js +1 -1
  16. package/cjs/drawer/drawer.styles.js.map +1 -1
  17. package/{drawer/drawer.styles_94hx3y.css → cjs/drawer/drawer.styles_a4bwe0.css} +6 -3
  18. package/cjs/external/dompurify/dist/purify.es.js +4 -2
  19. package/cjs/external/dompurify/dist/purify.es.js.map +1 -1
  20. package/cjs/file-upload/file-list/file-list.js +1 -1
  21. package/cjs/file-upload/file-list/file-list.js.map +1 -1
  22. package/cjs/file-upload/file-list/file-list.styles.js +1 -1
  23. package/cjs/file-upload/file-list/file-list.styles.js.map +1 -1
  24. package/cjs/file-upload/file-list/file-list.styles_1xbypnh.css +2 -0
  25. package/cjs/file-upload/file-list-item/file-item-actions.js +2 -0
  26. package/cjs/file-upload/file-list-item/file-item-actions.js.map +1 -0
  27. package/cjs/file-upload/file-list-item/file-item-actions.styles.js +2 -0
  28. package/cjs/file-upload/file-list-item/file-item-actions.styles.js.map +1 -0
  29. package/cjs/file-upload/file-list-item/file-item-actions.styles_1quulgy.css +8 -0
  30. package/cjs/file-upload/file-list-item/file-item-details.js +2 -0
  31. package/cjs/file-upload/file-list-item/file-item-details.js.map +1 -0
  32. package/cjs/file-upload/file-list-item/file-item-details.styles.js +2 -0
  33. package/cjs/file-upload/file-list-item/file-item-details.styles.js.map +1 -0
  34. package/cjs/file-upload/file-list-item/file-item-details.styles_1ce9akx.css +7 -0
  35. package/cjs/file-upload/file-list-item/file-list-item.js +1 -1
  36. package/cjs/file-upload/file-list-item/file-list-item.js.map +1 -1
  37. package/cjs/file-upload/file-list-item/file-list-item.styles.js +1 -1
  38. package/cjs/file-upload/file-list-item/file-list-item.styles.js.map +1 -1
  39. package/cjs/file-upload/file-list-item/file-list-item.styles_x3a500.css +10 -0
  40. package/cjs/file-upload/file-list-item/use-truncated-name.js +2 -0
  41. package/cjs/file-upload/file-list-item/use-truncated-name.js.map +1 -0
  42. package/cjs/local-nav/local-nav-dropdown/local-nav-dropdown.js +1 -1
  43. package/cjs/local-nav/local-nav-dropdown/local-nav-dropdown.js.map +1 -1
  44. package/cjs/local-nav/local-nav-dropdown/local-nav-dropdown.styles.js +1 -1
  45. package/cjs/local-nav/local-nav-dropdown/local-nav-dropdown.styles.js.map +1 -1
  46. package/{local-nav/local-nav-dropdown/local-nav-dropdown.styles_uhd4xl.css → cjs/local-nav/local-nav-dropdown/local-nav-dropdown.styles_kpel1m.css} +9 -6
  47. package/cjs/local-nav/local-nav-menu/local-nav-menu.js +1 -1
  48. package/cjs/local-nav/local-nav-menu/local-nav-menu.js.map +1 -1
  49. package/cjs/local-nav/local-nav-menu/local-nav-menu.styles.js +1 -1
  50. package/cjs/local-nav/local-nav-menu/local-nav-menu.styles.js.map +1 -1
  51. package/cjs/local-nav/local-nav-menu/local-nav-menu.styles_67fcyc.css +8 -0
  52. package/cjs/menu/menu-content.js +1 -1
  53. package/cjs/menu/menu-content.js.map +1 -1
  54. package/cjs/menu/menu-content.styles.js +1 -1
  55. package/cjs/menu/menu-content.styles.js.map +1 -1
  56. package/cjs/menu/menu-content.styles_1r8oxtk.css +5 -0
  57. package/cjs/menu/menu-item.styles.js +1 -1
  58. package/cjs/menu/menu-item.styles.js.map +1 -1
  59. package/cjs/menu/{menu-item.styles_liyed8.css → menu-item.styles_eut2j9.css} +1 -1
  60. package/cjs/menu/menu-link.styles.js +1 -1
  61. package/cjs/menu/menu-link.styles.js.map +1 -1
  62. package/cjs/menu/menu-link.styles_rng7n7.css +2 -0
  63. package/cjs/menu/menu-section.js +1 -1
  64. package/cjs/menu/menu-section.js.map +1 -1
  65. package/cjs/menu/menu-section.styles.js +1 -1
  66. package/cjs/menu/menu-section.styles.js.map +1 -1
  67. package/cjs/menu/menu-section.styles_1n0ppxl.css +5 -0
  68. package/cjs/modal-v2/slots/slot-styles.js +1 -1
  69. package/cjs/modal-v2/slots/slot-styles.js.map +1 -1
  70. package/cjs/modal-v2/slots/{slot-styles_ayb9il.css → slot-styles_ygu6zc.css} +1 -1
  71. package/cjs/navbar/navbar-items.js +1 -1
  72. package/cjs/navbar/navbar-items.js.map +1 -1
  73. package/cjs/notification-banner/notification-banner-hoc.js +1 -1
  74. package/cjs/notification-banner/notification-banner-hoc.js.map +1 -1
  75. package/cjs/pagination/pagination.js +1 -1
  76. package/cjs/pagination/pagination.js.map +1 -1
  77. package/cjs/shared/dropdown-list/dropdown-label.js +1 -1
  78. package/cjs/shared/dropdown-list/dropdown-label.js.map +1 -1
  79. package/cjs/shared/fade-wrapper/fade-wrapper.js +1 -1
  80. package/cjs/shared/fade-wrapper/fade-wrapper.js.map +1 -1
  81. package/cjs/theme/tokens/colour.js +1 -1
  82. package/cjs/theme/tokens/colour.js.map +1 -1
  83. package/cjs/theme/tokens/radius.js +1 -1
  84. package/cjs/theme/tokens/radius.js.map +1 -1
  85. package/codemods/run-codemod.js +8 -2
  86. package/codemods/run-codemod.js.map +1 -1
  87. package/codemods/run-codemod.ts +13 -4
  88. package/drawer/drawer.d.ts +1 -1
  89. package/drawer/drawer.js +1 -1
  90. package/drawer/drawer.js.map +1 -1
  91. package/drawer/drawer.styles.d.ts +3 -0
  92. package/drawer/drawer.styles.js +1 -1
  93. package/drawer/drawer.styles.js.map +1 -1
  94. package/{cjs/drawer/drawer.styles_94hx3y.css → drawer/drawer.styles_a4bwe0.css} +6 -3
  95. package/drawer/types.d.ts +7 -0
  96. package/external/dompurify/dist/purify.es.js +4 -2
  97. package/external/dompurify/dist/purify.es.js.map +1 -1
  98. package/file-upload/file-list/file-list.js +1 -1
  99. package/file-upload/file-list/file-list.js.map +1 -1
  100. package/file-upload/file-list/file-list.styles.d.ts +0 -1
  101. package/file-upload/file-list/file-list.styles.js +1 -1
  102. package/file-upload/file-list/file-list.styles.js.map +1 -1
  103. package/file-upload/file-list/file-list.styles_1xbypnh.css +2 -0
  104. package/file-upload/file-list-item/file-item-actions.d.ts +30 -0
  105. package/file-upload/file-list-item/file-item-actions.js +2 -0
  106. package/file-upload/file-list-item/file-item-actions.js.map +1 -0
  107. package/file-upload/file-list-item/file-item-actions.styles.d.ts +6 -0
  108. package/file-upload/file-list-item/file-item-actions.styles.js +2 -0
  109. package/file-upload/file-list-item/file-item-actions.styles.js.map +1 -0
  110. package/file-upload/file-list-item/file-item-actions.styles_1quulgy.css +8 -0
  111. package/file-upload/file-list-item/file-item-details.d.ts +15 -0
  112. package/file-upload/file-list-item/file-item-details.js +2 -0
  113. package/file-upload/file-list-item/file-item-details.js.map +1 -0
  114. package/file-upload/file-list-item/file-item-details.styles.d.ts +7 -0
  115. package/file-upload/file-list-item/file-item-details.styles.js +2 -0
  116. package/file-upload/file-list-item/file-item-details.styles.js.map +1 -0
  117. package/file-upload/file-list-item/file-item-details.styles_1ce9akx.css +7 -0
  118. package/file-upload/file-list-item/file-list-item.d.ts +1 -7
  119. package/file-upload/file-list-item/file-list-item.js +1 -1
  120. package/file-upload/file-list-item/file-list-item.js.map +1 -1
  121. package/file-upload/file-list-item/file-list-item.styles.d.ts +6 -13
  122. package/file-upload/file-list-item/file-list-item.styles.js +1 -1
  123. package/file-upload/file-list-item/file-list-item.styles.js.map +1 -1
  124. package/file-upload/file-list-item/file-list-item.styles_x3a500.css +10 -0
  125. package/file-upload/file-list-item/types.d.ts +10 -3
  126. package/file-upload/file-list-item/use-truncated-name.d.ts +1 -0
  127. package/file-upload/file-list-item/use-truncated-name.js +2 -0
  128. package/file-upload/file-list-item/use-truncated-name.js.map +1 -0
  129. package/input-group/types.d.ts +4 -1
  130. package/input-select/types.d.ts +1 -0
  131. package/local-nav/local-nav-dropdown/local-nav-dropdown.js +1 -1
  132. package/local-nav/local-nav-dropdown/local-nav-dropdown.js.map +1 -1
  133. package/local-nav/local-nav-dropdown/local-nav-dropdown.styles.d.ts +3 -0
  134. package/local-nav/local-nav-dropdown/local-nav-dropdown.styles.js +1 -1
  135. package/local-nav/local-nav-dropdown/local-nav-dropdown.styles.js.map +1 -1
  136. package/{cjs/local-nav/local-nav-dropdown/local-nav-dropdown.styles_uhd4xl.css → local-nav/local-nav-dropdown/local-nav-dropdown.styles_kpel1m.css} +9 -6
  137. package/local-nav/local-nav-menu/local-nav-menu.js +1 -1
  138. package/local-nav/local-nav-menu/local-nav-menu.js.map +1 -1
  139. package/local-nav/local-nav-menu/local-nav-menu.styles.d.ts +3 -0
  140. package/local-nav/local-nav-menu/local-nav-menu.styles.js +1 -1
  141. package/local-nav/local-nav-menu/local-nav-menu.styles.js.map +1 -1
  142. package/local-nav/local-nav-menu/local-nav-menu.styles_67fcyc.css +8 -0
  143. package/local-nav/types.d.ts +17 -0
  144. package/masked-input/types.d.ts +1 -2
  145. package/menu/menu-content.js +1 -1
  146. package/menu/menu-content.js.map +1 -1
  147. package/menu/menu-content.styles.d.ts +1 -0
  148. package/menu/menu-content.styles.js +1 -1
  149. package/menu/menu-content.styles.js.map +1 -1
  150. package/menu/menu-content.styles_1r8oxtk.css +5 -0
  151. package/menu/menu-item.styles.js +1 -1
  152. package/menu/menu-item.styles.js.map +1 -1
  153. package/menu/{menu-item.styles_liyed8.css → menu-item.styles_eut2j9.css} +1 -1
  154. package/menu/menu-link.styles.js +1 -1
  155. package/menu/menu-link.styles.js.map +1 -1
  156. package/menu/menu-link.styles_rng7n7.css +2 -0
  157. package/menu/menu-section.d.ts +1 -1
  158. package/menu/menu-section.js +1 -1
  159. package/menu/menu-section.js.map +1 -1
  160. package/menu/menu-section.styles.d.ts +7 -0
  161. package/menu/menu-section.styles.js +1 -1
  162. package/menu/menu-section.styles.js.map +1 -1
  163. package/menu/menu-section.styles_1n0ppxl.css +5 -0
  164. package/menu/menu.d.ts +1 -1
  165. package/menu/types.d.ts +10 -2
  166. package/modal-v2/slots/slot-styles.js +1 -1
  167. package/modal-v2/slots/slot-styles.js.map +1 -1
  168. package/modal-v2/slots/{slot-styles_ayb9il.css → slot-styles_ygu6zc.css} +1 -1
  169. package/navbar/navbar-items.js +1 -1
  170. package/navbar/navbar-items.js.map +1 -1
  171. package/navbar/types.d.ts +9 -0
  172. package/notification-banner/notification-banner-hoc.js +1 -1
  173. package/notification-banner/notification-banner-hoc.js.map +1 -1
  174. package/package.json +3 -4
  175. package/pagination/pagination.js +1 -1
  176. package/pagination/pagination.js.map +1 -1
  177. package/pagination/types.d.ts +8 -1
  178. package/scripts/cli.js +18 -0
  179. package/select-histogram/types.d.ts +1 -0
  180. package/shared/dropdown-list/dropdown-label.d.ts +2 -2
  181. package/shared/dropdown-list/dropdown-label.js +1 -1
  182. package/shared/dropdown-list/dropdown-label.js.map +1 -1
  183. package/shared/dropdown-list/types.d.ts +6 -2
  184. package/shared/fade-wrapper/fade-wrapper.js +1 -1
  185. package/shared/fade-wrapper/fade-wrapper.js.map +1 -1
  186. package/shared/fade-wrapper/types.d.ts +1 -0
  187. package/shared/styles/index.d.ts +5 -0
  188. package/skills/fds-build/SKILL.md +167 -0
  189. package/skills/fds-build/catalogue.md +181 -0
  190. package/skills/fds-build/evals/evals.json +519 -0
  191. package/skills/fds-build/resources/components/accordion.md +128 -0
  192. package/skills/fds-build/resources/components/alert.md +54 -0
  193. package/skills/fds-build/resources/components/animation.md +18 -0
  194. package/skills/fds-build/resources/components/avatar.md +10 -0
  195. package/skills/fds-build/resources/components/badge.md +18 -0
  196. package/skills/fds-build/resources/components/box-container.md +61 -0
  197. package/skills/fds-build/resources/components/breadcrumb.md +60 -0
  198. package/skills/fds-build/resources/components/button.md +74 -0
  199. package/skills/fds-build/resources/components/calendar.md +113 -0
  200. package/skills/fds-build/resources/components/card.md +43 -0
  201. package/skills/fds-build/resources/components/checkbox.md +53 -0
  202. package/skills/fds-build/resources/components/countdown-timer.md +20 -0
  203. package/skills/fds-build/resources/components/data-table.md +142 -0
  204. package/skills/fds-build/resources/components/date-navigator.md +25 -0
  205. package/skills/fds-build/resources/components/divider.md +23 -0
  206. package/skills/fds-build/resources/components/drawer.md +78 -0
  207. package/skills/fds-build/resources/components/error-display.md +137 -0
  208. package/skills/fds-build/resources/components/feedback-rating.md +61 -0
  209. package/skills/fds-build/resources/components/file-download.md +54 -0
  210. package/skills/fds-build/resources/components/file-upload.md +105 -0
  211. package/skills/fds-build/resources/components/filter.md +228 -0
  212. package/skills/fds-build/resources/components/footer.md +94 -0
  213. package/skills/fds-build/resources/components/form-date-input.md +85 -0
  214. package/skills/fds-build/resources/components/form-date-range-input.md +146 -0
  215. package/skills/fds-build/resources/components/form-e-signature.md +55 -0
  216. package/skills/fds-build/resources/components/form-input.md +65 -0
  217. package/skills/fds-build/resources/components/form-masked-input.md +69 -0
  218. package/skills/fds-build/resources/components/form-multi-select.md +100 -0
  219. package/skills/fds-build/resources/components/form-nested-multi-select.md +159 -0
  220. package/skills/fds-build/resources/components/form-nested-select.md +151 -0
  221. package/skills/fds-build/resources/components/form-otp-verification.md +175 -0
  222. package/skills/fds-build/resources/components/form-phone-number-input.md +87 -0
  223. package/skills/fds-build/resources/components/form-predictive-text-input.md +108 -0
  224. package/skills/fds-build/resources/components/form-select.md +103 -0
  225. package/skills/fds-build/resources/components/form-slider.md +116 -0
  226. package/skills/fds-build/resources/components/form-textarea.md +116 -0
  227. package/skills/fds-build/resources/components/form-time-range-picker.md +98 -0
  228. package/skills/fds-build/resources/components/form-timepicker.md +77 -0
  229. package/skills/fds-build/resources/components/form-unit-number-input.md +138 -0
  230. package/skills/fds-build/resources/components/fullscreen-image-carousel.md +159 -0
  231. package/skills/fds-build/resources/components/histogram-slider.md +41 -0
  232. package/skills/fds-build/resources/components/icon.md +167 -0
  233. package/skills/fds-build/resources/components/image-button.md +12 -0
  234. package/skills/fds-build/resources/components/input.md +51 -0
  235. package/skills/fds-build/resources/components/language-switcher.md +15 -0
  236. package/skills/fds-build/resources/components/layout.md +156 -0
  237. package/skills/fds-build/resources/components/link-list.md +120 -0
  238. package/skills/fds-build/resources/components/local-nav.md +119 -0
  239. package/skills/fds-build/resources/components/masthead.md +14 -0
  240. package/skills/fds-build/resources/components/menu.md +109 -0
  241. package/skills/fds-build/resources/components/modal.md +150 -0
  242. package/skills/fds-build/resources/components/navbar.md +270 -0
  243. package/skills/fds-build/resources/components/notification-banner.md +106 -0
  244. package/skills/fds-build/resources/components/pagination.md +104 -0
  245. package/skills/fds-build/resources/components/pill.md +55 -0
  246. package/skills/fds-build/resources/components/popover.md +80 -0
  247. package/skills/fds-build/resources/components/progress-indicator.md +47 -0
  248. package/skills/fds-build/resources/components/radio-button.md +66 -0
  249. package/skills/fds-build/resources/components/schedule.md +78 -0
  250. package/skills/fds-build/resources/components/sidenav.md +183 -0
  251. package/skills/fds-build/resources/components/singpass-button.md +14 -0
  252. package/skills/fds-build/resources/components/smart-app-banner.md +28 -0
  253. package/skills/fds-build/resources/components/tab.md +107 -0
  254. package/skills/fds-build/resources/components/table.md +65 -0
  255. package/skills/fds-build/resources/components/tag.md +68 -0
  256. package/skills/fds-build/resources/components/text-list.md +44 -0
  257. package/skills/fds-build/resources/components/time-slot-bar-week.md +66 -0
  258. package/skills/fds-build/resources/components/time-slot-bar.md +57 -0
  259. package/skills/fds-build/resources/components/time-slot-week-view.md +70 -0
  260. package/skills/fds-build/resources/components/timeline.md +69 -0
  261. package/skills/fds-build/resources/components/timetable.md +123 -0
  262. package/skills/fds-build/resources/components/toast.md +72 -0
  263. package/skills/fds-build/resources/components/toggle.md +119 -0
  264. package/skills/fds-build/resources/components/typography.md +86 -0
  265. package/skills/fds-build/resources/components/uneditable-section.md +84 -0
  266. package/skills/fds-build/resources/foundations/layout-composition-patterns.md +53 -0
  267. package/skills/fds-build/resources/foundations/tokens.md +547 -0
  268. package/skills/fds-build/resources/patterns/states.md +300 -0
  269. package/skills/fds-build/resources/setup/setup.md +202 -0
  270. package/skills/fds-build/resources/templates/form.md +427 -0
  271. package/skills/fds-build/resources/templates/listing.md +435 -0
  272. package/theme/styles/a11y-playground.css +52 -40
  273. package/theme/styles/bookingsg.css +52 -40
  274. package/theme/styles/careercompass.css +52 -40
  275. package/theme/styles/ccube.css +52 -40
  276. package/theme/styles/imda.css +52 -40
  277. package/theme/styles/lifesg.css +52 -40
  278. package/theme/styles/mylegacy.css +52 -40
  279. package/theme/styles/oneservice.css +52 -40
  280. package/theme/styles/pa.css +28 -22
  281. package/theme/styles/rbs.css +52 -40
  282. package/theme/styles/sgw-digital-lobby.css +52 -40
  283. package/theme/styles/smgs.css +52 -40
  284. package/theme/styles/spf.css +28 -27
  285. package/theme/styles/sportsg-orange.css +52 -40
  286. package/theme/styles/supportgowhere.css +52 -40
  287. package/theme/styles/tote-board.css +52 -40
  288. package/theme/styles/vica.css +52 -40
  289. package/theme/styles/websg.css +52 -40
  290. package/theme/styles/wise.css +52 -40
  291. package/theme/styles/wogaa.css +52 -40
  292. package/theme/tokens/colour.d.ts +6 -0
  293. package/theme/tokens/colour.js +1 -1
  294. package/theme/tokens/colour.js.map +1 -1
  295. package/theme/tokens/radius.d.ts +3 -0
  296. package/theme/tokens/radius.js +1 -1
  297. package/theme/tokens/radius.js.map +1 -1
  298. package/breadcrumb/breadcrumb.styles_1nl4aua.css +0 -18
  299. package/cjs/breadcrumb/breadcrumb.styles_1nl4aua.css +0 -18
  300. package/cjs/file-upload/file-item-edit.js +0 -2
  301. package/cjs/file-upload/file-item-edit.js.map +0 -1
  302. package/cjs/file-upload/file-item-edit.styles.js +0 -2
  303. package/cjs/file-upload/file-item-edit.styles.js.map +0 -1
  304. package/cjs/file-upload/file-item-edit.styles_19bvztn.css +0 -11
  305. package/cjs/file-upload/file-list/file-list.styles_1w7so98.css +0 -3
  306. package/cjs/file-upload/file-list-item/file-list-item.styles_kgtfke.css +0 -17
  307. package/cjs/local-nav/local-nav-menu/local-nav-menu.styles_pq19dh.css +0 -5
  308. package/cjs/menu/menu-content.styles_p7ijll.css +0 -2
  309. package/cjs/menu/menu-link.styles_hfxoez.css +0 -2
  310. package/cjs/menu/menu-section.styles_g7my4d.css +0 -3
  311. package/file-upload/file-item-edit.d.ts +0 -14
  312. package/file-upload/file-item-edit.js +0 -2
  313. package/file-upload/file-item-edit.js.map +0 -1
  314. package/file-upload/file-item-edit.styles.d.ts +0 -9
  315. package/file-upload/file-item-edit.styles.js +0 -2
  316. package/file-upload/file-item-edit.styles.js.map +0 -1
  317. package/file-upload/file-item-edit.styles_19bvztn.css +0 -11
  318. package/file-upload/file-list/file-list.styles_1w7so98.css +0 -3
  319. package/file-upload/file-list-item/file-list-item.styles_kgtfke.css +0 -17
  320. package/local-nav/local-nav-menu/local-nav-menu.styles_pq19dh.css +0 -5
  321. package/menu/menu-content.styles_p7ijll.css +0 -2
  322. package/menu/menu-link.styles_hfxoez.css +0 -2
  323. package/menu/menu-section.styles_g7my4d.css +0 -3
@@ -0,0 +1,427 @@
1
+ # Form Template
2
+
3
+ Single or multi-step form page.
4
+
5
+ ## When to use
6
+
7
+ - Collecting user input across one or more steps
8
+ - Profile, settings, or data-entry pages
9
+
10
+ ---
11
+
12
+ ## Page anatomy
13
+
14
+ ```
15
+ Navbar
16
+ ──────────────────────────────────────────────────────────
17
+ <main>
18
+ └─ pageWrapper (padding: spacing-32 0; lg: spacing-64)
19
+ └─ Layout.Container type="grid"
20
+ ├─ Layout.ColDiv xxsCols={[1, -1]}
21
+ │ └─ ProgressIndicator (multi-step only)
22
+ └─ Layout.ColDiv xxsCols={12} lgCols={8}
23
+ │ flex column · gap: spacing-32
24
+ ├─ pageHeadingBlock (flex column · gap: 16)
25
+ │ ├─ h1
26
+ │ └─ p (optional)
27
+ ├─ div (form content)
28
+ │ └─ Form fields (built-in margin-bottom: 32)
29
+ │ Non-form components → margin-bottom: 32
30
+ └─ FormNav
31
+ ──────────────────────────────────────────────────────────
32
+ Footer
33
+ ```
34
+
35
+ ---
36
+
37
+ ## Rules
38
+
39
+ | | Wrong | Correct |
40
+ | ------------------------ | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
41
+ | Content column width | `lgCols={12}` | `xxsCols={12} lgCols={8}` |
42
+ | Progress indicator | Inside 8-col div | Separate `lgCols={12}` ColDiv |
43
+ | Page heading | Static string | Defaults to `{STEPS[step]}`; override per step as needed |
44
+ | Field spacing | `gap` on wrapper div | Rely on component built-in `margin-bottom: 32`; add `margin-bottom: var(--fds-spacing-32)` via CSS Module class for components without it (BoxContainer, Toggle, FileUpload, ESignature) |
45
+ | Constrained-width inputs | `width` on element | `max-width` — works alongside internal `width: 100%` |
46
+ | Review step | Omitting | Required as last step — one `BoxContainer` per preceding step |
47
+
48
+ > **Setup:** If the project is not yet set up, read `../setup/setup.md` first before writing any code here.
49
+
50
+ ---
51
+
52
+ ## FormNav
53
+
54
+ ```tsx
55
+ // FormNav.tsx
56
+ import { useRef, useLayoutEffect } from "react";
57
+ import { Button } from "@lifesg/react-design-system/button";
58
+ import styles from "./FormNav.module.css";
59
+
60
+ interface FormNavProps {
61
+ onBack?: () => void;
62
+ onNext: () => void;
63
+ nextLabel?: string;
64
+ isSubmitting?: boolean;
65
+ actions?: React.ReactNode;
66
+ }
67
+
68
+ export default function FormNav({
69
+ onBack,
70
+ onNext,
71
+ nextLabel = "Next",
72
+ isSubmitting = false,
73
+ actions,
74
+ }: FormNavProps) {
75
+ const navRef = useRef<HTMLDivElement>(null);
76
+
77
+ useLayoutEffect(() => {
78
+ if (!navRef.current) return;
79
+ const buttons = Array.from(
80
+ navRef.current.querySelectorAll<HTMLElement>("button")
81
+ );
82
+ buttons.forEach((b) => {
83
+ b.style.width = "";
84
+ });
85
+ if (!window.matchMedia("(min-width: 769px)").matches) return;
86
+ const widest = Math.max(...buttons.map((b) => b.offsetWidth));
87
+ buttons.forEach((b) => {
88
+ b.style.width = `${widest}px`;
89
+ });
90
+ });
91
+
92
+ return (
93
+ <div ref={navRef} className={styles.nav}>
94
+ <div className={styles.primaryGroup}>
95
+ <Button
96
+ styleType="default"
97
+ onClick={onNext}
98
+ loading={isSubmitting}
99
+ disabled={isSubmitting}
100
+ className={styles.nextButton}
101
+ >
102
+ {nextLabel}
103
+ </Button>
104
+ {actions && (
105
+ <div className={styles.actionsGroup}>{actions}</div>
106
+ )}
107
+ </div>
108
+ {onBack && (
109
+ <Button
110
+ styleType="secondary"
111
+ onClick={onBack}
112
+ className={styles.backButton}
113
+ >
114
+ Back
115
+ </Button>
116
+ )}
117
+ </div>
118
+ );
119
+ }
120
+ ```
121
+
122
+ ```css
123
+ /* FormNav.module.css */
124
+ .nav {
125
+ margin-top: var(--fds-spacing-32);
126
+ display: flex;
127
+ flex-direction: column;
128
+ gap: var(--fds-spacing-24);
129
+
130
+ :global(body.fds-breakpoint-lg-min) & {
131
+ flex-direction: row;
132
+ align-items: center;
133
+ justify-content: space-between;
134
+ gap: 0;
135
+ }
136
+ }
137
+
138
+ .primaryGroup {
139
+ display: flex;
140
+ flex-direction: column;
141
+ gap: var(--fds-spacing-16);
142
+
143
+ :global(body.fds-breakpoint-lg-min) & {
144
+ flex-direction: row;
145
+ gap: var(--fds-spacing-16);
146
+ order: 2;
147
+ margin-left: auto;
148
+ }
149
+ }
150
+
151
+ .nextButton {
152
+ box-sizing: border-box;
153
+ width: 100%;
154
+
155
+ :global(body.fds-breakpoint-lg-min) & {
156
+ width: auto;
157
+ min-width: 10rem;
158
+ order: 1;
159
+ }
160
+ }
161
+
162
+ .actionsGroup {
163
+ display: flex;
164
+ flex-direction: column;
165
+ gap: var(--fds-spacing-16);
166
+
167
+ & > button {
168
+ box-sizing: border-box;
169
+ width: 100%;
170
+ }
171
+
172
+ :global(body.fds-breakpoint-lg-min) & {
173
+ flex-direction: row;
174
+ gap: var(--fds-spacing-16);
175
+ & > button {
176
+ width: auto;
177
+ min-width: 10rem;
178
+ }
179
+ }
180
+ }
181
+
182
+ .backButton {
183
+ box-sizing: border-box;
184
+ width: 100%;
185
+
186
+ :global(body.fds-breakpoint-lg-min) & {
187
+ width: auto;
188
+ min-width: 10rem;
189
+ order: 1;
190
+ }
191
+ }
192
+ ```
193
+
194
+ ---
195
+
196
+ ## Review step
197
+
198
+ ```tsx
199
+ import { BoxContainer } from "@lifesg/react-design-system/box-container";
200
+ import { UneditableSection } from "@lifesg/react-design-system/uneditable-section";
201
+ import styles from "./FormPage.module.css";
202
+
203
+ {
204
+ step === STEPS.length - 1 && (
205
+ <BoxContainer
206
+ title="Step one"
207
+ collapsible
208
+ expanded
209
+ className={styles.boxContainer}
210
+ >
211
+ <div className={styles.summaryPadding}>
212
+ <UneditableSection
213
+ fullWidth
214
+ background={false}
215
+ items={[
216
+ {
217
+ label: "Short field",
218
+ value: valueA || "—",
219
+ displayWidth: "half",
220
+ },
221
+ {
222
+ label: "Short field",
223
+ value: valueB || "—",
224
+ displayWidth: "half",
225
+ },
226
+ { label: "Long field", value: longValue || "—" },
227
+ ]}
228
+ />
229
+ </div>
230
+ </BoxContainer>
231
+ );
232
+ }
233
+ ```
234
+
235
+ ```css
236
+ /* in FormPage.module.css */
237
+ .boxContainer {
238
+ margin-bottom: var(--fds-spacing-32);
239
+ }
240
+
241
+ .summaryPadding {
242
+ padding: var(--fds-spacing-20);
243
+
244
+ :global(body.fds-breakpoint-md-min) & {
245
+ padding: var(--fds-spacing-32);
246
+ }
247
+ }
248
+ ```
249
+
250
+ ---
251
+
252
+ ## Complete template
253
+
254
+ ```tsx
255
+ // FormPage.tsx
256
+ import { useState } from "react";
257
+ import { Form } from "@lifesg/react-design-system/form";
258
+ import { ProgressIndicator } from "@lifesg/react-design-system/progress-indicator";
259
+ import { Layout } from "@lifesg/react-design-system/layout";
260
+ import { Alert } from "@lifesg/react-design-system/alert";
261
+ import FormNav from "./FormNav";
262
+ import styles from "./FormPage.module.css";
263
+
264
+ const STEPS = ["Step one", "Step two", "Review"];
265
+
266
+ const CATEGORY_OPTIONS = [
267
+ { label: "Option A", value: "a" },
268
+ { label: "Option B", value: "b" },
269
+ ];
270
+
271
+ export default function FormPage() {
272
+ const [step, setStep] = useState(0);
273
+ const isLastStep = step === STEPS.length - 1;
274
+
275
+ const [name, setName] = useState("");
276
+ const [category, setCategory] = useState("");
277
+ const [errors, setErrors] = useState<Record<string, string>>({});
278
+ const [isSubmitting, setIsSubmitting] = useState(false);
279
+ const [submitError, setSubmitError] = useState<string | null>(null);
280
+
281
+ function validateStep(s: number): Record<string, string> {
282
+ const e: Record<string, string> = {};
283
+ if (s === 0) {
284
+ if (!name.trim()) e.name = "Enter your full name";
285
+ if (!category) e.category = "Select a category";
286
+ }
287
+ return e;
288
+ }
289
+
290
+ function handleNext() {
291
+ const stepErrors = validateStep(step);
292
+ setErrors(stepErrors);
293
+ if (Object.keys(stepErrors).length > 0) return;
294
+ if (!isLastStep) setStep((s) => s + 1);
295
+ else handleSubmit();
296
+ }
297
+
298
+ async function handleSubmit() {
299
+ setIsSubmitting(true);
300
+ setSubmitError(null);
301
+ try {
302
+ // FIXME: replace with actual API call, e.g.:
303
+ // await apiClient.post("/api/v1/submissions", { name, category });
304
+ console.log("submitted", { name, category });
305
+ } catch (err: unknown) {
306
+ setSubmitError(
307
+ err instanceof Error
308
+ ? err.message
309
+ : "Submission failed. Please try again."
310
+ );
311
+ } finally {
312
+ setIsSubmitting(false);
313
+ }
314
+ }
315
+
316
+ return (
317
+ <>
318
+ <Layout.ColDiv
319
+ xsCols={12}
320
+ lgCols={12}
321
+ className={styles.progressColDiv}
322
+ >
323
+ <ProgressIndicator
324
+ steps={STEPS}
325
+ currentIndex={step}
326
+ displayExtractor={(s) => s}
327
+ />
328
+ </Layout.ColDiv>
329
+
330
+ <Layout.ColDiv
331
+ xsCols={12}
332
+ lgCols={9}
333
+ xlCols={8}
334
+ className={styles.formColDiv}
335
+ >
336
+ <div className={styles.pageHeadingBlock}>
337
+ <h1 className={styles.pageTitle}>{STEPS[step]}</h1>
338
+ {/* <p className={styles.pageDescription}>Optional description</p> */}
339
+ </div>
340
+
341
+ {submitError && <Alert type="error">{submitError}</Alert>}
342
+
343
+ <div>
344
+ {step === 0 && (
345
+ <>
346
+ <Form.Input
347
+ label="Full name"
348
+ value={name}
349
+ errorMessage={errors.name}
350
+ onChange={(e) => {
351
+ setName(e.target.value);
352
+ if (errors.name)
353
+ setErrors((prev) => ({
354
+ ...prev,
355
+ name: "",
356
+ }));
357
+ }}
358
+ />
359
+ <Form.Select
360
+ label="Category"
361
+ options={CATEGORY_OPTIONS}
362
+ selectedOption={CATEGORY_OPTIONS.find(
363
+ (o) => o.value === category
364
+ )}
365
+ listExtractor={(o) => o.label}
366
+ displayValueExtractor={(o) => o.label}
367
+ valueExtractor={(o) => o.value}
368
+ onSelectOption={(opt) => {
369
+ setCategory(opt.value);
370
+ if (errors.category)
371
+ setErrors((prev) => ({
372
+ ...prev,
373
+ category: "",
374
+ }));
375
+ }}
376
+ errorMessage={errors.category}
377
+ />
378
+ </>
379
+ )}
380
+ {step === 1 && <>{/* step 2 fields */}</>}
381
+ </div>
382
+
383
+ <FormNav
384
+ onBack={step > 0 ? () => setStep((s) => s - 1) : undefined}
385
+ onNext={handleNext}
386
+ nextLabel={isLastStep ? "Submit" : "Next"}
387
+ isSubmitting={isSubmitting}
388
+ />
389
+ </Layout.ColDiv>
390
+ </>
391
+ );
392
+ }
393
+ ```
394
+
395
+ ```css
396
+ /* FormPage.module.css */
397
+ .progressColDiv {
398
+ margin-bottom: var(--fds-spacing-32);
399
+ }
400
+
401
+ .formColDiv {
402
+ display: flex;
403
+ flex-direction: column;
404
+ gap: var(--fds-spacing-32);
405
+ }
406
+
407
+ .pageHeadingBlock {
408
+ display: flex;
409
+ flex-direction: column;
410
+ gap: var(--fds-spacing-16);
411
+ }
412
+
413
+ .pageTitle {
414
+ margin: 0;
415
+ }
416
+
417
+ .pageDescription {
418
+ margin: 0;
419
+ }
420
+ ```
421
+
422
+ ---
423
+
424
+ ## Notes
425
+
426
+ - Single-step: omit `ProgressIndicator` and pass no `onBack` to `FormNav`
427
+ - Pass `actions` to `FormNav` for secondary actions like Save draft