@nxgt/mail-ui 0.1.0
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/LICENSE +21 -0
- package/README.md +311 -0
- package/components/alert.vue +75 -0
- package/components/avatar-fallback.vue +21 -0
- package/components/avatar-group.vue +44 -0
- package/components/avatar-image.vue +20 -0
- package/components/avatar.vue +29 -0
- package/components/badge.vue +47 -0
- package/components/banner.vue +57 -0
- package/components/breakdown-card.vue +27 -0
- package/components/card-content.vue +16 -0
- package/components/card-description.vue +16 -0
- package/components/card-footer.vue +16 -0
- package/components/card-header.vue +33 -0
- package/components/card-title.vue +20 -0
- package/components/card.vue +30 -0
- package/components/chip.vue +41 -0
- package/components/code.vue +26 -0
- package/components/compare-card.vue +63 -0
- package/components/description.vue +24 -0
- package/components/entity-header.vue +48 -0
- package/components/goal-card.vue +33 -0
- package/components/hero.vue +36 -0
- package/components/layout.vue +85 -0
- package/components/link.vue +19 -0
- package/components/list-tile.vue +80 -0
- package/components/nx-button.vue +91 -0
- package/components/progress.vue +66 -0
- package/components/ratio-card.vue +36 -0
- package/components/see-also.vue +53 -0
- package/components/separator.vue +27 -0
- package/components/stat-card.vue +43 -0
- package/components/status-indicator.vue +35 -0
- package/components/steps-item.vue +48 -0
- package/components/steps.vue +31 -0
- package/components/summary-data.vue +45 -0
- package/components/table-body.vue +11 -0
- package/components/table-caption.vue +16 -0
- package/components/table-cell.vue +28 -0
- package/components/table-empty.vue +23 -0
- package/components/table-footer.vue +11 -0
- package/components/table-head.vue +25 -0
- package/components/table-header.vue +11 -0
- package/components/table-row.vue +15 -0
- package/components/table.vue +25 -0
- package/components/timeline.vue +70 -0
- package/components/typography.vue +64 -0
- package/components/ui.ts +166 -0
- package/dist/catalogues.d.ts +56 -0
- package/dist/catalogues.d.ts.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +214 -0
- package/dist/index.js.map +13 -0
- package/dist/packaged.d.ts +27 -0
- package/dist/packaged.d.ts.map +1 -0
- package/dist/plugin.d.ts +55 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/theme.d.ts +11 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/vue.d.ts +9 -0
- package/dist/vue.d.ts.map +1 -0
- package/docs/README.md +15 -0
- package/docs/guide/components.md +984 -0
- package/docs/guide/messages.md +156 -0
- package/docs/guide/plugin.md +365 -0
- package/docs/guide/theme.md +153 -0
- package/docs/roadmap.md +97 -0
- package/docs/troubleshooting.md +519 -0
- package/package.json +69 -0
- package/theme.css +292 -0
|
@@ -0,0 +1,984 @@
|
|
|
1
|
+
# Components
|
|
2
|
+
|
|
3
|
+
This page is for writing templates with the `Nx*` components: each one's
|
|
4
|
+
props, defaults and slots, and the `@nxgt/material-vue` component it mirrors.
|
|
5
|
+
|
|
6
|
+
```vue
|
|
7
|
+
<!-- emails/sign-in-code.vue -->
|
|
8
|
+
<template>
|
|
9
|
+
<NxLayout preheader="Your sign-in code">
|
|
10
|
+
<NxTypography variant="headline-small">Your sign-in code</NxTypography>
|
|
11
|
+
<NxTypography>Enter this code to sign in to {{ brand.name }}.</NxTypography>
|
|
12
|
+
<NxCode>493 812</NxCode>
|
|
13
|
+
<NxTypography variant="caption">It expires in 15 minutes.</NxTypography>
|
|
14
|
+
</NxLayout>
|
|
15
|
+
</template>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
<img src="https://raw.githubusercontent.com/softistx/nxgt-mail/refs/tags/@nxgt/mail-ui@0.1.0/packages/mail-ui/previews/components-en.png" width="420" alt="An e-mail using the first Nx components: layout, typography, code, buttons, separator, card with badge, summary data and status, alert, banner, link">
|
|
19
|
+
|
|
20
|
+
The components from `NxLayout` to `NxCode`, in one e-mail
|
|
21
|
+
([its template](https://github.com/softistx/nxgt-mail/blob/develop/packages/mail-ui/test/fixture/emails/welcome.vue)).
|
|
22
|
+
|
|
23
|
+
<img src="https://raw.githubusercontent.com/softistx/nxgt-mail/refs/tags/@nxgt/mail-ui@0.1.0/packages/mail-ui/previews/data-components.png" width="420" alt="An e-mail using the data components: a table with a footer and caption, an empty table, descriptions, list tiles with an avatar and a chip, chips, an avatar group and an avatar">
|
|
24
|
+
|
|
25
|
+
The components from `NxTable` to `NxAvatar`
|
|
26
|
+
([their template](https://github.com/softistx/nxgt-mail/blob/develop/packages/mail-ui/test/fixture/emails/gallery.vue)).
|
|
27
|
+
|
|
28
|
+
<img src="https://raw.githubusercontent.com/softistx/nxgt-mail/refs/tags/@nxgt/mail-ui@0.1.0/packages/mail-ui/previews/sequence-components.png" width="420" alt="An e-mail using the sequence components: three progress bars, three numbered steps joined by a line, a timeline of three toned events, and an empty timeline's text">
|
|
29
|
+
|
|
30
|
+
`NxProgress`, `NxSteps` and `NxTimeline`
|
|
31
|
+
([their template](https://github.com/softistx/nxgt-mail/blob/develop/packages/mail-ui/test/fixture/emails/sequence.vue)).
|
|
32
|
+
|
|
33
|
+
<img src="https://raw.githubusercontent.com/softistx/nxgt-mail/refs/tags/@nxgt/mail-ui@0.1.0/packages/mail-ui/previews/summary-components.png" width="420" alt="An e-mail using the summary components: a hero with an eyebrow and a button, an entity header with an icon, a status badge and a link, three stat cards with toned deltas, a goal card, a ratio card, a compare card, a breakdown card with three bars, and a see-also list of two links">
|
|
34
|
+
|
|
35
|
+
The components from `NxHero` to `NxSeeAlso`
|
|
36
|
+
([their template](https://github.com/softistx/nxgt-mail/blob/develop/packages/mail-ui/test/fixture/emails/summary.vue)).
|
|
37
|
+
|
|
38
|
+
Every component is registered by [`ui()`](plugin.md) and used with no import.
|
|
39
|
+
They keep material-vue's names (with the `Nx` prefix), its props and its
|
|
40
|
+
values, and render with tables and inlined styles, which every mail client
|
|
41
|
+
reads. They need no JavaScript and no web font.
|
|
42
|
+
|
|
43
|
+
## What every component shares
|
|
44
|
+
|
|
45
|
+
- **`class` is merged, not appended.** A class you pass wins over the
|
|
46
|
+
component's own for the same property (Tailwind Merge):
|
|
47
|
+
`<NxButton class="rounded-md">` replaces its `rounded-full`. Other
|
|
48
|
+
attributes (`style`, `id`, `data-*`) go to the same element as `class`.
|
|
49
|
+
- **Block components end with space below.** `NxTypography`, `NxAlert`,
|
|
50
|
+
`NxBanner`, `NxCard`, `NxCode`, `NxSummaryData`, `NxTable`,
|
|
51
|
+
`NxDescription`, `NxAvatarGroup`, `NxProgress`, `NxSteps`, `NxTimeline`,
|
|
52
|
+
`NxHero`, `NxEntityHeader`, the metric cards (`NxStatCard` to
|
|
53
|
+
`NxBreakdownCard`) and `NxSeeAlso` carry `mb-4`, `NxListTile` `mb-2`. On
|
|
54
|
+
`NxTypography`, `NxSummaryData`, `NxTable`, `NxDescription`,
|
|
55
|
+
`NxAvatarGroup`, `NxProgress`, `NxSteps`, `NxTimeline` and `NxSeeAlso`,
|
|
56
|
+
`class="mb-0"` removes it. `NxAlert`, `NxBanner`, `NxCard`, `NxCode`,
|
|
57
|
+
`NxListTile`, `NxHero`, `NxEntityHeader` and the metric cards put their
|
|
58
|
+
`class` on the box inside, and keep their 16px below: to change it, replace
|
|
59
|
+
the component with your own
|
|
60
|
+
(see [Replacing a component](plugin.md#replacing-a-component)).
|
|
61
|
+
- **Vertical space is Maizzle's `<Spacer>`**, which Outlook respects:
|
|
62
|
+
|
|
63
|
+
```vue
|
|
64
|
+
<Spacer height="24px" />
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
- **Icons are slots.** An e-mail has no icon font: pass an `<img>` with an
|
|
68
|
+
absolute URL, or a character, where a component has an `icon` slot.
|
|
69
|
+
- **A placeholder passes through.** `:href="placeholder('link')"` or
|
|
70
|
+
`{{ placeholder('code') }}` from `@nxgt/mail-i18n` is written as is; no
|
|
71
|
+
component branches on a value only known at send time.
|
|
72
|
+
|
|
73
|
+
## Summary
|
|
74
|
+
|
|
75
|
+
| Component | Mirrors | Props (default) | Slots |
|
|
76
|
+
| --- | --- | --- | --- |
|
|
77
|
+
| [`NxLayout`](#nxlayout) | the app shell | `lang`, `preheader`, `width` (`600`) | default, `footer` |
|
|
78
|
+
| [`NxTypography`](#nxtypography) | `Typography` | `variant` (`'normal'`), `as` | default |
|
|
79
|
+
| [`NxButton`](#nxbutton) | `Button` | `href` (required), `variant` (`'filled'`), `color` (`'primary'`), `size` (`'default'`), `align` | default |
|
|
80
|
+
| [`NxLink`](#nxlink) | a text link | `href` (required) | default |
|
|
81
|
+
| [`NxSeparator`](#nxseparator) | `Separator` | — | — |
|
|
82
|
+
| [`NxCard`](#nxcard-and-its-parts) | `Card` | — | default: its parts |
|
|
83
|
+
| `NxCardHeader` | `CardHeader` | `title`, `description` | default, `action` |
|
|
84
|
+
| `NxCardTitle`, `NxCardDescription` | `CardTitle`, `CardDescription` | — | default |
|
|
85
|
+
| `NxCardContent`, `NxCardFooter` | `CardContent`, `CardFooter` | — | default |
|
|
86
|
+
| [`NxBadge`](#nxbadge) | `Badge` | `variant` (`'default'`) | default |
|
|
87
|
+
| [`NxAlert`](#nxalert) | `Alert` | `variant` (`'primary'`), `title`, `description` | default, `icon`, `title`, `description` |
|
|
88
|
+
| [`NxBanner`](#nxbanner) | `Banner` | `tone` (`'info'`), `title`, `description` | default, `icon`, `action` |
|
|
89
|
+
| [`NxStatusIndicator`](#nxstatusindicator) | `StatusIndicator` | `tone` (`'neutral'`) | default |
|
|
90
|
+
| [`NxSummaryData`](#nxsummarydata) | `SummaryData` | `data` (`[]`), `inline` (`false`), `showEmpty` (`false`) | — |
|
|
91
|
+
| [`NxCode`](#nxcode) | — (e-mail's own) | — | default |
|
|
92
|
+
| [`NxTable`](#nxtable-and-its-parts) | `Table` | — | default: its parts |
|
|
93
|
+
| `NxTableHeader`, `NxTableBody`, `NxTableFooter` | `TableHeader`, `TableBody`, `TableFooter` | — | default: `NxTableRow`s |
|
|
94
|
+
| `NxTableRow`, `NxTableHead`, `NxTableCell`, `NxTableCaption` | `TableRow`, `TableHead`, `TableCell`, `TableCaption` | — | default |
|
|
95
|
+
| `NxTableEmpty` | `TableEmpty` | `colspan` (`1`) | default |
|
|
96
|
+
| [`NxDescription`](#nxdescription) | `Description` | `label` (required), `value` | default |
|
|
97
|
+
| [`NxListTile`](#nxlisttile) | `ListTile` | `title` (required), `subtitle`, `href`, `selected` (`false`), `disabled` (`false`), `size` (`'md'`) | `leading`, `trailing` |
|
|
98
|
+
| [`NxChip`](#nxchip) | `Chip` | `label`, `variant` (`'outlined'`), `color` (`'primary'`), `active` (`false`) | default, `avatar`, `leading`, `trailing` |
|
|
99
|
+
| [`NxAvatar`](#nxavatar-and-nxavatargroup) | `Avatar` | `size` (`32`, or its group's) | default: `NxAvatarImage` or `NxAvatarFallback` |
|
|
100
|
+
| `NxAvatarImage` | `AvatarImage` | `src` (required), `alt` | — |
|
|
101
|
+
| `NxAvatarFallback` | `AvatarFallback` | — | default |
|
|
102
|
+
| `NxAvatarGroup` | `AvatarGroup` | `max`, `size` (`'md'`) | default: `NxAvatar`s |
|
|
103
|
+
| [`NxProgress`](#nxprogress) | `Progress` | `modelValue` (`0`), `max` (`100`), `height` (`8`) | — |
|
|
104
|
+
| [`NxSteps`](#nxsteps-and-nxstepsitem) | `Steps` | — | default: `NxStepsItem`s |
|
|
105
|
+
| `NxStepsItem` | `StepsItem` | `title`, `index` (its place), `last` (set by `NxSteps`) | default, `index` |
|
|
106
|
+
| [`NxTimeline`](#nxtimeline) | `Timeline` | `items` (required), `empty` (a shared message) | — |
|
|
107
|
+
| [`NxHero`](#nxhero) | `Hero` | `title` (required), `eyebrow`, `description` | default, `actions` |
|
|
108
|
+
| [`NxEntityHeader`](#nxentityheader) | `EntityHeader` | `title` (required), `metadata` (`[]`) | default, `icon`, `status`, `actions` |
|
|
109
|
+
| [`NxStatCard`](#nxstatcard) | `StatCard` | `label` (required), `value`, `hint`, `delta`, `deltaTone` (by the delta's sign) | `icon` |
|
|
110
|
+
| [`NxGoalCard`](#nxgoalcard) | `GoalCard` | `label`, `value`, `target` (required), `unit` | — |
|
|
111
|
+
| [`NxRatioCard`](#nxratiocard) | `RatioCard` | `label`, `left`, `right`, `percent` (required) | — |
|
|
112
|
+
| [`NxCompareCard`](#nxcomparecard) | `CompareCard` | `label`, `current`, `previous` (required), `delta` | — |
|
|
113
|
+
| [`NxBreakdownCard`](#nxbreakdowncard) | `BreakdownCard` | `label`, `items` (required) | — |
|
|
114
|
+
| [`NxSeeAlso`](#nxseealso) | `SeeAlso` | `items` (required), `label` (a shared message) | — |
|
|
115
|
+
|
|
116
|
+
## NxLayout
|
|
117
|
+
|
|
118
|
+
The page of an e-mail: the brand's logo or name at the top, the content on a
|
|
119
|
+
card, and a footer. Every other component goes inside it: it holds the theme
|
|
120
|
+
in its `<style>`.
|
|
121
|
+
|
|
122
|
+
```vue
|
|
123
|
+
<template>
|
|
124
|
+
<NxLayout preheader="Your account is ready" :width="560">
|
|
125
|
+
<NxTypography>Welcome aboard.</NxTypography>
|
|
126
|
+
<template #footer>
|
|
127
|
+
<p class="m-0 mb-2">You are receiving this because you signed up.</p>
|
|
128
|
+
</template>
|
|
129
|
+
</NxLayout>
|
|
130
|
+
</template>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
| Prop | Type | Default | Effect |
|
|
134
|
+
| --- | --- | --- | --- |
|
|
135
|
+
| `lang` | `string` | the template's `locale` with `@nxgt/mail-i18n`, else `'en'` | `<html lang>` |
|
|
136
|
+
| `preheader` | `string` | none | The text a client shows beside the subject, before the e-mail is opened (Maizzle's `<Preheader>`) |
|
|
137
|
+
| `width` | `number` | `600` | The card's maximum width, in pixels — and the fixed width of the table Outlook on Windows draws, since it ignores a maximum width |
|
|
138
|
+
|
|
139
|
+
| Slot | Default content |
|
|
140
|
+
| --- | --- |
|
|
141
|
+
| default | — the e-mail's content, on the card |
|
|
142
|
+
| `footer` | With `@nxgt/mail-i18n` listed: `t('common.footer.why', { brand: brand.name })`, as `You received this e-mail because you have an account with Acme.` Without it: nothing |
|
|
143
|
+
|
|
144
|
+
Under the footer slot, the brand's name is always written, linked to
|
|
145
|
+
`brand.url` when there is one.
|
|
146
|
+
|
|
147
|
+
The page behind the card is `paper` (5% of the primary colour over the
|
|
148
|
+
background), the card `card` with a `border` line and `rounded-xl` corners.
|
|
149
|
+
The layout declares `<meta name="color-scheme" content="light">`: there is no
|
|
150
|
+
dark version — see [The theme](theme.md#light-only).
|
|
151
|
+
|
|
152
|
+
It is built on Maizzle's `<Html>`, `<Head>`, `<Body>`, `<Preheader>` and
|
|
153
|
+
`<Container>`, so Maizzle's Outlook and client resets apply, and the
|
|
154
|
+
`<Container>` writes the fixed-width table that holds the card at `width` in
|
|
155
|
+
Outlook.
|
|
156
|
+
|
|
157
|
+
## NxTypography
|
|
158
|
+
|
|
159
|
+
Text, on the tag an e-mail reads it as.
|
|
160
|
+
|
|
161
|
+
```vue
|
|
162
|
+
<template>
|
|
163
|
+
<NxTypography variant="headline-small">Reset your password</NxTypography>
|
|
164
|
+
<NxTypography>Someone asked to reset the password of your account.</NxTypography>
|
|
165
|
+
<NxTypography variant="caption">If it was not you, ignore this e-mail.</NxTypography>
|
|
166
|
+
</template>
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
| Prop | Type | Default | Effect |
|
|
170
|
+
| --- | --- | --- | --- |
|
|
171
|
+
| `variant` | see below | `'normal'` | Size and weight |
|
|
172
|
+
| `as` | `string` | by `variant` | The tag: `h1`, `p`, `span`… |
|
|
173
|
+
|
|
174
|
+
| `variant` | Style | Tag |
|
|
175
|
+
| --- | --- | --- |
|
|
176
|
+
| `normal`, `body-medium` | `text-sm` | `p` |
|
|
177
|
+
| `body-small` | `text-xs` | `p` |
|
|
178
|
+
| `caption` | `text-[10px]`, muted | `p` |
|
|
179
|
+
| `headline-large` | `text-4xl`, semibold | `h1` |
|
|
180
|
+
| `headline-medium` | `text-3xl`, semibold | `h1` |
|
|
181
|
+
| `headline-small` | `text-2xl`, semibold | `h1` |
|
|
182
|
+
| `title-large` | `text-xl`, semibold | `h2` |
|
|
183
|
+
| `title-medium` | `text-lg`, semibold | `h3` |
|
|
184
|
+
| `title-small` | `text-base`, semibold | `h4` |
|
|
185
|
+
|
|
186
|
+
All are `text-foreground` with `m-0 mb-4`, but `caption`, which is
|
|
187
|
+
`text-muted-foreground`.
|
|
188
|
+
|
|
189
|
+
## NxButton
|
|
190
|
+
|
|
191
|
+
material-vue's `Button`, as a link: its variants, colours and sizes, on
|
|
192
|
+
Maizzle's `<Button>`, which pads it for Outlook.
|
|
193
|
+
|
|
194
|
+
```vue
|
|
195
|
+
<template>
|
|
196
|
+
<NxButton :href="placeholder('link')">Confirm my address</NxButton>
|
|
197
|
+
<NxButton href="https://acme.example/billing" variant="tonal" size="sm">See my invoice</NxButton>
|
|
198
|
+
<NxButton href="https://acme.example/account/delete" variant="outlined" color="error">Delete my account</NxButton>
|
|
199
|
+
</template>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
| Prop | Type | Default | Effect |
|
|
203
|
+
| --- | --- | --- | --- |
|
|
204
|
+
| `href` | `string` | — (required) | Where it goes. A placeholder is fine |
|
|
205
|
+
| `variant` | `'filled' \| 'tonal' \| 'outlined' \| 'ghost' \| 'link'` | `'filled'` | See below |
|
|
206
|
+
| `color` | `'primary' \| 'secondary' \| 'info' \| 'success' \| 'warning' \| 'error' \| 'default'` | `'primary'` | `default` is the foreground colour |
|
|
207
|
+
| `size` | `'xs' \| 'sm' \| 'default' \| 'lg'` | `'default'` | Padding and text size |
|
|
208
|
+
| `align` | `'left' \| 'center' \| 'right'` | none | Aligns the button in its row |
|
|
209
|
+
|
|
210
|
+
| `variant` | Looks like |
|
|
211
|
+
| --- | --- |
|
|
212
|
+
| `filled` | The colour, with its `-foreground` text (`default`: foreground on background) |
|
|
213
|
+
| `tonal` | The colour at 15% over the background, with the colour as text |
|
|
214
|
+
| `outlined` | Transparent, a 1px border of the colour at 50%, the colour as text |
|
|
215
|
+
| `ghost` | Transparent, foreground text; `color` is ignored |
|
|
216
|
+
| `link` | No padding, no background: the colour as text, not underlined |
|
|
217
|
+
|
|
218
|
+
| `size` | Padding | Text |
|
|
219
|
+
| --- | --- | --- |
|
|
220
|
+
| `xs` | `px-2 py-1` | `text-xs` |
|
|
221
|
+
| `sm` | `px-3 py-2` | `text-sm` |
|
|
222
|
+
| `default` | `px-4 py-2.5` | `text-sm` |
|
|
223
|
+
| `lg` | `px-6 py-3` | `text-sm` |
|
|
224
|
+
|
|
225
|
+
Every variant but `link` is `rounded-full`, as in material-vue. The Outlook
|
|
226
|
+
padding for each size is set for you.
|
|
227
|
+
|
|
228
|
+
## NxLink
|
|
229
|
+
|
|
230
|
+
A link in running text, in material-vue's link colour (`info`), underlined —
|
|
231
|
+
a phone has no hover to reveal it.
|
|
232
|
+
|
|
233
|
+
```vue
|
|
234
|
+
<template>
|
|
235
|
+
<NxTypography>
|
|
236
|
+
Questions? Read the <NxLink href="https://acme.example/help">help centre</NxLink>.
|
|
237
|
+
</NxTypography>
|
|
238
|
+
</template>
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
| Prop | Type | Default | Effect |
|
|
242
|
+
| --- | --- | --- | --- |
|
|
243
|
+
| `href` | `string` | — (required) | Where it goes |
|
|
244
|
+
|
|
245
|
+
## NxSeparator
|
|
246
|
+
|
|
247
|
+
material-vue's horizontal `Separator`: a 1px line in the `border` colour,
|
|
248
|
+
with `py-6` around it. The space is padding, which Outlook keeps where it
|
|
249
|
+
drops a margin.
|
|
250
|
+
|
|
251
|
+
```vue
|
|
252
|
+
<template>
|
|
253
|
+
<NxSeparator />
|
|
254
|
+
<NxSeparator class="py-2" />
|
|
255
|
+
</template>
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
No props, no slot.
|
|
259
|
+
|
|
260
|
+
## NxCard and its parts
|
|
261
|
+
|
|
262
|
+
material-vue's `Card`: a bordered, rounded box, `bg-card`. Its parts are its
|
|
263
|
+
rows, so **only its parts go directly inside it**: `NxCardHeader`,
|
|
264
|
+
`NxCardContent`, `NxCardFooter`.
|
|
265
|
+
|
|
266
|
+
```vue
|
|
267
|
+
<template>
|
|
268
|
+
<NxCard>
|
|
269
|
+
<NxCardHeader title="Pro plan" description="Billed monthly">
|
|
270
|
+
<template #action><NxBadge variant="success">Active</NxBadge></template>
|
|
271
|
+
</NxCardHeader>
|
|
272
|
+
<NxCardContent>
|
|
273
|
+
<NxSummaryData :data="[{ label: 'Seats', value: 3 }, { label: 'Next invoice', value: '1 November' }]" />
|
|
274
|
+
</NxCardContent>
|
|
275
|
+
<NxCardFooter>
|
|
276
|
+
<NxButton href="https://acme.example/billing" variant="tonal" size="sm">Manage billing</NxButton>
|
|
277
|
+
</NxCardFooter>
|
|
278
|
+
</NxCard>
|
|
279
|
+
</template>
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
| Part | Props | Slots | Renders |
|
|
283
|
+
| --- | --- | --- | --- |
|
|
284
|
+
| `NxCard` | — | default: its parts | The box, `py-6` |
|
|
285
|
+
| `NxCardHeader` | `title?: string`, `description?: string` | default (under the description), `action` (on the right) | A row, `px-6 pb-6` |
|
|
286
|
+
| `NxCardTitle` | — | default | `<h3>`, `text-base` semibold |
|
|
287
|
+
| `NxCardDescription` | — | default | `<p>`, `text-sm` muted |
|
|
288
|
+
| `NxCardContent` | — | default | A row, `px-6` |
|
|
289
|
+
| `NxCardFooter` | — | default | A row, `px-6 pt-6` |
|
|
290
|
+
|
|
291
|
+
`title` and `description` are shorthands for `NxCardTitle` and
|
|
292
|
+
`NxCardDescription`; use the parts in the default slot when the title needs
|
|
293
|
+
markup:
|
|
294
|
+
|
|
295
|
+
```vue
|
|
296
|
+
<template>
|
|
297
|
+
<NxCardHeader>
|
|
298
|
+
<NxCardTitle>Order <strong>#{{ placeholder('orderNumber') }}</strong></NxCardTitle>
|
|
299
|
+
<NxCardDescription>Shipped today</NxCardDescription>
|
|
300
|
+
</NxCardHeader>
|
|
301
|
+
</template>
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## NxBadge
|
|
305
|
+
|
|
306
|
+
material-vue's `Badge`: a small pill of text, inline.
|
|
307
|
+
|
|
308
|
+
```vue
|
|
309
|
+
<template>
|
|
310
|
+
<NxTypography>Your plan is <NxBadge variant="success">Active</NxBadge></NxTypography>
|
|
311
|
+
</template>
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
| Prop | Type | Default |
|
|
315
|
+
| --- | --- | --- |
|
|
316
|
+
| `variant` | `'default' \| 'secondary' \| 'destructive' \| 'error' \| 'success' \| 'info' \| 'warning' \| 'outline' \| 'outlined'` | `'default'` |
|
|
317
|
+
|
|
318
|
+
`default` is the primary colour. Each colour variant is filled with its
|
|
319
|
+
`-foreground` text; `destructive` is `error` with white text; `outline` and
|
|
320
|
+
`outlined` are a `border` line with foreground text.
|
|
321
|
+
|
|
322
|
+
## NxAlert
|
|
323
|
+
|
|
324
|
+
material-vue's `Alert`: a block tinted at 5% of its colour, with an 8px bar of
|
|
325
|
+
the colour on its left.
|
|
326
|
+
|
|
327
|
+
```vue
|
|
328
|
+
<template>
|
|
329
|
+
<NxAlert variant="warning" title="Heads up" description="The code expires in 15 minutes." />
|
|
330
|
+
<NxAlert variant="error">
|
|
331
|
+
<template #icon><img src="https://acme.example/icons/alert.png" width="16" alt=""></template>
|
|
332
|
+
<template #title>Payment failed</template>
|
|
333
|
+
We could not charge your card ending in {{ placeholder('last4') }}.
|
|
334
|
+
</NxAlert>
|
|
335
|
+
</template>
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
| Prop | Type | Default | Effect |
|
|
339
|
+
| --- | --- | --- | --- |
|
|
340
|
+
| `variant` | `'primary' \| 'secondary' \| 'error' \| 'success' \| 'info' \| 'warning' \| 'foreground'` | `'primary'` | The colour. `foreground` is a foreground bar on the `muted` background |
|
|
341
|
+
| `title` | `string` | none | Bold, first line |
|
|
342
|
+
| `description` | `string` | none | Muted, under the title |
|
|
343
|
+
|
|
344
|
+
| Slot | Replaces |
|
|
345
|
+
| --- | --- |
|
|
346
|
+
| `icon` | Nothing: a 24px column on the left, shown only when given, in the variant's colour (`text-<variant>`), so a character icon takes it |
|
|
347
|
+
| `title`, `description` | The prop of the same name |
|
|
348
|
+
| default | — written under the description |
|
|
349
|
+
|
|
350
|
+
## NxBanner
|
|
351
|
+
|
|
352
|
+
material-vue's `Banner`: a status in a rounded box tinted at 10% of its tone,
|
|
353
|
+
with a border at 40%, and an optional action on the right.
|
|
354
|
+
|
|
355
|
+
```vue
|
|
356
|
+
<template>
|
|
357
|
+
<NxBanner tone="info" title="New sign-in" description="From Firefox on Linux.">
|
|
358
|
+
<template #action>
|
|
359
|
+
<NxLink href="https://acme.example/security">Review</NxLink>
|
|
360
|
+
</template>
|
|
361
|
+
</NxBanner>
|
|
362
|
+
</template>
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
| Prop | Type | Default | Effect |
|
|
366
|
+
| --- | --- | --- | --- |
|
|
367
|
+
| `tone` | `'info' \| 'success' \| 'warning' \| 'error'` | `'info'` | The colour of the box, and of the icon column's text |
|
|
368
|
+
| `title` | `string` | none | Semibold, first line |
|
|
369
|
+
| `description` | `string` | none | Muted, under the title |
|
|
370
|
+
|
|
371
|
+
| Slot | Effect |
|
|
372
|
+
| --- | --- |
|
|
373
|
+
| `icon` | A column on the left, in the tone's colour (a character takes it) |
|
|
374
|
+
| `action` | A column on the right, aligned to the middle |
|
|
375
|
+
| default | Written under the description |
|
|
376
|
+
|
|
377
|
+
## NxStatusIndicator
|
|
378
|
+
|
|
379
|
+
material-vue's `StatusIndicator`: a coloured dot before a label, inline. The
|
|
380
|
+
dot is a character (`●`), which every client sizes, where an empty `<span>`
|
|
381
|
+
would be dropped.
|
|
382
|
+
|
|
383
|
+
```vue
|
|
384
|
+
<template>
|
|
385
|
+
<NxTypography>Invoice: <NxStatusIndicator tone="success">Paid</NxStatusIndicator></NxTypography>
|
|
386
|
+
</template>
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
| Prop | Type | Default | Effect |
|
|
390
|
+
| --- | --- | --- | --- |
|
|
391
|
+
| `tone` | `'neutral' \| 'primary' \| 'success' \| 'info' \| 'warning' \| 'error'` | `'neutral'` | The dot's colour; `neutral` is muted |
|
|
392
|
+
|
|
393
|
+
## NxSummaryData
|
|
394
|
+
|
|
395
|
+
material-vue's `SummaryData`: labels and their values, one row each with a
|
|
396
|
+
line under it, or on one line with `inline`.
|
|
397
|
+
|
|
398
|
+
```vue
|
|
399
|
+
<template>
|
|
400
|
+
<NxSummaryData
|
|
401
|
+
:data="[
|
|
402
|
+
{ label: 'Order', value: placeholder('orderNumber') },
|
|
403
|
+
{ label: 'Items', value: 3 },
|
|
404
|
+
{ label: 'Discount', value: 0 },
|
|
405
|
+
{ label: 'Coupon', value: null },
|
|
406
|
+
]"
|
|
407
|
+
/>
|
|
408
|
+
<NxSummaryData inline :data="[{ label: 'Plan', value: 'Pro' }, { label: 'Seats', value: 3 }]" />
|
|
409
|
+
</template>
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
| Prop | Type | Default | Effect |
|
|
413
|
+
| --- | --- | --- | --- |
|
|
414
|
+
| `data` | `{ label: string; value?: string \| number \| null }[]` | `[]` | The rows, in order |
|
|
415
|
+
| `inline` | `boolean` | `false` | One paragraph, `Plan: Pro Seats: 3`, rather than a table |
|
|
416
|
+
| `showEmpty` | `boolean` | `false` | Keeps a row whose value is empty |
|
|
417
|
+
|
|
418
|
+
A row without a value (`undefined`, `null`, `''`) is left out, unless
|
|
419
|
+
`showEmpty`; `0` is a value and stays. Above, `Coupon` is left out and
|
|
420
|
+
`Discount` shows `0`. A placeholder is a value: its row is always written.
|
|
421
|
+
|
|
422
|
+
## NxCode
|
|
423
|
+
|
|
424
|
+
A code the reader types in, such as a one-time code: large, spaced, in
|
|
425
|
+
monospace, centred on the `muted` background. It has no material-vue
|
|
426
|
+
counterpart.
|
|
427
|
+
|
|
428
|
+
```vue
|
|
429
|
+
<template>
|
|
430
|
+
<NxCode>{{ placeholder('code') }}</NxCode>
|
|
431
|
+
</template>
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
No props; the code is the default slot.
|
|
435
|
+
|
|
436
|
+
## NxTable and its parts
|
|
437
|
+
|
|
438
|
+
material-vue's `Table`: rows ruled by a line under each, a header in
|
|
439
|
+
`font-medium`, and a footer on the `muted` background with a line above it.
|
|
440
|
+
The parts are the HTML table's, so they nest as it does.
|
|
441
|
+
|
|
442
|
+
```vue
|
|
443
|
+
<template>
|
|
444
|
+
<NxTable>
|
|
445
|
+
<NxTableCaption>Prices include VAT.</NxTableCaption>
|
|
446
|
+
<NxTableHeader>
|
|
447
|
+
<NxTableRow>
|
|
448
|
+
<NxTableHead>Item</NxTableHead>
|
|
449
|
+
<NxTableHead class="text-right">Amount</NxTableHead>
|
|
450
|
+
</NxTableRow>
|
|
451
|
+
</NxTableHeader>
|
|
452
|
+
<NxTableBody>
|
|
453
|
+
<NxTableRow>
|
|
454
|
+
<NxTableCell>Pro plan</NxTableCell>
|
|
455
|
+
<NxTableCell class="text-right">{{ placeholder('amount') }}</NxTableCell>
|
|
456
|
+
</NxTableRow>
|
|
457
|
+
</NxTableBody>
|
|
458
|
+
<NxTableFooter>
|
|
459
|
+
<NxTableRow>
|
|
460
|
+
<NxTableCell>Total</NxTableCell>
|
|
461
|
+
<NxTableCell class="text-right">{{ placeholder('total') }}</NxTableCell>
|
|
462
|
+
</NxTableRow>
|
|
463
|
+
</NxTableFooter>
|
|
464
|
+
</NxTable>
|
|
465
|
+
</template>
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
| Part | Props | Renders |
|
|
469
|
+
| --- | --- | --- |
|
|
470
|
+
| `NxTable` | — | `<table role="table">`, full width, `text-sm` |
|
|
471
|
+
| `NxTableHeader`, `NxTableBody`, `NxTableFooter` | — | `<thead>`, `<tbody>`, `<tfoot>` |
|
|
472
|
+
| `NxTableRow` | — | `<tr>` |
|
|
473
|
+
| `NxTableHead` | — | `<th>`, `h-10 px-2`, left-aligned, `font-medium`, a line under it; in `NxTableFooter`, a line above it instead, `bg-muted` |
|
|
474
|
+
| `NxTableCell` | — | `<td>`, `p-2`, a line under it; in `NxTableFooter`, a line above it instead, `bg-muted`, `font-medium` |
|
|
475
|
+
| `NxTableCaption` | — | `<caption>`, under the table, `text-sm` muted |
|
|
476
|
+
| `NxTableEmpty` | `colspan?: number` (`1`) | A row of one cell across `colspan` columns, centred, `py-10` — the text for a table with no rows. Its `class` goes on the cell |
|
|
477
|
+
|
|
478
|
+
A cell's text wraps, where material-vue's does not: a long value would
|
|
479
|
+
otherwise widen the e-mail past a phone's screen. `NxTableHead` keeps
|
|
480
|
+
`whitespace-nowrap`. Align a column with `class="text-right"` on its head and
|
|
481
|
+
its cells.
|
|
482
|
+
|
|
483
|
+
`NxTable` carries `role="table"`: Maizzle marks every other table
|
|
484
|
+
`role="none"`, a layout, and a screen reader then reads its rows as plain text.
|
|
485
|
+
The caption is placed under the table by `align="bottom"` as well as
|
|
486
|
+
`caption-side`, which some clients drop.
|
|
487
|
+
|
|
488
|
+
## NxDescription
|
|
489
|
+
|
|
490
|
+
material-vue's `Description`: a label in `font-semibold`, and its value under
|
|
491
|
+
it, muted.
|
|
492
|
+
|
|
493
|
+
```vue
|
|
494
|
+
<template>
|
|
495
|
+
<NxDescription label="Billing period" value="September 2026" />
|
|
496
|
+
<NxDescription label="Credits left" :value="0" />
|
|
497
|
+
<NxDescription label="Coupon" :value="null" />
|
|
498
|
+
</template>
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
| Prop | Type | Default | Effect |
|
|
502
|
+
| --- | --- | --- | --- |
|
|
503
|
+
| `label` | `string` | required | The first line |
|
|
504
|
+
| `value` | `string \| number \| null` | none | The second line |
|
|
505
|
+
|
|
506
|
+
Like a row of `NxSummaryData`, a description without a value (`undefined`,
|
|
507
|
+
`null`, `''`) is left out entirely, and `0` is a value: above, `Coupon` is
|
|
508
|
+
not written. The default slot is written under the value.
|
|
509
|
+
|
|
510
|
+
## NxListTile
|
|
511
|
+
|
|
512
|
+
material-vue's `ListTile`: a title, a subtitle under it, and `leading` and
|
|
513
|
+
`trailing` slots on its sides, on a rounded box tinted at 5% of the primary
|
|
514
|
+
colour.
|
|
515
|
+
|
|
516
|
+
```vue
|
|
517
|
+
<template>
|
|
518
|
+
<NxListTile title="Ada Lovelace" subtitle="Owner" href="https://acme.example/team/ada" selected>
|
|
519
|
+
<template #leading>
|
|
520
|
+
<NxAvatar><NxAvatarFallback>AL</NxAvatarFallback></NxAvatar>
|
|
521
|
+
</template>
|
|
522
|
+
<template #trailing><NxChip variant="tonal" color="success">Active</NxChip></template>
|
|
523
|
+
</NxListTile>
|
|
524
|
+
<NxListTile title="Grace Hopper" subtitle="Invited" size="sm" disabled />
|
|
525
|
+
</template>
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
| Prop | Type | Default | Effect |
|
|
529
|
+
| --- | --- | --- | --- |
|
|
530
|
+
| `title` | `string` | required | `text-sm` semibold (`md`) or `text-[13px]` medium (`sm`) |
|
|
531
|
+
| `subtitle` | `string` | none | Muted, under the title |
|
|
532
|
+
| `href` | `string` | none | Makes the title a link |
|
|
533
|
+
| `selected` | `boolean` | `false` | A border at 40% of the primary colour, on a 15% tint (`md`) or a 10% one (`sm`) |
|
|
534
|
+
| `disabled` | `boolean` | `false` | A muted title, and no link even with `href` |
|
|
535
|
+
| `size` | `'sm' \| 'md'` | `'md'` | `md` is `px-4 py-2` on the tint; `sm` is `px-2 py-1.5` with no tint until selected |
|
|
536
|
+
|
|
537
|
+
material-vue's ring is a border here: a mail client draws no `box-shadow`.
|
|
538
|
+
Only the title is a link, not the whole tile: a mail client cannot make a
|
|
539
|
+
table cell clickable.
|
|
540
|
+
|
|
541
|
+
## NxChip
|
|
542
|
+
|
|
543
|
+
material-vue's `Chip`, static: a rounded label, with the colours of
|
|
544
|
+
[`NxButton`](#nxbutton).
|
|
545
|
+
|
|
546
|
+
```vue
|
|
547
|
+
<template>
|
|
548
|
+
<NxTypography>
|
|
549
|
+
<NxChip label="Design" />
|
|
550
|
+
<NxChip label="Selected" active />
|
|
551
|
+
<NxChip variant="tonal" color="success">Paid</NxChip>
|
|
552
|
+
</NxTypography>
|
|
553
|
+
</template>
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
| Prop | Type | Default | Effect |
|
|
557
|
+
| --- | --- | --- | --- |
|
|
558
|
+
| `label` | `string` | none | The text, when the default slot is empty |
|
|
559
|
+
| `variant` | `'filled' \| 'tonal' \| 'outlined' \| 'ghost' \| 'link'` | `'outlined'` | As `NxButton`'s |
|
|
560
|
+
| `color` | `'default' \| 'primary' \| 'secondary' \| 'error' \| 'success' \| 'info' \| 'warning'` | `'primary'` | As `NxButton`'s |
|
|
561
|
+
| `active` | `boolean` | `false` | Makes it `filled`, whatever its `variant` |
|
|
562
|
+
|
|
563
|
+
| Slot | Effect |
|
|
564
|
+
| --- | --- |
|
|
565
|
+
| default | The text, in place of `label` |
|
|
566
|
+
| `avatar` | Before the text; an `NxAvatar` in it is 20px |
|
|
567
|
+
| `leading` | Before the text, when there is no `avatar` |
|
|
568
|
+
| `trailing` | After the text |
|
|
569
|
+
|
|
570
|
+
An e-mail runs no script: a chip has no dismiss button and no click. It is
|
|
571
|
+
inline; put several in an `NxTypography` to give them space below. Unlike
|
|
572
|
+
material-vue's, the avatar is not pulled into the chip's padding: Gmail drops
|
|
573
|
+
a negative margin.
|
|
574
|
+
|
|
575
|
+
## NxAvatar and NxAvatarGroup
|
|
576
|
+
|
|
577
|
+
material-vue's `Avatar`: a round picture, or initials on the `muted`
|
|
578
|
+
background when there is none.
|
|
579
|
+
|
|
580
|
+
```vue
|
|
581
|
+
<template>
|
|
582
|
+
<NxAvatar :size="48"><NxAvatarImage src="https://acme.example/ada.png" alt="Ada" /></NxAvatar>
|
|
583
|
+
<NxAvatarGroup :max="3" size="lg">
|
|
584
|
+
<NxAvatar><NxAvatarImage src="https://acme.example/ada.png" alt="Ada" /></NxAvatar>
|
|
585
|
+
<NxAvatar><NxAvatarFallback>GH</NxAvatarFallback></NxAvatar>
|
|
586
|
+
<NxAvatar><NxAvatarFallback>AT</NxAvatarFallback></NxAvatar>
|
|
587
|
+
<NxAvatar><NxAvatarFallback>KJ</NxAvatarFallback></NxAvatar>
|
|
588
|
+
</NxAvatarGroup>
|
|
589
|
+
</template>
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
| Part | Props | Renders |
|
|
593
|
+
| --- | --- | --- |
|
|
594
|
+
| `NxAvatar` | `size?: number`: pixels; its group's, else `32` | A round, inline box of that size |
|
|
595
|
+
| `NxAvatarImage` | `src: string` (required), `alt?: string` (`''`, a decorative picture) | The `<img>`, with `width` and `height` set to the avatar's size |
|
|
596
|
+
| `NxAvatarFallback` | — | Its text (initials), `text-[12px]`, centred on `bg-muted` |
|
|
597
|
+
| `NxAvatarGroup` | `max?: number`, `size?: 'sm' \| 'md' \| 'lg'` (`'md'`) | Its avatars in a row, each ringed with the background, sized 24, 32 or 40px; past `max`, one more reading `+N`. Without `max`, or with one under 1, all of them |
|
|
598
|
+
|
|
599
|
+
Choose `NxAvatarImage` or `NxAvatarFallback`: an e-mail cannot fall back on a
|
|
600
|
+
picture that fails to load, so there is no switching between them. Give the
|
|
601
|
+
image an absolute URL, and a square picture. A group's avatars sit side by
|
|
602
|
+
side, 4px apart, rather than overlapping as in material-vue: Gmail drops the
|
|
603
|
+
negative margin that stacks them. Avatars from a `v-for` count one by one.
|
|
604
|
+
|
|
605
|
+
The `+N` is labelled for a screen reader with the shared message
|
|
606
|
+
`common.avatarGroup.more` (`2 more`, `2 autres`) when `@nxgt/mail-i18n` is
|
|
607
|
+
listed, and in English otherwise; see [Shared messages](messages.md).
|
|
608
|
+
|
|
609
|
+
Outlook on Windows ignores the width and height of an inline box: there,
|
|
610
|
+
`NxAvatarFallback`'s initials show on a grey strip rather than in a circle. An
|
|
611
|
+
`NxAvatarImage` keeps its size everywhere, from its `width` and `height`.
|
|
612
|
+
|
|
613
|
+
## NxProgress
|
|
614
|
+
|
|
615
|
+
material-vue's `Progress`, still: a bar filled to `modelValue` out of `max`,
|
|
616
|
+
on a rounded track at 20% of the primary colour.
|
|
617
|
+
|
|
618
|
+
```vue
|
|
619
|
+
<template>
|
|
620
|
+
<NxTypography>2 of 3 steps done</NxTypography>
|
|
621
|
+
<NxProgress :model-value="2" :max="3" />
|
|
622
|
+
</template>
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
| Prop | Type | Default | Effect |
|
|
626
|
+
| --- | --- | --- | --- |
|
|
627
|
+
| `modelValue` | `number` | `0` | How much is done — material-vue's name, passed one way: an e-mail has no `v-model` |
|
|
628
|
+
| `max` | `number` | `100` | What `modelValue` is out of |
|
|
629
|
+
| `height` | `number` | `8` | The bar's height in pixels, above 0; an addition, where material-vue sets a class such as `h-1.5` |
|
|
630
|
+
|
|
631
|
+
The fill is `modelValue / max`, rounded to a whole percent and kept between 0 and
|
|
632
|
+
100. It is a table cell of that width, which every client draws: material-vue's
|
|
633
|
+
`transform` and pulse are not. The table carries `role="progressbar"` and the
|
|
634
|
+
`aria-value*` attributes.
|
|
635
|
+
|
|
636
|
+
The share is computed when the e-mail is built, so each prop is a number,
|
|
637
|
+
never a placeholder: one that is not
|
|
638
|
+
[fails the build](../troubleshooting.md#nxprogress-modelvalue-must-be-a-number-known-when-the-e-mail-is-built--a-placeholder-is-filled-only-when-it-is-sent).
|
|
639
|
+
A share that differs per recipient is written as text. The bar is left out of
|
|
640
|
+
the plain-text version, where it says nothing.
|
|
641
|
+
|
|
642
|
+
## NxSteps and NxStepsItem
|
|
643
|
+
|
|
644
|
+
material-vue's `Steps`: numbered circles one under the other, each with a
|
|
645
|
+
title and its text, joined by a line.
|
|
646
|
+
|
|
647
|
+
```vue
|
|
648
|
+
<template>
|
|
649
|
+
<NxSteps>
|
|
650
|
+
<NxStepsItem title="Create your account">Done on 1 September.</NxStepsItem>
|
|
651
|
+
<NxStepsItem title="Invite your team">Add the people who work with you.</NxStepsItem>
|
|
652
|
+
<NxStepsItem title="Connect your bank" />
|
|
653
|
+
</NxSteps>
|
|
654
|
+
</template>
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
| Part | Props | Slots | Renders |
|
|
658
|
+
| --- | --- | --- | --- |
|
|
659
|
+
| `NxSteps` | — | default: `NxStepsItem`s | A table of its items, `mb-4`; numbers them 1, 2, 3… |
|
|
660
|
+
| `NxStepsItem` | `title?: string`, `index?: number` (its place in `NxSteps`), `last?: boolean` (set by `NxSteps`) | default (the text), `index` (in the circle, in place of the number) | A 32px circle, `border-primary-25`, the number in `text-primary`; the title `text-base` semibold; the text `text-sm` muted; a line down to the next item |
|
|
661
|
+
|
|
662
|
+
**Only `NxStepsItem`s go directly inside `NxSteps`, and an `NxStepsItem` only
|
|
663
|
+
inside `NxSteps`**, as a `v-for` or one by one: `NxSteps` numbers them and
|
|
664
|
+
tells each whether it is the `last`, which draws no line under it. An item is
|
|
665
|
+
table rows: alone, it is broken HTML with an empty circle.
|
|
666
|
+
An `index` you give wins over the item's place. The item's `class` goes on its
|
|
667
|
+
text's cell.
|
|
668
|
+
|
|
669
|
+
The line is the border of a cell in the same row as the text, so it runs as far
|
|
670
|
+
down as the text in every client; Outlook on Windows draws the circle square.
|
|
671
|
+
|
|
672
|
+
## NxTimeline
|
|
673
|
+
|
|
674
|
+
material-vue's `Timeline`: events one under the other, each a toned marker on
|
|
675
|
+
a line, its title, a time on the right, and a description.
|
|
676
|
+
|
|
677
|
+
```vue
|
|
678
|
+
<template>
|
|
679
|
+
<NxTimeline
|
|
680
|
+
:items="[
|
|
681
|
+
{ id: 'sign-in', title: 'Signed in', description: 'Firefox on Linux', timestampLabel: placeholder('time'), tone: 'success' },
|
|
682
|
+
{ id: 'password', title: 'Password changed', tone: 'warning' },
|
|
683
|
+
{ id: 'created', title: 'Account created' },
|
|
684
|
+
]"
|
|
685
|
+
/>
|
|
686
|
+
<NxTimeline :items="[]" empty="Nothing this week" />
|
|
687
|
+
</template>
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
| Prop | Type | Default | Effect |
|
|
691
|
+
| --- | --- | --- | --- |
|
|
692
|
+
| `items` | `{ id: string; title: string; description?: string; timestampLabel?: string; tone?: TimelineTone }[]` | required | The events, in order; each `id` unique |
|
|
693
|
+
| `empty` | `string` | the shared message `common.timeline.empty` (`No activity yet`); without `@nxgt/mail-i18n`, `No activity yet` in every language | The text shown, centred and muted, when `items` is empty |
|
|
694
|
+
|
|
695
|
+
`TimelineTone` is `'default' | 'primary' | 'success' | 'info' | 'warning' | 'error'`:
|
|
696
|
+
the marker's border at 40% of the tone, its ground at 15%, and its dot in the
|
|
697
|
+
tone; `default` is `border`, `muted` and muted text.
|
|
698
|
+
|
|
699
|
+
An e-mail is built before it is sent, so there is no `timestamp` turned into
|
|
700
|
+
"2 hours ago" as in material-vue: that would be the time of the build. Write
|
|
701
|
+
the time as `timestampLabel`, most often a placeholder filled at send time.
|
|
702
|
+
There is no `loading` either: an e-mail does not load.
|
|
703
|
+
|
|
704
|
+
## NxHero
|
|
705
|
+
|
|
706
|
+
material-vue's `Hero`: an eyebrow, a large title, a description and actions,
|
|
707
|
+
in a rounded, bordered box. Its gradient and blurred shapes are a plain ground
|
|
708
|
+
at 5% of the primary colour: a mail client draws neither reliably.
|
|
709
|
+
|
|
710
|
+
```vue
|
|
711
|
+
<template>
|
|
712
|
+
<NxHero eyebrow="September" title="Your month at Acme" description="What your team did, and what is next.">
|
|
713
|
+
<template #actions><NxButton href="https://acme.example/report">Open the report</NxButton></template>
|
|
714
|
+
</NxHero>
|
|
715
|
+
</template>
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
| Prop | Type | Default | Effect |
|
|
719
|
+
| --- | --- | --- | --- |
|
|
720
|
+
| `title` | `string` | required | `<h1>`, `text-3xl` semibold |
|
|
721
|
+
| `eyebrow` | `string` | none | Above the title: `text-xs`, semibold, uppercase, in `text-primary` |
|
|
722
|
+
| `description` | `string` | none | Under the title, `text-base` muted |
|
|
723
|
+
|
|
724
|
+
| Slot | Effect |
|
|
725
|
+
| --- | --- |
|
|
726
|
+
| `actions` | Under the description, `mt-6`: an `NxButton` or two |
|
|
727
|
+
| default | Under the actions, `mt-8` |
|
|
728
|
+
|
|
729
|
+
The box is `rounded-2xl`, a `border` line, `px-8 py-10`; its `class` goes on
|
|
730
|
+
it.
|
|
731
|
+
|
|
732
|
+
## NxEntityHeader
|
|
733
|
+
|
|
734
|
+
material-vue's `EntityHeader`: what an e-mail is about — an icon, a title with
|
|
735
|
+
its status, its details on one line, and actions on the right. Its shadow is a
|
|
736
|
+
border here.
|
|
737
|
+
|
|
738
|
+
```vue
|
|
739
|
+
<template>
|
|
740
|
+
<NxEntityHeader title="Acme Labs" :metadata="[{ label: 'Plan', value: 'Pro' }, { label: 'Seats', value: 12 }]">
|
|
741
|
+
<template #icon>🏢</template>
|
|
742
|
+
<template #status><NxBadge variant="success">Active</NxBadge></template>
|
|
743
|
+
<template #actions><NxLink href="https://acme.example/settings">Settings</NxLink></template>
|
|
744
|
+
</NxEntityHeader>
|
|
745
|
+
</template>
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
| Prop | Type | Default | Effect |
|
|
749
|
+
| --- | --- | --- | --- |
|
|
750
|
+
| `title` | `string` | required | `<h2>`, `text-lg` semibold |
|
|
751
|
+
| `metadata` | `{ label: string; value?: string \| number \| null }[]` | `[]` | Under the title, as an `inline` [`NxSummaryData`](#nxsummarydata): `Plan: Pro Seats: 12`. A row without a value is left out, as there |
|
|
752
|
+
|
|
753
|
+
| Slot | Effect |
|
|
754
|
+
| --- | --- |
|
|
755
|
+
| `icon` | A column on the left, `text-3xl`: a character or an `<img>` |
|
|
756
|
+
| `status` | After the title, on its line: an `NxBadge` or an `NxStatusIndicator` |
|
|
757
|
+
| `actions` | A column on the right, aligned to the middle |
|
|
758
|
+
| default | Under the title and its details |
|
|
759
|
+
|
|
760
|
+
The box is `rounded`, a `border` line, `bg-background`, `p-4`; its `class`
|
|
761
|
+
goes on it.
|
|
762
|
+
|
|
763
|
+
## The metric cards
|
|
764
|
+
|
|
765
|
+
`NxStatCard`, `NxGoalCard`, `NxRatioCard`, `NxCompareCard` and
|
|
766
|
+
`NxBreakdownCard` are material-vue's metric cards: each is an
|
|
767
|
+
[`NxCard`](#nxcard-and-its-parts) with a `label` at its top, one under the
|
|
768
|
+
other, full width. Their `class` goes on the card's box.
|
|
769
|
+
|
|
770
|
+
They have no `loading` state: an e-mail does not load. The figures they
|
|
771
|
+
**write** — a `value`, a `delta` — may be placeholders; the numbers they
|
|
772
|
+
**draw** as an [`NxProgress`](#nxprogress) — `NxGoalCard`'s `value` and
|
|
773
|
+
`target`, `NxRatioCard`'s `percent`, each `percent` of `NxBreakdownCard` —
|
|
774
|
+
are known when the e-mail is built: a placeholder there
|
|
775
|
+
[fails the build](../troubleshooting.md#nxprogress-modelvalue-must-be-a-number-known-when-the-e-mail-is-built--a-placeholder-is-filled-only-when-it-is-sent).
|
|
776
|
+
|
|
777
|
+
### A delta and its arrow
|
|
778
|
+
|
|
779
|
+
`NxStatCard` and `NxCompareCard` write a `delta` as material-vue's
|
|
780
|
+
`formatStatDelta` does, with an arrow before it that follows its tone, as
|
|
781
|
+
material-vue's `TrendingUp`, `TrendingDown` and `Minus` icons do — a
|
|
782
|
+
character here, since an e-mail has no icon font:
|
|
783
|
+
|
|
784
|
+
| `delta` | Written | Tone (without `deltaTone`) |
|
|
785
|
+
| --- | --- | --- |
|
|
786
|
+
| `12` | `▲ +12` | `up`: `text-success` |
|
|
787
|
+
| `-3` | `▼ -3` | `down`: `text-error` |
|
|
788
|
+
| `0` | `– 0` | `neutral`: muted |
|
|
789
|
+
| `'flat'`, any string | `– flat`, as given | `neutral`: muted |
|
|
790
|
+
|
|
791
|
+
The arrow is `aria-hidden`, and left out of the plain-text version: there,
|
|
792
|
+
`▲ +12` reads `+12`.
|
|
793
|
+
|
|
794
|
+
## NxStatCard
|
|
795
|
+
|
|
796
|
+
material-vue's `StatCard`: a label, a figure, and under it a delta and a hint.
|
|
797
|
+
|
|
798
|
+
```vue
|
|
799
|
+
<template>
|
|
800
|
+
<NxStatCard label="Revenue" value="$12,400" :delta="12" hint="vs last month" />
|
|
801
|
+
<NxStatCard label="Refunds" value="3" :delta="-2" delta-tone="up" />
|
|
802
|
+
<NxStatCard label="Churn" value="0.4%" delta="flat" />
|
|
803
|
+
</template>
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
| Prop | Type | Default | Effect |
|
|
807
|
+
| --- | --- | --- | --- |
|
|
808
|
+
| `label` | `string` | required | The first line, `text-sm` muted |
|
|
809
|
+
| `value` | `string \| number` | none | The figure, `text-2xl` semibold. A placeholder is fine |
|
|
810
|
+
| `delta` | `number \| string` | none | Under the figure, toned, with its arrow — see [A delta and its arrow](#a-delta-and-its-arrow). `''` writes none |
|
|
811
|
+
| `deltaTone` | `'up' \| 'down' \| 'neutral'` | by the sign; `neutral` for a string | Overrides the tone, and so the arrow and colour |
|
|
812
|
+
| `hint` | `string` | none | After the delta, `text-xs` muted |
|
|
813
|
+
|
|
814
|
+
| Slot | Effect |
|
|
815
|
+
| --- | --- |
|
|
816
|
+
| `icon` | On the right of the label, muted: a character or an `<img>` |
|
|
817
|
+
|
|
818
|
+
`deltaTone` is for a figure whose fall is good: above, `Refunds` is `▲ -2` in
|
|
819
|
+
`text-success`. A delta known only when the e-mail is sent is a string —
|
|
820
|
+
`:delta="placeholder('delta')"` — and neutral, unless `delta-tone` says
|
|
821
|
+
otherwise: no component branches on a placeholder.
|
|
822
|
+
|
|
823
|
+
## NxGoalCard
|
|
824
|
+
|
|
825
|
+
material-vue's `GoalCard`: a figure against its target, `of {target}`, and a
|
|
826
|
+
thin bar of the share reached.
|
|
827
|
+
|
|
828
|
+
```vue
|
|
829
|
+
<template>
|
|
830
|
+
<NxGoalCard label="Signed contracts" :value="18" :target="24" />
|
|
831
|
+
<NxGoalCard label="Storage used" :value="42" :target="100" unit=" GB" />
|
|
832
|
+
</template>
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
| Prop | Type | Default | Effect |
|
|
836
|
+
| --- | --- | --- | --- |
|
|
837
|
+
| `label` | `string` | required | The first line, `text-sm` muted |
|
|
838
|
+
| `value` | `number` | required | The figure, `text-2xl` semibold, and the bar's `modelValue` |
|
|
839
|
+
| `target` | `number` | required | Written after the figure, and the bar's `max` |
|
|
840
|
+
| `unit` | `string` | none | Written right after `value`, with no space: `unit="%"` gives `18%`. Not after the target |
|
|
841
|
+
|
|
842
|
+
`of 24` is the shared message `common.metrics.ofTarget` (`of {target}`,
|
|
843
|
+
`sur {target}`) when `@nxgt/mail-i18n` is listed, and English otherwise; see
|
|
844
|
+
[Shared messages](messages.md). The bar is an `NxProgress` of `height` 6: a
|
|
845
|
+
`value` past its `target` fills it, and both are numbers known at build time.
|
|
846
|
+
|
|
847
|
+
## NxRatioCard
|
|
848
|
+
|
|
849
|
+
material-vue's `RatioCard`: two figures side by side, and a bar of the left
|
|
850
|
+
one's share, on a track at 15% of the primary colour.
|
|
851
|
+
|
|
852
|
+
```vue
|
|
853
|
+
<template>
|
|
854
|
+
<NxRatioCard label="Plans" :left="{ label: 'Pro', value: '72%' }" :right="{ label: 'Free', value: '28%' }" :percent="72" />
|
|
855
|
+
</template>
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
| Prop | Type | Default | Effect |
|
|
859
|
+
| --- | --- | --- | --- |
|
|
860
|
+
| `label` | `string` | required | The first line, `text-sm` muted |
|
|
861
|
+
| `left` | `{ label: string; value: string }` | required | On the left: its label small and uppercase, its value `text-lg` semibold |
|
|
862
|
+
| `right` | `{ label: string; value: string }` | required | On the right, the same, its value muted |
|
|
863
|
+
| `percent` | `number` | required | The bar's fill, out of 100: the left one's share |
|
|
864
|
+
|
|
865
|
+
The values are written as given, and `percent` is not computed from them:
|
|
866
|
+
pass both. `percent` is a number known at build time; the values may be
|
|
867
|
+
placeholders.
|
|
868
|
+
|
|
869
|
+
## NxCompareCard
|
|
870
|
+
|
|
871
|
+
material-vue's `CompareCard`: this period's figure beside the last one's, in
|
|
872
|
+
two boxes, and the delta between them.
|
|
873
|
+
|
|
874
|
+
```vue
|
|
875
|
+
<template>
|
|
876
|
+
<NxCompareCard label="Sign-ups" :current="{ value: '340' }" :previous="{ value: '298' }" :delta="42" />
|
|
877
|
+
<NxCompareCard label="Orders" :current="{ value: '51', label: 'October' }" :previous="{ value: '63', label: 'September' }" :delta="-12" />
|
|
878
|
+
</template>
|
|
879
|
+
```
|
|
880
|
+
|
|
881
|
+
| Prop | Type | Default | Effect |
|
|
882
|
+
| --- | --- | --- | --- |
|
|
883
|
+
| `label` | `string` | required | The first line, `text-sm` muted |
|
|
884
|
+
| `current` | `{ value: string; label?: string }` | required | The left box, `text-2xl` semibold. `label` defaults to the shared message `common.metrics.thisPeriod` (`This period`) |
|
|
885
|
+
| `previous` | `{ value: string; label?: string }` | required | The right box, muted. `label` defaults to `common.metrics.lastPeriod` (`Last period`) |
|
|
886
|
+
| `delta` | `number` | none | On the right of the label, toned by its sign, with its arrow — see [A delta and its arrow](#a-delta-and-its-arrow) |
|
|
887
|
+
|
|
888
|
+
Without `@nxgt/mail-i18n`, the two default labels are in English. The delta's
|
|
889
|
+
tone is its sign's: there is no `deltaTone` here.
|
|
890
|
+
|
|
891
|
+
## NxBreakdownCard
|
|
892
|
+
|
|
893
|
+
material-vue's `BreakdownCard`: the parts of a whole, each a label, its value,
|
|
894
|
+
and a thin bar of its share.
|
|
895
|
+
|
|
896
|
+
```vue
|
|
897
|
+
<template>
|
|
898
|
+
<NxBreakdownCard
|
|
899
|
+
label="Traffic"
|
|
900
|
+
:items="[
|
|
901
|
+
{ label: 'Search', percent: 54.4 },
|
|
902
|
+
{ label: 'Direct', value: '1,204', percent: 30 },
|
|
903
|
+
{ label: 'Social', percent: 15.6 },
|
|
904
|
+
]"
|
|
905
|
+
/>
|
|
906
|
+
</template>
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
| Prop | Type | Default | Effect |
|
|
910
|
+
| --- | --- | --- | --- |
|
|
911
|
+
| `label` | `string` | required | The first line, `text-sm` muted |
|
|
912
|
+
| `items` | `{ label: string; value?: string; percent: number }[]` | required | The parts, in order; each `label` unique |
|
|
913
|
+
|
|
914
|
+
Each part writes its `value`, or its share rounded to a whole percent when it
|
|
915
|
+
has none: above, `54%`, `1,204` and `16%`. Its bar is an `NxProgress` of
|
|
916
|
+
`height` 6 filled to `percent`, a number known at build time.
|
|
917
|
+
|
|
918
|
+
## NxSeeAlso
|
|
919
|
+
|
|
920
|
+
material-vue's `SeeAlso`: a line, a small uppercase label, and links one per
|
|
921
|
+
row, each with the `↗` material-vue gives an external link — every link of an
|
|
922
|
+
e-mail opens a browser.
|
|
923
|
+
|
|
924
|
+
```vue
|
|
925
|
+
<template>
|
|
926
|
+
<NxSeeAlso
|
|
927
|
+
:items="[
|
|
928
|
+
{ title: 'Billing', href: 'https://acme.example/billing' },
|
|
929
|
+
{ id: 'team', title: 'Your team', href: 'https://acme.example/team' },
|
|
930
|
+
]"
|
|
931
|
+
/>
|
|
932
|
+
</template>
|
|
933
|
+
```
|
|
934
|
+
|
|
935
|
+
| Prop | Type | Default | Effect |
|
|
936
|
+
| --- | --- | --- | --- |
|
|
937
|
+
| `items` | `{ id?: string; title: string; href: string }[]` | required | The links, in order; each `id`, or else each `href`, unique |
|
|
938
|
+
| `label` | `string` | the shared message `common.seeAlso` (`See also`); without `@nxgt/mail-i18n`, `See also` in every language | The label over the links |
|
|
939
|
+
|
|
940
|
+
With no `items`, it writes nothing: no line, no label. The `↗` is
|
|
941
|
+
`aria-hidden` and left out of the plain-text version. Its `class` is merged
|
|
942
|
+
on the block, `mt-10 pt-8 mb-4` with a line above.
|
|
943
|
+
|
|
944
|
+
## A complete template
|
|
945
|
+
|
|
946
|
+
With `@nxgt/mail-i18n` and [`uiCatalogues`](messages.md), in two locales:
|
|
947
|
+
|
|
948
|
+
```json
|
|
949
|
+
// locales/en.json — locales/fr.json has the same keys
|
|
950
|
+
{
|
|
951
|
+
"paymentFailed": {
|
|
952
|
+
"subject": "Your payment did not go through",
|
|
953
|
+
"title": "Your payment did not go through",
|
|
954
|
+
"body": "We could not charge your card for the {plan} plan.",
|
|
955
|
+
"action": "Update my card"
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
```vue
|
|
961
|
+
<!-- emails/payment-failed.vue -->
|
|
962
|
+
<template>
|
|
963
|
+
<NxLayout :preheader="t('paymentFailed.body', { plan: 'Pro' })">
|
|
964
|
+
<NxTypography variant="headline-small">{{ t('paymentFailed.title') }}</NxTypography>
|
|
965
|
+
<NxTypography>{{ t('common.greeting', { name: placeholder('name') }) }}</NxTypography>
|
|
966
|
+
<NxAlert variant="error" :description="t('paymentFailed.body', { plan: 'Pro' })" />
|
|
967
|
+
<NxSummaryData :data="[{ label: 'Invoice', value: placeholder('invoiceNumber') }, { label: 'Amount', value: placeholder('amount') }]" />
|
|
968
|
+
<NxButton :href="placeholder('link')" align="center">{{ t('paymentFailed.action') }}</NxButton>
|
|
969
|
+
<Spacer height="16px" />
|
|
970
|
+
<NxTypography variant="caption">{{ t('common.footer.ignore') }}</NxTypography>
|
|
971
|
+
</NxLayout>
|
|
972
|
+
</template>
|
|
973
|
+
```
|
|
974
|
+
|
|
975
|
+
`maizzle build` writes `dist/en/payment-failed.html` and
|
|
976
|
+
`dist/fr/payment-failed.html`, each with `{{ name }}`, `{{ invoiceNumber }}`,
|
|
977
|
+
`{{ amount }}` and `{{ link }}` left for the code that sends.
|
|
978
|
+
|
|
979
|
+
## See also
|
|
980
|
+
|
|
981
|
+
- [The plugin](plugin.md) — replacing one of these components with your own.
|
|
982
|
+
- [The theme](theme.md) — the colours and radii these classes use.
|
|
983
|
+
- [Shared messages](messages.md) — `common.greeting`, the footer's text, and
|
|
984
|
+
the words the components write themselves.
|