From 9bc78c86b84995bee8807e9242e3cbbabc0096d4 Mon Sep 17 00:00:00 2001 From: Howard Edwards Date: Thu, 12 Dec 2024 11:45:50 -0500 Subject: [PATCH 01/25] Update `APG` to `WAI` pipeline workflows to handle desync issues and better reports on errors (#361) * Fix #219 * Update 'deploy.yml' to use 'w3cgruntbot' as committer * Update 'pr-create.yml' to force a rebase to happen with main in the case generated branch doesn't have relevant changes; additional error handling coverage with 'create-pr' script * For consistency, rename 'create-pr' script to 'pr-create' * Remove unnecessary delete branch action --- .github/workflows/deploy.yml | 7 +- .github/workflows/pr-create.yml | 46 ++-- .github/workflows/remove-branch.yml | 23 +- scripts/create-pr/index.js | 108 -------- scripts/create-pr/package-lock.json | 372 ---------------------------- scripts/create-pr/package.json | 5 - scripts/pr-create/index.js | 167 +++++++++++++ scripts/pr-create/package-lock.json | 281 +++++++++++++++++++++ scripts/pr-create/package.json | 13 + 9 files changed, 508 insertions(+), 514 deletions(-) delete mode 100644 scripts/create-pr/index.js delete mode 100644 scripts/create-pr/package-lock.json delete mode 100644 scripts/create-pr/package.json create mode 100644 scripts/pr-create/index.js create mode 100644 scripts/pr-create/package-lock.json create mode 100644 scripts/pr-create/package.json diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4a3d9fb01..300c25a84 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -36,9 +36,12 @@ jobs: node ./scripts/pre-build - name: Commit changed files and submodule updates - uses: stefanzweifel/git-auto-commit-action@v4 + uses: stefanzweifel/git-auto-commit-action@v5 with: - commit_message: Commit changes + commit_user_name: w3cgruntbot + commit_user_email: w3cgruntbot@users.noreply.github.com + commit_author: w3cgruntbot + commit_message: "chore: Update `main` with latest changes from `aria-practices`" branch: main env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pr-create.yml b/.github/workflows/pr-create.yml index c769504fd..07cf0e2bd 100644 --- a/.github/workflows/pr-create.yml +++ b/.github/workflows/pr-create.yml @@ -17,16 +17,16 @@ on: required: false jobs: - create-pr: + pr-create: runs-on: ubuntu-latest steps: - name: Get current job and log url - uses: Tiryoh/gha-jobid-action@v0 + uses: Tiryoh/gha-jobid-action@v1 id: jobs with: github_token: ${{ secrets.GITHUB_TOKEN }} - job_name: create-pr + job_name: pr-create - uses: actions/checkout@v4 with: @@ -45,26 +45,30 @@ jobs: ruby-version: 3.3 - name: Update git submodules - if: github.event.inputs.fork_path == 'false' run: | git submodule update --recursive --remote cd _external/aria-practices + if [ -n "${{ github.event.inputs.fork_path }}" ] && [ "${{ github.event.inputs.fork_path }}" != 'false' ]; then + echo "Found fork: ${{ github.event.inputs.fork_path }}" + git remote add fork https://github.com/${{ github.event.inputs.fork_path }}.git + git fetch fork + fi git checkout ${{ github.event.inputs.apg_sha }} - - name: Update git submodules from fork - if: github.event.inputs.fork_path != 'false' - run: | - git submodule update --recursive --remote - cd _external/aria-practices - git remote add fork https://github.com/${{ github.event.inputs.fork_path }}.git - git fetch fork - git checkout ${{ github.event.inputs.apg_sha }} - - - name: Switch to and create/update branch + - name: Switch to and create or update branch run: | # switch to branch if exists or create branch git switch apg/${{ github.event.inputs.apg_branch }} 2>/dev/null || git switch -c apg/${{ github.event.inputs.apg_branch }} + - name: Force rebase the switched branch onto main + run: | + git config --global user.name "w3cgruntbot" + git config --global user.email "w3cgruntbot@users.noreply.github.com" + git fetch origin main + git rebase --force-rebase origin/main + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Update site files id: update_site_files working-directory: ./ @@ -76,19 +80,23 @@ jobs: - name: Commit changed files and submodule updates if: steps.update_site_files.outcome == 'success' - uses: stefanzweifel/git-auto-commit-action@v4 + uses: stefanzweifel/git-auto-commit-action@v5 with: - commit_message: Commit changed files and submodule updates + commit_user_name: w3cgruntbot + commit_user_email: w3cgruntbot@users.noreply.github.com + commit_author: w3cgruntbot + commit_message: "chore: Update branch with latest `${{ github.event.inputs.apg_branch }}` changes and submodule updates" branch: apg/${{ github.event.inputs.apg_branch }} push_options: '--force' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Create pull request + - name: Create or update pull request working-directory: ./ run: | - npm install -C scripts/create-pr - node ./scripts/create-pr + set -e + npm install -C scripts/pr-create + node ./scripts/pr-create env: GH_TOKEN: ${{ secrets.W3CGRUNTBOT_TOKEN }} APG_SHA: ${{ github.event.inputs.apg_sha }} diff --git a/.github/workflows/remove-branch.yml b/.github/workflows/remove-branch.yml index 3b5fe6d41..73e398e70 100644 --- a/.github/workflows/remove-branch.yml +++ b/.github/workflows/remove-branch.yml @@ -12,17 +12,24 @@ jobs: runs-on: ubuntu-latest steps: + - uses: actions/checkout@v4 + - name: Check if branch exists id: check_if_exists run: | - OUTPUT=$(git ls-remote --heads https://github.com/${{ github.repository_owner }}/wai-aria-practices.git apg/${{ github.event.inputs.apg_branch }} | wc -l) - echo "::set-output name=branch-exists::$(echo $OUTPUT)" + BRANCH_EXISTS=$(git ls-remote --heads https://github.com/${{ github.repository_owner }}/wai-aria-practices.git apg/${{ github.event.inputs.apg_branch }} | wc -l) + echo "BRANCH_EXISTS=$BRANCH_EXISTS" >> "$GITHUB_OUTPUT" + - name: Clean up generated branch - if: ${{ steps.check_if_exists.outputs.branch-exists == '1' }} - uses: dawidd6/action-delete-branch@v3 - with: - github_token: ${{ github.token }} - branches: apg/${{ github.event.inputs.apg_branch }} + if: ${{ steps.check_if_exists.outputs.BRANCH_EXISTS == '1' }} + run: | + BRANCH_NAME="apg/${{ github.event.inputs.apg_branch }}" + git config --global user.name "w3cgruntbot" + git config --global user.email "w3cgruntbot@users.noreply.github.com" + git push origin --delete $BRANCH_NAME + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Report branch already removed - if: ${{ steps.check_if_exists.outputs.branch-exists == '0' }} + if: ${{ steps.check_if_exists.outputs.BRANCH_EXISTS == '0' }} run: echo "Branch already removed" diff --git a/scripts/create-pr/index.js b/scripts/create-pr/index.js deleted file mode 100644 index ffd89681c..000000000 --- a/scripts/create-pr/index.js +++ /dev/null @@ -1,108 +0,0 @@ -const { Octokit } = require("@octokit/rest"); - -// octokit should be authenticated with GITHUB_TOKEN from GA -const octokit = new Octokit({ - auth: process.env.GH_TOKEN, -}); - -const jobId = process.env.JOB_ID; -const isSuccess = process.env.OUTCOME === "success"; -const repositoryOwner = process.env.REPO_OWNER; -const previewLink = "aria-practices.netlify.app"; - -const updateApgPrBody = async (waiPrNumber, createPullRequestResult) => { - // Update APG PR - const getApgPrResult = await octokit.rest.pulls.get({ - owner: repositoryOwner, - repo: "aria-practices", - pull_number: process.env.APG_PR_NUMBER, - }); - console.info("pulls.get.success"); - - // Needed to append error to PR body - let apgPrBody = getApgPrResult.data.body || ""; - - // Find index of existing successful or failure build links in the body - let previewLinkIndex = apgPrBody.indexOf("[WAI Preview Link]"); - if (previewLinkIndex < 0) { - // Need to account for error content - // Assumes a previous link didn't exist, find error content - previewLinkIndex = apgPrBody.indexOf("WAI Preview Link [failed to build]"); - } - - const previewLinkUrl = isSuccess - ? `https://deploy-preview-${ - waiPrNumber || createPullRequestResult.data.number - }--${previewLink}/ARIA/apg` - : `https://github.com/${repositoryOwner}/wai-aria-practices/runs/${jobId}?check_suite_focus=true`; - const additionalBodyContent = isSuccess - ? `[WAI Preview Link](${previewLinkUrl}) _(Last built on ${new Date().toUTCString()})._` - : `WAI Preview Link [failed to build](${previewLinkUrl}) on 'Update site files' step. _(Last tried on ${new Date().toUTCString()})._`; - if (previewLinkIndex < 0) { - // no preview link in PR body; append - apgPrBody = `${apgPrBody}\n___\n${additionalBodyContent}`; - } else { - // replace existing preview link in PR body - let stringRemainder = apgPrBody.substring(previewLinkIndex); - apgPrBody = apgPrBody.replace(stringRemainder, additionalBodyContent); - } - - await octokit.rest.pulls.update({ - owner: repositoryOwner, - repo: "aria-practices", - pull_number: process.env.APG_PR_NUMBER, - body: apgPrBody, - }); - console.info("pulls.update.success", apgPrBody); -}; - -(async () => { - let waiPrNumber; - let createPullRequestResult; - - if (!isSuccess) { - try { - await updateApgPrBody(); - } catch (e) { - console.error("failure.1", e); - } - return; - } - - try { - // Get current list of PRs for this repository - const pullsListResult = await octokit.rest.pulls.list({ - owner: repositoryOwner, - repo: "wai-aria-practices", - }); - console.info("pulls.list.success"); - - // Check to see if a PR already exists with the head branch and use that PR's number instead - for (let i = 0; i < pullsListResult.data.length; i++) { - let data = pullsListResult.data[i]; - if (data.head.ref === "apg/" + process.env.APG_BRANCH) { - waiPrNumber = data.number; - break; - } - } - - if (!waiPrNumber) { - // Create a PR if none exists - createPullRequestResult = await octokit.rest.pulls.create({ - owner: repositoryOwner, - repo: "wai-aria-practices", - head: "apg/" + process.env.APG_BRANCH, - base: "main", - title: "apg/" + process.env.APG_BRANCH + " generated by aria-practices", - body: `Generated by ${repositoryOwner}/aria-practices#${process.env.APG_PR_NUMBER}.`, - draft: true, - maintainer_can_modify: true, - }); - console.info("pulls.create.success"); - } - - await updateApgPrBody(waiPrNumber, createPullRequestResult); - } catch (e) { - console.error("failure.block.2", e); - } -})(); diff --git a/scripts/create-pr/package-lock.json b/scripts/create-pr/package-lock.json deleted file mode 100644 index 95bcba884..000000000 --- a/scripts/create-pr/package-lock.json +++ /dev/null @@ -1,372 +0,0 @@ -{ - "name": "create-pr", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "dependencies": { - "@octokit/rest": "^18.12.0" - } - }, - "node_modules/@octokit/auth-token": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", - "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", - "dependencies": { - "@octokit/types": "^6.0.3" - } - }, - "node_modules/@octokit/core": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz", - "integrity": "sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw==", - "dependencies": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.0", - "@octokit/request-error": "^2.0.5", - "@octokit/types": "^6.0.3", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/endpoint": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", - "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", - "dependencies": { - "@octokit/types": "^6.0.3", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/graphql": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", - "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", - "dependencies": { - "@octokit/request": "^5.6.0", - "@octokit/types": "^6.0.3", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/openapi-types": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz", - "integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==" - }, - "node_modules/@octokit/plugin-paginate-rest": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz", - "integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==", - "dependencies": { - "@octokit/types": "^6.34.0" - }, - "peerDependencies": { - "@octokit/core": ">=2" - } - }, - "node_modules/@octokit/plugin-request-log": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", - "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", - "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz", - "integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==", - "dependencies": { - "@octokit/types": "^6.34.0", - "deprecation": "^2.3.1" - }, - "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/@octokit/request": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz", - "integrity": "sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA==", - "dependencies": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.16.1", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.1", - "universal-user-agent": "^6.0.0" - } - }, - "node_modules/@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "dependencies": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "node_modules/@octokit/rest": { - "version": "18.12.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", - "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", - "dependencies": { - "@octokit/core": "^3.5.1", - "@octokit/plugin-paginate-rest": "^2.16.8", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^5.12.0" - } - }, - "node_modules/@octokit/types": { - "version": "6.34.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz", - "integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==", - "dependencies": { - "@octokit/openapi-types": "^11.2.0" - } - }, - "node_modules/before-after-hook": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", - "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==" - }, - "node_modules/deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" - }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/node-fetch": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", - "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" - }, - "node_modules/universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - } - }, - "dependencies": { - "@octokit/auth-token": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", - "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", - "requires": { - "@octokit/types": "^6.0.3" - } - }, - "@octokit/core": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz", - "integrity": "sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw==", - "requires": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.0", - "@octokit/request-error": "^2.0.5", - "@octokit/types": "^6.0.3", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/endpoint": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", - "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", - "requires": { - "@octokit/types": "^6.0.3", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/graphql": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", - "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", - "requires": { - "@octokit/request": "^5.6.0", - "@octokit/types": "^6.0.3", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/openapi-types": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz", - "integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==" - }, - "@octokit/plugin-paginate-rest": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz", - "integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==", - "requires": { - "@octokit/types": "^6.34.0" - } - }, - "@octokit/plugin-request-log": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", - "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", - "requires": {} - }, - "@octokit/plugin-rest-endpoint-methods": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz", - "integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==", - "requires": { - "@octokit/types": "^6.34.0", - "deprecation": "^2.3.1" - } - }, - "@octokit/request": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.2.tgz", - "integrity": "sha512-je66CvSEVf0jCpRISxkUcCa0UkxmFs6eGDRSbfJtAVwbLH5ceqF+YEyC8lj8ystKyZTy8adWr0qmkY52EfOeLA==", - "requires": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.16.1", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.1", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", - "requires": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "@octokit/rest": { - "version": "18.12.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", - "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", - "requires": { - "@octokit/core": "^3.5.1", - "@octokit/plugin-paginate-rest": "^2.16.8", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^5.12.0" - } - }, - "@octokit/types": { - "version": "6.34.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz", - "integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==", - "requires": { - "@octokit/openapi-types": "^11.2.0" - } - }, - "before-after-hook": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", - "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==" - }, - "deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" - }, - "is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" - }, - "node-fetch": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.6.tgz", - "integrity": "sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==", - "requires": { - "whatwg-url": "^5.0.0" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" - }, - "universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - } - } -} diff --git a/scripts/create-pr/package.json b/scripts/create-pr/package.json deleted file mode 100644 index 872c6c925..000000000 --- a/scripts/create-pr/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "dependencies": { - "@octokit/rest": "^18.12.0" - } -} diff --git a/scripts/pr-create/index.js b/scripts/pr-create/index.js new file mode 100644 index 000000000..a3d0866cb --- /dev/null +++ b/scripts/pr-create/index.js @@ -0,0 +1,167 @@ +import { Octokit } from '@octokit/rest'; + +// octokit should be authenticated with GITHUB_TOKEN from GA +const octokit = new Octokit({ + auth: process.env.GH_TOKEN, +}); + +const jobId = process.env.JOB_ID; +// Outside of auth errors, the only other potential error is if the 'Update site files' step fails. Use this boolean. +const isSiteFilesUpdateSuccess = process.env.OUTCOME === 'success'; +const repositoryOwner = process.env.REPO_OWNER; +const previewLink = 'aria-practices.netlify.app'; +const GENERATED_APG_WAI_BRANCH = 'apg/' + process.env.APG_BRANCH; + +const ERROR_APG_BODY_UPDATE = 101; +const ERROR_SITE_FILES_UPDATE = 102; +const ERROR_LIST_PULL_REQUESTS = 103; +const ERROR_CREATE_PULL_REQUEST = 104; +const ERROR_GET_PULL_REQUEST = 105; +const ERROR_UPDATE_PULL_REQUEST = 106; +// TODO: Uncomment when updated bot token is being used +// const ERROR_CREATE_COMMIT_STATUS = 107; + +const updateApgPrBody = async (waiPrNumber, createPullRequestResult) => { + // Update APG PR + let getApgPrResult; + + try { + getApgPrResult = await octokit.rest.pulls.get({ + owner: repositoryOwner, + repo: 'aria-practices', + pull_number: process.env.APG_PR_NUMBER, + }); + console.info('pulls.get.success'); + } catch (e) { + console.error('error.get.pull.request', e); + process.exit(ERROR_GET_PULL_REQUEST); + } + + // Needed to append error to PR body + let apgPrBody = getApgPrResult.data.body || ''; + + // Find index of existing successful or failure build links in the body + let previewLinkIndex = apgPrBody.indexOf('[WAI Preview Link]'); + if (previewLinkIndex < 0) { + // Need to account for error content + // Assumes a previous link didn't exist, find error content + previewLinkIndex = apgPrBody.indexOf('WAI Preview Link [failed to build]'); + } + + const previewLinkUrl = isSiteFilesUpdateSuccess + ? `https://deploy-preview-${ + waiPrNumber || createPullRequestResult.data.number + }--${previewLink}/ARIA/apg` + : `https://github.com/${repositoryOwner}/wai-aria-practices/runs/${jobId}?check_suite_focus=true`; + const additionalBodyContent = isSiteFilesUpdateSuccess + ? `[WAI Preview Link](${previewLinkUrl}) _(Last built on ${new Date().toUTCString()})._` + : `WAI Preview Link [failed to build](${previewLinkUrl}) on 'Update site files' step. _(Last tried on ${new Date().toUTCString()})._`; + if (previewLinkIndex < 0) { + // no preview link in PR body; append + apgPrBody = `${apgPrBody}\n___\n${additionalBodyContent}`; + } else { + // replace existing preview link in PR body + let stringRemainder = apgPrBody.substring(previewLinkIndex); + apgPrBody = apgPrBody.replace(stringRemainder, additionalBodyContent); + } + + try { + await octokit.rest.pulls.update({ + owner: repositoryOwner, + repo: 'aria-practices', + pull_number: process.env.APG_PR_NUMBER, + body: apgPrBody, + }); + console.info('pulls.update.success', apgPrBody); + } catch (e) { + console.error('error.update.pull.request', e); + process.exit(ERROR_UPDATE_PULL_REQUEST); + } + + // Means there was still an error updating the WAI Preview Link + if (!isSiteFilesUpdateSuccess) { + console.error('error.site.files.update', additionalBodyContent); + + // TODO: Uncomment when updated bot token is being used + /*try { + // Display build error on triggering PR's commit + await octokit.rest.repos.createCommitStatus({ + owner: repositoryOwner, + repo: 'aria-practices', + sha: process.env.APG_SHA, + state: 'failure', + target_url: previewLinkUrl, // Populated by the failing CI job:step link + description: `WAI Preview Link failed to build in wai-aria-practices/pr-create (${ERROR_SITE_FILES_UPDATE})`, + context: 'WAI Preview Link failed to build', + }); + } catch (e) { + console.error('error.create.commit.status', e); + process.exit(ERROR_CREATE_COMMIT_STATUS); + }*/ + + process.exit(ERROR_SITE_FILES_UPDATE); + } +}; + +(async () => { + let waiPrNumber; + let createPullRequestResult; + + if (!isSiteFilesUpdateSuccess) { + try { + await updateApgPrBody(); + } catch (e) { + console.error('error.apg.body.update', e); + process.exit(ERROR_APG_BODY_UPDATE); + } + return; + } + + try { + // Get current list of PRs for this repository + const pullsListResult = await octokit.rest.pulls.list({ + owner: repositoryOwner, + repo: 'wai-aria-practices', + }); + console.info('pulls.list.success'); + + // Check to see if a PR already exists with the head branch and use that PR's number instead + for (let i = 0; i < pullsListResult.data.length; i++) { + let data = pullsListResult.data[i]; + if (data.head.ref === GENERATED_APG_WAI_BRANCH) { + waiPrNumber = data.number; + break; + } + } + } catch (e) { + console.error('error.list.pull.requests', e); + process.exit(ERROR_LIST_PULL_REQUESTS); + } + + if (!waiPrNumber) { + try { + // Create a PR if none exists + createPullRequestResult = await octokit.rest.pulls.create({ + owner: repositoryOwner, + repo: 'wai-aria-practices', + head: GENERATED_APG_WAI_BRANCH, + base: 'main', + title: GENERATED_APG_WAI_BRANCH + ' generated by aria-practices', + body: `Generated by ${repositoryOwner}/aria-practices#${process.env.APG_PR_NUMBER}.`, + draft: true, + maintainer_can_modify: true, + }); + console.info('pulls.create.success'); + } catch (e) { + console.error('error.create.pull.request', e); + process.exit(ERROR_CREATE_PULL_REQUEST); + } + } + + try { + await updateApgPrBody(waiPrNumber, createPullRequestResult); + } catch (e) { + console.error('error.apg.body.update', e); + process.exit(ERROR_APG_BODY_UPDATE); + } +})(); diff --git a/scripts/pr-create/package-lock.json b/scripts/pr-create/package-lock.json new file mode 100644 index 000000000..a7a15b72c --- /dev/null +++ b/scripts/pr-create/package-lock.json @@ -0,0 +1,281 @@ +{ + "name": "pr-create", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "@octokit/rest": "^21.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@octokit/auth-token": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.1.tgz", + "integrity": "sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.2.tgz", + "integrity": "sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==", + "dependencies": { + "@octokit/auth-token": "^5.0.0", + "@octokit/graphql": "^8.0.0", + "@octokit/request": "^9.0.0", + "@octokit/request-error": "^6.0.1", + "@octokit/types": "^13.0.0", + "before-after-hook": "^3.0.2", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.1.tgz", + "integrity": "sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==", + "dependencies": { + "@octokit/types": "^13.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.1.1.tgz", + "integrity": "sha512-ukiRmuHTi6ebQx/HFRCXKbDlOh/7xEV6QUXaE7MJEKGNAncGI/STSbOkl12qVXZrfZdpXctx5O9X1AIaebiDBg==", + "dependencies": { + "@octokit/request": "^9.0.0", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "22.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", + "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.3.5.tgz", + "integrity": "sha512-cgwIRtKrpwhLoBi0CUNuY83DPGRMaWVjqVI/bGKsLJ4PzyWZNaEmhHroI2xlrVXkk6nFv0IsZpOp+ZWSWUS2AQ==", + "dependencies": { + "@octokit/types": "^13.6.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-5.3.1.tgz", + "integrity": "sha512-n/lNeCtq+9ofhC15xzmJCNKP2BWTv8Ih2TTy+jatNCCq/gQP/V7rK3fjIfuz0pDWDALO/o/4QY4hyOF6TQQFUw==", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "13.2.5", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.2.5.tgz", + "integrity": "sha512-c4pRWi7OUSFM4E6frfUs+qsAf052aOWt1x2qFQ6llQcd1J0HqQ/0Egfs2lm33IixXeXXhZ+GmC9tf92qbOs25Q==", + "dependencies": { + "@octokit/types": "^13.6.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=6" + } + }, + "node_modules/@octokit/request": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.3.tgz", + "integrity": "sha512-V+TFhu5fdF3K58rs1pGUJIDH5RZLbZm5BI+MNF+6o/ssFNT4vWlCh/tVpF3NxGtP15HUxTTMUbsG5llAuU2CZA==", + "dependencies": { + "@octokit/endpoint": "^10.0.0", + "@octokit/request-error": "^6.0.1", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.5.tgz", + "integrity": "sha512-IlBTfGX8Yn/oFPMwSfvugfncK2EwRLjzbrpifNaMY8o/HTEAFqCA1FZxjD9cWvSKBHgrIhc4CSBIzMxiLsbzFQ==", + "dependencies": { + "@octokit/types": "^13.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest": { + "version": "21.0.2", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-21.0.2.tgz", + "integrity": "sha512-+CiLisCoyWmYicH25y1cDfCrv41kRSvTq6pPWtRroRJzhsCZWZyCqGyI8foJT5LmScADSwRAnr/xo+eewL04wQ==", + "dependencies": { + "@octokit/core": "^6.1.2", + "@octokit/plugin-paginate-rest": "^11.0.0", + "@octokit/plugin-request-log": "^5.3.1", + "@octokit/plugin-rest-endpoint-methods": "^13.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "13.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.6.0.tgz", + "integrity": "sha512-CrooV/vKCXqwLa+osmHLIMUb87brpgUqlqkPGc6iE2wCkUvTrHiXFMhAKoDDaAAYJrtKtrFTgSQTg5nObBEaew==", + "dependencies": { + "@octokit/openapi-types": "^22.2.0" + } + }, + "node_modules/before-after-hook": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", + "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==" + }, + "node_modules/universal-user-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", + "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==" + } + }, + "dependencies": { + "@octokit/auth-token": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.1.tgz", + "integrity": "sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==" + }, + "@octokit/core": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.2.tgz", + "integrity": "sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==", + "requires": { + "@octokit/auth-token": "^5.0.0", + "@octokit/graphql": "^8.0.0", + "@octokit/request": "^9.0.0", + "@octokit/request-error": "^6.0.1", + "@octokit/types": "^13.0.0", + "before-after-hook": "^3.0.2", + "universal-user-agent": "^7.0.0" + } + }, + "@octokit/endpoint": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.1.tgz", + "integrity": "sha512-JYjh5rMOwXMJyUpj028cu0Gbp7qe/ihxfJMLc8VZBMMqSwLgOxDI1911gV4Enl1QSavAQNJcwmwBF9M0VvLh6Q==", + "requires": { + "@octokit/types": "^13.0.0", + "universal-user-agent": "^7.0.2" + } + }, + "@octokit/graphql": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.1.1.tgz", + "integrity": "sha512-ukiRmuHTi6ebQx/HFRCXKbDlOh/7xEV6QUXaE7MJEKGNAncGI/STSbOkl12qVXZrfZdpXctx5O9X1AIaebiDBg==", + "requires": { + "@octokit/request": "^9.0.0", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^7.0.0" + } + }, + "@octokit/openapi-types": { + "version": "22.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-22.2.0.tgz", + "integrity": "sha512-QBhVjcUa9W7Wwhm6DBFu6ZZ+1/t/oYxqc2tp81Pi41YNuJinbFRx8B133qVOrAaBbF7D/m0Et6f9/pZt9Rc+tg==" + }, + "@octokit/plugin-paginate-rest": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.3.5.tgz", + "integrity": "sha512-cgwIRtKrpwhLoBi0CUNuY83DPGRMaWVjqVI/bGKsLJ4PzyWZNaEmhHroI2xlrVXkk6nFv0IsZpOp+ZWSWUS2AQ==", + "requires": { + "@octokit/types": "^13.6.0" + } + }, + "@octokit/plugin-request-log": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-5.3.1.tgz", + "integrity": "sha512-n/lNeCtq+9ofhC15xzmJCNKP2BWTv8Ih2TTy+jatNCCq/gQP/V7rK3fjIfuz0pDWDALO/o/4QY4hyOF6TQQFUw==", + "requires": {} + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "13.2.5", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.2.5.tgz", + "integrity": "sha512-c4pRWi7OUSFM4E6frfUs+qsAf052aOWt1x2qFQ6llQcd1J0HqQ/0Egfs2lm33IixXeXXhZ+GmC9tf92qbOs25Q==", + "requires": { + "@octokit/types": "^13.6.0" + } + }, + "@octokit/request": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.1.3.tgz", + "integrity": "sha512-V+TFhu5fdF3K58rs1pGUJIDH5RZLbZm5BI+MNF+6o/ssFNT4vWlCh/tVpF3NxGtP15HUxTTMUbsG5llAuU2CZA==", + "requires": { + "@octokit/endpoint": "^10.0.0", + "@octokit/request-error": "^6.0.1", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^7.0.2" + } + }, + "@octokit/request-error": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.5.tgz", + "integrity": "sha512-IlBTfGX8Yn/oFPMwSfvugfncK2EwRLjzbrpifNaMY8o/HTEAFqCA1FZxjD9cWvSKBHgrIhc4CSBIzMxiLsbzFQ==", + "requires": { + "@octokit/types": "^13.0.0" + } + }, + "@octokit/rest": { + "version": "21.0.2", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-21.0.2.tgz", + "integrity": "sha512-+CiLisCoyWmYicH25y1cDfCrv41kRSvTq6pPWtRroRJzhsCZWZyCqGyI8foJT5LmScADSwRAnr/xo+eewL04wQ==", + "requires": { + "@octokit/core": "^6.1.2", + "@octokit/plugin-paginate-rest": "^11.0.0", + "@octokit/plugin-request-log": "^5.3.1", + "@octokit/plugin-rest-endpoint-methods": "^13.0.0" + } + }, + "@octokit/types": { + "version": "13.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.6.0.tgz", + "integrity": "sha512-CrooV/vKCXqwLa+osmHLIMUb87brpgUqlqkPGc6iE2wCkUvTrHiXFMhAKoDDaAAYJrtKtrFTgSQTg5nObBEaew==", + "requires": { + "@octokit/openapi-types": "^22.2.0" + } + }, + "before-after-hook": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", + "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==" + }, + "universal-user-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", + "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==" + } + } +} diff --git a/scripts/pr-create/package.json b/scripts/pr-create/package.json new file mode 100644 index 000000000..b1e18e2b7 --- /dev/null +++ b/scripts/pr-create/package.json @@ -0,0 +1,13 @@ +{ + "type": "module", + "scripts": { + "lint": "npx prettier --single-quote --tab-width 2 --arrow-parens avoid --check .", + "lint:fix": "npx prettier --single-quote --tab-width 2 --arrow-parens avoid --write ." + }, + "dependencies": { + "@octokit/rest": "^21.0.2" + }, + "engines": { + "node": ">=18" + } +} From e358a590ebfb6cb76a78cc86518ccfc8759183eb Mon Sep 17 00:00:00 2001 From: Howard Edwards Date: Thu, 19 Dec 2024 10:52:28 -0500 Subject: [PATCH 02/25] Infrastructure: Add case to specially handle conflicts in submodule folder to `pr-create.yml` (#373) * Add case to specially handle conflicts in submodule folder --- .github/workflows/pr-create.yml | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pr-create.yml b/.github/workflows/pr-create.yml index 07cf0e2bd..7b18ed241 100644 --- a/.github/workflows/pr-create.yml +++ b/.github/workflows/pr-create.yml @@ -66,6 +66,35 @@ jobs: git config --global user.email "w3cgruntbot@users.noreply.github.com" git fetch origin main git rebase --force-rebase origin/main + + # Handle cases where the aria-practices submodule has conflicts to be handled; + # _external/data shouldn't have conflicting changes + if [ -d "_external/aria-practices" ] && { [ -f ".git/rebase-apply" ] || [ -d ".git/rebase-merge" ]; }; then + echo "Handling submodule conflicts..." + cd _external/aria-practices + + # Resolve the conflict + git fetch origin + git merge origin/main + + # Add resolved submodule from project root + cd - + git add _external/aria-practices + + # Continue the rebase + git rebase --continue + + # Resolve conflicts for all other conflicting files automatically using 'theirs' from project root + git diff --name-only --diff-filter=U | while read -r file; do + git checkout --theirs "$file" + git add "$file" + done + + # Continue the rebase + git rebase --continue || true # Suppress the error with true if nothing additional + else + echo "No submodule conflicts detected or no rebase in progress." + fi env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -85,7 +114,7 @@ jobs: commit_user_name: w3cgruntbot commit_user_email: w3cgruntbot@users.noreply.github.com commit_author: w3cgruntbot - commit_message: "chore: Update branch with latest `${{ github.event.inputs.apg_branch }}` changes and submodule updates" + commit_message: 'chore: Update branch with latest `${{ github.event.inputs.apg_branch }}` changes and submodule updates' branch: apg/${{ github.event.inputs.apg_branch }} push_options: '--force' env: From 979ccc3604e6e6190111e051188f5bc0b8a9f464 Mon Sep 17 00:00:00 2001 From: Howard Edwards Date: Thu, 19 Dec 2024 11:07:52 -0500 Subject: [PATCH 03/25] Additional pr-create.yml workflow changes to support submodule conflict resolution (#374) --- .github/workflows/pr-create.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pr-create.yml b/.github/workflows/pr-create.yml index 7b18ed241..3a5e0686c 100644 --- a/.github/workflows/pr-create.yml +++ b/.github/workflows/pr-create.yml @@ -65,7 +65,7 @@ jobs: git config --global user.name "w3cgruntbot" git config --global user.email "w3cgruntbot@users.noreply.github.com" git fetch origin main - git rebase --force-rebase origin/main + git rebase --force-rebase origin/main || true # Handle cases where the aria-practices submodule has conflicts to be handled; # _external/data shouldn't have conflicting changes @@ -82,7 +82,7 @@ jobs: git add _external/aria-practices # Continue the rebase - git rebase --continue + git rebase --continue || true # Resolve conflicts for all other conflicting files automatically using 'theirs' from project root git diff --name-only --diff-filter=U | while read -r file; do @@ -91,7 +91,7 @@ jobs: done # Continue the rebase - git rebase --continue || true # Suppress the error with true if nothing additional + git rebase --continue || true else echo "No submodule conflicts detected or no rebase in progress." fi From 299a10e1088537307abdf583447b4c60f941055f Mon Sep 17 00:00:00 2001 From: Howard Edwards Date: Thu, 19 Dec 2024 11:27:28 -0500 Subject: [PATCH 04/25] Infrastructure: Disable git editor during `git rebase --continue` in `pr-create.yml` (#375) --- .github/workflows/pr-create.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-create.yml b/.github/workflows/pr-create.yml index 3a5e0686c..1f2b906e2 100644 --- a/.github/workflows/pr-create.yml +++ b/.github/workflows/pr-create.yml @@ -82,7 +82,7 @@ jobs: git add _external/aria-practices # Continue the rebase - git rebase --continue || true + GIT_EDITOR=true git rebase --continue || true # Resolve conflicts for all other conflicting files automatically using 'theirs' from project root git diff --name-only --diff-filter=U | while read -r file; do @@ -91,7 +91,7 @@ jobs: done # Continue the rebase - git rebase --continue || true + GIT_EDITOR=true git rebase --continue || true else echo "No submodule conflicts detected or no rebase in progress." fi From 821e464382953f49fc1818668e49e46aca995f48 Mon Sep 17 00:00:00 2001 From: Howard Edwards Date: Wed, 22 Jan 2025 12:31:41 -0500 Subject: [PATCH 05/25] Update transformPattern.js to support "patterns/landmarks" (#382) --- scripts/pre-build/library/transformPattern.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/pre-build/library/transformPattern.js b/scripts/pre-build/library/transformPattern.js index 12b095f97..156843891 100644 --- a/scripts/pre-build/library/transformPattern.js +++ b/scripts/pre-build/library/transformPattern.js @@ -26,11 +26,13 @@ const transformPattern = async (sourcePath, sourceContents) => { return sectionElement; } } + + if (slug === 'landmarks') return null; throw new Error(`Expected pattern ${slug} to have an Example(s) section`); }; - examplesSection = findExamplesSection(); - examplesSection.classList.add("examples-section"); + const examplesSection = findExamplesSection(); + if (examplesSection) examplesSection.classList.add("examples-section"); await rewriteElementPaths(html, { onSourcePath: sourcePath }); From 5665f18b61f686fc89f5e37ff882fe11367a4465 Mon Sep 17 00:00:00 2001 From: Howard Edwards Date: Tue, 4 Feb 2025 11:40:22 -0500 Subject: [PATCH 06/25] Pin ruby 3.3.3 instead of 3.3.x during workflow builds (#384) --- .github/workflows/deploy.yml | 2 +- .github/workflows/pr-create.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 300c25a84..503dd093c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -22,7 +22,7 @@ jobs: - name: Set up Ruby 3.3 uses: ruby/setup-ruby@v1 with: - ruby-version: 3.3 + ruby-version: '3.3.3' bundler-cache: true - name: Update git submodules diff --git a/.github/workflows/pr-create.yml b/.github/workflows/pr-create.yml index 1f2b906e2..3a1ae24f9 100644 --- a/.github/workflows/pr-create.yml +++ b/.github/workflows/pr-create.yml @@ -42,7 +42,7 @@ jobs: - name: Set up Ruby 3.3 uses: ruby/setup-ruby@v1 with: - ruby-version: 3.3 + ruby-version: '3.3.3' - name: Update git submodules run: | From 42c00da1739fb5a50029bb453ffe02551ecfa5d9 Mon Sep 17 00:00:00 2001 From: w3cgruntbot Date: Wed, 12 Feb 2025 16:12:10 +0000 Subject: [PATCH 07/25] chore: Update `main` with latest changes from `aria-practices` --- .../patterns/slider-multithumb/examples/slider-multithumb.md | 2 +- _external/aria-practices | 2 +- _external/data | 2 +- .../slider-multithumb/examples/js/slider-multithumb.js | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ARIA/apg/patterns/slider-multithumb/examples/slider-multithumb.md b/ARIA/apg/patterns/slider-multithumb/examples/slider-multithumb.md index 558b06b0a..d564a85a3 100644 --- a/ARIA/apg/patterns/slider-multithumb/examples/slider-multithumb.md +++ b/ARIA/apg/patterns/slider-multithumb/examples/slider-multithumb.md @@ -12,7 +12,7 @@ permalink: /ARIA/apg/patterns/slider-multithumb/examples/slider-multithumb/ sidebar: true -footer: " " +footer: " " # Context here: https://github.com/w3c/wai-aria-practices/issues/31 type_of_guidance: APG diff --git a/_external/aria-practices b/_external/aria-practices index d93a99acc..b7471691a 160000 --- a/_external/aria-practices +++ b/_external/aria-practices @@ -1 +1 @@ -Subproject commit d93a99accd3d2677e3a1c53216421f8409433a3d +Subproject commit b7471691a577728f447b7a7060b61f8c440884e8 diff --git a/_external/data b/_external/data index b9d6043d6..105ce142a 160000 --- a/_external/data +++ b/_external/data @@ -1 +1 @@ -Subproject commit b9d6043d68e2b81ee4fbb0abc72a0e25f974c272 +Subproject commit 105ce142a1b7441b8136c4650d482744b5afee3a diff --git a/content-assets/wai-aria-practices/patterns/slider-multithumb/examples/js/slider-multithumb.js b/content-assets/wai-aria-practices/patterns/slider-multithumb/examples/js/slider-multithumb.js index dbbb73538..3415ecab7 100644 --- a/content-assets/wai-aria-practices/patterns/slider-multithumb/examples/js/slider-multithumb.js +++ b/content-assets/wai-aria-practices/patterns/slider-multithumb/examples/js/slider-multithumb.js @@ -66,8 +66,8 @@ class SliderMultithumb { this.svgNode.setAttribute('width', this.svgWidth); this.svgNode.setAttribute('height', this.svgHeight); - this.minSliderFocusNode.setAttribute('y', this.focusY); - this.maxSliderFocusNode.setAttribute('y', this.focusY); + this.minSliderFocusNode.setAttribute('y', Math.max(2, this.focusY)); + this.maxSliderFocusNode.setAttribute('y', Math.max(2, this.focusY)); this.minSliderFocusNode.setAttribute('width', this.focusWidth); this.maxSliderFocusNode.setAttribute('width', this.focusWidth); this.minSliderFocusNode.setAttribute('height', this.focusHeight); From 0dc2f5e2dd5a0b5c93cd5cd9c6ac5459fa482934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20B=C3=A9tin?= Date: Fri, 14 Feb 2025 14:46:13 +0100 Subject: [PATCH 08/25] Remove use of ytkey (deprecated) (#370) --- _config.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/_config.yml b/_config.yml index 2d19396f7..377632ac8 100755 --- a/_config.yml +++ b/_config.yml @@ -33,8 +33,6 @@ kramwdown: highlighter: rouge repository: w3c/wai-aria-practices -ytkey: AIzaSyCiZ9uToWu9jb7BTx47NtzCvmGGXKXp8nI - remote_theme: w3c/wai-website-theme collections: From 891ad9706cc2b70b2ac6c5f6e8c80238eadf7a02 Mon Sep 17 00:00:00 2001 From: Howard Edwards Date: Thu, 6 Mar 2025 10:05:59 -0500 Subject: [PATCH 09/25] Infrastructure: Create `.tracked-html-assets.json` (#388) * Create .tracked-html-assets.json to make it easier to account for html assets which don't match with the expected html assets' naming structure --- .../library/.tracked-html-assets.json | 6 ++++ .../pre-build/library/determineContentType.js | 32 ++++++++++++++----- 2 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 scripts/pre-build/library/.tracked-html-assets.json diff --git a/scripts/pre-build/library/.tracked-html-assets.json b/scripts/pre-build/library/.tracked-html-assets.json new file mode 100644 index 000000000..ecbce46e6 --- /dev/null +++ b/scripts/pre-build/library/.tracked-html-assets.json @@ -0,0 +1,6 @@ +[ + "feed/examples/feed-display.html", + "content\\/patterns\\/landmarks\\/examples\\/.+\\.html", + "toolbar/examples/help.html", + "color-settings/test-contrast-theme.html" +] diff --git a/scripts/pre-build/library/determineContentType.js b/scripts/pre-build/library/determineContentType.js index 56305599a..05b9967f6 100644 --- a/scripts/pre-build/library/determineContentType.js +++ b/scripts/pre-build/library/determineContentType.js @@ -1,3 +1,23 @@ +const fs = require("fs"); +const path = require("path"); + +// Update .tracked-html-assets.json with paths or regexes for html assets which don't fall in line with the expected html file naming structures to consider them as valid 'htmlAsset's +const trackedHtmlAssetsData = fs.readFileSync(path.join(__dirname, '.tracked-html-assets.json'), { encoding: "utf8" }) +const trackedHtmlAssets = JSON.parse(trackedHtmlAssetsData); + +const processTrackedHtmlAsset = (sourcePath) => { + return trackedHtmlAssets.some(trackedAsset => { + if (sourcePath.endsWith(trackedAsset)) return true; + + try { + const regexp = new RegExp(trackedAsset); + return regexp.test(sourcePath); + } catch (e) { + return false; + } + }); +} + const determineContentType = (sourcePath) => { if (sourcePath.endsWith("content/apg-home.html")) { return "homepage"; @@ -9,13 +29,8 @@ const determineContentType = (sourcePath) => { ) { return "imageAsset"; } - if ( - sourcePath.endsWith("feed/examples/feed-display.html") || - sourcePath.match(/content\/patterns\/landmarks\/examples\/.+\.html/) || - sourcePath.endsWith("toolbar/examples/help.html") - ) { - return "htmlAsset"; - } + + if (processTrackedHtmlAsset(sourcePath)) return "htmlAsset"; if (!sourcePath.endsWith("html")) { return "otherAsset"; } @@ -54,7 +69,8 @@ const determineContentType = (sourcePath) => { } throw new Error( `Could not determine content type for file at ${sourcePath}\n` + - `This file may not follow established conventions.` + `This file may not follow established conventions.\n` + + `If this is a html file that should be included, please add it to .tracked-html-assets.json.` ); }; From 4d4c104df71379de06539419a10f9c78d8f35a56 Mon Sep 17 00:00:00 2001 From: w3cgruntbot Date: Tue, 18 Mar 2025 03:08:01 +0000 Subject: [PATCH 10/25] chore: Update `main` with latest changes from `aria-practices` --- .../grid-and-table-properties-practice.md | 7 +++---- _external/aria-practices | 2 +- _external/data | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/ARIA/apg/practices/grid-and-table-properties/grid-and-table-properties-practice.md b/ARIA/apg/practices/grid-and-table-properties/grid-and-table-properties-practice.md index 1102ad202..d4a9737a9 100644 --- a/ARIA/apg/practices/grid-and-table-properties/grid-and-table-properties-practice.md +++ b/ARIA/apg/practices/grid-and-table-properties/grid-and-table-properties-practice.md @@ -158,8 +158,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar'); aria-rowcount tells assistive technologies the actual size of the grid is 463 rows even though only 4 rows are present in the markup. --> -<table role="grid" aria-rowcount="463"> - aria-label="Student roster for history 101" +<table role="grid" aria-rowcount="463" aria-label="Student roster for history 101"> <thead> <tr aria-rowindex="1"> <th>Last Name</th> @@ -280,8 +279,8 @@ if (enableSidebar) document.body.classList.add('has-sidebar'); In this example, the first two columns with the student name are shown, but the score columns have been scrolled to show columns 10 through 13. Columns 3 through 9 are not visible so are not in the DOM.

-
<table role="grid" aria-rowcount="463" aria-colcount="13">
-  aria-label="Student grades for history 101"
+          
<table role="grid" aria-rowcount="463" aria-colcount="13"
+  aria-label="Student grades for history 101">
   <!--
     aria-rowcount and aria-colcount tell assistive technologies
     the actual size of the grid is 463 rows by 13 columns,
diff --git a/_external/aria-practices b/_external/aria-practices
index b7471691a..b9ed31b96 160000
--- a/_external/aria-practices
+++ b/_external/aria-practices
@@ -1 +1 @@
-Subproject commit b7471691a577728f447b7a7060b61f8c440884e8
+Subproject commit b9ed31b96e7649160e538fa539539207072bc99c
diff --git a/_external/data b/_external/data
index 105ce142a..245ca0c29 160000
--- a/_external/data
+++ b/_external/data
@@ -1 +1 @@
-Subproject commit 105ce142a1b7441b8136c4650d482744b5afee3a
+Subproject commit 245ca0c29520633afea7f598c6679a18c7d6d26b

From beb65b1bdff5ff10d0a2594577fd7daef76eccc1 Mon Sep 17 00:00:00 2001
From: w3cgruntbot 
Date: Tue, 18 Mar 2025 15:09:11 +0000
Subject: [PATCH 11/25] chore: Update `main` with latest changes from
 `aria-practices`

---
 ARIA/apg/patterns/treegrid/examples/treegrid-1.md              | 2 +-
 _external/aria-practices                                       | 2 +-
 .../patterns/treegrid/examples/css/treegrid-1.css              | 3 ++-
 3 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/ARIA/apg/patterns/treegrid/examples/treegrid-1.md b/ARIA/apg/patterns/treegrid/examples/treegrid-1.md
index a2af8e8e7..51d4294f1 100644
--- a/ARIA/apg/patterns/treegrid/examples/treegrid-1.md
+++ b/ARIA/apg/patterns/treegrid/examples/treegrid-1.md
@@ -12,7 +12,7 @@ permalink: /ARIA/apg/patterns/treegrid/examples/treegrid-1/
 
 sidebar: true
 
-footer: "          "
+footer: "          "
 
 # Context here: https://github.com/w3c/wai-aria-practices/issues/31
 type_of_guidance: APG
diff --git a/_external/aria-practices b/_external/aria-practices
index b9ed31b96..22c2bb451 160000
--- a/_external/aria-practices
+++ b/_external/aria-practices
@@ -1 +1 @@
-Subproject commit b9ed31b96e7649160e538fa539539207072bc99c
+Subproject commit 22c2bb451751262957faff46377bd24a8014647b
diff --git a/content-assets/wai-aria-practices/patterns/treegrid/examples/css/treegrid-1.css b/content-assets/wai-aria-practices/patterns/treegrid/examples/css/treegrid-1.css
index 9ed0a6c5e..2a643a1c4 100644
--- a/content-assets/wai-aria-practices/patterns/treegrid/examples/css/treegrid-1.css
+++ b/content-assets/wai-aria-practices/patterns/treegrid/examples/css/treegrid-1.css
@@ -91,7 +91,8 @@
   cursor: pointer;
 
   /* Load both right away so there is no lag when we need the other */
-  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12'%3E%3Cpolygon fill='black' points='2,0 2,10 10,5'%3E%3C/polygon%3E%3C/svg%3E%0A"),
+  background-image:
+    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12'%3E%3Cpolygon fill='black' points='2,0 2,10 10,5'%3E%3C/polygon%3E%3C/svg%3E%0A"),
     url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12'%3E%3Cpolygon fill='hsl(216, 94%25, 50%25)' points='2,0 2,10 10,5'%3E%3C/polygon%3E%3C/svg%3E%0A");
   background-repeat: no-repeat;
 }

From 574a8dbd8f6bb5a94a9e12496b0a73698bb135a1 Mon Sep 17 00:00:00 2001
From: w3cgruntbot 
Date: Tue, 18 Mar 2025 15:15:25 +0000
Subject: [PATCH 12/25] chore: Update `main` with latest changes from
 `aria-practices`

---
 .../apg/practices/landmark-regions/landmark-regions-practice.md | 2 +-
 _external/aria-practices                                        | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/ARIA/apg/practices/landmark-regions/landmark-regions-practice.md b/ARIA/apg/practices/landmark-regions/landmark-regions-practice.md
index e8715400b..1d7f8d487 100644
--- a/ARIA/apg/practices/landmark-regions/landmark-regions-practice.md
+++ b/ARIA/apg/practices/landmark-regions/landmark-regions-practice.md
@@ -424,7 +424,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
 
             
  • If a page includes more than one region landmark, each should have a unique label (see Step 3 above).
  • -
  • The region landmark can be used identify content that named landmarks do not appropriately describe.
  • +
  • The region landmark can be used to identify content that named landmarks do not appropriately describe.
  • diff --git a/_external/aria-practices b/_external/aria-practices index 22c2bb451..962f6e771 160000 --- a/_external/aria-practices +++ b/_external/aria-practices @@ -1 +1 @@ -Subproject commit 22c2bb451751262957faff46377bd24a8014647b +Subproject commit 962f6e771dccd4e3bbf8b83da717fb8d75b861da From 7454eae70ffff2eed40bab4f2461d98d7652c582 Mon Sep 17 00:00:00 2001 From: Howard Edwards Date: Wed, 19 Mar 2025 10:25:19 -0400 Subject: [PATCH 13/25] Update .tracked-html-assets.json (#395) --- scripts/pre-build/library/.tracked-html-assets.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/pre-build/library/.tracked-html-assets.json b/scripts/pre-build/library/.tracked-html-assets.json index ecbce46e6..7a01b8bc4 100644 --- a/scripts/pre-build/library/.tracked-html-assets.json +++ b/scripts/pre-build/library/.tracked-html-assets.json @@ -2,5 +2,6 @@ "feed/examples/feed-display.html", "content\\/patterns\\/landmarks\\/examples\\/.+\\.html", "toolbar/examples/help.html", - "color-settings/test-contrast-theme.html" + "color-settings/test-contrast-theme.html", + "content\\/patterns\\/expandable-region\\/.+\\.html" ] From 664518efd7950ae2ea8a1eadbcb5e564836eebc2 Mon Sep 17 00:00:00 2001 From: Howard Edwards Date: Wed, 19 Mar 2025 16:24:58 -0400 Subject: [PATCH 14/25] Update .tracked-html-assets.json to remove `/expandable-region/*.html` (#397) --- scripts/pre-build/library/.tracked-html-assets.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/pre-build/library/.tracked-html-assets.json b/scripts/pre-build/library/.tracked-html-assets.json index 7a01b8bc4..ecbce46e6 100644 --- a/scripts/pre-build/library/.tracked-html-assets.json +++ b/scripts/pre-build/library/.tracked-html-assets.json @@ -2,6 +2,5 @@ "feed/examples/feed-display.html", "content\\/patterns\\/landmarks\\/examples\\/.+\\.html", "toolbar/examples/help.html", - "color-settings/test-contrast-theme.html", - "content\\/patterns\\/expandable-region\\/.+\\.html" + "color-settings/test-contrast-theme.html" ] From 825bb157e40c681a91b168ccc400503d1a57b719 Mon Sep 17 00:00:00 2001 From: w3cgruntbot Date: Tue, 29 Apr 2025 15:54:33 +0000 Subject: [PATCH 15/25] chore: Update `main` with latest changes from `aria-practices` --- .../patterns/combobox/examples/combobox-autocomplete-list.md | 4 ++-- _external/aria-practices | 2 +- _external/data | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ARIA/apg/patterns/combobox/examples/combobox-autocomplete-list.md b/ARIA/apg/patterns/combobox/examples/combobox-autocomplete-list.md index a9eb473cd..b9cec4923 100644 --- a/ARIA/apg/patterns/combobox/examples/combobox-autocomplete-list.md +++ b/ARIA/apg/patterns/combobox/examples/combobox-autocomplete-list.md @@ -12,7 +12,7 @@ permalink: /ARIA/apg/patterns/combobox/examples/combobox-autocomplete-list/ sidebar: true -footer: " " +footer: " " # Context here: https://github.com/w3c/wai-aria-practices/issues/31 type_of_guidance: APG @@ -210,7 +210,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar'); Note: Because transparent borders are visible on some systems with operating system high contrast settings enabled, transparency cannot be used to create a visual difference between the element that is focused and other elements. Instead of using transparency, the focused element has a thicker border and less padding. When an element receives focus, its border changes from zero to two pixels and padding is reduced by two pixels. - When an element loses focus, its border changes from two pixels to two and padding is increased by two pixels. + When an element loses focus, its border changes from two pixels to zero and padding is increased by two pixels. diff --git a/_external/aria-practices b/_external/aria-practices index 962f6e771..11ab69e56 160000 --- a/_external/aria-practices +++ b/_external/aria-practices @@ -1 +1 @@ -Subproject commit 962f6e771dccd4e3bbf8b83da717fb8d75b861da +Subproject commit 11ab69e56ee9506f1409589d49cb035b34883e38 diff --git a/_external/data b/_external/data index 245ca0c29..87c02ba68 160000 --- a/_external/data +++ b/_external/data @@ -1 +1 @@ -Subproject commit 245ca0c29520633afea7f598c6679a18c7d6d26b +Subproject commit 87c02ba68f650129cdfc3e9d4cb75e7197f76119 From 6990b7e9f9e2b92ec6af0c9f947b62c99b33b002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Be=CC=81tin?= Date: Thu, 15 May 2025 15:28:31 +0200 Subject: [PATCH 16/25] Remove unused files & update data submodule --- _data/techniques.yml | 1 - _data/wcag.yml | 1 - _external/data | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) delete mode 120000 _data/techniques.yml delete mode 120000 _data/wcag.yml diff --git a/_data/techniques.yml b/_data/techniques.yml deleted file mode 120000 index bb32d1897..000000000 --- a/_data/techniques.yml +++ /dev/null @@ -1 +0,0 @@ -../_external/data/techniques.yml \ No newline at end of file diff --git a/_data/wcag.yml b/_data/wcag.yml deleted file mode 120000 index 354b6df19..000000000 --- a/_data/wcag.yml +++ /dev/null @@ -1 +0,0 @@ -../_external/data/wcag.yml \ No newline at end of file diff --git a/_external/data b/_external/data index 87c02ba68..6bb806b04 160000 --- a/_external/data +++ b/_external/data @@ -1 +1 @@ -Subproject commit 87c02ba68f650129cdfc3e9d4cb75e7197f76119 +Subproject commit 6bb806b045f33286c8b9b57e0d9aa5e15675fad8 From bde5ae348c0b32f668af57d0fdfd518fd7fd3034 Mon Sep 17 00:00:00 2001 From: w3cgruntbot Date: Tue, 3 Jun 2025 06:05:42 +0000 Subject: [PATCH 17/25] chore: Update `main` with latest changes from `aria-practices` --- ARIA/apg/patterns/slider/examples/slider-temperature.md | 6 +++--- _external/aria-practices | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ARIA/apg/patterns/slider/examples/slider-temperature.md b/ARIA/apg/patterns/slider/examples/slider-temperature.md index 9c74b6346..54b72a035 100644 --- a/ARIA/apg/patterns/slider/examples/slider-temperature.md +++ b/ARIA/apg/patterns/slider/examples/slider-temperature.md @@ -12,7 +12,7 @@ permalink: /ARIA/apg/patterns/slider/examples/slider-temperature/ sidebar: true -footer: " " +footer: " " # Context here: https://github.com/w3c/wai-aria-practices/issues/31 type_of_guidance: APG @@ -125,9 +125,9 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
    Temperature
    - 25°C - + 25°C + 25°C diff --git a/_external/aria-practices b/_external/aria-practices index 11ab69e56..6a933a9f6 160000 --- a/_external/aria-practices +++ b/_external/aria-practices @@ -1 +1 @@ -Subproject commit 11ab69e56ee9506f1409589d49cb035b34883e38 +Subproject commit 6a933a9f66a340aecf5b1c9e89153b920fd5af5b From 1ce450bde4f7fadfe45df91b811019efd5e9509d Mon Sep 17 00:00:00 2001 From: w3cgruntbot Date: Tue, 10 Jun 2025 02:34:05 +0000 Subject: [PATCH 18/25] chore: Update `main` with latest changes from `aria-practices` --- .../patterns/landmarks/examples/search.html | 25 +++++++++-- .../landmark-regions-practice.md | 44 +++++++------------ _external/aria-practices | 2 +- _external/data | 2 +- 4 files changed, 40 insertions(+), 33 deletions(-) diff --git a/ARIA/apg/patterns/landmarks/examples/search.html b/ARIA/apg/patterns/landmarks/examples/search.html index a08b22a6b..cd9348d5e 100644 --- a/ARIA/apg/patterns/landmarks/examples/search.html +++ b/ARIA/apg/patterns/landmarks/examples/search.html @@ -65,11 +65,30 @@

    Design Patterns

    -

    There is no HTML element that defines a search landmark, see the ARIA techniques for defining a search landmark.

    +

    Use the HTML search element to define a search landmark.

    +

    HTML Example

    +
    + + + +
    +
    + <search> +
    +
    +   <input type="search" aria-label="search text" size="20"> +
    +
    +   <input type="submit" value="Search"> +
    +
    + </search> +
    +

    A role="search" attribute is used to define a search landmark.

    -

    ARIA Example

    +

    ARIA Example

    @@ -90,7 +109,7 @@

    ARIA Example

    </form>
    -
    +
    diff --git a/ARIA/apg/practices/landmark-regions/landmark-regions-practice.md b/ARIA/apg/practices/landmark-regions/landmark-regions-practice.md index 1d7f8d487..2992b9a05 100644 --- a/ARIA/apg/practices/landmark-regions/landmark-regions-practice.md +++ b/ARIA/apg/practices/landmark-regions/landmark-regions-practice.md @@ -72,58 +72,45 @@ if (enableSidebar) document.body.classList.add('has-sidebar');

    HTML Sectioning Elements

    -

    - Several HTML sectioning elements automatically create ARIA landmark regions. - So, in order to provide assistive technology users with a logical view of a page, it is important to understand the effects of using HTML sectioning elements. - [[HTML-ARIA]] contains more information on HTML element role mapping. -

    +

    The following HTML sectioning elements create ARIA landmark regions.

    - - + - - - - - - + + - - - - + + - - - - - - - - + + + + + +
    - Default landmark roles for HTML sectioning elements + Landmark roles generated by HTML sectioning elements
    HTML ElementDefault Landmark RoleImplied Landmark Role
    asidecomplementary
    footercontentinfo when in context of the body elementfooter if it is in the context of a body elementcontentinfo
    headerbanner when in context of the body elementheader if it is in the context of a body elementbanner
    mainmain
    navnavigation
    sectionregion when it has an accessible name using aria-labelledby or aria-labelsection if it has an accessible name provided by aria-labelledby or aria-labelregion
    searchsearch
    @@ -457,11 +444,12 @@ if (enableSidebar) document.body.classList.add('has-sidebar');

    HTML Technique

    -

    There is no HTML element that defines a search landmark.

    + +

    Use the HTML search element to define a search landmark.

    ARIA Technique

    -

    The role="search" attribute defines a search landmark.

    +

    If the HTML search element technique is not being used, use a role="search" attribute to define a search landmark.

    diff --git a/_external/aria-practices b/_external/aria-practices index 6a933a9f6..9226c5ec3 160000 --- a/_external/aria-practices +++ b/_external/aria-practices @@ -1 +1 @@ -Subproject commit 6a933a9f66a340aecf5b1c9e89153b920fd5af5b +Subproject commit 9226c5ec39760eef42f5282b3b8955a03b533e42 diff --git a/_external/data b/_external/data index 6bb806b04..85cc23aa8 160000 --- a/_external/data +++ b/_external/data @@ -1 +1 @@ -Subproject commit 6bb806b045f33286c8b9b57e0d9aa5e15675fad8 +Subproject commit 85cc23aa8db341fc2d92dff60f2a2bc643f47dfb From c29a6ff8f6f16959d2872dde090808cba02de9d7 Mon Sep 17 00:00:00 2001 From: w3cgruntbot Date: Tue, 10 Jun 2025 02:47:29 +0000 Subject: [PATCH 19/25] chore: Update `main` with latest changes from `aria-practices` --- .../disclosure/examples/disclosure-faq.md | 36 +++++++------------ .../examples/disclosure-image-description.md | 2 +- _external/aria-practices | 2 +- .../examples/css/disclosure-faq.css | 33 +++++++++-------- .../examples/js/disclosure-button.js | 10 +++--- 5 files changed, 38 insertions(+), 45 deletions(-) diff --git a/ARIA/apg/patterns/disclosure/examples/disclosure-faq.md b/ARIA/apg/patterns/disclosure/examples/disclosure-faq.md index c59f224c5..ebd1eac38 100644 --- a/ARIA/apg/patterns/disclosure/examples/disclosure-faq.md +++ b/ARIA/apg/patterns/disclosure/examples/disclosure-faq.md @@ -12,7 +12,7 @@ permalink: /ARIA/apg/patterns/disclosure/examples/disclosure-faq/ sidebar: true -footer: " " +footer: " " # Context here: https://github.com/w3c/wai-aria-practices/issues/31 type_of_guidance: APG @@ -117,46 +117,38 @@ if (enableSidebar) document.body.classList.add('has-sidebar');

    Parking FAQs

    -
    -
    +
      +
    • -
    -
    Park at the nearest available parking meter without paying the meter and call 999-999-9999 to report the problem. We will note and approve your alternate location and will investigate the cause of the shortage in your assigned facility.
    -
    -
    + +
  • -
  • -
    You should come to the Parking office and report the loss. There is a fee to replace your lost permit. However, if your permit was stolen, a copy of a police report needs to be submitted along with a stolen parking permit form for a fee replacement exemption.
    -
    -
    + +
  • -
  • -
    All facilities are restricted from 2:00 am - 6:00 am on all days. No exceptions are made for any holiday or recess except those officially listed as a Holidays in the calendar. Please note: 24-hour rental spaces, 24-hour rental lots, and disabled parking is enforced at all times.
    -
    -
    + +
  • -
  • -
    Some parking facility restrictions differ from others. Be sure to take note of the signs at each lot entrance.
    -
    -
    + +
    @@ -165,10 +157,8 @@ if (enableSidebar) document.body.classList.add('has-sidebar');

    Accessibility Features

    • - The semantic structure of the FAQ is conveyed with native dl, dt and dd elements. - To ensure the list structure is communicated to assistive technologies, instead of applying a button role to the dt element, a button element is contained within the dt element. - Similarly, each div element containing answer content that can be shown or hidden by the button is a child of a dd element. - Since all the dd elements are present in the DOM even if some answers are hidden, the dl structure is always complete. + The semantic structure of the FAQ list is conveyed with native ul and li elements. + To ensure the list structure is communicated to assistive technologies, a button element containing the question and a div element containing answer are children of an li element. NOTE: The button actions are used to hide and show the div element with the answer.
    • To help people with visual impairments identify the disclosure as interactive and make it easier to perceive that clicking either the disclosure button or its label changes the expanded state, when a pointer hovers over the button or its label, the background color changes, a border appears, and the cursor changes to a pointer.
    • diff --git a/ARIA/apg/patterns/disclosure/examples/disclosure-image-description.md b/ARIA/apg/patterns/disclosure/examples/disclosure-image-description.md index e2177a02d..b3dbe789c 100644 --- a/ARIA/apg/patterns/disclosure/examples/disclosure-image-description.md +++ b/ARIA/apg/patterns/disclosure/examples/disclosure-image-description.md @@ -12,7 +12,7 @@ permalink: /ARIA/apg/patterns/disclosure/examples/disclosure-image-description/ sidebar: true -footer: " " +footer: " " # Context here: https://github.com/w3c/wai-aria-practices/issues/31 type_of_guidance: APG diff --git a/_external/aria-practices b/_external/aria-practices index 9226c5ec3..a8abeff66 160000 --- a/_external/aria-practices +++ b/_external/aria-practices @@ -1 +1 @@ -Subproject commit 9226c5ec39760eef42f5282b3b8955a03b533e42 +Subproject commit a8abeff66d90d7de86d8479ba18f21f46f1a3ce1 diff --git a/content-assets/wai-aria-practices/patterns/disclosure/examples/css/disclosure-faq.css b/content-assets/wai-aria-practices/patterns/disclosure/examples/css/disclosure-faq.css index 386ea0c54..6a86a19f5 100644 --- a/content-assets/wai-aria-practices/patterns/disclosure/examples/css/disclosure-faq.css +++ b/content-assets/wai-aria-practices/patterns/disclosure/examples/css/disclosure-faq.css @@ -1,4 +1,13 @@ -dl.faq button { +ul.faq, +ul.faq li { + margin: 0; + padding: 0; + list-style: none; + color: #000; + background-color: #fff; +} + +ul.faq button { margin: 0; margin-top: 16px; padding: 4px 8px; @@ -7,40 +16,34 @@ dl.faq button { border: none; background-color: transparent; border-radius: 5px; + color: #000; } -dl dd { - margin: 0; - padding: 0; - margin-left: 1.5em; - padding-bottom: 20px; - border-bottom: 2px solid #777; -} - -dl.faq .desc { +ul.faq div.desc { margin: 0; margin-top: 0.25em; - padding: 0.25em; + margin-left: 1em; + padding: 0.5em; font-size: 110%; display: none; background-color: #fed; } -dl.faq button:hover, -dl.faq button:focus { +ul.faq button:hover, +ul.faq button:focus { padding: 2px 6px; background-color: #def; border: 2px solid #005a9c; cursor: pointer; } -dl.faq button[aria-expanded="false"]::before { +ul.faq button[aria-expanded="false"]::before { content: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' style='forced-color-adjust: auto;'%3E%3Cpolygon points='1 1, 1 11, 8 6' fill='currentcolor' stroke= 'currentcolor' /%3E%3C/svg%3E%0A"); position: relative; left: -2px; } -dl.faq button[aria-expanded="true"]::before { +ul.faq button[aria-expanded="true"]::before { content: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' style='forced-color-adjust: auto;'%3E%3Cpolygon points='1 1, 11 1, 6 8' fill='currentcolor' stroke= 'currentcolor' /%3E%3C/svg%3E "); position: relative; left: -4px; diff --git a/content-assets/wai-aria-practices/patterns/disclosure/examples/js/disclosure-button.js b/content-assets/wai-aria-practices/patterns/disclosure/examples/js/disclosure-button.js index 5621777ed..c4a5a2001 100644 --- a/content-assets/wai-aria-practices/patterns/disclosure/examples/js/disclosure-button.js +++ b/content-assets/wai-aria-practices/patterns/disclosure/examples/js/disclosure-button.js @@ -19,7 +19,7 @@ class DisclosureButton { this.buttonNode = buttonNode; this.controlledNode = false; - var id = this.buttonNode.getAttribute('aria-controls'); + const id = this.buttonNode.getAttribute('aria-controls'); if (id) { this.controlledNode = document.getElementById(id); @@ -75,13 +75,13 @@ class DisclosureButton { window.addEventListener( 'load', function () { - var buttons = document.querySelectorAll( + const buttons = document.querySelectorAll( 'button[aria-expanded][aria-controls]' ); - for (var i = 0; i < buttons.length; i++) { - new DisclosureButton(buttons[i]); - } + buttons.forEach((b) => { + new DisclosureButton(b); + }); }, false ); From b0516a0cdac16406be0a7b5be4e6491eebcaee8d Mon Sep 17 00:00:00 2001 From: Howard Edwards Date: Wed, 18 Jun 2025 16:12:22 -0400 Subject: [PATCH 20/25] Update netlify.toml to force a cache clearance Netlify has removed the option for clearing the cache on each build. This PR attempts to do that by removing the likely cache locations. This is needed because in very specific cases, when the changes coming from the `aria-practices` .gitmodule is force-pushed onto, it may not successfully rebuild unless the Netlify branch deploy is re-ran without the cache option. --- netlify.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/netlify.toml b/netlify.toml index df0a92105..cca6075ea 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,5 +1,6 @@ [build] -command = "git submodule update --init --remote && bundle exec jekyll build --config '_config.yml,_config_staging.yml'" +# NB rm -rf typically applies to UNIX environments which this Netlify deploy works; a temporary solution to while figuring why .gitmodule updates are causing deploy issues unless cache is reset +command = "rm -rf .git/modules .git/modules/* .git/index .git/.gitmodules .netlify .cache node_modules build dist out && git submodule update --init --remote && bundle exec jekyll build --config '_config.yml,_config_staging.yml'" publish = "_site" [build.environment] From 7cb00430fc52ac4400ac3fd343f255d4d22315d2 Mon Sep 17 00:00:00 2001 From: w3cgruntbot Date: Tue, 8 Jul 2025 16:59:40 +0000 Subject: [PATCH 21/25] chore: Update `main` with latest changes from `aria-practices` --- ARIA/apg/patterns/accordion/accordion-pattern.md | 2 +- _external/aria-practices | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ARIA/apg/patterns/accordion/accordion-pattern.md b/ARIA/apg/patterns/accordion/accordion-pattern.md index 303d44b7b..c0c7b8c4e 100644 --- a/ARIA/apg/patterns/accordion/accordion-pattern.md +++ b/ARIA/apg/patterns/accordion/accordion-pattern.md @@ -117,7 +117,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
    -

    WAI-ARIA Roles, States, and Properties:

    +

    WAI-ARIA Roles, States, and Properties

    • The title of each accordion header is contained in an element with role button.
    • diff --git a/_external/aria-practices b/_external/aria-practices index a8abeff66..02b8edd09 160000 --- a/_external/aria-practices +++ b/_external/aria-practices @@ -1 +1 @@ -Subproject commit a8abeff66d90d7de86d8479ba18f21f46f1a3ce1 +Subproject commit 02b8edd09ba67783c6bfaf0af93e7ebc61f338be From 8df44e75efeee85b019dcaf0890c2bf3177dad63 Mon Sep 17 00:00:00 2001 From: w3cgruntbot Date: Tue, 8 Jul 2025 21:53:54 +0000 Subject: [PATCH 22/25] chore: Update `main` with latest changes from `aria-practices` --- .../coverage-and-quality-report.md | 65 +- ARIA/apg/index/index.md | 9 +- .../patterns/disclosure/disclosure-pattern.md | 1 + .../disclosure/examples/disclosure-card.md | 666 ++++++++++++++++++ .../disclosure/examples/disclosure-faq.md | 3 +- .../examples/disclosure-image-description.md | 3 +- .../examples/disclosure-navigation-hybrid.md | 3 +- .../examples/disclosure-navigation.md | 3 +- _external/aria-practices | 2 +- .../coverage-and-quality/prop-coverage.csv | 4 +- .../coverage-and-quality/role-coverage.csv | 2 +- .../examples/css/disclosure-card.css | 256 +++++++ .../disclosure/examples/js/disclosure-card.js | 54 ++ 13 files changed, 1045 insertions(+), 26 deletions(-) create mode 100644 ARIA/apg/patterns/disclosure/examples/disclosure-card.md create mode 100644 content-assets/wai-aria-practices/patterns/disclosure/examples/css/disclosure-card.css create mode 100644 content-assets/wai-aria-practices/patterns/disclosure/examples/js/disclosure-card.js diff --git a/ARIA/apg/about/coverage-and-quality/coverage-and-quality-report.md b/ARIA/apg/about/coverage-and-quality/coverage-and-quality-report.md index a0525f99e..d3484d5aa 100644 --- a/ARIA/apg/about/coverage-and-quality/coverage-and-quality-report.md +++ b/ARIA/apg/about/coverage-and-quality/coverage-and-quality-report.md @@ -69,7 +69,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
      -

      Page last updated: September 12, 2024

      +

      Page last updated: July 8, 2025

      About These Reports

      @@ -106,8 +106,8 @@ if (enableSidebar) document.body.classList.add('has-sidebar');

      -

      Roles with at Least One Guidance or Example (13)

      +

      Roles with at Least One Guidance or Example (12)

      NOTE: The HC abbreviation means example has High Contrast support.
      @@ -172,12 +172,6 @@ if (enableSidebar) document.body.classList.add('has-sidebar'); - - - - - @@ -256,7 +250,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
      -

      Roles with More than One Guidance or Example (37)

      +

      Roles with More than One Guidance or Example (38)

      NOTE: The HC abbreviation means example has High Contrast support.
      alertdialog Alert Dialog -
      articleInfinite Scrolling Feed
      @@ -278,6 +272,15 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
    • Alert
    • Alert Dialog
    • + + + + + + @@ -941,6 +944,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
    • Date Picker Combobox (HC)
    • Select-Only Combobox
    • Editable Combobox with Grid Popup
    • +
    • Disclosure (Show/Hide) Card (HC)
    • Disclosure (Show/Hide) for Answers to Frequently Asked Questions (HC)
    • Disclosure (Show/Hide) for Image Description (HC)
    • Disclosure Navigation Menu with Top-Level Links
    • @@ -1002,6 +1006,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
    • Date Picker Combobox (HC)
    • Select-Only Combobox
    • Editable Combobox with Grid Popup
    • +
    • Disclosure (Show/Hide) Card (HC)
    • Disclosure (Show/Hide) for Answers to Frequently Asked Questions (HC)
    • Disclosure (Show/Hide) for Image Description (HC)
    • Disclosure Navigation Menu with Top-Level Links
    • @@ -1330,23 +1335,23 @@ if (enableSidebar) document.body.classList.add('has-sidebar'); - + - + - + - + - + @@ -1367,7 +1372,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar'); - + @@ -1628,6 +1633,19 @@ if (enableSidebar) document.body.classList.add('has-sidebar'); + + + + + + + + + + + + + @@ -2306,6 +2324,14 @@ if (enableSidebar) document.body.classList.add('has-sidebar'); + + + + + + + + @@ -2590,6 +2616,11 @@ if (enableSidebar) document.body.classList.add('has-sidebar'); + + + + + diff --git a/ARIA/apg/index/index.md b/ARIA/apg/index/index.md index fe3d06da3..aa4ff463c 100644 --- a/ARIA/apg/index/index.md +++ b/ARIA/apg/index/index.md @@ -95,7 +95,12 @@ if (enableSidebar) document.body.classList.add('has-sidebar'); - + @@ -570,6 +575,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
    • Date Picker Combobox (HC)
    • Select-Only Combobox
    • Editable Combobox with Grid Popup
    • +
    • Disclosure (Show/Hide) Card (HC)
    • Disclosure (Show/Hide) for Answers to Frequently Asked Questions (HC)
    • Disclosure (Show/Hide) for Image Description (HC)
    • Disclosure Navigation Menu with Top-Level Links
    • @@ -629,6 +635,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
    • Date Picker Combobox (HC)
    • Select-Only Combobox
    • Editable Combobox with Grid Popup
    • +
    • Disclosure (Show/Hide) Card (HC)
    • Disclosure (Show/Hide) for Answers to Frequently Asked Questions (HC)
    • Disclosure (Show/Hide) for Image Description (HC)
    • Disclosure Navigation Menu with Top-Level Links
    • diff --git a/ARIA/apg/patterns/disclosure/disclosure-pattern.md b/ARIA/apg/patterns/disclosure/disclosure-pattern.md index 01e9a80b6..b428d1ced 100644 --- a/ARIA/apg/patterns/disclosure/disclosure-pattern.md +++ b/ARIA/apg/patterns/disclosure/disclosure-pattern.md @@ -77,6 +77,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
    • Disclosure (Show/Hide) of Answers to Frequently Asked Questions
    • Disclosure (Show/Hide) Navigation Menu
    • Disclosure (Show/Hide) Navigation Menu with Top-Level Links
    • +
    • Disclosure (Show/Hide) Card
    • diff --git a/ARIA/apg/patterns/disclosure/examples/disclosure-card.md b/ARIA/apg/patterns/disclosure/examples/disclosure-card.md new file mode 100644 index 000000000..5c3dfe6a7 --- /dev/null +++ b/ARIA/apg/patterns/disclosure/examples/disclosure-card.md @@ -0,0 +1,666 @@ +--- +# This file was generated by scripts/pre-build/library/formatForJekyll.js +title: "Example Disclosure (Show/Hide) Card" +ref: /ARIA/apg/patterns/disclosure/examples/disclosure-card/ + +github: + repository: w3c/aria-practices + branch: main + path: content/patterns/disclosure/examples/disclosure-card.html +feedbackmail: public-aria-practices@w3.org +permalink: /ARIA/apg/patterns/disclosure/examples/disclosure-card/ + +sidebar: true + +footer: " " + +# Context here: https://github.com/w3c/wai-aria-practices/issues/31 +type_of_guidance: APG + +lang: en +--- + + +Example Disclosure (Show/Hide) Card + + + + + + + + + + + + + + + + + +
      + +

      Read This First

      +
      + + The code in this example is not intended for production environments. + Before using it for any purpose, read this to understand why. + +

      This is an illustrative example of one way of using ARIA that conforms with the ARIA specification.

      + +
      + + +
      + + +
      +

      About This Example

      + +

      + The following example demonstrates using the Disclosure Pattern to create an + expandable card. +

      +

      + Authors sometimes want to display a summary of content in a collapsed disclosure that includes content that is more rich or complex than a plain text summary. + In addition, they may want to enable users of pointing devices to toggle the expanded state by clicking anywhere within the visible boundaries of the collapsed summary. + Authors may assume these goals can be achieved by nesting the entire rich summary within a disclosure button. However, doing so can: +

      +
        +
      • + Make the accessible label of the disclosure button cumbersome, inaccurate, or disorienting. +
      • +
      • + Result in invalid HTML, since + the <button> element + only permits phrasing content. +
      • +
      +

      + The following example avoids these problems by modifying the structure + of the basic disclosure pattern. Instead of having the entire summary + contained within a button that controls a separate details container, + it nests the expand and collapse disclosure button within an article + element that presents a card. When the card is collapsed, it presents + a rich summary and the disclosure button. When the card is expanded, + its content also includes the additional details content. This + structure allows the summary to include headings, links, or any other + type of rich content while also enabling the disclosure button to have + a concise, understandable accessible name. Finally, some light + JavaScript makes the entire card clickable, so its pointer + interactions are similar to the summary of a basic disclosure. +

      +

      Similar examples include:

      + +
      + +
      +
      +

      Example

      +
      + +
      +
      +

      Musical Education Conference

      +
        +
      1. +
        +
        +
        +

        + Symphonic Structure: Form, Function, and Feeling +

        +

        Ludwig van Beethoven

        +
        +

        + 7 AM in Theater an der Wien + +

        +
        +
        +
        +

        + Beethoven invites you to uncover the architectural + principles that make a composition — or a curriculum — + sing. He compares symphonic form to effective educational frameworks, + emphasizing how recurring motifs and variations foster + retention, engagement, and deep emotional resonance. He + asserts that structure should never constrain passion; + rather, it channels it into something truly enduring. +

        +

        + Blending musical theory with pedagogical insight, + Beethoven encourages educators to embrace contrast, + conflict, and resolution in their lesson planning. Just + as the second movement of a symphony can be a quiet + reflection following a turbulent first, so too should + pacing and emotional cadence be considered in designing + impactful learning experiences. +

        +
        + +
        Session registration
        + + + +
        +
        +
      2. +
      3. +
        +
        +
        +

        + Folk Futures: Tradition in the Classroom +

        +

        Antonín Dvořák

        +
        +

        + 8 AM in Rudolfinum Hall + +

        +
        +
        +
        +

        + Dvořák explores the power of cultural roots in modern + music education, drawing from his own deep connection to + the folk melodies of Bohemia. He demonstrates how + incorporating regional traditions into teaching not only + diversifies students’ musical vocabulary, but fosters a + sense of identity and continuity across generations. + Through examples from his own works, he illustrates how + authenticity can coexist with innovation. +

        +

        + Attendees will learn strategies to weave traditional + forms into contemporary curricula, using rhythm, + language, and storytelling as tools of engagement. + Dvořák makes the case that honoring heritage doesn’t + mean resisting progress—instead, it provides a vibrant + foundation on which students can build their own musical + voices. +

        +
        +
        +
        Session registration
        + + + +
        +
        +
      4. +
      5. +
        +
        +
        +

        + Playful Dissonance: Teaching with Wit and Wonder +

        +

        Francis Poulenc

        +
        +

        + 9 AM in Église Saint-Roch + +

        +
        +
        +
        +

        + Poulenc brings charm and surprise to the spotlight, + making a compelling case for humor as pedagogy. Through anecdotes, musical + excerpts, and sly asides, he reveals how wit and whimsy + can disarm resistance and unlock creativity in learners + of all ages. He shows that even dissonance, when + playfully framed, becomes an invitation rather than a + disruption. +

        +

        + This session challenges the notion that music education + must be rigid or overly serious. Poulenc encourages + instructors to embrace imperfection, joy, and even + contradiction in their teaching. The result? A classroom + atmosphere that not only nurtures skill but sparks + lifelong curiosity and delight. +

        +
        +
        +
        Session registration
        + + + +
        +
        +
      6. +
      +
      +
      + +
      + +
      +

      Accessibility Features

      +
        +
      • + To form an accessible name that is brief but sufficiently + descriptive, the “Details” button uses aria-labelledby + to combine the card’s heading with the text “Details”. +
      • +
      • + To help people with visual impairments identify the disclosure card + as interactive and make it easier to perceive that interacting with + the card will expand or collapse additional content: +
          +
        • + When a pointer hovers over the card: +
            +
          • The card area gains a thin gray border.
          • +
          • The “Details” button gains an underline.
          • +
          • The chevron icon enlarges slightly.
          • +
          • + The text and icon color of the “Details” button change from + gray to black. +
          • +
          +
        • +
        • + When the “Details” button receives keyboard focus: +
            +
          • + The entire card gains a blue outline, giving the illusion + that the card itself is “focused”. +
          • +
          • The “Details” button gains an underline.
          • +
          • The chevron icon enlarges slightly.
          • +
          • + The text and icon color of the “Details” button change from + gray to blue. +
          • +
          • + The card continues to react to hover events by “stacking” + the thin gray border alongside the blue outline, reinforcing + that the entire card remains clickable in both its collapsed + and expanded states. +
          • +
          +
        • +
        • + The “Details” button is accompanied by an encircled chevron icon + which points in a direction matching its + aria-expanded state: +
            +
          • + Downward when the disclosure is collapsed, indicating + that activating it will expand the card. +
          • +
          • + Upward when the disclosure is expanded, indicating + that activating it will collapse the card. +
          • +
          +
        • +
        +
      • +
      • + To create a logical and predictable keyboard experience for + multimodal users, when any location within the card is clicked by a + pointer, keyboard focus is programmatically placed on the “Details” + button as if the user had activated it explicitly. +
      • +
      • + If the user’s click operation triggers a text selection within the + card (i.e., highlight), the disclosure’s toggling behavior is + suppressed to avoid interference. +
      • +
      • + If the user’s click operation targets a focusable element nested + within the card (e.g., a link, form control, or button) or a + clickable <label>, the disclosure’s toggling + behavior is suppressed to avoid interference. +
      • +
      • + When forced colors are enabled, system + color keywords are used to ensure visibility and sufficient + contrast for the card’s content and interactive states. +
      • +
      • + Animation is suppressed for users who have indicated a preference + for reduced motion or high contrast. +
      • +
      • + Voice control users can toggle a disclosure card by speaking an + activation command such as Click Folk Futures: Tradition in the + Classroom details. +
      • +
      • + The content of each card is contained within an HTML <article> + element that is labeled by the heading element within the card. This + gives each card the article + role, which enables screen reader users to perceive the boundaries + of each card and easily move their reading cursor to the next or + previous card. The article element is preferable to the section + element because section elements would create ARIA landmark + regions, and excessive use of landmark regions diminishes their + utility. +
      • +
      +
      + +
      +

      Keyboard Support

      +
      article
      Total Examples6162
      High Contrast Documentation3233
      Uses SVG3536
      Uses forced-colors media query23
      Uses currentColor value2829
      Mouse Events1617
      Pointer Events3
      Disclosure (Show/Hide) CardYesex11142banner,article,aria-hidden,aria-labelledby
      Disclosure (Show/Hide) for Answers to Frequently Asked Questions class
      Disclosure (Show/Hide) CardYesYesYesYes
      Disclosure (Show/Hide) for Answers to Frequently Asked Questions Yes
      Disclosure (Show/Hide) CardYes
      (Deprecated) Collapsible Dropdown Listbox Yes
      articleInfinite Scrolling Feed + +
      banner
      + + + + + + + + + + + + + + + + +
      KeyFunction
      + Tab + Moves keyboard focus to the disclosure button.
      + Space or
      + Enter +
      + Activates the disclosure button, which toggles the + expanded/collapsed state of the card. +
      +
      + +
      +

      Role, Property, State, and Tabindex Attributes

      +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      RoleAttributeElementUsage
      + aria-controls="ID_REFERENCE" + buttonIdentifies the element controlled by the disclosure button.
      + aria-expanded="false" + + button + +
        +
      • Indicates that the container controlled by the disclosure button + is hidden.
      • +
      • + CSS attribute selectors (e.g. + [aria-expanded="false"]) are used to synchronize the + visual states with the value of the aria-expanded + attribute. +
      • +
      +
      + aria-expanded="true" + + button + +
        +
      • Indicates that the container controlled by the disclosure button + is visible.
      • +
      • + CSS attribute selectors (e.g. [aria-expanded="true"]) + are used to synchronize the visual states with the value of the + aria-expanded attribute. +
      • +
      +
      articlearticle +
        +
      • + This does not have to be declared in the code because it is + implicit in the HTML article element. +
      • +
      • + Enables screen reader users to perceive the boundaries of + each card and easily move a reading cursor from card to + card. +
      • +
      +
      +
      + +
      +

      JavaScript and CSS Source Code

      + + +
      + +
      +

      HTML Source Code

      +

      To copy the following HTML code, please open it in CodePen.

      + +
      + + +
      +
      + + + + diff --git a/ARIA/apg/patterns/disclosure/examples/disclosure-faq.md b/ARIA/apg/patterns/disclosure/examples/disclosure-faq.md index ebd1eac38..fe0fc2df9 100644 --- a/ARIA/apg/patterns/disclosure/examples/disclosure-faq.md +++ b/ARIA/apg/patterns/disclosure/examples/disclosure-faq.md @@ -12,7 +12,7 @@ permalink: /ARIA/apg/patterns/disclosure/examples/disclosure-faq/ sidebar: true -footer: " " +footer: " " # Context here: https://github.com/w3c/wai-aria-practices/issues/31 type_of_guidance: APG @@ -107,6 +107,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar'); Example Disclosure Navigation Menu
    • Example Disclosure Navigation Menu with Top-Level Links
    • +
    • Disclosure (Show/Hide) Card
    diff --git a/ARIA/apg/patterns/disclosure/examples/disclosure-image-description.md b/ARIA/apg/patterns/disclosure/examples/disclosure-image-description.md index b3dbe789c..b3ae5df9e 100644 --- a/ARIA/apg/patterns/disclosure/examples/disclosure-image-description.md +++ b/ARIA/apg/patterns/disclosure/examples/disclosure-image-description.md @@ -12,7 +12,7 @@ permalink: /ARIA/apg/patterns/disclosure/examples/disclosure-image-description/ sidebar: true -footer: " " +footer: " " # Context here: https://github.com/w3c/wai-aria-practices/issues/31 type_of_guidance: APG @@ -105,6 +105,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar'); Example Disclosure Navigation Menu
  • Example Disclosure Navigation Menu with Top-Level Links
  • +
  • Disclosure (Show/Hide) Card
  • diff --git a/ARIA/apg/patterns/disclosure/examples/disclosure-navigation-hybrid.md b/ARIA/apg/patterns/disclosure/examples/disclosure-navigation-hybrid.md index f9b958bba..e564cf570 100644 --- a/ARIA/apg/patterns/disclosure/examples/disclosure-navigation-hybrid.md +++ b/ARIA/apg/patterns/disclosure/examples/disclosure-navigation-hybrid.md @@ -12,7 +12,7 @@ permalink: /ARIA/apg/patterns/disclosure/examples/disclosure-navigation-hybrid/ sidebar: true -footer: " " +footer: " " # Context here: https://github.com/w3c/wai-aria-practices/issues/31 type_of_guidance: APG @@ -111,6 +111,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
  • Example Disclosure Navigation Menu
  • Example Disclosure (Show/Hide) for Answers to Frequently Asked Questions
  • Example Disclosure (Show/Hide) for an Image Description
  • +
  • Disclosure (Show/Hide) Card
  • Navigation Menubar Example
  • Example Usage Options

    diff --git a/ARIA/apg/patterns/disclosure/examples/disclosure-navigation.md b/ARIA/apg/patterns/disclosure/examples/disclosure-navigation.md index cef21179c..f4a15098d 100644 --- a/ARIA/apg/patterns/disclosure/examples/disclosure-navigation.md +++ b/ARIA/apg/patterns/disclosure/examples/disclosure-navigation.md @@ -12,7 +12,7 @@ permalink: /ARIA/apg/patterns/disclosure/examples/disclosure-navigation/ sidebar: true -footer: " " +footer: " " # Context here: https://github.com/w3c/wai-aria-practices/issues/31 type_of_guidance: APG @@ -111,6 +111,7 @@ if (enableSidebar) document.body.classList.add('has-sidebar');
  • Example Disclosure (Show/Hide) for Answers to Frequently Asked Questions
  • Example Disclosure (Show/Hide) for an Image Description
  • Example Disclosure Navigation Menu with Top-Level Links
  • +
  • Disclosure (Show/Hide) Card
  • Navigation Menubar Example
  • Example Usage Options

    diff --git a/_external/aria-practices b/_external/aria-practices index 02b8edd09..8267510c6 160000 --- a/_external/aria-practices +++ b/_external/aria-practices @@ -1 +1 @@ -Subproject commit 02b8edd09ba67783c6bfaf0af93e7ebc61f338be +Subproject commit 8267510c6a0ea5222a8a32966240b08d4139125c diff --git a/content-assets/wai-aria-practices/about/coverage-and-quality/prop-coverage.csv b/content-assets/wai-aria-practices/about/coverage-and-quality/prop-coverage.csv index f0f597ea1..64c42b359 100644 --- a/content-assets/wai-aria-practices/about/coverage-and-quality/prop-coverage.csv +++ b/content-assets/wai-aria-practices/about/coverage-and-quality/prop-coverage.csv @@ -7,14 +7,14 @@ "aria-colcount","1","1","Guidance: Using aria-colcount and aria-colindex","Example: Data Grid" "aria-colindex","3","1","Guidance: Using aria-colcount and aria-colindex","Guidance: Using aria-colindex When Column Indices Are Contiguous","Guidance: Using aria-colindex When Column Indices Are Not Contiguous","Example: Data Grid" "aria-colspan","1","0","Guidance: Defining cell spans using aria-colspan and aria-rowspan" -"aria-controls","0","21","Example: Accordion","Example: Auto-Rotating Image Carousel with Buttons for Slide Control","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Checkbox (Mixed-State)","Example: Editable Combobox With Both List and Inline Autocomplete","Example: Editable Combobox With List Autocomplete","Example: Editable Combobox without Autocomplete","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Disclosure (Show/Hide) for Answers to Frequently Asked Questions","Example: Disclosure (Show/Hide) for Image Description","Example: Disclosure Navigation Menu with Top-Level Links","Example: Disclosure Navigation Menu","Example: Actions Menu Button Using aria-activedescendant","Example: Actions Menu Button Using element.focus()","Example: Navigation Menu Button","Example: Experimental Tabs with Action Buttons","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: Toolbar" +"aria-controls","0","22","Example: Accordion","Example: Auto-Rotating Image Carousel with Buttons for Slide Control","Example: Auto-Rotating Image Carousel with Tabs for Slide Control","Example: Checkbox (Mixed-State)","Example: Editable Combobox With Both List and Inline Autocomplete","Example: Editable Combobox With List Autocomplete","Example: Editable Combobox without Autocomplete","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Disclosure (Show/Hide) Card","Example: Disclosure (Show/Hide) for Answers to Frequently Asked Questions","Example: Disclosure (Show/Hide) for Image Description","Example: Disclosure Navigation Menu with Top-Level Links","Example: Disclosure Navigation Menu","Example: Actions Menu Button Using aria-activedescendant","Example: Actions Menu Button Using element.focus()","Example: Navigation Menu Button","Example: Experimental Tabs with Action Buttons","Example: Tabs with Automatic Activation","Example: Tabs with Manual Activation","Example: Toolbar" "aria-current","0","5","Example: Breadcrumb","Example: Disclosure Navigation Menu with Top-Level Links","Example: Disclosure Navigation Menu","Example: Navigation Menubar","Example: Navigation Treeview" "aria-describedby","1","6","Guidance: Describing by referencing content with aria-describedby","Example: Alert Dialog","Example: Date Picker Combobox","Example: Date Picker Dialog","Example: Modal Dialog","Example: Infinite Scrolling Feed","Example: Table" "aria-details","0","0" "aria-disabled","0","3","Example: Alert Dialog","Example: Editor Menubar","Example: Toolbar" "aria-dropeffect","0","0" "aria-errormessage","0","0" -"aria-expanded","0","22","Example: Accordion","Example: Editable Combobox With Both List and Inline Autocomplete","Example: Editable Combobox With List Autocomplete","Example: Editable Combobox without Autocomplete","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Disclosure (Show/Hide) for Answers to Frequently Asked Questions","Example: Disclosure (Show/Hide) for Image Description","Example: Disclosure Navigation Menu with Top-Level Links","Example: Disclosure Navigation Menu","Example: (Deprecated) Collapsible Dropdown Listbox","Example: Actions Menu Button Using aria-activedescendant","Example: Actions Menu Button Using element.focus()","Example: Navigation Menu Button","Example: Editor Menubar","Example: Navigation Menubar","Example: Toolbar","Example: Treegrid Email Inbox","Example: File Directory Treeview Using Computed Properties","Example: File Directory Treeview Using Declared Properties","Example: Navigation Treeview" +"aria-expanded","0","23","Example: Accordion","Example: Editable Combobox With Both List and Inline Autocomplete","Example: Editable Combobox With List Autocomplete","Example: Editable Combobox without Autocomplete","Example: Date Picker Combobox","Example: Select-Only Combobox","Example: Editable Combobox with Grid Popup","Example: Disclosure (Show/Hide) Card","Example: Disclosure (Show/Hide) for Answers to Frequently Asked Questions","Example: Disclosure (Show/Hide) for Image Description","Example: Disclosure Navigation Menu with Top-Level Links","Example: Disclosure Navigation Menu","Example: (Deprecated) Collapsible Dropdown Listbox","Example: Actions Menu Button Using aria-activedescendant","Example: Actions Menu Button Using element.focus()","Example: Navigation Menu Button","Example: Editor Menubar","Example: Navigation Menubar","Example: Toolbar","Example: Treegrid Email Inbox","Example: File Directory Treeview Using Computed Properties","Example: File Directory Treeview Using Declared Properties","Example: Navigation Treeview" "aria-flowto","0","0" "aria-grabbed","0","0" "aria-haspopup","0","9","Example: Date Picker Combobox","Example: Editable Combobox with Grid Popup","Example: (Deprecated) Collapsible Dropdown Listbox","Example: Actions Menu Button Using aria-activedescendant","Example: Actions Menu Button Using element.focus()","Example: Navigation Menu Button","Example: Editor Menubar","Example: Navigation Menubar","Example: Toolbar" diff --git a/content-assets/wai-aria-practices/about/coverage-and-quality/role-coverage.csv b/content-assets/wai-aria-practices/about/coverage-and-quality/role-coverage.csv index d3d229644..e760c3cb2 100644 --- a/content-assets/wai-aria-practices/about/coverage-and-quality/role-coverage.csv +++ b/content-assets/wai-aria-practices/about/coverage-and-quality/role-coverage.csv @@ -2,7 +2,7 @@ "alert","2","2","Guidance: Alert Pattern","Guidance: Alert and Message Dialogs Pattern","Example: Alert","Example: Alert Dialog" "alertdialog","0","1","Example: Alert Dialog" "application","0","0" -"article","0","1","Example: Infinite Scrolling Feed" +"article","0","2","Example: Disclosure (Show/Hide) Card","Example: Infinite Scrolling Feed" "banner","1","3","Guidance: Banner","Example: Navigation Menubar","Example: Navigation Treeview","Example: Banner Landmark" "button","2","2","Guidance: Button Pattern","Guidance: Menu Button Pattern","Example: Button (IDL Version)","Example: Button" "caption","0","0" diff --git a/content-assets/wai-aria-practices/patterns/disclosure/examples/css/disclosure-card.css b/content-assets/wai-aria-practices/patterns/disclosure/examples/css/disclosure-card.css new file mode 100644 index 000000000..1a14901c9 --- /dev/null +++ b/content-assets/wai-aria-practices/patterns/disclosure/examples/css/disclosure-card.css @@ -0,0 +1,256 @@ +.disclosure-cards { + --length-xs: calc(10 / 16 * 0.175rem); + --length-s: calc(10 / 16 * 0.25rem); + --length-m: calc(10 / 16 * 1rem); + --length-l: calc(10 / 16 * 1.5rem); + --length-xl: calc(10 / 16 * 2rem); + --length-xxl: calc(10 / 16 * 3rem); + --color-interactive-idle: transparent; + --color-interactive-hover: gray; + --color-interactive-hover-text: black; + --color-interactive-focus: var(--wai-green, #005a6a); + --color-interactive-focus-text: var(--wai-green, #005a6a); + --transition-duration-snappy: 0; + --transition-duration-leisurely: 0; + + @media (forced-colors: active) { + --color-interactive-idle: Canvas; + --color-interactive-hover: Highlight; + --color-interactive-hover-text: Highlight; + --color-interactive-focus: Highlight; + --color-interactive-focus-text: Highlight; + } + + @media (prefers-reduced-motion: no-preference) and (forced-colors: none) { + --transition-duration-snappy: 0.15s; + --transition-duration-leisurely: 0.5s; + } + + font-family: system-ui, sans-serif; + font-size: var(--length-m); + line-height: 1.4; + background-color: ghostwhite; + margin-inline: -2em; + padding: 2em; + interpolate-size: allow-keywords; + + *, + *::before, + *::after { + box-sizing: border-box; + margin: 0; + padding: 0; + } + + *:focus { + outline: var(--length-s) solid var(--color-interactive-focus); + outline-offset: var(--length-s); + } + + h3 { + font-size: var(--length-xxl); + font-weight: 500; + border-block-end: var(--length-xs) solid currentcolor; + text-wrap: balance; + } + + h5 { + font-style: normal; + font-size: var(--length-xl); + font-weight: 600; + line-height: 1.25; + } + + button { + appearance: none; + font: inherit; + color: inherit; + border: none; + background: none; + + &[aria-expanded] { + display: inline-flex; + gap: 0.175ch; + padding-inline-start: 0.65ch; + outline: 0; + font-weight: 600; + text-decoration: underline; + text-underline-offset: 1em; + text-decoration-color: var(--color-interactive-idle); + text-decoration-thickness: var(--length-s); + border-inline-start: var(--length-xs) solid lightgray; + transition: + color var(--transition-duration-snappy) ease, + text-underline-offset var(--transition-duration-snappy) ease, + text-decoration-color var(--transition-duration-snappy) ease; + + svg { + scale: 0.8; + rotate: 0deg; + transition: + scale var(--transition-duration-snappy) ease, + rotate var(--transition-duration-snappy) ease; + } + } + + &[aria-expanded="true"] svg { + rotate: -180deg; + } + + &:not([aria-expanded]) { + font-size: var(--length-xl); + padding: 0.5ch 1ch; + border: 1px solid black; + color: white; + font-weight: bold; + background-color: rebeccapurple; + border-radius: var(--length-s); + + &:hover, + &:focus { + background-color: darkorchid; + } + + &:active { + background-color: midnightblue; + } + } + } + + .disclosure-card { + border: 0.2em solid transparent; + outline: 0 solid transparent; + outline-offset: 0; + transition: + outline-offset var(--transition-duration-snappy) ease, + outline-width var(--transition-duration-snappy) ease, + outline-color var(--transition-duration-snappy) ease, + border-color var(--transition-duration-snappy) ease; + + &:hover { + border-color: var(--color-interactive-hover); + + button[aria-expanded] { + color: var(--color-interactive-hover-text); + text-underline-offset: 0.2em; + text-decoration-color: var(--color-interactive-hover-text); + } + } + + &:hover, + &:has(button[aria-expanded]:focus) { + button[aria-expanded] { + svg { + scale: 1; + } + } + } + + &[data-being-pressed], + &:has(button[aria-expanded]:focus) { + outline-offset: 0.25em; + outline-width: 0.3em; + outline-color: var(--color-interactive-focus); + + &[data-being-pressed] { + outline-width: 0.6em; + } + + button[aria-expanded] { + color: var(--color-interactive-focus-text); + text-decoration-color: var(--color-interactive-focus); + text-underline-offset: 0.2em; + } + } + } + + .sessions { + list-style: none; + display: flex; + flex-direction: column; + gap: 1em; + margin-block: 2em; + } + + .session { + padding: 1.5em 1.5em 0; + background-color: white; + box-shadow: 0 0.25em 0.5em rgba(0 0 0 / 20%); + margin: 0.25em; + border-radius: 0.25em; + max-inline-size: 84ch; + + header, + hgroup { + display: flex; + flex-direction: column; + gap: 0.5em; + color: darkslategray; + } + + hgroup { + color: black; + font-weight: 400; + + h4 { + color: black; + font-size: 2em; + font-style: normal; + font-weight: 700; + } + + p { + font-size: 1.8em; + } + } + + hgroup + p { + display: flex; + flex-wrap: wrap; + font-size: 1.6em; + gap: 1ch; + margin-block-start: var(--length-xs); + } + } + + .details { + block-size: auto; + opacity: 1; + overflow-y: clip; + font-size: 1.6em; + transition: all var(--transition-duration-leisurely) ease; + padding-block-end: var(--length-l); + + &[inert] { + block-size: 0; + opacity: 0; + } + + .description { + display: flex; + flex-direction: column; + gap: var(--length-l); + padding-block-start: var(--length-l); + + > *:first-child { + border-block-start: var(--length-xs) solid lightgray; + padding-block-start: var(--length-l); + } + } + + form { + display: flex; + flex-direction: column; + gap: var(--length-l); + margin-block-start: var(--length-l); + border-block-start: var(--length-xs) solid lightgray; + padding-block-start: var(--length-l); + + label:has(input[type="checkbox"]) { + display: flex; + gap: 0.75ch; + align-items: baseline; + } + } + } +} diff --git a/content-assets/wai-aria-practices/patterns/disclosure/examples/js/disclosure-card.js b/content-assets/wai-aria-practices/patterns/disclosure/examples/js/disclosure-card.js new file mode 100644 index 000000000..991a3ac9e --- /dev/null +++ b/content-assets/wai-aria-practices/patterns/disclosure/examples/js/disclosure-card.js @@ -0,0 +1,54 @@ +/* + * This content is licensed according to the W3C Software License at + * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document + * + * Supplemental JS for disclosure card event handling + */ + +'use strict'; + +document.addEventListener('click', (e) => { + handleInteraction(e, true); +}); +document.addEventListener('mousedown', (e) => { + handleInteraction(e, false); +}); +document.addEventListener('mouseup', handlePressEnd); + +function handleInteraction(e, isClick) { + const disclosureCard = e.target.closest('.disclosure-card'); + if (!disclosureCard) return; + + const button = disclosureCard.querySelector('button[aria-expanded]'); + const isTextSelected = window.getSelection().toString().length > 0; + const target = e.target; + const targetTag = target.tagName.toLowerCase(); + + // Don’t proceed if the user has: + // - Triggered event on any nested focusable other than the card’s button + // - Triggered event on a label element + // - Triggered event via text selection + if ( + (button !== target && (isFocusable(target) || targetTag === 'label')) || + isTextSelected + ) + return; + + if (isClick) { + const isExpanded = button.ariaExpanded === 'true'; + const details = disclosureCard.querySelector('.details'); + button.ariaExpanded = isExpanded ? 'false' : 'true'; + details.inert = isExpanded; + button.focus(); + } else { + disclosureCard.setAttribute('data-being-pressed', 'true'); + } +} + +function handlePressEnd() { + document + .querySelector('.disclosure-card[data-being-pressed]') + ?.removeAttribute('data-being-pressed'); +} + +const isFocusable = (element) => element.tabIndex >= 0 && !element.disabled; From c940bcefc808691564a3214cd9b509cc11b552a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Re=CC=81mi=20Be=CC=81tin?= Date: Wed, 9 Jul 2025 22:40:55 +0200 Subject: [PATCH 23/25] Remove CODEOWNERS file --- .github/CODEOWNERS | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index 098230aca..000000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1,7 +0,0 @@ -# Order is important; the last matching pattern takes the most -# precedence. - -# These owners are the default owners for everything in -# the repo. - -* @shawna-slh From 65505a9fa4b9087e37e72721e5c61b92e86e834e Mon Sep 17 00:00:00 2001 From: Howard Edwards Date: Thu, 10 Jul 2025 09:18:52 -0400 Subject: [PATCH 24/25] Remove manual netlify cache clear from netlify.toml --- netlify.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/netlify.toml b/netlify.toml index cca6075ea..df0a92105 100644 --- a/netlify.toml +++ b/netlify.toml @@ -1,6 +1,5 @@ [build] -# NB rm -rf typically applies to UNIX environments which this Netlify deploy works; a temporary solution to while figuring why .gitmodule updates are causing deploy issues unless cache is reset -command = "rm -rf .git/modules .git/modules/* .git/index .git/.gitmodules .netlify .cache node_modules build dist out && git submodule update --init --remote && bundle exec jekyll build --config '_config.yml,_config_staging.yml'" +command = "git submodule update --init --remote && bundle exec jekyll build --config '_config.yml,_config_staging.yml'" publish = "_site" [build.environment] From ed0208dc7cd5604a9fcd09f86d4442a28ef061e4 Mon Sep 17 00:00:00 2001 From: w3cgruntbot Date: Tue, 15 Jul 2025 14:52:24 +0000 Subject: [PATCH 25/25] chore: Update `main` with latest changes from `aria-practices` --- ARIA/apg/patterns/landmarks/examples/search.html | 1 - _external/aria-practices | 2 +- _external/data | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/ARIA/apg/patterns/landmarks/examples/search.html b/ARIA/apg/patterns/landmarks/examples/search.html index cd9348d5e..e12d43e86 100644 --- a/ARIA/apg/patterns/landmarks/examples/search.html +++ b/ARIA/apg/patterns/landmarks/examples/search.html @@ -67,7 +67,6 @@

    Design Patterns

    Use the HTML search element to define a search landmark.

    HTML Example

    -
    diff --git a/_external/aria-practices b/_external/aria-practices index 8267510c6..d16d25dd1 160000 --- a/_external/aria-practices +++ b/_external/aria-practices @@ -1 +1 @@ -Subproject commit 8267510c6a0ea5222a8a32966240b08d4139125c +Subproject commit d16d25dd176638c1a04d779bdfebd87ff00ff793 diff --git a/_external/data b/_external/data index 85cc23aa8..fa0ce25cb 160000 --- a/_external/data +++ b/_external/data @@ -1 +1 @@ -Subproject commit 85cc23aa8db341fc2d92dff60f2a2bc643f47dfb +Subproject commit fa0ce25cb3160a6e08d634740a2477dfe7e8fdcb