{"id":3309,"date":"2020-01-23T15:44:57","date_gmt":"2020-01-23T13:44:57","guid":{"rendered":"https:\/\/blog.tomayac.com\/2020\/01\/23\/progressive-enhancement-in-the-age-of-fugu-apis\/"},"modified":"2020-01-23T15:44:57","modified_gmt":"2020-01-23T13:44:57","slug":"progressive-enhancement-in-the-age-of-fugu-apis","status":"publish","type":"post","link":"https:\/\/tomayac.com\/wordpress\/2020\/01\/23\/progressive-enhancement-in-the-age-of-fugu-apis\/","title":{"rendered":"Progressive Enhancement In the Age of Fugu APIs"},"content":{"rendered":"\n\t\t\t<p>Back in March 2003, <a href=\"http:\/\/nickfinck.com\/\">Nick Finck<\/a> and\n<a href=\"https:\/\/twitter.com\/schampeo\">Steven Champeon<\/a> stunned the web design world\nwith the concept of\n<a href=\"http:\/\/hesketh.com\/publications\/inclusive_web_design_for_the_future\/\">progressive enhancement<\/a>:<\/p>\n<blockquote>\n<p>Rather than hoping for graceful degradation, [progressive enhancement] builds documents\nfor the least capable or differently capable devices first,\nthen moves on to enhance those documents with separate logic for presentation,\nin ways that don't place an undue burden on baseline devices\nbut which allow a richer experience for those users with modern graphical browser software.<\/p>\n<\/blockquote>\n<p>While in <em>2003<\/em>, progressive enhancement was mostly about using <em>presentational<\/em> features\nlike at the time modern CSS properties, unobtrusive JavaScript for improved usability,\nand even nowadays basic things like Scalable Vector Graphics;\nI see progressive enhancement in <em>2020<\/em> as being about using new <em>functional<\/em> browser capabilities.<\/p>\n<h2 id=\"sometimes-we-agree-to-disagree\">Sometimes we agree to disagree <a class=\"direct-link\" href=\"https:\/\/blog.tomayac.com\/2020\/01\/23\/progressive-enhancement-in-the-age-of-fugu-apis\/#sometimes-we-agree-to-disagree\">\u2693<\/a><\/h2>\n<p>Feature support for core JavaScript language features by major browsers is great.\nKangax' <a href=\"https:\/\/kangax.github.io\/compat-table\/es2016plus\/\">ECMAScript 2016+ compatibility table<\/a>\nis almost all green, and browser vendors generally agree and are quick to implement.\nIn contrast, there is less agreement on what we colloquially call <em>Fugu \ud83d\udc21<\/em> features.\nIn <a href=\"https:\/\/developers.google.com\/web\/updates\/capabilities\">Project Fugu<\/a>,\nour objective is the following:<\/p>\n<blockquote>\n<p>Enable web apps to do anything native apps can,\nby exposing the capabilities of native platforms to the web platform,\nwhile maintaining user security, privacy, trust, and other core tenets of the web.<\/p>\n<\/blockquote>\n<p>You can see all the capabilities we want to tackle in the context of the project\nby having a look at our <a href=\"https:\/\/goo.gle\/fugu-api-tracker\">Fugu API tracker<\/a>.\nI have also written about <a href=\"https:\/\/blog.tomayac.com\/2019\/09\/21\/project-fugu-at-w3c-tpac\/\">Project Fugu at W3C TPAC 2019<\/a>.<\/p>\n<p>To get an impression of the debate around these features\nwhen it comes to the different browser vendors, I recommend reading the discussions\naround the request for a\n<a href=\"https:\/\/lists.webkit.org\/pipermail\/webkit-dev\/2020-January\/031006.html\">WebKit position on Web NFC<\/a>\nor the request for a\n<a href=\"https:\/\/github.com\/mozilla\/standards-positions\/issues\/210\">Mozilla position on screen Wake Lock<\/a>\n(both discussions contain links to the particular specs in question).\nIn some cases, the result of these positioning threads might be a &quot;we agree to disagree&quot;.\nAnd that's fine.<\/p>\n<h2 id=\"progressive-enhancement-for-fugu-features\">Progressive enhancement for Fugu features <a class=\"direct-link\" href=\"https:\/\/blog.tomayac.com\/2020\/01\/23\/progressive-enhancement-in-the-age-of-fugu-apis\/#progressive-enhancement-for-fugu-features\">\u2693<\/a><\/h2>\n<p>As a result of this disagreement, some Fugu features\nwill probably never be implemented by all browser vendors.\nBut what does this mean for developers?\nNow and then, in 2003 just like in 2020,\n<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Learn\/Tools_and_testing\/Cross_browser_testing\/Feature_detection\">feature detection<\/a>\nplays a central role.\nBefore using a <em>potentially future<\/em> new browser capability like, say, the\n<a href=\"https:\/\/web.dev\/native-file-system\/\">Native File System API<\/a>,\ndevelopers need to feature-detect the presence of the API.\nFor the Native File System API, it might look like this:<\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token keyword\">if<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token string\">'chooseFileSystemEntries'<\/span> <span class=\"token keyword\">in<\/span> window<span class=\"token punctuation\">)<\/span> <span class=\"token punctuation\">{<\/span><br>  <span class=\"token comment\">\/\/ Yay, the Native File System API is available! \ud83d\udcbe<\/span><br><span class=\"token punctuation\">}<\/span> <span class=\"token keyword\">else<\/span> <span class=\"token punctuation\">{<\/span><br>  <span class=\"token comment\">\/\/ Nay, a legacy approach is required. \ud83d\ude14<\/span><br><span class=\"token punctuation\">}<\/span><\/code><\/pre>\n<p>In the worst case, there is no legacy approach (the <code>else<\/code> branch in the code snippet above).\nSome Fugu features are so groundbreakingly new that there simply is no replacement.\nThe <a href=\"https:\/\/web.dev\/contact-picker\/\">Contact Picker API<\/a> (that allows users to select contacts\nfrom their device's native contact manager) is such an example.<\/p>\n<p>But in other cases, like with the Native File System API,\ndevelopers can fall back to\n<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Element\/a#attr-download\"><code>&lt;a download&gt;<\/code><\/a>\nfor saving and\n<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/HTML\/Element\/input\/file\"><code>&lt;input type=&quot;file&quot;&gt;<\/code><\/a>\nfor opening files.\nThe experience will not be the same (while you can open a file, you cannot write back to it;\nyou will always create a new file that will land in your Downloads folder),\nbut it is the next best thing.<\/p>\n<p>A suboptimal way to deal with this situation would be to force users to load both code paths,\nthe legacy approach and the new approach.\nLuckily,\n<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Statements\/import#Dynamic_Imports\">dynamic <code>import()<\/code><\/a>\nmakes differential loading feasible and\u2014as a\n<a href=\"https:\/\/tc39.es\/process-document\/\">stage 4 of the TC39 process<\/a>\nfeature\u2014has <a href=\"https:\/\/caniuse.com\/#feat=es6-module-dynamic-import\">great browser support<\/a>.<\/p>\n<h2 id=\"experimenting-with-browser-nativefs\">Experimenting with <code>browser-nativefs<\/code> <a class=\"direct-link\" href=\"https:\/\/blog.tomayac.com\/2020\/01\/23\/progressive-enhancement-in-the-age-of-fugu-apis\/#experimenting-with-browser-nativefs\">\u2693<\/a><\/h2>\n<p>I have been exploring this pattern of progressively enhancing a web application with Fugu features.\nThe other day, I came across an interesting project by\n<a href=\"https:\/\/blog.vjeux.com\/\">Christopher Chedeau<\/a>, who also goes by\n<a href=\"https:\/\/twitter.com\/vjeux\">@Vjeux<\/a> on most places on the Internet.\nChristopher <a href=\"https:\/\/blog.vjeux.com\/2020\/uncategorized\/reflections-on-excalidraw.html\">blogged<\/a>\nabout a new app of his, <a href=\"https:\/\/excalidraw.com\/\">Excalidraw<\/a>, and how the project &quot;exploded&quot;\n(in a positive sense).\nMade curious from the blog post, I played with the app myself\nand immediately thought that it could profit from the Native File System API.\nI opened an initial <a href=\"https:\/\/github.com\/excalidraw\/excalidraw\/pull\/388\">Pull Request<\/a>\nthat was quickly merged and that implements the fallback scenario mentioned above,\nbut I was not really happy with the code duplication I had introduced.<\/p>\n<p><img src=\"https:\/\/blog.tomayac.com\/images\/excalidraw.png\" alt=\"Excalidraw web app with open &quot;file save&quot; dialog.\"><\/p>\n<p>As the logical next step, I created an experimental library\nthat supports the differential loading pattern via dynamic <code>import()<\/code>.\nIntroducing <a href=\"https:\/\/github.com\/GoogleChromeLabs\/browser-nativefs\"><code>browser-nativefs<\/code><\/a>,\nan abstraction layer that exposes two functions, <code>fileOpen()<\/code> and <code>fileSave()<\/code>,\nwhich under the hood either use the Native File System API or the <code>&lt;a download&gt;<\/code> and\n<code>&lt;input type=&quot;file&quot;&gt;<\/code> legacy approach.\nA Pull Request based on this library is now <a href=\"https:\/\/github.com\/excalidraw\/excalidraw\/pull\/510\">merged<\/a>\ninto Excalidraw, and so far it seems to work fine (only the dynamic <code>import()<\/code>\n<a href=\"https:\/\/github.com\/excalidraw\/excalidraw\/issues\/512\">breaks CodeSandbox<\/a>,\nlikely a <a href=\"https:\/\/github.com\/codesandbox\/codesandbox-client\/issues\/1774\">known issue<\/a>).\nYou can see the core API of the library below.<\/p>\n<pre class=\"language-js\"><code class=\"language-js\"><span class=\"token comment\">\/\/ The imported methods will use the Native File<\/span><br><span class=\"token comment\">\/\/ System API or a fallback implementation.<\/span><br><span class=\"token keyword\">import<\/span> <span class=\"token punctuation\">{<\/span><br>  fileOpen<span class=\"token punctuation\">,<\/span><br>  fileSave<span class=\"token punctuation\">,<\/span><br><span class=\"token punctuation\">}<\/span> <span class=\"token keyword\">from<\/span> <span class=\"token string\">'https:\/\/unpkg.com\/browser-nativefs'<\/span><span class=\"token punctuation\">;<\/span><br><br><span class=\"token punctuation\">(<\/span><span class=\"token keyword\">async<\/span> <span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span> <span class=\"token operator\">=><\/span> <span class=\"token punctuation\">{<\/span><br>  <span class=\"token comment\">\/\/ Open a file.<\/span><br>  <span class=\"token keyword\">const<\/span> blob <span class=\"token operator\">=<\/span> <span class=\"token keyword\">await<\/span> <span class=\"token function\">fileOpen<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">{<\/span><br>    mimeTypes<span class=\"token operator\">:<\/span> <span class=\"token punctuation\">[<\/span><span class=\"token string\">'image\/*'<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span><br>  <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><br>  <span class=\"token comment\">\/\/ Open multiple files.<\/span><br>  <span class=\"token keyword\">const<\/span> blobs <span class=\"token operator\">=<\/span> <span class=\"token keyword\">await<\/span> <span class=\"token function\">fileOpen<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">{<\/span><br>    mimeTypes<span class=\"token operator\">:<\/span> <span class=\"token punctuation\">[<\/span><span class=\"token string\">'image\/*'<\/span><span class=\"token punctuation\">]<\/span><span class=\"token punctuation\">,<\/span><br>    multiple<span class=\"token operator\">:<\/span> <span class=\"token boolean\">true<\/span><span class=\"token punctuation\">,<\/span><br>  <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><br>  <span class=\"token comment\">\/\/ Save a file.<\/span><br>  <span class=\"token keyword\">await<\/span> <span class=\"token function\">fileSave<\/span><span class=\"token punctuation\">(<\/span>blob<span class=\"token punctuation\">,<\/span> <span class=\"token punctuation\">{<\/span><br>    fileName<span class=\"token operator\">:<\/span> <span class=\"token string\">'Untitled.png'<\/span><span class=\"token punctuation\">,<\/span><br>  <span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><br><span class=\"token punctuation\">}<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">(<\/span><span class=\"token punctuation\">)<\/span><span class=\"token punctuation\">;<\/span><\/code><\/pre>\n<h2 id=\"polyfill-or-ponyfill-or-abstraction\">Polyfill or ponyfill or abstraction <a class=\"direct-link\" href=\"https:\/\/blog.tomayac.com\/2020\/01\/23\/progressive-enhancement-in-the-age-of-fugu-apis\/#polyfill-or-ponyfill-or-abstraction\">\u2693<\/a><\/h2>\n<p>Triggered by this project, I provided some feedback on the Native File System specification:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/WICG\/native-file-system\/issues\/146\">#146<\/a> on the API shape and the naming.<\/li>\n<li><a href=\"https:\/\/github.com\/WICG\/native-file-system\/issues\/148\">#148<\/a>\non whether a <code>File<\/code> object should have an attribute\nthat points to its associated\n<a href=\"https:\/\/wicg.github.io\/native-file-system\/#filesystemhandle\"><code>FileSystemHandle<\/code><\/a>.<\/li>\n<li><a href=\"https:\/\/github.com\/WICG\/native-file-system\/issues\/149\">#149<\/a>\non the ability to provide a name hint for a to-be-saved file.<\/li>\n<\/ul>\n<p>There are several other <a href=\"https:\/\/github.com\/WICG\/native-file-system\/issues\">open issues<\/a>\nfor the API, and its shape is not stable yet.\nSome of the API's concepts like <code>FileSystemHandle<\/code> only make sense when used with the actual API,\nbut not with a legacy fallback,\nso <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Glossary\/Polyfill\">polyfilling<\/a>\nor <a href=\"https:\/\/ponyfill.com\/\">ponyfilling<\/a> (as pointed out by my colleague\n<a href=\"https:\/\/jeffy.info\/\">Jeff Posnick<\/a>) is\u2014in my humble opinion\u2014less of an option,\nat least for the moment.<\/p>\n<p>My current thinking goes more in the direction of positioning this library as an abstraction\nlike jQuery's <a href=\"https:\/\/api.jquery.com\/jquery.ajax\/\"><code>$.ajax()<\/code><\/a> or\nAxios' <a href=\"https:\/\/api.jquery.com\/jquery.ajax\/\"><code>axios.get()<\/code><\/a>,\nwhich a significant amount of developers still prefer even over newer APIs like <code>fetch()<\/code>.\nIn a similar vein, Node.js offers a function\n<a href=\"https:\/\/nodejs.org\/api\/fs.html#fs_fspromises_readfile_path_options\"><code>fsPromises.readFile()<\/code><\/a>\nthat\u2014apart from a <a href=\"https:\/\/nodejs.org\/api\/fs.html#fs_class_filehandle\"><code>FileHandle<\/code><\/a>\u2014also\njust takes a filename <code>path<\/code> string, that is, it acts as an optional shortcut to\n<a href=\"https:\/\/nodejs.org\/api\/fs.html#fs_fspromises_open_path_flags_mode\"><code>fsPromises.open()<\/code><\/a>,\nwhich returns a <a href=\"https:\/\/nodejs.org\/api\/fs.html#fs_class_filehandle\"><code>FileHandle<\/code><\/a>\nthat one can then use with\n<a href=\"https:\/\/nodejs.org\/api\/fs.html#fs_filehandle_readfile_options\">filehandle.readFile()<\/a>\nthat finally returns a <code>Buffer<\/code> or a <code>string<\/code>, just like <code>fsPromises.readFile()<\/code>.<\/p>\n<p>Thus, should the Native File System API then just have a <code>window.readFile()<\/code> method? Maybe.\nBut more recently the trend seems to be to rather expose generic tools like\n<a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/AbortController\"><code>AbortController<\/code><\/a>\nthat can be used to cancel many things, including\n<a href=\"https:\/\/github.com\/mdn\/dom-examples\/blob\/2f15930c36a4eeb31eb6d9862c277f2dc9a829b2\/abort-api\/index.html#L72\"><code>fetch()<\/code><\/a>\nrather than more specific mechanisms.\nWhen the lower-level primitives are there, developers can build abstractions on top,\nand optionally never expose the primitives, just like the <code>fileOpen()<\/code> and <code>fileSave()<\/code> methods\nin <code>browser-nativefs<\/code> that one <em>can<\/em> (but never has to) perfectly use\nwithout ever touching a <code>FileSystemHandle<\/code>.<\/p>\n<h2 id=\"conclusion\">Conclusion <a class=\"direct-link\" href=\"https:\/\/blog.tomayac.com\/2020\/01\/23\/progressive-enhancement-in-the-age-of-fugu-apis\/#conclusion\">\u2693<\/a><\/h2>\n<p>Progressive enhancement in the age of Fugu APIs in my opinion is more alive than ever.\nI have shown the concept at the example of the Native File System API,\nbut there are several other new API proposals where this idea (which by no means I claim as new)\ncould be applied.\nFor instance, the <a href=\"https:\/\/web.dev\/shape-detection\/\">Shape Detection API<\/a>\ncan fall back to JavaScript or Web Assembly libraries, as shown in the\n<a href=\"https:\/\/github.com\/GoogleChromeLabs\/perception-toolkit\/#overview\">Perception Toolkit<\/a>.\nAnother example is the (screen) <a href=\"https:\/\/web.dev\/wakelock\/\">Wake Lock API<\/a>\nthat can fall back to playing an invisible video,\nwhich is the way <a href=\"https:\/\/github.com\/richtr\/NoSleep.js\/\">NoSleep.js<\/a> implements it.\nAs I wrote above, the experience probably will not be the same,\nbut the next best thing.\nIf you want, give <a href=\"https:\/\/github.com\/GoogleChromeLabs\/browser-nativefs\"><code>browser-nativefs<\/code><\/a> a try.<\/p>\n\n\t\t\t<p>\n\t\t\t\t<img alt=\"Thomas Steiner\" width=\"32\" height=\"32\" src=\"https:\/\/blog.tomayac.com\/feed.php?dl=https%3A%2F%2Fblog.tomayac.com%2F2020%2F01%2F23%2Fprogressive-enhancement-in-the-age-of-fugu-apis%2F&dp=%2F2020%2F01%2F23%2Fprogressive-enhancement-in-the-age-of-fugu-apis%2F&dt=Progressive%20Enhancement%20In%20the%20Age%20of%20Fugu%20APIs\" alt=\"\">\n\t\t\t\t<br\/>This post appeared first on <a href=\"https:\/\/blog.tomayac.com\/2020\/01\/23\/progressive-enhancement-in-the-age-of-fugu-apis\/\">https:\/\/blog.tomayac.com\/2020\/01\/23\/progressive-enhancement-in-the-age-of-fugu-apis\/<\/a>.\n\t\t\t<\/p>\n\t\t","protected":false},"excerpt":{"rendered":"<p>\t\t\tBack in March 2003, Nick Finck and<br \/>\nSteven Champeon stunned the web design world<br \/>\nwith the concept of<br \/>\nprogressive enhancement:<\/p>\n<p>Rather than hoping for graceful degradation, [progressive enhancement] builds documents<br \/>\nfor the least capable or different&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"intlwemo_wallet_address":"","footnotes":""},"categories":[],"tags":[],"class_list":["post-3309","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/posts\/3309","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/comments?post=3309"}],"version-history":[{"count":4,"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/posts\/3309\/revisions"}],"predecessor-version":[{"id":3310,"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/posts\/3309\/revisions\/3310"}],"wp:attachment":[{"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/media?parent=3309"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/categories?post=3309"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tomayac.com\/wordpress\/wp-json\/wp\/v2\/tags?post=3309"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}