Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions components/vf-hero/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
### 4.0.8

* Changed: Update vf-hero component for optimised responsive image delivery and performance [Tracking issue](https://github.com/visual-framework/vf-core/issues/2405)

### 4.0.7

* Changed: Documentation updated [Tracking issue](https://github.com/visual-framework/vf-core/issues/2392)
Expand Down
65 changes: 55 additions & 10 deletions components/vf-hero/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ By default the `vf-hero` makes use of the roundels background image. To keep the

You can bring your own image to use with the vf-hero.

- Dimensions - recommended dimensions for the hero image are:
- Desktop: 1920 × 1080 px
- Mobile: 360 × 200 px
- Large / high-res displays: 2560 × 1440 px
- Size: Image size should be below 500KB
- Format: WebP or AVIF provide better image quality at smaller file sizes
- Positioning: people and other important imagery should be placed on the right side. The image will, by default, vertically centre and align to the right side.
Expand All @@ -25,15 +29,16 @@ You can bring your own image to use with the vf-hero.

| Content name | Usage | `.yml` key |
| ------------ | ------------------------------------------------------------------------------ | -------------------- |
| Heading | To be used for the heading of the page. | `vf_hero_heading` |
| Heading HREF | To be used as a 'return to home' link for the micro site. | `vf_hero_heading_href` |
| Subheading | Optional content to be used along with the Heading for a terse explainer. | `vf_hero_subheading` |
| Heading | To be used for the heading of the page. | `vf_hero_heading` |
| Heading HREF | To be used as a 'return to home' link for the micro site. | `vf_hero_heading_href` |
| Subheading | Optional content to be used along with the Heading for a terse explainer. | `vf_hero_subheading` |
| Kicker | Optional content that helps define the context of overall content of the page. | `vf_hero_kicker` |
| Text | Optional content that can help explain the page content in a brief paragraph. | `vf_hero_text` |
| Link Text | The text for the hero 'call to action' link. Only works if there is associated Link HREF. | `vf_hero_link_text` |
| Link HREF | The url that the Link Text would be pointing to and opens in the same browser tab. This only works if there is associated Link Text | `vf_hero_link_href` |
| Spacing | If added spacing can be any of these values only : 200, 400, 500, 600, 800, 1200, 1600 | 'spacing'
| Text | Optional content that can help explain the page content in a brief paragraph. | `vf_hero_text` |
| Link Text | The text for the hero 'call to action' link. Only works if there is associated Link HREF. | `vf_hero_link_text` |
| Link HREF | The url that the Link Text would be pointing to and opens in the same browser tab. This only works if there is associated Link Text | `vf_hero_link_href` |
| Spacing | If added spacing can be any of these values only : 200, 400, 500, 600, 800, 1200, 1600 | 'spacing' |

<br>

| Content name | `.yml` key | CSS classname |
| ------------ | -------------------- | --------------------- |
Expand All @@ -44,6 +49,25 @@ You can bring your own image to use with the vf-hero.
| Text | `vf_hero_text` | `vf-hero__text` |
| Link Text | `vf_hero_link_text` | `vf-hero__link` |
| Link HREF | `vf_hero_link_href` | n/a |
| Image Mobile | `vf_hero_image_mobile_src` | `vf-hero__image` |
| Image Desktop | `vf_hero_image_src` | `vf-hero__image` |
| Image Large | `vf_hero_image_large_src` | `vf-hero__image` |
| Image Width | `vf_hero_image_width` | `vf-hero__image` |
| Image Height | `vf_hero_image_height` | `vf-hero__image` |
| Image Priority | `vf_hero_image_fetchpriority` | `vf-hero__image` |
| Image Loading | `vf_hero_image_loading` | `vf-hero__image` |
| Image Sizes | `vf_hero_image_sizes` | `vf-hero__image` |
Comment on lines +57 to +59

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of asking users to pass multiple parameters, and you adding it in your code logic. Instead just use one parameter call it "extras" or some better name so users can combine and send it to that.

As there are tons of features cloudimg provides, please add documentation link as well if anybody want to tweak it - https://docs.cloudimage.io/transformations/image-operations

| Image Alt | `vf_hero_image_alt` | `vf-hero__image` |

<br>

For responsive hero delivery, use Cloudimage transformation URLs on the image sources. The recommended pattern is:

- Mobile image: `width=360&height=200&func=crop`
- Desktop image: `width=1920&height=1080&func=crop`
- Large/high-res image: `width=2560&height=1440&func=crop`

The initial HTML renders the hero image with `fetchpriority="high"`, `loading="eager"`, and explicit `width` and `height` defaults to help LCP and layout stability.

### CSS Custom Properties

Expand All @@ -57,7 +81,7 @@ IE 11 will get the background colour and the "roundels" image. Under the approac

### EMBL usage

For EMBL sites, the `vf-hero` can take an image (provided by Design) which should be uploaded to [the files site](https://www.embl.org/files) and applied using the custom property available (`--vf-hero--bg-image`).
For EMBL sites, the `vf-hero` can take an image (provided by Design) which should be uploaded to [the files site](https://www.embl.org/files) and applied using the custom property available (`--vf-hero--bg-image`). When you want the hero to be responsive in the initial HTML, prefer the Cloudimage transformation URLs shown above and pass them through the responsive image props instead of relying on the CSS background alone.

### Angular

Expand All @@ -83,7 +107,14 @@ This package was generated with Angular version 15.2.0 and has been tested on ap
[vf_hero_text]="['The Hentze group combines biochemical and <a href=JavaScript:Void(0);>systems–level approaches</a> to investigate the connections between <a href=JavaScript:Void(0);>gene expression</a> and <a href=JavaScript:Void(0);>cell metabolism</a>, and their roles in human disease.']"
[vf_hero_link_text]="'Learn more'"
[vf_hero_link_href]="'JavaScript:Void(0);'"
[vf_hero_image_size]="'auto 28.5rem'">
[vf_hero_image_size]="'auto 28.5rem'"
[vf_hero_image_mobile_src]="'https://acxngcvroo.cloudimg.io/v7/https://www.embl.org/files/wp-content/uploads/vf-hero-intense.png?width=360&height=200&func=crop'"
[vf_hero_image_src]="'https://acxngcvroo.cloudimg.io/v7/https://www.embl.org/files/wp-content/uploads/vf-hero-intense.png?width=1920&height=1080&func=crop'"
[vf_hero_image_large_src]="'https://acxngcvroo.cloudimg.io/v7/https://www.embl.org/files/wp-content/uploads/vf-hero-intense.png?width=2560&height=1440&func=crop'"
[vf_hero_image_width]="'1920'"
[vf_hero_image_height]="'1080'"
[vf_hero_image_fetchpriority]="'high'"
[vf_hero_image_loading]="'eager'">
</vf-hero>
```
4. add to your styles.scss
Expand All @@ -107,7 +138,14 @@ Usage:
[vf_hero_text]="['The Hentze group combines biochemical and <a href=JavaScript:Void(0);>systems–level approaches</a> to investigate the connections between <a href=JavaScript:Void(0);>gene expression</a> and <a href=JavaScript:Void(0);>cell metabolism</a>, and their roles in human disease.']"
[vf_hero_link_text]="'Learn more'"
[vf_hero_link_href]="'JavaScript:Void(0);'"
[vf_hero_image_size]="'auto 28.5rem'">
[vf_hero_image_size]="'auto 28.5rem'"
[vf_hero_image_mobile_src]="'https://acxngcvroo.cloudimg.io/v7/https://www.embl.org/files/wp-content/uploads/vf-hero-intense.png?width=360&height=200&func=crop'"
[vf_hero_image_src]="'https://acxngcvroo.cloudimg.io/v7/https://www.embl.org/files/wp-content/uploads/vf-hero-intense.png?width=1920&height=1080&func=crop'"
[vf_hero_image_large_src]="'https://acxngcvroo.cloudimg.io/v7/https://www.embl.org/files/wp-content/uploads/vf-hero-intense.png?width=2560&height=1440&func=crop'"
[vf_hero_image_width]="'1920'"
[vf_hero_image_height]="'1080'"
[vf_hero_image_fetchpriority]="'high'"
[vf_hero_image_loading]="'eager'">
</vf-hero>
```

Expand All @@ -134,6 +172,13 @@ As of version 4.0.0-alpha.2 vf-hero has experimental React support which has bee
vf_hero_link_text="Learn more"
vf_hero_link_href="'JavaScript:Void(0);'"
vf_hero_image_size="auto 28.5rem"
vf_hero_image_mobile_src="https://acxngcvroo.cloudimg.io/v7/https://www.embl.org/files/wp-content/uploads/vf-hero-intense.png?width=360&height=200&func=crop"
vf_hero_image_src="https://acxngcvroo.cloudimg.io/v7/https://www.embl.org/files/wp-content/uploads/vf-hero-intense.png?width=1920&height=1080&func=crop"
vf_hero_image_large_src="https://acxngcvroo.cloudimg.io/v7/https://www.embl.org/files/wp-content/uploads/vf-hero-intense.png?width=2560&height=1440&func=crop"
vf_hero_image_width="1920"
vf_hero_image_height="1080"
vf_hero_image_fetchpriority="high"
vf_hero_image_loading="eager"
/>
```
4. Styling changes
Expand Down
27 changes: 27 additions & 0 deletions components/vf-hero/vf-hero.angular.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ import { Component, Input, OnInit } from "@angular/core";
selector: "vf-hero",
template: `
<section [attr.id] = "id !== undefined ? id : null" [ngClass]="class" [style]="style">
<picture *ngIf="hasResponsiveMedia" class="vf-hero__media">
<source *ngIf="vf_hero_image_large_src !== ''" media="(min-width: 1600px)" [attr.srcset]="vf_hero_image_large_src">
<source *ngIf="vf_hero_image_mobile_src !== ''" media="(max-width: 767px)" [attr.srcset]="vf_hero_image_mobile_src">
<img
class="vf-hero__image"
[attr.src]="vf_hero_image_src !== '' ? vf_hero_image_src : vf_hero_image_mobile_src"
[attr.sizes]="vf_hero_image_sizes !== '' ? vf_hero_image_sizes : '(max-width: 767px) 100vw, 1920px'"
[attr.alt]="vf_hero_image_alt"
[attr.width]="vf_hero_image_width !== '' ? vf_hero_image_width : '1920'"
[attr.height]="vf_hero_image_height !== '' ? vf_hero_image_height : '1080'"
[attr.loading]="vf_hero_image_loading !== '' ? vf_hero_image_loading : 'eager'"
[attr.fetchpriority]="vf_hero_image_fetchpriority !== '' ? vf_hero_image_fetchpriority : 'high'"
decoding="async">
</picture>
<div class="vf-hero__content | vf-box | vf-stack vf-stack--400">
<p *ngIf="vf_hero_kicker !== ''" class="vf-hero__kicker" [innerHTML]="vf_hero_kicker"></p>

Expand Down Expand Up @@ -33,6 +47,15 @@ export class VfHeroAngularComponent implements OnInit {
/* Initialize values based on input values */
@Input() vf_hero_image = '';
@Input() vf_hero_image_size = '';
@Input() vf_hero_image_src = '';
@Input() vf_hero_image_mobile_src = '';
@Input() vf_hero_image_large_src = '';
@Input() vf_hero_image_alt = '';
@Input() vf_hero_image_width = '';
@Input() vf_hero_image_height = '';
@Input() vf_hero_image_fetchpriority = '';
@Input() vf_hero_image_loading = '';
@Input() vf_hero_image_sizes = '';
@Input() vf_hero_kicker = '';
@Input() vf_hero_heading = '';
@Input() vf_hero_heading_href = '';
Expand All @@ -47,6 +70,7 @@ export class VfHeroAngularComponent implements OnInit {

class = '';
style = '';
hasResponsiveMedia = false;
vf_hero_link_innerhtml = '';

ngOnInit(): void {
Expand All @@ -62,13 +86,16 @@ export class VfHeroAngularComponent implements OnInit {
/* Initialize/Reset the values for class and style */
this.class = 'vf-hero ';
this.style = '';
this.hasResponsiveMedia = false;

if (this.vf_hero_heading_additional !== '') {
this.vf_hero_kicker = this.vf_hero_heading_additional;
}

/* Set values ass per the input */
this.class += this.spacing !== undefined ? 'vf-hero--' + this.spacing + ' ' : '';
this.hasResponsiveMedia = this.vf_hero_image_src !== '' || this.vf_hero_image_mobile_src !== '' || this.vf_hero_image_large_src !== '';
this.class += this.hasResponsiveMedia ? 'vf-hero--has-media ' : '';
this.class += '| vf-u-fullbleed';
this.class += this.modifier_class !== '' ? ' ' + this.modifier_class : '';
this.style += this.vf_hero_image !== '' ? '--vf-hero--bg-image: ' + this.vf_hero_image : '';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ import * as i0 from "@angular/core";
export declare class VfHeroAngularComponent implements OnInit {
vf_hero_image: string;
vf_hero_image_size: string;
vf_hero_image_src: string;
vf_hero_image_mobile_src: string;
vf_hero_image_large_src: string;
vf_hero_image_alt: string;
vf_hero_image_width: string;
vf_hero_image_height: string;
vf_hero_image_fetchpriority: string;
vf_hero_image_loading: string;
vf_hero_image_sizes: string;
vf_hero_kicker: string;
vf_hero_heading: string;
vf_hero_heading_href: string;
Expand All @@ -16,10 +25,11 @@ export declare class VfHeroAngularComponent implements OnInit {
vf_hero_heading_additional: string;
class: string;
style: string;
hasResponsiveMedia: boolean;
vf_hero_link_innerhtml: string;
ngOnInit(): void;
ngOnChanges(): void;
setValues(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<VfHeroAngularComponent, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<VfHeroAngularComponent, "vf-hero", never, { "vf_hero_image": "vf_hero_image"; "vf_hero_image_size": "vf_hero_image_size"; "vf_hero_kicker": "vf_hero_kicker"; "vf_hero_heading": "vf_hero_heading"; "vf_hero_heading_href": "vf_hero_heading_href"; "vf_hero_subheading": "vf_hero_subheading"; "vf_hero_text": "vf_hero_text"; "vf_hero_link_text": "vf_hero_link_text"; "vf_hero_link_href": "vf_hero_link_href"; "spacing": "spacing"; "id": "id"; "modifier_class": "modifier_class"; "vf_hero_heading_additional": "vf_hero_heading_additional"; }, {}, never, never, false, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<VfHeroAngularComponent, "vf-hero", never, { "vf_hero_image": "vf_hero_image"; "vf_hero_image_size": "vf_hero_image_size"; "vf_hero_image_src": "vf_hero_image_src"; "vf_hero_image_mobile_src": "vf_hero_image_mobile_src"; "vf_hero_image_large_src": "vf_hero_image_large_src"; "vf_hero_image_alt": "vf_hero_image_alt"; "vf_hero_image_width": "vf_hero_image_width"; "vf_hero_image_height": "vf_hero_image_height"; "vf_hero_image_fetchpriority": "vf_hero_image_fetchpriority"; "vf_hero_image_loading": "vf_hero_image_loading"; "vf_hero_image_sizes": "vf_hero_image_sizes"; "vf_hero_kicker": "vf_hero_kicker"; "vf_hero_heading": "vf_hero_heading"; "vf_hero_heading_href": "vf_hero_heading_href"; "vf_hero_subheading": "vf_hero_subheading"; "vf_hero_text": "vf_hero_text"; "vf_hero_link_text": "vf_hero_link_text"; "vf_hero_link_href": "vf_hero_link_href"; "spacing": "spacing"; "id": "id"; "modifier_class": "modifier_class"; "vf_hero_heading_additional": "vf_hero_heading_additional"; }, {}, never, never, false, never>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,30 @@ describe('VfHeroAngularComponent', () => {
expect(linkElement.nativeElement.innerHTML).toContain('Link Text');
expect(linkElement.nativeElement.getAttribute('href')).toBe('http://example.com');
});

it('should render responsive media with high priority defaults', () => {
component.vf_hero_image_mobile_src = '/hero-mobile-360x200.jpg';
component.vf_hero_image_src = '/hero-desktop-1920x1080.jpg';
component.vf_hero_image_large_src = '/hero-large-2560x1440.jpg';
fixture.detectChanges();

expect(component.class).toContain('vf-hero--has-media');

const imageElement = fixture.debugElement.query(By.css('.vf-hero__image'));
expect(imageElement.nativeElement.getAttribute('src')).toBe('/hero-desktop-1920x1080.jpg');
expect(imageElement.nativeElement.getAttribute('fetchpriority')).toBe('high');
expect(imageElement.nativeElement.getAttribute('loading')).toBe('eager');
expect(imageElement.nativeElement.getAttribute('width')).toBe('1920');
expect(imageElement.nativeElement.getAttribute('height')).toBe('1080');
expect(imageElement.nativeElement.getAttribute('sizes')).toBe('(max-width: 767px) 100vw, 1920px');
});

it('should use the default sizes hint when one is not provided', () => {
component.vf_hero_image_mobile_src = '/hero-mobile-360x200.jpg';
component.vf_hero_image_src = '/hero-desktop-1920x1080.jpg';
fixture.detectChanges();

const imageElement = fixture.debugElement.query(By.css('.vf-hero__image'));
expect(imageElement.nativeElement.getAttribute('sizes')).toBe('(max-width: 767px) 100vw, 1920px');
});
});
Loading
Loading