A place to cache linked articles (think custom and personal wayback machine)
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

index.html 56KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang=fr>
  4. <!-- Has to be within the first 1024 bytes, hence before the <title>
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset=utf-8>
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name=viewport content="width=device-width,minimum-scale=1,initial-scale=1,shrink-to-fit=no">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>Bonnes pratiques en CSS : BEM et OOCSS (archive) — David Larlet</title>
  13. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  14. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons/apple-touch-icon.png">
  15. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons/favicon-32x32.png">
  16. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons/favicon-16x16.png">
  17. <link rel="manifest" href="/manifest.json">
  18. <link rel="mask-icon" href="/static/david/icons/safari-pinned-tab.svg" color="#5bbad5">
  19. <link rel="shortcut icon" href="/static/david/icons/favicon.ico">
  20. <meta name="apple-mobile-web-app-title" content="David Larlet">
  21. <meta name="application-name" content="David Larlet">
  22. <meta name="msapplication-TileColor" content="#da532c">
  23. <meta name="msapplication-config" content="/static/david/icons/browserconfig.xml">
  24. <meta name="theme-color" content="#f0f0ea">
  25. <!-- That good ol' feed, subscribe :p. -->
  26. <link rel=alternate type="application/atom+xml" title=Feed href="/david/log/">
  27. <meta name="robots" content="noindex, nofollow">
  28. <meta content="origin-when-cross-origin" name="referrer">
  29. <!-- Canonical URL for SEO purposes -->
  30. <link rel="canonical" href="http://www.alsacreations.com/article/lire/1641-bonnes-pratiques-en-css-bem-et-oocss.html">
  31. <style>
  32. /* http://meyerweb.com/eric/tools/css/reset/ */
  33. html, body, div, span,
  34. h1, h2, h3, h4, h5, h6, p, blockquote, pre,
  35. a, abbr, address, big, cite, code,
  36. del, dfn, em, img, ins,
  37. small, strike, strong, tt, var,
  38. dl, dt, dd, ol, ul, li,
  39. fieldset, form, label, legend,
  40. table, caption, tbody, tfoot, thead, tr, th, td,
  41. article, aside, canvas, details, embed,
  42. figure, figcaption, footer, header, hgroup,
  43. menu, nav, output, ruby, section, summary,
  44. time, mark, audio, video {
  45. margin: 0;
  46. padding: 0;
  47. border: 0;
  48. font-size: 100%;
  49. font: inherit;
  50. vertical-align: baseline;
  51. }
  52. /* HTML5 display-role reset for older browsers */
  53. article, aside, details, figcaption, figure,
  54. footer, header, hgroup, menu, nav, section { display: block; }
  55. body { line-height: 1; }
  56. blockquote, q { quotes: none; }
  57. blockquote:before, blockquote:after,
  58. q:before, q:after {
  59. content: '';
  60. content: none;
  61. }
  62. table {
  63. border-collapse: collapse;
  64. border-spacing: 0;
  65. }
  66. /* http://practicaltypography.com/equity.html */
  67. /* https://calendar.perfplanet.com/2016/no-font-face-bulletproof-syntax/ */
  68. /* https://www.filamentgroup.com/lab/js-web-fonts.html */
  69. @font-face {
  70. font-family: 'EquityTextB';
  71. src: url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff2') format('woff2'),
  72. url('/static/david/css/fonts/Equity-Text-B-Regular-webfont.woff') format('woff');
  73. font-weight: 300;
  74. font-style: normal;
  75. font-display: swap;
  76. }
  77. @font-face {
  78. font-family: 'EquityTextB';
  79. src: url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff2') format('woff2'),
  80. url('/static/david/css/fonts/Equity-Text-B-Italic-webfont.woff') format('woff');
  81. font-weight: 300;
  82. font-style: italic;
  83. font-display: swap;
  84. }
  85. @font-face {
  86. font-family: 'EquityTextB';
  87. src: url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff2') format('woff2'),
  88. url('/static/david/css/fonts/Equity-Text-B-Bold-webfont.woff') format('woff');
  89. font-weight: 700;
  90. font-style: normal;
  91. font-display: swap;
  92. }
  93. @font-face {
  94. font-family: 'ConcourseT3';
  95. src: url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff2') format('woff2'),
  96. url('/static/david/css/fonts/concourse_t3_regular-webfont-20190806.woff') format('woff');
  97. font-weight: 300;
  98. font-style: normal;
  99. font-display: swap;
  100. }
  101. /* http://practice.typekit.com/lesson/caring-about-opentype-features/ */
  102. body {
  103. /* http://www.cssfontstack.com/ Palatino 99% Win 86% Mac */
  104. font-family: "EquityTextB", Palatino, serif;
  105. background-color: #f0f0ea;
  106. color: #07486c;
  107. font-kerning: normal;
  108. -moz-osx-font-smoothing: grayscale;
  109. -webkit-font-smoothing: subpixel-antialiased;
  110. text-rendering: optimizeLegibility;
  111. font-variant-ligatures: common-ligatures contextual;
  112. font-feature-settings: "kern", "liga", "clig", "calt";
  113. }
  114. pre, code, kbd, samp, var, tt {
  115. font-family: 'TriplicateT4c', monospace;
  116. }
  117. em {
  118. font-style: italic;
  119. color: #323a45;
  120. }
  121. strong {
  122. font-weight: bold;
  123. color: black;
  124. }
  125. nav {
  126. background-color: #323a45;
  127. color: #f0f0ea;
  128. display: flex;
  129. justify-content: space-around;
  130. padding: 1rem .5rem;
  131. }
  132. nav:last-child {
  133. border-bottom: 1vh solid #2d7474;
  134. }
  135. nav a {
  136. color: #f0f0ea;
  137. }
  138. nav abbr {
  139. border-bottom: 1px dotted white;
  140. }
  141. h1 {
  142. border-top: 1vh solid #2d7474;
  143. border-bottom: .2vh dotted #2d7474;
  144. background-color: #e3e1e1;
  145. color: #323a45;
  146. text-align: center;
  147. padding: 5rem 0 4rem 0;
  148. width: 100%;
  149. font-family: 'ConcourseT3';
  150. display: flex;
  151. flex-direction: column;
  152. }
  153. h1.single {
  154. padding-bottom: 10rem;
  155. }
  156. h1 span {
  157. position: absolute;
  158. top: 1vh;
  159. left: 20%;
  160. line-height: 0;
  161. }
  162. h1 span a {
  163. line-height: 1.7;
  164. padding: 1rem 1.2rem .6rem 1.2rem;
  165. border-radius: 0 0 6% 6%;
  166. background: #2d7474;
  167. font-size: 1.3rem;
  168. color: white;
  169. text-decoration: none;
  170. }
  171. h2 {
  172. margin: 4rem 0 1rem;
  173. border-top: .2vh solid #2d7474;
  174. padding-top: 1vh;
  175. }
  176. h3 {
  177. text-align: center;
  178. margin: 3rem 0 .75em;
  179. }
  180. hr {
  181. height: .4rem;
  182. width: .4rem;
  183. border-radius: .4rem;
  184. background: #07486c;
  185. margin: 2.5rem auto;
  186. }
  187. time {
  188. display: bloc;
  189. margin-left: 0 !important;
  190. }
  191. ul, ol {
  192. margin: 2rem;
  193. }
  194. ul {
  195. list-style-type: square;
  196. }
  197. a {
  198. text-decoration-skip-ink: auto;
  199. text-decoration-thickness: 0.05em;
  200. text-underline-offset: 0.09em;
  201. }
  202. article {
  203. max-width: 50rem;
  204. display: flex;
  205. flex-direction: column;
  206. margin: 2rem auto;
  207. }
  208. article.single {
  209. border-top: .2vh dotted #2d7474;
  210. margin: -6rem auto 1rem auto;
  211. background: #f0f0ea;
  212. padding: 2rem;
  213. }
  214. article p:last-child {
  215. margin-bottom: 1rem;
  216. }
  217. p {
  218. padding: 0 .5rem;
  219. margin-left: 3rem;
  220. }
  221. p + p,
  222. figure + p {
  223. margin-top: 2rem;
  224. }
  225. blockquote {
  226. background-color: #e3e1e1;
  227. border-left: .5vw solid #2d7474;
  228. display: flex;
  229. flex-direction: column;
  230. align-items: center;
  231. padding: 1rem;
  232. margin: 1.5rem;
  233. }
  234. blockquote cite {
  235. font-style: italic;
  236. }
  237. blockquote p {
  238. margin-left: 0;
  239. }
  240. figure {
  241. border-top: .2vh solid #2d7474;
  242. background-color: #e3e1e1;
  243. text-align: center;
  244. padding: 1.5rem 0;
  245. margin: 1rem 0 0;
  246. font-size: 1.5rem;
  247. width: 100%;
  248. }
  249. figure img {
  250. max-width: 250px;
  251. max-height: 250px;
  252. border: .5vw solid #323a45;
  253. padding: 1px;
  254. }
  255. figcaption {
  256. padding: 1rem;
  257. line-height: 1.4;
  258. }
  259. aside {
  260. display: flex;
  261. flex-direction: column;
  262. background-color: #e3e1e1;
  263. padding: 1rem 0;
  264. border-bottom: .2vh solid #07486c;
  265. }
  266. aside p {
  267. max-width: 50rem;
  268. margin: 0 auto;
  269. }
  270. /* https://fvsch.com/code/css-locks/ */
  271. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  272. font-size: 1rem;
  273. line-height: calc( 1.5em + 0.2 * 1rem );
  274. }
  275. h1 {
  276. font-size: 1.9rem;
  277. line-height: calc( 1.2em + 0.2 * 1rem );
  278. }
  279. h2 {
  280. font-size: 1.6rem;
  281. line-height: calc( 1.3em + 0.2 * 1rem );
  282. }
  283. h3 {
  284. font-size: 1.35rem;
  285. line-height: calc( 1.4em + 0.2 * 1rem );
  286. }
  287. @media (min-width: 20em) {
  288. /* The (100vw - 20rem) / (50 - 20) part
  289. resolves to 0-1rem, depending on the
  290. viewport width (between 20em and 50em). */
  291. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  292. font-size: calc( 1rem + .6 * (100vw - 20rem) / (50 - 20) );
  293. line-height: calc( 1.5em + 0.2 * (100vw - 50rem) / (20 - 50) );
  294. margin-left: 0;
  295. }
  296. h1 {
  297. font-size: calc( 1.9rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  298. line-height: calc( 1.2em + 0.2 * (100vw - 50rem) / (20 - 50) );
  299. }
  300. h2 {
  301. font-size: calc( 1.5rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  302. line-height: calc( 1.3em + 0.2 * (100vw - 50rem) / (20 - 50) );
  303. }
  304. h3 {
  305. font-size: calc( 1.35rem + 1.5 * (100vw - 20rem) / (50 - 20) );
  306. line-height: calc( 1.4em + 0.2 * (100vw - 50rem) / (20 - 50) );
  307. }
  308. }
  309. @media (min-width: 50em) {
  310. /* The right part of the addition *must* be a
  311. rem value. In this example we *could* change
  312. the whole declaration to font-size:2.5rem,
  313. but if our baseline value was not expressed
  314. in rem we would have to use calc. */
  315. p, li, pre, code, kbd, samp, var, tt, time, details, figcaption {
  316. font-size: calc( 1rem + .6 * 1rem );
  317. line-height: 1.5em;
  318. }
  319. p, li, pre, details {
  320. margin-left: 3rem;
  321. }
  322. h1 {
  323. font-size: calc( 1.9rem + 1.5 * 1rem );
  324. line-height: 1.2em;
  325. }
  326. h2 {
  327. font-size: calc( 1.5rem + 1.5 * 1rem );
  328. line-height: 1.3em;
  329. }
  330. h3 {
  331. font-size: calc( 1.35rem + 1.5 * 1rem );
  332. line-height: 1.4em;
  333. }
  334. figure img {
  335. max-width: 500px;
  336. max-height: 500px;
  337. }
  338. }
  339. figure.unsquared {
  340. margin-bottom: 1.5rem;
  341. }
  342. figure.unsquared img {
  343. height: inherit;
  344. }
  345. @media print {
  346. body { font-size: 100%; }
  347. a:after { content: " (" attr(href) ")"; }
  348. a, a:link, a:visited, a:after {
  349. text-decoration: underline;
  350. text-shadow: none !important;
  351. background-image: none !important;
  352. background: white;
  353. color: black;
  354. }
  355. abbr[title] { border-bottom: 0; }
  356. abbr[title]:after { content: " (" attr(title) ")"; }
  357. img { page-break-inside: avoid; }
  358. @page { margin: 2cm .5cm; }
  359. h1, h2, h3 { page-break-after: avoid; }
  360. p3 { orphans: 3; widows: 3; }
  361. img {
  362. max-width: 250px !important;
  363. max-height: 250px !important;
  364. }
  365. nav, aside { display: none; }
  366. }
  367. ul.with_columns {
  368. column-count: 1;
  369. }
  370. @media (min-width: 20em) {
  371. ul.with_columns {
  372. column-count: 2;
  373. }
  374. }
  375. @media (min-width: 50em) {
  376. ul.with_columns {
  377. column-count: 3;
  378. }
  379. }
  380. ul.with_two_columns {
  381. column-count: 1;
  382. }
  383. @media (min-width: 20em) {
  384. ul.with_two_columns {
  385. column-count: 1;
  386. }
  387. }
  388. @media (min-width: 50em) {
  389. ul.with_two_columns {
  390. column-count: 2;
  391. }
  392. }
  393. .gallery {
  394. display: flex;
  395. flex-wrap: wrap;
  396. justify-content: space-around;
  397. }
  398. .gallery figure img {
  399. margin-left: 1rem;
  400. margin-right: 1rem;
  401. }
  402. .gallery figure figcaption {
  403. font-family: 'ConcourseT3'
  404. }
  405. footer {
  406. font-family: 'ConcourseT3';
  407. display: flex;
  408. flex-direction: column;
  409. border-top: 3px solid white;
  410. padding: 4rem 0;
  411. background-color: #07486c;
  412. color: white;
  413. }
  414. footer > * {
  415. max-width: 50rem;
  416. margin: 0 auto;
  417. }
  418. footer a {
  419. color: #f1c40f;
  420. }
  421. footer .avatar {
  422. width: 200px;
  423. height: 200px;
  424. border-radius: 50%;
  425. float: left;
  426. -webkit-shape-outside: circle();
  427. shape-outside: circle();
  428. margin-right: 2rem;
  429. padding: 2px 5px 5px 2px;
  430. background: white;
  431. border-left: 1px solid #f1c40f;
  432. border-top: 1px solid #f1c40f;
  433. border-right: 5px solid #f1c40f;
  434. border-bottom: 5px solid #f1c40f;
  435. }
  436. </style>
  437. <h1>
  438. <span><a id="jumper" href="#jumpto" title="Un peu perdu ?">?</a></span>
  439. Bonnes pratiques en CSS : BEM et OOCSS (archive)
  440. <time>Pour la pérennité des contenus liés. Non-indexé, retrait sur simple email.</time>
  441. </h1>
  442. <section>
  443. <article>
  444. <h3><a href="http://www.alsacreations.com/article/lire/1641-bonnes-pratiques-en-css-bem-et-oocss.html">Source originale du contenu</a></h3>
  445. <p>
  446. Des années durant, j'ai intégré des sites Web et développé des applications JavaScript sans ressentir le besoin d'une méthodologie pour nommer les classes CSS. Puis, les projets grossissant, le code CSS est devenu douloureux…</p>
  447. <p>
  448. L'épineux sujet du nommage en CSS est loin d'être fermé. Depuis le début de la décennie, plusieurs auteurs majeurs ont partagé leurs recherches. Ils ont apporté un regard nouveau et sont allés à contrecourant, en rupture avec ce qui faisait jusqu'alors consensus. Je raconte dans cet article mon propre cheminement dans leurs travaux en espérant qu'il sera utile à l'intégrateur Web autant qu'au développeur JavaScript. J'ai cherché en effet une approche adaptée à la fois aux pages et aux applications Web.</p>
  449. <h3>
  450. Sommaire</h3>
  451. <ol class="tdm">
  452. <li>
  453. <a href="#oocss">OOCSS</a></li>
  454. <li>
  455. <a href="#bem">BEM</a></li>
  456. <li>
  457. <a href="#pertinence-de-bem">Pertinence de BEM</a></li>
  458. <li>
  459. <a href="#une-syntaxe-bem-jolie">Une syntaxe BEM… jolie !</a></li>
  460. <li>
  461. <a href="#ingerences-transversales-et-oocss">Ingérences transversales et… OOCSS</a></li>
  462. <li>
  463. <a href="#cas-d-utilisation-partie-1-html">Cas d'utilisation, partie 1 : HTML</a></li>
  464. <li>
  465. <a href="#cas-d-utilisation-partie-2-css-avec-sass">Cas d'utilisation, partie 2 : CSS avec SASS</a></li>
  466. <li>
  467. <a href="#conclusion">Conclusion</a></li>
  468. </ol>
  469. <h3 id="oocss">
  470. OOCSS</h3>
  471. <p>
  472. Je me suis d'abord tourné vers OOCSS (<em>Object Oriented CSS</em>), un ensemble de bonnes pratiques initiées par Nicole Sullivan (<a href="http://www.stubbornella.org/">Stubbornella</a>).</p>
  473. <p>
  474. Le concept de OOCSS est de repérer des « objets CSS », c'est-à-dire des « patterns visuels » qui se répètent, et de définir ainsi des classes réutilisables. La méthode consiste à prendre le design comme point de départ : on repère des répétitions visuelles <em>puis</em> on les nomme. La sémantique du document n'est donc plus une base de travail, et des classes CSS nommées selon l'apparence sont autorisées à partir du moment où elles sont génériques. En cela, OOCSS est en décalage avec les bonnes pratiques des années 2000. L'ancien nommage par la sémantique préconisait des classes comme <code>.last-articles-box</code> ou <code>.comment-title</code>, alors qu'en OOCSS des classes <code>.links-box</code> ou <code>.tiny-title</code> seront préférées. Cela implique que le pilotage de l'apparence se fait depuis le code HTML. Ainsi, lorsqu'une feuille de styles écrite à la manière OOCSS est bien faite, il devient possible d'ajouter des morceaux entiers dans le design sans toucher à la feuille CSS, juste en réutilisant des objets CSS déjà existants.</p>
  475. <p>
  476. OOCSS met en avant deux principes :</p>
  477. <ol>
  478. <li>
  479. Le principe de séparation de la structure et de l'apparence ;</li>
  480. <li>
  481. Le principe de séparation du conteneur et du contenu.</li>
  482. </ol>
  483. <p>
  484. Le premier principe nous fera préférer, dans les sélecteurs CSS, les classes plutôt que des identifiants ou des noms d'éléments HTML. Il incite également à factoriser les propriétés visuelles répétées. Pour l'exemple, partons du code HTML suivant :</p>
  485. <pre class="code">
  486. <code class="html">&lt;button class="<b>small-btn</b>"&gt;&lt;/button&gt;
  487. &lt;button class="<b>large-btn</b>"&gt;&lt;/button&gt;
  488. </code></pre>
  489. <p>
  490. Mieux vaut factoriser dans une classe <code>btn</code> les règles CSS communes aux deux boutons, ce qui donne :</p>
  491. <pre class="code">
  492. <code class="html">&lt;button class="<b>btn small-btn</b>"&gt;&lt;/button&gt;
  493. &lt;button class="<b>btn large-btn</b>"&gt;&lt;/button&gt;
  494. </code></pre>
  495. <p>
  496. Le second principe consiste à éviter des cascades CSS comme <code>.links-box .title</code>, car l'apparence du contenu <code>.title</code> serait alors couplée au conteneur <code>.links-box</code>. Une classe <code>.box-title</code> sera plus réutilisable.</p>
  497. <p>
  498. Je recommande au lecteur l'exposé de Nicole Sullivan : <a href="http://www.slideshare.net/stubbornella/our-best-practices-are-killing-us"><em>Our best practices are killing us</em></a>, publié en 2011. Cet exposé est formidable. Il a été, pour de nombreux intégrateurs, le point de départ d'une aventure OOCSS. Le lecteur désirant aller plus loin lira ensuite cette <a href="http://nicoespeon.com/fr/2013/05/plongee-au-coeur-de-oocss/">introduction à OOCSS</a> (en français) ainsi que le <a href="https://github.com/stubbornella/oocss/wiki">wiki du framework OOCSS</a>.</p>
  499. <p>
  500. Mais OOCSS correspondait imparfaitement à mon besoin. Les designers avec lesquels je travaille conçoivent trop de variations entre chaque bloc. OOCSS ne donne pas des règles de construction nettes et fermes, et le temps passé à factoriser l'apparence peut dépasser le gain de la réutilisation. L'approche OOCSS aide certainement à afficher des milliers d'objets dans une plateforme tentaculaire comme Facebook, car le designer lui-même raisonne alors par objets. Elle reste globalement inappropriée pour intégrer un joli design monolithique fait sur mesure pour un site plus modeste. En outre, dans le cadre de la conception d'une application JavaScript, le découpage de l'interface ne devrait pas être fait par l'apparence, les ressemblances entre composants différents étant toujours superficielles et amenées à diverger.</p>
  501. <p>
  502. Quoi qu'il en soit, les nouvelles bonnes pratiques de OOCSS font prendre conscience que l'ancien nommage par la seule sémantique est obsolète dans la mesure où il nous fait produire du code CSS non-réutilisable. Mettons OOCSS de côté pour le moment, nous y reviendrons plus tard.</p>
  503. <figure class="center">
  504. <img alt="Design des Halles de Paris, 1863, par Victor Baltard [Source: Wikipedia]" src="/xmedia/doc/full/Design_des_Halles_de_Paris,_1863,_par_Victor_Baltard_[Source:_Wikipedia].jpg"/> <figcaption>La méthode consiste à prendre le design comme point de départ… [Design des Halles de Paris, 1863, par Victor Baltard, Wikipédia]</figcaption></figure>
  505. <h3 id="bem">
  506. BEM</h3>
  507. <p>
  508. La méthodologie BEM est une solution élaborée par Yandex et publiée en 2010. BEM a deux faces : il s'agit d'abord d'une méthode déclarative de l'interface utilisateur servant à décrire un « arbre BEM », les outils <em>open source</em> de Yandex travaillent ensuite sur cet arbre. BEM apporte également une convention de nommage des classes CSS qui a gagné une certaine popularité. C'est de cette méthodologie du nommage, véritablement puissante, que nous allons parler ici.</p>
  509. <p>
  510. BEM est l'acronyme de <em>Block, Element, Modifier</em>, et toute la méthodologie du nommage à la manière BEM tient dans ces trois mots. La force du concept ? Ce qui compose un page ou une application Web peut toujours être rangé dans une arborescence de blocs, d'éléments et de modificateurs.</p>
  511. <p>
  512. Un <strong>bloc</strong> est une entité indépendante, une « brique » de l'application ou de la page Web. Un bloc forme son propre contexte autonome. Ci-dessous des exemples de blocs dans une illustration tirée du site officiel :</p>
  513. <figure class="center">
  514. <img alt="Logo, Onglets, Formulaires, etc." src="/xmedia/doc/full/bem-blocks-540.jpg"/> <figcaption>Des blocs BEM [source : <a href="http://bem.info/method/definitions/#Unified-Data-Domain">bem.info</a>]</figcaption></figure>
  515. <p>
  516. Un <strong>élément</strong> est une partie d'un bloc. Le contexte d'un élément est celui du bloc. Ci-dessous deux exemples empruntés toujours au site officiel :</p>
  517. <figure class="center">
  518. <img alt="Champ Input, Button" src="/xmedia/doc/full/bem-elements-540.jpg"/> <figcaption>Des éléments BEM [source : <a href="http://bem.info/method/definitions/#Element">bem.info</a>]</figcaption></figure>
  519. <p>
  520. En tant que « brique de construction », un bloc est réutilisable dans d'autres blocs ou dans des éléments. Il ne connait toutefois que son propre contexte et non celui du parent. Un bloc n'est donc pas livré avec les règles CSS de son propre positionnement au sein du conteneur parent. Nous éclaircirons ultérieurement, sur un cas d'utilisation, ce point important.</p>
  521. <p>
  522. Un <strong>modificateur</strong> est une propriété qui sert à créer des variantes, pour faire des modifications minimes comme changer des couleurs. Il existe des modificateurs de blocs et des modificateurs d'éléments.</p>
  523. <p>
  524. La méthodologie BEM établit ensuite trois règles essentielles :</p>
  525. <ol>
  526. <li>
  527. Les blocs et les éléments doivent chacun avoir un nom unique, lequel sera utilisé comme classe CSS ;</li>
  528. <li>
  529. Les sélecteurs CSS ne doivent pas utiliser les noms des éléments HTML (pas de <code>.menu td</code>) ;</li>
  530. <li>
  531. Les cascades dans les sélecteurs CSS devraient être évitées.</li>
  532. </ol>
  533. <p>
  534. À propos de la première règle, précisons que les identifiants HTML (les attributs <code>id</code>) ne doivent pas être utilisés en CSS, chaque bloc pouvant par principe être instancié plusieurs fois. Les identifiants HTML ne servent que d'ancres. La deuxième règle est nécessaire dans la mesure où les blocs peuvent être imbriqués. Un sélecteur <code>.menu td</code> briserait la séparation des contextes en interagissant avec les balises <code>&lt;td&gt;</code> des sous-blocs, cela doit être évité.</p>
  535. <p>
  536. Ces règles impliquent de préfixer les noms des éléments par leur contexte. Venons-en à la convention de nommage des classes CSS. Le site officiel prend soin de préciser que seuls comptent les concepts, la syntaxe restant libre. L'équipe de BEM utilise pour sa part une syntaxe à base de <em>underscores</em> :</p>
  537. <ul>
  538. <li>
  539. <code>block-name</code></li>
  540. <li>
  541. <code>block-name_modifier_name</code></li>
  542. <li>
  543. <code>block-name__element-name</code></li>
  544. <li>
  545. <code>block-name__element-name_modifier_name</code></li>
  546. </ul>
  547. <p>
  548. Pouah ! Hideux ! La raison d'une telle laideur est un manque de caractères utilisables dans les identifiants en CSS.</p>
  549. <p>
  550. Le code CSS, en méthodologie BEM, est presque plat. Voici un exemple de code CSS pour un bloc <code>search-box</code> doté d'un modificateur <code>light</code>, contenant un élément <code>btn</code> avec un modificateur <code>max_visible</code> :</p>
  551. <pre class="code">
  552. <code class="css">.<b>search-box</b> {
  553. height: 300px;
  554. width: 300px;
  555. }
  556. .search-box_<b>light</b> {
  557. background-color: #DEF;
  558. color: #777;
  559. }
  560. .search-box__<b>btn</b> {
  561. padding: 4px;
  562. }
  563. .search-box__btn_<b>max_visible</b> {
  564. font-weight: bold;
  565. }
  566. </code></pre>
  567. <p>
  568. Le code HTML :</p>
  569. <pre class="code">
  570. <code class="html">&lt;div class="<b>search-box</b> search-box_<b>light</b>"&gt;
  571. &lt;!-- (input field here) --&gt;
  572. &lt;button class="search-box__<b>btn</b> search-box__btn_<b>max_visible</b>"&gt;Search&lt;/button&gt;
  573. &lt;/div&gt;
  574. </code></pre>
  575. <p>
  576. Une cascade est utilisée lorsqu'un modificateur de bloc a un effet sur un élément :</p>
  577. <pre class="code">
  578. <code class="css">.search-box_<b>light</b> .search-box__<b>btn</b> {
  579. background-color: #9AB;
  580. }
  581. </code></pre>
  582. <p>
  583. Signalons entre parenthèses que cette cascade est à éviter sur les blocs pouvant s'imbriquer récursivement, car le modificateur du bloc parent affecterait alors les blocs enfants. Fort heureusement le cas est rare.</p>
  584. <p>
  585. Nous n'en avons pas terminé avec BEM. Dans la suite de l'article cependant nous nous écarterons de la syntaxe et même de la terminologie originale. Je suggère au lecteur intéressé par l'orthodoxie : la <a href="http://bem.info/method/definitions/">présentation officielle de la méthodologie BEM</a> dont sont tirées les deux illustrations, et un article sur <a href="http://www.smashingmagazine.com/2014/07/17/bem-methodology-for-small-projects/">l'utilisation de BEM dans de petits projets</a> (incluant la partie déclarative de l'arbre BEM).</p>
  586. <h3 id="pertinence-de-bem">
  587. Pertinence de BEM</h3>
  588. <p>
  589. BEM, c'est un peu le chic type au visage ingrat. Il a des qualités mais qu'est-ce qu'il est… LAID ! Je ne sais pas pour vous ? En ce qui me concerne, travailler sur un code qui me dégoute, ça, jamais !</p>
  590. <p>
  591. Tout de même, juger sur l'apparence n'est pas bien. Donnons-lui une chance et regardons au moins ses avantages.</p>
  592. <h4 id="la-proprete">
  593. La propreté</h4>
  594. <p>
  595. En BEM, aucun risque d'aboutir à ce code-là :</p>
  596. <pre class="code">
  597. <code class="css">.my-aside-title {
  598. font-size: 1.5em; /* Je style mon titre, OK c'est propre */
  599. }
  600. .my-aside h2.my-aside-title {
  601. position: inherit; /* Ah merde, annulation d'une règle sur h2 prévue pour autre chose */
  602. }
  603. </code></pre>
  604. <h4 id="la-performance">
  605. <img alt="Performance [source: all-free-photos.com]" class="left" src="/xmedia/doc/full/Performance_[source:_all-free-photos.com].jpg"/>La performance</h4>
  606. <p>
  607. La performance concerne plus particulièrement les applications Web. Les navigateurs rangent les classes CSS dans une table de hachage globale au document, mais il serait trop couteux de créer des sous-tables pour les descendants au niveau de chaque élément HTML. Aussi, en CSS, seul le premier niveau de sélection est performant. Les cascades CSS, lorsqu'elles sont nombreuses, engendrent des problèmes de fluidité surtout sur les pages animées des applications Web.</p>
  608. <p>
  609. BEM, en limitant drastiquement l'usage des cascades CSS, incite à élaborer des feuilles de styles performantes.</p>
  610. <h4 id="la-scalability-et-une-architecture-par-composants">
  611. La <em>scalability</em> et une architecture par composants</h4>
  612. <p>
  613. <img alt="Scalability [source: Wikipedia]" class="right" src="/xmedia/doc/full/Scalability_[source:_Wikipedia].jpg"/>Un bloc peut être placé n'importe où dans la page, ou encore apparaitre (être instancié) plusieurs fois. Cela est possible parce que ses règles CSS sont radicalement séparées de celles des autres blocs. Il est alors possible de construire des applications géantes tout en travaillant toujours à une échelle réduite : le contexte d'un bloc.</p>
  614. <p>
  615. Un parallèle est à faire avec les « composants Web », lesquels seront les briques des futures applications JavaScript. Chaque composant Web embarquera ses propres règles CSS et son propre code JavaScript. La norme prévoit un <em>shadow DOM</em>, c'est-à-dire une <em>sandbox</em> qui encapsule une portion de DOM dans le but d'empêcher les sélecteurs CSS et les identifiants HTML d'interagir par erreur avec le reste du DOM. Aujourd'hui, tout le challenge des <i>frameworks</i> JavaScript est de s'adapter à cette manière de travailler.</p>
  616. <p>
  617. Les blocs BEM correspondent bien à la philosophie du développement par composants. BEM donne pour les technologies d'aujourd'hui un format de nommage utilisable et une manière de travailler compatible avec celle de demain.</p>
  618. <h3 id="une-syntaxe-bem-jolie">
  619. Une syntaxe BEM… jolie !</h3>
  620. <p>
  621. D'un côté BEM en vaut la peine, de l'autre il n'est pas séduisant. Nous risquons le mariage de raison… Les lignes suivantes relatent une démarche qui m'a pris plusieurs mois.</p>
  622. <p>
  623. HTML 5 est venu avec une bonne et une mauvaise nouvelle. La bonne nouvelle, c'est qu'il est désormais possible d'utiliser <a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/dom.html#classes">n'importe quel caractère</a> dans les identifieurs des attributs <code>class</code> et <code>id</code>. Et la mauvaise nouvelle, c'est que… pas en CSS. Un auteur Belge a dressé <a href="http://mathiasbynens.be/notes/css-escapes#css">la liste des caractères</a> qui ont un sens spécial en CSS et qu'il faut donc échapper : <code>!</code>, <code>"</code>, <code>#</code>, <code>$</code>, <code>%</code>, <code>&amp;</code>, <code>'</code>, <code>(</code>, <code>)</code>, <code>*</code>, <code>+</code>, <code>,</code>, <code>-</code>, <code>.</code>, <code>/</code>, <code>:</code>, <code>;</code>, <code>&lt;</code>, <code>=</code>, <code>&gt;</code>, <code>?</code>, <code>@</code>, <code>[</code>, <code>\</code>, <code>]</code>, <code>^</code>, <code>`</code>, <code>{</code>, <code>|</code>, <code>}</code> et <code>~</code>. Échapper n'est pas une option, on ne va pas remplacer une laideur par une autre.</p>
  624. <p>
  625. Il reste alors deux caractères de séparation : le trait d'union (<code>-</code>) qu'une règle spéciale permet d'utiliser dans un identifieur sauf en première position, et le <i>underscore</i> (<code>_</code>).</p>
  626. <p>
  627. Aïe.</p>
  628. <p>
  629. Bloqué.</p>
  630. <p>
  631. Prenons le problème autrement. La norme en CSS dit ceci :</p>
  632. <blockquote>
  633. All CSS syntax is case-insensitive within the ASCII range […], except for parts that are not under the control of CSS. For example, the case-sensitivity of values of the HTML attributes "id" and "class" […] (<a href="http://www.w3.org/TR/CSS21/syndata.html#characters">Source : W3C</a>).</blockquote>
  634. <p>
  635. La casse est donc utilisable. À titre personnel je rechignais un peu. J'ai toujours nommé mes classes CSS en minuscules avec des traits d'union, tous les gens bien font comme cela. Mais ici il faut sacrifier quelque chose. Accepter un séparateur illisible et moche ? Plutôt mourir ! S'assoir sur la norme ? Bon j'avoue avoir envisagé un temps une utilisation irrégulière du tilde (<code>~</code>)… Mais quoi ! La casse est valide, on l'utilise ailleurs en programmation, zut alors ! À cœur vaillant rien d'impossible, tant pis pour la tradition.</p>
  636. <p>
  637. Une syntaxe BEM basée sur la casse est décrite par Nicolas Gallagher dans son mémorable article : <a href="http://nicolasgallagher.com/about-html-semantics-front-end-architecture/"><em>About HTML semantics and front-end architecture</em></a>, 2012. L'auteur en fait usage dans son framework <em>SUIT CSS</em>. La voici :</p>
  638. <ul>
  639. <li>
  640. <code>ComponentName</code></li>
  641. <li>
  642. <code>ComponentName--modifierName</code></li>
  643. <li>
  644. <code>ComponentName-descendantName</code></li>
  645. <li>
  646. <code>ComponentName.is-stateOfComponent</code></li>
  647. </ul>
  648. <p>
  649. On l'aura compris, les composants sont les blocs et les descendants sont les éléments. La terminologie de BEM est ambigüe car « bloc » en CSS est aussi un type de flux d'affichage (l'opposé de « en ligne ») et « élément » désigne une balise HTML et son contenu. Aussi, adoptons définitivement les termes « composant » et « descendant ». Un <strong>composant CSS</strong> évoque en effet avec justesse l'idée d'encapsulation du composant Web, et un <strong>descendant</strong> en est effectivement un dans l'arbre DOM. Un composant Web pouvant embarquer plusieurs composants CSS, je préfère accoler ainsi « CSS » afin de les distinguer.</p>
  650. <p>
  651. La syntaxe de l'état est intéressante : un simple point de séparation. C'est la syntaxe du sélecteur pour deux classes affectées à un même élément HTML. Ce sélecteur est devenu utilisable depuis que Windows XP et donc Internet Explorer 6 ont été abandonnés. Ci-dessous un exemple de bloc <code>MenuBtn</code> marqué avec l'état <code>current</code> en syntaxe SUIT CSS :</p>
  652. <pre class="code">
  653. <code class="html">&lt;button class="<b>MenuBtn</b> <b>is-current</b>"&gt;Open&lt;/button&gt;
  654. </code></pre>
  655. <p>
  656. En revanche, la syntaxe des modificateurs oblige encore, à l'instar de celle originale de BEM, à de lourdes répétitions. Car la classe CSS d'un modificateur est déclarée <em>en plus</em> de celle du composant CSS. Voici comment s'écrira un bouton doté des modificateurs <code>big</code> et <code>darkBlue</code> :</p>
  657. <pre class="code">
  658. <code class="html">&lt;button class="<b>MenuBtn</b> MenuBtn--<b>big</b> MenuBtn--<b>darkBlue</b>"&gt;Open&lt;/button&gt;
  659. </code></pre>
  660. <p>
  661. Pourquoi le principe de la double classe n'a-t-il pas été retenu pour les modificateurs ? L'auteur m'a répondu : « It helps keep specificity low » (« Ça aide à garder basse la spécificité »). Et c'est une réalité. La <a href="http://www.w3.org/TR/CSS21/cascade.html#specificity">spécificité CSS</a> est une mesure de la priorité d'un sélecteur CSS. Par exemple, le code suivant affichera en bleu les éléments ayant les deux classes <code>c1</code> et <code>c2</code>, car la spécificité de <code>.c1.c2</code> est plus élevée que celle de <code>.c1</code> :</p>
  662. <pre class="code">
  663. <code class="css">.c1.c2 {
  664. color: blue;
  665. }
  666. .c1 {
  667. color: red;
  668. }
  669. </code></pre>
  670. <p>
  671. Toutefois, si <code>c1</code> était le nom du composant CSS et <code>c2</code> celui d'un modificateur, alors une spécificité plus haute pour le modificateur aurait du sens. Le but d'un modificateur n'est-il pas précisément de surcharger les règles d'affichage de base du composant ?</p>
  672. <p>
  673. En outre, sur le plan de la performance, sélectionner simultanément deux classes CSS revient à faire l'intersection des résultats de deux sélections simples. La complexité, au sens algorithmique du terme, reste dans le même ordre de grandeur. Par conséquent la sélection simultanée de deux classes CSS est performante.</p>
  674. <p>
  675. Voici alors la convention de nommage que je propose à mon tour, dérivée de celle de SUIT CSS, dérivée de BEM :</p>
  676. <ul>
  677. <li>
  678. <code>ComponentName</code></li>
  679. <li>
  680. <code>ComponentName.modifierName</code></li>
  681. <li>
  682. <code>ComponentName-descendantName</code></li>
  683. <li>
  684. <code>ComponentName-descendantName.modifierName</code></li>
  685. <li>
  686. <code>ComponentName.isStateOfComponent</code></li>
  687. </ul>
  688. <p>
  689. J'ai gardé l'idée d'une convention pour un état booléen. Mais, visuellement, un état n'est rien d'autre qu'un modificateur spécial et je l'ai donc intégré dans la syntaxe des modificateurs. Voici un exemple de code HTML contenant de surcroit un descendant <code>keyword</code> :</p>
  690. <pre class="code">
  691. <code class="html">&lt;button class="<b>MenuBtn</b> <b>big</b> <b>darkBlue</b> <b>isCurrent</b>"&gt;
  692. Open the &lt;b class="MenuBtn-<b>keyword</b>"&gt;archives&lt;/b&gt;
  693. &lt;/button&gt;</code></pre>
  694. <p>
  695. Il arrive qu'une classe ne s'applique que sur certains types d'écrans. Un simple suffixe en majuscules fera ressortir cette caractéristique. Par exemple le modificateur <code>stickyMT</code> s'appliquera sur les écrans des mobiles et des tablettes seulement. Je compose pour ma part les suffixes avec <code>D</code> (<em>desktop</em>), <code>T</code> (<em>tablet</em>) et <code>M</code> (<em>mobile</em>). En cas d'écrans multiples, ordonner alphabétiquement.</p>
  696. <h3 id="ingerences-transversales-et-oocss">
  697. Ingérences transversales et… OOCSS</h3>
  698. <p>
  699. La méthodologie BEM apporte une solide séparation entre les contextes CSS des différents composants CSS. Pourtant, tous ces contextes reposent en définitive sur du sable mouvant. Les composants supposent en effet toujours un certain contexte CSS global, comme l'existence d'un <em>reset CSS</em> pour annuler les marges ou encore le fait qu'une balise <code>&lt;strong&gt;</code> affiche par défaut son contenu en gras.</p>
  700. <p>
  701. Et il est à mon avis justifié de jouer volontairement sur la corde transversale en marquant des éléments du DOM avec des classes ne respectant pas l'arborescence des composants CSS. Ainsi, un site réutilisant des patterns visuels globaux à la façon OOCSS fera dépendre de classes CSS transversales des propriétés décoratives. Dans une application JavaScript, un service du support multilingue peut travailler de manière transversale sur des libellés disposant de traductions.</p>
  702. <p>
  703. Pour ces marqueurs transversaux, je propose d'utiliser un préfixe commençant par une lettre minuscule et se terminant par le premier trait d'union : <code>myPrefix-…</code>. Par exemple un <em>objet CSS</em> aux propriétés décoratives : <code>ob-prettyBox</code> ; un libellé dont le contenu est modifiable par un service multilingue transversal : <code>lang-localizableLabel</code>. Pas de convention pour ce qui suit le préfixe : <code>lang-LocalizableLabel</code> ou encore <code>lang-localizable-label</code> au bon vouloir du concepteur.</p>
  704. <p>
  705. Nota. — Les ingérences transversales sortent de l'orthodoxie BEM.</p>
  706. <h4 id="a-propos-d-objets-css">
  707. À propos d'objets CSS</h4>
  708. <p>
  709. Parmi les objets CSS valides en OOCSS, distinguons ceux purement décoratifs de ceux qui organisent l'interface utilisateur.</p>
  710. <p>
  711. Les objets CSS décoratifs qui se limitent aux propriétés sans effet sur le flux d'affichage, comme les couleurs, les ombres, les coins arrondis, sont à consommer sans modération. Cependant, la mise en commun de propriétés décoratives peut aller plus loin. S'il apparait que la moitié des composants CSS partagent un <code>padding</code> de dix pixels et une bordure de deux pixels, pourquoi ne pas définir une classe transversale <code>ob-commonBox</code> ? Grâce aux objets CSS, nous ne sommes plus limités à un seul contexte CSS global, nous pouvons créer de multiples contextes CSS globaux. Ils seront ensuite affectés aux composants ou descendants, et joueront le rôle d'habillage par défaut.</p>
  712. <p>
  713. Quant aux objets CSS qui structurent l'interface utilisateur, à l'instar du <em><a href="http://www.stubbornella.org/content/2010/06/25/the-media-object-saves-hundreds-of-lines-of-code/">media object</a></em>, leur usage coïncide avec celui des composants CSS. Une syntaxe BEM devrait alors être préférée. Ensuite, lorsqu'il s'agit de délimiter nos composants CSS à partir d'un design, l'approche OOCSS consistant à repérer des répétitions de patterns visuels est excellente.</p>
  714. <h3 id="cas-d-utilisation-partie-1-html">
  715. Cas d'utilisation, partie 1 : HTML</h3>
  716. <p>
  717. Un peu de pratique pour fixer les idées. Nous allons élaborer un modèle de page innovant pour un blog. Admirez la beauté :</p>
  718. <figure class="center">
  719. <p>
  720. <img alt="Template de blog" src="/xmedia/doc/full/blog-template-540.jpg"/></p>
  721. </figure>
  722. <p>
  723. Quatre grands composants CSS se distinguent aisément : <code>SiteHeader</code> pour l'en-tête, <code>MainContent</code> l'article principal en blanc, <code>Sidebar</code> la barre latérale et <code>SiteFooter</code> le pied de page.</p>
  724. <p>
  725. Nous avons également besoin d'un composant <code>PageWrapper</code> pour centrer la page et lui fixer une largeur, et <code>BodyLayout</code> le conteneur de l'article principal et de la barre latérale. Intéressons-nous au code HTML de ce dernier :</p>
  726. <pre class="code">
  727. <code class="html"> &lt;div class="BodyLayout"&gt;
  728. &lt;main class="BodyLayout-<b>mainContent</b>"&gt;
  729. &lt;article class="<b>MainContent</b>"&gt;&lt;!-- Content here… --&gt;&lt;/article&gt;
  730. &lt;/main&gt;
  731. &lt;div class="BodyLayout-<b>sidebar</b> <b>Sidebar</b>"&gt;&lt;!-- Widgets here… --&gt;&lt;/div&gt;
  732. &lt;/div&gt;
  733. </code></pre>
  734. <p>
  735. En tant que composants CSS, <code>MainContent</code> et <code>Sidebar</code> ne doivent pas contenir leur propre positionnement. Aussi seront-ils positionnés par des conteneurs <code>mainContent</code> et <code>sidebar</code> (notez bien les premières lettres minuscules qui distinguent les descendants des composants). Et dans le cas de la barre latérale, le descendant <code>sidebar</code> et le composant <code>Sidebar</code> sont associés au même élément HTML. Cela est autorisé par BEM : on dit alors du nœud du DOM qu'il est un <em>mix</em> des deux.</p>
  736. <p>
  737. Les <em>mix</em> sont pratiques dans le cas d'un modèle de page Web car ils économisent des balises HTML. Ils impliquent toutefois une plus grande rigueur dans les CSS, comme nous le verrons dans la section suivante.</p>
  738. <p>
  739. Voici le code HTML complet :</p>
  740. <pre class="code">
  741. <code class="html">&lt;!DOCTYPE html&gt;
  742. &lt;html&gt;
  743. &lt;head&gt;
  744. &lt;link rel="stylesheet" href="hello-bem.css"&gt;
  745. &lt;/head&gt;
  746. &lt;body&gt;
  747. &lt;div class="<b>PageWrapper</b>"&gt;
  748. &lt;header class="<b>SiteHeader</b>"&gt;
  749. &lt;a class="SiteHeader-<b>titles</b>" href="/"&gt;
  750. &lt;p class="SiteHeader-<b>h1</b>"&gt;Hello, World!&lt;/p&gt;
  751. &lt;p class="SiteHeader-<b>h2</b>"&gt;BEM is sooo handy&lt;/p&gt;
  752. &lt;/a&gt;
  753. &lt;/header&gt;
  754. &lt;div class="<b>BodyLayout</b>"&gt;
  755. &lt;main class="BodyLayout-<b>mainContent</b>"&gt;
  756. &lt;article class="<b>MainContent</b> <b>ob-formattedText</b>"&gt;&lt;p&gt;Main content here&lt;/p&gt;&lt;/article&gt;
  757. &lt;/main&gt;
  758. &lt;div class="BodyLayout-<b>sidebar</b> <b>Sidebar</b>"&gt;
  759. &lt;ul&gt;
  760. &lt;li class="Sidebar-<b>li</b>"&gt;&lt;aside class="<b>SmallBox</b> <b>sticky</b>"&gt;Widget 1&lt;/aside&gt;&lt;/li&gt;
  761. &lt;li class="Sidebar-<b>li</b>"&gt;&lt;aside class="<b>SmallBox</b>"&gt;Widget 2&lt;/aside&gt;&lt;/li&gt;
  762. &lt;/ul&gt;
  763. &lt;/div&gt;
  764. &lt;/div&gt;
  765. &lt;footer class="<b>SiteFooter</b>"&gt;Something about copies here&lt;/footer&gt;
  766. &lt;/div&gt;
  767. </code></pre>
  768. <p>
  769. Du texte formaté pourrait apparaitre dans plusieurs composants CSS différents, il fait donc l'objet d'une classe transversale <code>ob-formattedText</code>. Notons également la présence du modificateur <code>sticky</code> sur la première des deux instances du composant <code>SmallBox</code>.</p>
  770. <h4 id="discussion-html">
  771. Discussion HTML</h4>
  772. <p>
  773. Deux sujets méritent une attention particulière. Premièrement, constatons que les arbres BEM et DOM ne coïncident pas en tout point. Nous avons des descendants frères : <code>SiteHeader-titles</code>, <code>SiteHeader-h1</code> et <code>SiteHeader-h2</code>, tous trois sont des enfants du composant <code>SiteHeader</code> alors que dans l'arbre DOM les deux derniers sont des enfants du premier. Autre exemple : le descendant <code>BodyLayout-sidebar</code> est le parent du composant <code>Sidebar</code> dans l'arbre BEM alors que dans le DOM il s'agit du même élément.</p>
  774. <p>
  775. Ensuite, certains morceaux de HTML peuvent être au choix des descendants ou des composants. Pour déterminer quel est le meilleur choix, la question n'est pas : « Cette chose-là a-t-elle du sens indépendamment du reste ? » L'en-tête d'un article, contenant le titre et la date de publication, perdrait son sens s'il était séparé du corps de l'article. Il mérite pourtant souvent d'être un composant car la question à se poser est plutôt : « A-t-on besoin de créer un contexte (d'apparence) à ce niveau ? » Et en effet, la zone de titre d'un article, surtout si elle est complexe, mérite d'être un composant. Cela permettra, par exemple, de la déplacer sous le corps de l'article le jour où ce dernier deviendra une vidéo.</p>
  776. <p>
  777. En cas d'indécision, la règle que je suggère est de faire au plus simple. Dans le code HTML au-dessus, le descendant <code>SiteHeader-titles</code> pourrait être un composant <code>SiteTitles</code>. Dans la mesure où le composant englobant <code>SiteHeader</code> est presque vide, j'ai préféré utiliser son contexte. Mais le jour où nous lui ajouterons d'autres enfants, il deviendra plus pratique de créer un contexte composant à part pour les titres du site.</p>
  778. <h3 id="cas-d-utilisation-partie-2-css-avec-sass">
  779. Cas d'utilisation, partie 2 : CSS avec SASS</h3>
  780. <p>
  781. Dans cette partie, je vais utiliser le préprocesseur SASS avec sa variante syntaxique SCSS. SASS autorise les imbrications et depuis la version 3.3 sortie cette année elles fonctionnent même sur des noms composés.</p>
  782. <p>
  783. En SASS, le <code>&amp;</code> représente le sélecteur du bloc de déclarations parent. Un exemple de code SCSS :</p>
  784. <pre class="code">
  785. <code class="css"><b>.Sidebar</b> {
  786. background-color: #998;
  787. min-height: 160px;
  788. padding: 20px 0;
  789. &amp;<b>-li</b> {
  790. margin-bottom: 10px;
  791. padding: 0 20px;
  792. }
  793. }
  794. </code></pre>
  795. <p>
  796. … Après compilation par SASS, le code CSS généré est le suivant :</p>
  797. <pre class="code">
  798. <code class="css"><b>.Sidebar</b> {
  799. background-color: #998;
  800. min-height: 160px;
  801. padding: 20px 0
  802. }
  803. <b>.Sidebar-li</b> {
  804. margin-bottom: 10px;
  805. padding: 0 20px
  806. }
  807. </code></pre>
  808. <p>
  809. Remarquez la réutilisation du sélecteur parent <code>.Sidebar</code> pour composer celui de l'enfant <code>.Sidebar-li</code>. Simple et nette.</p>
  810. <p>
  811. Reprenons maintenant notre <em>mix</em> du descendant <code>BodyLayout-sidebar</code> et du composant <code>Sidebar</code>. Surtout ne vous laissez pas embrouiller. Cette explication est réellement facile. Il n'y a à chaque fois qu'un simple pattern de deux niveaux de hiérarchie, lequel se répète de manière imbriquée.</p>
  812. <p>
  813. Dans l'arbre BEM, <code>BodyLayout-sidebar</code> est le conteneur de <code>Sidebar</code>. La règle CSS du composant <code>Sidebar</code> régit l'intérieur de la barre latérale, elle a été donnée plus haut. Le positionnement échoit au conteneur dont voici la règle :</p>
  814. <pre class="code">
  815. <code class="css">.BodyLayout-sidebar {
  816. float: right;
  817. width: 25%;
  818. }
  819. </code></pre>
  820. <p>
  821. La largeur du conteneur est définie en pourcentage : <code>25%</code>. Celle du composant <code>Sidebar</code> est indéfinie, ce dernier prendra donc automatiquement la largeur allouée par le conteneur. Rappelons-nous que, dans le cas qui nous préoccupe, le composant et son conteneur sont en réalité le même élément HTML. Or, en CSS, un <em>padding</em> à gauche ou à droite, ou encore une bordure, s'ajoute à la largeur prise par l'élément HTML. Voilà pourquoi un <em>mix</em> demande de la rigueur : ici le composant ne doit pas utiliser ces propriétés CSS sous peine d'agrandir son propre conteneur.</p>
  822. <p>
  823. Si l'on ne peut contrôler les règles CSS qui s'appliquent sur un composant, comme cela est le cas, notamment, dans une application JavaScript modulaire, alors mieux vaut éviter les <em>mix</em> en dissociant dans le DOM les descendants et les composants.</p>
  824. <p>
  825. Le contenu complet du fichier <code>hello-bem.scss</code> :</p>
  826. <pre class="code">
  827. <code class="css">@charset "UTF-8";
  828. // Reset CSS (partial)
  829. html, body, div, p, a, ul, li, footer, header, main {
  830. border: 0;
  831. font: inherit;
  832. margin: 0;
  833. padding: 0;
  834. vertical-align: baseline;
  835. }
  836. body {
  837. line-height: 1;
  838. }
  839. ul {
  840. list-style: none;
  841. }
  842. // CSS Objects
  843. <b>.ob-formattedText</b> {
  844. p {
  845. margin-bottom: .5em;
  846. }
  847. }
  848. // CSS Components
  849. body {
  850. background-color: #EEB;
  851. }
  852. <b>.PageWrapper</b> {
  853. background-color: #CCC;
  854. margin: 0 auto;
  855. width: 750px;
  856. }
  857. <b>.SiteHeader</b> {
  858. height: 120px;
  859. position: relative;
  860. &amp;<b>-titles</b> {
  861. display: inline-block;
  862. left: 80px;
  863. position: absolute;
  864. top: 20px;
  865. }
  866. &amp;<b>-h1</b> {
  867. font-size: 3em;
  868. font-weight: bold;
  869. }
  870. &amp;<b>-h2</b> {
  871. font-size: 1.5em;
  872. font-style: italic;
  873. }
  874. }
  875. <b>.BodyLayout</b> {
  876. &amp;<b>-mainContent</b> {
  877. float: left;
  878. width: 73%;
  879. }
  880. &amp;<b>-sidebar</b> {
  881. float: right;
  882. width: 25%;
  883. }
  884. &amp;::after {
  885. clear: both;
  886. content: "";
  887. display: block;
  888. }
  889. }
  890. <b>.MainContent</b> {
  891. background-color: #FFF;
  892. min-height: 160px;
  893. padding: 20px 40px;
  894. }
  895. <b>.Sidebar</b> {
  896. background-color: #998;
  897. min-height: 160px;
  898. padding: 20px 0;
  899. &amp;<b>-li</b> {
  900. margin-bottom: 10px;
  901. padding: 0 20px;
  902. }
  903. }
  904. <b>.SmallBox</b> {
  905. background-color: #665;
  906. color: #fff;
  907. line-height: 50px;
  908. text-align: center;
  909. &amp;<b>.sticky</b> {
  910. font-weight: bold;
  911. }
  912. }
  913. <b>.SiteFooter</b> {
  914. line-height: 2em;
  915. text-align: center;
  916. }
  917. </code></pre>
  918. <h4 id="discussion-css">
  919. Discussion CSS</h4>
  920. <p>
  921. Intéressons-nous tout d'abord à l'objet CSS <code>ob-formattedText</code>. Il est fait pour habiller des textes provenant d'un éditeur WYSIWYG. En situation réelle, il accueillera l'ensemble des règles d'affichage des éléments de formatage : du paragraphe aux listes à puces en passant par les sous-titres. Puisque les noms des balises HTML apparaissent directement, la séparation des contextes pour les éventuels sous-blocs est brisée. Cela n'est pas conforme à BEM mais il faut bien faire avec la réalité. Seuls des sous-blocs « résistants aux formatages » comme des médias devront être autorisés à l'intérieur de cet objet CSS.</p>
  922. <p>
  923. Un mot encore à propos du positionnement. Le principe général est que les composants CSS et leurs modificateurs ne contiennent pas leur propre positionnement dans leur conteneur. Cela implique, la plupart du temps, de ne définir aucune marge sur l'élément HTML du composant (attention à la fusion des marges) et de ne toucher à aucune propriété qui influencerait le positionnement du composant dans son conteneur.</p>
  924. <p>
  925. Ce principe n'est pas absolu. Le vrai principe sous-jacent est la réutilisation. Un positionnement est utilisé à bon escient dès qu'il fait partie intégrante du composant et ne gène donc aucunement sa réutilisation. Par exemple, le composant <code>PageWrapper</code> dans le code donné ci-dessus contient son propre positionnement centré. Ou encore, un composant <code>InlineFig</code> encapsulant une image avec sa légende, à l'intérieur d'un texte formaté, sera aligné en flottant à gauche ou à droite au moyen d'un modificateur :</p>
  926. <pre class="code">
  927. <code class="css">.InlineFig {
  928. &amp;.alignLeft {
  929. float: left;
  930. margin: 10px 10px 10px 0;
  931. }
  932. &amp;.alignRight {
  933. float: right;
  934. margin: 10px 0 10px 10px;
  935. }
  936. }
  937. </code></pre>
  938. <h3 id="conclusion">
  939. Conclusion</h3>
  940. <p>
  941. Tant que sera respecté le principe d'une séparation rigoureuse entre les contextes des composants CSS, BEM fournira une robuste armature à nos pages Web et à nos applications JavaScript. Mais le Web autorise également des manipulations transversales puissantes. Les objets CSS décoratifs et les traitements en JavaScript travaillant en dehors de toute hiérarchie tirent parti des capacités si particulières des navigateurs, ne nous en privons pas.</p>
  942. <p>
  943. Un parallèle parlera aux amateurs de langages à objets. Un composant CSS est un contexte, de la même manière qu'en programmation un objet en est un. À l'intérieur du composant, toutes sortes de descendants s'appuient sans façon les uns sur les autres, en programmation les membres privés d'un objet font de même. Vu depuis l'extérieur, le composant CSS apparait comme une « boite noire » réutilisable dont le mode d'implémentation n'importe plus. De telles considérations ont donné leur nom aux « objets CSS » de OOCSS.</p>
  944. <p>
  945. Dans le cadre d'une application JavaScript, une bonne ergonomie fera toujours correspondre la ressemblance visuelle à une similitude du comportement programmé en JavaScript. Aussi, les règles CSS doivent être couplées au code JavaScript qui anime la portion du DOM concernée. Terminons alors sur les composants Web, ces briques de construction dans le domaine applicatif et non plus seulement visuel. Ils reposent sur quatre fonctionnalités utilisables séparément :</p>
  946. <p>
  947. Le support par les navigateurs progresse vite. Les quatre sont implémentées par Chrome et Opera, Firefox est en train de suivre, l'équipe d'Internet Explorer <a href="http://status.modern.ie/">y réfléchit</a>. Safari, en revanche, <a href="http://jonrimmer.github.io/are-we-componentized-yet/#code">reste en dehors</a> de la course.</p>
  948. <p>
  949. Avec l'avènement du <em>shadow DOM</em>, certains nœuds du DOM deviennent des points de montage pour des portions de DOM encapsulées. Le DOM acquiert, en quelque sorte, du volume. La taille d'une portion de DOM ainsi encapsulée n'a pas de limite, un composant Web peut embarquer toute une application ! À l'intérieur des <em>shadow DOM</em>, de même bien entendu que dans le DOM principal du document, une syntaxe BEM reste appropriée. Du point de vue CSS, le <em>shadow DOM</em> est un espace de nommage pour les composants CSS embarqués.</p>
  950. <p>
  951. En nous offrant de raisonner « par composants » à tous les niveaux de l'habillage en CSS, la méthodologie BEM et ses syntaxes préfixées s'intègrent naturellement dans un environnement fait de composants Web. Elles sont à la fois le présent et le futur des bonnes pratiques en CSS.</p>
  952. <p class="info">
  953. <em>26/08/2014 : Mise à jour sur l'état de l'implémentation des Web components par les navigateurs.</em><br/>
  954. <em>Publication originale <a href="http://tarh.developpez.com/articles/2014/bonnes-pratiques-en-css-bem-et-oocss/">sur Developpez.com</a>.</em></p>
  955. </article>
  956. </section>
  957. <nav id="jumpto">
  958. <p>
  959. <a href="/david/blog/">Accueil du blog</a> |
  960. <a href="http://www.alsacreations.com/article/lire/1641-bonnes-pratiques-en-css-bem-et-oocss.html">Source originale</a> |
  961. <a href="/david/stream/2019/">Accueil du flux</a>
  962. </p>
  963. </nav>
  964. <footer>
  965. <div>
  966. <img src="/static/david/david-larlet-avatar.jpg" loading="lazy" class="avatar" width="200" height="200">
  967. <p>
  968. Bonjour/Hi!
  969. Je suis <a href="/david/" title="Profil public">David&nbsp;Larlet</a>, je vis actuellement à Montréal et j’alimente cet espace depuis 15 ans. <br>
  970. Si tu as apprécié cette lecture, n’hésite pas à poursuivre ton exploration. Par exemple via les <a href="/david/blog/" title="Expériences bienveillantes">réflexions bimestrielles</a>, la <a href="/david/stream/2019/" title="Pensées (dés)articulées">veille hebdomadaire</a> ou en t’abonnant au <a href="/david/log/" title="S’abonner aux publications via RSS">flux RSS</a> (<a href="/david/blog/2019/flux-rss/" title="Tiens c’est quoi un flux RSS ?">so 2005</a>).
  971. </p>
  972. <p>
  973. Je m’intéresse à la place que je peux avoir dans ce monde. En tant qu’humain, en tant que membre d’une famille et en tant qu’associé d’une coopérative. De temps en temps, je fais aussi des <a href="https://github.com/davidbgk" title="Principalement sur Github mais aussi ailleurs">trucs techniques</a>. Et encore plus rarement, <a href="/david/talks/" title="En ce moment je laisse plutôt la place aux autres">j’en parle</a>.
  974. </p>
  975. <p>
  976. Voici quelques articles choisis :
  977. <a href="/david/blog/2019/faire-equipe/" title="Accéder à l’article complet">Faire équipe</a>,
  978. <a href="/david/blog/2018/bivouac-automnal/" title="Accéder à l’article complet">Bivouac automnal</a>,
  979. <a href="/david/blog/2018/commodite-effondrement/" title="Accéder à l’article complet">Commodité et effondrement</a>,
  980. <a href="/david/blog/2017/donnees-communs/" title="Accéder à l’article complet">Des données aux communs</a>,
  981. <a href="/david/blog/2016/accompagner-enfant/" title="Accéder à l’article complet">Accompagner un enfant</a>,
  982. <a href="/david/blog/2016/senior-developer/" title="Accéder à l’article complet">Senior developer</a>,
  983. <a href="/david/blog/2016/illusion-sociale/" title="Accéder à l’article complet">L’illusion sociale</a>,
  984. <a href="/david/blog/2016/instantane-scopyleft/" title="Accéder à l’article complet">Instantané Scopyleft</a>,
  985. <a href="/david/blog/2016/enseigner-web/" title="Accéder à l’article complet">Enseigner le Web</a>,
  986. <a href="/david/blog/2016/simplicite-defaut/" title="Accéder à l’article complet">Simplicité par défaut</a>,
  987. <a href="/david/blog/2016/minimalisme-esthetique/" title="Accéder à l’article complet">Minimalisme et esthétique</a>,
  988. <a href="/david/blog/2014/un-web-omni-present/" title="Accéder à l’article complet">Un web omni-présent</a>,
  989. <a href="/david/blog/2014/manifeste-developpeur/" title="Accéder à l’article complet">Manifeste de développeur</a>,
  990. <a href="/david/blog/2013/confort-convivialite/" title="Accéder à l’article complet">Confort et convivialité</a>,
  991. <a href="/david/blog/2013/testament-numerique/" title="Accéder à l’article complet">Testament numérique</a>,
  992. et <a href="/david/blog/" title="Accéder aux archives">bien d’autres…</a>
  993. </p>
  994. <p>
  995. On peut <a href="mailto:david%40larlet.fr" title="Envoyer un courriel">échanger par courriel</a>. Si éventuellement tu souhaites que l’on travaille ensemble, tu devrais commencer par consulter le <a href="http://larlet.com">profil dédié à mon activité professionnelle</a> et/ou contacter directement <a href="http://scopyleft.fr/">scopyleft</a>, la <abbr title="Société coopérative et participative">SCOP</abbr> dont je fais partie depuis six ans. Je recommande au préalable de lire <a href="/david/blog/2018/cout-site/" title="Attention ce qui va suivre peut vous choquer">combien coûte un site</a> et pourquoi je suis plutôt favorable à une <a href="/david/pro/devis/" title="Discutons-en !">non-demande de devis</a>.
  996. </p>
  997. <p>
  998. Je ne traque pas ta navigation mais mon
  999. <abbr title="Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33.184162340">hébergeur</abbr>
  1000. conserve des logs d’accès.
  1001. </p>
  1002. </div>
  1003. </footer>
  1004. <script type="text/javascript">
  1005. ;(_ => {
  1006. const jumper = document.getElementById('jumper')
  1007. jumper.addEventListener('click', e => {
  1008. e.preventDefault()
  1009. const anchor = e.target.getAttribute('href')
  1010. const targetEl = document.getElementById(anchor.substring(1))
  1011. targetEl.scrollIntoView({behavior: 'smooth'})
  1012. })
  1013. })()
  1014. </script>