{{ page.title }}{{ page.extra.maturity }}
-
-
+{{ page.content | markdown | safe }}
-+ {{ page.title }} + {{ page.extra.maturity }} +
+ +Something wrong? Report the issue here.
Features
-{{ clients::category_label(category_name=category) }}
-
- {% for feature, supported in features %}
- -
-
- {{ clients::feature_label(feature_name=feature) | trim }}
-
-
Something wrong? Report the issue here.
+{{ section.title }}
{{ section.extra.summary }}
@@ -38,29 +44,81 @@Featured clients
mature ones you can safely use. -{{ client.title }}
-
+
+
+
+
+ {{ client.title }}
+ {{ client.extra.maturity }}
+ #
+
+ {% if client.extra.good_for %}
+ Good for: {{ client.extra.good_for }}
+ {% else %}
+ {{ client.content | markdown(inline=true) }}
+ {% endif %}
+
+
+ {{ clients::platforms(packages=client.extra.packages) }}
+
-
diff --git a/templates/macros/clients.html b/templates/macros/clients.html
index f0c471401..123399fd2 100644
--- a/templates/macros/clients.html
+++ b/templates/macros/clients.html
@@ -9,7 +9,7 @@
{%- endif -%}
{% endmacro %}
-{# Human-readable labels for features #}
+{# Human-readable label for a feature #}
{% macro feature_label(feature_name) %}
{%- if feature_name == "e2ee" -%}
End-to-end encryption
@@ -88,31 +88,16 @@
{%- endif -%}
{% endmacro %}
+{# CSS filter classes for the all-clients card deck #}
{% macro classes(client) %}
-{% if client.packages.apple_app_store.app_id and client.packages.apple_app_store.org %}
-platform-ios
-{% endif %}
-{% if client.packages.google_play_store.app_id %}
-platform-android
-{% endif %}
-{% if client.packages.flathub.app_id %}
-platform-flathub
-{% endif %}
-{% if client.packages.other_linux_link %}
-platform-linux
-{% endif %}
-{% if client.packages.webapp %}
-platform-web
-{% endif %}
-{% if client.packages.windows_installer %}
-platform-windows
-{% endif %}
-{% if client.packages.macos_installer %}
-platform-macos
-{% endif %}
-
+{% if client.packages.apple_app_store.app_id and client.packages.apple_app_store.org %}platform-ios {% endif %}
+{% if client.packages.google_play_store.app_id %}platform-android {% endif %}
+{% if client.packages.flathub.app_id %}platform-flathub {% endif %}
+{% if client.packages.other_linux_link %}platform-linux {% endif %}
+{% if client.packages.webapp %}platform-web {% endif %}
+{% if client.packages.windows_installer %}platform-windows {% endif %}
+{% if client.packages.macos_installer %}platform-macos {% endif %}
maturity-{{ client.maturity | lower }}
-
licence-{{ client.licence | slugify }}
{% for feature_level_name, features in client.features %}
@@ -120,38 +105,34 @@
{% if feature_state == "supported" %}feature-{{ feature_name | replace(from="_", to="-") }}{% endif %}
{% endfor %}
{% endfor %}
-{% endmacro classes %}
+{% endmacro %}
+{# Store badge install links #}
{% macro install_links(packages) %}
{% if packages.windows_installer %}
-
+
{% endif %}
{% if packages.macos_installer %}
-
+
{% endif %}
{% if packages.flathub %}
-
+
{% endif %}
{% if packages.webapp %}
{% endif %}
{% if packages.apple_app_store %}
-
+
{% endif %}
{% if packages.f_droid %}
-
+
{% endif %}
{% if packages.google_play_store %}
-
+
{% endif %}
{% endmacro %}
+{# Colored platform pills #}
{% macro platforms(packages) %}
{% if packages.apple_app_store %}iOS{% endif %}
{% if packages.google_play_store or packages.f_droid %}Android{% endif %}
From 1ccceb168a7c3b5dc4bad5e7d5d281e88050abd3 Mon Sep 17 00:00:00 2001
From: MTRNord
Date: Wed, 4 Mar 2026 08:47:59 +0100
Subject: [PATCH 03/17] redesign bridge network pages
Replace the old full-width implementation list with bridge-card components
that use the shared .ecosystem-detail-* layout. Each card has a two-column
grid (description + access pills on the left, feature support list on the
right). The bridge listing page gains a platforms-grid of clickable network
tiles. Add a bridges macro for feature labels and descriptions.
Signed-off-by: MTRNord
---
sass/_bridges.scss | 298 +++++++++++++++---
.../ecosystem/bridge_implementations.html | 208 ++++++++----
templates/ecosystem/bridges.html | 4 +-
templates/macros/bridges.html | 67 ++++
4 files changed, 466 insertions(+), 111 deletions(-)
create mode 100644 templates/macros/bridges.html
diff --git a/sass/_bridges.scss b/sass/_bridges.scss
index 5c4fa24da..9d3ace551 100644
--- a/sass/_bridges.scss
+++ b/sass/_bridges.scss
@@ -4,68 +4,266 @@
.platforms-grid {
display: grid;
- grid-template-columns: repeat(auto-fill, 17.6rem);
- gap: .8rem;
+ grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
+ gap: 0.75rem;
margin-block: 2rem 3rem;
margin-inline: auto;
justify-content: center;
+}
+
+.platform {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 1rem;
+ padding: 0.85rem 1.1rem;
+
+ color: var(--color-text);
+ text-decoration: none;
- .platform {
- @extend .clickable-card;
+ border: 1px solid #d2d2d2;
+ border-radius: 16px;
+ transition:
+ border-color 0.15s ease,
+ box-shadow 0.15s ease;
+
+ &:hover {
+ border-color: #999;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
+ }
+ img {
+ width: 52px;
+ height: 52px;
+ flex-shrink: 0;
+ object-fit: contain;
+ border-radius: 10px;
+ border: 1px solid #e8e8e8;
+ padding: 0.3rem;
+ background: #fafafa;
+ box-sizing: border-box;
+ }
+
+ .details {
display: flex;
- flex-direction: row;
- gap: 1rem;
- align-items: center;
- padding-block: .5rem;
- padding-inline: .5rem 1rem;
- cursor: pointer;
+ flex-direction: column;
+ gap: 0.15rem;
+ min-width: 0;
+
+ h2 {
+ font-size: 1.05rem;
+ font-weight: 700;
+ margin: 0;
+ line-height: 1.2;
+ }
+
+ p {
+ margin: 0;
+ font-size: 0.82rem;
+ color: #555;
+ line-height: 1.3;
+ }
+ }
+}
+
+.platform.no-bridge {
+ opacity: 0.35;
+ pointer-events: none;
+
+ img {
+ filter: grayscale(1);
+ }
+}
+
+// ── Bridge network detail page ────────────────────────────────────────────────
+
+.bridge-network-header {
+ max-width: 60rem;
+ margin-inline: auto;
+ padding-top: 1rem;
+ margin-bottom: 2rem;
+}
+
+.bridge-network-back {
+ align-self: flex-start;
+}
+
+.bridge-cards {
+ max-width: 60rem;
+ margin-inline: auto;
+ padding-bottom: 3rem;
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+}
+
+.bridge-card {
+ border: 1px solid #d2d2d2;
+ border-radius: 16px;
+ overflow: hidden;
+}
+
+.bridge-card-header {
+ padding: 1.25rem 1.5rem 1rem;
+ border-bottom: 1px solid #e8e8e8;
+ display: flex;
+ flex-direction: column;
+ gap: 0.4rem;
+}
+
+.bridge-card-title-row {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 0.5rem 1rem;
+}
+
+.bridge-card-name {
+ margin: 0;
+ font-size: 1.35rem;
+ font-weight: 700;
+ line-height: 1.2;
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+.bridge-card-anchor {
+ color: inherit;
+ text-decoration: none;
+
+ &:hover {
+ text-decoration: underline;
+ }
+}
+
+.bridge-card-recommended {
+ display: inline-block;
+ font-size: 0.78rem;
+ font-weight: 600;
+ color: #245c0c;
+ background-color: #dff6d5;
+ border: 1px solid #245c0c;
+ border-radius: 9999px;
+ padding: 0.1rem 0.55rem;
+ line-height: 1.6;
+}
+
+.bridge-card-access {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 0.35rem;
+ margin-top: 0.1rem;
+}
+
+.bridge-card-access-label {
+ font-size: 0.8rem;
+ font-weight: 600;
+ color: #666;
+ text-transform: uppercase;
+ letter-spacing: 0.03em;
+ margin-right: 0.15rem;
+}
+
+.bridge-card-access-pill {
+ display: inline-flex;
+ align-items: center;
+ font-size: 0.8rem;
+ border-radius: 9999px;
+ padding: 0.1rem 0.6rem;
+ line-height: 1.6;
+ border: 1px solid;
+
+ // Platform side — muted indigo/blue
+ &--platform {
+ background-color: #eef0fb;
+ border-color: #8892d4;
+ color: #2c3894;
+ }
+
+ // Matrix side — Matrix green
+ &--matrix {
+ background-color: #dff6d5;
+ border-color: #4a9e2f;
+ color: #245c0c;
+ }
+}
+
+.bridge-card-grid {
+ display: grid;
+ grid-template-columns: 1fr 15rem;
- color: var(--color-text);
+ @media (max-width: 700px) {
+ grid-template-columns: 1fr;
+ }
+}
- border: 1px solid #d2d2d2;
- border-radius: 12px;
- transition: border-color 0.3s ease, background-color 0.3s ease;
+.bridge-card-left {
+ // Left column holds both the header and main — the aside spans full height
+ display: flex;
+ flex-direction: column;
+ min-width: 0;
+}
- img {
- width: 64px;
- height: 64px;
+.bridge-card-main {
+ padding: 1.25rem 1.5rem;
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ flex: 1;
+}
+
+.bridge-card-features {
+ background-color: #fafafa;
+ border-left: 1px solid #e8e8e8;
+ padding: 1.25rem 1rem;
+ @media (max-width: 700px) {
+ border-left: none;
+ border-top: 1px solid #e8e8e8;
+ background-color: #fafafa;
+ }
- border: 1px solid #d2d2d2;
- border-radius: 15px;
+ .ecosystem-detail-features {
+ dl {
+ border: none;
+ border-radius: 0;
}
- .details {
- display: flex;
- flex-direction: column;
+ .feature-row {
+ gap: 0.1rem;
+ padding: 0.4rem 0.5rem;
+ border-radius: 8px;
- h2 {
- font-size: 1.5rem;
- font-weight: 700;
- margin: 0;
- line-height: 1.2;
+ & + .feature-row {
+ border-top: none;
}
- p {
- margin: 0;
- font-size: .875rem;
- color: #545454;
+ &.supported {
+ background-color: transparent;
+ }
+
+ &.unsupported {
+ opacity: 0.5;
}
}
- &:hover {
- background-color: #e9e9e9;
- border-color: #000;
+ dt {
+ font-size: 0.85rem;
+ }
+
+ dd {
+ font-size: 0.75rem;
+ padding-left: calc(1em + 0.4rem);
+ color: #666;
}
}
}
-.platform.no-bridge {
- opacity: 0.15;
- pointer-events: none;
-}
+// ── Legacy styles (kept for any other pages still referencing them) ────────────
-.bridges_implem> :nth-child(even) {
+.bridges_implem > :nth-child(even) {
background-color: #fafafa;
}
@@ -87,23 +285,19 @@
}
.title-row {
- display: flex;
- flex-direction: row;
align-items: center;
justify-content: flex-start;
- gap: .5rem;
- width: auto;
+ gap: 0.5rem;
h3 {
- margin-block: .5rem;
- text-align: left;
+ margin-block: 1rem;
}
}
.recommended-chip {
display: flex;
align-items: center;
- gap: .2rem;
+ gap: 0.2rem;
max-width: fit-content;
font-style: normal;
border-bottom: 1px dotted var(--borders-color);
@@ -113,23 +307,23 @@
.licence {
display: flex;
flex-wrap: wrap;
- gap: .5rem;
- margin-block: .5rem;
+ gap: 0.5rem;
+ margin-block: 0.5rem;
margin-bottom: 1rem;
}
- .privileges>div,
- .licence>div {
+ .privileges > div,
+ .licence > div {
display: flex;
flex-direction: row;
flex-wrap: wrap;
- gap: .5rem;
+ gap: 0.5rem;
div,
a {
background-color: #e9e9e9;
border-radius: 9999px;
- padding-inline: .5rem;
+ padding-inline: 0.5rem;
color: var(--color-text);
}
}
diff --git a/templates/ecosystem/bridge_implementations.html b/templates/ecosystem/bridge_implementations.html
index 0f5d3f8e7..40754839a 100644
--- a/templates/ecosystem/bridge_implementations.html
+++ b/templates/ecosystem/bridge_implementations.html
@@ -1,71 +1,163 @@
{% extends "section.html" %}
+{% import "macros/bridges.html" as bridges %}
+{% import "macros/breadcrumb.html" as breadcrumb %}
{% block content %}
-
-
- {{ section.title }}
- All the {{ section.title }} bridges
-
-
+{% set bridges_data = load_data(path=section.path~"bridges.toml") %}
-
- {% set bridges_data = load_data(path=section.path~"bridges.toml") %}
- {% for bridge in bridges_data.bridges %}
-
-
-
- {% if bridge.featured %}Recommended{% endif %}
-
- {{ bridge.name }}
- {{ bridge.maturity | title }}
-
- {{ bridge.summary | markdown(inline=true) | safe }}
-
- {% if bridge.docs %}
-
- {{ load_data(path="/assets/documentation.svg") | safe }}
- Documentation
-
- {% endif %}
- {% if bridge.room %}
-
- {{ load_data(path="/assets/support.svg") | safe }}
- Support room
-
- {% endif %}
- {% if bridge.repo %}
-
- {{ load_data(path="/assets/repository.svg") | safe }}
- Repository
-
+
+ {{ breadcrumb::breadcrumb(l1_label="Ecosystem", l1_path="/ecosystem/", l2_label="Bridges", l2_path="/ecosystem/bridges/", l3_label=section.title) }}
+
+
+
+
+
+
+ {{ section.title }}
+
-
- Access needed:
-
- {{ section.title }} {{ bridge.privilege.platform }}
- Matrix {{ bridge.privilege.matrix }}
+
+
+
+
+
+ {% for bridge in bridges_data.bridges %}
+
+
+
+
+
+
+ {{ bridge.name }}
+ {{ bridge.maturity | title }}
+
+ {% if bridge.featured %}
+ Recommended
+ {% endif %}
+
+
-
-
- Licence:
-
- {{ bridge.license }}
+
+
+
+ {{ bridge.summary | markdown | safe }}
+
+
+
+ Access needed
+ {{ section.title }} {{ bridge.privilege.platform }}
+ Matrix {{ bridge.privilege.matrix }}
+
+
+
+ {% if bridge.docs %}
+
+ {{ load_data(path="/assets/documentation.svg") | safe }}
+ Documentation
+
+ {% endif %} {% if bridge.room %}
+
+ {{ load_data(path="/assets/support.svg") | safe }}
+ Support room
+
+ {% endif %} {% if bridge.repo %}
+
+ {{ load_data(path="/assets/repository.svg") | safe }}
+ Source Code
+
+ {% endif %}
+
+
+ Something wrong? Report the issue here.
-
-
-
- {% for feature, supported in bridge.supports %}
- -
- {{ feature | replace(from="_", to=" ") | title }}
-
- {% endfor %}
-
- Something wrong? Report the issue here.
+
+ {% if bridge.supports %}
+
+ {% endif %}
+ {% endfor %}
- {% endfor %}
+
-
{% endblock content %}
diff --git a/templates/ecosystem/bridges.html b/templates/ecosystem/bridges.html
index 1f16f8548..522cf6071 100644
--- a/templates/ecosystem/bridges.html
+++ b/templates/ecosystem/bridges.html
@@ -1,9 +1,11 @@
{% extends "section.html" %}
+{% import "macros/breadcrumb.html" as breadcrumb %}
{% block head_extra %}
{% endblock head_extra %}
{% block content %}
+ {{ breadcrumb::breadcrumb(l1_label="Ecosystem", l1_path="/ecosystem/", l2_label="Bridges") }}
{{ section.title }}
{{ section.extra.summary }}
@@ -18,7 +20,7 @@ {{ section.title }}
{% for subsection_path in section.subsections %}
{% set subsection = get_section(path=subsection_path) %}
{% set bridges_data = load_data(path=subsection.path~"bridges.toml") %}
-
diff --git a/templates/macros/bridges.html b/templates/macros/bridges.html
new file mode 100644
index 000000000..f61241ef8
--- /dev/null
+++ b/templates/macros/bridges.html
@@ -0,0 +1,67 @@
+{# Bridge feature macros for the matrix.org website. #}
+
+{# Human-readable label for a bridge feature key #}
+{% macro feature_label(feature_name) %}
+{% if feature_name == "dm" %}Direct messages
+{% elif feature_name == "channels" %}Channels
+{% elif feature_name == "channel" %}Channels
+{% elif feature_name == "groups" %}Groups
+{% elif feature_name == "group_chats" %}Group chats
+{% elif feature_name == "group_chat" %}Group chats
+{% elif feature_name == "formatted_text" %}Formatted text
+{% elif feature_name == "message_media" %}Media messages
+{% elif feature_name == "attachments" %}Attachments
+{% elif feature_name == "replies" %}Replies
+{% elif feature_name == "mentions" %}Mentions
+{% elif feature_name == "threads" %}Threads
+{% elif feature_name == "redactions" %}Redactions
+{% elif feature_name == "editing" %}Message editing
+{% elif feature_name == "reactions" %}Reactions
+{% elif feature_name == "presence" %}Presence
+{% elif feature_name == "typing_notifications" %}Typing notifications
+{% elif feature_name == "read_receipts" %}Read receipts
+{% elif feature_name == "encryption" %}Encryption
+{% elif feature_name == "history" %}History sync
+{% elif feature_name == "history_from_android" %}History from Android
+{% elif feature_name == "mms" %}MMS
+{% elif feature_name == "sms" %}SMS
+{% elif feature_name == "rcs" %}RCS
+{% elif feature_name == "sending_mail" %}Sending mail
+{% elif feature_name == "receiving_mail" %}Receiving mail
+{% elif feature_name == "quotes" %}Quotes
+{% elif feature_name == "communities" %}Communities
+{% elif feature_name == "multi_domain" %}Multi-domain
+{% else %}{{ feature_name | replace(from="_", to=" ") | title }}
+{% endif %}
+{% endmacro %}
+
+{# Short description for a bridge feature key #}
+{% macro feature_description(feature_name) %}
+{% if feature_name == "dm" %}Bridge direct/private messages between users.
+{% elif feature_name == "channels" or feature_name == "channel" %}Bridge public or private channels to Matrix rooms.
+{% elif feature_name == "groups" or feature_name == "group_chats" or feature_name == "group_chat" %}Bridge group conversations to Matrix rooms.
+{% elif feature_name == "formatted_text" %}Rich text formatting such as bold, italic, and code blocks is preserved.
+{% elif feature_name == "message_media" %}Images, video, audio and other file attachments are bridged.
+{% elif feature_name == "attachments" %}File attachments are bridged across platforms.
+{% elif feature_name == "replies" %}Threaded replies are bridged and displayed correctly on both sides.
+{% elif feature_name == "mentions" %}User @mentions are bridged and resolve to the correct users.
+{% elif feature_name == "threads" %}Threaded conversations are kept intact across the bridge.
+{% elif feature_name == "redactions" %}Deleted messages are removed on the other side of the bridge.
+{% elif feature_name == "editing" %}Edited messages are updated on both sides of the bridge.
+{% elif feature_name == "reactions" %}Emoji reactions are synced across the bridge.
+{% elif feature_name == "presence" %}Online/offline presence status is synced across the bridge.
+{% elif feature_name == "typing_notifications" %}Typing indicators are shown while the other party is composing.
+{% elif feature_name == "read_receipts" %}Read receipts are synced so you can see when messages have been read.
+{% elif feature_name == "encryption" %}End-to-end encryption is supported across the bridge.
+{% elif feature_name == "history" %}Past message history is available after connecting the bridge.
+{% elif feature_name == "history_from_android" %}Message history can be imported from an Android device.
+{% elif feature_name == "mms" %}MMS multimedia messages are bridged.
+{% elif feature_name == "sms" %}SMS text messages are bridged.
+{% elif feature_name == "rcs" %}RCS rich communication messages are bridged.
+{% elif feature_name == "sending_mail" %}You can send emails from Matrix.
+{% elif feature_name == "receiving_mail" %}Incoming emails appear as Matrix messages.
+{% elif feature_name == "quotes" %}Quoted messages are preserved when bridging.
+{% elif feature_name == "communities" %}Platform communities or servers are bridged as Matrix spaces.
+{% elif feature_name == "multi_domain" %}Bridging across multiple domains or workspaces is supported.
+{% endif %}
+{% endmacro %}
From 246a318a2f60c6516d5e2284d7cc3ec62fdaca5c Mon Sep 17 00:00:00 2001
From: MTRNord
Date: Wed, 4 Mar 2026 08:48:00 +0100
Subject: [PATCH 04/17] redesign hosting listing and add provider detail pages
Replace the shortcode-based hosting list with a card grid that reuses the
.project-card / .hosting-provider-card pattern. Add a hosting-provider.html
detail template using the shared .ecosystem-detail--single layout (no
features aside). Update the hosting_providers shortcode formatting.
Signed-off-by: MTRNord
---
sass/_hosting.scss | 147 +++++++++++++-------
templates/ecosystem/hosting-provider.html | 66 +++++++++
templates/ecosystem/hosting.html | 56 +++++++-
templates/shortcodes/hosting_providers.html | 18 ++-
4 files changed, 221 insertions(+), 66 deletions(-)
create mode 100644 templates/ecosystem/hosting-provider.html
diff --git a/sass/_hosting.scss b/sass/_hosting.scss
index d5b4d3759..a55584545 100644
--- a/sass/_hosting.scss
+++ b/sass/_hosting.scss
@@ -1,9 +1,8 @@
+// ── Hosting section page ──────────────────────────────────────────────────────
+
.hosting_page {
margin: 0 auto;
- padding-block: 5.625rem 2rem;
- @media (max-width: 767px) {
- padding: 5.625rem 32px 2rem 32px;
- }
+ padding-top: 3rem;
h2 {
text-align: center;
@@ -12,57 +11,97 @@
margin-bottom: 1rem;
}
- .hosting_providers {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
- gap: 3.125rem;
- justify-content: center;
- align-items: stretch;
+ > p {
+ text-align: left;
+ font-weight: 400;
+ font-size: 1.125rem;
+ line-height: 1.6;
+ margin: 0 0 1rem 0;
+ }
+}
+
+// ── Provider listing grid ─────────────────────────────────────────────────────
+// Mirrors the .projects-card-deck pattern used by clients.
+
+.hosting-provider-cards {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, 17.6rem);
+ justify-content: center;
+ gap: 1rem;
+ margin-inline: auto;
+ margin-bottom: 5rem;
+
+ @media (max-width: 767px) {
+ max-width: 17.6rem;
+ }
+}
+
+.hosting-provider-cards > div {
+ height: 100%;
+ width: 100%;
+}
+
+.hosting-provider-card {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 1.5rem;
+ gap: 0.75rem;
+ min-height: 100%;
+
+ border: 1px solid #d2d2d2;
+ border-radius: 16px;
+ color: var(--color-text);
+ text-decoration: none;
+ transition: border-color 0.2s ease;
+
+ &:hover {
+ border-color: #888;
+ }
+
+ .hosting-provider-logo-container {
width: 100%;
+ height: 3.5rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ img {
+ height: 3.5rem;
+ width: auto;
+ max-width: 100%;
+ object-fit: contain;
+ margin: 0;
+ }
+
+ h3 {
+ margin: 0;
+ font-size: 1.3rem;
+ font-weight: 700;
+ text-align: center;
+ line-height: 1.2;
+ }
+
+ .provider-since {
+ margin: 0;
+ font-size: 0.85rem;
+ color: #666;
+ line-height: 1;
+ }
+
+ .provider-audience-tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.3rem;
+ justify-content: center;
+ }
- .provider_card {
- display: flex;
- flex-direction: column;
- justify-content: space-between;
-
- padding: 32px;
- width: 100%;
- margin: 0 auto;
-
- border: 1px solid #d2d2d2;
- border-radius: 16px;
-
- .details {
- text-align: center;
-
- .provider_logo_container {
- height: 10rem;
- }
-
- h3 {
- height: 5rem;
- margin: 0;
- text-align: center;
-
- font-style: normal;
- font-weight: 700;
- font-size: 2rem;
- line-height: 120%;
- }
-
- .operating_since {
- height: 1.5rem;
- font-size: 0.9rem;
- }
- }
-
- .call-to-action {
- background-color: #000;
- color: #fff;
- border-radius: 1000px;
- padding: 0.4rem 2rem;
- align-self: center;
- }
- }
+ .open-project {
+ margin-top: auto;
+ color: var(--color-text-light);
+ font-size: 0.9rem;
+ line-height: 1;
}
}
diff --git a/templates/ecosystem/hosting-provider.html b/templates/ecosystem/hosting-provider.html
new file mode 100644
index 000000000..3923a7a2d
--- /dev/null
+++ b/templates/ecosystem/hosting-provider.html
@@ -0,0 +1,66 @@
+{% extends "page.html" %}
+{% import "macros/breadcrumb.html" as breadcrumb %}
+{% block head_extra %}
+
+{% endblock head_extra %}
+{% block content %}
+
+ {{ breadcrumb::breadcrumb(l1_label="Ecosystem", l1_path="/ecosystem/", l2_label="Hosting", l2_path="/ecosystem/hosting/", l3_label=page.title) }}
+
+
+ {% if page.extra.image %}
+
+
+
+ {% endif %}
+
+ {{ page.title }}
+
+
+
+
+
+
+ {{ page.content | safe }}
+
+
+
+ {% if page.extra.website %}
+
+ Visit {{ page.title }}
+
+ {% endif %}
+
+
+
+
+{% endblock content %}
diff --git a/templates/ecosystem/hosting.html b/templates/ecosystem/hosting.html
index f89f6245d..693511b4e 100644
--- a/templates/ecosystem/hosting.html
+++ b/templates/ecosystem/hosting.html
@@ -1,18 +1,60 @@
-{% extends "page.html" %}
+{% extends "section.html" %}
+{% import "macros/breadcrumb.html" as breadcrumb %}
{% block head_extra %}
-
+
+
{% endblock head_extra %}
{% block content %}
+ {{ breadcrumb::breadcrumb(l1_label="Ecosystem", l1_path="/ecosystem/", l2_label="Hosting") }}
- {{ page.title }}
- {{ page.extra.summary }}
+ {{ section.title }}
+ {{ section.extra.summary }}
-
- {{ page.content | safe }}
-
+ {{ section.content | safe }}
+ {% if section.pages %}
+
+ {% for provider in section.pages %}
+
+ {% endfor %}
+
+ {% endif %}
{% endblock content %}
diff --git a/templates/shortcodes/hosting_providers.html b/templates/shortcodes/hosting_providers.html
index b2e3f5f22..1cd966927 100644
--- a/templates/shortcodes/hosting_providers.html
+++ b/templates/shortcodes/hosting_providers.html
@@ -1,16 +1,24 @@
-{% set providers_data = load_data(path="content/ecosystem/hosting/providers.toml") %}
+{% set providers_data =
+load_data(path="content/ecosystem/hosting/providers.toml") %}
{% for provider in providers_data.providers %}
-
+
{{ provider.name }}
-
+
- {{ provider.description | markdown(inline=true) | safe }}
- Try {{ provider.name }}
+ {{ provider.description | markdown | safe }}
+ Try {{ provider.name }}
{% endfor %}
From 5c499e4649fe92dc3530c5f15ff338a578af56f2 Mon Sep 17 00:00:00 2001
From: MTRNord
Date: Wed, 4 Mar 2026 08:48:00 +0100
Subject: [PATCH 05/17] update servers, distributions, SDKs and integrations
pages
Minor template updates across the remaining ecosystem listing pages:
consistent page-header hero markup, SDK pill classes now include licence
and language slugs for colour coding matching the client card pill scheme.
Signed-off-by: MTRNord
---
templates/ecosystem/distributions.html | 4 +++-
templates/ecosystem/integrations.html | 5 ++---
templates/ecosystem/sdks.html | 7 +++----
templates/ecosystem/servers.html | 4 +++-
templates/macros/sdks.html | 8 ++++----
5 files changed, 15 insertions(+), 13 deletions(-)
diff --git a/templates/ecosystem/distributions.html b/templates/ecosystem/distributions.html
index f71ebab8e..42e0d847d 100644
--- a/templates/ecosystem/distributions.html
+++ b/templates/ecosystem/distributions.html
@@ -1,13 +1,15 @@
{% extends "page.html" %}
+{% import "macros/breadcrumb.html" as breadcrumb %}
{% block head_extra %}
-{% endblock head_extra%}
+{% endblock head_extra %}
{% block content %}
+ {{ breadcrumb::breadcrumb(l1_label="Ecosystem", l1_path="/ecosystem/", l2_label="Distributions") }}
{{ page.title }}
{{ page.extra.summary }}
diff --git a/templates/ecosystem/integrations.html b/templates/ecosystem/integrations.html
index 48c48d3f6..8bc4eb755 100644
--- a/templates/ecosystem/integrations.html
+++ b/templates/ecosystem/integrations.html
@@ -1,15 +1,14 @@
{% extends "page.html" %}
+{% import "macros/breadcrumb.html" as breadcrumb %}
{% block content %}
+ {{ breadcrumb::breadcrumb(l1_label="Ecosystem", l1_path="/ecosystem/", l2_label="Integrations") }}
{{ page.title }}
{{ page.extra.summary }}
-
-
{{ page.content | safe }}
-
{% endblock content %}
diff --git a/templates/ecosystem/sdks.html b/templates/ecosystem/sdks.html
index da51ec3c0..d15422958 100644
--- a/templates/ecosystem/sdks.html
+++ b/templates/ecosystem/sdks.html
@@ -1,4 +1,5 @@
{% extends "page.html" %}
+{% import "macros/breadcrumb.html" as breadcrumb %}
{% block head_extra %}
-{% endblock head_extra%}
+{% endblock head_extra %}
{% block content %}
+ {{ breadcrumb::breadcrumb(l1_label="Ecosystem", l1_path="/ecosystem/", l2_label="SDKs") }}
{{ page.title }}
{{ page.extra.summary }}
-
-
{{ page.content | safe }}
-
{% endblock content %}
diff --git a/templates/ecosystem/servers.html b/templates/ecosystem/servers.html
index 5e3a8f045..7345bac52 100644
--- a/templates/ecosystem/servers.html
+++ b/templates/ecosystem/servers.html
@@ -1,13 +1,15 @@
{% extends "page.html" %}
+{% import "macros/breadcrumb.html" as breadcrumb %}
{% block head_extra %}
-{% endblock head_extra%}
+{% endblock head_extra %}
{% block content %}
+ {{ breadcrumb::breadcrumb(l1_label="Ecosystem", l1_path="/ecosystem/", l2_label="Servers") }}
{{ page.title }}
{{ page.extra.summary }}
diff --git a/templates/macros/sdks.html b/templates/macros/sdks.html
index fcace2a83..991aa81b9 100644
--- a/templates/macros/sdks.html
+++ b/templates/macros/sdks.html
@@ -94,10 +94,10 @@
{{ sdk.name }}
{{ sdk.maturity }}
- {% if sdk.maintainer %}{{ sdk.maintainer }}{% endif %}
-
@@ -141,9 +151,6 @@
{% endif %}
-
- {{ clients::install_links(packages=page.extra.packages) }}
-
Something wrong? Report the issue here.
@@ -152,37 +159,20 @@ Features
{% for category, features in page.extra.features %}
- {{ clients::category_label(category_name=category) }}
-
+ {{ clients::category_label(category_name=category) }}
+
{% for feature, supported in features %}
-
-
-
- {{ clients::feature_label(feature_name=feature) | trim }}
-
-
-
-
-
- {{ clients::feature_description(feature_name=feature) | trim }}
-
-
+
+ -
+
+ {{ clients::feature_label(feature_name=feature) | trim }}
+
+ - {{ clients::feature_description(feature_name=feature) | trim }}
+
{% endfor %}
-
+
{% endfor %}
diff --git a/templates/ecosystem/clients.html b/templates/ecosystem/clients.html
index e48b50c09..959ab6b15 100644
--- a/templates/ecosystem/clients.html
+++ b/templates/ecosystem/clients.html
@@ -44,81 +44,9 @@ Featured clients
mature ones you can safely use.
-
+
{% for client in featured_clients %}
-
-
-
-
-
-
- {{ client.title }}
- {{ client.extra.maturity }}
- #
-
- {% if client.extra.good_for %}
- Good for: {{ client.extra.good_for }}
- {% else %}
- {{ client.content | markdown(inline=true) }}
- {% endif %}
-
-
- {{ clients::platforms(packages=client.extra.packages) }}
-
-
-
-
-
- {% set lightbox_id = "eco-lightbox-" ~ client.slug %}
- {% set screenshots = client.extra.screenshots | default(value=[]) %}
- {% if screenshots | length > 0 %}
- {% set multi = screenshots | length > 1 %}
-
- {% for shot in screenshots %}
- {% set shot_lightbox_id = lightbox_id ~ "-" ~ loop.index %}
-
-
-
- {% endfor %}
-
- {% for shot in screenshots %}
- {% set shot_lightbox_id = lightbox_id ~ "-" ~ loop.index %}
-
- {% endfor %}
- {% endif %}
-
-
- {{ client.content | markdown(inline=true) }}
-
-
- {% set feat = client.extra.features %}
- {% if feat.e2ee %}E2EE{% endif %}
- {% if feat.spaces %}Spaces{% endif %}
- {% if feat.threads %}Threads{% endif %}
- {% if feat.voip_1to1 %}Voice & video calls{% endif %}
- {% if feat.voip_jitsi %}Group calls (Jitsi){% endif %}
- {% if feat.multi_account %}Multi-account{% endif %}
- {% if feat.oauth %}OAuth 2.0{% endif %}
- {% if feat.sso %}SSO{% endif %}
-
-
-
- {{ clients::install_links(packages=client.extra.packages) }}
-
-
- Full details →
-
-
-
+ {{ clients::featured_client_card(client=client, id_prefix="eco") }}
{% endfor %}
@@ -252,11 +180,11 @@ All known clients
{{ client.title }}
{{ client.extra.maturity }}
-
- {% if server.author %}{{ server.author }}{% endif %}
-
- Something wrong? Report the issue here.
+ Something wrong? Report the issue here.
{% endblock content %}
diff --git a/templates/ecosystem/hosting.html b/templates/ecosystem/hosting.html
index 693511b4e..ab43426af 100644
--- a/templates/ecosystem/hosting.html
+++ b/templates/ecosystem/hosting.html
@@ -56,5 +56,6 @@ {{ provider.title }}
{% endfor %}
{% endif %}
+ Something missing? Report the issue here.
{% endblock content %}
From 14db4ea9e4d230ee46eec3e337cf3c93ee5bf4b2 Mon Sep 17 00:00:00 2001
From: MTRNord
Date: Thu, 9 Jul 2026 20:16:41 +0200
Subject: [PATCH 14/17] fix scroll behaviour
Signed-off-by: MTRNord
---
templates/ecosystem/clients.html | 2 +-
templates/ecosystem/hosting.html | 6 ------
templates/try-matrix.html | 2 +-
3 files changed, 2 insertions(+), 8 deletions(-)
diff --git a/templates/ecosystem/clients.html b/templates/ecosystem/clients.html
index 48350e94a..6a14db1f3 100644
--- a/templates/ecosystem/clients.html
+++ b/templates/ecosystem/clients.html
@@ -8,7 +8,7 @@
{% endblock head_extra%}
{% block content %}
diff --git a/templates/ecosystem/hosting.html b/templates/ecosystem/hosting.html
index ab43426af..6ada6ec86 100644
--- a/templates/ecosystem/hosting.html
+++ b/templates/ecosystem/hosting.html
@@ -2,12 +2,6 @@
{% import "macros/breadcrumb.html" as breadcrumb %}
{% block head_extra %}
-
{% endblock head_extra %}
{% block content %}
diff --git a/templates/try-matrix.html b/templates/try-matrix.html
index c80d0b2b4..701ed68f6 100644
--- a/templates/try-matrix.html
+++ b/templates/try-matrix.html
@@ -2,7 +2,7 @@
{% import "macros/clients.html" as clients %}
{% block head_extra %}
{% endblock head_extra %}
{% block content %}
From 702b9e06c17384b5e0573c0b352a5a1fc32647a8 Mon Sep 17 00:00:00 2001
From: MTRNord
Date: Thu, 9 Jul 2026 20:20:05 +0200
Subject: [PATCH 15/17] add header before the hosting provider grid
Signed-off-by: MTRNord
---
templates/ecosystem/hosting.html | 1 +
1 file changed, 1 insertion(+)
diff --git a/templates/ecosystem/hosting.html b/templates/ecosystem/hosting.html
index 6ada6ec86..d9d6cea85 100644
--- a/templates/ecosystem/hosting.html
+++ b/templates/ecosystem/hosting.html
@@ -15,6 +15,7 @@ {{ section.title }}
{{ section.content | safe }}
{% if section.pages %}
+ Known providers
{% for provider in section.pages %}
From 4c6932759c01e99fab5413528e9c9a5c10343876 Mon Sep 17 00:00:00 2001
From: MTRNord
Date: Thu, 9 Jul 2026 20:21:15 +0200
Subject: [PATCH 16/17] internlink hosting with docs
Signed-off-by: MTRNord
---
content/ecosystem/hosting/_index.md | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/content/ecosystem/hosting/_index.md b/content/ecosystem/hosting/_index.md
index 1b51d2364..16326a732 100644
--- a/content/ecosystem/hosting/_index.md
+++ b/content/ecosystem/hosting/_index.md
@@ -11,8 +11,8 @@ Everyone can host their Matrix server, but not everybody wants to. These are the
## Why not matrix.org?
-Matrix could be described as what happens when email, instant messaging and
-encryption meet. To be able to use Matrix, you need a Matrix account which is
+[Matrix](/docs/matrix-concepts/elements-of-matrix/) could be described as what happens when email, instant messaging and
+encryption meet. To be able to use Matrix, you need a [Matrix account](/docs/chat_basics/matrix-for-im/#creating-a-matrix-account) which is
provided by... a provider! The Matrix accounts identifiers look like
`@username:example.com`. People on different providers can talk to one another,
in the same way you can send emails to someone using another email provider.
@@ -26,8 +26,9 @@ or WhatsApp.
**Organisations and community managers** will be interested in providers for two
main reasons: they can proudly display their identity with their own domain
-in username and rooms identifiers, and their administrators remain in control
-of the data of their organisation.
+in username and rooms identifiers when [getting started with a community](/docs/communities/getting-started/),
+and their administrators remain in control of the data of their organisation
+through [room administration](/docs/communities/administration/) tools.
The following providers have been brought to the attention of The Matrix.org
Foundation. Please note that The Matrix.org Foundation doesn't endorse them,
From 761c1b7c7758df9f059ea03d5638a0cb151e2404 Mon Sep 17 00:00:00 2001
From: MTRNord
Date: Thu, 9 Jul 2026 20:24:14 +0200
Subject: [PATCH 17/17] remove redundant spaces
Signed-off-by: MTRNord
---
templates/macros/clients.html | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/templates/macros/clients.html b/templates/macros/clients.html
index c2aab51c4..07ebbc98d 100644
--- a/templates/macros/clients.html
+++ b/templates/macros/clients.html
@@ -90,13 +90,13 @@
{# CSS filter classes for the all-clients card deck #}
{% macro classes(client) %}
-{% if client.packages.apple_app_store.app_id and client.packages.apple_app_store.org %}platform-ios {% endif %}
-{% if client.packages.google_play_store.app_id %}platform-android {% endif %}
-{% if client.packages.flathub.app_id %}platform-flathub {% endif %}
-{% if client.packages.other_linux_link %}platform-linux {% endif %}
-{% if client.packages.webapp %}platform-web {% endif %}
-{% if client.packages.windows_installer %}platform-windows {% endif %}
-{% if client.packages.macos_installer %}platform-macos {% endif %}
+{% if client.packages.apple_app_store.app_id and client.packages.apple_app_store.org %}platform-ios{% endif %}
+{% if client.packages.google_play_store.app_id %}platform-android{% endif %}
+{% if client.packages.flathub.app_id %}platform-flathub{% endif %}
+{% if client.packages.other_linux_link %}platform-linux{% endif %}
+{% if client.packages.webapp %}platform-web{% endif %}
+{% if client.packages.windows_installer %}platform-windows{% endif %}
+{% if client.packages.macos_installer %}platform-macos{% endif %}
maturity-{{ client.maturity | lower }}
licence-{{ client.licence | slugify }}
{{ section.title }}
-All the {{ section.title }} bridges
-{{ bridge.name }}
-{{ bridge.summary | markdown(inline=true) | safe }}
-{{ section.title }}
+ -+ {{ bridge.name }} + {{ bridge.maturity | title }} +
+ {% if bridge.featured %} + Recommended + {% endif %} +Something wrong? Report the issue here.
-
- {% for feature, supported in bridge.supports %}
-
- - {{ feature | replace(from="_", to=" ") | title }} - - {% endfor %} -
Something wrong? Report the issue here.
+ + {% if bridge.supports %} + + {% endif %}{{ section.title }}
{{ section.extra.summary }}
@@ -18,7 +20,7 @@{{ section.title }}
{% for subsection_path in section.subsections %} {% set subsection = get_section(path=subsection_path) %} {% set bridges_data = load_data(path=subsection.path~"bridges.toml") %} -{{ page.title }}
+ +{{ page.title }}
-{{ page.extra.summary }}
+{{ section.title }}
+{{ section.extra.summary }}
{{ provider.name }}
- +{{ provider.description | markdown(inline=true) | safe }}
- Try {{ provider.name }} +{{ provider.description | markdown | safe }}
+ Try {{ provider.name }}{{ page.title }}
{{ page.extra.summary }}
diff --git a/templates/ecosystem/integrations.html b/templates/ecosystem/integrations.html index 48c48d3f6..8bc4eb755 100644 --- a/templates/ecosystem/integrations.html +++ b/templates/ecosystem/integrations.html @@ -1,15 +1,14 @@ {% extends "page.html" %} +{% import "macros/breadcrumb.html" as breadcrumb %} {% block content %}{{ page.title }}
{{ page.extra.summary }}
{{ page.title }}
{{ page.extra.summary }}
{{ page.title }}
{{ page.extra.summary }}
diff --git a/templates/macros/sdks.html b/templates/macros/sdks.html index fcace2a83..991aa81b9 100644 --- a/templates/macros/sdks.html +++ b/templates/macros/sdks.html @@ -94,10 +94,10 @@{{ sdk.name }}
{% endif %}
Something wrong? Report the issue here.
Features
{{ clients::category_label(category_name=category) }}
--
+
-
-
- {{ clients::feature_label(feature_name=feature) | trim }}
-
-
- --
- {{ clients::feature_description(feature_name=feature) | trim }} --
+ - + + {{ clients::feature_label(feature_name=feature) | trim }} + +
- {{ clients::feature_description(feature_name=feature) | trim }} +
{{ clients::category_label(category_name=category) }}
+-
{% for feature, supported in features %}
-
Featured clients
mature ones you can safely use. -
-
-
-
-
- {{ client.title }}
- {{ client.extra.maturity }}
- #
-
- {% if client.extra.good_for %}
- Good for: {{ client.extra.good_for }}
- {% else %}
- {{ client.content | markdown(inline=true) }}
- {% endif %}
-
-
- {{ clients::platforms(packages=client.extra.packages) }}
-
-
-
-
- {{ client.content | markdown(inline=true) }}
- -All known clients
{{ client.title }}
Something wrong? Report the issue here.
Something wrong? Report the issue here.
{% endblock content %} diff --git a/templates/ecosystem/hosting.html b/templates/ecosystem/hosting.html index 693511b4e..ab43426af 100644 --- a/templates/ecosystem/hosting.html +++ b/templates/ecosystem/hosting.html @@ -56,5 +56,6 @@{{ provider.title }}
{% endfor %}Something missing? Report the issue here.