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.

index.html 35KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829
  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>Quantity Queries for CSS (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://alistapart.com/article/quantity-queries-for-css">
  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. Quantity Queries for CSS (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://alistapart.com/article/quantity-queries-for-css">Source originale du contenu</a></h3>
  445. <p>Don’t you just hate documentaries that don’t deliver? They have enticing names like <cite>In Search of the Giant Squid</cite>, and tease you with shots of murky underwater shapes and excited scientists pointing far out to sea. You settle down to watch, eyes narrowed with suspicion, thinking, “I better see some squid or I’m writing an angry letter to the network.”</p>
  446. <p>Sure enough, 90 minutes of interviews with bored-looking fishermen later, the presenter is forced to conclude, “No… no, we didn’t find any big squids. But maybe one day [majestic orchestral flourish].” Great. You wanted <cite>Finding Nemo</cite> and got <cite>Not Finding Nemo</cite> instead.</p>
  447. <p>I wouldn’t do that to you, friends. This is your guide to creating style breakpoints for quantities of HTML elements, much as you already do with <code>@media</code> queries for viewport dimensions. I’m not pointing at some blurry specification in the distance or a twinkle in an implementer’s eye. We’re going to do this <em>today</em>, with CSS that’s already available.</p>
  448. <h2>Dynamic content</h2>
  449. <p><a href="http://www.abookapart.com/products/responsive-web-design">Responsive web design</a> is primarily concerned with one variable: space. In testing responsive layouts, we take an amount of content and see which spaces it will successfully fit into. The content is deemed constant; the space, variable.</p>
  450. <p>The <code>@media</code> query is the darling of responsive web design because it allows us to insert “breakpoints” wherever one layout strategy ceases to be viable and another should succeed it. However, it’s not just viewport dimensions, but the <em>quantity</em> of content that can put pressure on space. </p>
  451. <p>Just as your end users are liable to operate devices with a multitude of different screen sizes, your content editors are liable to add and remove content. That’s what content management systems are for.  This makes Photoshop mockups of web pages doubly obsolete: they are snapshots of just one viewport, with content in just one state.</p>
  452. <p>In this article, I will be outlining a technique to make CSS <em>quantity-aware</em> using specially formed selectors. I will be applying these selectors to one classic problem in particular: how to alter the display of items in a horizontal navigation menu when there are too many to suit the initial layout mode. That is, I will demonstrate how to switch from a <code>display: table-cell</code> to a <code>display: inline-block</code> layout when the number of items in the menu becomes “more than or equal to 6.”</p>
  453. <p>I will not be relying on any JavaScript or template logic, and the menu’s list markup will remain devoid of class attribution. By using CSS only, the technique honors the <a href="http://www.w3.org/TR/html-design-principles/#separation-of-concerns">separation of concerns</a> principle, according to which content (HTML) and presentation (CSS) have clearly defined roles. Layout is CSS’s job and, where possible, CSS’s only.</p>
  454. <figure>
  455. <img src="http://alistapart.com/d/415/fig1_menu.png" alt="Comparing the initial menu bar layout for fewer than six items with the layout for six or more items"/>
  456. </figure>
  457. <p>The demonstration is <a href="http://codepen.io/heydon/pen/QwjGZp">available on CodePen</a> and will be referred to throughout the article.</p>
  458. <p>To help me illustrate this qualification of quantity, I’ll be employing diagrams of squids in the article to represent HTML elements. Green squids with ticks represent elements that match the CSS selector in question, red squids with crosses are unselected elements, and grayed-out squids denote elements that don’t exist.</p>
  459. <figure>
  460. <img src="http://alistapart.com/d/415/fig2_squid.png" alt="A key for the three squid symbols to be used in following diagrams. A green squid (for selected elements), a red squid (for unselected elements) and a grey squid for elements that don't exist"/>
  461. </figure>
  462. <h2>Counting</h2>
  463. <p>The key to determining the quantity of elements in a given context is to count them. CSS doesn’t provide an explicit “counting API,” but we can solve the same problem with an inventive combination of selectors.</p>
  464. <h3>Counting to one</h3>
  465. <p>The <code>:only-child</code> selector provides a means to style elements if they appear in isolation. Essentially, it lets us “style all the child elements of a particular element, if counting those children returns 1 as the total.” Aside from its stablemate <code>:only-of-type</code>, it is the only simple selector that can be described as quantity-based.</p>
  466. <p>In the following example, I use <code>:only-of-type</code> to add a special style to any buttons that are the <em>only</em> elements of their element type among sibling elements. I give these lone buttons an increased <code>font-size</code> because singularity suggests importance.</p>
  467. <pre><code class="language-css">
  468. button {
  469. font-size: 1.25em;
  470. }
  471. button:only-of-type {
  472. font-size: 2em;
  473. }
  474. </code></pre>
  475. <p>Here’s the crucial part. If I were to start out with one button, replete with a larger font size, and add buttons before or after it, <em>each</em> button would then adopt a smaller font size. The style of all the elements in the set is dependent on a quantity threshold of two: if there are “fewer than two” elements, the larger font size is honored. Take a look at that code again with the “fewer than two” notion in mind:</p>
  476. <pre><code class="language-css">
  477. button {
  478. font-size: 1.25em;
  479. }
  480. button:only-of-type {
  481. font-size: 2em;
  482. }
  483. </code></pre>
  484. <figure>
  485. <img src="http://alistapart.com/d/415/fig3_fewerthan.png" alt="The fewer than two logic means one selected element (green squid) becomes two unselected elements (red squids) when an element is added"/>
  486. </figure>
  487. <p>If it feels more natural, you can turn the CSS logic on its head using <a href="http://css-tricks.com/almanac/selectors/n/not/">negation</a> and make the condition “more than one.”</p>
  488. <pre><code class="language-css">
  489. /* "More than one" results in a smaller font size */
  490. button {
  491. font-size: 2em;
  492. }
  493. button:not(:only-of-type) {
  494. font-size: 1.25em;
  495. }
  496. </code></pre>
  497. <figure>
  498. <img src="http://alistapart.com/d/415/fig4_morethan.png" alt="The more than one logic means one unselected element (red squid) becomes two selected elements (green squids) when an element is added"/>
  499. </figure>
  500. <h3>Quantity <i>n</i></h3>
  501. <p>Styling elements based on the “more than one” and “fewer than two” thresholds is a neat trick, but a flexible “quantity query” interface would accept any quantity. That is, I should be able to style “more than or equal to <i>n</i>” for any value of <i>n</i>. Then I can style “more than or equal to 6” in our navigation menu.</p>
  502. <p>With a view to achieving this final goal, what if I were able to style discrete quantities like “exactly 6 in total” or “exactly 745”? How would I go about that? I would need to use a selector that allowed me to traverse sets of elements of any quantity numerically.</p>
  503. <p>Fortunately, the <a href="http://css-tricks.com/almanac/selectors/n/nth-last-child/"><code>:nth-last-child(n)</code> selector</a> accepts the number “<i>n</i>”, enabling me to count sets of elements from the <em>end</em> of the set toward the beginning. For example, <code>:nth-last-child(6)</code> matches the element that is sixth from last among sibling elements.</p>
  504. <p>Things get interesting when concatenating <code>:nth-last-child(6)</code> with <code>:first-child</code>, introducing a second condition. In this case, I am looking for any element that is <em>both</em> the sixth element from the end <em>and</em> the first element.</p>
  505. <pre><code class="language-css">
  506. li:nth-last-child(6):first-child {
  507. /* green squid styling */
  508. }
  509. </code></pre>
  510. <p>If this element exists, the set of elements <em>must be exactly six</em> in quantity. Somewhat radically, I have written CSS that tells me how many elements I am looking at.</p>
  511. <figure>
  512. <img src="http://alistapart.com/d/415/fig5_firstchild.png" alt="Of six squids, the first is green and the rest red. The first is subject to the nth-last-child(6) selector as well as the first-child selector"/>
  513. </figure>
  514. <p>All that remains is to leverage this key element to style the remaining elements in the set. For this, I employ the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/General_sibling_selectors">general sibling combinator</a>.</p>
  515. <figure>
  516. <img src="http://alistapart.com/d/415/fig6_firstchild_li.png" alt="Six green squids because the first green squid is combined with the general sibling combinator to make all the red squids that follow green"/>
  517. </figure>
  518. <p>If you’re not familiar with the general sibling combinator, the <code>~ li</code> in <code>li:nth-last-child(6):first-child ~ li</code> means “any <code>li</code> elements that occur after <code>li:nth-last-child(6):first-child</code>.” In the following example, the elements each adopt a green font color if there are <em>precisely</em> six of them in total.</p>
  519. <pre><code class="language-css">
  520. li:nth-last-child(6):first-child,
  521. li:nth-last-child(6):first-child ~ li {
  522. color: green;
  523. }
  524. </code></pre>
  525. <h2>More than or equal to 6</h2>
  526. <p>Targeting a discrete quantity—whether it’s 6, 19, or 653—is not especially useful because it pertains to such a specific situation. Using discrete widths rather than <code>min-width</code> or <code>max-width</code> in our <code>@media</code> queries would be similarly unhelpful:</p>
  527. <pre><code class="language-css">
  528. @media screen and (width: 500px) {
  529. /* styles for exactly 500px wide viewports */
  530. }
  531. </code></pre>
  532. <p>In <a href="http://alistapart.com/d/415/content-aware-navigation/">the navigation menu</a>, I really want to switch layouts at a <em>threshold</em>: a quantity watershed. I want to switch at six <em>or more</em> items—not <em>exactly</em> six items. When I reach that threshold, I would like to change from a distributed table layout to a simpler, wrappable <code>inline-block</code> configuration. Importantly, I would like to <em>retain</em> that switched configuration as the number of items further increases. </p>
  533. <p>The question is, how does one begin to construct such a selector? It’s a question of <em>offsets</em>.</p>
  534. <h3>The <i>n</i>+6 argument</h3>
  535. <p>Another arithmetical argument adoptable by the <code>:nth-child()</code> selector takes the form “<i>n</i> + [integer]”. For example, <code>:nth-child(n+6)</code> styles all the elements in a set starting from the sixth.</p>
  536. <figure>
  537. <img src="http://alistapart.com/d/415/fig7_adinfinitum.png" alt="A set of red squids that become green at the sixth squid for the remainder of the set (which can be of any size), counting upwards."/>
  538. </figure>
  539. <p>Though this has conceivable applications all its own, it’s not a “quantity-aware” selection method as such: we’re not styling anything because there are six elements or more in total; we’re just styling the ones that happen to enumerate higher than five.</p>
  540. <p>To begin solving the problem properly, what we really need is to create a set of elements that excludes the last five items. Using the opposite of <code>:nth-child(n+6)</code>—<code>:nth-last-child(n+6)</code>—I can apply the switched layout properties to all “last elements” <em>starting from the sixth</em>, counting back toward the beginning of the set.</p>
  541. <pre><code class="language-css">
  542. li:nth-last-child(n+6) {
  543. /* properties here */
  544. }
  545. </code></pre>
  546. <p>This omits the last five items from a set of any length, meaning that when you reduce the length of the set below six, you cease to see any selected items. It’s a sort of “sliding doors” effect.</p>
  547. <figure>
  548. <img src="http://alistapart.com/d/415/fig8_slidingdoors.png" alt="A set of green squids (to the left) and red squids (to the right) become a set of just red squids when the set becomes fewer than six in number"/>
  549. </figure>
  550. <p>If, indeed, the set is greater than or equal to six in total, then all that remains is to style those last five items as well. This is easy: where there are more than six items, one or more items that “return true” (in JavaScript-speak) for the <code>nth-last-child(n+6)</code> condition must exist. Any and all of these extant elements can be combined with “<code>~</code>” to affect all items (including the last five) that follow it.</p>
  551. <figure>
  552. <img src="http://alistapart.com/d/415/fig9_breakpoint.png" alt="When a set of red squids has squids added to it, the squids to the right of the set become green and can be used to make the rest of the red squids green too (with the general sibling combinator)"/>
  553. </figure>
  554. <p>The surprisingly terse solution to our problem is this:</p>
  555. <pre><code class="language-css">
  556. li:nth-last-child(n+6),
  557. li:nth-last-child(n+6) ~ li {
  558. /* properties here */
  559. }
  560. </code></pre>
  561. <p>Naturally, 6 can be replaced with any positive integer, even 653,279.</p>
  562. <h2>Fewer than or equal to <i>n</i></h2>
  563. <p>As in the earlier <code>:only-of-type</code> example, you can turn the logic on its head, switching from “more than or equal to <i>n</i>” to “fewer than or equal to <i>n</i>.” Which brand of logic you use depends on which state you consider the more natural default state. “Fewer than or equal to <i>n</i>” is possible by negating <i>n</i> and reinstating the <code>:first-child</code> condition.</p>
  564. <pre><code class="language-css">
  565. li:nth-last-child(-n+6):first-child,
  566. li:nth-last-child(-n+6):first-child ~ li {
  567. /* properties here */
  568. }
  569. </code></pre>
  570. <p>In effect, the use of “-” switches the direction of the selection: instead of pointing toward the start from the sixth, it points toward the end from the sixth. In each case, the selector is inclusive of the sixth item.</p>
  571. <h3><code>nth-child</code> versus <code>nth-of-type</code></h3>
  572. <p>Note that I am using <code>:nth-child()</code> and <code>:nth-last-child()</code> in the preceding examples, not <code>:nth-of-type()</code> and <code>:nth-last-of-type()</code>. Because I am dealing in <code>&lt;li&gt;</code> elements and <code>&lt;li&gt;</code>s are the only legitimate children of <code>&lt;ul&gt;</code>s, <code>:last-child()</code> and <code>:last-of-type()</code> would both work here.</p>
  573. <p>The <code>:nth-child()</code> and <code>:nth-of-type()</code> families of selectors have different advantages depending on what you are trying to achieve. Because <code>:nth-child()</code> is element agnostic, you could apply the described technique across different element type siblings:</p>
  574. <pre><code class="language-markup">
  575. &lt;div class="container"&gt;
  576. &lt;p&gt;...&lt;/p&gt;
  577. &lt;p&gt;...&lt;/p&gt;
  578. &lt;blockquote&gt;...&lt;/blockquote&gt;
  579. &lt;figure&gt;...&lt;/figure&gt;
  580. &lt;p&gt;...&lt;/p&gt;
  581. &lt;p&gt;...&lt;/p&gt;
  582. &lt;/div&gt;
  583. </code></pre>
  584. <pre><code class="language-css">
  585. .container &gt; :nth-last-child(n+3),
  586. .container &gt; :nth-last-child(n+3) ~ * {
  587. /* properties here */
  588. }
  589. </code></pre>
  590. <p>(Note how I am using <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Universal_selectors">the universal selector</a> to maintain element agnosticism here. <code>:last-child(n+3) ~ *</code> means “any element of any type following <code>:last-child(n+3)</code>.”)</p>
  591. <p>The advantage of <code>:nth-last-of-type()</code>, on the other hand, is that you are able to target groups of like elements where other siblings of different types are present. For example, you could target the quantity of paragraphs in the following snippet, despite them being bookended by a <code>&lt;div&gt;</code> and a <code>&lt;blockquote&gt;</code>.</p>
  592. <pre><code class="language-markup">
  593. &lt;div class="container"&gt;
  594. &lt;div&gt;...&lt;/div&gt;
  595. &lt;p&gt;...&lt;/p&gt;
  596. &lt;p&gt;...&lt;/p&gt;
  597. &lt;p&gt;...&lt;/p&gt;
  598. &lt;p&gt;...&lt;/p&gt;
  599. &lt;p&gt;...&lt;/p&gt;
  600. &lt;p&gt;...&lt;/p&gt;
  601. &lt;p&gt;...&lt;/p&gt;
  602. &lt;blockquote&gt;...&lt;/blockquote&gt;
  603. &lt;/div&gt;
  604. </code></pre>
  605. <pre><code class="language-css">
  606. p:nth-last-of-type(n+6),
  607. p:nth-last-of-type(n+6) ~ p {
  608. /* properties here */
  609. }
  610. </code></pre>
  611. <h2>Selector support</h2>
  612. <p>All of the CSS2.1 and CSS3 selectors used in this article are supported in <a href="http://caniuse.com/#feat=css-sel3">Internet Explorer 9 and above</a>, including all reasonably recent mobile/handheld stock browsers. </p>
  613. <p>Internet Explorer 8 support is good for most selector types, but technically partial, so you might want to consider a JavaScript polyfill. Alternately, you could pair the selectors for the “safer” of the layout strategies with <a href="http://nicolasgallagher.com/better-conditional-classnames-for-hack-free-css/">IE9-specific classes</a>. In the case of the navigation menu, the safer option is the one catering to more items, using <code>inline-block</code>. The declaration block would look something like this:</p>
  614. <pre><code class="language-css">
  615. nav li:nth-last-child(n+6),
  616. nav li:nth-last-child(n+6) ~ li,
  617. .lt-ie9 nav li {
  618. display: inline-block;
  619. /* etc */
  620. }
  621. </code></pre>
  622. <h2>In the real world</h2>
  623. <p>Suppose our <a href="http://alistapart.com/d/415/content-aware-navigation/">navigation menu</a> belongs to a content-managed site. Depending on who is administering the theme, it will be populated with a greater or fewer number of options. Some authors will keep things simple with just “Home” and “About” links provided, while others will cram their menu full of custom page and category options. </p>
  624. <p>By providing alternative layouts depending on the number of menu items present, we increase the elegance with which we tolerate different implementations of the theme: we address variable content as we might variable screen dimensions.</p>
  625. <figure>
  626. <img src="http://alistapart.com/d/415/fig1_menu.png" alt="Comparing the initial menu bar layout for fewer than six items with the layout for six or more items"/>
  627. </figure>
  628. <p>So, there you have it: squid ahoy! You can now add quantity as a styling condition to your repertoire.</p>
  629. <h2>Content-independent design</h2>
  630. <p>Responsive web design solves an important problem: it makes the <em>same</em> content comfortably digestible between <em>different</em> devices. For folks to receive different content just because they have different devices would be unacceptable. Similarly, it’s unacceptable for a design to dictate the nature of the content. We wouldn’t tell an editor, “Lose that, will you? It makes the design look wrong.”</p>
  631. <p>But what form the content takes, and how much of it there is at any one time, is frequently indeterminate—another unknown. And we can’t always rely on text wrapping and truncation scripts. To get a real handle on content independence, we need to develop new tools and techniques. Quantity queries are just one idea.</p>
  632. <p>Web design is about mutability, difference, uncertainty. It’s about <em>not knowing</em>. Uniquely, it is a mode of visual design not about manifesting a form, but about anticipating the different forms something <em>might</em> take. To some it is unbearably perplexing, but to you and me it is a challenge to relish. Like the elusive giant squid, it is a seriously slippery customer.</p>
  633. </article>
  634. </section>
  635. <nav id="jumpto">
  636. <p>
  637. <a href="/david/blog/">Accueil du blog</a> |
  638. <a href="http://alistapart.com/article/quantity-queries-for-css">Source originale</a> |
  639. <a href="/david/stream/2019/">Accueil du flux</a>
  640. </p>
  641. </nav>
  642. <footer>
  643. <div>
  644. <img src="/static/david/david-larlet-avatar.jpg" loading="lazy" class="avatar" width="200" height="200">
  645. <p>
  646. Bonjour/Hi!
  647. 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>
  648. 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>).
  649. </p>
  650. <p>
  651. 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>.
  652. </p>
  653. <p>
  654. Voici quelques articles choisis :
  655. <a href="/david/blog/2019/faire-equipe/" title="Accéder à l’article complet">Faire équipe</a>,
  656. <a href="/david/blog/2018/bivouac-automnal/" title="Accéder à l’article complet">Bivouac automnal</a>,
  657. <a href="/david/blog/2018/commodite-effondrement/" title="Accéder à l’article complet">Commodité et effondrement</a>,
  658. <a href="/david/blog/2017/donnees-communs/" title="Accéder à l’article complet">Des données aux communs</a>,
  659. <a href="/david/blog/2016/accompagner-enfant/" title="Accéder à l’article complet">Accompagner un enfant</a>,
  660. <a href="/david/blog/2016/senior-developer/" title="Accéder à l’article complet">Senior developer</a>,
  661. <a href="/david/blog/2016/illusion-sociale/" title="Accéder à l’article complet">L’illusion sociale</a>,
  662. <a href="/david/blog/2016/instantane-scopyleft/" title="Accéder à l’article complet">Instantané Scopyleft</a>,
  663. <a href="/david/blog/2016/enseigner-web/" title="Accéder à l’article complet">Enseigner le Web</a>,
  664. <a href="/david/blog/2016/simplicite-defaut/" title="Accéder à l’article complet">Simplicité par défaut</a>,
  665. <a href="/david/blog/2016/minimalisme-esthetique/" title="Accéder à l’article complet">Minimalisme et esthétique</a>,
  666. <a href="/david/blog/2014/un-web-omni-present/" title="Accéder à l’article complet">Un web omni-présent</a>,
  667. <a href="/david/blog/2014/manifeste-developpeur/" title="Accéder à l’article complet">Manifeste de développeur</a>,
  668. <a href="/david/blog/2013/confort-convivialite/" title="Accéder à l’article complet">Confort et convivialité</a>,
  669. <a href="/david/blog/2013/testament-numerique/" title="Accéder à l’article complet">Testament numérique</a>,
  670. et <a href="/david/blog/" title="Accéder aux archives">bien d’autres…</a>
  671. </p>
  672. <p>
  673. 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>.
  674. </p>
  675. <p>
  676. Je ne traque pas ta navigation mais mon
  677. <abbr title="Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33.184162340">hébergeur</abbr>
  678. conserve des logs d’accès.
  679. </p>
  680. </div>
  681. </footer>
  682. <script type="text/javascript">
  683. ;(_ => {
  684. const jumper = document.getElementById('jumper')
  685. jumper.addEventListener('click', e => {
  686. e.preventDefault()
  687. const anchor = e.target.getAttribute('href')
  688. const targetEl = document.getElementById(anchor.substring(1))
  689. targetEl.scrollIntoView({behavior: 'smooth'})
  690. })
  691. })()
  692. </script>