gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-taler-merchant-demos] branch master updated (ad45c1d -> 2e66581)


From: gnunet
Subject: [taler-taler-merchant-demos] branch master updated (ad45c1d -> 2e66581)
Date: Sat, 10 Oct 2020 22:55:34 +0200

This is an automated email from the git hooks/post-receive script.

grothoff pushed a change to branch master
in repository taler-merchant-demos.

    from ad45c1d  work around lang undefined from branch merge
     new d6459ab  towards supporting language switching
     new 3b46ba1  Re-Designed blog
     new 0f5e458  add license
     new 05a99e0  Added readme stuff, ad messed with css, and other stuff, and 
more stuff.
     new 2d67ccf  fixed thing in readme
     new 7c08b69  language switcher now works
     new 3b378dd  removed a br to make it look better
     new 06c708d  fix merge issue
     new 728c4bf  add logic for repurchase detection, adjusting order_id if 
provided
     new 41f611f  Moved css to scss (See readme for build instructions)
     new 0de3b1f  stuff?
     new 764738b  fixed scss complaining
     new 3f494eb  gitignore changes
     new 2f4403f  gitignore changes: add newline towards eof
     new 61edc48  Merge branch 'torsten-redesign' of 
git+ssh://git.taler.net/taler-merchant-demos into torsten-redesign
     new 2fcce20  front-port Dold patch from master
     new 58af977  fix logic to match spec changes of #6616
     new 2e66581  merge torsten-redesign branch, implement i18n support

The 18 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .gitignore                                         |   3 +-
 Makefile                                           |  41 +++++-
 README.md                                          |  33 ++++-
 setup.py                                           |   1 +
 talermerchantdemos/.gitignore                      |   6 +
 talermerchantdemos/.vscode/launch.json             |  15 +++
 talermerchantdemos/blog/articles/scrap1_11.html    |   1 -
 talermerchantdemos/blog/blog.py                    | 139 ++++++++++++++++-----
 talermerchantdemos/blog/content.py                 |  13 +-
 .../blog/templates/article_frame.html              |  13 --
 .../blog/templates/article_frame.html.j2           |  15 +++
 .../blog/templates/article_refunded.html           |  10 --
 .../blog/templates/article_refunded.html.j2        |  16 +++
 talermerchantdemos/blog/templates/base.html        |  79 ------------
 talermerchantdemos/blog/templates/base.html.j2     | 119 ++++++++++++++++++
 .../blog/templates/confirm_refund.html             |  19 ---
 .../blog/templates/confirm_refund.html.j2          |  22 ++++
 talermerchantdemos/blog/templates/error.html       |  22 ----
 talermerchantdemos/blog/templates/error.html.j2    |  24 ++++
 talermerchantdemos/blog/templates/index.html       |  40 ------
 talermerchantdemos/blog/templates/show_refund.html |  28 -----
 talermerchantdemos/static/__init__.py              |   1 +
 talermerchantdemos/static/{demo.css => demo.scss}  |  27 ++--
 talermerchantdemos/static/navbar.css               |  75 +++++++++++
 talermerchantdemos/static/navbar.css.map           |   1 +
 talermerchantdemos/static/navbar.scss              | 103 +++++++++++++++
 talermerchantdemos/static/{pure.css => pure.scss}  |   2 +-
 .../{taler-fallback.css => taler-fallback.scss}    |   0
 28 files changed, 606 insertions(+), 262 deletions(-)
 create mode 100644 talermerchantdemos/.gitignore
 create mode 100644 talermerchantdemos/.vscode/launch.json
 delete mode 100644 talermerchantdemos/blog/templates/article_frame.html
 create mode 100644 talermerchantdemos/blog/templates/article_frame.html.j2
 delete mode 100644 talermerchantdemos/blog/templates/article_refunded.html
 create mode 100644 talermerchantdemos/blog/templates/article_refunded.html.j2
 delete mode 100644 talermerchantdemos/blog/templates/base.html
 create mode 100644 talermerchantdemos/blog/templates/base.html.j2
 delete mode 100644 talermerchantdemos/blog/templates/confirm_refund.html
 create mode 100644 talermerchantdemos/blog/templates/confirm_refund.html.j2
 delete mode 100644 talermerchantdemos/blog/templates/error.html
 create mode 100644 talermerchantdemos/blog/templates/error.html.j2
 delete mode 100644 talermerchantdemos/blog/templates/index.html
 delete mode 100644 talermerchantdemos/blog/templates/show_refund.html
 create mode 100644 talermerchantdemos/static/__init__.py
 rename talermerchantdemos/static/{demo.css => demo.scss} (66%)
 create mode 100644 talermerchantdemos/static/navbar.css
 create mode 100644 talermerchantdemos/static/navbar.css.map
 create mode 100644 talermerchantdemos/static/navbar.scss
 rename talermerchantdemos/static/{pure.css => pure.scss} (99%)
 rename talermerchantdemos/static/{taler-fallback.css => taler-fallback.scss} 
(100%)

diff --git a/.gitignore b/.gitignore
index bee8a64..92ed544 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-__pycache__
+talermerchantdemos/static/*.css
+talermerchantdemos/static/navbar.css.map
diff --git a/Makefile b/Makefile
index d40e2f4..0cea1c3 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@ install_global=false
 
 .PHONY: all
 all:
-       @echo "This is a python project, no compilation required"
+       @echo "This is a python project, no compilation required\nSCSS/SASS 
Initial Setup: make sass-setup\nSCSS/SASS Build (for static files): make 
sass-build"
 
 .PHONY: install
 
@@ -38,3 +38,42 @@ dist:
 .PHONY: pretty
 pretty:
        yapf -r -i talerblog/
+
+# i18n
+extract:
+#      Note: Flask-BabelEx expects 'translations/' for the dirname,
+#       even though GNU gettext typically uses 'locale/'
+       mkdir -p translations/
+       pybabel extract -F babel.cfg -o translations/messages.pot .
+#      Add new language as follows:
+#      pybabel init -i locale/messages.pot -d locale/ -l de
+
+compile:
+       pybabel compile -d translations/
+
+update: extract
+       pybabel update -i translations/messages.pot -d translations/
+
+# SASS/SCSS
+
+sass-setup:
+       @echo "This is the initial sass-installation/setup script."
+       @echo "This setup must run as root, on a machine that has NPM 
installed!"
+       @echo "If your password is requested (for escalation), please enter it."
+       sudo npm install -g sass
+
+scss-setup: sass-setup
+
+sass-build:
+       @echo "Warning: If Sass/Scss is not installed, please run \`make 
sass-setup\` first!"
+       @echo "This script will only convert files inside /static"
+       sass talermerchantdemos/static:talermerchantdemos/static
+
+scss-build: sass-build
+
+sass-autobuild:
+       @echo "Warning: If Sass/Scss is not installed, please run \`make 
sass-setup\` first!"
+       @echo "This script will automatically build sass/scss files in the 
static directory!"
+       sass --watch talermerchantdemos/static:talermerchantdemos/static
+
+scss-autobuild: sass-autobuild
diff --git a/README.md b/README.md
index 953ec69..c9f3e7f 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,18 @@
 # Taler Merchant Demos Setup
 *Warning: This page is not completed yet. Follow the instructions at your own 
risk*
 ## Installing the dependencies
+*Step 0 (Optional - Reccomended): Update Apt Repositories*
+> ```$ sudo apt update```
+
+<br/>
 Step 1: `cd` into the directory:<br/>
 
 > ```$ cd taler-merchant-demos```
 
 <br/>
-Step 2: Ensure Python3.5 or above is installed using a command like:<br/>
+Step 2: Ensure Python 3.5 or above and Flask with the Babel extension are 
installed using a command like:<br/>
 
-> ```$ sudo apt install python3.8 -y```
+> ```$ sudo apt install python3.8 python3-flask-babel -y```
 
 <br/>
 Step 3: Ensure Python3 Pip is installed:<br/>
@@ -16,7 +20,7 @@ Step 3: Ensure Python3 Pip is installed:<br/>
 > ```$ sudo apt install python3-pip -y```
 
 <br/>
-Step 4: configure it using:<br/>
+Step 4: configure this package using:<br/>
 
 > ```$ ./configure --destination=local```
 
@@ -27,14 +31,28 @@ Step 5: Install UWSGI<br>
 
 <br/>
 Step 6: Install LXML
-*NOTE: DO NOT INSTALL USING PIP2 (on my system, that is what the pip command 
uses) - INSTALL IT USING PIP3*
+*NOTE: DO NOT INSTALL USING PIP2 (on my system, that is what the pip command 
uses) - INSTALL IT USING PIP3*<br>
 
 > ```$ pip3 install lxml```
+
+<br/>
+
+Step 7: Install NPM<br>
+
+> ```$ sudo apt install npm; sudo npm install -g npm node```
+
 <br>
 
+Step 8: Install scss<br>
+> ```$ make sass-install```
+
+This will the NPM implementation of sass. If you have a Ruby
+tool called 'sass' installed, this build will NOT work.
+
+
 ## Quick Install for the dependencies
 Here's one command to automatically install all dependencies at once:
-> ```$ sudo apt install python3.8 python3-pip -y; pip3 install lxml uwsgi; 
./configure --destination=local```
+> ```$ sudo apt install python3.8 python3-pip npm -y; pip3 install lxml uwsgi; 
./configure --destination=local; sudo npm install -g npm node;make 
sass-install```
 
 ## Configuring the demo
 *This is just how I did it, and not the main method of doing it*
@@ -72,6 +90,11 @@ Step 3: Configure the config:
 To apply changes, use
 > ```$ make install```
 
+<br>
+
+To apply ***SCSS*** changes, use
+> ```$ make scss-build```
+
 ## Running the program
 To start the server, use the following command:<br>
 > ```$ taler-merchant-demos blog```
diff --git a/setup.py b/setup.py
index deeba73..af37dc7 100755
--- a/setup.py
+++ b/setup.py
@@ -20,6 +20,7 @@ setup(name='talermerchantdemos',
               "static/*.svg",
               # Blog files
               "blog/templates/*.html",
+              "blog/templates/*.j2",
               "blog/articles/*",
               "blog/data/*",
               # Donation files
diff --git a/talermerchantdemos/.gitignore b/talermerchantdemos/.gitignore
new file mode 100644
index 0000000..c940928
--- /dev/null
+++ b/talermerchantdemos/.gitignore
@@ -0,0 +1,6 @@
+*.pyc
+*/__pycache__
+
+./static/*.css
+
+*/*.css.map
\ No newline at end of file
diff --git a/talermerchantdemos/.vscode/launch.json 
b/talermerchantdemos/.vscode/launch.json
new file mode 100644
index 0000000..7a9dfa0
--- /dev/null
+++ b/talermerchantdemos/.vscode/launch.json
@@ -0,0 +1,15 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: 
https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "type": "pwa-chrome",
+            "request": "launch",
+            "name": "Launch Chrome against localhost",
+            "url": "http://localhost:8080";,
+            "webRoot": "${workspaceFolder}"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/talermerchantdemos/blog/articles/scrap1_11.html 
b/talermerchantdemos/blog/articles/scrap1_11.html
index 71c693b..17471df 100644
--- a/talermerchantdemos/blog/articles/scrap1_11.html
+++ b/talermerchantdemos/blog/articles/scrap1_11.html
@@ -52,4 +52,3 @@ traditional style, please visit
   <br/>
  </p>
  <img alt="song-book-jutta-scrunch-crop" 
src="/essay/11._The_Free_Software_Song/data/song-book-jutta-scrunch-crop.jpg"/>
-
diff --git a/talermerchantdemos/blog/blog.py b/talermerchantdemos/blog/blog.py
index d6cd3fc..c8315e5 100644
--- a/talermerchantdemos/blog/blog.py
+++ b/talermerchantdemos/blog/blog.py
@@ -1,6 +1,6 @@
 ##
-# This file is part of GNU taler.
-# Copyright (C) 2014-2017 INRIA
+# This file is part of GNU Taler.
+# Copyright (C) 2014-2020 Taler Systems SA
 #
 # TALER is free software; you can redistribute it and/or modify it under the
 # terms of the GNU Lesser General Public License as published by the Free 
Software
@@ -24,6 +24,11 @@ import traceback
 import uuid
 import base64
 import flask
+from flask import request
+from flask_babel import Babel
+from flask_babel import refresh
+from flask_babel import force_locale
+from flask_babel import gettext
 import time
 import sys
 from urllib.parse import urljoin, urlencode, urlparse
@@ -85,6 +90,34 @@ ARTICLE_AMOUNT = CURRENCY + ":0.5"
 BACKEND_URL = urljoin(BACKEND_BASE_URL, "instances/blog/")
 
 app.config.from_object(__name__)
+babel = Babel(app)
+
+print("Using translations from:")
+print(list(babel.translation_directories))
+translations = [str(translation) for translation in babel.list_translations()]
+translations.append('en')
+print("Operating with the following translations available:")
+print(translations)
+
+
+##
+# Helper function used inside Jinja2 logic to create a links
+# to the current page but in a different language. Used to
+# implement the "Language" menu.
+#
+def self_localized(lang):
+    """
+    Return URL for the current page in another locale.
+    """
+    path = request.path
+    # path must have the form "/$LANG/$STUFF"
+    parts = path.split('/', 2)
+    if (2 >= len(parts)):
+        # Totally unexpected path format, do not localize
+        return path
+    return "/" + lang + "/" + parts[2]
+
+app.jinja_env.globals.update(self_localized=self_localized)
 
 
 ##
@@ -107,7 +140,7 @@ def utility_processor():
 # @param abort_status_code status code to return along the response.
 # @param params _kw_ arguments to passed verbatim to the templating engine.
 def err_abort(abort_status_code, **params):
-    t = flask.render_template("templates/error.html", **params)
+    t = flask.render_template("templates/error.html.j2", **params)
     flask.abort(flask.make_response(t, abort_status_code))
 
 
@@ -120,23 +153,44 @@ def err_abort(abort_status_code, **params):
 @app.errorhandler(Exception)
 def internal_error(e):
     return flask.render_template(
-        "templates/error.html", message="Internal error", 
stack=traceback.format_exc()
+        "templates/error.html.j2", message=gettext("Internal error"), 
stack=traceback.format_exc()
     )
 
 
 ##
-# Serve the main index page.
+# Serve the main index page, redirecting to /<lang>/
 #
 # @return response object of the index page.
 @app.route("/")
 def index():
+    default = 'en'
+    target = flask.request.accept_languages.best_match(translations, default)
+    return flask.redirect("/" + target + "/", code=302)
+
+@babel.localeselector
+def get_locale():
+    parts = request.path.split('/', 2)
+    if (2 >= len(parts)):
+        # Totally unexpected path format, do not localize
+        return "en"
+    lang = parts[1]
+    if lang in translations:
+        return lang
+    return "en"
+
+##
+# Serve the main index page for a particular language.
+#
+# @return response object of the index page.
+@app.route("/<lang>/")
+def start(lang):
     return flask.render_template(
-        "templates/index.html", merchant_currency=CURRENCY, 
articles=ARTICLES.values()
+        "templates/index.html.j2", lang=lang, merchant_currency=CURRENCY, 
articles=ARTICLES.values()
     )
 
 
-@app.route("/confirm-refund/<order_id>", methods=["GET"])
-def confirm_refund(order_id):
+@app.route("/<lang>/confirm-refund/<order_id>", methods=["GET"])
+def confirm_refund(lang, order_id):
     session_id = flask.session.get("session_id", "")
     pay_status = backend_get(
         BACKEND_URL, f"private/orders/{order_id}", 
params=dict(session_id=session_id)
@@ -144,16 +198,16 @@ def confirm_refund(order_id):
     order_status = pay_status.get("order_status")
     if order_status != "paid":
         err_abort(
-            400, message="can't refund unpaid article",
+            400, message="Cannot refund unpaid article",
         )
     article_name = pay_status["contract_terms"]["extra"]["article_name"]
 
     if not refundable(pay_status):
         return flask.render_template(
-            "templates/error.html", message="Item not refundable (anymore)"
+            "templates/error.html.j2", message=gettext("Article is not anymore 
refundable")
         )
     return flask.render_template(
-        "templates/confirm_refund.html", article_name=article_name, 
order_id=order_id
+        "templates/confirm_refund.html.j2", article_name=article_name, 
order_id=order_id
     )
 
 
@@ -163,16 +217,15 @@ def confirm_refund(order_id):
 #
 # @param order_id the order ID of the transaction to refund.
 # @return the following errors (named by HTTP response code):
-#         - 400: no article was asked to be refunded!
-#         - 401: the refund was asked on a non-payed article.
-#         - 500: the backend was unable to give response.
-#         Or, in the successful case, a redirection to the
-#         "refund URL" is returned; then the wallet will run
-#         the refund protocol in a transparent way.
+#         - 400: order unknown
+#         - 402: the refund was asked on an unpaid article.
+#         - 302: in the successful case, a redirection to the
+#           "refund URL" is returned; then the wallet will run
+#           the refund protocol in a transparent way.
 @app.route("/refund/<order_id>", methods=["POST"])
-def refund(order_id):
+def refund(lang, order_id):
     if not order_id:
-        return flask.jsonify(dict(error="Aborting refund: article not 
payed")), 401
+        return flask.jsonify(dict(error="Aborting refund: order unknown")), 400
     session_id = flask.session.get("session_id", "")
     pay_status = backend_get(
         BACKEND_URL, f"private/orders/{order_id}", 
params=dict(session_id=session_id)
@@ -219,7 +272,7 @@ def render_article(article_name, data, order_id, 
refundable):
         err_abort(404, message=m)
     # the order_id is needed for refunds
     return flask.render_template(
-        "templates/article_frame.html",
+        "templates/article_frame.html.j2",
         article_file=get_article_file(article_info),
         article_name=article_name,
         order_id=order_id,
@@ -247,6 +300,25 @@ def post_order(article_name,lang):
         dict(order=order, refund_delay=dict(d_ms=1000 * 120)))
     return order_resp
 
+##
+# Setup a fresh order with the backend.
+#
+# @param article_name which article the order is for
+# @param lang which language to use
+#
+def post_order(article_name,lang):
+    order = dict(
+        amount=ARTICLE_AMOUNT,
+        extra=dict(article_name=article_name,lang=lang),
+        fulfillment_url=flask.request.base_url,
+        summary="Essay: " + article_name.replace("_", " "),
+        # 10 minutes time for a refund
+        refund_deadline=dict(t_ms=1000 * int(time.time() + 10 * 30)),
+        wire_transfer_deadline=dict(t_ms=1000 * int(time.time() + 15 * 30)),
+    )
+    order_resp = backend_post(BACKEND_URL, "private/orders", dict(order=order))
+    return order_resp
+
 
 ##
 # Trigger a article purchase.  The logic follows the main steps:
@@ -267,9 +339,10 @@ def post_order(article_name,lang):
 #         In the successful case, either the article is returned, or
 #         the browser gets redirected to a page where the wallet can
 #         send the payment.
-@app.route("/essay/<article_name>")
+@app.route("/<lang>/essay/<article_name>")
+@app.route("/<lang>/essay/<article_name>/data/<data>")
 @app.route("/essay/<article_name>/data/<data>")
-def article(article_name, data=None):
+def article(article_name, lang=None, data=None):
     # We use an explicit session ID so that each payment (or payment replay) is
     # bound to a browser.  This forces re-play and prevents sharing the article
     # by just sharing the URL.
@@ -279,8 +352,6 @@ def article(article_name, data=None):
     if not session_id:
         session_id = flask.session["session_id"] = str(uuid.uuid4())
         order_id = None
-    # Temporary workaround...
-    lang="en"
     ##
     # First-timer; generate order first.
     if not order_id:
@@ -309,12 +380,19 @@ def article(article_name, data=None):
     if order_status == "paid":
         refunded = pay_status["refunded"]
         if refunded:
-            return flask.render_template(
-                "templates/article_refunded.html",
+             return flask.render_template(
+                "templates/article_refunded.html.j2",
                 article_name=article_name,
                 order_id=order_id,
             )
-        return render_article(article_name, data, order_id, 
refundable(pay_status))
+        response = render_article(article_name, data, order_id, 
refundable(pay_status))
+        response.set_cookie(
+            "order_id", order_id, 
path=urllib.parse.quote(f"/essay/{article_name}")
+        )
+        response.set_cookie(
+            "order_id", order_id, 
path=urllib.parse.quote(f"/{lang}/essay/{article_name}")
+        )
+        return response
 
     # Check if the customer came on this page via the
     # re-purchase detection mechanism
@@ -333,14 +411,17 @@ def article(article_name, data=None):
     response.set_cookie(
         "order_id", order_id, path=urllib.parse.quote(f"/essay/{article_name}")
     )
+    response.set_cookie(
+        "order_id", order_id, 
path=urllib.parse.quote(f"/{lang}/essay/{article_name}")
+    )
     return response
 
 @app.errorhandler(500)
 def handler(e):
     return flask.render_template(
-        "templates/error.html", message="Internal server error")
+        "templates/error.html.j2", message=gettext("Internal server error"))
 
 @app.errorhandler(404)
 def handler(e):
     return flask.render_template(
-        "templates/error.html", message="Page not found")
+        "templates/error.html.j2", message=gettext("Page not found"))
diff --git a/talermerchantdemos/blog/content.py 
b/talermerchantdemos/blog/content.py
index a0e90dd..fa9ace2 100644
--- a/talermerchantdemos/blog/content.py
+++ b/talermerchantdemos/blog/content.py
@@ -26,7 +26,7 @@ from pkg_resources import resource_stream, resource_filename
 LOGGER = logging.getLogger(__name__)
 NOISY_LOGGER = logging.getLogger("chardet.charsetprober")
 NOISY_LOGGER.setLevel(logging.INFO)
-Article = namedtuple("Article", "slug title teaser main_file extra_files")
+Article = namedtuple("Article", "slug title teaser main_file extra_files lang")
 
 ##
 # @var if a article is added to this list, then it will
@@ -43,8 +43,9 @@ ARTICLES = OrderedDict()
 # @param main_file path to the article's HTML file.
 # @param extra_file collection of extra files associated with the
 #        article, like images and sounds.
-def add_article(slug, title, teaser, main_file, extra_files):
-    ARTICLES[slug] = Article(slug, title, teaser, main_file, extra_files)
+# @param lang language of the arcile
+def add_article(slug, title, teaser, main_file, extra_files, lang='en'):
+    ARTICLES[slug] = Article(slug, title, teaser, main_file, extra_files, lang)
 
 
 ##
@@ -85,7 +86,7 @@ def add_from_html(resource_name, teaser_paragraph=0, 
title=None):
     if title is None:
         title_el = soup.find("h1", attrs={"class": ["chapter", "unnumbered"]})
         if title_el is None:
-            LOGGER.warning("Can't extract title from '%s'", resource_name)
+            LOGGER.warning("Cannot extract title from '%s'", resource_name)
             title = resource_name
         else:
             title = title_el.get_text().strip()
@@ -97,7 +98,7 @@ def add_from_html(resource_name, teaser_paragraph=0, 
title=None):
         teaser = paragraphs[teaser_paragraph].get_text()
     else:
         teaser = teaser.get_text()
-    re_proc = re.compile("^/essay/[^/]+/data/[^/]+$")
+    re_proc = re.compile("^/[^/][^/]/essay/[^/]+/data/[^/]+$")
     imgs = soup.find_all("img")
     extra_files = []
     for img in imgs:
@@ -114,7 +115,7 @@ def add_from_html(resource_name, teaser_paragraph=0, 
title=None):
             else:
                 LOGGER.warning("Image src and slug don't match: '%s' != '%s'" \
                                % (img['src'].split(os.sep)[2], slug))
-    add_article(slug, title, teaser, resource_name, extra_files)
+    add_article(slug, title, teaser, resource_name, extra_files, 'en')
 
 
 add_from_html("blog/articles/scrap1_U.0.html", 0)
diff --git a/talermerchantdemos/blog/templates/article_frame.html 
b/talermerchantdemos/blog/templates/article_frame.html
deleted file mode 100644
index a5050d3..0000000
--- a/talermerchantdemos/blog/templates/article_frame.html
+++ /dev/null
@@ -1,13 +0,0 @@
-{% extends "templates/base.html" %}
-{% block main %}
-{% include "articles/" + article_file %}
-
-{% if refundable %}
-<hr>
-<p>
-  You don't like this article?  <a href="{{ url_for('confirm_refund', 
order_id=order_id) }}">Get a refund</a> within
-  the first hour after buying it.
-</p>
-{% endif %}
-
-{% endblock main %}
diff --git a/talermerchantdemos/blog/templates/article_frame.html.j2 
b/talermerchantdemos/blog/templates/article_frame.html.j2
new file mode 100644
index 0000000..a878e95
--- /dev/null
+++ b/talermerchantdemos/blog/templates/article_frame.html.j2
@@ -0,0 +1,15 @@
+{% extends "templates/base.html.j2" %}
+{% block main %}
+{% include "articles/" + article_file %}
+
+{% if refundable %}
+<hr>
+<p>
+  {{
+    gettext("You did not like this article?") +
+    gettext("You can <a href="{url}">request a refund</a> within the first 
hour after buying it.").format(url=url_for('confirm_refund', lang='en', 
order_id=order_id)
+  }}
+</p>
+{% endif %}
+
+{% endblock main %}
diff --git a/talermerchantdemos/blog/templates/article_refunded.html 
b/talermerchantdemos/blog/templates/article_refunded.html
deleted file mode 100644
index 95c4a6b..0000000
--- a/talermerchantdemos/blog/templates/article_refunded.html
+++ /dev/null
@@ -1,10 +0,0 @@
-{% extends "templates/base.html" %}
-{% block main %}
-
-<h2>Refunded</h2>
-
-<p>Your payment (order ID <tt>{{ order_id }}<tt>) for the article "{{ 
article_name }}" has been refunded.</p>
-
-<p>You won't be able to view it until you pay for it again.</p>
-
-{% endblock main %}
diff --git a/talermerchantdemos/blog/templates/article_refunded.html.j2 
b/talermerchantdemos/blog/templates/article_refunded.html.j2
new file mode 100644
index 0000000..34e0a0b
--- /dev/null
+++ b/talermerchantdemos/blog/templates/article_refunded.html.j2
@@ -0,0 +1,16 @@
+{% extends "templates/base.html.j2" %}
+{% block main %}
+
+<h2>{{ gettext("Refunded") }}</h2>
+
+<p>
+{{
+  gettext("Your payment (order ID <tt>{order}<tt>) for the article "{article}" 
has been refunded.").format(order=order_id,article=article_name)
+}}
+</p>
+
+<p>
+{{ gettext("You won't be able to view it until you pay for it again.") }}
+</p>
+
+{% endblock main %}
diff --git a/talermerchantdemos/blog/templates/base.html 
b/talermerchantdemos/blog/templates/base.html
deleted file mode 100644
index 3d7d0f0..0000000
--- a/talermerchantdemos/blog/templates/base.html
+++ /dev/null
@@ -1,79 +0,0 @@
-<!DOCTYPE html>
-<!-- 
-  This file is part of GNU TALER.
-  Copyright (C) 2014, 2015, 2016 INRIA
-
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU Lesser General Public License as published by the Free 
Software
-  Foundation; either version 2.1, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more 
details.
-
-  You should have received a copy of the GNU Lesser General Public License 
along with
-  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
--->
-
-<html>
-<head>
-  <meta charset="UTF-8">
-  <meta name="viewport" content="width=device-width, initial-scale=1.0">
-  {% block meta %}{% endblock %}
-  <title>Taler Essay Shop Demo</title>
-  <link rel="stylesheet" type="text/css" href="{{ url_for('static', 
filename='pure.css') }}" />
-  <link rel="stylesheet" type="text/css" href="{{ url_for('static', 
filename='demo.css') }}" />
-  <style>
-    .warn {
-      background-color: #aa393977;
-      padding: 1em;
-    }
-    .notice {
-      border-radius: 1em;
-      background: #0333;
-      border-left: 0.3em solid #033;
-      padding-left: 1em;
-      padding-top: 0.5em;
-      padding-bottom: 0.5em;
-      margin-top: 2em;
-      margin-bottom: 2em;
-    }
-    #main a:link, #main a:visited, #main a:hover, #main a:active {
-        color: black;
-    }
-  </style>
-  {% block styles %}{% endblock %}
-  {% block scripts %}{% endblock %}
-</head>
-
-<body>
-  <div class="demobar" style="display: flex; flex-direction: column;">
-    <h1><span class="tt adorn-brackets">Taler Demo</span></h1>
-    <h1><span class="it"><a href="{{ env('TALER_ENV_URL_MERCHANT_BLOG') 
}}">Shop</a></span></h1>
-    <p>On this page you can buy articles using an imaginary currency (for now).
-       The articles are chapters from Richard Stallman's book &quot;Free 
Software, Free Society&quot;,
-       which is also
-      <a 
href="http://shop.fsf.org/product/free-software-free-society-2/";>published by 
the FSF</a>
-      and available gratis at <a href="http://www.gnu.org/";>gnu.org</a>.
-    </p>
-    <ul>
-      <li><a href="{{ env('TALER_ENV_URL_INTRO', '#') }}">Introduction</a></li>
-      <li><a href="{{ env('TALER_ENV_URL_BANK', '#') }}">Bank</a></li>
-      <li><a href="{{ env('TALER_ENV_URL_MERCHANT_BLOG', '#') }}">Essay 
Shop</a></li>
-      <li><a href="{{ env('TALER_ENV_URL_MERCHANT_DONATIONS', '#') 
}}">Donations</a></li>
-      <li><a href="{{ env('TALER_ENV_URL_MERCHANT_SURVEY', '#') 
}}">Tipping/Survey</a></li>
-      <li><a href="{{ env('TALER_ENV_URL_BACKOFFICE', '#') 
}}">Back-office</a></li>
-    </ul>
-    <p>You can learn more about Taler on our main <a 
href="https://taler.net";>website</a>.</p>
-    <div style="flex-grow:1"></div>
-    <p>Copyright &copy; 2014&mdash;2018 Inria</p>
-  </div>
-
-  <section id="main" class="content">
-    {% block main %}
-      This is the main content of the page.
-    {% endblock %}
-    <hr />
-  </section>
-</body>
-</html>
diff --git a/talermerchantdemos/blog/templates/base.html.j2 
b/talermerchantdemos/blog/templates/base.html.j2
new file mode 100644
index 0000000..58ce857
--- /dev/null
+++ b/talermerchantdemos/blog/templates/base.html.j2
@@ -0,0 +1,119 @@
+<!DOCTYPE html>
+<!--
+  This file is part of GNU TALER.
+  Copyright (C) 2014, 2015, 2016, 2020 Taler Systems SA
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU Lesser General Public License as published by the Free 
Software
+  Foundation; either version 2.1, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more 
details.
+
+  You should have received a copy of the GNU Lesser General Public License 
along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+-->
+
+<html>
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  {% block meta %}{% endblock %}
+  <title>{{ gettext("Taler Essay Shop Demo") }}</title>
+  <link rel="stylesheet" type="text/css" href="{{ url_for('static', 
filename='pure.css') }}" />
+  <link rel="stylesheet" type="text/css" href="{{ url_for('static', 
filename='demo.css') }}" />
+  <link rel="stylesheet" type="text/css" href="{{ url_for('static', 
filename='navbar.css') }}" />
+  <style>
+    .warn {
+      background-color: #aa393977;
+      padding: 1em;
+    }
+    @keyframes hoveranim {
+      from {left:0;}
+      to {left:1vw;}
+    }
+    @keyframes hoveranimrevert {
+      from {left:1vw;}
+      to {left:0;}
+    }
+    .notice {
+      border-radius: 1em;
+      background: #0333;
+      border-left: 0.3em solid #033;
+      padding-left: 1em;
+      padding-top: 0.5em;
+      padding-bottom: 0.5em;
+      margin-top: 2em;
+      margin-bottom: 2em;
+    }
+    .notice {
+      position: relative;
+      left: 0;
+      animation-name: hoveranimrevert;
+      animation-duration: 1s;
+    }
+    .notice:hover {
+      left: 1vw;
+      animation-name: hoveranim;
+      animation-duration: 1s;
+    }
+    #main a:link, #main a:visited, #main a:hover, #main a:active {
+        color: black;
+    }
+  </style>
+
+  {% block styles %}{% endblock %}
+  {% block scripts %}{% endblock %}
+</head>
+
+<body>
+  <header class="demobar" style="display: flex; flex-direction: column;">
+    <h1><span class="tt adorn-brackets">Taler Demo</span></h1>
+    <h1><span class="it"><a href="{{ env('TALER_ENV_URL_MERCHANT_BLOG') 
}}">Shop</a></span></h1>
+    <p>{{
+      gettext("On this page you can buy articles using an imaginary 
currency.") + "<br>" +
+      gettext("The articles are chapters from Richard Stallman's book 
&quot;Free Software, Free Society&quot;.") + "<br>" +
+      gettext('The book is <a href="{shop}">published by the FSF</a> and 
available gratis at <a 
href="{gnu}">gnu.org</a>.').format(shop="https://shop.fsf.org/product/free-software-free-society-2";,
 gnu="https://www.gnu.org";)
+      }}
+    </p>
+  </header>
+  <div style="display:flex; flex-direction: column;" class="navcontainer">
+    <nav class="demolist">
+      <a href="{{ env('TALER_ENV_URL_INTRO', '#') 
}}">{{gettext("Introduction")}}</a>
+      <a href="{{ env('TALER_ENV_URL_BANK', '#') }}">{{gettext("Bank")}}</a>
+      <a href="{{ env('TALER_ENV_URL_MERCHANT_BLOG', '#') }}" 
class="active">{{gettext("Essay Shop")}}</a>
+      <a href="{{ env('TALER_ENV_URL_MERCHANT_DONATIONS', '#') 
}}">{{gettext("Donations")}}</a>
+      <a href="{{ env('TALER_ENV_URL_MERCHANT_SURVEY', '#') 
}}">{{gettext("Tipping/Survey")}}</a>
+      <!-- a href="{{ env('TALER_ENV_URL_BACKOFFICE', '#') 
}}">{{gettext("Back-office")}}</a -->
+      <span class="right">
+        {{ gettext("English [en]") }}
+        <!-- <input type="checkbox"> -->
+        <div class="nav">
+          <br>
+          <!--<hr style="width: 100%;">-->
+          {% if lang != 'en' %}
+          <a href="{{ self_localized('en') }}" class="navbtn">English 
[en]</a><br>
+          {% endif %}
+          {% if lang != 'de' %}
+          <a href="{{ self_localized('de') }}" class="navbtn">Deutsch 
[de]</a><br>
+          {% endif %}
+        </div>
+      </span>
+    </nav>
+  </div>
+  <!-- <input type="checkbox" class="r"><label>test</label> -->
+
+  <section id="main" class="content">
+    {% block main %}
+      This is the main content of the page.
+    {% endblock %}
+    <hr />
+    <div>
+      <p>{{ gettext('You can learn more about Taler on our main <a 
href="{site}">website</a>.').format(site="https://taler.net/";) }}</p>
+      <div style="flex-grow:1"></div>
+      <p>Copyright &copy; 2014&mdash;2020 Taler Systems SA</p>
+    </div>
+  </section>
+</body>
+</html>
diff --git a/talermerchantdemos/blog/templates/confirm_refund.html 
b/talermerchantdemos/blog/templates/confirm_refund.html
deleted file mode 100644
index 10aaa74..0000000
--- a/talermerchantdemos/blog/templates/confirm_refund.html
+++ /dev/null
@@ -1,19 +0,0 @@
-{% extends "templates/base.html" %}
-{% block main %}
-  <h1>Refund Article?</h1>
-
-  <p>
-    Do you want to get a refund for the article <em>{{ article_name }}</em>?  
After you've requested a refund,
-    you won't be able to read the article anymore.
-  </p>
-
-  <p>
-      You will only be able to receive the refund on the same wallet that 
you've used to pay
-      for this article originally.
-  </p>
-
-  <form action="{{ url_for('refund', order_id=order_id) }}" method="POST">
-    <input type="text" name="article_name" value={{ article_name}} hidden>
-    <input type="submit" value="Request refund">
-  </form>
-{% endblock main %}
diff --git a/talermerchantdemos/blog/templates/confirm_refund.html.j2 
b/talermerchantdemos/blog/templates/confirm_refund.html.j2
new file mode 100644
index 0000000..09f3730
--- /dev/null
+++ b/talermerchantdemos/blog/templates/confirm_refund.html.j2
@@ -0,0 +1,22 @@
+{% extends "templates/base.html.j2" %}
+{% block main %}
+  <h1>{{ gettext("Confirm refund request for article"))</h1>
+
+  <p>
+    {{
+       gettext("Do you want to get a refund for the article 
<em>{name}</em>?").format(name=article_name) +
+       gettext("After you have requested a refund, you won't be able to read 
the article anymore.")
+    }}
+  </p>
+
+  <p>
+    {{
+      gettext ("You will only be able to receive the refund on the same wallet 
that you have used to pay for this article originally.")
+    }}
+  </p>
+
+  <form action="{{ url_for('refund', order_id=order_id) }}" method="POST">
+    <input type="text" name="article_name" value={{ article_name}} hidden>
+    <input type="submit" value="Request refund">
+  </form>
+{% endblock main %}
diff --git a/talermerchantdemos/blog/templates/error.html 
b/talermerchantdemos/blog/templates/error.html
deleted file mode 100644
index 0d4bd02..0000000
--- a/talermerchantdemos/blog/templates/error.html
+++ /dev/null
@@ -1,22 +0,0 @@
-{% extends "templates/base.html" %}
-{% block main %}
-  <h1>An Error Occurred</h1>
-
-  <p>{{ message }}</p>
-
-  {% if status_code %}
-  <p>The backend returned status code {{ status_code }}.</p>
-  {% endif %}
-
-  {% if json %}
-  <p>Backend Response:</p>
-  <pre>{{ json }}</pre>
-  {% endif %}
-
-  {% if stack %}
-  <p>Stack trace:</p>
-  <pre>
-    {{ stack }}
-  </pre>
-  {% endif %}
-{% endblock main %}
diff --git a/talermerchantdemos/blog/templates/error.html.j2 
b/talermerchantdemos/blog/templates/error.html.j2
new file mode 100644
index 0000000..ffc2e1f
--- /dev/null
+++ b/talermerchantdemos/blog/templates/error.html.j2
@@ -0,0 +1,24 @@
+{% extends "templates/base.html.j2" %}
+{% block main %}
+  <h1>{{ gettext("Error encountered") }}</h1>
+
+  <p>{{ message }}</p>
+
+  {% if status_code %}
+  <p>
+    {{ gettext ("The backend returned status code 
{code}.").format(code=status_code) }}.
+  </p>
+  {% endif %}
+
+  {% if json %}
+  <p>{{gettext("Backend response:")}}</p>
+  <pre>{{ json }}</pre>
+  {% endif %}
+
+  {% if stack %}
+  <p>{{gettext("Stack trace:")}}</p>
+  <pre>
+    {{ stack }}
+  </pre>
+  {% endif %}
+{% endblock main %}
diff --git a/talermerchantdemos/blog/templates/index.html 
b/talermerchantdemos/blog/templates/index.html
deleted file mode 100644
index 0159779..0000000
--- a/talermerchantdemos/blog/templates/index.html
+++ /dev/null
@@ -1,40 +0,0 @@
-{% extends "templates/base.html" %}
-{% block main %}
-    <h1>Essay Shop: Free Software, Free Society</h1>
-    <div style="font-size: smaller;">
-      <p>This is the second edition of <cite>Free Software, Free Society: 
Selected Essays of Richard M. Stallman.</cite><br>
-      Free Software Foundation<br>
-      51 Franklin Street, Fifth Floor<br>
-      Boston, MA 02110-1335
-      <br>
-      Copyright &copy; 2002, 2010 Free Software Foundation, Inc.
-      </p>
-
-      <p>Verbatim copying and distribution of this entire book are permitted
-      worldwide, without royalty, in any medium, provided this notice is
-      preserved. Permission is granted to copy and distribute translations
-      of this book from the original English into another language provided
-      the translation has been approved by the Free Software Foundation and
-      the copyright notice and this permission notice are preserved on all
-      copies.
-      </p>
-      <p>ISBN 978-0-9831592-0-9</p>
-    </div>
-
-    <h2>Chapters</h2>
-    <div>
-      Click on an individual chapter to to purchase it.  You can
-      get free, virtual money to buy articles on this page at the <a href="{{ 
env('TALER_ENV_URL_BANK', '#') }}">bank</a>.
-    </div>
-
-    <div>
-      {% for article in articles %}
-      <div class="notice">
-      <h3><a href="{{ url_for('article', article_name=article.slug) 
}}">{{article.title}}</a></h3>
-      <p>{{ article.teaser|safe }} <a href="{{ url_for('article', 
article_name=article.slug) }}">(Pay to read more...)</a></p>
-      </div>
-      {% else %}
-      <em>(No articles available)</em>
-      {% endfor %}
-    </div>
-{% endblock main %}
diff --git a/talermerchantdemos/blog/templates/show_refund.html 
b/talermerchantdemos/blog/templates/show_refund.html
deleted file mode 100644
index 913b6a5..0000000
--- a/talermerchantdemos/blog/templates/show_refund.html
+++ /dev/null
@@ -1,28 +0,0 @@
-{% extends "templates/base.html" %}
-
-{% block main %}
-
-<h1>Refund</h1>
-
-<div class="taler-installed-hide">
-  <p>
-  Looks like your browser doesn't support GNU Taler payments.  You can try
-  installing a <a href="https://taler.net/en/wallet.html";>wallet browser 
extension</a>.
-  </p>
-</div>
-
-<div>
-
-  <p>
-  You can use this QR code to get a refund with your mobile wallet:
-  </p>
-
-  {{ qrcode_svg | safe }}
-
-  <p>
-  Click <a href="{{ taler_refund_uri }}">this link</a> to open your system's 
Taler wallet if it exists.
-  </p>
-
-</div>
-
-{% endblock main %}
diff --git a/talermerchantdemos/static/__init__.py 
b/talermerchantdemos/static/__init__.py
new file mode 100644
index 0000000..6b61bdc
--- /dev/null
+++ b/talermerchantdemos/static/__init__.py
@@ -0,0 +1 @@
+app.static_folder = 'static'
diff --git a/talermerchantdemos/static/demo.css 
b/talermerchantdemos/static/demo.scss
similarity index 66%
rename from talermerchantdemos/static/demo.css
rename to talermerchantdemos/static/demo.scss
index b2688ad..7733515 100644
--- a/talermerchantdemos/static/demo.css
+++ b/talermerchantdemos/static/demo.scss
@@ -44,26 +44,39 @@
 }
 
 body {
-  overflow-y: scroll;
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+
+body * {
+  /* margin-left: 0.5vw; */
+  left: 0.1vw;
 }
 
 @media (min-width: 500px) {
   .content {
-    margin-left: 25%;
+    /* margin-left: 0vw; */
     padding-left: 2em;
     margin-right: 1em;
-    overflow-x: auto;
+    overflow-x: hidden;
+    overflow-y: auto;
   }
   .demobar {
-    height: 100%;
+    /* height: 100%; */
+    /* width: 25%; */
+    /* NOTE: Please use "vh"/"vw" instead of "%" when possible, in the future. 
*/
+    height: min-content;
+    width: 100vw;
     margin: 0;
     top: 0;
     left: 0;
     background-color: #033;
     color: white;
-    position: fixed;
-    width: 25%;
+    position: relative;
     padding-right: 1em;
-    overflow: auto;
+    overflow-x: hidden;
+    overflow-y: auto;
+    text-align: center;
   }
 }
+
diff --git a/talermerchantdemos/static/navbar.css 
b/talermerchantdemos/static/navbar.css
new file mode 100644
index 0000000..aabacc3
--- /dev/null
+++ b/talermerchantdemos/static/navbar.css
@@ -0,0 +1,75 @@
+/**
+ * @author      Torsten Grothoff
+ * @name        navbar.css
+ * @description Makes the navigation bar have styles
+ * @license     LGPL-3.0-or-later
+ */
+.navcontainer {
+  overflow: hidden;
+  background: #144;
+  margin-bottom: 50px;
+  width: 100%;
+  color: white;
+  position: -webkit-sticky;
+  position: sticky;
+  top: 0px;
+  width: 100vw;
+  backdrop-filter: blur(10px);
+  opacity: 1;
+  z-index: 10000;
+}
+
+nav {
+  left: 1vw;
+  position: relative;
+  background: #144;
+  z-index: 10000;
+}
+
+nav a, nav span, .navbtn {
+  border: none;
+  color: white;
+  text-align: center;
+  text-decoration: none;
+  display: inline-block;
+  font-size: 16px;
+  background: #00000000;
+  height: inherit;
+}
+
+nav a, nav span, .navbtn {
+  padding: 15px 32px;
+}
+
+nav a:hover, nav span:hover, .navbtn:hover {
+  background: #00000022;
+}
+
+nav a.active, nav span.active, .navbtn.active {
+  background-color: #4CAF50;
+}
+
+nav a.active:hover, nav span.active:hover, .navbtn.active:hover {
+  background: #377c39;
+}
+
+nav a, nav span, .navbtn {
+  cursor: pointer;
+}
+
+nav .right {
+  float: right;
+  margin-right: 5vw;
+}
+nav .right div.nav {
+  display: none;
+}
+nav .right div.nav:hover {
+  display: block;
+}
+
+nav .right:hover div.nav {
+  display: block;
+}
+
+/*# sourceMappingURL=navbar.css.map */
diff --git a/talermerchantdemos/static/navbar.css.map 
b/talermerchantdemos/static/navbar.css.map
new file mode 100644
index 0000000..1725bd6
--- /dev/null
+++ b/talermerchantdemos/static/navbar.css.map
@@ -0,0 +1 @@
+{"version":3,"sourceRoot":"","sources":["navbar.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;AAEJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAGJ;EACI;;;AAEJ;EAiCI;EACA;;AAPA;EACI;;AAEJ;EACI;;;AAMJ;EACI","file":"navbar.css"}
\ No newline at end of file
diff --git a/talermerchantdemos/static/navbar.scss 
b/talermerchantdemos/static/navbar.scss
new file mode 100644
index 0000000..312bf0c
--- /dev/null
+++ b/talermerchantdemos/static/navbar.scss
@@ -0,0 +1,103 @@
+/**
+ * @author      Torsten Grothoff
+ * @name        navbar.css
+ * @description Makes the navigation bar have styles
+ * @license     LGPL-3.0-or-later
+ */
+ 
+ 
+.navcontainer{
+    overflow:hidden;
+    background:#144;
+    margin-bottom:50px;
+    width:100%; 
+    color:white; 
+    position:-webkit-sticky;
+    position:sticky;
+    top:0px;
+    width: 100vw;
+    backdrop-filter: blur(10px);
+    opacity: 1;
+    z-index: 10000;
+}
+nav {
+    left: 1vw;
+    position: relative;
+    background:#144;
+    z-index: 10000;
+}
+
+nav a, nav span,.navbtn {
+    border: none;
+    color: white;
+    text-align: center;
+    text-decoration: none;
+    display: inline-block;
+    font-size: 16px;
+    background: #00000000;
+    height: inherit;
+}
+nav a, nav span,.navbtn{
+    padding: 15px 32px;
+}
+
+nav a:hover, nav span:hover,.navbtn:hover {
+    background: #00000022;
+}
+
+nav a.active, nav span.active,.navbtn.active {
+    background-color: #4CAF50;
+}
+
+nav a.active:hover, nav span.active:hover,.navbtn.active:hover {
+    background: #377c39;
+}
+
+nav a, nav span,.navbtn {
+    cursor: pointer;
+}
+nav .right {
+    // input[type=checkbox] {
+    //     // // opacity: 0;
+    //     // // position: absolute;
+    //     // position: relative;
+    //     // left:0.5vw;
+    //     // // top:0;
+    //     // width: inherit;
+    //     // height: inherit;
+
+    //     // $sx: 1.5;
+    //     // $sy: 1.5;
+
+    //     // -ms-transform: scale($sx,$sy); /* IE */
+    //     // -moz-transform: scale($sx,$sy); /* FF */
+    //     // -webkit-transform: scale($sx,$sy); /* Safari and Chrome */
+    //     // -o-transform: scale($sx,$sy); /* Opera */
+    //     // transform: scale($sx,$sy);
+    //     // float:right;
+    //     opacity: 0;
+    // }
+    // input[type=checkbox]:checked + div.nav {
+    //     display: block;
+    // }
+    // input[type=checkbox] + div.nav {
+    //     display: none;
+    // }
+    div.nav {
+        display: none;
+    }
+    div.nav:hover {
+        display: block;
+    }
+    float:right;
+    margin-right: 5vw;
+}
+nav .right:hover {
+    div.nav {
+        display:block;
+    }
+}
+
+// input[type=checkbox]:checked + label {
+//     color: red;
+// }
\ No newline at end of file
diff --git a/talermerchantdemos/static/pure.css 
b/talermerchantdemos/static/pure.scss
similarity index 99%
rename from talermerchantdemos/static/pure.css
rename to talermerchantdemos/static/pure.scss
index 7391139..9b555ed 100644
--- a/talermerchantdemos/static/pure.css
+++ b/talermerchantdemos/static/pure.scss
@@ -821,7 +821,7 @@ this the same font stack that Normalize.css sets for the 
`body`.
 .pure-button-active,
 .pure-button:active {
     box-shadow: 0 0 0 1px rgba(0,0,0, 0.15) inset, 0 0 6px rgba(0,0,0, 0.20) 
inset;
-    border-color: #000\9;
+    border-color: #000;
 }
 
 .pure-button[disabled],
diff --git a/talermerchantdemos/static/taler-fallback.css 
b/talermerchantdemos/static/taler-fallback.scss
similarity index 100%
rename from talermerchantdemos/static/taler-fallback.css
rename to talermerchantdemos/static/taler-fallback.scss

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]