{"id":825,"date":"2024-02-13T14:45:25","date_gmt":"2024-02-13T20:45:25","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=825"},"modified":"2024-02-13T14:45:25","modified_gmt":"2024-02-13T20:45:25","slug":"how-to-fix-the-invisible-scrollbar-issue-in-ios","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/how-to-fix-the-invisible-scrollbar-issue-in-ios\/","title":{"rendered":"How to fix the invisible scrollbar issue in iOS browsers"},"content":{"rendered":"\n<p>The page scrollbar in web browsers serves a useful function: The vertical position of the scrollbar thumb tells the user where they are in the page (their scroll position), while the size (height) of the scrollbar thumb tells them roughly how long the page is. Because scrollbars are useful, they should be clearly visible.<\/p>\n\n\n\n<p>On Apple\u2019s platforms, most notably on iOS, the page scrollbar is placed inside the viewport and laid on top of web content. In some cases, this can result in a poor contrast between the scrollbar thumb and the page background beneath it. One website that has this problem is <a href=\"https:\/\/webkit.org\/blog\/\">WebKit Blog<\/a>. The page scrollbar on that website in iOS browsers is almost completely invisible.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"768\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/02\/invisible-scrollbar.png?resize=1024%2C768&#038;ssl=1\" alt=\"WebKit Blog article in Safari on iPhone\" class=\"wp-image-828\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/02\/invisible-scrollbar.png?resize=1024%2C768&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/02\/invisible-scrollbar.png?resize=300%2C225&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/02\/invisible-scrollbar.png?resize=768%2C576&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/02\/invisible-scrollbar.png?resize=1536%2C1152&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/02\/invisible-scrollbar.png?w=1779&amp;ssl=1 1779w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">When scrolling a WebKit Blog article in mobile Safari, the scrollbar is not visible<\/figcaption><\/figure>\n\n\n\n<p>I assure you that there is indeed a scrollbar present in the above screenshot, but don\u2019t worry if you can\u2019t spot it. Even a person with perfect vision could probably not say with certainty that they can see a scrollbar in that image. Let\u2019s zoom in to take a closer look at this supposed scrollbar, and maybe we\u2019ll manage to actually see it this time.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"688\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/02\/invisible-scrollbar-closeup.png?resize=1024%2C688&#038;ssl=1\" alt=\"Close-up of scrollbar thumb\" class=\"wp-image-830\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/02\/invisible-scrollbar-closeup.png?resize=1024%2C688&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/02\/invisible-scrollbar-closeup.png?resize=300%2C201&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/02\/invisible-scrollbar-closeup.png?resize=768%2C516&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/02\/invisible-scrollbar-closeup.png?resize=1536%2C1031&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/02\/invisible-scrollbar-closeup.png?resize=2048%2C1375&amp;ssl=1 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">Even in this close-up, the scrollbar is only barely visible<\/figcaption><\/figure>\n\n\n\n<p>The contrast ratio between the scrollbar (<code>#fbfbfb<\/code>) and page background (<code>#f7f7f7<\/code>) is 1.03:1. This is only a smidgen above the theoretical minimum of 1:1 (no contrast), which is what you get if you compare a color to itself. For all intents and purposes, the scrollbar is invisible.<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='315' src='https:\/\/videopress.com\/embed\/UNCZtka5?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;posterUrl=https%3A%2F%2Ffrontendmasters.com%2Fblog%2Fwp-content%2Fuploads%2F2024%2F02%2Finvisible-scrollbar-demo-poster.png&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1674852142'><\/script><\/div>\n\t\t\t<figcaption> Observe how the scrollbar \u201cdisappears\u201d as soon as the user scrolls past the page header<\/figcaption>\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>The invisible scrollbar problem is not specific to iOS. It affects Apple\u2019s other platforms as well. However, macOS users can fix this issue by setting the \u201cShow scroll bars\u201d option to \u201cAlways\u201d in system settings (in the Appearance section), which causes the scrollbar to be placed in a separate \u201cscrollbar gutter\u201d outside of the viewport in the browser. There is no such option on iOS, so it\u2019s up to the website to ensure that the page scrollbar is clearly visible.<\/p>\n\n\n\n<p>Websites that are affected by the invisible scrollbar problem can fix this issue by adjusting how they apply CSS background colors to the page. In the future, it will be possible to fix this issue by coloring the scrollbar via the CSS <code>scrollbar-color<\/code> property, which currently isn\u2019t supported in Safari. <a href=\"https:\/\/github.com\/WebKit\/standards-positions\/issues\/134#issuecomment-1708699632\">Apple has expressed support for this property<\/a>, but the implementation in WebKit is blocked on a limitation in lower-level frameworks on Apple\u2019s platforms.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">html<\/span> {\n  <span class=\"hljs-comment\">\/* A scrollbar with a gray thumb and a transparent track *\/<\/span>\n  <span class=\"hljs-comment\">\/* Not yet supported in Safari and other WebKit-based iOS browsers *\/<\/span>\n  <span class=\"hljs-attribute\">scrollbar-color<\/span>: gray transparent;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Note that the non-standard CSS <code>::-webkit-scrollbar<\/code> pseudo-elements are not supported on iOS, so they cannot be used as a fallback for the <code>scrollbar-color<\/code> property on that platform.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What is causing the invisible scrollbar<\/h2>\n\n\n\n<p>On WebKit Blog, the header and footer have a dark background, while the main content on the page has a light background. The website achieves this by applying a dark background color to the <code>&lt;body&gt;<\/code> element and a light background color to the <code>&lt;main&gt;<\/code> element.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">background-color<\/span>: <span class=\"hljs-number\">#00253d<\/span>; <span class=\"hljs-comment\">\/* a dark color *\/<\/span>\n}\n\n<span class=\"hljs-selector-tag\">main<\/span> {\n  <span class=\"hljs-attribute\">background-color<\/span>: <span class=\"hljs-number\">#f7f7f7<\/span>; <span class=\"hljs-comment\">\/* a light color *\/<\/span>\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Since the header and footer don\u2019t have their own background color, they are transparent (in CSS, the <code>background-color<\/code> property <a href=\"https:\/\/drafts.csswg.org\/css-backgrounds-3\/#propdef-background-color\">defaults<\/a> to the <code>transparent<\/code> value), so the <code>&lt;body&gt;<\/code> element\u2019s dark background is visible through them, resulting in the following appearance:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">body<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">header<\/span>&gt;<\/span>\n    <span class=\"hljs-comment\">&lt;!-- has dark background color --&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">header<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n    <span class=\"hljs-comment\">&lt;!-- has light background color --&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">footer<\/span>&gt;<\/span>\n    <span class=\"hljs-comment\">&lt;!-- has dark background color --&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">footer<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">body<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The page scrollbar in web browsers on Apple\u2019s platforms can have a light and dark appearance. The operating system automatically chooses the scrollbar appearance based on the background colors of the web page. Since WebKit Blog sets a dark background color on the <code>&lt;body&gt;<\/code> element, the system assumes that the page is mostly dark and chooses the light scrollbar appearance, resulting in an invisible scrollbar against the <code>&lt;main&gt;<\/code> element\u2019s light background color.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How to fix this issue<\/h2>\n\n\n\n<p>The fix is rather simple. In order to get a dark scrollbar appearance, the page should avoid setting a dark background color on the <code>&lt;html&gt;<\/code> or <code>&lt;body&gt;<\/code> elements. In the case of WebKit Blog, the dark background color should be applied directly to the <code>&lt;header&gt;<\/code> and <code>&lt;footer&gt;<\/code> elements, while the light background color can be applied to the <code>&lt;main&gt;<\/code> element or the <code>&lt;body&gt;<\/code> element (either works).<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">body<\/span> {\n  <span class=\"hljs-attribute\">background-color<\/span>: <span class=\"hljs-number\">#f7f7f7<\/span>; <span class=\"hljs-comment\">\/* a light color *\/<\/span>\n}\n\n<span class=\"hljs-selector-tag\">header<\/span>,\n<span class=\"hljs-selector-tag\">footer<\/span> {\n  <span class=\"hljs-attribute\">background-color<\/span>: <span class=\"hljs-number\">#00253d<\/span>; <span class=\"hljs-comment\">\/* a dark color *\/<\/span>\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>I should note that this fix can affect the overscroll effect in some browsers. On WebKit blog, when the user scrolls to the top of the page, the overscroll animation creates an impression that the page header is being stretched. In Safari on macOS, this effect is preserved after applying the fix, but in Chrome it isn\u2019t (I <a href=\"https:\/\/issues.chromium.org\/issues\/324819919\">filed a Chromium issue<\/a> for this).<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='281' src='https:\/\/videopress.com\/embed\/eHxXmdvO?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=0&amp;persistVolume=1&amp;playsinline=0&amp;preloadContent=metadata&amp;useAverageColor=1&amp;posterUrl=https%3A%2F%2Ffrontendmasters.com%2Fblog%2Fwp-content%2Fuploads%2F2024%2F02%2Foverscroll-animation-poster.png&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1674852142'><\/script><\/div>\n\t\t\t<figcaption>Safari on the left, Chrome on the right<\/figcaption>\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>Some people may see this as a (minor) degradation in aesthetics, but I think that an invisible scrollbar is a much bigger problem for users, and websites should prioritize usability over elegance.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The page scrollbar in web browsers serves a useful function: The vertical position of the scrollbar thumb tells the user where they are in the page (their scroll position), while the size (height) of the scrollbar thumb tells them roughly how long the page is. Because scrollbars are useful, they should be clearly visible. On [&hellip;]<\/p>\n","protected":false},"author":10,"featured_media":842,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[7,107,92],"class_list":["post-825","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-css","tag-ios","tag-scrolling"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/02\/invis-background-thumb.jpg?fit=1000%2C500&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/825","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/10"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=825"}],"version-history":[{"count":4,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/825\/revisions"}],"predecessor-version":[{"id":841,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/825\/revisions\/841"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/842"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=825"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=825"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=825"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}