Files
blap/apps/web/res/css/yap/_yap.pcss
T
enki 5b9d457704 Blap reskin: social UI, fixes, and update/release pipeline
Fork of element-web v1.12.22 reskinned as "Blap", a Discord-style social
Matrix client. Visual fork only: calls stay on the Element Call widget.

UI / reskin:
- yap CSS overlay (_yap.pcss) wired into the dark theme @layer; fonts, surfaces
- spaces rail icon-only with the collapse mechanism removed
- YapRoomList with manual drag-and-drop ordering (works in Home; drop-to-bottom)
- Discord-style inline message timestamps next to the sender name
- image lightbox toolbar visibility; themed context menus + toasts
- bridges panel + call facepile
- login screen rebuilt to the Blap design (centered card, accent buttons)
- accent (purple) primary buttons app-wide via Compound token overrides

Desktop / distribution:
- updater.ts switched Squirrel -> electron-updater (NSIS); Codeberg generic feed
- scripts/release-codeberg.mjs uploads artifacts to the Codeberg release
- Blap build variant (apps/desktop/blap/build.json, appId lol.utn.blap)

Calls (visual-only via widget API):
- "join with camera off" device setting + Voice settings toggle

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 21:37:49 -07:00

1366 lines
46 KiB
Plaintext

/*
* Yap reskin — base token + foundation overlay.
* Loaded as the top-priority `yap` @layer from the dark-custom theme entry,
* so these win over upstream Element/Compound styles.
*
* Scope: global tokens, fonts, base surfaces, scrollbars.
* Per-surface restyles (rail, room list, etc.) live in their own files and
* consume the --yap-* tokens defined here.
*/
@layer yap {
:root {
/* ---- Typography ---- */
--yap-font-sans: "Hanken Grotesk", "Inter", system-ui, sans-serif;
--yap-font-mono: "JetBrains Mono", "Courier New", monospace;
/* Override both legacy + Compound font tokens */
--font-family: var(--yap-font-sans);
--font-family-monospace: var(--yap-font-mono);
--cpd-font-family-sans: var(--yap-font-sans);
--cpd-font-family-mono: var(--yap-font-mono);
/* ---- Surfaces (dark only) ---- */
--yap-surface-titlebar: #101017;
--yap-surface-rail: #101017;
--yap-surface-roomlist: #1a1922;
--yap-surface-main: #201e29;
--yap-surface-userpanel: #131219;
--yap-surface-composer: #2a2735;
--yap-surface-card: rgba(255, 255, 255, 0.025);
--yap-surface-card-hover: rgba(255, 255, 255, 0.06);
--yap-hairline: rgba(255, 255, 255, 0.06);
--yap-divider-heavy: rgba(0, 0, 0, 0.25);
/* ---- Text ---- */
--yap-text-primary: #ece9f5;
--yap-text-secondary: #b8b4c6;
--yap-text-muted: #8b8799;
--yap-text-faint: #6b6878;
/* ---- Accent (themeable) ---- */
--yap-accent: #7c66e0;
--yap-accent-soft: color-mix(in srgb, var(--yap-accent) 16%, transparent);
/* ---- Status / semantic ---- */
--yap-success: #46c08d;
--yap-idle: #e8b04b;
--yap-danger: #e5697a;
/* ---- Network (bridge) brand colors ---- */
--yap-net-telegram: #2aabee;
--yap-net-discord: #5b6ee8;
--yap-net-meta: #0a7cff;
/* ---- Squircle radius scale (consumed per-surface) ---- */
--yap-radius-space: 15px;
--yap-radius-avatar-chat: 14px;
--yap-radius-avatar-member: 11px;
--yap-radius-avatar-user: 12px;
--yap-radius-avatar-dm: 10px;
--yap-radius-card: 16px;
--yap-radius-row: 8px;
/* ---- Accent remap: override Compound's green accent → Yap purple ----
(Was in config.json custom theme; moved here so it loads on the
built-in Dark theme instead of the fragile custom-theme path.) */
--cpd-color-text-action-accent: var(--yap-accent) !important;
--cpd-color-icon-accent-primary: var(--yap-accent) !important;
--cpd-color-icon-accent-tertiary: #9580ea !important;
/* Compound's token defs are unlayered, so these MUST be !important to win
over them — otherwise primary buttons (Sign in, Continue, Got it, the
E2E verify dialogs) render Element-default white instead of accent.
Note the canonical name is `-hovered`, not `-hover`. */
--cpd-color-bg-action-primary-rest: var(--yap-accent) !important;
--cpd-color-bg-action-primary-hovered: #8b78e8 !important;
--cpd-color-bg-action-primary-pressed: #6e58d0 !important;
--cpd-color-border-interactive-primary: var(--yap-accent) !important;
/* Remap the WHOLE Compound green scale → purple. Element's brand accent is
green and legacy `$accent-*` resolve to these tokens, so any leftover
default (new-message bar, jump-to-bottom badge, read marker, links, etc.)
inherits Blap purple from here in one shot. Dark ramp: low = subtle bg,
~900 = solid accent, high = light text. */
--cpd-color-green-100: #18151f;
--cpd-color-green-200: #1e1a2b;
--cpd-color-green-300: #271f3d;
--cpd-color-green-400: #322852;
--cpd-color-green-500: #3f3469;
--cpd-color-green-600: #4f4185;
--cpd-color-green-700: #6253a8;
--cpd-color-green-800: #6f5ecb;
--cpd-color-green-900: #7c66e0;
--cpd-color-green-1000: #8b78e8;
--cpd-color-green-1100: #9d8bec;
--cpd-color-green-1200: #b3a5f1;
--cpd-color-green-1300: #ccc1f6;
--cpd-color-green-1400: #e2dbfb;
/* Squircle ALL Compound avatars (people + rooms) in one shot:
the Avatar component renders `border-radius: var(--cpd-avatar-radius)`.
!important to beat the unlayered default. */
--cpd-avatar-radius: 30% !important;
/* ---- Background canvas tokens (the MAIN pane). Without these the
main content area falls back to light. Force the Yap surfaces. ---- */
--cpd-color-bg-canvas-default: var(--yap-surface-main) !important;
--cpd-color-bg-canvas-disabled: #1a1922 !important;
--cpd-color-bg-subtle-primary: #26242f !important;
--cpd-color-bg-subtle-secondary: var(--yap-surface-roomlist) !important;
}
body {
font-family: var(--yap-font-sans);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* Mono everywhere timestamps / ids / code render */
.mx_MessageTimestamp,
code,
kbd,
pre {
font-family: var(--yap-font-mono);
}
/* ---- Scrollbars (README spec) ---- */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.07);
border-radius: 8px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.14);
}
/* ============================================================
Per-surface restyle
(yap is the top @layer, so these win over Element's app-web
layer without specificity fights; !important only where
Element sets values inline.)
============================================================ */
/* ---- Three distinct pane tiers ---- */
.mx_SpacePanel {
background-color: var(--yap-surface-rail);
}
.mx_LeftPanel,
.mx_RoomListPanel {
background-color: var(--yap-surface-roomlist);
}
/* main column (mirrors Element's own canvas selector) */
.mx_MatrixChat
> :not(.mx_LeftPanel):not(.mx_SpacePanel):not(.mx_ResizeHandle):not(.mx_LeftPanel_outerWrapper) {
background-color: var(--yap-surface-main);
}
.mx_RoomView,
.mx_HomePage {
background-color: var(--yap-surface-main);
}
/* ---- Spaces rail ---- */
.mx_SpacePanel,
.mx_SpacePanel.collapsed {
width: 74px;
padding-block: 10px;
}
.mx_SpacePanel {
/* chunkier top-level space icons (mockup ~46px) */
--height-topLevel: 44px;
--gutterSize: 0px;
}
/* center the icons in the rail. The selectionWrapper is flex:1 / calc(100%-32px)
(reserving room for the removed label+chevron), so the avatar sits left.
Shrink it to the avatar, then center the button content. */
.mx_SpacePanel .mx_SpaceItem,
.mx_SpacePanel .mx_SpaceItem > .mx_SpaceButton,
.mx_SpacePanel .mx_SpaceItem.hasSubSpaces > .mx_SpaceButton {
margin-left: 0 !important;
padding-left: 0 !important;
}
.mx_SpacePanel .mx_SpaceButton {
justify-content: center !important;
padding: 4px !important;
}
.mx_SpacePanel .mx_SpaceButton_selectionWrapper {
flex: 0 0 auto !important;
width: auto !important;
margin: 0 !important;
}
/* force centering at every level of the rail (panel > list > item) */
.mx_SpacePanel,
.mx_SpacePanel > .mx_AutoHideScrollbar,
.mx_SpacePanel .mx_SpaceTreeLevel,
.mx_SpacePanel ul,
.mx_SpacePanel .mx_SpaceItem {
align-items: center !important;
}
.mx_SpacePanel .mx_SpaceItem {
width: 100% !important;
padding-inline: 0 !important;
margin-inline: 0 !important;
justify-content: center !important;
}
/* squircle space icons (Element sets avatar radius inline) */
.mx_SpacePanel .mx_BaseAvatar {
border-radius: var(--yap-radius-space) !important;
}
/* active space = accent ring, no Discord pill */
.mx_SpacePanel .mx_SpaceButton_active {
--activeBorder-color: var(--yap-accent);
--activeBackground-color: transparent;
}
/* Clean icon-only rail: kill overflowing name labels, the inline subspace
dropdown chevrons (just open the space in the main view), and the
redundant Settings button (now in the bottom user panel). */
.mx_SpacePanel .mx_SpaceButton_name,
.mx_SpacePanel .mx_SpaceButton_toggleCollapse,
.mx_SpacePanel_toggleCollapse,
.mx_QuickSettingsButton,
.mx_SpacePanel .mx_ThreadsActivityCentreButton,
.mx_SpacePanel .mx_UserMenu {
display: none !important;
}
/* Threads moved into the bottom user panel — normalise it to a centered
32px box matching the settings button. */
.mx_YapUserPanel_threads {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
}
.mx_YapUserPanel_threads .mx_ThreadsActivityCentreButton_Text,
.mx_YapUserPanel_threads .mx_ThreadsActivityCentreButton_Icon + * {
display: none !important;
}
.mx_YapUserPanel_threads .mx_ThreadsActivityCentreButton,
.mx_YapUserPanel_threads button {
width: 32px !important;
min-width: 32px !important;
height: 32px !important;
padding: 0 !important;
margin: 0 !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
color: var(--yap-text-muted) !important;
}
/* center the icons now that labels are gone */
.mx_SpacePanel .mx_SpaceButton {
justify-content: center;
}
/* ---- Space "..." options context menu (mx_SpacePanel_contextMenu) ----
Opened from the narrow rail it rendered too narrow, ellipsizing labels
("Home" -> "Ho") and clipping the option tooltips, and it sat on the
default surface. Give it a Blap surface + enough room so nothing clips. */
.mx_ContextualMenu.mx_SpacePanel_contextMenu,
.mx_SpacePanel_contextMenu {
min-width: 240px;
max-width: 320px;
background-color: var(--yap-surface-composer, var(--cpd-color-bg-canvas-default));
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 14px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.45);
overflow: visible; /* let option tooltips escape the menu box */
}
.mx_SpacePanel_contextMenu .mx_IconizedContextMenu_optionList,
.mx_SpacePanel_contextMenu .mx_IconizedContextMenu_optionList > * {
overflow: visible;
}
/* never ellipsize the option labels — there's room for them now */
.mx_SpacePanel_contextMenu .mx_IconizedContextMenu_label {
overflow: visible;
text-overflow: clip;
white-space: nowrap;
}
.mx_SpacePanel_contextMenu_header {
color: var(--yap-text-primary, inherit);
}
/* ---- Image lightbox: make the top toolbar + tools clearly visible ----
The panel was transparent over the dark backdrop, so the zoom/rotate/
download/close tools nearly vanished. Give it a translucent bar and
brighten the icons with a hover affordance. */
.mx_ImageView_panel {
background: rgba(20, 18, 26, 0.82);
backdrop-filter: blur(8px);
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.mx_ImageView_button svg {
color: var(--yap-text-primary) !important;
}
.mx_ImageView_button:hover {
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
}
.mx_ImageView_info_sender,
.mx_ImageView_title {
color: var(--yap-text-primary);
}
/* ---- Toasts (incl. the "New update" ping) → Blap surface ----
Was Element-default; match the app surface + rounding. The primary
"Update" button picks up the accent fix automatically. */
.mx_Toast_toast {
background-color: var(--yap-surface-composer) !important;
border: 1px solid rgba(255, 255, 255, 0.07);
border-radius: 14px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
}
.mx_Toast_title h2 {
color: var(--yap-text-primary);
}
.mx_Toast_description {
color: var(--yap-text-secondary);
}
/* ---- Room list: the LIVE list is shared-components mx_RoomListItemView,
NOT the legacy mx_RoomTile. Target the real class. ---- */
.mx_RoomListItemView .mx_BaseAvatar {
border-radius: var(--yap-radius-avatar-dm) !important;
}
/* ---- Yap channel-row room list (replaces Element's filter-list) ---- */
.mx_YapRoomList {
display: flex;
flex-direction: column;
gap: 1px;
padding: 6px;
overflow-y: auto;
flex: 1 1 0;
min-height: 0;
}
.mx_YapRoomRow {
display: flex;
align-items: center;
gap: 8px;
height: 34px;
padding: 0 8px;
border-radius: var(--yap-radius-row);
color: var(--yap-text-muted);
cursor: pointer;
font-size: 15px;
font-weight: 500;
user-select: none;
}
.mx_YapRoomRow:hover {
background-color: rgba(255, 255, 255, 0.04);
color: var(--yap-text-secondary);
}
.mx_YapRoomRow_unread {
color: var(--yap-text-primary);
}
.mx_YapRoomRow_unread .mx_YapRoomRow_name {
font-weight: 600;
color: var(--yap-text-primary);
}
.mx_YapRoomRow_active,
.mx_YapRoomRow_active:hover {
background-color: var(--yap-accent-soft);
box-shadow: inset 3px 0 0 var(--yap-accent);
color: var(--yap-text-primary);
}
.mx_YapRoomRow_active .mx_YapRoomRow_name {
color: var(--yap-text-primary);
font-weight: 600;
}
.mx_YapRoomRow_glyph {
font-size: 20px;
color: var(--yap-text-faint);
flex-shrink: 0;
width: 20px;
text-align: center;
}
.mx_YapRoomRow_glyphSvg {
flex-shrink: 0;
color: var(--yap-text-faint);
}
.mx_YapRoomRow_name {
flex: 1 1 0;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.mx_YapRoomRow_dm .mx_BaseAvatar {
flex-shrink: 0;
}
.mx_YapRoomRow_badge {
flex-shrink: 0;
min-width: 18px;
height: 18px;
padding: 0 5px;
border-radius: 9px;
background-color: var(--yap-text-faint);
color: var(--yap-surface-rail);
font-size: 11px;
font-weight: 700;
font-variant-numeric: tabular-nums;
display: flex;
align-items: center;
justify-content: center;
}
.mx_YapRoomRow_badge_mention {
background-color: var(--yap-danger);
color: #fff;
}
/* bridge-platform badge (which network a DM is from) */
.mx_YapPlatformBadge {
flex-shrink: 0;
padding: 1px 5px;
border-radius: 6px;
font-size: 9.5px;
font-weight: 800;
letter-spacing: 0.03em;
line-height: 1.5;
color: #fff;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.25);
}
/* invite pill */
.mx_YapRoomRow_inviteBadge {
margin-left: auto;
flex-shrink: 0;
padding: 1px 7px;
border-radius: 8px;
background-color: var(--yap-accent);
color: #fff;
font-size: 10.5px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.04em;
}
/* draggable room rows */
.mx_YapRoomRow[draggable="true"] {
cursor: pointer;
}
.mx_YapRoomRow[draggable="true"]:active {
cursor: grabbing;
}
/* drop-line indicator: shows where the dragged room will land */
.mx_YapRoomRow_dropBefore {
position: relative;
}
.mx_YapRoomRow_dropAfter {
position: relative;
}
.mx_YapRoomRow_dropBefore::before,
.mx_YapRoomRow_dropAfter::after {
content: "";
position: absolute;
left: 8px;
right: 8px;
height: 2px;
border-radius: 1px;
background-color: var(--yap-accent);
box-shadow: 0 0 4px var(--yap-accent);
pointer-events: none;
}
.mx_YapRoomRow_dropBefore::before {
top: -2px;
}
.mx_YapRoomRow_dropAfter::after {
bottom: -2px;
}
.mx_YapBrowseRooms {
display: flex;
align-items: center;
gap: 8px;
height: 36px;
margin: 2px 0 4px;
padding: 0 8px;
border-radius: var(--yap-radius-row);
color: var(--yap-accent);
font-size: 14px;
font-weight: 600;
cursor: pointer;
}
.mx_YapBrowseRooms:hover {
background-color: rgba(255, 255, 255, 0.04);
}
/* "N new rooms since you last looked" count badge */
.mx_YapBrowseRooms_count {
margin-left: auto;
flex-shrink: 0;
min-width: 18px;
height: 18px;
padding: 0 5px;
border-radius: 9px;
background-color: var(--yap-accent);
color: #fff;
font-size: 11px;
font-weight: 700;
font-variant-numeric: tabular-nums;
display: flex;
align-items: center;
justify-content: center;
}
.mx_YapRoomList_header {
padding: 14px 10px 4px;
font-size: 11.5px;
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
color: var(--yap-text-faint);
}
/* in-call members shown under a voice channel (who's in before you join) */
.mx_YapVoiceMembers {
display: flex;
flex-direction: column;
gap: 1px;
padding: 2px 0 4px 18px;
}
.mx_YapVoiceMember {
display: flex;
align-items: center;
gap: 8px;
height: 28px;
padding: 0 8px;
border-radius: var(--yap-radius-row);
color: var(--yap-text-secondary);
font-size: 14px;
}
.mx_YapVoiceMember:hover {
background-color: rgba(255, 255, 255, 0.04);
}
.mx_YapVoiceMember_name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* ---- Bottom user panel + voice-connected strip ---- */
.mx_YapUserPanel {
margin-top: auto;
flex-shrink: 0;
background-color: var(--yap-surface-userpanel);
border-top: 1px solid var(--yap-hairline);
}
.mx_YapVoiceConnected {
display: flex;
align-items: center;
gap: 8px;
margin: 8px;
padding: 8px 10px;
border-radius: 10px;
background-color: color-mix(in srgb, var(--yap-success) 7%, transparent);
border: 1px solid color-mix(in srgb, var(--yap-success) 18%, transparent);
cursor: pointer;
}
.mx_YapVoiceConnected_icon {
color: var(--yap-success);
flex-shrink: 0;
}
.mx_YapVoiceConnected_text {
display: flex;
flex-direction: column;
min-width: 0;
}
.mx_YapVoiceConnected_label {
color: var(--yap-success);
font-size: 12.5px;
font-weight: 700;
}
.mx_YapVoiceConnected_room {
color: var(--yap-text-muted);
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.mx_YapUserPanel_row {
display: flex;
align-items: center;
gap: 8px;
height: 56px;
padding: 0 10px;
}
.mx_YapUserPanel_info {
display: flex;
flex-direction: column;
min-width: 0;
flex: 1 1 0;
}
.mx_YapUserPanel_name {
color: var(--yap-text-primary);
font-size: 14px;
font-weight: 700;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.mx_YapUserPanel_id {
color: var(--yap-text-faint);
font-size: 11.5px;
font-family: var(--yap-font-mono);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.mx_YapUserPanel_btn {
flex-shrink: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border: none;
background: transparent;
color: var(--yap-text-muted);
border-radius: 8px;
cursor: pointer;
}
.mx_YapUserPanel_btn:hover {
background-color: rgba(255, 255, 255, 0.06);
color: var(--yap-text-primary);
}
.mx_RoomListItemView {
border-radius: var(--yap-radius-row);
margin-inline: 6px;
}
.mx_RoomListItemView:hover {
background-color: rgba(255, 255, 255, 0.04);
}
.mx_RoomListItemView_selected {
background-color: var(--yap-accent-soft) !important;
box-shadow: inset 3px 0 0 var(--yap-accent);
}
/* legacy list (kept harmless in case it ever renders) */
.mx_RoomTile_avatarContainer .mx_BaseAvatar {
border-radius: var(--yap-radius-avatar-dm) !important;
}
.mx_RoomTile_selected {
background-color: var(--yap-accent-soft);
box-shadow: inset 3px 0 0 var(--yap-accent);
}
/* ============================================================
Room view (header / timeline / composer)
============================================================ */
/* ---- Room header ---- */
.mx_RoomHeader {
height: 50px;
background-color: var(--yap-surface-main);
border-bottom: 1px solid var(--yap-divider-heavy);
}
/* ---- Timeline surface ---- */
.mx_RoomView_messagePanel,
.mx_RoomView_messageListWrapper {
background-color: var(--yap-surface-main);
}
/* squircle chat avatars (Element sets radius inline) */
.mx_EventTile_avatar .mx_BaseAvatar {
border-radius: var(--yap-radius-avatar-chat) !important;
}
/* sender name weight */
.mx_DisambiguatedProfile_displayName {
font-weight: 700;
}
/* image-spoiler upload checkbox */
.mx_UploadConfirmDialog_spoiler {
display: flex;
align-items: center;
gap: 8px;
margin: 14px 0 2px;
color: var(--yap-text-secondary);
font-size: 14px;
cursor: pointer;
}
/* message links in accent */
.mx_EventTile_body a,
.mx_EventTile_content a {
color: var(--yap-accent);
text-decoration: none;
}
.mx_EventTile_body a:hover,
.mx_EventTile_content a:hover {
text-decoration: underline;
}
/* subtle per-message hover */
.mx_EventTile:not([data-layout="bubble"]):hover .mx_EventTile_line {
background-color: rgba(255, 255, 255, 0.02);
border-radius: 6px;
}
/* comfortable message vertical rhythm */
.mx_EventTile {
padding-top: 6px;
}
/* ---- Discord-style sender row: name + timestamp inline ----
In group layout Element renders the timestamp absolutely at the left
of the message line, which lands it under the avatar on the first
message of a group. We render an inline copy next to the sender name
(see EventTile.tsx mx_EventTile_senderRow) and hide the gutter one on
first-in-group tiles. Continuation lines keep the gutter timestamp. */
.mx_EventTile_senderRow {
display: flex;
align-items: baseline;
gap: 7px;
margin-left: 64px; /* $left-gutter */
max-width: calc(100% - 64px);
}
.mx_EventTile_senderRow .mx_DisambiguatedProfile {
margin-left: 0;
min-width: 0;
}
.mx_EventTile_senderRow .mx_MessageTimestamp {
position: static;
text-align: left;
opacity: 0.5;
flex: 0 0 auto;
font-size: 11px;
}
/* hide the gutter timestamp on the first message of a group (now inline) */
.mx_EventTile[data-layout="group"]:not(.mx_EventTile_continuation) > .mx_EventTile_line > .mx_MessageTimestamp {
display: none;
}
/* ---- Composer pill ---- */
.mx_MessageComposer {
background-color: var(--yap-surface-main);
}
.mx_MessageComposer_wrapper {
background-color: var(--yap-surface-composer);
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 13px;
margin: 0 16px 6px;
padding: 2px 10px;
}
.mx_MessageComposer_input {
background-color: transparent;
border: none;
padding: 4px 4px;
}
.mx_MessageComposer_input textarea {
background-color: transparent !important;
}
/* remove the left-gutter e2e-status + avatar icons that overlapped the
placeholder (encryption status is in the room header; mockup composer
has no left icon) and reclaim their padding. */
.mx_MessageComposer_e2eIconWrapper,
.mx_MessageComposer .mx_MessageComposer_avatar {
display: none !important;
}
.mx_MessageComposer {
padding-left: 8px !important;
}
/* ============================================================
Call presence — in-call avatar stack on room rows (headline feature)
============================================================ */
.mx_YapCallFacepile {
display: flex;
align-items: center;
gap: 4px;
margin-inline-start: auto; /* push to trailing edge of the row */
padding-inline: 4px;
flex-shrink: 0;
}
/* squircle peek avatars with a list-colored border, slight overlap */
.mx_YapCallFacepile .mx_BaseAvatar {
border-radius: 7px !important;
box-shadow: 0 0 0 2px var(--yap-surface-roomlist);
}
.mx_YapCallFacepile_more {
font: var(--cpd-font-body-xs-medium, 11px/1 var(--yap-font-mono));
font-family: var(--yap-font-mono);
color: var(--yap-accent);
font-weight: 700;
}
/* ============================================================
Right panel — Members
============================================================ */
.mx_MemberListView,
.mx_MemberList {
background-color: var(--yap-surface-roomlist);
}
/* squircle member avatars */
.mx_MemberTileView_avatar .mx_BaseAvatar {
border-radius: var(--yap-radius-avatar-member) !important;
}
.mx_MemberTileView_name {
font-weight: 600;
}
/* role / section separators (OWNER · ONLINE · OFFLINE) */
.mx_MemberListView_separator {
color: var(--yap-text-muted);
font-size: 11.5px;
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
}
/* presence dot colors */
.mx_PresenceLabel_online {
color: var(--yap-success);
}
.mx_PresenceLabel_unavailable {
color: var(--yap-idle);
}
/* ============================================================
Reactions — pills
============================================================ */
.mx_ReactionsRowButton {
background-color: var(--yap-accent-soft);
border: 1px solid color-mix(in srgb, var(--yap-accent) 30%, transparent);
border-radius: 999px;
}
.mx_ReactionsRowButton_count {
color: var(--yap-accent);
font-weight: 700;
font-variant-numeric: tabular-nums;
}
/* ============================================================
Thread summary — chip
============================================================ */
.mx_ThreadSummary {
background-color: rgba(255, 255, 255, 0.035);
border-radius: 12px;
}
.mx_ThreadSummary_replyCount,
.mx_ThreadSummary .mx_ThreadSummary_content {
color: var(--yap-accent);
}
/* ============================================================
Room intro block
============================================================ */
.mx_NewRoomIntro {
color: var(--yap-text-secondary);
}
/* ============================================================
Notification badges (rail + room list, consistent)
mention = red, unread dot = accent
============================================================ */
.mx_NotificationBadge_level_highlight {
background-color: var(--yap-danger);
}
.mx_NotificationBadge_dot {
background-color: var(--yap-accent);
}
/* ============================================================
Space landing (the space "directory" head start) — #5
============================================================ */
.mx_SpaceRoomView_landing_name {
font-size: 30px;
font-weight: 800;
color: var(--yap-text-primary);
}
.mx_SpaceRoomView_landing_topic {
color: var(--yap-text-muted);
}
/* "this is a space, not a server" explainer callout (mockup) */
.mx_YapSpaceExplainer {
margin: 18px 0 6px;
padding: 11px 14px;
border-radius: 12px;
background-color: color-mix(in srgb, var(--yap-accent) 9%, transparent);
border: 1px solid color-mix(in srgb, var(--yap-accent) 22%, transparent);
color: var(--yap-text-secondary);
font-size: 13.5px;
line-height: 1.5;
}
.mx_YapSpaceExplainer strong {
color: var(--yap-text-primary);
font-weight: 700;
}
/* ============================================================
Room header — icon cluster + header facepile
============================================================ */
.mx_RoomHeader_icon {
color: var(--yap-text-muted);
}
.mx_RoomHeader_heading {
font-weight: 700;
color: var(--yap-text-primary);
}
/* # glyph + topic line in the header (mockup) */
.mx_RoomHeader_info {
display: flex !important;
align-items: center;
min-width: 0;
}
.mx_YapHeader_hash {
color: var(--yap-text-faint);
font-weight: 700;
margin-right: 3px;
}
.mx_YapHeader_topic {
margin-left: 12px;
padding-left: 12px;
border-left: 1px solid var(--yap-hairline);
color: var(--yap-text-muted);
font-size: 13.5px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex: 1 1 auto;
min-width: 0;
}
.mx_RoomHeader .mx_FacePile .mx_BaseAvatar {
border-radius: 7px !important;
}
/* make the call control read as the mockup's accent "Call" affordance */
.mx_RoomHeader_videoCallOption {
color: var(--yap-accent);
}
/* ============================================================
Polish — selection bar, composer buttons, rail add button
============================================================ */
/* active room: inset accent left-bar (mockup) */
.mx_RoomTile_selected {
box-shadow: inset 3px 0 0 var(--yap-accent);
}
/* composer: send in accent, other actions muted */
.mx_MessageComposer_sendMessage {
color: var(--yap-accent);
}
.mx_MessageComposer_button {
color: var(--yap-text-muted);
}
/* spaces rail: "add" utility reads green (mockup) */
.mx_SpaceButton_new .mx_SpaceButton_icon {
color: var(--yap-success);
}
/* ============================================================
Space Directory — browse & join card grid (#5)
Restyle of SpaceHierarchy's list into the mockup card grid.
(Element shows joined + unjoined in one list with View/Join
buttons; a two-section split would need React.)
============================================================ */
.mx_SpaceHierarchy_list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(282px, 1fr));
gap: 14px;
list-style: none;
padding: 0;
}
.mx_SpaceHierarchy_roomTileWrapper {
margin: 0;
}
.mx_SpaceHierarchy_roomTile {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 10px;
background-color: var(--yap-surface-card);
border: 1px solid var(--yap-hairline);
border-radius: var(--yap-radius-card);
padding: 16px;
}
.mx_SpaceHierarchy_roomTile:hover {
background-color: var(--yap-surface-card-hover);
}
.mx_SpaceHierarchy_roomTile_avatar .mx_BaseAvatar {
border-radius: 13px !important;
}
.mx_SpaceHierarchy_roomTile_name {
font-weight: 700;
color: var(--yap-text-primary);
}
.mx_SpaceHierarchy_listHeader {
color: var(--yap-text-muted);
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
}
/* join-state section headers (Your rooms / haven't joined / Spaces) span the grid */
.mx_YapHierarchy_sectionHeader {
grid-column: 1 / -1;
margin: 16px 0 2px;
font-size: 11.5px;
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
color: var(--yap-text-muted);
}
/* hide the admin "manage suggested rooms" clutter (checkboxes + ADD/REMOVE/
MARK buttons) — not in the mockup; cards are clean + clickable.
The checkbox is a Compound <Form>/InlineField, not .mx_Checkbox. */
.mx_SpaceHierarchy_listHeader_buttons,
.mx_SpaceHierarchy_actions form,
.mx_SpaceHierarchy_actions [class*="_inline-field"],
.mx_SpaceHierarchy_actions input[type="checkbox"] {
display: none !important;
}
/* show the Open/Join button always (Element hides it until hover), bottom-right */
.mx_SpaceHierarchy_roomTile .mx_SpaceHierarchy_actions {
margin: 8px 0 0 auto;
align-self: flex-end;
}
.mx_SpaceHierarchy_roomTile .mx_SpaceHierarchy_actions .mx_AccessibleButton {
visibility: visible !important;
border-radius: 9px;
font-weight: 700;
}
.mx_SpaceHierarchy_roomTile .mx_AccessibleButton_kind_primary {
background-color: var(--yap-accent) !important;
}
/* ============================================================
Bridges panel (#6) — static bridge cards in the right panel
============================================================ */
.mx_YapBridgesView {
background-color: var(--yap-surface-roomlist);
}
.mx_YapBridgesView_list {
display: flex;
flex-direction: column;
gap: 10px;
padding: 12px;
}
.mx_YapBridgesView_empty {
color: var(--yap-text-muted);
font-size: 13.5px;
padding: 8px 4px;
}
/* ---- Pinned messages (banner + right-panel card + tiles) ---- */
.mx_PinnedMessageBanner {
background-color: var(--yap-surface-main);
border-bottom: 1px solid var(--yap-hairline);
box-shadow: none;
}
.mx_PinnedMessageBanner_main {
border-radius: 8px;
}
.mx_PinnedMessageBanner_main:hover {
background-color: var(--yap-surface-card);
}
.mx_PinnedMessageBanner_title {
color: var(--yap-accent) !important;
}
.mx_PinnedMessageBanner_PinIcon {
fill: var(--yap-text-muted) !important;
}
.mx_PinnedMessageBanner_Indicator {
background-color: var(--yap-hairline) !important;
}
.mx_PinnedMessageBanner_Indicator--active {
background-color: var(--yap-accent) !important;
}
/* right-panel card */
.mx_PinnedMessagesCard .mx_PinnedMessagesCard_Separator hr,
.mx_PinnedMessagesCard .mx_Separator {
border-color: var(--yap-hairline);
}
.mx_PinnedMessagesCard_unpin {
background: var(--yap-surface-roomlist);
box-shadow: none;
border-top: 1px solid var(--yap-hairline);
}
/* each pinned message rendered as a soft card */
.mx_PinnedEventTile {
padding: 10px 12px;
border-radius: 12px;
background-color: var(--yap-surface-card);
border: 1px solid var(--yap-hairline);
transition: background-color 0.12s ease;
}
.mx_PinnedEventTile:hover {
background-color: var(--yap-surface-card-hover);
}
.mx_PinnedEventTile_sender {
color: var(--yap-text-primary);
}
.mx_PinnedEventTile_thread span {
color: var(--yap-text-muted) !important;
}
.mx_PinnedEventTile_thread svg {
fill: var(--yap-text-faint) !important;
}
.mx_PinnedEventTile_thread button,
.mx_PinnedEventTile .mx_EventTile_body a {
color: var(--yap-accent);
}
/* ---- Right panel: room info card ---- */
.mx_RoomSummaryCard_roomName {
color: var(--yap-text-primary);
font-weight: 700;
}
.mx_RoomSummaryCard_alias {
color: var(--yap-text-muted);
}
.mx_RoomSummaryCard_topic,
.mx_RoomSummaryCard_topic_box {
color: var(--yap-text-secondary);
}
/* info pills (encryption state / history visibility) themed off Element blue/green */
.mx_RoomSummaryCard_badges [data-kind] {
background-color: var(--yap-surface-card) !important;
border: 1px solid var(--yap-hairline) !important;
color: var(--yap-text-secondary) !important;
box-shadow: none !important;
}
.mx_RoomSummaryCard_badges [data-kind] svg {
color: currentColor !important;
fill: currentColor !important;
}
.mx_RoomSummaryCard_badges [data-kind="green"] {
color: #5ed1a0 !important; /* encrypted = positive */
border-color: color-mix(in srgb, #5ed1a0 32%, transparent) !important;
}
/* separators + menu rows */
.mx_RoomSummaryCard hr,
.mx_RoomSummaryCard [role="separator"] {
border-color: var(--yap-hairline) !important;
background-color: var(--yap-hairline) !important;
}
.mx_RoomSummaryCard [role="menuitem"]:hover,
.mx_RoomSummaryCard [role="menuitemcheckbox"]:hover {
background-color: var(--yap-surface-card) !important;
border-radius: 8px;
}
/* accent the edit/links + the search field */
.mx_RoomSummaryCard_topic_edit,
.mx_RoomSummaryCard a {
color: var(--yap-accent);
}
.mx_RoomSummaryCard_search {
background-color: var(--yap-surface-card);
border: 1px solid var(--yap-hairline);
border-radius: 10px;
}
/* ---- Polls (timeline poll body + options) ---- */
.mx_MPollBody_totalVotes {
color: var(--yap-text-muted);
}
.mx_PollOption {
border: 1px solid var(--yap-hairline) !important;
background-color: var(--yap-surface-card) !important;
border-radius: 10px !important;
}
.mx_PollOption.mx_PollOption_live-option:hover {
background-color: var(--yap-surface-card-hover) !important;
border-color: var(--yap-accent) !important;
}
/* selected option gets an accent outline */
.mx_PollOption_checked {
border-color: var(--yap-accent) !important;
}
/* popularity bar: track + accent fill */
.mx_PollOption_popularityBackground {
background-color: var(--yap-hairline) !important;
}
.mx_PollOption_popularityAmount {
background-color: var(--yap-accent) !important;
}
.mx_PollOption_checked .mx_PollOption_popularityBackground .mx_PollOption_popularityAmount,
.mx_PollOption_ended.mx_PollOption_checked .mx_PollOption_popularityBackground .mx_PollOption_popularityAmount {
background-color: var(--yap-accent) !important;
}
/* checked radio → accent fill + checkmark */
.mx_PollOption_checked .mx_StyledRadioButton_checked input[type="radio"]:checked + div,
.mx_PollOption_ended.mx_PollOption_checked .mx_StyledRadioButton_checked input[type="radio"]:checked + div {
border-color: var(--yap-accent) !important;
background-color: var(--yap-accent) !important;
}
.mx_PollOption_optionVoteCount {
color: var(--yap-text-secondary);
}
/* ---- Poll create dialog ---- */
.mx_PollCreateDialog_option {
background-color: var(--yap-surface-card);
border: 1px solid var(--yap-hairline);
border-radius: 10px;
}
.mx_PollCreateDialog_addOption {
color: var(--yap-accent);
}
/* ---- Settings menus (dialog: tabs, sections, fields) ---- */
/* left tab list: active tab → accent */
.mx_TabbedView_tabLabel_active {
background-color: var(--yap-accent-soft) !important;
color: var(--yap-accent) !important;
}
.mx_TabbedView_tabLabel_active .mx_TabbedView_tabLabel_text {
color: var(--yap-accent) !important;
}
.mx_TabbedView_tabLabel:hover {
background-color: var(--yap-surface-card) !important;
}
.mx_UserSettingsDialog_title_strong,
.mx_SettingsSection_header,
.mx_SettingsSubsectionHeading_heading {
color: var(--yap-text-primary);
}
.mx_SettingsSubsection_description,
.mx_SettingsTab_subsectionText,
.mx_SettingsFieldset_description,
.mx_SettingsTab_section_caption {
color: var(--yap-text-muted);
}
/* dividers between sections */
.mx_SettingsTab .mx_SettingsSubsection + .mx_SettingsSubsection,
.mx_SettingsSection + .mx_SettingsSection {
border-color: var(--yap-hairline);
}
/* form fields + dropdowns in settings → Yap surfaces */
.mx_SettingsTab .mx_Field,
.mx_SettingsTab .mx_Dropdown,
.mx_SettingsTab select,
.mx_SettingsTab input[type="text"],
.mx_SettingsTab input[type="password"] {
background-color: var(--yap-surface-card);
border-color: var(--yap-hairline);
}
.mx_SettingsTab .mx_Field:focus-within {
border-color: var(--yap-accent);
}
/* toggle switches → accent when on (app-wide) */
.mx_ToggleSwitch_on,
.mx_ToggleSwitch.mx_ToggleSwitch_on {
background-color: var(--yap-accent) !important;
}
/* ---- leftover Element-green defaults → Blap accent ---- */
/* "new messages" bar above the timeline + jump-to-bottom */
.mx_TopUnreadMessagesBar_scrollUp,
.mx_TopUnreadMessagesBar_markAsRead {
color: var(--yap-accent) !important;
}
.mx_JumpToBottomButton_highlight .mx_JumpToBottomButton_badge {
background-color: var(--yap-accent) !important;
}
/* read-marker line ("new messages" divider) */
.mx_RoomView_myReadMarker {
background-color: var(--yap-accent) !important;
border-top-color: var(--yap-accent) !important;
}
/* one-off hardcoded brand green */
.mx_AnalyticsLearnMoreDialog a {
color: var(--yap-accent) !important;
}
/* ---- Login / auth screen (Blap branded) ----
Target = the blap mockup: one centered card on the molecule background,
NOT Element's two-pane (left logo / right form) layout. */
.mx_AuthPage {
background-color: var(--yap-surface-rail) !important;
background-image:
linear-gradient(180deg, rgba(15, 13, 20, 0.5), rgba(15, 13, 20, 0.8)),
url("../../../img/blap-login-bg.png") !important;
background-size: cover !important;
background-position: center !important;
background-repeat: no-repeat !important;
align-items: center !important;
justify-content: center !important;
}
/* collapse the two-pane modal into a single transparent wrapper */
.mx_AuthPage_modal {
margin: auto !important;
background-color: transparent !important;
backdrop-filter: none;
border: none !important;
box-shadow: none !important;
border-radius: 20px !important;
}
.mx_AuthPage_modalContent {
flex-direction: column !important;
background-color: transparent !important;
}
/* hide the left logo / language pane — the card carries its own brand */
.mx_AuthHeader {
display: none !important;
}
/* the card itself */
.mx_AuthBody {
width: 384px !important;
box-sizing: border-box;
background-color: var(--yap-surface-main) !important;
border: 1px solid rgba(255, 255, 255, 0.07) !important;
border-radius: 20px !important;
box-shadow: 0 24px 70px rgba(0, 0, 0, 0.55) !important;
padding: 30px 30px 24px !important;
color: var(--yap-text-secondary) !important;
}
/* branded welcome header (rendered by Login.tsx) */
.mx_AuthBody_blapWelcome {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: 4px;
margin-bottom: 22px;
}
.mx_AuthBody_blapWelcome_mark {
width: 58px;
height: 58px;
border-radius: 17px;
background: linear-gradient(135deg, #7c66e0, #b16ae0);
padding: 11px;
object-fit: contain;
box-shadow: 0 8px 24px rgba(124, 102, 224, 0.4);
margin-bottom: 8px;
}
.mx_AuthBody .mx_AuthBody_blapWelcome_title {
margin: 0 !important;
font-size: 27px !important;
font-weight: 800 !important;
color: var(--yap-text-primary) !important;
}
.mx_AuthBody_blapWelcome_tagline {
color: var(--yap-text-muted);
font-size: 14px;
}
/* server picker → boxed homeserver field */
.mx_AuthBody .mx_ServerPicker {
background-color: rgba(0, 0, 0, 0.22);
border: 1px solid rgba(255, 255, 255, 0.05);
border-radius: 11px;
padding: 9px 14px;
margin: 0 0 16px !important;
align-items: center;
}
.mx_AuthBody .mx_ServerPicker_change {
color: var(--yap-accent);
}
/* gradient sign-in button */
.mx_AuthBody .mx_Login_submit {
width: 100%;
margin-top: 6px;
background: linear-gradient(135deg, #7c66e0, #b16ae0) !important;
border: none !important;
border-radius: 12px !important;
padding: 11px !important;
font-weight: 700 !important;
color: #fff !important;
}
.mx_AuthBody .mx_Login_submit:hover {
filter: brightness(1.07);
}
.mx_AuthBody .mx_SSOButtons {
margin-top: 14px;
}
.mx_AuthBody,
.mx_AuthFooter {
color: var(--yap-text-secondary);
}
.mx_AuthBody a,
.mx_AuthFooter a {
color: var(--yap-accent);
}
}