Add Metatag Link submodule (metatag_link) with support for preload attribute

Created on 29 May 2024, 11 months ago
Updated 4 June 2024, 11 months ago

Add support for custom link attribute including support for rel=preload on link tag. This should be configurable and typically placed in the head tag.

e.g.

<link rel="preload" href="style.css" as="style" />

The preload value of the
element's rel attribute lets you declare fetch requests in the HTML's , specifying resources that your page will need very soon, which you want to start loading early in the page lifecycle, before browsers' main rendering machinery kicks in. This ensures they are available earlier and are less likely to block the page's render, improving performance. Even though the name contains the term load, it doesn't load and execute the script but only schedules it to be downloaded and cached with a higher priority.

https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/preload

Use Cases

example 1 - used for preloading css and js

<head>
  <meta charset="utf-8" />
  <title>JS and CSS preload example</title>

  <link rel="preload" href="style.css" as="style" />
  <link rel="preload" href="main.js" as="script" />

  <link rel="stylesheet" href="style.css" />
</head>

<body>
  <h1>bouncing balls</h1>
  <canvas></canvas>

  <script src="main.js" defer></script>
</body>

example 2 - including a mime 'type'

<head>
  <meta charset="utf-8" />
  <title>Image preload example</title>

  <link rel="preload" href="flower.avif" as="image" type="image/avif" />
</head>
<body>
  <picture>
    <source src="flower.avif" type="image/avif" />
    <source src="flower.webp" type="image/webp" />
    <img src="flower.jpg" />
  </picture>
</body>

example 3 - cors enabled fetches

<head>
  <meta charset="utf-8" />
  <title>Web font example</title>

  <link
    rel="preload"
    href="fonts/cicle_fina-webfont.woff2"
    as="font"
    type="font/woff2"
    crossorigin />
  <link
    rel="preload"
    href="fonts/zantroke-webfont.woff2"
    as="font"
    type="font/woff2"
    crossorigin />

  <link href="style.css" rel="stylesheet" />
</head>
<body>
  …
</body>

example 4 - including media

<head>
  <meta charset="utf-8" />
  <title>Responsive preload example</title>

  <link
    rel="preload"
    href="bg-image-narrow.png"
    as="image"
    media="(max-width: 600px)" />
  <link
    rel="preload"
    href="bg-image-wide.png"
    as="image"
    media="(min-width: 601px)" />

  <link rel="stylesheet" href="main.css" />
</head>
<body>
  <header>
    <h1>My site</h1>
  </header>

  <script>
    const mediaQueryList = window.matchMedia("(max-width: 600px)");
    const header = document.querySelector("header");

    if (mediaQueryList.matches) {
      header.style.backgroundImage = "url(bg-image-narrow.png)";
    } else {
      header.style.backgroundImage = "url(bg-image-wide.png)";
    }
  </script>
</body>

Use of the rel attribute can help to optimise a site for mobile and to help reduce the Largest Contentful Paint to under 2 seconds.

Also see https://webdesign.tutsplus.com/what-is-largest-contentful-paint-lcp--cms...

✨ Feature request
Status

Active

Version

2.0

Component

Code

Created by

πŸ‡¬πŸ‡§United Kingdom 2dareis2do

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @2dareis2do
  • πŸ‡¬πŸ‡§United Kingdom 2dareis2do
  • πŸ‡ΊπŸ‡ΈUnited States DamienMcKenna NH, USA

    So that means the following attributes need to be editable:

    • href (main value, required)
    • as
    • on
    • media
    • crossorigin
    • type
  • πŸ‡¬πŸ‡§United Kingdom 2dareis2do

    Looking at the schema for field_metatags_value it looks a bit like this:

    {
    "image_src": "[node:field_picture:entity:field_media_image]",
    "og_image": "[node:field_picture:entity:field_media_image]",
    "og_image_secure_url": "[node:field_picture:entity:field_media_image]",
    "og_image_url": "[node:field_picture:entity:field_media_image]",
    "twitter_cards_image": "[node:field_picture]",
    }

    Notice the relationship here is one to one i.e. each key has a single value. In this case each item is a token.

    What I think might be required here is to be able to pass an associative array of items.

    e.g. for <link rel="preload" href="style.css" as="style" />

    "link_preload": [ 
      "href" : "[site:css-path]",
      "as" : "style",
      "on" : "null",
      "media" : "null",
      "crossorigin" : "null",
      "type": "null",
    ]
    

    Notice in this example we may also need a token for [site:css-path], (if this doesn't exist already).

    for <link rel="preload" href="main.js" as="script" />

    We may need a similar [site:js-path] token

    Whats interesting about <link rel="stylesheet" href="style.css" /> is that the rel attribute is different. Of course this is handled by drupal front end theme already, but it begs the question, do we need to make the relative attribute configurable as well?

    Possible relationship values listed here. Certainly there are a number of them that can be applied to the link tag:

    https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel

    for example 2 <link rel="preload" href="flower.avif" as="image" type="image/avif" /> we could have something like:

    "link": [ 
      "rel" : "preload",
      "href" : "[node:field_picture:entity:field_media_image]",
      "as" : "image",
      "on" : "null",
      "media" : "null",
      "crossorigin" : "null",
      "type": "image/avif",
    ]
    

    In this example it would also be good to have a token for the image type e.g. [node:field_picture:entity:field_media_image:type]

    we do have [node:field_image:entity:extension]

    for example 3 - cors enabled fetches

    e.g.

    <link
        rel="preload"
        href="fonts/cicle_fina-webfont.woff2"
        as="font"
        type="font/woff2"
        crossorigin />

    something like:

    "link": [ 
      "rel" : "preload",
      "href" : "fonts/cicle_fina-webfont.woff2",
      "as" : "font",
      "on" : "null",
      "media" : "null",
      "crossorigin" : "true",
      "type": "font/woff2",
    ]
    

    Finally for media e.g.

    <link
        rel="preload"
        href="bg-image-narrow.png"
        as="image"
        media="(max-width: 600px)" />
      <link
        rel="preload"
        href="bg-image-wide.png"
        as="image"
        media="(min-width: 601px)" />
    

    something like:

    "link": [ 
      "rel" : "preload",
      "href" : [node:field_image:large],
      "as" : "image",
      "on" : "null",
      "media" : ""(max-width: 600px)",
      "crossorigin" : "null",
      "type": "null",
    ]
    

    So here we have the challenge of multiple attributes all with the same name.

    So perhaps this might be a better schema.

    "link": [
      0 => [ 
        "rel" : "preload",
        "href" : [node:field_image:small],
        "as" : "image",
        "on" : "null",
        "media" : ""(max-width: 600px)",
        "crossorigin" : "null",
        "type": "null",
      1 => [
        "rel" : "preload",
        "href" : [node:field_image:large],
        "as" : "image",
        "on" : "null",
        "media" : "(min-width: 601px)",
        "crossorigin" : "null",
        "type": "null",
      ]
    ]
    

    So in this example we end up with something like:

    {
      "image_src": "[node:field_picture:entity:field_media_image]",
      "og_image": "[node:field_picture:entity:field_media_image]",
      "og_image_secure_url": "[node:field_picture:entity:field_media_image]",
      "og_image_url": "[node:field_picture:entity:field_media_image]",
      "twitter_cards_image": "[node:field_picture]",
      "link": [
        [ 
          "rel" : "preload",
          "href" : [node:field_image:small],
          "as" : "image",
          "on" : "null",
          "media" : "(max-width: 600px)",
          "crossorigin" : "null",
          "type": "null",
        ],
        [
          "rel" : "preload",
          "href" : [node:field_image:large],
          "as" : "image",
          "on" : "null",
          "media" : "(min-width: 601px)",
          "crossorigin" : "null",
          "type": "null",
        ]
      ]
    }
    
  • πŸ‡¬πŸ‡§United Kingdom 2dareis2do

    Looks like we have support for Link tag in /metatag/src/Plugin/metatag/Tag/LinkRelBase.php

    This is being used by metatag/metatag_favicons/src/Plugin/metatag/Tag/LinkSizesBase.php

    The pattern is different though. It looks like we have support for predefined fields that are not repeatable.

    In that sense this requirement is a bit like a custom meta tag in that we can define new attributes on the fly, rather than filling in one of several pre defined fields that are also relevant.

    The main difference with custom metatags is:

    1. that we assume the link tag is always the same.
    2. we support more than 2 attributes

    e.g of use of use of link tag uisng an attribute other than rel="preload"

    <link rel="icon" href="favicon.ico" />

    https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link

    list of all attributes supported:

    1. as
    2. blocking (experimental)
    3. crossorigin
    4. disabled
    5. fetchpriority
    6. href
    7. hreflang
    8. imagesizes
    9. imagesrcset
    10. integrity
    11. media
    12. referrerpolicy
    13. rel
    14. sizes
    15. title
    16. type

    Non standard

    1. methods
    2. target

    Obsolete

    1. charset
    2. rev

    Also link attribute supports use of global attributes:

    These are:

    1. accesskey
    2. autocapitalize
    3. autofocus
    4. class
    5. contenteditable
    6. data-*
    7. dir
    8. draggable
    9. enterkeyhint
    10. exportparts
    11. hidden
    12. id
    13. inert
    14. inputmode
    15. is
    16. itemid
    17. itemprop
    18. itemref
    19. itemscope
    20. itemtype
    21. lang
    22. nonce
    23. part
    24. popover
    25. role
    26. slot
    27. spellcheck
    28. style
    29. tabindex
    30. title (same as above )
    31. translate
    32. virtualkeyboardpolicy (experimental)

    https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes

    https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attributes

  • πŸ‡¬πŸ‡§United Kingdom 2dareis2do

    I should mention that the recommended use of some of these attributes is quite complicated.

    e.g.

    as
    This attribute is required when rel="preload" has been set on the
    element, optional when rel="modulepreload" has been set, and otherwise should not be used. It specifies the type of content being loaded by the
    , which is necessary for request matching, application of correct content security policy, and setting of correct Accept request header.

    Furthermore, rel="preload" uses this as a signal for request prioritization. The table below lists the valid values for this attribute and the elements or resources they apply to.

    Value Applies To
    audio elements
    document

    and elements embed elements fetch fetch, XHR Note: This value also requires to contain the crossorigin attribute, see CORS-enabled fetches. font CSS @font-face Note: This value also requires to contain the crossorigin attribute, see CORS-enabled fetches. image and elements with srcset or imageset attributes, SVG elements, CSS *-image rules object elements script elements, Worker importScripts style elements, CSS @import track elements video elements worker Worker, SharedWorker
  • πŸ‡¬πŸ‡§United Kingdom 2dareis2do
  • πŸ‡¬πŸ‡§United Kingdom 2dareis2do
  • πŸ‡¬πŸ‡§United Kingdom 2dareis2do
Production build 0.71.5 2024