Change an account's colour from its row in settings (#69)

An account's colour was handed out once, when the account was added, and nothing could change it. Every row in Settings → Mail accounts now has a swatch that opens the palette of eight.

It is a control on the row and a route of its own, not a field in the edit form, because that form refuses OAuth accounts and the swatch would have been missing on every Gmail and Outlook account. The value is range-checked rather than wrapped. Two accounts may share a colour. No migration: colorIndex already exists and is already in config backups.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Walid ManiandClaude Opus 5.5 authored and GitHub committed 2026-10-11 11:45:44 +02:00
1 parent ff1344ceb9
commit 92116fcdcb
16 files changed
+713 -26

No files matched your search

+2 -1
View File
@@ -55,7 +55,8 @@
Two things were wrong with it. It read `sortOrder`, so
reordering the accounts swapped their colours and the mark
stopped identifying anything; that is `colorIndex` now, which
is assigned once and never moves. And it had no name of any
no reordering touches — only the user's own choice of colour
in Settings does. And it had no name of any
kind, so an orange circle beside one address and a green one
beside another could not be interpreted at all — the obvious
reading being a status light, which it is not and never was.
+46
View File
@@ -103,6 +103,52 @@
</form>
{% endif %}
{# The colour this account wears on its sidebar dot and on
every message row. Offered on every account, OAuth ones
included, which is why it is not a field in the edit
modal: edit() is closed to them.
Each entry is its own submit button, so choosing is one
click and needs no script beyond the menu opening. The
current entry is marked by aria-pressed and a tick, not
by its hue alone.
It opens UPWARD. The list sits in a card with
overflow-hidden that is only as tall as its rows, so a
menu hanging below the last row was cut off at the
card's edge; above, there is always room. #}
<div class="relative" data-controller="ui--dropdown">
<button
type="button"
data-action="click->ui--dropdown#toggle"
aria-haspopup="true"
class="w-8 h-8 flex items-center justify-center rounded-lg hover:bg-hover transition-colors cursor-pointer"
aria-label="{{ 'settings.accounts.colour.change'|trans({ '%account%': account.displayAddress }) }}"
title="{{ 'settings.accounts.colour.change'|trans({ '%account%': account.displayAddress }) }}"
>
<span class="w-3 h-3 rounded-full {{ account.colorIndex|account_color }}" aria-hidden="true"></span>
</button>
<form method="post" action="{{ path('app_account_colour', { id: account.id }) }}"
data-ui--dropdown-target="menu" hidden data-enter="rise"
role="group"
aria-label="{{ 'settings.accounts.colour.change'|trans({ '%account%': account.displayAddress }) }}"
class="absolute bottom-full mb-1 right-0 z-30 popover p-2 grid grid-cols-[repeat(4,1.75rem)] gap-2">
<input type="hidden" name="_token" value="{{ csrf_token('account-colour' ~ account.id) }}">
{% for index in 0..(constant('App\\Twig\\SenderAvatarExtension::ACCOUNT_COLOURS') - 1) %}
{% set swatchName = ('settings.accounts.colour.swatch.' ~ index)|trans %}
<button
type="submit" name="colour" value="{{ index }}"
aria-pressed="{{ index == account.colorIndex ? 'true' : 'false' }}"
aria-label="{{ swatchName }}" title="{{ swatchName }}"
class="w-7 h-7 flex items-center justify-center rounded-full cursor-pointer transition-transform hover:scale-110 {{ index|account_color }}"
>
<i class="fa-solid fa-check text-[11px] text-white dark:text-black {{ index == account.colorIndex ? '' : 'invisible' }}" aria-hidden="true"></i>
</button>
{% endfor %}
</form>
</div>
{% if isPassword %}
<button
type="button"