@flamingo-stack/openframe-frontend-core 0.0.702 → 0.0.703-snapshot.20261006080901

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 (341) hide show
  1. package/dist/{chunk-CYYTD5ZK.cjs → chunk-262YQFOS.cjs} +47 -47
  2. package/dist/{chunk-SAA7B3DA.js → chunk-2G546OND.js} +500 -67
  3. package/dist/{chunk-6JPQDYYY.js → chunk-2JDVL7UV.js} +2614 -2001
  4. package/dist/{chunk-ASN65P3F.cjs → chunk-4DQMTBMK.cjs} +15 -15
  5. package/dist/{chunk-CZ34HXDW.js → chunk-4KGXLVCF.js} +686 -725
  6. package/dist/{chunk-TCZCMD5Q.js → chunk-5HBHO5TB.js} +1 -1
  7. package/dist/{chunk-446KS5BG.js → chunk-5QJDM6UV.js} +5 -5
  8. package/dist/{chunk-UJJWTJ6S.js → chunk-6WQIUQUC.js} +30 -16
  9. package/dist/{chunk-YR6JPJF4.js → chunk-6XIF2Z7I.js} +4 -4
  10. package/dist/{chunk-YGRHITPY.cjs → chunk-7JIL3WOH.cjs} +24 -24
  11. package/dist/{chunk-RS7HWAYX.js → chunk-7LFIKJX2.js} +3 -3
  12. package/dist/{chunk-4OGAXAEG.cjs → chunk-ABU3TGXA.cjs} +3 -3
  13. package/dist/{chunk-3WSFIV5G.cjs → chunk-AMYY3PNL.cjs} +69 -55
  14. package/dist/{chunk-BRXOGCH3.cjs → chunk-BIZNSF3U.cjs} +3 -3
  15. package/dist/{chunk-2A4XES7R.cjs → chunk-BM5MVGH3.cjs} +3 -3
  16. package/dist/{chunk-IIMICN7L.cjs → chunk-C3BWIU7R.cjs} +4 -44
  17. package/dist/{chunk-LZNJH23A.cjs → chunk-C7XGRLT5.cjs} +12 -12
  18. package/dist/{chunk-6YCXA7CN.js → chunk-CH5232HV.js} +20 -19
  19. package/dist/{chunk-UIBSSVON.cjs → chunk-D7BTSVMS.cjs} +2 -2
  20. package/dist/{chunk-BO5ZHDA4.js → chunk-ERCV6HHJ.js} +7 -7
  21. package/dist/{chunk-5V6O73EH.js → chunk-FLKJYMVB.js} +3 -3
  22. package/dist/{chunk-Z5HDCZAS.js → chunk-FXENTYE5.js} +15 -15
  23. package/dist/{chunk-HIXGAY2L.cjs → chunk-HFJW24JJ.cjs} +121 -121
  24. package/dist/{chunk-HPJ4XS2L.js → chunk-HJAYVKWQ.js} +24 -24
  25. package/dist/{chunk-PLK6GHN7.js → chunk-I7DFUQMN.js} +10 -10
  26. package/dist/{chunk-KJ5LWATV.js → chunk-IHU5ANUY.js} +7 -7
  27. package/dist/{chunk-AEQANLBW.cjs → chunk-IZGDG5X6.cjs} +505 -72
  28. package/dist/{chunk-2V4Z44G6.js → chunk-JEYRXT5L.js} +17 -17
  29. package/dist/{chunk-B3LZJ6J3.cjs → chunk-L3IC42IF.cjs} +32 -32
  30. package/dist/{chunk-RTGPIHVO.cjs → chunk-LDPEQGKT.cjs} +2243 -1630
  31. package/dist/{chunk-FD4DBTFO.cjs → chunk-LTSTC3N5.cjs} +1253 -1292
  32. package/dist/{chunk-OGLCD3UL.js → chunk-MFFCUVWG.js} +6 -6
  33. package/dist/{chunk-3L7KCOGO.cjs → chunk-MGGGBHYD.cjs} +30 -30
  34. package/dist/{chunk-QFW3RUZS.cjs → chunk-MI3JUJ75.cjs} +33 -33
  35. package/dist/{chunk-EMCQGFXD.cjs → chunk-MMOODW57.cjs} +28 -27
  36. package/dist/{chunk-Q76ZTHNV.js → chunk-MP2C3LJ4.js} +1 -1
  37. package/dist/{chunk-E3DH543A.cjs → chunk-N3TO7JV3.cjs} +98 -98
  38. package/dist/{chunk-2T6QWNXU.js → chunk-NDESEPW5.js} +99 -11
  39. package/dist/{chunk-ETFFEA3J.js → chunk-NRLJUBCS.js} +7 -7
  40. package/dist/{chunk-HUOLB7ZN.cjs → chunk-OHFS7VOU.cjs} +9 -9
  41. package/dist/{chunk-MWUBBBWZ.js → chunk-QVSFBSWV.js} +10 -10
  42. package/dist/{chunk-MDBHZBW7.cjs → chunk-RKV5B7EU.cjs} +92 -4
  43. package/dist/{chunk-3G76LRGL.cjs → chunk-SYXQLETR.cjs} +29 -29
  44. package/dist/{chunk-FHM7GD5B.cjs → chunk-UK62YTH6.cjs} +28 -14
  45. package/dist/{chunk-HUMWQMCU.js → chunk-UT4YHQZW.js} +3 -3
  46. package/dist/{chunk-NGJCWEYX.cjs → chunk-VHCACDKP.cjs} +46 -46
  47. package/dist/{chunk-5FFNNJM7.js → chunk-VR2ZLFCP.js} +1 -1
  48. package/dist/{chunk-IZKOQVXO.js → chunk-VTASHWER.js} +14 -14
  49. package/dist/{chunk-64O4L7WN.js → chunk-WUJNYGO2.js} +11 -51
  50. package/dist/{chunk-JOY6HVPV.js → chunk-XATTBO5X.js} +25 -11
  51. package/dist/{chunk-L7KZA7ZD.cjs → chunk-XIFOBQRQ.cjs} +6 -6
  52. package/dist/{chunk-CXDJUKC6.cjs → chunk-XJKZ4IKT.cjs} +4 -4
  53. package/dist/{chunk-XCIFHW4E.cjs → chunk-XOYH2YQE.cjs} +2 -2
  54. package/dist/{chunk-HYFLTFFB.js → chunk-ZQFZSFQI.js} +2 -2
  55. package/dist/components/case-studies/index.cjs +24 -24
  56. package/dist/components/case-studies/index.js +10 -10
  57. package/dist/components/chat/chat-archive-page.d.ts +3 -3
  58. package/dist/components/chat/chat-archive-page.d.ts.map +1 -1
  59. package/dist/components/chat/chat-nav-sidebar.d.ts +4 -0
  60. package/dist/components/chat/chat-nav-sidebar.d.ts.map +1 -1
  61. package/dist/components/chat/chat-panel-header.d.ts +3 -2
  62. package/dist/components/chat/chat-panel-header.d.ts.map +1 -1
  63. package/dist/components/chat/embeddable-chat.d.ts +36 -2
  64. package/dist/components/chat/embeddable-chat.d.ts.map +1 -1
  65. package/dist/components/chat/index.cjs +14 -6
  66. package/dist/components/chat/index.d.ts +4 -1
  67. package/dist/components/chat/index.d.ts.map +1 -1
  68. package/dist/components/chat/index.js +13 -5
  69. package/dist/components/chat/mingo-archive-list.d.ts +17 -0
  70. package/dist/components/chat/mingo-archive-list.d.ts.map +1 -0
  71. package/dist/components/chat/mingo-chat-header.d.ts +31 -0
  72. package/dist/components/chat/mingo-chat-header.d.ts.map +1 -0
  73. package/dist/components/chat/mingo-chat-rail.d.ts +54 -0
  74. package/dist/components/chat/mingo-chat-rail.d.ts.map +1 -0
  75. package/dist/components/chat/model-display.d.ts.map +1 -1
  76. package/dist/components/contact/contact-form.d.ts.map +1 -1
  77. package/dist/components/contact/index.cjs +11 -11
  78. package/dist/components/contact/index.js +10 -10
  79. package/dist/components/content-loading-container.d.ts +1 -1
  80. package/dist/components/content-loading-container.d.ts.map +1 -1
  81. package/dist/components/docs/index.cjs +14 -14
  82. package/dist/components/docs/index.js +13 -13
  83. package/dist/components/docs/skeletons.d.ts.map +1 -1
  84. package/dist/components/embeds/index.cjs +11 -11
  85. package/dist/components/embeds/index.js +10 -10
  86. package/dist/components/faq/index.cjs +12 -12
  87. package/dist/components/faq/index.js +11 -11
  88. package/dist/components/features/figma-prototype-viewer.d.ts.map +1 -1
  89. package/dist/components/features/index.cjs +10 -10
  90. package/dist/components/features/index.js +9 -9
  91. package/dist/components/features/media-gallery-manager.d.ts.map +1 -1
  92. package/dist/components/features/parallax-image-showcase.d.ts.map +1 -1
  93. package/dist/components/features/policy-configuration-panel.d.ts.map +1 -1
  94. package/dist/components/features/section-selector.d.ts.map +1 -1
  95. package/dist/components/help-center-pages/index.cjs +119 -119
  96. package/dist/components/help-center-pages/index.js +39 -39
  97. package/dist/components/help-center-pages/trust-center-sections.d.ts +2 -2
  98. package/dist/components/help-center-pages/trust-center-sections.d.ts.map +1 -1
  99. package/dist/components/index.cjs +94 -86
  100. package/dist/components/index.js +32 -24
  101. package/dist/components/layout/article-detail-layout.d.ts +1 -1
  102. package/dist/components/layout/article-detail-layout.d.ts.map +1 -1
  103. package/dist/components/layout/page-container.d.ts.map +1 -1
  104. package/dist/components/layout/title-block.d.ts +2 -2
  105. package/dist/components/layout/title-block.d.ts.map +1 -1
  106. package/dist/components/loading/page-layout-skeleton.d.ts.map +1 -1
  107. package/dist/components/loading/unified-skeleton.d.ts.map +1 -1
  108. package/dist/components/meeting-scheduler/booking-form.d.ts +4 -4
  109. package/dist/components/meeting-scheduler/booking-form.d.ts.map +1 -1
  110. package/dist/components/meeting-scheduler/context-panel.d.ts.map +1 -1
  111. package/dist/components/meeting-scheduler/index.cjs +127 -127
  112. package/dist/components/meeting-scheduler/index.d.ts +2 -2
  113. package/dist/components/meeting-scheduler/index.d.ts.map +1 -1
  114. package/dist/components/meeting-scheduler/index.js +50 -50
  115. package/dist/components/navigation/app-header.d.ts +4 -0
  116. package/dist/components/navigation/app-header.d.ts.map +1 -1
  117. package/dist/components/navigation/app-layout-drawer.d.ts.map +1 -1
  118. package/dist/components/navigation/app-layout-side-panel.d.ts +142 -0
  119. package/dist/components/navigation/app-layout-side-panel.d.ts.map +1 -0
  120. package/dist/components/navigation/app-layout.d.ts +11 -1
  121. package/dist/components/navigation/app-layout.d.ts.map +1 -1
  122. package/dist/components/navigation/header-mingo-button.d.ts +4 -1
  123. package/dist/components/navigation/header-mingo-button.d.ts.map +1 -1
  124. package/dist/components/navigation/index.cjs +13 -13
  125. package/dist/components/navigation/index.d.ts +1 -0
  126. package/dist/components/navigation/index.d.ts.map +1 -1
  127. package/dist/components/navigation/index.js +12 -12
  128. package/dist/components/navigation/navigation-sidebar.d.ts +8 -1
  129. package/dist/components/navigation/navigation-sidebar.d.ts.map +1 -1
  130. package/dist/components/navigation/sliding-sidebar.d.ts.map +1 -1
  131. package/dist/components/onboarding-guides/index.cjs +14 -14
  132. package/dist/components/onboarding-guides/index.js +13 -13
  133. package/dist/components/persistent-filter-controls.d.ts.map +1 -1
  134. package/dist/components/related-content/index.cjs +7 -7
  135. package/dist/components/related-content/index.js +6 -6
  136. package/dist/components/shared/detail-page-skeleton.d.ts.map +1 -1
  137. package/dist/components/tickets/help-center-create-form.d.ts +4 -4
  138. package/dist/components/tickets/index.cjs +15 -15
  139. package/dist/components/tickets/index.js +14 -14
  140. package/dist/components/ui/announcement-bar-view.d.ts.map +1 -1
  141. package/dist/components/ui/breadcrumb.d.ts.map +1 -1
  142. package/dist/components/ui/checkbox-block.d.ts.map +1 -1
  143. package/dist/components/ui/dashboard-info-card.d.ts +1 -1
  144. package/dist/components/ui/dashboard-info-card.d.ts.map +1 -1
  145. package/dist/components/ui/data-table/data-table-body.d.ts +2 -2
  146. package/dist/components/ui/data-table/data-table-skeleton.d.ts +2 -2
  147. package/dist/components/ui/data-table/data-table-skeleton.d.ts.map +1 -1
  148. package/dist/components/ui/data-table/utils.d.ts +2 -2
  149. package/dist/components/ui/dialog.d.ts.map +1 -1
  150. package/dist/components/ui/drawer.d.ts +7 -3
  151. package/dist/components/ui/drawer.d.ts.map +1 -1
  152. package/dist/components/ui/entity-image.d.ts +2 -2
  153. package/dist/components/ui/file-manager/index.cjs +36 -36
  154. package/dist/components/ui/file-manager/index.js +3 -3
  155. package/dist/components/ui/filter-modal.d.ts.map +1 -1
  156. package/dist/components/ui/index.cjs +18 -10
  157. package/dist/components/ui/index.js +17 -9
  158. package/dist/components/ui/info-card-row.d.ts.map +1 -1
  159. package/dist/components/ui/menubar.d.ts +1 -1
  160. package/dist/components/ui/menubar.d.ts.map +1 -1
  161. package/dist/components/ui/modal-v2.d.ts.map +1 -1
  162. package/dist/components/ui/modal.d.ts.map +1 -1
  163. package/dist/components/ui/no-data/no-data-actions.d.ts.map +1 -1
  164. package/dist/components/ui/no-data/no-data-styles.d.ts +1 -1
  165. package/dist/components/ui/no-data/no-data-styles.d.ts.map +1 -1
  166. package/dist/components/ui/page-actions.d.ts.map +1 -1
  167. package/dist/components/ui/panel-resize-handle.d.ts +25 -0
  168. package/dist/components/ui/panel-resize-handle.d.ts.map +1 -0
  169. package/dist/components/ui/sheet.d.ts.map +1 -1
  170. package/dist/components/ui/table/table-skeleton.d.ts +1 -1
  171. package/dist/components/ui/table/table-skeleton.d.ts.map +1 -1
  172. package/dist/hooks/index.cjs +23 -9
  173. package/dist/hooks/index.js +24 -10
  174. package/dist/hooks/ui/index.d.ts +1 -0
  175. package/dist/hooks/ui/index.d.ts.map +1 -1
  176. package/dist/hooks/ui/use-content-breakpoint.d.ts +32 -0
  177. package/dist/hooks/ui/use-content-breakpoint.d.ts.map +1 -0
  178. package/dist/hooks/ui/use-isomorphic-layout-effect.d.ts +2 -2
  179. package/dist/hooks/ui/use-isomorphic-layout-effect.d.ts.map +1 -1
  180. package/dist/hooks/ui/use-media-query.d.ts.map +1 -1
  181. package/dist/hooks/ui/use-resizable-panel-size.d.ts +46 -0
  182. package/dist/hooks/ui/use-resizable-panel-size.d.ts.map +1 -0
  183. package/dist/index.cjs +39 -17
  184. package/dist/index.js +41 -19
  185. package/dist/styles/layout-steps.d.ts +30 -0
  186. package/dist/styles/layout-steps.d.ts.map +1 -0
  187. package/dist/tailwind.config.cjs +36 -11
  188. package/dist/tailwind.config.js +36 -11
  189. package/eslint-config/README.md +9 -0
  190. package/eslint-config/base.js +17 -1
  191. package/eslint-config/layout-variants.js +57 -0
  192. package/package.json +3 -1
  193. package/src/ODS_TOKEN_RULES.md +40 -0
  194. package/src/components/chat/chat-archive-page.tsx +9 -7
  195. package/src/components/chat/chat-nav-sidebar.tsx +10 -4
  196. package/src/components/chat/chat-panel-header.tsx +8 -5
  197. package/src/components/chat/embeddable-chat.tsx +686 -494
  198. package/src/components/chat/index.ts +4 -0
  199. package/src/components/chat/mingo-archive-list.tsx +108 -0
  200. package/src/components/chat/mingo-chat-header.tsx +104 -0
  201. package/src/components/chat/mingo-chat-rail.tsx +361 -0
  202. package/src/components/chat/model-display.tsx +8 -1
  203. package/src/components/contact/contact-form.tsx +11 -9
  204. package/src/components/content-loading-container.tsx +4 -2
  205. package/src/components/docs/doc-viewer.tsx +7 -7
  206. package/src/components/docs/skeletons.tsx +7 -4
  207. package/src/components/dynamic-skeleton.tsx +1 -1
  208. package/src/components/embeds/claude-embed.tsx +1 -1
  209. package/src/components/embeds/embed-viewer-frame.tsx +4 -4
  210. package/src/components/embeds/figma-embed.tsx +2 -2
  211. package/src/components/embeds/google-sheets-viewer.tsx +1 -1
  212. package/src/components/embeds/pdf-viewer.tsx +3 -3
  213. package/src/components/empty-state.tsx +6 -6
  214. package/src/components/faq/faq-section.tsx +2 -2
  215. package/src/components/faq-accordion.tsx +1 -1
  216. package/src/components/features/board/board-column-header.tsx +1 -1
  217. package/src/components/features/command-box.tsx +3 -3
  218. package/src/components/features/entity-video-section.tsx +2 -2
  219. package/src/components/features/figma-prototype-viewer.tsx +4 -1
  220. package/src/components/features/media-gallery-manager.tsx +3 -1
  221. package/src/components/features/parallax-image-showcase.tsx +9 -6
  222. package/src/components/features/policy-configuration-panel.tsx +9 -7
  223. package/src/components/features/provider-button.tsx +1 -1
  224. package/src/components/features/section-selector.tsx +11 -6
  225. package/src/components/features/select-button.tsx +4 -4
  226. package/src/components/features/seo-editor-preview.tsx +2 -2
  227. package/src/components/features/video-ratio-tabs.tsx +3 -3
  228. package/src/components/features/waitlist-form.tsx +1 -1
  229. package/src/components/help-center-pages/trust-center-page.tsx +1 -1
  230. package/src/components/help-center-pages/trust-center-sections.tsx +14 -14
  231. package/src/components/icon-display.tsx +1 -1
  232. package/src/components/layout/article-detail-layout.tsx +4 -2
  233. package/src/components/layout/list-page-layout.tsx +2 -2
  234. package/src/components/layout/page-container.tsx +26 -14
  235. package/src/components/layout/page-heading.tsx +1 -1
  236. package/src/components/layout/page-layout.tsx +1 -1
  237. package/src/components/layout/title-block.tsx +13 -13
  238. package/src/components/loading/card-skeleton.tsx +11 -11
  239. package/src/components/loading/category-vendor-selector-skeleton.tsx +1 -1
  240. package/src/components/loading/content-skeleton.tsx +6 -6
  241. package/src/components/loading/device-card-skeleton.tsx +1 -1
  242. package/src/components/loading/margin-report-skeleton.tsx +7 -7
  243. package/src/components/loading/msp-profile-form-skeleton.tsx +3 -3
  244. package/src/components/loading/organization-card-skeleton.tsx +2 -2
  245. package/src/components/loading/page-layout-skeleton.tsx +80 -68
  246. package/src/components/loading/unified-skeleton.tsx +16 -6
  247. package/src/components/media-carousel.tsx +3 -3
  248. package/src/components/meeting-scheduler/booking-form.tsx +9 -8
  249. package/src/components/meeting-scheduler/context-panel.tsx +8 -6
  250. package/src/components/meeting-scheduler/directory.tsx +4 -4
  251. package/src/components/meeting-scheduler/index.tsx +15 -15
  252. package/src/components/meeting-scheduler/slot-picker.tsx +12 -12
  253. package/src/components/navigation/app-header.tsx +6 -0
  254. package/src/components/navigation/app-layout-drawer.tsx +14 -237
  255. package/src/components/navigation/app-layout-side-panel.tsx +594 -0
  256. package/src/components/navigation/app-layout.tsx +69 -4
  257. package/src/components/navigation/header-mingo-button.tsx +13 -3
  258. package/src/components/navigation/index.ts +5 -0
  259. package/src/components/navigation/navigation-sidebar.tsx +17 -3
  260. package/src/components/navigation/sliding-sidebar.tsx +2 -1
  261. package/src/components/pagination.tsx +3 -3
  262. package/src/components/persistent-filter-controls.tsx +6 -2
  263. package/src/components/platform/ScriptArguments.tsx +1 -1
  264. package/src/components/platform/ScriptInfoSection.tsx +4 -4
  265. package/src/components/shared/article-author-byline.tsx +1 -1
  266. package/src/components/shared/delivery/delivery-lists.tsx +2 -2
  267. package/src/components/shared/delivery/delivery-row.tsx +7 -7
  268. package/src/components/shared/delivery/delivery-table.tsx +3 -3
  269. package/src/components/shared/detail-page-skeleton.tsx +11 -5
  270. package/src/components/shared/dev-section/dev-card-row.tsx +6 -6
  271. package/src/components/shared/dev-section/dev-section-view.tsx +1 -1
  272. package/src/components/shared/product-release/product-release-card-skeleton.tsx +8 -8
  273. package/src/components/shared/product-release/product-release-card.tsx +9 -9
  274. package/src/components/shared/product-release/product-releases-view.tsx +1 -1
  275. package/src/components/shared/product-release/release-detail-page.tsx +5 -5
  276. package/src/components/shared/roadmap/roadmap-grid-skeleton.tsx +1 -1
  277. package/src/components/shared/roadmap/roadmap-grid.tsx +3 -3
  278. package/src/components/tickets/help-center-card.tsx +1 -1
  279. package/src/components/tickets/help-center-create-form.tsx +12 -12
  280. package/src/components/ui/announcement-bar-view.tsx +2 -10
  281. package/src/components/ui/benefit-card.tsx +18 -18
  282. package/src/components/ui/brand-association-grid.tsx +4 -4
  283. package/src/components/ui/breadcrumb.tsx +4 -1
  284. package/src/components/ui/card.tsx +1 -1
  285. package/src/components/ui/checkbox-block.tsx +9 -7
  286. package/src/components/ui/content-loader.tsx +3 -3
  287. package/src/components/ui/cursor-pagination.tsx +3 -3
  288. package/src/components/ui/dashboard-info-card.tsx +13 -9
  289. package/src/components/ui/data-attribution.tsx +2 -2
  290. package/src/components/ui/data-table/data-table-body.tsx +2 -2
  291. package/src/components/ui/data-table/data-table-header.tsx +15 -15
  292. package/src/components/ui/data-table/data-table-skeleton.tsx +6 -6
  293. package/src/components/ui/data-table/utils.ts +5 -5
  294. package/src/components/ui/dialog.tsx +4 -1
  295. package/src/components/ui/drawer.tsx +43 -189
  296. package/src/components/ui/duration-input.tsx +1 -1
  297. package/src/components/ui/entity-image.tsx +3 -3
  298. package/src/components/ui/feature-card.tsx +10 -10
  299. package/src/components/ui/file-manager/file-manager-action-bar.tsx +1 -1
  300. package/src/components/ui/file-manager/file-manager.tsx +1 -1
  301. package/src/components/ui/filter-modal.tsx +8 -1
  302. package/src/components/ui/highlight-card.tsx +7 -7
  303. package/src/components/ui/info-card-row.tsx +10 -6
  304. package/src/components/ui/modal-v2.tsx +7 -2
  305. package/src/components/ui/modal.tsx +3 -2
  306. package/src/components/ui/no-data/no-data-actions.tsx +10 -4
  307. package/src/components/ui/no-data/no-data-styles.ts +1 -1
  308. package/src/components/ui/no-data/no-data.tsx +1 -1
  309. package/src/components/ui/organization-card.tsx +1 -1
  310. package/src/components/ui/page-actions.tsx +34 -5
  311. package/src/components/ui/panel-resize-handle.tsx +184 -0
  312. package/src/components/ui/progress-bar.tsx +2 -2
  313. package/src/components/ui/query-report-table/query-report-table.tsx +1 -1
  314. package/src/components/ui/search-input.tsx +2 -2
  315. package/src/components/ui/service-card.tsx +2 -2
  316. package/src/components/ui/sheet.tsx +2 -1
  317. package/src/components/ui/skeleton.tsx +3 -3
  318. package/src/components/ui/stacked-rows-panel.tsx +4 -4
  319. package/src/components/ui/tab-navigation.tsx +2 -2
  320. package/src/components/ui/tab-selector.tsx +2 -2
  321. package/src/components/ui/table/table-header.tsx +3 -3
  322. package/src/components/ui/table/table-skeleton.tsx +3 -3
  323. package/src/components/ui/table/utils.ts +5 -5
  324. package/src/components/ui/tag-search-input.tsx +3 -3
  325. package/src/components/ui/tags-manager.tsx +5 -5
  326. package/src/components/ui/test-run.tsx +2 -2
  327. package/src/components/ui/ticket-info-section.tsx +1 -1
  328. package/src/components/ui/ticket-notes-section.tsx +1 -1
  329. package/src/components/ui/ticket-status-config-row.tsx +15 -15
  330. package/src/hooks/ui/index.ts +1 -0
  331. package/src/hooks/ui/use-content-breakpoint.ts +58 -0
  332. package/src/hooks/ui/use-media-query.ts +3 -2
  333. package/src/hooks/ui/use-resizable-panel-size.ts +114 -0
  334. package/src/styles/index.css +2 -0
  335. package/src/styles/layout-steps.ts +14 -0
  336. package/src/styles/ods-colors.css +15 -0
  337. package/src/styles/ods-content-area-tokens.css +27 -0
  338. package/src/styles/ods-content-area.css +31 -0
  339. package/src/styles/ods-effects.css +4 -2
  340. package/src/styles/ods-responsive-tokens.css +14 -4
  341. package/tailwind.config.ts +42 -7
@@ -93,7 +93,7 @@ export function TicketStatusConfigRow({
93
93
  <div
94
94
  className={cn(
95
95
  // Transparent background — only the border outlines the row.
96
- 'flex w-full items-start gap-3 rounded-md border border-ods-border md:gap-[var(--spacing-system-m)]',
96
+ 'flex w-full items-start gap-3 rounded-md border border-ods-border content-md:gap-[var(--spacing-system-m)]',
97
97
  'p-[var(--spacing-system-m)]',
98
98
  // The dragged row travels over its siblings, so it needs an opaque back.
99
99
  isDragging && 'bg-ods-bg opacity-70 shadow-lg',
@@ -102,7 +102,7 @@ export function TicketStatusConfigRow({
102
102
  {dragAndDropEnabled && (
103
103
  <div
104
104
  className={cn(
105
- 'flex h-11 w-8 shrink-0 items-center justify-center text-ods-text-secondary md:h-12',
105
+ 'flex h-11 w-8 shrink-0 items-center justify-center text-ods-text-secondary content-md:h-12',
106
106
  fieldRowOffset,
107
107
  )}
108
108
  >
@@ -131,14 +131,14 @@ export function TicketStatusConfigRow({
131
131
  and the chip pins to the card's right edge on tablet/mobile. */}
132
132
  <div
133
133
  className={cn(
134
- 'min-w-0 flex-1 items-start gap-x-3 gap-y-[var(--spacing-system-m)] md:gap-x-[var(--spacing-system-m)]',
134
+ 'min-w-0 flex-1 items-start gap-x-3 gap-y-[var(--spacing-system-m)] content-md:gap-x-[var(--spacing-system-m)]',
135
135
  isSystem
136
136
  ? 'flex flex-wrap'
137
137
  : // Mobile custom rows are a strict two-column grid, per the mock:
138
138
  // fields (name / color / hex, all one width) on the left, the
139
139
  // controls and the chip stacked in the right column. From md up
140
140
  // the same children flow as a wrapping flex row.
141
- 'grid grid-cols-[minmax(0,1fr)_auto] md:flex md:flex-wrap',
141
+ 'grid grid-cols-[minmax(0,1fr)_auto] content-md:flex content-md:flex-wrap',
142
142
  )}
143
143
  >
144
144
  {/* System rows keep the chip beside the name on every width, so the name
@@ -158,7 +158,7 @@ export function TicketStatusConfigRow({
158
158
  </div>
159
159
 
160
160
  {showColorPicker ? (
161
- <div className="order-3 flex min-w-0 grow basis-full flex-col gap-[var(--spacing-system-xxs)] md:order-2 md:basis-0">
161
+ <div className="order-3 flex min-w-0 grow basis-full flex-col gap-[var(--spacing-system-xxs)] content-md:order-2 content-md:basis-0">
162
162
  <Label variant="large">Color</Label>
163
163
  <ColorPresetSelect value={color} presetKey={presetKey} onChange={onColorChange} />
164
164
  {/* Custom hex: a second field row inside the Color column, directly
@@ -170,16 +170,16 @@ export function TicketStatusConfigRow({
170
170
  )}
171
171
  </div>
172
172
  ) : (
173
- <div aria-hidden className="hidden min-w-0 grow basis-0 lg:order-2 lg:block" />
173
+ <div aria-hidden className="hidden min-w-0 grow basis-0 content-lg:order-2 content-lg:block" />
174
174
  )}
175
175
 
176
176
  {/* Reserved desktop column slot (the mock's "No activity Indicator" zone). */}
177
- <div aria-hidden className="hidden min-w-0 grow basis-0 lg:order-3 lg:block" />
177
+ <div aria-hidden className="hidden min-w-0 grow basis-0 content-lg:order-3 content-lg:block" />
178
178
 
179
179
  {isSystem ? (
180
180
  <div
181
181
  className={cn(
182
- 'order-2 flex h-11 min-w-0 grow basis-0 items-center justify-end md:h-12 lg:order-4',
182
+ 'order-2 flex h-11 min-w-0 grow basis-0 items-center justify-end content-md:h-12 content-lg:order-4',
183
183
  fieldRowOffset,
184
184
  )}
185
185
  >
@@ -191,7 +191,7 @@ export function TicketStatusConfigRow({
191
191
  before the controls. */}
192
192
  <div
193
193
  className={cn(
194
- 'hidden h-12 min-w-0 grow basis-0 items-center justify-end lg:order-4 lg:flex',
194
+ 'hidden h-12 min-w-0 grow basis-0 items-center justify-end content-lg:order-4 content-lg:flex',
195
195
  fieldRowOffset,
196
196
  )}
197
197
  >
@@ -199,25 +199,25 @@ export function TicketStatusConfigRow({
199
199
  </div>
200
200
  {/* Tablet: the chip takes a full row of its own, pinned to the
201
201
  card's right edge (the controls above no longer narrow it). */}
202
- <div className="hidden min-w-0 basis-full items-center justify-end md:order-4 md:flex lg:hidden">
202
+ <div className="hidden min-w-0 basis-full items-center justify-end content-md:order-4 content-md:flex content-lg:hidden">
203
203
  {chip}
204
204
  </div>
205
205
  {/* Mobile: the chip fills the second grid column beside the color
206
206
  field, left-aligned with the controls above it. */}
207
- <div className={cn('order-4 flex h-11 min-w-0 items-center md:hidden', fieldRowOffset)}>{chip}</div>
207
+ <div className={cn('order-4 flex h-11 min-w-0 items-center content-md:hidden', fieldRowOffset)}>{chip}</div>
208
208
  </>
209
209
  )}
210
210
 
211
211
  <div
212
212
  className={cn(
213
- 'flex h-11 shrink-0 items-center justify-end gap-[var(--spacing-system-s)] md:h-12 lg:order-5',
214
- isSystem ? 'order-3' : 'order-2 md:order-3',
213
+ 'flex h-11 shrink-0 items-center justify-end gap-[var(--spacing-system-s)] content-md:h-12 content-lg:order-5',
214
+ isSystem ? 'order-3' : 'order-2 content-md:order-3',
215
215
  fieldRowOffset,
216
216
  )}
217
217
  >
218
218
  {!isSystem && moveButtons}
219
219
  {isSystem ? (
220
- <div className="flex w-11 justify-center md:w-12">
220
+ <div className="flex w-11 justify-center content-md:w-12">
221
221
  <TouchFriendlyTooltip content={systemTooltip}>
222
222
  <button
223
223
  type="button"
@@ -238,7 +238,7 @@ export function TicketStatusConfigRow({
238
238
  aria-label="Delete status"
239
239
  disabled={deleteDisabled}
240
240
  onClick={deleteDisabled ? undefined : onDelete}
241
- className={cn('size-11 md:size-12', deleteDisabled && 'pointer-events-none')}
241
+ className={cn('size-11 content-md:size-12', deleteDisabled && 'pointer-events-none')}
242
242
  >
243
243
  <TrashIcon className="text-ods-error" />
244
244
  </Button>
@@ -1,5 +1,6 @@
1
1
  // UI Hooks exports
2
2
  export * from './use-auto-limit-tags';
3
+ export * from './use-content-breakpoint';
3
4
  export * from './use-debounce';
4
5
  export * from './use-deferred-error';
5
6
  export * from './use-drag-and-drop-enabled';
@@ -0,0 +1,58 @@
1
+ 'use client';
2
+
3
+ import { createContext, createElement, type ReactNode, useContext } from 'react';
4
+ import { LAYOUT_STEPS } from '../../styles/layout-steps';
5
+ import { useLgUp, useMdUp } from './use-media-query';
6
+
7
+ export type ContentBreakpoint = 'mobile' | 'tablet' | 'desktop';
8
+
9
+ /** Content-area steps, in px of the area's own width. */
10
+ export const CONTENT_BREAKPOINTS = { md: LAYOUT_STEPS.md.content, lg: LAYOUT_STEPS.lg.content } as const;
11
+
12
+ /**
13
+ * Width of the enclosing content area (`AppLayout` with a side panel provides
14
+ * it), or `null` outside one.
15
+ */
16
+ export const ContentAreaWidthContext = createContext<number | null>(null);
17
+
18
+ /**
19
+ * For window chrome opened from a content area (a modal, a drawer): its subtree
20
+ * is back on the viewport's steps. Context crosses a portal, so without this
21
+ * the hooks below would still answer with the content area's width.
22
+ */
23
+ export function ViewportBreakpoints({ children }: { children: ReactNode }) {
24
+ return createElement(ContentAreaWidthContext.Provider, { value: null }, children);
25
+ }
26
+
27
+ export function contentBreakpointFor(width: number): ContentBreakpoint {
28
+ if (width >= CONTENT_BREAKPOINTS.lg) return 'desktop';
29
+ if (width >= CONTENT_BREAKPOINTS.md) return 'tablet';
30
+ return 'mobile';
31
+ }
32
+
33
+ /**
34
+ * The breakpoint a component should lay out for: the content area's when it
35
+ * renders inside one, the viewport's otherwise. `undefined` until the viewport
36
+ * is known (first client render), like `useMdUp`.
37
+ */
38
+ export function useContentBreakpoint(): ContentBreakpoint | undefined {
39
+ const width = useContext(ContentAreaWidthContext);
40
+ const mdUp = useMdUp();
41
+ const lgUp = useLgUp();
42
+ if (width !== null) return contentBreakpointFor(width);
43
+ if (mdUp === undefined) return undefined;
44
+ if (lgUp) return 'desktop';
45
+ return mdUp ? 'tablet' : 'mobile';
46
+ }
47
+
48
+ /** Content-area counterpart of `useMdUp`. */
49
+ export function useContentMdUp(): boolean | undefined {
50
+ const breakpoint = useContentBreakpoint();
51
+ return breakpoint === undefined ? undefined : breakpoint !== 'mobile';
52
+ }
53
+
54
+ /** Content-area counterpart of `useLgUp`. */
55
+ export function useContentLgUp(): boolean | undefined {
56
+ const breakpoint = useContentBreakpoint();
57
+ return breakpoint === undefined ? undefined : breakpoint === 'desktop';
58
+ }
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useState } from 'react';
4
+ import { LAYOUT_STEPS } from '../../styles/layout-steps';
4
5
  import { useIsomorphicLayoutEffect } from './use-isomorphic-layout-effect';
5
6
 
6
7
  /**
@@ -35,8 +36,8 @@ export function useMediaQuery(query: string): boolean | undefined {
35
36
  * Predefined breakpoints for common screen sizes
36
37
  */
37
38
  export const breakpoints = {
38
- md: '(min-width: 800px)', // Tablet: 50rem
39
- lg: '(min-width: 1280px)', // Desktop: 80rem
39
+ md: `(min-width: ${LAYOUT_STEPS.md.viewport}px)`, // Tablet
40
+ lg: `(min-width: ${LAYOUT_STEPS.lg.viewport}px)`, // Desktop
40
41
  };
41
42
 
42
43
  /** @deprecated Use useMdUp instead */
@@ -0,0 +1,114 @@
1
+ 'use client';
2
+
3
+ import { useCallback, useMemo, useState } from 'react';
4
+
5
+ import { clamp } from '../../utils/common';
6
+ import { createLocalStorageAdapter } from '../../utils/local-storage-adapter';
7
+
8
+ /**
9
+ * The size of a drag-to-resize panel (`Drawer`, `AppLayoutDrawer`).
10
+ *
11
+ * Two values are kept apart, and only one of them is ever stored:
12
+ *
13
+ * - the size the user CHOSE (a drag or a key press on the handle), stored, and
14
+ * `null` until they choose one;
15
+ * - the size RENDERED, derived on every render from the choice (or, without
16
+ * one, from `defaultSize`) and clamped to the room there is right now.
17
+ *
18
+ * The room changing never writes anything. A narrow window, docked dev tools or
19
+ * a phone-sized emulation shrink the panel for as long as they last and the
20
+ * chosen size comes back with the room. A panel nobody resized follows
21
+ * `defaultSize`: given as a function of the room, it tracks the window, and a
22
+ * changed default reaches everyone who never chose a size.
23
+ *
24
+ * The choice is stored as `{ size }` under `<storageKey>:chosen`, never under
25
+ * `storageKey` itself. The previous implementation wrote a bare number there,
26
+ * defaults and clamped sizes included, so that value cannot be told apart from
27
+ * a real choice and is not read; and a page still running that implementation
28
+ * (a tab left open across a deploy) keeps writing it, which must not replace
29
+ * the choice.
30
+ */
31
+
32
+ /** A fixed size in px, or one computed from the room along the resize axis
33
+ * (0 while the room is not measured, and on the server). */
34
+ export type PanelDefaultSize = number | ((available: number) => number);
35
+
36
+ const CHOSEN_SIZE_KEY_SUFFIX = ':chosen';
37
+
38
+ export interface UseResizablePanelSizeArgs {
39
+ /** False leaves the panel at `defaultSize` and reads or writes nothing. */
40
+ enabled: boolean;
41
+ minSize: number;
42
+ maxSize: number;
43
+ /** Size while the user has chosen none. */
44
+ defaultSize: PanelDefaultSize;
45
+ /** Room along the resize axis, in px. 0 while it is not measured yet. */
46
+ available: number;
47
+ /** Px of `available` the panel never takes (the gap that keeps the handle on screen). */
48
+ reserve: number;
49
+ /** Base localStorage key for the chosen size. Omit to keep it for the mount only. */
50
+ storageKey?: string;
51
+ }
52
+
53
+ interface StoredPanelSize {
54
+ size: number;
55
+ }
56
+
57
+ function isStoredPanelSize(value: unknown): value is StoredPanelSize {
58
+ if (typeof value !== 'object' || value === null) return false;
59
+ const { size } = value as { size?: unknown };
60
+ return typeof size === 'number' && Number.isFinite(size) && size > 0;
61
+ }
62
+
63
+ export function useResizablePanelSize({
64
+ enabled,
65
+ minSize,
66
+ maxSize,
67
+ defaultSize,
68
+ available,
69
+ reserve,
70
+ storageKey,
71
+ }: UseResizablePanelSizeArgs) {
72
+ const storage = useMemo(
73
+ () =>
74
+ enabled && storageKey
75
+ ? createLocalStorageAdapter<StoredPanelSize>({
76
+ key: `${storageKey}${CHOSEN_SIZE_KEY_SUFFIX}`,
77
+ validate: isStoredPanelSize,
78
+ logTag: `[panel-size:${storageKey}]`,
79
+ })
80
+ : null,
81
+ [enabled, storageKey],
82
+ );
83
+
84
+ // The choice belongs to the storage it was read from. When that changes (a
85
+ // drawer reused under another key, or resizing switched on after mount) it is
86
+ // read again during render, so no frame shows the other key's size.
87
+ const [held, setChosen] = useState(() => ({ storage, size: storage?.load()?.size ?? null }));
88
+ const chosen = held.storage === storage ? held : { storage, size: storage?.load()?.size ?? null };
89
+ if (chosen !== held) setChosen(chosen);
90
+ const chosenSize = chosen.size;
91
+
92
+ const clampSize = useCallback(
93
+ (value: number) => {
94
+ const ceiling = available > 0 ? Math.min(maxSize, available - reserve) : maxSize;
95
+ return clamp(value, minSize, Math.max(minSize, ceiling));
96
+ },
97
+ [available, reserve, minSize, maxSize],
98
+ );
99
+
100
+ const fallbackSize = typeof defaultSize === 'function' ? defaultSize(available) : defaultSize;
101
+ const size = enabled ? clampSize(chosenSize ?? fallbackSize) : fallbackSize;
102
+
103
+ /** The user's choice. The only path that stores a size. */
104
+ const setSize = useCallback(
105
+ (next: number) => {
106
+ const nextSize = Math.round(clampSize(next));
107
+ setChosen({ storage, size: nextSize });
108
+ storage?.save({ size: nextSize });
109
+ },
110
+ [clampSize, storage],
111
+ );
112
+
113
+ return { size, setSize, clampSize };
114
+ }
@@ -5,6 +5,8 @@
5
5
  @import './ods-interaction-states.css';
6
6
  @import './ods-effects.css';
7
7
  @import './ods-responsive-tokens.css';
8
+ @import './ods-content-area.css';
9
+ @import './ods-content-area-tokens.css';
8
10
  @import './app-globals.css';
9
11
  @import './chat-animations.css';
10
12
  @import './ods-page-shell.css';
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Layout steps in px, the one place they are written. `viewport` is the window
3
+ * step (`md:`, `useMdUp`), `content` its content-area counterpart
4
+ * (`content-md:`, `useContentMdUp`): the viewport step less the navigation.
5
+ *
6
+ * Imported by tailwind.config.ts, so it must stay free of imports and aliases.
7
+ */
8
+ export const LAYOUT_STEPS = {
9
+ sm: { viewport: 640, content: 640 },
10
+ md: { viewport: 800, content: 720 },
11
+ lg: { viewport: 1280, content: 1024 },
12
+ xl: { viewport: 1440, content: 1216 },
13
+ '2xl': { viewport: 1536, content: 1312 },
14
+ } as const;
@@ -417,6 +417,21 @@
417
417
  --color-bg: color-mix(in srgb, var(--color-accent-primary) 2%, var(--ods-system-greys-background));
418
418
  }
419
419
 
420
+ /* Mingo's own surface (Figma backgrounds/mingo-background): 2% of its cyan
421
+ * over the page background, whatever the tenant accent. */
422
+ [data-surface='mingo'] {
423
+ --color-bg: color-mix(in srgb, var(--ods-flamingo-cyan-base) 2%, var(--ods-system-greys-background));
424
+ --glow-color: var(--ods-flamingo-cyan-base);
425
+ }
426
+
427
+ /* Mingo-branded controls (its chat list): the cyan as the accent, and the
428
+ * selection fill (Figma flamingo-colors/cyan-sec) as its secondary. Scoped to
429
+ * those controls, not the whole panel: an approval's Approve stays yellow. */
430
+ [data-accent='mingo'] {
431
+ --color-accent-primary: var(--ods-flamingo-cyan-base);
432
+ --color-accent-secondary: color-mix(in srgb, var(--ods-flamingo-cyan-base) 31%, var(--color-bg));
433
+ }
434
+
420
435
  /* ===================================================================== */
421
436
  /* PLATFORM OVERRIDES — brand/accent ONLY. */
422
437
  /* Reference `--ods-*` so they follow the active theme; never touch */
@@ -0,0 +1,27 @@
1
+ /* GENERATED from ods-responsive-tokens.css. Do not edit: `npm run generate:content-area`. */
2
+
3
+ @container ods-content not (min-width: 720px) {
4
+ .ods-content-scope {
5
+ --device-type: mobile;
6
+ --device-screen-width: 26.875rem;
7
+ --spacing-system-xs: 0.25rem;
8
+ --spacing-system-s: 0.5rem;
9
+ --spacing-system-m: 0.75rem;
10
+ --spacing-system-l: 1rem;
11
+ --spacing-system-xl: 1.5rem;
12
+ --spacing-system-xxl: 2.5rem;
13
+ --font-size-h1-title: 2.5rem;
14
+ --font-size-h2-sub-title: 1.5rem;
15
+ --font-size-h3-body: 0.875rem;
16
+ --font-size-h4-body: 0.875rem;
17
+ --font-size-h5-caption: 0.75rem;
18
+ --font-size-h6-caption: 0.75rem;
19
+ --font-line-space-h1-main-title: 2.5rem;
20
+ --font-line-space-h2-sub-title: 2rem;
21
+ --font-line-space-h3-body: 1.25rem;
22
+ --font-line-space-h4-body: 1.25rem;
23
+ --font-line-space-h5-caption: 1rem;
24
+ --font-line-space-h6-caption: 1rem;
25
+ --icon-size-icon-size: 1rem;
26
+ }
27
+ }
@@ -0,0 +1,31 @@
1
+ /* Open Design System – Content Area Tokens */
2
+ /*
3
+ * The responsive tokens in ods-responsive-tokens.css follow the VIEWPORT. A
4
+ * layout with a docked side panel narrows the content area without the
5
+ * viewport changing, so content inside `.ods-content-area` follows its OWN
6
+ * width instead:
7
+ *
8
+ * - `.ods-content-area` is a named inline-size container (`ods-content`).
9
+ * - `.ods-content-scope` sits inside it and re-declares the mobile tokens
10
+ * while the area is narrower than the content `md` step
11
+ * (ods-content-area-tokens.css, generated). The variables cascade, so
12
+ * every `text-h*` and `var(--spacing-system-*)` below the scope picks
13
+ * them up with no change to the components.
14
+ * - From that step up the scope declares nothing and inherits the viewport
15
+ * tokens from :root, so a wide content area renders exactly as before.
16
+ *
17
+ * A container query cannot style the container itself, hence two elements.
18
+ * The scope is `display: contents`: it carries the variables without adding a
19
+ * box, so pages keep `<main>` as their layout parent.
20
+ *
21
+ * Layout switches inside the area use the `content-md:` / `content-lg:`
22
+ * Tailwind variants. The steps themselves live in layout-steps.ts.
23
+ */
24
+
25
+ .ods-content-area {
26
+ container: ods-content / inline-size;
27
+ }
28
+
29
+ .ods-content-scope {
30
+ display: contents;
31
+ }
@@ -57,10 +57,12 @@
57
57
  the corner, radii 100% × 103% as in the design). Written
58
58
  on the class, not as a :root variable, so the accent resolves on the element
59
59
  and follows a platform or runtime (tenant) accent set below the root. */
60
+ /* The color is `--glow-color` when a surface sets one (Mingo's cyan), the
61
+ accent otherwise. */
60
62
  .ods-glow-accent-corner {
61
63
  background-image: radial-gradient(
62
64
  100% 103% at 100% 100%,
63
- color-mix(in srgb, var(--color-accent-primary) 4%, transparent) 0%,
64
- color-mix(in srgb, var(--color-accent-primary) 0%, transparent) 100%
65
+ color-mix(in srgb, var(--glow-color, var(--color-accent-primary)) 4%, transparent) 0%,
66
+ color-mix(in srgb, var(--glow-color, var(--color-accent-primary)) 0%, transparent) 100%
65
67
  );
66
68
  }
@@ -1,7 +1,15 @@
1
1
  /* Open Design System – Responsive Design Tokens */
2
2
  /* Mobile-first approach: base = mobile, then scale up with min-width */
3
-
4
- :root {
3
+ /*
4
+ * `.ods-viewport-layer` restates the same values on an element: chrome that
5
+ * follows the WINDOW (a modal, a drawer) but renders inside a narrow content
6
+ * area, whose scope re-declares the mobile values
7
+ * (ods-content-area-tokens.css), gets the viewport's tokens back for its own
8
+ * subtree.
9
+ */
10
+
11
+ :root,
12
+ .ods-viewport-layer {
5
13
  /* =================================================== */
6
14
  /* DEVICE CONTEXT */
7
15
  /* =================================================== */
@@ -77,7 +85,8 @@
77
85
  /* =================================================== */
78
86
 
79
87
  @media (min-width: 800px) {
80
- :root {
88
+ :root,
89
+ .ods-viewport-layer {
81
90
  /* Device Context */
82
91
  --device-type: tablet;
83
92
  --device-screen-width: 50rem;
@@ -122,7 +131,8 @@
122
131
  /* =================================================== */
123
132
 
124
133
  @media (min-width: 1280px) {
125
- :root {
134
+ :root,
135
+ .ods-viewport-layer {
126
136
  /* Device Context */
127
137
  --device-type: desktop;
128
138
  --device-screen-width: 80rem;
@@ -2,6 +2,7 @@ import containerQueries from '@tailwindcss/container-queries';
2
2
  import type { Config } from 'tailwindcss';
3
3
  import tailwindcssAnimate from 'tailwindcss-animate';
4
4
  import plugin from 'tailwindcss/plugin';
5
+ import { LAYOUT_STEPS } from './src/styles/layout-steps';
5
6
 
6
7
  const odsTypographyPlugin = plugin(({ addUtilities }) => {
7
8
  addUtilities({
@@ -78,6 +79,42 @@ const odsTypographyPlugin = plugin(({ addUtilities }) => {
78
79
  });
79
80
  });
80
81
 
82
+ /**
83
+ * Content-area breakpoints: `content-md:` and friends apply by the width of the
84
+ * nearest `.ods-content-area`, not the viewport. Each step is the viewport
85
+ * step less the navigation sidebar (`sm` is phone-only, where there is none),
86
+ * so a page with nothing docked beside it switches where it did before.
87
+ *
88
+ * Outside a content area (apps without a side panel, overlays portalled to
89
+ * <body>) each falls back to its viewport step, so a component can switch to
90
+ * them without changing anywhere else it renders. So does everything under an
91
+ * `.ods-viewport-layer`: a modal or drawer that renders inside the content
92
+ * area but follows the window.
93
+ *
94
+ * Registered `max-*` first and then ascending, like Tailwind's own screens, so
95
+ * a wider step wins over a narrower one on the same property.
96
+ */
97
+ const CONTENT_STEPS = Object.entries(LAYOUT_STEPS).map(([name, step]) => ({ name, ...step }));
98
+
99
+ const odsContentAreaPlugin = plugin(({ addVariant }) => {
100
+ const viewportLayer = '.ods-viewport-layer, .ods-viewport-layer *';
101
+ const byContent = `&:where(:not(${viewportLayer}))`;
102
+ const byViewport = `&:where(:not(.ods-content-area *), ${viewportLayer})`;
103
+ for (const { name, content, viewport } of CONTENT_STEPS) {
104
+ if (name !== 'md' && name !== 'lg') continue;
105
+ addVariant(`content-max-${name}`, [
106
+ `@container ods-content not (min-width: ${content}px) { ${byContent} }`,
107
+ `@media not all and (min-width: ${viewport}px) { ${byViewport} }`,
108
+ ]);
109
+ }
110
+ for (const { name, content, viewport } of CONTENT_STEPS) {
111
+ addVariant(`content-${name}`, [
112
+ `@container ods-content (min-width: ${content}px) { ${byContent} }`,
113
+ `@media (min-width: ${viewport}px) { ${byViewport} }`,
114
+ ]);
115
+ }
116
+ });
117
+
81
118
  /**
82
119
  * Make Tailwind opacity modifiers (`bg-ods-error/10`, `hover:bg-ods-accent/90`, …)
83
120
  * actually work on var()-based ODS tokens. Without `<alpha-value>` in the color
@@ -106,6 +143,8 @@ const config: Config = {
106
143
  'lg:hidden',
107
144
  'md:flex',
108
145
  'lg:flex',
146
+ // ...and their content-area forms (DataTable `hideAt`)
147
+ ...['md', 'lg', 'xl', '2xl'].flatMap(step => [`content-${step}:hidden`, `content-${step}:flex`]),
109
148
  ],
110
149
  theme: {
111
150
  container: {
@@ -305,12 +344,8 @@ const config: Config = {
305
344
  current: 'var(--ods-current)',
306
345
  }),
307
346
  },
308
- // Custom breakpoints (aligned with ODS responsive tokens from Figma)
309
- screens: {
310
- md: '800px', // Tablet: 50rem
311
- lg: '1280px', // Desktop: 80rem
312
- xl: '1440px', // Large desktop: 90rem
313
- },
347
+ // Aligned with the ODS responsive tokens from Figma: md tablet, lg desktop.
348
+ screens: Object.fromEntries(CONTENT_STEPS.map(({ name, viewport }) => [name, `${viewport}px`])),
314
349
 
315
350
  borderRadius: {
316
351
  lg: 'var(--radius)', // 8px
@@ -429,7 +464,7 @@ const config: Config = {
429
464
  },
430
465
  },
431
466
  },
432
- plugins: [tailwindcssAnimate, odsTypographyPlugin, containerQueries],
467
+ plugins: [tailwindcssAnimate, odsTypographyPlugin, odsContentAreaPlugin, containerQueries],
433
468
  };
434
469
 
435
470
  export default config;