@cdx-ui/components 0.2.2 → 0.2.4
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.
- package/AGENTS.md +24 -0
- package/README.md +6 -2
- package/coverage.svg +1 -1
- package/docs/components/accordion/content.md +538 -0
- package/docs/components/alert/content.md +203 -0
- package/docs/components/alert-dialog/content.md +319 -0
- package/docs/components/avatar/content.md +368 -0
- package/docs/components/avatar/design.md +202 -0
- package/docs/components/badge/content.md +235 -0
- package/docs/components/bottom-sheet/content.md +351 -0
- package/docs/components/bottom-sheet/design.md +249 -0
- package/docs/components/breadcrumbs/content.md +169 -0
- package/docs/components/button/content.md +398 -0
- package/docs/components/button/design.md +258 -0
- package/docs/components/calendar/content.md +137 -0
- package/docs/components/card/content.md +283 -0
- package/docs/components/carousel/content.md +416 -0
- package/docs/components/checkbox/content.md +236 -0
- package/docs/components/checkbox/design.md +214 -0
- package/docs/components/chip/content.md +439 -0
- package/docs/components/chip/design.md +418 -0
- package/docs/components/color-area/content.md +86 -0
- package/docs/components/color-field/content.md +134 -0
- package/docs/components/color-picker/content.md +208 -0
- package/docs/components/color-slider/content.md +120 -0
- package/docs/components/color-swatch/content.md +64 -0
- package/docs/components/color-swatch-picker/content.md +126 -0
- package/docs/components/content.md +97 -0
- package/docs/components/currency-input/content.md +205 -0
- package/docs/components/data-grid/content.md +522 -0
- package/docs/components/date-field/content.md +147 -0
- package/docs/components/date-picker/content.md +360 -0
- package/docs/components/date-range-picker/content.md +301 -0
- package/docs/components/date-time-field/content.md +157 -0
- package/docs/components/date-time-list/content.md +105 -0
- package/docs/components/date-time-picker/content.md +272 -0
- package/docs/components/dialog/content.md +436 -0
- package/docs/components/dialog/design.md +192 -0
- package/docs/components/divider/content.md +112 -0
- package/docs/components/drawer/content.md +436 -0
- package/docs/components/field/content.md +277 -0
- package/docs/components/form/content.md +62 -0
- package/docs/components/full-screen-dialog/content.md +881 -0
- package/docs/components/heading/content.md +55 -0
- package/docs/components/icon/content.md +190 -0
- package/docs/components/icon-button/content.md +200 -0
- package/docs/components/icon-button/design.md +165 -0
- package/docs/components/image/content.md +104 -0
- package/docs/components/input/content.md +281 -0
- package/docs/components/input/design.md +389 -0
- package/docs/components/insurance-disclosure/content.md +103 -0
- package/docs/components/link/content.md +180 -0
- package/docs/components/link/design.md +160 -0
- package/docs/components/list-item/content.md +695 -0
- package/docs/components/menu/content.md +1247 -0
- package/docs/components/month-calendar/content.md +84 -0
- package/docs/components/month-picker/content.md +273 -0
- package/docs/components/otp-input/content.md +327 -0
- package/docs/components/progress-bar/content.md +87 -0
- package/docs/components/progress-circle/content.md +176 -0
- package/docs/components/progress-segmented/content.md +119 -0
- package/docs/components/quick-action-button/content.md +154 -0
- package/docs/components/quick-action-button/design.md +170 -0
- package/docs/components/radio/content.md +224 -0
- package/docs/components/range-calendar/content.md +123 -0
- package/docs/components/reorderable-list/content.md +309 -0
- package/docs/components/scoped-theme/content.md +47 -0
- package/docs/components/scroll-shadow/content.md +686 -0
- package/docs/components/select/content.md +400 -0
- package/docs/components/select/design.md +301 -0
- package/docs/components/skeleton/content.md +103 -0
- package/docs/components/slider/content.md +423 -0
- package/docs/components/stack/content.md +161 -0
- package/docs/components/switch/content.md +150 -0
- package/docs/components/switch/design.md +208 -0
- package/docs/components/table/content.md +548 -0
- package/docs/components/table-pagination/content.md +177 -0
- package/docs/components/tabs/content.md +292 -0
- package/docs/components/text/content.md +142 -0
- package/docs/components/textarea/content.md +222 -0
- package/docs/components/tile/content.md +703 -0
- package/docs/components/toast/content.md +456 -0
- package/docs/components/toggle-button/content.md +505 -0
- package/docs/components/tooltip/content.md +350 -0
- package/docs/components/virtualized-list/content.md +250 -0
- package/docs/foundations/content.md +9 -0
- package/docs/foundations/expressions/icons/content.md +19 -0
- package/docs/foundations/styles/tokens/all-tokens/content.md +192 -0
- package/docs/foundations/styles/tokens/what-are-tokens/content.md +33 -0
- package/docs/foundations/styles/tokens/what-are-tokens/design.md +33 -0
- package/docs/getting-started/content.md +4 -0
- package/docs/getting-started/designers/content.md +13 -0
- package/docs/getting-started/designers/design.md +13 -0
- package/docs/getting-started/developers/accessibility/content.md +31 -0
- package/docs/getting-started/developers/content.md +612 -0
- package/docs/getting-started/developers/styling/content.md +193 -0
- package/docs/llms.txt +193 -0
- package/docs/patterns/account-management/content.md +75 -0
- package/docs/patterns/authentication/content.md +52 -0
- package/docs/patterns/content.md +10 -0
- package/docs/patterns/dashboard/content.md +39 -0
- package/docs/patterns/empty-states/content.md +31 -0
- package/docs/patterns/forms/content.md +109 -0
- package/docs/patterns/onboarding/content.md +69 -0
- package/docs/patterns/payments/content.md +35 -0
- package/docs/patterns/settings/content.md +72 -0
- package/docs/resources/content.md +9 -0
- package/docs/resources/figma-libraries/content.md +42 -0
- package/docs/resources/release-notes/0.0.1-beta.118/content.md +27 -0
- package/docs/resources/release-notes/0.0.1-beta.122/content.md +21 -0
- package/docs/resources/release-notes/0.0.1-beta.124/content.md +16 -0
- package/docs/resources/release-notes/0.0.1-beta.126/content.md +10 -0
- package/docs/resources/release-notes/0.0.1-beta.127/content.md +14 -0
- package/docs/resources/release-notes/0.0.1-beta.128/content.md +9 -0
- package/docs/resources/release-notes/0.0.1-beta.129/content.md +18 -0
- package/docs/resources/release-notes/0.0.1-beta.130/content.md +18 -0
- package/docs/resources/release-notes/0.0.1-beta.133/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.134/content.md +17 -0
- package/docs/resources/release-notes/0.0.1-beta.136/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.137/content.md +10 -0
- package/docs/resources/release-notes/0.0.1-beta.138/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.139/content.md +13 -0
- package/docs/resources/release-notes/0.0.1-beta.140/content.md +26 -0
- package/docs/resources/release-notes/0.0.1-beta.141/content.md +63 -0
- package/docs/resources/release-notes/0.0.1-beta.142/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.143/content.md +12 -0
- package/docs/resources/release-notes/0.0.1-beta.144/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.145/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.147/content.md +10 -0
- package/docs/resources/release-notes/0.0.1-beta.149/content.md +10 -0
- package/docs/resources/release-notes/0.0.1-beta.150/content.md +13 -0
- package/docs/resources/release-notes/0.0.1-beta.152/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.154/content.md +15 -0
- package/docs/resources/release-notes/0.0.1-beta.156/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.158/content.md +9 -0
- package/docs/resources/release-notes/0.0.1-beta.159/content.md +11 -0
- package/docs/resources/release-notes/0.0.1-beta.160/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.161/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.163/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.164/content.md +9 -0
- package/docs/resources/release-notes/0.0.1-beta.166/content.md +17 -0
- package/docs/resources/release-notes/0.0.1-beta.167/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.168/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.169/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.171/content.md +16 -0
- package/docs/resources/release-notes/0.0.1-beta.172/content.md +24 -0
- package/docs/resources/release-notes/0.0.1-beta.173/content.md +15 -0
- package/docs/resources/release-notes/0.0.1-beta.174/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.176/content.md +32 -0
- package/docs/resources/release-notes/0.0.1-beta.179/content.md +11 -0
- package/docs/resources/release-notes/0.0.1-beta.182/content.md +13 -0
- package/docs/resources/release-notes/0.0.1-beta.185/content.md +7 -0
- package/docs/resources/release-notes/0.0.1-beta.187/content.md +27 -0
- package/docs/resources/release-notes/0.1.1/content.md +23 -0
- package/docs/resources/release-notes/0.1.2/content.md +7 -0
- package/docs/resources/release-notes/0.1.4/content.md +19 -0
- package/docs/resources/release-notes/0.1.5/content.md +7 -0
- package/docs/resources/release-notes/0.1.6/content.md +7 -0
- package/docs/resources/release-notes/0.2.0/content.md +31 -0
- package/docs/resources/release-notes/0.2.1/content.md +17 -0
- package/docs/resources/release-notes/0.2.2/content.md +11 -0
- package/docs/resources/release-notes/0.2.3/content.md +15 -0
- package/docs/resources/release-notes/content.md +64 -0
- package/lib/commonjs/components/Checkbox/index.js.map +1 -1
- package/lib/commonjs/components/DateTimeList/index.js +11 -26
- package/lib/commonjs/components/DateTimeList/index.js.map +1 -1
- package/lib/commonjs/components/DateTimeList/styles.js +2 -12
- package/lib/commonjs/components/DateTimeList/styles.js.map +1 -1
- package/lib/commonjs/components/Heading/index.js.map +1 -1
- package/lib/commonjs/components/Icon/index.js.map +1 -1
- package/lib/commonjs/components/ListItem/index.js +170 -9
- package/lib/commonjs/components/ListItem/index.js.map +1 -1
- package/lib/commonjs/components/ListItem/styles.js +38 -2
- package/lib/commonjs/components/ListItem/styles.js.map +1 -1
- package/lib/commonjs/components/Menu/index.js +11 -5
- package/lib/commonjs/components/Menu/index.js.map +1 -1
- package/lib/commonjs/components/RangeCalendar/index.js.map +1 -1
- package/lib/commonjs/components/ReorderableList/ReorderableListShared.js +91 -0
- package/lib/commonjs/components/ReorderableList/ReorderableListShared.js.map +1 -0
- package/lib/commonjs/components/ReorderableList/index.js +135 -0
- package/lib/commonjs/components/ReorderableList/index.js.map +1 -0
- package/lib/commonjs/components/ReorderableList/index.web.js +233 -0
- package/lib/commonjs/components/ReorderableList/index.web.js.map +1 -0
- package/lib/commonjs/components/ReorderableList/labels.js +47 -0
- package/lib/commonjs/components/ReorderableList/labels.js.map +1 -0
- package/lib/commonjs/components/ReorderableList/reorderItems.js +16 -0
- package/lib/commonjs/components/ReorderableList/reorderItems.js.map +1 -0
- package/lib/commonjs/components/ReorderableList/rowContext.js +42 -0
- package/lib/commonjs/components/ReorderableList/rowContext.js.map +1 -0
- package/lib/commonjs/components/ReorderableList/types.js +6 -0
- package/lib/commonjs/components/ReorderableList/types.js.map +1 -0
- package/lib/commonjs/components/ReorderableList/useReorder.js +131 -0
- package/lib/commonjs/components/ReorderableList/useReorder.js.map +1 -0
- package/lib/commonjs/components/ReorderableList/webDnd.js +64 -0
- package/lib/commonjs/components/ReorderableList/webDnd.js.map +1 -0
- package/lib/commonjs/components/ScopedTheme/index.js.map +1 -1
- package/lib/commonjs/components/ScrollShadow/gradient.js +29 -0
- package/lib/commonjs/components/ScrollShadow/gradient.js.map +1 -0
- package/lib/commonjs/components/ScrollShadow/index.js +171 -44
- package/lib/commonjs/components/ScrollShadow/index.js.map +1 -1
- package/lib/commonjs/components/ScrollShadow/styles.js +58 -14
- package/lib/commonjs/components/ScrollShadow/styles.js.map +1 -1
- package/lib/commonjs/components/ScrollShadow/useComposedScrollHandler.js +63 -0
- package/lib/commonjs/components/ScrollShadow/useComposedScrollHandler.js.map +1 -0
- package/lib/commonjs/components/ScrollShadow/useScrollShadow.js +48 -8
- package/lib/commonjs/components/ScrollShadow/useScrollShadow.js.map +1 -1
- package/lib/commonjs/components/Skeleton/index.js.map +1 -1
- package/lib/commonjs/components/TablePagination/index.js.map +1 -1
- package/lib/commonjs/components/Tabs/index.js +16 -23
- package/lib/commonjs/components/Tabs/index.js.map +1 -1
- package/lib/commonjs/components/Tabs/styles.js +1 -10
- package/lib/commonjs/components/Tabs/styles.js.map +1 -1
- package/lib/commonjs/components/Text/index.js.map +1 -1
- package/lib/commonjs/components/Tile/styles.js +1 -1
- package/lib/commonjs/components/Tile/styles.js.map +1 -1
- package/lib/commonjs/components/index.js.map +1 -1
- package/lib/commonjs/figma/ListItem.SectionHeader.figma.js +20 -5
- package/lib/commonjs/figma/ListItem.SectionHeader.figma.js.map +1 -1
- package/lib/commonjs/reorderable-list.js +17 -0
- package/lib/commonjs/reorderable-list.js.map +1 -0
- package/lib/module/components/Checkbox/index.js.map +1 -1
- package/lib/module/components/DateTimeList/index.js +12 -27
- package/lib/module/components/DateTimeList/index.js.map +1 -1
- package/lib/module/components/DateTimeList/styles.js +1 -11
- package/lib/module/components/DateTimeList/styles.js.map +1 -1
- package/lib/module/components/Heading/index.js.map +1 -1
- package/lib/module/components/Icon/index.js.map +1 -1
- package/lib/module/components/ListItem/index.js +163 -14
- package/lib/module/components/ListItem/index.js.map +1 -1
- package/lib/module/components/ListItem/styles.js +37 -1
- package/lib/module/components/ListItem/styles.js.map +1 -1
- package/lib/module/components/Menu/index.js +11 -5
- package/lib/module/components/Menu/index.js.map +1 -1
- package/lib/module/components/RangeCalendar/index.js.map +1 -1
- package/lib/module/components/ReorderableList/ReorderableListShared.js +86 -0
- package/lib/module/components/ReorderableList/ReorderableListShared.js.map +1 -0
- package/lib/module/components/ReorderableList/index.js +130 -0
- package/lib/module/components/ReorderableList/index.js.map +1 -0
- package/lib/module/components/ReorderableList/index.web.js +229 -0
- package/lib/module/components/ReorderableList/index.web.js.map +1 -0
- package/lib/module/components/ReorderableList/labels.js +41 -0
- package/lib/module/components/ReorderableList/labels.js.map +1 -0
- package/lib/module/components/ReorderableList/reorderItems.js +12 -0
- package/lib/module/components/ReorderableList/reorderItems.js.map +1 -0
- package/lib/module/components/ReorderableList/rowContext.js +38 -0
- package/lib/module/components/ReorderableList/rowContext.js.map +1 -0
- package/lib/module/components/ReorderableList/types.js +4 -0
- package/lib/module/components/ReorderableList/types.js.map +1 -0
- package/lib/module/components/ReorderableList/useReorder.js +123 -0
- package/lib/module/components/ReorderableList/useReorder.js.map +1 -0
- package/lib/module/components/ReorderableList/webDnd.js +59 -0
- package/lib/module/components/ReorderableList/webDnd.js.map +1 -0
- package/lib/module/components/ScopedTheme/index.js.map +1 -1
- package/lib/module/components/ScrollShadow/gradient.js +24 -0
- package/lib/module/components/ScrollShadow/gradient.js.map +1 -0
- package/lib/module/components/ScrollShadow/index.js +170 -48
- package/lib/module/components/ScrollShadow/index.js.map +1 -1
- package/lib/module/components/ScrollShadow/styles.js +57 -13
- package/lib/module/components/ScrollShadow/styles.js.map +1 -1
- package/lib/module/components/ScrollShadow/useComposedScrollHandler.js +57 -0
- package/lib/module/components/ScrollShadow/useComposedScrollHandler.js.map +1 -0
- package/lib/module/components/ScrollShadow/useScrollShadow.js +46 -8
- package/lib/module/components/ScrollShadow/useScrollShadow.js.map +1 -1
- package/lib/module/components/Skeleton/index.js.map +1 -1
- package/lib/module/components/TablePagination/index.js.map +1 -1
- package/lib/module/components/Tabs/index.js +17 -24
- package/lib/module/components/Tabs/index.js.map +1 -1
- package/lib/module/components/Tabs/styles.js +0 -9
- package/lib/module/components/Tabs/styles.js.map +1 -1
- package/lib/module/components/Text/index.js.map +1 -1
- package/lib/module/components/Tile/styles.js +1 -1
- package/lib/module/components/Tile/styles.js.map +1 -1
- package/lib/module/components/index.js.map +1 -1
- package/lib/module/figma/ListItem.SectionHeader.figma.js +20 -5
- package/lib/module/figma/ListItem.SectionHeader.figma.js.map +1 -1
- package/lib/module/reorderable-list.js +4 -0
- package/lib/module/reorderable-list.js.map +1 -0
- package/lib/typescript/components/Checkbox/index.d.ts +22 -0
- package/lib/typescript/components/Checkbox/index.d.ts.map +1 -1
- package/lib/typescript/components/DateTimeList/index.d.ts.map +1 -1
- package/lib/typescript/components/DateTimeList/styles.d.ts +1 -4
- package/lib/typescript/components/DateTimeList/styles.d.ts.map +1 -1
- package/lib/typescript/components/Heading/index.d.ts +20 -2
- package/lib/typescript/components/Heading/index.d.ts.map +1 -1
- package/lib/typescript/components/Icon/index.d.ts +2 -0
- package/lib/typescript/components/Icon/index.d.ts.map +1 -1
- package/lib/typescript/components/ListItem/index.d.ts +28 -5
- package/lib/typescript/components/ListItem/index.d.ts.map +1 -1
- package/lib/typescript/components/ListItem/styles.d.ts +6 -1
- package/lib/typescript/components/ListItem/styles.d.ts.map +1 -1
- package/lib/typescript/components/Menu/index.d.ts +116 -4
- package/lib/typescript/components/Menu/index.d.ts.map +1 -1
- package/lib/typescript/components/RangeCalendar/index.d.ts +2 -0
- package/lib/typescript/components/RangeCalendar/index.d.ts.map +1 -1
- package/lib/typescript/components/ReorderableList/ReorderableListShared.d.ts +31 -0
- package/lib/typescript/components/ReorderableList/ReorderableListShared.d.ts.map +1 -0
- package/lib/typescript/components/ReorderableList/index.d.ts +8 -0
- package/lib/typescript/components/ReorderableList/index.d.ts.map +1 -0
- package/lib/typescript/components/ReorderableList/index.web.d.ts +8 -0
- package/lib/typescript/components/ReorderableList/index.web.d.ts.map +1 -0
- package/lib/typescript/components/ReorderableList/labels.d.ts +16 -0
- package/lib/typescript/components/ReorderableList/labels.d.ts.map +1 -0
- package/lib/typescript/components/ReorderableList/reorderItems.d.ts +3 -0
- package/lib/typescript/components/ReorderableList/reorderItems.d.ts.map +1 -0
- package/lib/typescript/components/ReorderableList/rowContext.d.ts +57 -0
- package/lib/typescript/components/ReorderableList/rowContext.d.ts.map +1 -0
- package/lib/typescript/components/ReorderableList/types.d.ts +75 -0
- package/lib/typescript/components/ReorderableList/types.d.ts.map +1 -0
- package/lib/typescript/components/ReorderableList/useReorder.d.ts +31 -0
- package/lib/typescript/components/ReorderableList/useReorder.d.ts.map +1 -0
- package/lib/typescript/components/ReorderableList/webDnd.d.ts +27 -0
- package/lib/typescript/components/ReorderableList/webDnd.d.ts.map +1 -0
- package/lib/typescript/components/ScopedTheme/index.d.ts +6 -1
- package/lib/typescript/components/ScopedTheme/index.d.ts.map +1 -1
- package/lib/typescript/components/ScrollShadow/gradient.d.ts +7 -0
- package/lib/typescript/components/ScrollShadow/gradient.d.ts.map +1 -0
- package/lib/typescript/components/ScrollShadow/index.d.ts +57 -15
- package/lib/typescript/components/ScrollShadow/index.d.ts.map +1 -1
- package/lib/typescript/components/ScrollShadow/styles.d.ts +8 -2
- package/lib/typescript/components/ScrollShadow/styles.d.ts.map +1 -1
- package/lib/typescript/components/ScrollShadow/useComposedScrollHandler.d.ts +13 -0
- package/lib/typescript/components/ScrollShadow/useComposedScrollHandler.d.ts.map +1 -0
- package/lib/typescript/components/ScrollShadow/useScrollShadow.d.ts +15 -2
- package/lib/typescript/components/ScrollShadow/useScrollShadow.d.ts.map +1 -1
- package/lib/typescript/components/Skeleton/index.d.ts +3 -0
- package/lib/typescript/components/Skeleton/index.d.ts.map +1 -1
- package/lib/typescript/components/TablePagination/index.d.ts +54 -0
- package/lib/typescript/components/TablePagination/index.d.ts.map +1 -1
- package/lib/typescript/components/Tabs/index.d.ts.map +1 -1
- package/lib/typescript/components/Tabs/styles.d.ts +0 -7
- package/lib/typescript/components/Tabs/styles.d.ts.map +1 -1
- package/lib/typescript/components/Text/index.d.ts +12 -2
- package/lib/typescript/components/Text/index.d.ts.map +1 -1
- package/lib/typescript/components/Tile/styles.d.ts.map +1 -1
- package/lib/typescript/components/ToggleButton/styles.d.ts +1 -1
- package/lib/typescript/components/index.d.ts +1 -0
- package/lib/typescript/components/index.d.ts.map +1 -1
- package/lib/typescript/figma/ListItem.SectionHeader.figma.d.ts.map +1 -1
- package/lib/typescript/reorderable-list.d.ts +2 -0
- package/lib/typescript/reorderable-list.d.ts.map +1 -0
- package/package.json +37 -8
- package/src/components/Checkbox/index.tsx +22 -0
- package/src/components/DateTimeList/index.tsx +13 -26
- package/src/components/DateTimeList/styles.ts +1 -10
- package/src/components/Heading/index.tsx +10 -1
- package/src/components/Icon/index.tsx +2 -0
- package/src/components/ListItem/index.tsx +232 -15
- package/src/components/ListItem/styles.ts +41 -0
- package/src/components/Menu/index.tsx +128 -12
- package/src/components/RangeCalendar/index.tsx +2 -0
- package/src/components/ReorderableList/ReorderableListShared.tsx +113 -0
- package/src/components/ReorderableList/index.tsx +162 -0
- package/src/components/ReorderableList/index.web.tsx +254 -0
- package/src/components/ReorderableList/labels.ts +52 -0
- package/src/components/ReorderableList/reorderItems.ts +16 -0
- package/src/components/ReorderableList/rowContext.tsx +72 -0
- package/src/components/ReorderableList/types.ts +80 -0
- package/src/components/ReorderableList/useReorder.ts +159 -0
- package/src/components/ReorderableList/webDnd.ts +60 -0
- package/src/components/ScopedTheme/index.tsx +6 -1
- package/src/components/ScrollShadow/gradient.ts +48 -0
- package/src/components/ScrollShadow/index.tsx +300 -69
- package/src/components/ScrollShadow/styles.ts +45 -28
- package/src/components/ScrollShadow/useComposedScrollHandler.ts +58 -0
- package/src/components/ScrollShadow/useScrollShadow.ts +72 -10
- package/src/components/Skeleton/index.tsx +3 -0
- package/src/components/TablePagination/index.tsx +54 -0
- package/src/components/Tabs/index.tsx +25 -35
- package/src/components/Tabs/styles.ts +0 -9
- package/src/components/Text/index.tsx +6 -1
- package/src/components/Tile/styles.ts +4 -1
- package/src/components/index.ts +5 -0
- package/src/reorderable-list.ts +1 -0
|
@@ -0,0 +1,612 @@
|
|
|
1
|
+
# Installation (https://design.candescent.com/docs/getting-started/developers)
|
|
2
|
+
|
|
3
|
+
## Expo
|
|
4
|
+
|
|
5
|
+
### Install packages
|
|
6
|
+
|
|
7
|
+
Install the core packages in your Expo / React Native project:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @cdx-ui/components @cdx-ui/icons @cdx-ui/styles @cdx-ui/native uniwind tailwindcss
|
|
11
|
+
# or: npm install @cdx-ui/components @cdx-ui/icons @cdx-ui/styles @cdx-ui/native uniwind tailwindcss
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
- **`@cdx-ui/styles`** — provides the design token CSS files (`theme.css`, `utilities.css`) imported in `global.css`.
|
|
15
|
+
- **`@cdx-ui/native`** — native platform integration: the Metro config wrapper (`@cdx-ui/native/metro`) and the Expo config plugin that embeds Forge fonts (listed as `"@cdx-ui/native"` in `app.json` plugins). It ships **no root export** — always import from a subpath.
|
|
16
|
+
- **`tailwindcss`** v4 — required by the CSS entry file (`@import 'tailwindcss'`).
|
|
17
|
+
- **`uniwind`** — required by the CSS entry file (`@import 'uniwind'`) and by the Metro transformer for React Native styling.
|
|
18
|
+
|
|
19
|
+
#### Packages
|
|
20
|
+
|
|
21
|
+
[@cdx-ui/components](https://www.npmjs.com/package/@cdx-ui/components) — Styled UI components (Button, Input, Dialog, etc.)
|
|
22
|
+
|
|
23
|
+
[@cdx-ui/icons](https://www.npmjs.com/package/@cdx-ui/icons) — Icon library with Material Symbols SVGs
|
|
24
|
+
|
|
25
|
+
[@cdx-ui/primitives](https://www.npmjs.com/package/@cdx-ui/primitives) — Unstyled behavioral components (a11y, interactions)
|
|
26
|
+
|
|
27
|
+
[@cdx-ui/styles](https://www.npmjs.com/package/@cdx-ui/styles) — Design tokens and themes
|
|
28
|
+
|
|
29
|
+
[@cdx-ui/native](https://www.npmjs.com/package/@cdx-ui/native) — Metro config wrapper and Expo font loading
|
|
30
|
+
|
|
31
|
+
[@cdx-ui/utils](https://www.npmjs.com/package/@cdx-ui/utils) — Shared helpers (styling utilities, event handlers, refs)
|
|
32
|
+
|
|
33
|
+
#### Peer dependencies
|
|
34
|
+
|
|
35
|
+
Make sure the following are already installed (Expo projects include most of them by default):
|
|
36
|
+
|
|
37
|
+
| Package | Version | Required by |
|
|
38
|
+
| -------------------------------- | ------------------- | ------------------------------------- |
|
|
39
|
+
| `react` | ^18.2.0 or ^19.0.0 | all packages |
|
|
40
|
+
| `react-native` | >=0.76.0 | all packages |
|
|
41
|
+
| `react-native-svg` | ^13 / ^14 / ^15 | `@cdx-ui/icons` |
|
|
42
|
+
| `react-native-safe-area-context` | >=4.0.0 | `@cdx-ui/components` |
|
|
43
|
+
| `react-native-reanimated` | >=4.0.0 (optional) | `@cdx-ui/components` |
|
|
44
|
+
| `react-native-worklets` | >=0.4.0 (optional) | `@cdx-ui/components` |
|
|
45
|
+
| `react-native-gesture-handler` | >=2.0.0 (optional) | `@cdx-ui/components` |
|
|
46
|
+
| `react-native-web` | >=0.19.0 (optional) | `@cdx-ui/components` |
|
|
47
|
+
| `@shopify/flash-list` | >=2.0.0 (optional) | `@cdx-ui/components` |
|
|
48
|
+
| `react-native-sortables` | ^1.10.1 (optional) | `@cdx-ui/components/reorderable-list` |
|
|
49
|
+
| `@dnd-kit/core` | ^6.3.0 (optional) | `@cdx-ui/components/reorderable-list` |
|
|
50
|
+
| `@dnd-kit/sortable` | ^10.0.0 (optional) | `@cdx-ui/components/reorderable-list` |
|
|
51
|
+
| `@dnd-kit/utilities` | ^3.2.0 (optional) | `@cdx-ui/components/reorderable-list` |
|
|
52
|
+
| `expo` | >=52.0.0 (optional) | `@cdx-ui/native` |
|
|
53
|
+
| `metro-config` | >=0.80.0 (optional) | `@cdx-ui/native` |
|
|
54
|
+
| `tailwindcss` | v4 | build tooling |
|
|
55
|
+
|
|
56
|
+
- **`react-native-safe-area-context`** — required by `SafeAreaView` and `useSafeAreaInsets` re-exports; most Expo projects include it. You still need a root `SafeAreaProvider` even when using `ForgeDesignProvider`.
|
|
57
|
+
- **`react-native-reanimated`** / **`react-native-worklets`** — required by animated components (`Dialog`, `AlertDialog`, `Toast`, `BottomSheet`, `Skeleton`, `Carousel`, `Tabs`, and Select overlays). Worklets is Reanimated's companion runtime.
|
|
58
|
+
- **`react-native-gesture-handler`** — required for Dialog/AlertDialog swipe-to-dismiss, Toast swipe gestures, and BottomSheet on native. Wrap the app root in a `GestureHandlerRootView` (see [step 4](#4-import-css-and-wrap-the-app-root)); without it, opening a `Dialog`, `AlertDialog`, `BottomSheet`, or `Toast` on native throws in development.
|
|
59
|
+
- **`react-native-web`** — needed when targeting web; Expo web projects include it automatically.
|
|
60
|
+
- **`@shopify/flash-list`** — only required if using `VirtualizedList`.
|
|
61
|
+
- **`react-native-sortables`** (native) and **`@dnd-kit/core`**, **`@dnd-kit/sortable`**, **`@dnd-kit/utilities`** (web) — only required if you import `ReorderableList` from `@cdx-ui/components/reorderable-list`; the main `@cdx-ui/components` entry never loads them. See [ReorderableList installation](https://design.candescent.com/docs/components/reorderable-list/content.md#installation).
|
|
62
|
+
- **`expo`** — only required by the `@cdx-ui/native` font config plugin; every Expo project already has it. Browser-only apps do not need it: they load fonts through `@cdx-ui/styles/fonts`.
|
|
63
|
+
- **`metro-config`** — only required by `@cdx-ui/native/metro`; every Expo/React Native project already has it.
|
|
64
|
+
|
|
65
|
+
> [!WARNING]
|
|
66
|
+
>
|
|
67
|
+
> **Uniwind requirement**
|
|
68
|
+
>
|
|
69
|
+
> While the published `peerDependencies` floor is `react-native >=0.76.0`, Forge UI's Tailwind-based styling (via Uniwind) requires **React Native 0.81+** (New Architecture) and **Expo SDK 54+**. Projects on earlier versions will satisfy the peer dep check but cannot use the styling pipeline.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
### Metro Setup (React Native / Expo)
|
|
74
|
+
|
|
75
|
+
Forge UI uses Tailwind-based styling via [Uniwind](https://github.com/uni-stack/uniwind). A helper wraps your Metro config so you don't need to configure Uniwind directly.
|
|
76
|
+
|
|
77
|
+
#### 1. Create a CSS entry file
|
|
78
|
+
|
|
79
|
+
Create `global.css` in your project root:
|
|
80
|
+
|
|
81
|
+
```css
|
|
82
|
+
@import 'tailwindcss';
|
|
83
|
+
@import 'uniwind';
|
|
84
|
+
|
|
85
|
+
@custom-variant dark (&:where(.dark, .dark *));
|
|
86
|
+
|
|
87
|
+
@import '@cdx-ui/styles/theme.css';
|
|
88
|
+
@import '@cdx-ui/styles/utilities.css';
|
|
89
|
+
@import '@cdx-ui/components/styles.css';
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- **`@import 'uniwind'`** — Registers Uniwind's CSS transforms and plugins with Tailwind. Must appear immediately after `@import 'tailwindcss'` — before any other directives or theme imports.
|
|
93
|
+
- **`@custom-variant dark`** — Switches Tailwind v4's dark mode from `@media (prefers-color-scheme)` to class-based activation. Required for `ScopedTheme` to work on web. Harmless if you don't use `ScopedTheme` — global dark mode continues to work via `Uniwind.setTheme()`.
|
|
94
|
+
- **`@cdx-ui/styles/theme.css`** — Design tokens (colors, spacing, radii, shadows, fonts) as a Tailwind v4 `@theme static` block, plus light/dark mode and platform font overrides via `@variant`.
|
|
95
|
+
- **`@cdx-ui/styles/utilities.css`** — Composite typography utilities (`heading-xl`…`heading-xs`, `body-xl`…`body-xs`, `label-sm`, `label-xs`) used by `Heading`, `Text`, and `Link`. Each has a `-size` companion (`body-sm-size`) that sets only the font size, line height, and letter spacing, so nested text can change size and keep its inherited font family and weight.
|
|
96
|
+
- **`@cdx-ui/components/styles.css`** — `@source` directive telling Tailwind where to scan for component utility classes.
|
|
97
|
+
|
|
98
|
+
> [!NOTE]
|
|
99
|
+
>
|
|
100
|
+
> **Non-Tailwind consumers**
|
|
101
|
+
>
|
|
102
|
+
> Import `@cdx-ui/styles/vanilla.css` for a plain CSS file with `:root` variables and utility
|
|
103
|
+
> classes — no Tailwind `@theme`/`@variant` syntax required.
|
|
104
|
+
|
|
105
|
+
> [!WARNING]
|
|
106
|
+
>
|
|
107
|
+
> **Web consumers: @cdx-ui/styles/web.css is required**
|
|
108
|
+
>
|
|
109
|
+
> Every browser app must import `@cdx-ui/styles/web.css` as a **separate JS import** — not via `@import` in your CSS file — after your Tailwind CSS entry. It bridges a cascade-layer gap in Tailwind v4's `@theme static` output (unlayered `:root` beats anything inside `@layer theme`), and carries the unlayered `.dark`/`.light` overrides the layered output cannot, for apps that toggle dark mode via a `.dark` class (for example with `next-themes` or a manual toggle).
|
|
110
|
+
>
|
|
111
|
+
> ```tsx
|
|
112
|
+
> import './global.css'; // Tailwind + Uniwind CSS entry
|
|
113
|
+
> import '@cdx-ui/styles/web.css'; // Unlayered token overrides — MUST be after Tailwind
|
|
114
|
+
> ```
|
|
115
|
+
|
|
116
|
+
#### 2. Configure Metro
|
|
117
|
+
|
|
118
|
+
In `metro.config.js`, wrap your config with `withForgeMetroConfig`:
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
const { getDefaultConfig } = require('expo/metro-config');
|
|
122
|
+
const { withForgeMetroConfig } = require('@cdx-ui/native/metro');
|
|
123
|
+
|
|
124
|
+
const config = getDefaultConfig(__dirname);
|
|
125
|
+
|
|
126
|
+
module.exports = withForgeMetroConfig(config, {
|
|
127
|
+
cssEntryFile: './global.css',
|
|
128
|
+
generatedTypesFile: './uniwind-types.d.ts', // optional, enables className autocomplete
|
|
129
|
+
});
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
> [!NOTE]
|
|
133
|
+
>
|
|
134
|
+
> `withForgeMetroConfig` must be the **outermost** wrapper if you compose multiple Metro config
|
|
135
|
+
> helpers.
|
|
136
|
+
|
|
137
|
+
#### 3. Add the font config plugin
|
|
138
|
+
|
|
139
|
+
Add `@cdx-ui/native` to the `plugins` array in `app.json`, then rebuild the native app (`npx expo prebuild`, `expo run:ios` / `expo run:android`, or an EAS build):
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"expo": {
|
|
144
|
+
"plugins": ["@cdx-ui/native"]
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The plugin embeds every Forge font family into the iOS and Android builds, each registered as one family carrying all of its faces. That is what lets `font-medium`, `font-semibold`, `font-bold`, and `italic` select a real face on native, the same way `font-weight` and `font-style` do in a browser. Fonts are part of the binary, so there is no loading state to wait for. Config plugins need a [development build](https://docs.expo.dev/develop/development-builds/introduction/); Expo Go cannot load them.
|
|
150
|
+
|
|
151
|
+
Font selection is handled entirely by the CSS variable layer, including a `ThemeOverride` that switches the Display family at runtime.
|
|
152
|
+
|
|
153
|
+
> [!NOTE]
|
|
154
|
+
>
|
|
155
|
+
> **Upgrading from useForgeFonts**
|
|
156
|
+
>
|
|
157
|
+
> `useForgeFonts` loaded fonts at runtime through `expo-font`, which registers each face as a separate family. On Android that shape cannot carry weight or italic. It is deprecated and now always reports `loaded`: add the plugin, rebuild, and remove the hook along with any rendering gate on it. An Expo app that also exports to web keeps calling `loadForgeWebFonts` from [`@cdx-ui/styles/fonts`](#fonts-on-web), which is inert off the browser.
|
|
158
|
+
|
|
159
|
+
#### 4. Import CSS and wrap the app root
|
|
160
|
+
|
|
161
|
+
In your root layout (e.g. `app/_layout.tsx`), import the CSS entry file and wrap the tree with `GestureHandlerRootView`, `SafeAreaProvider`, and `ForgeDesignProvider`.
|
|
162
|
+
|
|
163
|
+
> [!NOTE]
|
|
164
|
+
>
|
|
165
|
+
> The example below uses [Expo Router](https://docs.expo.dev/router/introduction/) (`expo-router`)
|
|
166
|
+
> for navigation. If your project uses a different router, replace the `Stack` import with your own
|
|
167
|
+
> root navigator.
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
import '../global.css';
|
|
171
|
+
|
|
172
|
+
import { Stack } from 'expo-router';
|
|
173
|
+
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
174
|
+
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|
175
|
+
import { ForgeDesignProvider } from '@cdx-ui/components';
|
|
176
|
+
|
|
177
|
+
export default function RootLayout() {
|
|
178
|
+
return (
|
|
179
|
+
<GestureHandlerRootView style={{ flex: 1 }}>
|
|
180
|
+
<SafeAreaProvider>
|
|
181
|
+
<ForgeDesignProvider>
|
|
182
|
+
<Stack />
|
|
183
|
+
</ForgeDesignProvider>
|
|
184
|
+
</SafeAreaProvider>
|
|
185
|
+
</GestureHandlerRootView>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`GestureHandlerRootView` must sit above `ForgeDesignProvider`. Overlays render through the provider's portal host, so the `Dialog`, `AlertDialog`, `BottomSheet`, and `Toast` gestures need the root view as an ancestor of that host, not just of the screen that opens them.
|
|
191
|
+
|
|
192
|
+
`ForgeDesignProvider` mounts shared app-root services:
|
|
193
|
+
|
|
194
|
+
- `PortalHost` — required for `Dialog` / `AlertDialog` (without a host, overlays fail silently)
|
|
195
|
+
- `ToastProvider` — required for `useToast()` (disable with `config={{ toast: false }}` if unused)
|
|
196
|
+
- Global animation settings and text scaling props (`config.animation`, `config.textProps`)
|
|
197
|
+
- `SafeAreaListener` → keeps Uniwind insets in sync
|
|
198
|
+
|
|
199
|
+
It does **not** replace `SafeAreaProvider`. If you skip `ForgeDesignProvider`, mount one `<PortalHost />` from `@cdx-ui/primitives` (and `ToastProvider` when using toasts) yourself — and do not add a second host when the provider is already in the tree.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
### Minimal Example
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
import { Button, Heading, Text, VStack } from '@cdx-ui/components';
|
|
207
|
+
|
|
208
|
+
export default function HomeScreen() {
|
|
209
|
+
return (
|
|
210
|
+
<VStack space="lg" className="flex-1 p-6">
|
|
211
|
+
<Heading>Welcome</Heading>
|
|
212
|
+
<Text>Get started with Forge UI.</Text>
|
|
213
|
+
<Button onPress={() => {}}>
|
|
214
|
+
<Button.Label>Continue</Button.Label>
|
|
215
|
+
</Button>
|
|
216
|
+
</VStack>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Components carry their own look, so the example sets no colors or typography. Before you style a screen, read [Styling and customization](https://design.candescent.com/docs/getting-started/developers/styling/content.md).
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
### React Native re-exports
|
|
226
|
+
|
|
227
|
+
`@cdx-ui/components` re-exports several core React Native components and hooks so consuming apps can import everything from a single package:
|
|
228
|
+
|
|
229
|
+
```tsx
|
|
230
|
+
import {
|
|
231
|
+
View,
|
|
232
|
+
ScrollView,
|
|
233
|
+
KeyboardAvoidingView,
|
|
234
|
+
SafeAreaView,
|
|
235
|
+
useSafeAreaInsets,
|
|
236
|
+
} from '@cdx-ui/components';
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
| Export | Source |
|
|
240
|
+
| ---------------------- | -------------------------------- |
|
|
241
|
+
| `View` | `react-native` |
|
|
242
|
+
| `ScrollView` | `react-native` |
|
|
243
|
+
| `KeyboardAvoidingView` | `react-native` |
|
|
244
|
+
| `SafeAreaView` | `react-native-safe-area-context` |
|
|
245
|
+
| `useSafeAreaInsets` | `react-native-safe-area-context` |
|
|
246
|
+
|
|
247
|
+
These are convenience re-exports of the underlying packages — not Uniwind wrappers. `className` works on RN views when Uniwind's Metro pipeline is configured; importing from `@cdx-ui/components` vs `react-native` does not change that.
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
### Next steps
|
|
252
|
+
|
|
253
|
+
[Components reference](https://design.candescent.com/docs/components/content.md) — detailed docs for every component\
|
|
254
|
+
[Styling and customization](https://design.candescent.com/docs/getting-started/developers/styling/content.md) — what to style, and what to leave to the components\
|
|
255
|
+
[Accessibility](https://design.candescent.com/docs/getting-started/developers/accessibility/content.md) — what the components handle, and what your screen still provides\
|
|
256
|
+
[Icons](https://design.candescent.com/docs/foundations/expressions/icons/content.md) — using `@cdx-ui/icons`\
|
|
257
|
+
[Link / navigation](https://design.candescent.com/docs/components/link/content.md) — router integration with `LinkProvider`
|
|
258
|
+
|
|
259
|
+
## Vite
|
|
260
|
+
|
|
261
|
+
Use this path when you want Forge UI components in a standard Vite + React + TypeScript web app. Forge UI is authored against React Native primitives; on web those resolve through `react-native-web` in the same browser bundle as your existing React components.
|
|
262
|
+
|
|
263
|
+
### Create a project
|
|
264
|
+
|
|
265
|
+
Scaffold a new app with the React + TypeScript template:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
npm create vite@latest my-forge-app -- --template react-ts
|
|
269
|
+
cd my-forge-app
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Use `pnpm create vite@latest` or `yarn create vite` if you prefer those package managers.
|
|
273
|
+
|
|
274
|
+
### Install dependencies
|
|
275
|
+
|
|
276
|
+
Install all five runtime `@cdx-ui/*` packages at the **same version** — even if you only import from `@cdx-ui/components` directly. Mismatched versions can fork the renderer and cause confusing runtime errors. `@cdx-ui/native` is not needed here: it is the Metro/Expo integration package.
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
pnpm add @cdx-ui/components @cdx-ui/icons @cdx-ui/primitives @cdx-ui/styles @cdx-ui/utils react-native-web react-native-reanimated react-native-worklets react-native-gesture-handler react-native-safe-area-context react-native-svg
|
|
280
|
+
# or: npm install @cdx-ui/components @cdx-ui/icons @cdx-ui/primitives @cdx-ui/styles @cdx-ui/utils react-native-web react-native-reanimated react-native-worklets react-native-gesture-handler react-native-safe-area-context react-native-svg
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Add the Forge Vite integration and its build tooling:
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
pnpm add @cdx-ui/web @tailwindcss/vite tailwindcss uniwind react-native -D
|
|
287
|
+
# or: npm install @cdx-ui/web @tailwindcss/vite tailwindcss uniwind react-native -D
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
`@cdx-ui/components` owns the React Native runtime peer ranges in the first command. Install `@cdx-ui/web` from the same Forge UI release; it owns the Vite integration and declares `vite`, `@tailwindcss/vite`, `tailwindcss`, and `uniwind` as its build-time peers. The Vite scaffold already includes `vite` and `@vitejs/plugin-react`.
|
|
291
|
+
|
|
292
|
+
| Package | Role |
|
|
293
|
+
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
294
|
+
| `@cdx-ui/web` | Build-time Forge UI integration for Vite |
|
|
295
|
+
| `react-native-web` | Web rendering target for React Native primitives |
|
|
296
|
+
| `react-native-reanimated` | Required by animated Forge UI components (`Dialog`, `Toast`, `Skeleton`, `Carousel`, `Tabs`, and related overlays) |
|
|
297
|
+
| `react-native-worklets` | Companion runtime for Reanimated 4 |
|
|
298
|
+
| `react-native-gesture-handler` | Gesture support for Dialog swipe-to-dismiss and Toast swipe |
|
|
299
|
+
| `react-native-safe-area-context` | Required peer of `@cdx-ui/components` |
|
|
300
|
+
| `react-native-svg` | Required transitively by `@cdx-ui/icons`; resolved to its web entry by `@cdx-ui/web/vite` |
|
|
301
|
+
| `uniwind` | Compiles `className` strings into CSS on web |
|
|
302
|
+
| `react-native` (dev) | TypeScript types only — the Forge Vite integration resolves `react-native` imports through Uniwind's web implementation |
|
|
303
|
+
|
|
304
|
+
#### Fonts on web
|
|
305
|
+
|
|
306
|
+
Browser apps load their own fonts — `@cdx-ui/native` is the Metro/Expo integration and should not be installed here. What Forge provides is the answer to _which_ fonts, generated from the token pipeline so it cannot drift from `theme.css`:
|
|
307
|
+
|
|
308
|
+
```tsx
|
|
309
|
+
import { forgeWebFontSpec } from '@cdx-ui/styles/fonts';
|
|
310
|
+
|
|
311
|
+
forgeWebFontSpec.platform; // --font-sans, --font-mono — e.g. Inter, IBM Plex Mono
|
|
312
|
+
forgeWebFontSpec.display; // every Display family a ThemeOverride can select
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Each entry is a **base family** with the weights and italics it actually ships (`{ family, normalWeights, italicWeights }`). Register the family once per weight and let `font-weight` select the face — that is how a browser resolves weight, and it is what makes `font-medium`, `font-semibold` and `italic` reach the right file.
|
|
316
|
+
|
|
317
|
+
Register them statically, in your document head, so the browser's preload scanner finds them while parsing HTML rather than after your bundle evaluates. `buildWebFontProviderUrl` gives you the href:
|
|
318
|
+
|
|
319
|
+
```tsx
|
|
320
|
+
import { buildWebFontProviderUrl, forgeWebFontSpec } from '@cdx-ui/styles/fonts';
|
|
321
|
+
|
|
322
|
+
// Log it once, paste the result into index.html. Most apps ship one Display
|
|
323
|
+
// family, so there is no need to request all of them:
|
|
324
|
+
buildWebFontProviderUrl([...forgeWebFontSpec.platform, forgeWebFontSpec.display[0]]);
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
> [!WARNING]
|
|
328
|
+
>
|
|
329
|
+
> **Ask only for weights a family actually has**
|
|
330
|
+
>
|
|
331
|
+
> A font provider rejects the **entire** request if any family is asked for a weight it does not ship — one bad axis silently drops every font in that URL. Availability differs per family (Libre Caslon Text has 400 and 700 only, with italic at 400 only), which is why the weights travel with the family in `forgeWebFontSpec` rather than being a fixed 400/500/600/700 list.
|
|
332
|
+
|
|
333
|
+
**Self-hosting.** If your deployment cannot call an external host, write the `@font-face` rules yourself against files you host. The shape that matters is one rule per face, declared under the **base family**, with `font-weight` and `font-style` descriptors:
|
|
334
|
+
|
|
335
|
+
```css
|
|
336
|
+
@font-face {
|
|
337
|
+
font-family: 'Inter';
|
|
338
|
+
src: url('/fonts/Inter-500.woff2') format('woff2');
|
|
339
|
+
font-weight: 500;
|
|
340
|
+
font-style: normal;
|
|
341
|
+
font-display: swap;
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Declaring a face under its own family name instead (`font-family: 'Inter Medium'`) will not work — `font-weight` has nothing to select against.
|
|
346
|
+
|
|
347
|
+
**Fonts chosen at runtime.** If the family is not known until runtime — an FI font picker, or a live theme preview — a static tag cannot cover it. `loadForgeWebFonts` injects the `<link>` and returns a cleanup function:
|
|
348
|
+
|
|
349
|
+
```tsx
|
|
350
|
+
import { loadForgeWebFonts, forgeWebFontSpec } from '@cdx-ui/styles/fonts';
|
|
351
|
+
|
|
352
|
+
useEffect(() => loadForgeWebFonts({ families: [selectedDisplayFamily] }), [selectedDisplayFamily]);
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
> [!NOTE]
|
|
356
|
+
>
|
|
357
|
+
> **Weight and style, not face families**
|
|
358
|
+
>
|
|
359
|
+
> Font tokens name one family per role — `--font-sans`, `--font-body`, `--font-mono`, `--font-display`. On `Text`, set weight and style with the `weight` and `italic` props, which select the right face on every platform:
|
|
360
|
+
>
|
|
361
|
+
> ```tsx
|
|
362
|
+
> <Text weight="semibold" italic />
|
|
363
|
+
> ```
|
|
364
|
+
>
|
|
365
|
+
> Outside `Text`, the `font-medium` / `font-semibold` / `font-bold` / `italic` classes do the same over the family a composite typography utility already applies.
|
|
366
|
+
>
|
|
367
|
+
> Some Display families ship no italic faces, and an FI can switch the Display family at runtime. Style italic Display text with `[font-style:var(--font-display-italic-style)]`, which is `normal` for those families, so it renders upright at the right weight instead of each platform improvising a slant.
|
|
368
|
+
|
|
369
|
+
### Dependency footprint
|
|
370
|
+
|
|
371
|
+
Only `react` and `react-native-safe-area-context` are hard-required peers of `@cdx-ui/components`; the remaining React Native runtime peers are optional and enter the bundle according to the components you use.
|
|
372
|
+
|
|
373
|
+
| Package | min (KB) | gzip (KB) |
|
|
374
|
+
| -------------------------------- | ---------: | --------: |
|
|
375
|
+
| `react-native-web` | 521.4 | 160.9 |
|
|
376
|
+
| `react-native-reanimated` | 472.7 | 148.0 |
|
|
377
|
+
| `react-native-gesture-handler` | 290.2 | 94.3 |
|
|
378
|
+
| `uniwind` (runtime) | 67.6 | 30.6 |
|
|
379
|
+
| `react-native-worklets` | 51.1 | 18.6 |
|
|
380
|
+
| `react-native-svg` | 77.4 | 16.6 |
|
|
381
|
+
| `react-native-safe-area-context` | 0.4 | 0.3 |
|
|
382
|
+
| **RN + styling subtotal** | **1480.9** | **469.4** |
|
|
383
|
+
|
|
384
|
+
These measured contributions from a component-heavy production Vite build are an upper bound: React Native Web is the web baseline, while Reanimated is the conditional heavy entry and is currently unoptimized on Vite. At 469.4 KB gzip for the RN + styling subtotal, the footprint is comparable to a mainstream web component kit; these are shipped bytes, not `node_modules` disk sizes.
|
|
385
|
+
|
|
386
|
+
### Configure Vite
|
|
387
|
+
|
|
388
|
+
Replace `vite.config.ts` with the following:
|
|
389
|
+
|
|
390
|
+
```ts
|
|
391
|
+
import { defineConfig } from 'vite';
|
|
392
|
+
import react from '@vitejs/plugin-react';
|
|
393
|
+
import { forge } from '@cdx-ui/web/vite';
|
|
394
|
+
|
|
395
|
+
export default defineConfig({
|
|
396
|
+
plugins: [forge({ cssEntryFile: 'src/index.css' }), react()],
|
|
397
|
+
});
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
`forge()` owns the `__DEV__` and `process.env.NODE_ENV` defines, `react-native` and `react-native-svg` aliases, `.web.*` resolution, Tailwind `optimize: false` plus Uniwind ordering, dependency pre-bundling, package deduplication, Rollup compatibility settings, and the React Native Worklets compatibility patch. Keep `react()` after `forge()` so the owned sub-plugins run in the required order.
|
|
401
|
+
|
|
402
|
+
- **`cssEntryFile`** — Path to the app-owned CSS entry described below; `forge()` passes it to Uniwind but does not create or import the file.
|
|
403
|
+
- **`patchWorklets`** — Controls the React Native Worklets compatibility patch. It defaults to `true`; pass `false` only when you need to disable that patch for your installed Worklets version.
|
|
404
|
+
|
|
405
|
+
### CSS entry
|
|
406
|
+
|
|
407
|
+
Replace `src/index.css` with:
|
|
408
|
+
|
|
409
|
+
```css
|
|
410
|
+
@import 'tailwindcss/theme';
|
|
411
|
+
@import 'tailwindcss/utilities';
|
|
412
|
+
@import 'uniwind';
|
|
413
|
+
|
|
414
|
+
@custom-variant dark (&:where(.dark, .dark *));
|
|
415
|
+
|
|
416
|
+
@import '@cdx-ui/styles/theme.css';
|
|
417
|
+
@import '@cdx-ui/styles/utilities.css';
|
|
418
|
+
@import '@cdx-ui/components/styles.css';
|
|
419
|
+
|
|
420
|
+
body {
|
|
421
|
+
background-color: var(--color-surface-primary);
|
|
422
|
+
color: var(--color-content-primary);
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
Import `theme` and `utilities` separately (not the all-in-one `@import 'tailwindcss'`) so Uniwind can inject its layer between them. The `@cdx-ui/components/styles.css` import is critical — without it, components render unstyled because Tailwind never scans the package for `className` usage. It also reverts React Native Web's `display: inline` reset on root `Text` elements, so text aligns inside plain block containers. If your CSS entry has an older `[class*='css-text-']` override, remove it: it only matches development builds and overrides `numberOfLines` and Tailwind display utilities.
|
|
427
|
+
|
|
428
|
+
`@custom-variant dark` enables class-based dark mode for `ScopedTheme` on web. Always import `@cdx-ui/styles/web.css` as a **separate JS import** after your CSS entry (see App entry below) — it carries the web font family values and the unlayered `.dark`/`.light` overrides.
|
|
429
|
+
|
|
430
|
+
### App entry
|
|
431
|
+
|
|
432
|
+
Import the CSS file as the first line of `src/main.tsx`, then wrap the app with
|
|
433
|
+
`SafeAreaProvider` and `ForgeDesignProvider`:
|
|
434
|
+
|
|
435
|
+
```tsx
|
|
436
|
+
import './index.css';
|
|
437
|
+
import '@cdx-ui/styles/web.css';
|
|
438
|
+
|
|
439
|
+
import { StrictMode } from 'react';
|
|
440
|
+
import { createRoot } from 'react-dom/client';
|
|
441
|
+
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|
442
|
+
import { ForgeDesignProvider } from '@cdx-ui/components';
|
|
443
|
+
import App from './App';
|
|
444
|
+
|
|
445
|
+
createRoot(document.getElementById('root')!).render(
|
|
446
|
+
<StrictMode>
|
|
447
|
+
<SafeAreaProvider>
|
|
448
|
+
<ForgeDesignProvider>
|
|
449
|
+
<App />
|
|
450
|
+
</ForgeDesignProvider>
|
|
451
|
+
</SafeAreaProvider>
|
|
452
|
+
</StrictMode>,
|
|
453
|
+
);
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
`SafeAreaProvider` must remain outside `ForgeDesignProvider`. `ForgeDesignProvider` mounts
|
|
457
|
+
`PortalHost` and `ToastProvider` for you. If you prefer not to use the Forge provider, keep
|
|
458
|
+
`SafeAreaProvider` and mount one `<PortalHost />` from `@cdx-ui/primitives` as a sibling of your app
|
|
459
|
+
(and `ToastProvider` when using toasts). Without a host, `Dialog` / `AlertDialog` fail **silently** —
|
|
460
|
+
state updates, nothing appears, and no error is logged. Do not mount a second host when
|
|
461
|
+
`ForgeDesignProvider` is already in the tree.
|
|
462
|
+
|
|
463
|
+
Select uses React Native Web / native Modal portals and does not require `PortalHost`.
|
|
464
|
+
|
|
465
|
+
### Runtime shim
|
|
466
|
+
|
|
467
|
+
Add an inline script to `index.html` **before** the module script. React Native dependencies reference `global`, which is undefined in browsers:
|
|
468
|
+
|
|
469
|
+
```html
|
|
470
|
+
<body>
|
|
471
|
+
<script>window.global = window;</script>
|
|
472
|
+
<div id="root"></div>
|
|
473
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
474
|
+
</body>
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
This cannot be moved into a module — some references evaluate before your app code runs.
|
|
478
|
+
|
|
479
|
+
### TypeScript
|
|
480
|
+
|
|
481
|
+
Ensure `tsconfig.json` uses bundler resolution (the Vite React + TS template includes this):
|
|
482
|
+
|
|
483
|
+
```json
|
|
484
|
+
{
|
|
485
|
+
"compilerOptions": {
|
|
486
|
+
"module": "ESNext",
|
|
487
|
+
"moduleResolution": "bundler",
|
|
488
|
+
"jsx": "react-jsx",
|
|
489
|
+
"strict": true,
|
|
490
|
+
"isolatedModules": true,
|
|
491
|
+
"skipLibCheck": true,
|
|
492
|
+
"lib": ["ES2020", "DOM", "DOM.Iterable"]
|
|
493
|
+
},
|
|
494
|
+
"include": ["src"]
|
|
495
|
+
}
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
The Uniwind Vite plugin generates `uniwind-types.d.ts` at the project root on first run. Commit this file so CI typecheck does not depend on running the dev server first.
|
|
499
|
+
|
|
500
|
+
### Verify
|
|
501
|
+
|
|
502
|
+
Render a Forge UI component to confirm the pipeline is wired end to end:
|
|
503
|
+
|
|
504
|
+
```tsx
|
|
505
|
+
import { Button, Heading, Text, VStack } from '@cdx-ui/components';
|
|
506
|
+
|
|
507
|
+
export default function App() {
|
|
508
|
+
return (
|
|
509
|
+
<VStack space="lg" className="p-6">
|
|
510
|
+
<Heading>Welcome</Heading>
|
|
511
|
+
<Text>Get started with Forge UI.</Text>
|
|
512
|
+
<Button onPress={() => {}}>
|
|
513
|
+
<Button.Label>Continue</Button.Label>
|
|
514
|
+
</Button>
|
|
515
|
+
</VStack>
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
Then run:
|
|
521
|
+
|
|
522
|
+
```bash
|
|
523
|
+
npm run dev # http://localhost:5173 should render styled components
|
|
524
|
+
npm run build # must succeed with no Rollup errors
|
|
525
|
+
npm run preview # production bundle should match dev
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
If the button renders unstyled, check that `@cdx-ui/components/styles.css` is imported in `src/index.css`.
|
|
529
|
+
|
|
530
|
+
### Testing with Vitest
|
|
531
|
+
|
|
532
|
+
Use Vitest + React Testing Library for consumer-side **web** tests. Native component tests remain Jest + RNTL inside Forge UI packages.
|
|
533
|
+
|
|
534
|
+
Do **not** reuse `forge()` in Vitest. Add a dedicated `vitest.config.ts`:
|
|
535
|
+
|
|
536
|
+
```ts
|
|
537
|
+
import { defineConfig } from 'vitest/config';
|
|
538
|
+
import react from '@vitejs/plugin-react';
|
|
539
|
+
import { forgeVitest } from '@cdx-ui/web/vite';
|
|
540
|
+
|
|
541
|
+
export default defineConfig({
|
|
542
|
+
plugins: [react(), forgeVitest()],
|
|
543
|
+
test: {
|
|
544
|
+
setupFiles: ['./src/__tests__/setup.ts'],
|
|
545
|
+
},
|
|
546
|
+
});
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
Install test dependencies (pin exact versions in apps that follow the example lockfile policy):
|
|
550
|
+
|
|
551
|
+
```bash
|
|
552
|
+
pnpm add vitest @testing-library/react @testing-library/jest-dom jsdom -D
|
|
553
|
+
# or: npm install vitest @testing-library/react @testing-library/jest-dom jsdom -D
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
```ts
|
|
557
|
+
// src/__tests__/setup.ts
|
|
558
|
+
import '@testing-library/jest-dom/vitest';
|
|
559
|
+
import { afterEach } from 'vitest';
|
|
560
|
+
import { cleanup } from '@testing-library/react';
|
|
561
|
+
|
|
562
|
+
afterEach(() => {
|
|
563
|
+
cleanup();
|
|
564
|
+
});
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
Render Forge UI inside `ForgeDesignProvider`. Without its portal host, `Dialog` state can open with no popup in the DOM:
|
|
568
|
+
|
|
569
|
+
```tsx
|
|
570
|
+
import type { ReactElement } from 'react';
|
|
571
|
+
import { render } from '@testing-library/react';
|
|
572
|
+
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|
573
|
+
import { ForgeDesignProvider } from '@cdx-ui/components';
|
|
574
|
+
|
|
575
|
+
export function renderWithForgeProviders(ui: ReactElement) {
|
|
576
|
+
return render(ui, {
|
|
577
|
+
wrapper: ({ children }) => (
|
|
578
|
+
<SafeAreaProvider>
|
|
579
|
+
<ForgeDesignProvider config={{ toast: false }}>{children}</ForgeDesignProvider>
|
|
580
|
+
</SafeAreaProvider>
|
|
581
|
+
),
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
jsdom is sufficient for static rendering, controlled interactions (Switch, Checkbox), and Dialog portal behavior. It does **not** prove production CSS, layout, or animation fidelity — `forgeVitest()` substitutes Uniwind, Reanimated, Gesture Handler, and related native packages for that reason.
|
|
587
|
+
|
|
588
|
+
A copy-paste harness with representative tests lives in the [Forge UI + MUI Vite example](https://github.com/dfh-candescent/forge-design-system/tree/main/examples/forge-ui-mui-vite).
|
|
589
|
+
|
|
590
|
+
### Troubleshooting
|
|
591
|
+
|
|
592
|
+
| Symptom | Likely cause |
|
|
593
|
+
| -------------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
594
|
+
| Components render unstyled | Missing `@cdx-ui/components/styles.css` import in `src/index.css` |
|
|
595
|
+
| `__DEV__ is not defined` | Missing `define` block in `vite.config.ts` |
|
|
596
|
+
| `global is not defined` | Missing `window.global` shim in `index.html` |
|
|
597
|
+
| Dark mode works in dev but not production | Ensure `forge()` is present before `react()` and its Tailwind configuration is not overridden |
|
|
598
|
+
| Dialog opens with no visible popup | Missing `ForgeDesignProvider` or standalone `PortalHost` |
|
|
599
|
+
| `SyntaxError: Unexpected token 'typeof'` in Vitest | `forge()` is in the Vitest config — use `forgeVitest()` from `@cdx-ui/web/vite` instead |
|
|
600
|
+
| Build fails on `react-native-svg` | Ensure `forge()` is present and its `react-native-svg` alias is not overridden |
|
|
601
|
+
| "Two copies of Reanimated" | Mismatched `@cdx-ui/*` package versions |
|
|
602
|
+
|
|
603
|
+
---
|
|
604
|
+
|
|
605
|
+
### Next steps
|
|
606
|
+
|
|
607
|
+
[Components reference](https://design.candescent.com/docs/components/content.md) — detailed docs for every component\
|
|
608
|
+
[Styling and customization](https://design.candescent.com/docs/getting-started/developers/styling/content.md) — what to style, and what to leave to the components\
|
|
609
|
+
[Accessibility](https://design.candescent.com/docs/getting-started/developers/accessibility/content.md) — what the components handle, and what your screen still provides\
|
|
610
|
+
[Icons](https://design.candescent.com/docs/foundations/expressions/icons/content.md) — using `@cdx-ui/icons`\
|
|
611
|
+
[Dialog](https://design.candescent.com/docs/components/dialog/content.md) — overlay setup and portal host requirements\
|
|
612
|
+
[Link / navigation](https://design.candescent.com/docs/components/link/content.md) — router integration with `LinkProvider`
|