Something went wrong. Try again.
Hangar is your Bluesky home base—built natively for Linux. ✈️ hangar.blue
libadwaita gtk4 bluesky flatpak gnome atprotocol linux atproto appimage rust
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697/* SPDX-License-Identifier: MPL-2.0 *//* Hangar Stylesheet */
/* Sidebar rail styling */.sidebar-rail { background-color: @window_bg_color; border-right: 1px solid @borders;}
.sidebar-rail listbox { background: transparent;}
.sidebar-rail listbox row { padding: 4px; margin: 2px 4px; border-radius: 8px;}
.sidebar-rail listbox row:selected { background-color: alpha(@accent_bg_color, 0.15);}
/* Selected nav item icon and label use accent color */.sidebar-rail listbox row:selected image { color: @accent_color;}
.sidebar-rail listbox row:selected label { color: @accent_color;}
.sidebar-rail listbox row:hover:not(:selected) { background-color: alpha(@window_fg_color, 0.05);}
/* Navigation item labels */.sidebar-rail .caption { font-size: 10px; font-weight: 500;}
/* Compose button in sidebar */.sidebar-rail .suggested-action.circular { min-width: 48px; min-height: 48px;}
/* Post row styling. Cleaner look without a heavy card background. */.post-row { padding: 8px 16px 8px 12px; border-bottom: 1px solid alpha(@borders, 0.5);}
.post-row .heading { font-weight: 600; font-size: 1em;}
/* Avatar button styling */.post-row .avatar-button { padding: 0; min-width: 42px; min-height: 42px;}
.post-row .avatar-button:hover { background-color: alpha(@window_fg_color, 0.05);}
/* Verified badge */.post-row .verified-badge { color: @accent_color;}
/* Repost attribution, the small text above the post */.post-row .repost-attribution { margin-bottom: 2px;}
.post-row .repost-avatar { min-width: 16px; min-height: 16px;}
/* Clickable repost/reply buttons */.post-row .repost-btn { padding: 0 4px; min-height: 20px;}
.post-row .reply-handle-btn { padding: 0 2px; margin-left: 4px; min-height: 18px;}
.post-row .repost-btn:hover,.post-row .reply-handle-btn:hover { background-color: alpha(@window_fg_color, 0.08);}
/* Link-styled label (uses accent color for high contrast compatibility) */.link-label { color: @accent_color;}
/* Action bar styling */.post-row .action-count { font-size: 0.85em;}
/* Post embed images */.post-embed-image { border-radius: 12px; border: none; background: transparent;}
.post-embed-image > picture { border-radius: 12px;}
/* Action buttons in posts */.post-row .flat.circular { min-width: 28px; min-height: 28px; padding: 4px;}
.post-row .flat.circular image { -gtk-icon-size: 16px;}
/* Legacy card class for backwards compatibility */.card { padding: 16px; border-bottom: 1px solid alpha(@borders, 0.5);}
.card .heading { font-weight: 600;}
/* Liked state uses the accent color (blue by default, follows system theme) */.flat.circular.liked,button.flat.circular.liked { color: @accent_color;}
/* Reposted state uses the accent color to match the like button */.flat.circular.reposted,menubutton.flat.circular.reposted > button { color: @accent_color;}
/* Error label styling */.error { color: @error_color;}
/* Timeline background, scoped to the nodes that ask for it. Tooltip and popover windows carry .background too and painting them broke light theme tooltips and drew slabs behind chat context menus. */listview.background,box.background { background-color: @window_bg_color;}
/* Feed selector button */.title { font-weight: 600;}
/* Login dialog styling */window.background entry { min-height: 36px;}
/* New posts banner */.new-posts-banner { box-shadow: 0 2px 8px alpha(black, 0.3); padding: 6px 14px; min-height: 0;}
.new-posts-banner .banner-icon { color: white; -gtk-icon-size: 14px;}
.new-posts-banner .banner-label { color: white; font-weight: 600; font-size: 0.9em;}
/* Feed selector popover */popover.menu { background: transparent;}
popover.menu > contents { box-shadow: none; background: @popover_bg_color; border: 1px solid @borders; border-radius: 12px;}
/* Repost attribution */.post-row .repost-attribution image { opacity: 0.7;}
/* External link card */.external-card-button { padding: 0; border-radius: 12px;}
.external-card-button:hover .external-card { background-color: alpha(@card_bg_color, 0.8);}
.external-card { background-color: alpha(@card_bg_color, 0.6); border: 1px solid alpha(@borders, 0.5); border-radius: 12px; /* No padding: the thumbnail runs edge to edge across the top. The text block below it carries the padding instead. */ padding: 0;}
.external-card .external-card-text { padding: 12px;}
/* Thumbnail across the top of the card. No min-width: the timeline's ScrolledWindow has no horizontal valve, so a width floor here would be a width floor for the whole window. */.external-card .external-thumb-top { border: none; background: transparent;}
.external-card .external-title { font-weight: 600; font-size: 0.95em;}
/* Video embed */.video-embed { border-radius: 12px;}
.video-embed .osd.circular { background-color: alpha(black, 0.6); min-width: 48px; min-height: 48px;}
.video-embed .osd.circular:hover { background-color: alpha(black, 0.8);}
/* GIF badge. A GIF embed shows a still frame until it is clicked, and a still frame is indistinguishable from a photograph without something saying so. Fixed colours rather than theme ones: it sits on top of arbitrary image pixels, where a light-theme foreground would be unreadable half the time. */.gif-badge,.alt-badge { background-color: alpha(black, 0.7); color: white; font-size: 0.75em; font-weight: bold; padding: 2px 6px; border-radius: 6px;}
/* Video player: letterbox against black so a portrait clip does not sit on a bright dialog background. */.video-surface { background-color: black;}
.video-controls { padding: 6px 12px;}
/* Inline transport. An overlay strip rather than a bar under the picture: the row's height was decided from the video's aspect ratio before the player existed, and a bar underneath would grow the embed past it. Sat on top of arbitrary video pixels, so the same fixed dark treatment as the play button rather than a theme colour that would be unreadable half the time. */.video-inline-controls { background-color: alpha(black, 0.6); padding: 4px 8px; border-radius: 0 0 12px 12px;}
.video-inline-controls button { color: white;}
.video-inline-controls label { color: white;}
/* One line saying why the thumbnail came back, where a dialog would show a whole status page. Ellipsized and capped rather than wrapped: this is over an embed in a scroller with no horizontal valve. */.video-inline-error { background-color: alpha(black, 0.75); color: white; /* 12px matches .video-controls. The flat Open button pads its own right edge; at 8px the text side read as flush. */ padding: 6px 12px; border-radius: 8px;}
.video-inline-error label { color: white; font-size: 0.9em;}
/* Quote post card. The clickable wrapper is a plain box rather than a button so interactive controls inside the card can claim their own clicks, and that means it also needs its own focus ring. */.quote-card-button { padding: 0; border-radius: 12px;}
.quote-card-button:hover .quote-card { background-color: alpha(@card_bg_color, 0.6);}
.quote-card-button:focus-visible { outline: 2px solid @accent_color; outline-offset: -2px;}
.quote-card { background-color: alpha(@card_bg_color, 0.4); border: 1px solid alpha(@borders, 0.5); border-radius: 12px; padding: 12px;}
/* Nested quote (quote of a quote) */.quote-card .quote-card { background-color: alpha(@window_bg_color, 0.3); margin-top: 8px;}
/* Image grid layouts */.post-embed .image-grid { border-radius: 12px;}
/* Caption class for smaller text */.caption { font-size: 0.95em;}
/* Thread view separator */.thread-separator { background-color: alpha(@window_fg_color, 0.03); border-top: 1px solid alpha(@borders, 0.5); border-bottom: 1px solid alpha(@borders, 0.5); padding: 12px 16px; margin: 0;}
/* Activity row styling */.activity-row { padding: 4px 0;}
/* Activity badge overlay on avatar */.activity-badge { background-color: @window_bg_color; border-radius: 50%; padding: 2px; margin: -4px;}
.activity-badge.liked { color: @error_color;}
.activity-badge.reposted { color: @success_color;}
.activity-badge.followed { color: @accent_color;}
/* Activity embedded post card */.activity-row .card { background-color: alpha(@card_bg_color, 0.4); border: 1px solid alpha(@borders, 0.3); border-radius: 12px; padding: 0;}
/* Conversation row styling */.conversation-row { padding: 4px 0;}
/* Chat message bubbles */.msg-bubble { padding: 8px 12px; border-radius: 12px;}
.msg-in { background-color: alpha(@card_bg_color, 0.8);}
.msg-out { background-color: alpha(@accent_bg_color, 0.25);}
/* Links in messages are underlined, same reasoning as post content */.msg-bubble label a { text-decoration: underline;}
/* Skeleton rows while a first load is out. The pulse dies with the reduce-motion override, which zeroes every animation duration. */@keyframes skeleton-pulse { 0% { opacity: 0.4; } 50% { opacity: 0.75; } 100% { opacity: 0.4; }}
.skeleton-shape { background-color: alpha(@borders, 0.7); border-radius: 8px; animation: skeleton-pulse 1.4s ease-in-out infinite;}
.skeleton-circle { border-radius: 50%;}
/* A focused post row shows where the post shortcuts will land */.post-row:focus-visible { outline: 2px solid @accent_color; outline-offset: -2px; border-radius: 8px;}
/* Profile pages */.profile-banner { background-color: alpha(@accent_bg_color, 0.2);}
.profile-avatar { border-radius: 50%; box-shadow: 0 0 0 4px @window_bg_color;}
.condensed-profile-bar { background-color: @window_bg_color; padding: 6px 12px; border-bottom: 1px solid alpha(@borders, 0.6);}
/* Shared posts in chat. The button supplies the card look; margins on the content box do the padding. */.msg-post-card { padding: 0;}
.msg-card-thumb { border-radius: 6px;}
/* Reaction chips hang on the bubble's bottom edge */.msg-reactions { margin-top: -10px; margin-left: 10px; margin-right: 10px;}
.reaction-chip { padding: 1px 8px; border-radius: 10px; font-size: 0.85em; min-height: 0; background-color: @card_bg_color; border: 1px solid alpha(@borders, 0.8);}
.reaction-chip-mine { background-color: alpha(@accent_bg_color, 0.35);}
/* Settings sidebar navigation */.settings-sidebar { background-color: @window_bg_color;}
.settings-sidebar listbox.navigation-sidebar { background: transparent; padding: 6px;}
.settings-sidebar listbox.navigation-sidebar row { border-radius: 8px; margin: 1px 0;}
/* Settings preview post */.text-size-preview .preview-heading { font-weight: 600;}
/* Profile page styling */.profile-banner-placeholder { background: linear-gradient(135deg, alpha(@accent_bg_color, 0.3), alpha(@accent_bg_color, 0.1));}
/* ============================================================ Accessibility Enhancements ============================================================ */
/* Links in post content are underlined; WCAG 1.4.1 says color alone is not enough */.post-row label a { text-decoration: underline;}
/* Link-styled label: also add underline for accessibility */.link-label { text-decoration: underline;}
/* Liked state: add font-weight as non-color indicator (WCAG 1.4.1) */.flat.circular.liked image,button.flat.circular.liked image { font-weight: 900;}
/* Reposted state: add font-weight as non-color indicator */.flat.circular.reposted image,menubutton.flat.circular.reposted > button image { font-weight: 900;}
/* Ensure focus rings are always visible on interactive elements */.post-row .flat.circular:focus-visible { outline: 2px solid @accent_color; outline-offset: 2px;}
.post-row .avatar-button:focus-visible { outline: 2px solid @accent_color; outline-offset: 2px;}
/* Ensure minimum target size for action buttons (WCAG 2.5.5) */.post-row .flat.circular { min-width: 36px; min-height: 36px;}
/* Mention autocomplete popover */popover.mention-popover { background: transparent;}
popover.mention-popover > contents { background: @popover_bg_color; border: 1px solid @borders; border-radius: 12px; box-shadow: 0 4px 12px alpha(black, 0.15); padding: 4px 0;}
.mention-list { background: transparent;}
.mention-list row { border-radius: 8px; margin: 2px 4px; min-height: 44px;}
.mention-list row:selected { background-color: alpha(@accent_bg_color, 0.15);}
.mention-list row:hover:not(:selected) { background-color: alpha(@window_fg_color, 0.05);}
.mention-list row .heading { font-weight: 600; font-size: 0.95em;}
/* Compose dialog image attachment strip */.compose-image-strip { margin-top: 8px;}
.compose-thumbnail { border-radius: 8px;}
.compose-thumbnail-image { border-radius: 8px;}
.compose-alt-badge { font-size: 10px; font-weight: 700; padding: 2px 4px; border-radius: 4px; background-color: alpha(black, 0.6); color: white;}
/* Upload progress floating over the video tile's preview frame */.compose-video-status { padding: 2px 8px; border-radius: 8px; background-color: alpha(black, 0.6); color: white;}
.compose-video-status label { color: white;}
/* Compose dialog character counter */.char-counter { font-variant-numeric: tabular-nums; min-width: 3em;}
.char-counter-warn { color: @warning_color; font-weight: 600;}
.char-counter-over { color: @error_color; font-weight: 700;}
/* Compose dialog link card preview */.compose-link-card { background-color: alpha(@card_bg_color, 0.4); border: 1px solid alpha(@borders, 0.5); border-radius: 12px; padding: 10px;}
.compose-link-card .compose-link-thumb { border-radius: 8px; min-width: 72px; min-height: 72px;}
/* Compose status row with right-aligned labels */.compose-status-row { margin-top: 2px; margin-bottom: 4px;}
/* Thread post separator */.thread-post-separator { margin-top: 8px; margin-bottom: 4px;}
/* Unread count pill on the sidebar rail icons */.unread-badge { background-color: @accent_bg_color; color: @accent_fg_color; border-radius: 999px; padding: 0px 4px; font-size: 10px; font-weight: 700; min-width: 8px; margin-top: -6px; margin-right: -10px;}