Commit 5c529a83 authored by Yassine Doghri's avatar Yassine Doghri
Browse files

feat(settings): add theme settings to set an accent color for all public pages

set 6 base accent colors: pine, lake, jacaranda, crimson, amber and onyx
parent a746a781
......@@ -447,4 +447,6 @@ class App extends BaseConfig
'192' => '/icon-192.png',
'512' => '/icon-512.png',
];
public string $theme = 'crimson';
}
@layer components {
.color-radio-btn {
@apply absolute opacity-0;
&:focus + label {
@apply ring-accent;
}
&:checked {
@apply ring-2 ring-contrast;
& + label {
@apply flex items-center justify-center text-2xl text-accent-contrast bg-accent-base;
&::before {
content: "✓";
}
}
}
& + label {
@apply inline-block w-16 h-16 text-sm font-semibold rounded-full cursor-pointer border-contrast bg-accent-base text-accent-contrast border-3;
color: hsl(var(--color-text-muted));
}
}
}
/*
--color-brand-lighter: hsl(173 44% 96%);
--color-brand-light: hsl(111 64% 94%);
--color-brand-base: hsl(174 100% 29%);
--color-brand-dark: hsl(172 100% 17%);
--color-brand-darker: hsl(131 100% 12%);
--color-background-elevated: hsl(0 0% 100%);
--color-background-base: hsl(173 44% 96%);
--color-text-base: hsl(240 17% 2%);
--color-text-muted: hsl(240 8% 63%);
--color-text-inverted: hsl(0 0% 100%);
--color-brand-lighter: 173 44% 96%;
--color-brand-light: 111 64% 94%;
--color-brand-base: 174 100% 29%;
--color-brand-dark: 172 100% 17%;
--color-brand-darker: 131 100% 12%;
--color-background-elevated: 0 0% 100%;
--color-background-base: 173 44% 96%;
--color-text-base: 240 17% 2%;
--color-text-muted: 240 8% 63%;
--color-text-inverted: 0 0% 100%;
*/
/*
--color-accent-base: 0 100% 38%;
--color-accent-hover: 0 100% 48%;
--color-accent-muted: 0 8% 63%;
--color-heading-foreground: 0 64% 94%;
--color-heading-background: 0 100% 17%;
--color-background-elevated: 209 35% 15%;
--color-background-base: 210 34% 13%;
--color-background-navigation: 210 34% 11%;
--color-background-header: 200 38% 15%;
--color-background-highlight: 200 38% 25%;
--color-background-backdrop: 0 0% 50%;
--color-border-subtle: 240 8% 27%;
--color-border-contrast: 240 8% 78%;
--color-border-navigation: 210 34% 4%;
--color-text-base: 240 17% 100%;
--color-text-muted: 240 8% 63%;
*/
@layer base {
:root {
--color-accent-base: 174 100% 29%;
......
......@@ -2,10 +2,12 @@
@import "./custom.css";
@import "./fonts.css";
@import "./colors.css";
@import "./themes.css";
@import "./breadcrumb.css";
@import "./dropdown.css";
@import "./choices.css";
@import "./radioBtn.css";
@import "./colorRadioBtn.css";
@import "./switch.css";
@import "./radioToggler.css";
@import "./formInputTabs.css";
......
@layer components {
.form-radio-btn {
@apply absolute mt-3 ml-3 border-contrast border-3 text-accent-base;
}
.form-radio-btn:focus + label {
@apply ring-accent;
}
&:focus {
@apply ring-accent;
}
.form-radio-btn + label {
@apply inline-block py-2 pl-8 pr-2 text-sm font-semibold rounded-lg cursor-pointer border-contrast bg-elevated border-3;
color: hsl(var(--color-text-muted));
}
&:checked {
@apply ring-2 ring-contrast;
.form-radio-btn:checked + label {
@apply text-accent-contrast bg-accent-base;
}
& + label {
@apply text-accent-contrast bg-accent-base;
}
}
.form-radio-btn:checked {
@apply ring-2 ring-contrast;
& + label {
@apply inline-block py-2 pl-8 pr-2 text-sm font-semibold rounded-lg cursor-pointer border-contrast bg-elevated border-3;
color: hsl(var(--color-text-muted));
}
}
}
/* Castopod's brand color */
.theme-pine {
--color-accent-base: 174 100% 29%;
--color-accent-hover: 172 100% 17%;
--color-accent-muted: 131 100% 12%;
--color-accent-contrast: 0 0% 100%;
--color-heading-foreground: 172 100% 17%;
--color-heading-background: 111 64% 94%;
--color-background-elevated: 0 0% 100%;
--color-background-base: 173 44% 96%;
--color-background-navigation: 172 100% 17%;
--color-background-header: 172 100% 17%;
--color-background-highlight: 111 64% 94%;
--color-background-backdrop: 0 0% 50%;
--color-border-subtle: 111 42% 86%;
--color-border-contrast: 0 0% 0%;
--color-border-navigation: 131 100% 12%;
--color-text-base: 158 8% 3%;
--color-text-muted: 172 8% 38%;
}
/* Red / Rose color */
.theme-crimson {
--color-accent-base: 350 87% 61%;
--color-accent-hover: 348 75% 40%;
--color-accent-muted: 348 73% 32%;
--color-accent-contrast: 0 0% 100%;
--color-heading-foreground: 348 73% 32%;
--color-heading-background: 344 79% 96%;
--color-background-elevated: 0 0% 100%;
--color-background-base: 350 44% 96%;
--color-background-header: 348 75% 40%;
--color-background-highlight: 344 79% 96%;
--color-background-backdrop: 0 0% 50%;
--color-border-subtle: 348 42% 86%;
--color-border-contrast: 0 0% 0%;
--color-text-base: 340 8% 3%;
--color-text-muted: 345 8% 38%;
}
/* Blue color */
.theme-lake {
--color-accent-base: 194 100% 44%;
--color-accent-hover: 194 100% 22%;
--color-accent-muted: 195 100% 11%;
--color-accent-contrast: 0 0% 100%;
--color-heading-foreground: 194 100% 22%;
--color-heading-background: 195 100% 92%;
--color-background-elevated: 0 0% 100%;
--color-background-base: 196 44% 96%;
--color-background-header: 194 100% 22%;
--color-background-highlight: 195 100% 92%;
--color-background-backdrop: 0 0% 50%;
--color-border-subtle: 195 42% 86%;
--color-border-contrast: 0 0% 0%;
--color-text-base: 194 8% 3%;
--color-text-muted: 195 8% 38%;
}
/* Orange color */
.theme-amber {
--color-accent-base: 17 100% 57%;
--color-accent-hover: 17 100% 35%;
--color-accent-muted: 17 100% 24%;
--color-accent-contrast: 0 0% 100%;
--color-heading-foreground: 17 100% 35%;
--color-heading-background: 17 100% 89%;
--color-background-elevated: 0 0% 100%;
--color-background-base: 15 44% 96%;
--color-background-header: 17 100% 35%;
--color-background-highlight: 17 100% 89%;
--color-background-backdrop: 0 0% 50%;
--color-border-subtle: 17 42% 86%;
--color-border-contrast: 0 0% 0%;
--color-text-base: 15 8% 3%;
--color-text-muted: 17 8% 38%;
}
/* Violet color */
.theme-jacaranda {
--color-accent-base: 254 72% 52%;
--color-accent-hover: 254 73% 30%;
--color-accent-muted: 254 71% 19%;
--color-accent-contrast: 0 0% 100%;
--color-heading-foreground: 254 73% 30%;
--color-heading-background: 254 73% 84%;
--color-background-elevated: 0 0% 100%;
--color-background-base: 253 44% 96%;
--color-background-header: 254 73% 30%;
--color-background-highlight: 254 88% 91%;
--color-background-backdrop: 0 0% 50%;
--color-border-subtle: 254 42% 86%;
--color-border-contrast: 0 0% 0%;
--color-text-base: 253 8% 3%;
--color-text-muted: 254 8% 38%;
}
/* Black color */
.theme-onyx {
--color-accent-base: 240 17% 2%;
--color-accent-hover: 240 17% 17%;
--color-accent-muted: 240 17% 17%;
--color-accent-contrast: 0 0% 100%;
--color-heading-foreground: 240 17% 17%;
--color-heading-background: 240 17% 94%;
--color-background-elevated: 0 0% 100%;
--color-background-base: 240 17% 96%;
--color-background-header: 240 12% 17%;
--color-background-highlight: 240 17% 94%;
--color-background-backdrop: 0 0% 50%;
--color-border-subtle: 240 17% 86%;
--color-border-contrast: 0 0% 0%;
--color-text-base: 240 8% 3%;
--color-text-muted: 240 8% 38%;
}
<?php
declare(strict_types=1);
namespace App\Views\Components\Forms;
class ColorRadioButton extends FormComponent
{
protected bool $isChecked = false;
public function setIsChecked(string $value): void
{
$this->isChecked = $value === 'true';
}
public function render(): string
{
$radioInput = form_radio(
[
'id' => $this->value,
'name' => $this->name,
'class' => 'color-radio-btn',
],
$this->value,
old($this->name) ? old($this->name) === $this->value : $this->isChecked,
);
return <<<HTML
<div class="{$this->class}">
{$radioInput}
<label for="{$this->value}" title="{$this->slot}" data-tooltip="bottom"></label>
</div>
HTML;
}
}
......@@ -31,6 +31,14 @@ $routes->group(
'as' => 'settings-instance-delete-icon',
'filter' => 'permission:settings-manage',
]);
$routes->get('theme', 'SettingsController::theme', [
'as' => 'settings-theme',
'filter' => 'permission:settings-manage',
]);
$routes->post('theme', 'SettingsController::attemptSetInstanceTheme', [
'as' => 'settings-theme',
'filter' => 'permission:settings-manage',
]);
});
$routes->group('persons', function ($routes): void {
......
......@@ -88,7 +88,7 @@ class SettingsController extends BaseController
]);
}
return redirect()->back();
return redirect('settings-general')->with('message', lang('Settings.general.instanceEditSuccess'));
}
public function deleteIcon(): RedirectResponse
......@@ -100,6 +100,25 @@ class SettingsController extends BaseController
service('settings')
->forget('App.siteIcon');
return redirect()->back();
return redirect('settings-general')->with('message', lang('Settings.general.deleteIconSuccess'));
}
public function theme(): string
{
helper('form');
return view('settings/theme');
}
public function attemptSetInstanceTheme(): RedirectResponse
{
$theme = $this->request->getPost('theme');
service('settings')
->set('App.theme', $theme);
// delete all pages cache
cache()
->deleteMatching('page*');
return redirect('settings-theme')->with('message', lang('Settings.theme.setInstanceThemeSuccess'));
}
}
<?php
declare(strict_types=1);
/**
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
return [
'go_to_website' => 'View site',
'go_to_admin' => 'Go to admin',
'dashboard' => 'Dashboard',
'admin' => 'Home',
'podcasts' => 'Podcasts',
'podcast-list' => 'All podcasts',
'podcast-create' => 'New podcast',
'podcast-import' => 'Import a podcast',
'persons' => 'Persons',
'person-list' => 'All persons',
'person-create' => 'New person',
'fediverse' => 'Fediverse',
'fediverse-blocked-actors' => 'Blocked accounts',
'fediverse-blocked-domains' => 'Blocked domains',
'users' => 'Users',
'user-list' => 'All users',
'user-create' => 'New user',
'pages' => 'Pages',
'page-list' => 'All pages',
'page-create' => 'New Page',
'settings' => 'Settings',
'settings-general' => 'General',
'account' => [
'my-account' => 'My account',
'change-password' => 'Change password',
'logout' => 'Logout',
],
];
......@@ -17,6 +17,7 @@ return [
'contributors' => 'contributors',
'pages' => 'pages',
'settings' => 'settings',
'theme' => 'theme',
'add' => 'add',
'new' => 'new',
'edit' => 'edit',
......
......@@ -10,6 +10,7 @@ declare(strict_types=1);
return [
'go_to_website' => 'View site',
'go_to_admin' => 'Go to admin',
'dashboard' => 'Dashboard',
'admin' => 'Home',
'podcasts' => 'Podcasts',
......@@ -28,6 +29,9 @@ return [
'pages' => 'Pages',
'page-list' => 'All pages',
'page-create' => 'New Page',
'settings' => 'Settings',
'settings-general' => 'General',
'settings-theme' => 'Theme',
'account' => [
'my-account' => 'My account',
'change-password' => 'Change password',
......
......@@ -10,7 +10,7 @@ declare(strict_types=1);
return [
'title' => 'General settings',
'form' => [
'general' => [
'site_section_title' => 'Instance',
'site_icon' => 'Site icon',
'site_icon_delete' => 'Delete site icon',
......@@ -19,5 +19,20 @@ return [
'site_name' => 'Site name',
'site_description' => 'Site description',
'submit' => 'Save',
'instanceEditSuccess' => 'Instance has been updated successfully!',
'deleteIconSuccess' => 'Site icon has been remove successfully!',
],
'theme' => [
'title' => 'Theme',
'accent_section_title' => 'Accent color',
'accent_section_subtitle' => 'Choose the color to determine the look and feel of all public pages.',
'pine' => 'Pine',
'crimson' => 'Crimson',
'amber' => 'Amber',
'lake' => 'Lake',
'jacaranda' => 'Jacaranda',
'onyx' => 'Onyx',
'submit' => 'Save',
'setInstanceThemeSuccess' => 'Theme has been updated successfully!',
],
];
<?php
declare(strict_types=1);
/**
* @copyright 2020 Podlibre
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
* @link https://castopod.org/
*/
return [
'go_to_website' => 'Visiter le site',
'dashboard' => 'Tableau de bord',
'admin' => 'Accueil',
'podcasts' => 'Podcasts',
'podcast-list' => 'Tous les podcasts',
'podcast-create' => 'Créer un podcast',
'podcast-import' => 'Importer un podcast',
'persons' => 'Intervenants',
'person-list' => 'Tous les intervenants',
'person-create' => 'Nouvel intervenant',
'fediverse' => 'Fédiverse',
'fediverse-blocked_actors' => 'Utilisateurs blockés',
'fediverse-blocked_domains' => 'Domaines blockés',
'users' => 'Utilisateurs',
'user-list' => 'Tous les utilisateurs',
'user-create' => 'Créer un utilisateur',
'pages' => 'Pages',
'page-list' => 'Toutes les pages',
'page-create' => 'Créer une page',
'settings' => 'Paramètres',
'settings-general' => 'Général',
'account' => [
'my-account' => 'Mon compte',
'change-password' => 'Modifier le mot de passe',
'logout' => 'Déconnexion',
],
];
......@@ -16,6 +16,8 @@ return [
'episodes' => 'épisodes',
'contributors' => 'contributeurs',
'pages' => 'pages',
'settings' => 'paramètres',
'theme' => 'thème',
'add' => 'ajouter',
'new' => 'créer',
'edit' => 'modifier',
......
......@@ -28,6 +28,9 @@ return [
'pages' => 'Pages',
'page-list' => 'Toutes les pages',
'page-create' => 'Créer une page',
'settings' => 'Paramètres',
'settings-general' => 'Général',
'settings-themes' => 'Thèmes',
'account' => [
'my-account' => 'Mon compte',
'change-password' => 'Modifier le mot de passe',
......
......@@ -10,15 +10,28 @@ declare(strict_types=1);
return [
'title' => 'Paramètres généraux',
'form' => [
'general' => [
'site_section_title' => 'Instance',
'site_section_subtitle' => 'configuration de l’instance',
'site_icon' => 'Favicon du site',
'site_icon_delete' => 'Supprimer la favicon du site',
'site_icon_hint' => 'Les favicons sont ce que vous voyez sur les onglets de votre navigateur, dans votre barre de favoris, et lorsque vous ajoutez un site web en raccourci sur des appareils mobiles.',
'site_icon_helper' => 'La favicon doit être carrée, avec au minimum 512px de largeur et de hauteur.',
'site_name' => 'Titre du site',
'site_description' => 'Description du site',
'submit' => 'Save',
'submit' => 'Sauvegarder',
'instanceEditSuccess' => 'L’instance a bien été mise à jour !',
'deleteIconSuccess' => 'La favicon du site a bien été retirée !',
],
'theme' => [
'title' => 'Thème',
'accent_section_title' => 'Couleur d’accentuation',
'accent_section_subtitle' => 'Sélectionnez une couleur qui déterminera l’apparence de toutes les pages publiques.',
'pine' => 'Pin',
'crimson' => 'Cramoisi',
'amber' => 'Ambre',
'lake' => 'Lac',
'jacaranda' => 'Jacaranda',
'onyx' => 'Onyx',
'setInstanceThemeSuccess' => 'Le thème a bien été mis à jour !',
],
];
......@@ -107,6 +107,7 @@ module.exports = {
podcastMain: "1fr minmax(200px, 300px)",
cards: "repeat(auto-fill, minmax(14rem, 1fr))",
latestEpisodes: "repeat(5, 1fr)",
colorButtons: "repeat(auto-fill, minmax(4rem, 1fr))",
},
gridTemplateRows: {
admin: "40px 1fr",
......
......@@ -11,7 +11,7 @@
<a href="<?= route_to(
'home',
) ?>" class="inline-flex items-center h-full px-6 text-sm font-semibold hover:underline focus:ring-inset focus:ring-accent">
<?= lang('AdminNavigation.go_to_website') ?>
<?= lang('Navigation.go_to_website') ?>
<?= icon('external-link', 'ml-1 opacity-60') ?>
</a>
</div>
......@@ -48,12 +48,12 @@
$menuItems = [
[
'type' => 'link',
'title' => lang('AdminNavigation.account.my-account'),
'title' => lang('Navigation.account.my-account'),
'uri' => route_to('my-account'),
],
[
'type' => 'link',
'title' => lang('AdminNavigation.account.change-password'),
'title' => lang('Navigation.account.change-password'),
'uri' => route_to('change-password'),
],
[
......@@ -61,7 +61,7 @@
],
[
'type' => 'link',
'title' => lang('AdminNavigation.account.logout'),
'title' => lang('Navigation.account.logout'),
'uri' => route_to('logout'),
],
];
......
......@@ -24,7 +24,7 @@ $navigation = [
],
'settings' => [
'icon' => 'settings',
'items' => ['settings-general'],
'items' => ['settings-general', 'settings-theme'],
],
]; ?>
......