A place to cache linked articles (think custom and personal wayback machine)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

преди 5 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  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>Nesting Components (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://simurai.com/blog/2015/05/11/nesting-components/">
  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. Nesting Components (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://simurai.com/blog/2015/05/11/nesting-components/">Source originale du contenu</a></h3>
  445. <p>..or the struggles with contextual styling. — Using CSS components is somewhat straightforward. We add the markup and give it the component’s class name and all is good. Where it gets trickier is when we try to nest components. And when they need to be tweaked based on the context. Where should the styles be defined? It’s a question I’ve been asking myself a few times and what this article is trying to explore.</p>
  446. <blockquote>
  447. <p>Just to clarify before we start, with "CSS components", I mean the small building blocks that get used to assemble a website or app. Like buttons, inputs, navs, headers etc. Some also call them modules or patterns. Also I'm using the <a href="https://github.com/suitcss/suit/blob/master/doc/naming-conventions.md">SUIT</a> naming convention in the examples below, but any other convention would be fine as well. And just a heads, there isn't some awesome solution at the end that solves all the problems. It's just me whining most of the time.</p>
  448. </blockquote>
  449. <p>Ok, best is to go straight into it and look at an example. Let’s say we have a Header component where we would like to add a Button component inside.</p>
  450. <div class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;header</span> <span class="na">class=</span><span class="s">“Header”</span><span class="nt">&gt;</span>
  451. <span class="nt">&lt;button</span> <span class="na">class=</span><span class="s">“Button”</span><span class="nt">&gt;</span>Button<span class="nt">&lt;/button&gt;</span>
  452. <span class="nt">&lt;/header&gt;</span>
  453. </code></pre></div>
  454. <p>Now because the Button is inside the Header, we want to make the Button a bit smaller than it would be on its own.</p>
  455. <p><img src="/img/posts/nesting-components-1.png" alt="Button in Header"/></p>
  456. <p>Here a few approaches how to do that:</p>
  457. <h2 id="option-1---descendant-selector">Option 1 - Descendant selector</h2>
  458. <p>Maybe the most common way is to use a descendant selector to change the <code>font-size</code> whenever a Button is inside a Header.</p>
  459. <div class="highlight"><pre><code class="language-css" data-lang="css"><span class="nc">.Header</span> <span class="nc">.Button</span> <span class="p">{</span>
  460. <span class="k">font-size</span><span class="o">:</span> <span class="m">.75em</span><span class="p">;</span>
  461. <span class="p">}</span>
  462. </code></pre></div>
  463. <p>This works great but the question is, where should this rule be added? We probably split our components into separate files, so is it in <code>header.scss</code> or in <code>button.scss</code>? In other words, should the Header know about what other components might get nested or should the Button know in what environment it will get placed?</p>
  464. <p>But wait, the point of creating components is to separate them, make them modular. Each component should be kept isolated and shouldn’t know about other components. So we can make changes, rename or remove them without having to check if they might get used somewhere else.</p>
  465. <h2 id="option-2---variations">Option 2 - Variations</h2>
  466. <p>Another way is to create variations. We add a <code>.Button--small</code> class that we can use whenever we would like the button to be smaller without having to worry about ancestors.</p>
  467. <div class="highlight"><pre><code class="language-css" data-lang="css"><span class="nc">.Button--small</span> <span class="p">{</span>
  468. <span class="k">font-size</span><span class="o">:</span> <span class="m">.75em</span><span class="p">;</span>
  469. <span class="p">}</span>
  470. </code></pre></div>
  471. <div class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;header</span> <span class="na">class=</span><span class="s">“Header”</span><span class="nt">&gt;</span>
  472. <span class="nt">&lt;button</span> <span class="na">class=</span><span class="s">“Button</span> <span class="na">Button--small</span><span class="err">”</span><span class="nt">&gt;</span>Button<span class="nt">&lt;/button&gt;</span>
  473. <span class="nt">&lt;/header&gt;</span>
  474. </code></pre></div>
  475. <p>This works great too, but could get out of hand quickly. What do you do if at some point you want the <code>font-size</code> to be <code>.9em</code>? Create yet another variation? <code>Button--justALittleSmaller</code>. As the project keeps growing, the number of variations will too. We will start to loose sight where they actually get used and we’re not sure anymore if we can change a variation or if it will have <a href="http://philipwalton.com/articles/side-effects-in-css/">side effects</a> in some other place. We could create “contextual” variations like <code>Button--header</code> or <code>Button--footer</code>, but then we’re back at the beginning and could just as well use “descendant selectors”.</p>
  476. <p>Same goes for using states. <code>.Button.is-small</code> should only be used if there is a change in state and not to fit a certain context.</p>
  477. <h2 id="option-3---adopted-child">Option 3 - Adopted Child</h2>
  478. <p>I can’t remember where I read about this approach but somehow it stuck with me. I also forgot how it was called. So for now I’ll just call it “Adopted Child”.</p>
  479. <p>Let’s switch it around and look at it from the Header’s perspective. What would we do if we wouldn’t know what the components are called that might get nested? But we know that we want to make them a bit smaller. Well, we probably would create a generic <code>.Header-item</code> class and use it like this:</p>
  480. <div class="highlight"><pre><code class="language-css" data-lang="css"><span class="nc">.Header-item</span> <span class="p">{</span>
  481. <span class="k">font-size</span><span class="o">:</span> <span class="m">.75em</span><span class="p">;</span>
  482. <span class="p">}</span>
  483. </code></pre></div>
  484. <div class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;header</span> <span class="na">class=</span><span class="s">“Header”</span><span class="nt">&gt;</span>
  485. <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">“Header-item”</span><span class="nt">&gt;&lt;/div&gt;</span>
  486. <span class="nt">&lt;/header&gt;</span>
  487. </code></pre></div>
  488. <p>Ok, that gets us a bit closer. Now, it’s probably strange saying it like that when talking about CSS, but what would we do if we don’t want to create an own child, but still have one. Right, we could adopt one. In our example we adopt a Button component as our own child. We didn’t create it, but now we can tweak.. erm.. I mean “raise” it like it’s our own:</p>
  489. <div class="highlight"><pre><code class="language-scss" data-lang="scss"><span class="c1">// born in button.scss</span>
  490. <span class="nc">.Button</span> <span class="p">{</span>
  491. <span class="na">font-size</span><span class="o">:</span> <span class="mi">1</span><span class="kt">em</span><span class="p">;</span>
  492. <span class="p">}</span>
  493. <span class="c1">// raised in header.css</span>
  494. <span class="nc">.Header</span> <span class="nc">.Header-item</span> <span class="p">{</span>
  495. <span class="na">font-size</span><span class="o">:</span> <span class="mf">.75</span><span class="kt">em</span><span class="p">;</span>
  496. <span class="p">}</span>
  497. </code></pre></div>
  498. <div class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;header</span> <span class="na">class=</span><span class="s">“Header”</span><span class="nt">&gt;</span>
  499. <span class="nt">&lt;button</span> <span class="na">class=</span><span class="s">“Header-item</span> <span class="na">Button</span><span class="err">”</span><span class="nt">&gt;</span>Button<span class="nt">&lt;/button&gt;</span>
  500. <span class="nt">&lt;/header&gt;</span>
  501. </code></pre></div>
  502. <p>It is a bit uncommon that the same HTML element shares classes from two different components. And it’s not without any risks. More about them later. But I really like this approach because it keeps the components independent without having to know about each other.</p>
  503. <p>Another nice thing is that if we want to add other components to the Header that also need the same adjustments, we can reuse the same <code>Header-item</code> class, like for example on a text Input.</p>
  504. <div class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;header</span> <span class="na">class=</span><span class="s">“Header”</span><span class="nt">&gt;</span>
  505. <span class="nt">&lt;input</span> <span class="na">class=</span><span class="s">“Header-item</span> <span class="na">Input</span><span class="err">”</span><span class="nt">&gt;</span>
  506. <span class="nt">&lt;button</span> <span class="na">class=</span><span class="s">“Header-item</span> <span class="na">Button</span><span class="err">”</span><span class="nt">&gt;</span>Button<span class="nt">&lt;/button&gt;</span>
  507. <span class="nt">&lt;/header&gt;</span>
  508. </code></pre></div>
  509. <p><img src="/img/posts/nesting-components-2.png" alt="Button and Input in Header"/></p>
  510. <p>Ok, about those risks. Well, depending on what properties we wanna change, it might not always be ideal. For example, because the Button already had <code>font-size</code> defined, we had to increase specificity by using <code>.Header .Header-item</code>. But that would also override variations like <code>.Button--small</code>. That might be how we want it, but there are also situations where we’d like the variation to always be “stronger”. An example would be when changing colors. When the color of Buttons should be different inside a Header, but not when its a variation, like <code>.Button—primary</code>. Yeah, we could take a look inside button.scss or our style-guide, but remember our goal.. we actually don’t want to make decisions by looking how other components are made.</p>
  511. <p>So, as a general rule, don’t use “adopted children” for any properties that are theme related and only where you can be sure that you want to override them all the time. Like for layout/size related properties or adjusting the position.</p>
  512. <h2 id="more-options?">More options?</h2>
  513. <p>There are some more ways to do contextual styling that came to mind. I'll just mention them briefly for completeness, but think the 3 above are better suited.</p>
  514. <p><strong>Option 4</strong> - We could use a preprocessor to <strong>extend</strong> an existing component. In our example it would be a clone of the Button with some tweaks added and used as a new child component <code>.Header-button</code>. Now we only rely that the Button exists in the source, but don't have to worry about other contexts. Downside is inflating our CSS output. As well as having to remember lots of new child component classes.</p>
  515. <p><strong>Option 5</strong> - We could create a <strong>utility</strong> class like <code>.u-small</code>. It's similar to variations, but not scoped to a single component and could be used for other components as well. And for that reason it becomes very risky to ever change later.</p>
  516. <p><strong>Option 6</strong> - And of course, we could use <strong>inline styles</strong>. But I would leave that to JavaScript only.</p>
  517. <hr/>
  518. <p>So after all that, which is best? I’m afraid there isn't a clear winner. It would be nice to keep it consistent with a single approach throughout the entire project, but I guess we just have to decide on a per case basis:</p>
  519. <ol>
  520. <li><strong>Descendant selectors</strong> if we can expect that components don’t change much. Like when using a UI Kit or library.</li>
  521. <li><strong>Variations</strong> if it makes sense that a component has different versions that get reused anyways, and not just for a specific context.</li>
  522. <li><strong>Adopted Child</strong> for layout, sizing, positioning or where we are sure to always want to override a property. Also for changing multiple child components at once.</li>
  523. <li><strong>Extending</strong> when we truly want the components to be separated and don’t mind inflating the CSS output.</li>
  524. <li><strong>Utilities</strong> for very specific things, that once the class is defined, it will never change, like clearing floats.</li>
  525. <li><strong>Inline styles</strong> if it needs to be dynamically added with JavaScript.</li>
  526. </ol>
  527. <p>As said at the beginning, I haven't found a "fits all" solution and maybe the conclusion is: Try to keep contextual styling to a minimum.</p>
  528. <h2 id="updates">Updates</h2>
  529. <p>The "Adopted Child" approach is called "Mixes" in BEM. Here some <a href="https://en.bem.info/forum/issues/4/">more infos</a>.</p>
  530. <hr/>
  531. <p>SUIT also <a href="https://github.com/suitcss/suit/blob/master/doc/components.md#styling-dependencies">recommends</a> using "Adopted Child/Mixes". But also another option:</p>
  532. <p><strong>Option 7</strong> - Adding a <strong>wrapper element</strong>. It's the <code>&lt;div class="Excerpt-wrapButton"&gt;</code> in that <a href="https://github.com/suitcss/suit/blob/master/doc/components.md#styling-dependencies">example</a>. I think it works great in most cases. But for example when using Flexbox, because it has this parent/child relationship, adding an extra wrapper in between would break it. And then you might still need to set the width of the wrapped component to 100% or so. Anyways, this is a great addition. Thanks Pablo in the comments.</p>
  533. <hr/>
  534. <p><strong>Option 8</strong> - <strong>Single Purpose Classes</strong>. It's where every class has only a single property. It's somewhere between utilities (Option 5) and inline styles (Option 6). <a href="http://acss.io">Atomic CSS</a> and <a href="http://tachyons.io/">Tachyons</a> use this approach. I haven't used them on a real project, but just from looking at it, the concerns are similar to the ones from utilites. If you want to change the value in a SP class, it seems unpredictable. Because in another place (where that same class is used), you might want to keep the current value. So you would have to first check if the change has any unwanted effects somewhere else.</p>
  535. </article>
  536. </section>
  537. <nav id="jumpto">
  538. <p>
  539. <a href="/david/blog/">Accueil du blog</a> |
  540. <a href="http://simurai.com/blog/2015/05/11/nesting-components/">Source originale</a> |
  541. <a href="/david/stream/2019/">Accueil du flux</a>
  542. </p>
  543. </nav>
  544. <footer>
  545. <div>
  546. <img src="/static/david/david-larlet-avatar.jpg" loading="lazy" class="avatar" width="200" height="200">
  547. <p>
  548. Bonjour/Hi!
  549. 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>
  550. 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>).
  551. </p>
  552. <p>
  553. 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>.
  554. </p>
  555. <p>
  556. Voici quelques articles choisis :
  557. <a href="/david/blog/2019/faire-equipe/" title="Accéder à l’article complet">Faire équipe</a>,
  558. <a href="/david/blog/2018/bivouac-automnal/" title="Accéder à l’article complet">Bivouac automnal</a>,
  559. <a href="/david/blog/2018/commodite-effondrement/" title="Accéder à l’article complet">Commodité et effondrement</a>,
  560. <a href="/david/blog/2017/donnees-communs/" title="Accéder à l’article complet">Des données aux communs</a>,
  561. <a href="/david/blog/2016/accompagner-enfant/" title="Accéder à l’article complet">Accompagner un enfant</a>,
  562. <a href="/david/blog/2016/senior-developer/" title="Accéder à l’article complet">Senior developer</a>,
  563. <a href="/david/blog/2016/illusion-sociale/" title="Accéder à l’article complet">L’illusion sociale</a>,
  564. <a href="/david/blog/2016/instantane-scopyleft/" title="Accéder à l’article complet">Instantané Scopyleft</a>,
  565. <a href="/david/blog/2016/enseigner-web/" title="Accéder à l’article complet">Enseigner le Web</a>,
  566. <a href="/david/blog/2016/simplicite-defaut/" title="Accéder à l’article complet">Simplicité par défaut</a>,
  567. <a href="/david/blog/2016/minimalisme-esthetique/" title="Accéder à l’article complet">Minimalisme et esthétique</a>,
  568. <a href="/david/blog/2014/un-web-omni-present/" title="Accéder à l’article complet">Un web omni-présent</a>,
  569. <a href="/david/blog/2014/manifeste-developpeur/" title="Accéder à l’article complet">Manifeste de développeur</a>,
  570. <a href="/david/blog/2013/confort-convivialite/" title="Accéder à l’article complet">Confort et convivialité</a>,
  571. <a href="/david/blog/2013/testament-numerique/" title="Accéder à l’article complet">Testament numérique</a>,
  572. et <a href="/david/blog/" title="Accéder aux archives">bien d’autres…</a>
  573. </p>
  574. <p>
  575. 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>.
  576. </p>
  577. <p>
  578. Je ne traque pas ta navigation mais mon
  579. <abbr title="Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33.184162340">hébergeur</abbr>
  580. conserve des logs d’accès.
  581. </p>
  582. </div>
  583. </footer>
  584. <script type="text/javascript">
  585. ;(_ => {
  586. const jumper = document.getElementById('jumper')
  587. jumper.addEventListener('click', e => {
  588. e.preventDefault()
  589. const anchor = e.target.getAttribute('href')
  590. const targetEl = document.getElementById(anchor.substring(1))
  591. targetEl.scrollIntoView({behavior: 'smooth'})
  592. })
  593. })()
  594. </script>