{
    "version": "https:\/\/jsonfeed.org\/version\/1.1",
    "title": "Ilya Birman’s Blog: posts tagged feedback",
    "_rss_description": "Ilya Birman’s blog on design, cities, music, and life",
    "_rss_language": "en",
    "_itunes_email": "ilyabirman@ilyabirman.net",
    "_itunes_categories_xml": "<itunes:category text=\"Arts\"><itunes:category text=\"Design\" \/><\/itunes:category>\r\n<itunes:category text=\"Society &amp; Culture\"><itunes:category text=\"Personal Journals\" \/><\/itunes:category>\r\n<itunes:category text=\"Technology\" \/>\r\n",
    "_itunes_image": "https:\/\/ilyabirman.net\/meanwhile\/pictures\/userpic\/userpic-square@2x.jpg?1573933764",
    "_itunes_explicit": "no",
    "home_page_url": "https:\/\/ilyabirman.net\/meanwhile\/tags\/feedback\/",
    "feed_url": "https:\/\/ilyabirman.net\/meanwhile\/tags\/feedback\/json\/",
    "icon": "https:\/\/ilyabirman.net\/meanwhile\/pictures\/userpic\/userpic@2x.jpg?1573933764",
    "authors": [
        {
            "name": "Ilya Birman",
            "url": "https:\/\/ilyabirman.net\/meanwhile\/",
            "avatar": "https:\/\/ilyabirman.net\/meanwhile\/pictures\/userpic\/userpic@2x.jpg?1573933764"
        }
    ],
    "items": [
        {
            "id": "346",
            "url": "https:\/\/ilyabirman.net\/meanwhile\/all\/one-spinner-is-enough\/",
            "title": "One spinner is enough",
            "content_html": "<p>In user interfaces, a spinner is a normal indicator of thinking or loading. However, the modern web is often build from blocks that could be loading independently. As a result, you got multiple spinners spinning:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/ilyabirman.net\/meanwhile\/pictures\/twitter-multi-spinners@2x.png\" width=\"570\" height=\"540\" alt=\"\" \/>\n<\/div>\n<p>Not good.<\/p>\n<p>The application architecture should account for the fact that multiple things could be loading at once. Where would the spinner be displayed? In the case of multiple instances of the same block type, it’s enough to show the spinner in lieu of the first one. When the blocks are diverse, in general it’s best to put the spinner in the largest or the top-most block.<\/p>\n<p>The classic iPhone used to have a loading indicator right in the status bar:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/ilyabirman.net\/meanwhile\/pictures\/iphone-statusbar-spinner@2x.jpg\" width=\"320\" height=\"26\" alt=\"\" \/>\n<\/div>\n<p>If memory serves, there was an API for an application to tell the status bar: “Hey, I already have an indicator in my interface, so don’t show yours”.<\/p>\n<p>But the art of thinking of how to avoid rubbish on the screen, has long been forgotten even at Apple, so you can’t expect this level of care from Twitter.<\/p>\n",
            "summary": "In user interfaces, a spinner is a normal indicator of thinking or loading. However, the modern web is often build from blocks that could be loading independently",
            "date_published": "2022-11-12T11:00:31+03:00",
            "date_modified": "2022-11-12T11:00:28+03:00",
            "tags": [
                "feedback",
                "interface"
            ],
            "image": "https:\/\/ilyabirman.net\/meanwhile\/pictures\/twitter-multi-spinners@2x.png",
            "_date_published_rfc2822": "Sat, 12 Nov 2022 11:00:31 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "346",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": true,
                "links_required": [],
                "og_images": [
                    "https:\/\/ilyabirman.net\/meanwhile\/pictures\/twitter-multi-spinners@2x.png",
                    "https:\/\/ilyabirman.net\/meanwhile\/pictures\/iphone-statusbar-spinner@2x.jpg"
                ]
            }
        },
        {
            "id": "320",
            "url": "https:\/\/ilyabirman.net\/meanwhile\/all\/hover-matches-click\/",
            "title": "The hover and click should match",
            "content_html": "<p>This principle seems obvious, but there are too many interfaces that violate it. Thus, a blog post.<\/p>\n<p>If an interface element exhibits some hover effect, the element should be clickable. If an element is clickable, it should exhibit some hover effect. The element’s hover and click areas should match down to a pixel.<\/p>\n<p>Here are some of the mistakes I’ve seen:<\/p>\n<ul>\n  <li>A website main menu is composed of links, each wrapped into a container. The containers style includes a hover highlight. The containers are slightly bigger that the links because of paddings. Within the paddings themselves, the highlighting works, but the links do not.<\/li>\n  <li>A link underlining is implemented so that clicking precisely at the line itself does not cause the link activation even though there is a hover effect. In some cases, it happens the other way around, too: there is no hover effect, but clicking works.<\/li>\n  <li>To dismiss a modal popup dialog, you can just click outside of it. When the dialog is shown, the hover effect of the elements around it continue to work, even though clicking them will just dismiss the popup and not trigger the associated action.<\/li>\n  <li>An element’s action is disabled by a script, but the hover effect remains. For example, a form’s submit button is disabled because of an incorrect value in some field, however is still reacts to a hover as if it worked.<\/li>\n  <li>A large box with a picture and a heading is clickable as a whole and exhibits some hover effect. In the box’s corner, there is a small icon that does something else, say, adds the object to “Favourites”. When hovering the icon, the box’s hover effect remains, even though clicking the icon will not trigger the action, associated with the box itself.<\/li>\n<\/ul>\n",
            "summary": "This principle seems obvious, but there are too many interfaces that violate it. Thus, a blog post",
            "date_published": "2019-07-04T15:51:34+03:00",
            "date_modified": "2019-07-04T15:49:49+03:00",
            "tags": [
                "feedback",
                "interface"
            ],
            "_date_published_rfc2822": "Thu, 04 Jul 2019 15:51:34 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "320",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": true,
                "links_required": [],
                "og_images": []
            }
        },
        {
            "id": "296",
            "url": "https:\/\/ilyabirman.net\/meanwhile\/all\/feedback-first\/",
            "title": "Feedback first",
            "content_html": "<p>Here is my talk from FDConf, Minsk about feedback in the user interface:<\/p>\n<div class=\"e2-text-video\">\n<iframe src=\"https:\/\/www.youtube.com\/embed\/zZ6XgD8xe1s?enablejsapi=1\" allow=\"autoplay\" frameborder=\"0\" allowfullscreen><\/iframe>\n<\/div>\n",
            "summary": "Here is my talk from FDConf, Minsk about feedback in the user interface",
            "date_published": "2017-11-13T23:59:22+03:00",
            "date_modified": "2017-11-13T23:59:13+03:00",
            "tags": [
                "feedback",
                "interface",
                "talks",
                "video"
            ],
            "image": "https:\/\/ilyabirman.net\/meanwhile\/pictures\/remote\/youtube-zZ6XgD8xe1s-cover.jpg",
            "_date_published_rfc2822": "Mon, 13 Nov 2017 23:59:22 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "296",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": true,
                "links_required": [],
                "og_images": [
                    "https:\/\/ilyabirman.net\/meanwhile\/pictures\/remote\/youtube-zZ6XgD8xe1s-cover.jpg"
                ]
            }
        },
        {
            "id": "279",
            "url": "https:\/\/ilyabirman.net\/meanwhile\/all\/slider\/",
            "title": "Implementing a slider well",
            "content_html": "<p>A slider is a simple user interface control where you adjust some value by dragging a handle in a groove:<\/p>\n<style>\n.ibs8-slider-slide-area {\n  display: inline-block;\n  background: #f0f0f0;\n  padding: 10px 20px;\n  border-radius: 4px;\n}\n.ibs8-slider-preview {\n  display: inline-block;\n  background: rgb(96, 96, 192);\n  color: #fff;\n  width: 40px;\n  height: 40px;\n  margin-left: 20px;\n  vertical-align: middle;\n  padding: 10px 0px;\n  text-align: center;\n  position: relative;\n  top: -1px;\n  border-radius: 4px;\n}\n.ibs8-slider-is-hoverable .ibs8-slider-slide-area,\n.ibs8-slider-is-cursorable .ibs8-slider-slide-area {\n  cursor: pointer;\n}\n.ibs8-slider-slide {\n  position: relative;\n  display: inline-block;\n  vertical-align: middle;\n  width: 200px;\n  height: 1px;\n  top: -1px;\n}\n.ibs8-slider-slide-groove {\n  position: absolute;\n  left: 0; top: -1px;\n  width: 200px;\n  height: 2px;\n}\n.ibs8-slider-bad-groove-only .ibs8-slider-slide-groove {\n  cursor: pointer;\n}\n.ibs8-slider-slider {\n  position: absolute;\n  left: 100px;\n  width: 16px;\n  height: 16px;\n}\n.ibs8-slider-handle {\n  border-radius: 6px;\n  width: 12px;\n  height: 12px;\n  cursor: pointer;\n  position: absolute;\n  left: -6px;\n  top: -5px;\n}\n.ibs8-slider-slide-groove,\n.ibs8-slider-handle {\n  background: #000;\n  transition-property: background;\n  transition-duration: .5s;\n  transition-timing-function: ease-out;\n}\n.ibs8-slider-is-hoverable .ibs8-slider-slide-area:hover .ibs8-slider-slide-groove,\n.ibs8-slider-is-hoverable .ibs8-slider-slide-area:hover .ibs8-slider-handle,\n.ibs8-slider-is-hoverable .ibs8-slider-is-current .ibs8-slider-slide-groove,\n.ibs8-slider-is-hoverable .ibs8-slider-is-current .ibs8-slider-handle {\n  background: #c30;\n  transition: none;\n}\n<\/style>\n<script type=\"text\/javascript\">\n\nif ($) $ (function () {\n\nvar $sliders = $ ('.ibs8-slider-example')\nvar timeout = false\nvar lastX = false\n\n$sliders.html ('<span class=\"ibs8-slider-slide-area\"><span class=\"ibs8-slider-slide\"><div class=\"ibs8-slider-slide-groove\"><div class=\"ibs8-slider-slider\" style=\"left: 100px;\"><span class=\"ibs8-slider-handle\"><\/span><\/div><\/div><\/span><\/span><span class=\"ibs8-slider-preview\">50<\/span>')\n\nvar ibs8UpdateData = function (who, doColor, doNumber, number) {\n  if (doNumber) {\n    $ (who).parent ().find ('.ibs8-slider-preview').html (number)\n  }\n  if (doColor) {\n    var color = Math.round (v*192\/200)\n    $ (who).parent ().find ('.ibs8-slider-preview').css ('background', 'rgb(' + (192-color) + ','+ color + ',192)')\n  }\n}\n\nvar ibs8UpdateSlider = function (who, event, isFinal, isForReal = false) {\n  var x = event.clientX\n  if (event.originalEvent.touches && event.originalEvent.touches.length > 0) x = event.originalEvent.changedTouches[0].clientX\n  if (!x) x = lastX\n  lastX = x\n  if (!isForReal && $ (who).parent ().is ('.ibs8-slider-slow')) {\n    if (!timeout) {\n      timeout = setTimeout (function () {\n        ibs8UpdateSlider (who, event, isFinal, true)\n        timeout = false\n      }, 200 + Math.random () * 200)\n    }\n    return false\n  }\n  if (!who) return false\n  if (!isFinal && $ (who).parent ().is ('.ibs8-slider-bad-release-only')) return false\n  v = (x - $ (who).find('.ibs8-slider-slide').offset ().left)\n  if (v < 0) v = 0\n  if (v > 200) v = 200\n  $ (who).find ('.ibs8-slider-slider').css ('left', v)\n  console.log ($ (who).parent ().find ('.ibs8-slider-preview'))\n  if (isFinal || !$ (who).parent ().is ('.ibs8-slider-preview-release-only')) {\n    if ($ (who).parent ().is ('.ibs8-slider-slow-data')) {\n      if ($ (who).parent ().is ('.ibs8-slider-slow-data-partial')) {\n        ibs8UpdateData (who, false, true, Math.round (v \/ 2))\n      }\n      if (!timeout) {\n        timeout = setTimeout (function () {\n          ibs8UpdateData (who, true, true, Math.round (v \/ 2))\n          timeout = false\n        }, 200 + Math.random () * 200)\n      }\n    } else {\n      ibs8UpdateData (who, true, true, Math.round (v \/ 2))\n    }\n  }\n  event.stopPropagation ()\n  event.preventDefault ()\n  return false\n}\n\nvar current = false\n\nvar ibs8MouseDown = function (event) {\n  console.log (event.target)\n  if ($ (this).parent ().is ('.ibs8-slider-bad-handle-only')) {\n    if (!$ (event.target).is ('.ibs8-slider-slider, .ibs8-slider-handle')) return\n  }\n  if ($ (this).parent ().is ('.ibs8-slider-bad-groove-only')) {\n    if (!$ (event.target).is ('.ibs8-slider-slide-groove, .ibs8-slider-slider, .ibs8-slider-handle')) return\n  }\n  if ($ (this).parent ().is ('.ibs8-slider-bad-jump-only')) {\n    if (!$ (event.target).is ('.ibs8-slider-slider, .ibs8-slider-handle')) return ibs8UpdateSlider (this, event, false)\n  }\n  current = this\n  $ (current).addClass ('ibs8-slider-is-current')\n  return ibs8UpdateSlider (current, event, false)\n}\n\nvar ibs8MouseUp = function (event) {\n  ibs8UpdateSlider (current, event, true)\n  $ (current).removeClass ('ibs8-slider-is-current')\n  current = false\n  return false\n}\n\nvar ibs8MouseMove = function (event) {\n  if (!current) return\n  return ibs8UpdateSlider (current, event, false)\n}\n\n$ ('.ibs8-slider-slide-area').bind ('mousedown touchstart', ibs8MouseDown)\n$ (document.body).bind ('mouseup touchend touchcancel', ibs8MouseUp)\n$ (document.body).bind ('mousemove touchmove', ibs8MouseMove)\n})\n\n<\/script>\n<div class=\"e2-text-generic-object ibs8-slider-example ibs8-slider-is-hoverable\"><\/div><p>Most web developers can’t get it right. You may think that there is not much you can fail at with such a simple thing. But most sliders are bad. They don’t respect the Fitts’s law and don’t provide decent feedback.<\/p>\n<p>I decided to write a manual. Let’s see what usually goes wrong and how to fix it. If you are reading this via RSS, please go to the browser to see the demos.<\/p>\n<h2>Fitts’s law<\/h2>\n<p>Common mistake is requiring to grab the handle to drag it. Logically, this makes sense. But a small handle is very hard to grab. Try here:<\/p>\n<div class=\"e2-text-generic-object ibs8-slider-example ibs8-slider-bad-handle-only\"><\/div><p>A tiny bit better is making the groove clickable:<\/p>\n<div class=\"e2-text-generic-object ibs8-slider-example ibs8-slider-bad-groove-only\"><\/div><p>But in this design the groove is so thin that it hardly adds anything — aiming is still a pain, particularly on a touch screen. Speaking of touch screens, some implementations would just forget about them and not handle the touch events.<\/p>\n<p>You should be able to grab the handle from any point in the slider area:<\/p>\n<div class=\"e2-text-generic-object ibs8-slider-example ibs8-slider-is-cursorable\"><\/div><p>Make this area at least as big as a comfortable button would be. Notice how the areas to the left and to the right of the slider also work, moving the slider to the minimal and maximal positions.<\/p>\n<h2>Feedback<\/h2>\n<p>In all the examples above the mouse cursor was changing to a pointing finger when you hovered over the draggable area of a slider. Sometimes this feedback is absent or inconsistent. Here, you can drag from anywhere, but the mouse cursor changes only over the handle:<\/p>\n<div class=\"e2-text-generic-object ibs8-slider-example\"><\/div><p>Having no sign that the slider would work otherwise, many users would try to grab the handle. An even worse mistake would be requiring to drag the handle but displaying a pointing finger over the whole slider area.<\/p>\n<p>Use feedback consistently, change the cursor over all slider area and make it all work:<\/p>\n<div class=\"e2-text-generic-object ibs8-slider-example ibs8-slider-is-cursorable\"><\/div><p>The change of the cursor is the minimal feedback necessary for the user to understand how the slider works. But it’s better to also highlight the slider itself:<\/p>\n<div class=\"e2-text-generic-object ibs8-slider-example ibs8-slider-is-hoverable\"><\/div><p>Notice how this one just feels nicer to use.<\/p>\n<p>A small detail: the slider gets its red “hover” highlight, but also keeps it while being dragged, even when mouse is outside.<\/p>\n<h2>Continuous feedback<\/h2>\n<p>The slider should move continuously as I move the mouse. Some implementations would only repaint the slider when I release the mouse button:<\/p>\n<div class=\"e2-text-generic-object ibs8-slider-example ibs8-slider-bad-release-only ibs8-slider-is-hoverable\"><\/div><p>Others would accept clicks in the whole slider area, but only move the handle to the click position instead of grabbing it (unless you grab the very handle):<\/p>\n<div class=\"e2-text-generic-object ibs8-slider-example ibs8-slider-bad-jump-only ibs8-slider-is-hoverable\"><\/div><p>Both feel broken.<\/p>\n<p>The slider is usually there to control something else. In this case, the rectangle to the right of it. In some implementations, the slider would move continuously, but the external changes would happen only on release:<\/p>\n<div class=\"e2-text-generic-object ibs8-slider-example ibs8-slider-is-hoverable ibs8-slider-preview-release-only\"><\/div><p>Everything should stay in sync: hover effects, the cursor shape, actual active areas; reaction to click and drag; feedback inside and outside the slider.<\/p>\n<h2>Feedback and slow data<\/h2>\n<p>Sometimes it may take noticeable time for the changes in slider to take effect somewhere else. You may need to make complex calculations or get data over the network. You may think you just can’t provide continuous feedback in such cases. But you must still aim to provide as much continuous feedback as possible.<\/p>\n<p>In my examples, the slider controls the numeric value and the background colour. Let’s pretend they are slow to update.<\/p>\n<p>In the worst implementations, the slider would get repainted only when the data is ready:<\/p>\n<div class=\"e2-text-generic-object ibs8-slider-example ibs8-slider-is-hoverable ibs8-slider-slow\"><\/div><p>This is painful to use.<\/p>\n<p>At least <i>the slider itself<\/i> should repaint continuously during the drag, no matter how long it takes for the change to take effect:<\/p>\n<div class=\"e2-text-generic-object ibs8-slider-example ibs8-slider-is-hoverable ibs8-slider-slow-data\"><\/div><p>But we can do better. To fake continuous zooming, Google Maps shows at least a blurry map image (see my post on <a href=\"http:\/\/ilyabirman.net\/meanwhile\/all\/immediate-feedback-when-data-is-unavailable\/\">immediate feedback when data is unavailable<\/a>).<\/p>\n<p>What if only the colour is slow to update, but the number can change instantly?<\/p>\n<div class=\"e2-text-generic-object ibs8-slider-example ibs8-slider-is-hoverable ibs8-slider-slow-data ibs8-slider-slow-data-partial\"><\/div><p>Again, compare with the previous implementation — this one just feels snappier. So: always consider at least partial continuous updating. This is much better than waiting for a full update before making any change.<\/p>\n<p>Notice that the data updates as fast as it can without the need to release the mouse button. If the data takes even more time to update, some progress indicator may be used, but the slider should never become unresponsive.<\/p>\n<h2>Reference implementation<\/h2>\n<p>There are too many sliders above. Which are the good ones?<\/p>\n<p>This is the best, with full continuous feedback:<\/p>\n<div class=\"e2-text-generic-object ibs8-slider-example ibs8-slider-is-hoverable\"><\/div><p>This is the second best, with partial continuous feedback when some data needs time to update (colour, in this example):<\/p>\n<div class=\"e2-text-generic-object ibs8-slider-example ibs8-slider-is-hoverable ibs8-slider-slow-data ibs8-slider-slow-data-partial\"><\/div><p>Please show this to your web development team.<\/p>\n<div class=\"caption\"><p>You should follow me on Twitter, <a href=\"https:\/\/twitter.com\/ilyabirmannet\/\">here<\/a><\/p>\n<\/div>",
            "summary": "A slider is a simple user interface control where you adjust some value by dragging a handle in a groove",
            "date_published": "2017-06-21T00:44:11+03:00",
            "date_modified": "2017-06-21T10:18:55+03:00",
            "tags": [
                "feedback",
                "interface"
            ],
            "image": "https:\/\/ilyabirman.net\/meanwhile\/pictures\/slider@2x.png",
            "_date_published_rfc2822": "Wed, 21 Jun 2017 00:44:11 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "279",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": true,
                "links_required": [],
                "og_images": [
                    "https:\/\/ilyabirman.net\/meanwhile\/pictures\/slider@2x.png"
                ]
            }
        },
        {
            "id": "262",
            "url": "https:\/\/ilyabirman.net\/meanwhile\/all\/mimic-client-feedback\/",
            "title": "Mimic 2.0: The client’s feedback",
            "content_html": "<p>I’ve recently designed the new <a href=\"http:\/\/ilyabirman.net\/projects\/mimic\/\">user interface for Mimic<\/a>, a web developer tool for mocking server responses in a browser. Ilya Gelman, one of the Mimic’s developers, comments on working with me:<\/p>\n<blockquote>\n<p>Ilya is of that rare kind of designers that seek to find solutions to problems instead of matching colors and shadows. He asks the right questions, concentrates on important things and understands that real products aren’t just beautiful pictures on dribble. He helped us redefine our tool to make it simpler to use and easier to understand.<\/p>\n<p>Working with local Israeli designers, we usually communicated by real-time channels like Slack, WhatsApp or phone. It was a bit different with Ilya because we had to communicate mainly via email and had to schedule our calls ahead of time. Part of this was the difference in time zones and part of it was Ilya’s principle of managing his own time, which I can fully understand and respect. All in all we never had an issue of lack of communication and everything was always delivered on time.<\/p>\n<p>What I liked about Ilya the most is that he was able to explain the reasoning behind his design decisions, and that he knew how to work together with us to get our tool released on time.<\/p>\n<\/blockquote>\n",
            "summary": "I’ve recently designed the new user interface for Mimic, a web developer tool for mocking server responses in a browser",
            "date_published": "2017-04-01T20:42:30+03:00",
            "date_modified": "2017-04-01T20:42:28+03:00",
            "tags": [
                "feedback",
                "projects",
                "quotes"
            ],
            "_date_published_rfc2822": "Sat, 01 Apr 2017 20:42:30 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "262",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": false,
                "links_required": [],
                "og_images": []
            }
        },
        {
            "id": "172",
            "url": "https:\/\/ilyabirman.net\/meanwhile\/all\/immediate-feedback-when-data-is-unavailable\/",
            "title": "Immediate feedback when data is unavailable",
            "content_html": "<p>Immediate feedback is one of the core principles of good user interface design. But what if the data is unavailable for the feedback?<\/p>\n<p>When you increase the zoom level in Google Maps, you see the enlarged view of the data you had already downloaded:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/ilyabirman.net\/meanwhile\/pictures\/gmaps-blurry.jpg\" width=\"768\" height=\"200\" alt=\"\" \/>\n<\/div>\n<p>And a couple of seconds later the view updates:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/ilyabirman.net\/meanwhile\/pictures\/gmaps-normal.jpg\" width=\"768\" height=\"200\" alt=\"\" \/>\n<\/div>\n<p>While blurry maps suck, having to wait for several seconds before seeing <i>anything<\/i> would suck much more.<\/p>\n<p>Another example would be Apple’s Safari on the first iPhone. The device had so little memory, it could not fit a whole webpage into it, so it had to render only the parts you were currently looking at. When you scrolled past the rendered area, you saw a checkerboard pattern:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/ilyabirman.net\/meanwhile\/pictures\/safari-checkerboard.jpg\" width=\"768\" height=\"200\" alt=\"\" \/>\n<\/div>\n<p>And a couple of seconds later the page rendered:<\/p>\n<div class=\"e2-text-picture\">\n<img src=\"https:\/\/ilyabirman.net\/meanwhile\/pictures\/safari-checkerboard-loaded.jpg\" width=\"768\" height=\"200\" alt=\"\" \/>\n<\/div>\n<p>While the pattern is not much fun to look at, this design is significantly better than if the browser would have to wait until the data was ready before responding to your touches.<\/p>\n<p><b>Lesson learnt:<\/b> when you cannot respond with a precise result immediately, show an approximation immediately, then the precise result a moment later.<\/p>\n",
            "summary": "Immediate feedback is one of the core principles of good user interface design. But what if the data is unavailable for the feedback?",
            "date_published": "2014-07-17T14:20:39+03:00",
            "date_modified": "2020-04-27T20:45:55+03:00",
            "tags": [
                "design",
                "feedback",
                "interface"
            ],
            "image": "https:\/\/ilyabirman.net\/meanwhile\/pictures\/gmaps-blurry.jpg",
            "_date_published_rfc2822": "Thu, 17 Jul 2014 14:20:39 +0300",
            "_rss_guid_is_permalink": "false",
            "_rss_guid": "172",
            "_rss_enclosures": [],
            "_e2_data": {
                "is_favourite": true,
                "links_required": [],
                "og_images": [
                    "https:\/\/ilyabirman.net\/meanwhile\/pictures\/gmaps-blurry.jpg",
                    "https:\/\/ilyabirman.net\/meanwhile\/pictures\/gmaps-normal.jpg",
                    "https:\/\/ilyabirman.net\/meanwhile\/pictures\/safari-checkerboard.jpg",
                    "https:\/\/ilyabirman.net\/meanwhile\/pictures\/safari-checkerboard-loaded.jpg"
                ]
            }
        }
    ],
    "_e2_version": 4259,
    "_e2_ua_string": "Aegea 12.0a (v4259e)"
}