diff --git a/files/en-us/web/api/credentialscontainer/get/index.md b/files/en-us/web/api/credentialscontainer/get/index.md index 302d03e10cd524d..1c71b67f52ba465 100644 --- a/files/en-us/web/api/credentialscontainer/get/index.md +++ b/files/en-us/web/api/credentialscontainer/get/index.md @@ -50,7 +50,7 @@ get(options) > In the case of a [federated authentication (FedCM API)](/en-US/docs/Web/API/FedCM_API) request, a `mediation` value of `optional` or `silent` may result in attempted [auto-reauthentication](/en-US/docs/Web/API/FedCM_API/RP_sign-in#auto-reauthentication). Whether this occurred is communicated to the identity provider (IdP) via the [`is_auto_selected`](/en-US/docs/Web/API/FedCM_API/IDP_integration#is_auto_selected) parameter sent to the IdP's `id_assertion_endpoint` during validation and the relying party (RP) via the {{domxref("IdentityCredential.isAutoSelected")}} property. This is useful for performance evaluation, security requirements (the IdP may wish to reject automatic reauthentication requests and always require user mediation), and general UX (an IdP or RP may wish to present different UX for auto and non-auto login experiences). - `signal` {{optional_inline}} - - : An {{domxref("AbortSignal")}} object instance that allows an ongoing `get()` operation to be aborted. An aborted operation may complete normally (generally if the abort was received after the operation finished) or reject with an `AbortError` {{domxref("DOMException")}}. + - : An {{domxref("AbortSignal")}} object instance that allows an ongoing `get()` operation to be aborted. An aborted operation may complete normally (generally if the abort was received after the operation finished) or reject with the signal's reason (which is an `AbortError` {{domxref("DOMException")}} by default, or a custom value if one was provided when calling {{domxref("AbortController.abort", "abort()")}}). - `password` {{optional_inline}} - : This option asks the browser to retrieve a stored [password](/en-US/docs/Web/API/Credential_Management_API/Credential_types#passwords) as a {{domxref("PasswordCredential")}} object. It is a boolean value. @@ -99,6 +99,10 @@ If a single credential cannot be unambiguously obtained, the promise resolves wi - `AbortError` {{domxref("DOMException")}} - : The request was aborted by a call to the {{domxref("AbortController.abort", "abort()")}} method of the {{domxref("AbortController")}} associated with this method's [`signal`](#signal) option. + Note that if the caller of `abort()` provided a `reason` argument, then `get()` will be rejected with the value of `reason`, instead of an `AbortController` exception. + +- `TimeoutError` {{domxref("DOMException")}} + - : The request was automatically aborted due to a timeout set using {{domxref("AbortSignal.timeout_static", "AbortSignal.timeout()")}}. - {{domxref("IdentityCredentialError")}} - : When requesting an {{domxref("IdentityCredential")}}, the request to the [ID assertion endpoint](/en-US/docs/Web/API/FedCM_API/IDP_integration#the_id_assertion_endpoint) is unable to validate the authentication, and rejects with an error response containing information about the reason. @@ -257,6 +261,42 @@ navigator.credentials }); ``` +### Implementing a timeout + +In this example, we use {{domxref("AbortSignal.timeout_static", "AbortSignal.timeout()")}} to automatically abort the request if it takes longer than 10 seconds. + +```js +async function authenticateUser() { + const publicKey = { + challenge: new Uint8Array([139, 66, 181, 87, 7, 203 /* ,… */]), + rpId: "acme.com", + allowCredentials: [ + { + type: "public-key", + id: new Uint8Array([64, 66, 25, 78, 168, 226, 174 /* ,… */]), + }, + ], + userVerification: "required", + }; + + try { + const credential = await navigator.credentials.get({ + publicKey, + signal: AbortSignal.timeout(10000), // Abort after 10 seconds + }); + console.log("Authentication successful:", credential); + } catch (err) { + if (err.name === "TimeoutError") { + console.error("The authentication request timed out."); + } else if (err.name === "AbortError") { + console.log("The request was cancelled by the user."); + } else { + console.error("An unexpected error occurred:", err); + } + } +} +``` + ## Specifications {{Specifications}} diff --git a/files/en-us/web/css/guides/fonts/index.md b/files/en-us/web/css/guides/fonts/index.md index e8c98ca304b8949..d377bdb6cf773c4 100644 --- a/files/en-us/web/css/guides/fonts/index.md +++ b/files/en-us/web/css/guides/fonts/index.md @@ -80,6 +80,10 @@ The CSS fonts module also defines the {{cssxref("@font-feature-values/font-displ - {{cssxref("@font-palette-values/font-family", "font-family")}} - {{cssxref("@font-palette-values/override-colors", "override-colors")}} +### Functions + +The CSS fonts module defines the `generic()` function. Currently, no browsers support this feature. + ### Data types `font-size` types: diff --git a/files/en-us/web/css/reference/properties/scroll-padding/index.md b/files/en-us/web/css/reference/properties/scroll-padding/index.md index cf38fb206f9f350..7cc7c27fde4c8b3 100644 --- a/files/en-us/web/css/reference/properties/scroll-padding/index.md +++ b/files/en-us/web/css/reference/properties/scroll-padding/index.md @@ -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")}} @@ -19,7 +19,7 @@ scroll-padding: 20px; ``` ```css interactive-example-choice -scroll-padding: 2em; +scroll-padding: 20%; ``` ```html interactive-example @@ -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: @@ -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}} @@ -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 diff --git a/scripts/analyze-pr-build.js b/scripts/analyze-pr-build.js index c180b56670affcb..ebb293051231a3f 100644 --- a/scripts/analyze-pr-build.js +++ b/scripts/analyze-pr-build.js @@ -18,6 +18,10 @@ const MAX_COMMENT_BODY_LENGTH = 65000; const hiddenCommentRegex = //; +/** + * @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. @@ -25,14 +29,15 @@ const hiddenCommentRegex = */ 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); } @@ -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); } @@ -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 = `${title} (${countText})`; + if (expanded) { + return `${header}\n\n${body}`; + } + return `
${header}\n\n${body}\n\n
`; +} + /** * 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) { @@ -155,13 +173,13 @@ async function postAboutDeployment(buildDirectory, config) { links.sort(); if (links.length > 0) { - if (links.length > 5) { - const heading = `
Preview URLs (${links.length} pages)\n\n`; - return heading + links.join("\n") + "\n\n
"; - } else { - const heading = `Preview URLs\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!* 🙀"; } @@ -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; @@ -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; @@ -359,17 +375,30 @@ async function postAboutFlaws(buildDirectory, config) { lines.push(comment); perDocComments.push(lines.join("\n")); } - let heading = `\n
Flaws (${totalFlaws})\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
"; + 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 = [];