davidmcarati / element-capacitor · fork of element-hq/element-web
An Element fork tuned for daily use — the same end-to-end-encrypted Matrix client underneath, with the everyday experience reworked to feel like the chat apps people already know.
00 · why fork element at all
Matrix gets the hard parts right — federation, encryption, self-hosting. But for people arriving from Slack or Discord, Element's navigation asks them to learn a new mental model before they can send a message. Capacitor keeps the engine and rebuilds the cabin.
01 · navigation & room list
Upstream Element pools everything into one list. Capacitor splits it into two synthetic
sections — Channels for group rooms and Contacts for
direct messages — so the sidebar maps onto how people actually think about their
conversations. Implemented in the room list store (RoomListStoreV3) with
new section definitions, so filtering, sorting, and virtualization all keep working.
Avatars drop to 24 px and row heights tighten, fitting noticeably more
conversations on screen — the difference between scanning and scrolling.
RoomAvatarView was made size-configurable rather than hard-coded, so the
compact size is a parameter, not a hack.
Rooms can be manually reordered within Channels and Contacts. The custom order is stored in Matrix account data, so it syncs across every device you sign in on — arrange your sidebar once, keep it everywhere.
The meta-space shortcuts (Home, "Other rooms") collapse behind a single toggle by default, with the state persisted locally. The left edge of the app stops competing for attention it doesn't need.
channels / contacts · 24px avatars · drag to reorder
02 · threads where you can see them
threads active in the last 3 days · click to open
In upstream Element, threads live behind a panel you have to remember to open — easy to miss, easy to lose. Capacitor lifts a room's recently active threads directly into the room list, as clickable rows nested under the room itself.
activeThreadDays, default 3), so the list shows what's alive, not an
archive.The feature ships with 170+ lines of new viewmodel tests and Storybook stories for every state.
03 · gif support
A GIF button sits next to the emoji button in the message composer. It opens a searchable picker — trending GIFs by default, live search as you type — and a click sends the GIF as an animated image into the room.
config.json and is never committed. Without a key, the button still
appears and the picker explains how to configure one.{
"gif_provider": {
"provider": "giphy",
"api_key": "YOUR_GIPHY_API_KEY",
"rating": "pg-13"
}
}
trending by default · live search · one click to send
04 · everyday polish
Element decorates messages whose authenticity merely "can't be guaranteed" with a grey informational shield — noise that trains users to ignore all shields. Capacitor hides the purely-informational grey icon while keeping every genuine red security warning intact. Signal stays, noise goes.
On the new compact rows the online/offline indicator drifted off the avatar; it now sits correctly on the avatar corner.
App name, window icons, manifest, and in-app logos (including a redrawn SVG logo) are updated from Element to Capacitor — so the fork reads as its own product, not a mislabeled Element install.
informational noise removed · real warnings preserved
05 · under the hood
Every feature follows Element's own architecture, so the fork stays mergeable and the code reads like it belongs.
| MVVM | New UI follows Element's viewmodel pattern — thread visibility, section headers,
and item state live in RoomListItemViewModel and friends, with views
kept thin in the shared-components package. |
| tested | New behavior ships with unit tests (170+ lines for the threads viewmodel alone) and Storybook stories covering default, unread, expanded, and overflow states. |
| i18n | Every new string goes through the translation pipeline, including proper pluralization ("Show 1 more thread" / "Show 5 more threads"). |
| a11y | Thread rows are real buttons with descriptive aria-labels; unread
state is summarized for screen readers rather than conveyed by color alone. |
| synced | User preferences live at the right layer: room order in Matrix account data (cross-device), rail collapse state locally (per device), thread recency in account settings. |
| configurable | The GIF provider is runtime configuration, not a compile-time constant — deploys choose their own key, provider, and content rating. |