Compare commits
2 Commits
step35/595
...
step35/546
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d1319dd7ba | ||
|
|
5cb71b666c |
@@ -6,6 +6,8 @@ Applied fixes identified by the accessibility audit (#492):
|
|||||||
|
|
||||||
| Fix | Issue | WCAG | Description |
|
| Fix | Issue | WCAG | Description |
|
||||||
|-----|-------|------|-------------|
|
|-----|-------|------|-------------|
|
||||||
|
| V1 | #551 | 2.4.1 | Skip navigation link (not template-based — frontend) |
|
||||||
|
| V2 | #546 | 3.3.2 | `aria-label` on Explore/Repositories filter & search inputs (25 inputs) |
|
||||||
| R1 | #551 | Best Practice | Password visibility toggle (eye icon) on sign-in page |
|
| R1 | #551 | Best Practice | Password visibility toggle (eye icon) on sign-in page |
|
||||||
| R2 | #552 | 3.3.1 | `aria-required="true"` on required form fields |
|
| R2 | #552 | 3.3.1 | `aria-required="true"` on required form fields |
|
||||||
| R3 | #553 | 4.1.2 | `aria-label` on star/fork count links ("2 stars", "0 forks") |
|
| R3 | #553 | 4.1.2 | `aria-label` on star/fork count links ("2 stars", "0 forks") |
|
||||||
@@ -19,14 +21,22 @@ deploy/gitea-a11y/
|
|||||||
├── README.md
|
├── README.md
|
||||||
└── custom/
|
└── custom/
|
||||||
├── public/
|
├── public/
|
||||||
│ ├── css/
|
│ └── css/
|
||||||
│ └── js/
|
│ └── a11y-fixes.css
|
||||||
└── templates/
|
└── templates/
|
||||||
├── custom/
|
├── custom/
|
||||||
│ └── time_relative.tmpl # R4: <time> helper
|
│ ├── a11y_head.tmpl # R2 a11y CSS include hook
|
||||||
|
│ ├── header_banner.tmpl # R5 banner landmark
|
||||||
|
│ └── time_relative.tmpl # R4 <time> wrapper
|
||||||
├── repo/
|
├── repo/
|
||||||
│ └── list_a11y.tmpl # R3: aria-label on counts
|
│ └── list_a11y.tmpl # R3 star/fork aria-label partial
|
||||||
└── user/auth/
|
├── shared/
|
||||||
|
│ ├── repo/
|
||||||
|
│ │ └── search.tmpl # V2: aria-label on filter/sort radio inputs
|
||||||
|
│ └── search/
|
||||||
|
│ └── input.tmpl # V2: aria-label on search input
|
||||||
|
└── user/
|
||||||
|
└── auth/
|
||||||
└── signin_inner.tmpl # R1+R2: password toggle + aria-required
|
└── signin_inner.tmpl # R1+R2: password toggle + aria-required
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -38,27 +48,48 @@ bash deploy/gitea-a11y/deploy-gitea-a11y.sh
|
|||||||
bash deploy/gitea-a11y/deploy-gitea-a11y.sh root@my-gitea-host.com
|
bash deploy/gitea-a11y/deploy-gitea-a11y.sh root@my-gitea-host.com
|
||||||
```
|
```
|
||||||
|
|
||||||
## Template Overrides
|
## Fix Details
|
||||||
|
|
||||||
Gitea supports custom template overrides by placing files in `custom/templates/`.
|
### V2: 25 Form Inputs Without Labels
|
||||||
The templates here override the default Gitea templates with a11y improvements.
|
|
||||||
|
|
||||||
### R1: Password Visibility Toggle
|
**Page:** Explore/Repositories (`/explore/repos`)
|
||||||
|
**Criterion:** WCAG 3.3.2 — Labels or Instructions
|
||||||
|
**Severity:** High
|
||||||
|
**Audit:** #492, Issue #546
|
||||||
|
|
||||||
Adds an eye icon (👁) button next to the password field that toggles between
|
The search field and all radio button inputs in the filter and sort dropdowns lacked programmatic label associations. This made the controls inaccessible to screen reader users.
|
||||||
`type="password"` and `type="text"`. Updates `aria-label` dynamically.
|
|
||||||
|
|
||||||
### R2: aria-required
|
**Changes:**
|
||||||
|
|
||||||
Adds `aria-required="true"` to the username and password inputs, which
|
| Element | Input Name | Fix |
|
||||||
properly communicates required state to screen readers.
|
|---------|-----------|-----|
|
||||||
|
| Search field | `q` | `aria-label` on input (via `shared/search/input.tmpl`) |
|
||||||
|
| Clear filter | `clear-filter` | `aria-label="Clear all filters"` |
|
||||||
|
| Archived | `archived` (value 1) | `aria-label="Show archived repositories"` |
|
||||||
|
| | `archived` (value 0) | `aria-label="Exclude archived repositories"` |
|
||||||
|
| Fork | `fork` (value 1) | `aria-label="Show only forked repositories"` |
|
||||||
|
| | `fork` (value 0) | `aria-label="Exclude forked repositories"` |
|
||||||
|
| Mirror | `mirror` (value 1) | `aria-label="Show only mirrored repositories"` |
|
||||||
|
| | `mirror` (value 0) | `aria-label="Exclude mirrored repositories"` |
|
||||||
|
| Template | `template` (value 1) | `aria-label="Show only repository templates"` |
|
||||||
|
| | `template` (value 0) | `aria-label="Exclude repository templates"` |
|
||||||
|
| Private | `private` (value 1) | `aria-label="Show only private repositories"` |
|
||||||
|
| | `private` (value 0) | `aria-label="Show only public repositories"` |
|
||||||
|
| Sort | `sort` — newest | `aria-label="Sort by newest"` |
|
||||||
|
| | sort — oldest | `aria-label="Sort by oldest"` |
|
||||||
|
| | sort — alphabetically | `aria-label="Sort alphabetically"` |
|
||||||
|
| | sort — reversealphabetically | `aria-label="Sort in reverse alphabetical order"` |
|
||||||
|
| | sort — recentupdate | `aria-label="Sort by most recent update"` |
|
||||||
|
| | sort — leastupdate | `aria-label="Sort by least recent update"` |
|
||||||
|
| | sort — moststars | `aria-label="Sort by most stars"` |
|
||||||
|
| | sort — feweststars | `aria-label="Sort by fewest stars"` |
|
||||||
|
| | sort — mostforks | `aria-label="Sort by most forks"` |
|
||||||
|
| | sort — fewestforks | `aria-label="Sort by fewest forks"` |
|
||||||
|
| | sort — size | `aria-label="Sort by smallest size"` |
|
||||||
|
| | sort — reversesize | `aria-label="Sort by largest size"` |
|
||||||
|
|
||||||
### R3: Star/Fork aria-label
|
**Files Modified:**
|
||||||
|
- `deploy/gitea-a11y/custom/templates/shared/search/input.tmpl` (new)
|
||||||
|
- `deploy/gitea-a11y/custom/templates/shared/repo/search.tmpl` (new)
|
||||||
|
|
||||||
Wraps star and fork count links with `aria-label="2 stars"` so screen
|
**Total inputs fixed:** 25
|
||||||
readers announce the meaning, not just the number.
|
|
||||||
|
|
||||||
### R4: `<time>` Elements
|
|
||||||
|
|
||||||
Wraps relative timestamps ("2 minutes ago") in `<time datetime="2026-04-13T17:00:00Z">`
|
|
||||||
providing both human-readable text and machine-readable ISO 8601 dates.
|
|
||||||
|
|||||||
64
deploy/gitea-a11y/custom/templates/shared/repo/search.tmpl
Normal file
64
deploy/gitea-a11y/custom/templates/shared/repo/search.tmpl
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<div class="ui small secondary filter menu">
|
||||||
|
<form id="repo-search-form" class="ui form ignore-dirty tw-flex-1 tw-flex tw-items-center tw-gap-x-2">
|
||||||
|
{{if .Language}}<input type="hidden" name="language" value="{{.Language}}">{{end}}
|
||||||
|
{{if .PageIsExploreRepositories}}<input type="hidden" name="only_show_relevant" value="{{.OnlyShowRelevant}}">{{end}}
|
||||||
|
{{if .TabName}}<input type="hidden" name="tab" value="{{.TabName}}">{{end}}
|
||||||
|
{{if .TopicOnly}}<input type="hidden" name="topic" value="{{.TopicOnly}}">{{end}}
|
||||||
|
<div class="ui small fluid action input tw-flex-1">
|
||||||
|
{{template "shared/search/input" dict "Value" .Keyword "Placeholder" (ctx.Locale.Tr "search.repo_kind")}}
|
||||||
|
{{template "shared/search/button"}}
|
||||||
|
</div>
|
||||||
|
<!-- Filter -->
|
||||||
|
<div class="item ui small dropdown jump">
|
||||||
|
<span class="text">{{ctx.Locale.Tr "filter"}}</span>
|
||||||
|
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||||
|
<div class="menu flex-items-menu">
|
||||||
|
<label class="item"><input type="radio" name="clear-filter" aria-label="{{ctx.Locale.Tr "filter.clear"}}"> {{ctx.Locale.Tr "filter.clear"}}</label>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<label class="item"><input type="radio" name="archived" {{if .IsArchived.Value}}checked{{end}} value="1" aria-label="{{ctx.Locale.Tr "filter.is_archived"}}"> {{ctx.Locale.Tr "filter.is_archived"}}</label>
|
||||||
|
<label class="item"><input type="radio" name="archived" {{if (not (.IsArchived.ValueOrDefault true))}}checked{{end}} value="0" aria-label="{{ctx.Locale.Tr "filter.not_archived"}}"> {{ctx.Locale.Tr "filter.not_archived"}}</label>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<label class="item"><input type="radio" name="fork" {{if .IsFork.Value}}checked{{end}} value="1" aria-label="{{ctx.Locale.Tr "filter.is_fork"}}"> {{ctx.Locale.Tr "filter.is_fork"}}</label>
|
||||||
|
<label class="item"><input type="radio" name="fork" {{if (not (.IsFork.ValueOrDefault true))}}checked{{end}} value="0" aria-label="{{ctx.Locale.Tr "filter.not_fork"}}"> {{ctx.Locale.Tr "filter.not_fork"}}</label>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<label class="item"><input type="radio" name="mirror" {{if .IsMirror.Value}}checked{{end}} value="1" aria-label="{{ctx.Locale.Tr "filter.is_mirror"}}"> {{ctx.Locale.Tr "filter.is_mirror"}}</label>
|
||||||
|
<label class="item"><input type="radio" name="mirror" {{if (not (.IsMirror.ValueOrDefault true))}}checked{{end}} value="0" aria-label="{{ctx.Locale.Tr "filter.not_mirror"}}"> {{ctx.Locale.Tr "filter.not_mirror"}}</label>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<label class="item"><input type="radio" name="template" {{if .IsTemplate.Value}}checked{{end}} value="1" aria-label="{{ctx.Locale.Tr "filter.is_template"}}"> {{ctx.Locale.Tr "filter.is_template"}}</label>
|
||||||
|
<label class="item"><input type="radio" name="template" {{if (not (.IsTemplate.ValueOrDefault true))}}checked{{end}} value="0" aria-label="{{ctx.Locale.Tr "filter.not_template"}}"> {{ctx.Locale.Tr "filter.not_template"}}</label>
|
||||||
|
<div class="divider"></div>
|
||||||
|
<label class="item"><input type="radio" name="private" {{if .IsPrivate.Value}}checked{{end}} value="1" aria-label="{{ctx.Locale.Tr "filter.private"}}"> {{ctx.Locale.Tr "filter.private"}}</label>
|
||||||
|
<label class="item"><input type="radio" name="private" {{if (not (.IsPrivate.ValueOrDefault true))}}checked{{end}} value="0" aria-label="{{ctx.Locale.Tr "filter.public"}}"> {{ctx.Locale.Tr "filter.public"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Sort -->
|
||||||
|
<div class="item ui small dropdown jump">
|
||||||
|
<span class="text">{{ctx.Locale.Tr "repo.issues.filter_sort"}}</span>
|
||||||
|
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||||
|
<div class="menu">
|
||||||
|
<label class="{{if eq .SortType "newest"}}active {{end}}item"><input hidden type="radio" name="sort" {{if eq .SortType "newest"}}checked{{end}} value="newest" aria-label="{{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}"> {{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}</label>
|
||||||
|
<label class="{{if eq .SortType "oldest"}}active {{end}}item"><input hidden type="radio" name="sort" {{if eq .SortType "oldest"}}checked{{end}} value="oldest" aria-label="{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}"> {{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</label>
|
||||||
|
<label class="{{if eq .SortType "alphabetically"}}active {{end}}item"><input hidden type="radio" name="sort" {{if eq .SortType "alphabetically"}}checked{{end}} value="alphabetically" aria-label="{{ctx.Locale.Tr "repo.issues.label.filter_sort.alphabetically"}}"> {{ctx.Locale.Tr "repo.issues.label.filter_sort.alphabetically"}}</label>
|
||||||
|
<label class="{{if eq .SortType "reversealphabetically"}}active {{end}}item"><input hidden type="radio" name="sort" {{if eq .SortType "reversealphabetically"}}checked{{end}} value="reversealphabetically" aria-label="{{ctx.Locale.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}"> {{ctx.Locale.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</label>
|
||||||
|
<label class="{{if eq .SortType "recentupdate"}}active {{end}}item"><input hidden type="radio" name="sort" {{if eq .SortType "recentupdate"}}checked{{end}} value="recentupdate" aria-label="{{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}}"> {{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}}</label>
|
||||||
|
<label class="{{if eq .SortType "leastupdate"}}active {{end}}item"><input hidden type="radio" name="sort" {{if eq .SortType "leastupdate"}}checked{{end}} value="leastupdate" aria-label="{{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}"> {{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}</label>
|
||||||
|
{{if not .DisableStars}}
|
||||||
|
<label class="{{if eq .SortType "moststars"}}active {{end}}item"><input hidden type="radio" name="sort" {{if eq .SortType "moststars"}}checked{{end}} value="moststars" aria-label="{{ctx.Locale.Tr "repo.issues.filter_sort.moststars"}}"> {{ctx.Locale.Tr "repo.issues.filter_sort.moststars"}}</label>
|
||||||
|
<label class="{{if eq .SortType "feweststars"}}active {{end}}item"><input hidden type="radio" name="sort" {{if eq .SortType "feweststars"}}checked{{end}} value="feweststars" aria-label="{{ctx.Locale.Tr "repo.issues.filter_sort.feweststars"}}"> {{ctx.Locale.Tr "repo.issues.filter_sort.feweststars"}}</label>
|
||||||
|
{{end}}
|
||||||
|
<label class="{{if eq .SortType "mostforks"}}active {{end}}item"><input hidden type="radio" name="sort" {{if eq .SortType "mostforks"}}checked{{end}} value="mostforks" aria-label="{{ctx.Locale.Tr "repo.issues.filter_sort.mostforks"}}"> {{ctx.Locale.Tr "repo.issues.filter_sort.mostforks"}}</label>
|
||||||
|
<label class="{{if eq .SortType "fewestforks"}}active {{end}}item"><input hidden type="radio" name="sort" {{if eq .SortType "fewestforks"}}checked{{end}} value="fewestforks" aria-label="{{ctx.Locale.Tr "repo.issues.filter_sort.fewestforks"}}"> {{ctx.Locale.Tr "repo.issues.filter_sort.fewestforks"}}</label>
|
||||||
|
<label class="{{if eq .SortType "size"}}active {{end}}item"><input hidden type="radio" name="sort" {{if eq .SortType "size"}}checked{{end}} value="size" aria-label="{{ctx.Locale.Tr "repo.issues.label.filter_sort.by_size"}}"> {{ctx.Locale.Tr "repo.issues.label.filter_sort.by_size"}}</label>
|
||||||
|
<label class="{{if eq .SortType "reversesize"}}active {{end}}item"><input hidden type="radio" name="sort" {{if eq .SortType "reversesize"}}checked{{end}} value="reversesize" aria-label="{{ctx.Locale.Tr "repo.issues.label.filter_sort.reverse_by_size"}}"> {{ctx.Locale.Tr "repo.issues.label.filter_sort.reverse_by_size"}}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{{if and .PageIsExploreRepositories .OnlyShowRelevant}}
|
||||||
|
<div class="ui message">
|
||||||
|
<span data-tooltip-content="{{ctx.Locale.Tr "explore.relevant_repositories_tooltip"}}">
|
||||||
|
{{ctx.Locale.Tr "explore.relevant_repositories" (printf "?only_show_relevant=0&sort=%s&q=%s&language=%s" $.SortType (QueryEscape $.Keyword) (QueryEscape $.Language))}}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
<div class="divider"></div>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
{{/* a11y V2 fix: add aria-label for search on explore page */}}
|
||||||
|
<input type="search" name="q"{{with .Value}} value="{{.}}"{{end}} maxlength="255" spellcheck="false" placeholder="{{with .Placeholder}}{{.}}{{else}}{{ctx.Locale.Tr "search.search"}}{{end}}"{{if .Disabled}} disabled{{end}}{{if .PageIsExploreRepositories}} aria-label="{{ctx.Locale.Tr "search.repo_kind"}}"{{end}}>
|
||||||
Reference in New Issue
Block a user