# Using CSS `scroll-padding` to un-obscure content

## Metadata

CSS

## When to Use

All technologies that support CSS.

## Description

The objective of this technique is to ensure that user interface components (for example: links, buttons, and form fields) that are initially completely obscured by a fixed-position component can still be accessed by users. In this example, this is achieved using CSS `padding` and `scroll-padding` properties to create space underneath the site footer and allow the link in the footer to scroll into view when it is focused with a keyboard.

## Examples

### Using CSS `scroll-padding` to un-obscure content

This example shows a situation where there is a fixed-position banner at the bottom of the screen that is covering up the site footer, which contains a link. This type of fixed-position banner is a common design for cookie-consent banners.

This example demonstrates a technique to un-obscure content underneath a fixed-position banner. To prevent the page becoming unusable at smaller screen sizes, the banner becomes un-fixed. To observe the `scroll-padding` effect you will need to use a viewport over 800px wide.

Working example: Using CSS `scroll-padding` to un-obscure content.

    <!doctype html>
    <html lang="en">
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
      <title>Using CSS scroll-padding to un-obscure content</title>
      <style>
        ...

        :root{
          --height-dialog: 400px;
          --breathing-room: 20px;
          --scroll-padding: calc(var(--height-dialog) + var(--breathing-room));
        }

        .wrapper {
          display:grid;
          gap:1rem;
          grid-template-columns:repeat(9, 1fr);
          grid-template-rows:8rem auto minmax(10rem, max-content);
          min-block-size: 100vh;
        }

        .wrapper > * {
          border:1px solid var(--black);
          padding:1rem;
        }

        header {
          grid-column:1 / -1;
          grid-row:1;
        }

        main {
          grid-column:1 / 8;
        }

        aside {
          grid-column:8 / 10;
        }

        footer {
          grid-column:1 / -1;
        }

        @media (max-width:50rem) {
          main {
           grid-column:1 / -1;
          }

          aside {
            grid-column:1 / -1;
          }
        }

        .fixed-position-banner {
          background:var(--banner-background);
          border:3px solid var(--banner-border);
          margin-block-end:0.5rem;
          padding:1rem 1rem 5rem;
          position:relative;
          width:calc(100vw - 1rem);
        }

        @media (min-width: 50rem) {
          html {
            padding-bottom:var(--height-dialog);
            scroll-padding-bottom:var(--scroll-padding);
          }

         .fixed-position-banner {
            margin-block-end:0;
            position:fixed;
            inset:auto 0 0 0;
          }
        }
        ...
      </style>
    </head>
    <body>
      <dialog class="fixed-position-banner">
        <h2 tabindex="-1">Fixed-Position Banner</h2>
        <button aria-label="close fixed-position banner" class="close-banner" type="button">
          ...
        </button>
      </dialog>
      <div class="wrapper">
        <header>
          <p>Header Content</p>
        </header>
        <main>
          <h1>Main Content</h1>
        </main>
        <aside>
          <h2>Sidebar Content</h2>
          <p><a href="https://example.com">Here's an example link in the sidebar</a>.</p>
        </aside>
        <footer>
          <h2>Footer Content</h2>
          <p><a href="https://example.com">Here's an example link in the footer</a>.</p>
        </footer>
      </div>
      <script>
        window.addEventListener('DOMContentLoaded', () => {
          const elFixedBanner = document.querySelector('dialog');
          const elCloseBannerBtn = document.querySelector(".close-banner");

          elFixedBanner.show();

          const getDialogHeight = () => {
            const height = elFixedBanner.offsetHeight;
            document.documentElement.style.setProperty('--height-dialog', `${height}px`);
            document.documentElement.style.setProperty('--breathing-room', `${height ? 20 : 0}px`);
          }

          const observer = new ResizeObserver(getDialogHeight);
          observer.observe(elFixedBanner);

          elCloseBannerBtn.addEventListener("click", function(e){
            elFixedBanner.close();
          }, false);
        });
      </script>
    </body>
    </html>

## Tests

### Procedure

For each user interface component that can receive keyboard focus:

1. Check that the user interface component is not entirely hidden when it receives keyboard focus.
2. Check that no part of the user interface component is hidden when it receives keyboard focus.

### Expected Results

- Check #1 is true.
- For Focus Not Obscured (Enhanced) (Level AAA), #2 is also true.

## Related Techniques

1. C32
2. C34

## Resources

1. W3C - CSS padding.
2. W3C - CSS the `scroll-padding` property.