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

store: add dark mode (#2327)

This commit is contained in:
Alexandre Aubin 2024-05-12 17:37:37 +02:00 committed by GitHub
parent ba68da3d21
commit 14a23a072d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 86 additions and 50 deletions

View file

@ -32,6 +32,9 @@ And then start the dev server:
```bash
source venv/bin/activate
FLASK_APP=app.py FLASK_ENV=development flask --debug run
# In another term, launch the tailwindcss process to autorebuild css:
cd assets; ./tailwindcss-linux-x64 --input tailwind-local.css --output tailwind.css --watch
```
## Translation

View file

@ -1,11 +1,14 @@
# Production -> download standalone tailwind to compile only what we need
wget https://github.com/tailwindlabs/tailwindcss/releases/download/v3.3.3/tailwindcss-linux-x64
wget https://github.com/tailwindlabs/tailwindcss/releases/download/v3.4.3/tailwindcss-linux-x64
chmod +x tailwindcss-linux-x64
./tailwindcss-linux-x64 --input tailwind-local.css --output tailwind.css --minify
# Development -> we use the JS magic thingy
curl -L https://cdn.tailwindcss.com?plugins=forms > tailwind-css.js
#curl -L https://cdn.tailwindcss.com?plugins=forms > tailwind-css.js
# Dark theme stuff
git clone https://github.com/jjranalli/nightwind
# Canvasjs (for the chart page only)
curl -L https://cdn.canvasjs.com/ga/canvasjs.min.js > canvasjs.min.js

View file

@ -3,8 +3,8 @@
@tailwind utilities;
@layer utilities {
body {
@apply text-gray-800;
input, textarea, select {
@apply !rounded-md shadow-sm border-gray-200 !bg-neutral-50;
}
.btn {
@apply text-sm font-medium rounded-md px-4 py-2 transition;

View file

@ -1,11 +1,10 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['../templates/*.html'],
theme: {
extend: {},
},
darkMode: 'selector',
plugins: [
require('@tailwindcss/forms'),
require('./nightwind/src/index.js'),
],
safelist: [
'safelisted',

View file

@ -82,9 +82,9 @@
<span class="inline sm:hidden">{{ _("Demo") }}</span>
</a>
{% endif %}
<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 }}">
<a aria-label="{{ _('Install with YunoHost') }}" title="{{ _('Install with YunoHost') }}" class="h-9.5 inline-block rounded-md border p-0 bg-gray-900 dark:bg-dark 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 alt="YunoHost" src="{{ url_for('static', filename='horizontal-yunohost.svg') }}"></span>
<span class="inline-block pr-2 pt-1 dark:invert"><img alt="YunoHost" src="{{ url_for('static', filename='horizontal-yunohost.svg') }}"></span>
</a>
</div>

View file

@ -12,17 +12,17 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{{ url_for('static', filename='fork-awesome.min.css') }}">
{% if config.DEBUG %}
<script src="{{ url_for('static', filename='tailwind-css.js') }}"></script>
<style type="text/tailwindcss">
{{ tailwind_local }}
</style>
{% else %}
<link rel="stylesheet" href="{{ url_for('static', filename='tailwind.css') }}">
{% endif %}
<script>
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
</script>
</head>
<body>
<body class="bg-neutral-50 text-gray-800">
<header class="pb-2 shadow-sm">
<div
class="flex h-12 items-center gap-8 pt-2 px-4 sm:px-6 lg:px-8"
@ -36,24 +36,24 @@
<nav class="hidden md:block">
<ul class="flex items-center gap-6 text-sm">
<li>
<a class="font-bold transition hover:text-gray-500/75" href="{{ url_for('browse_catalog') }}">
<a class="font-bold transition hover:opacity-75" href="{{ url_for('browse_catalog') }}">
{{ _("Catalog") }}
</a>
</li>
<li>
<a class="font-bold transition hover:text-gray-500/75" href="{{ url_for('browse_wishlist') }}">
<a class="font-bold transition hover:opacity-75" href="{{ url_for('browse_wishlist') }}">
{{ _("Wishlist") }}
</a>
</li>
{% if packaging_enabled %}
<li>
<a class="font-bold transition hover:text-gray-500/75" href="{{ url_for('dash') }}">
<a class="font-bold transition hover:opacity-75" href="{{ url_for('dash') }}">
{{ _("Packaging dashboard") }}
</a>
</li>
<li>
<a class="font-bold transition hover:text-gray-500/75" href="{{ url_for('charts') }}">
<a class="font-bold transition hover:opacity-75" href="{{ url_for('charts') }}">
{{ _("Charts & history") }}
</a>
</li>
@ -70,6 +70,15 @@
<i class="fa fa-external-link fa-fw" aria-hidden="true"></i>
{{ _("YunoHost documentation") }}
</a>
<a
class="inline-block"
role="button"
target="_blank"
id="toggleDarkMode"
>
<i class="fa fa-sun-o inline-block dark:hidden rounded-md border border-gray-300 px-3 py-2.5 hover:bg-gray-100" aria-hidden="true" title="{{ _('Toggle light/dark mode') }}"></i>
<i class="fa fa-moon-o hidden dark:inline-block rounded-md border border-gray-300 px-3 py-2.5 hover:bg-gray-100" aria-hidden="true" title="{{ _('Toggle light/dark mode') }}"></i>
</a>
{% if not user %}
<a
class="btn btn-primary inline-block"
@ -252,14 +261,25 @@
<footer class="h-5 my-5 text-center">
<p>
{{ _("Made with <i class='text-red-500 fa fa-heart-o' aria-label='love'></i> using <a class='text-blue-800' href='https://flask.palletsprojects.com'>Flask</a> and <a class='text-blue-800' href='https://tailwindcss.com/'>TailwindCSS</a>") }}
<a class='text-blue-800' href='https://github.com/YunoHost/apps/tree/master/store'><i class='fa fa-code fa-fw' aria-hidden='true'></i> {{ _("Source") }}</a>
<a class='text-blue-800' href='https://yunohost.org/tos'>{{ _("Terms of Services") }}</a>
{{ _("Made with <i class='text-red-500 fa fa-heart-o' aria-label='love'></i> using <a class='text-blue-800 dark:text-blue-400' href='https://flask.palletsprojects.com'>Flask</a> and <a class='text-blue-800 dark:text-blue-400' href='https://tailwindcss.com/'>TailwindCSS</a>") }}
<a class='text-blue-800 dark:text-blue-400' href='https://github.com/YunoHost/apps/tree/master/store'><i class='fa fa-code fa-fw' aria-hidden='true'></i> {{ _("Source") }}</a>
<a class='text-blue-800 dark:text-blue-400' href='https://yunohost.org/tos'>{{ _("Terms of Services") }}</a>
</p>
</footer>
</body>
<script type="text/javascript">
document.getElementById('toggleDarkMode').addEventListener('click', () => {
document.documentElement.classList.toggle('dark')
if (document.documentElement.classList.contains("dark") == true)
{
localStorage.theme = 'dark';
}
else
{
localStorage.theme = 'light';
}
});
{% if user %}
document.getElementById('toggleUserMenu').addEventListener('click', () => {
document.getElementById('userMenu').classList.toggle("hidden");
@ -269,5 +289,4 @@
document.getElementById('menu').classList.toggle("hidden");
});
</script>
</html>

View file

@ -16,7 +16,7 @@
>
<a
href="{{ url_for('app_info', app_id=app) }}"
class="relative block overflow-hidden rounded-lg py-2 px-4 hover:bg-gray-100 mx-2 md:mx-0"
class="relative block overflow-hidden rounded-lg py-2 px-4 hover:bg-neutral-100 mx-2 md:mx-0"
>
<div class="flex justify-between gap-4">
<div class="shrink-0">
@ -27,7 +27,7 @@
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"
class="h-12 w-12 rounded-lg object-cover shadow mt-1"
>
</div>
<div class="w-full">
@ -53,14 +53,16 @@
</span>
</span>
</span>
<p class="max-w-[40ch] text-xs text-gray-700">
<p class="max-w-[40ch] text-xs opacity-75">
{{ infos['manifest']['description']|localize }}
</p>
<div class="hidden">
{{ infos["potential_alternative_to"]|join(', ') }}
</div>
{% if infos['category'] %}
<span class="rounded-full px-2.5 py-0.5 text-[10px] border text-{{ catalog['categories'][infos['category']]['color'] }}-600 border-{{ catalog['categories'][infos['category']]['color'] }}-400 ">
<span class="rounded-full px-2.5 py-0.5 text-[10px] border
text-{{ catalog['categories'][infos['category']]['color'] }}-600
border-{{ catalog['categories'][infos['category']]['color'] }}-400 ">
{{ catalog['categories'][infos['category']]['title']|localize|lower }}
</span>
{% endif %}
@ -90,7 +92,7 @@
id="search"
placeholder="{{ _('Search for…') }}"
{% if request.args.get("search") %}value="{{ request.args.get("search") }}"{% endif %}
class="w-full rounded-md border-gray-200 shadow-sm sm:text-sm py-2 pe-10"
class="w-full sm:text-sm py-2 pe-10"
>
<span class="absolute inset-y-0 end-0 grid w-10 place-content-center pr-4">
@ -102,7 +104,7 @@
<select
name="selectcategory"
id="selectcategory"
class="w-full rounded-md border-gray-200 shadow-sm sm:test-sm px-2 py-1.5"
class="w-full sm:text-sm px-2 py-1.5"
>
<option value="">{{ _("All apps") }}</option>
{% for id, category in catalog['categories'].items() %}
@ -118,14 +120,14 @@
<select
name="selectsort"
id="selectsort"
class="inline-block rounded-md border-gray-200 text-sm ml-1 pl-1 pr-7 h-8 py-0"
class="inline-block text-sm ml-1 pl-1 pr-7 h-8 py-0"
>
<option {% if request.args.get("sort") in [None, "popularity"] %}selected{% endif %} value="popularity">{{ _("Popularity") }}</option>
<option {% if request.args.get("sort") == "newest" %}selected{% endif %} value="newest">{{ _("Newest") }}</option>
<option {% if request.args.get("sort") == "alpha" %}selected{% endif %} value="alpha">{{ _("Alphabetical") }}</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') }}" aria-label="{{ _('Requires to be logged-in') }}"{% endif %}>
<div class="inline-block flex items-center px-2 pt-2 md:pt-0 {% if not user %}opacity-75{% 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">
<span class="sr-only">{{ _("Show only apps you starred") }}</span>
<input type="checkbox" id="starsonly" class="peer sr-only" {% if user and request.args.get("starsonly") %}checked{% endif %} {% if not user%}disabled{% endif %} >

View file

@ -59,6 +59,12 @@
<script>
window.onload = function () {
var theme = "light1";
if (document.documentElement.classList.contains("dark") == true)
{
theme = "dark1";
}
var colors_per_level = [
"#d9534f",
"#E26D4F",
@ -73,6 +79,8 @@ window.onload = function () {
];
new CanvasJS.Chart("levelRing", {
backgroundColor: 'transparent',
theme: theme,
animationEnabled: false,
data: [{
type: "doughnut",
@ -98,6 +106,8 @@ window.onload = function () {
new CanvasJS.Chart("levelHistory", {
backgroundColor: 'transparent',
theme: theme,
animationEnabled: false,
toolTip: {
reversed: true,

View file

@ -143,7 +143,7 @@
<i class="fa fa-flask"></i>
{% for s in infos["testing"]["statuses"] %}
{% if s["context"] == "ci-apps-dev" %}
<i class="fa fa-{% if s["state"] == "success" %}check-circle text-green-600{% else %}times-circle text-red-600{% endif %}"></i>
<i class="fa fa-{% if s["state"] == "success" %}check-circle text-green-600{% else %}times-circle text-red-500{% endif %}"></i>
{% endif %}
{% endfor %}
{% if infos["testing"]["timestamp_updated"] | days_ago > 30 %}
@ -158,7 +158,7 @@
<i class="fa fa-arrow-up"></i>
{% for s in infos["ci-auto-update"]["statuses"] %}
{% if s["context"] == "ci-apps-dev" %}
<i class="fa fa-{% if s["state"] == "success" %}check-circle text-green-600{% else %}times-circle text-red-600{% endif %}"></i>
<i class="fa fa-{% if s["state"] == "success" %}check-circle text-green-600{% else %}times-circle text-red-500{% endif %}"></i>
{% endif %}
{% endfor %}
{% if infos["ci-auto-update"]["timestamp_updated"] | days_ago > 30 %}

View file

@ -5,14 +5,14 @@
{% block main %}
<div class="mx-auto w-full text-center p-8">
<img alt="YunoHost logo" src="{{ url_for('static', filename='ynh_logo_black.svg') }}" class="w-32 mx-auto">
<img alt="YunoHost logo" src="{{ url_for('static', filename='ynh_logo_black.svg') }}" class="w-32 mx-auto dark:invert">
<h1 class="text-2xl font-bold">
{{ _("Application Store") }}
</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">
<div class="text-center border rounded-lg h-32 mx-3 sm:mx-0">
<div class="text-center border rounded-lg h-32 mx-3 sm:mx-0 border-gray-200">
<a
href="{{ url_for('browse_catalog') }}"
class="h-full relative block overflow-hidden hover:bg-gray-200 pt-12"
@ -23,7 +23,7 @@
</a>
</div>
{% for id, category in catalog['categories'].items() %}
<div class="text-center border rounded-lg h-32 mx-3 sm:mx-0">
<div class="text-center border rounded-lg h-32 mx-3 sm:mx-0 border-gray-200">
<a
href="{{ url_for('browse_catalog', category=id) }}"
class="h-full relative block overflow-hidden hover:bg-gray-200 pt-10"
@ -32,7 +32,7 @@
<i class="fa fa-{{ category['icon'] }}" aria-hidden="true"></i>
{{ category['title']|localize }}
</h2>
<p class="mx-auto max-w-[40ch] text-xs text-gray-500">
<p class="mx-auto max-w-[40ch] text-xs opacity-75">
{{ category['description']|localize }}
</p>
</a>

View file

@ -7,22 +7,22 @@
<h1 class="text-2xl font-bold">
{{ _("Application Wishlist") }}
</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>
<p class="text-sm opacity-75 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>
<div class="max-w-screen-md mx-auto mt-3 mb-3">
<div class="flex flex-col md:flex-row items-center">
<div class="px-2 inline-block relative basis-2/3 text-gray-700">
<div class="px-2 inline-block relative basis-2/3">
<label for="search" class="sr-only"> {{ _("Search") }} </label>
<input
type="text"
id="search"
placeholder="{{ _('Search for…') }}"
class="w-full rounded-md border-gray-200 shadow-sm sm:text-sm py-2 pe-10"
class="w-full sm:text-sm py-2 pe-10"
>
<span class="absolute inset-y-0 end-0 grid w-10 place-content-center pr-4">
<span class="absolute inset-y-0 end-0 grid w-10 place-content-center pr-4 opacity-75">
<i class="fa fa-search" aria-hidden="true"></i>
</span>
</div>
@ -40,13 +40,13 @@
<select
name="selectsort"
id="selectsort"
class="inline-block rounded-md border-gray-200 text-sm ml-1 pl-1 pr-7 h-8 py-0"
class="inline-block text-sm ml-1 pl-1 pr-7 h-8 py-0"
>
<option {% if request.args.get("sort") in [None, "popularity"] %}selected{% endif %} value="popularity">{{ _("Popularity") }}</option>
<option {% if request.args.get("sort") == "alpha" %}selected{% endif %} value="alpha">{{ _("Alphabetical") }}</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') }}" aria-label="{{ _('Requires to be logged-in') }}"{% endif %}>
<div class="inline-block flex items-center px-2 pt-2 md:pt-0 {% if not user %}opacity-75{% 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">
<span class="sr-only">{{ _("Show only apps you starred") }}</span>
<input type="checkbox" id="starsonly" class="peer sr-only" {% if user and request.args.get("starsonly") %}checked{% endif %} {% if not user%}disabled{% endif %}>
@ -64,7 +64,7 @@
</div>
<div class="overflow-x-auto max-w-screen-lg mx-auto pt-5">
<table class="min-w-full divide-y-2 divide-gray-200 bg-white text-sm">
<table class="min-w-full divide-y-2 divide-gray-100 dark:divide-gray-700 text-sm">
<thead>
<tr>
<th class="hidden sm:table-cell whitespace-nowrap px-4 py-2 font-medium">
@ -79,7 +79,7 @@
</tr>
</thead>
<tbody id="wishlist" class="divide-y divide-gray-200">
<tbody id="wishlist" class="divide-y divide-gray-100 dark:divide-gray-700">
{% for app, infos in wishlist.items() %}
{% set this_app_stars = stars.get(app, {})|length %}
{% if user %}
@ -95,7 +95,7 @@
<td class="inline-block sm:table-cell px-4 py-2 font-bold sm:max-w-[10em]">
{{ infos['name'] }}
</td>
<td class="block sm:table-cell px-4 py-0 sm:py-2 text-gray-700 max-w-md">{{ infos['description'] }}</td>
<td class="block sm:table-cell px-4 py-0 sm:py-2 opacity-75 max-w-md">{{ infos['description'] }}</td>
<td class="float-right sm:float-none sm:table-cell py-2 px-1 sm:px-0">
{% if infos['website'] %}
<a
@ -105,7 +105,7 @@
class="inline-block"
target="_blank"
>
<i class="fa fa-globe rounded-md border px-3 py-2 hover:bg-gray-100" aria-hidden="true"></i>
<i class="fa fa-globe rounded-md border border-gray-300 px-3 py-2 hover:bg-gray-100" aria-hidden="true"></i>
</a>
{% endif %}
</td>
@ -118,7 +118,7 @@
class="inline-block"
target="_blank"
>
<i class="fa fa-code rounded-md border px-3 py-2 hover:bg-gray-100" aria-hidden="true"></i>
<i class="fa fa-code rounded-md border border-gray-300 px-3 py-2 hover:bg-gray-100" aria-hidden="true"></i>
</a>
{% endif %}
</td>