diff --git a/docs/architecture.md b/docs/architecture.md
index d96ba98..c9b1cc3 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -1081,6 +1081,20 @@ table table-striped table-hover align-middle mb-0
The responsive wrapper is enabled by default through `table-responsive`.
+### Sortable header visual polish
+
+Sortable headers render a clearer Bootstrap-friendly control.
+
+The current implementation uses dependency-free textual indicators:
+
+- `↕` for sortable but unsorted columns;
+- `↑` for ascending sort;
+- `↓` for descending sort.
+
+The active column still exposes `aria-sort`.
+
+The visual indicator is marked `aria-hidden="true", while the accessible state remains available through visually hidden text and ARIA attributes.
+
### Bootstrap rendering defaults
Bootstrap table display variants can be configured globally.
diff --git a/docs/table-controls.md b/docs/table-controls.md
index 05af6ba..d293ed9 100644
--- a/docs/table-controls.md
+++ b/docs/table-controls.md
@@ -244,6 +244,22 @@ Next
Go to page 2
```
+## Sortable header indicators
+
+Sortable headers include visual indicators:
+
+```text
+↕ unsorted
+↑ ascending
+↓ descending
+```
+
+These indicators do not require an icon library.
+
+The active sorted column also exposes `aria-sort`.
+
+The indicator is decorative and hidden from assistive technologies.
+
## Loading state
During Ajax refresh, the controller:
diff --git a/templates/bootstrap/_header.html.twig b/templates/bootstrap/_header.html.twig
index e9a5e1d..d7ca74b 100644
--- a/templates/bootstrap/_header.html.twig
+++ b/templates/bootstrap/_header.html.twig
@@ -5,19 +5,24 @@
{% for column in visibleColumns %}
{% set is_current_sort = column.name == current_sort_field %}
+ {% set aria_sort = null %}
+ {% if is_current_sort %}
+ {% set aria_sort = current_sort_direction == 'desc' ? 'descending' : 'ascending' %}
+ {% endif %}
+