The underline on the current navigation link should be a choice, not a fixture #289

Closed
opened 2026-09-19 17:24:56 +00:00 by tiagoagueda · 1 comment
Owner

Reported as: the highlight on the navigation is unwelcome. This is what put it there, and what to do about it.

Where it came from, in two parts

The current link has had a background tint since the first interface commit — 181c5728e (M1), .nav-link-active { @apply nav-link bg-ink-100 text-ink-900 dark:bg-ink-800 dark:text-ink-50; }. That is the original design and nobody has objected to it.

What is new is the weight and the underline, added three days ago by f6fbc6e08 (#274), with aria-current="page" beside it:

.nav-link-active {
  @apply nav-link bg-ink-100 font-semibold text-ink-900 underline decoration-2 underline-offset-4
    dark:bg-ink-800 dark:text-ink-50;
}

It was added deliberately, for accessibility, and the reason is in the rule's own comment (assets/css/app.css:489) and in that commit's message:

The current navigation link was a tint at 1.1:1 and nothing else, and nothing for a screen reader. It carries aria-current="page", weight and an underline: a text decoration survives forced colours, where a background is discarded.

That is WCAG 2.2 SC 1.4.1, Use of Colour: a 1.1:1 tint is the only thing saying which page you are on, which is nothing to somebody who does not distinguish those two greys — and nothing at all under Windows High Contrast, where the background is thrown away and a text decoration is kept. a94ec5c2b (#277) then gave it a ButtonText border in the forced-colours block for the same reason.

So it was intentional, and by the standing rule — if it was added for accessibility, make it optional rather than removing it — this is a preference, not a revert.

What to build

A switch under Settings → Accessibility, which exists since #281 and is exactly where this belongs, beside the career order number and the keyboard shortcuts.

The switch should govern the underline alone. Removing every non-colour cue would put the person's own interface below what the project claims in the README and on the wiki's Accessibility page, and they would not have been told that is what the switch does. Keeping font-semibold is a visual difference that is not colour, so SC 1.4.1 holds in both states, and the quiet version is genuinely quieter:

Underline on (default) Underline off
Background tint yes yes
Bold yes yes
Underline, 2px, offset 4 yes no
aria-current="page" yes yes

It must not apply under forced colours. @media (forced-colors: active) discards the background and most of the weight distinction, and the text decoration is the cue that survives — so the underline stays there whatever the preference. That is one extra rule, and leaving it out would be the version of this change that actually breaks something.

Where it is, and what else it touches

.nav-link-active is not only the masthead. Five templates use it, and a preference on the class reaches all of them:

Where File
The masthead's links templates/partials/nav_links.html:12
Settings and Server settings sidebar templates/partials/sidebar.html:15
The calendar's month/week/day/agenda switcher templates/applications/calendar.html:64
The listings filters templates/jobs/listing_list.html:72
The career page's section links per tests/test_career_sections.py

Consistency argues for one switch over all of them. If the complaint is only about the masthead, say so on the issue and it can be narrowed — but two kinds of current-link in one interface is worse than either.

How, with no script

The established pattern, from #227: the profile field reaches the body as a data attribute, and the stylesheet reads it. base.html:57 already carries data-shortcuts="off", fed by core/context_processors.py:63. So:

  • a BooleanField on Profile, defaulting to True, with a migration;
  • data-nav-underline="off" on <body> when it is off;
  • body[data-nav-underline="off"] .nav-link-active { text-decoration: none; }, and the forced-colours block putting it back.

No request, no script, and it is right with scripts off, which is the rule for every control here.

Points to settle

  • Default on or off. On, for the reason it was added: the default is what the conformance claim is about, and somebody who wants it quieter says so. Worth confirming, since the report is from the person who runs the instance.
  • The wording of the label. It says what it does, not why — "Underline the page you are on", with help text saying the current page is still marked by weight and shade without it, and that under a high-contrast theme the underline always shows.
  • Whether the tint itself should change. It is the original design and nobody asked; out of scope here unless the report is really about the tint, which would be a different issue.

Notes for whoever takes it

  • tests/test_contrast.py:89 asserts font-semibold and underline are both in the rule. It stays true of the default; the test should say default and gain a sibling for the off state, rather than being weakened.
  • tests/test_navigation.py:96,104,107 and tests/test_career_sections.py:66-77 pin the class and aria-current; neither changes, since only the decoration moves.
  • A browser test belongs here: the computed text-decoration-line of the current link with the preference on and off, and under emulated forced colours, where it must be underline either way. tests/e2e/test_rendering_modes.py already emulates that.
  • The wiki's Accessibility page lists what a person can change (#281); this is a third entry there. Refs postulo/postulo#N.
  • One new string and one label into fr-fr, pt-pt, pt-br as draft; the rest at the release sweep. npm run build:css, since the stylesheet gains a rule.
Reported as: the highlight on the navigation is unwelcome. This is what put it there, and what to do about it. ## Where it came from, in two parts The current link has had **a background tint** since the first interface commit — `181c5728e` (M1), `.nav-link-active { @apply nav-link bg-ink-100 text-ink-900 dark:bg-ink-800 dark:text-ink-50; }`. That is the original design and nobody has objected to it. What is new is **the weight and the underline**, added three days ago by `f6fbc6e08` (**#274**), with `aria-current="page"` beside it: ```css .nav-link-active { @apply nav-link bg-ink-100 font-semibold text-ink-900 underline decoration-2 underline-offset-4 dark:bg-ink-800 dark:text-ink-50; } ``` **It was added deliberately, for accessibility**, and the reason is in the rule's own comment (`assets/css/app.css:489`) and in that commit's message: > The current navigation link was a tint at 1.1:1 and nothing else, and nothing for a screen reader. It carries `aria-current="page"`, weight and an underline: a text decoration survives forced colours, where a background is discarded. That is WCAG 2.2 SC 1.4.1, *Use of Colour*: a 1.1:1 tint is the only thing saying which page you are on, which is nothing to somebody who does not distinguish those two greys — and nothing at all under Windows High Contrast, where the background is thrown away and a text decoration is kept. `a94ec5c2b` (#277) then gave it a `ButtonText` border in the forced-colours block for the same reason. So it was intentional, and by the standing rule — *if it was added for accessibility, make it optional rather than removing it* — this is a preference, not a revert. ## What to build **A switch under *Settings → Accessibility***, which exists since #281 and is exactly where this belongs, beside the career order number and the keyboard shortcuts. **The switch should govern the underline alone.** Removing every non-colour cue would put the person's own interface below what the project claims in the README and on the wiki's *Accessibility* page, and they would not have been told that is what the switch does. Keeping `font-semibold` is a visual difference that is not colour, so SC 1.4.1 holds in both states, and the quiet version is genuinely quieter: | | Underline on (default) | Underline off | | --- | --- | --- | | Background tint | yes | yes | | Bold | yes | yes | | Underline, 2px, offset 4 | **yes** | no | | `aria-current="page"` | yes | yes | **It must not apply under forced colours.** `@media (forced-colors: active)` discards the background and most of the weight distinction, and the text decoration is the cue that survives — so the underline stays there whatever the preference. That is one extra rule, and leaving it out would be the version of this change that actually breaks something. ## Where it is, and what else it touches `.nav-link-active` is not only the masthead. Five templates use it, and a preference on the class reaches all of them: | Where | File | | --- | --- | | The masthead's links | `templates/partials/nav_links.html:12` | | Settings and Server settings sidebar | `templates/partials/sidebar.html:15` | | The calendar's month/week/day/agenda switcher | `templates/applications/calendar.html:64` | | The listings filters | `templates/jobs/listing_list.html:72` | | The career page's section links | per `tests/test_career_sections.py` | Consistency argues for one switch over all of them. If the complaint is only about the masthead, say so on the issue and it can be narrowed — but two kinds of current-link in one interface is worse than either. ## How, with no script The established pattern, from #227: the profile field reaches the body as a data attribute, and the stylesheet reads it. `base.html:57` already carries `data-shortcuts="off"`, fed by `core/context_processors.py:63`. So: - a `BooleanField` on `Profile`, defaulting to `True`, with a migration; - `data-nav-underline="off"` on `<body>` when it is off; - `body[data-nav-underline="off"] .nav-link-active { text-decoration: none; }`, and the forced-colours block putting it back. No request, no script, and it is right with scripts off, which is the rule for every control here. ## Points to settle - **Default on or off.** On, for the reason it was added: the default is what the conformance claim is about, and somebody who wants it quieter says so. Worth confirming, since the report is from the person who runs the instance. - **The wording of the label.** It says what it does, not why — *"Underline the page you are on"*, with help text saying the current page is still marked by weight and shade without it, and that under a high-contrast theme the underline always shows. - **Whether the tint itself should change.** It is the original design and nobody asked; out of scope here unless the report is really about the tint, which would be a different issue. ## Notes for whoever takes it - `tests/test_contrast.py:89` asserts `font-semibold` and `underline` are both in the rule. It stays true of the default; the test should say *default* and gain a sibling for the off state, rather than being weakened. - `tests/test_navigation.py:96,104,107` and `tests/test_career_sections.py:66-77` pin the class and `aria-current`; neither changes, since only the decoration moves. - A browser test belongs here: the computed `text-decoration-line` of the current link with the preference on and off, and under emulated forced colours, where it must be `underline` either way. `tests/e2e/test_rendering_modes.py` already emulates that. - The wiki's *Accessibility* page lists what a person can change (#281); this is a third entry there. `Refs postulo/postulo#N`. - One new string and one label into `fr-fr`, `pt-pt`, `pt-br` as `draft`; the rest at the release sweep. `npm run build:css`, since the stylesheet gains a rule.
tiagoagueda added this to the 0.4.0 milestone 2026-09-19 17:24:56 +00:00
Author
Owner

Landed on main as ceaad90ea.

Landed on `main` as ceaad90ea.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Postulo/postulo#289
No description provided.