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 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  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>Progressive enhancement is still important (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="https://jakearchibald.com/2013/progressive-enhancement-still-important/">
  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. Progressive enhancement is still important (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="https://jakearchibald.com/2013/progressive-enhancement-still-important/">Source originale du contenu</a></h3>
  445. <p>About 5 years ago it felt like the progressive enhancement battle had been won, but after watching the reactions to Nicholas Zakas' <a href="http://www.slideshare.net/nzakas/enough-withthejavascriptalready">"Enough with the JavaScript already"</a> it seems all the old arguments are back with reinforcements. Well, I'm wearing my angry-pants and I'm ready for a jog down ranty lane.</p>
  446. <h2 id="this-is-not-about-users-without-js">This is not about users without JS</h2>
  447. <p>If you turn JavaScript off, you're going to have a tough time on the web. If you remove the steering wheel from your car you're going to have a tough time going round corners. My advice: Leave the steering wheel where it is.</p>
  448. <p>Progressive enhancement has never been about users who've turned JavaScript off, or least it wasn't for me.</p>
  449. <h2 id="elevators-vs-escalators">Elevators vs escalators</h2>
  450. <p>Christian Heilmann <a href="http://christianheilmann.com/2012/02/16/stumbling-on-the-escalator/">re-purposes a Mitch Hedberg observation</a> to relate to progressive enhancement. Basically, when an elevator fails, it's useless. When an escalator fails, it becomes stairs. We should be building escalators, not elevators.</p>
  451. <p>Given the diversity of user agents, your JS failing isn't an edge case. Sometimes it'll be the browser's fault, sometimes yours. For example, a few months ago the JavaScript on the <a href="https://www.google.com/intl/en/chrome/browser/">Download Chrome</a> page failed. Y'know what happened when you clicked the "Download Chrome" button? Nothing. A dash of progressive enhancement would have allowed people to continue downloading Chrome while the problem was fixed.</p>
  452. <h2 id="reduce-your-testing-efforts-in-older-browsers">Reduce your testing efforts in older browsers</h2>
  453. <p>A couple of years ago I was working on an intranet site for a large company. I built it with progressive enhancement out of habit. The users were mostly on IE7, it was a tightly controlled environment.</p>
  454. <p>Then, at the 11th hour, the client asked for the site to work on Blackberrys, and not the shiny WebKit ones, some of them were using the old Blackberry browser. The site was a disaster on those phones.</p>
  455. <p>However, the ancient Blackberry wasn't too bad at the ol' HTML and CSS, but when it came to JS it was a random error generator.</p>
  456. <p>A little (ahem) UA-sniffing later and we weren't serving JS to the Blackberrys. This got us 90% of the way there, instantly. The rest was just minor CSS tweaks. This was only possible because the site worked without JS. Sure, there were some full-page refreshes that newer browsers did quicker with XHR, and some nice transitions were missing, but it <em>worked</em>.</p>
  457. <p>We took this idea further for <a href="http://lanyrd.com/mobile/">Lanyrd's mobile site</a> where a basic feature detect was used to decide if scripts should be loaded. We didn't use JavaScript for anything that couldn't handle ApplicationCache, which was what most of the JS was handling.</p>
  458. <p>The BBC call this basic feature test <a href="http://responsivenews.co.uk/post/18948466399/cutting-the-mustard">"Cuts the mustard"</a>. Do yourself a favour, save your JS for the cutting-edge browsers, then you only have to drag the older browsers through HTML &amp; CSS.</p>
  459. <p>It's important to do this at the script-loading stage rather than just before execution. Not only do you avoid downloading the JS in browsers that don't need it, but you save on parsing too. On bockety old mobiles, such as the Blackberry, parsing can take many orders of magnitude longer than the downloading, and the UI is blocked while this happens.</p>
  460. <h2 id="reduce-your-testing-efforts-in-general">Reduce your testing efforts in general</h2>
  461. <p>When I make something work on the server, it has to work in Django 1.5.1 running on Python 2.7.3 served through Nginx 1.1.19 etc etc etc. I control the lot, if I change one of the dependencies I can test it before deploying.</p>
  462. <p>Code running on the client is more of a testing effort due to the diversity of interpreter vendors and versions. Unless your server architecture is frequently changing, having most of your logic on the server is easier.</p>
  463. <h2 id="be-faster">Be faster</h2>
  464. <p>JavaScript is more powerful than HTML &amp; CSS, it's like a Formula 1 car whereas HTML &amp; CSS is a bicycle. However, in a race, the bicycle will get off the line first. An F1 car has a complex start-up procedure and requires a team of people to get it going. If the race is short enough, the bicycle will win.</p>
  465. <p>Here's how most pages load:</p>
  466. <ol>
  467. <li>HTML downloads</li>
  468. <li>CSS downloads</li>
  469. <li>CSS fetches additional assets</li>
  470. <li>JS downloads</li>
  471. <li>JS executes</li>
  472. <li>JS fetches additional assets</li>
  473. <li>JS updates DOM</li>
  474. </ol>
  475. <p>This is the order in which they generally start, 1-4 can happen in parallel to some degree.</p>
  476. <p>In a progressively enhanced page, the whole of step 2 blocks rendering, but other than that the HTML can be parsed and rendered in chunks as it downloads. This is assuming your scripts are async or are at the bottom of the document, which they should be (<a href="http://www.html5rocks.com/en/tutorials/speed/script-loading/">more on script loading</a>). In this case, step 6 probably isn't necessary and step 7 is minor.</p>
  477. <p>Also, most modern browsers will take a peek at pages you <em>might</em> open and scans them for things it needs, so there's a chance the browser already has a head start on downloading the CSS and JS.</p>
  478. <p>In a page that's entirely JS-driven, as in <code>&lt;body&gt;&lt;/body&gt;</code>, your first render is blocked by all 7 steps. Sure, your HTML download is tiny, but your JS isn't, and you don't get any progressive rendering. If your JS needs to download additional assets, this also blocks rendering and cannot be seen by the look-ahead scanner that can pick up CSS &amp; JS downloads.</p>
  479. <p>JS dependent pages that aren't simply <code>&lt;body&gt;&lt;/body&gt;</code> may be able to get some stuff onto the screen sooner, but the page cannot be interacted with until all 7 steps are complete.</p>
  480. <p>But yes, if the race is a bit longer, the F1 car will win. This is why you progressively enhance. You're off the line straight away on your bicycle, but as the F1 car comes past to overtake, you jump off the bike, do a jaw-dropping backflip in the air, land in the F1 cockpit, and accelerate into the distance.</p>
  481. <p>A great example of this is <a href="http://www.twitter.com">Twitter</a>, their JS-driven site was painfully slow and clunky. They're fixing this by <a href="https://blog.twitter.com/2012/improving-performance-twittercom">switching to progressive enhancement</a>.</p>
  482. <p>Fans of progressive enhancement had a "told you so" moment when <a href="http://nerds.airbnb.com/weve-launched-our-first-nodejs-app-to-product">Airbnb's improved performance</a> by delivering requested content as HTML (and sold it as a new idea).</p>
  483. <p>And don't get me started on <a href="http://blogger.com">blogger.com</a>.</p>
  484. <h2 id="its-not-doubling-up-on-work">It's not doubling up on work</h2>
  485. <p>There's a perception that progressive enhancement means building everything on the server then building it again, like for like, on the client. This is rarely the case. Lean on the server as much as possible.</p>
  486. <p>If you want to dynamically update a part of the page, that's great if it's actually faster, but do you need client-side templates? Could the server just send you the new HTML for the element? Often, yes. Remember, HTML is a semantic data format just like JSON, and if you're simply going to convert that JSON to HTML on the client, just do it on the server.</p>
  487. <p>If you need client-side templates, use a format that can be shared with the server, such as <a href="http://mustache.github.io/">Mustache</a>. Even then, consider compiling the templates to JavaScript functions on the server and serve those, saves every client having to do the parsing &amp; compilation.</p>
  488. <h2 id="work-with-the-browser-not-against-it">Work with the browser, not against it</h2>
  489. <p><a href="http://lanyrd.com/mobile/">Lanyrd's mobile site</a> is built the progressive enhancement way, but we used JavaScript to handle all page-to-page navigation (similar to using XHR to bring the new content in). This was to hack around limitations in AppCache, and it came at a cost.</p>
  490. <p>So, you click on a link, JS changes the content. At this point, the URL isn't reflecting the content. That's ok, we have <code>history.pushState()</code>. Then the user clicks the back button, we pick up the URL change and switch the content back. However, this doesn't feel natural because the user is sent back to the top of the page, whereas expectation is to restore the scroll position. Ok, so now we have to record scroll positions before we change content, and work out the conditions for when scroll position should be restored.</p>
  491. <p>The more you take over from the browser, the more complex yet expected browser behaviour you have to reimplement in JavaScript. And if different browsers do different thing, you pick one behaviour &amp; it feels odd to users who aren't used to it.</p>
  492. <h2 id="app-is-not-an-excuse">"App" is not an excuse</h2>
  493. <p>"Yeah, but I'm building a webapp, not a website" - I hear this a lot and it isn't an excuse. I challenge you to define the difference between a webapp and a website that isn't just a vague list of best practices that "apps" are for some reason allowed to disregard. <a href="http://adactio.com/journal/6246/">Jeremy Keith makes this point</a> brilliantly.</p>
  494. <p>For example, is Wikipedia an app? What about when I edit an article? What about when I search for an article?</p>
  495. <p>Whether you label your web page as a "site", "app", "microsite", whatever, it doesn't make it exempt from accessibility, performance, browser support and so on.</p>
  496. <p>If you need to excuse yourself from progressive enhancement, you need a better excuse.</p>
  497. <h2 id="there-are-of-course-exceptions">There are, of course, exceptions</h2>
  498. <p>…but they must be exceptions. <a href="http://www.spritecow.com/">SpriteCow</a>, <a href="http://jakearchibald.github.io/jank-invaders/">Jank Invaders</a> and <a href="http://jakearchibald.github.io/request-quest/">Request Quest</a> all depend on JS.</p>
  499. <p>SpriteCow could be done server-side, but it's a rare case where the client can totally outperform the server due to the transfer of image data.</p>
  500. <p>Jank Invaders is basically a demo of a JavaScript feature, and like many games simply doesn't have a sensible server-side fallback.</p>
  501. <p>Request Quest depends on JavaScript because it's… well… it has to process… yeah ok, I just got lazy. Don't do that!</p>
  502. <p>One of the best things about the web is it can rival native applications without a hefty initial download, without an install process, and do so across devices old and new. Let's keep it that way.</p>
  503. <p><strong>Edit:</strong> Emil Björklund wrote <a href="http://thatemil.com/blog/2013/07/02/progressive-enhancement-still-not-dead/">a similar post yesterday</a> that's worth reading. <strong>Another edit:</strong> Emil's server appears to be down at the moment, not even progressive enhancement can save us from that.</p>
  504. </article>
  505. </section>
  506. <nav id="jumpto">
  507. <p>
  508. <a href="/david/blog/">Accueil du blog</a> |
  509. <a href="https://jakearchibald.com/2013/progressive-enhancement-still-important/">Source originale</a> |
  510. <a href="/david/stream/2019/">Accueil du flux</a>
  511. </p>
  512. </nav>
  513. <footer>
  514. <div>
  515. <img src="/static/david/david-larlet-avatar.jpg" loading="lazy" class="avatar" width="200" height="200">
  516. <p>
  517. Bonjour/Hi!
  518. 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>
  519. 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>).
  520. </p>
  521. <p>
  522. 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>.
  523. </p>
  524. <p>
  525. Voici quelques articles choisis :
  526. <a href="/david/blog/2019/faire-equipe/" title="Accéder à l’article complet">Faire équipe</a>,
  527. <a href="/david/blog/2018/bivouac-automnal/" title="Accéder à l’article complet">Bivouac automnal</a>,
  528. <a href="/david/blog/2018/commodite-effondrement/" title="Accéder à l’article complet">Commodité et effondrement</a>,
  529. <a href="/david/blog/2017/donnees-communs/" title="Accéder à l’article complet">Des données aux communs</a>,
  530. <a href="/david/blog/2016/accompagner-enfant/" title="Accéder à l’article complet">Accompagner un enfant</a>,
  531. <a href="/david/blog/2016/senior-developer/" title="Accéder à l’article complet">Senior developer</a>,
  532. <a href="/david/blog/2016/illusion-sociale/" title="Accéder à l’article complet">L’illusion sociale</a>,
  533. <a href="/david/blog/2016/instantane-scopyleft/" title="Accéder à l’article complet">Instantané Scopyleft</a>,
  534. <a href="/david/blog/2016/enseigner-web/" title="Accéder à l’article complet">Enseigner le Web</a>,
  535. <a href="/david/blog/2016/simplicite-defaut/" title="Accéder à l’article complet">Simplicité par défaut</a>,
  536. <a href="/david/blog/2016/minimalisme-esthetique/" title="Accéder à l’article complet">Minimalisme et esthétique</a>,
  537. <a href="/david/blog/2014/un-web-omni-present/" title="Accéder à l’article complet">Un web omni-présent</a>,
  538. <a href="/david/blog/2014/manifeste-developpeur/" title="Accéder à l’article complet">Manifeste de développeur</a>,
  539. <a href="/david/blog/2013/confort-convivialite/" title="Accéder à l’article complet">Confort et convivialité</a>,
  540. <a href="/david/blog/2013/testament-numerique/" title="Accéder à l’article complet">Testament numérique</a>,
  541. et <a href="/david/blog/" title="Accéder aux archives">bien d’autres…</a>
  542. </p>
  543. <p>
  544. 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>.
  545. </p>
  546. <p>
  547. Je ne traque pas ta navigation mais mon
  548. <abbr title="Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33.184162340">hébergeur</abbr>
  549. conserve des logs d’accès.
  550. </p>
  551. </div>
  552. </footer>
  553. <script type="text/javascript">
  554. ;(_ => {
  555. const jumper = document.getElementById('jumper')
  556. jumper.addEventListener('click', e => {
  557. e.preventDefault()
  558. const anchor = e.target.getAttribute('href')
  559. const targetEl = document.getElementById(anchor.substring(1))
  560. targetEl.scrollIntoView({behavior: 'smooth'})
  561. })
  562. })()
  563. </script>