Adaptation of "Twitter/X Goodie Bag" for Bluesky
Bluesky Goodie Bag by o_o
Details
Authoro_o
LicenseNo License
Categorybsky
Created
Updated
Size12 kB
Statistics
Learn how we calculate statistics in the FAQ.
Failed to fetch stats.
Description
Notes
Twitter/X Goodie Bag but for Bluesky! This currently is not 1:1 in functionality with Twitter/X Goodie Bag, but it still has some killer features.
Options
- π± - Exclusive to the mobile layout
- π§ - Incomplete, but usable
- π - Requires the userscript add-on (more details below)
Posts
- Show like count on button - toggles the count for like buttons.
- Show repost count on button - toggles the count for repost buttons.
- Show reply count on button - toggles the count for reply buttons.
- Expanded like count - toggles the count for the like text above the buttons when you open a post. You can choose to keep just the text for tapping, or just get rid of it all together.
- Expanded repost count - toggles the count for the repost text above the buttons when you open a post. You can choose to keep just the text for tapping, or just get rid of it all together.
- Tip: Hide the expanded like and repost count to entirely get rid of the metric bar.
Profile
- π§ π π± Show more buttons on the top bar upon scroll - makes the follow and more buttons sticky, so you can still access it when scrolling on someone's profile. Similar to Twitter.
- Issue: doesn't disable itself on desktop
- π§ π π± Sticky profile details instead of tabs - replaces the profile tabs (posts, replies, media, feeds) with the user's username and handle as you scroll. Similar to Twitter.
- Issue: doesn't disable itself on desktop
Navigation
- π± Hide top bar on scroll - toggles the top bar hiding as you scroll.
- π± Hide bottom bar on scroll - toggles the bottom bar hiding as you scroll.
Image viewer
- Move buttons to bottom - moves the buttons of the image viewer to the bottom, and prevents tapping anywhere to close. Useful on mobile for when you're zooming in.
Goodie bag
- π Warn about userscript version - shows a warning if the userscript add-on's version is not compatible with the currently installed userstyle.
Theme compatibility
These have to do with having this userstyle work well with other userstyles that theme Bluesky. These options alone do not theme Bluesky.
If you only have this userstyle installed, you can safely leave these options as the default.
Userscript add-on
Unfortunately, not all that Twitter/X Goodie Bag does is possible on Bluesky with CSS alone. So, this userstyle also comes with an optional userscript that you can install to add more functionality.
You still configure from Stylus. The userscript doesn't have its own special menu to hunt for.
Source code
Source code has over 10K characters, so we truncated it. You can inspect the full source code on install link./* ==UserStyle==
@name Bluesky Goodie Bag
@namespace dabric.xyz/post/bluesky-goodie-bag
@version 0.2.2
@description Several tweaks to Bluesky, all disabled by default
@author dabric
@preprocessor stylus
@var checkbox show-likes-inline "Posts: Show like count on button" 1
@var checkbox show-retweets-inline "Posts: Show repost count on button" 1
@var checkbox show-replies-inline "Posts: Show reply button count on button" 1
@var select show-likes "Posts: Expanded like count" ["show:Show", "nonum:Hide number", "hide:Hide entirely"]
@var select show-retweets "Posts: Expanded repost count" ["show:Show", "nonum:Hide number", "hide:Hide entirely"]
@var checkbox more-profile-buttons-on-nav "π§ π π± Profile: Show more buttons on the top bar upon scroll" 0
@var checkbox show-profile-details-on-nav "π§ π π± Profile: Sticky profile details instead of tabs" 0
@var checkbox image-viewer-buttons-on-bottom "Image viewer: Move buttons to bottom" 0
@var checkbox hide-bottom-bar-on-scroll "π± Navigation: Hide bottom bar on scroll" 1
@var checkbox hide-top-bar-on-scroll "π± Navigation: Hide top bar on scroll" 1
@var checkbox show-version-warning "π Goodie bag: Warn about userscript version" 1
@var select theme "Theme compatibility: Theme" ["vanilla:Vanilla", "mocha:Catppuccin Mocha", "custom:Custom"]
@var color custom-background-color "Theme compatability: Custom Background" #000
@var color custom-text-color "Theme compatability: Custom Text" #fff
@var color custom-primary-color "Theme compatability: Custom Primary" #208bfe
@var color custom-overlay-button-color "Theme compatability: Custom Overlay Button" rgba(0, 0, 0, 0.47)
==/UserStyle== */
@-moz-document domain("bsky.app") {
// all of twitter.com
if theme == "vanilla" {
// the paranthesis in the selector seeems to conflict
// with stylus's parser
:root {
--goodie-primary-color: rgb(32, 139, 254);
}
@css {
/*dark*/
body[style*="background-color: rgb(0, 0, 0)"] {
--goodie-background-color: black;
--goodie-text-color: white;
--goodie-overlay-button-color: rgba(0, 0, 0, 0.47);
}
/*dim*/
body[style*="background-color: rgb(22, 30, 39)"] {
--goodie-background-color: rgb(22, 30, 39);
--goodie-text-color: white;
--goodie-overlay-button-color: rgba(0, 0, 0, 0.47);
}
/*light*/
body[style*="background-color: rgb(255, 255, 255)"] {
--goodie-background-color: white;
--goodie-text-color: black;
--goodie-overlay-button-color: rgba(0, 0, 0, 0.47);
}
}
} else if theme == "mocha" {
:root {
--goodie-primary-color: #74c7ec;
--goodie-text-color: #cdd6f4;
--goodie-background-color: #1e1e2e;
--goodie-overlay-button-color: rgba(0, 0, 0, 0.47);
}
} else if theme == "custom" {
:root {
--goodie-primary-color: custom-primary-color;
--goodie-text-color: custom-text-color;
--goodie-background-color: custom-background-color;
--goodie-overlay-button-color: custom-overlay-button-color;
}
}
:root {
--goodie-addon-version: "0.0.3"
unless show-version-warning {
--goodie-hide-version-warning: "true"
}
}
background-color = var(--goodie-background-color);
text-color = var(--goodie-text-color);
overlay-button-color = var(--goodie-overlay-button-color)
//
// metrics on post buttons
//
style-inline-metric(testid, variable) {
unless variable {
[data-testid={testid}] {
> div {
display: none
}
}
}
}
style-inline-metric("likeBtn", show-likes-inline)
style-inline-metric("replyBtn", show-replies-inline)
// the repost button dosent have a test id, for some reason.
unless show-retweets-inline {
[data-testid=repostCount] {
display: none
}
}
//
// expanded metrics
//
style-expanded-metric(testid, variable, name) {
if variable == "nonum" {
[data-testid={testid}] {
span {
visibility: hidden;
overflow: hidden;
width: 0;
display: inline-flex;
}
font-size: 0px !important;
// - capitalize it since there's no number
// - obscure the count by always being plural
&::before {
font-size: 16px;
content: name;
}
}
} else if variable == "hide" {
div:has(> [data-testid={testid}]) {
display: none;
}
}
}
style-expanded-metric("likeCount-expanded", show-likes, "Likes")
style-expanded-metric("repostCount-expanded", show-retweets, "Reposts")
if show-likes == "hide" and show-retweets == "hide" {
// just hide the entire toolbar
div:has(> div > [data-testid={repostCount-expanded}]) {
display: none;
}
}
unless hide-top-bar-on-scroll {
// topbar
div:has(> [data-testid=homeScreenFeedTabs]) {
opacity: 1 !important;
transform: none !important;
pointer-events: unset !important;
}
}
unless hide-bottom-bar-on-scroll {
// nav
[style*="padding-bottom: 15px"]:has(> div[role=link]) {
opacity: 1 !important;
transform: none !important;
pointer-events: unset !important;
}
// back to top fab
button[aria-label="Load new posts"] {
transform: translateY(-44px) !important;
}
// compose fab
button[data-testid="composeFAB"] {
transform: translateY(-44px) !important;
}
}
if image-viewer-buttons-on-bottom {
button-height = 44px;
button-margin = 10px;
button-area-height = button-height + button-margin * 2
[aria-label="Close image viewer"] {
--button-area-height: button-area-height;
pointer-events: none;
max-height: calc(100% - var(--button-area-height));
button {
pointer-events: all;
top: unset;
bottom: (button-margin + button-height) * -1;
}
}
div:has(> [aria-label="Close image viewer"]) {
> div:has(button) {
top: unset;
bottom: button-margin;
left: 50%;
right: unset;
transform: translateX(-50%)
button {
margin: 0;
}
}
}
// show alt as a button as well
div:has(> div[aria-label="Expand alt text"]) {
margin-bottom: button-area-height;
&:has(> div > [style*="line-clamp"]) {
* {font-size: 0 !important;}
margin-bottom: 0;
position: absolute;
bottom: button-margin;
left: 50%;
transform: translateX(-50%) translateX(-48px)
padding: 0;
height: button-height;
width: button-height;
align-items: center;
justify-content: center;
border-radius: 999999px;
background-color: overlay-button-color;
backdrop-filter: blur(10px);
div[aria-label="Expand alt text"] {
width: 100%;
height: 100%;
position: absolute;
}
&::after {
content: "ALT";
font-size: 12px;
font-weight: 800;
}
}
}
}
}
@-moz-document regexp("^\\S+:\\/\\/bsky\\.app\\/profile\\/[^\\/]+$") {
//
// show buttons on scroll
//
if more-profile-buttons-on-nav {
gap = 10px;
padding = 8px + 14px;
button-width = 40px;
body-apply-for-children($amount) {
body:has([data-testid*="UserAvatar-Container-"].r-1v6e3re ~ div > :nth-child({$amount + 1})) {
[data-testid="TopNavBar"] [data-testid*=follow] {
margin-left: (gap + button-width) * $amount;
}
}
}
for num in (1..4) {
body-apply-for-children(num)
}
body:has([data-testid="profileHeaderDropdownListAddRemoveBtn"]) {
overflow: hidden
}
body.dgb-back-to-top-shown {
//body {
// a bit of a hack, but it dosen't seem to break anything
#root > div .css-175oi2r:has([data-testid="profileHeaderDropdownBtn"]) {
z-index: unset;
}
div:has(> div > [data-testid="profileHeaderDropdownBtn"]) {
// prevent shifts
height: 48px;
button(pos) {
animation: fade-in 100ms;
@keyframes fade-in {
0%{opacity: 0}
100%{opacity: 1}
}
right: padding + (button-width + gap) * pos;
}
[data-testid="profileHeaderDropdownBtn"] {
// solves the dots getting squished
padding: 0 !important;
height: 100%;
width: 100%;
}
[data-testid="profileHeaderEditProfileButton"] {
div {
font-size: 0 !important;
line-height: 0 !important;
&::after {
content: "Edit"
font-size: 14px !important;
line-height: 14px !important;
}
}
}
[data-testid*="followBtn"] {
// hide text in follow button
div:last-child {
display: none;
}
// center icon
padding: 0 !important;
display: flex;
align-items: center !important;
justify-content: center !important;
div:first-child {
margin: unset !important;
}
}
apply-for-children($amount) {
&:has(> :nth-child({$amount + 2})) {
> * {
position:fixed;
top: 2px;
z-index: 2 !important;
// force...