Prochaines modifications des en-têtes - préparation des thèmes et plugins

We’ve recently been working on updating Discourse’s header from the legacy ‘widget’ rendering system to modern Glimmer components. This change is now available in Discourse core behind the glimmer header mode site setting.

:timer_clock: Approximate Timeline

(very rough estimates - subject to change in either direction)

Q1 2024:

  • :white_check_mark: core implementation finished & enabled on Meta

  • :white_check_mark: upgrade advice published; console deprecation messages enabled

  • :white_check_mark: work begins to update all official and third-party plugins/themes

Q2 2024:

  • :white_check_mark: start enabling new header implementation by default

  • :white_check_mark: official and third-party themes/plugins are ready for the upgrade

  • :white_check_mark: Deprecation messages start triggering an admin warning banner for any remaining issues

Q3 2024:

  • :white_check_mark: Announcement topic posted for wider visibility: Preparing your community for behind-the-scenes header changes

  • :white_check_mark: w/c 5th August 2024 (v3.4.0.beta1): new header enabled for all sites by default. It will still be possible for admins to switch back to the old header by toggling the ‘glimmer header mode’ site setting.

  • :white_check_mark: w/c 2nd September 2024: final removal of feature flag and legacy code

:eyes: What Does it Mean for Me?

If your plugin or theme uses any ‘widget’ APIs to customize the header, those will need to be updated for compatibility with the new header.

:person_tipping_hand: How Do I Try the New Header?

In the latest version of Discourse, the new header is automatically enabled when all your themes/plugins are compatible.

If your themes/plugins are not compatible, then the legacy header will still be used, and a warning will be printed to the console alongside the existing deprecation messages. A warning banner will also be shown to admins in the UI.

In the unlikely event that this automatic system does not work as expected, you can temporarily override this ‘automatic feature flag’ via the glimmer header mode site setting. If you do that, please let us know the reason in this topic.

:technologist: Do I Need to Update My Plugin/Theme?

To determine whether your customization needs to be updated, check if it uses decorateWidget, changeWidgetSetting, reopenWidget or attachWidgetAction on any of these widgets:

  • header
  • site-header
  • header-contents
  • header-buttons
  • user-status-bubble
  • sidebar-toggle
  • header-icons
  • header-topic-info
  • header-notifications
  • home-logo
  • user-dropdown

or uses one of these plugin API methods:

  • addToHeaderIcons
  • addHeaderPanel

All of these things will now cause deprecation messages to be printed to the console. Deprecation IDs are:

  • discourse.add-header-panel
  • discourse.header-widget-overrides

:warning: If you use more than one theme in your instance, be sure to check all of them.

Admin notice

As of June 20, 2024, we’ve enabled the admin notice for the deprecations above.

If your instance was deployed after this date and your instance’s current plugins, theme, or theme components triggers one of the deprecation warnings, the following message will be displayed only for the admins*:

This message is just to alert the admins that they need to take action soon to modernize the affected customizations: the old customizations will still work until we remove the legacy codebase.

:twisted_rightwards_arrows: What Are the Replacements?

Each theme/plugin is different, but here is some guidance for the most common use cases:

addToHeaderIcons

:information_source: For custom header icons, we recommend removing your code and installing the official Custom Header Links (Icons) Theme Component. If that doesn’t meet your requirements, see below for information for details on the required code changes:

The addToHeaderIcons plugin API has been deprecated in favor of the new headerIcons API. It exists to allow adding, removing, or re-ordering of icons in the header. It requires a Component to be passed.

The component can be passed as so:

Before After
api.addToHeaderIcons(“widget-foo”) api.headerIcons.add(“foo”, FooIcon)
api.decorateWidget(“header-icons:before”, () => return helper.h(“div”, “widget-foo”)) api.headerIcons.add(“foo”, FooIcon, { before: “search” })
api.decorateWidget(“header-icons:after”, () => return helper.h(“div”, “widget-foo”)) api.headerIcons.add(“foo”, FooComponent, { after: “search” })

This example uses Ember’s Template Tag Format (gjs) to define a component inline and pass it to the headerButtons.add API:

// .../discourse/api-initializers/add-my-button.gjs

import DButton from "discourse/components/d-button";
import { apiInitializer } from "discourse/lib/api";

export default apiInitializer("1.0", (api) => {
  api.headerIcons.add("some-unique-name", <template>
    <li><DButton class="icon btn-flat" @href="/u" @icon="address-book" /></li>
  </template>);
});

Or for a dropdown, you could use <DMenu instead of <DButton:

import DButton from "discourse/components/d-button";
import { apiInitializer } from "discourse/lib/api";
import DMenu from "float-kit/components/d-menu";

export default apiInitializer("1.0", (api) => {
  api.headerIcons.add("some-unique-name", <template>
    <li>
      <DMenu class="icon btn-flat" @icon="address-book">
        <DButton @translatedLabel="User 1" @href="/u/user1" />
        <DButton @translatedLabel="User 2" @href="/u/user2" />
        <DButton @translatedLabel="User 3" @href="/u/user3" />
      </DMenu>
    </li>
  </template>);
});

Example upgrade commits:

decorateWidget("header-buttons:*")

:information_source: For custom header links, we recommend removing your code and installing the official Custom Header Links Theme Component. If that doesn’t meet your requirements, see below for information for details on the required code changes:

The header-buttons widget has been deprecated and we have introduced a headerButtons plugin API. It exists to allow adding, removing, or re-ordering of buttons in the header. It requires a Component to be passed.

Before After
api.decorateWidget(“header-buttons:before”) api.headerButtons(“button-name”, ButtonComponent, { before: “auth” })
api.decorateWidget(“header-buttons:after”) api.headerButtons(“button-name”, ButtonComponent, { after: “auth” })

changeWidgetSetting(...) for the header widgets

:information_source: The most common uses of changeWidgetSetting can be achieved using these theme components:

If these don’t fit your use-case, read on…

Some customizations on the header widgets were using the changeWidgetSetting API.

Although, there is no direct replacement for customizations like the one above, due to how the Glimmer components fields work, we introduced a new plugin API on Discourse 3.3.0.beta3 to handle some of these cases.

registerValueTransformer can be used to override values that were tagged in the source code as overridable, this is a similar approach to how plugin outlets work.

We already added two transformers for the use cases we found to be common in our source code base:

  • home-logo-href: can be used to override the URL in the home logo anchor. See the section home-logo below for examples.

  • header-notifications-avatar-size: can be used to change the size of the image fetched to the user avatar in the header. Example:

The code below:

api.changeWidgetSetting(
  "header-notifications",
  "avatarSize",
  settings.header_avatars_size
);

Would be converted to:

api.registerValueTransformer(
  "header-notifications-avatar-size",
  () => settings.header_avatars_size
);

These transformers need to be added to the Discourse source code. If you need a different one, please let us know posting your use case below.

More details about the new value transformer APIs can be found here.

home-logo

We have introduced a home-logo plugin outlet in replacement of home-logo:before or home-logo:after widget decorations. You can utilize the automatic __before and __after naming in your connector file to specify where your custom content should be placed.

More details on before/after connector file naming can be found here.

Before After
api.decorateWidget(“home-logo:before”) Move content to /connectors/home-logo__before
api.decorateWidget(“header-buttons:after”) Move content to /connectors/home-logo__after)

Altering the home-logo anchor URL:

A very common need is altering the URL the home-logo links to. We introduced the home-logo-href value transformer to address this. Examples:

  • to change the link to a static URL

    api.registerValueTransformer("home-logo-href", () => "https://example.com");
    
  • to return a dynamic URL based on the current user

    api.registerValueTransformer("home-logo-href", () => {
      const currentUser = api.getCurrentUser();
      return `https://example.com/${currentUser.username}`;
    });
    
  • to return a URL based on a theme-component setting

    api.registerValueTransformer("home-logo-href", () => {
      return settings.example_logo_url_setting;
    });
    

:sos: What about other customizations?

If your customization cannot be achieved using CSS, PluginOutlets, or the new APIs we’ve introduced, please let us know by creating a new Dev topic to discuss.

:sparkles: How do I update a theme/plugin to support both old and new header?

All the new APIs and plugin outlets listed in this document are supported on both the new and the old header. So you only need to make one update to your theme/plugin now, and users will be ready for the switch.

31 « J'aime »

Mais comment définir un FooIcon ?

Dans un plugin, j’ai essayé de créer /assets/javascripts/discourse/components/server-link.js (comme pour d’autres composants que j’utilise dans un fichier hbs)

import Component from "@ember/component";
import discourseComputed from "discourse-common/utils/decorators";

export default Component.extend({
// quelque chose doit-il aller ici ?
});

Et un assets/javascripts/discourse/templates/components/server-link.hbs avec
ceci est un lien (je pense que je peux en faire un lien si j’arrive à faire fonctionner ce “Hello, world”)

L’exemple ci-dessus a const IconWithDropdown = ... mais où cela irait-il ? J’ai essayé de le mettre dans un initialiseur (où se trouvait api.decorateWidget) mais cela ne me semble pas être du javascript valide ni pour ember, d’après ce que je peux dire.

Avant, j’avais un tableau headerlinks et je faisais

        headerLinks.push(
          h(
            `li.headerLink.no-servers`,
            h("a", anchorAttributes, I18n.t("pfaffmanager.no_servers_title"))
          )
        );

pour ajouter les liens que je voulais. Je pense que si j’arrive à faire fonctionner

      api.headerIcons.add("foo", ServerLink, { before: "search" });

alors je pourrai simplement le mettre dans la boucle qui a construit ce tableau.

1 « J'aime »

OMG. Les composants Glimmer vont dans assets/javascripts/discourse/component et les composants Ember vont dans assets/javascripts/discourse/components ?!?!

J’ai maintenant un server-link.gjs

import Component from "@ember/component";
export default class ServerLink extends Component {
  // Argument requis pour l'URL
  url = null;
  // Argument optionnel pour le texte du lien
  text = 'asdf';
  click() {
    console.log('ServerLink clicked!',this);

  }
  // Template pour le composant
  <template>
    {{log "my template" this}}
    LIEN !
    <a href={{this.url}}>{{this.text}}</a>
  </template>
}

Et dans mon initialiseur ceci :

      api.headerIcons.add("foo", ServerLink, { param: "url, yo", before: "search" });

Maintenant, j’ai quelque chose dans l’en-tête.

Mais comment passer des choses à ServerLink ? Je dois l’appeler plusieurs fois avec différentes URL et différents textes à cliquer. Je n’arrive pas à voir les choses dans ce {} dans le composant.

Et vous ne voulez pas vraiment mettre de javascript avant le <template>, car mon console.log("") ne sera pas analysé !

J’ai aussi essayé de faire :

      const x = new ServerLink({
        url: "mylink",
        text: "my-text",
        name: 'Bob',
        message: 'Generated from JavaScript',
      });

et ensuite passer x au lieu de ServerLink, mais toujours pas de succès.

2 « J'aime »

Voulez-vous dire que vous voulez plusieurs boutons dans les en-têtes avec différentes icônes/textes/URL ou le même bouton, mais selon le contexte, le texte/URL peut changer ?

Oui, vous êtes dans une classe — vous y déclareriez des variables, des fonctions ou des modèles !

Oui. Les liens changent pour différents utilisateurs. L’ancien code passait par un tableau de servers et les ajoutait à ce tableau :

            headerLinks.push(
              h(
                `li.headerLink${deviceClass}${newClass}`,
                h("a", anchorAttributes, linkText)
              )
            );

Et faisait ensuite ceci :

      // api.decorateWidget("header-buttons:before", (helper) => {
      //   return helper.h("ul.pfaffmanager-header-links", headerLinks);
      // });

J’avais donc jusqu’à 3 liens qui étaient ajoutés à l’en-tête, chacun pointant vers une URL de serveur distincte.

Aha. Je comprends maintenant.

Non, ne vous inquiétez pas - la convention est toujours /components/ :sweat_smile:

(Techniquement, vous pouvez définir et passer des composants GJS comme vous le souhaitez, donc vous pouvez choisir le nom de répertoire que vous voulez. Mais nous nous en tenons à /components/.)

Oui, c’est une question légitime - je vais travailler à la rédaction de quelques documentations « à partir de zéro » sur la façon d’ajouter des icônes à l’en-tête afin que nous ayons un meilleur point de référence.

En attendant, vous pourriez aimer jeter un œil à la mise à jour de discourse-icon-header-links pour vous inspirer. Ce qui est cool avec GJS, c’est que vous pouvez définir des composants n’importe où, et ils ont accès aux variables de la portée locale.

Donc, si vous renommez votre initialiseur en .gjs, vous pouvez faire des choses comme

servers.forEach((server) => {
  api.headerIcons.add(`server-${server.id}`, <li><DButton @translatedLabel={{server.name}} @icon="server" /></li>);
});

Ou vous pouvez définir un composant plus tôt dans le même fichier, et l’utiliser comme

class ServerButton extends Component {
  get icon(){
    // logique pour décider de l'icône
  }
  <li><DButton @translatedLabel={{@server.name}} @icon={{this.icon}} /></li>
}

...

servers.forEach((server) => {
  api.headerIcons.add(`server-${server.id}`, <ServerButton @server={{server}} />);
});

Ou vous pourriez déplacer l’itération à l’intérieur du modèle (utile si la liste des serveurs est un TrackedArray qui peut changer à l’exécution !)

api.headerIcons.add("server-buttons",
  {{#each servers as |server|}}
    <ServerButton @server={{server}} />
  {{/each}}
);
7 « J'aime »

Pendant que nous travaillons sur des exemples de code plus détaillés

Oh. Hourra. Je pensais l’avoir essayé dans components et ça n’avait pas marché.

Merci ! Je pense que je peux faire fonctionner l’une de ces choses. Le lien vers les liens d’en-tête est d’une grande aide. Je suis à peu près sûr que lorsque j’ai écrit mon code, j’ai eu assez de bon sens pour regarder ce composant très précis pour le comprendre alors.

4 « J'aime »

Un rayon d’espoir !

Salut @david et @Arkshine ! J’ai réussi !

Quuuuuuuuuuuuuuuuuuuuuoi ? Il suffit de le renommer ? C’est dingue. Mais effectivement. Je l’ai fait, et maintenant

          servers.filter(Boolean).map((server) => {
            const linkHref = `/pfaffmanager/servers/${server.id}`;
            const linkTitle = `click to configure server ${server.id}`;
            let host = String(server.hostname);
            const linkText = host.replace(
              /www.|community.|forums?.|talk.|discourse./,
              ""
            );
            const serverLink = <template>
              <li class="headerLink">
                <a class="btn-flat" href={{linkHref}} title={{linkTitle}}>
                  {{host}}
                </a>
              </li>
            </template>;
            const beforeIcon = ["chat", "search", "hamburger", "user-menu"];
            api.headerIcons.add(host, serverLink, { before: beforeIcon });
          });

Et ça fait ce que ça faisait avant, ce qui est ce que je voulais !

Ouais, maintenant ça a l’air très cool, et encore plus ce que je veux, mais ces choses ne changent pas beaucoup, donc je vais en rester là pour aujourd’hui. S’ils changent un nom d’hôte, ils devront recharger la page pour que le lien change.

Je suppose que vous supprimerez mon surplus ici lorsque vous mettrez à jour ce qui précède (ou je n’aurais peut-être pas été si bavard dans un sujet de documentation…).

5 « J'aime »

J’ai mis à jour l’OP avec des exemples gjs complets et inclus un lien vers la documentation Ember en amont. Qu’en penses-tu @pfaffman ? Y a-t-il autre chose qui mériterait d’être ajouté ?

1 « J'aime »

C’est mieux car il y a un exemple fonctionnel. Mais si je comprends bien, il existe des composants Ember et des composants Glimmer ? Et si c’est le cas, vous devriez dire qu’un composant Glimmer est requis, je pense ?

Et peut-être lier la documentation Glimmer sur leur fonctionnement ?

Il semble que vous puissiez avoir des composants en ligne comme dans votre exemple, et un autre type où vous l’assignez à quelque chose comme une variable dans le même fichier ou vous le mettez dans un autre fichier que vous placez dans le répertoire des composants, puis que vous incluez ? Je pense que tout cela est peut-être trop pour ce sujet, mais j’aimerais un sujet dédié à cela.

2 « J'aime »

Ils sont totalement interchangeables - vous pouvez utiliser un composant Ember classique ou un composant Glimmer. Et pour l’un ou l’autre, vous pouvez choisir de les écrire en utilisant l’ancien format .js/.hbs ou le nouveau format .gjs.

Je vais voir si je peux ajouter des liens vers la documentation Ember :+1:

4 « J'aime »

:mega: Aujourd’hui, nous avons fusionné cette modification, qui activera automatiquement la nouvelle implémentation de l’en-tête pour les sites dotés de thèmes/plugins compatibles.

Si vos thèmes/plugins ne sont pas compatibles, l’en-tête hérité sera toujours utilisé et un avertissement sera imprimé dans la console, en plus des messages de dépréciation existants. Dans un avenir proche, cet avertissement de console sera remplacé par une bannière d’avertissement dans l’interface utilisateur.

Dans le cas peu probable où cette modification automatique causerait des problèmes, vous pouvez remplacer temporairement ce « drapeau de fonctionnalité automatique » via le paramètre de site glimmer header mode. Si vous le faites, veuillez nous en faire part dans ce sujet.

3 « J'aime »

Je ne cherchais pas à apporter de changements, mais les avis de dépréciation me disent le contraire,

Il y a donc un choix et peut-être un moyen facile de simplement maintenir le statu quo ?

ou

Qu’est-ce que je manquerais en choisissant de maintenir un ancien header, je ne comprends pas ce que le nouveau signifie, je vois des paramètres de groupe, la personnalisation pour différents groupes est intrigante, mais qu’est-ce qui peut être personnalisé ?

C’est ce que j’ai trouvé aujourd’hui,

Je ne suis pas un gourou ou un expert de ces changements, ils prennent du temps et je ne les fais pas assez souvent pour vraiment vouloir apprendre les techniques que les utilisateurs ici semblent facilement comprendre/connaître.

Je regrette un peu d’avoir à les faire en premier lieu, mais avant de crier comme un vieil homme à qui il manque du pudding, j’aimerais savoir, quoi, pourquoi et où cela va-t-il ?

Je fais ça pour gagner ma vie et je trouve toujours que ce truc JavaScript est loin d’être facile.

Je suis un vieil homme et je ressens votre douleur.

C’est juste le progrès, j’ai peur. Cette mise à niveau d’ember a cassé un tas de choses et ce n’est pas fini.

Vous l’avez “demandé” en faisant cette personnalisation. Je parie que dans les cinq dernières années, vous avez eu un nouveau téléphone ou un nouvel ordinateur portable.

Si j’étais vous (et que vous étiez comme moi, sans le poste à temps plein sur Discourse), je posterais dans Marketplace. Si j’étais moi, je ne répondrais probablement pas pour moins de 300 , mais il y a une chance raisonnable que quelqu'un d'autre le fasse pour 100 ou 200 $. Je suppose que cela ne cassera plus avant 5 ans ou plus.

Je pense que le sélecteur de thème hamburger dont vous pouvez vous débarrasser et utiliser la barre latérale.

3 « J'aime »

Belle réponse honnête, j’apprécie mais il n’y a pas grand-chose à exploiter, peut-être qu’il y aura plus à venir (j’espère)

Je ne savais même pas que nous avions affaire à du Java ici :man_shrugging:

Je ne veux pas que quelqu’un te pique ta crème non plus :face_with_hand_over_mouth:

Bien sûr, mais quel est le but recherché, ce logiciel touche à tant de choses que je me demande qui voit quoi à la fin ?

Est-ce simplement nécessaire pour la mise à niveau d’ember ?

Je ne sais pas pourquoi ember a été fait non plus, mais si ça marche, pourquoi le réparer, je suis sûr qu’il y a une longue explication profonde qui mène à l’avenir des choses, mais n’y a-t-il pas une vraie vision à partager ?

Je visite d’autres forums qui utilisent des logiciels très anciens, personnellement, je trouve discourse bien meilleur que tous les autres, mais ils ne semblent pas en souffrir en comparaison, ils ont les mêmes problèmes de croissance, la plupart sont des personnalités contre le logiciel à mon avis, trop de vieux routiers qui ont perdu leur crème, je me demande, y a-t-il un avenir IOT qui rendra littéralement tous ces forums obsolètes où ils ne fonctionneront pas du tout et discourse est conscient et se prépare ?

Il y a plus de cette honnêteté que vous fournissez :grin: c’est vrai, et j’étais plus désireux d’apprendre, plus ambitieux, je sentais le potentiel plus, j’ai été battu, renversé et laissé pour mort depuis lors

ok, tu es partant, je prends ce pari et comme tu as déjà perdu, tu m’aides avec ça, nous serons amis.

Alors tu aurais probablement abandonné il y a longtemps, le fait d’être battu, renversé et laissé pour mort était une description un peu euphémique, c’est juste moi qui reste au volant, je suppose que tout le monde est dans un panneau quelque part à essayer de réparer le hyper-drive, je ne sais pas car je ne communique pas souvent avec les autres, nous n’avons aucun financement, nous (FULL30) avons été dé-plateformés des médias sociaux et de discourse également, je me demande combien d’autres clients payants discourse a volontairement coupés ou combien d’autres ont été jugés si offensants dans leurs croyances que discourse a publiquement dépensé de l’argent contre eux ?

Pourtant, tout en disant la vérité, je ne me vexe pas, vis et laisse vivre, je sais qu’un avenir arrive, ce que je ne sais pas, c’est pourquoi je suis toujours là et que j’essaie toujours, mais je le fais, alors je continuerai d’essayer, comme les AA, juste pour aujourd’hui :hugs:

Mais c’était la grande mode quand je l’ai utilisé :expressionless:

La barre latérale (ici) peut être fermée avec un menu hamburger, il n’y a pas beaucoup de différence de fonction, elle ouvre et ferme une fenêtre de navigation, mais la mienne ne peut pas être facilement sauvegardée ?

Oui, j’aimerais et je préférerais payer quelqu’un pour nettoyer mon code personnalisé et rendre les choses agréables, et je paierais volontiers, j’aime employer les autres, partager la richesse, quand je serai grand, je veux être un philanthrope, mais aujourd’hui j’ai besoin d’un philanthrope :innocent: et encore une fois, j’apprécie toute aide que d’autres peuvent offrir.

L’autre façon de procéder est de demander de l’aide à votre communauté, d’arrêter la personnalisation en cours, de créer un nouveau sujet partageant votre code et de demander de l’aide. J’ai reçu beaucoup d’aide à ce sujet récemment.

2 « J'aime »

Malheureusement non. La possibilité de conserver « l’ancien en-tête » n’est qu’une mesure temporaire pendant la période de transition. Bientôt, le nouvel en-tête sera la seule option.

Oui ! Nous sommes toujours heureux d’aider avec les questions dans Dev. De plus, partager le code et les solutions publiquement crée une ressource utile pour les autres.

4 « J'aime »

ouf, ma communauté est plus sensible à d’autres problèmes

Je pourrais certainement partager ici, mais alors cela va dans l’autre sens, quel codeur s’intéresse à nous aider ?

L’ironie, le codage pourrait bien être les armes à feu du futur, pourrait bien causer beaucoup plus de morts et de destructions, je digresse

Très bien, qu’est-ce que cela signifie exactement pour moi, créer un groupe d’utilisateurs, public et non connecté peut-être ?

Ces paramètres de groupe, je perçois qu’ils sont basés sur des niveaux de confiance par rapport à de vrais groupes différents, comme un groupe de chasse et un groupe de pêche ?

Je dois d’abord comprendre l’objectif et où mes efforts en tant que loup solitaire auront le plus d’impact, en gagnant du temps et en conservant le sentiment personnalisé de mon forum.


Je ne souhaite pas dérailler le fil de discussion de qui que ce soit, si cela est jugé nécessaire d’en faire un fil de discussion distinct, je suis d’accord

mais comment y aura-t-il jamais une véritable relation cohésive lorsque les gens ressentent le besoin de supprimer quelque chose qui les offense ?

Il faut de la patience pour comprendre les autres, le lien supprimé, il montrait un logo manquant après mais pas pendant la publication, un autre problème de header à discuter ?

C’était un post sur mon forum écrit par un homme que je crois avoir bien plus de 80 ans, je pourrais lui demander avec certitude mais il refuse de me parler, dois-je le réprimander, le bannir ou le fuir ?

Non, pourquoi, parce qu’il y a une meilleure façon, mais cela signifie supporter les autres et leur façon de penser, je trouve de bonnes personnes dans de mauvais quartiers, de bonnes personnes qui ont l’air mauvaises, et juste le contraire dans les deux cas.

Exactement, je viens de trouver les erreurs, je souhaite les corriger mais je ne comprends pas la cause profonde, à part que l’avenir avance, nous avons besoin d’un nouveau header, d’accord, quel est le bon cap à viser, un simple réglage, une correction de cap complète ?

Discutons-nous seulement du besoin de travailler sur ces trois domaines ?

J’ai une utilisation mixte des composants, j’ai commencé sans puis j’ai appris qu’ils pouvaient être bénéfiques, je ne suis jamais allé jusqu’au bout des composants et j’ai un mélange.

Voici mon thème pour ce qu’il vaut sans les composants
discourse-full30-ii.zip (10,1 Ko)
Je peux aussi les poster, certains, les modales, ne fonctionnent déjà plus récemment