Merge branch 'main' into frontmatter

This commit is contained in:
Carson Gross 2023-06-02 16:23:04 -06:00
commit 89182674dd
25 changed files with 662 additions and 604 deletions

View File

@ -2,8 +2,8 @@
= Introduction
:chapter: 00
:part: Hypermedia Concepts
:part_url: ./part/hypermedia-concepts/
:url: ./introduction/
:part_url: /part/hypermedia-concepts/
:url: /introduction/
This is a book about building applications using hypermedia systems. _Hypermedia systems_ might seem like a strange phrase:
how is hypermedia a _system_? Isn't hypermedia just a way to link documents together?
@ -70,7 +70,8 @@ It is important to understand that, in his dissertation, Fielding was describing
late 1990s. The web, at that point, was simply web browsers exchanging hypermedia. That system, with its simple links
and forms, was what Fielding was calling RESTful.
JSON APIs were a decade away from becoming a common tool in web development: REST was about _hypermedia_ and _the Web 1.0_.
JSON APIs were a decade away from becoming a common tool in web development: REST was about _hypermedia_ and the 1.0
version of The Web.
== Hypermedia-Driven Applications
@ -147,16 +148,31 @@ ____
[.design-note]
.HTML Notes: Why Effective HTML?
[.html-note]
.HTML Notes: Hypermedia In Practice
****
At the end of each chapter you will find tips and best practices for writing effective HTML.
At the end of each chapter you will find tips and best practices for writing HTML well.
It's very easy (and sometimes acceptable) to produce mediocre HTML that _seems to_ work,
and many websites settle with _seeming to_ work.
It's very easy (and sometimes acceptable) to produce mediocre HTML that, within a purview limited to one particular application, works -- this seems to be enough for most websites.
But our websites, like men, are not islands.
That is, they are not applications of a _platform_,
but constituents of a _system_.
In a systems view of the web, the purpose of writing HTML is not just to develop a particular application, but also to play along with other members of the Web.
Thankfully for us hypermedia advocates, a few components, most prominently search engines and assistive technologies, have enough sway to keep people caring about HTML.
Less fortunately, lack of awareness around hypermedia means that these components are seen as annoyances to get out of the way, demons to appease, or worse, legacy leftovers to ignore.
After all, web development is so much easier without all this Web stuff.
So why resist the tide? Why shouldn't we discard hypermedia and rewrite the web into the application platform it can clearly be?
That's what the rest of this book will try to answer, in a practical, non theory-addled way.
And at the ends of chapters, these HTML Notes will connect the concepts of hypermedia we discuss and the code samples we present to day-to-day web development.
The executive summary:
Well-written HTML is easier to read and debug, more accessible to all, ranked higher by search engines (not out of bias, but because they have an easier time scraping it).
An important caveat:
The mantra that HTML is "accessible by default" is misleading and shunning other technologies like JavaScript is misguided.
Doing everything with "pure", "semantic" HTML is not a panacea, and testing is the ultimate indicator of quality over spec adherence.
But writing good, spec-compliant HTML lets browsers do a bunch of work for you. Furthermore, even when they don't, it makes it easier to write scripts that do. Fewer issues will be found during testing and you can release faster. When issues do come up, you can often fix them more easily by refactoring HTML as opposed to heaping JavaScript and ARIA attributes over everything.
// TODO: check last sentence here; pulled together from various paragraphs
Effective HTML typically loads faster, is easier to read and debug, performs better both with search engine ranking and screen reader accessibility, and can be scraped programatically.
****

View File

@ -1,7 +1,7 @@
= Hypermedia: A Reintroduction
:chapter: 01
:url: ./hypermedia-reintroduction/
:url: /hypermedia-reintroduction/
Hypermedia is a universal technology today, almost as common as electricity.
@ -290,7 +290,7 @@ issuing an "`Asynchronous JavaScript and XML,`" or AJAX request, available in al
<button onclick="fetch('/api/v1/contacts/1') <1>
.then(response => response.json()) <2>
.then(data => updateUI(data))"> <3>
Fetch Contacts
Fetch Contact
</button>
----
<1> Issue the request.
@ -351,7 +351,7 @@ It needs to know:
In short, the logic in `updateUI()` needs to have intimate knowledge of the API endpoint at `/api/v1/contact/1`, knowledge provided
via some side-channel beyond the response itself. As a result, the `updateUI()` code and the
API have a strong relationship, known as _tight coupling_: if the format of the JSON response changes, then the code for `updateUI()` will almost certainly
also need to be changed.
also need to be changed as well.
==== Single Page Applications
@ -376,8 +376,8 @@ are updated via JavaScript code and the framework then "`reacts`" to these chang
When the user interface is updated by a user these changes also flow _into_ the model objects, establishing a "`two-way`"
binding mechanism: the model can update the UI, and the UI can update the model.
This is all very sophisticated and, today, very popular. But the fact is that developers that adopt this approach to building
web applications have largely abandoned the web's underlying hypermedia system.
This is all very sophisticated and, today, very popular. However, web developers that adopt this approach to building
their web applications are abandoning the web's underlying hypermedia system.
HTML is still used to build user interfaces, but the _hypermedia_ aspect of the two major hypermedia controls,
anchors and forms, are ignored. Neither tag interacts with a server via their native _hypermedia_ mechanism. Rather,
@ -388,8 +388,8 @@ So, as with our simple button above, the Single Page Application approach is _no
It does not take advantage of the existing RESTful architecture of the web, nor does it utilize the built-in functionality
found in HTML's native hypermedia controls.
SPAs are somewhat like _thick client applications_ like the client-server applications of the
1980s -- an architecture popular _before_ the web came along.
SPAs are more much like _thick client applications_, that is, like the client-server applications of the
1980s -- an architecture popular _before_ the web came along and that the web was, in many ways, a reaction to.
This approach _isn't necessarily wrong_, but it is worth thinking about _why_ web developers so frequently take it and
if there are reasons _not_ to go down this path.
@ -408,24 +408,25 @@ This idea has really swept the internet. It started with a few major popular web
marketing sites and blogs.
____
The JavaScript-based Single Page Application approach has taken the web development world by storm, and there was one
major and very good reason for its success: The Single Page Application offers a far more interactive and immersive experience
than the old, gronky, Web 1.0 hypermedia-based applications could. It had the ability to smoothly update elements inline on
a page without a dramatic reload of the entire document, the ability to use CSS transitions to create nice visual effects,
the ability to hook into arbitrary events like mouse movements. All of these gave JavaScript-based applications a huge advantage
in building sophisticated user experiences.
The JavaScript-based Single Page Application approach has taken the web development world by storm, and if there was one
major for its wild success it was this: The Single Page Application offers a far more interactive and immersive experience
than the old, gronky, Web 1.0 hypermedia-based applications could. SPAs had the ability to smoothly update elements inline on
a page without a dramatic reload of the entire document, they had the ability to use CSS transitions to create nice visual effects,
and the ability to hook into arbitrary events like mouse movements.
So why on earth would you abandon this popular and modern approach for an older and much less discussed
approach such as hypermedia?
All of these gave JavaScript-based applications a huge advantage in building sophisticated user experiences.
So, given this success, why on earth would you set aside this popular and modern approach to consider an older and
less popular approach like hypermedia?
=== JavaScript Fatigue
Well, we are glad you asked.
We are glad you asked!
It turns out that the hypermedia architecture, even in its original Web 1.0 form, has a number of advantages when compared with
the Single Page Application + JSON Data API approach:
the Single Page Application + JSON Data API approach. Three of the biggest are:
* It is an extremely simple approach to building web applications.
* It is an extremely _simple_ approach to building web applications.
* It is extremely tolerant of content and API changes. In fact, it thrives on them!
@ -438,15 +439,17 @@ The first two advantages, in particular, address major pain points in modern web
* JSON API churn -- constant changes made to JSON APIs to support application needs -- has become a major pain point for
many application teams.
The combination of these two problems, along with other issues such as JavaScript library churn, has led to a phenomenon known as "`JavaScript Fatigue.`" This refers to a general sense of exhaustion with all the hoops that are necessary to jump through to
get anything done in modern-day web applications.
The combination of these two problems, along with other issues such as JavaScript library churn, has led to a phenomenon
known as "`JavaScript Fatigue.`" This refers to a general sense of exhaustion with all the hoops that are necessary to
jump through to get anything done in modern-day web applications.
We believe that a hypermedia architecture can help cure JavaScript Fatigue for many developers and teams. But if hypermedia is so great, and if it addresses so many of the problems that beset the web
development industry, why was it abandoned in the first place? After all, hypermedia was there first.
We believe that a hypermedia architecture can help cure JavaScript Fatigue for many developers and teams.
Why didn't web developers just stick with it?
But if hypermedia is so great, and if it addresses so many of the problems that beset the web
development industry, why was it abandoned in the first place? After all, hypermedia was there first. Why didn't web
developers just stick with it?
We believe that hypermedia hasn't made a comeback yet for two reasons.
There are two major reasons hypermedia hasn't made a comeback in web development.
The first is this: the expressiveness of HTML _as a hypermedia_ hasn't changed much, if at all, since HTML 2.0, which
was released _in the mid 1990s_. Many new _features_ have been added to HTML, of course, but there haven't been _any_
@ -460,7 +463,8 @@ HTML-as-hypermedia has fallen on hard times: as the interactivity and expressive
demands of web users have continued to increase, calling for more and more interactive web applications.
JavaScript-based applications coupled to data-oriented JSON APIs have stepped in as a way to provide these more
sophisticated user interfaces. It was the _user experience_ that you could achieve in JavaScript, and that you couldn't achieve in plain HTML, that drove the web development community to the JavaScript-based
sophisticated user interfaces. It was the _user experience_ that you could achieve in JavaScript, and that you couldn't
achieve in plain HTML, that drove the web development community to the JavaScript-based
Single Page Application approach. The shift was not driven by any inherent superiority of the Single Page Application as a system
architecture.
@ -474,9 +478,9 @@ understandable move to a more familiar model for building rich applications.
Not everyone abandoned hypermedia, of course. There have been heroic efforts to continue to advance hypermedia outside of
HTML, efforts like HyTime, VoiceXML, and HAL.
But HTML, the most widely used hypermedia in the world, stopped making progress as a hypermedia. The web development
world moved on, solving the interactivity problems with HTML and adopting a completely different
system architecture along the way.
But HTML, the most widely used hypermedia in the world, mostly stopped making progress as a hypermedia. The web development
world moved on, solving the interactivity problems with HTML by adopting JavaScript-based SPAs and, inadvertently,
a completely different system architecture.
== A Hypermedia Resurgence?
@ -504,14 +508,16 @@ links and forms located on multiple web pages, submitting HTTP requests and gett
MPA applications, by their nature, are Hypermedia-Driven Applications: after all, they are exactly what Roy Fielding
was describing in his dissertation.
These applications tend to be clunky, but they work reasonably well. Many web developers and teams choose to accept the limitations of plain HTML in the interest of simplicity and reliability.
These applications tend to be clunky, but they work reasonably well. Many web developers and teams choose to accept the
limitations of plain HTML in the interest of simplicity and reliability.
Rich Harris, creator of Svelte.js, a popular SPA library, and a thought-leader on the SPA side of the debate, has proposed a mix
of this older MPA style and the newer SPA style. Harris calls this approach to building web applications "`transitional,`" in that
it attempts to blend the MPA approach and the newer SPA approach into a coherent whole. (This is somewhat
similar to the "`transitional`" trend in architecture, which combines traditional and modern architectural styles.)
"`Transitional`" is a fitting term for mixed-style applications, and it offers a reasonable compromise between the two approaches, using either one as appropriate on a case-by-case basis.
"`Transitional`" is a fitting term for mixed-style applications, and it offers a reasonable compromise between the two
approaches, using either one as appropriate on a case-by-case basis.
But this compromise still feels unsatisfactory.
@ -523,7 +529,7 @@ of a "`transitional`" application -- for a particular feature.
It turns out that by adopting a hypermedia-oriented library, the interactivity gap between the MPA and the SPA approach
closes dramatically. You can use the MPA approach, that is, the hypermedia approach, for much more of your application
without compromising your user interface. You might even be able to use the hypermedia approach for all your application
without compromising your user interface. You might even be able to use the hypermedia approach for _all_ your application
needs.
Rather than having an SPA with a bit of hypermedia around the edges, or some mix of the two approaches, you can often create
@ -535,7 +541,7 @@ software. While there are still times and places for the more complex SPA appro
by adopting a hypermedia-first approach and using a hypermedia-oriented library to push HTML as far as possible,
your web application can be powerful, interactive _and_ simple.
One such hypermedia oriented library is https://htmx.org[htmx]. Htmx will be the focus of Part Two.
One such hypermedia oriented library is https://htmx.org[htmx]. Htmx will be the focus of Part Two of this book.
We show that you can, in fact, create many common "`modern`" UI features found in sophisticated Single
Page Applications by instead using the hypermedia model.
@ -575,7 +581,7 @@ Let's look at an htmx-powered implementation of the simple JavaScript-powered bu
<1> issues a `GET` request to `/contacts/1`, replacing the `contact-ui`.
As with the JavaScript powered button, this button has been annotated with some attributes. However, in
this case we do not have any JavaScript scripting.
this case we do not have any (explicit) JavaScript scripting.
Instead, we have _declarative_ attributes much like the `href` attribute on anchor tags and the `action` attribute on
form tags. The `hx-get` attribute tells htmx: "`When the user clicks this button, issue a `GET` request to `/contacts/1`.`"
@ -586,7 +592,24 @@ Here we get to the crux of htmx and how it allows you to build Hypermedia-Driven
_The HTTP response from the server is expected to be in HTML format, not JSON_.
This htmx-powered button is exchanging _hypermedia_ with the server, just like an anchor tag or form
An HTTP response to this htmx-driven request might look something like this:
.JSON
[source,html]
----
<details>
<div>
Contact: HTML Example
</div>
<div>
<a href="mailto:html-example@example.com">Email</a>
</div>
</details>
----
This small bit of HTML would be placed into the element in the DOM with the id `contact-ui`.
Thus, this htmx-powered button is exchanging _hypermedia_ with the server, just like an anchor tag or form
might, and thus the interaction is still within the original hypermedia model of the web. Htmx _is_ adding functionality
to this button (via JavaScript), but that functionality is _augmenting_ HTML as a hypermedia. Htmx extends the hypermedia
system of the web, rather than _replacing_ that hypermedia system with a totally different architecture.
@ -661,7 +684,7 @@ Any software project has a complexity budget, explicit or not: there is only so
team can tolerate and every new feature and implementation choice adds at least a bit more to the overall complexity
of the system.
What is particularly nasty about complexity is that it appears to grow exponentially: one day you can keep the entire
What is particularly nasty about complexity is that it tends to grow exponentially: one day you can keep the entire
system in your head and understand the ramifications of a particular change, and a week later the whole system seems
intractable. Even worse, efforts to help control complexity, such as introducing abstractions or infrastructure to
manage the complexity, often end up making things even more complex. Truly, the job of the good software engineer
@ -695,20 +718,3 @@ We hope that, in addition, you will also become as passionate about it as we are
This book is, in part, a plea that we "`let the web be the web`", that we take the original architecture of the web
seriously, and that we consider the entire _hypermedia system_ it makes available to us when we build applications
with it.
[.design-note]
.HTML Note: Stay Close to the Output
****
[quote, Manuel Matuzović, 'https://www.matuzo.at/blog/2023/single-page-applications-criticism[Why I\'m not the biggest fan of Single Page Applications]']
The fact that the HTML document is something that you barely touch, because everything you need in there will be injected via JavaScript, puts the document and the page structure out of focus.
In order to avoid `<div>` soup (or Markdown soup, or Component soup), you need to be aware of the markup you're producing and be able to change it.
Some SPA frameworks, and some web components, make this more difficult by putting layers of abstraction between the code the developer writes and the generated markup.
While these abstractions can allow developers to create richer UI or work faster,
their pervasiveness means that developers can lose sight of the actual HTML (and JavaScript) being sent to clients.
Without diligent testing, this leads to inaccessibility, poor SEO, and bloat.
****

View File

@ -1,7 +1,7 @@
= Components Of A Hypermedia System
:chapter: 02
:url: ./hypermedia-components/
:url: /hypermedia-components/
A _hypermedia system_ consists of a number of components, including:
@ -53,14 +53,14 @@ A typical URL might look like this:
.A simple URL
----
https://hypermedia.systems/book
https://hypermedia.systems/book/contents/
----
This particular URL is made up of the following components:
* A protocol or scheme (in this case, `https`)
* A domain (e.g., `hypermedia.systems`)
* A path (e.g., `/book`)
* A path (e.g., `/book/contents`)
This URL uniquely identifies a retrievable _resource_ on the internet, to which an _HTTP Request_ can be issued by
a hypermedia client that "`speaks`" HTTPS, such as a web browser. If this URL is found as the reference of a
@ -85,7 +85,7 @@ would be `https://hypermedia.systems/book/contents/`.
=== Hypermedia Protocols
The hypermedia control (link) above tells a browser: "`When a user clicks on this text, issue a request to
https://hypermedia.systems/book/contents/ using the Hypertext Transfer Protocol,`" or HTTP.
`https://hypermedia.systems/book/contents/` using the Hypertext Transfer Protocol,`" or HTTP.
HTTP is the _protocol_ used to transfer HTML (hypermedia) between browsers (hypermedia clients) and servers (hypermedia
servers) and, as such, is the key network technology that binds the distributed hypermedia system of the web together.
@ -175,7 +175,8 @@ These methods _roughly_ line up with the "`Create/Read/Update/Delete`" or CRUD p
.Put vs. Post
****
While HTTP Actions correspond roughly to CRUD, they are not the same. The technical specfications for these methods make no such connection, and are often somewhat difficult to read. Here, for example, is the documentation
While HTTP Actions correspond roughly to CRUD, they are not the same. The technical specifications for these methods make
no such connection, and are often somewhat difficult to read. Here, for example, is the documentation
on the distinction between a `POST` and a `PUT` from https://www.rfc-editor.org/rfc/rfc2616[RFC-2616].
[quote, RFC-2616, https://www.rfc-editor.org/rfc/rfc2616#section-9.6]
@ -185,14 +186,15 @@ resource's own semantics, whereas the enclosed representation in a PUT request i
target resource. Hence, the intent of PUT is idempotent and visible to intermediaries, even though the exact
effect is only known by the origin server.
____
So, in plain terms, a `POST` can be handled by a server pretty much however it likes, whereas a `PUT` should be handled
as a "`replacement`" of the resource, although the language, once again allows the server to do pretty much whatever it
would like within the constraint of being https://developer.mozilla.org/en-US/docs/Glossary/Idempotent[_idempotent_].
In plain terms, a `POST` can be handled by a server pretty much however it likes, whereas a `PUT` should be handled
as a "`replacement`" of the resource, although the language, once again allows the server to do pretty much whatever it
would like within the constraint of being https://developer.mozilla.org/en-US/docs/Glossary/Idempotent[_idempotent_].
****
In a properly structured HTML-based hypermedia system you would use an appropriate HTTP method for the operation a
particular hypermedia control performs. For example, if a hypermedia control such as a button _deletes_ a resource, ideally it should issue an HTTP `DELETE` request to do so.
particular hypermedia control performs. For example, if a hypermedia control such as a button _deletes_ a resource,
ideally it should issue an HTTP `DELETE` request to do so.
A strange thing about HTML, though, is that the native hypermedia controls can only issue HTTP `GET` and `POST` requests.
@ -201,7 +203,9 @@ Anchor tags always issue a `GET` request.
Forms can issue either a `GET` or `POST` using the `method` attribute.
Despite the fact that HTML -- the world's most popular hypermedia -- has been designed alongside
HTTP (which is the Hypertext Transfer Protocol, after all!): if you wish to issue `PUT`, `PATCH` or `DELETE` requests you currently _have to_ resort to JavaScript to do so. Since a `POST` can do almost anything, it ends up being used for any mutation on the server, and `PUT`, `PATCH` and `DELETE` are left aside in plain HTML-based
HTTP (which is the Hypertext Transfer Protocol, after all!): if you wish to issue `PUT`, `PATCH` or `DELETE` requests
you currently _have to_ resort to JavaScript to do so. Since a `POST` can do almost anything, it ends up being used for
any mutation on the server, and `PUT`, `PATCH` and `DELETE` are left aside in plain HTML-based
applications.
This is an obvious shortcoming of HTML as a hypermedia; it would be wonderful to see this fixed in the
@ -268,12 +272,13 @@ Here are some of the more common or interesting ones:
There are some fairly subtle differences between HTTP response codes (and, to be honest, some ambiguities between them).
The difference between a `302` redirect and a `303` redirect, for example, is that the former will issue the request to the
new URL using the same HTTP method, whereas the latter will always use a `GET`. A small, but often crucial difference,
as we will see later in the book.
new URL using the same HTTP method as the initial request, whereas the latter will always use a `GET`. This is a small
but often crucial difference, as we will see later in the book.
Nonetheless, a well crafted hypermedia system will take advantage of both HTTP methods and HTTP response codes to create a sensible
hypermedia API. You do not want to build a hypermedia system that uses a `POST` method for all requests and responds
with `200 OK` for every response. Some JSON Data APIs built on top of HTTP do exactly this!
A well crafted Hypermedia-Driven Application will take advantage of both HTTP methods and HTTP response codes to create
a sensible hypermedia API. You do not want to build a Hypermedia-Driven Application that uses a `POST` method for all
requests and responds with `200 OK` for every response, for example. (Some JSON Data APIs built on top of HTTP do exactly
this!)
When building a Hypermedia-Driven Application, you want, instead, to go "`with the grain`" of the web and use HTTP methods
and response codes as they were designed to be used.
@ -291,13 +296,15 @@ another HTTP request for that resource until that time limit has expired.
Another important caching-related response header is `Vary`. This response header can be used to indicate exactly what
headers in an HTTP Request form the unique identifier for a cached result. This becomes important to allow the browser
to correctly cache content in situations where a particular header affects the form of the server response. A common
pattern in htmx-powered applications, for example, is to use a custom header set by htmx, `HX-Request`, to differentiate between
"`normal`" web requests and requests submitted by htmx. To properly cache the response to these requests, the `HX-Request`
request header must be indicated by the `Vary` response header.
to correctly cache content in situations where a particular header affects the form of the server response.
A common pattern in htmx-powered applications, for example, is to use a custom header set by htmx, `HX-Request`, to
differentiate between "`normal`" web requests and requests submitted by htmx. To properly cache the response to these
requests, the `HX-Request` request header must be indicated by the `Vary` response header.
A full discussion of caching HTTP responses is beyond the scope of this chapter; see
the https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching[MDN Article on HTTP Caching] if you would like to know more on the topic.
the https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching[MDN Article on HTTP Caching] if you would like to know more
on the topic.
=== Hypermedia Servers
@ -305,17 +312,17 @@ Hypermedia servers are any server that can respond to an HTTP request with an HT
this means that nearly any programming language can be used to build a hypermedia server. There are a vast number of
libraries available for building HTTP-based hypermedia servers in nearly every programming language imaginable.
This is one of the best aspects of adopting hypermedia as your primary technology for building a web application: it removes
the pressure of adopting JavaScript as a backend technology. In contrast, if you decide to adopt a JavaScript-heavy
Single Page Application-based front end, and you use JSON Data APIs, you will feel significant pressure to adopt
JavaScript on the back end.
This turns out to be one of the best aspects of adopting hypermedia as your primary technology for building a web application: it removes
the pressure of adopting JavaScript as a backend technology. If you use a JavaScript-heavy Single Page Application-based
front end, and you use JSON Data APIs, you are going to feel significant pressure to adopt JavaScript on the back end as
well.
In this latter situation, you already have a ton of code written in JavaScript. Why maintain two separate code bases in
two different languages? Why not create reusable domain logic on the client-side as well as the server-side? Now that
JavaScript has excellent server-side technologies available like Node and Deno, why not just use a single language for
everything?
In contrast, using a hypermedia-based front end gives you a lot more freedom in picking the back end technology you want
In contrast, building a Hypermedia-Driven Application gives you a lot more freedom in picking the back end technology you want
to use. Your decision can be based on the domain of your application, what languages and server software you are familiar
with or are passionate about, or just what you feel like trying out.
@ -368,7 +375,8 @@ to hang together. Without a sophisticated client that can do this, hypermedia c
much less useful.
This is one reason why JSON APIs have rarely adopted hypermedia controls successfully: JSON APIs are typically consumed
by code that is expecting a fixed format and isn't designed to be a hypermedia client. For clients like this, the
by code that is expecting a fixed format and that isn't designed to be a hypermedia client. This is totally understandable:
building a good hypermedia client is hard! For JSON API clients like this, the
power of hypermedia controls embedded within an API response is irrelevant and often simply annoying:
[quote, Freddie Karlbom,https://techblog.commercetools.com/graphql-and-rest-level-3-hateoas-70904ff1f9cf]
@ -378,7 +386,7 @@ after almost 20 years, HATEOAS still hasnt gained wide adoption among develop
like wildfire because it solves real-world problems.
____
HATEOAS will be described in more detail below, but the take away here is that a good hypermedia client is a necessary
HATEOAS will be described in more detail below, but the takeaway here is that a good hypermedia client is a necessary
component within a larger hypermedia system.
== REST
@ -394,27 +402,28 @@ perspective: Section 5.1. This section contains the core concepts (Fielding call
State Transfer, or REST.
Before we get into the muck, however, it is important to understand that Fielding discusses REST as a _network architecture_,
that is an entirely different way of architecting a distributed system. And a novel one that should be _contrasted_ with
earlier distributed systems.
that is, as an entirely different way of architecting a distributed system. And, further, as a novel network
architecture that should be _contrasted_ with earlier approaches to distributed systems.
It is also important to emphasize that, at the time Fielding wrote his dissertation, JSON APIs and AJAX did not exist.
He was describing the early web, with HTML being transferred over HTTP by early browsers, as a hypermedia system.
Today, in a strange turn of events, the term "`REST`" is mainly associated with JSON Data APIs, rather than with HTML
and hypermedia. This becomes extremely humorous once you realize that the vast majority of JSON Data APIs aren't
RESTful, and, in fact _can't_ be RESTful, since they aren't using a natural hypermedia format.
and hypermedia. This is extremely funny once you realize that the vast majority of JSON Data APIs aren't
RESTful, in the original sense, and, in fact, _can't_ be RESTful, since they aren't using a natural hypermedia format.
To re-emphasize: REST, as coined by Fielding, describes _the pre-JSON API web_, and letting go of the current, common
usage of the term as "`JSON API`" is necessary to develop a proper understanding of it.
To re-emphasize: REST, as coined by Fielding, describes _the pre-API web_, and letting go of the current, common
usage of the term REST to simply mean "`a JSON API`" is necessary to develop a proper understanding of the idea.
=== The "`Constraints`" of REST
Fielding defines various "`constraints`" to describe how a RESTful system must behave. This approach
In his dissertation, Fielding defines various "`constraints`" to describe how a RESTful system must behave. This approach
can feel a little round-about and difficult to follow for many people, but it is an appropriate approach for an academic
dissertation. Given a bit of time thinking about the constraints he outlines, and some concrete examples, it will
become easy to assess whether a given system actually satisfies the architectural requirements of REST or not.
document. Given a bit of time thinking about the constraints he outlines and some concrete examples of those
constraints it will become easy to assess whether a given system actually satisfies the architectural requirements of
REST or not.
Here are the constraints of REST, as outlined in Fielding's dissertation:
Here are the constraints of REST Fielding outlines:
* It is a client-server architecture (section 5.1.2).
* It must be stateless; (section 5.1.3) that is, every request contains all information necessary to respond to that request.
@ -454,11 +463,10 @@ This session information is typically stored in some sort of shared storage acro
like the current user's email or id, their roles, partially created domain objects, caches, and so forth.
This violation of the Statelessness REST architectural constraint has proven to be useful for building web applications
and does not appear to have had a significant impact on the overall flexibility of the approach. But
bear in mind that even Web 1.0 applications often violate the purity of REST in the interest of pragmatic
trade-offs.
and does not appear to have had a major impact on the overall flexibility the web. But it is worth bearing in mind that
even Web 1.0 applications often violate the purity of REST in the interest of pragmatic trade-offs.
Note, however, that sessions do cause additional operational complexity headaches when deploying hypermedia
And it must be said that sessions _do_ cause additional operational complexity headaches when deploying hypermedia
servers; these may need shared access to session state information stored across an entire cluster. So
Fielding was correct in pointing out that an ideal RESTful system, one that did not violate this constraint, would be simpler and therefore more robust.
@ -476,7 +484,8 @@ easy to see how this constraint is satisfied by the web.
=== The Uniform Interface Constraint
Now we come to the most interesting and, in our opinion, innovative constraint in REST: that of the _uniform interface_.
Now we come to the most interesting and, in our opinion, most innovative constraint in REST: that of the _uniform interface_.
This constraint is the source of much of the _flexibility_ and _simplicity_ of a hypermedia system, so we are going to
spend some time on it.
@ -522,14 +531,15 @@ the Uniform Interface, of REST and why hypermedia provides such a powerful syste
The Self-Descriptive Messages constraint requires that, in a RESTful system, messages must be _self-describing_.
This means that _all information_ necessary to both display _and also operate_ on the data being represented must be
present in the response. In a properly RESTful system, there can be no additional "`side`" information necessary for a client to transform a response from a server into a useful user interface. Everything must "`be in`" the message itself,
present in the response. In a properly RESTful system, there can be no additional "`side`" information necessary for a
client to transform a response from a server into a useful user interface. Everything must "`be in`" the message itself,
in the form of hypermedia controls.
This might sound a little abstract, let's look at a concrete example.
This might sound a little abstract so let's look at a concrete example.
Consider two different potential responses from an HTTP server for the URL `\https://example.com/contacts/42`.
Both responses will return information about a contact, but they will take very different forms.
Both responses will return information about a contact, but each response will take very different forms.
The first implementation returns an HTML representation:
@ -577,7 +587,7 @@ So REST _trades off_ representational efficiency for other goals.
To understand these other goals, first notice that the HTML representation has a hyperlink in it to navigate to a page
to archive the contact. The JSON representation, in contrast, does not have this link.
What are the ramifications of this fact for a client of the JSON API?
What are the ramifications of this fact for a _client_ of the JSON API?
What this means is that the JSON API client must know _in advance_ exactly what other URLs (and request methods) are
available for working with the contact information. If the JSON client is able to update this contact in some way, it
@ -585,20 +595,21 @@ must know how to do so from some source of information _external_ to the JSON me
status, say "`Archived`", does this change the allowable actions? If so, what are the new allowable actions?
The source of all this information might be API documentation, word of mouth or, if the developer controls both the server
and the client, internal knowledge. But it is _outside_ the message.
and the client, internal knowledge. But this information is implicit and _outside_ the response.
The hypermedia (or HTML) client, on the other hand, needs only to know how to render the given HTML. It doesn't need to understand
what actions are available for this contact: they are simply encoded _within_ the HTML itself as hypermedia controls. It doesn't need to
understand what the status field means or, in fact, what a contact even is!
Contrast this with the hypermedia (HTML) response. In this case, the hypermedia client (that is, the browser) needs
only to know how to render the given HTML. It doesn't need to understand what actions are available for this contact:
they are simply encoded _within_ the HTML response itself as hypermedia controls. It doesn't need to understand what
the status field means. In fact, the client doesn't even know what a contact is!
The browser, our hypermedia client, simply renders the HTML and allows the user, who presumably understands the concept
of a Contact, to make a decision on what action to pursue from the actions made available in the representation.
This difference between the two responses demonstrates the crux of REST and hypermedia, what makes them so powerful
and flexible: clients (that is, web browsers) don't need to understand _anything_ about the underlying resources being
and flexible: clients (again, web browsers) don't need to understand _anything_ about the underlying resources being
represented.
Browsers only (only! As if it is easy!) need to understand how to parse and display hypermedia, in this case HTML. This
Browsers only (only! As if it is easy!) need to understand how to interpret and display hypermedia, in this case HTML. This
gives hypermedia-based systems unprecedented flexibility in dealing with changes to both the backing representations and
to the system itself.
@ -651,7 +662,8 @@ representation, in a way that the JSON representation does not.
A client interpreting the JSON response must, again, understand not only the general concept of a Contact,
but also specifically what the "`status`" field with the value "`Archived`" means. It must know exactly what operations
are available on an "`Archived`" contact, to appropriately display them to an end user. The state of the application is not encoded in the response, but rather conveyed through a mix of raw data and side channel information such as
are available on an "`Archived`" contact, to appropriately display them to an end user. The state of the application is
not encoded in the response, but rather conveyed through a mix of raw data and side channel information such as
API documentation.
Furthermore, in the majority of front end SPA frameworks today, this contact information would live _in memory_ in a
@ -762,7 +774,8 @@ nonetheless.
=== An Optional Constraint: Code-On-Demand
We called The Layered System constraint the final "`required`" constraint because
Fielding mentions one additional constraint on a RESTful system. This Code On Demand constraint is somewhat awkwardly described as "`optional`" (Section 5.1.7).
Fielding mentions one additional constraint on a RESTful system. This Code On Demand constraint is somewhat awkwardly
described as "`optional`" (Section 5.1.7).
In this section, Fielding says:
@ -778,8 +791,9 @@ So, scripting was and is a native aspect of the original RESTful model of the we
should of course be allowed in a Hypermedia-Driven Application.
However, in a Hypermedia-Driven Application the presence of scripting should _not_ change the fundamental networking
model: hypermedia should still be the engine of application state, server communication should still consist of
hypermedia exchanges rather than, for example, JSON data exchanges, and so on. (JSON Data API's certainly have their place; in Chapter 10 we'll discuss when and how to use them).
model: hypermedia should continue to be the engine of application state, server communication should still consist of
hypermedia exchanges rather than, for example, JSON data exchanges, and so on. (JSON Data API's certainly have their
place; in Chapter 10 we'll discuss when and how to use them).
Today, unfortunately, the scripting layer of the web, JavaScript, is quite often used to _replace_, rather than augment
the hypermedia model. We will elaborate in a later chapter what scripting that does not replace the underlying hypermedia
@ -796,10 +810,13 @@ working in web development, and building a hypermedia-oriented library to boot,
special nature of HTML, hypermedia and the web!
[.design-note]
.HTML Note: <div> Soup
[.html-note]
.HTML Notes: <div> Soup
****
The best-known kind of messy HTML is `<div>` soup.
The best-known kind of messy HTML is `<div>` soup.
Advances in CSS and JavaScript technology have mostly obviated the need to add extraneous "wrapper" elements to out documents.
However, while the number of elements can fall, the quality of usage does not necessatily improve.
When developers fall back on the generic `<div>` and `<span>` elements instead of more meaningful tags,
we either degrade the quality of our websites or create more work for ourselves -- probably both.
@ -832,5 +849,16 @@ It's not obvious from the HTML source that this is a button,
making the source harder to read and the absence of these attributes harder to spot.
The source code of pages with div soup is difficult to edit and debug.
To avoid div soup, learn the meaning of available tags and consider each another tool in your tool chest. (With the 113 elements currently defined in the spec, it's more of a tool shed).
"`This is silly,`" you might be thinking.
Who uses a div as a button?
Ignoring the answer (more people than you'd think), not every UI pattern has a designated HTML element.
We often need to compose elements and augment them with attributes.
Do you know, off the top of your head, the best elements and attributes to use for...
// TODO dzk (maybe give the correct elements here? I don't know them... :)
* A "command palette" menu?
* A play-pause button?
* A search field with a search button next to it? (Check the HTML spec -- there might be things on there you don't remember from before!)
To avoid div soup, learn the meaning of available tags and consider each another tool in your tool chest. (With the 113 elements currently defined in the spec, it's more of a whole shed).
****

View File

@ -1,17 +1,18 @@
= A Web 1.0 Application
:chapter: 03
:url: ./a-web-1-0-application/
:url: /a-web-1-0-application/
To start our journey into Hypermedia-Driven Applications, we are going to create a simple contact management web
application called Contact.app. We will start with a basic, "`Web 1.0-style`" Multi-Page Application (MPA), in the grand
CRUD (Create, Read, Update, Delete) tradition. It will not be the best contact management application in the world; it will be simple and it will do its job.
CRUD (Create, Read, Update, Delete) tradition. It will not be the best contact management application in the world, but
it will be simple and it will do its job.
This application will also be easy to incrementally improve in the coming chapters by utilizing the hypermedia-oriented
library htmx.
By the time we are finished building and enhancing the application, over the next few chapters, it will have some very
slick features that most developers today would assume requires the use of a heavy JavaScript framework.
slick features that most developers today would assume requires the use of a SPA JavaScript framework.
== Picking A "`Web Stack`"
@ -36,29 +37,31 @@ More importantly, Python is easy to read even if you aren't familiar with it.
We chose the Flask web framework because it is simple and does not impose a lot of structure on top of the
basics of HTTP request handling.
This bare-bones approach is a good match for our needs: in other cases you might consider a more full-featured Python framework, such as https://www.djangoproject.com/[Django], which supplies much more functionality out of the box than Flask does.
This bare-bones approach is a good match for our needs: in other cases you might consider a more full-featured Python
framework, such as https://www.djangoproject.com/[Django], which supplies much more functionality out of the box than
Flask does.
By using Flask we are able
to keep the book focused on _hypermedia
exchanges_.
So, even if this isn't your preferred stack, keep going: you will gain from the patterns we introduce here.
By using Flask for our book, we will be able to keep our code focused on _hypermedia exchanges_.
We picked Jinja2 templates because they are the default templating language for Flask. They are simple enough and
similar enough to most other server-side templating languages that most people who are familiar with any server-side
(or client-side) templating library should be able to understand them quickly and easily.
Even if this combination of technologies isn't your preferred stack, please, keep reading: you will learn quite a bit from
the patterns we introduce in the coming chapters and it shouldn't be hard to map them into your preferred language and
frameworks.
With this stack we will be rendering HTML _on the server-side_ to return to clients, rather than producing JSON. This
is the traditional approach to building web applications, but, with the rise of SPAs, is not as widely used a technique
as it once was. Today, as people are rediscovering this approach to building web applications, the term "`Server-Side
Rendering`" or SSR is emerging as the way that people talk about this style of templating. This is in contrast with
is the traditional approach to building web applications. However, with the rise of SPAs, this approach is not as
widely used a technique as it once was. Today, as people are rediscovering this style of web applications, the term
"`Server-Side Rendering`" or SSR is emerging as the way that people talk about it. This contrasted with
"`Client-Side Rendering`", that is, rendering templates in the browser with data retrieved in JSON form from the server,
as is common in SPA libraries.
In Contact.app we will intentionally keep things as simple as possible to maximize the teaching value of our code: it
won't be perfectly factored code, but it will
be easy to follow for readers even if they have little Python experience, and it should be easy to translate the application
and the techniques demonstrated into your preferred programming language and web framework.
won't be perfectly factored code, but it will be easy to follow for readers, even if they have little Python experience,
and it should be easy to translate both the application and the techniques demonstrated into your preferred programming
environment.
== Python
@ -67,15 +70,15 @@ the various technologies we use _around_ that hypermedia. This has some obvious
with Python, for example, some example python code in the book may be a bit confusing or mysterious at first.
If you feel like you need a quick introduction to the language before diving into the code, we recommend the following
books/websites:
books and websites:
* https://nostarch.com/python-crash-course-3rd-edition[Python Crash Course] from No Starch Press
* https://learnpythonthehardway.org/python3/[Learn Python The Hard Way] by Zed Shaw
* https://www.py4e.com/[Python For Everybody] by Dr. Charles R. Severance
We think most web developers, even developers who are unfamiliar with Python, should be able to follow
along. Most of the authors hadn't written much Python before writing this book, and we got the hang of
it pretty quickly.
along with our examples. Most of the authors of this book hadn't written much Python before writing it, and we got the
hang of it pretty quickly.
== Introducing Flask: Our First Route
@ -131,7 +134,7 @@ we are going to redirect to another path, the `/contacts` path. Redirects are a
redirect a client to another location with an HTTP response.
We are going to display a list of contacts as our root page, and, arguably, redirecting to the `/contacts` path to
display this information is a bit more consistent with notion of resources with REST. This is a judgement call on our
display this information is a bit more consistent with the notion of resources with REST. This is a judgement call on our
part, and not something we feel is too important, but it makes sense in terms of routes we will set up later in the
application.
@ -407,8 +410,7 @@ of the `Contact` class in Python, if you aren't familiar with it.)
While the handler code for this route is very simple, the `new.html` template is more complicated.
****
For the
remaining templates we are going to omit the layout directive and the content block declaration, but you
For the remaining templates we are going to omit the layout directive and the content block declaration, but you
can assume they are the same unless we say otherwise. This will let us focus on the "meat" of the template.
****
@ -436,7 +438,7 @@ Here is what our HTML looks like:
In the first line of code we create a form that will submit back _to the same path_ that we are handling: `/contacts/new`.
Rather than issuing an HTTP `GET` to this path, however, we will issue an HTTP `POST` to it. Using a `POST` in this manner
will signal to the server that we want to create a new Contact, rather than get a form.
will signal to the server that we want to create a new Contact, rather than get a form for creating one.
We then have a label (always a good practice!) and an input that captures the email of
the contact being created. The name of the input is `email` and, when this form is submitted, the value of this input
@ -478,12 +480,11 @@ Finally, we have a button that will submit the form, the end of the form tag, an
</p>
----
Easy to miss in this straight-forward example: we are seeing the flexibility
of hypermedia in action.
It is easy to miss in this straight-forward example: we are seeing the flexibility of hypermedia in action.
If we add a new field, remove a field, or change the logic around how fields are validated or work with one another,
this new state of affairs would be reflected in the new hypermedia representation given to users. A user would see the
updated new form, and be able to work with new features, with no software update required.
updated new form and be able to work with these new features, with no software update required.
==== Handling the post to /contacts/new
@ -856,20 +857,22 @@ Is it time to reach for a JavaScript framework and JSON APIs to make our contact
No. No it isn't.
It turns out that we can improve the user experience of this application within the
hypermedia architecture. In the next few chapters we will look at https://htmx.org[htmx], a hypermedia-oriented library
It turns out that we can improve the user experience of this application while retaining its fundamental hypermedia
architecture.
In the next few chapters we will look at https://htmx.org[htmx], a hypermedia-oriented library
that will let us improve our contact application _without_ abandoning the hypermedia approach we have used so far.
[.design-note]
.HTML Note: Semantic HTML
.HTML Notes: Semantic HTML
****
Telling people to "use semantic HTML" instead of "read the spec" has led to a lot of people guessing at the meaning of tags -- "`looks pretty semantic to me!" -- instead of engaging with the spec.
[quote,https://t-ravis.com/post/doc/semantic_the_8_letter_s-word/]
I think being asked to write _meaningful_ HTML better lights the path to realizing that it isn't about what the text means to humans--it's about using tags for the purpose outlined in the specs to meet the needs of software like browsers, assistive technologies, and search engines.
Telling people to "use semantic HTML" instead of "read the spec" has led to a lot of people guessing at the meaning of tags -- "`looks pretty semantic to me!" -- instead of engaging with the spec.
We recommend talking about, and writing, _conformant_ HTML.
(We can always bikeshed further).
Use the elements to the full extent provided by the HTML specification,
and let the software take from it whatever meaning they can.
****

View File

@ -2,8 +2,8 @@
= Extending HTML As Hypermedia
:chapter: 04
:part: Hypermedia-Driven Web Applications with Htmx
:part_url: ./part/htmx/
:url: ./extending-html-as-hypermedia/
:part_url: /part/htmx/
:url: /extending-html-as-hypermedia/
In the previous chapter we introduced a simple Web 1.0-style hypermedia application to manage contacts. Our application
supported the normal CRUD operations for contacts, as well as a simple mechanism for searching contacts. Our application
@ -11,7 +11,8 @@ was built using nothing but forms and anchor tags, the traditional hypermedia co
The application exchanges hypermedia (HTML) with the server over HTTP, issuing `GET` and `POST` HTTP requests and
receiving back full HTML documents in response.
It is a basic web application, but it is also definitely a Hypermedia-Driven Application. It is robust, it leverages the web's native technologies, and it is simple to understand.
It is a basic web application, but it is also definitely a Hypermedia-Driven Application. It is robust, it leverages the
web's native technologies, and it is simple to understand.
So what's not to like about the application?
@ -33,7 +34,8 @@ cousins.
We could address this issue by adopting a Single Page Application framework, and updating our server-side to
provide JSON-based responses. Single Page Applications eliminate the clunkiness of web 1.0 applications by updating a
web page directly: they mutate the Document Object Model (DOM) directly, without doing a full page refresh.
web page without refreshing it: they can mutate parts of the Document Object Model (DOM) of the existing page without
needing to replace (and re-render) the entire page.
.The DOM
****
@ -55,19 +57,24 @@ with the application sacrificing the advantages of hypermedia in order to provid
Many web developers today would not even consider the hypermedia approach due to the perceived "`legacy`" feel of these
web 1.0 style applications.
The second, technical point may strike you as a bit pedantic, and we are the first to admit that conversations around
REST and which HTTP Action is right for a given operation can become very tedious. But still, it's odd that,
when using plain HTML, it is impossible to use HTTP fully.
Now, the second more technical issue we mentioned may strike you as a bit pedantic, and we are the first to admit that
conversations around REST and which HTTP Action is right for a given operation can become very tedious. But still, it's
odd that, when using plain HTML, it is impossible to use all the functionality of HTTP!
Just seems wrong, doesn't it?
== A Close Look At A Hyperlink
It turns out that we can boost the interactivity of our application without resorting to the SPA approach, by using one of several available hypermedia-oriented JavaScript libraries.
We'll use the library that we've developed, https://htmx.org[htmx], not only because we know it inside and out, but because we built it purely to extend HTML as a hypermedia.
It turns out that we can boost the interactivity of our application and address both of these issues _without_ resorting
to the SPA approach. We can do so by using one of several available _hypermedia-oriented_ JavaScript libraries.
In this book, naturally, we will use the library that we've developed, https://htmx.org[htmx]. We will use it not only
because we know it inside and out, but also because we built it to extend HTML as a hypermedia and address the issues
with legacy HTML applications we mentioned above (as well as few others.)
To understand how htmx allows us to improve the UX of our Web 1.0 style
application without abandoning hypermedia, let's revisit the hyperlink/anchor tag from Chapter 1. Recall, a hyperlink
is what is known as a _hypermedia control_, a mechanism that describes some sort of interaction by encoding information about that interaction directly and completely within
itself.
Before we get into how htmx allows us to improve the UX of our Web 1.0 style application, let's revisit the
hyperlink/anchor tag from Chapter 1. Recall, a hyperlink is what is known as a _hypermedia control_, a mechanism that
describes some sort of interaction with a server by encoding information about that interaction directly and completely
within the control itself.
Consider again this simple anchor tag which, when interpreted by a browser, creates a hyperlink to the website for
this book:
@ -87,8 +94,8 @@ Let's break down exactly what happens with this link:
* The browser will issue an HTTP `GET` to `https://hypermedia.systems`...
* The browser will load the HTML body of the HTTP response into the browser window, replacing the current document.
So we have four aspects of a simple hypermedia link like this, with the last three aspects supplying the mechanism that distinguishes
a hyperlink from "`normal`" text and makes this a hypermedia control.
So we have four aspects of a simple hypermedia link like this, with the last three aspects supplying the mechanism that
distinguishes a hyperlink from "`normal`" text and, thus, makes this a hypermedia control.
Now, let's take a moment and think about how we can _generalize_ these last three aspects of a hyperlink.
@ -99,10 +106,10 @@ Consider: what makes anchor tags (and forms) so special?
Why can't other elements issue HTTP requests as well?
For example, why shouldn't `button` elements be able to issue HTTP requests? It seems arbitrary to have to wrap a
form tag around a button just to make deleting contacts work in our application.
form tag around a button just to make deleting contacts work in our application, for example.
Maybe: other elements should be able
to issue HTTP requests as well, and act as hypermedia controls on their own.
Maybe: other elements should be able to issue HTTP requests as well. Maybe other elements should be able to act as
hypermedia controls on their own.
This is our first opportunity to generalize HTML as a hypermedia.
@ -112,13 +119,13 @@ This is our first opportunity to generalize HTML as a hypermedia.
HTML could be extended to allow _any_ element to issue a request to the server and act as a hypermedia control.
====
=== Why Only Clicks & Submits?
=== Why Only Click & Submit Events?
Next, let's consider the event that triggers the request to the server on our link: a click event.
Well, what's so special about clicking (in the case of anchors) or submitting (in the case of forms)? Those are just two
of many, many events that are fired by the DOM, after all. Events like mouse down, or key up, or blur are all events
you might want to use to issue an HTTP request.
Well, what's so special about clicking (in the case of anchors) or submitting (in the case of forms) things? Those are
just two of many, many events that are fired by the DOM, after all. Events like mouse down, or key up, or blur are all
events you might want to use to issue an HTTP request.
Why shouldn't these other events be able to trigger requests as well?
@ -164,10 +171,11 @@ HTML could be extended so that it allows access to the missing three HTTP method
As a final observation, consider the last aspect of a hyperlink: it replaces the _entire_ screen when a user clicks on it.
It turns out that this technical detail is the primary culprit for poor user experience in Web 1.0 Applications.
A full page refresh can cause a flash of unstyled content, it destroys the scroll state of the user by scrolling to the
top of the page, and so forth.
A full page refresh can cause a flash of unstyled content, where content "jumps" on the screen as it transitions from
its initial to its styled final form. It also destroys the scroll state of the user by scrolling to the
top of the page, removes focus from a focused element and so forth.
But there is no rule saying that hypermedia exchanges _must_ replace the entire document.
But, if you think about it, there is no rule saying that hypermedia exchanges _must_ replace the entire document.
This gives us our fourth, final and perhaps most important opportunity to generalize HTML:
@ -202,7 +210,7 @@ can be added to a web application by simply including it via a `script` tag in y
Because of this simple installation model, you can take advantage of tools like public CDNs to install the library.
Below is an example using the popular https://unpkg.com[unpkg] Content Delivery Network (CDN) to install version `1.7.0`
Below is an example using the popular https://unpkg.com[unpkg] Content Delivery Network (CDN) to install version `1.9.2`
of the library. We use an integrity hash to ensure that the delivered JavaScript content matches what we expect. This
SHA can be found on the htmx website.
@ -213,9 +221,9 @@ We also mark the script as `crossorigin="anonymous"` so no credentials will be s
[source,html]
----
<head>
<script src="https://unpkg.com/htmx.org@1.7.0"
integrity="sha384-EzBXYPt0/T6gxNp0nuPtLkmRpmDBbjg6WmCUZRLXBBwYYmwAUxzlSGej0ARHX0Bo"
crossorigin="anonymous"></script>
<script src="https://unpkg.com/htmx.org@1.9.2"
integrity="sha384-L6OqL9pRWyyFU3+/bjdSri+iIphTN/bvYyM37tICVyOJkWZLpP2vGn6VUEXgzg6h"
crossorigin="anonymous"></script>
</head>
----
@ -234,11 +242,10 @@ Once htmx has been installed, you can begin using it immediately.
=== No JavaScript Required...
And here we get to the fun part of htmx: htmx does not require you,
the user of htmx, to actually write any JavaScript.
And here we get to the interesting part of htmx: htmx does not require you, the user of htmx, to actually write any JavaScript.
Instead, you will use _attributes_ placed directly on elements in your HTML to drive more dynamic behavior. Htmx extends
HTML as a hypermedia, and it wants that extension to be as natural and consistent as possible with existing
HTML as a hypermedia, and it is designed so that extension is as natural and consistent as possible with existing
HTML concepts. Just as an anchor tag uses an `href` attribute to specify the URL to retrieve, and forms use an `action`
attribute to specify the URL to submit the form to, htmx uses HTML _attributes_ to specify the URL that an HTTP request
should be issued to.
@ -282,8 +289,8 @@ Very easy to understand and very consistent with the rest of HTML.
With the request issued by the button above, we get to perhaps the most important thing to understand about htmx:
it expects the response to this AJAX request _to be HTML_. Htmx is an extension of HTML. A native hypermedia control
like an anchor tag will typically get an HTML response to a request it creates. Similarly, htmx expects the server to
respond to the requests that it makes with HTML.
like an anchor tag will typically get an HTML response to an HTTP request it creates. Similarly, htmx expects the server to
respond to the requests that _it_ makes with HTML.
This may surprise web developers who are used to responding to an AJAX request with JSON,
which is far and away the most common response format for such requests. But AJAX requests are just HTTP requests and
@ -341,10 +348,10 @@ content into the existing page (rather than replacing the entire page), the ques
content be placed?
It turns out that the default htmx behavior is to simply put the returned content inside the element that triggered the
request. That's obviously _not_ a good thing in the case of our button: we will end up with a list of contacts awkwardly embedded within
request. That's _not_ a good thing in the case of our button: we will end up with a list of contacts awkwardly embedded within
the button element. That will look pretty silly and is obviously not what we want.
Fortunately htmx provides another attribute, `hx-target` which can be used to specify exactly where in the DOM the
Fortunately htmx provides another attribute, `hx-target` which can be used to specify exactly _where_ in the DOM the
new content should be placed. The value of the `hx-target` attribute is a Cascading Style Sheet (CSS) _selector_ that
allows you to specify the element to put the new hypermedia content into.
@ -465,15 +472,14 @@ four opportunities for improvement that we enumerated regarding plain HTML:
* Opportunity 1: We can now issue an HTTP request with _any_ element (in this case we are using a button).
* Opportunity 3: We can issue _any sort_ of HTTP request we want, `PUT`, `PATCH` and `DELETE`, in particular.
And, with `hx-target` and `hx-swap` we have addressed a third shortcoming:
the requirement that the entire page be replaced.
And, with `hx-target` and `hx-swap` we have addressed a third shortcoming: the requirement that the entire page be replaced.
* Opportunity 4: We can now replace any element we want in our page via transclusion, and we can do so in any manner want.
So, with only seven relatively simple additional attributes, we have addressed most of the shortcomings of HTML as a
hypermedia that we identified earlier.
What's next? Recall the other shortcoming we noted: the fact that only a `click` event (on an anchor) or a `submit` event
What's next? Recall the one other opportunity we noted: the fact that only a `click` event (on an anchor) or a `submit` event
(on a form) can trigger a HTTP request. Let's look at how we can address that limitation.
== Using Events
@ -547,7 +553,7 @@ Note that we have a comma separated list of events that can trigger this element
one potential triggering event. We still want to respond to the `click` event and load the contacts, in addition
to handling the `Ctrl-L` keyboard shortcut.
There are, unfortunately, two problems with our `keyup` addition: As it stands, it will trigger requests on _any_ keyup
Unfortunately there are two problems with our `keyup` addition: As it stands, it will trigger requests on _any_ keyup
event that occurs. And, worse, it will only trigger when a keyup occurs _within_ this button. The
user would need to tab onto the button to make it active and then begin typing.
@ -625,7 +631,7 @@ outlined at the start of this chapter:
That's a grand total of eight, count 'em, _eight_ attributes that all fall squarely within the same conceptual model as
normal HTML and that, by extending HTML as a hypermedia, open up a whole new world of user interaction possibilities
within HTML.
within it.
Here is a table summarizing those opportunities and which htmx attributes address them:
@ -876,59 +882,7 @@ conceptually coherent with the underlying markup language. Like any technical c
trade-offs: by staying so close to HTML, htmx does not give developers a lot of infrastructure that many might feel
should be there "`by default`".
A good example is the concept of modal dialogs. Many web applications today make heavy use of modal dialogs, effectively
in-page pop-ups that sit "`on top`" of the existing page. (Of course, in reality, this is an optical illusion and it is
all just a web page: the web has no notion of "`modals`" in this regard.)
A web developer might expect htmx, as a front end library, to provide some sort of modal dialog component out of the box.
Htmx, however, has no such notion of modals. That's not to say you can't use modals with htmx, and we will look at how you
can do so later. But htmx, like HTML itself, won't give you an API specifically for creating modals. You
would need to use a 3rd party library or roll your own modal implementation and then integrate htmx into it if you want
to use modals within an htmx-based application.
By staying closer to the original model of the web, htmx aims to strike a balance between simplicity and functionality,
deferring to other libraries for more elaborate frontend extensions on top of the existing web platform. The good news
is that htmx plays well with others, so when these needs arise it is often easy enough to bring in another library to handle
them.
[.design-note]
.HTML Note: Caution with Improvised UI Elements
****
Accessibility problems can arise when we try to implement controls that aren't built into HTML.
For example, what if you make something that looks like a set of tabs, but you use radio buttons and CSS hacks to build it?
The problem here is that tabs have requirements beyond clicking to change content.
Your improvised tabs may be missing features that will lead to user confusion and frustration, as well as some undesirable behaviors.
From the link:https://www.w3.org/WAI/ARIA/apg/patterns/tabs/[ARIA Authoring Practices Guide on tabs]:
* Keyboard interaction
** Can the tabs be focused with the Tab key?
* ARIA roles, states, and properties
** "`[The element that contains the tabs] has role `tablist`.`"
** "`Each [tab] has role `tab` [...]`"
** "`Each element that contains the content panel for a `tab` has role `tabpanel`.`"
** "`Each [tab] has the property `aria-controls` referring to its associated tabpanel element.`"
** "`The active `tab` element has the state `aria-selected` set to `true` and all other `tab` elements have it set to `false`.`"
** "`Each element with role `tabpanel` has the property `aria-labelledby` referring to its associated `tab` element.`"
You would need to write a lot of code to make your improvised tabs fulfill all of these requirements. Some of the ARIA attributes can be added directly in HTML,
but they are repetitive
and others (like `aria-selected`) need to be set through JavaScript since they are dynamic.
The keyboard interactions can be error-prone too.
It's not impossible to make your own tab set implementation.
However, it's difficult to trust that a new implementation will work in all environments, since most of us have limited access to testing devices.
Stick with established libraries for UI interactions. If a use case requires an improvised solution, test carefully for keyboard interaction and accessibility.
****

View File

@ -1,7 +1,7 @@
= Htmx Patterns
:chapter: 05
:url: ./htmx-in-action/
:url: /htmx-in-action/
Now that we've seen how htmx extends HTML as a hypermedia, it's time to put it into action. As we use htmx, we will still
be using hypermedia: we will issue HTTP requests and get back HTML. But, with the additional functionality that htmx provides,
@ -255,10 +255,14 @@ support and so on. And, if JavaScript isn't enabled, it will fall back to the n
All this with one htmx attribute.
The `hx-boost` attribute is more "`magic`" than others. Htmx attributes generally are lower level and require more explicit
annotation in order to specify exactly what you want htmx to do. In general, this is the design philosophy of htmx:
prefer explicit to implicit and obvious to "`magic.`" However, the `hx-boost` attribute is too useful to allow dogma to
override practicality, and so it is included as a feature in the library.
The `hx-boost` attribute is neat, but is different than other htmx attributes in that it is pretty "`magical`": by
making one small change you modify the behavior of a large number of elements on the page, turning them into
AJAX-powered elements. Most other htmx attributes are generally lower level and require more explicit annotations in
order to specify exactly what you want htmx to do. In general, this is the design philosophy of htmx: prefer explicit
over implicit and obvious over "`magic.`"
However, the `hx-boost` attribute was too useful to allow dogma to override practicality, and so it is included as a
feature in the library.
== A Second Step: Deleting Contacts With HTTP DELETE
@ -308,9 +312,9 @@ A couple of things to notice:
Note that we have done something pretty magical here: we have turned this button into a _hypermedia control_. It is no
longer necessary that this button be placed within a larger `form` tag in order to trigger an HTTP request: it is a
stand-alone, and fully featured hypermedia control on its own. This is at the heart of htmx, allowing any element to become
a hypermedia control and fully participate in the Hypermedia-Driven Application.
a hypermedia control and fully participate in a Hypermedia-Driven Application.
We should note that, unlike with the `hx-boost` examples above, this solution will _not_ degrade gracefully. To make
We should also note that, unlike with the `hx-boost` examples above, this solution will _not_ degrade gracefully. To make
this solution degrade gracefully, we would need to wrap the button in a form element and handle a `POST` on the server
side as well.
@ -489,9 +493,9 @@ all while using declarative attributes in our HTML and staying firmly within the
=== Progressive Enhancement?
One thing to note about this solution, however, is that it is _not_ a progressive enhancement to our web application: if
someone has disabled JavaScript then this "`Delete Contact`" button will no longer work. We could do additional work to keep
the older mechanism working in a JavaScript-disabled environment.
As we noted earlier about this solution: it is _not_ a progressive enhancement to our web application: if
someone has disabled JavaScript then this "`Delete Contact`" button will no longer work. We would need to do additional
work to keep the older form-based mechanism working in a JavaScript-disabled environment.
Progressive Enhancement can be a hot-button topic in web development, with lots of passionate opinions and perspectives.
Like nearly all JavaScript libraries, htmx makes it possible to create applications that do not function in the absence of
@ -826,7 +830,7 @@ experience to our web application. Even better, any email validation rules we a
_automatically_ just work using this model: because we are using hypermedia as our communication mechanism there is no
need to keep a client-side and server-side model in sync with one another.
A great demonstration of the power of the hypermedia architecture.
A great demonstration of the power of the hypermedia architecture!
== Another Application Improvement: Paging
@ -838,7 +842,7 @@ all 10,000 contacts on the root page. Showing so much data can bog a browser (a
adopt a concept of "`paging`" to deal with data sets this large, where only one "`page`" of a smaller number of items is
shown, with the ability to navigate around the pages in the data set.
Let's fix our application, so that we only show ten contacts at a time with a "`Next`" and "`Previous`" link if there are more
Let's fix our application so that we only show ten contacts at a time with a "`Next`" and "`Previous`" link if there are more
than 10 contacts in the contact database.
The first change we will make is to add a simple paging widget to our `index.html` template.
@ -904,7 +908,7 @@ knows which page to return.
And, with that small change, we are done: we now have a very basic paging mechanism for our web application.
And, believe it or not, it is already using AJAX, thanks to our use of `hx-boost` in the application. Easy.
And, believe it or not, it is already using AJAX, thanks to our use of `hx-boost` in the application. Easy!
=== Click To Load
@ -1033,9 +1037,9 @@ behavior, we are still exchanging hypermedia with the server, with no JSON API r
As the web was designed.
// TODO dz4k rewrite
[.design-note]
.Component Soup?
.HTML Notes: Framework Soup
****
Components encapsulate a section of a page along with its dynamic behavior.
While encapsulating behavior is a good way to organize code,
@ -1049,17 +1053,14 @@ Before you reach for components for reuse, consider your options.
Lower-level mechanisms often (allow you to) produce better HTML.
In some cases, components can actually _improve_ the clarity of your HTML.
To decide if a component is appropriate for your use case, a good rule of thumb is to ask:
"`Could this reasonably be a built-in HTML element?`"
For example, a code editor is a good candidate,
since HTML already has `<textarea>` and `contenteditable` elements.
In addition, a fully-featured code editor will have many child elements that won't provide much information anyway.
We can use features like
link:https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM[Shadow DOM]
to encapsulate these elementsfootnote:[
Beware that Shadow DOM is a newer web platform feature that's still in development at the time of writing.
In particular, there are some accessibility bugs that may occur when elements inside and outside the shadow root interact.].
We can create a
link:https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements[custom element],
`<code-area>`, that we can drop into our page whenever we want.
****
[quote, Manuel Matuzović, 'https://www.matuzo.at/blog/2023/single-page-applications-criticism[Why I\'m not the biggest fan of Single Page Applications]']
The fact that the HTML document is something that you barely touch, because everything you need in there will be injected via JavaScript, puts the document and the page structure out of focus.
In order to avoid `<div>` soup (or Markdown soup, or Component soup), you need to be aware of the markup you're producing and be able to change it.
Some SPA frameworks, and some web components, make this more difficult by putting layers of abstraction between the code the developer writes and the generated markup.
While these abstractions can allow developers to create richer UI or work faster,
their pervasiveness means that developers can lose sight of the actual HTML (and JavaScript) being sent to clients.
Without diligent testing, this leads to inaccessibility, poor SEO, and bloat.
****

View File

@ -1,7 +1,7 @@
= More Htmx Patterns
:chapter: 06
:url: ./more-htmx-patterns/
:url: /more-htmx-patterns/
== Active Search
@ -204,27 +204,21 @@ It turns out that htmx helps us distinguish between these two cases by including
it makes requests. Request Headers are a feature of HTTP, allowing clients (e.g., web browsers) to include name/value pairs
of metadata associated with requests to help the server understand what the client is requesting.
Here is an example of (some of) the headers the FireFox browser issues when requesting `https://manning.com`:
Here is an example of (some of) the headers the FireFox browser issues when requesting `https://hypermedia.systems`:
.HTTP headers
[source,http]
----
GET / HTTP/2
Host: www.manning.com
Host: hypermedia.systems
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:103.0) Gecko/20100101 Firefox/103.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
DNT: 1
Accept-Language: en-US,en;q=0.5
Cache-Control: no-cache
Connection: keep-alive
Cookie: ...
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Sec-GPC: 1
TE: trailers
DNT: 1
Pragma: no-cache
----
Htmx takes advantage of this feature of HTTP and adds additional headers and, therefore, additional _context_ to the
@ -921,7 +915,7 @@ dramatically different behavior. Hypermedia is powerful in this manner.
=== The Htmx Swapping Model
This is pretty cool, but there is another improvement we can make if we take some time to understand the htmx content
swapping model: it would nice if, rather than just instantly deleting the row, we faded it out before we removed
swapping model: it would be nice if, rather than just instantly deleting the row, we faded it out before we removed
it. The fade would make it clear that the row is being removed, giving the user some nice visual feedback on the
deletion.
@ -1089,7 +1083,7 @@ By default, if an element is a child of a `form` element and makes a non-`GET` r
inputs within that form. In situations like this, where there is a bulk operation for a table, it is common to enclose
the whole table in a form tag, so that it is easy to add buttons that operate on the selected items.
Let's add that form tag around the form, and be sure to enclose the button in it as well:
Let's add that form tag around the table, and be sure to enclose the button in it as well:
.The "`delete selected contacts`" button
[source, html]
@ -1171,7 +1165,7 @@ By using these elements, a page can make false promises, like `<article>` elemen
The most authoritative resource for learning about HTML is the HTML specification.
The current specification lives on link:https://html.spec.whatwg.org/multipage[].footnote:[
The single-page version is too slow to load and render on most computers.
There's also a developers' edition at /dev, but I prefer the styling of the standard version.]
There's also a developers' edition at /dev, but the standard version has nicer styling.]
There's no need to rely on hearsay to keep up with developments in HTML.
Section 4 features a list of all available elements,

View File

@ -1,7 +1,7 @@
= A Dynamic Archive UI
:chapter: 07
:url: ./a-dynamic-archive-ui/
:url: /a-dynamic-archive-ui/
== A Dynamic Archive UI
@ -634,33 +634,81 @@ pure hypermedia. It took about 16 lines of front end code and 16 lines of backen
HTML, with a bit of help from a hypermedia-oriented JavaScript library such as htmx, can in fact be extremely powerful and expressive.
// TODO: work here? better as a vanilla pullout?
[.design-note]
.HTML Notes: On Web Components
.HTML Notes: Markdown soup
****
Web Components is the collective name of a few standards;
Custom Elements and Shadow DOM, and `<template>` and `<slot>`.
[.dfn]_Markdown soup_ is the lesser known sibling of `<div>` soup.
This is the result of web developers limiting themselves to the set of elements that the Markdown language provides shorthand for,
even when these elements are incorrect.
More seriously, it's important to be aware of the full power of our tools, including HTML.
Consider the following example of an IEEE-style citation:
All of these standards bring useful capabilities to the table.
`<template>` elements remove their contents from the document, while still parsing them as HTML (unlike comments) and making them accessible to JavaScript.
Custom Elements let us initialize and tear down behaviors when elements are added or removed, which would previously require manual work or MutationObservers.
Shadow DOM lets us encapsulate elements, leaving the "light" (non-shadow) DOM clean.
[source,markdown]
----
[1] C.H. Gross, A. Stepinski, and D. Akşimşek, <1>
_Hypermedia Systems_, <2>
Bozeman, MT, USA: Big Sky Software.
Available: <https://hypermedia.systems/>
----
<1> The reference number is written in brackets.
<2> Underscores around the book title creates an <em> element.
However, trying to reap these benefits is often frustrating.
Some difficulties are simply growing pains of new standards
(like the accessibility problems of Shadow DOM)
that are actively being worked on.
Others are the result of Web Components trying to be too many things at the same time:
Here, <em> is used because it's the only Markdown element that is presented in italics by default.
This indicates that the book title is being stressed, but the purpose is to mark it as the title of a work.
HTML has the `<cite>` element that's intended for this exact purpose.
* An extension mechanism for HTML. To this end, each custom element is a tag we add to the language.
* A lifecycle mechanism for behaviors. Methods like `createdCallback`, `connectedCallback`, etc. allow behavior to be added to elements without needing to be manually invoked when those elements are added.
* A unit of encapsulation. Shadow DOM insulates elements from their surroundings.
Furthermore, even though this is a numbered list perfect for the `<ol>` element, which Markdown supports, plain text is used for the reference numbers instead.
Why could this be?
The IEEE citation style requires that these numbers are presented in square brackets.
This could be achieved on an `<ol>` with CSS,
but Markdown doesn't have a way to add a class to elements meaning the square brackets would apply to all ordered lists.
The result is that if you want any one of these things,
the others come along for the ride.
If you want to attach some behaviors to some elements using lifecycle callbacks,
you need to create a new tag,
which means you can't have multiple behaviors on one element,
and you isolate elements you add from elements already in the page,
which is a problem if they need to have ARIA relationships.
****
Don't shy away from using embedded HTML in Markdown.
For larger sites, also consider Markdown extensions.
[source,markdown]
----
{.ieee-reference-list} <1>
1. C.H. Gross, A. Stepinski, and D. Akşimşek, <2>
<cite>Hypermedia Systems</cite>, <3>
Bozeman, MT, USA: Big Sky Software.
Available: <https://hypermedia.systems/>
----
<1> Many Markdown dialects let us add ids, classes and attributes using curly braces.
<2> We can now use the <ol> element, and create the brackets in CSS.
<3> We use `<cite>` to mark the title of the work being cited (not the whole citation!)
You can also use custom processors to produce extra-detailed HTML instead of writing it by hand:
[source,markdown]
----
{% reference_list %} <1>
[hypers2023]: <2>
C.H. Gross, A. Stepinski, and D. Akşimşek, _Hypermedia Systems_,
Bozeman, MT, USA: Big Sky Software, 2023.
Available: <https://hypermedia.systems/>
{% end %}
----
<1> `reference_list` is a macro that will transform the plain text to highly-detailed HTML.
<2> A processor can also resolve identifiers, so we don't have to manually keep the reference list in order and the in-text citations in sync.
****
[.design-note]
.HTML Notes: Budgeting For HTML
****
The close relationship between content and markup means that
good HTML is labor-intensive.
Most sites have a separation between the authors,
who are rarely familiar with HTML,
and the developers, who need to develop a generic system able to handle any content that's thrown at it --
this separation usually taking the form of a CMS.
As a result, having markup tailored to content, which is often necessary for advanced HTML, is rarely feasible.
Furthermore, for internationalized sites, content in different languages being injected into the same elements can degrade markup quality as stylistic conventions differ between languages.
It's an expense few organizations can spare.
Thus, we don't expect every site to contain perfectly conformant HTML.
What's most important is to avoid _wrong_ HTML -- it can be better to fall back on a more generic element than to be precisely incorrect.
If you have the resources, however, putting more care in your HTML will produce a more polished site.
****

View File

@ -1,7 +1,7 @@
= Tricks Of The Htmx Masters
:chapter: 08
:url: ./deep-htmx/
:url: /deep-htmx/
[partintro]
@ -53,8 +53,9 @@ In chapter 5, we also looked at the `swap` delay modifier for `hx-swap`, which a
In addition to these, `hx-swap` offers further control with the following modifiers:
`settle`::
Like `swap`, this allows you to apply a specific delay between when the content has been swapped into the DOM and when its attributes are "`settled`", that is, updated from their old values (if any) to their new values.
// TODO: check: This gives you fine control of css-transitions, for example.
Like `swap`, this allows you to apply a specific delay between when the content has been swapped into the DOM and when
its attributes are "`settled`", that is, updated from their old values (if any) to their new values. This can give you
fine-grained control over CSS transitions.
`show`::
Allows you to specify an element that should be shown -- that is, scrolled into the viewport of the browser if necessary -- when a request is completed.
@ -297,14 +298,12 @@ Consider the following
scenario: your server-side team has decided that they want you to include a token for extra validation on every request.
The token is going to be stored in `localStorage` in the browser, in the slot `special-token`. The server-side team
wants you to include this special token on every request made by htmx, as the `X-SPECIAL-TOKEN` header.
// TODO: check: maybe, briefly show how to set the value in local storage
// TODO 1cg: check: maybe, briefly show how to set the value in local storage
How could you achieve this? One way would be to catch the `htmx:configRequest` event and update the `detail.headers`
object with this token from `localStorage`.
In VanillaJS, it would look something like this:
In VanillaJS, it would look something like this, placed in a `<script>` tag in the `<head>` of our HTML document:
// TODO: check: In our Contact.app, for example, we would put this code... (where?)
// Mention htmx helper for adding listeners like this?
.Adding the `X-SPECIAL-TOKEN` header
[source,js]
----
@ -316,15 +315,15 @@ document.body.addEventListener("htmx:configRequest", function(configEvent){
As you can see, we add a new value to the `headers` property of the event's detail. After the event handler executes,
the `headers` property is read by htmx and used to construct the headers for an AJAX request.
// TODO: check: add basic info, is configEvent.detail.headers an htmx function?
// TODO: check: briefly explain what a header 'detail' refers to
// TODO 1cg: check: add basic info, is configEvent.detail.headers an htmx function?
// TODO 1cg: check: briefly explain what a header 'detail' refers to
So, with this bit of
JavaScript code, we have added a new custom header to every AJAX request that htmx makes. Slick!
// TODO: check: explain the use case, something like 'this pattern of passing
// TODO 1cg: check: explain the use case, something like 'this pattern of passing
// and checking tokens is sometimes used for security'
You can also update the `parameters` property to change the parameters submitted by the request, change the target
of the request, and so on.
// TODO: check: an example parameter might be...
// TODO 1cg: check: an example parameter might be...
Full documentation for the `htmx:configRequest` event can be found
https://htmx.org/events/#htmx:configRequest[on the htmx website].
@ -397,9 +396,12 @@ a `POST` to the `/integrations/1` path, it will trigger a synchronization with t
Now, this synchronization may or may not result in new contacts being created. In the case where new contacts _are_
created, we want to refresh our contacts table. In the case where no contacts are created, we don't want to refresh
the table.
// TODO: check: show brief code, how to conditionally add HX-Trigger
To implement this we could conditionally add an `HX-Trigger`
response header with the value `contacts-updated`. This value would trigger the `contacts-updated` event on the button that
To implement this we could conditionally add an `HX-Trigger` response header with the value `contacts-updated`:
// TODO 1cg: check: show brief code, how to conditionally add HX-Trigger
This value would trigger the `contacts-updated` event on the button that
made the AJAX request to `/integrations/1`. We can then take advantage of the `from:` modifier of the `hx-trigger`
attribute to listen for that event. With this pattern we can effectively trigger htmx requests from the server side.
@ -732,7 +734,7 @@ Htmx is typically configured via a `meta` tag, found in the header of a page. T
an example:
.An htmx configuration via `meta` tag
[source, javascript]
[source, html]
----
<meta name="htmx-config" content='{"defaultSwapStyle":"outerHTML"}'>
----
@ -741,24 +743,28 @@ In this case, we are overriding the default swap style from the usual `innerHTML
if you find yourself using `outerHTML` more frequently than `innerHTML` and want to avoid having to explicitly set that
swap value throughout your application.
[.design-note]
.HTML Notes: Know Your HTML Budget
.HTML Notes: "'Display: none'"
****
The close relationship between content and markup means that
good HTML is labor-intensive.
Most sites have a separation between the authors,
who are rarely familiar with HTML,
and the developers, who need to develop a generic system able to handle any content that's thrown at it --
this separation usually taking the form of a CMS.
As a result, having markup tailored to content, which is often necessary for advanced HTML, is rarely feasible.
Sadly, not even good HTML-- or HXML -- can cure all ills.
If you care about machine readability, or human readability, or page weight, the most important thing to do is _testing_.
Test manually.
Test automatically.
Test with screenreaders, test with a keyboard, test on different browsers and hardware, and run linters (while coding and/or in CI).
Furthermore, for internationalized sites, content in different languages being injected into the same elements can degrade markup quality as stylistic conventions differ between languages.
It's an expense few organizations can spare.
One common problem is with the use of `display: none;` in CSS. The issue is that it is not purely cosmetic -- it also removes elements from the accessibility tree and keyboard focus. This is often desirable to present the same content to visual and aural interfaces. If you want to hide an element visually without hiding it from assistive technology (e.g. the element contains information that is communicated through styling), you can use this utility class:
Thus, we don't expect every site to contain perfectly conformant HTML.
What's most important is to avoid _wrong_ HTML -- it can be better to fall back on a more generic element than to be precisely incorrect.
[source,css]
----
.vh {
clip: rect(0 0 0 0);
clip-path: inset(50%);
block-size: 1px;
inline-size: 1px;
overflow: hidden;
white-space: nowrap;
}
----
If you have the resources, however, putting more care in your HTML will produce a more polished site.
`vh` is short for "`visually hidden.`" This class uses multiple methods and workarounds to make sure no browser removes the element's function.
****

View File

@ -1,7 +1,7 @@
= Client-Side Scripting
:chapter: 09
:url: ./client-side-scripting/
:url: /client-side-scripting/
[quote, Roy Fielding, Architectural Styles and the Design of Network-based Software Architectures]
REST allows client functionality to be extended by downloading and executing code in the form of applets or scripts.
@ -746,12 +746,15 @@ To get a flavor of AlpineJS, let's look at how to implement our counter example
For the counter, the only state we need to keep track of is the current number, so let's declare a JavaScript object
with one property, `count`, in an `x-data` attribute on the div for our counter:
// TODO: check: removed class="counter" to avoid confusion
// TODO wt (please restore, used in scripting): check: removed class="counter" to avoid confusion
.Counter with Alpine, line 1
[source,html]
----
<div x-data="{ count: 0 }">
----
----
This defines our state, that is, the data we are going to be using to drive dynamic updates to the DOM. With the state
declared like this, we can now use it _within_ the div element it is declared on. Let's add an `output` element with
an `x-text` attribute.
@ -848,7 +851,8 @@ Recall that we want to show the toolbar if and only if one or more contacts are
have the ids of the selected contacts in the `selected` property. Therefore, we can check the _length_ of that array
to see if there are any selected contacts, quite easily:
// TODO: were we going to have a selected count in the toolbar too?
// DONE dz4k: were we going to have a selected count in the toolbar too?
// yes? -- dz4k
[source,html]
----
<template x-if="selected.length > 0"> <1>
@ -1027,11 +1031,9 @@ from using DOM-specific, non-natural language when appropriate.
=== +_hyperscript+ in Action: A Keyboard Shortcut
// TODO: alt-S instead? shift-S too aggressive?
While the counter demo is a good way to compare various approaches to scripting, the rubber meets the road when
you try to actually implement a useful feature with an approach. For +_hyperscript+, let's add a keyboard shortcut
to Contact.app: when a user hits Shift-S in our app, we will focus the search field.
to Contact.app: when a user hits Alt+S in our app, we will focus the search field.
Since our keyboard shortcut focuses the search input, let's put the code for it on that search input, satisfying
locality.
@ -1046,8 +1048,8 @@ Here is the original HTML for the search input:
We will add an event listener using the `on keydown` syntax, which will fire whenever a keydown occurs. Further, we
can use an _event filter_ syntax in +_hyperscript+ using square brackets after the event. In the square brackets we
can place a _filter expression_ that will filter out `keydown` events we aren't interested in. In our case, we only
want to consider events where the shift key is held down and where the "`S`" key is being pressed. We can create a
boolean expression that inspects the `shiftKey` property (to see if it is `true`) and the `code` property (to see if
want to consider events where the Alt key is held down and where the "`S`" key is being pressed. We can create a
boolean expression that inspects the `altKey` property (to see if it is `true`) and the `code` property (to see if
it is `"KeyS"`) of the event to achieve this.
So far our +_hyperscript+ looks like this:
@ -1055,7 +1057,7 @@ So far our +_hyperscript+ looks like this:
.A start on our keyboard shortcut
[source, hyperscript]
----
on keydown[shiftKey and code is 'KeyS'] ...
on keydown[altKey and code is 'KeyS'] ...
----
Now, by default, +_hyperscript+ will listen for a given event _on the element where it is declared_. So, with the script we have, we would only get `keydown` events if the search box is already focused. That's not what
@ -1067,7 +1069,7 @@ case we want to listen for the `keyDown` from the window, and our code ends up l
.Listening globally
[source, hyperscript]
----
on keydown[shiftKey and code is 'KeyS'] from window ...
on keydown[altKey and code is 'KeyS'] from window ...
----
Using the `from` clause, we can attach the listener to the window while, at the same time, keeping the code on the
@ -1082,7 +1084,7 @@ Here is the entire script, embedded in HTML:
[source,html]
----
<input id="search" name="q" type="search" placeholder="Search Contacts"
_="on keydown[shiftKey and code is 'KeyS'] from the window
_="on keydown[altKey and code is 'KeyS'] from the window
me.focus()"> <1>
----
<1> "`me`" refers to the element that the script is written on.
@ -1141,6 +1143,50 @@ libraries go beyond simple DOM manipulation, and require that you integrate with
with a JSON data API. This means you are no longer building a Hypermedia-Driven Application, simply because a particular
widget demands something different. A shame!
.Web Components/Custom Elements
****
Web Components is the collective name of a few standards;
Custom Elements and Shadow DOM, and `<template>` and `<slot>`.
All of these standards bring useful capabilities to the table.
`<template>` elements remove their contents from the document, while still parsing them as HTML (unlike comments) and making them accessible to JavaScript.
Custom Elements let us initialize and tear down behaviors when elements are added or removed, which would previously require manual work or MutationObservers.
Shadow DOM lets us encapsulate elements, leaving the "light" (non-shadow) DOM clean.
However, trying to reap these benefits is often frustrating.
Some difficulties are simply growing pains of new standards
(like the accessibility problems of Shadow DOM)
that are actively being worked on.
Others are the result of Web Components trying to be too many things at the same time:
* An extension mechanism for HTML. To this end, each custom element is a tag we add to the language.
* A lifecycle mechanism for behaviors. Methods like `createdCallback`, `connectedCallback`, etc. allow behavior to be added to elements without needing to be manually invoked when those elements are added.
* A unit of encapsulation. Shadow DOM insulates elements from their surroundings.
The result is that if you want any one of these things,
the others come along for the ride.
If you want to attach some behaviors to some elements using lifecycle callbacks,
you need to create a new tag,
which means you can't have multiple behaviors on one element,
and you isolate elements you add from elements already in the page,
which is a problem if they need to have ARIA relationships.
When should we use Web Components?
A good rule of thumb is to ask yourself:
"`Could this reasonably be a built-in HTML element?`"
For example, a code editor is a good candidate,
since HTML already has `<textarea>` and `contenteditable` elements.
In addition, a fully-featured code editor will have many child elements that won't provide much information anyway.
We can use features like
link:https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM[Shadow DOM]
to encapsulate these elementsfootnote:[
Beware that Shadow DOM is a newer web platform feature that's still in development at the time of writing.
In particular, there are some accessibility bugs that may occur when elements inside and outside the shadow root interact.].
We can create a
link:https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements[custom element],
`<code-area>`, that we can drop into our page whenever we want.
****
=== Integration Options
The best JavaScript libraries to work with when you are building a Hypermedia-Driven Application are ones that:
@ -1303,32 +1349,58 @@ And even on these topics, sometimes a web developer has to do what a web develop
Just don't make it a habit.
[.design-note]
.HTML Notes: Markdown soup
.HTML Notes: Accessible by Default?
****
[.dfn]_Markdown soup_ is the lesser known sibling of `<div>` soup.
This is the result of web developers limiting themselves to the set of elements that the Markdown language provides shorthand for,
even when these elements are incorrect.
Consider the following example of an IEEE-style citation:
Accessibility problems can arise when we try to implement controls that aren't built into HTML.
[source,markdown]
For example, what if you make something that looks like a set of tabs, but you use radio buttons and CSS hacks to build it? It's a neat hack that makes the rounds in web development communities from time to time.
The problem here is that tabs have requirements beyond clicking to change content.
Your improvised tabs may be missing features that will lead to user confusion and frustration, as well as some undesirable behaviors.
From the link:https://www.w3.org/WAI/ARIA/apg/patterns/tabs/[ARIA Authoring Practices Guide on tabs]:
* Keyboard interaction
** Can the tabs be focused with the Tab key?
* ARIA roles, states, and properties
** "`[The element that contains the tabs] has role `tablist`.`"
** "`Each [tab] has role `tab` [...]`"
** "`Each element that contains the content panel for a `tab` has role `tabpanel`.`"
** "`Each [tab] has the property `aria-controls` referring to its associated tabpanel element.`"
** "`The active `tab` element has the state `aria-selected` set to `true` and all other `tab` elements have it set to `false`.`"
** "`Each element with role `tabpanel` has the property `aria-labelledby` referring to its associated `tab` element.`"
You would need to write a lot of code to make your improvised tabs fulfill all of these requirements. Some of the ARIA attributes can be added directly in HTML,
but they are repetitive
and others (like `aria-selected`) need to be set through JavaScript since they are dynamic.
The keyboard interactions can be error-prone too.
It's not impossible, not even that hard, to make your own tab set implementation.
However, it's difficult to trust that a new implementation will work for all users in all environments, since most of us have limited resporces for testing.
*Stick with established libraries* for UI interactions. If a use case requires a bespoke solution, *test exhaustively* for keyboard interaction and accessibility.
Also consider: Does the information need to be presented as tabs?
Sometimes the answer is yes, but if not, a sequence of details and disclosures fulfills a very similar purpose.
[source,html]
----
[1] C.H. Gross, A. Stepinski, and D. Akşimşek, <1>
_Hypermedia Systems_, <2>
Bozeman, MT, USA: Big Sky Software.
Available: <https://hypermedia.systems/>
<details><summary>Disclosure 1</summary>
Disclosure 1 contents
</details>
<details><summary>Disclosure 2</summary>
Disclosure 2 contents
</details>
----
<1> The reference number is written in brackets.
<2> Underscores around the book title creates an <em> element.
Here, <em> is used because it's the only Markdown element that is presented in italics by default.
This indicates that the book title is being stressed, but the purpose is to mark it as the title of a work.
HTML has the `<cite>` element that's intended for this exact purpose.
Furthermore, even though this is a numbered list perfect for the `<ol>` element, which Markdown supports, plain text is used for the reference numbers instead.
Why could this be?
The IEEE citation style requires that these numbers are presented in square brackets.
This could be achieved on an `<ol>` with CSS,
but Markdown doesn't have a way to add a class to elements meaning the square brackets would apply to all ordered lists.
Compromising UX just to avoid JavaScript is bad development.
But sometimes it's possible to achieve an equal (or better!) quality of UX while allowing for a simpler and more robust implementation.
****

View File

@ -1,7 +1,7 @@
= JSON Data APIs & Hypermedia-Driven Applications
:chapter: 10
:url: ./json-data-apis/
:url: /json-data-apis/
[partintro]
== JSON Data APIs
@ -96,7 +96,7 @@ By supporting both of these types of APIs separately you can get the strengths o
styles of code and infrastructure needs cleanly split out.
Let's contrast the needs of JSON APIs with Hypermedia APIs:
// TODO: check: row spacing?
[cols="a,a"]
|===
|JSON API Needs |Hypermedia API
@ -504,26 +504,12 @@ another.
With properly built "`thin`" controllers and "`fat`" models, keeping two separate APIs both in sync and yet
still evolving separately is not as difficult or as crazy as it might sound.
// TODO: does some version of this fit here?
// or, cut if you don't recommend using
[.design-note]
.HTML Notes: Microformats
****
In some cases you may want to include machine-readable structured data in HTML.
https://microformats.org/[Microformats] is one standard for doing so.
It uses classes to mark certain elements as containing information to be extracted.
The microformats2 standard uses five kinds of classes:
* `h-` classes denote that an element represents a machine-readable entity, e.g., `h-entry`, `h-resume`
* The other prefixes denote that an element represents properties of an enclosing entity:
** `p-` classes are plain text properties, from an element's inner text or `alt` attribute, e.g., `p-name`, `p-category`
** `u-` classes are URL properties, from an element's `href` or `src`, e.g., `u-url`, `u-email`, `u-photo`
** `dt-` classes are date/time properties, from `<time>` elements, e.g., `dt-published`, `dt-updated`
** `e-` classes are embedded markup properties, from an element's inner HTML, e.g., `e-content`, `e-description`
There are also conventions for extracting common properties like name, URL and photo without needing classes for each property.
https://microformats.org/[Microformats] is a standard for embedding machine-readable structured data in HTML.
It uses classes to mark certain elements as containing information to be extracted,
with conventions for extracting common properties like name, URL and photo without classes.
By adding these classes into the HTML representation of an object, we allow the properties of the object to be recovered from the HTML. For example, this simple HTML:
[source,html]
@ -533,8 +519,7 @@ By adding these classes into the HTML representation of an object, we allow the
</a>
----
can be parsed into this JSON-like structure:
// maybe: briefly describe how
can be parsed into this JSON-like structure by a microformats parser:
[source,json]
@ -544,7 +529,6 @@ can be parsed into this JSON-like structure:
"properties": {
"name": ["John Doe"],
"photo": ["john.jpg"],
""
"url": ["https://john.example"]
}
}
@ -552,9 +536,14 @@ can be parsed into this JSON-like structure:
Using a variety of properties and nested objects, we could mark up every bit of information about a contact, for example, in a machine-readable way.
// maybe: rule of thumb for when to use a microformat
// Would this be worthwhile for contact.app?
Microformats and the extensibility of HTML can prove useful, but embedding data in HTML is not appropriate for every use case.
As explained in the above chapter, trying to use the same mechanism for human and machine interaction is not a good idea.
Your human-facing and machine-facing interfaces may end up being limited by each other.
It's often the best option to define a JSON data API separate from your HTML.
If you want to expose domain-specific data and actions to users and developers, a JSON API is a great option.
However, microformats are way easier to adopt.
A protocol or standard that requires websites to implement a JSON API has a high technical barrier.
In comparison, any website can be augmented with microformats simply by adding a few classes.
Other HTML-embedded data formats like microdata, Open Graph are similarly easy to adopt.
This makes microformats useful for cross-website (dare we say _web-scale_) systems like the https://indieweb.org[IndieWeb], which uses it pervasively.
****

View File

@ -2,8 +2,8 @@
= Hyperview: A Mobile Hypermedia
:chapter: 11
:part: Bringing Hypermedia To Mobile
:part_url: ./part/hyperview/
:url: ./hyperview-a-mobile-hypermedia/
:part_url: /part/hyperview/
:url: /hyperview-a-mobile-hypermedia/
You may be forgiven for thinking the hypermedia architecture is synonymous with the Web, web browsers, and HTML.
No doubt, the Web is the largest hypermedia system, and web browsers are the most popular hypermedia client.
@ -14,7 +14,7 @@ Mobile as a platform has different constraints than the Web.
It requires different trade-offs and design decisions.
Nonetheless, the concepts of hypermedia, HATEOAS, and REST can be directly applied to build delightful mobile applications.
// TODO: chapter overview changed to fit other chapters. okay?
// TODO astep: chapter overview changed to fit other chapters. okay?
In this chapter we will cover shortcomings with the current state of mobile app development, and how a hypermedia architecture can address these problems. We will then look at a path toward hypermedia on mobile: Hyperview, a mobile app framework that uses the hypermedia architecture. We'll conclude with an overview of HXML, the hypermedia format used by Hyperview.
== The State of Mobile App Development
@ -235,7 +235,7 @@ But as we will show at the end of this chapter, it doesn't take a lot of work to
But before we get there, we need to introduce the concepts of elements and behaviors in Hyperview.
Then, we'll re-build our contacts app in Hyperview.
//TODO: consider advice to web developers wanting to go mobile
//TODO astep: consider advice to web developers wanting to go mobile
// i.e., beyond the hypermedia concept, is this a path you recommend
// when compared to alternatives? Avoid any sense of promotion
@ -1241,7 +1241,7 @@ These concepts can be mixed together too.
It's not unusual for a production Hyperview app to contain several behaviors, some triggering together and others triggering on different interactions.
Using multiple behaviors with custom actions keeps HXML declarative, without sacrificing functionality.
// TODO: keep? reformat?
// TODO astep: keep? reformat?
We're covering a lot of new material, so we'll add brief summaries to our sprint through Hyperview and HXML.
.Summary
@ -1256,7 +1256,7 @@ We're covering a lot of new material, so we'll add brief summaries to our sprint
- New actions can be added to HXML using namespaced attributes. The Hyperview client can be easily extended to interpret the new actions.
****
// TODO: suggested conclusion, transition
// TODO astep: suggested conclusion, transition
== Hypermedia, for Mobile
There is a story for Hypermedia-Driven Applications on mobile. Mobile app platforms push developers towards a thick-client architecture. But apps that use a thick client suffer from the same problems as SPAs on the web. Using the hypermedia architecture for mobile apps can solve these problems.
@ -1264,38 +1264,3 @@ There is a story for Hypermedia-Driven Applications on mobile. Mobile app platfo
Hyperview, based on a new format called HXML, offers a path here. It provides an open-source mobile thin-client to render HXML. And HXML opens a took-kit of elements and patterns that correspond to mobile UIs. Developers can evolve Hyperview to suit their apps' requirements, while fully embracing the hypermedia architecture. That's a win.
Yes, hypermedia can work for mobile apps, too. In the next two chapters we'll show how by moving Contact.app to mobile.
[.design-note]
.HTML Notes: Embedding HTML in Markdown
****
Don't shy away from using embedded HTML in Markdown.
For larger sites, also consider Markdown extensions.
[source,markdown]
----
{.ieee-reference-list} <1>
1. C.H. Gross, A. Stepinski, and D. Akşimşek, <2>
<cite>Hypermedia Systems</cite>, <3>
Bozeman, MT, USA: Big Sky Software.
Available: <https://hypermedia.systems/>
----
<1> Many Markdown dialects let us add ids, classes and attributes using curly braces.
<2> We can now use the <ol> element, and create the brackets in CSS.
<3> We use `<cite>` to mark the title of the work being cited (not the whole citation!)
You can also use custom processors to produce extra-detailed HTML instead of writing it by hand:
[source,markdown]
----
{% reference_list %} <1>
[hypers2023]: <2>
C.H. Gross, A. Stepinski, and D. Akşimşek, _Hypermedia Systems_,
Bozeman, MT, USA: Big Sky Software, 2023.
Available: <https://hypermedia.systems/>
{% end %}
----
<1> `reference_list` is a macro that will transform the plain text to highly-detailed HTML.
<2> A processor can also resolve identifiers, so we don't have to manually keep the reference list in order and the in-text citations in sync.
****

View File

@ -1,7 +1,7 @@
= Building a Contacts App With Hyperview
:chapter: 12
:url: ./building-a-contacts-app-with-hyperview/
:url: /building-a-contacts-app-with-hyperview/
Earlier chapters in this book explained the benefits of building apps using the hypermedia architecture.
@ -841,7 +841,7 @@ Luckily, there's a solution to this problem in Hyperview: events.
Events are built into the behavior system, and allow lightweight communication between different parts of the UI.
// TODO: consider an intro to events as 'html note' topic, earlier chapters
// TODO 1cg: consider an intro to events as 'html note' topic, earlier chapters
.Event Behaviors
****
Events are a client-side feature of Hyperview.
@ -849,7 +849,7 @@ Earlier chapter discussed events while working with HTML, htmx and the DOM.
DOM Elements will dispatch events as a result of user interactions.
Scripts can listen for these events, and respond to them by running arbitrary JavaScript code.
We saw some examples in chapter 9.
// TODO: optional, add link to an example
// TODO astep: optional, add link to an example
Events in Hyperview are a good deal simpler, but they don't require any scripting and can be defined declaratively in the HXML.
This is done through the behavior system.
@ -1117,7 +1117,7 @@ Any time you make a change in the HXML, just reload the screen to see the UI upd
So we have the app running on a physical device, but it's still not production ready.
To get the app into the hands of our users, there's a few things we need to do:
// TODO: mention UI, presentation here?
// TODO astep: optional mention UI, presentation in this list (e.g. styling, etc)?
1. Deploy our backend in production.
We need to use a production-grade web server like Gunicorn instead of the Flask development server.
And we should run our app on a machine reachable on the Internet, most likely using a cloud provider like AWS or Heroku.
@ -1332,25 +1332,3 @@ Since our HXML templates don't expose any entrypoints to the Archive functionali
We've covered a lot of ground in this chapter. Take a breath and take stock of how far we've come: we ported the core functionality of the Contact.app web application to mobile. And we did it by re-using much of our Flask backend and while sticking with Jinja templating. We again saw the utility of events for connecting different aspects of an application.
We're not done yet. In the next chapter we'll implement custom behaviors and UI elements to finish our mobile Contact.app.
// TODO: okay, or better as integrated info gem?
[.design-note]
.HTML Notes: Remember <details>
****
Does the information need to be presented as tabs?
Sometimes the answer is yes, but if not, a sequence of details and disclosures fulfills a very similar purpose.
[source,html]
----
<details><summary>Disclosure 1</summary>
Disclosure 1 contents
</details>
<details><summary>Disclosure 2</summary>
Disclosure 2 contents
</details>
----
Compromising UX just to avoid JavaScript is bad development.
But sometimes it's possible to achieve an equal (or better!) quality of UX while allowing for a simpler and more robust implementation.
****

View File

@ -1,7 +1,7 @@
= Extending the Hyperview Client
:chapter: 13
:url: ./extending-the-hypermedia-client/
:url: /extending-the-hypermedia-client/
In the previous chapter, we created a fully-featured native mobile version of our Contacts app.
@ -726,37 +726,9 @@ And importantly, this evolution preserves the Hypermedia, server-driven architec
****
//TODO: conclusion okay? add?
//TODO astep: wc wrote a basic conclusion here, is it okay? add more?
=== Mobile Hypermedia-Driven Applications
That concludes our build of mobile Contact.app. Step back from the code details and consider the broader pattern: the Hypermedia-Driven Application architecture allowed for signicant code reuse, and led to a manageable stack for ongoing app updates and maintenance for both web and mobile.
Yes, there is a story for Hypermedia-Driven Applications on mobile.
[.design-note]
.HTML Note: "'Display: none'"
****
Sadly, not even good HTML-- or HXML -- can cure all ills.
If you care about machine readability, or human readability, or page weight, the most important thing to do is _testing_.
Test manually.
Test automatically.
Test with screenreaders, test with a keyboard, test on different browsers and hardware, and run linters (while coding and/or in CI).
// TODO: okay here? not sure where to put this
One common problem is with the use of `display: none;` in CSS. The issue is that it is not purely cosmetic -- it also removes elements from the accessibility tree and keyboard focus. If you want to hide an element visually without hiding it from assistive technology, you can use this utility class:
[source,css]
----
.vh {
clip: rect(0 0 0 0);
clip-path: inset(50%);
block-size: 1px;
inline-size: 1px;
overflow: hidden;
white-space: nowrap;
}
----
`vh` is short for "`visually hidden.`" This class uses multiple methods and workarounds to make sure no browser removes the element's function.
****

View File

@ -2,8 +2,8 @@
= Conclusion
:chapter: 14
:part: Conclusion
:part_url: ./part/conclusion/
:url: ./conclusion/
:part_url: /part/conclusion/
:url: /conclusion/
[partintro]
== Hypermedia Reconsidered
@ -13,7 +13,7 @@ or a technology only appropriate for "`documents`" of links, text and pictures,
building _applications_. In this book you have seen how to build sophisticated user interfaces -- for both the web, with htmx,
and for mobile applications, using Hyperview -- using hypermedia as a core underlying application technology.
// TODO: check: edits
// TODO 1cg: reread after editing
Many web developers view the links and forms of "`plain`" HTML as bygone tools
from a less sophisticated age. And, in some ways, they are right: there were definite usability issues with the
original web. However, there are now JavaScript libraries that extend HTML by addressing its core limitations. Htmx, for example, allowed us to:
@ -50,12 +50,12 @@ technology is going to change everything. It tends to favor _sophistication_ ov
_simplicity_. People are scared to ask "`Is this too complex?`" because it sounds an awful lot like "`I'm not smart enough
to understand this.`"
// TODO: check: emphasize established, vs. old or past
// TODO 1cg: check: emphasize established, vs. old or past
The software industry tends, especially in web development, to lean far more towards innovating, rather than
understanding existing technologies and building on them or within them. We tend to look ahead for new, genius
solutions, rather than looking to established ideas. This is understandable: the technology world is necessarily
a forward-looking industry.
// TODO: check: refer back to Fielding?
// TODO 1cg: check: refer back to Fielding?
On the other hand -- as we saw with Roy Fielding's formulation of REST -- some early architects of the web had some great ideas which have been overlooked. We are old enough
to have seen hypermedia come and go as the "`new new`" idea. It was a little shocking to us to see powerful ideas like REST discarded so cavalierly
@ -64,42 +64,23 @@ by the industry. Fortunately, the concepts are still sitting there, waiting to b
Perhaps, following Mark Twain's advice, it is time to pause and reflect. Perhaps, for a few quiet moments, we can
put the endless swirl of the "`new new`" aside, look back on where the web came from, and learn.
// TODO: check: suggest this as the stronger concluding line
// TODO 1cg: check: suggest this as the stronger concluding line
Perhaps it's time to give hypermedia a chance.
// TODO: check. Does this list accurately condense the work?
// DONE dz4k: check. Does this list accurately condense the work?
// detract from the conclusion?
[.design-note]
.HTML Notes: Toward Effective HTML
****
Throughout the book we discuss a number of best practices for writing effective HTML. They are, in a nutshell:
* Stay close to the HTML markup youre producing and be able to change it. Prefer frameworks and components that help you do this.
// TODO: maybe add. Aim for 'locality of behavior' in your markup, so that you or another developer can understand what each part of the page is doing just by looking at that part of the page.
* Make the full range of available tags part of your toolkit.
* Aim for good fit between tag specs and your use case; check the HTML specs when in doubt.
* Stay close to the HTML markup youre producing and be able to change it. Prefer frameworks and components that help you do this.
* Apply the Locality of Behavior principle to HTML.
* Familiarize yourself with the full range of available tags and attributes and make them part of your toolkit. Don't restrict yourself to what Markdown can do.
// we should be able to say the s word by now
* Aim for good fit between tag semantics and your use case; make frequent use of the HTML specs.
* When more specific tags don't fit, it is usually better to step back to <div> than to use a tag incorrectly.
* Stick with established libraries for UI interactions. If a use case requires an improvised solution, test carefully for keyboard interaction and accessibility.
* Stick with established libraries for UI interactions. If a use case requires an improvised solution, test carefully for keyboard and touch interaction and accessibility.
* Prefer components that extend HTML, rather than abstracting it away.
* Test your HTML with screenreaders, with a keyboard, with different browsers and hardware, and run linters (while coding and/or in CI).
// TODO: Finish or cut. Placement okay?
.Helpful Resources
* Foundations
* HTML specification: https://html.spec.whatwg.org/multipage
* TODO link resources on alt text.
* https://htmhell.dev: Along with sinister abuses of HTML, this website shares development tips that will help ypu keep up-to-date with best practice.
* Referenced
** Manuel Matuzović, [.cite]_Lost in Translation_, https://www.youtube.com/watch?v=Wno1IhEBTxc.
** Manuel Matuzović, [.cite]_Why I'm not the biggest fan of Single Page Applications_, https://www.matuzo.at/blog/2023/single-page-applications-criticism/
** [.cite]_semantic: the 8 letter s-word_, https://t-ravis.com/post/doc/semantic_the_8_letter_s-word/
****
* To the extent possible, test your HTML with screenreaders, with a keyboard, with different browsers and hardware, and run linters (while coding and/or in CI).
****

View File

@ -1,13 +0,0 @@
:layout: frontmatter.njk
:url: /colophon/
= Colophon =
Text:: Authored in https://asciidoc.org[AsciiDoc (markup language)].
Website:: Generated with https://lume.land[Lume (static site generator)],
styled with https://missing.style[Missing.css (stylesheet)].
Fonts:: Headings in https://fontlibrary.org/en/font/chicagoflf[ChicagoFLF],
body text in https://www.type-together.com/literata-font[Literata],
code in https://berkeleygraphics.com/typefaces/berkeley-mono/[Berkeley Mono].

View File

@ -9,20 +9,20 @@
While there have been many books on the topic of hypermedia, there is a select number of publications that chronicle important advances in the field of hypermedia and this book is one of them. Not only does this book describe the benefits of creating hypermedia-driven applications (HDAs), it leads the reader through working and practical examples of how to do just that. And, in doing so, the authors (Gross, Stepinski, and Akşimşek) call out contributions from important figures in the history of hypermedia systems. And, as of this writing, that history spans more than half a century.
In 1974, Ted Nelson's _“Computer Lib/Machine Dreams”_ marked the start of the modern hypermedia era with a book that Steven Levy (author of _“Hackers”_) described as "the epic of the computer revolution." Nelson is credited with coining the terms HyperText, HyperLink, HyperMedia, and HyperData as well as Intertwingluarity; the notion that all information is connected -- both intertwined and intermingled. Almost half a century ago, he foretold a future where any person could publish anything anytime without the need for permission from any central controlling source. And his hyperlinks were the engine of that future.
In 1974, Ted Nelson's _“Computer Lib/Machine Dreams”_ marked the start of the modern hypermedia era with a book that Steven Levy (author of _“Hackers”_) described as "`the epic of the computer revolution.`" Nelson is credited with coining the terms HyperText, HyperLink, HyperMedia, and HyperData as well as Intertwingularity; the notion that all information is connected -- both intertwined and intermingled. Almost half a century ago, he foretold a future where any person could publish anything anytime without the need for permission from any central controlling source. And his hyperlinks were the engine of that future.
It took two decades before Nelson's idea of intertwingled computing became widespread. Along the way, Douglas Engelbart created the _oN-Line System_ or NLS, Wendy Hall built the _Microcosm_, and, eventually, in the 1980s, Tim Berners-Lee defined the World Wide Web (WWW), HTML, and HTTP. It was Berners-Lee's iteration that has become the backbone and the standard for the intertingularity we all experience today.
It took two decades before Nelson's idea of intertwingled computing became widespread. Along the way, Douglas Engelbart created the _oN-Line System_ or NLS, Wendy Hall built the _Microcosm_, and, eventually, in the 1980s, Tim Berners-Lee defined the World Wide Web (WWW), HTML, and HTTP. It was Berners-Lee's iteration that has become the backbone and the standard for the intertwingularity we all experience today.
By the year 2000, the technical foundations of "the web" were documented in Roy Fielding's PhD dissertation (_“Architectural Styles and the Design of Network-based Software Architectures”_). In that work, Fielding defined the architectural model of _REpresentational State Transfer_ or REST. This set of system properties and implementation constraints have proven -- even a quarter-century later -- to be a reliable model for designing and building the intertwingled machines that today affect billions of people around the globe.
By the year 2000, the technical foundations of "`the web`" were documented in Roy Fielding's PhD dissertation (_“Architectural Styles and the Design of Network-based Software Architectures”_). In that work, Fielding defined the architectural model of _REpresentational State Transfer_ or REST. This set of system properties and implementation constraints have proven -- even a quarter-century later -- to be a reliable model for designing and building the intertwingled machines that today affect billions of people around the globe.
Even though Fielding's work as important, it wasn't until Leonard Richardson and Sam Ruby published _“RESTful Web Services”_ in 2008 that the REST model became well-known to the world of software architecture and development. Backed by the Ruby programming platform, the ideas behind Fielding's REST model became _de rigueur_ for the creation of web-based services and client applications.
Even though Fielding's work was important, it wasn't until Leonard Richardson and Sam Ruby published _“RESTful Web Services”_ in 2008 that the REST model became well-known to the world of software architecture and development. Backed by the Ruby programming platform, the ideas behind Fielding's REST model became _de rigueur_ for the creation of web-based services and client applications.
One of the reasons Richardson and Ruby's work was so important was that, unlike dissertations and futuristic predictions, the _RESTful Web Services_ book outlined a practical working framework for building powerful applications for the Web. It described not only the power of REST but also provided step-by-step instructions on how to build them. Richard and Ruby brought together the hypermedia scholarship of the previous twenty years all in one place.
And now we can add this book (_“Hypermedia Systems”_) to that list of important works. From the book's introduction, through the step-by-step directions on how to use HTMX for browsers and Hyperview for mobile devices the authors describe the benefits of creating hypermedia-driven applications (HDAs). They also offer dozens of practical working examples the reader can use right away in building their own hypermedia solutions.
I've been working in the field of hypermedia for close to thirty years and have seen quite a few books, papers, dissertations, and programming platforms come and go in that time. Occasionally, one of these works "nails it" -- provides the right mix of theory and practice delivered in a way that helps readers make a connection between their own efforts and the activities of the community at large. I am happy to say that this book is one of those works. The authors have not only created powerful tooling in HTMX and Hyperview, they have also advanced the notion of hypermedia systems and hypermedia-driven applications in ways that a wide audience can understand and apply.
I've been working in the field of hypermedia for close to thirty years and have seen quite a few books, papers, dissertations, and programming platforms come and go in that time. Occasionally, one of these works "`nails it`" -- provides the right mix of theory and practice delivered in a way that helps readers make a connection between their own efforts and the activities of the community at large. I am happy to say that this book is one of those works. The authors have not only created powerful tooling in HTMX and Hyperview, they have also advanced the notion of hypermedia systems and hypermedia-driven applications in ways that a wide audience can understand and apply.
Nelson describes a future where the barriers to publishing and data sharing are lowered and the creative energies of the world are easily shared and applied. This is neither a new or unique idea but one that does need continual renewal and encouragement. Nelson saw his hyperlink and hypermedia as the driving force for intertingularity between people and machines around the world. In this idea alone, hypermedia is a powerful approach to creating computer systems that enable people to work together for the common good. As this book's authors say, "Hypermedia was a great idea! It still is!"
Nelson describes a future where the barriers to publishing and data sharing are lowered and the creative energies of the world are easily shared and applied. This is neither a new or unique idea but one that does need continual renewal and encouragement. Nelson saw his hyperlink and hypermedia as the driving force for intertwingularity between people and machines around the world. In this idea alone, hypermedia is a powerful approach to creating computer systems that enable people to work together for the common good. As this book's authors say, "`Hypermedia was a great idea! It still is!`"
_Mike Amundsen, April 2023_

View File

@ -11,7 +11,7 @@ Adam Stepinski, Carson Gross, Deniz Akşimşek
:sectnums: all
:sectnumlevels: 4
:layout: chapter.njk
:url: ./hypermedia-systems/
:url: /hypermedia-systems/
:is_whole_book: 1
ifdef::hypermedia-systems-pdf[]
@ -30,25 +30,24 @@ endif::[]
include::book/CH00_Introduction.adoc[leveloffset=1]
include::book/CH01_HypermediaAReintroduction.adoc[leveloffset=1]
include::book/CH02_ComponentsOfAHypermediaSystem.adoc[leveloffset=1]
include::book/CH03_EffectiveHtml.adoc[leveloffset=1]
include::book/CH04_BuildingASimpleWebApplication.adoc[leveloffset=1]
include::book/CH03_BuildingASimpleWebApplication.adoc[leveloffset=1]
= Hypermedia-Driven Web Applications With htmx
include::book/CH05_ExtendingHTMLAsHypermedia.adoc[leveloffset=1]
include::book/CH06_htmxPatterns.adoc[leveloffset=1]
include::book/CH07_MorehtmxPatterns.adoc[leveloffset=1]
include::book/CH08_ADynamicArchiveUIWithhtmx.adoc[leveloffset=1]
include::book/CH09_TricksOfThehtmxMasters.adoc[leveloffset=1]
include::book/CH10_ScriptingInAHypermediaApplication.adoc[leveloffset=1]
include::book/CH11_JSONDataAPIs.adoc[leveloffset=1]
include::book/CH05_htmxPatterns.adoc[leveloffset=1]
include::book/CH06_MorehtmxPatterns.adoc[leveloffset=1]
include::book/CH07_ADynamicArchiveUIWithhtmx.adoc[leveloffset=1]
include::book/CH08_TricksOfThehtmxMasters.adoc[leveloffset=1]
include::book/CH09_ScriptingInAHypermediaApplication.adoc[leveloffset=1]
include::book/CH10_JSONDataAPIs.adoc[leveloffset=1]
= Bringing Hypermedia To Mobile
include::book/CH12_HyperviewAMobileHypermedia.adoc[leveloffset=1]
include::book/CH13_BuildingAContactsAppWithHyperview.adoc[leveloffset=1]
include::book/CH14_ExtendingTheHyperviewClient.adoc[leveloffset=1]
include::book/CH11_HyperviewAMobileHypermedia.adoc[leveloffset=1]
include::book/CH12_BuildingAContactsAppWithHyperview.adoc[leveloffset=1]
include::book/CH13_ExtendingTheHyperviewClient.adoc[leveloffset=1]
= Conclusion
include::book/CH15_Conclusion.adoc[leveloffset=1]
include::book/CH14_Conclusion.adoc[leveloffset=1]

View File

@ -6,3 +6,7 @@ set -e
curl -fsSL https://deno.land/x/install/install.sh | sh
/opt/buildhome/.deno/bin/deno task build
"""
[[redirects]]
from="/book/*"
to=":splat"
status=308

View File

@ -11,7 +11,7 @@ layout: layout.njk
<nav aria-label="Chapters navigation" class="<small>">
{% macro nav(show_toc=false) %}
<div class="f-row flex-wrap:wrap justify-content:space-between">
<div class="grid spacious margin-block">
{% if prev or part %}
<p>
Previous:
@ -22,7 +22,7 @@ layout: layout.njk
{% endif %}
{% endif %}
{% if next %}
<p>
<p class="text-align:end" data-cols="2" data-cols@s="1">
<strong>
Next:
{% if next.data.part %}
@ -57,7 +57,7 @@ layout: layout.njk
{{ 'Part ' + loop.index + ':' if isPart }}
{{ sect.getTitle() | safe }}
</a>
{{ toc(sect, level + 1) if level < 2 and sect.getSections().length > 0 }}
{{ toc(sect, level + 1, url=url) if level < 2 and sect.getSections().length > 0 }}
</li>
{% endfor %}
</ul>

View File

@ -1,10 +1,6 @@
<nav class="f-row flex-wrap:wrap crowded" aria-label="Site navigation">
<span id="footer-book-name" class="bold allcaps">Hypermedia Systems</span>
<span class="f-row flex-wrap:wrap">
<a href="/">Cover</a>
<a href="/book/contents">Contents</a>
<a href="/colophon">Colophon</a>
<a href="https://github.com/bigskysoftware/hypermedia-systems">GitHub</a>
</span>
<nav class="f-row flex-wrap:wrap justify-content:center crowded" aria-label="Site navigation">
<a href="/" id="footer-book-name" class="bold allcaps display-font italic">Hypermedia Systems</a>
<a href="/book/contents">Contents</a>
<a href="https://github.com/bigskysoftware/hypermedia-systems">GitHub</a>
</nav>

View File

@ -75,14 +75,38 @@
body > header { border-block-end: 1px dotted var(--muted-fg) }
body > footer { border-block-start: none }
figure {
display: block;
background: none;
border: none;
padding: 0 var(--gap);
}
figure>* { --density: .5 }
figcaption { font-size: .8em; line-height: calc(var(--rhythm) * 2 / 3); }
figcaption { font-size: .8em; line-height: calc(var(--rhythm) * 3 / 4); }
figcaption:first-child { border-block-end: 1px solid var(--faded-fg) }
figcaption:last-child { border-block-start: 1px solid var(--faded-fg) }
table tr:not(:last-child) > * {
padding-bottom: var(--rhythm);
}
.iconbutton { box-shadow: none !important; }
.text-align\:end { text-align: end }
aside {
font-size: .8em;
line-height: calc(var(--rhythm)*3/4);
--gap: calc(var(--rhythm) * var(--density) * 3 / 4);
border-block: 1px solid var(--graphical-fg);
padding-block: var(--gap);
margin-block: calc(var(--gap)*4/3);
}
/* #endregion */
header, footer, summary, button, input, small, .\<small\>, aside, figcaption, .secondary-font { font-variation-settings: "opsz" 7 }
pre { line-height: calc(var(--rhythm) * 3 / 4) }
@media (prefers-color-scheme: dark) {
img[src*="/images/diagram"] { filter: invert(1) }
@ -93,10 +117,15 @@
.colist {
counter-reset: colist;
font-size: .8em;
line-height: calc(var(--rhythm) * 2 / 3);
line-height: calc(var(--rhythm) * 3 / 4);
font-variation-settings: "opsz" 8;
}
aside .colist {
font-size: inherit;
line-height: inherit;
}
.colist li {
counter-increment: colist;
margin-inline-start: 1.2ch;

View File

@ -1,4 +1,4 @@
import { Document, Element } from "lume/deps/dom.ts";
import { Document, Element, Text } from "lume/deps/dom.ts";
import { Site } from "lume/core.ts";
export default () => {
@ -17,7 +17,7 @@ export default () => {
$$(document, ".listingblock, .imageblock").forEach((el) => {
el.tagName = "figure";
el.classList.add("contents"); // Don't draw box around images and code listings
// el.classList.add("contents"); // Don't draw box around images and code listings
const title = el.querySelector(".title");
if (title) title.tagName = "figcaption";
})
@ -93,6 +93,20 @@ export default () => {
$$(document, "li>p:only-child").forEach(el => {
el.replaceWith(...el.childNodes);
})
$$(document, "#footnotes").forEach(fns => {
fns.classList.add("<small>");
fns.firstElementChild!.remove(); // remove hr
fns.tagName = "ol";
[...fns.children].forEach(fn => {
fn.tagName = "li";
const backlink = fn.firstElementChild!;
backlink.textContent = "↵";
fn.append(backlink);
const tn = fn.childNodes[1] as Text;
tn.data = tn.data.slice(2); // remove dot
});
})
})
}
}

View File

@ -26,6 +26,7 @@ Enhancing web applications without using SPA frameworks.">
.Cover-Header::before {
content: "";
position: absolute;
z-index: -1;
top: 0; left: 0; width: 100%; height: 100%;
background-image: url(/images/radialbugpattern-spine-black-realsize.png);
background-position: center calc(var(--bgh) - 28em);
@ -42,7 +43,7 @@ Enhancing web applications without using SPA frameworks.">
</style>
<header class="Cover-Header">
<h1 class="massivetext italic allcaps text-align:center fullbleed"
<h1 class="massivetext italic allcaps text-align:center fullbleed padding-inline"
>Hypermedia Systems</h1>
<p class="allcaps text-align:center f-row dense justify-content:center flex-wrap:wrap" style="row-gap: 0">
@ -50,13 +51,30 @@ Enhancing web applications without using SPA frameworks.">
<span>Adam&nbsp;Stepinski</span>
<span lang="tr">Deniz&nbsp;Akşimşek</span>
</p>
<p class="text-align:center italic">
With a foreword by
<a href="https://training.amundsen.com/#h.p_6AntBa6tTn_J">Mike Amundsen</a>
</p>
</header>
<main class="">
<div class="box warn crowded color">
<p>
<strong class="color margin-inline-end allcaps secondary-font">Note</strong>
<div class="box warn crowded color <small>" data-note>
<script>{
const me = document.currentScript.parentElement;
if (localStorage.dismissedNote) me.remove();
}</script>
<button class="iconbutton float:right"
onclick="
localStorage.dismissedNote = true;
this.closest(`[data-note]`).remove();
">
<span aria-hidden="true">×</span>
<v-h>Dismiss</v-h>
</button>
<p style="margin: 0">
<strong class="allcaps margin-inline-end">Note</strong>
Hypermedia Systems is a rewrite of a book that fell out of contract with
a publisher and is currently a <strong>work in progress</strong>.</p>
</div>

View File

@ -3,27 +3,25 @@ layout: layout.njk
---
{# See ./parts.tmpl.ts #}
<main>
<h1>
<main class="airy">
<h1 style="margin-block-end: 50vh">
<sub-title>Part {{ partNumber }}</sub-title>
{{ part }}
</h1>
<div class="airy">
<p>
<strong>
Next:
<a href="{{ chapters[0].data.url }}">{{ chapters[0].data.title }}</a>
</strong>
</p>
{% if previousPart %}
{% set previousChapter = previousPart.chapters | last %}
{% set prev = previousPart and previousPart.chapters | last %}
{% set next = chapters[0] %}
<div class="grid margin-block">
{% if prev %}
<p>
Previous:
<a href="{{ previousChapter.data.url }}">{{ previousChapter.data.title | safe }}</a>
</p>
<a href="{{ prev.data.url }}">{{ prev.data.title | safe }}</a>
{% endif %}
<p class="text-align:end" data-cols="2" data-cols@s="1">
<strong>
Next:
<a href="{{ next.data.url }}">{{ next.data.title | safe }}</a>
</strong>
</div>
</main>