@iress-oss/ids-components 6.0.0-beta.3 → 6.0.0-beta.5

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 (32) hide show
  1. package/.ai/components/popover.md +23 -0
  2. package/.ai/components/provider.md +2 -1
  3. package/.ai/skills/figma-to-ids.md +134 -60
  4. package/.ai/skills/ui-doctor.md +1 -1
  5. package/.ai/skills/ui-translation.md +96 -70
  6. package/dist/Provider-DvnuqPfQ.js +58 -0
  7. package/dist/{Shadow-DdGxqWgh.js → Shadow-C-AJ5pf_.js} +23 -22
  8. package/dist/components/Field/FieldGroup/FieldGroup.d.ts +1 -1
  9. package/dist/components/Field/FieldGroup/FieldGroup.js +12 -11
  10. package/dist/components/Field/FieldGroup/FieldGroup.styles.d.ts +12 -0
  11. package/dist/components/Field/FieldGroup/FieldGroup.styles.js +7 -3
  12. package/dist/components/Icon/helpers/getMaterialSymbolsList.js +1 -1
  13. package/dist/components/Popover/PopoverProvider.d.ts +15 -0
  14. package/dist/components/Popover/PopoverProvider.js +14 -0
  15. package/dist/components/Popover/components/PopoverContent.js +25 -24
  16. package/dist/components/Popover/hooks/usePopoverContainer.d.ts +17 -0
  17. package/dist/components/Popover/hooks/usePopoverContainer.js +5 -0
  18. package/dist/components/Popover/index.d.ts +4 -0
  19. package/dist/components/Popover/index.js +7 -5
  20. package/dist/components/Provider/Provider.d.ts +16 -2
  21. package/dist/components/Provider/Provider.js +1 -1
  22. package/dist/components/Provider/index.js +1 -1
  23. package/dist/index.d-pXVOif1T.js +4 -0
  24. package/dist/main.js +132 -130
  25. package/dist/patterns/ContextualMenu/ContextualMenu.d.ts +1 -1
  26. package/dist/patterns/ContextualMenu/ContextualMenu.js +22 -21
  27. package/dist/patterns/Shadow/Shadow.d.ts +1 -1
  28. package/dist/patterns/Shadow/Shadow.js +1 -1
  29. package/dist/patterns/Shadow/index.js +1 -1
  30. package/package.json +12 -12
  31. package/dist/Provider-CQ9D7IoC.js +0 -54
  32. package/dist/index.d-BJM5_ZcV.js +0 -4
@@ -173,6 +173,29 @@ This will be the case when you are rendering into its parent container, which is
173
173
 
174
174
  [View "ParentContainer" example in Storybook →](https://main--691abcc79dfa560a36d0a74f.chromatic.com/?path=/story/components_components-popover--parent-container)
175
175
 
176
+ ### Using the `IressPopoverProvider`
177
+
178
+ You can use the `IressPopoverProvider` to set a shared container for
179
+ all nested popovers. This is useful when you want all popovers in a
180
+ section of your application to render into the same DOM node, similar
181
+ to how `IressModalProvider` and `IressSlideoutProvider` work.
182
+
183
+ Individual popovers can still override the provider's container by
184
+ setting their own `container` prop.
185
+
186
+ > **Note:** If you are already using `IressProvider` or `IressShadow`,
187
+ > you do not need to add `IressPopoverProvider` separately — it is
188
+ > already included. To set a shared container for popovers through
189
+ > `IressProvider`, use the `popoverContainer` prop. You can also set
190
+ > `popoverContainer="container"` to reuse the same container that
191
+ > modals, slideouts and toasts render into.
192
+
193
+ ```tsx
194
+ <AppWithPopoverProvider />
195
+ ```
196
+
197
+ [View "Provider" example in Storybook →](https://main--691abcc79dfa560a36d0a74f.chromatic.com/?path=/story/components_components-provider--provider)
198
+
176
199
  ## `IressInputPopover`
177
200
 
178
201
  If you need a popover that is triggered by input changes, you can use the `IressInputPopover` component. This component has an additional `minLength` prop that allows you to specify the minimum number of characters required before the popover is shown.
@@ -19,10 +19,11 @@ The design system provider automates some set-up tasks for you, including:
19
19
 
20
20
  - Adding the icon fonts and CSS variables to the document head
21
21
  - Consistent container handling for providers, if you need the modals, slideouts and toasts rendered in a specific area (common with micro frontends)
22
+ - Optional separate container for popovers via the `popoverContainer` prop
22
23
 
23
24
  In most cases, you should wrap the entire application with the `IressProvider` component. This will ensure that the design system is set up correctly and consistently across the application.
24
25
 
25
- > **Note:** `IressProvider` already includes `IressModalProvider`, `IressSlideoutProvider`, `IressToasterProvider`, and `IressIconProvider`. You do not need to add these providers separately when using `IressProvider`. Similarly, `IressShadow` includes `IressProvider` internally, so you do not need any additional providers when using `IressShadow`.
26
+ > **Note:** `IressProvider` already includes `IressModalProvider`, `IressSlideoutProvider`, `IressToasterProvider`, `IressPopoverProvider`, and `IressIconProvider`. You do not need to add these providers separately when using `IressProvider`. Similarly, `IressShadow` includes `IressProvider` internally, so you do not need any additional providers when using `IressShadow`.
26
27
 
27
28
  ```tsx
28
29
  import { IressProvider } from '@iress-oss/ids-components';
@@ -4,12 +4,53 @@
4
4
 
5
5
  Translate Figma design properties and structures into IDS (Iress Design System) component implementations. This skill helps AI agents interpret Figma design metadata (from tools like Figma MCP or exported design specs) and produce accurate IDS code.
6
6
 
7
+ ## Figma MCP Setup
8
+
9
+ AI agents need a Figma MCP server to read Figma files directly. Without one, you can still use this skill by pasting exported design specs or Figma component descriptions manually.
10
+
11
+ ### Setup
12
+
13
+ 1. **Get a Figma personal access token** — In Figma, go to *Settings → Account → Personal access tokens* and create a token with **File content (Read-only)** scope.
14
+
15
+ 2. **Add the MCP server to your agent config.** The exact location depends on your tool:
16
+
17
+ | Tool | Config file |
18
+ | --- | --- |
19
+ | **Kiro CLI** | `~/.kiro/settings/mcp.json` (global) or `.kiro/settings/mcp.json` (workspace) |
20
+ | **Cursor** | `.cursor/mcp.json` |
21
+ | **Claude Code** | `.claude/mcp.json` or `~/.claude/mcp.json` |
22
+ | **VS Code (GitHub Copilot)** | `.vscode/mcp.json` |
23
+
24
+ Example configuration (using the community [`figma-developer-mcp`](https://github.com/nicholasgriffintn/figma-developer-mcp) server):
25
+
26
+ ```json
27
+ {
28
+ "mcpServers": {
29
+ "Figma": {
30
+ "command": "npx",
31
+ "args": ["-y", "figma-developer-mcp", "--stdio"],
32
+ "env": {
33
+ "FIGMA_API_KEY": "<your-figma-token>"
34
+ }
35
+ }
36
+ }
37
+ }
38
+ ```
39
+
40
+ For Kiro CLI, you can also add it via the command line:
41
+
42
+ ```bash
43
+ kiro-cli mcp add --name Figma --command npx --args "-y figma-developer-mcp --stdio" --env "FIGMA_API_KEY=<your-figma-token>"
44
+ ```
45
+
46
+ 3. **Verify** — Ask your agent to fetch data from a Figma file URL. It should return frame and component information.
47
+
7
48
  ## Process
8
49
 
9
50
  1. **Analyse Figma structure** — Identify frames, auto-layout, and component instances
10
51
  2. **Map components** — Match Figma component names/variants to IDS components
11
52
  3. **Extract tokens** — Convert Figma design values to IDS design token references
12
- 4. **Generate code** — Produce clean React/TypeScript with proper IDS imports
53
+ 4. **Generate code** — Produce clean, minimal React/TypeScript with proper IDS imports. Use the fewest components possible — check whether parent components already handle layout before adding `IressInline`/`IressStack` wrappers. Never wrap a single child in a layout component.
13
54
  5. **Verify output** — Check that all imports resolve, no raw HTML is used where IDS components exist, grid layouts use responsive `span` values, and no common anti-patterns are present (disabled buttons, slot attributes, redundant textStyle)
14
55
 
15
56
  > **Important:** IDS v6 is currently in beta. Install with the `@beta` tag:
@@ -49,11 +90,11 @@ When mapping Figma components to IDS, read references/component-mapping.md for t
49
90
  | Input / Text | `IressField` + `IressInput` | label, placeholder |
50
91
  | Input / Currency | `IressField` + `IressInputCurrency` | label |
51
92
  | Select / Dropdown | `IressField` + `IressSelect` | label, options |
52
- | Checkbox | `IressCheckbox` | label |
53
- | Checkbox Group | `IressCheckboxGroup` + `IressCheckbox`s | label |
54
- | Radio Group | `IressRadioGroup` + `IressRadio`s | label |
55
- | Toggle | `IressToggle` | label |
56
- | Card | `IressCard` | Header, Body, Footer slots |
93
+ | Checkbox | `IressCheckbox` | `children` for label |
94
+ | Checkbox Group | `IressCheckboxGroup` + `IressCheckbox`s | Wrap in `IressField` for label |
95
+ | Radio Group | `IressRadioGroup` + `IressRadio`s | Wrap in `IressField` for label |
96
+ | Toggle | `IressToggle` | `children` for label |
97
+ | Card | `IressCard` | `heading`, `footer` props; `children` for body |
57
98
  | Panel | `IressPanel` | |
58
99
  | Alert / Success | `IressAlert status="success"` | |
59
100
  | Alert / Danger | `IressAlert status="danger"` | |
@@ -65,7 +106,7 @@ When mapping Figma components to IDS, read references/component-mapping.md for t
65
106
  | Modal / Warning | `IressModal status="warning"` | actions, size sm/md only |
66
107
  | Slideout / Drawer | `IressSlideout` | |
67
108
  | Tabs | `IressTabSet` + `IressTab` | |
68
- | Table | `IressTable` | Head, Body, Row, Cell |
109
+ | Table | `IressTable` | Data-driven: `rows`, `columns`, `caption` props |
69
110
  | Tag | `IressTag` | `bordered` for visible border; `element="button"` for clickable, `element="a"` for link; `onClick` alone also auto-renders as button |
70
111
  | Pill | `IressPill` | |
71
112
  | Tooltip | `IressTooltip` | |
@@ -132,19 +173,23 @@ IDS base spacing unit = 4px (0.25rem). Map Figma pixel values:
132
173
 
133
174
  ## Typography
134
175
 
135
- | Figma Text Style | IDS Token | Component |
136
- | ---------------------------------- | ---------------------------- | --------------------------------------- |
137
- | Heading / H1 (Ubuntu 24px/500) | `typography.heading.1` | `<IressText element="h1">` |
138
- | Heading / H2 (Ubuntu 20px/500) | `typography.heading.2` | `<IressText element="h2">` |
139
- | Heading / H3 (Ubuntu 18px/500) | `typography.heading.3` | `<IressText element="h3">` |
140
- | Heading / H4 (Ubuntu 16px/500) | `typography.heading.4` | `<IressText element="h4">` |
141
- | Heading / H5 (Ubuntu 16px/400) | `typography.heading.5` | `<IressText element="h5">` |
142
- | Body / MD Regular (Inter 14px/400) | `typography.body.md.regular` | `<IressText>` (default) |
143
- | Body / MD Medium (Inter 14px/500) | `typography.body.md.medium` | `<IressText weight="medium">` |
144
- | Body / MD Strong (Inter 14px/600) | `typography.body.md.strong` | `<IressText weight="strong">` |
145
- | Body / SM Regular (Inter 12px/400) | `typography.body.sm.regular` | `<IressText size="sm">` |
146
- | Body / SM Strong (Inter 12px/600) | `typography.body.sm.strong` | `<IressText size="sm" weight="strong">` |
147
- | Code (Space 16px/400) | `typography.code` | `<IressText element="code">` |
176
+ Prefer semantic HTML elements via the `element` prop — they convey meaning to screen readers. Only fall back to `textStyle` when no semantic element matches the visual treatment.
177
+
178
+ | Figma Text Style | IDS Token | Component |
179
+ | ---------------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------- |
180
+ | Heading / H1 (Ubuntu 24px/500) | `typography.heading.1` | `<IressText element="h1">` |
181
+ | Heading / H2 (Ubuntu 20px/500) | `typography.heading.2` | `<IressText element="h2">` |
182
+ | Heading / H3 (Ubuntu 18px/500) | `typography.heading.3` | `<IressText element="h3">` |
183
+ | Heading / H4 (Ubuntu 16px/500) | `typography.heading.4` | `<IressText element="h4">` |
184
+ | Heading / H5 (Ubuntu 16px/400) | `typography.heading.5` | `<IressText element="h5">` |
185
+ | Body / MD Regular (Inter 14px/400) | `typography.body.md.regular` | `<IressText>` (default) or `<IressText element="p">` for paragraph semantics |
186
+ | Body / MD Medium (Inter 14px/500) | `typography.body.md.medium` | `<IressText textStyle="typography.body.md.medium">` — use `textStyle` because there is no semantic medium element |
187
+ | Body / MD Strong (Inter 14px/600) | `typography.body.md.strong` | `<IressText element="strong">` — conveys emphasis to screen readers |
188
+ | Body / SM Regular (Inter 12px/400) | `typography.body.sm` | `<IressText element="small">` — conveys fine print / secondary text |
189
+ | Body / SM Strong (Inter 12px/600) | `typography.body.sm.strong` | `<IressText element="small"><strong>...</strong></IressText>` |
190
+ | Code (Space 16px/400) | `typography.code` | `<IressText element="code">` |
191
+
192
+ > **When to use `textStyle`:** Only when you need to visually override the default styling of a semantic element — e.g. making an `h2` look like an `h4` for visual hierarchy while keeping the correct heading level for accessibility: `<IressText element="h2" textStyle="typography.heading.4">`.
148
193
 
149
194
  > **Tip:** When translating Figma frames that contain mixed or unstructured text (e.g. CMS content, markdown, rich text blocks), wrap the content in `IressText` and nest native HTML elements (`<p>`, `<strong>`, `<a>`, `<ul>`, etc.) inside it. This is an allowed pattern that lets `IressText` apply consistent typography while preserving the original content structure.
150
195
 
@@ -176,8 +221,8 @@ import {
176
221
 
177
222
  function LoginForm() {
178
223
  return (
179
- <IressCard p="6">
180
- <IressStack gap="4">
224
+ <IressCard p="lg">
225
+ <IressStack gap="md">
181
226
  <IressText element="h2">Log In</IressText>
182
227
  <IressField label="Email" htmlFor="email" required>
183
228
  <IressInput id="email" type="email" />
@@ -256,39 +301,41 @@ import { IressModal } from '@iress-oss/ids-components';
256
301
 
257
302
  ```tsx
258
303
  import { IressTable, IressTag, IressButton } from '@iress-oss/ids-components';
304
+ import type { TableColumn } from '@iress-oss/ids-components';
259
305
 
260
- function UsersTable({ users }) {
261
- return (
262
- <IressTable>
263
- <IressTable.Head>
264
- <IressTable.Row>
265
- <IressTable.HeaderCell>Name</IressTable.HeaderCell>
266
- <IressTable.HeaderCell>Email</IressTable.HeaderCell>
267
- <IressTable.HeaderCell>Status</IressTable.HeaderCell>
268
- <IressTable.HeaderCell>Actions</IressTable.HeaderCell>
269
- </IressTable.Row>
270
- </IressTable.Head>
271
- <IressTable.Body>
272
- {users.map((user) => (
273
- <IressTable.Row key={user.id}>
274
- <IressTable.Cell>{user.name}</IressTable.Cell>
275
- <IressTable.Cell>{user.email}</IressTable.Cell>
276
- <IressTable.Cell>
277
- <IressTag>{user.status}</IressTag>
278
- </IressTable.Cell>
279
- <IressTable.Cell>
280
- <IressButton mode="tertiary" icon="edit">
281
- Edit
282
- </IressButton>
283
- </IressTable.Cell>
284
- </IressTable.Row>
285
- ))}
286
- </IressTable.Body>
287
- </IressTable>
288
- );
306
+ interface User {
307
+ name: string;
308
+ email: string;
309
+ status: string;
310
+ id: string;
311
+ }
312
+
313
+ const columns: TableColumn<User>[] = [
314
+ { key: 'name', label: 'Name' },
315
+ { key: 'email', label: 'Email' },
316
+ {
317
+ key: 'status',
318
+ label: 'Status',
319
+ format: (value) => <IressTag>{value}</IressTag>,
320
+ },
321
+ {
322
+ key: 'actions',
323
+ label: 'Actions',
324
+ format: (_, row) => (
325
+ <IressButton mode="tertiary" icon="edit">
326
+ Edit
327
+ </IressButton>
328
+ ),
329
+ },
330
+ ];
331
+
332
+ function UsersTable({ users }: { users: User[] }) {
333
+ return <IressTable caption="Users" rows={users} columns={columns} />;
289
334
  }
290
335
  ```
291
336
 
337
+ > **Key insight:** `IressTable` is data-driven — pass `rows` and `columns` props instead of composing sub-components. Use the `format` function on columns to render custom cell content like tags or buttons.
338
+
292
339
  ## Responsive Layout
293
340
 
294
341
  **Always produce responsive output**, even when Figma only provides a single desktop frame. IDS uses a 12-column grid with 6 breakpoints — every translation should consider how the layout adapts to smaller screens.
@@ -351,10 +398,12 @@ When Figma provides separate mobile and desktop frames for the same layout:
351
398
  When Figma only provides a desktop frame with a sidebar + main content area, infer the mobile layout:
352
399
 
353
400
  ```tsx
401
+ import { useState } from 'react';
354
402
  import {
355
403
  useBreakpoint,
356
404
  IressSlideout,
357
405
  IressButton,
406
+ IressStack,
358
407
  IressRow,
359
408
  IressCol,
360
409
  } from '@iress-oss/ids-components';
@@ -368,7 +417,7 @@ function Page() {
368
417
  <>
369
418
  {isMobile ? (
370
419
  // Mobile: primary content first, secondary content in slideout
371
- <IressStack gap="4">
420
+ <IressStack gap="md">
372
421
  <IressButton
373
422
  mode="secondary"
374
423
  icon="filter_list"
@@ -425,17 +474,42 @@ function Navigation() {
425
474
 
426
475
  ## Best Practices
427
476
 
428
- 1. **Use IDS components, not raw elements** IDS components encapsulate correct spacing, colours, border radius, and accessibility
429
- 2. **Don't recreate component internals** — If Figma shows a button with specific padding/radius, use `IressButton` with the right `mode` — the styling is built in
430
- 3. **Map Figma gap/padding to spacing tokens** Divide pixel value by 4 to get the token number
431
- 4. **Prefer semantic props over manual styling** — Use `status="danger"` instead of `bg="colour.system.danger.fill"`
432
- 5. **Use IressField for all form inputs** — It provides the label, hint, and validation layout
433
- 6. **Respect responsive patterns** — Use `hideFrom`/`hideBelow` props or the `useBreakpoint` hook for responsive visibility; use responsive `span` on `IressCol` for adaptive grid layouts
434
- 7. **Always make grid layouts responsive** — When translating Figma multi-column layouts, use responsive `span` values (e.g. `span={{ xs: 12, md: 6 }}`) so columns stack on mobile
435
- 8. **Check the component docs** — Read the specific component doc for detailed props and patterns (`node_modules/@iress-oss/ids-components/.ai/components/`)
477
+ 1. **Minimise component nesting** — Use the fewest components possible. Every wrapper must earn its place. Before adding `IressInline` or `IressStack`, check whether the parent already handles layout (e.g. `IressCard` has `heading` and `footer` props; `IressModal` has `actions`; `IressButtonGroup` handles horizontal button layout). Don't wrap a single child in a layout component.
478
+ 2. **Use IDS components, not raw elements** — IDS components encapsulate correct spacing, colours, border radius, and accessibility
479
+ 3. **Don't recreate component internals** — If Figma shows a button with specific padding/radius, use `IressButton` with the right `mode` the styling is built in
480
+ 4. **Map Figma gap/padding to spacing tokens** — Divide pixel value by 4 to get the token number, then use the full token: 16px → `"spacing.4"`, 24px → `"spacing.6"`. Alias tokens (`"xs"`, `"sm"`, `"md"`, `"lg"`, `"xl"`) are also valid. Never use bare numbers like `gap="4"`.
481
+ 5. **Prefer semantic props over manual styling** — Use `status="danger"` instead of `bg="colour.system.danger.fill"`
482
+ 6. **Use IressField for all form inputs** It provides the label, hint, and validation layout
483
+ 7. **Respect responsive patterns** — Use `hideFrom`/`hideBelow` props or the `useBreakpoint` hook for responsive visibility; use responsive `span` on `IressCol` for adaptive grid layouts
484
+ 8. **Always make grid layouts responsive** — When translating Figma multi-column layouts, use responsive `span` values (e.g. `span={{ xs: 12, md: 6 }}`) so columns stack on mobile
485
+ 9. **Check the component docs** — Read the specific component doc for detailed props and patterns (`node_modules/@iress-oss/ids-components/.ai/components/`)
436
486
 
437
487
  ## Common Mistakes
438
488
 
489
+ ### Unnecessary layout wrappers
490
+
491
+ Don't add `IressInline` or `IressStack` when it adds no value. Every Figma auto-layout frame does NOT need its own layout wrapper — check the IDS component first.
492
+
493
+ ```tsx
494
+ // ❌ Unnecessary nesting — IressStack wrapping a single child
495
+ <IressStack gap="md">
496
+ <IressInline gap="sm">
497
+ <IressButton mode="primary">Save</IressButton>
498
+ <IressButton mode="secondary">Cancel</IressButton>
499
+ </IressInline>
500
+ </IressStack>
501
+
502
+ // ✅ Single group of buttons only needs IressInline
503
+ <IressInline gap="sm">
504
+ <IressButton mode="primary">Save</IressButton>
505
+ <IressButton mode="secondary">Cancel</IressButton>
506
+ </IressInline>
507
+ ```
508
+
509
+ **Rule of thumb:** When Figma shows an auto-layout frame, check if the corresponding IDS component already provides that layout before adding a wrapper. Components like `IressModal` (with `actions`) and `IressButtonGroup` already handle their internal layout. For `IressCard`, use the `heading` and `footer` props to structure content — but note the `footer` slot does not auto-layout its children, so use `IressInline` inside `footer` when you need horizontal button layout.
510
+
511
+ ### Other common anti-patterns
512
+
439
513
  For the full list of common anti-patterns (disabled buttons, redundant textStyle, legacy slot attributes, raw HTML, hardcoded values), read the Common Mistakes guide at `node_modules/@iress-oss/ids-components/.ai/guides/foundations-common-mistakes.md` (requires `@iress-oss/ids-components` to be installed).
440
514
 
441
515
  **Figma-specific addition:** When Figma shows named content areas ("prepend", "append", "footer"), map them to the corresponding **React prop**, not to a `slot` attribute. When Figma shows a greyed-out or disabled button state, do not use `disabled` — see the guide for alternatives.
@@ -1812,7 +1812,7 @@ Not every raw HTML element is a violation. The following are **acceptable except
1812
1812
 
1813
1813
  #### a. Provider & CSS Setup
1814
1814
 
1815
- - **IressProvider must wrap the application root** — Required for fonts, CSS variables, and theming. `IressProvider` already includes `IressModalProvider`, `IressSlideoutProvider`, `IressToasterProvider`, and `IressIconProvider` — these should not be added separately. Similarly, `IressShadow` includes `IressProvider` internally, so no additional providers are needed when using `IressShadow`.
1815
+ - **IressProvider must wrap the application root** — Required for fonts, CSS variables, and theming. `IressProvider` already includes `IressModalProvider`, `IressSlideoutProvider`, `IressToasterProvider`, `IressPopoverProvider`, and `IressIconProvider` — these should not be added separately. Similarly, `IressShadow` includes `IressProvider` internally, so no additional providers are needed when using `IressShadow`.
1816
1816
  - **The IDS component CSS must be imported** — `@iress-oss/ids-components/dist/style.css` contains all component styles
1817
1817
  - Users only need to install `@iress-oss/ids-components@beta` — the tokens are bundled within the component library and do not need to be installed separately. **IDS v6 is currently in beta**, so the `@beta` tag is required (e.g. `npm install @iress-oss/ids-components@beta`)
1818
1818
  - **CSP must allowlist IDS external origins** — If the app enforces a Content Security Policy, `fonts.googleapis.com` and `fonts.gstatic.com` must be in `style-src`/`font-src`. Add `cdn.iress.com` if using legacy Font Awesome icons or `IressTheme`. If using `IressShadow` and inline styles are blocked, add `<meta name="csp-nonce" content="...">` in `<head>`. See the CSP Guide at `node_modules/@iress-oss/ids-components/.ai/guides/get-started-content-security-policy.md` for details (requires `@iress-oss/ids-components` to be installed).