diff --git a/web/themes/custom/leshed/assets/js/main.js b/web/themes/custom/leshed/assets/js/main.js index 27952fb..c68b692 100644 --- a/web/themes/custom/leshed/assets/js/main.js +++ b/web/themes/custom/leshed/assets/js/main.js @@ -59,46 +59,119 @@ import Splitting from "splitting"; return Math.floor(Math.random() * (max - min)) + min; } - function initTitles(){ - let titles = document.querySelectorAll('#block-leshed-identitedusite>a, article.node-type-projet>h2'); + // Rendu typographique des titres. Police Epilogue = axe variable `wght` + // (100–900) uniquement ; l'italique est un fichier séparé (font-style). + const TITLE_STYLE = { + // 3 groupes de graisse : thin = majorité (défaut), medium = quelques, + // bold = les pics. Les fines ne sont pas comptées (c'est le reste). + wghtThin: { min: 100, max: 200 }, // majorité (défaut) + wghtMedium: { min: 200, max: 400 }, // quelques + wghtBold: { min: 800, max: 900 }, // pics + boldRatio: 0.28, // proportion de pics gras par mot + mediumRatio: 0.12, // proportion de medium par mot + italicOnBold: 0.6, // proba d'italique sur un pic + italicOnMedium: 0.4, // proba d'italique sur un medium + italicOnThin: 0.30, // proba d'italique sur une fine + }; + function initTitles() { + const titles = document.querySelectorAll('#block-leshed-identitedusite>a, article.node-type-projet>header>h2'); for (const txt of titles) { - console.log(txt.innerText, '- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -'); - let splitted = Splitting({ target: txt, by: 'chars'}); - console.log("splitted", splitted); - + const splitted = Splitting({ target: txt, by: 'chars' }); for (const word of splitted[0].words) { - let chars = word.getElementsByClassName('char'); - console.log("chars", chars); + styleWordChars(Array.from(word.getElementsByClassName('char'))); + } + } + } - for (const char of chars) { - console.log(char.innerText); + /** + * Tire k positions réparties régulièrement dans [0, n[ : le mot est découpé + * en k cases contiguës, une position au hasard par case (aléa contrôlé, pas + * de paquets). Les positions déjà prises par un autre groupe sont évitées. + */ + function spreadPositions(n, k, exclude = new Set()) { + const set = new Set(); + for (let i = 0; i < k; i++) { + const start = Math.floor((i * n) / k); + const end = Math.floor(((i + 1) * n) / k); // borne exclue + const span = Math.max(1, end - start); + let pos = randomInt(start, start + span); + let guard = 0; + while ((exclude.has(pos) || set.has(pos)) && guard < span) { + pos = start + ((pos - start + 1) % span); + guard++; + } + set.add(pos); + } + return set; + } - // Weight - let wght = randomInt(150, 600);//200 + Math.random() * 800; + /** + * Applique graisse + italique caractère par caractère. + * + * Majorité de fines ; quelques medium et quelques pics gras, chacun réparti + * régulièrement dans le mot. L'italique est corrélé à la graisse. + */ + function styleWordChars(chars) { + const n = chars.length; + if (!n) return; - // Ital - let ital = Math.random() > 0.7 ? 1 : 0; - // char.style.fontVariationSettings = `'wght' ${wght}, 'wdth' ${wdth}, 'ital' ${ital}`; - char.style.fontVariationSettings = `'wght' ${wght}`; - char.style.fontStyle = ital ? 'italic' : 'normal'; + // Accents répartis régulièrement : d'abord les pics gras, puis les medium + // (en évitant les positions déjà grasses). Le reste sera fin. + const bold = spreadPositions(n, Math.round(n * TITLE_STYLE.boldRatio)); + const medium = spreadPositions(n, Math.round(n * TITLE_STYLE.mediumRatio), bold); - // // Letter spacing - // let wdth_wght_ratio = wdth*wght / 10000; - // console.log('wdth_wght_ratio',wdth_wght_ratio); + // 1) Décide graisse + italique pour chaque caractère. + const decisions = chars.map((char, i) => { + let range, pItal; + if (bold.has(i)) { + range = TITLE_STYLE.wghtBold; + pItal = TITLE_STYLE.italicOnBold; + } + else if (medium.has(i)) { + range = TITLE_STYLE.wghtMedium; + pItal = TITLE_STYLE.italicOnMedium; + } + else { + range = TITLE_STYLE.wghtThin; + pItal = TITLE_STYLE.italicOnThin; + } + return { + char, + wght: randomInt(range.min, range.max + 1), + ital: Math.random() < pItal, + }; + }); - // char.style.letterSpacing = wdth_wght_ratio < 1 ? "0.5em" : ital ? "0.05em" : 0; - // if (ital) { - // let prev_char = char.previousSibling; - // if (prev_char) { - // char.style.letterSpacing = prev_char.style.letterSpacing = "0.03em"; - // } - // } + // 2) Au moins une italique par mot : si aucune, on en force une, de + // préférence sur un pic gras ou un medium, sinon sur une lettre au hasard. + if (!decisions.some((d) => d.ital)) { + const accents = [...bold, ...medium]; + const idx = accents.length ? accents[randomInt(0, accents.length)] : randomInt(0, n); + decisions[idx].ital = true; + } + + // 3) Pas plus de 2 italiques consécutives : on casse les séries de 3+. + let run = 0; + for (const d of decisions) { + if (d.ital) { + run += 1; + if (run > 2) { + d.ital = false; + run = 0; } } + else { + run = 0; + } + } - - + // 4) Applique. + for (const { char, wght, ital } of decisions) { + char.style.fontVariationSettings = `'wght' ${wght}`; + char.style.fontStyle = ital ? 'italic' : 'normal'; + char.classList.toggle('italic', ital); + char.classList.toggle('normal', !ital); } } diff --git a/web/themes/custom/leshed/assets/scss/main.scss b/web/themes/custom/leshed/assets/scss/main.scss index a65d642..9e7aa52 100644 --- a/web/themes/custom/leshed/assets/scss/main.scss +++ b/web/themes/custom/leshed/assets/scss/main.scss @@ -72,7 +72,7 @@ header[role="banner"]{ display: flex; justify-content:space-between; align-items: center; - padding: 0.2em 2em; + padding: 0.2em 0; } #block-leshed-identitedusite{ @@ -170,3 +170,76 @@ header[role="banner"]{ } } // end of header[role="banner"]{ + +// _____ _____ __ ____ ___ _ ____ +// |_ _/ _ \ \ / / / ___/ _ \| | / ___| +// | || | | \ \ /\ / / | | | | | | | \___ \ +// | || |_| |\ V V / | |__| |_| | |___ ___) | +// |_| \___/ \_/\_/ \____\___/|_____|____/ + +div.layout.layout--twocol-section--50-50{ + flex-wrap: nowrap; + column-gap: 1em; + div.layout__region{ + .views-row{ + // border: 1px solid red; + // box-sizing: border-box; + margin-bottom: 1.5em; + article.node-type-projet{ + >header{ + >h2{ + margin: 0; + font-size: 5em; + text-transform:lowercase; + filter: drop-shadow(0 0 5px #fff) drop-shadow(0 0 15px #fff); + span.word{ + hyphens: auto; + + // $bgcolor:rgba(255,255,255,0.8); + // // background-color: $bgcolor; + // border-radius: 0.8em; + // box-shadow: 0 0 20px #fff; + + span.char{ + letter-spacing:-0.2em; + } + } + // span.whitespace{ + // letter-spacing: 0.5em; + // } + } + } + >div{ + // margin-top: 3em; + } + // only with images, title overlap the image + &.has-image{ + >header{ + >h2{ + position: relative; + max-height: 1em; + overflow: visible; + z-index: 10; + // text-shadow: 0 0 10px #fff; + // box-shadow: 0 0 15px #f00; + // filter: drop-shadow(0 0 5px #fff); + } + div.field_images{ + position:relative; + // width: 100%; + z-index: 5; + a{ + display:block; + img{ + width:100%; + height: auto; + } + } + } + } + } + } + + } + } +} diff --git a/web/themes/custom/leshed/leshed.theme b/web/themes/custom/leshed/leshed.theme index 76ed4c4..7cf0453 100644 --- a/web/themes/custom/leshed/leshed.theme +++ b/web/themes/custom/leshed/leshed.theme @@ -74,15 +74,25 @@ function parse_menu_item(&$items, $key){ * Implements hook_preprocess_HOOK() for node.html.twig. */ function leshed_preprocess_node(&$variables) { - $node_type = $variables['node']->getType(); + /** @var \Drupal\node\Entity\Node $node */ + $node = $variables['node']; + $node_type = $node->getType(); if (!isset($variables["attributes"]['class'])) { $variables["attributes"]['class'] = []; } $variables["attributes"]['class'][] = "node-type-{$node_type}"; + + if ($variables['view_mode'] === "teaser") { + /** @var \Drupal\file\Plugin\\Field\FieldType\FileFieldItemList $field_images */ + $field_images = $node->get('field_images'); + if (!$field_images->isEmpty()) { + $variables["attributes"]['class'][] = "has-image"; + } + } } -function leshed_preprocess_contanier(&$variables) { +function leshed_preprocess_container(&$variables) { } diff --git a/web/themes/custom/leshed/templates/node--personne--teaser.html.twig b/web/themes/custom/leshed/templates/node--personne--teaser.html.twig index e69de29..1e0ba9e 100644 --- a/web/themes/custom/leshed/templates/node--personne--teaser.html.twig +++ b/web/themes/custom/leshed/templates/node--personne--teaser.html.twig @@ -0,0 +1,89 @@ +{# +/** + * @file + * Default theme implementation to display a node. + * + * Available variables: + * - node: The node entity with limited access to object properties and methods. + * Only method names starting with "get", "has", or "is" and a few common + * methods such as "id", "label", and "bundle" are available. For example: + * - node.getCreatedTime() will return the node creation timestamp. + * - node.hasField('field_example') returns TRUE if the node bundle includes + * field_example. (This does not indicate the presence of a value in this + * field.) + * - node.isPublished() will return whether the node is published or not. + * Calling other methods, such as node.delete(), will result in an exception. + * See \Drupal\node\Entity\Node for a full list of public properties and + * methods for the node object. + * - label: (optional) The title of the node. + * - content: All node items. Use {{ content }} to print them all, + * or print a subset such as {{ content.field_example }}. Use + * {{ content|without('field_example') }} to temporarily suppress the printing + * of a given child element. + * - author_picture: The node author user entity, rendered using the "compact" + * view mode. + * - metadata: Metadata for this node. + * - date: (optional) Themed creation date field. + * - author_name: (optional) Themed author name field. + * - url: Direct URL of the current node. + * - display_submitted: Whether submission information should be displayed. + * - attributes: HTML attributes for the containing element. + * The attributes.class element may contain one or more of the following + * classes: + * - node: The current template type (also known as a "theming hook"). + * - node--type-[type]: The current node type. For example, if the node is an + * "Article" it would result in "node--type-article". Note that the machine + * name will often be in a short form of the human readable label. + * - node--view-mode-[view_mode]: The View Mode of the node; for example, a + * teaser would result in: "node--view-mode-teaser", and + * full: "node--view-mode-full". + * The following are controlled through the node publishing options. + * - node--promoted: Appears on nodes promoted to the front page. + * - node--sticky: Appears on nodes ordered above other non-sticky nodes in + * teaser listings. + * - node--unpublished: Appears on unpublished nodes visible only to site + * admins. + * - title_attributes: Same as attributes, except applied to the main title + * tag that appears in the template. + * - content_attributes: Same as attributes, except applied to the main + * content tag that appears in the template. + * - author_attributes: Same as attributes, except applied to the author of + * the node tag that appears in the template. + * - title_prefix: Additional output populated by modules, intended to be + * displayed in front of the main title tag that appears in the template. + * - title_suffix: Additional output populated by modules, intended to be + * displayed after the main title tag that appears in the template. + * - view_mode: View mode; for example, "teaser" or "full". + * - teaser: Flag for the teaser state. Will be true if view_mode is 'teaser'. + * - page: Flag for the full page state. Will be true if view_mode is 'full'. + * + * @see template_preprocess_node() + * + * @ingroup themeable + */ +#} + + + {{ title_prefix }} + {% if label and not page %} + + {{ label }} + + {% endif %} + {{ title_suffix }} + + {% if display_submitted %} +
+ {{ author_picture }} + + {% trans %}Submitted by {{ author_name }} on {{ date }}{% endtrans %} + {{ metadata }} + +
+ {% endif %} + + + {{ content }} + + + diff --git a/web/themes/custom/leshed/templates/node--projet--teaser.html.twig b/web/themes/custom/leshed/templates/node--projet--teaser.html.twig new file mode 100644 index 0000000..5d37cfa --- /dev/null +++ b/web/themes/custom/leshed/templates/node--projet--teaser.html.twig @@ -0,0 +1,89 @@ +{# +/** + * @file + * Default theme implementation to display a node. + * + * Available variables: + * - node: The node entity with limited access to object properties and methods. + * Only method names starting with "get", "has", or "is" and a few common + * methods such as "id", "label", and "bundle" are available. For example: + * - node.getCreatedTime() will return the node creation timestamp. + * - node.hasField('field_example') returns TRUE if the node bundle includes + * field_example. (This does not indicate the presence of a value in this + * field.) + * - node.isPublished() will return whether the node is published or not. + * Calling other methods, such as node.delete(), will result in an exception. + * See \Drupal\node\Entity\Node for a full list of public properties and + * methods for the node object. + * - label: (optional) The title of the node. + * - content: All node items. Use {{ content }} to print them all, + * or print a subset such as {{ content.field_example }}. Use + * {{ content|without('field_example') }} to temporarily suppress the printing + * of a given child element. + * - author_picture: The node author user entity, rendered using the "compact" + * view mode. + * - metadata: Metadata for this node. + * - date: (optional) Themed creation date field. + * - author_name: (optional) Themed author name field. + * - url: Direct URL of the current node. + * - display_submitted: Whether submission information should be displayed. + * - attributes: HTML attributes for the containing element. + * The attributes.class element may contain one or more of the following + * classes: + * - node: The current template type (also known as a "theming hook"). + * - node--type-[type]: The current node type. For example, if the node is an + * "Article" it would result in "node--type-article". Note that the machine + * name will often be in a short form of the human readable label. + * - node--view-mode-[view_mode]: The View Mode of the node; for example, a + * teaser would result in: "node--view-mode-teaser", and + * full: "node--view-mode-full". + * The following are controlled through the node publishing options. + * - node--promoted: Appears on nodes promoted to the front page. + * - node--sticky: Appears on nodes ordered above other non-sticky nodes in + * teaser listings. + * - node--unpublished: Appears on unpublished nodes visible only to site + * admins. + * - title_attributes: Same as attributes, except applied to the main title + * tag that appears in the template. + * - content_attributes: Same as attributes, except applied to the main + * content tag that appears in the template. + * - author_attributes: Same as attributes, except applied to the author of + * the node tag that appears in the template. + * - title_prefix: Additional output populated by modules, intended to be + * displayed in front of the main title tag that appears in the template. + * - title_suffix: Additional output populated by modules, intended to be + * displayed after the main title tag that appears in the template. + * - view_mode: View mode; for example, "teaser" or "full". + * - teaser: Flag for the teaser state. Will be true if view_mode is 'teaser'. + * - page: Flag for the full page state. Will be true if view_mode is 'full'. + * + * @see template_preprocess_node() + * + * @ingroup themeable + */ +#} + +{# +{% if content.field_images.getvalue|length %} + {% set attributes = attributes.addClass('has-image') %} +{% endif %} #} + + + + {# {{ title_prefix }} #} + {# {% if label and not page %} #} +
+ + {{ label }} + + {{ content.field_images }} +
+ {# {% endif %} #} + {# {{ title_suffix }} #} + + + + {{ content|without('field_images') }} + + +