Skip to content

Commit b69fe74

Browse files
committed
docs: add custom illustrations guide for ui5-illustrated-message
Add comprehensive documentation for registering and using custom illustrations in UI5 Web Components projects.
1 parent 89e86ce commit b69fe74

File tree

2 files changed

+284
-0
lines changed

2 files changed

+284
-0
lines changed
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
# Using Custom Illustrations
2+
3+
*Learn how to create and register custom illustrations for the UI5 Web Components IllustratedMessage component.*
4+
5+
The [`ui5-illustrated-message`](https://ui5.github.io/webcomponents/components/fiori/IllustratedMessage/) component enhances user experience by displaying contextual illustrations in empty states, error conditions, onboarding flows, and other scenarios where visual communication improves usability.
6+
7+
While UI5 Web Components includes a comprehensive set of [built-in illustrations](https://ui5.github.io/webcomponents/components/fiori/enums/IllustrationMessageType/), you can extend this collection by registering your own custom illustrations that match your application's brand and design requirements.
8+
9+
## Overview
10+
11+
Custom illustrations in UI5 Web Components consist of four size variants that automatically adapt to different container dimensions and [design contexts](https://ui5.github.io/webcomponents/components/fiori/enums/IllustrationMessageDesign/):
12+
13+
| Variant | Size | Breakpoint | Container Width | Current Design Name |
14+
|------------|-------------|------------|-----------------|---------------------|
15+
| **Scene** | Large | L | > 681px | Large |
16+
| **Dialog** | Medium | M | ≤ 681px | Medium |
17+
| **Spot** | Small | S | ≤ 360px | Small |
18+
| **Dot** | Extra Small | XS | ≤ 260px | ExtraSmall |
19+
20+
Each custom illustration must include all four variants to ensure optimal display across different use cases and responsive breakpoints.
21+
22+
## Prerequisites
23+
24+
Before implementing custom illustrations, ensure you have:
25+
26+
### 1. Install Required Packages
27+
28+
```bash
29+
npm install @ui5/webcomponents @ui5/webcomponents-fiori
30+
```
31+
32+
The [`@ui5/webcomponents`](https://www.npmjs.com/package/@ui5/webcomponents) package provides the base framework and asset registry functionality, while [`@ui5/webcomponents-fiori`](https://www.npmjs.com/package/@ui5/webcomponents-fiori) contains the `IllustratedMessage` component.
33+
34+
### 2. Prepare SVG Assets
35+
36+
Create four SVG files for each illustration following the naming convention:
37+
```
38+
{set}-{Variant}-{IllustrationName}.js
39+
```
40+
41+
**Example file structure:**
42+
```
43+
assets/
44+
├── custom-Scene-EmptyCart.js # Large variant
45+
├── custom-Dialog-EmptyCart.js # Medium variant
46+
├── custom-Spot-EmptyCart.js # Small variant
47+
└── custom-Dot-EmptyCart.js # Extra small variant
48+
```
49+
50+
## Implementation
51+
52+
### Registration
53+
54+
Register your custom illustration using the `registerIllustration` function:
55+
56+
**JavaScript:**
57+
```js
58+
import "@ui5/webcomponents-fiori/dist/IllustratedMessage.js";
59+
import { registerIllustration } from "@ui5/webcomponents-base/dist/asset-registries/Illustrations.js";
60+
61+
// Import SVG assets
62+
import sceneSvg from "./assets/custom-Scene-EmptyCart.js";
63+
import dialogSvg from "./assets/custom-Dialog-EmptyCart.js";
64+
import spotSvg from "./assets/custom-Spot-EmptyCart.js";
65+
import dotSvg from "./assets/custom-Dot-EmptyCart.js";
66+
67+
// Register the illustration
68+
registerIllustration("EmptyCart", {
69+
sceneSvg,
70+
dialogSvg,
71+
spotSvg,
72+
dotSvg,
73+
title: "Your cart is empty",
74+
subtitle: "Add items to get started with your order",
75+
set: "custom"
76+
});
77+
```
78+
79+
**TypeScript:**
80+
```ts
81+
import "@ui5/webcomponents-fiori/dist/IllustratedMessage.js";
82+
import { registerIllustration } from "@ui5/webcomponents-base/dist/asset-registries/Illustrations.js";
83+
import type { I18nText } from "@ui5/webcomponents-base/dist/i18nBundle.js";
84+
85+
// Import SVG assets
86+
import sceneSvg from "./assets/custom-Scene-EmptyCart.js";
87+
import dialogSvg from "./assets/custom-Dialog-EmptyCart.js";
88+
import spotSvg from "./assets/custom-Spot-EmptyCart.js";
89+
import dotSvg from "./assets/custom-Dot-EmptyCart.js";
90+
91+
// Register the illustration with proper typing
92+
registerIllustration("EmptyCart", {
93+
sceneSvg,
94+
dialogSvg,
95+
spotSvg,
96+
dotSvg,
97+
title: "Your cart is empty" as I18nText,
98+
subtitle: "Add items to get started with your order" as I18nText,
99+
set: "custom"
100+
});
101+
```
102+
103+
### SVG Asset Structure
104+
105+
Each SVG asset file should export the SVG content as a string:
106+
107+
**custom-Dialog-EmptyCart.js:**
108+
```js
109+
export default `<svg
110+
id="custom-Dialog-EmptyCart"
111+
width="160"
112+
height="120"
113+
viewBox="0 0 160 120"
114+
fill="none"
115+
xmlns="http://www.w3.org/2000/svg"
116+
>
117+
<!-- Cart outline -->
118+
<path
119+
d="M20 30h120l-8 60H28l-8-60z"
120+
stroke="var(--sapContent_Illustrative_Color1)"
121+
stroke-width="2"
122+
fill="var(--sapContent_Illustrative_Color2)"
123+
/>
124+
<!-- Cart handle -->
125+
<path
126+
d="M35 30V20a10 10 0 0 1 20 0v10"
127+
stroke="var(--sapContent_Illustrative_Color3)"
128+
stroke-width="2"
129+
/>
130+
<!-- Empty state indicator -->
131+
<circle
132+
cx="80"
133+
cy="60"
134+
r="15"
135+
fill="var(--sapContent_Illustrative_Color4)"
136+
opacity="0.3"
137+
/>
138+
</svg>`;
139+
```
140+
141+
### Design Guidelines
142+
143+
**SVG Requirements:**
144+
- Include a unique `id` attribute: `{set}-{Variant}-{IllustrationName}`
145+
- Use responsive dimensions appropriate for each variant
146+
- Implement proper `viewBox` for scalability
147+
148+
**Theming Support:**
149+
- Use CSS custom properties for colors
150+
- Avoid hard-coded colors to ensure theme compatibility
151+
- Test illustrations across different UI5 themes
152+
153+
**Security Compliance:**
154+
- ⚠️ **No inline JavaScript**: Avoid `<script>` tags or event handlers
155+
- ⚠️ **No unsafe styles**: Avoid `style` attributes that violate CSP policies
156+
-**Use CSS custom properties**: Prefer theme-aware styling
157+
-**Test with strict CSP**: Validate in CSP-enabled environments
158+
159+
## Usage
160+
161+
### Basic Implementation
162+
163+
Use your registered illustration by referencing it with the format `{set}/{name}`:
164+
165+
**HTML:**
166+
```html
167+
<ui5-illustrated-message name="custom/EmptyCart">
168+
<ui5-button design="Emphasized">Start Shopping</ui5-button>
169+
</ui5-illustrated-message>
170+
```
171+
172+
**React:**
173+
```jsx
174+
import { Button, IllustratedMessage } from '@ui5/webcomponents-react';
175+
176+
function EmptyCartView() {
177+
return (
178+
<IllustratedMessage name="custom/EmptyCart">
179+
<Button design="Emphasized">Start Shopping</Button>
180+
</IllustratedMessage>
181+
);
182+
}
183+
```
184+
185+
### Configuration
186+
187+
Control illustration size and behavior using the [`design`](https://ui5.github.io/webcomponents/components/fiori/IllustratedMessage/#design) property:
188+
189+
```html
190+
<!-- Auto-responsive (default) -->
191+
<ui5-illustrated-message name="custom/EmptyCart" design="Auto">
192+
<ui5-button>Add Items</ui5-button>
193+
</ui5-illustrated-message>
194+
195+
<!-- Fixed size -->
196+
<ui5-illustrated-message name="custom/EmptyCart" design="Large">
197+
<ui5-button>Add Items</ui5-button>
198+
</ui5-illustrated-message>
199+
```
200+
201+
### Accessibility
202+
203+
For accessibility considerations, use the [`decorative`](https://ui5.github.io/webcomponents/components/fiori/IllustratedMessage/#decorative) property when appropriate:
204+
205+
```html
206+
<!-- Decorative illustrations (no accessibility labels) -->
207+
<ui5-illustrated-message name="custom/EmptyCart" decorative>
208+
<ui5-button>Add Items</ui5-button>
209+
</ui5-illustrated-message>
210+
```
211+
212+
**Note:** Use the `decorative` property when the illustration is purely visual and doesn't convey important information. This improves accessibility by preventing screen readers from announcing decorative content.
213+
214+
## Best Practices
215+
216+
- **Consistent Design Language**: Maintain visual consistency with your application's design system
217+
- **Meaningful Illustrations**: Create illustrations that clearly communicate the intended message
218+
- **Performance**: Optimize SVG file sizes while maintaining quality
219+
- **Accessibility**: Ensure illustrations support users with visual impairments through proper titles and descriptions
220+
- **Testing**: Validate illustrations across different themes, screen sizes, and browsers

packages/base/src/asset-registries/Illustrations.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,40 @@ import getSharedResource from "../getSharedResource.js";
22
import type { I18nText } from "../i18nBundle.js";
33
import { getTheme } from "../config/Theme.js";
44

5+
/**
6+
* Loader function for lazy-loading illustration data.
7+
*/
58
type IllustrationLoader = (illustrationName: string) => Promise<IllustrationData>;
69

10+
/**
11+
* Core illustration properties containing SVG variants and metadata.
12+
*
13+
* @public
14+
*/
715
type IllustrationProperties = {
16+
/** SVG content for the medium variant (M breakpoint, ≤ 681px) */
817
dialogSvg: string,
18+
/** SVG content for the large variant (L breakpoint, > 681px) */
919
sceneSvg: string,
20+
/** SVG content for the small variant (S breakpoint, ≤ 360px) */
1021
spotSvg: string,
22+
/** SVG content for the extra small variant (XS breakpoint, ≤ 260px) */
1123
dotSvg: string,
24+
/** The illustration title text (supports i18n) */
1225
title: I18nText,
26+
/** The illustration subtitle text (supports i18n) */
1327
subtitle: I18nText,
1428
};
1529

30+
/**
31+
* Complete illustration data for registration.
32+
*
33+
* @public
34+
*/
1635
type IllustrationData = IllustrationProperties & {
36+
/** The illustration set identifier (e.g., "custom") */
1737
set: string,
38+
/** Collection identifier (defaults to "V4") */
1839
collection: string,
1940
};
2041

@@ -65,6 +86,28 @@ const processName = (name: string) => {
6586
};
6687
};
6788

89+
/**
90+
* Registers a custom illustration in the global registry.
91+
*
92+
* @param name - The name of the illustration (without set prefix)
93+
* @param data - The illustration data (see {@link IllustrationData})
94+
*
95+
* @public
96+
* @example
97+
* ```js
98+
* import { registerIllustration } from "@ui5/webcomponents-base/dist/asset-registries/Illustrations.js";
99+
*
100+
* registerIllustration("EmptyCart", {
101+
* sceneSvg: "<svg>...</svg>",
102+
* dialogSvg: "<svg>...</svg>",
103+
* spotSvg: "<svg>...</svg>",
104+
* dotSvg: "<svg>...</svg>",
105+
* title: "Your cart is empty",
106+
* subtitle: "Add items to get started",
107+
* set: "custom"
108+
* });
109+
* ```
110+
*/
68111
const registerIllustration = (name: string, data: IllustrationData) => {
69112
const collection = data.collection || FALLBACK_COLLECTION;
70113
registry.set(`${data.set}/${collection}/${name}`, {
@@ -95,11 +138,27 @@ const _loadIllustrationOnce = (illustrationName: string) => {
95138
return illustrationPromises.get(registryKey);
96139
};
97140

141+
/**
142+
* Synchronously retrieves illustration data from the registry.
143+
*
144+
* @param illustrationName - The illustration identifier in format "set/name"
145+
* @returns The illustration properties or undefined if not available
146+
*
147+
* @public
148+
*/
98149
const getIllustrationDataSync = (illustrationName: string) => {
99150
const { registryKey } = processName(illustrationName);
100151
return registry.get(registryKey);
101152
};
102153

154+
/**
155+
* Asynchronously retrieves illustration data, loading it if necessary.
156+
*
157+
* @param illustrationName - The illustration identifier in format "set/name"
158+
* @returns Promise resolving to illustration properties or undefined
159+
*
160+
* @public
161+
*/
103162
const getIllustrationData = async (illustrationName: string) => {
104163
const { registryKey } = processName(illustrationName);
105164

@@ -113,3 +172,8 @@ export {
113172
registerIllustrationLoader,
114173
getIllustrationData,
115174
};
175+
176+
export type {
177+
IllustrationData,
178+
IllustrationProperties,
179+
};

0 commit comments

Comments
 (0)