vanilla-framework 4.45.0 → 4.48.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vanilla-framework",
3
- "version": "4.45.0",
3
+ "version": "4.48.0",
4
4
  "author": {
5
5
  "email": "webteam@canonical.com",
6
6
  "name": "Canonical Webteam"
@@ -1636,3 +1636,16 @@
1636
1636
  @mixin vf-icon-storage-volume-themed {
1637
1637
  @include vf-themed-icon($light-value: vf-icon-storage-volume-url($colors--light-theme--icon), $dark-value: vf-icon-storage-volume-url($colors--dark-theme--icon));
1638
1638
  }
1639
+
1640
+ // circle-of-friends
1641
+ @function vf-icon-circle-of-friends-url($color) {
1642
+ @return url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='none' viewBox='0 0 16 16'%3E%3Cg fill='#{vf-url-friendly-color($color)}' clip-path='url(%23a)'%3E%3Cpath d='M.81 7.36a1.92 1.92 0 1 1 3.799.572A1.92 1.92 0 0 1 .81 7.36M8.826 3.033a1.92 1.92 0 1 1 3.755.806 1.92 1.92 0 0 1-3.755-.806M7.04 12.585a4.68 4.68 0 0 1-3.19-2.432 2.76 2.76 0 0 1-1.64.202 6.25 6.25 0 0 0 4.498 3.77c.45.098.908.144 1.364.141a2.74 2.74 0 0 1-.562-1.605 5 5 0 0 1-.47-.076M8.394 12.193a1.92 1.92 0 0 1 3.754.805 1.92 1.92 0 1 1-3.754-.805M12.943 11.89a6.3 6.3 0 0 0 1.22-2.587 6.3 6.3 0 0 0-.905-4.782 2.77 2.77 0 0 1-1.08 1.265 4.7 4.7 0 0 1-.154 4.674c.45.37.77.87.919 1.43M2.56 4.892a2.75 2.75 0 0 1 1.603.41 4.68 4.68 0 0 1 3.77-2.015q.012-.218.057-.433c.088-.411.268-.795.525-1.124A6.31 6.31 0 0 0 2.56 4.892'/%3E%3C/g%3E%3Cdefs%3E%3CclipPath id='a'%3E%3Cpath fill='#{vf-url-friendly-color($color)}' d='M0 0h16v16H0z'/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E");
1643
+ }
1644
+
1645
+ @mixin vf-icon-circle-of-friends($color: $colors--light-theme--icon) {
1646
+ background-image: vf-icon-circle-of-friends-url($color);
1647
+ }
1648
+
1649
+ @mixin vf-icon-circle-of-friends-themed {
1650
+ @include vf-themed-icon($light-value: vf-icon-circle-of-friends-url($colors--light-theme--icon), $dark-value: vf-icon-circle-of-friends-url($colors--dark-theme--icon));
1651
+ }
@@ -85,6 +85,7 @@
85
85
  @include vf-chips-caution;
86
86
  @include vf-chips-negative;
87
87
  @include vf-chips-information;
88
+ @include vf-chips-branded;
88
89
  }
89
90
 
90
91
  @mixin vf-chip-interactive(
@@ -273,3 +274,54 @@
273
274
  );
274
275
  }
275
276
  }
277
+
278
+ @mixin vf-chips-branded {
279
+ .p-chip--branded {
280
+ @extend %vf-chip;
281
+ @extend %vf-button-dense-vertical-padding;
282
+
283
+ align-items: center;
284
+ background-color: $colors--theme--background-alt;
285
+ gap: $sph--x-small;
286
+ height: 1.5rem;
287
+ justify-content: center;
288
+ margin-bottom: $spv-nudge-compensation;
289
+ min-width: max-content;
290
+ padding-left: $sph--x-small;
291
+
292
+ @include vf-theme-dark; // Default to dark theme
293
+
294
+ // stylelint-disable selector-max-type -- Branded chip theming is based on theming of the document body.
295
+ // Apply light theme if the page or the chip component is dark
296
+ body.is-dark &,
297
+ &.is-dark {
298
+ @include vf-theme-light;
299
+ }
300
+
301
+ // Override to dark theme if the page or the chip component is light or paper
302
+ body.is-light &,
303
+ body.is-paper &,
304
+ &.is-light,
305
+ &.is-paper {
306
+ @include vf-theme-dark;
307
+ }
308
+ // stylelint-enable selector-max-type
309
+
310
+ .p-chip__value {
311
+ color: $colors--theme--text-default;
312
+ font-weight: $font-weight-medium;
313
+ }
314
+
315
+ & > .p-icon--circle-of-friends {
316
+ @include vf-icon-circle-of-friends-themed;
317
+ background-size: contain;
318
+ margin: 0;
319
+ }
320
+
321
+ &:has(> .p-icon--circle-of-friends:only-child) {
322
+ border-radius: 50%;
323
+ padding: 0;
324
+ width: 1.5rem;
325
+ }
326
+ }
327
+ }
@@ -0,0 +1,427 @@
1
+ /*
2
+ @classreference
3
+ content-card:
4
+ Content card:
5
+ .p-content-card:
6
+ Main element of the content card component.
7
+ "&.p-content-card--2-cols":
8
+ Modifier for a 2-column width vertical layout. Applied ONLY to the parent .p-content-card.
9
+ "&.p-content-card--4-cols":
10
+ Modifier for a 4-column width horizontal layout. Applied ONLY to the parent .p-content-card.
11
+ "&.p-content-card--6-cols":
12
+ Modifier for a 6-column width horizontal layout. Applied ONLY to the parent .p-content-card.
13
+ "&.p-content-card__has-image":
14
+ State class applied to the parent when the card includes an image.
15
+ "&.p-content-card__has-desc":
16
+ State class applied to the parent when the card includes a description that appears on hover.
17
+ Links:
18
+ .p-content-card__overlay-link:
19
+ Invisible absolute link spanning the entire card to make it clickable.
20
+ .p-content-card__main-link:
21
+ The primary text link, typically wrapping the heading.
22
+ .p-content-card__author-link:
23
+ Interactive link for the author name.
24
+ Media:
25
+ .p-content-card__image-wrapper:
26
+ Container for the card's image. Styled contextually by parent modifiers.
27
+ .p-content-card__image:
28
+ The image element, maintaining a 16/9 aspect ratio. Styled contextually by parent modifiers.
29
+ Content:
30
+ .p-content-card__content:
31
+ Flex container wrapping the main body and footer of the card.
32
+ .p-content-card__body:
33
+ Grid container holding both the primary and hover content areas. Styled contextually by parent modifiers.
34
+ .p-content-card__primary-content:
35
+ The default visible content area (heading, author, date).
36
+ .p-content-card__hover-content:
37
+ The content area that slides up on hover (description).
38
+ .p-content-card__heading:
39
+ The card's title element. Styled contextually by parent modifiers.
40
+ .p-content-card__description:
41
+ The text description that appears on hover. Styled contextually by parent modifiers.
42
+ .p-content-card__author-and-date:
43
+ Container for the author and publication date. Styled contextually by parent modifiers.
44
+ Footer:
45
+ .p-content-card__footer-outer:
46
+ Outer container for the footer section. Styled contextually by parent modifiers.
47
+ .p-content-card__footer-inner:
48
+ Horizontally scrollable inner container for footer metadata (chips, icons).
49
+ .p-content-card__icon:
50
+ Icon element utilized within the footer.
51
+ .p-content-card__small:
52
+ Small text element utilized within the footer.
53
+ */
54
+
55
+ @import 'settings';
56
+
57
+ @mixin vf-p-content-card {
58
+ .p-content-card {
59
+ align-items: flex-start;
60
+ border: 1px solid var(--color-border-low-contrast, rgba(0, 0, 0, 0.1));
61
+ display: flex;
62
+ flex-direction: column;
63
+ height: 23.75rem; /* 380px */
64
+ justify-content: space-between;
65
+ overflow: hidden;
66
+ padding-top: var(--spacing-vertical-large, $sp-medium);
67
+ position: relative;
68
+ text-decoration: none !important;
69
+ transition: all 0.4s ease-in-out;
70
+ width: var(--size-width-grid-col-2, 17.75rem); /* 284px */
71
+
72
+ @media screen and (width >= $breakpoint-small) {
73
+ &.p-content-card--4-cols,
74
+ &.p-content-card--6-cols {
75
+ align-items: flex-start;
76
+ align-self: stretch;
77
+ flex-direction: row;
78
+ gap: $sp-medium; /* 16px */
79
+ height: 11.703125rem; /* 187.25px */
80
+ padding: $sp-medium $sp-medium 0;
81
+ width: var(--size-width-grid-col-4, 37.5rem); /* 600px */
82
+ }
83
+
84
+ &.p-content-card--4-cols:not(.has-image),
85
+ &.p-content-card--6-cols:not(.has-image) {
86
+ height: 12rem; /* 192px - Fixed height */
87
+ }
88
+
89
+ &.p-content-card--4-cols:not(.has-image) .p-rule--muted,
90
+ &.p-content-card--6-cols:not(.has-image) .p-rule--muted {
91
+ margin-left: -$sp-medium;
92
+ margin-right: -$sp-medium;
93
+ width: calc(100% + $sp-large); /* 32px */
94
+ }
95
+ }
96
+
97
+ @media screen and ($breakpoint-small <= width < $breakpoint-large) {
98
+ &.p-content-card--2-cols {
99
+ align-items: flex-start;
100
+ align-self: stretch;
101
+ flex-direction: row;
102
+ gap: $sp-medium; /* 16px */
103
+ height: 11.703125rem; /* 187.25px */
104
+ padding: $sp-medium $sp-medium 0;
105
+ width: var(--size-width-grid-col-4, 37.5rem); /* 600px */
106
+ }
107
+
108
+ &.p-content-card--2-cols:not(.has-image) {
109
+ height: 10rem; /* 160px - Fixed height */
110
+ }
111
+
112
+ &.p-content-card--2-cols:not(.has-image) .p-rule--muted {
113
+ margin-left: -$sp-medium;
114
+ margin-right: -$sp-medium;
115
+ width: calc(100% + $sp-large); /* 32px */
116
+ }
117
+ }
118
+
119
+ @media screen and (width >= $breakpoint-large) {
120
+ &.p-content-card--6-cols {
121
+ height: 11.984375rem; /* 191.75px */
122
+ width: var(--size-width-grid-col-6, 57.25rem); /* 916px */
123
+ }
124
+ }
125
+
126
+ &__overlay-link {
127
+ inset: 0;
128
+ position: absolute;
129
+ z-index: 1;
130
+ }
131
+
132
+ &__main-link {
133
+ -webkit-box-orient: vertical;
134
+ color: inherit !important;
135
+ display: -webkit-box;
136
+ -webkit-line-clamp: 3;
137
+ line-clamp: 3;
138
+ outline: none !important;
139
+ overflow: hidden;
140
+ text-decoration: none !important;
141
+
142
+ &:hover,
143
+ &:focus,
144
+ &:active,
145
+ &:visited {
146
+ color: inherit !important;
147
+ text-decoration: none !important;
148
+ }
149
+ }
150
+
151
+ &__author-link {
152
+ pointer-events: auto;
153
+ position: relative;
154
+ z-index: 2;
155
+
156
+ &:focus-visible {
157
+ border-radius: 2px;
158
+ outline: 2px solid var(--color-focus, #0066cc);
159
+ outline-offset: 2px;
160
+ }
161
+ }
162
+
163
+ &__heading {
164
+ -webkit-box-orient: vertical;
165
+ display: -webkit-box;
166
+ -webkit-line-clamp: 3;
167
+ line-clamp: 3;
168
+ line-height: 1.5;
169
+ margin-top: 0 !important;
170
+ max-height: 4.5em;
171
+ max-width: none;
172
+ overflow: hidden;
173
+ padding-top: 0 !important;
174
+ text-wrap: wrap;
175
+ width: 100%;
176
+
177
+ @media screen and (width >= $breakpoint-large) {
178
+ .p-content-card--6-cols & {
179
+ margin-top: -0.45rem !important;
180
+ }
181
+ }
182
+ }
183
+
184
+ &__content {
185
+ display: flex;
186
+ flex: 1;
187
+ flex-direction: column;
188
+ height: 100%;
189
+ justify-content: space-between;
190
+ min-width: 0;
191
+ width: 100%;
192
+ }
193
+
194
+ &__body {
195
+ align-items: flex-start;
196
+ align-self: stretch;
197
+ display: grid;
198
+ grid-template-columns: 1fr;
199
+ grid-template-rows: 1fr;
200
+ overflow: hidden;
201
+ padding: 0 $sp-medium; /* 16px */
202
+
203
+ @media screen and (width >= $breakpoint-small) {
204
+ .p-content-card--4-cols &,
205
+ .p-content-card--6-cols & {
206
+ padding: 0;
207
+ }
208
+ }
209
+ @media screen and ($breakpoint-small <= width < $breakpoint-large) {
210
+ .p-content-card--2-cols & {
211
+ padding: 0;
212
+ }
213
+ }
214
+ }
215
+
216
+ &__primary-content,
217
+ &__hover-content {
218
+ display: flex;
219
+ flex-direction: column;
220
+ grid-area: 1 / 1 / 2 / 2;
221
+ transition:
222
+ transform 0.4s ease-in-out,
223
+ opacity 0.4s ease-in-out;
224
+ }
225
+
226
+ &__primary-content {
227
+ opacity: 1;
228
+ transform: translateY(0);
229
+ }
230
+
231
+ &__hover-content {
232
+ opacity: 0;
233
+ transform: translateY(150px);
234
+ }
235
+
236
+ &__description {
237
+ -webkit-box-orient: vertical;
238
+ display: -webkit-box;
239
+ -webkit-line-clamp: 4;
240
+ line-clamp: 4;
241
+ margin: 0;
242
+ overflow: hidden;
243
+
244
+ @media screen and (width >= $breakpoint-large) {
245
+ .p-content-card--6-cols & {
246
+ margin-top: -0.45rem !important;
247
+ }
248
+ }
249
+ }
250
+
251
+ &__footer-outer {
252
+ align-items: center;
253
+ align-self: stretch;
254
+ display: flex;
255
+ height: 2.25rem; /* Fixed height: 36px */
256
+ overflow: hidden;
257
+ padding: 0 $sp-medium var(--spacing-vertical-small, $sp-small) $sp-medium;
258
+ position: relative;
259
+
260
+ @media screen and (width >= $breakpoint-small) {
261
+ .p-content-card--4-cols &,
262
+ .p-content-card--6-cols & {
263
+ padding: 0 0 var(--spacing-vertical-small, $sp-small) 0;
264
+ }
265
+ }
266
+ @media screen and ($breakpoint-small <= width < $breakpoint-large) {
267
+ .p-content-card--2-cols & {
268
+ padding: 0 0 var(--spacing-vertical-small, $sp-small) 0;
269
+ }
270
+ }
271
+ }
272
+
273
+ &__footer-inner {
274
+ align-items: center;
275
+ display: flex;
276
+ gap: $sp-small; /* 8px */
277
+ height: 1.75rem; /* 28px */
278
+ mask-image: linear-gradient(to right, black 85%, transparent 100%);
279
+ -ms-overflow-style: none;
280
+ overflow-x: auto;
281
+ pointer-events: auto;
282
+ position: relative;
283
+ scrollbar-width: none;
284
+ width: 100%;
285
+ z-index: 2;
286
+
287
+ &::-webkit-scrollbar {
288
+ display: none;
289
+ }
290
+
291
+ > * {
292
+ flex-shrink: 0;
293
+ white-space: nowrap;
294
+ }
295
+
296
+ .p-chip--information {
297
+ margin: 0 1.5625rem 0 0; /* 25px -> 1.5625rem */
298
+ vertical-align: baseline;
299
+ }
300
+
301
+ .u-has-icon {
302
+ align-items: center;
303
+ display: flex;
304
+ gap: $sp-x-small; /* 4px */
305
+
306
+ .p-content-card__icon[class*='p-icon--'],
307
+ .p-content-card__small {
308
+ margin: 0;
309
+ }
310
+
311
+ .p-content-card__small {
312
+ line-height: normal !important;
313
+ }
314
+ }
315
+ }
316
+
317
+ &__image {
318
+ aspect-ratio: 16/9;
319
+ display: block;
320
+ flex-shrink: 0;
321
+ height: 9.984375rem; /* 159.75px */
322
+ object-fit: cover;
323
+
324
+ @media screen and (width >= $breakpoint-small) {
325
+ .p-content-card--4-cols &,
326
+ .p-content-card--6-cols & {
327
+ height: 9.703125rem; /* 155.25px */
328
+ }
329
+ }
330
+ @media screen and ($breakpoint-small <= width < $breakpoint-large) {
331
+ .p-content-card--2-cols & {
332
+ height: 9.703125rem; /* 155.25px */
333
+ }
334
+ }
335
+
336
+ @media screen and (width >= $breakpoint-large) {
337
+ .p-content-card--6-cols & {
338
+ height: 9.984375rem; /* 159.75px */
339
+ }
340
+ }
341
+ }
342
+
343
+ &__image-wrapper {
344
+ align-items: flex-start;
345
+ display: flex;
346
+ flex-direction: column;
347
+ gap: var(--spacing-vertical-image-container, 0.625rem); /* 10px -> 0.625rem */
348
+ padding-bottom: var(--spacing-vertical-large, $sp-medium);
349
+ width: 17.75rem; /* 284px */
350
+
351
+ @media screen and (width >= $breakpoint-small) {
352
+ .p-content-card--4-cols &,
353
+ .p-content-card--6-cols & {
354
+ align-self: stretch;
355
+ flex: 0 0 auto;
356
+ }
357
+ }
358
+ @media screen and ($breakpoint-small <= width < $breakpoint-large) {
359
+ .p-content-card--2-cols & {
360
+ align-self: stretch;
361
+ flex: 0 0 auto;
362
+ }
363
+ }
364
+ }
365
+
366
+ &__author-and-date {
367
+ align-items: flex-start;
368
+ align-self: stretch;
369
+ display: flex;
370
+
371
+ > :first-child {
372
+ margin-bottom: 0 !important;
373
+ }
374
+
375
+ @media screen and ($breakpoint-small <= width < $breakpoint-large) {
376
+ .p-content-card--2-cols &,
377
+ .p-content-card--6-cols & {
378
+ display: none;
379
+ }
380
+ }
381
+
382
+ @media screen and (width >= $breakpoint-large) {
383
+ .p-content-card--6-cols & {
384
+ display: flex;
385
+ margin-top: -0.45rem;
386
+ }
387
+ }
388
+ }
389
+
390
+ &:not(.has-image) {
391
+ height: 12.25rem;
392
+ }
393
+
394
+ &.has-image {
395
+ padding-top: 0;
396
+
397
+ @media screen and (width >= $breakpoint-small) {
398
+ &.p-content-card--4-cols,
399
+ &.p-content-card--6-cols {
400
+ padding-top: $sp-medium; /* 16px */
401
+ }
402
+ }
403
+ }
404
+
405
+ &:has(.p-content-card__main-link:focus-visible) {
406
+ outline: 3px solid var(--color-focus, #0066cc);
407
+ outline-offset: 2px;
408
+ }
409
+
410
+ &:hover,
411
+ &:focus-within {
412
+ border-color: var(--color-border-high-contrast, #707070);
413
+ text-decoration: none;
414
+
415
+ &.has-desc {
416
+ .p-content-card__primary-content {
417
+ opacity: 0;
418
+ transform: translateY(-150px);
419
+ }
420
+ .p-content-card__hover-content {
421
+ opacity: 1;
422
+ transform: translateY(0);
423
+ }
424
+ }
425
+ }
426
+ }
427
+ }
@@ -31,6 +31,7 @@
31
31
  @include vf-p-icon-spinner;
32
32
  @include vf-p-icon-show;
33
33
  @include vf-p-icon-hide;
34
+ @include vf-p-icon-circle-of-friends;
34
35
 
35
36
  // social icons
36
37
  @include vf-p-icon-facebook;
@@ -2090,4 +2091,13 @@
2090
2091
  }
2091
2092
  }
2092
2093
 
2093
- // **Base and Pattern mixins accurate as of February 2026**
2094
+ // ICONS ADDED IN MARCH 2026
2095
+
2096
+ @mixin vf-p-icon-circle-of-friends {
2097
+ .p-icon--circle-of-friends {
2098
+ @extend %icon;
2099
+ @include vf-icon-circle-of-friends-themed;
2100
+ }
2101
+ }
2102
+
2103
+ // **Base and Pattern mixins accurate as of March 2026**
@@ -15,6 +15,7 @@ $base-font-sizes: (
15
15
  // TODO removeme? This variable is not used in Vanilla - worth seeing if it's used downstream.
16
16
  $font-weight-thin: 300 !default;
17
17
  $font-weight-regular-text: 400 !default;
18
+ $font-weight-medium: 500 !default;
18
19
  $font-weight-bold: 550 !default;
19
20
  $font-weight-display-heading: 100 !default;
20
21
 
@@ -12,6 +12,7 @@
12
12
  @import 'patterns_card';
13
13
  @import 'patterns_chip';
14
14
  @import 'patterns_code-snippet';
15
+ @import 'patterns_content-card';
15
16
  @import 'patterns_contextual-menu';
16
17
  @import 'patterns_cta';
17
18
  @import 'patterns_data-spotlight';
@@ -115,6 +116,7 @@
115
116
  @include vf-p-card;
116
117
  @include vf-p-chip;
117
118
  @include vf-p-code-snippet;
119
+ @include vf-p-content-card;
118
120
  @include vf-p-contextual-menu;
119
121
  @include vf-p-cta-block;
120
122
  @include vf-p-data-spotlight;
@@ -132,6 +134,7 @@
132
134
  @include vf-p-section;
133
135
  @include vf-p-form-password-toggle;
134
136
  @include vf-p-icons;
137
+ @include vf-p-icon-topic;
135
138
  @include vf-p-image;
136
139
  @include vf-p-links;
137
140
  @include vf-p-list-tree;
@@ -0,0 +1,101 @@
1
+ {#
2
+ All Params
3
+ columns: Grid column span for the card. Options are 2, 4, 6. Default is 2.
4
+ heading: H4 title text. Truncated at 3 lines. Required.
5
+ link: Link to referenced item. Makes the entire card clickable. Required.
6
+ image: Dictionary containing 'src' and 'alt' for the 16:9 image. Required for col-6.
7
+ author: Optional string for the author's name. Hidden if no image is provided.
8
+ date: Optional string for the publication date. Hidden if no image is provided.
9
+ footer: Dictionary containing 'resource_type' (icon and text) and 'content_type' (read-only chip).
10
+ description: Optional text that slides up on hover, truncating at 4 lines.
11
+
12
+ Variants:
13
+ col-2: 2-column width card layout (Vertical)
14
+ Params: columns (2), heading (req), link (req), image (opt), author (opt, hidden if no image), date (opt, hidden if no image), footer (opt), description (opt)
15
+
16
+ col-4: 4-column width card layout (Horizontal)
17
+ Params: columns (4), heading (req), link (req), image (opt), footer (opt), description (opt)
18
+
19
+ col-6: 6-column width card layout (Horizontal)
20
+ Params: columns (6), heading (req), link (req), image (req), author (opt), date (opt), footer (opt), description (opt)
21
+ #}
22
+
23
+ {%- macro vf_card(columns="2", link=None, heading=None, image=None, author=None, date=None, footer=None, description=None) -%}
24
+
25
+ {%- set layout = 'p-content-card--' ~ columns ~ '-cols' -%}
26
+ {%- set img_class = 'has-image' if image else '' -%}
27
+ {%- set has_desc = 'has-desc' if description else '' -%}
28
+
29
+ <div class="p-content-card {{ layout }} {{ img_class }} {{ has_desc }}">
30
+
31
+ {%- if link -%}
32
+ <a href="{{ link }}" class="p-content-card__overlay-link" tabindex="-1" aria-hidden="true"></a>
33
+ {%- endif -%}
34
+
35
+ {%- if image -%}
36
+ <div class="p-content-card__image-wrapper">
37
+ <img class="p-content-card__image" src="{{ image.src }}" alt="{{ image.alt }}" />
38
+ </div>
39
+ {%- endif -%}
40
+
41
+ <div class="p-content-card__content">
42
+ <div class="p-content-card__body">
43
+
44
+ <div class="p-content-card__primary-content">
45
+ <h4 class="p-content-card__heading">
46
+ {%- if link -%}
47
+ <a href="{{ link }}" class="p-content-card__main-link">{{ heading }}</a>
48
+ {%- else -%}
49
+ {{ heading }}
50
+ {%- endif -%}
51
+ </h4>
52
+
53
+ {%- if image and (author or date) -%}
54
+ <div class="p-content-card__author-and-date u-sv-3">
55
+ <small>
56
+ {%- if author -%}
57
+ <span class="u-text--muted">{{ author }}</span>
58
+ {%- endif -%}
59
+
60
+ {%- if author and date -%}
61
+ &middot;
62
+ {%- endif -%}
63
+
64
+ {%- if date -%}
65
+ <span class="u-text--muted">{{ date }}</span>
66
+ {%- endif -%}
67
+ </small>
68
+ </div>
69
+ {%- endif -%}
70
+ </div>
71
+
72
+ {%- if description -%}
73
+ <div class="p-content-card__hover-content">
74
+ <p class="p-content-card__description">{{ description }}</p>
75
+ </div>
76
+ {%- endif -%}
77
+
78
+ </div>
79
+
80
+ {%- if footer -%}
81
+ <div>
82
+ <hr class="p-rule--muted">
83
+ <div class="p-content-card__footer-outer">
84
+ <div class="p-content-card__footer-inner" tabindex="-1">
85
+ {%- if footer.resource_type -%}
86
+ <span class="u-has-icon">
87
+ <i class="p-content-card__icon p-icon--{{ footer.resource_type.icon }}"></i>
88
+ <small class="p-content-card__small">{{ footer.resource_type.text }}</small>
89
+ </span>
90
+ {%- endif -%}
91
+
92
+ {%- if footer.content_type %}
93
+ <span class="p-chip--information is-readonly">{{ footer.content_type}}</span>
94
+ {%- endif -%}
95
+ </div>
96
+ </div>
97
+ </div>
98
+ {%- endif -%}
99
+ </div>
100
+ </div>
101
+ {%- endmacro -%}
@@ -1,11 +1,15 @@
1
+ {% from "_macros/shared/vf_cta-block.jinja" import vf_cta_block %}
2
+ {% from "_macros/shared/vf_description-block.jinja" import vf_description_block %}
3
+
1
4
  # All Params
2
5
  # title_text: H2 title text
3
6
  # variant: variant for the cta section. Options are "default", "block". Default is "default".
4
7
  # layout: Layout type of cta section. Options are "100", "25-75".
8
+ # blocks: list of content blocks for the CTA section. Includes description and cta blocks.
5
9
 
6
10
  # All Slots
7
- # description: Paragraph-style (one or more) content below the title. This slot is required for "cta-block-100" and "cta-block-25-75" layouts.
8
- # cta: Call-to-action block with action links/buttons. This slot is required for "cta-block-100" and "cta-block-25-75" layouts.
11
+ # description (deprecated): Paragraph-style (one or more) content below the title. This slot is required for "cta-block-100" and "cta-block-25-75" layouts.
12
+ # cta (deprecated): Call-to-action block with action links/buttons. This slot is required for "cta-block-100" and "cta-block-25-75" layouts.
9
13
 
10
14
  # Variants:
11
15
  # default-100: Full-width CTA with title and link text
@@ -14,6 +18,7 @@
14
18
  # variant: default
15
19
  # layout: 100
16
20
  # attrs: A dictionary of attributes to apply to the section element
21
+ # blocks: cta-block is required
17
22
 
18
23
  # Slots:
19
24
  # cta: The cta link - required
@@ -23,6 +28,7 @@
23
28
  # title_text: H2 title text - optional
24
29
  # variant: default
25
30
  # layout: 25-75
31
+ # blocks: cta-block is required
26
32
 
27
33
  # Slots:
28
34
  # cta: The cta link - required
@@ -32,6 +38,7 @@
32
38
  # title_text: H2 title text - required
33
39
  # variant: block
34
40
  # layout: 100
41
+ # blocks: cta-block is required
35
42
 
36
43
  # Slots:
37
44
  # description: Paragraph-style (one or more) content below the title - Optional
@@ -42,31 +49,69 @@
42
49
  # title_text: H2 title text - required
43
50
  # variant: block
44
51
  # layout: 25-75
52
+ # blocks: cta-block is required
45
53
 
46
54
  # Slots:
47
55
  # description: Paragraph-style (one or more) content below the title - Optional
48
56
  # cta: Call-to-action block (required)
49
57
 
50
- {%- macro vf_cta_section(title_text, variant='default', layout='100', caller=None, attrs={}) -%}
58
+ {%- macro vf_cta_section(title_text, variant='default', layout='100', caller=None, attrs={}, blocks=[]) -%}
59
+
60
+ {%- set description_block = blocks | selectattr("type", "equalto", "description") | list | last | default(None) -%}
61
+ {%- set cta_block = blocks | selectattr("type", "equalto", "cta") | list | last | default(None) -%}
62
+
51
63
  {% set description_content = caller('description') %}
52
- {% set has_description = description_content|trim|length > 0 %}
64
+ {% set has_description = description_block or description_content|trim|length > 0 %}
53
65
  {% set cta_content = caller('cta') %}
54
- {% set has_cta = cta_content|trim|length > 0 %}
66
+ {% set has_cta = cta_block or cta_content|trim|length > 0 %}
67
+
68
+ {%- if cta_block -%}
69
+ {%- set cta_block_item = cta_block.get("item", {}) -%}
70
+ {%- set cta_block_type = cta_block_item.get("type","") | trim -%}
71
+
72
+ {%- if cta_block_type == "html" -%}
73
+ {%- set cta_content = cta_block_item.get("content","") | trim -%}
74
+ {%- endif -%}
75
+ {%- endif -%}
76
+
77
+ {%- if description_block -%}
78
+ {%- set description_block_item = description_block.get("item", {}) -%}
79
+ {%- endif -%}
80
+
55
81
  {#- User can pass layout as "X-Y" or "X/Y" -#}
56
82
  {% set layout = layout | trim | replace('/', '-') %}
83
+
57
84
  {% if layout not in ['100', '25-75'] %}
58
85
  {% set layout = "100" %}
59
86
  {% endif %}
87
+
60
88
  {% set variant = variant | trim %}
61
89
  {% if variant not in ['default', 'block'] %}
62
90
  {% set variant = "default" %}
63
91
  {% endif %}
92
+
64
93
  {%- macro _description_block() -%}
65
- {% if has_description %}{{ description_content }}{% endif %}
66
- {%- endmacro -%}
94
+ {%- if description_block -%}
95
+ {{ vf_description_block(type = description_block_item.get("type", ""), content = description_block_item.get("content","")) }}
96
+ {% elif has_description %}
97
+ {{ description_content }}
98
+ {% endif %}
99
+ {%- endmacro %}
100
+
67
101
  {%- macro _cta_block() -%}
68
- {% if has_cta -%}<div class="p-cta-block">{{ cta_content }}</div>{% endif %}
69
- {%- endmacro -%}
102
+ {%- if cta_block -%}
103
+ {%- if cta_block_type == "html" -%}
104
+ <div class="p-cta-block">{{- cta_content | safe -}}</div>
105
+ {%- else -%}
106
+ {{ vf_cta_block( primary=cta_block_item.get("primary", {}),
107
+ secondaries=cta_block_item.get("secondaries", []),
108
+ link=cta_block_item.get("link",{})) }}
109
+ {% endif %}
110
+ {% elif has_cta %}
111
+ <div class="p-cta-block">{{ cta_content }}</div>
112
+ {% endif %}
113
+ {%- endmacro %}
114
+
70
115
  {%- macro _cta_default_variant() -%}
71
116
  <h2>
72
117
  {%- if title_text -%}
@@ -76,11 +121,13 @@
76
121
  {{ cta_content }}
77
122
  </h2>
78
123
  {%- endmacro -%}
124
+
79
125
  {%- macro _cta_block_variant() -%}
80
126
  <h2>{{ title_text }}</h2>
81
127
  {{ _description_block() -}}
82
128
  {{ _cta_block() }}
83
129
  {%- endmacro -%}
130
+
84
131
  {%- macro _cta_variant() -%}
85
132
  {%- if variant == 'default' -%}
86
133
  {{ _cta_default_variant() }}
@@ -88,6 +135,7 @@
88
135
  {{ _cta_block_variant() }}
89
136
  {%- endif -%}
90
137
  {%- endmacro -%}
138
+
91
139
  <hr class="p-rule is-fixed-width u-no-margin--bottom" />
92
140
  <section class="p-strip is-deep {{ attrs.get("class", "") -}}"
93
141
  {%- for attr, value in attrs.items() -%}