{"id":55966,"date":"2021-01-29T09:00:55","date_gmt":"2021-01-29T17:00:55","guid":{"rendered":"https:\/\/github.blog\/?p=55966"},"modified":"2021-02-11T12:11:10","modified_gmt":"2021-02-11T20:11:10","slug":"making-githubs-new-homepage-fast-and-performant","status":"publish","type":"post","link":"https:\/\/github.blog\/engineering\/user-experience\/making-githubs-new-homepage-fast-and-performant\/","title":{"rendered":"Making GitHub\u2019s new homepage fast and performant"},"content":{"rendered":"<p>This post is the third installment of our five-part series on building GitHub\u2019s new homepage:<\/p>\n<ol>\n<li><a href=\"https:\/\/github.blog\/2020-12-21-how-we-built-the-github-globe\/\">How our globe is built<\/a><\/li>\n<li><a href=\"https:\/\/github.blog\/2020-12-21-visualizing-githubs-global-community\/\">How we collect and use the data behind the globe<\/a><\/li>\n<li><a href=\"https:\/\/github.blog\/2021-01-29-making-githubs-new-homepage-fast-and-performant\/\">How we made the page fast and performant<\/a><\/li>\n<li><a href=\"https:\/\/github.blog\/2021-02-04-how-we-illustrate-at-github\/\">How we illustrate at GitHub<\/a><\/li>\n<li><a href=\"https:\/\/github.blog\/2021-02-11-how-we-designed-and-wrote-the-narrative-for-our-homepage\/\" rel=\"noopener\" target=\"_blank\">How we designed the homepage and wrote the narrative<\/a><\/li>\n<\/ol>\n<p>Creating a page full of product shots, animations, and videos that still loads fast and performs well can be tricky. Throughout the process of building <a href=\"https:\/\/github.com\/home\">GitHub\u2019s new homepage<\/a>, we\u2019ve used the <a href=\"https:\/\/web.dev\/vitals\/\">Core Web Vitals<\/a> as one of our North Stars and measuring sticks. There are many <a href=\"https:\/\/web.dev\/optimize-lcp\/\">different<\/a> <a href=\"https:\/\/web.dev\/optimize-fid\/\">ways<\/a> of <a href=\"https:\/\/web.dev\/optimize-cls\/\">optimizing<\/a> for these metrics, and we\u2019ve already written about <a href=\"https:\/\/github.blog\/2020-12-21-how-we-built-the-github-globe\/#creative-constraints-from-performance-optimizations\">how we optimized our WebGL globe<\/a>. We\u2019re going to take a deep-dive here into two of the strategies that produced the overall biggest performance impact for us: crafting high performance animations and serving the perfect image.<\/p>\n<h2 id=\"high-performance-animation-and-interactivity\"><a class=\"heading-link\" href=\"#high-performance-animation-and-interactivity\">High performance animation and interactivity<span class=\"heading-hash pl-2 text-italic text-bold\" aria-hidden=\"true\"><\/span><\/a><\/h2>\n<p>As you scroll down the GitHub homepage, we animate in certain elements to bring your attention to them:<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" src=\"https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/106009518-b6ad6d00-60b8-11eb-980d-061d5f414a37.gif\" \/><\/p>\n<p>Traditionally, a typical way of building this relied on listening to the scroll event, calculating the visibility of all elements that you\u2019re tracking, and triggering animations depending on the elements\u2019 position in the viewport:<\/p>\n<pre><code class=\"javascript\">\/\/ Old-school scroll event listening (avoid)\nwindow.addEventListener('scroll', () =&gt; checkForVisibility)\nwindow.addEventListener('resize', () =&gt; checkForVisibility)\n\nfunction checkForVisibility() {\n  animatedElements.map(element =&gt; {\n    const distTop = element.getBoundingClientRect().top\n    const distBottom = element.getBoundingClientRect().bottom\n    const distPercentTop = Math.round((distTop \/ window.innerHeight) * 100)\n    const distPercentBottom = Math.round((distBottom \/ window.innerHeight) * 100)\n    \/\/ Based on this position, animate element accordingly\n  }\n}\n<\/code><\/pre>\n<p>There\u2019s at least one big problem with an approach like this: calls to getBoundingClientRect() will <a href=\"https:\/\/gist.github.com\/paulirish\/5d52fb081b3570c81e3a\">trigger reflows<\/a>, and utilizing this technique might quickly create a performance bottleneck.<\/p>\n<p>Luckily, <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Intersection_Observer_API\"><code>IntersectionObservers<\/code><\/a> are <a href=\"https:\/\/caniuse.com\/intersectionobserver\">supported in all modern browsers<\/a>, and they can be set up to notify you of an element&#8217;s position in the viewport, without ever listening to scroll events, or without calling getBoundingClientRect.\u00a0 An IntersectionObserver can be set up in just a few lines of code to track if an element is shown in the viewport, and trigger animations depending on its state, using each entry\u2019s isIntersecting method:<\/p>\n<pre><code class=\"javascript\">\/\/ Create an intersection observer with default options, that \n\/\/ triggers a class on\/off depending on an element\u2019s visibility \n\/\/ in the viewport\nconst animationObserver = new IntersectionObserver((entries, observer) =&gt; {\n  for (const entry of entries) {\n    entry.target.classList.toggle('build-in-animate', entry.isIntersecting)\n  }\n});\n\n\/\/ Use that IntersectionObserver to observe the visibility\n\/\/ of some elements\nfor (const element of querySelectorAll('.js-build-in')) {\n  animationObserver.observe(element);\n}\n<\/code><\/pre>\n<h3 id=\"avoiding-animation-pollution\"><a class=\"heading-link\" href=\"#avoiding-animation-pollution\">Avoiding animation pollution<span class=\"heading-hash pl-2 text-italic text-bold\" aria-hidden=\"true\"><\/span><\/a><\/h3>\n<p>As we moved over to <code>IntersectionObservers<\/code> for our animations, we also went through all of our animations and doubled down on one of the core tenets of optimizing animations: only animate the transform and opacity properties, since these properties are <a href=\"https:\/\/www.html5rocks.com\/en\/tutorials\/speed\/high-performance-animations\/\">easier for browsers to animate<\/a> (generally computationally less expensive). We thought we did a fairly good job of following this principle already, but we discovered that in some circumstances we did not, because unexpected properties were bleeding into our transitions and polluting them as elements changed state.<\/p>\n<p>One might think a reasonable implementation of the \u201conly animate transform and opacity\u201d principle might be to define a transition in CSS like so:<\/p>\n<pre><code class=\"javascript\">\/\/ Don\u2019t do this\n.animated {\n  opacity: 0;\n  transform: translateY(10px);\n  transition: * 0.6s ease;\n}\n\n.animated:hover {\n  opacity: 1;\n  transform: translateY(0);\n}\n<\/code><\/pre>\n<p>In other words, we\u2019re only explicitly changing opacity and transform, but we\u2019re defining the transition to animate all changed properties. These transitions can lead to poor performance since other property changes can pollute the transition (you may have a global style that changes the text color on hover, for example), which can cause unnecessary style and layout calculations. To avoid this kind of animation pollution, we moved to always explicitly defining only opacity and transform as animatable:<\/p>\n<pre><code class=\"javascript\">\/\/ Be explicit about what can animate (and not)\n.animated {\n  opacity: 0;\n  transform: translateY(10px);\n  transition: opacity 0.6s ease, transform 0.6s ease;\n}\n\n.animated:hover {\n  opacity: 0;\n  transform: translateY(0);\n}\n<\/code><\/pre>\n<p>As we rebuilt all of our animations to be triggered through IntersectionObservers and to explicitly specify only opacity and transform as animatable, we saw a drastic decrease in CPU usage and style recalculations, helping to improve our Cumulative Layout Shift score:<\/p>\n<p><img data-recalc-dims=\"1\" decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-55970\" src=\"https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/106009495-af865f00-60b8-11eb-84de-b28f3281f5de.png?resize=1024%2C654\" alt=\"\" width=\"1024\" height=\"654\" srcset=\"https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/106009495-af865f00-60b8-11eb-84de-b28f3281f5de.png?w=1280 1280w, https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/106009495-af865f00-60b8-11eb-84de-b28f3281f5de.png?w=300 300w, https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/106009495-af865f00-60b8-11eb-84de-b28f3281f5de.png?w=768 768w, https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/106009495-af865f00-60b8-11eb-84de-b28f3281f5de.png?w=1024 1024w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/p>\n<h3 id=\"lazy-loading-videos-with-intersectionobservers\"><a class=\"heading-link\" href=\"#lazy-loading-videos-with-intersectionobservers\">Lazy-loading videos with <code>IntersectionObservers<\/code><span class=\"heading-hash pl-2 text-italic text-bold\" aria-hidden=\"true\"><\/span><\/a><\/h3>\n<p>If you\u2019re powering any animations through video elements, you likely want to do two things: only play the video while it\u2019s visible in the viewport, and lazy-load the video when it\u2019s needed. Sadly, <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/Performance\/Lazy_loading#images_and_iframes\">the lazy load attribute<\/a> doesn\u2019t work on videos, but if we use <code>IntersectionObservers<\/code> to play videos as they appear in the viewport, we can get both of these features in one go:<\/p>\n<pre><code class=\"javascript\">&lt;!-- HTML: A video that plays inline, muted, w\/o autoplay &amp; preload --&gt;\n&lt;video loop muted playsinline preload=\"none\" class=\"js-viewport-aware-video\" poster=\"video-first-frame.jpg\"&gt;\n  &lt;source type=\"video\/mp4\" src=\"video.h264.mp4\"&gt;\n&lt;\/video&gt;\n\n\/\/ JS: Play videos while they are visible in the viewport\nconst videoObserver = new IntersectionObserver((entries, observer) =&gt; {\n  for (const entry of entries) entry.isIntersecting ? video.play() : video.pause();\n});\n\nfor (const element of querySelectorAll('.js-viewport-aware-video')) {\n  videoObserver.observe(element);\n}\n<\/code><\/pre>\n<p>Together with setting <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Element\/video\">preload<\/a> to none, this simple observer setup saves us several megabytes on each page load.<\/p>\n<h2 id=\"serving-the-perfect-image\"><a class=\"heading-link\" href=\"#serving-the-perfect-image\">Serving the perfect image<span class=\"heading-hash pl-2 text-italic text-bold\" aria-hidden=\"true\"><\/span><\/a><\/h2>\n<p>We visit web pages with a myriad of different devices, screens and browsers, and something simple as displaying an image is becoming increasingly complex if you want to cover all bases. Our particular illustration style also happens to fall between all of the classic JPG, PNG or SVG formats. Take this illustration, for example, that we use to transition from the main narrative to the footer:<\/p>\n<p><img data-recalc-dims=\"1\" decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-55971\" src=\"https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/106011272-99799e00-60ba-11eb-8c47-b636c1619353.jpg?resize=1024%2C561\" alt=\"\" width=\"1024\" height=\"561\" srcset=\"https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/106011272-99799e00-60ba-11eb-8c47-b636c1619353.jpg?w=1280 1280w, https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/106011272-99799e00-60ba-11eb-8c47-b636c1619353.jpg?w=300 300w, https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/106011272-99799e00-60ba-11eb-8c47-b636c1619353.jpg?w=768 768w, https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/106011272-99799e00-60ba-11eb-8c47-b636c1619353.jpg?w=1024 1024w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/p>\n<p>To render this illustration, we would ideally need the transparency from PNGs but combine it with the compression from JPGs, as saving an illustration like this as a PNG would weigh in at several megabytes. Luckily, <a href=\"https:\/\/developers.google.com\/speed\/webp\">WebP<\/a> is, as of iOS 14 and macOS Big Sur, supported in Safari on both desktops and phones, which brings <a href=\"https:\/\/caniuse.com\/webp\">browser support up to a solid +90%<\/a>. WebP does in fact give us the best of both worlds: we can create compressed, lossy images with transparency. What about support for older browsers? Even a new Mac running the latest version of Safari on macOS Catalina can\u2019t render WebP images, so we have to do <i>something<\/i>.<\/p>\n<p>This challenge eventually led us to develop a somewhat obscure solution: two JPGs embedded in an SVG (one for the image data and one for the mask), embedded as base64 data\u2014essentially creating a transparent JPG with one single HTTP request. Take a look at <a href=\"https:\/\/github.githubassets.com\/images\/modules\/site\/home\/footer-illustration.svg\">this image<\/a>. Download it, open it up, and inspect it. Yes, it\u2019s a JPG with transparency, encoded in base64, wrapped in an SVG.<\/p>\n<p>Part of the SVG specification is the <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/SVG\/Element\/mask\">mask element<\/a>. With it, you can mask out parts of an SVG. If we embed an SVG in a document, we can use the mask element in tandem with the image element to render an image with transparency:<\/p>\n<pre><code class=\"html\">&lt;svg viewBox=\"0 0 300 300\"&gt;\n  &lt;defs&gt;\n    &lt;mask id=\"mask\"&gt;\n      &lt;image width=\"300\" height=\"300\" href=\"mask.jpg\"&gt;&lt;\/image&gt;\n    &lt;\/mask&gt;\n  &lt;\/defs&gt;\n  &lt;image mask=\"url(#mask)\" width=\"300\" height=\"300\" href=\"image.jpg\"&gt;&lt;\/image&gt;\n&lt;\/svg&gt;\n<\/code><\/pre>\n<p>This is great, but it won\u2019t work as a fallback for WebP. Since the paths for these images are dynamic (see href in the example above), the SVG needs to be embedded inside the document. If we instead save this SVG in a file and set it as the src of a regular img, the images won\u2019t be loaded, and we\u2019ll see nothing.<\/p>\n<p>We can work around this limitation by embedding the image data inside the SVG as <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Glossary\/Base64\">base64<\/a>. There are services online where you can convert an image to base64, but if you\u2019re on a Mac, base64 is available by default in your Terminal, and you can use it like so:<\/p>\n<pre><code>base64 -i &lt;in-file&gt; -o &lt;outfile&gt;<\/code><\/pre>\n<p>Where the in-file is your image of choice, the outfile is a text file where you\u2019ll save the base64 data. With this technique, we can embed the images inside of the SVG and use the SVG as a src on a regular image.<\/p>\n<p>These are the two images that we\u2019re using to construct the footer illustration\u2014one for the image data and one for the mask (black is completely transparent and white is fully opaque):<\/p>\n<p><img data-recalc-dims=\"1\" decoding=\"async\" loading=\"lazy\" class=\"aligncenter size-full wp-image-55972\" src=\"https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/106009473-ab5a4180-60b8-11eb-8da9-dd432a5c832b.jpg?resize=1024%2C606\" alt=\"\" width=\"1024\" height=\"606\" srcset=\"https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/106009473-ab5a4180-60b8-11eb-8da9-dd432a5c832b.jpg?w=1280 1280w, https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/106009473-ab5a4180-60b8-11eb-8da9-dd432a5c832b.jpg?w=300 300w, https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/106009473-ab5a4180-60b8-11eb-8da9-dd432a5c832b.jpg?w=768 768w, https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/106009473-ab5a4180-60b8-11eb-8da9-dd432a5c832b.jpg?w=1024 1024w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/p>\n<p>We convert the mask and the image to base64 using the Terminal command and then paste the data into the SVG:<\/p>\n<pre><code class=\"plain\">&lt;svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"0 0 2900 1494\"&gt;\n  &lt;defs&gt;\n    &lt;mask id=\"mask\"&gt;\n      &lt;image width=\"300\" height=\"300\" href=\"data:image\/png;base64,\/* your image in base64 *\/\u201d&gt;&lt;\/image&gt;\n    &lt;\/mask&gt;\n  &lt;\/defs&gt;\n  &lt;image mask=\"url(#mask)\" width=\"300\" height=\"300\" href=\"data:image\/jpeg;base64,\/* your image in base64 *\/\u201d&gt;&lt;\/image&gt;\n&lt;\/svg&gt;\n<\/code><\/pre>\n<p>You can save that SVG and use it like any regular image. We can then safely use WebP with lazy loading and a solid fallback that works in all browsers:<\/p>\n<pre><code class=\"html\">&lt;picture&gt;\n  &lt;source srcset=\"compressed-transparent-image.webp\" type=\"image\/webp\"&gt;\n  &lt;img src=\"compressed-transparent-image.svg\" loading=\"lazy\"&gt;\n&lt;\/picture&gt;\n<\/code><\/pre>\n<p>This somewhat obscure SVG hack saves us hundreds of kilobytes on each page load, and it enables us to utilize the latest technologies for the browsers and operating systems that support them.<\/p>\n<h2 id=\"towards-a-faster-web\"><a class=\"heading-link\" href=\"#towards-a-faster-web\">Towards a faster web<span class=\"heading-hash pl-2 text-italic text-bold\" aria-hidden=\"true\"><\/span><\/a><\/h2>\n<p>We\u2019re working throughout the company to create a faster and more reliable GitHub, and these are some of the techniques that we\u2019re utilizing. We still have a long way to go, and if you\u2019d like to be part of that journey, <a href=\"https:\/\/github.com\/about\/careers\">check out our careers page<\/a>.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post is the third installment of our five-part series on building GitHub\u2019s new homepage: How our globe is built How we collect and use the data behind the globe&hellip;<\/p>\n","protected":false},"author":1466,"featured_media":55990,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_gh_post_show_toc":"","_gh_post_is_no_robots":"","_gh_post_is_featured":"","_gh_post_is_excluded":"","_gh_post_is_unlisted":"","_gh_post_related_link_1":"","_gh_post_related_link_2":"","_gh_post_related_link_3":"","_gh_post_sq_img":"","_gh_post_sq_img_id":"","_gh_post_cta_title":"","_gh_post_cta_text":"","_gh_post_cta_link":"","_gh_post_cta_button":"","_gh_post_recirc_hide":"","_gh_post_recirc_col_1":"","_gh_post_recirc_col_2":"","_gh_post_recirc_col_3":"","_gh_post_recirc_col_4":"","_featured_video":"","_gh_post_additional_query_params":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"{title}\n\n{excerpt}\n\n{url}","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"_wpas_customize_per_network":false,"jetpack_post_was_ever_published":false,"_links_to":"","_links_to_target":""},"categories":[72,3312],"tags":[1708],"coauthors":[],"class_list":["post-55966","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-engineering","category-user-experience","tag-homepage-design"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v28.6 (Yoast SEO v28.6) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Making GitHub\u2019s new homepage fast and performant - The GitHub Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/github.blog\/engineering\/user-experience\/making-githubs-new-homepage-fast-and-performant\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Making GitHub\u2019s new homepage fast and performant\" \/>\n<meta property=\"og:description\" content=\"This post is the third installment of our five-part series on building GitHub\u2019s new homepage: How our globe is built How we collect and use the data behind the globe&hellip;\" \/>\n<meta property=\"og:url\" content=\"https:\/\/github.blog\/engineering\/user-experience\/making-githubs-new-homepage-fast-and-performant\/\" \/>\n<meta property=\"og:site_name\" content=\"The GitHub Blog\" \/>\n<meta property=\"article:published_time\" content=\"2021-01-29T17:00:55+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2021-02-11T20:11:10+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/102393310-07478b80-3f8d-11eb-84eb-392d555ebd29.png?fit=1200%2C630\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Tobias Ahlin\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/102393310-07478b80-3f8d-11eb-84eb-392d555ebd29.png?fit=1200%2C630\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Tobias Ahlin\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/github.blog\\\/engineering\\\/user-experience\\\/making-githubs-new-homepage-fast-and-performant\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/github.blog\\\/engineering\\\/user-experience\\\/making-githubs-new-homepage-fast-and-performant\\\/\"},\"author\":{\"name\":\"Tobias Ahlin\",\"@id\":\"https:\\\/\\\/github.blog\\\/#\\\/schema\\\/person\\\/8270382fcf3dd07e211123ed216ac1c0\"},\"headline\":\"Making GitHub\u2019s new homepage fast and performant\",\"datePublished\":\"2021-01-29T17:00:55+00:00\",\"dateModified\":\"2021-02-11T20:11:10+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/github.blog\\\/engineering\\\/user-experience\\\/making-githubs-new-homepage-fast-and-performant\\\/\"},\"wordCount\":1261,\"image\":{\"@id\":\"https:\\\/\\\/github.blog\\\/engineering\\\/user-experience\\\/making-githubs-new-homepage-fast-and-performant\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/github.blog\\\/wp-content\\\/uploads\\\/2021\\\/01\\\/102393310-07478b80-3f8d-11eb-84eb-392d555ebd29.png?fit=1200%2C630\",\"keywords\":[\"homepage design\"],\"articleSection\":[\"Engineering\",\"User experience\"],\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/github.blog\\\/engineering\\\/user-experience\\\/making-githubs-new-homepage-fast-and-performant\\\/\",\"url\":\"https:\\\/\\\/github.blog\\\/engineering\\\/user-experience\\\/making-githubs-new-homepage-fast-and-performant\\\/\",\"name\":\"Making GitHub\u2019s new homepage fast and performant - The GitHub Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/github.blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/github.blog\\\/engineering\\\/user-experience\\\/making-githubs-new-homepage-fast-and-performant\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/github.blog\\\/engineering\\\/user-experience\\\/making-githubs-new-homepage-fast-and-performant\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/github.blog\\\/wp-content\\\/uploads\\\/2021\\\/01\\\/102393310-07478b80-3f8d-11eb-84eb-392d555ebd29.png?fit=1200%2C630\",\"datePublished\":\"2021-01-29T17:00:55+00:00\",\"dateModified\":\"2021-02-11T20:11:10+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/github.blog\\\/#\\\/schema\\\/person\\\/8270382fcf3dd07e211123ed216ac1c0\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/github.blog\\\/engineering\\\/user-experience\\\/making-githubs-new-homepage-fast-and-performant\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/github.blog\\\/engineering\\\/user-experience\\\/making-githubs-new-homepage-fast-and-performant\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/github.blog\\\/engineering\\\/user-experience\\\/making-githubs-new-homepage-fast-and-performant\\\/#primaryimage\",\"url\":\"https:\\\/\\\/github.blog\\\/wp-content\\\/uploads\\\/2021\\\/01\\\/102393310-07478b80-3f8d-11eb-84eb-392d555ebd29.png?fit=1200%2C630\",\"contentUrl\":\"https:\\\/\\\/github.blog\\\/wp-content\\\/uploads\\\/2021\\\/01\\\/102393310-07478b80-3f8d-11eb-84eb-392d555ebd29.png?fit=1200%2C630\",\"width\":1200,\"height\":630},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/github.blog\\\/engineering\\\/user-experience\\\/making-githubs-new-homepage-fast-and-performant\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/github.blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Engineering\",\"item\":\"https:\\\/\\\/github.blog\\\/engineering\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"User experience\",\"item\":\"https:\\\/\\\/github.blog\\\/engineering\\\/user-experience\\\/\"},{\"@type\":\"ListItem\",\"position\":4,\"name\":\"Making GitHub\u2019s new homepage fast and performant\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/github.blog\\\/#website\",\"url\":\"https:\\\/\\\/github.blog\\\/\",\"name\":\"The GitHub Blog\",\"description\":\"Updates, ideas, and inspiration from GitHub to help developers build and design software.\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/github.blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/github.blog\\\/#\\\/schema\\\/person\\\/8270382fcf3dd07e211123ed216ac1c0\",\"name\":\"Tobias Ahlin\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/6ecb90392fb1900a55daebd8324d4b597e61f05d7c5e22b61c99beff0e0b29a9?s=96&d=mm&r=g4e064b4023f3a970d0fad4d6fbb60349\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/6ecb90392fb1900a55daebd8324d4b597e61f05d7c5e22b61c99beff0e0b29a9?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/6ecb90392fb1900a55daebd8324d4b597e61f05d7c5e22b61c99beff0e0b29a9?s=96&d=mm&r=g\",\"caption\":\"Tobias Ahlin\"},\"sameAs\":[\"http:\\\/\\\/tobiasahlin.com\"],\"url\":\"https:\\\/\\\/github.blog\\\/author\\\/tobiasahlin\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Making GitHub\u2019s new homepage fast and performant - The GitHub Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/github.blog\/engineering\/user-experience\/making-githubs-new-homepage-fast-and-performant\/","og_locale":"en_US","og_type":"article","og_title":"Making GitHub\u2019s new homepage fast and performant","og_description":"This post is the third installment of our five-part series on building GitHub\u2019s new homepage: How our globe is built How we collect and use the data behind the globe&hellip;","og_url":"https:\/\/github.blog\/engineering\/user-experience\/making-githubs-new-homepage-fast-and-performant\/","og_site_name":"The GitHub Blog","article_published_time":"2021-01-29T17:00:55+00:00","article_modified_time":"2021-02-11T20:11:10+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/102393310-07478b80-3f8d-11eb-84eb-392d555ebd29.png?fit=1200%2C630","type":"image\/png"}],"author":"Tobias Ahlin","twitter_card":"summary_large_image","twitter_image":"https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/102393310-07478b80-3f8d-11eb-84eb-392d555ebd29.png?fit=1200%2C630","twitter_misc":{"Written by":"Tobias Ahlin","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/github.blog\/engineering\/user-experience\/making-githubs-new-homepage-fast-and-performant\/#article","isPartOf":{"@id":"https:\/\/github.blog\/engineering\/user-experience\/making-githubs-new-homepage-fast-and-performant\/"},"author":{"name":"Tobias Ahlin","@id":"https:\/\/github.blog\/#\/schema\/person\/8270382fcf3dd07e211123ed216ac1c0"},"headline":"Making GitHub\u2019s new homepage fast and performant","datePublished":"2021-01-29T17:00:55+00:00","dateModified":"2021-02-11T20:11:10+00:00","mainEntityOfPage":{"@id":"https:\/\/github.blog\/engineering\/user-experience\/making-githubs-new-homepage-fast-and-performant\/"},"wordCount":1261,"image":{"@id":"https:\/\/github.blog\/engineering\/user-experience\/making-githubs-new-homepage-fast-and-performant\/#primaryimage"},"thumbnailUrl":"https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/102393310-07478b80-3f8d-11eb-84eb-392d555ebd29.png?fit=1200%2C630","keywords":["homepage design"],"articleSection":["Engineering","User experience"],"inLanguage":"en-US"},{"@type":"WebPage","@id":"https:\/\/github.blog\/engineering\/user-experience\/making-githubs-new-homepage-fast-and-performant\/","url":"https:\/\/github.blog\/engineering\/user-experience\/making-githubs-new-homepage-fast-and-performant\/","name":"Making GitHub\u2019s new homepage fast and performant - The GitHub Blog","isPartOf":{"@id":"https:\/\/github.blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/github.blog\/engineering\/user-experience\/making-githubs-new-homepage-fast-and-performant\/#primaryimage"},"image":{"@id":"https:\/\/github.blog\/engineering\/user-experience\/making-githubs-new-homepage-fast-and-performant\/#primaryimage"},"thumbnailUrl":"https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/102393310-07478b80-3f8d-11eb-84eb-392d555ebd29.png?fit=1200%2C630","datePublished":"2021-01-29T17:00:55+00:00","dateModified":"2021-02-11T20:11:10+00:00","author":{"@id":"https:\/\/github.blog\/#\/schema\/person\/8270382fcf3dd07e211123ed216ac1c0"},"breadcrumb":{"@id":"https:\/\/github.blog\/engineering\/user-experience\/making-githubs-new-homepage-fast-and-performant\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/github.blog\/engineering\/user-experience\/making-githubs-new-homepage-fast-and-performant\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/github.blog\/engineering\/user-experience\/making-githubs-new-homepage-fast-and-performant\/#primaryimage","url":"https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/102393310-07478b80-3f8d-11eb-84eb-392d555ebd29.png?fit=1200%2C630","contentUrl":"https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/102393310-07478b80-3f8d-11eb-84eb-392d555ebd29.png?fit=1200%2C630","width":1200,"height":630},{"@type":"BreadcrumbList","@id":"https:\/\/github.blog\/engineering\/user-experience\/making-githubs-new-homepage-fast-and-performant\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/github.blog\/"},{"@type":"ListItem","position":2,"name":"Engineering","item":"https:\/\/github.blog\/engineering\/"},{"@type":"ListItem","position":3,"name":"User experience","item":"https:\/\/github.blog\/engineering\/user-experience\/"},{"@type":"ListItem","position":4,"name":"Making GitHub\u2019s new homepage fast and performant"}]},{"@type":"WebSite","@id":"https:\/\/github.blog\/#website","url":"https:\/\/github.blog\/","name":"The GitHub Blog","description":"Updates, ideas, and inspiration from GitHub to help developers build and design software.","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/github.blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/github.blog\/#\/schema\/person\/8270382fcf3dd07e211123ed216ac1c0","name":"Tobias Ahlin","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/6ecb90392fb1900a55daebd8324d4b597e61f05d7c5e22b61c99beff0e0b29a9?s=96&d=mm&r=g4e064b4023f3a970d0fad4d6fbb60349","url":"https:\/\/secure.gravatar.com\/avatar\/6ecb90392fb1900a55daebd8324d4b597e61f05d7c5e22b61c99beff0e0b29a9?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/6ecb90392fb1900a55daebd8324d4b597e61f05d7c5e22b61c99beff0e0b29a9?s=96&d=mm&r=g","caption":"Tobias Ahlin"},"sameAs":["http:\/\/tobiasahlin.com"],"url":"https:\/\/github.blog\/author\/tobiasahlin\/"}]}},"jetpack_publicize_connections":[],"jetpack_shortlink":"https:\/\/wp.me\/pamS32-eyG","jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/github.blog\/wp-content\/uploads\/2021\/01\/102393310-07478b80-3f8d-11eb-84eb-392d555ebd29.png?fit=1200%2C630","_links":{"self":[{"href":"https:\/\/github.blog\/wp-json\/wp\/v2\/posts\/55966","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/github.blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/github.blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/github.blog\/wp-json\/wp\/v2\/users\/1466"}],"replies":[{"embeddable":true,"href":"https:\/\/github.blog\/wp-json\/wp\/v2\/comments?post=55966"}],"version-history":[{"count":22,"href":"https:\/\/github.blog\/wp-json\/wp\/v2\/posts\/55966\/revisions"}],"predecessor-version":[{"id":56234,"href":"https:\/\/github.blog\/wp-json\/wp\/v2\/posts\/55966\/revisions\/56234"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/github.blog\/wp-json\/wp\/v2\/media\/55990"}],"wp:attachment":[{"href":"https:\/\/github.blog\/wp-json\/wp\/v2\/media?parent=55966"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/github.blog\/wp-json\/wp\/v2\/categories?post=55966"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/github.blog\/wp-json\/wp\/v2\/tags?post=55966"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/github.blog\/wp-json\/wp\/v2\/coauthors?post=55966"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}