Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions files/en-us/web/css/reference/properties/scroll-padding/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ browser-compat: css.properties.scroll-padding
sidebar: cssref
---

The **`scroll-padding`** [shorthand property](/en-US/docs/Web/CSS/Guides/Cascade/Shorthand_properties) sets scroll padding on all sides of an element at once, much like the {{cssxref("padding")}} property does for padding on an element.
The **`scroll-padding`** [shorthand property](/en-US/docs/Web/CSS/Guides/Cascade/Shorthand_properties) sets scroll padding on all sides of an element at once. It specifies offsets that define the optimal viewing region of a scrollport within a {{glossary("scroll container")}}.

{{InteractiveExample("CSS Demo: scroll-padding")}}

Expand All @@ -19,7 +19,7 @@ scroll-padding: 20px;
```

```css interactive-example-choice
scroll-padding: 2em;
scroll-padding: 20%;
```

```html interactive-example
Expand Down Expand Up @@ -70,8 +70,6 @@ scroll-padding: 2em;
}
```

The `scroll-padding-*` properties define offsets for the _optimal viewing region_ of the scrollport: the region used as the target region for placing things in view of the user. This allows the author to exclude regions of the scrollport that are obscured by other content (such as fixed-positioned toolbars or sidebars), or to put more breathing room between a targeted element and the edges of the scrollport.

## Constituent properties

This property is a shorthand for the following CSS properties:
Expand Down Expand Up @@ -107,6 +105,14 @@ scroll-padding: unset;
- `auto`
- : The offset is determined by the user agent. This will generally be `0px`, but the user agent is free to detect and do something else if a non-zero value is more appropriate.

## Description

The `scroll-padding` property is a shorthand that sets {{CSSXref("scroll-padding-top")}}, {{CSSXref("scroll-padding-right")}}, {{CSSXref("scroll-padding-bottom")}}, and {{CSSXref("scroll-padding-left")}}, in that order, setting the top, right, bottom, and left scroll padding of a scroll container, respectively.

Useful when creating scroll-snap containers, the `scroll-padding` property enables defining offsets for the _optimal viewing region_ of the scrollport: the region used as the target region for placing elements in view of the user. This allows you to create insets in the scrollport to make room for objects that might obscure the content, such as fixed-positioned toolbars or sidebars, or to put more breathing room between a targeted element and the edges of the scrollport.

While defined in the [CSS scroll snap](/en-US/docs/Web/CSS/Guides/Scroll_snap) module, this property applies to all scroll containers, no matter the value of the {{cssxref("scroll-snap-type")}} property.

## Formal definition

{{cssinfo}}
Expand All @@ -125,5 +131,8 @@ scroll-padding: unset;

## See also

- [CSS scroll snap](/en-US/docs/Web/CSS/Guides/Scroll_snap)
- [Well-controlled scrolling with CSS scroll snap](https://web.dev/articles/css-scroll-snap)
- {{cssxref("scroll-snap-type")}}
- [Basic concepts of scroll-snap](/en-US/docs/Web/CSS/Guides/Scroll_snap/Basic_concepts)
- [CSS scroll snap](/en-US/docs/Web/CSS/Guides/Scroll_snap) module
- [CSS overflow](/en-US/docs/Web/CSS/Guides/Overflow) module
- [CSS scroll-driven animations](/en-US/docs/Web/CSS/Guides/Scroll-driven_animations) module
85 changes: 57 additions & 28 deletions scripts/analyze-pr-build.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,26 @@ const MAX_COMMENT_BODY_LENGTH = 65000;
const hiddenCommentRegex =
/<!-- build_hash: ([a-f0-9]+) date: ([\d:.\-TZ]+) -->/;

/**
* @import { Doc } from "@mdn/rari"
*/

/**
* Main function to analyze a PR build directory and post (or print) a comment.
* @param {string} buildDirectory - Path to the build directory.
* @param {object} config - Configuration object.
*/
async function analyzePR(buildDirectory, config) {
const combinedComments = [];
const docs = await getBuiltDocs(buildDirectory);

if (config.prefix) {
const deploymentComment = await postAboutDeployment(buildDirectory, config);
const deploymentComment = postAboutDeployment(docs, config);
if (deploymentComment) combinedComments.push(deploymentComment);
}

if (config.analyze_flaws) {
const flawsComment = await postAboutFlaws(buildDirectory, config);
const flawsComment = postAboutFlaws(docs, config);
if (flawsComment) combinedComments.push(flawsComment);
}

Expand All @@ -47,11 +52,7 @@ async function analyzePR(buildDirectory, config) {
console.error(`Error reading diff file: ${err}`);
}
}
const dangerousComment = await postAboutDangerousContent(
buildDirectory,
patch,
config,
);
const dangerousComment = postAboutDangerousContent(docs, patch, config);
if (dangerousComment) combinedComments.push(dangerousComment);
}

Expand Down Expand Up @@ -137,13 +138,30 @@ function truncateComment(comment) {
return comment;
}

/**
* Formats a section with a heading, collapsed by default unless expanded is set.
* @param {object} options - Formatting options.
* @param {string} options.title - The section title (e.g., "Preview URLs", "Flaws").
* @param {number} options.count - The count to display in parentheses.
* @param {string} [options.countLabel] - Optional label after count (e.g., "pages").
* @param {string} options.body - The section content.
* @param {boolean} [options.expanded] - If true, show expanded (no details wrapper).
*/
function formatSection({ title, count, countLabel, body, expanded }) {
const countText = countLabel ? `${count} ${countLabel}` : count;
const header = `<b>${title}</b> (${countText})`;
if (expanded) {
return `${header}\n\n${body}`;
}
return `<details><summary>${header}</summary>\n\n${body}\n\n</details>`;
}

/**
* Constructs a comment about the deployment with preview URLs.
* @param {string} buildDirectory - Path to the build directory.
* @param {Doc[]} docs - Array of built document objects.
* @param {object} config - Configuration object.
*/
async function postAboutDeployment(buildDirectory, config) {
const docs = await getBuiltDocs(buildDirectory);
function postAboutDeployment(docs, config) {
let links = [];
for (const doc of docs) {
if (doc.mdn_url) {
Expand All @@ -155,13 +173,13 @@ async function postAboutDeployment(buildDirectory, config) {
links.sort();

if (links.length > 0) {
if (links.length > 5) {
const heading = `<details><summary><b>Preview URLs</b> (${links.length} pages)</summary>\n\n`;
return heading + links.join("\n") + "\n\n</details>";
} else {
const heading = `<b>Preview URLs</b>\n\n`;
return heading + links.join("\n");
}
return formatSection({
title: "Preview URLs",
count: links.length,
countLabel: links.length == 1 ? "page" : "pages",
body: links.join("\n"),
expanded: links.length <= 5,
});
}
return "*seems not a single file was built!* 🙀";
}
Expand All @@ -178,13 +196,12 @@ function mdnUrlToDevUrl(prefix, host, mdnUrl) {

/**
* Constructs a comment reporting any dangerous external URLs.
* @param {string} buildDirectory - Path to the build directory.
* @param {Doc[]} docs - Array of built document objects.
* @param {Array} patch - Array of patch objects (from parse-diff).
* @param {object} config - Configuration object.
*/
async function postAboutDangerousContent(buildDirectory, patch, config) {
function postAboutDangerousContent(docs, patch, config) {
const OK_URL_PREFIXES = ["https://github.com/mdn/"];
const docs = await getBuiltDocs(buildDirectory);
const comments = [];
let totalUrls = 0;

Expand Down Expand Up @@ -286,11 +303,10 @@ async function postAboutDangerousContent(buildDirectory, patch, config) {

/**
* Constructs a comment reporting document flaws.
* @param {string} buildDirectory - Path to the build directory.
* @param {Doc[]} docs - Array of built document objects.
* @param {object} config - Configuration object.
*/
async function postAboutFlaws(buildDirectory, config) {
const docs = await getBuiltDocs(buildDirectory);
function postAboutFlaws(docs, config) {
const comments = [];
const MAX_FLAW_EXPLANATION = 5;
let docsWithZeroFlaws = 0;
Expand Down Expand Up @@ -359,17 +375,30 @@ async function postAboutFlaws(buildDirectory, config) {
lines.push(comment);
perDocComments.push(lines.join("\n"));
}
let heading = `\n<details><summary><b>Flaws</b> (${totalFlaws})</summary>\n\n`;
if (docsWithZeroFlaws) {
heading += `Note! *${docsWithZeroFlaws} document${docsWithZeroFlaws === 1 ? "" : "s"} with no flaws that don't need to be listed. 🎉*\n\n`;
}
return heading + perDocComments.join("\n\n---\n\n") + "\n\n</details>";
const zeroFlawsNote = docsWithZeroFlaws
? `Note! *${docsWithZeroFlaws} document${docsWithZeroFlaws === 1 ? "" : "s"} with no flaws that don't need to be listed. 🎉*\n\n`
: "";

const reportIssueNote =
"*Found an unexpected or unresolvable flaw? [Please report it here](https://github.com/mdn/rari/issues/new?template=bug.yml).*\n\n";

return (
"\n" +
formatSection({
title: "Flaws",
count: totalFlaws,
body:
zeroFlawsNote + reportIssueNote + perDocComments.join("\n\n---\n\n"),
expanded: docs.length <= 5 || totalFlaws <= 5,
})
);
}
}

/**
* Recursively finds and returns the parsed JSON document objects from all index.json files.
* @param {string} buildDirectory - Path to the build directory.
* @returns {Doc[]}
*/
async function getBuiltDocs(buildDirectory) {
const docs = [];
Expand Down