feat(a11y): add Accessible roles, names, objectNames to all core QML components

Core: CButton, CIconButton, CFab, CChip, CListItem — Button roles, activeFocusOnTab
Forms: CTextField, CSelect, CCheckbox, CSwitch, CRadio, CRating — EditableText, CheckBox, ComboBox, Slider
Feedback: CAlert, CDialog, CSnackbar — AlertMessage, Dialog roles
Navigation: CTabBar — PageTabList + PageTab on delegates
Data: CAvatar, CBadge, CTable, CStatBadge, CStatusBadge — Graphic, StaticText, Table, Row
Surfaces: CCard (Pane), CAccordionItem (Button + expanded), CAppBar (ToolBar)
Progress: CProgress (ProgressBar + value), CSpinner (Animation)
Divider: CDivider (Separator)

28 files, 157 lines of a11y properties added. Zero to full coverage on core library.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 16:00:55 +00:00
parent b7e9009900
commit 5456f7eb4c
28 changed files with 157 additions and 1 deletions

View File

@@ -18,6 +18,13 @@ import QmlComponents 1.0
Button {
id: control
Accessible.role: Accessible.Button
Accessible.name: text || "Button"
Accessible.description: ""
activeFocusOnTab: true
objectName: "btn_" + text.toLowerCase()
.replace(/ /g, "_")
// default, primary, secondary,
// ghost, outlined, danger, text
property string variant: "default"

View File

@@ -39,6 +39,9 @@ Rectangle {
? Qt.rgba(1, 1, 1, 0.08)
: Qt.rgba(0.31, 0.31, 0.44, 0.10)
Accessible.role: Accessible.Pane
Accessible.name: title
// Geometry
radius: 12
clip: true

View File

@@ -18,6 +18,11 @@ import QmlComponents 1.0
Rectangle {
id: chip
Accessible.role: Accessible.Button
Accessible.name: text
objectName: "chip_" + text.toLowerCase()
.replace(/ /g, "_")
property string text: ""
property string icon: ""
// assist, filter, input, suggestion,

View File

@@ -1,4 +1,5 @@
import QtQuick
import QtQuick.Controls
import QmlComponents 1.0
/**
@@ -10,6 +11,13 @@ import QmlComponents 1.0
Rectangle {
id: root
Accessible.role: Accessible.Button
Accessible.name: icon || "Button"
Accessible.description: ""
activeFocusOnTab: true
objectName: "btn_" + (icon || "fab")
.toLowerCase().replace(/ /g, "_")
property alias icon: iconLabel.text
property int size: 56

View File

@@ -16,6 +16,13 @@ import QmlComponents 1.0
Item {
id: control
Accessible.role: Accessible.Button
Accessible.name: tooltip || icon || "Button"
Accessible.description: ""
activeFocusOnTab: true
objectName: "btn_" + (tooltip || icon)
.toLowerCase().replace(/ /g, "_")
property string icon: ""
property string size: "md" // sm, md, lg
property string variant: "default" // default, primary, ghost

View File

@@ -6,6 +6,12 @@ import "../theming"
Rectangle {
id: listItem
Accessible.role: Accessible.ListItem
Accessible.name: title
activeFocusOnTab: true
objectName: "listitem_" + title.toLowerCase()
.replace(/ /g, "_")
property string title: ""
property string subtitle: ""
property string caption: ""

View File

@@ -40,6 +40,9 @@ Rectangle {
}
}
Accessible.role: Accessible.Graphic
Accessible.name: initials || "Avatar"
width: _size
height: _size
radius: _size / 2

View File

@@ -45,6 +45,9 @@ Rectangle {
readonly property bool _showText: !dot && (text !== "" || count > 0)
Accessible.role: Accessible.StaticText
Accessible.name: text || count.toString()
// MD3 small badge: 6px dot, standard badge: 16px pill
width: dot ? 6 : Math.max(16, label.implicitWidth + 8)
height: dot ? 6 : 16

View File

@@ -24,9 +24,12 @@ Item {
// MD3: 1px line using outlineVariant (softer than border)
readonly property color _lineColor: Theme.border
Accessible.role: Accessible.Separator
// Size
implicitWidth: orientation === "horizontal" ? 200 : 1
implicitHeight: orientation === "horizontal" ? (text ? 24 : 1) : 200
implicitHeight: orientation === "horizontal"
? (text ? 24 : 1) : 200
// Horizontal divider
Row {

View File

@@ -63,6 +63,9 @@ Rectangle {
readonly property var _config: _sizes[size] || _sizes.md
Accessible.role: Accessible.StaticText
Accessible.name: label + ": " + value
color: _bgColor
radius: 12 // MD3 medium container radius

View File

@@ -51,6 +51,9 @@ Rectangle {
// MD3 on-container text: full status color
readonly property color _textColor: _statusColor
Accessible.role: Accessible.StaticText
Accessible.name: text + " (" + status + ")"
implicitHeight: 24
implicitWidth: badgeRow.implicitWidth + 20
radius: 12 // Full pill for status badges

View File

@@ -29,6 +29,8 @@ Rectangle {
property bool sortAscending: true
signal headerClicked(int columnIndex)
Accessible.role: Accessible.Table
color: "transparent"
radius: StyleVariables.radiusSm
border.width: bordered ? 1 : 0
@@ -131,6 +133,8 @@ Rectangle {
Layout.fillWidth: true
implicitHeight: 48
Accessible.role: Accessible.Row
property bool hovered: rowMouse.containsMouse
// MD3: alternating tint + hover state layer (4%)

View File

@@ -16,6 +16,11 @@ import QmlComponents 1.0
Rectangle {
id: root
// Accessibility
Accessible.role: Accessible.AlertMessage
Accessible.name: title || text
objectName: "alert_" + severity
// Public properties
property string text: ""
property string title: ""

View File

@@ -10,6 +10,12 @@ import QmlComponents 1.0
Popup {
id: root
// Accessibility
Accessible.role: Accessible.Dialog
Accessible.name: title
objectName: "dialog_" + title.toLowerCase()
.replace(/ /g, "_")
property string title: ""
property string size: "md" // sm, md, lg, xl
property bool showClose: true

View File

@@ -15,6 +15,11 @@ import QmlComponents 1.0
Item {
id: root
// Accessibility
Accessible.role: Accessible.ProgressBar
Accessible.name: label || "Progress"
Accessible.value: value * 100
// Public properties
property real value: 0 // 0.0 to 1.0
property bool indeterminate: false

View File

@@ -19,6 +19,10 @@ import QmlComponents 1.0
Item {
id: root
// Accessibility
Accessible.role: Accessible.AlertMessage
Accessible.name: _message
// Public properties
property int duration: 4000 // Auto-hide duration in ms (0 = no auto-hide)
property string position: "bottom" // bottom, top

View File

@@ -16,6 +16,10 @@ import QmlComponents 1.0
Item {
id: root
// Accessibility
Accessible.role: Accessible.Animation
Accessible.name: "Loading"
// Public properties
property string size: "md" // sm (24px), md (40px), lg (56px)
property color color: Theme.primary

View File

@@ -26,6 +26,14 @@ Item {
anchors.left: parent.left
anchors.right: parent.right
placeholderText: root.placeholderText
// Accessibility
Accessible.role: Accessible.EditableText
Accessible.name:
root.placeholderText || ""
Accessible.description: ""
activeFocusOnTab: true
objectName: "input_autocomplete"
color: Theme.text
font.pixelSize: 14
font.family: Theme.fontFamily

View File

@@ -16,6 +16,14 @@ Rectangle {
property alias text: label.text
property bool enabled: true
// Accessibility
Accessible.role: Accessible.CheckBox
Accessible.name: text
Accessible.checked: checked
activeFocusOnTab: true
objectName: "checkbox_"
+ text.toLowerCase().replace(/ /g, "_")
signal toggled(bool checked)
width: row.implicitWidth

View File

@@ -14,6 +14,12 @@ Rectangle {
property alias text: label.text
property bool enabled: true
// Accessibility
Accessible.role: Accessible.RadioButton
Accessible.name: text
Accessible.checked: checked
activeFocusOnTab: true
signal toggled(bool checked)
width: row.implicitWidth

View File

@@ -18,6 +18,11 @@ Row {
property color filledColor: "#ffc107"
property color emptyColor: Theme.border
// Accessibility
Accessible.role: Accessible.Slider
Accessible.name: "Rating"
Accessible.value: value
signal valueChanged(int newValue)
spacing: 4

View File

@@ -17,6 +17,11 @@ ComboBox {
property bool hasError: errorText.length > 0
property string size: "md" // "sm", "md", "lg"
// Accessibility
Accessible.role: Accessible.ComboBox
Accessible.name: label || ""
activeFocusOnTab: true
model: []
Layout.preferredWidth: 200
implicitHeight: size === "sm"

View File

@@ -16,6 +16,14 @@ Rectangle {
property alias text: label.text
property bool enabled: true
// Accessibility
Accessible.role: Accessible.CheckBox
Accessible.name: text
Accessible.checked: checked
activeFocusOnTab: true
objectName: "switch_"
+ text.toLowerCase().replace(/ /g, "_")
signal toggled(bool checked)
width: row.implicitWidth

View File

@@ -20,6 +20,15 @@ TextField {
property bool clearable: false
property string size: "md" // "sm", "md", "lg"
// Accessibility
Accessible.role: Accessible.EditableText
Accessible.name: label || placeholderText || ""
Accessible.description: helper || ""
activeFocusOnTab: true
objectName: "input_"
+ (label || "field")
.toLowerCase().replace(/ /g, "_")
signal suffixClicked()
implicitHeight: size === "sm"

View File

@@ -45,6 +45,17 @@ ScrollView {
TextArea {
id: textArea
// Accessibility
Accessible.role: Accessible.EditableText
Accessible.name:
root.label || placeholderText || ""
Accessible.description: root.helper || ""
activeFocusOnTab: true
objectName: "input_"
+ (root.label || "field")
.toLowerCase().replace(/ /g, "_")
wrapMode: Text.WordWrap
font.pixelSize: 14
font.family: Theme.fontFamily

View File

@@ -6,6 +6,10 @@ import "../theming"
Rectangle {
id: tabBar
// Accessibility
Accessible.role: Accessible.PageTabList
Accessible.name: "Tab bar"
property int currentIndex: 0
property var tabs: [] // [{label, icon}]
@@ -25,6 +29,12 @@ Rectangle {
Rectangle {
id: tabDelegate
// Accessibility
Accessible.role: Accessible.PageTab
Accessible.name:
modelData.label || modelData
activeFocusOnTab: true
readonly property bool isActive:
tabBar.currentIndex === index

View File

@@ -31,6 +31,11 @@ Rectangle {
readonly property color surfaceContainer:
isDark ? Qt.rgba(1, 1, 1, 0.05) : Qt.rgba(0.31, 0.31, 0.44, 0.06)
Accessible.role: Accessible.Button
Accessible.name: title
Accessible.expanded: expanded
activeFocusOnTab: true
// -- Layout --
Layout.fillWidth: true
radius: 12

View File

@@ -5,6 +5,8 @@ import QmlComponents 1.0
ToolBar {
id: appbar
Accessible.role: Accessible.ToolBar
Accessible.name: "Application toolbar"
background: Rectangle {
color: Theme.paper