1
0
Fork 0
mirror of https://github.com/YunoHost/apps.git synced 2024-09-03 20:06:07 +02:00

appstore: misc good practices / a11y tweaks

This commit is contained in:
Alexandre Aubin 2023-09-19 16:34:57 +02:00
parent 662fcd2af3
commit c6889e4b01
7 changed files with 82 additions and 58 deletions

View file

@ -1,18 +1,22 @@
{% extends "base.html" %}
{% block title %}
{{ infos['manifest']['name'] }}
{% endblock %}
{% block main %}
<div class="max-w-screen-md mx-auto pt-5 px-5 lg:px-0">
<span class="sm:flex sm:flex-row sm:items-end mb-1">
<span class="flex flex-row items-end">
<img {% if infos['logo_hash'] %}
<img alt="{{ _('Logo for %(app)s',app=infos['manifest']['name']) }}"
{% if infos['logo_hash'] %}
src="https://app.yunohost.org/default/v3/logos/{{ infos['logo_hash'] }}.png"
{% else %}
src="{{ url_for('static', filename='app_logo_placeholder.png') }}"
{% endif %}
loading="lazy"
class="h-12 w-12 rounded-lg object-cover shadow-sm mt-1"
/>
<h2 class="flex-0 pl-2 pt-3 text-3xl font-bold text-gray-900">{{ infos["manifest"]["name"] }}</h2>
>
<h1 class="flex-0 pl-2 pt-3 text-3xl font-bold text-gray-900">{{ infos["manifest"]["name"] }}</h1>
{% if infos['category'] %}
<span class="ml-2 mb-1 rounded-full px-2.5 py-0.5 text-[10px] border text-{{ catalog['categories'][infos['category']]['color'] }}-500 border-{{ catalog['categories'][infos['category']]['color'] }}-400 ">
@ -23,15 +27,15 @@
{% if infos['level'] == "?" or infos["level"]|int <= 4 %}
<span class="ml-2 mb-1.5">
<i class="fa fa-exclamation-circle text-red-500 py-0.5"
aria-hidden="true"
title="{{ _('This app is currently flagged as broken because it failed our automatic tests.') }} {{ _('This is usually a temporary situation which requires packagers to fix something in the app.') }}"
aria-label="{{ _('This app is currently flagged as broken because it failed our automatic tests.') }} {{ _('This is usually a temporary situation which requires packagers to fix something in the app.') }}"
></i>
</span>
{% elif infos['level'] == 8 %}
<span class="ml-2 mb-1.5">
<i class="fa fa-diamond text-teal-500 py-0.5"
aria-hidden="true"
title="{{ _('This app has been good quality according to our automatic tests over at least one year.') }}"
aria-label="{{ _('This app has been good quality according to our automatic tests over at least one year.') }}"
></i>
</span>
{% endif %}
@ -78,9 +82,9 @@
<span class="inline sm:hidden">{{ _("Demo") }}</span>
</a>
{% endif %}
<a class="h-9.5 inline-block rounded-md border p-0 bg-gray-900 text-white " href="https://install-app.yunohost.org/?app={{ app_id }}">
<a aria-label="{{ _('Install with YunoHost') }}" title="{{ _('Install with YunoHost') }}" class="h-9.5 inline-block rounded-md border p-0 bg-gray-900 text-white " href="https://install-app.yunohost.org/?app={{ app_id }}">
<span class="inline-block text-[11px] leading-3 text-center py-1.5 pl-2">Install<br/>with</span>
<span class="inline-block pr-2 pt-1"><img src="{{ url_for('static', filename='horizontal-yunohost.svg') }}" /></span>
<span class="inline-block pr-2 pt-1"><img alt="YunoHost" src="{{ url_for('static', filename='horizontal-yunohost.svg') }}"></span>
</a>
</div>
@ -94,7 +98,7 @@
<div class="from-markdown">{{ infos["full_description_html"]|safe }}</div>
{% if infos["screenshot"] %}
<img class="my-3" src="{{ infos["screenshot"] }}" />
<img alt="{{ _("Screenshot for %(app)s",app=infos["manifest"]["name"]) }}" class="my-3" src="{{ infos["screenshot"] }}">
{% endif %}
{% if infos["manifest"]["integration"]["architectures"] != "all" %}
@ -111,13 +115,13 @@
{% if infos["pre_install_html"] %}
<div class="my-3 rounded-md bg-blue-200 text-blue-800 px-5 py-2">
<h3 class="inline-block text-xl mb-2 font-semibold">{{ _("Important infos before installing") }}</h3>
<h2 class="inline-block text-xl mb-2 font-semibold">{{ _("Important infos before installing") }}</h2>
<div class="from-markdown">{{ infos["pre_install_html"] | safe }}</div>
</div>
{% endif %}
{% if infos["antifeatures"] %}
<h3 class="inline-block text-xl mb-2 font-semibold">{{ _("Anti-features") }}</h3>
<h2 class="inline-block text-xl mb-2 font-semibold">{{ _("Anti-features") }}</h2>
<p class="inline-block text-sm">{{ _("(This app has features you may not like)") }}</p>
<div class="my-3 rounded-md bg-red-200 text-red-800 px-5 py-2">
<ul>
@ -129,7 +133,7 @@
{% endif %}
<h3 class="text-xl mb-2 font-semibold">{{ _("Useful links") }}</h3>
<h2 class="text-xl mb-2 font-semibold">{{ _("Useful links") }}</h2>
<div>
{% set upstream = infos["manifest"]["upstream"] %}
<a class="block btn btn-link my-1" href="https://spdx.org/licenses/{{upstream.license}}"><i class="fa fa-institution fa-fw" aria-hidden="true"></i> {{ _("License: %(license)s", license=upstream.license) }}</a>

View file

@ -2,11 +2,11 @@
<html lang="{{ locale }}">
<head>
<title>{{ _("YunoHost app store") }}</title>
<title>{{ _("YunoHost app store") }} | {% block title %}{% endblock %} </title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="{{ url_for('static', filename='tailwindcss.js') }}"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='fork-awesome.min.css') }}" rel="preload">
<script type="text/javascript" src="{{ url_for('static', filename='tailwindcss.js') }}"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='fork-awesome.min.css') }}">
<style type="text/tailwindcss">
@layer utilities {
.btn {
@ -54,7 +54,7 @@
>
<a class="block text-teal-600" href="/">
<span class="sr-only">{{ _("Home") }}</span>
<img src="{{ url_for('static', filename='ynh_logo_roundcorner.png') }}" style="height: 3em;" />
<img alt="YunoHost Logo" src="{{ url_for('static', filename='ynh_logo_roundcorner.png') }}" style="height: 3em;">
</a>
<div class="flex flex-1 items-center justify-end md:justify-between">
@ -99,10 +99,10 @@
class="group flex shrink-0 items-center rounded-md transition"
>
<img
alt="Man"
alt="Avatar"
src="{{ user['avatar_url'] }}"
class="h-10 w-10 rounded-full object-cover"
/>
>
<p class="ms-2 text-left text-xs inline-block">
<strong class="block font-medium">{{ user['username'] }}</strong>
</p>
@ -198,7 +198,7 @@
alt="Avatar"
src="{{ user['avatar_url'] }}"
class="h-10 w-10 rounded-full object-cover inline-block"
/>
>
<p class="ms-2 inline-block text-left">
<strong class="font-medium">{{ user['username'] }}</strong>
</p>
@ -221,13 +221,15 @@
</div>
</header>
<main>
{% block main %}
{% endblock %}
</main>
<footer class="h-5 mt-5"></footer>
</body>
<script>
<script type="text/javascript">
{% if user %}
document.getElementById('toggleUserMenu').addEventListener('click', () => {
document.getElementById('userMenu').classList.toggle("hidden");

View file

@ -20,15 +20,15 @@
>
<div class="flex justify-between gap-4">
<div class="shrink-0">
<img
{% if infos['logo_hash'] %}
src="https://app.yunohost.org/default/v3/logos/{{ infos['logo_hash'] }}.png"
{% else %}
src="{{ url_for('static', filename='app_logo_placeholder.png') }}"
{% endif %}
loading="lazy"
class="h-12 w-12 rounded-lg object-cover shadow-sm mt-1"
/>
<img alt="{{ _('Logo for %(app)s',app=infos['manifest']['name']) }}"
{% if infos['logo_hash'] %}
src="https://app.yunohost.org/default/v3/logos/{{ infos['logo_hash'] }}.png"
{% else %}
src="{{ url_for('static', filename='app_logo_placeholder.png') }}"
{% endif %}
loading="lazy"
class="h-12 w-12 rounded-lg object-cover shadow-sm mt-1"
>
</div>
<div class="w-full">
<span class="flex">
@ -38,11 +38,12 @@
<span class="text-xs">
{% if infos['level'] == "?" or infos["level"]|int <= 4 %}
<i class="fa fa-exclamation-circle text-red-500 py-0.5"
aria-hidden="true"
aria-label="{{ _('This app is currently flagged as broken because it failed our automatic tests.') }} {{ _('This is usually a temporary situation which requires packagers to fix something in the app.') }}"
title="{{ _('This app is currently flagged as broken because it failed our automatic tests.') }} {{ _('This is usually a temporary situation which requires packagers to fix something in the app.') }}"
></i>
{% elif infos['level'] == 8 %}
<i class="fa fa-diamond text-teal-500 py-0.5" aria-hidden="true"
<i class="fa fa-diamond text-teal-500 py-0.5"
aria-label="{{ _('This app has been good quality according to our automatic tests over at least one year.') }}"
title="{{ _('This app has been good quality according to our automatic tests over at least one year.') }}"
></i>
{% endif %}
@ -70,11 +71,14 @@
{%- endmacro %}
{% extends "base.html" %}
{% block title %}
{{ _("Application Catalog") }}
{% endblock %}
{% block main %}
<div class="mt-5 text-center">
<h2 class="text-2xl font-bold text-gray-900">
<h1 class="text-2xl font-bold text-gray-900">
{{ _("Application Catalog") }}
</h2>
</h1>
</div>
<div class="max-w-screen-md mx-auto mt-3 mb-3">
<div class="flex flex-col md:flex-row items-center">
@ -87,7 +91,7 @@
placeholder="{{ _('Search for...') }}"
{% if init_search %}value="{{ init_search }}"{% endif %}
class="w-full rounded-md border-gray-200 shadow-sm sm:text-sm py-2 pe-10"
/>
>
<span class="absolute inset-y-0 end-0 grid w-10 place-content-center pr-4">
<i class="fa fa-search" aria-hidden="true"></i>
@ -102,8 +106,7 @@
>
<option value="">{{ _("All apps") }}</option>
{% for id, category in catalog['categories'].items() %}
{{ category['title']|localize }}
<option {% if id == init_category %}selected{% endif %} value="{{ id }}" {{ id == init_category }} >{{ category['title']|localize }}</option>
<option {% if id == init_category %}selected{% endif %} value="{{ id }}">{{ category['title']|localize }}</option>
{% endfor %}
</select>
</div>
@ -122,9 +125,10 @@
<option {% if init_sort == "popularity" %}selected{% endif %} value="popularity">{{ _("Popularity") }}</option>
</select>
</div>
<div class="inline-block flex items-center px-2 pt-2 md:pt-0 {% if not user %}text-gray-500{% endif %}" {% if not user %}title="{{ _('Requires to be logged-in') }}"{% endif %}>
<div class="inline-block flex items-center px-2 pt-2 md:pt-0 {% if not user %}text-gray-500{% endif %}" {% if not user %}title="{{ _('Requires to be logged-in') }}" aria-label="{{ _('Requires to be logged-in') }}"{% endif %}>
<label for="starsonly" class="inline-block relative mr-2 h-4 w-7 cursor-pointer">
<input type="checkbox" id="starsonly" class="peer sr-only" {% if user and init_starsonly %}checked{% endif %} {% if not user%}disabled{% endif %} />
<span class="sr-only">{{ _("Show only apps you starred") }}</span>
<input type="checkbox" id="starsonly" class="peer sr-only" {% if user and init_starsonly %}checked{% endif %} {% if not user%}disabled{% endif %} >
<span class="absolute inset-0 rounded-full bg-gray-300 transition peer-checked:bg-green-500">
</span>
@ -175,7 +179,7 @@
</div>
<script>
<script type="text/javascript">
// A little delay
let typingTimer;
let typeInterval = 500; // Half a second

View file

@ -1,11 +1,14 @@
{% extends "base.html" %}
{% block title %}
{{ _("Home") }}
{% endblock %}
{% block main %}
<div class="mx-auto w-full text-center p-8">
<img src="{{ url_for('static', filename='ynh_logo_black.svg') }}" class="w-32 mx-auto" />
<h2 class="text-2xl font-bold text-gray-900">
<img alt="YunoHost logo" src="{{ url_for('static', filename='ynh_logo_black.svg') }}" class="w-32 mx-auto">
<h1 class="text-2xl font-bold text-gray-900">
{{ _("Application Store") }}
</h2>
</h1>
</div>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-3 max-w-screen-lg mx-auto pt-5">
@ -14,9 +17,9 @@
href="{{ url_for('browse_catalog') }}"
class="h-full relative block overflow-hidden hover:bg-gray-200 pt-12"
>
<h3 class="text-md font-bold text-gray-900">
<h2 class="text-md font-bold text-gray-900">
{{ _("Browse all applications") }}
</h3>
</h2>
</a>
</div>
{% for id, category in catalog['categories'].items() %}
@ -25,10 +28,10 @@
href="{{ url_for('browse_catalog', category=id) }}"
class="h-full relative block overflow-hidden hover:bg-gray-200 pt-10"
>
<h3 class="text-md font-bold text-gray-900">
<h2 class="text-md font-bold text-gray-900">
<i class="fa fa-{{ category['icon'] }}" aria-hidden="true"></i>
{{ category['title']|localize }}
</h3>
</h2>
<p class="mx-auto max-w-[40ch] text-xs text-gray-500">
{{ category['description']|localize }}
</p>

View file

@ -1,9 +1,12 @@
{% extends "base.html" %}
{% block title %}
{{ _("Application Wishlist") }}
{% endblock %}
{% block main %}
<div class="text-center max-w-screen-md mx-auto mt-5 mx-2">
<h2 class="text-2xl font-bold text-gray-900">
<h1 class="text-2xl font-bold text-gray-900">
{{ _("Application Wishlist") }}
</h2>
</h1>
<p class="text-sm text-gray-700 mx-10 mt-2">{{ _("The wishlist is the place where people can collectively suggest and vote for apps that they would like to see packaged and made available in YunoHost's official apps catalog. Nevertheless, the fact that apps are listed here should by no mean be interpreted as a fact that the YunoHost project plans to integrate it, and is merely a source of inspiration for packaging volunteers.") }}</p>
</div>
@ -17,7 +20,7 @@
id="search"
placeholder="{{ _('Search for...') }}"
class="w-full rounded-md border-gray-200 shadow-sm sm:text-sm py-2 pe-10"
/>
>
<span class="absolute inset-y-0 end-0 grid w-10 place-content-center pr-4">
<i class="fa fa-search" aria-hidden="true"></i>
@ -43,9 +46,10 @@
<option {% if init_sort == "popularity" %}selected{% endif %} value="popularity">{{ _("Popularity") }}</option>
</select>
</div>
<div class="inline-block flex items-center px-2 pt-2 md:pt-0 {% if not user %}text-gray-500{% endif %}" {% if not user %}title="{{ _('Requires to be logged-in') }}"{% endif %}>
<div class="inline-block flex items-center px-2 pt-2 md:pt-0 {% if not user %}text-gray-500{% endif %}" {% if not user %}title="{{ _('Requires to be logged-in') }}" aria-label="{{ _('Requires to be logged-in') }}"{% endif %}>
<label for="starsonly" class="inline-block relative mr-2 h-4 w-7 cursor-pointer">
<input type="checkbox" id="starsonly" class="peer sr-only" {% if user and init_starsonly %}checked{% endif %} {% if not user%}disabled{% endif %} />
<span class="sr-only">{{ _("Show only apps you starred") }}</span>
<input type="checkbox" id="starsonly" class="peer sr-only" {% if user and init_starsonly %}checked{% endif %} {% if not user%}disabled{% endif %}>
<span class="absolute inset-0 rounded-full bg-gray-300 transition peer-checked:bg-green-500">
</span>
@ -96,6 +100,7 @@
{% if infos['website'] %}
<a
title="{{ _('Official website') }}"
aria-label="{{ _('Official website') }}"
href="{{ infos['website'] }}"
class="inline-block"
>
@ -107,6 +112,7 @@
{% if infos['upstream'] %}
<a
title="{{ _('Code repository') }}"
aria-label="{{ _('Code repository') }}"
href="{{ infos['upstream'] }}"
class="inline-block"
>
@ -117,7 +123,9 @@
<td class="float-right sm:float-none sm:table-cell py-2 px-1 sm:px-0 text-center max-w-[5em]">
<a
role="button"
title="{{ _('Star this app') }}"
aria-label="{{ _('Star this app') }}"
href="{{ url_for('star_app', app_id=app, action="unstar" if user_starred_this_app else "star") }}"
class="inline-block group btn-sm border text-violet-600 border-violet-500 hover:bg-violet-500 hover:text-white"
>
@ -146,7 +154,7 @@
</div>
</div>
<script>
<script type="text/javascript">
// A little delay
let typingTimer;
let typeInterval = 500; // Half a second

View file

@ -1,9 +1,12 @@
{% extends "base.html" %}
{% block title %}
{{ _("Suggest an app") }}
{% endblock %}
{% block main %}
<div class="mt-5 text-center px-3 sm:px-0">
<h2 class="text-2xl font-bold text-gray-900">
<h1 class="text-2xl font-bold text-gray-900">
{{ _("Suggest an application to be added to YunoHost's catalog") }}
</h2>
</h1>
</div>
@ -50,17 +53,17 @@
<form method="POST" action="{{ url_for('add_to_wishlist') }}" class="mt-8 mb-8" >
<label for="name" class="mt-5 block font-bold text-gray-700">{{ _("Name") }}</label>
<input name="name" type="text" class="w-full mt-1 rounded-md border-gray-200 text-gray-700 shadow-sm" maxlength="30" required onkeyup="this.value = this.value.replace(/[^a-zA-Z0-9.-\\(\\)\\ ]/, '')" />
<input name="name" type="text" class="w-full mt-1 rounded-md border-gray-200 text-gray-700 shadow-sm" maxlength="30" required onkeyup="this.value = this.value.replace(/[^a-zA-Z0-9.-\\(\\)\\ ]/, '')" >
<label for="description" class="mt-5 block font-bold text-gray-700">{{ _("Description") }}</label>
<label for="description" class="mt-5 block font-bold text-gray-700">{{ _("App's description") }}</label>
<textarea name="description" type="text" class="w-full mt-1 rounded-md border-gray-200 text-gray-700 shadow-sm" required rows='3' maxlength='100'></textarea>
<span class="text-xs text-gray-600"><span class="font-bold">{{ _("Please be concise and focus on what the app does.") }}</span> {{ _("No need to repeat '[App] is ...'. No need to state that it is free/open-source or self-hosted (otherwise it wouldn't be packaged for YunoHost). Avoid marketing stuff like 'the most', or vague properties like 'easy', 'simple', 'lightweight'.") }}</span>
<label for="upstream" class="mt-5 block font-bold text-gray-700">{{ _("Project code repository") }}</label>
<input name="upstream" type="url" class="w-full mt-1 rounded-md border-gray-200 text-gray-700 shadow-sm" maxlength="150" required />
<input name="upstream" type="url" class="w-full mt-1 rounded-md border-gray-200 text-gray-700 shadow-sm" maxlength="150" required >
<label for="website" class="mt-5 block font-bold text-gray-700">{{ _("Project website") }}</label>
<input name="website" type="url" class="w-full mt-1 rounded-md border-gray-200 text-gray-700 shadow-sm" maxlength="150" />
<input name="website" type="url" class="w-full mt-1 rounded-md border-gray-200 text-gray-700 shadow-sm" maxlength="150" >
<span class="text-xs text-gray-600">{{ _("Please *do not* just copy-paste the code repository URL. If the project has no proper website, then leave the field empty.") }}</span>
<button

View file

@ -14,7 +14,7 @@ AVAILABLE_LANGUAGES = ["en"] + os.listdir("translations")
def get_locale():
# try to guess the language from the user accept
# The best match wins.
return request.accept_languages.best_match(AVAILABLE_LANGUAGES)
return request.accept_languages.best_match(AVAILABLE_LANGUAGES) or "en"
def get_catalog():