vanilla-framework 4.50.0 → 4.52.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 +1 -1
- package/scss/_layouts_full-width.scss +1 -2
- package/scss/_patterns_content-card.scss +130 -13
- package/scss/_patterns_image.scss +35 -0
- package/scss/_patterns_lists.scss +3 -3
- package/scss/_patterns_suru.scss +3 -3
- package/scss/_settings_colors.scss +1 -1
- package/templates/_macros/vf_card.jinja +17 -11
- package/templates/_macros/vf_rich-vertical-list.jinja +106 -106
- package/templates/_macros/vf_tiered-list.jinja +6 -2
- package/templates/static/js/example.js +1 -1
package/package.json
CHANGED
|
@@ -19,11 +19,10 @@
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
.l-full-width__sidebar {
|
|
22
|
-
background: $colors--theme--background-alt;
|
|
23
|
-
|
|
24
22
|
// height of top navigation, as padding applied to .p-navigation__link + line-heigh of the anchor text inside
|
|
25
23
|
$navigation-top-height: $spv--large * 2 + map-get($settings-text-default, line-height);
|
|
26
24
|
|
|
25
|
+
background: $colors--theme--background-alt;
|
|
27
26
|
height: calc(100% - $navigation-top-height); // height of document reduced by height of top nav
|
|
28
27
|
min-height: calc(100dvh - $navigation-top-height);
|
|
29
28
|
position: absolute;
|
|
@@ -10,10 +10,12 @@
|
|
|
10
10
|
Modifier for a 4-column width horizontal layout. Applied ONLY to the parent .p-content-card.
|
|
11
11
|
"&.p-content-card--cols-6":
|
|
12
12
|
Modifier for a 6-column width horizontal layout. Applied ONLY to the parent .p-content-card.
|
|
13
|
+
"&.p-content-card--cols-8":
|
|
14
|
+
Modifier for an 8-column (full-width) layout with 50/50 content and image split on desktop. Applied ONLY to the parent .p-content-card. Image is required.
|
|
13
15
|
"&.has-image":
|
|
14
16
|
State class applied to the parent when the card includes an image.
|
|
15
17
|
"&.has-desc":
|
|
16
|
-
State class applied to the parent when the card includes a description
|
|
18
|
+
State class applied to the parent when the card includes a description. For col-2/4/6 the description slides up on hover; for col-8 it is always visible below the heading.
|
|
17
19
|
Links:
|
|
18
20
|
.p-content-card__overlay-link:
|
|
19
21
|
Invisible absolute link spanning the entire card to make it clickable.
|
|
@@ -78,9 +80,10 @@ $card-author-padding-bottom: $sp-small;
|
|
|
78
80
|
|
|
79
81
|
$card-fallback-spacing-large: $sp-x-large;
|
|
80
82
|
$card-fallback-image-gap: $sp-small + $sp-xx-small;
|
|
83
|
+
$card-height-8col: 22rem;
|
|
81
84
|
|
|
82
85
|
@mixin mq-min($breakpoint) {
|
|
83
|
-
@media screen and (width
|
|
86
|
+
@media screen and (width >=$breakpoint) {
|
|
84
87
|
@content;
|
|
85
88
|
}
|
|
86
89
|
}
|
|
@@ -92,7 +95,7 @@ $card-fallback-image-gap: $sp-small + $sp-xx-small;
|
|
|
92
95
|
}
|
|
93
96
|
|
|
94
97
|
@mixin mq-between($min, $max) {
|
|
95
|
-
@media screen and ($min <=
|
|
98
|
+
@media screen and ($min <=width < $max) {
|
|
96
99
|
@content;
|
|
97
100
|
}
|
|
98
101
|
}
|
|
@@ -111,18 +114,24 @@ $card-fallback-image-gap: $sp-small + $sp-xx-small;
|
|
|
111
114
|
|
|
112
115
|
/* 2-Column Spanning */
|
|
113
116
|
&--2 {
|
|
114
|
-
grid-column: 1 / -1;
|
|
117
|
+
grid-column: 1 / -1;
|
|
118
|
+
|
|
119
|
+
/* Mobile */
|
|
115
120
|
@include mq-between($breakpoint-small, $breakpoint-large) {
|
|
116
|
-
grid-column: span 2;
|
|
121
|
+
grid-column: span 2;
|
|
122
|
+
/* Medium */
|
|
117
123
|
}
|
|
124
|
+
|
|
118
125
|
@include mq-min($breakpoint-large) {
|
|
119
|
-
grid-column: span 2;
|
|
126
|
+
grid-column: span 2;
|
|
127
|
+
/* Desktop */
|
|
120
128
|
}
|
|
121
129
|
}
|
|
122
130
|
|
|
123
131
|
/* 4-Column Spanning */
|
|
124
132
|
&--4 {
|
|
125
133
|
grid-column: 1 / -1;
|
|
134
|
+
|
|
126
135
|
@include mq-min($breakpoint-large) {
|
|
127
136
|
grid-column: span 4;
|
|
128
137
|
}
|
|
@@ -131,10 +140,16 @@ $card-fallback-image-gap: $sp-small + $sp-xx-small;
|
|
|
131
140
|
/* 6-Column Spanning */
|
|
132
141
|
&--6 {
|
|
133
142
|
grid-column: 1 / -1;
|
|
143
|
+
|
|
134
144
|
@include mq-min($breakpoint-large) {
|
|
135
145
|
grid-column: span 6;
|
|
136
146
|
}
|
|
137
147
|
}
|
|
148
|
+
|
|
149
|
+
/* 8-Column Spanning (full width) */
|
|
150
|
+
&--8 {
|
|
151
|
+
grid-column: 1 / -1;
|
|
152
|
+
}
|
|
138
153
|
}
|
|
139
154
|
|
|
140
155
|
/* =========================================
|
|
@@ -168,7 +183,8 @@ $card-fallback-image-gap: $sp-small + $sp-xx-small;
|
|
|
168
183
|
&:not(:has(.p-content-card__footer-outer)) {
|
|
169
184
|
height: auto;
|
|
170
185
|
min-height: 0;
|
|
171
|
-
padding-bottom: var(--spacing-vertical-large, $sp-medium);
|
|
186
|
+
padding-bottom: var(--spacing-vertical-large, $sp-medium);
|
|
187
|
+
/* Keeps spacing balanced */
|
|
172
188
|
}
|
|
173
189
|
}
|
|
174
190
|
|
|
@@ -221,6 +237,16 @@ $card-fallback-image-gap: $sp-small + $sp-xx-small;
|
|
|
221
237
|
}
|
|
222
238
|
}
|
|
223
239
|
|
|
240
|
+
/* 8-Column: Desktop horizontal grid layout */
|
|
241
|
+
@include mq-min($breakpoint-large) {
|
|
242
|
+
&.p-content-card--cols-8.has-image {
|
|
243
|
+
display: grid;
|
|
244
|
+
grid-template-columns: 1fr 1fr;
|
|
245
|
+
min-height: $card-height-8col;
|
|
246
|
+
padding: 0;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
224
250
|
&__overlay-link {
|
|
225
251
|
inset: 0;
|
|
226
252
|
position: absolute;
|
|
@@ -242,6 +268,18 @@ $card-fallback-image-gap: $sp-small + $sp-xx-small;
|
|
|
242
268
|
line-clamp: 2;
|
|
243
269
|
}
|
|
244
270
|
|
|
271
|
+
.p-content-card--cols-8 & {
|
|
272
|
+
-webkit-line-clamp: 3;
|
|
273
|
+
line-clamp: 3;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
@include mq-between($breakpoint-small, $breakpoint-large) {
|
|
277
|
+
.p-content-card--cols-8 & {
|
|
278
|
+
-webkit-line-clamp: 2;
|
|
279
|
+
line-clamp: 2;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
245
283
|
&:hover,
|
|
246
284
|
&:focus,
|
|
247
285
|
&:active,
|
|
@@ -268,7 +306,6 @@ $card-fallback-image-gap: $sp-small + $sp-xx-small;
|
|
|
268
306
|
display: -webkit-box;
|
|
269
307
|
-webkit-line-clamp: 3;
|
|
270
308
|
line-clamp: 3;
|
|
271
|
-
line-height: 1.5;
|
|
272
309
|
margin-top: 0 !important;
|
|
273
310
|
max-height: 4.5em;
|
|
274
311
|
max-width: none;
|
|
@@ -283,11 +320,25 @@ $card-fallback-image-gap: $sp-small + $sp-xx-small;
|
|
|
283
320
|
max-height: 3em;
|
|
284
321
|
}
|
|
285
322
|
|
|
323
|
+
.p-content-card--cols-8 & {
|
|
324
|
+
-webkit-line-clamp: 3;
|
|
325
|
+
line-clamp: 3;
|
|
326
|
+
max-height: 4.5em;
|
|
327
|
+
}
|
|
328
|
+
|
|
286
329
|
@include mq-min($breakpoint-large) {
|
|
287
330
|
.p-content-card--cols-6 & {
|
|
288
331
|
margin-top: $card-nudge-negative !important;
|
|
289
332
|
}
|
|
290
333
|
}
|
|
334
|
+
|
|
335
|
+
@include mq-between($breakpoint-small, $breakpoint-large) {
|
|
336
|
+
.p-content-card--cols-8 & {
|
|
337
|
+
-webkit-line-clamp: 2;
|
|
338
|
+
line-clamp: 2;
|
|
339
|
+
max-height: 3em;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
291
342
|
}
|
|
292
343
|
|
|
293
344
|
&__content {
|
|
@@ -298,6 +349,12 @@ $card-fallback-image-gap: $sp-small + $sp-xx-small;
|
|
|
298
349
|
justify-content: space-between;
|
|
299
350
|
min-width: 0;
|
|
300
351
|
width: 100%;
|
|
352
|
+
|
|
353
|
+
@include mq-min($breakpoint-large) {
|
|
354
|
+
.p-content-card--cols-8 & {
|
|
355
|
+
grid-column: 1;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
301
358
|
}
|
|
302
359
|
|
|
303
360
|
&__body {
|
|
@@ -315,6 +372,20 @@ $card-fallback-image-gap: $sp-small + $sp-xx-small;
|
|
|
315
372
|
padding: 0;
|
|
316
373
|
}
|
|
317
374
|
}
|
|
375
|
+
|
|
376
|
+
.p-content-card--cols-8 & {
|
|
377
|
+
display: flex;
|
|
378
|
+
flex-direction: column;
|
|
379
|
+
min-height: 0;
|
|
380
|
+
padding: 0 $sp-medium;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
@include mq-min($breakpoint-large) {
|
|
384
|
+
.p-content-card--cols-8 & {
|
|
385
|
+
align-self: stretch;
|
|
386
|
+
padding: $sp-medium $sp-medium 0 $sp-medium;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
318
389
|
}
|
|
319
390
|
|
|
320
391
|
&__primary-content,
|
|
@@ -335,6 +406,13 @@ $card-fallback-image-gap: $sp-small + $sp-xx-small;
|
|
|
335
406
|
&__hover-content {
|
|
336
407
|
opacity: 0;
|
|
337
408
|
transform: translateY(150px);
|
|
409
|
+
|
|
410
|
+
.p-content-card--cols-8 & {
|
|
411
|
+
flex: 1;
|
|
412
|
+
min-height: 0;
|
|
413
|
+
opacity: 1;
|
|
414
|
+
transform: translateY(0);
|
|
415
|
+
}
|
|
338
416
|
}
|
|
339
417
|
|
|
340
418
|
&__description {
|
|
@@ -350,6 +428,19 @@ $card-fallback-image-gap: $sp-small + $sp-xx-small;
|
|
|
350
428
|
line-clamp: 2;
|
|
351
429
|
}
|
|
352
430
|
|
|
431
|
+
.p-content-card--cols-8 & {
|
|
432
|
+
-webkit-line-clamp: 3;
|
|
433
|
+
line-clamp: 3;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
@include mq-max($breakpoint-large) {
|
|
437
|
+
.p-content-card--cols-8 & {
|
|
438
|
+
-webkit-line-clamp: 2;
|
|
439
|
+
line-clamp: 2;
|
|
440
|
+
margin-bottom: 1rem;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
353
444
|
@include mq-min($breakpoint-large) {
|
|
354
445
|
.p-content-card--cols-6 & {
|
|
355
446
|
margin-top: $card-nudge-negative !important;
|
|
@@ -359,9 +450,13 @@ $card-fallback-image-gap: $sp-small + $sp-xx-small;
|
|
|
359
450
|
|
|
360
451
|
&__footer-container {
|
|
361
452
|
/* Structural grouping element.
|
|
362
|
-
Because the parent (.p-content-card__content) uses flexbox space-between,
|
|
453
|
+
Because the parent (.p-content-card__content) uses flexbox space-between,
|
|
363
454
|
this wrapper prevents the <hr> from floating awkwardly in the middle of the card.
|
|
364
455
|
*/
|
|
456
|
+
|
|
457
|
+
.p-content-card--cols-8 & {
|
|
458
|
+
width: 100%;
|
|
459
|
+
}
|
|
365
460
|
}
|
|
366
461
|
|
|
367
462
|
&__footer-outer {
|
|
@@ -444,6 +539,11 @@ $card-fallback-image-gap: $sp-small + $sp-xx-small;
|
|
|
444
539
|
.p-content-card--cols-6 & {
|
|
445
540
|
height: $card-image-height-horizontal-large;
|
|
446
541
|
}
|
|
542
|
+
|
|
543
|
+
.p-content-card--cols-8 & {
|
|
544
|
+
aspect-ratio: auto;
|
|
545
|
+
height: 100%;
|
|
546
|
+
}
|
|
447
547
|
}
|
|
448
548
|
}
|
|
449
549
|
|
|
@@ -457,6 +557,17 @@ $card-fallback-image-gap: $sp-small + $sp-xx-small;
|
|
|
457
557
|
|
|
458
558
|
width: 100%;
|
|
459
559
|
|
|
560
|
+
@include mq-min($breakpoint-large) {
|
|
561
|
+
.p-content-card--cols-8 & {
|
|
562
|
+
grid-column: 2;
|
|
563
|
+
grid-row: 1 / -1;
|
|
564
|
+
height: 100%;
|
|
565
|
+
padding: 0.5rem;
|
|
566
|
+
padding-bottom: 0;
|
|
567
|
+
width: 100%;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
460
571
|
@include mq-min($breakpoint-small) {
|
|
461
572
|
.p-content-card--cols-4 &,
|
|
462
573
|
.p-content-card--cols-6 & {
|
|
@@ -465,6 +576,12 @@ $card-fallback-image-gap: $sp-small + $sp-xx-small;
|
|
|
465
576
|
width: $card-image-width;
|
|
466
577
|
}
|
|
467
578
|
}
|
|
579
|
+
|
|
580
|
+
@include mq-max($breakpoint-large) {
|
|
581
|
+
.p-content-card--cols-8 & {
|
|
582
|
+
padding: 0rem 0.5rem 1rem 0.5rem;
|
|
583
|
+
}
|
|
584
|
+
}
|
|
468
585
|
}
|
|
469
586
|
|
|
470
587
|
&__author-and-date {
|
|
@@ -508,14 +625,14 @@ $card-fallback-image-gap: $sp-small + $sp-xx-small;
|
|
|
508
625
|
}
|
|
509
626
|
|
|
510
627
|
@include mq-min($breakpoint-large) {
|
|
511
|
-
&.has-desc:hover .p-content-card__primary-content,
|
|
512
|
-
&.has-desc:focus-within .p-content-card__primary-content {
|
|
628
|
+
&.has-desc:not(.p-content-card--cols-8):hover .p-content-card__primary-content,
|
|
629
|
+
&.has-desc:not(.p-content-card--cols-8):focus-within .p-content-card__primary-content {
|
|
513
630
|
opacity: 0;
|
|
514
631
|
transform: translateY(-150px);
|
|
515
632
|
}
|
|
516
633
|
|
|
517
|
-
&.has-desc:hover .p-content-card__hover-content,
|
|
518
|
-
&.has-desc:focus-within .p-content-card__hover-content {
|
|
634
|
+
&.has-desc:not(.p-content-card--cols-8):hover .p-content-card__hover-content,
|
|
635
|
+
&.has-desc:not(.p-content-card--cols-8):focus-within .p-content-card__hover-content {
|
|
519
636
|
opacity: 1;
|
|
520
637
|
transform: translateY(0);
|
|
521
638
|
}
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
Wraps contents in a container with an aspect ratio of 2.4:1.
|
|
19
19
|
.p-image-container--square:
|
|
20
20
|
Wraps contents in a container with an aspect ratio of 1:1.
|
|
21
|
+
.p-image-container--auto-height:
|
|
22
|
+
Stretches the container to the height of an adjacent grid column, clamped between a 16:9 minimum and a 2:3 maximum of the column width using CSS container queries.
|
|
23
|
+
.p-image-container--auto-height-on-(small|medium|large):
|
|
24
|
+
Auto-height variant applied only at the specified breakpoint.
|
|
21
25
|
.p-image-container--(16-9|3-2|2-3|cinematic|square)-on-(small|medium|large):
|
|
22
26
|
Wraps contents in a container with the specified aspect ratio on the specified breakpoint.
|
|
23
27
|
Image:
|
|
@@ -37,6 +41,11 @@ $aspect-ratios: (
|
|
|
37
41
|
'square': 1,
|
|
38
42
|
);
|
|
39
43
|
|
|
44
|
+
// Auto-height clamp bounds, expressed as a percentage of the container's width.
|
|
45
|
+
// Upper bound matches the 2:3 (portrait) ratio; lower bound matches 16:9.
|
|
46
|
+
$image-container-auto-height-max: math.div(1, map-get($aspect-ratios, '2-3')) * 100cqw;
|
|
47
|
+
$image-container-auto-height-min: math.div(1, map-get($aspect-ratios, '16-9')) * 100cqw;
|
|
48
|
+
|
|
40
49
|
@mixin apply-aspect-ratio-styles($padding-value) {
|
|
41
50
|
&::before {
|
|
42
51
|
content: '';
|
|
@@ -57,6 +66,19 @@ $aspect-ratios: (
|
|
|
57
66
|
}
|
|
58
67
|
}
|
|
59
68
|
|
|
69
|
+
@mixin apply-auto-height-styles {
|
|
70
|
+
container-type: inline-size;
|
|
71
|
+
height: 100%;
|
|
72
|
+
max-height: $image-container-auto-height-max;
|
|
73
|
+
min-height: $image-container-auto-height-min;
|
|
74
|
+
|
|
75
|
+
.p-image-container__image {
|
|
76
|
+
height: 100%;
|
|
77
|
+
object-fit: contain;
|
|
78
|
+
width: 100%;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
60
82
|
@mixin aspect-ratio-classes {
|
|
61
83
|
@each $aspect-ratio, $aspect-ratio-value in $aspect-ratios {
|
|
62
84
|
$padding-percentage: math.div(1, $aspect-ratio-value) * 100%;
|
|
@@ -65,6 +87,10 @@ $aspect-ratios: (
|
|
|
65
87
|
}
|
|
66
88
|
}
|
|
67
89
|
|
|
90
|
+
.p-image-container--auto-height {
|
|
91
|
+
@include apply-auto-height-styles;
|
|
92
|
+
}
|
|
93
|
+
|
|
68
94
|
// Responsive aspect ratios
|
|
69
95
|
@each $breakpoint-name, $breakpoint-bounds-pair in $breakpoint-bounds {
|
|
70
96
|
$min-width: map-get($breakpoint-bounds-pair, min);
|
|
@@ -78,6 +104,9 @@ $aspect-ratios: (
|
|
|
78
104
|
@include apply-aspect-ratio-styles($padding-percentage);
|
|
79
105
|
}
|
|
80
106
|
}
|
|
107
|
+
.p-image-container--auto-height-on-#{$breakpoint-name} {
|
|
108
|
+
@include apply-auto-height-styles;
|
|
109
|
+
}
|
|
81
110
|
}
|
|
82
111
|
} @else if $min-width {
|
|
83
112
|
@media (width >= $min-width) {
|
|
@@ -87,6 +116,9 @@ $aspect-ratios: (
|
|
|
87
116
|
@include apply-aspect-ratio-styles($padding-percentage);
|
|
88
117
|
}
|
|
89
118
|
}
|
|
119
|
+
.p-image-container--auto-height-on-#{$breakpoint-name} {
|
|
120
|
+
@include apply-auto-height-styles;
|
|
121
|
+
}
|
|
90
122
|
}
|
|
91
123
|
} @else if $max-width {
|
|
92
124
|
@media (width < $max-width) {
|
|
@@ -96,6 +128,9 @@ $aspect-ratios: (
|
|
|
96
128
|
@include apply-aspect-ratio-styles($padding-percentage);
|
|
97
129
|
}
|
|
98
130
|
}
|
|
131
|
+
.p-image-container--auto-height-on-#{$breakpoint-name} {
|
|
132
|
+
@include apply-auto-height-styles;
|
|
133
|
+
}
|
|
99
134
|
}
|
|
100
135
|
}
|
|
101
136
|
}
|
|
@@ -262,6 +262,9 @@ $list-step-bullet-margin: $sph--x-large;
|
|
|
262
262
|
|
|
263
263
|
display: grid;
|
|
264
264
|
|
|
265
|
+
// --columns is set in the container queries above
|
|
266
|
+
grid-template-columns: repeat(var(--columns), 1fr);
|
|
267
|
+
|
|
265
268
|
@container (width >= #{$horizontal-list-section-medium-content-width}) {
|
|
266
269
|
grid-gap: 0 map-get($grid-gutter-widths, default);
|
|
267
270
|
}
|
|
@@ -282,9 +285,6 @@ $list-step-bullet-margin: $sph--x-large;
|
|
|
282
285
|
}
|
|
283
286
|
}
|
|
284
287
|
|
|
285
|
-
// --columns is set in the container queries above
|
|
286
|
-
grid-template-columns: repeat(var(--columns), 1fr);
|
|
287
|
-
|
|
288
288
|
.p-list__item {
|
|
289
289
|
@extend %vf-list-item;
|
|
290
290
|
@include vf-list-item-icon-divisor-offset;
|
package/scss/_patterns_suru.scss
CHANGED
|
@@ -82,13 +82,13 @@
|
|
|
82
82
|
// same as %section-padding--default
|
|
83
83
|
padding-bottom: $spv--strip-regular * 0.5;
|
|
84
84
|
|
|
85
|
+
// padding top based on p-section--hero
|
|
86
|
+
padding-top: $spv--large;
|
|
87
|
+
|
|
85
88
|
@media (min-width: $breakpoint-large) {
|
|
86
89
|
padding-bottom: $spv--strip-regular;
|
|
87
90
|
}
|
|
88
91
|
|
|
89
|
-
// padding top based on p-section--hero
|
|
90
|
-
padding-top: $spv--large;
|
|
91
|
-
|
|
92
92
|
// on large screens, same as %section-padding--shallow
|
|
93
93
|
@media (min-width: $breakpoint-large) {
|
|
94
94
|
padding-top: $spv--x-large;
|
|
@@ -58,7 +58,7 @@ $hover-background-opacity-percentage: $hover-background-opacity-amount * 100%;
|
|
|
58
58
|
// NON-SEMANTIC COLOURS
|
|
59
59
|
$color-label-validated: #006b75;
|
|
60
60
|
$color-code-background: rgba($color-x-dark, 0.03);
|
|
61
|
-
$color-code-background-dark: rgba($color-x-light, 0.
|
|
61
|
+
$color-code-background-dark: rgba($color-x-light, 0.15);
|
|
62
62
|
$color-code-heading-background: rgba($color-x-dark, 0.08);
|
|
63
63
|
|
|
64
64
|
// Background colours for form elements
|
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
{#
|
|
2
2
|
All Params
|
|
3
|
-
columns: Grid column span for the card. Options are 2, 4, 6. Default is 2.
|
|
3
|
+
columns: Grid column span for the card. Options are 2, 4, 6, 8. Default is 2.
|
|
4
4
|
heading: H4 title text. Truncated at 3 lines. Required.
|
|
5
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.
|
|
6
|
+
image: Dictionary containing 'src' and 'alt' for the 16:9 image. Required for col-6 and col-8.
|
|
7
7
|
author: Optional string for the author's name. Hidden if no image is provided.
|
|
8
8
|
date: Optional string for the publication date. Hidden if no image is provided.
|
|
9
9
|
footer: Dictionary containing 'resource_type' (icon and text) and 'content_type' (read-only chip).
|
|
10
|
-
description: Optional text
|
|
10
|
+
description: Optional text. For col-2/4/6 slides up on hover (4 lines; 2 for col-6). For col-8 always visible below the heading (3 lines desktop/tablet, 2 lines mobile).
|
|
11
11
|
|
|
12
12
|
Variants:
|
|
13
13
|
col-2: 2-column width card layout (Vertical)
|
|
14
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
|
-
|
|
15
|
+
|
|
16
16
|
col-4: 4-column width card layout (Horizontal)
|
|
17
17
|
Params: columns (4), heading (req), link (req), image (opt), footer (opt), description (opt)
|
|
18
|
-
|
|
18
|
+
|
|
19
19
|
col-6: 6-column width card layout (Horizontal)
|
|
20
20
|
Params: columns (6), heading (req), link (req), image (req), author (opt), date (opt), footer (opt), description (opt)
|
|
21
|
+
|
|
22
|
+
col-8: 8-column (full-width) card layout (50/50 horizontal on desktop, vertical stack on mobile/tablet)
|
|
23
|
+
Heading uses h4 with p-heading--1 styling. Image appears on the right on desktop, above heading on mobile/tablet.
|
|
24
|
+
Params: columns (8), heading (req), link (req), image (req), author (opt), date (opt), footer (opt), description (opt)
|
|
21
25
|
#}
|
|
22
26
|
|
|
23
27
|
{%- macro vf_card(columns="2", link=None, heading=None, image=None, author=None, date=None, footer=None, description=None) -%}
|
|
@@ -26,11 +30,12 @@
|
|
|
26
30
|
{%- set layout = 'p-content-card--cols-' ~ col_str -%}
|
|
27
31
|
{%- set img_class = 'has-image' if image else '' -%}
|
|
28
32
|
{%- set has_desc = 'has-desc' if description else '' -%}
|
|
33
|
+
{%- set heading_extra_class = 'p-heading--1 ' if col_str == '8' else '' -%}
|
|
29
34
|
|
|
30
35
|
<div class="p-content-card-wrapper p-content-card-wrapper--{{ col_str }}">
|
|
31
|
-
|
|
36
|
+
|
|
32
37
|
<div class="p-content-card {{ layout }} {{ img_class }} {{ has_desc }}">
|
|
33
|
-
|
|
38
|
+
|
|
34
39
|
{%- if link -%}
|
|
35
40
|
<a href="{{ link }}" class="p-content-card__overlay-link" tabindex="-1" aria-hidden="true"></a>
|
|
36
41
|
{%- endif -%}
|
|
@@ -43,16 +48,16 @@
|
|
|
43
48
|
|
|
44
49
|
<div class="p-content-card__content">
|
|
45
50
|
<div class="p-content-card__body">
|
|
46
|
-
|
|
51
|
+
|
|
47
52
|
<div class="p-content-card__primary-content">
|
|
48
|
-
<h4 class="p-content-card__heading">
|
|
53
|
+
<h4 class="{{ heading_extra_class }}p-content-card__heading">
|
|
49
54
|
{%- if link -%}
|
|
50
55
|
<a href="{{ link }}" class="p-content-card__main-link">{{ heading }}</a>
|
|
51
56
|
{%- else -%}
|
|
52
57
|
{{ heading }}
|
|
53
58
|
{%- endif -%}
|
|
54
59
|
</h4>
|
|
55
|
-
|
|
60
|
+
|
|
56
61
|
{%- if image and (author or date) -%}
|
|
57
62
|
<div class="p-content-card__author-and-date u-sv-3">
|
|
58
63
|
<small>
|
|
@@ -72,7 +77,7 @@
|
|
|
72
77
|
{%- endif -%}
|
|
73
78
|
|
|
74
79
|
</div>
|
|
75
|
-
|
|
80
|
+
|
|
76
81
|
{%- if footer -%}
|
|
77
82
|
<div class="p-content-card__footer-container">
|
|
78
83
|
<hr class="p-rule--muted">
|
|
@@ -95,4 +100,5 @@
|
|
|
95
100
|
</div>
|
|
96
101
|
</div>
|
|
97
102
|
</div>
|
|
103
|
+
|
|
98
104
|
{%- endmacro -%}
|
|
@@ -1,127 +1,127 @@
|
|
|
1
|
+
{% from "_macros/vf_basic-section.jinja" import basic_section_title, vf_basic_section_blocks %}
|
|
2
|
+
{% from "_macros/shared/vf_section_top_rule.jinja" import vf_section_top_rule %}
|
|
3
|
+
|
|
1
4
|
# Params
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
5
|
+
# title (dict) (required): {text, link_attrs?} — rendered via basic_section_title (always renders <h2>).
|
|
6
|
+
# items (array) (optional): Array of {type, item} dicts rendered via vf_basic_section_blocks.
|
|
7
|
+
# Supported types: description, list, code-block, logo-block, cta-block.
|
|
8
|
+
# Entries with any other type are silently dropped.
|
|
9
|
+
# media (dict) (required): Media column config. Keys:
|
|
10
|
+
# - type (string): "image" | "video". Defaults to "image".
|
|
11
|
+
# - ratio.large (string): "16-9" | "3-2" | "1-1" | "2-3" | "auto-height". Defaults to "3-2".
|
|
12
|
+
# - ratio.medium_small (string): "16-9" | "3-2" | "1-1". Defaults to "3-2".
|
|
13
|
+
# - fit (string): "cover" | "contain". Defaults to "cover".
|
|
14
|
+
# - attrs (dict): Passthrough HTML attributes for the <img> or <iframe>.
|
|
15
|
+
# is_flipped (bool) (optional): Swap content and media columns. Defaults to false.
|
|
16
|
+
# padding (string) (optional): "deep" | "shallow" | "default". Defaults to "default".
|
|
17
|
+
# top_rule_variant (string) (optional): "default" | "muted". Defaults to "default".
|
|
18
|
+
# attrs (dict) (optional): HTML attrs for the <section>.
|
|
10
19
|
{% macro vf_rich_vertical_list(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
20
|
+
title={},
|
|
21
|
+
items=[],
|
|
22
|
+
media={},
|
|
23
|
+
is_flipped=false,
|
|
24
|
+
padding="default",
|
|
25
|
+
top_rule_variant="default",
|
|
26
|
+
attrs={}
|
|
14
27
|
) -%}
|
|
15
|
-
{
|
|
16
|
-
{
|
|
17
|
-
{
|
|
18
|
-
{
|
|
19
|
-
{
|
|
20
|
-
{% set has_cta = cta_content|trim|length > 0 %}
|
|
21
|
-
{% set has_list = caller('list_item_1')|trim|length > 0 %}
|
|
22
|
-
{% set image_content = caller('image') %}
|
|
23
|
-
{% set max_list_items = 7 %}
|
|
24
|
-
|
|
25
|
-
{% set list_item_tick_style=list_item_tick_style|trim|lower %}
|
|
26
|
-
{% if list_item_tick_style|length > 0 and list_item_tick_style not in ['bullet', 'tick', 'number'] %}
|
|
27
|
-
{% set list_item_tick_style = '' %}
|
|
28
|
-
{% endif %}
|
|
28
|
+
{#- Normalise & validate padding -#}
|
|
29
|
+
{%- set padding = padding | string | trim | lower -%}
|
|
30
|
+
{%- if padding not in ['deep', 'shallow', 'default'] -%}{%- set padding = 'default' -%}{%- endif -%}
|
|
31
|
+
{%- set padding_classes = 'p-section--' ~ padding -%}
|
|
32
|
+
{%- if padding == 'default' -%}{%- set padding_classes = 'p-section' -%}{%- endif -%}
|
|
29
33
|
|
|
30
|
-
{
|
|
31
|
-
|
|
32
|
-
{
|
|
33
|
-
{% set list_item_tick_class = "is-ticked" %}
|
|
34
|
-
{% endif %}
|
|
34
|
+
{#- Normalise & validate top_rule_variant -#}
|
|
35
|
+
{%- set top_rule_variant = top_rule_variant | string | trim | lower -%}
|
|
36
|
+
{%- if top_rule_variant not in ['default', 'muted'] -%}{%- set top_rule_variant = 'default' -%}{%- endif -%}
|
|
35
37
|
|
|
36
|
-
{
|
|
37
|
-
{
|
|
38
|
-
|
|
39
|
-
{% endif %}
|
|
38
|
+
{#- Normalise & validate media config -#}
|
|
39
|
+
{%- set media_type = (media.get('type', 'image') | string | trim | lower) -%}
|
|
40
|
+
{%- if media_type not in ['image', 'video'] -%}{%- set media_type = 'image' -%}{%- endif -%}
|
|
40
41
|
|
|
41
|
-
{
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
{% set list_items = [] %}
|
|
46
|
-
{% if has_list %}
|
|
47
|
-
{% for list_item_index in range(1, max_list_items + 1) %}
|
|
48
|
-
{% set list_item_content = caller('list_item_' + list_item_index|string) %}
|
|
49
|
-
{% set has_list_item_content = list_item_content|trim|length > 0 %}
|
|
50
|
-
{% if has_list_item_content %}
|
|
51
|
-
{{ list_items.append(list_item_content) or ""}}
|
|
52
|
-
{% endif %}
|
|
53
|
-
{% endfor %}
|
|
54
|
-
{% endif %}
|
|
42
|
+
{%- set media_ratio = media.get('ratio', {}) -%}
|
|
43
|
+
{%- set media_ratio_large = (media_ratio.get('large', '3-2') | string | trim | lower) -%}
|
|
44
|
+
{%- set valid_large_ratios = ['16-9', '3-2', '1-1', '2-3', 'auto-height'] -%}
|
|
45
|
+
{%- if media_ratio_large not in valid_large_ratios -%}{%- set media_ratio_large = '3-2' -%}{%- endif -%}
|
|
55
46
|
|
|
56
|
-
{%-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
</div>
|
|
61
|
-
|
|
62
|
-
{%- if has_logo_section %}
|
|
63
|
-
{#- Optional logo section -#}
|
|
64
|
-
<div class="p-section--shallow">
|
|
65
|
-
<div class="u-fixed-width">
|
|
66
|
-
{{- logo_section_content -}}
|
|
67
|
-
</div>
|
|
68
|
-
</div>
|
|
69
|
-
{%- endif -%}
|
|
47
|
+
{%- set media_ratio_medium_small = (media_ratio.get('medium_small', '3-2') | string | trim | lower) -%}
|
|
48
|
+
{#- 'auto-height' is intentionally excluded — it requires side-by-side columns, but medium/small layouts stack -#}
|
|
49
|
+
{%- set valid_medium_small_ratios = ['16-9', '3-2', '1-1'] -%}
|
|
50
|
+
{%- if media_ratio_medium_small not in valid_medium_small_ratios -%}{%- set media_ratio_medium_small = '3-2' -%}{%- endif -%}
|
|
70
51
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
<div class="p-section--shallow">
|
|
74
|
-
{{- description_content -}}
|
|
75
|
-
</div>
|
|
76
|
-
{%- endif -%}
|
|
52
|
+
{%- set media_fit = (media.get('fit', 'cover') | string | trim | lower) -%}
|
|
53
|
+
{%- if media_fit not in ['cover', 'contain'] -%}{%- set media_fit = 'cover' -%}{%- endif -%}
|
|
77
54
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
<{{ list_element_type }} class="p-list--divided">
|
|
81
|
-
{% for list_item in list_items %}
|
|
82
|
-
<li class="p-list__item {{ list_item_tick_class }}">
|
|
83
|
-
{{- list_item -}}
|
|
84
|
-
</li>
|
|
85
|
-
{% endfor %}
|
|
86
|
-
</{{ list_element_type }}>
|
|
87
|
-
{%- endif -%}
|
|
55
|
+
{%- set media_attrs = media.get('attrs', {}) -%}
|
|
56
|
+
{%- set is_auto_height = (media_ratio_large == 'auto-height') -%}
|
|
88
57
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
{{- cta_content -}}
|
|
93
|
-
</div>
|
|
94
|
-
{%- endif -%}
|
|
58
|
+
{#- Constrain items to the curated allow-list. Disallowed types are silently dropped. -#}
|
|
59
|
+
{%- set allowed_item_types = ['description', 'list', 'code-block', 'logo-block', 'cta-block'] -%}
|
|
60
|
+
{%- set filtered_items = items | selectattr('type', 'in', allowed_item_types) | list -%}
|
|
95
61
|
|
|
62
|
+
{%- macro _rich_list_image(ratio_large, ratio_medium_small, fit, attrs) %}
|
|
63
|
+
{%- set is_cover = (fit == 'cover') -%}
|
|
64
|
+
{#- The image-container CSS uses 'square' for the 1:1 ratio class, not '1-1'. -#}
|
|
65
|
+
{%- set ratio_large_class = 'square' if ratio_large == '1-1' else ratio_large -%}
|
|
66
|
+
{%- set ratio_medium_small_class = 'square' if ratio_medium_small == '1-1' else ratio_medium_small -%}
|
|
67
|
+
{%- set classes = 'p-image-container--' ~ ratio_large_class ~ '-on-large' -%}
|
|
68
|
+
{%- set classes = classes ~ ' p-image-container--' ~ ratio_medium_small_class ~ '-on-medium' -%}
|
|
69
|
+
{%- set classes = classes ~ ' p-image-container--' ~ ratio_medium_small_class ~ '-on-small' -%}
|
|
70
|
+
{%- if is_cover -%}{%- set classes = classes ~ ' is-cover' -%}{%- endif -%}
|
|
71
|
+
<div class="{{ classes }}">
|
|
72
|
+
<img class="p-image-container__image{%- if 'class' in attrs %} {{ attrs['class'] }}{%- endif %}"
|
|
73
|
+
{%- for attr, value in attrs.items() -%}
|
|
74
|
+
{%- if attr != 'class' %} {{ attr }}="{{ value }}"{%- endif -%}
|
|
75
|
+
{%- endfor -%}
|
|
76
|
+
/>
|
|
77
|
+
</div>
|
|
96
78
|
{%- endmacro -%}
|
|
97
79
|
|
|
98
|
-
{%- macro
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
80
|
+
{%- macro _rich_list_video(attrs) %}
|
|
81
|
+
<div class="u-embedded-media">
|
|
82
|
+
<iframe class="u-embedded-media__element{%- if 'class' in attrs %} {{ attrs['class'] }}{%- endif %}"
|
|
83
|
+
{%- for attr, value in attrs.items() -%}
|
|
84
|
+
{%- if attr != 'class' %} {{ attr }}="{{ value }}"{%- endif -%}
|
|
85
|
+
{%- endfor -%}
|
|
86
|
+
></iframe>
|
|
102
87
|
</div>
|
|
103
88
|
{%- endmacro -%}
|
|
104
89
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
90
|
+
{%- macro _media_column_contents(
|
|
91
|
+
media_type, media_ratio_large, media_ratio_medium_small, media_fit, media_attrs, is_auto_height
|
|
92
|
+
) -%}
|
|
93
|
+
{%- if is_auto_height -%}
|
|
94
|
+
<div>
|
|
95
|
+
{%- else -%}
|
|
96
|
+
<div class="p-section--shallow">
|
|
97
|
+
{%- endif -%}
|
|
98
|
+
{%- if media_type == 'video' -%}
|
|
99
|
+
{{ _rich_list_video(media_attrs) }}
|
|
115
100
|
{%- else -%}
|
|
116
|
-
{
|
|
117
|
-
<div class="grid-col">
|
|
118
|
-
{{- _image_column_contents() -}}
|
|
119
|
-
</div>
|
|
120
|
-
<div class="grid-col">
|
|
121
|
-
{{- _text_column_contents(list_items) -}}
|
|
122
|
-
</div>
|
|
101
|
+
{{ _rich_list_image(media_ratio_large, media_ratio_medium_small, media_fit, media_attrs) }}
|
|
123
102
|
{%- endif -%}
|
|
124
103
|
</div>
|
|
125
|
-
|
|
104
|
+
{%- endmacro -%}
|
|
126
105
|
|
|
106
|
+
{%- macro _content_column_contents(title, items) -%}
|
|
107
|
+
{{ basic_section_title(title) }}
|
|
108
|
+
{{ vf_basic_section_blocks(items=items) }}
|
|
109
|
+
{%- endmacro -%}
|
|
110
|
+
|
|
111
|
+
<section class="{{ padding_classes }}{%- if 'class' in attrs %} {{ attrs['class'] }}{%- endif -%}"
|
|
112
|
+
{%- for attr, value in attrs.items() -%}
|
|
113
|
+
{%- if attr != 'class' %} {{ attr }}="{{ value }}"{%- endif -%}
|
|
114
|
+
{%- endfor -%}
|
|
115
|
+
>
|
|
116
|
+
<div class="grid-row--50-50-on-large">
|
|
117
|
+
{{ vf_section_top_rule(top_rule_variant) }}
|
|
118
|
+
{%- if not is_flipped -%}
|
|
119
|
+
<div class="grid-col">{{ _content_column_contents(title, filtered_items) }}</div>
|
|
120
|
+
<div class="grid-col">{{ _media_column_contents(media_type, media_ratio_large, media_ratio_medium_small, media_fit, media_attrs, is_auto_height) }}</div>
|
|
121
|
+
{%- else -%}
|
|
122
|
+
<div class="grid-col">{{ _media_column_contents(media_type, media_ratio_large, media_ratio_medium_small, media_fit, media_attrs, is_auto_height) }}</div>
|
|
123
|
+
<div class="grid-col">{{ _content_column_contents(title, filtered_items) }}</div>
|
|
124
|
+
{%- endif -%}
|
|
125
|
+
</div>
|
|
126
|
+
</section>
|
|
127
127
|
{%- endmacro %}
|
|
@@ -4,16 +4,20 @@
|
|
|
4
4
|
# is_list_full_width_on_tablet: whether list title/description each have their
|
|
5
5
|
# own row on tablet vs. 50/50 split between title/description
|
|
6
6
|
# padding: Type of padding to apply. Options are "deep", "shallow", "default". Default is "default".
|
|
7
|
+
# top_rule_variant: Variant of the horizontal rule rendered at the top of the pattern.
|
|
8
|
+
# Options are "default", "muted", "highlighted", "none". Default is "default".
|
|
7
9
|
# Slots
|
|
8
10
|
# title: top-level title element
|
|
9
11
|
# description: top-level description element
|
|
10
12
|
# list_item_title_[1-25]: title element of each child list item
|
|
11
13
|
# list_item_description_[1-25]: description element of each child list item
|
|
12
14
|
# cta: CTA block element
|
|
15
|
+
{% from "_macros/shared/vf_section_top_rule.jinja" import vf_section_top_rule %}
|
|
13
16
|
{% macro vf_tiered_list(
|
|
14
17
|
padding="default",
|
|
15
18
|
is_description_full_width_on_desktop=true,
|
|
16
|
-
is_list_full_width_on_tablet=true
|
|
19
|
+
is_list_full_width_on_tablet=true,
|
|
20
|
+
top_rule_variant="default") -%}
|
|
17
21
|
{%- set title_content = caller('title') -%}
|
|
18
22
|
{%- set description_content = caller('description') -%}
|
|
19
23
|
{%- set has_description = description_content|trim|length > 0 -%}
|
|
@@ -30,7 +34,7 @@
|
|
|
30
34
|
{%- endif -%}
|
|
31
35
|
|
|
32
36
|
<div class="{{ padding_classes }} u-fixed-width">
|
|
33
|
-
|
|
37
|
+
{{ vf_section_top_rule(top_rule_variant) }}
|
|
34
38
|
<div class="p-section--shallow">
|
|
35
39
|
{% if has_description == true -%}
|
|
36
40
|
{%- if is_description_full_width_on_desktop == true -%}
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
// if it's a demo, use local build.css for better QA, otherwise use latest published Vanilla
|
|
68
68
|
/demos\.haus$/.test(window.location.host)
|
|
69
69
|
? `${window.location.origin}/static/build/css/build.css`
|
|
70
|
-
: 'https://assets.ubuntu.com/v1/vanilla_framework_version_' + VANILLA_VERSION
|
|
70
|
+
: 'https://assets.ubuntu.com/v1/vanilla_framework_version_' + VANILLA_VERSION + '.min.css',
|
|
71
71
|
// link to example stylesheet (to set margin on body)
|
|
72
72
|
'https://assets.ubuntu.com/v1/4653d9ba-example.css',
|
|
73
73
|
],
|