Comment créer un thème pour Cyca

Ce document vous guidera dans la création d’un thème pour Cyca.

Installez les dépendances

Vous aurez besoin d’installer un certain nombre d’outils pour créer un thème pour Cyca:

  • git
  • node.js (Cyca utilise la version LTS)
  • le code source de Cyca
git clone https://github.com/RichardDern/Cyca

Installez les dépendances de Cyca:

cd Cyca
npm i

Un thème pour Cyca est essentiellement un fichier de configuration pour tailwind. Si vous savez déjà comment configurer tailwind, vous savez - presque - déjà créer un thème pour Cyca ! Dans le cas contraire, vous devriez lire la documentation de tailwind.

Si vous jetez un oeil au fichier resources/themes/baseTheme.js, vous verrez que certains éléments sont déjà pris en charge. Tout ce dont vous avez besoin est d’étendre cet objet.

Voyons comment créer un thème.

Créez le thème

Créez un nouveau répertoire dans resources/themes/.

mkdir resources/themes/my_theme

La structure d’un thème est relativement simple. Dans le répertoire de votre thème, vous devez avoir les fichiers et dossiers suivants:

  • dist/ : Ce dossier sera créé automatiquement pendant la compilation
  • resources/ : Vous placerez dans ce dossier les ressources dont votre thème a besoin (fontes, icones, etc.)
  • theme.css : C’est le point d’entrée de votre thème pour PostCSS
  • theme.js : Ce fichier contient votre configuration pour tailwind
  • theme.json : Ce fichier contient les méta-données de votre thème, comme l’auteur, les liens, etc.

Commençons avec le fichier le plus simple: theme.css. Tout ce dont il a besoin c’est d’importer la feuille de style de base de Cyca, et contient donc la ligne suivante:

@import "../../css/app.css";

Bien sûr, après cette ligne, vous pouvez ajouer tout le CSS dont votre thème a besoin.

Créez ensuite le fichier theme.json:

{
    "author": "Richard Dern",
    "name": "Cyca Dark",
    "description": "Cyca's default theme - Dark colors",
    "url": "https://github.com/RichardDern/cyca_theme_dark",
    "screenshot": "images/screenshot.png",
    "icons": "images/icons.svg",
    "inherits": null
}

Référence du fichier theme.json

Clé Contenu
author Nom de l’auteur du thème
name Nom du thème
description Description courte du thème
url URL vers le dépôt git du thème
screenshot Chemin relatif au dossier resources/ du thème vers une capture d’écran
icons Chemin relatif au dossier resources/ du thème vers le fichier d’icones du thème
inherits Nom du thème hérité

Le fichier screenshot doit être une image valide, au format jpg ou png, d’une taille maximale de 1280x720 pixels.

Référence des icones

Le fichier icons doit être un unique fichier de sprites SVG. Les symbol_id utilisés par Cyca sont les suivants:

Symbol id Représente
account Lien vers “Mon compte”
add Bouton “Ajouter”
check Coche
checkmark Ruban coché
collapsed Dossier réduit
expanded Dossier étendu
folder Dossier normal
house Dossier racine
logout Lien vers la déconnexion
open Bouton “Ouvrir”
unread_items Dossier des éléments non-lus
update Bouton “Mettre à jour”
share Bouton “Partager”

Fichier theme.js

Il s’agit du fichier où vous définissez les fontes, les couleurs, et tout ce qui concerne l’interface utilisateur de Cyca.

Prenons l’exemple du thème par défaut dark theme :

// We will use lodash's merge method to recursively merge base theme object and ours
const _ = require('lodash');

// We will create a plugin to add a locally stored font
const plugin = require("tailwindcss/plugin");

// We begin the theme's definition by merging it with Cyca's provided base configuration
const theme = _.merge(require("../baseTheme.js"), {
    theme: {
        // Re-defining the "sans-serif" font family to add Quicksand, "our" custom font
        fontFamily: {
            sans:
                'Quicksand, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"'
        },
        // Here, we import a colors file which contains our color scheme
        colors: require("./colors")
    },
    plugins: [
        // And we create a plugin to reference our font.
        // Note: the same could have been achieved directly in the CSS
        // /themes/cyca-dark/fonts is relative to Cyca's public/ directory
        plugin(function({ addBase, config }) {
            const fontUrl =
                "/themes/cyca-dark/fonts/Quicksand-Medium.ttf";

            addBase({
                "@font-face": {
                    fontFamily: "Quicksand",
                    src: "url(" + fontUrl + ")"
                }
            });
        })
    ]
});

// Finally, we export the module so tailwind can use it
module.exports = theme;

La police Quicksand est stockée dans la structure du thème, plus précisément dans le dossier suivant (relatif à la racine de Cyca):

  • resources
    • themes
      • cyca-dark
        • resources
          • fonts
            • Quicksand-Medium.ttf

Tout ce qui se trouve dans le répertoire resources/ sera copié dans le dossier public/themes/theme_name/ pendant la compilation.

Vous aurez peut-être noté que nous importons un fichier colors.js. Vous ne le savez pas encore, à moins que vous ayez jeté un oeil au code source du thème, mais le fichier colors.js lui-même importe le fichier colorScheme.js.

Le fichier colorScheme.js stocke la “base de données” des couleurs utilisées par le thème. Bien qu’il ne soit pas strictement nécessaire de le stocker à part, c’est une bonne pratique. Dans notre cas, c’est un simple module javascript:

module.exports = {
    white: "#ffffff",
    red: {
        "800": "#9B2C2C",
        "900": "#742A2A"
    },
    yellow: {
        "500": "#ED8936"
    },
    green: {
        "400": "#68D391",
        "600": "#38A169",
        "800": "#276749",
        "900": "#22543D"
    },
    blue: {
        "400": "#63B3ED",
        "500": "#4299E1",
        "700": "#2B6CB0",
        "800": "#2C5282"
    },
    purple: {
        "500": "#9F7AEA"
    },
    "cool-gray": {
        "500": "#A0AEC0"
    },
    gray: {
        "100": "#5c5c5c",
        "200": "#525252",
        "300": "#474747",
        "400": "#3d3d3d",
        "500": "#333333",
        "600": "#292929",
        "700": "#1f1f1f",
        "800": "#141414",
        "900": "#0a0a0a"
    }
}

Tailwind ne génèrera que les classes correspondantes aux couleurs mentionnées ici, ce qui produira un fichier CSS plus léger. Sans ce fichier, tailwind génèrerait un fichier CSS beaucoup plus volumineux.

Maintenant, revenons au fichier colors.js, qui associe un nom à une couleur.

// Include colors we just defined
const baseColors = require('./colorScheme');

/**
 * Colors used in the CSS by calling ```theme("colors.<named color>")```
 * For a hovered danger button background color, we will call
 * ```theme("colors.button.danger.bg-hover")``` in our CSS
 */
module.exports = {
    baseColors,
    /**
     * Base
     */
    a: {
        normal: baseColors.blue[400],
        hover: baseColors.blue[500]
    },
    article: {
        text: baseColors.white,
        bg: baseColors.gray[300],
        border: baseColors.gray[700],
        body: baseColors.white
    },
    body: {
        text: baseColors.white,
        bg: baseColors.gray[900]
    },
    dl: {
        text: baseColors.white
    },
    dt: {
        bg: baseColors.gray[700]
    },
    dd: {
        bg: baseColors.gray[400]
    },
    button: {
        text: baseColors.white,
        danger: {
            bg: baseColors.red[800],
            "bg-hover": baseColors.red[900]
        },
        success: {
            bg: baseColors.green[600],
            "bg-hover": baseColors.green[800]
        },
        info: {
            bg: baseColors.blue[700],
            "bg-hover": baseColors.blue[800]
        }
    },
    label: {
        text: baseColors.gray[100]
    },
    formGroup: {
        text: baseColors.white
    },
    input: {
        text: baseColors.white,
        bg: baseColors.gray[300]
    },
    scrollbar: baseColors.gray[300],
    /**
     * Components
     */
    badge: {
        text: baseColors.white,
        bg: baseColors.gray[300],
        "bg-article": baseColors.gray[500]
    },
    caret: baseColors.gray[100],
    "folders-tree": baseColors.gray[800],
    "documents-list": baseColors.gray[700],
    "feeditems-list": baseColors.gray[600],
    "details-view": baseColors.gray[500],
    "list-item": {
        text: baseColors.white,
        active: {
            text: baseColors.white,
            bg: baseColors.gray[400]
        },
        "dragged-over": {
            bg: baseColors.green[900]
        },
        "cannot-drop": {
            bg: baseColors.red[900]
        }
    },
    "feed-item": {
        text: baseColors.white,
        active: {
            text: baseColors.white,
            bg: baseColors.gray[400]
        },
        read: baseColors.gray[100],
        meta: baseColors["cool-gray"][500]
    },
    account: {
        menu: {
            bg: baseColors.gray[800],
            item: {
                text: baseColors.gray[300],
                hovered: baseColors.blue[500],
                selected: {
                    text: baseColors.white
                }
            }
        },
        content: {
            bg: baseColors.gray[700]
        }
    },
    folders: {
        common: baseColors.yellow[500],
        unread: {
            "not-empty": baseColors.purple[500],
            empty: baseColors.gray[100]
        },
        root: baseColors.blue[500],
        account: baseColors.green[600],
        logout: baseColors.red[800]
    },
    "themes-browser": {
        card: {
            "border-color": baseColors.gray[300],
            selected: {
                "border-color": baseColors.green[600],
            },
            hovered: {
                "border-color": baseColors.blue[500],
            }
        }
    },
    alerts: {
        success: {
            bg: baseColors.green[600],
            text: baseColors.white
        },
        error: {
            bg: baseColors.red[900],
            text: baseColors.white
        },
        warning: {
            bg: baseColors.yellow[500],
            text: baseColors.white
        }
    }
};

Ceci est l’exemple le plus complet et à jour. Il montre tous les composants configurable, mais les choses sont considérablement plus simple quand vous héritez votre thème.

Héritage de thème

Cette fois, nous allons utiliser le thème clair de Cyca comme exemple, puisqu’il hérite du thème dark que nous venons de voir.

Lorsque votre thème hérite d’un autre, à moins que vous n’en décidiez autrement, il utilisera la configuration du thème parent, incluant les fontes, couleurs, icons, plugins, etc.

Dans le cas du thème clair, il hérite du thème sombre. Donc son fichier theme.json ressemblera à ceci:

{
    "author": "Richard Dern",
    "name": "Cyca Light",
    "description": "Cyca's default theme - Light colors",
    "url": "https://github.com/RichardDern/cyca_theme_light",
    "screenshot": "images/screenshot.png",
    "icons": null,
    "inherits": "cyca-dark"
}

Notez que la clé icons est définie à null, parce qu’on ne propose pas d’icones dans ce thème: nous utiliserons les icones du thème sombre, explicitement mentionné par la clé inherits.

Notre fichier theme.js est considérablement plus léger:

const _ = require('lodash');

const theme = _.merge(require("../cyca_theme_dark/theme"), {
    theme: {
        colors: require("./colors")
    }
});

module.exports = theme;

Le fichier colorScheme.js référence toujours toutes les couleurs utilisées dans le thème, mais elles sont évidemment différentes de celles du thème sombre:

module.exports = {
    white: "#ffffff",
    black: "#000000",
    red: {
        "100": "#FFF5F5",
        "800": "#9B2C2C",
        "900": "#742A2A"
    },
    yellow: {
        "100": "#FFFAF0",
        "500": "#ED8936"
    },
    green: {
        "100": "#F0FFF4",
        "400": "#68D391",
        "600": "#38A169",
        "800": "#276749",
        "900": "#22543D"
    },
    blue: {
        "400": "#63B3ED",
        "500": "#4299E1",
        "700": "#2B6CB0",
        "800": "#2C5282"
    },
    purple: {
        "500": "#9F7AEA"
    },
    "cool-gray": {
        "700": "#4A5568"
    },
    gray: {
        "100": "#F3F3F3",
        "200": "#E8E8E8",
        "300": "#DCDCDC",
        "400": "#D0D0D0",
        "500": "#C5C5C5",
        "600": "#B9B9B9",
        "700": "#ADADAD",
        "800": "#A2A2A2",
        "900": "#969696"
    }
}

Mais le fichier colors.js est, lui-aussi, beaucoup plus léger:

const baseColors = require('./colorScheme');

module.exports = {
    /**
     * Base
     */
    a: {
        normal: baseColors.blue[800],
        hover: baseColors.blue[700]
    },
    article: {
        text: baseColors.black,
        bg: baseColors.gray[500],
        border: baseColors.gray[500],
        body: baseColors.black
    },
    body: {
        text: baseColors.black,
        bg: baseColors.gray[100]
    },
    label: {
        text: baseColors.gray[900]
    },
    input: {
        text: baseColors.black,
        bg: baseColors.white
    },
    scrollbar: baseColors.gray[900],
    /**
     * Components
     */
    badge: {
        text: baseColors.black,
        bg: baseColors.gray[700],
        "bg-article": baseColors.gray[700]
    },
    caret: baseColors.gray[900],
    "folders-tree": baseColors.gray[500],
    "documents-list": baseColors.gray[400],
    "feeditems-list": baseColors.gray[300],
    "details-view": baseColors.gray[200],
    "list-item": {
        text: baseColors.black,
        active: {
            text: baseColors.black,
            bg: baseColors.gray[700]
        },
    },
    "feed-item": {
        text: baseColors.black,
        active: {
            text: baseColors.black,
            bg: baseColors.gray[500]
        },
        read: baseColors.gray[900],
        meta: baseColors["cool-gray"][700]
    },
    account: {
        menu: {
            bg: baseColors.gray[300],
            item: {
                text: baseColors.gray[900],
                selected: {
                    text: baseColors.black
                }
            }
        },
        content: {
            bg: baseColors.gray[200]
        }
    },
    alerts: {
        success: {
            bg: baseColors.green[100],
            text: baseColors.green[900]
        },
        error: {
            bg: baseColors.red[100],
            text: baseColors.red[900]
        },
        warning: {
            bg: baseColors.yellow[100],
            text: baseColors.yellow[500]
        }
    }
};

Notez que comparé au fichier colors.js du thème sombre, celui-ci est plus court, parce que nous avons hérité la couleur de certains composants. Nous n’avons donc pas besoin de les re-déclarer ici, à moins de vouloir les modifier.

Comme notre thème hérite d’un autre, nous devons l’installer puisque le processus de compilation a besoin de ses fichiers.

Nous devons donc connaitre l’URL du dépôt git du thème parent. Dans le cas du thème clair, nous héritons du thème sombre, dont l’URL est https://github.com/RichardDern/cyca_theme_dark.

Veuillez noter que si le parent lui-même hérite d’un autre thème, il devra également être installé.

Pour installer un thème, une fois que vous connaissez son URL, vous pouvez exécuter la commande suivante dans le répertoire racine de Cyca:

cd resources/themes/
git submodules add https://github.com/RichardDern/cyca_theme_dark

Compiler votre thème

Vous pouvez utiliser le processus de compilation des assets de Laravel pour simplifier votre développement.

Par exemple, vous pouvez utiliser la commande suivante:

npm run watch

Qui compilera vos ressources dès que vous les enregistrez.

Une fois que vous êtes satisfait de votre travail, vous voudrez compiler vos ressources pour la production, ce qui se fait simplement avec la commande

npm run prod

Cette commande va créer un répertoire dist/ à la racine de votre thème. Il est important que ce répertoire soit présent dans le dépôt de votre thème, parce que c’est celui-ci qui sera utilisé par Cyca.

Vous pouvez consulter les dépôt des thèmes officiels de Cyca afin d’observer leur structure et vous assurer que le votre soit similaire:

Publier votre thème:

Pour rendre votre thème disponible aux utilisateurs de Cyca, vous devez au moins disposer d’un dépôt git (GitHub ou auto-hébergé, peu importe du moment que votre dépôt respecte la même structure que les thèmes officiels). Vous pouvez ensuite proposer une pull-request dans le dépôt des thèmes de Cyca.

Vous devrez ajouter votre thème sous la forme suivante:

{
    "community": {
        "your-theme-name": "<URL de votre dépôt>"
    }
}

Votre thème sera examiné, et dans le meilleur des cas, ajouté à la base de données.