changeset 776540959eac in www.tryton.org:default details: https://hg.tryton.org/www.tryton.org?cmd=changeset&node=776540959eac description: Include critical CSS to landing pages and load CSS asynchronously
issue11469 review413111003 diffstat: app.py | 41 +++++++++++++++++++++++++++++++++++++++-- critical-css/Dockerfile | 21 +++++++++++++++++++++ critical-css/critical.sh | 33 +++++++++++++++++++++++++++++++++ docker-compose.yml | 14 ++++++++++++++ templates/layout.html | 9 ++++++++- 5 files changed, 115 insertions(+), 3 deletions(-) diffs (196 lines): diff -r db7156b455ed -r 776540959eac app.py --- a/app.py Sat May 07 18:37:47 2022 +0200 +++ b/app.py Sat May 14 10:01:44 2022 +0200 @@ -13,7 +13,7 @@ from http import HTTPStatus from logging.handlers import SMTPHandler from operator import attrgetter -from random import sample, shuffle +from random import choice, sample, shuffle from urllib.parse import urljoin, urlparse import requests @@ -61,6 +61,8 @@ ('SISalp', [(45.903956, 6.099937), (43.132028, 5.935532)]), ('Virtual Things', [(48.13585, 11.577415), (50.775116, 6.083565)]), ] +CRITICAL_CSS_DIR = os.environ.get('CRITICAL_CSS') +CRITICAL_CSS_COOKIE = 'critical-css' cache = Cache(config={'CACHE_TYPE': 'simple'}) if os.environ.get('MEMCACHED'): @@ -121,7 +123,11 @@ def cache_key_prefix_view(): scheme = 'https' if request.is_secure else 'http' - return 'view/%s/%s' % (scheme, request.path) + if not request.cookies.get('critical-css'): + return 'view/%s/%s/%s' % ( + scheme, request.path, critical_css(timestamp=True)) + else: + return 'view/%s/%s' % (scheme, request.path) LinkHeader = namedtuple( @@ -216,6 +222,32 @@ return dict(url_for_canonical=url_for_canonical) +def critical_css(timestamp=False): + if (CRITICAL_CSS_DIR + and request.endpoint + and not request.cookies.get(CRITICAL_CSS_COOKIE)): + file = os.path.join(CRITICAL_CSS_DIR, request.endpoint + '.css') + if os.path.exists(file): + if timestamp: + return int(os.path.getmtime(file)) + else: + return open(file, 'r').read() + + +@app.after_request +def add_critical_css_cookie(response): + if (CRITICAL_CSS_DIR + and response.mimetype == 'text/html' + and not request.cookies.get(CRITICAL_CSS_COOKIE)): + response.set_cookie(CRITICAL_CSS_COOKIE, '1') + return response + + +@app.context_processor +def inject_critical_css(): + return dict(critical_css=critical_css) + + @cache.memoize(timeout=365 * 24 * 60 * 60) def dominant_color(path): if app.debug: @@ -523,6 +555,8 @@ @cache.cached(key_prefix=cache_key_prefix_view) @add_links(PRECONNECT_HEADERS + JS_LINK_HEADERS + CSS_LINK_HEADERS) def success_story(story): + if story == '_': + story = choice(CASES).name cases = [c for c in CASES if c.story or c.name == story] try: next_case = cases[(cases.index(story) + 1) % len(cases)] @@ -578,6 +612,9 @@ @cache.cached(key_prefix=cache_key_prefix_view) @add_links(PRECONNECT_HEADERS + JS_LINK_HEADERS + CSS_LINK_HEADERS) def event(event): + if event == '_': + event = 'layout' + class Day: def __init__(self, date, *events, location=None, full=False): if not isinstance(date, datetime.date): diff -r db7156b455ed -r 776540959eac critical-css/Dockerfile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/critical-css/Dockerfile Sat May 14 10:01:44 2022 +0200 @@ -0,0 +1,21 @@ +from node:15 +LABEL maintainer="Tryton <foundat...@tryton.org>" \ + org.label-schema.name="Tryton" \ + org.label-schema.url="http://www.tryton.org/" \ + org.label-schema.vendor="Tryton" + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + libasound2 \ + libatk-bridge2.0 \ + libgtk-3-0 \ + libnss3 \ + libx11-xcb1 \ + libxss1 \ + libxtst6 \ + && rm -rf /var/lib/apt/lists/* + +RUN npm install -g critical + +COPY critical.sh / +CMD ["/critical.sh"] diff -r db7156b455ed -r 776540959eac critical-css/critical.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/critical-css/critical.sh Sat May 14 10:01:44 2022 +0200 @@ -0,0 +1,33 @@ +#!/bin/bash + +set -e + +: ${OUTPUT:='/tmp'} +: ${SITE:='localhost'} + +for page in \ + "/ index" \ + "/success-stories success_stories" \ + "/success_stories/_ success_story" \ + "/download download" \ + "/forum forum" \ + "/presentations presentations" \ + "/events/_ event" \ + "/contribute contribute" \ + "/develop develop" \ + "/develop/guidelines/code guidelines_code" \ + "/develop/guidelines/documentation guidelines_documentation" \ + "/develop/guidelines/help-text guidelines_documentation_help" \ + "/develop/guidelines/howto guidelines_documentation_howto" \ + "/foundation foundation" \ + "/supporters supporters" \ + "/donate donate" \ + "/service-providers service_providers" \ + "/service-providers/start service_providers_start" \ + "/not_found not_found" +do + set -- ${page} + critical http://${SITE}${1} > "${OUTPUT}/${2}.css" +done + +tail -f /dev/null diff -r db7156b455ed -r 776540959eac docker-compose.yml --- a/docker-compose.yml Sat May 07 18:37:47 2022 +0200 +++ b/docker-compose.yml Sat May 14 10:01:44 2022 +0200 @@ -7,6 +7,9 @@ - website.env environment: - MEMCACHED=memcached + - CRITICAL_CSS=/critical-css + volumes: + - critical-css:/critical-css ports: - "127.0.0.1:5000:5000" restart: unless-stopped @@ -18,3 +21,14 @@ ports: - "127.0.0.1:11211:11211" restart: unless-stopped + critical-css: + build: critical-css + environment: + - OUTPUT=/critical-css + - SITE=website:5000 + volumes: + - critical-css:/critical-css + depends_on: + - website +volumes: + critical-css: diff -r db7156b455ed -r 776540959eac templates/layout.html --- a/templates/layout.html Sat May 07 18:37:47 2022 +0200 +++ b/templates/layout.html Sat May 14 10:01:44 2022 +0200 @@ -36,8 +36,15 @@ {% endif %} {% block style %} - <link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}"/> + {% set css = critical_css() %} + {% if css %} + <style type="text/css"> + {{ css|safe }} + </style> + {% endif %} + <link rel="preload" href="{{ url_for('static', filename='css/main.css') }}" as="style" onload="this.onload=null;this.rel='stylesheet'"> <noscript> + <link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}"/> <link rel="stylesheet" href="{{ url_for('static', filename='css/noscript.css') }}"/> </noscript> {% endblock style %}