diff --git a/news/changelog-1.9.md b/news/changelog-1.9.md
index 593a9a5b737..6d73818471c 100644
--- a/news/changelog-1.9.md
+++ b/news/changelog-1.9.md
@@ -19,6 +19,7 @@ All changes included in 1.9:
### `html`
- ([#13413](https://github.com/quarto-dev/quarto-cli/issues/13413)): Fix uncentered play button in `video` shortcodes from cross-reference divs. (author: @bruvellu)
+- ([#13508](https://github.com/quarto-dev/quarto-cli/issues/13508)): Add `aria-label` support to `video` shortcode for improved accessibility.
### `typst`
diff --git a/src/resources/extensions/quarto/video/_tests/test-suite.lua b/src/resources/extensions/quarto/video/_tests/test-suite.lua
index 6436f4d9ef8..485334db829 100644
--- a/src/resources/extensions/quarto/video/_tests/test-suite.lua
+++ b/src/resources/extensions/quarto/video/_tests/test-suite.lua
@@ -139,6 +139,15 @@ function TestYouTubeBuilder:testHeightWidth()
checkYouTubeBuilder(params, expected)
end
+function TestYouTubeBuilder:testAriaLabel()
+ local params = {
+ src = 'https://www.youtube.com/embed/wo9vZccmqwc',
+ ariaLabel = 'Video demonstration of features',
+ }
+ local expected = {snippet = '', type = VIDEO_TYPES.YOUTUBE, src='https://www.youtube.com/embed/wo9vZccmqwc', videoId = 'wo9vZccmqwc'}
+ checkYouTubeBuilder(params, expected)
+end
+
TestBrightcoveBuilder = {}
local checkBrightcoveBuilder = function(params, expected)
result = helpers.brightcoveBuilder(params)
@@ -206,6 +215,15 @@ function TestBrightcoveBuilder:testHeightWidth()
checkBrightcoveBuilder(params, expected)
end
+function TestBrightcoveBuilder:testAriaLabel()
+ local params = {
+ src = 'https://players.brightcove.net/1460825906/default_default/index.html?videoId=5988531335001',
+ ariaLabel = 'Video demonstration of features',
+ }
+ local expected = {snippet = '', type = VIDEO_TYPES.BRIGHTCOVE, src='https://players.brightcove.net/1460825906/default_default/index.html?videoId=5988531335001' }
+ checkBrightcoveBuilder(params, expected)
+end
+
TestVimeoBuilder = {}
local checkVimeoBuilder = function(params, expected)
result = helpers.vimeoBuilder(params)
@@ -288,6 +306,15 @@ function TestVimeoBuilder:testHeightWidth()
checkVimeoBuilder(params, expected)
end
+function TestVimeoBuilder:testAriaLabel()
+ local params = {
+ src = 'https://vimeo.com/548291210',
+ ariaLabel = 'Video demonstration of features',
+ }
+ local expected = {snippet = '', type = VIDEO_TYPES.VIMEO, src='https://player.vimeo.com/video/548291210', videoId = '548291210' }
+ checkVimeoBuilder(params, expected)
+end
+
TestVideoJSBuilder = {}
local checkVideoJSBuilder = function(params, expected)
VIDEO_SHORTCODE_NUM_VIDEOJS = 0 -- Reset Counter
@@ -368,6 +395,20 @@ function TestVideoJSBuilder:testHeightWidth()
checkVideoJSBuilder(params, expected)
end
+function TestVideoJSBuilder:testAriaLabel()
+ local params = {
+ ariaLabel = 'Video demonstration of features',
+ src = './intro-cern.mp4'
+ }
+ local expected = {
+ snippet="",
+ type="VIDEOJS",
+ src='./intro-cern.mp4',
+ id="video_shortcode_videojs_video1"
+ }
+ checkVideoJSBuilder(params, expected)
+end
+
TestVideoResponsive = {}
function TestVideoResponsive:testNoResponsive()
result = helpers.wrapWithDiv('fake-to-wrap')
diff --git a/src/resources/extensions/quarto/video/video.lua b/src/resources/extensions/quarto/video/video.lua
index 72c8a2fae4a..f79e0d1fce7 100644
--- a/src/resources/extensions/quarto/video/video.lua
+++ b/src/resources/extensions/quarto/video/video.lua
@@ -66,6 +66,7 @@ local replaceCommonAttributes = function(snippet, params)
height = params.height and ' height="' .. params.height .. '"' or '',
width = params.width and ' width="' .. params.width .. '"' or '',
title = params.title or '',
+ ariaLabel = params.ariaLabel and ' aria-label="' .. params.ariaLabel .. '"' or '',
}
return result
end
@@ -89,7 +90,7 @@ local youTubeBuilder = function(params)
local YOUTUBE_EMBED = 'https://www.youtube.com/embed/'
params.src = YOUTUBE_EMBED .. match
- local SNIPPET = [[]]
+ local SNIPPET = [[]]
snippet = replaceCommonAttributes(SNIPPET, params)
local result = {}
@@ -116,7 +117,7 @@ local brightcoveBuilder = function(params)
local result = {}
- local SNIPPET = [[]]
+ local SNIPPET = [[]]
result.snippet = replaceCommonAttributes(SNIPPET, params)
result.type = VIDEO_TYPES.BRIGHTCOVE
result.src = params.src
@@ -143,7 +144,7 @@ local vimeoBuilder = function(params)
end
- local SNIPPET = [[]]
+ local SNIPPET = [[]]
local result = {}
@@ -160,7 +161,7 @@ local videoJSBuilder = function(params)
VIDEO_SHORTCODE_NUM_VIDEOJS = VIDEO_SHORTCODE_NUM_VIDEOJS + 1
local id = "video_shortcode_videojs_video" .. VIDEO_SHORTCODE_NUM_VIDEOJS
- local SNIPPET = [[]]
+ local SNIPPET = [[]]
local snippet = params.snippet or SNIPPET
snippet = replaceCommonAttributes(snippet, params)
snippet = interpolate {
@@ -176,14 +177,14 @@ local videoJSBuilder = function(params)
result.id = id
return result
end
-local getSnippetFromBuilders = function(src, height, width, title, start)
+local getSnippetFromBuilders = function(src, height, width, title, start, ariaLabel)
local builderList = {
youTubeBuilder,
brightcoveBuilder,
vimeoBuilder,
videoJSBuilder}
- local params = { src = src, height = height, width = width, title = title, start = start }
+ local params = { src = src, height = height, width = width, title = title, start = start, ariaLabel = ariaLabel }
for i = 1, #builderList do
local builtSnippet = builderList[i](params)
@@ -211,12 +212,12 @@ function formatAsciiDocVideo(src, type)
return 'video::' .. src .. '[' .. type .. ']'
end
-local function asciidocVideo(src, height, width, title, start, _aspectRatio)
+local function asciidocVideo(src, height, width, title, start, _aspectRatio, ariaLabel)
local asciiDocVideoRawBlock = function(src, type)
return pandoc.RawBlock("asciidoc", formatAsciiDocVideo(src, type) .. '\n\n')
end
- local videoSnippetAndType = getSnippetFromBuilders(src, height, width, title, start)
+ local videoSnippetAndType = getSnippetFromBuilders(src, height, width, title, start, ariaLabel)
if videoSnippetAndType.type == VIDEO_TYPES.YOUTUBE then
-- Use the video id to form an asciidoc video
if videoSnippetAndType.videoId ~= nil then
@@ -233,13 +234,13 @@ local function asciidocVideo(src, height, width, title, start, _aspectRatio)
end
-function htmlVideo(src, height, width, title, start, aspectRatio)
+function htmlVideo(src, height, width, title, start, aspectRatio, ariaLabel)
-- https://github.com/quarto-dev/quarto-cli/issues/6833
-- handle partially-specified width, height, and aspectRatio
if aspectRatio then
-- https://github.com/quarto-dev/quarto-cli/issues/11699#issuecomment-2549219533
- -- we remove quotes as a
+ -- we remove quotes as a
-- local workaround for inconsistent shortcode argument parsing on our end.
--
-- removing quotes in general is not a good idea, but the
@@ -256,7 +257,7 @@ function htmlVideo(src, height, width, title, start, aspectRatio)
end
end
- local videoSnippetAndType = getSnippetFromBuilders(src, height, width, title, start)
+ local videoSnippetAndType = getSnippetFromBuilders(src, height, width, title, start, ariaLabel)
local videoSnippet
videoSnippet = videoSnippetAndType.snippet
@@ -326,6 +327,7 @@ return {
local heightValue = checkArg(kwargs, 'height')
local widthValue = checkArg(kwargs, 'width')
local aspectRatio = checkArg(kwargs, 'aspectRatio')
+ local ariaLabelValue = checkArg(kwargs, 'aria-label')
if isEmpty(aspectRatio) then
aspectRatio = checkArg(kwargs, 'aspect-ratio')
@@ -343,9 +345,9 @@ return {
end
if quarto.doc.is_format("html:js") then
- return htmlVideo(srcValue, heightValue, widthValue, titleValue, startValue, aspectRatio)
+ return htmlVideo(srcValue, heightValue, widthValue, titleValue, startValue, aspectRatio, ariaLabelValue)
elseif quarto.doc.is_format("asciidoc") then
- return asciidocVideo(srcValue, heightValue, widthValue, titleValue, startValue, aspectRatio)
+ return asciidocVideo(srcValue, heightValue, widthValue, titleValue, startValue, aspectRatio, ariaLabelValue)
elseif quarto.doc.is_format("markdown") then
if srcValue:sub(1, 4) == "http" then
-- For remote videos, we can emit a link
diff --git a/tests/docs/smoke-all/video/video-aria-label.qmd b/tests/docs/smoke-all/video/video-aria-label.qmd
new file mode 100644
index 00000000000..e572f6f9b56
--- /dev/null
+++ b/tests/docs/smoke-all/video/video-aria-label.qmd
@@ -0,0 +1,45 @@
+---
+title: "Video Shortcode aria-label Support (Issue #13508)"
+_quarto:
+ tests:
+ html:
+ ensureHtmlElements:
+ -
+ - '#youtube-aria-label-test iframe[aria-label="Video demonstration of Positron features"]'
+
+ - '#vimeo-aria-label-test iframe[aria-label="Accessible Vimeo video description"]'
+
+ - '#video-js-aria-label-test video[aria-label="Local video with custom label"]'
+
+ - '#brightcove-aria-label-test iframe[aria-label="Brightcove video accessible label"]'
+---
+
+Test for aria-label support in video shortcode for improved accessibility.
+
+::: {#youtube-aria-label-test}
+
+{{< video https://www.youtube.com/embed/wo9vZccmqwc
+ aria-label="Video demonstration of Positron features" >}}
+
+:::
+
+::: {#vimeo-aria-label-test}
+
+{{< video https://vimeo.com/548291297
+ aria-label="Accessible Vimeo video description" >}}
+
+:::
+
+::: {#video-js-aria-label-test}
+
+{{< video local-video.mp4
+ aria-label="Local video with custom label" >}}
+
+:::
+
+::: {#brightcove-aria-label-test}
+
+{{< video https://players.brightcove.net/1460825906/default_default/index.html?videoId=5988531335001
+ aria-label="Brightcove video accessible label" >}}
+
+:::