diff --git a/content/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-policies-for-code-security-and-analysis-for-your-enterprise.md b/content/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-policies-for-code-security-and-analysis-for-your-enterprise.md index da10260c6e95..8c5a01aa3b59 100644 --- a/content/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-policies-for-code-security-and-analysis-for-your-enterprise.md +++ b/content/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-policies-for-code-security-and-analysis-for-your-enterprise.md @@ -127,17 +127,15 @@ Across all of your enterprise's organizations, you can allow or disallow people {% ifversion ai-powered-security-detections %} -## Enforcing a policy to manage the use of AI-powered security detections in your enterprise's repositories +## Enforcing a policy to manage AI Scan in your enterprise's repositories -As an enterprise owner, you can control whether organization and repository administrators can enable AI-powered security detections for their organizations and repositories. This policy is set to "Not allowed" by default. +As an enterprise owner, you can control whether organizations in your enterprise can enable AI Scan. This policy is set to "Not allowed" by default. -Allowing AI-powered security detections at the enterprise level does not enable the feature. Organization administrators must still explicitly enable AI-powered security detections. Repository administrators can opt-out of the feature. - -This policy only takes effect if {% data variables.product.prodname_codeql %} default setup is enabled. +Allowing AI Scan at the enterprise level does not enable it for any organization. Organization administrators must enable AI Scan for their organization. Eligible repositories where {% data variables.product.prodname_code_scanning %} is enabled inherit the organization setting, and repository administrators can opt out for individual repositories. {% data reusables.enterprise-accounts.access-enterprise %} {% data reusables.enterprise-accounts.policies-tab %} {% data reusables.enterprise-accounts.code-security-and-analysis-policies %} -1. Under "AI Findings", select the dropdown menu and click a policy. +1. Under "AI Scan", select the dropdown menu and click a policy. {% endif %} diff --git a/content/code-security/concepts/code-scanning/ai-powered-security-detections.md b/content/code-security/concepts/code-scanning/ai-powered-security-detections.md index aff4e62a426a..1de37f973a1b 100644 --- a/content/code-security/concepts/code-scanning/ai-powered-security-detections.md +++ b/content/code-security/concepts/code-scanning/ai-powered-security-detections.md @@ -1,8 +1,8 @@ --- -title: AI-powered security detections in pull requests -shortTitle: AI-powered security detections +title: AI Scan for pull requests +shortTitle: AI Scan allowTitleToDifferFromFilename: true -intro: 'AI-powered security detections use an AI-based scanning engine to find security vulnerabilities in pull requests for languages and frameworks not covered by {% data variables.product.prodname_codeql %}.' +intro: 'AI Scan uses an AI-based scanning engine to find security vulnerabilities in pull requests for languages and frameworks not covered by {% data variables.product.prodname_codeql %}.' versions: feature: ai-powered-security-detections contentType: concepts @@ -11,50 +11,51 @@ category: --- > [!NOTE] -> AI-powered security detections are currently in {% data variables.release-phases.public_preview %} and subject to change. +> AI Scan is currently in {% data variables.release-phases.public_preview %} and subject to change. -AI-powered security detections are additional security findings produced by an AI-based scanning engine that runs on pull requests and complements {% data variables.product.prodname_codeql %}. Unlike {% data variables.product.prodname_codeql %} alerts, AI-powered findings are only available on pull requests and do not appear as backlog alerts in the repository's security view. +AI Scan produces additional security findings with an AI-based scanning engine that runs on pull requests and complements {% data variables.product.prodname_codeql %}. Unlike {% data variables.product.prodname_codeql %} alerts, AI Scan findings are only available on pull requests and do not appear as backlog alerts in the repository's security view. -While {% data variables.product.prodname_codeql %} provides high-precision static analysis for a specific set of supported languages and queries, many repositories use languages and frameworks that {% data variables.product.prodname_codeql %} does not cover. AI-powered detections expand {% data variables.product.prodname_code_scanning %} coverage into these areas, helping you find vulnerabilities without adding new tools or configuration. +While {% data variables.product.prodname_codeql %} provides high-precision static analysis for a specific set of supported languages and queries, many repositories use languages and frameworks that {% data variables.product.prodname_codeql %} does not cover. AI Scan expands {% data variables.product.prodname_code_scanning %} coverage into these areas, helping you find vulnerabilities without adding new tools or configuration. -During the {% data variables.release-phases.public_preview %}, AI-powered security detections require a {% data variables.product.prodname_GHAS %} license and a {% data variables.product.prodname_copilot %} license. +During the {% data variables.release-phases.public_preview %}, AI Scan requires a {% data variables.product.prodname_GHAS %} license and a {% data variables.product.prodname_copilot %} license. Usage consumes {% data variables.product.prodname_ai_credits_short %}. See [AUTOTITLE](/copilot/concepts/billing/organizations-and-enterprises/usage-based-billing). -## How AI-powered security detections work +## How AI Scan works -AI-powered security detections run automatically on pull requests in repositories where {% data variables.product.prodname_codeql %} default setup is enabled and AI-powered detections have been opted into. The AI-based scan is triggered on pull request creation and after each new commit, the same as {% data variables.product.prodname_codeql %}. +AI Scan runs automatically when {% data variables.product.prodname_code_scanning %} is enabled for a repository, the repository's effective AI Scan setting is enabled, and an eligible pull request contains qualifying changes in a supported language or framework that {% data variables.product.prodname_codeql %} does not cover. These conditions also determine when AI Scan usage begins. The scan is triggered on pull request creation and after each new commit, the same as {% data variables.product.prodname_codeql %}. -AI-powered findings are advisory and do not block pull request merges. They provide signals about where code security can be improved without interrupting your workflow. +AI Scan does not require {% data variables.product.prodname_codeql %} default setup and does not depend on the state of {% data variables.product.prodname_codeql %} analysis. If {% data variables.product.prodname_codeql %} analysis fails or is in a waiting state, AI Scan will still run. -The AI scanning engine works directly with the code in the pull request and does not require a build system. It uses tools such as code search to gather additional context from the repository when deciding whether to flag an issue. It uses its own specialized prompts and does not use custom instruction files such as `/.github/copilot-instructions.md` or `/CLAUDE.md`. +AI Scan findings are advisory and do not block pull request merges. They provide signals about where code security can be improved without interrupting your workflow. -The AI scan runs independently of {% data variables.product.prodname_codeql %}'s status. If {% data variables.product.prodname_codeql %} default setup fails or is in a waiting state, AI-powered detections will still run. +The AI scanning engine works directly with the code in the pull request and does not require a build system. It uses tools such as code search to gather additional context from the repository when deciding whether to flag an issue. It uses its own specialized prompts and does not use custom instruction files such as `/.github/copilot-instructions.md` or `/CLAUDE.md`. -Results are posted to the pull request as they are found. If the {% data variables.product.prodname_codeql %} scan takes longer to complete, you may see AI-powered findings before {% data variables.product.prodname_codeql %} results appear, or vice versa. +Results are posted to the pull request as they are found. If the {% data variables.product.prodname_codeql %} scan takes longer to complete, you may see AI Scan findings before {% data variables.product.prodname_codeql %} results appear, or vice versa. -## How findings appear on pull requests +## How AI Scan findings appear on pull requests -AI-powered findings appear alongside {% data variables.product.prodname_codeql %} alerts on the **Conversation** and **Files changed** tabs of a pull request. Each AI-powered finding is labeled with an "AI" indicator so you can distinguish it from {% data variables.product.prodname_codeql %} alerts. +AI Scan findings appear alongside {% data variables.product.prodname_codeql %} alerts on the **Conversation** and **Files changed** tabs of a pull request. Each finding is labeled with an "AI" indicator so you can distinguish it from {% data variables.product.prodname_codeql %} alerts. Each finding includes a description of the security issue and an explanation of the risk. Most findings also include a suggested remediation, but not every finding has one. Where a suggested remediation is available, {% data variables.copilot.copilot_autofix_short %} is included and provides a recommended code change to fix the issue, the same way it does for {% data variables.product.prodname_codeql %} alerts. Findings also include a thumbs up/down feedback mechanism that helps improve detection quality over time. ## Limitations -* AI-powered security detections analyze pull requests only. Full repository scans are not supported. -* AI-powered findings cannot yet be used in rulesets to enforce merge requirements +* AI Scan analyzes pull requests only. Full repository scans are not supported. +* AI Scan findings cannot yet be used in rulesets to enforce merge requirements. +* AI Scan does not run on pull requests from forks or pull requests created by {% data variables.product.prodname_dependabot %}. * Detection categories and supported languages may change as the feature evolves. -* As with any AI-based tool, findings may include false positives. Use the feedback mechanism to report inaccurate results. +* As with any AI-based tool, AI Scan findings may include false positives. Use the feedback mechanism to report inaccurate results. ## Supported languages -AI-powered security detections are designed to cover languages and frameworks that are not currently supported by {% data variables.product.prodname_codeql %}. This includes, but is not limited to, languages such as PHP, Shell/Bash, Terraform configuration (HCL), and Dockerfiles, as well as framework coverage gaps such as JSP for Java and Blazor for C#. +AI Scan is designed to cover languages and frameworks that are not currently supported by {% data variables.product.prodname_codeql %}. This includes, but is not limited to, languages such as PHP, Shell/Bash, Terraform configuration (HCL), and Dockerfiles, as well as framework coverage gaps such as JSP for Java and Blazor for C#. For a full list of languages supported by {% data variables.product.prodname_codeql %}, see [AUTOTITLE](/code-security/concepts/code-scanning/codeql/codeql-code-scanning). ## Detection categories -AI-powered security detections currently cover the following categories. These categories describe how findings are classified. The AI scanner may evolve over time as models improve. +AI Scan currently covers the following categories. These categories describe how findings are classified. The scanning engine may evolve over time as models improve. * **String injection** — Unsafe string-built SQL, HTML, shell, JSON, or YAML with missing or incorrect escaping or sanitization. * **Weak cryptography** — Weak algorithms, small keys, insecure randomness, missing encryption, or weak password hashing. @@ -66,12 +67,14 @@ AI-powered security detections currently cover the following categories. These c * **Server-side request forgery (SSRF)** — Server fetches attacker-controlled URLs, hosts, or protocols. * **Supply chain risks** — Unpinned third-party actions, packages, or images, or downloads without integrity checks. -## Enabling AI-powered security detections +## Enabling AI Scan + +AI Scan is not allowed at the enterprise level by default and disabled at the organization and repository levels. Enterprise owners must allow AI Scan before organization administrators can enable it. Enabling AI Scan for an organization applies it to eligible repositories where {% data variables.product.prodname_code_scanning %} is enabled. Repository administrators can opt out for individual repositories. For an eligible public repository owned by a personal account, a repository administrator must enable AI Scan directly for the repository. -AI-powered security detections are not allowed at the enterprise level by default and disabled at the organization and repository levels. Enterprise administrators must explicitly allow the feature before organizations can enable it. Organization administrators must explicitly opt in to the feature. Repository administrators can opt-out of the feature. Additionally, you need to have the {% data variables.product.prodname_codeql %} default setup enabled. +You do not need to select a model to enable AI Scan. -You do not need to select a model to enable AI-powered security detections. +* **Enterprise**: The **AI Scan** policy under "Code Security" controls whether organizations can enable the feature. See [AUTOTITLE](/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-policies-for-code-security-and-analysis-for-your-enterprise#enforcing-a-policy-to-manage-ai-scan-in-your-enterprises-repositories). +* **Organization**: The **AI Scan** setting under "Code scanning" enables AI Scan for eligible repositories in the organization where {% data variables.product.prodname_code_scanning %} is enabled. See [AUTOTITLE](/code-security/how-tos/secure-at-scale/configure-organization-security/establish-complete-coverage/configure-global-settings#enabling-ai-scan). +* **Repository**: For an organization-owned repository, the **AI Scan** toggle under "Code scanning" lets repository administrators opt out when AI Scan is enabled for the organization. For an eligible public repository owned by a personal account, the toggle enables or disables AI Scan directly. -* **Enterprise**: The **AI Findings** policy under "Code Security" controls whether organizations can enable the feature. See [AUTOTITLE](/admin/enforcing-policies/enforcing-policies-for-your-enterprise/enforcing-policies-for-code-security-and-analysis-for-your-enterprise#enforcing-a-policy-to-manage-the-use-of-ai-powered-security-detections-in-your-enterprises-repositories). -* **Organization**: The **AI findings** setting under "Code scanning" enables AI-powered detections for repositories in the organization. See [AUTOTITLE](/code-security/how-tos/secure-at-scale/configure-organization-security/establish-complete-coverage/configure-global-settings#enabling-ai-powered-security-detections). -* **Repository**: The **AI findings** toggle under "Code scanning" enables or disables AI-powered detections for the individual repository. Repositories inherit the organization setting but can opt out individually. +You can use the REST API to manage the organization or repository `ai-scan` setting. See [AUTOTITLE](/rest/code-scanning/code-scanning#get-the-ai-scan-setting-for-an-organization). diff --git a/content/code-security/getting-started/github-security-features.md b/content/code-security/getting-started/github-security-features.md index fb43ea3d8f64..6498b46930bd 100644 --- a/content/code-security/getting-started/github-security-features.md +++ b/content/code-security/getting-started/github-security-features.md @@ -205,7 +205,7 @@ Get automatically generated fixes for {% data variables.product.prodname_code_sc {% ifversion ai-powered-security-detections %} -### AI-powered security detections +### AI Scan Find vulnerabilities in languages and frameworks not covered by {% data variables.product.prodname_codeql %} with an AI-based scanning engine that runs during pull request review. See [AUTOTITLE](/code-security/concepts/code-scanning/ai-powered-security-detections). diff --git a/content/code-security/getting-started/quickstart-for-securing-your-repository.md b/content/code-security/getting-started/quickstart-for-securing-your-repository.md index 2a0c3501c480..d787c3e67320 100644 --- a/content/code-security/getting-started/quickstart-for-securing-your-repository.md +++ b/content/code-security/getting-started/quickstart-for-securing-your-repository.md @@ -123,10 +123,16 @@ You can configure {% data variables.product.prodname_code_scanning %} to automat 1. If "{% data variables.product.prodname_code_security %}" or "{% data variables.product.prodname_GHAS %}" is not already enabled, click **Enable**. 1. To the right of "CodeQL analysis", select **Set up** {% octicon "triangle-down" aria-hidden="true" aria-label="triangle-down" %}, then click **Default**. 1. In the pop-up window that appears, review the default configuration settings for your repository, then click **Enable {% data variables.product.prodname_codeql %}**.{% ifversion code-scanning-autofix %} -1. Choose whether you want to enable addition features, such as {% data variables.copilot.copilot_autofix_short %} or AI-powered security detections.{% endif %} +1. Choose whether you want to enable additional features, such as {% data variables.copilot.copilot_autofix_short %}.{% endif %} As an alternative to default setup, you can use advanced setup, which generates a workflow file you can edit to customize your {% data variables.product.prodname_code_scanning %} with {% data variables.product.prodname_codeql %}. For more information, see [AUTOTITLE](/code-security/how-tos/find-and-fix-code-vulnerabilities/configure-code-scanning/configuring-advanced-setup-for-code-scanning#configuring-advanced-setup-for-code-scanning-with-codeql). +{% ifversion ai-powered-security-detections %} + +You can separately enable AI Scan to extend {% data variables.product.prodname_code_scanning %} coverage to supported languages and frameworks that {% data variables.product.prodname_codeql %} does not cover. AI Scan requires {% data variables.product.prodname_code_scanning %}, but does not require {% data variables.product.prodname_codeql %} default setup. See [AUTOTITLE](/code-security/concepts/code-scanning/ai-powered-security-detections). + +{% endif %} + ## Configuring {% data variables.product.prodname_secret_protection %} {% ifversion fpt or ghec %} diff --git a/content/code-security/how-tos/secure-at-scale/configure-organization-security/establish-complete-coverage/configure-global-settings.md b/content/code-security/how-tos/secure-at-scale/configure-organization-security/establish-complete-coverage/configure-global-settings.md index f4faffd6ca03..dc5c95320a66 100644 --- a/content/code-security/how-tos/secure-at-scale/configure-organization-security/establish-complete-coverage/configure-global-settings.md +++ b/content/code-security/how-tos/secure-at-scale/configure-organization-security/establish-complete-coverage/configure-global-settings.md @@ -107,7 +107,7 @@ You can customize several {% data variables.product.prodname_global_settings %} * [Enabling {% data variables.copilot.copilot_autofix_short %} for {% data variables.product.prodname_codeql %}](#enabling-copilot-autofix-for-codeql) {%- endif %} {%- ifversion ai-powered-security-detections %} -* [Enabling AI-powered security detections](#enabling-ai-powered-security-detections) +* [Enabling AI Scan](#enabling-ai-scan) {%- endif %} * [Expanding {% data variables.product.prodname_codeql %} analysis](#expanding-codeql-analysis) {%- ifversion code-scanning-inactive-repos %} @@ -130,9 +130,9 @@ You can select **{% data variables.copilot.copilot_autofix_short %}** to enable {% ifversion ai-powered-security-detections %} -### Enabling AI-powered security detections +### Enabling AI Scan -You can select **AI-powered security detections** to enable AI-powered security detections for all repositories in your organization that use {% data variables.product.prodname_codeql %} default setup. See [AUTOTITLE](/code-security/concepts/code-scanning/ai-powered-security-detections). +You can select **AI Scan** to enable AI Scan for eligible repositories in your organization where {% data variables.product.prodname_code_scanning %} is enabled. Repositories inherit the organization setting, but repository administrators can opt out for individual repositories. See [AUTOTITLE](/code-security/concepts/code-scanning/ai-powered-security-detections). {% endif %} diff --git a/content/get-started/learning-about-github/about-github-advanced-security.md b/content/get-started/learning-about-github/about-github-advanced-security.md index c69f11faa53a..93683fb4336e 100644 --- a/content/get-started/learning-about-github/about-github-advanced-security.md +++ b/content/get-started/learning-about-github/about-github-advanced-security.md @@ -36,7 +36,7 @@ You get the following features with {% data variables.product.prodname_GH_code_s * **{% data variables.copilot.copilot_autofix_short %}**: Get automatically generated fixes for {% data variables.product.prodname_code_scanning %} alerts.{% ifversion ai-powered-security-detections %} -* **AI-powered security detections**: Find vulnerabilities in languages and frameworks not covered by {% data variables.product.prodname_codeql %} with an AI-based scanning engine that runs during pull request review.{% endif %}{% endif %}{% ifversion security-campaigns %} +* **AI Scan**: Find vulnerabilities in languages and frameworks not covered by {% data variables.product.prodname_codeql %} with an AI-based scanning engine that runs during pull request review.{% endif %}{% endif %}{% ifversion security-campaigns %} * **Security campaigns**: Reduce security debt at scale.{% endif %} diff --git a/src/color-schemes/components/BrandThemeProvider.tsx b/src/color-schemes/components/BrandThemeProvider.tsx index 67f63c9107bd..b59c4add706e 100644 --- a/src/color-schemes/components/BrandThemeProvider.tsx +++ b/src/color-schemes/components/BrandThemeProvider.tsx @@ -1,4 +1,4 @@ -import type { PropsWithChildren } from 'react' +import { useEffect, useState, type PropsWithChildren } from 'react' import { useTheme as usePrimerTheme } from '@primer/react' import { ThemeProvider } from '@primer/react-brand' @@ -9,7 +9,31 @@ export const BrandThemeProvider = ({ children }: PropsWithChildren) => { // otherwise many unhandled edge cases. // E.g. auto mode + dark mode + light scheme. const { resolvedColorScheme } = usePrimerTheme() - const colorMode = getBrandColorMode(resolvedColorScheme) + + // Brand's ThemeProvider renders a real `
`, and brand + // declares its ENTIRE palette on the bare `[data-color-mode="light"]` / + // `[data-color-mode="dark"]` attribute. So a nested wrapper re-declares every + // brand token for its own subtree — canvas, text, borders, links, the lot. + // + // `resolvedColorScheme` is only correct after PRC's cookie-reading effect has + // run. On the server it resolves to light, and the first client render has to + // match the server markup, so it is light there too. Emitting `light` would + // override the correct mode colorModeScript has already stamped on `` + // before first paint, and every brand token on the page would resolve to its + // light value until React hydrates — a white flash on every dark-mode load. + // + // We cannot server-render the real mode: that HTML is shared-cacheable in the + // CDN and must be identical for every request, which is why the pre-paint + // script exists at all. + // + // `auto` matches none of brand's blocks, so the wrapper declares nothing and + // brand's tokens inherit from ``. Verified in-browser: with `` at + // dark, a wrapper at `auto` resolves byte-identical token values to the root, + // whereas a wrapper at `light` flips all 16 tokens this app uses. + const [hydrated, setHydrated] = useState(false) + useEffect(() => setHydrated(true), []) + + const colorMode = hydrated ? getBrandColorMode(resolvedColorScheme) : 'auto' return {children} } diff --git a/src/content-render/stylesheets/accessibility.scss b/src/content-render/stylesheets/accessibility.scss index a3f11ccfaa30..4f859c02cf71 100644 --- a/src/content-render/stylesheets/accessibility.scss +++ b/src/content-render/stylesheets/accessibility.scss @@ -48,7 +48,13 @@ .tooltipped { &:focus-visible::before, &:focus-visible::after { - outline: 2px solid var(--color-focus-outset); + // --color-focus-outset is defined nowhere in this app, which made the whole + // `outline` shorthand invalid at computed-value time — so the longhands reset + // and `outline-style: none` won, leaving high-contrast users with NO focus + // ring at all, in either colour mode. Brand's focus token, matching the same + // repair already made in annotate.scss. + outline: var(--brand-borderWidth-thick, 2px) solid + var(--brand-color-focus, #0377ff); outline-offset: 2px; } } diff --git a/src/content-render/stylesheets/syntax-highlighting.scss b/src/content-render/stylesheets/syntax-highlighting.scss index 791d64163f02..30b9b9efa5ae 100644 --- a/src/content-render/stylesheets/syntax-highlighting.scss +++ b/src/content-render/stylesheets/syntax-highlighting.scss @@ -8,7 +8,11 @@ from https://unpkg.com/highlight.js@9.15.8/styles/github.css .hljs { display: block; padding: 0.5em; - color: var(--color-fg-default); + // The block's BASE text colour — the tokens below are prettylights, which has + // no Brand equivalent and stays on Primer deliberately, but this one is just + // "default text" and was painting Primer's #e6edf3 inside a Brand-framed code + // block. The `background` is inert here (markdown-overrides paints the `pre`). + color: var(--brand-color-text-default); background: var(--color-canvas-subtle); } diff --git a/src/fixtures/tests/playwright-rendering.spec.ts b/src/fixtures/tests/playwright-rendering.spec.ts index c238b036fa50..4dc9e1909542 100644 --- a/src/fixtures/tests/playwright-rendering.spec.ts +++ b/src/fixtures/tests/playwright-rendering.spec.ts @@ -1,7 +1,11 @@ import dotenv from 'dotenv' import { test, expect } from '@playwright/test' import { turnOffExperimentsInPage } from '../helpers/turn-off-experiments' -import { HOVERCARDS_ENABLED, ANALYTICS_ENABLED } from '../../frame/lib/constants' +import { + HOVERCARDS_ENABLED, + ANALYTICS_ENABLED, + COLOR_MODE_COOKIE_NAME, +} from '../../frame/lib/constants' // This exists for the benefit of local testing. // In GitHub Actions, we rely on setting the environment variable directly @@ -19,6 +23,52 @@ test('view home page', async ({ page }) => { await expect(page).toHaveTitle(/GitHub Docs/) }) +test.describe('Brand document canvas', () => { + test('follows system color scheme changes in auto mode without a cookie', async ({ page }) => { + await page.emulateMedia({ colorScheme: 'dark' }) + await page.goto('/get-started/foo/bar') + await expect(page.locator('html')).toHaveAttribute('data-color-mode', 'auto') + + // Check both the initial dark paint and live preference changes without reloading. + for (const colorScheme of ['dark', 'light', 'dark'] as const) { + await page.emulateMedia({ colorScheme }) + const backgroundColor = colorScheme === 'dark' ? 'rgb(0, 0, 0)' : 'rgb(255, 255, 255)' + const textColor = colorScheme === 'dark' ? 'rgb(255, 255, 255)' : 'rgb(0, 0, 0)' + + for (const selector of ['html', 'body']) { + await expect(page.locator(selector)).toHaveCSS('background-color', backgroundColor) + await expect(page.locator(selector)).toHaveCSS('color', textColor) + } + } + }) + + for (const colorMode of ['light', 'dark'] as const) { + test(`preserves explicit ${colorMode} mode against the opposite system preference`, async ({ + page, + context, + baseURL, + }) => { + await page.emulateMedia({ colorScheme: colorMode === 'light' ? 'dark' : 'light' }) + await context.addCookies([ + { + name: COLOR_MODE_COOKIE_NAME, + value: encodeURIComponent(JSON.stringify({ color_mode: colorMode })), + url: new URL('/', baseURL).href, + }, + ]) + await page.goto('/get-started/foo/bar') + await expect(page.locator('html')).toHaveAttribute('data-color-mode', colorMode) + + const backgroundColor = colorMode === 'dark' ? 'rgb(0, 0, 0)' : 'rgb(255, 255, 255)' + const textColor = colorMode === 'dark' ? 'rgb(255, 255, 255)' : 'rgb(0, 0, 0)' + for (const selector of ['html', 'body']) { + await expect(page.locator(selector)).toHaveCSS('background-color', backgroundColor) + await expect(page.locator(selector)).toHaveCSS('color', textColor) + } + }) + } +}) + test('logo link keeps current version', async ({ page }) => { await page.goto('/enterprise-cloud@latest') await turnOffExperimentsInPage(page) diff --git a/src/frame/components/DefaultLayout.module.scss b/src/frame/components/DefaultLayout.module.scss index 93c832ddee7f..695b62ceb52b 100644 --- a/src/frame/components/DefaultLayout.module.scss +++ b/src/frame/components/DefaultLayout.module.scss @@ -61,3 +61,13 @@ display: flex; } } + +// The a11y skip link is a filled accent chip. Was primer/css +// `color-bg-accent-emphasis color-fg-on-emphasis`, which paints the chip from +// the PRC palette on a Brand surface. The Brand accent fill inverts between +// modes (dark green in light, light green in dark), so onEmphasis text is +// correct here. +.skipButton { + background-color: var(--brand-color-accent-primary); + color: var(--brand-color-text-onEmphasis); +} diff --git a/src/frame/components/DefaultLayout.tsx b/src/frame/components/DefaultLayout.tsx index b7d3bf153f6e..f61788f770e1 100644 --- a/src/frame/components/DefaultLayout.tsx +++ b/src/frame/components/DefaultLayout.tsx @@ -223,7 +223,7 @@ export const DefaultLayout = (props: Props) => { {/* a11y */} diff --git a/src/frame/components/GenericError.module.scss b/src/frame/components/GenericError.module.scss index 247697d1cbf3..3b498e3ec00a 100644 --- a/src/frame/components/GenericError.module.scss +++ b/src/frame/components/GenericError.module.scss @@ -1,3 +1,20 @@ .logoContainer { z-index: 3; } + +// Header rule under the simple error-page banner. Was primer/css +// `border-bottom color-border-muted`; the utility emits the whole shorthand +// with !important, so the replacement has to own the shorthand too. +.headerBorder { + border-bottom: 1px solid var(--brand-color-border-muted); +} + +// Wordmark logo + "GitHub Docs" link. Was primer/css `color-fg-default`. +.fgDefault { + color: var(--brand-color-text-default); +} + +// Footer copyright line. Was primer/css `color-fg-muted`. +.fgMuted { + color: var(--brand-color-text-muted); +} diff --git a/src/frame/components/GenericError.tsx b/src/frame/components/GenericError.tsx index 15e3dd3a56c4..9febf868dc35 100644 --- a/src/frame/components/GenericError.tsx +++ b/src/frame/components/GenericError.tsx @@ -40,7 +40,7 @@ export function GenericError() { export const SimpleHeader = () => { const router = useRouter() return ( -
+
{ >
- + GitHub Docs @@ -68,7 +68,7 @@ export const SimpleFooter = () => {