mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-08-02 03:28:28 -04:00
themes: more detailed WCAG breakdowns in browser
This commit is contained in:
@@ -110,14 +110,22 @@ type ThemeWCAGGroup struct {
|
||||
WorstPair []string `json:"worstPair,omitempty"`
|
||||
}
|
||||
|
||||
type ThemeWCAGBreakdown struct {
|
||||
Name string `json:"name"`
|
||||
Mode string `json:"mode"`
|
||||
Level string `json:"level"`
|
||||
BodyLevel string `json:"bodyLevel"`
|
||||
}
|
||||
|
||||
type ThemeWCAGMode struct {
|
||||
Level string `json:"level"`
|
||||
MinRatio float64 `json:"minRatio"`
|
||||
WorstPair []string `json:"worstPair,omitempty"`
|
||||
Body *ThemeWCAGGroup `json:"body,omitempty"`
|
||||
Accent *ThemeWCAGGroup `json:"accent,omitempty"`
|
||||
NonText *ThemeWCAGGroup `json:"nonText,omitempty"`
|
||||
Variants map[string]string `json:"variants,omitempty"`
|
||||
Level string `json:"level"`
|
||||
MinRatio float64 `json:"minRatio"`
|
||||
WorstPair []string `json:"worstPair,omitempty"`
|
||||
Body *ThemeWCAGGroup `json:"body,omitempty"`
|
||||
Accent *ThemeWCAGGroup `json:"accent,omitempty"`
|
||||
NonText *ThemeWCAGGroup `json:"nonText,omitempty"`
|
||||
Variants map[string]string `json:"variants,omitempty"`
|
||||
Breakdown []ThemeWCAGBreakdown `json:"breakdown,omitempty"`
|
||||
}
|
||||
|
||||
type ThemeWCAG struct {
|
||||
|
||||
@@ -504,30 +504,145 @@ FloatingWindow {
|
||||
Rectangle {
|
||||
id: wcagBadge
|
||||
|
||||
// Rated for the mode being viewed, since a theme
|
||||
// often passes in one mode and not the other.
|
||||
readonly property var modeReport: Theme.isLightMode ? modelData.wcag?.light : modelData.wcag?.dark
|
||||
readonly property string level: modeReport?.level ?? ""
|
||||
readonly property string bodyLevel: modeReport?.body?.level ?? ""
|
||||
readonly property bool fullPass: level === "AA" || level === "AAA"
|
||||
readonly property bool bodyPass: bodyLevel === "AA" || bodyLevel === "AAA"
|
||||
readonly property var rows: modeReport?.breakdown ?? []
|
||||
readonly property var allRows: (modelData.wcag?.dark?.breakdown ?? []).concat(modelData.wcag?.light?.breakdown ?? [])
|
||||
readonly property string level: wcagBadge.rollupLevel(wcagBadge.rows, "level")
|
||||
readonly property string bodyLevel: wcagBadge.rollupLevel(wcagBadge.rows, "bodyLevel")
|
||||
readonly property bool fullPass: level !== ""
|
||||
readonly property bool bodyPass: bodyLevel !== ""
|
||||
readonly property bool partial: wcagBadge.isPartial(wcagBadge.rows, fullPass ? "level" : "bodyLevel")
|
||||
|
||||
function rollupLevel(rows, key) {
|
||||
let hasAAA = false;
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const value = rows[i][key];
|
||||
if (value === "AA")
|
||||
return "AA";
|
||||
if (value === "AAA")
|
||||
hasAAA = true;
|
||||
}
|
||||
return hasAAA ? "AAA" : "";
|
||||
}
|
||||
|
||||
function isPartial(rows, key) {
|
||||
let total = 0;
|
||||
let passing = 0;
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const value = rows[i][key];
|
||||
if (!value)
|
||||
continue;
|
||||
total++;
|
||||
if (value !== "fail")
|
||||
passing++;
|
||||
}
|
||||
return passing > 0 && passing < total;
|
||||
}
|
||||
|
||||
height: 18
|
||||
width: wcagText.implicitWidth + Theme.spacingS
|
||||
width: wcagRow.implicitWidth + Theme.spacingS
|
||||
radius: 9
|
||||
color: Theme.withAlpha(fullPass ? Theme.info : Theme.surfaceVariantText, 0.15)
|
||||
color: Theme.withAlpha(fullPass ? Theme.info : Theme.surfaceVariantText, wcagArea.containsMouse ? 0.25 : 0.15)
|
||||
border.color: Theme.withAlpha(fullPass ? Theme.info : Theme.surfaceVariantText, 0.4)
|
||||
border.width: 1
|
||||
visible: fullPass || bodyPass
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
StyledText {
|
||||
id: wcagText
|
||||
Row {
|
||||
id: wcagRow
|
||||
anchors.centerIn: parent
|
||||
text: wcagBadge.fullPass ? "WCAG " + wcagBadge.level : I18n.tr("WCAG %1 body", "contrast badge when only body text passes").arg(wcagBadge.bodyLevel)
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: wcagBadge.fullPass ? Theme.info : Theme.surfaceVariantText
|
||||
font.weight: Font.Medium
|
||||
spacing: 2
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: {
|
||||
let label = "WCAG " + (wcagBadge.fullPass ? wcagBadge.level : wcagBadge.bodyLevel);
|
||||
if (!wcagBadge.fullPass)
|
||||
label += " " + I18n.tr("Body", "notification rule match field option");
|
||||
if (wcagBadge.partial)
|
||||
label += " (" + I18n.tr("Partial", "contrast badge suffix: only some variants pass") + ")";
|
||||
return label;
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: wcagBadge.fullPass ? Theme.info : Theme.surfaceVariantText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
name: "info"
|
||||
size: 12
|
||||
visible: wcagBadge.allRows.length > 1
|
||||
color: wcagBadge.fullPass ? Theme.info : Theme.surfaceVariantText
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: wcagArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: wcagBadge.allRows.length > 1 ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
onClicked: {
|
||||
if (wcagBadge.allRows.length <= 1)
|
||||
return;
|
||||
wcagDetail.opened ? wcagDetail.close() : wcagDetail.open();
|
||||
}
|
||||
}
|
||||
|
||||
Popup {
|
||||
id: wcagDetail
|
||||
y: wcagBadge.height + Theme.spacingXS
|
||||
padding: Theme.spacingS
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
||||
|
||||
background: Rectangle {
|
||||
color: Theme.surfaceContainerHigh
|
||||
radius: Theme.cornerRadius
|
||||
border.width: 1
|
||||
border.color: Theme.outlineMedium
|
||||
}
|
||||
|
||||
contentItem: Column {
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Contrast by variant", "WCAG breakdown popup heading")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: wcagBadge.allRows
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Rectangle {
|
||||
width: 8
|
||||
height: 8
|
||||
radius: 4
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: modelData.level === "fail" ? Theme.error : Theme.success
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: modelData.name + " · " + modelData.mode
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
StyledText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: modelData.level === "fail" ? I18n.tr("below AA", "contrast level below the AA threshold") : "WCAG " + modelData.level
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
font.weight: Font.Medium
|
||||
color: modelData.level === "fail" ? Theme.error : Theme.surfaceText
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user