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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/cjs/external/dompurify/dist/purify.es.js +4 -2
  2. package/cjs/external/dompurify/dist/purify.es.js.map +1 -1
  3. package/cjs/modal-v2/slots/slot-styles.js +1 -1
  4. package/cjs/modal-v2/slots/slot-styles.js.map +1 -1
  5. package/cjs/modal-v2/slots/{slot-styles_ayb9il.css → slot-styles_ygu6zc.css} +1 -1
  6. package/cjs/notification-banner/notification-banner-hoc.js +1 -1
  7. package/cjs/notification-banner/notification-banner-hoc.js.map +1 -1
  8. package/external/dompurify/dist/purify.es.js +4 -2
  9. package/external/dompurify/dist/purify.es.js.map +1 -1
  10. package/modal-v2/slots/slot-styles.js +1 -1
  11. package/modal-v2/slots/slot-styles.js.map +1 -1
  12. package/modal-v2/slots/{slot-styles_ayb9il.css → slot-styles_ygu6zc.css} +1 -1
  13. package/notification-banner/notification-banner-hoc.js +1 -1
  14. package/notification-banner/notification-banner-hoc.js.map +1 -1
  15. package/package.json +2 -2
  16. package/scripts/cli.js +18 -0
  17. package/skills/fds-build/SKILL.md +167 -0
  18. package/skills/fds-build/catalogue.md +181 -0
  19. package/skills/fds-build/evals/evals.json +519 -0
  20. package/skills/fds-build/resources/components/accordion.md +128 -0
  21. package/skills/fds-build/resources/components/alert.md +54 -0
  22. package/skills/fds-build/resources/components/animation.md +18 -0
  23. package/skills/fds-build/resources/components/avatar.md +10 -0
  24. package/skills/fds-build/resources/components/badge.md +18 -0
  25. package/skills/fds-build/resources/components/box-container.md +61 -0
  26. package/skills/fds-build/resources/components/breadcrumb.md +60 -0
  27. package/skills/fds-build/resources/components/button.md +74 -0
  28. package/skills/fds-build/resources/components/calendar.md +113 -0
  29. package/skills/fds-build/resources/components/card.md +43 -0
  30. package/skills/fds-build/resources/components/checkbox.md +53 -0
  31. package/skills/fds-build/resources/components/countdown-timer.md +20 -0
  32. package/skills/fds-build/resources/components/data-table.md +142 -0
  33. package/skills/fds-build/resources/components/date-navigator.md +25 -0
  34. package/skills/fds-build/resources/components/divider.md +23 -0
  35. package/skills/fds-build/resources/components/drawer.md +78 -0
  36. package/skills/fds-build/resources/components/error-display.md +137 -0
  37. package/skills/fds-build/resources/components/feedback-rating.md +61 -0
  38. package/skills/fds-build/resources/components/file-download.md +54 -0
  39. package/skills/fds-build/resources/components/file-upload.md +105 -0
  40. package/skills/fds-build/resources/components/filter.md +228 -0
  41. package/skills/fds-build/resources/components/footer.md +94 -0
  42. package/skills/fds-build/resources/components/form-date-input.md +85 -0
  43. package/skills/fds-build/resources/components/form-date-range-input.md +146 -0
  44. package/skills/fds-build/resources/components/form-e-signature.md +55 -0
  45. package/skills/fds-build/resources/components/form-input.md +65 -0
  46. package/skills/fds-build/resources/components/form-masked-input.md +69 -0
  47. package/skills/fds-build/resources/components/form-multi-select.md +100 -0
  48. package/skills/fds-build/resources/components/form-nested-multi-select.md +159 -0
  49. package/skills/fds-build/resources/components/form-nested-select.md +151 -0
  50. package/skills/fds-build/resources/components/form-otp-verification.md +175 -0
  51. package/skills/fds-build/resources/components/form-phone-number-input.md +87 -0
  52. package/skills/fds-build/resources/components/form-predictive-text-input.md +108 -0
  53. package/skills/fds-build/resources/components/form-select.md +103 -0
  54. package/skills/fds-build/resources/components/form-slider.md +116 -0
  55. package/skills/fds-build/resources/components/form-textarea.md +116 -0
  56. package/skills/fds-build/resources/components/form-time-range-picker.md +98 -0
  57. package/skills/fds-build/resources/components/form-timepicker.md +77 -0
  58. package/skills/fds-build/resources/components/form-unit-number-input.md +138 -0
  59. package/skills/fds-build/resources/components/fullscreen-image-carousel.md +159 -0
  60. package/skills/fds-build/resources/components/histogram-slider.md +41 -0
  61. package/skills/fds-build/resources/components/icon.md +167 -0
  62. package/skills/fds-build/resources/components/image-button.md +12 -0
  63. package/skills/fds-build/resources/components/input.md +51 -0
  64. package/skills/fds-build/resources/components/language-switcher.md +15 -0
  65. package/skills/fds-build/resources/components/layout.md +156 -0
  66. package/skills/fds-build/resources/components/link-list.md +120 -0
  67. package/skills/fds-build/resources/components/local-nav.md +119 -0
  68. package/skills/fds-build/resources/components/masthead.md +14 -0
  69. package/skills/fds-build/resources/components/menu.md +109 -0
  70. package/skills/fds-build/resources/components/modal.md +150 -0
  71. package/skills/fds-build/resources/components/navbar.md +270 -0
  72. package/skills/fds-build/resources/components/notification-banner.md +106 -0
  73. package/skills/fds-build/resources/components/pagination.md +104 -0
  74. package/skills/fds-build/resources/components/pill.md +55 -0
  75. package/skills/fds-build/resources/components/popover.md +80 -0
  76. package/skills/fds-build/resources/components/progress-indicator.md +47 -0
  77. package/skills/fds-build/resources/components/radio-button.md +66 -0
  78. package/skills/fds-build/resources/components/schedule.md +78 -0
  79. package/skills/fds-build/resources/components/sidenav.md +183 -0
  80. package/skills/fds-build/resources/components/singpass-button.md +14 -0
  81. package/skills/fds-build/resources/components/smart-app-banner.md +28 -0
  82. package/skills/fds-build/resources/components/tab.md +107 -0
  83. package/skills/fds-build/resources/components/table.md +65 -0
  84. package/skills/fds-build/resources/components/tag.md +68 -0
  85. package/skills/fds-build/resources/components/text-list.md +44 -0
  86. package/skills/fds-build/resources/components/time-slot-bar-week.md +66 -0
  87. package/skills/fds-build/resources/components/time-slot-bar.md +57 -0
  88. package/skills/fds-build/resources/components/time-slot-week-view.md +70 -0
  89. package/skills/fds-build/resources/components/timeline.md +69 -0
  90. package/skills/fds-build/resources/components/timetable.md +123 -0
  91. package/skills/fds-build/resources/components/toast.md +72 -0
  92. package/skills/fds-build/resources/components/toggle.md +119 -0
  93. package/skills/fds-build/resources/components/typography.md +86 -0
  94. package/skills/fds-build/resources/components/uneditable-section.md +84 -0
  95. package/skills/fds-build/resources/foundations/layout-composition-patterns.md +53 -0
  96. package/skills/fds-build/resources/foundations/tokens.md +547 -0
  97. package/skills/fds-build/resources/patterns/states.md +300 -0
  98. package/skills/fds-build/resources/setup/setup.md +202 -0
  99. package/skills/fds-build/resources/templates/form.md +427 -0
  100. package/skills/fds-build/resources/templates/listing.md +435 -0
@@ -0,0 +1,300 @@
1
+ # States Pattern
2
+
3
+ Loading, empty, error, and success states for async operations and data absence.
4
+
5
+ ---
6
+
7
+ ## Loading states
8
+
9
+ ### Full-page loading
10
+
11
+ ```tsx
12
+ import { Layout } from "@lifesg/react-design-system/layout";
13
+ import { Card } from "@lifesg/react-design-system/card";
14
+ import { Typography } from "@lifesg/react-design-system/typography";
15
+ import { Spacing } from "@lifesg/react-design-system/theme";
16
+
17
+ if (isLoading) {
18
+ return (
19
+ <Layout.Container type="grid">
20
+ <Layout.ColDiv xsCols={8} lgCols={12}>
21
+ <Card
22
+ style={{
23
+ textAlign: "center",
24
+ padding: `${Spacing["spacing-64"]} ${Spacing["spacing-32"]}`,
25
+ }}
26
+ >
27
+ <LoadingSpinner />
28
+ {/* import { LoadingSpinner } from "@lifesg/react-design-system/loading-spinner" */}
29
+ <Typography.BodyBL>Loading...</Typography.BodyBL>
30
+ </Card>
31
+ </Layout.ColDiv>
32
+ </Layout.Container>
33
+ );
34
+ }
35
+ ```
36
+
37
+ ### Skeleton loading (preferred for content-heavy pages)
38
+
39
+ Render placeholder blocks matching the shape of real content. Use a CSS `pulse` animation (`opacity 0.5 → 1` at 1.5s). Show the same grid structure as the loaded state — same column count, same card shapes.
40
+
41
+ ### Button loading (inline async)
42
+
43
+ ```tsx
44
+ <Button type="submit" loading={isSubmitting} disabled={isSubmitting}>
45
+ {isSubmitting ? "Submitting..." : "Submit"}
46
+ </Button>
47
+ ```
48
+
49
+ Always combine `loading` + `disabled` during async to prevent double-submission.
50
+
51
+ ---
52
+
53
+ ## Empty states
54
+
55
+ ### First-time use (no data)
56
+
57
+ ```tsx
58
+ import { Card } from "@lifesg/react-design-system/card";
59
+ import { Typography } from "@lifesg/react-design-system/typography";
60
+ import { Button } from "@lifesg/react-design-system/button";
61
+ import { Spacing } from "@lifesg/react-design-system/theme";
62
+
63
+ <Card
64
+ style={{
65
+ textAlign: "center",
66
+ padding: `${Spacing["spacing-64"]} ${Spacing["spacing-32"]}`,
67
+ }}
68
+ >
69
+ <Typography.HeadingSM>No items yet</Typography.HeadingSM>
70
+ <Typography.BodyBL>
71
+ Get started by creating your first item
72
+ </Typography.BodyBL>
73
+ <Button
74
+ icon={<PlusIcon />}
75
+ style={{ marginTop: Spacing["spacing-24"] }}
76
+ onClick={handleCreate}
77
+ >
78
+ Create item
79
+ </Button>
80
+ </Card>;
81
+ ```
82
+
83
+ ### No search results
84
+
85
+ ```tsx
86
+ <Card
87
+ style={{
88
+ textAlign: "center",
89
+ padding: `${Spacing["spacing-64"]} ${Spacing["spacing-32"]}`,
90
+ }}
91
+ >
92
+ <Typography.HeadingSM>No results found</Typography.HeadingSM>
93
+ <Typography.BodyBL>Try adjusting your search or filters</Typography.BodyBL>
94
+ <Button
95
+ styleType="secondary"
96
+ style={{ marginTop: Spacing["spacing-24"] }}
97
+ onClick={handleClearFilters}
98
+ >
99
+ Clear filters
100
+ </Button>
101
+ </Card>
102
+ ```
103
+
104
+ For listing pages, prefer `ErrorDisplay type="no-item-found"` over a custom card.
105
+
106
+ ### Empty section (within a page)
107
+
108
+ ```tsx
109
+ import { Typography } from "@lifesg/react-design-system/typography";
110
+ import { Colour, Spacing } from "@lifesg/react-design-system/theme";
111
+
112
+ <div style={{ textAlign: "center", padding: `${Spacing["spacing-32"]} 0` }}>
113
+ <Typography.BodySM style={{ color: Colour["text-subtler"] }}>
114
+ No recent activity to display
115
+ </Typography.BodySM>
116
+ </div>;
117
+ ```
118
+
119
+ ---
120
+
121
+ ## Error states
122
+
123
+ ### API / fetch error
124
+
125
+ ```tsx
126
+ import { Alert } from "@lifesg/react-design-system/alert";
127
+ import { Layout } from "@lifesg/react-design-system/layout";
128
+
129
+ if (error) {
130
+ return (
131
+ <Layout.Container type="grid">
132
+ <Layout.ColDiv xsCols={8} lgCols={12}>
133
+ <Alert
134
+ type="error"
135
+ actionLink={{
136
+ children: "Try again",
137
+ onClick: () => window.location.reload(),
138
+ }}
139
+ >
140
+ {error.message}
141
+ </Alert>
142
+ </Layout.ColDiv>
143
+ </Layout.Container>
144
+ );
145
+ }
146
+ ```
147
+
148
+ ### 404 Not found
149
+
150
+ ```tsx
151
+ import { Card } from "@lifesg/react-design-system/card";
152
+ import { Typography } from "@lifesg/react-design-system/typography";
153
+ import { Button } from "@lifesg/react-design-system/button";
154
+ import { Spacing } from "@lifesg/react-design-system/theme";
155
+
156
+ <Card
157
+ style={{
158
+ textAlign: "center",
159
+ padding: `${Spacing["spacing-64"]} ${Spacing["spacing-32"]}`,
160
+ }}
161
+ >
162
+ <Typography.HeadingXL>404</Typography.HeadingXL>
163
+ <Typography.HeadingSM>Page not found</Typography.HeadingSM>
164
+ <Typography.BodyBL>
165
+ The page you're looking for doesn't exist or has been moved
166
+ </Typography.BodyBL>
167
+ <Button
168
+ style={{ marginTop: Spacing["spacing-24"] }}
169
+ onClick={() => navigate("/")}
170
+ >
171
+ Go to home
172
+ </Button>
173
+ </Card>;
174
+ ```
175
+
176
+ ### Permission denied
177
+
178
+ ```tsx
179
+ <Card
180
+ style={{
181
+ textAlign: "center",
182
+ padding: `${Spacing["spacing-64"]} ${Spacing["spacing-32"]}`,
183
+ }}
184
+ >
185
+ <Typography.HeadingSM>Access denied</Typography.HeadingSM>
186
+ <Typography.BodyBL>
187
+ You don't have permission to view this page
188
+ </Typography.BodyBL>
189
+ <Button
190
+ styleType="secondary"
191
+ style={{ marginTop: Spacing["spacing-24"] }}
192
+ onClick={() => navigate("/")}
193
+ >
194
+ Go to home
195
+ </Button>
196
+ </Card>
197
+ ```
198
+
199
+ ---
200
+
201
+ ## Success states
202
+
203
+ ### Inline success
204
+
205
+ ```tsx
206
+ <Alert
207
+ type="success"
208
+ actionLink={{ children: "View details", onClick: handleViewDetails }}
209
+ >
210
+ {successMessage}
211
+ </Alert>
212
+ ```
213
+
214
+ ### Toast notification
215
+
216
+ ```tsx
217
+ import { Toast } from "@lifesg/react-design-system/toast";
218
+
219
+ <Toast type="success">Changes saved successfully</Toast>
220
+ <Toast type="error">Failed to save changes</Toast>
221
+ ```
222
+
223
+ Use `Toast` for non-critical feedback. Use `Alert` for errors requiring action.
224
+
225
+ ### Full-page success (major operations)
226
+
227
+ ```tsx
228
+ import { Card } from "@lifesg/react-design-system/card";
229
+ import { Typography } from "@lifesg/react-design-system/typography";
230
+ import { Button } from "@lifesg/react-design-system/button";
231
+ import { Spacing } from "@lifesg/react-design-system/theme";
232
+
233
+ <Card
234
+ style={{
235
+ textAlign: "center",
236
+ padding: `${Spacing["spacing-64"]} ${Spacing["spacing-32"]}`,
237
+ }}
238
+ >
239
+ <Typography.HeadingMD>
240
+ Application submitted successfully
241
+ </Typography.HeadingMD>
242
+ <Typography.BodyBL>Your application is being processed</Typography.BodyBL>
243
+ <div
244
+ style={{
245
+ display: "flex",
246
+ gap: Spacing["spacing-16"],
247
+ justifyContent: "center",
248
+ marginTop: Spacing["spacing-32"],
249
+ }}
250
+ >
251
+ <Button onClick={() => navigate("/applications")}>
252
+ View applications
253
+ </Button>
254
+ <Button styleType="secondary" onClick={() => navigate("/")}>
255
+ Go to home
256
+ </Button>
257
+ </div>
258
+ </Card>;
259
+ ```
260
+
261
+ ---
262
+
263
+ ## State management pattern
264
+
265
+ ```tsx
266
+ const [state, setState] = useState({
267
+ data: null,
268
+ isLoading: true,
269
+ error: null,
270
+ });
271
+
272
+ // In component:
273
+ if (state.isLoading) return <LoadingState />;
274
+ if (state.error) return <ErrorState message={state.error} />;
275
+ if (!state.data || state.data.length === 0) return <EmptyState />;
276
+ return <DataDisplay data={state.data} />;
277
+ ```
278
+
279
+ ---
280
+
281
+ ## Rules
282
+
283
+ - Show loading feedback for any operation taking longer than 200ms
284
+ - Prefer skeleton loading over a spinner for content-heavy initial page loads
285
+ - Always provide a way out of an error state (retry, go home)
286
+ - Empty state messages: 1–2 sentences max, action-oriented, sentence case
287
+ - Error messages: name the specific problem — never "An error occurred"
288
+ - `Toast` auto-dismisses — use for confirmations; use `Alert` for errors needing action
289
+ - Full-page state containers: `padding: spacing-64 spacing-32`, `textAlign: center`, inside a `Card`
290
+ - Disable interactive elements during loading to prevent race conditions
291
+ - Use `style={{}}` with `Spacing` and `Colour` tokens for all custom spacing and colour
292
+
293
+ ## Anti-patterns
294
+
295
+ - Do not show a spinner for operations under 200ms — causes flicker
296
+ - Do not show an empty container with no state message in a listing page
297
+ - Do not use `Toast` for critical errors — user may miss it
298
+ - Do not use generic error text — always tell the user what failed
299
+ - Do not leave submit buttons enabled during async — always set `disabled` + `loading`
300
+ - Do not hardcode `px` values or hex colours — always use `Spacing` and `Colour` tokens
@@ -0,0 +1,202 @@
1
+ # App Setup — Flagship v4
2
+
3
+ Full project setup for `@lifesg/react-design-system` v4. Work through all steps before writing any component code.
4
+
5
+ ---
6
+
7
+ ## Step 1 — Install packages
8
+
9
+ ```bash
10
+ npm install @lifesg/react-design-system @lifesg/react-icons @floating-ui/react
11
+ ```
12
+
13
+ Do NOT install `styled-components` — it is not used in v4.
14
+
15
+ ---
16
+
17
+ ## Step 2 — CDN stylesheets
18
+
19
+ Add these stylesheets to normalise browser styles and display fonts.
20
+
21
+ ```html
22
+ <link
23
+ rel="stylesheet"
24
+ type="text/css"
25
+ href="https://assets.life.gov.sg/react-design-system/v4/css/main.css"
26
+ />
27
+ <link
28
+ rel="stylesheet"
29
+ type="text/css"
30
+ href="https://assets.life.gov.sg/react-design-system/v3/css/open-sans.css"
31
+ />
32
+ ```
33
+
34
+ Or if you are importing to an existing CSS file:
35
+
36
+ ```css
37
+ @import url("https://assets.life.gov.sg/react-design-system/v4/css/main.css");
38
+ @import url("https://assets.life.gov.sg/react-design-system/v3/css/open-sans.css");
39
+ ```
40
+
41
+ Replace `open-sans` with the font for your theme:
42
+
43
+ | Theme | Font CSS |
44
+ | ---------------------------------------- | ---------------------------------- |
45
+ | lifesg, ccube, mylegacy, oneservice, rbs | `open-sans` |
46
+ | bookingsg, careercompass, smgs | `plus-jakarta-sans` |
47
+ | spf, vica, websg, wise, wogaa | `public-sans` |
48
+ | sgw-digital-lobby, supportgowhere | `libre-franklin` |
49
+ | pa | `lato` |
50
+ | imda | `montserrat` |
51
+ | tote-board | `inter` |
52
+ | a11y-playground | `atkinson-hyperlegible-next` |
53
+ | sportsg-orange | `apfel-grotezk` + `hanken-grotesk` |
54
+
55
+ ---
56
+
57
+ ## Step 3 — Theme stylesheet
58
+
59
+ In your application's CSS entrypoint, import the theme stylesheet:
60
+
61
+ ```css
62
+ @import "@lifesg/react-design-system/theme/styles/lifesg.css";
63
+ ```
64
+
65
+ > CSS `@import` from `node_modules` requires your framework or build tool to support it. Vite and Next.js resolve this automatically; other build tools may need a CSS resolver configured.
66
+
67
+ Or in your application's JavaScript entrypoint, import the theme stylesheet:
68
+
69
+ ```tsx
70
+ import "@lifesg/react-design-system/theme/styles/lifesg.css";
71
+ ```
72
+
73
+ Replace `lifesg` with your chosen theme slug: `a11y-playground` · `bookingsg` · `careercompass` · `ccube` · `imda` · `lifesg` · `mylegacy` · `oneservice` · `pa` · `rbs` · `sgw-digital-lobby` · `smgs` · `spf` · `sportsg-orange` · `supportgowhere` · `tote-board` · `vica` · `websg` · `wise` · `wogaa`
74
+
75
+ ---
76
+
77
+ ## Step 4 — ThemeProvider
78
+
79
+ Wrap your app root in `ThemeProvider`. The `theme` prop is a string matching the slug used in Step 3.
80
+
81
+ ```tsx
82
+ import { ThemeProvider } from "@lifesg/react-design-system/theme";
83
+
84
+ const App = () => {
85
+ return (
86
+ <ThemeProvider theme="lifesg">
87
+ <Component />
88
+ </ThemeProvider>
89
+ );
90
+ };
91
+ ```
92
+
93
+ **Dark/light mode:** `mode` prop defaults to `"auto"` (OS preference).
94
+
95
+ ```tsx
96
+ <ThemeProvider theme="lifesg" mode="light"> {/* force light */}
97
+ <ThemeProvider theme="lifesg" mode="dark"> {/* force dark */}
98
+ ```
99
+
100
+ ---
101
+
102
+ ## Vite
103
+
104
+ No special configuration needed for the design system. Standard React setup:
105
+
106
+ ```ts
107
+ // vite.config.ts
108
+ import { defineConfig } from "vite";
109
+ import react from "@vitejs/plugin-react";
110
+
111
+ export default defineConfig({
112
+ plugins: [react()],
113
+ });
114
+ ```
115
+
116
+ ---
117
+
118
+ ## Next.js App Router
119
+
120
+ No special `next.config.js` is needed. Suggested configuration below.
121
+
122
+ ### CSS
123
+
124
+ Create a global CSS file with all three imports — CDN stylesheets and theme:
125
+
126
+ ```css
127
+ /* global.css */
128
+ @import url("https://assets.life.gov.sg/react-design-system/v4/css/main.css");
129
+ @import url("https://assets.life.gov.sg/react-design-system/v3/css/open-sans.css");
130
+ @import "@lifesg/react-design-system/theme/styles/lifesg.css";
131
+ ```
132
+
133
+ Wrap the app in a flex column layout so the footer stays pinned to the bottom on short pages:
134
+
135
+ ```css
136
+ body {
137
+ min-height: 100vh;
138
+ display: flex;
139
+ flex-direction: column;
140
+ }
141
+
142
+ main {
143
+ flex: 1;
144
+ }
145
+
146
+ .theme-provider {
147
+ display: flex;
148
+ flex-direction: column;
149
+ }
150
+ ```
151
+
152
+ ### ThemeProvider
153
+
154
+ `ThemeProvider` is a client component, so create a wrapper:
155
+
156
+ ```tsx
157
+ // src/app/providers.tsx
158
+ "use client";
159
+ import { ThemeProvider } from "@lifesg/react-design-system/theme";
160
+
161
+ export default function Providers({ children }: { children: React.ReactNode }) {
162
+ return (
163
+ <ThemeProvider theme="lifesg" className="theme-provider">
164
+ {children}
165
+ </ThemeProvider>
166
+ );
167
+ }
168
+ ```
169
+
170
+ ### Root layout
171
+
172
+ Import the global CSS and the providers wrapper in `layout.tsx`:
173
+
174
+ ```tsx
175
+ // src/app/layout.tsx
176
+ import "./global.css";
177
+ import Providers from "./providers";
178
+
179
+ export default function RootLayout({
180
+ children,
181
+ }: {
182
+ children: React.ReactNode;
183
+ }) {
184
+ return (
185
+ <html lang="en">
186
+ <body>
187
+ <Providers>{children}</Providers>
188
+ </body>
189
+ </html>
190
+ );
191
+ }
192
+ ```
193
+
194
+ ---
195
+
196
+ ## Verification checklist
197
+
198
+ - [ ] `@lifesg/react-design-system`, `@lifesg/react-icons`, `@floating-ui/react` installed
199
+ - [ ] `styled-components` NOT installed
200
+ - [ ] `main.css` and font CDN links imported in HTML doc or CSS file
201
+ - [ ] `@lifesg/react-design-system/theme/styles/{theme}.css` imported in entrypoint or CSS file
202
+ - [ ] `ThemeProvider` wraps app root with correct theme string