123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296 |
- <!doctype html><!-- This is a valid HTML5 document. -->
- <!-- Screen readers, SEO, extensions and so on. -->
- <html lang="fr">
- <!-- Has to be within the first 1024 bytes, hence before the `title` element
- See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
- <meta charset="utf-8">
- <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
- <!-- The viewport meta is quite crowded and we are responsible for that.
- See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
- <meta name="viewport" content="width=device-width,initial-scale=1">
- <!-- Required to make a valid HTML5 document. -->
- <title>The #ViewSource Affordance (archive) — David Larlet</title>
- <meta name="description" content="Publication mise en cache pour en conserver une trace.">
- <!-- That good ol' feed, subscribe :). -->
- <link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
- <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
- <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
- <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
- <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
- <link rel="manifest" href="/static/david/icons2/site.webmanifest">
- <link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
- <link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
- <meta name="msapplication-TileColor" content="#f7f7f7">
- <meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
- <meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
- <meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
- <!-- Is that even respected? Retrospectively? What a shAItshow…
- https://neil-clarke.com/block-the-bots-that-feed-ai-models-by-scraping-your-website/ -->
- <meta name="robots" content="noai, noimageai">
- <!-- Documented, feel free to shoot an email. -->
- <link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
- <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
- <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
- <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
- <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
- <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
- <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
- <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
- <script>
- function toggleTheme(themeName) {
- document.documentElement.classList.toggle(
- 'forced-dark',
- themeName === 'dark'
- )
- document.documentElement.classList.toggle(
- 'forced-light',
- themeName === 'light'
- )
- }
- const selectedTheme = localStorage.getItem('theme')
- if (selectedTheme !== 'undefined') {
- toggleTheme(selectedTheme)
- }
- </script>
-
- <meta name="robots" content="noindex, nofollow">
- <meta content="origin-when-cross-origin" name="referrer">
- <!-- Canonical URL for SEO purposes -->
- <link rel="canonical" href="https://htmx.org/essays/right-click-view-source/">
-
- <body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">
-
-
- <article>
- <header>
- <h1>The #ViewSource Affordance</h1>
- </header>
- <nav>
- <p class="center">
- <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
- <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
- </svg> Accueil</a> •
- <a href="https://htmx.org/essays/right-click-view-source/" title="Lien vers le contenu original">Source originale</a>
- </p>
- </nav>
- <hr>
- <blockquote>
- <p>Not for nothing, Hypercard presaged the web’s critical “#ViewSource” affordance, which allowed people to copy,
- modify, customize and improve on the things that they found delightful or useful. This affordance was later adapted
- by other human-centered projects like #Scratch, and is a powerful tonic against #enshittification.</p>
- <p>--<a rel="noopener" target="_blank" href="https://twitter.com/doctorow/status/1701934612686196872">Cory Doctorow @pluralistic@mamot.fr</a></p>
- </blockquote>
- <h2 id="open-culture-the-web"><a class="zola-anchor" href="#open-culture-the-web" aria-label="Anchor link for: open-culture-the-web">#</a>Open Culture & The Web</h2>
- <p>When people talk about open source software, that conversation is often dominated by
- <a rel="noopener" target="_blank" href="https://www.gnu.org/philosophy/free-sw.html">the Free Software Foundation’s notion of free software</a>:</p>
- <blockquote>
- <p>“Free software” means software that respects users’ freedom and community. Roughly, it means that the users have the
- freedom to run, copy, distribute, study, change and improve the software.“</p>
- </blockquote>
- <p>This definition of free software has been a useful one and, through advocating for it, the FSF has gifted the world a
- lot of wonderful open source software.</p>
- <p>Web applications, however, have always been an uncomfortable fit for this definition of free. This is mainly
- for technical reasons: web applications involve a web browser interacting with a web server that is, typically, running
- on a remote system.</p>
- <p>At a fundamental level, the REST-ful architecture of the web was built around <em>hypermedia representations</em> of remote
- resources: browsers deal only with hypermedia representations provided by the server and, thus, have no visibility into
- the actual source of the code executing on the server side.</p>
- <p>Now, the web has certainly <em>leveraged</em> free and open source software in its growth: browsers are typically (at least mostly)
- open source, server software is often open source, and so on. And there are, of course, open source web applications
- that users may run for things like forums and so forth.</p>
- <p>However, from the standpoint of typical web application users, web applications are not free in the FSF sense of that
- term: the users are unable to see and modify the source of the server code that is being executed as they interact with
- the application via the browser.</p>
- <h3 id="right-click-view-source-as-culture"><a class="zola-anchor" href="#right-click-view-source-as-culture" aria-label="Anchor link for: right-click-view-source-as-culture">#</a>Right-Click-View-Source As Culture</h3>
- <p>Despite the fact that the web has a somewhat uncomfortable relationship with the notion of free software, the early web
- none-the-less had a radically open <em>developer culture</em>. </p>
- <p>In fact, in some important and practical ways, the early web had a <em>more</em> open developer culture than what was achieved
- by the free software movement.</p>
- <p>The <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/View-source_URI_scheme">#ViewSource</a> affordance available in browsers allowed people
- to understand and “own”, at least in an informal way, the web in a way that even most FSF-conforming applications could
- not: you had direct access to the “source”, or at least <em>part</em> of the source, of the application available from
- <em>within</em> the application itself.</p>
- <p>You could copy-and-paste (or save) the “source” (HTML, JavaScript & CSS) and start modifying it, without a complicated
- build tool chain or, indeed, without any tool chain at all.</p>
- <p>This radical openness of the web allowed many people, often not formally trained computer programmers, to learn how to
- create web pages and applications in an ad hoc and informal way.</p>
- <p>In strict free software terms, this was, of course, a compromise: as a user of a web application, you had no visibility
- into how a server was constructing a given hypermedia response.</p>
- <p>But you could see <em>what</em> the server was responding with: you could download and tweak it, poke and prod at it. You could,
- if you were an advanced user, use browser tools to modify the application in place.</p>
- <p>And, most importantly, you could <em>learn from it</em>, even if you couldn’t see how the HTML was being produced.</p>
- <p>This radical openness of the client and network protocol, and the culture it produced, was a big part of the success
- of the early web.</p>
- <h2 id="digital-enclosure-vs-technical-enclosure"><a class="zola-anchor" href="#digital-enclosure-vs-technical-enclosure" aria-label="Anchor link for: digital-enclosure-vs-technical-enclosure">#</a>Digital Enclosure vs. Technical Enclosure</h2>
- <p>The <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/Enclosure">Enclosure Movement</a> was a period in English history when what were
- previously <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/Commons">commons</a> were privatized.</p>
- <p>This was a traumatic event in English history, as evidenced by this poem by an 18th century anon:</p>
- <blockquote>
- <p>The law locks up the man or woman</p>
- <p>Who steals the goose from off the common,</p>
- <p>But lets the greater felon loose</p>
- <p>Who steals the common from the goose.</p>
- <p>–18th century anon</p>
- </blockquote>
- <p>In the last decade, the web has gone through a period of “Digital Enclosure”, where <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/Closed_platform">“Walled Gardens”</a>,
- such as Facebook & Twitter, have replaced the earlier, more open and more chaotic blogs and internet forums.</p>
- <h3 id="technical-enclosure"><a class="zola-anchor" href="#technical-enclosure" aria-label="Anchor link for: technical-enclosure">#</a>Technical Enclosure</h3>
- <p>Many (most?) web developers have decried this trend.</p>
- <p>However, despite recognizing the danger of an increasingly closed internet, many web developers don’t consider their own
- technical decisions and how those decisions can also contribute to the disappearance of web’s <em>culture</em> of openness.</p>
- <p>Inadvertently (for the most part) technical trends and decisions in web development in the last two decades have lead
- to what we term a “Technical Enclosure” of the web, a processes whereby technical decisions chip away at the #ViewSource
- affordance that Cory Doctrow discusses in the opening quote of this article, an affordance that existed as a commons
- for early web developers.</p>
- <p>To see a stark example of the decline of the <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/View-source_URI_scheme">#ViewSource</a> affordance
- in web development and Technical Enclosure in action, we can look at what is perhaps the most popular web page on the
- internet, <a rel="noopener" target="_blank" href="https://google.com">The Google Homepage</a>.</p>
- <p>Here is the nearly complete source of that page from the year 2000, taken from
- <a rel="noopener" target="_blank" href="http://web.archive.org/web/20000229040250/http://www.google.com/">the wayback machine</a>:</p>
- <h3 id="google-in-2000"><a class="zola-anchor" href="#google-in-2000" aria-label="Anchor link for: google-in-2000">#</a>Google in 2000</h3>
- <p><img src="https://htmx.org/img/google-2000.png" alt="Google Source Code in 2000"></p>
- <p>In contrast, here is a random snapshot of roughly 1/100th of the current source code for the website:</p>
- <h3 id="google-in-2023"><a class="zola-anchor" href="#google-in-2023" aria-label="Anchor link for: google-in-2023">#</a>Google in 2023</h3>
- <p><img src="https://htmx.org/img/google-2023.png" alt="Google Source Code in 2023"></p>
- <p>These two screenshots dramatically demonstrate the decline in the effectiveness of the <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/View-source_URI_scheme">#ViewSource</a> affordance over time:
- yes, you can still right-click the page and view its underlying source, but making sense of the latter code would be
- challenging for even the most seasoned web developer.</p>
- <p>A new web developer would have almost no chance of deriving any value from doing so.</p>
- <p>Now, this is not to criticize the google engineer’s technical decisions that lead to this situation <em>as technical
- decisions</em>: obviously, despite similar appearances, the google homepage of 2023 is far more sophisticated than the one
- available in 2000.</p>
- <p>The 2023 google homepage is going to be a lot more complicated than the 2000 page and, given the zeitgeist, it is going to
- involve a lot of JavaScript.</p>
- <p>However, this is to point out that something deeply important about the early web has been lost, almost certainly
- unintentionally, along the way: the ability to view the source of the page, make sense of what it is doing and, most
- importantly, to learn from it.</p>
- <h2 id="right-click-view-source-extremism"><a class="zola-anchor" href="#right-click-view-source-extremism" aria-label="Anchor link for: right-click-view-source-extremism">#</a>Right-Click-View-Source Extremism</h2>
- <p>Both <a href="https://htmx.org/">htmx</a> and <a rel="noopener" target="_blank" href="https://hyperscript.org">hyperscript</a> adhere to the <a href="https://htmx.org/essays/locality-of-behaviour/">Locality of Behavior</a>
- design principle.</p>
- <p>This principle states that:</p>
- <blockquote>
- <p>The behaviour of a unit of code should be as obvious as possible by looking only at that unit of code</p>
- </blockquote>
- <p>The main technical advantage of Locality of Behavior is ease of maintenance, as outlined in the essay above.</p>
- <p>However, there is an important cultural benefit to the Locality of Behavior of htmx and hyperscript as well: <strong>it restores
- the power of the <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/View-source_URI_scheme">#ViewSource</a> affordance on the web</strong>.</p>
- <p>Consider <a rel="noopener" target="_blank" href="https://arhamjain.com/hyperwordle/">Hyperwordle</a>, a hyperscript-based clone of the popular
- <a rel="noopener" target="_blank" href="https://www.nytimes.com/games/wordle/index.html">Wordle</a> game, now owned by the New York Times.</p>
- <p>You can visit Hyperwordle, right click and view the source of it, and you will be presented with some HTML and hyperscript,
- all of which is, with a bit of effort, understandable.</p>
- <p>The <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/View-source_URI_scheme">#ViewSource</a> affordance is effective in this case.</p>
- <p>Contrast this with the view-source experience of the Wordle implementation at the New York Times.</p>
- <p>Now, this is of course a bit unfair: the NYTimes version has a lot more functionality and is heavily optimized. Hyperwordle
- is a proof of concept and not being hammered by millions of users every day.</p>
- <p>Despite that qualification, Hyperwordle demonstrates a potential future for the web, a future where a culture of openness,
- of <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/View-source_URI_scheme">#ViewSource</a> politeness, is once again a touchstone of the
- culture of the web.</p>
-
- <p>Engineers who care about the open culture of the web should recognize that the threats to that culture come not only from
- Digital Enclosure by large, private companies of the most important pieces of the web.</p>
- <p>They should also recognize the risks of Technical Enclosure, and the <em>non-technical</em> value of the
- <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/View-source_URI_scheme">#ViewSource</a> affordance in perpetuating the open culture of
- web development. They should start thinking about making this affordance a priority in their technical decisions. As
- with all priorities, this may involve trading off against other technical and even functional priorities during
- application development.</p>
- <p>But if we don’t stand up for <a rel="noopener" target="_blank" href="https://en.wikipedia.org/wiki/View-source_URI_scheme">#ViewSource</a>, no one else will.</p>
-
- <p><img src="https://htmx.org/img/memes/viewsource.png" alt="Right Click View Source Guy"></p>
- </article>
-
-
- <hr>
-
- <footer>
- <p>
- <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
- <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
- </svg> Accueil</a> •
- <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
- <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
- </svg> Suivre</a> •
- <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
- <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
- </svg> Pro</a> •
- <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
- <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
- </svg> Email</a> •
- <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
- <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
- </svg> Légal</abbr>
- </p>
- <template id="theme-selector">
- <form>
- <fieldset>
- <legend><svg class="icon icon-brightness-contrast">
- <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
- </svg> Thème</legend>
- <label>
- <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
- </label>
- <label>
- <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
- </label>
- <label>
- <input type="radio" value="light" name="chosen-color-scheme"> Clair
- </label>
- </fieldset>
- </form>
- </template>
- </footer>
- <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
- <script>
- function loadThemeForm(templateName) {
- const themeSelectorTemplate = document.querySelector(templateName)
- const form = themeSelectorTemplate.content.firstElementChild
- themeSelectorTemplate.replaceWith(form)
-
- form.addEventListener('change', (e) => {
- const chosenColorScheme = e.target.value
- localStorage.setItem('theme', chosenColorScheme)
- toggleTheme(chosenColorScheme)
- })
-
- const selectedTheme = localStorage.getItem('theme')
- if (selectedTheme && selectedTheme !== 'undefined') {
- form.querySelector(`[value="${selectedTheme}"]`).checked = true
- }
- }
-
- const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
- window.addEventListener('load', () => {
- let hasDarkRules = false
- for (const styleSheet of Array.from(document.styleSheets)) {
- let mediaRules = []
- for (const cssRule of styleSheet.cssRules) {
- if (cssRule.type !== CSSRule.MEDIA_RULE) {
- continue
- }
- // WARNING: Safari does not have/supports `conditionText`.
- if (cssRule.conditionText) {
- if (cssRule.conditionText !== prefersColorSchemeDark) {
- continue
- }
- } else {
- if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
- continue
- }
- }
- mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
- }
-
- // WARNING: do not try to insert a Rule to a styleSheet you are
- // currently iterating on, otherwise the browser will be stuck
- // in a infinite loop…
- for (const mediaRule of mediaRules) {
- styleSheet.insertRule(mediaRule.cssText)
- hasDarkRules = true
- }
- }
- if (hasDarkRules) {
- loadThemeForm('#theme-selector')
- }
- })
- </script>
- </body>
- </html>
|