<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Intégration web Archives | DOTPROGS</title>
	<atom:link href="https://www.dotprogs.com/rubrique/dotweb/integration-web/feed/" rel="self" type="application/rss+xml" />
	<link></link>
	<description>DOTPROGS - Conception de sites web</description>
	<lastBuildDate>Wed, 08 Jun 2022 08:57:30 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://www.dotprogs.com/wp-content/uploaded-files/cropped-dotprogs-favicon-32x32.png</url>
	<title>Intégration web Archives | DOTPROGS</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Exemple d&#8217;utilisation d&#8217;une fonction « macro » Twig récursive</title>
		<link>https://www.dotprogs.com/exemple-utilisation-fonction-macro-twig-recursive/</link>
		
		<dc:creator><![CDATA[DOTPROGS]]></dc:creator>
		<pubDate>Tue, 04 Jun 2019 18:50:43 +0000</pubDate>
				<category><![CDATA[Développement web]]></category>
		<category><![CDATA[Dotweb]]></category>
		<category><![CDATA[Intégration web]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[function macro]]></category>
		<category><![CDATA[import]]></category>
		<category><![CDATA[récursivité]]></category>
		<category><![CDATA[Symfony]]></category>
		<category><![CDATA[template]]></category>
		<category><![CDATA[Twig]]></category>
		<guid isPermaLink="false">https://www.dotprogs.com/?p=965</guid>

					<description><![CDATA[<p><img width="1024" height="576" src="https://www.dotprogs.com/wp-content/uploaded-files/bout-de-code0119.png" class="attachment-md_post_thumb_large size-md_post_thumb_large wp-post-image" alt="Bout de code - Fonction macro Twig récursive" decoding="async" fetchpriority="high" /></p>
<p>Partons du principe que l'on souhaite lister les erreurs de validation lors de l'utilisation d'un formulaire avec Symfony 4.<br />
Ça ne sert pas à grand chose mais cet exemple permet de récupérer "l'équivalent" simplifié des données qui remontent dans le profiler de Symfony pour les violations de contraintes sur les champs de ce formulaire.</p>
<p>Pour faire du "debug" rapide, j'utilise simplement un <strong>dump()</strong> des erreurs directement côté Twig pour m'assurer que les contraintes s'appliquent bien en testant visuellement le formulaire.</p>
<p>J'ai donc déclaré une fonction <strong>"macro"</strong> utilisée de façon récursive et exécutée pour boucler sur les objets <strong>FormErrorsIterator</strong> de chaque champ enfant (et enfant(s) d'enfant ...).</p>
<p><strong><u>Note:</u> "_self" désigne le template courant et permet une utilisation immédiate.</strong></p>
<p>Pour Symfony, tout type de champ de formulaire est également un <a href="https://symfony.com/doc/current/forms.html" title="Formulaire Symfony" rel="noopener" target="_blank">formulaire</a>, il existe donc la notion de formulaire parent et enfant.</p>
<p>La récursivité pour cet exemple provient simplement du fait que j'ai décidé d'afficher l'ensemble des violations de contraintes présentes au sein du <strong>root form</strong> ici appelé "<strong>myRootForm</strong>" (variable déclarée pour le formulaire global) :</p>
<pre class="EnlighterJSRAW" data-enlighter-language="php">{% macro form_errors(forms) %}
  {% for form in forms %}
    {% for formError in form.vars.errors %}
      {{ dump(formError.cause.propertyPath, formError.cause.constraint, formError.cause.message) }}
    {% endfor %}
    {% if form.children is not empty %}
      {# Use macro recursively #}
      {{ _self.form_errors(form.children) }}
    {% endif %}
  {% endfor %}
{% endmacro %}

{# Use macro immediately after declaration in the same template #}
{{ _self.form_errors(myRootForm.children) }}</pre>
<p>Ce "dump" affiche le propertyPath (le champ concerné), le type de contrainte objet, et le message d'erreur personnalisé ou proposé par défaut.<br />
Les contraintes personnalisées (<a href="https://symfony.com/doc/current/validation/custom_constraint.html" title="Custom constraints Symfony" rel="noopener" target="_blank">custom constraints</a>) proposées par Symfony, éventuellement mises en place sur un projet, remontent également dans les boucles.</p>
<p>Pour utiliser votre macro où vous le souhaitez, il suffit de la déclarer dans un template dédié aux déclarations des fonctions macros et ensuite de faire un import de ce template dans un autre template pour pouvoir l'utiliser :</p>
<pre class="EnlighterJSRAW" data-enlighter-language="php">{# Import macro in another template #}
{% from 'macros.html.twig' import form_errors %}

{# Use macro #}
{{ form_errors(myRootForm.children) }}

{# ------------------------------------------- #}

{# Or import macro like this #} 
{% import "macros.html.twig" as macros %}

{# Use macro in the same way #}
{{ macros.form_errors(myRootForm.children) }}</pre>
<p>Vous pouvez consulter la <a href="https://twig.symfony.com/doc/3.x/tags/macro.html" title="Documentation macro Twig" rel="noopener" target="_blank">documentation</a> concernant les macros Twig.</p>
<p>Voilà, c'est déjà fini !</p>
<p>Cet article <a href="https://www.dotprogs.com/exemple-utilisation-fonction-macro-twig-recursive/">Exemple d&rsquo;utilisation d&rsquo;une fonction « macro » Twig récursive</a> est apparu en premier sur <a href="https://www.dotprogs.com">DOTPROGS</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><img width="1024" height="576" src="https://www.dotprogs.com/wp-content/uploaded-files/bout-de-code0119.png" class="attachment-md_post_thumb_large size-md_post_thumb_large wp-post-image" alt="Bout de code - Fonction macro Twig récursive" decoding="async" /></p>Partons du principe que l'on souhaite lister les erreurs de validation lors de l'utilisation d'un formulaire avec Symfony 4.
Ça ne sert pas à grand chose mais cet exemple permet de récupérer "l'équivalent" simplifié des données qui remontent dans le profiler de Symfony pour les violations de contraintes sur les champs de ce formulaire.

Pour faire du "debug" rapide, j'utilise simplement un <strong>dump()</strong> des erreurs directement côté Twig pour m'assurer que les contraintes s'appliquent bien en testant visuellement le formulaire.

J'ai donc déclaré une fonction <strong>"macro"</strong> utilisée de façon récursive et exécutée pour boucler sur les objets <strong>FormErrorsIterator</strong> de chaque champ enfant (et enfant(s) d'enfant ...).

<strong><u>Note:</u> "_self" désigne le template courant et permet une utilisation immédiate.</strong>

Pour Symfony, tout type de champ de formulaire est également un <a href="https://symfony.com/doc/current/forms.html" title="Formulaire Symfony" rel="noopener" target="_blank">formulaire</a>, il existe donc la notion de formulaire parent et enfant.

La récursivité pour cet exemple provient simplement du fait que j'ai décidé d'afficher l'ensemble des violations de contraintes présentes au sein du <strong>root form</strong> ici appelé "<strong>myRootForm</strong>" (variable déclarée pour le formulaire global) :
<pre class="EnlighterJSRAW" data-enlighter-language="php">{% macro form_errors(forms) %}
  {% for form in forms %}
    {% for formError in form.vars.errors %}
      {{ dump(formError.cause.propertyPath, formError.cause.constraint, formError.cause.message) }}
    {% endfor %}
    {% if form.children is not empty %}
      {# Use macro recursively #}
      {{ _self.form_errors(form.children) }}
    {% endif %}
  {% endfor %}
{% endmacro %}

{# Use macro immediately after declaration in the same template #}
{{ _self.form_errors(myRootForm.children) }}</pre>
Ce "dump" affiche le propertyPath (le champ concerné), le type de contrainte objet, et le message d'erreur personnalisé ou proposé par défaut.
Les contraintes personnalisées (<a href="https://symfony.com/doc/current/validation/custom_constraint.html" title="Custom constraints Symfony" rel="noopener" target="_blank">custom constraints</a>) proposées par Symfony, éventuellement mises en place sur un projet, remontent également dans les boucles.

Pour utiliser votre macro où vous le souhaitez, il suffit de la déclarer dans un template dédié aux déclarations des fonctions macros et ensuite de faire un import de ce template dans un autre template pour pouvoir l'utiliser :
<pre class="EnlighterJSRAW" data-enlighter-language="php">{# Import macro in another template #}
{% from 'macros.html.twig' import form_errors %}

{# Use macro #}
{{ form_errors(myRootForm.children) }}

{# ------------------------------------------- #}

{# Or import macro like this #} 
{% import "macros.html.twig" as macros %}

{# Use macro in the same way #}
{{ macros.form_errors(myRootForm.children) }}</pre>
Vous pouvez consulter la <a href="https://twig.symfony.com/doc/3.x/tags/macro.html" title="Documentation macro Twig" rel="noopener" target="_blank">documentation</a> concernant les macros Twig.

Voilà, c'est déjà fini !<p>Cet article <a href="https://www.dotprogs.com/exemple-utilisation-fonction-macro-twig-recursive/">Exemple d&rsquo;utilisation d&rsquo;une fonction « macro » Twig récursive</a> est apparu en premier sur <a href="https://www.dotprogs.com">DOTPROGS</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Mettre en place une pagination personnalisée avec Twig</title>
		<link>https://www.dotprogs.com/mettre-en-place-une-pagination-personnalisee-avec-twig/</link>
		
		<dc:creator><![CDATA[DOTPROGS]]></dc:creator>
		<pubDate>Sun, 18 Nov 2018 18:02:48 +0000</pubDate>
				<category><![CDATA[Développement web]]></category>
		<category><![CDATA[Dotweb]]></category>
		<category><![CDATA[Intégration web]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Symfony]]></category>
		<category><![CDATA[Templating]]></category>
		<category><![CDATA[Twig]]></category>
		<guid isPermaLink="false">https://www.dotprogs.com/?p=856</guid>

					<description><![CDATA[<p><img width="1024" height="576" src="https://www.dotprogs.com/wp-content/uploaded-files/twig-php-pagination-1.png" class="attachment-md_post_thumb_large size-md_post_thumb_large wp-post-image" alt="Pagination personnalisée avec Twig" decoding="async" /></p>
<p>Dans le cadre de la création d'un blog, je me suis documenté sur quelques approches basiques pour réaliser une pagination en PHP - MySQL afin de lister des articles.<br />
une fois le principe de base compris, j'ai décidé par la suite de "personnaliser" un tantinet cette pagination et l'afficher avec <a href="https://twig.symfony.com" target="_blank" rel="noopener">Twig</a> dans un autre projet utilisant le framework <a href="https://symfony.com" target="_blank" rel="noopener">Symfony</a>.</p>
<p>- J'utilise par exemple un "<strong>entity service layer</strong>" qui est une classe PHP qui sert ici de passerelle entre un Repository Doctrine et une Action/Controller.<br />
Je prends en compte également un affichage descendant ou ascendant "$order" qui est bien sûr facultatif car on peut faire plus simple !<br />
L'objectif est de faire une requête SQL en utilisant les paramètres OFFSET ET LIMIT - ici grâce au Repository avec <strong>findByLimitOffsetWithOrder(...)</strong> dans la méthode <strong>getFilteredList(...)</strong> - pour obtenir les posts (articles) propres à la page courante "currentPage" définie dans <strong>getPaginationParameters(...)</strong>.<br />
Les principales méthodes personnelles nécessaires de cet "entity service layer" que j'utilisent sont présentées ci-dessous :</p>
<pre class="EnlighterJSRAW" data-enlighter-language="php">&lt;?php 
 
declare(strict_types = 1); 
 
namespace App\Service; 
 
// Used classes are not declared to simplify code demo! 
// use ... 
// use ... 
 
class PostServiceLayer 
{ 
    // Traits, properties and constructor with dependency injection and other previous methods 
    // ...
 
    /** 
    * Count all posts without filter. 
    * 
    * @return int 
    * 
    * @throws \Doctrine\ORM\NonUniqueResultException 
    * @throws \UnexpectedValueException 
    */ 
    public function countAll() : int 
    { 
        $result = $this-&gt;repository-&gt;countAll();
        if (\is_null($result)) {
            throw new \UnexpectedValueException('Post total count error: list can not be generated!');
        }
        return $result;
    }
 
    /**
     * Get filtered post list depending on parameters.
     *
     * @param int|null $offset
     * @param int      $limit
     * @param string   $order
     *
     * @return array
     *
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function getFilteredList(
        int $offset = null,
        int $limit = Post::POST_NUMBER_PER_LOADING,
        string $order = Post::POST_LOADING_MODE
    ) : array {
        // Init value to define starting rank
        $init = ('DESC' === $order) ? $this-&gt;countAll() : -1;
        // Offset starts at 0 (i.e. the 15th Post rank has a value of 14)
        $start = $offset;
        $end = $offset + $limit;
        return $this-&gt;repository-&gt;findByLimitOffsetWithOrder($order, $init, $start, $end);
    }
 
    /**
     * Get default parameters to show a post list.
     *
     * (e.g. sort direction, post number for "load more", ...)
     *
     * @return array
     */
    public function getListDefaultParameters() : array
    {
        return [
            'loadingMode'      =&gt; Post::POST_LOADING_MODE,
            'numberPerLoading' =&gt; Post::POST_NUMBER_PER_LOADING,
            'numberPerPage'    =&gt; Post::POST_NUMBER_PER_PAGE
        ];
    }
 
    /**
     * Get pagination parameters to manage page links.
     *
     * This is used on complete post list accessible on "posts" page.
     *
     * @param int $pageIndex
     *
     * @return array|null
     *
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function getPaginationParameters(int $pageIndex) : ?array
    {
        $countAll = $this-&gt;countAll();
        $listDefaultParameters = $this-&gt;getListDefaultParameters();
        $postNumberPerPage = $listDefaultParameters['numberPerPage'];
        $pageCount = $countAll % $postNumberPerPage == 0
            ? $countAll / $postNumberPerPage
            : (int) floor($countAll / $postNumberPerPage) + 1;
        $loadingMode = $listDefaultParameters['loadingMode'];
        if ($pageIndex &lt;= 0 || $pageIndex &gt; $pageCount) {
            return null;
        }
        if ('DESC' === $loadingMode) {
            $offset = $countAll - $pageIndex * $postNumberPerPage &lt; 0
                ? 0 : $countAll - $pageIndex * $postNumberPerPage;
            $limit = $offset === 0
                ? $countAll % $postNumberPerPage : $postNumberPerPage;
 
        } else {
            $offset = $pageIndex === 1
                ? 0 : ($pageIndex - 1) * $postNumberPerPage;
            $limit = $offset + $postNumberPerPage &gt; $countAll - 1
                ? $countAll % $postNumberPerPage : $postNumberPerPage;
        }
        return [
            'currentPage'   =&gt; $pageIndex,
            'currentOffset' =&gt; $offset,
            'currentLimit'  =&gt; $limit,
            'pageCount'     =&gt; $pageCount,
            'loadingMode'   =&gt; $loadingMode,
            'postCount'     =&gt; $countAll
        ];
    }
 
    // Other following methods
    // ...
 
}</pre>
<p>- Voici la partie Twig à intégrer dans un template (les commentaires sont en anglais, c'est une habitude personnelle !) :<br />
Les deux variables importantes à transmettre à la vue depuis une "<strong>Action</strong>" ou un "<strong>Controller</strong>" (non détaillé ici) sont le nombre de page total "<strong>pageCount</strong>" et la page courante "<strong>currentPage</strong>" pour sa mise en exergue et structurer les comportements autour d'elle.<br />
Dans une deuxième temps, il s'agit d'initialiser des variables en fonction des conditions induites par la personnalisation que l'on souhaite obtenir.<br />
Je décide par exemple d'afficher les 2 pages précédentes et suivantes (libre à vous d'en afficher plus!) autour de la page courante : si cela n'est pas possible, j'évalue la possibilité d'afficher 1 page, ou aucune, avant ou après la page courante, ce qui explique les conditions initialisées au préalable.<br />
Je substitue les autres numéros de page par "<strong>...</strong>" pour les matérialiser, à l'exception de la première et la dernière pour conserver les bornes extrêmes, et ainsi gérer un nombre conséquent de page à afficher le cas échéant.</p>
<pre class="EnlighterJSRAW" data-enlighter-language="html">&lt;!-- Generate pagination block if there is at least more than 1 page! --&gt;
    {% if pageCount &gt; 1 %}
        &lt;!-- Pagination --&gt;
        {# Page quantity to show around current page is 2 or a calculated minimum value #}
        {% set defaultPageQuantityAround = 2 %}
        {# Mininum value #}
        {% set minimumPageQuantityAround = min(currentPage - 1, pageCount - currentPage) %}
        {# Condition to show the right page numbers before current page: default or minimum value #}
        {% set conditionBefore = currentPage != 1 and minimumPageQuantityAround &lt;= currentPage - 1 %}
        {# Condition to show the right page numbers after current page: default or minimum value #}
        {% set conditionAfter = currentPage != pageCount and minimumPageQuantityAround &lt;= pageCount - currentPage %}
        {# Define page numbers before, other pages will be replaced by "..." #}
        {% set PageQuantityAroundBefore = conditionBefore ? defaultPageQuantityAround : minimumPageQuantityAround %}
        {# Define page numbers after, other pages will be replaced by "..." #}
        {% set PageQuantityAroundAfter = conditionAfter ? defaultPageQuantityAround : minimumPageQuantityAround %}
        &lt;div class="uk-flex uk-flex-center"&gt;
            &lt;ul class="uk-pagination uk-text-bold uk-text-uppercase"&gt;
                {# Previous link #}
                {% if currentPage - 1 != 0 %}
                &lt;li&gt;&lt;a class="st-color-yellow" href="{{ path('posts', { 'page': currentPage - 1 }) }}" title="Previous"&gt;&lt;span class="uk-margin-small-right" uk-pagination-previous&gt;&lt;/span&gt; Previous&lt;/a&gt;&lt;/li&gt;
                {% endif %}
                {% for i in 1..pageCount %}
                {# Current page to show #}
                {% if currentPage == i %}
                &lt;li class="st-color-red"&gt;{{ i }}&lt;/li&gt;
                {# Show "..." before current page depending on page numbers to show before #}
                {% elseif (i &lt; currentPage and 1 != i) and (i == currentPage - PageQuantityAroundBefore - 1) %}
                &lt;li class="uk-disabled"&gt;...&lt;/li&gt;
                {# Show "..." after current page depending on page numbers to show after #}
                {% elseif (i &gt; currentPage and pageCount != i) and (i == currentPage + PageQuantityAroundAfter + 1) %}
                &lt;li class="uk-disabled"&gt;...&lt;/li&gt;
                {# Hide pages under current page and before "..." excepted page 1 #}
                {% elseif (1 != i) and (i &lt; currentPage - PageQuantityAroundBefore - 1) %}
                &lt;li class="uk-hidden"&gt;&lt;a href="{{ path('posts', { 'page': i }) }}" title="Page {{ i }}"&gt;{{ i }}&lt;/a&gt;&lt;/li&gt;
                {# Hide pages over current page and after "..." excepted page with number "pageCount" (last) #}
                {% elseif (pageCount != i) and (i &gt; currentPage + PageQuantityAroundAfter + 1) %}
                &lt;li class="uk-hidden"&gt;&lt;a href="{{ path('posts', { 'page': i }) }}" title="Page {{ i }}"&gt;{{ i }}&lt;/a&gt;&lt;/li&gt;
                {# Apply particular style for lowest link corresponding to fisrt page 1, and Highest link corresponding to page total count #}
                {% elseif i == 1 or i == pageCount %}
                &lt;li&gt;&lt;a class="st-color-blue" href="{{ path('posts', { 'page': i }) }}" title="Page {{ i }}"&gt;{{ i }}&lt;/a&gt;&lt;/li&gt;
                {# Normal links which are not concerned by conditions above #}
                {% else %}
                &lt;li&gt;&lt;a href="{{ path('posts', { 'page': i }) }}" title="Page {{ i }}"&gt;{{ i }}&lt;/a&gt;&lt;/li&gt;
                {% endif %}
                {% endfor %}
                {# Next link #}
                {% if currentPage + 1 &lt;= pageCount %}
                &lt;li class="uk-margin-auto-left"&gt;&lt;a class="st-color-yellow" href="{{ path('posts', { 'page': currentPage + 1 }) }}" title="Next"&gt;Next &lt;span class="uk-margin-small-left" uk-pagination-next&gt;&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
                {% endif %}
            &lt;/ul&gt;
        &lt;/div&gt;
    {% endif %}</pre>
<p>Et voici le résultat visuel de la pagination :</p>
<p><img src="http://www.dotprogs.com/wp-content/uploaded-files/exemples-pagination-twig.jpg" alt="Exemples de pagination personnalisée Twig" width="500" height="250" class="size-full wp-image-872" /></p>
<p>Les classes CSS utilisées ici sont personnalisées (pour les couleurs) et issues du framework <a href="https://getuikit.com/" target="_blank" rel="noopener">UIkit</a> pour le comportement (lien désactivé, lien caché ...), donc rien de significatif pour ce qui est de l'aspect mise en forme.</p>
<p>Un tel système de pagination peut très facilement être adapté pour de l'<strong>AJAX</strong> et afficher les posts de la page courante (en renvoyant du contenu HTML avec un block Twig par exemple) en asynchrone afin d'être davantage "user friendly" !</p>
<p>Ces extraits de code sont issus d'un projet opérationnel, ceci-dit des "petites coquilles" peuvent s'être glissées entre les lignes.<br />
N'hésitez pas à me contacter pour signaler des erreurs éventuelles ou pour partager des améliorations, car on peut souvent mieux faire ou faire plus pragmatique !</p>
<p>Cet article <a href="https://www.dotprogs.com/mettre-en-place-une-pagination-personnalisee-avec-twig/">Mettre en place une pagination personnalisée avec Twig</a> est apparu en premier sur <a href="https://www.dotprogs.com">DOTPROGS</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><img width="1024" height="576" src="https://www.dotprogs.com/wp-content/uploaded-files/twig-php-pagination-1.png" class="attachment-md_post_thumb_large size-md_post_thumb_large wp-post-image" alt="Pagination personnalisée avec Twig" decoding="async" loading="lazy" /></p>Dans le cadre de la création d'un blog, je me suis documenté sur quelques approches basiques pour réaliser une pagination en PHP - MySQL afin de lister des articles.
une fois le principe de base compris, j'ai décidé par la suite de "personnaliser" un tantinet cette pagination et l'afficher avec <a href="https://twig.symfony.com" target="_blank" rel="noopener">Twig</a> dans un autre projet utilisant le framework <a href="https://symfony.com" target="_blank" rel="noopener">Symfony</a>.

- J'utilise par exemple un "<strong>entity service layer</strong>" qui est une classe PHP qui sert ici de passerelle entre un Repository Doctrine et une Action/Controller.
Je prends en compte également un affichage descendant ou ascendant "$order" qui est bien sûr facultatif car on peut faire plus simple !
L'objectif est de faire une requête SQL en utilisant les paramètres OFFSET ET LIMIT - ici grâce au Repository avec <strong>findByLimitOffsetWithOrder(...)</strong> dans la méthode <strong>getFilteredList(...)</strong> - pour obtenir les posts (articles) propres à la page courante "currentPage" définie dans <strong>getPaginationParameters(...)</strong>.
Les principales méthodes personnelles nécessaires de cet "entity service layer" que j'utilisent sont présentées ci-dessous :
<pre class="EnlighterJSRAW" data-enlighter-language="php">&lt;?php 
 
declare(strict_types = 1); 
 
namespace App\Service; 
 
// Used classes are not declared to simplify code demo! 
// use ... 
// use ... 
 
class PostServiceLayer 
{ 
    // Traits, properties and constructor with dependency injection and other previous methods 
    // ...
 
    /** 
    * Count all posts without filter. 
    * 
    * @return int 
    * 
    * @throws \Doctrine\ORM\NonUniqueResultException 
    * @throws \UnexpectedValueException 
    */ 
    public function countAll() : int 
    { 
        $result = $this-&gt;repository-&gt;countAll();
        if (\is_null($result)) {
            throw new \UnexpectedValueException('Post total count error: list can not be generated!');
        }
        return $result;
    }
 
    /**
     * Get filtered post list depending on parameters.
     *
     * @param int|null $offset
     * @param int      $limit
     * @param string   $order
     *
     * @return array
     *
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function getFilteredList(
        int $offset = null,
        int $limit = Post::POST_NUMBER_PER_LOADING,
        string $order = Post::POST_LOADING_MODE
    ) : array {
        // Init value to define starting rank
        $init = ('DESC' === $order) ? $this-&gt;countAll() : -1;
        // Offset starts at 0 (i.e. the 15th Post rank has a value of 14)
        $start = $offset;
        $end = $offset + $limit;
        return $this-&gt;repository-&gt;findByLimitOffsetWithOrder($order, $init, $start, $end);
    }
 
    /**
     * Get default parameters to show a post list.
     *
     * (e.g. sort direction, post number for "load more", ...)
     *
     * @return array
     */
    public function getListDefaultParameters() : array
    {
        return [
            'loadingMode'      =&gt; Post::POST_LOADING_MODE,
            'numberPerLoading' =&gt; Post::POST_NUMBER_PER_LOADING,
            'numberPerPage'    =&gt; Post::POST_NUMBER_PER_PAGE
        ];
    }
 
    /**
     * Get pagination parameters to manage page links.
     *
     * This is used on complete post list accessible on "posts" page.
     *
     * @param int $pageIndex
     *
     * @return array|null
     *
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function getPaginationParameters(int $pageIndex) : ?array
    {
        $countAll = $this-&gt;countAll();
        $listDefaultParameters = $this-&gt;getListDefaultParameters();
        $postNumberPerPage = $listDefaultParameters['numberPerPage'];
        $pageCount = $countAll % $postNumberPerPage == 0
            ? $countAll / $postNumberPerPage
            : (int) floor($countAll / $postNumberPerPage) + 1;
        $loadingMode = $listDefaultParameters['loadingMode'];
        if ($pageIndex &lt;= 0 || $pageIndex &gt; $pageCount) {
            return null;
        }
        if ('DESC' === $loadingMode) {
            $offset = $countAll - $pageIndex * $postNumberPerPage &lt; 0
                ? 0 : $countAll - $pageIndex * $postNumberPerPage;
            $limit = $offset === 0
                ? $countAll % $postNumberPerPage : $postNumberPerPage;
 
        } else {
            $offset = $pageIndex === 1
                ? 0 : ($pageIndex - 1) * $postNumberPerPage;
            $limit = $offset + $postNumberPerPage &gt; $countAll - 1
                ? $countAll % $postNumberPerPage : $postNumberPerPage;
        }
        return [
            'currentPage'   =&gt; $pageIndex,
            'currentOffset' =&gt; $offset,
            'currentLimit'  =&gt; $limit,
            'pageCount'     =&gt; $pageCount,
            'loadingMode'   =&gt; $loadingMode,
            'postCount'     =&gt; $countAll
        ];
    }
 
    // Other following methods
    // ...
 
}</pre>
- Voici la partie Twig à intégrer dans un template (les commentaires sont en anglais, c'est une habitude personnelle !) :
Les deux variables importantes à transmettre à la vue depuis une "<strong>Action</strong>" ou un "<strong>Controller</strong>" (non détaillé ici) sont le nombre de page total "<strong>pageCount</strong>" et la page courante "<strong>currentPage</strong>" pour sa mise en exergue et structurer les comportements autour d'elle.
Dans une deuxième temps, il s'agit d'initialiser des variables en fonction des conditions induites par la personnalisation que l'on souhaite obtenir.
Je décide par exemple d'afficher les 2 pages précédentes et suivantes (libre à vous d'en afficher plus!) autour de la page courante : si cela n'est pas possible, j'évalue la possibilité d'afficher 1 page, ou aucune, avant ou après la page courante, ce qui explique les conditions initialisées au préalable.
Je substitue les autres numéros de page par "<strong>...</strong>" pour les matérialiser, à l'exception de la première et la dernière pour conserver les bornes extrêmes, et ainsi gérer un nombre conséquent de page à afficher le cas échéant.
<pre class="EnlighterJSRAW" data-enlighter-language="html">&lt;!-- Generate pagination block if there is at least more than 1 page! --&gt;
    {% if pageCount &gt; 1 %}
        &lt;!-- Pagination --&gt;
        {# Page quantity to show around current page is 2 or a calculated minimum value #}
        {% set defaultPageQuantityAround = 2 %}
        {# Mininum value #}
        {% set minimumPageQuantityAround = min(currentPage - 1, pageCount - currentPage) %}
        {# Condition to show the right page numbers before current page: default or minimum value #}
        {% set conditionBefore = currentPage != 1 and minimumPageQuantityAround &lt;= currentPage - 1 %}
        {# Condition to show the right page numbers after current page: default or minimum value #}
        {% set conditionAfter = currentPage != pageCount and minimumPageQuantityAround &lt;= pageCount - currentPage %}
        {# Define page numbers before, other pages will be replaced by "..." #}
        {% set PageQuantityAroundBefore = conditionBefore ? defaultPageQuantityAround : minimumPageQuantityAround %}
        {# Define page numbers after, other pages will be replaced by "..." #}
        {% set PageQuantityAroundAfter = conditionAfter ? defaultPageQuantityAround : minimumPageQuantityAround %}
        &lt;div class="uk-flex uk-flex-center"&gt;
            &lt;ul class="uk-pagination uk-text-bold uk-text-uppercase"&gt;
                {# Previous link #}
                {% if currentPage - 1 != 0 %}
                &lt;li&gt;&lt;a class="st-color-yellow" href="{{ path('posts', { 'page': currentPage - 1 }) }}" title="Previous"&gt;&lt;span class="uk-margin-small-right" uk-pagination-previous&gt;&lt;/span&gt; Previous&lt;/a&gt;&lt;/li&gt;
                {% endif %}
                {% for i in 1..pageCount %}
                {# Current page to show #}
                {% if currentPage == i %}
                &lt;li class="st-color-red"&gt;{{ i }}&lt;/li&gt;
                {# Show "..." before current page depending on page numbers to show before #}
                {% elseif (i &lt; currentPage and 1 != i) and (i == currentPage - PageQuantityAroundBefore - 1) %}
                &lt;li class="uk-disabled"&gt;...&lt;/li&gt;
                {# Show "..." after current page depending on page numbers to show after #}
                {% elseif (i &gt; currentPage and pageCount != i) and (i == currentPage + PageQuantityAroundAfter + 1) %}
                &lt;li class="uk-disabled"&gt;...&lt;/li&gt;
                {# Hide pages under current page and before "..." excepted page 1 #}
                {% elseif (1 != i) and (i &lt; currentPage - PageQuantityAroundBefore - 1) %}
                &lt;li class="uk-hidden"&gt;&lt;a href="{{ path('posts', { 'page': i }) }}" title="Page {{ i }}"&gt;{{ i }}&lt;/a&gt;&lt;/li&gt;
                {# Hide pages over current page and after "..." excepted page with number "pageCount" (last) #}
                {% elseif (pageCount != i) and (i &gt; currentPage + PageQuantityAroundAfter + 1) %}
                &lt;li class="uk-hidden"&gt;&lt;a href="{{ path('posts', { 'page': i }) }}" title="Page {{ i }}"&gt;{{ i }}&lt;/a&gt;&lt;/li&gt;
                {# Apply particular style for lowest link corresponding to fisrt page 1, and Highest link corresponding to page total count #}
                {% elseif i == 1 or i == pageCount %}
                &lt;li&gt;&lt;a class="st-color-blue" href="{{ path('posts', { 'page': i }) }}" title="Page {{ i }}"&gt;{{ i }}&lt;/a&gt;&lt;/li&gt;
                {# Normal links which are not concerned by conditions above #}
                {% else %}
                &lt;li&gt;&lt;a href="{{ path('posts', { 'page': i }) }}" title="Page {{ i }}"&gt;{{ i }}&lt;/a&gt;&lt;/li&gt;
                {% endif %}
                {% endfor %}
                {# Next link #}
                {% if currentPage + 1 &lt;= pageCount %}
                &lt;li class="uk-margin-auto-left"&gt;&lt;a class="st-color-yellow" href="{{ path('posts', { 'page': currentPage + 1 }) }}" title="Next"&gt;Next &lt;span class="uk-margin-small-left" uk-pagination-next&gt;&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
                {% endif %}
            &lt;/ul&gt;
        &lt;/div&gt;
    {% endif %}</pre>
Et voici le résultat visuel de la pagination :

<img src="http://www.dotprogs.com/wp-content/uploaded-files/exemples-pagination-twig.jpg" alt="Exemples de pagination personnalisée Twig" width="500" height="250" class="size-full wp-image-872" />

Les classes CSS utilisées ici sont personnalisées (pour les couleurs) et issues du framework <a href="https://getuikit.com/" target="_blank" rel="noopener">UIkit</a> pour le comportement (lien désactivé, lien caché ...), donc rien de significatif pour ce qui est de l'aspect mise en forme.

Un tel système de pagination peut très facilement être adapté pour de l'<strong>AJAX</strong> et afficher les posts de la page courante (en renvoyant du contenu HTML avec un block Twig par exemple) en asynchrone afin d'être davantage "user friendly" !

Ces extraits de code sont issus d'un projet opérationnel, ceci-dit des "petites coquilles" peuvent s'être glissées entre les lignes.
N'hésitez pas à me contacter pour signaler des erreurs éventuelles ou pour partager des améliorations, car on peut souvent mieux faire ou faire plus pragmatique !<p>Cet article <a href="https://www.dotprogs.com/mettre-en-place-une-pagination-personnalisee-avec-twig/">Mettre en place une pagination personnalisée avec Twig</a> est apparu en premier sur <a href="https://www.dotprogs.com">DOTPROGS</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>SVG et son utilisation pour le web : des ressources variées</title>
		<link>https://www.dotprogs.com/format-svg-utilisation-web/</link>
		
		<dc:creator><![CDATA[DOTPROGS]]></dc:creator>
		<pubDate>Sun, 13 Nov 2016 17:25:02 +0000</pubDate>
				<category><![CDATA[Dotweb]]></category>
		<category><![CDATA[Intégration web]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[image]]></category>
		<category><![CDATA[pattern]]></category>
		<category><![CDATA[responsive design]]></category>
		<category><![CDATA[SVG]]></category>
		<category><![CDATA[vecteur]]></category>
		<category><![CDATA[XML]]></category>
		<guid isPermaLink="false">https://www.dotprogs.com/?p=305</guid>

					<description><![CDATA[<p><img width="1024" height="576" src="https://www.dotprogs.com/wp-content/uploaded-files/scalable-vector-graphics.png" class="attachment-md_post_thumb_large size-md_post_thumb_large wp-post-image" alt="SVG - Scalable Vector Graphics" decoding="async" loading="lazy" /></p>
<h2 class="dp-level2-title">Des possibilités d'intégration intéressantes avec le format "SVG"</h2>
<h3 class="dp-level3-title">Ses atouts majeurs</h3>
<p>"SVG" (pour "Scalable Vector Graphic") est un format d'image pour les vecteurs graphiques et c'est ce qui fait son charme !<br />
Ceci implique que ce format est facilement adaptable en dimensions, sans perte de qualité. Les fichiers en format "SVG" ont une taille réduite et bénéficie d'un bon taux de compression. L'affichage des images "SVG" sur des périphériques à haute définition et densité de pixel (type "retina" ou autre) est du coup bien géré.</p>
<p>Les formes dessinées sont aisément contrôlables, nous pouvons les animer facilement et leur appliquer des styles et de l'interactivité avec CSS et JavaScript.</p>
<p>Les navigateurs modernes permettent désormais de profiter de ce format, mais bien que "SVG" ne soit pas une véritablement une nouveauté, il n'est pas régulièrement utilisé sur un site web.</p>
<p>L'intégration, bien que clairement documentée nécessite un certain apprentissage. Il existe aussi certaines limites et exceptions comme souvent qui nécessitent de les contourner notamment en consultant <a tile="SVG et compatibilité navigateurs" href="http://caniuse.com/#feat=svg" target="_blank" rel="noopener">caniuse</a>, notre référence pour les problématiques d'utilisation.</p>
<p>"SVG" est basé sur le langage "XML", un fichier est donc facilement manipulable dans un simple éditeur de code.<br />
un exemple de format "SVG" qui représente un pictogramme "tools" présent sur le visuel de cet article :</p>
<pre class="EnlighterJSRAW" data-enlighter-language="html">&lt;svg version="1.1" id="Tools" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="300px" height="300px" viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve"&gt;
&lt;path d="M3.135,6.89c0.933-0.725,1.707-0.225,2.74,0.971c0.116,0.135,0.272-0.023,0.361-0.1c0.088-0.078,1.451-1.305,1.518-1.361
 C7.82,6.341,7.9,6.231,7.795,6.108C7.688,5.985,7.301,5.483,7.052,5.157c-1.808-2.365,4.946-3.969,3.909-3.994
 c-0.528-0.014-2.646-0.039-2.963-0.004C6.715,1.294,5.104,2.493,4.293,3.052C3.232,3.778,2.836,4.204,2.771,4.263
 c-0.3,0.262-0.048,0.867-0.592,1.344C1.604,6.11,1.245,5.729,0.912,6.021C0.747,6.167,0.285,6.513,0.153,6.628
 C0.02,6.745-0.004,6.942,0.132,7.099c0,0,1.264,1.396,1.37,1.52C1.607,8.741,1.893,8.847,2.069,8.69
 c0.177-0.156,0.632-0.553,0.708-0.623C2.855,8.001,2.727,7.206,3.135,6.89z M8.843,7.407c-0.12-0.139-0.269-0.143-0.397-0.029
 L7.012,8.63c-0.113,0.1-0.129,0.283-0.027,0.4l8.294,9.439c0.194,0.223,0.53,0.246,0.751,0.053L17,17.709
 c0.222-0.195,0.245-0.533,0.052-0.758L8.843,7.407z M19.902,3.39c-0.074-0.494-0.33-0.391-0.463-0.182
 c-0.133,0.211-0.721,1.102-0.963,1.506c-0.24,0.4-0.832,1.191-1.934,0.41c-1.148-0.811-0.749-1.377-0.549-1.758
 c0.201-0.383,0.818-1.457,0.907-1.59c0.089-0.135-0.015-0.527-0.371-0.363c-0.357,0.164-2.523,1.025-2.823,2.26
 c-0.307,1.256,0.257,2.379-0.85,3.494l-1.343,1.4l1.349,1.566l1.654-1.57c0.394-0.396,1.236-0.781,1.998-0.607
 c1.633,0.369,2.524-0.244,3.061-1.258C20.057,5.792,19.977,3.884,19.902,3.39z M2.739,17.053c-0.208,0.209-0.208,0.549,0,0.758
 l0.951,0.93c0.208,0.209,0.538,0.121,0.746-0.088l4.907-4.824L7.84,12.115L2.739,17.053z"/&gt;
&lt;/svg&gt;</pre>
<p>Des formes géométriques simples peuvent être traitées (rect, circle, elipse et polygon), ou plus complexes (balises polyline et path) comme l'exemple ci-dessus.</p>
<p>Le code avec balise "svg" ci-dessus peut-être placé tel quel dans une page web en HTML5, il existe en réalité différentes manières d'afficher et utiliser un "SVG" pour un site web dont notamment la balise "iframe", "object" et "img" et également l'intégration en background CSS.</p>
<p>L’attribut "viewBox" est important et permet l'affichage des formes représentées. Les attributs "width" et "height" définissent quant à eux le cadre fondamental qui va le contenir et l'afficher dans une page web. Lors de l'intégration, il faut donc apporter une attention particulière à ses valeurs. Voici ce que cela donne concrètement avec une <a title="SVG et attributs viewBox, width, height" href="http://codepen.io/dbj/pen/RWmQNJ" target="_blank" rel="noopener">illustration</a>.</p>
<p>Vous pouvez exporter un fichier SVG à partir d'un éditeur graphique tel que Illustrator, Inkscape ou autres, cependant le code généré nécessitera d'être <a title="SVG et optimisation" href="https://github.com/svg/svgo" target="_blank" rel="noopener">optimisé</a>.</p>
<p>"SVG" permet des effets graphiques comme le détourage, masquage (clipPath et mask), ainsi que des filtres, et surtout il est adaptatif ce qui le rend pratique pour le responsive design.</p>
<p>Le "profil" de "SVG" a été brièvement abordé dans cet article, pour aller plus loin dans le domaine, je vous propose un <a title="SVG et intégration web" href="https://la-cascade.io/tag/svg/" target="_blank" rel="noopener">lien</a> très riche avec de nombreuses ressources et une revue assez large de ses capacités et surtout comme apprendre à l'utiliser.</p>
<p>Cet article <a href="https://www.dotprogs.com/format-svg-utilisation-web/">SVG et son utilisation pour le web : des ressources variées</a> est apparu en premier sur <a href="https://www.dotprogs.com">DOTPROGS</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><img width="1024" height="576" src="https://www.dotprogs.com/wp-content/uploaded-files/scalable-vector-graphics.png" class="attachment-md_post_thumb_large size-md_post_thumb_large wp-post-image" alt="SVG - Scalable Vector Graphics" decoding="async" loading="lazy" /></p><h2 class="dp-level2-title">Des possibilités d'intégration intéressantes avec le format "SVG"</h2>
<h3 class="dp-level3-title">Ses atouts majeurs</h3>
"SVG" (pour "Scalable Vector Graphic") est un format d'image pour les vecteurs graphiques et c'est ce qui fait son charme !
Ceci implique que ce format est facilement adaptable en dimensions, sans perte de qualité. Les fichiers en format "SVG" ont une taille réduite et bénéficie d'un bon taux de compression. L'affichage des images "SVG" sur des périphériques à haute définition et densité de pixel (type "retina" ou autre) est du coup bien géré.

Les formes dessinées sont aisément contrôlables, nous pouvons les animer facilement et leur appliquer des styles et de l'interactivité avec CSS et JavaScript.

Les navigateurs modernes permettent désormais de profiter de ce format, mais bien que "SVG" ne soit pas une véritablement une nouveauté, il n'est pas régulièrement utilisé sur un site web.

L'intégration, bien que clairement documentée nécessite un certain apprentissage. Il existe aussi certaines limites et exceptions comme souvent qui nécessitent de les contourner notamment en consultant <a tile="SVG et compatibilité navigateurs" href="http://caniuse.com/#feat=svg" target="_blank" rel="noopener">caniuse</a>, notre référence pour les problématiques d'utilisation.

"SVG" est basé sur le langage "XML", un fichier est donc facilement manipulable dans un simple éditeur de code.
un exemple de format "SVG" qui représente un pictogramme "tools" présent sur le visuel de cet article :
<pre class="EnlighterJSRAW" data-enlighter-language="html">&lt;svg version="1.1" id="Tools" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="300px" height="300px" viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve"&gt;
&lt;path d="M3.135,6.89c0.933-0.725,1.707-0.225,2.74,0.971c0.116,0.135,0.272-0.023,0.361-0.1c0.088-0.078,1.451-1.305,1.518-1.361
 C7.82,6.341,7.9,6.231,7.795,6.108C7.688,5.985,7.301,5.483,7.052,5.157c-1.808-2.365,4.946-3.969,3.909-3.994
 c-0.528-0.014-2.646-0.039-2.963-0.004C6.715,1.294,5.104,2.493,4.293,3.052C3.232,3.778,2.836,4.204,2.771,4.263
 c-0.3,0.262-0.048,0.867-0.592,1.344C1.604,6.11,1.245,5.729,0.912,6.021C0.747,6.167,0.285,6.513,0.153,6.628
 C0.02,6.745-0.004,6.942,0.132,7.099c0,0,1.264,1.396,1.37,1.52C1.607,8.741,1.893,8.847,2.069,8.69
 c0.177-0.156,0.632-0.553,0.708-0.623C2.855,8.001,2.727,7.206,3.135,6.89z M8.843,7.407c-0.12-0.139-0.269-0.143-0.397-0.029
 L7.012,8.63c-0.113,0.1-0.129,0.283-0.027,0.4l8.294,9.439c0.194,0.223,0.53,0.246,0.751,0.053L17,17.709
 c0.222-0.195,0.245-0.533,0.052-0.758L8.843,7.407z M19.902,3.39c-0.074-0.494-0.33-0.391-0.463-0.182
 c-0.133,0.211-0.721,1.102-0.963,1.506c-0.24,0.4-0.832,1.191-1.934,0.41c-1.148-0.811-0.749-1.377-0.549-1.758
 c0.201-0.383,0.818-1.457,0.907-1.59c0.089-0.135-0.015-0.527-0.371-0.363c-0.357,0.164-2.523,1.025-2.823,2.26
 c-0.307,1.256,0.257,2.379-0.85,3.494l-1.343,1.4l1.349,1.566l1.654-1.57c0.394-0.396,1.236-0.781,1.998-0.607
 c1.633,0.369,2.524-0.244,3.061-1.258C20.057,5.792,19.977,3.884,19.902,3.39z M2.739,17.053c-0.208,0.209-0.208,0.549,0,0.758
 l0.951,0.93c0.208,0.209,0.538,0.121,0.746-0.088l4.907-4.824L7.84,12.115L2.739,17.053z"/&gt;
&lt;/svg&gt;</pre>
Des formes géométriques simples peuvent être traitées (rect, circle, elipse et polygon), ou plus complexes (balises polyline et path) comme l'exemple ci-dessus.

Le code avec balise "svg" ci-dessus peut-être placé tel quel dans une page web en HTML5, il existe en réalité différentes manières d'afficher et utiliser un "SVG" pour un site web dont notamment la balise "iframe", "object" et "img" et également l'intégration en background CSS.

L’attribut "viewBox" est important et permet l'affichage des formes représentées. Les attributs "width" et "height" définissent quant à eux le cadre fondamental qui va le contenir et l'afficher dans une page web. Lors de l'intégration, il faut donc apporter une attention particulière à ses valeurs. Voici ce que cela donne concrètement avec une <a title="SVG et attributs viewBox, width, height" href="http://codepen.io/dbj/pen/RWmQNJ" target="_blank" rel="noopener">illustration</a>.

Vous pouvez exporter un fichier SVG à partir d'un éditeur graphique tel que Illustrator, Inkscape ou autres, cependant le code généré nécessitera d'être <a title="SVG et optimisation" href="https://github.com/svg/svgo" target="_blank" rel="noopener">optimisé</a>.

"SVG" permet des effets graphiques comme le détourage, masquage (clipPath et mask), ainsi que des filtres, et surtout il est adaptatif ce qui le rend pratique pour le responsive design.

Le "profil" de "SVG" a été brièvement abordé dans cet article, pour aller plus loin dans le domaine, je vous propose un <a title="SVG et intégration web" href="https://la-cascade.io/tag/svg/" target="_blank" rel="noopener">lien</a> très riche avec de nombreuses ressources et une revue assez large de ses capacités et surtout comme apprendre à l'utiliser.<p>Cet article <a href="https://www.dotprogs.com/format-svg-utilisation-web/">SVG et son utilisation pour le web : des ressources variées</a> est apparu en premier sur <a href="https://www.dotprogs.com">DOTPROGS</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Typographie proportionnelle aux dimensions du « viewport »</title>
		<link>https://www.dotprogs.com/typographie-proportionnelle-aux-dimensions-du-viewport/</link>
		
		<dc:creator><![CDATA[DOTPROGS]]></dc:creator>
		<pubDate>Tue, 19 Jul 2016 13:43:16 +0000</pubDate>
				<category><![CDATA[Dotweb]]></category>
		<category><![CDATA[Intégration web]]></category>
		<category><![CDATA[typographie]]></category>
		<category><![CDATA[viewport relative unit]]></category>
		<guid isPermaLink="false">https://www.dotprogs.com/?p=434</guid>

					<description><![CDATA[<p><img width="1024" height="576" src="https://www.dotprogs.com/wp-content/uploaded-files/viewport-units.png" class="attachment-md_post_thumb_large size-md_post_thumb_large wp-post-image" alt="Unités relatives au viewport" decoding="async" loading="lazy" /></p>
<h2 class="dp-level2-title">Une taille de police de caractère réactive</h2>
<h3 class="dp-level3-title">Les unités "vw" et "vh"</h3>
<p>Actuellement, la plupart des navigateurs sont capables de gérer une nouvelle unité relative à la taille du viewport : le "vw" pour "viewport-width" et le "vh" pour "viewport-height".<br />
Vous l'aurez compris comme moi, il s'agit de définir une taille par rapport à la largeur et la hauteur du "device" utilisé.</p>
<p>Petit rappel : la notion de "viewport" désigne la surface de la fenêtre du navigateur sur desktop, sur les périphériques nomades type mobile ou tablette, on parle davantage de surface réelle d'affichage (qui n'est généralement pas la surface physique du périphérique) avec les valeurs de "device-width" et "device-height". La complexité est bien expliqué sur le site d'<a title="principe de viewport" href="http://www.alsacreations.com/article/lire/1490-comprendre-le-viewport-dans-le-web-mobile.html">Alsacréations</a>.</p>
<p>Le principe revient à utiliser les pourcentages, donc "100vw" et "100vh" constituent la largeur et hauteur totales disponibles.<br />
Ainsi le principe de correspondance de taille est simplement le suivant :<br />
15vw = 15% de la largeur du "viewport"<br />
50vh = la moitié de la hauteur du "viewport"</p>
<p>Si on considère que l'on souhaite appliquer ces unités à une taille de typographie, on a la possibilité d'afficher un texte réactif est proportionnel au "viewport".</p>
<p>Même si le raccourci est un semblant hasardeux, j'apprécie ce principe qui se rapproche quelque peu de l'utilisation du "rem" (pour "root em") pour son côté adaptable et facilement maintenable où on se réfère à une valeur "étalon" qui sert de socle à l'ensemble des tailles.</p>
<p>Pour illustrer, voici ce qui est défini pour la CSS du menu "overlay" de www.dotprogs.com :</p>
<pre class="EnlighterJSRAW" data-enlighter-language="css">.menu-over-page ul.main-nav li {
    font-size: 6vh; /* viewport height */
}</pre>
<p>En fonction des contraintes d'affichage présentes sur un site web, il sera parfois plus opportun d'utiliser une taille proportionnelle à la hauteur ou à la largeur.</p>
<p>Quelles conclusions j'en tire personnellement ?</p>
<p>Et bien je pense que ce principe est intéressant à utiliser de manière ponctuelle si vous souhaitez améliorer le confort de lecture sur un élément de site en particulier comme l'affichage des items d'un menu en fonction du périphérique de l'utilisateur.<br />
Pour l'instant avec le recul minimal et la mise en pratique restreinte que j'ai de "vw" et "vh", je ne perçois pas encore l'apport éventuel que cela peut avoir sur du "templating" de site.</p>
<p>Il est certain également que l'on donne un rendu assez empirique de la typographie au premier abord, puisque ce qui va nous intéresser avant toute chose, est la lisibilité de la taille des textes relatifs à l'espace disponible pour un "viewport" donné, car il n'y a pas véritablement d'intérêt à savoir quelle est la taille réelle en pixel de notre contenu en fonction de la résolution (définition) du "viewport". La diversité des périphériques s'avère même être un casse-tête lorsqu'on s'intéresse plus précisément aux dimensions du viewport et à leurs spécificités (densité de pixel, aspect ratio, etc ...) listées sur de nombreux sites web comme <a title="multitude de devices et dimensions de viewport" href="http://mydevice.io/devices/" target="_blank" rel="noopener">celui-ci</a> ou encore <a title="multitude de devices et dimensions de viewport" href="http://screensiz.es/" target="_blank" rel="noopener">là</a> !</p>
<p>Finalement, il est clair que si le rendu proportionnel nous convient visuellement, on essaye le moins possible de se préoccuper des "media queries" pour affiner le rendu de nos textes, le "device" utilisé va en quelque sorte automatiquement provoquer le rendu, pour peu que les tailles définies par l'intégrateur web soient judicieuses !</p>
<p>Vous pouvez étudier la compatibilité de ces unités avec les navigateurs actuels sur <a href="http://caniuse.com/#feat=viewport-units" title="viewport units" target="_blank" rel="noopener">can i use</a>. Je n'aborde pas la notion de "vmin" et "vmax" qui peuvent également être utilisées mais de manière plus aléatoire, ces dimensions représentent la plus petite ou la plus grande valeur entre "vw" et "vh".</p>
<p>Ces quelques lignes ne sont évidemment qu'une mise en exergue des unités de "viewport", donc voici un article plus technique et complet sur la <a title="typographie fluide" href="https://www.smashingmagazine.com/2016/05/fluid-typography/">typographie fluide</a> pour aller bien plus loin.</p>
<p>Et aussi : un <a title="taille de typographie et viewport" href="http://thenewcode.com/739/Creating-Responsive-Hero-Text-With-vw-Units" target="_blank" rel="noopener">article</a> (en anglais) intéressant concernant la typographie et sa taille définie en unités de "viewport"</p>
<p>Cet article <a href="https://www.dotprogs.com/typographie-proportionnelle-aux-dimensions-du-viewport/">Typographie proportionnelle aux dimensions du « viewport »</a> est apparu en premier sur <a href="https://www.dotprogs.com">DOTPROGS</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><img width="1024" height="576" src="https://www.dotprogs.com/wp-content/uploaded-files/viewport-units.png" class="attachment-md_post_thumb_large size-md_post_thumb_large wp-post-image" alt="Unités relatives au viewport" decoding="async" loading="lazy" /></p><h2 class="dp-level2-title">Une taille de police de caractère réactive</h2>
<h3 class="dp-level3-title">Les unités "vw" et "vh"</h3>
Actuellement, la plupart des navigateurs sont capables de gérer une nouvelle unité relative à la taille du viewport : le "vw" pour "viewport-width" et le "vh" pour "viewport-height".
Vous l'aurez compris comme moi, il s'agit de définir une taille par rapport à la largeur et la hauteur du "device" utilisé.

Petit rappel : la notion de "viewport" désigne la surface de la fenêtre du navigateur sur desktop, sur les périphériques nomades type mobile ou tablette, on parle davantage de surface réelle d'affichage (qui n'est généralement pas la surface physique du périphérique) avec les valeurs de "device-width" et "device-height". La complexité est bien expliqué sur le site d'<a title="principe de viewport" href="http://www.alsacreations.com/article/lire/1490-comprendre-le-viewport-dans-le-web-mobile.html">Alsacréations</a>.

Le principe revient à utiliser les pourcentages, donc "100vw" et "100vh" constituent la largeur et hauteur totales disponibles.
Ainsi le principe de correspondance de taille est simplement le suivant :
15vw = 15% de la largeur du "viewport"
50vh = la moitié de la hauteur du "viewport"

Si on considère que l'on souhaite appliquer ces unités à une taille de typographie, on a la possibilité d'afficher un texte réactif est proportionnel au "viewport".

Même si le raccourci est un semblant hasardeux, j'apprécie ce principe qui se rapproche quelque peu de l'utilisation du "rem" (pour "root em") pour son côté adaptable et facilement maintenable où on se réfère à une valeur "étalon" qui sert de socle à l'ensemble des tailles.

Pour illustrer, voici ce qui est défini pour la CSS du menu "overlay" de www.dotprogs.com :
<pre class="EnlighterJSRAW" data-enlighter-language="css">.menu-over-page ul.main-nav li {
    font-size: 6vh; /* viewport height */
}</pre>
En fonction des contraintes d'affichage présentes sur un site web, il sera parfois plus opportun d'utiliser une taille proportionnelle à la hauteur ou à la largeur.

Quelles conclusions j'en tire personnellement ?

Et bien je pense que ce principe est intéressant à utiliser de manière ponctuelle si vous souhaitez améliorer le confort de lecture sur un élément de site en particulier comme l'affichage des items d'un menu en fonction du périphérique de l'utilisateur.
Pour l'instant avec le recul minimal et la mise en pratique restreinte que j'ai de "vw" et "vh", je ne perçois pas encore l'apport éventuel que cela peut avoir sur du "templating" de site.

Il est certain également que l'on donne un rendu assez empirique de la typographie au premier abord, puisque ce qui va nous intéresser avant toute chose, est la lisibilité de la taille des textes relatifs à l'espace disponible pour un "viewport" donné, car il n'y a pas véritablement d'intérêt à savoir quelle est la taille réelle en pixel de notre contenu en fonction de la résolution (définition) du "viewport". La diversité des périphériques s'avère même être un casse-tête lorsqu'on s'intéresse plus précisément aux dimensions du viewport et à leurs spécificités (densité de pixel, aspect ratio, etc ...) listées sur de nombreux sites web comme <a title="multitude de devices et dimensions de viewport" href="http://mydevice.io/devices/" target="_blank" rel="noopener">celui-ci</a> ou encore <a title="multitude de devices et dimensions de viewport" href="http://screensiz.es/" target="_blank" rel="noopener">là</a> !

Finalement, il est clair que si le rendu proportionnel nous convient visuellement, on essaye le moins possible de se préoccuper des "media queries" pour affiner le rendu de nos textes, le "device" utilisé va en quelque sorte automatiquement provoquer le rendu, pour peu que les tailles définies par l'intégrateur web soient judicieuses !

Vous pouvez étudier la compatibilité de ces unités avec les navigateurs actuels sur <a href="http://caniuse.com/#feat=viewport-units" title="viewport units" target="_blank" rel="noopener">can i use</a>. Je n'aborde pas la notion de "vmin" et "vmax" qui peuvent également être utilisées mais de manière plus aléatoire, ces dimensions représentent la plus petite ou la plus grande valeur entre "vw" et "vh".

Ces quelques lignes ne sont évidemment qu'une mise en exergue des unités de "viewport", donc voici un article plus technique et complet sur la <a title="typographie fluide" href="https://www.smashingmagazine.com/2016/05/fluid-typography/">typographie fluide</a> pour aller bien plus loin.

Et aussi : un <a title="taille de typographie et viewport" href="http://thenewcode.com/739/Creating-Responsive-Hero-Text-With-vw-Units" target="_blank" rel="noopener">article</a> (en anglais) intéressant concernant la typographie et sa taille définie en unités de "viewport"<p>Cet article <a href="https://www.dotprogs.com/typographie-proportionnelle-aux-dimensions-du-viewport/">Typographie proportionnelle aux dimensions du « viewport »</a> est apparu en premier sur <a href="https://www.dotprogs.com">DOTPROGS</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Peut-on passer sereinement à l&#8217;usage des flexbox ?</title>
		<link>https://www.dotprogs.com/passage-a-un-usage-de-flexbox/</link>
		
		<dc:creator><![CDATA[DOTPROGS]]></dc:creator>
		<pubDate>Sun, 17 Apr 2016 10:26:11 +0000</pubDate>
				<category><![CDATA[Dotweb]]></category>
		<category><![CDATA[Intégration web]]></category>
		<category><![CDATA[autoprefixer]]></category>
		<category><![CDATA[flexbox]]></category>
		<category><![CDATA[grid system]]></category>
		<category><![CDATA[postprocessing]]></category>
		<guid isPermaLink="false">https://www.dotprogs.com/?p=307</guid>

					<description><![CDATA[<p><img width="1024" height="576" src="https://www.dotprogs.com/wp-content/uploaded-files/flexbox-module-layout.png" class="attachment-md_post_thumb_large size-md_post_thumb_large wp-post-image" alt="CSS3 flexible box layout module" decoding="async" loading="lazy" /></p>
<h2 class="dp-level2-title">Un atout et une solution miraculeuse ?</h2>
<h3 class="dp-level3-title">De bonnes raisons de s'y mettre</h3>
<p>Après "moult" hésitations, je me suis enfin dit qu'en tant qu'intégrateur web, je devrais m'intéresser davantage à l'utilisation des "Flexbox" pour des mises en page d'éléments nécessitant d'être un acrobate des CSS.</p>
<p>Le but de cet article n'est pas d'expliquer les flexbox, beaucoup de ressources (voir les liens proposés) très bien faites existent déjà, mais modestement se poser la question pourquoi cela peut-être un progrès.</p>
<p>Pourquoi je ne me suis pas décidé plus tôt ?</p>
<p>Les motifs sont assez simples, une forme de prudence vis à vis de "la nouveauté" où on nous disait clairement "allez-y mais il existe des contraintes", le fait aussi de sortir de sa zone de confort avec notre très chère propriété "float" et notre classe révolutionnaire ".clearfix" !</p>
<p>Quelles raisons m'ont poussé à m'y intéresser ?</p>
<p>Du temps s'est écoulé depuis et en me documentant à nouveau, un certain nombre d'arguments m'ont invité à ne plus être "has been" mais surtout et plus sérieusement à percevoir de réelles avancées, donc voici une liste de faits qui m'ont amené à me remettre en question :</p>
<p>- La compatibilité navigateur est correcte pour franchir le pas, en se basant sur <a href="http://caniuse.com/#search=flexbox" target="_blank" rel="noopener">can i use</a>, avec en liens des ressources proposées pour gérer les bugs de prise en charge.</p>
<p>- On peut gérer correctement son implémentation en postprocessing avec <a href="https://github.com/postcss/autoprefixer" target="_blank" rel="noopener">autoprefixer</a> en l'utilisant au sein d'une tâche Gulp (que j'apprécie) par exemple ou avec bien d'autres outils.</p>
<p>- Le centrage de contenu horizontal et vertical est désormais facilité, on oublie donc les tricks de notre kit de survie, comme les positions absolues, marges négatives, ou les comportements de tableau avec display: table / table-cell, ou bien d'autres techniques encore ...</p>
<p>- Le positionnement "inline" devient aussi plus naturel sans avoir les contraintes et dommages collatéraux d'utilisation du fameux "display: inline-block" pourtant bien pratique, avec une distribution, un espacement, un dimensionnement des blocs plus aisés.</p>
<p>- L'ordre des blocs (items) peut être modifié de manière très souple, ce qui simplifie grandement le templating par rapport à un "device" particulier.</p>
<p>- Les systèmes de grid en vogue suivent le mouvement, je pense notamment à <a href="http://foundation.zurb.com/sites/docs/flexbox.html" target="_blank" rel="noopener">Foundation 6</a> qui confirme son utilisation, un <a href="http://v4-alpha.getbootstrap.com/layout/flexbox-grid/" target="_blank" rel="noopener">Bootstrap 4</a> qui va en proposer l'utilisation, d'autres s'y sont mis, il y a un certain temps déjà, comme <span><a title="flexbox avec Knacss" href="http://knacss.com/grillade/" target="_blank" rel="noopener">Knacss</a> ou <a title="flexbox avec Gridlex" href="http://gridlex.devlint.fr/" target="_blank" rel="noopener">Gridlex</a>.</span></p>
<p>Le principe de flexbox ne permet cependant que le positionnement d'items dans une dimension unique, alors que la spécification en "<a href="https://la-cascade.io/css-grid-layout-guide-complet/" target="_blank" rel="noopener">grid layout</a>" très prometteuse permettra, une fois qu'elle sera bien prise en charge, de faire un positionnement en deux dimensions.</p>
<p>Plusieurs voyants sont véritablement au vert pour nous inciter à aller de l'avant et considérer flexbox comme un "atout" à la réalisation de certaines pirouettes particulières, donc si la vie de l'intégrateur web peut être plus agréable, profitons-en ...</p>
<p>Voici quelques liens sur le sujet proposés par Alsacréations :</p>
<ul>
<li>W3C : <a title="spécification flexbox fodule" href="http://www.w3.org/TR/css3-flexbox/" target="_blank" rel="noopener">Spécification flexbox module</a></li>
<li><a title="démonstrations flexbox" href="http://jackintheflexbox.tumblr.com/" target="_blank" rel="noopener">Jack in the flexbox</a> : une galerie de démonstrations dédiée à flexbox</li>
<li><a title="apports de flexbox" href="http://philipwalton.github.io/solved-by-flexbox/" target="_blank" rel="noopener">Solved by flexbox </a> : des problèmes CSS classiques réglés grâce à flexbox</li>
<li><a title="tests flexbox" href="http://the-echoplex.net/flexyboxes/" target="_blank" rel="noopener">Flexyboxes</a> : un bac à sable pour tester flexbox</li>
<li><a title="flexbox en détail" href="http://chriswrightdesign.com/experiments/flexbox-adventures/" target="_blank" rel="noopener">Flexbox Adventures</a> : un article anglais très détaillé sur les subtilités de flexbox</li>
<li><a title="flexbox et bugs navigateurs" href="https://github.com/philipwalton/flexbugs/" target="_blank" rel="noopener">Flexbugs</a> : les bugs connus de flexbox et comment les contourner</li>
</ul>
<p>Et aussi :</p>
<ul>
<li><a title="autres tests flexbox" href="http://codepen.io/enxaneta/full/adLPwv/" target="_blank" rel="noopener">Flexbox Playground</a> - Tester en live flexbox</li>
<li><a title="guide complet flexbox" href="https://css-tricks.com/snippets/css/a-guide-to-flexbox/" target="_blank" rel="noopener">A Complete Guide to Flexbox</a> (CSS Tricks) - Tour d'horizon sur flexbox</li>
<li><a title="jeu pour apprendre flexbox" href="http://flexboxfroggy.com/" target="_blank" rel="noopener">Flexbox Froggy</a> - Un jeu pour apprendre flexbox</li>
<li><a title="un polyfill flexbox" href="https://github.com/10up/flexibility" target="_blank" rel="noopener">Flexibility</a> - Un polyfill pour flexbox</li>
</ul>
<p>Cet article <a href="https://www.dotprogs.com/passage-a-un-usage-de-flexbox/">Peut-on passer sereinement à l&rsquo;usage des flexbox ?</a> est apparu en premier sur <a href="https://www.dotprogs.com">DOTPROGS</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><img width="1024" height="576" src="https://www.dotprogs.com/wp-content/uploaded-files/flexbox-module-layout.png" class="attachment-md_post_thumb_large size-md_post_thumb_large wp-post-image" alt="CSS3 flexible box layout module" decoding="async" loading="lazy" /></p><h2 class="dp-level2-title">Un atout et une solution miraculeuse ?</h2>
<h3 class="dp-level3-title">De bonnes raisons de s'y mettre</h3>
Après "moult" hésitations, je me suis enfin dit qu'en tant qu'intégrateur web, je devrais m'intéresser davantage à l'utilisation des "Flexbox" pour des mises en page d'éléments nécessitant d'être un acrobate des CSS.

Le but de cet article n'est pas d'expliquer les flexbox, beaucoup de ressources (voir les liens proposés) très bien faites existent déjà, mais modestement se poser la question pourquoi cela peut-être un progrès.

Pourquoi je ne me suis pas décidé plus tôt ?

Les motifs sont assez simples, une forme de prudence vis à vis de "la nouveauté" où on nous disait clairement "allez-y mais il existe des contraintes", le fait aussi de sortir de sa zone de confort avec notre très chère propriété "float" et notre classe révolutionnaire ".clearfix" !

Quelles raisons m'ont poussé à m'y intéresser ?

Du temps s'est écoulé depuis et en me documentant à nouveau, un certain nombre d'arguments m'ont invité à ne plus être "has been" mais surtout et plus sérieusement à percevoir de réelles avancées, donc voici une liste de faits qui m'ont amené à me remettre en question :

- La compatibilité navigateur est correcte pour franchir le pas, en se basant sur <a href="http://caniuse.com/#search=flexbox" target="_blank" rel="noopener">can i use</a>, avec en liens des ressources proposées pour gérer les bugs de prise en charge.

- On peut gérer correctement son implémentation en postprocessing avec <a href="https://github.com/postcss/autoprefixer" target="_blank" rel="noopener">autoprefixer</a> en l'utilisant au sein d'une tâche Gulp (que j'apprécie) par exemple ou avec bien d'autres outils.

- Le centrage de contenu horizontal et vertical est désormais facilité, on oublie donc les tricks de notre kit de survie, comme les positions absolues, marges négatives, ou les comportements de tableau avec display: table / table-cell, ou bien d'autres techniques encore ...

- Le positionnement "inline" devient aussi plus naturel sans avoir les contraintes et dommages collatéraux d'utilisation du fameux "display: inline-block" pourtant bien pratique, avec une distribution, un espacement, un dimensionnement des blocs plus aisés.

- L'ordre des blocs (items) peut être modifié de manière très souple, ce qui simplifie grandement le templating par rapport à un "device" particulier.

- Les systèmes de grid en vogue suivent le mouvement, je pense notamment à <a href="http://foundation.zurb.com/sites/docs/flexbox.html" target="_blank" rel="noopener">Foundation 6</a> qui confirme son utilisation, un <a href="http://v4-alpha.getbootstrap.com/layout/flexbox-grid/" target="_blank" rel="noopener">Bootstrap 4</a> qui va en proposer l'utilisation, d'autres s'y sont mis, il y a un certain temps déjà, comme <span><a title="flexbox avec Knacss" href="http://knacss.com/grillade/" target="_blank" rel="noopener">Knacss</a> ou <a title="flexbox avec Gridlex" href="http://gridlex.devlint.fr/" target="_blank" rel="noopener">Gridlex</a>.</span>

Le principe de flexbox ne permet cependant que le positionnement d'items dans une dimension unique, alors que la spécification en "<a href="https://la-cascade.io/css-grid-layout-guide-complet/" target="_blank" rel="noopener">grid layout</a>" très prometteuse permettra, une fois qu'elle sera bien prise en charge, de faire un positionnement en deux dimensions.

Plusieurs voyants sont véritablement au vert pour nous inciter à aller de l'avant et considérer flexbox comme un "atout" à la réalisation de certaines pirouettes particulières, donc si la vie de l'intégrateur web peut être plus agréable, profitons-en ...

Voici quelques liens sur le sujet proposés par Alsacréations :
<ul>
 	<li>W3C : <a title="spécification flexbox fodule" href="http://www.w3.org/TR/css3-flexbox/" target="_blank" rel="noopener">Spécification flexbox module</a></li>
 	<li><a title="démonstrations flexbox" href="http://jackintheflexbox.tumblr.com/" target="_blank" rel="noopener">Jack in the flexbox</a> : une galerie de démonstrations dédiée à flexbox</li>
 	<li><a title="apports de flexbox" href="http://philipwalton.github.io/solved-by-flexbox/" target="_blank" rel="noopener">Solved by flexbox </a> : des problèmes CSS classiques réglés grâce à flexbox</li>
 	<li><a title="tests flexbox" href="http://the-echoplex.net/flexyboxes/" target="_blank" rel="noopener">Flexyboxes</a> : un bac à sable pour tester flexbox</li>
 	<li><a title="flexbox en détail" href="http://chriswrightdesign.com/experiments/flexbox-adventures/" target="_blank" rel="noopener">Flexbox Adventures</a> : un article anglais très détaillé sur les subtilités de flexbox</li>
 	<li><a title="flexbox et bugs navigateurs" href="https://github.com/philipwalton/flexbugs/" target="_blank" rel="noopener">Flexbugs</a> : les bugs connus de flexbox et comment les contourner</li>
</ul>
Et aussi :
<ul>
 	<li><a title="autres tests flexbox" href="http://codepen.io/enxaneta/full/adLPwv/" target="_blank" rel="noopener">Flexbox Playground</a> - Tester en live flexbox</li>
 	<li><a title="guide complet flexbox" href="https://css-tricks.com/snippets/css/a-guide-to-flexbox/" target="_blank" rel="noopener">A Complete Guide to Flexbox</a> (CSS Tricks) - Tour d'horizon sur flexbox</li>
 	<li><a title="jeu pour apprendre flexbox" href="http://flexboxfroggy.com/" target="_blank" rel="noopener">Flexbox Froggy</a> - Un jeu pour apprendre flexbox</li>
 	<li><a title="un polyfill flexbox" href="https://github.com/10up/flexibility" target="_blank" rel="noopener">Flexibility</a> - Un polyfill pour flexbox</li>
</ul><p>Cet article <a href="https://www.dotprogs.com/passage-a-un-usage-de-flexbox/">Peut-on passer sereinement à l&rsquo;usage des flexbox ?</a> est apparu en premier sur <a href="https://www.dotprogs.com">DOTPROGS</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
