this repo has no description

Add bluesky brand guide and updated styles

dame 175bef29 b0e10bfc

.DS_Store

This is a binary file and will not be displayed.

+35
.gitignore
··· 1 + # Dependencies 2 + node_modules/ 3 + .pnp 4 + .pnp.js 5 + 6 + # Testing 7 + coverage/ 8 + 9 + # Production 10 + build/ 11 + dist/ 12 + .next/ 13 + out/ 14 + 15 + # Misc 16 + .DS_Store 17 + .env.local 18 + .env.development.local 19 + .env.test.local 20 + .env.production.local 21 + .env 22 + 23 + # Debug 24 + npm-debug.log* 25 + yarn-debug.log* 26 + yarn-error.log* 27 + 28 + # IDE 29 + .idea/ 30 + .vscode/ 31 + *.swp 32 + *.swo 33 + 34 + # Vercel 35 + .vercel/
+377
html-example.html
··· 1 + <head> 2 + <meta charset="UTF-8"> 3 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 4 + <title>How to use Bluesky to grow your brand</title> 5 + <link rel="stylesheet" href="../styles.css"> 6 + <link rel="stylesheet" href="guide-styles.css"> 7 + <script src="https://unpkg.com/react@18/umd/react.production.min.js"></script> 8 + <script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script> 9 + <script src="https://unpkg.com/liquid-glass-react/dist/index.js"></script> 10 + <script defer="" src="../theme.js"></script> 11 + </head> 12 + <body><div class="reading-progress"><div class="progress-bar"></div><div class="progress-sections"><div class="progress-section" style="width: 14.2857%;"></div><div class="progress-section" style="width: 14.2857%;"></div><div class="progress-section" style="width: 14.2857%;"></div><div class="progress-section" style="width: 14.2857%;"></div><div class="progress-section" style="width: 14.2857%;"></div><div class="progress-section" style="width: 14.2857%;"></div><div class="progress-section" style="width: 14.2857%;"></div></div></div> 13 + <div class="guide-container"> 14 + <main class="guide-content"><div class="reading-time">๐Ÿ“š 38 min read</div> 15 + <div id="markdown-content"><h1>How to use Bluesky to grow your brand</h1> 16 + <p>A comprehensive guide for brands, organizations, and creators</p> 17 + <p><em>Thanks to Protocol Labs for their support in helping make this guide possible.</em></p> 18 + <h2 id="section-0">Introduction</h2> 19 + <p>As of May 2025, Bluesky has over 36 million users. Journalists, politicians, activists, celebrities, and everyday individuals are now using it to engage with the content and communities that matter most to them. If you're wondering whether your brand should be there too, this guide will help you decideโ€”and show you exactly how to succeed if you do.</p> 20 + <p>Getting started on Bluesky is actually very easy, and it doesn't require a big time investment. You don't need to completely change your content strategy or brand voice to make it work. All it takes are a few small tweaks. You can eventually choose to dive deeper and use Bluesky's more advanced features, but they are not necessary.</p> 21 + <p>But first, why should you listen to me about this? Well, I've been on Bluesky since basically the beginningโ€ฆ I was account #1,216. I also set up one of the first brand accounts there, back when that wasn't something anyone thought was worth doing. My background is in social media and community growth, but nowadays I create resources that help people have a more enjoyable experience online. My hope is that I can use all the hours I've invested on Bluesky to make it more accessible to everyone else, including brands and communities that want to connect with their audiences there.</p> 22 + <p>We'll start with the basics if you're itching to jump right in, and then proceed to covering the complexities if you're ready to take things to the next level.</p> 23 + <p>Let's do this.</p> 24 + <h2 id="section-1">Quick Start Guide</h2> 25 + <ol> 26 + <li><p>Create your account.<br>Go to <a href="https://bsky.app/">bsky.app</a> and click "Create account".<br>Choose an initial default username.</p> 27 + </li> 28 + <li><p>Setup your profile.<br>Add an avatar, banner image, display name, and description.<br>Include a link to your website in your bio.</p> 29 + </li> 30 + <li><p>Connect your custom domain.<br>Usernames on Bluesky are just domain names. You get a default one to start, but if you want to look credible and trustworthy, you should likely use your own domain.<br>Go to <a href="https://bsky.app/settings/account">bsky.app/settings/account</a> and choose "Update Handle".<br>Press the "I have my own domain" button.<br>Update your domain's DNS settings with the records provided. If you work at a company, you may have to get your IT department to make this change for you.<br>Wait for the DNS record changes to take effect (usually within 15 minutes, could be 24 hours).</p> 31 + </li> 32 + <li><p>Make your first post.<br>Introduce yourself, announce your latest news, or share something helpful within your niche. It's probably best to wait to do this until your handle updates to your custom domain though.</p> 33 + </li> 34 + <li><p>Share about it on other channels.<br>Let your world know they can find you on Bluesky now. </p> 35 + </li> 36 + <li><p>Add your Bluesky link to your website.<br>If you have icons or links to your social media profiles on your website, add Bluesky to that list.</p> 37 + </li> 38 + </ol> 39 + <p>Now, while you wait for your custom domain to finish connecting or your IT department to make the necessary configurations, let's learn more about what makes Bluesky tick.</p> 40 + <h2 id="section-2">Understanding Bluesky</h2> 41 + <h3>Backstory</h3> 42 + <p>In 2019, the CEO of Twitter announced an internal research project called "Bluesky". The goal was to explore the feasibility of creating a decentralized internet protocol that could power Twitter and other social media platforms. After a couple of years and an initial funding pool of $13 million contributed by Twitter, Bluesky became an independent organization with zero ties to its originator. In late 2022, Bluesky privately launched its new social media app to a small group of test users. The app was very similar to Twitter, but it was powered by what became known as the AT Protocol.</p> 43 + <h3>The AT Protocol</h3> 44 + <p>While you might be tempted to gloss over the decentralized technology that underlies Bluesky, learning the basics of it can be extremely helpful in both understanding part of the Bluesky culture and also tapping into the unique opportunities it can provide.</p> 45 + <p>On a traditional social media site like Twitter or Facebook, all of the user data is controlled by one centralized entity โ€” a for-profit corporation โ€” and stored within that entity's database in a proprietary data format that will only work with their app. If that social media app goes rogue, declines in quality, or gets bought by a billionaire, then the users don't have much of a choice in the matter... they either have to forfeit all of the followers and content they amassed over a long period of time or continue using the platform despite all of its toxicity and general crappiness.</p> 46 + <p>The AT Protocol was built to solve this problem, and within a short period of time it has come a long way towards reaching its goals. You can now own your social networking data, take it with you, and hopefully avoid being locked into one specific app in the future.</p> 47 + <h3>Where your data is stored</h3> 48 + <p>When you create an account on Bluesky, you're actually creating an AT Protocol account, even if you don't realize it. When you make a post on Bluesky, reply to another user, or like an image, all of that data gets stored in what's called your account's Personal Data Server (PDS). The PDS is basically just a file folder containing lots of different files that get stored on a server in the cloud. You can decide which company (or even yourself) "hosts" your data, similar to how a website is hosted. By default, new Bluesky accounts have their PDS data hosted on one of many different Bluesky servers, each of which is named after a different type of mushroom.</p> 49 + <p>shiitake.us-east.host.bsky.network<br>oyster.us-east.host.bsky.network<br>maitake.us-west.host.bsky.network</p> 50 + <p>If Bluesky were to ever shut down or become hostile, users could move their data to a new host and still maintain all of their likes, followers, and posts. </p> 51 + <p>Think of it like your email providerโ€“there are many different companies that provide email hosting services such as Gmail, Outlook, and iCloud. If you ever need or want to, you can download your email archive and contacts and import that data into another email provider. The reason this is possible is because all of these companies chose to build their product on top of the decentralized protocols that emailing technology uses. In that sense, the AT Protocol is sort of like email but for social media.</p> 52 + <h3>How Bluesky is different from Twitter</h3> 53 + <p>Many people think Bluesky is just a decentralized version of Twitter. And while that's true in some ways, Bluesky has many features that Twitter (now X) never had and likely never will have. Let's walk through each of these features now and then revisit some of them later to discuss how to use them effectively.</p> 54 + <h4>1. Custom feeds (algorithms)</h4> 55 + <p>Perhaps its flagship feature, Bluesky has become famous for how it allows anyone to not only choose which algorithm their feed uses, but also build their own. Gone are the days of being forced to scroll through a non-chronological feed containing posts from random accounts you've never followed. For creators and brands, this means that your primary goal is no longer just "appease the algorithm"โ€ฆ because there isn't just one singular algorithm to please.</p> 56 + <p>Bluesky comes with two optional feeds out of the box: 1) Discover, and 2) Following. Discover is Bluesky's proprietary suggestion algorithm similar to TikTok's "For You" feed. The difference of course is that no one has to use the Discover feed or even have it appear in their app at allโ€ฆ you can disable it completely and rely upon the Following feed, another community-made feed, or a custom feed of your own design.</p> 57 + <p>[MAYBE: PRODUCT SCREENSHOT]</p> 58 + <h4>2. Third-party moderation services</h4> 59 + <p>After the AT Protocol launched, there was a lot of talk about the concept of "composable moderation", which basically just means a suite of tools and infrastructure that would let anyone provide moderation services on top of the network. One of the most well known examples of this is a service called Blacksky which provides a space for Black community building. Users can report posts/content to this third-party service and filter content out of their feeds based on it instead of Bluesky's official moderation.</p> 60 + <p>There's even <a href="https://bsky.app/profile/xblock.aendra.dev">a moderation service that automatically detects and flags posts that contain screenshots</a> from other prominent social networks like X, allowing users to hide this content from their feeds completely. So, cross-posting by using screenshots might not be the wisest idea.</p> 61 + <p>Bluesky also has "moderation lists" that any user can create and subscribe to. On a basic level, these are just lists of accounts, but you can use them to automatically block or mute accounts in bulk with one tap. For instance, there are a series of <a href="https://bsky.app/profile/automated-lists.bsky.social">semi-popular moderation lists</a> for blocking or muting all accounts that follow more than a certain number of accounts (10,000+, 50,000+, etc). Lists like these allow people to keep follower farmers away from them before they even have a chance.</p> 62 + <p>[MAYBE: PRODUCT SCREENSHOT]</p> 63 + <h4>3. Labelers + content filters</h4> 64 + <p>Connected to third-party moderation services are a special tool called "labelers". A labelerโ€ฆ well, it applies labels to things. There are moderation labelers that allow you to hide, warn, or filter posts/accounts based on specific labels (i.e. like the screenshot labeler mentioned earlier), but then there are also vanity labelers that allow users to self-identify with communities, causes, and more by applying a voluntary public label to their own account. For instance, there's a <a href="https://bsky.app/profile/eras.bsky.sh">vanity labeler for Swifties</a> that lets them add a little "flair" to their profile in the form of their favorite Taylor Swift Era. Custom labelers of all types and sizes can be created by anyone, though the process to do so is still quite technical at this time.</p> 65 + <p>[MAYBE: PRODUCT SCREENSHOT]</p> 66 + <h4>4. Domain names as usernames</h4> 67 + <p>Ever been frustrated when joining a new app and learning that your username of choice has already been taken? On Bluesky, this problem is not nearly as significant thanks to the fact that all usernames on the network are actually domain names. When you create a new account, you can get a default username that looks like <code>name.bsky.social</code>, but you can (and should) set up your own because brands and creators without default names are sometimes seen as potential impersonators. </p> 68 + <p>Using your own domain name is a way to self-verify your account's legitimacy. For instance, NPR (National Public Radio) has the username of @npr.org, which is also its official website. Only the owner of the domain can possibly set it as a Bluesky username, so you know with confidence that the account is official. There are even many government accounts that can be quickly identified by their country's government domain structure (such as .gov in the United States).</p> 69 + <p>You can even set up subdomains as usernames, so if you are a company or part of an organization, you could set up team member accounts such as name.brand.team. Bluesky itself does this for many of its employeesโ€”check out the CEO's handle: @jay.bsky.team</p> 70 + <p><a href="https://bsky.social/about/blog/4-28-2023-domain-handle-tutorial">How to use your domain as your Bluesky handle</a> </p> 71 + <p>[MAYBE: PRODUCT SCREENSHOT]</p> 72 + <h4>5. Trusted Verifiers</h4> 73 + <p>In addition to being able to self-verify your account via your domain name, there is also a unique verification system run by Bluesky that functions similarly to the original "blue check" from Twitter before it became X. While it may look the same on the surface though, there's actually a lot more going on.</p> 74 + <p>Bluesky itself can issue blue check verifications like you would expect, but it also can designate a third-party organization like the New York Times to be considered a "Trusted Verifier", meaning that there are now multiple independent sources for verification. At launch, there are only a handful of "Trusted Verifiers", but Bluesky says they will be increasing this over time.</p> 75 + <p>Furthermore, the underlying technology that makes this system possible is completely composable, meaning that any user on the network can issue a "verification" record for any other account and theoretically become a "Trusted Verifier" themself. While the odds of this are relatively low in the official Bluesky app, there are already third-party Bluesky client apps like <a href="https://deer.social">deer.social</a> that allow you to fully customize and set your own list of "Trusted Verifiers". This means that you could have blue check marks appear next to accounts verified by your friends or colleagues rather than from Bluesky or the New York Times. </p> 76 + <h4>6. Free, open API</h4> 77 + <p>Back in the day, Twitter used to have a free and relatively open stream of public data that was accessible via an API (Application Programming Interface). But when Elon Musk took over, he quickly shut this API down and started charging exorbitant prices to access the data. If you're unsure of what an API is or why it's significant, just compare the vibrant ecosystem of third-party integrations and applications that exists on Bluesky with what X has today. Bluesky is like a lush beautiful garden filled with lots of diverse plant life, and X is a vacant backlot with some old and dying corporate landscaping. An API is merely a highway that developers and creators can use to retrieve and send data. With Bluesky, that highway is open, free, and lets you explore the world. With X, only the wealthiest can drive on the highway and there are speed traps everywhere.</p> 78 + <p>An open and free API means an environment friendly to innovation, network effects, and the public good.</p> 79 + <h4>7. Everything is public</h4> 80 + <p>Speaking of the public, almost everything you do on Bluesky at the moment is publicly accessible data (even blocks and likes). This won't matter that much to brands, organizations, and creators who explicitly want their content to be public, but it might matter to some of the people in your audience or community. Stay mindful about it, and just know that there are long-term plans to introduce some form of private accounts or content in the future. </p> 81 + <h4>8. In-app media players</h4> 82 + <p>On most social platforms, sharing a YouTube or Spotify link won't play the media directly within the app. Instead, users have to leave the platform and open YouTube or Spotify to watch or listen. On Bluesky, this isn't the case. You can listen to music or watch embedded videos without having to leave the app. </p> 83 + <h4>9. No outbound link suppression</h4> 84 + <p>Since we're on the subject of sharing links, now's a great time to mention that another defining and powerful feature of Bluesky is that its "Discover" feed algorithm doesn't suppress and down rank content that contains links to outside websites. This is huge for companies, brands, and creators because you no longer need to perform silly algorithmic gymnastics just to get followers onto your website.</p> 85 + <h4>10. Starter packs</h4> 86 + <p>One of the difficult parts of joining a new social network is finding accounts that share your common interests and values. Bluesky tackled this issue by introducing an industry-first that was soon copied by Meta/Instagram: Starter Packs. </p> 87 + <p>At their core, Starter Packs are hand-curated lists of accounts that anyone can make. Each starter pack even has an invite-link attached to it. By sharing a Starter Pack with someone, they can create a new Bluesky account and follow a bunch of relevant accounts instantly. This feature works especially well for community building, whether that's for a city (Boston!), a fandom (kpop!), or even bicycle riders. If you're a brand, you could set up a Starter Pack that has your entire team in it or trusted industry voices.</p> 88 + <h4>11. Advanced preferences</h4> 89 + <p>Hidden under the surface of Bluesky are a plethora of user customizations that apply to the "Following" feed (the reverse-chronological feed of just posts from accounts someone follows) and beyond. For instance, you can disable seeing reposts, quote posts, and replies. Within threads, you have the option of ordering the replies however you want: most liked, oldest, newest, prioritize following, hot, or even random (aka "Poster's Roulette"). </p> 90 + <h4>12. Advanced reply/quote settings</h4> 91 + <p>Not only can you turn off replies to your Bluesky posts, but you can also customize reply permissions in extremely granular ways. For instance, you can specify that only users on a custom list can contribute to the conversation or make it so that someone has to be following you in order to reply. Additionally, you can prevent people from quote-posting your content completely. Both of these features are even available to apply retroactively.</p> 92 + <h4>13. Nuclear block</h4> 93 + <p>Bluesky has intentionally tried to cultivate an environment where users have more control over who can interact with them. One of the ways they achieved this is through what is colloquially known as the Nuclear Block. If two people are having a conversation on Bluesky and one blocks the other, the entire conversation thread becomes hidden from view for everyone else. This feature is a bit controversial, having divided the community into two camps: people who love it and people who hate it. There's not much that can be done about it though, so it's good to keep in mind if you ever run into a situation where you see a "blocked" message in a thread and wonder what's going on. It doesn't mean that you've been blocked by anyone.</p> 94 + <h4>14. Paragraph breaks in bios</h4> 95 + <p>Lastly, it's a small detail, but Bluesky profiles offer flexible bio formatting, making it easy to create a clean, readable description without needing tedious tricks or workarounds.</p> 96 + <h3>Audience and Culture</h3> 97 + <p>Over the course of Bluesky's two-year history, its culture has shifted as the network has become more populated. When there were only several thousands users, the space reflected the industry it sprang out of, so it was very centered on the United States' technology scene. Eventually there were growth periods that were dominated by other countries such as Japan or Brazil, with the latter case happening due to X temporarily abandoning Brazilian users entirely.</p> 98 + <p>The biggest growth event that the network has experienced so far though was in the days surrounding the 2024 United States presidential election, at which point millions of people created new Bluesky accounts in an effort to migrate off of big tech social networks like Facebook, Instagram, and X.</p> 99 + <p>In the months that followed, many people mistakenly started to assume that Bluesky had effectively become a politically left-leaning platform just for democrats. In reality, the culture of Bluesky is so multi-faceted at this point that it really doesn't make sense to think about it with such a hegemonic lens. For starters, there isn't a singular "Bluesky Culture" anymore. The user base is made up of millions of people from all over the world with an innumerable number of niche interests.</p> 100 + <p>Start thinking about Bluesky as a diverse system of hundreds (if not thousands) of large to small sub-cultures. Approaching the network with this perspective will better prepare you for all the different types of people you likely will encounter on any given day, and it will help you find the communities you are most interested in.</p> 101 + <h3>Why people use Bluesky</h3> 102 + <p>While it's true that Bluesky is hard to pin-down culturally, there are a variety of big ideas that tend to attract people to it and influence it on a monocultural level. Keeping these themes in mind might help you avoid some faux paus.</p> 103 + <h4>1. It's an open, decentralized, interoperable network.</h4> 104 + <p>For the past two decades most people have only experienced an online social network that is both centralized and closed. Platforms like Facebook, TikTok, and X became known for trying to keep people on their apps for as long as possible to make money off of advertisements, even if it meant making the user experience worse by pushing invasive algorithms and hiding links to outside websites.</p> 105 + <p>As the years went on and the user experience continued to degrade, more and more people started to be fed up. A new future began to be conceived of, and experimental versions of alternative social apps started to gain some traction. The properties that defined these platforms were usually one or all of the following:</p> 106 + <h5>A. Open</h5> 107 + <p>A significant portion of the network's data is publicly accessible via open APIs and storage. Additionally, anyone can create and host their own account.</p> 108 + <h5>B. Decentralized</h5> 109 + <p>The network is designed and implemented in such a way that one centralized entity can't own or control everything within it. Decentralization exists on a spectrum, but in general a decentralized social network will be accessible from many different apps called "clients" and the data on the network is somewhat distributed across many different "nodes" or "hosts".</p> 110 + <h5>C. Interoperable</h5> 111 + <p>An "interoperable" network means that the data and features that exist in one place can be easily used in many different places. A simple example of this would be multiple different apps that rely on the same user profile data. For instance, you can login to a new app and it could recognize your existing profile data that you made on Bluesky, and it would automatically use that for your profile. </p> 112 + <h4>2. There aren't any adsโ€ฆ yet.</h4> 113 + <p>One of the primary reasons traditional corporations implement user-hostile things like link suppression and intrusive algorithms is to maintain their revenue model: advertising. Their primary incentive is to keep users in their apps for as long as possible at all costs.</p> 114 + <p>With Bluesky, an advertising model is not nearly as feasible or sustainable due to the underlying protocol's design and open nature. If Bluesky began having advertisements in their app, people could easily choose to consume the same content through the "window" of a third-party app that doesn't have ads.</p> 115 + <p>In this situation, the primary way to keep people using your app (and even get them to pay for it!) is to offer an unparalleled positive experience with quality features. This framework better aligns the company's incentives to be in step with users' needs. This isn't to say that advertising will never exist within Bluesky, but at the very least it won't manifest in the exact same way as it has in the past.</p> 116 + <h4>3. There's more control.</h4> 117 + <p>One of the many benefits of an open and interoperable network is that users have greater control over their individual experiences. Bluesky empowers users with a significantly higher degree of agency in comparison to traditional social platforms. This reality also contributes to Bluesky's multicultural environment... for any given user that you meet, you can't assume that they are engaging with the network using the same "lens" that you are.</p> 118 + <h4>4. It's not "big tech".</h4> 119 + <p>Given the fact that almost all of the major social media platforms in the United States have decided to align themselves with specific government administrations and policy proposals, even to extreme degrees, many people are uncomfortable spending their time with products made by those companies. Bluesky does not have the same affiliations or baggage, making it an attractive alternative.</p> 120 + <p>Now that you have a solid understanding of Bluesky, its underlying technology, and its multifaceted community, you're ready to start thinking about how you can practically fit into the equation.</p> 121 + <h2 id="section-3">Achieving Growth and Engagement</h2> 122 + <p>If you're reading this guide, then you're probably not on Bluesky just to have fun and blow off steam. You're likely an organization or a public figure that has a product, service, or cause that you need to promote to achieve your goals. In order to do that, you need to get peoples' attention and sustain their interest in what you have to say. Thus, you need followers and engagement.</p> 123 + <p>Attracting followers wasn't easy on traditional social media, so it's not easy on a decentralized network like Bluesky either. Plus, there aren't many ways to do advertising on Bluesky, so you can't reliably use "paid media" to force your way into peoples' attention spans. Instead, you have to provide valuable content that gets spread organically. This guide won't be teaching you how to do that, but it will be showing you how to apply that content creation to the unique context of Bluesky in a way that maximizes its potential. </p> 124 + <h3>Formatting and packaging your content for Bluesky</h3> 125 + <h4>1. Use links directly, without any weird hacks</h4> 126 + <p>As we've discussed in previous sections, Bluesky does not suppress outbound links to other websites. This means that you don't need to perform the same algorithmic gymnastics just to sneak in a link to your website. Instead of making a main post and then following up with a reply containing the link or pointing to your bio, just put the link in the main post. If you keep using the workarounds from other platforms when you post to Bluesky, many people will see your activity as half-hearted, ignorant, or even cringe. On Bluesky, people like links and don't want to have to go searching for them in weird places.</p> 127 + <p>Don't: 'Great article! (link in bio)' </p> 128 + <p>Do: 'Great article on sustainable fashion: [direct link]'</p> 129 + <h4>2. If you post images, always include alt text</h4> 130 + <p>More so than in other places, a significant part of Bluesky's culture cares a lot about web accessibility. By including alt text on your images, you're being inclusive to people with certain disabilities, but you're also making your content more discoverable because alt text is included in search results! For instance, if you post a screenshot of a document or promotional graphic, you should include all of that content as alt text too. If you didn't do this, then your post would not end up in relevant search results where it otherwise could and should. There are even Bluesky users who refuse to share content from other accounts if alt text isn't present.</p> 131 + <h4>3. Be conservative and mindful with your hashtag usage</h4> 132 + <p>While hashtags are supported on Bluesky and used in many different contexts, including them in all of your posts can be seen as a desperate attempt for attention and a form of engagement hacking in many spheres. Depending on your industry and context though, the usage of hashtags might be more normal and prevalent. It's wise to take a look at what other accounts in your community/niche are doing to see what is and isn't common practice.</p> 133 + <h4>4. Consider the variables or conditions of relevant topical feeds</h4> 134 + <p>Remember, Bluesky doesn't just have one algorithmic feed. It has a limitless number of customizable user-generated algorithms in addition to its default "Discover" algorithm. Many people disable the default algorithm completely and choose to rely upon more niche or topical feeds instead. For instance, there are community-controlled feeds dedicated to lovers of mushrooms/fungi, web development, and academic research. Each of these feeds has a custom algorithm with different conditions/rules that dictates which posts or content show up in the feed (for instance, this is often a good use-case for hashtags). Consider if there are any popular feeds in your niche that would welcome your contributions. If you do find such feeds, try to see if there is any public documentation available that shows how the feed works, and then use that information appropriately while crafting your related content.</p> 135 + <p>Be warned though, if your activity violates the norms or rules of a custom feed, your entire account could get banned from ever appearing in it again. Don't be a bad actor.</p> 136 + <h4>5. There is no edit button, so spell-check your content carefully</h4> 137 + <p>Most social platforms these days allow for editing a post if you make a typo or need to add something after the fact. Bluesky does not currently support this feature. While it is technically possible to edit posts under the hood on the protocol level, doing so is a bit technical, requires third-party tools, and usually is not advisable (it also will reset the post's engagement stats).</p> 138 + <h4>6. You can embed videos and audio from some third-party services directly in a post</h4> 139 + <p>While it doesn't make sense to post a YouTube link on Twitter/X anymore because of suppression, Bluesky embraces offsite media content and allows users to watch/listen without ever leaving the app. At the time of writing, the following services are supported: YouTube, Vimeo, Twitch, GIPHY, Spotify, Apple Music, Soundcloud, and Flickr.</p> 140 + <h3>Leveraging Bluesky's unique features for growth and engagement</h3> 141 + <h4>1. Labelers (Advanced/Expert)</h4> 142 + <p>A Labeler is a customizable integration that can be used to display helpful information within the Bluesky app. They are used to apply textual and visual labels to either specific accounts or specific posts. These labels are only visible to users who "Subscribe" to the Labeler, meaning users must explicitly opt-in for the tags to appear in the user interface of their app.</p> 143 + <p>While Labelers were originally designed as a "serious" moderation tool, they have now evolved to include even more casual or even silly use cases. For instance, the official Bluesky company released a Superbowl Labeler for the 2025 Superbowl which allowed users to apply a label to their accounts to display which football team they were rooting for. Other Labelers use more advanced technology to automatically analyze and categorize accounts/posts based on publicly available data. </p> 144 + <p>Let's say you are a video game studio with a beloved game that has many different playable characters. You could create a Labeler that would allow your community members to tag their profile with their preferred character's name. Or, maybe you are a coffee shop chain and want to create a Labeler that would allow people to display their favorite drink order.</p> 145 + <p>Here is a list of some different Labelers to give you more inspiration and ideas:</p> 146 + <ul> 147 + <li><a href="https://bsky.app/profile/labeler.bikesky.social">Bikesky Labeler</a> </li> 148 + <li><a href="https://bsky.app/profile/did:plc:h2btirb6mgshvzf2jqrsggbc">Super Bowl Labeler</a> </li> 149 + <li><a href="https://bsky.app/profile/pronouns.diy">Pronouns Labeler</a> </li> 150 + <li><a href="https://bsky.app/profile/oracle.posters.rip">The Cave of Trophonius</a> </li> 151 + <li><a href="https://bsky.app/profile/xblock.aendra.dev">Screenshots Labeler</a></li> 152 + </ul> 153 + <p>Creating your own Labeler is not user-friendly at the moment, but it is possible if you have some coding/development skills. To get you started <a href="https://github.com/aliceisjustplaying/labeler-starter-kit-bsky">check out this Github Repository</a> that a community member created to serve as a starter kit for creating a Labeler.</p> 154 + <h4>2. Feeds (Easy/Moderate)</h4> 155 + <p>One of the most popular, powerful, and relatively easy things you can do is create a custom feed using the no-code tool <a href="https://graze.social">graze.social</a>. In fact, I would recommend creating at least two different feeds: 1) an internal feed you use for actively listening for mentions of your brand, product, or cause, and 2) a public community feed that ties in with your niche in some way.</p> 156 + <p>The internal feed is designed to include only mentions of your brand name, links to your website, or important keywords for monitoring trends. You, your social media manager, or customer support team can use this feed to stay on top of feedback or community questions. Keep in mind though that any feed you create is public and could be followed by any other user.</p> 157 + <p>The public feed is your brand's contribution to the community. It should be a highly valuable feed that displays curated or relevant posts from other accounts that are about or adjacent to your niche. This feed shouldn't be specifically for just the people that are aware of you, but rather it should be for appealing to newcomers first and foremost. By doing this, you're providing a valuable free service to people and getting your brand name out there at the same time. Be tactful in how you promote yourself though using this method, most people are not going to be interested in a feed that is created or managed in a way that comes across as self promo or advertising.</p> 158 + <h4>3. Starter packs (Easy)</h4> 159 + <p>As a reminder, Starter Packs are just curated lists of Bluesky accounts that you're recommending other folks check out or follow. They are helpful for sending to new community members or onboarding people to Bluesky itself. When you send a Starter Pack link to someone, they can easily follow all of the accounts contained within it and create their very own Bluesky account using the pack's special invite link.</p> 160 + <p>When thinking about creating your own Starter Packs, one simple option would be to create a Starter Pack with prominent public-facing members of your organization or team that you want people to also follow. Another idea would be to curate a list of adjacent brands and content creators within your niche. For instance, if you are a fashion designer, then you could put together a list of other prominent designers and clothing labels within your industry. There's a chance your Starter Pack might be well-received and get spread around a lot, thereby earning you some attention for it.</p> 161 + <p>You can even include up to three custom feeds in a Starter Pack, so people can use them to find the feeds you created earlier as well.</p> 162 + <h4>4. Custom domains / handles (Moderate)</h4> 163 + <p>While the primary purpose of domain names as usernames on Bluesky is for self-verification purposes, people have begun using subdomains for community identification and recognition. A subdomain is when you see a normal domain like example.com but it has an extra part tacked on at the beginning such as extra.example.com, with the "extra" part being the subdomain. This can be powerful when you pair it with a tool like <a href="https://handles.net/">handles.net</a> which allows you to give custom subdomains to your community members, volunteers, or team members.</p> 164 + <p>For instance, Taylor Swift fans can get their very own free username that uses the swifties.social domain, making it easy for other Swifties to identify them as part of the group. So your username could be @hannah.swifties.social. The Bluesky team itself uses this for some of their team members, such as @jay.bsky.team. If you only want to issue a handful of subdomain handles, then you can do this fairly easy within your domain manager's DNS settings, but if you intend to issue a large number of usernames then it's best to get your IT team to assist or use a service like <a href="https://handles.net/">handles.net</a>.</p> 165 + <h3>Case Studies</h3> 166 + <h4>The City of Boston</h4> 167 + <p><a href="https://bsky.app/profile/boston.gov">https://bsky.app/profile/boston.gov</a></p> 168 + <p>The most populous city in the commonwealth of Massachusetts is also one of the most active municipalities on Bluesky currently. Boasting over two dozen interconnected government-owned accounts, Boston's embrace of Bluesky is a great blueprint for how a large entity could join the social network. </p> 169 + <p>Each of their major city departments has its own Bluesky account with consistent branding and official usernames that are self-verified using the subdomain system. Anyone can instantly recognize a Boston account as being official because they all end in the <a href="https://boston.gov">boston.gov</a> domain name. Furthermore, they have a Starter Pack that contains all of the city accounts, plus the mayor, in one place so that Boston residents can quickly follow all of these local sources with one tap.</p> 170 + <p>Boston even has a unified custom feed that you can follow to see just city updates across all departments in one timeline, clutter free.</p> 171 + <h4>Bikesky</h4> 172 + <p><a href="https://bsky.app/profile/bikesky.social">https://bsky.app/profile/bikesky.social</a></p> 173 + <p>A bicycle rider named Derek van Vliet took it upon himself to organize a community on Bluesky for bike enthusiasts. He called it "Bikesky", and it now has over 12,000 followers. It also supports almost every single one of Bluesky's unique features such as custom feeds, labelers, starter packs, and even a moderation service. </p> 174 + <p>The Bikesky labeler lets a community member add a tag to their profile to display what kind of bike enthusiast they are. These labels range from "Tandem Bike Partner", "Recumbent Rider", to even "Folding Bike Fan". This tool makes it easy for community members to recognize and connect with one another, especially those that share common interests or traits.</p> 175 + <p>Bikesky even has over a dozen custom feeds so that bike enjoyers can find niche content created within the biking community. Many of these feeds gather/curate posts automatically by matching the #Bikesky hashtag.</p> 176 + <h4>Bandcamp</h4> 177 + <p><a href="https://blog.bandcamp.com/2025/03/17/now-introducing-our-integration-with-bluesky/">https://blog.bandcamp.com/2025/03/17/now-introducing-our-integration-with-bluesky/</a></p> 178 + <p>The popular online music platform for independent recording artists known as Bandcamp recently launched a first-of-its-kind integration with Bluesky that allows its Pro users to set their Bluesky username to be their Bandcamp subdomain such as <code>artist.bandcamp.com</code>. It's unclear how many of its users have adopted the feature yet, but either way it serves as a powerful example of how another platform can choose to connect to Bluesky and let its community self-identify in a way that makes sense to them.</p> 179 + <h2 id="section-4">Analyzing and Measuring success</h2> 180 + <p>Once you've started posting on Bluesky, there might come a time where you need to see if what you're doing is having any effect. How you go about investigating this will largely depend on your specific goals and needs, but in general here are some of the key areas you can pay attention to:</p> 181 + <ul> 182 + <li>Follower count </li> 183 + <li>Engagements received (likes, reposts, replies, mentions) </li> 184 + <li>Engagement rate </li> 185 + <li>Website traffic from Bluesky</li> 186 + </ul> 187 + <p>Unlike other major social media platforms, Bluesky does not yet offer in-app analytics or social media management features that you might be accustomed to. Thanks to its free and public API though, dozens of third-party social media management tools have popped up that can help fill the gap.</p> 188 + <p>While most of these tools are free for anyone to use, they vary widely in terms of the quality and set of features they provide. The right combination of tools for you will depend on what you're trying to accomplish or track. If you're a for-profit business with a marketing budget, consider donating or leaving a tip for the creator of the tool you end up using the most! </p> 189 + <h3>Analytics</h3> 190 + <ul> 191 + <li><a href="https://blueskymeter.com/">Bluesky Meter</a> </li> 192 + <li><a href="https://skykit.blue/">Skykit</a> </li> 193 + <li><a href="https://blueview.app/">Blueview</a> </li> 194 + <li><a href="https://skyzoo.blue/">Skyzoo</a> </li> 195 + <li><a href="https://cred.blue/alt-text">Alt Text Rating</a> </li> 196 + <li><a href="https://fedica.com/">Fedica</a> </li> 197 + <li><a href="https://besttimetopost.blue/">Best time to post</a></li> 198 + </ul> 199 + <p>Note: there is no way to count the number of views/impressions that a Bluesky post gets. The core metrics for a post are limited to likes, replies, quotes, and reposts.</p> 200 + <h3>Cross-posting and Scheduling</h3> 201 + <ul> 202 + <li><a href="https://buffer.com/">Buffer</a> </li> 203 + <li><a href="https://fedica.com/">Fedica</a> </li> 204 + <li><a href="https://coschedule.com/">CoSchedule</a></li> 205 + </ul> 206 + <h3>Monitoring</h3> 207 + <ul> 208 + <li><a href="https://deck.blue/">deck.blue</a> </li> 209 + <li><a href="https://graze.social/">graze.social</a> </li> 210 + <li><a href="https://clearsky.app/">ClearSky</a></li> 211 + </ul> 212 + <h3>Utilities</h3> 213 + <ul> 214 + <li><a href="https://bsky-follow-finder.theo.io/">Network Analyzer</a> </li> 215 + <li><a href="https://skeetbeaver.pages.dev/">Skeetbeaver</a> </li> 216 + <li><a href="https://boat.kelinci.net/">Boat</a></li> 217 + </ul> 218 + <h2 id="section-5">Risk Mitigation</h2> 219 + <p>The internet is a wonderful place, but it can sometimes be difficult to navigate safely thanks to the fact that humans are complicated and don't always get along well. They have differing (sometimes opposing) viewpoints, cultural norms, and expectations for everything under the sun. It's important to keep this in mind as you join the Bluesky community because there are some key things you should be aware of and watch out for.</p> 220 + <h3>Sensitive Topics</h3> 221 + <p>In additions to all of the usual touchy subjects in social settings such as politics and religion, Bluesky in particular has several vocal sub communities that are critical or outright opposed to the following issues:</p> 222 + <ul> 223 + <li>Artificial Intelligence (especially Image Generation) </li> 224 + <li>Cryptocurrency and Blockchain </li> 225 + <li>Big Tech (Facebook, Google, etc)</li> 226 + </ul> 227 + <p>If your brand provides products or content that cover any of these topics, keep in mind that you could encounter some pushback or even aggression in the replies to your posts at times. You can reduce the degree to which this becomes an issue by communicating clearly and being prepared with some mitigation strategies.</p> 228 + <h3>Mitigation Strategies</h3> 229 + <h4>Safety features</h4> 230 + <p>One approach to risk mitigation is through effective use of Bluesky's safety features, as well as a few third-party tools. Let's briefly take a look at each technique so that you'll understand how they work.</p> 231 + <p>When using these features it is important to remember that the way in which you use them can sometimes make matters worse if you don't know what you're doing. Also, I don't reference the "mute" feature here because I don't typically recommend brands mute people because you will no longer have visibility into what they are potentially saying under your posts or about you in other places. If you are an individual, personality, or content creator this advice probably doesn't apply.</p> 232 + <h5>Post Interaction Settings (Thread gates)</h5> 233 + <p>[SCREENSHOT]</p> 234 + <p>At any moment you can change who is able to reply to or quote your posts. If a potentially risky or sensitive post starts getting traction outside of your direct audience, you could always preemptively restrict who has permission to reply and quote in order to avoid unwanted engagement. </p> 235 + <p>While it's generally best to let anyone reply to a post, there are plenty of scenarios in which it would make sense to add restrictions. You can even limit replies to specific groups of users by making custom lists.</p> 236 + <p>And if you find yourself in an extreme scenario, there's even <a href="https://boat.kelinci.net/bsky-threadgate-applicator">a third-party tool</a> that helps you limit the replies on all of your past posts in bulk.</p> 237 + <h5>Blocks and blocklists</h5> 238 + <p>For organization or company accounts, it's typically best to avoid blocking people because it could make your brand be perceived negatively, so proceed with caution.</p> 239 + <p>If you're a content creator, influencer, or personality, then utilizing the block feature is much more acceptable and likely not to create any PR issues. Your mental health and safety matters a lot, so protect yourself as is needed.</p> 240 + <p>That being said, you should know that your blocks and blocklists are public data, so anyone can use a tool like <a href="https://clearsky.app/">Clearsky</a> to see who you're trying to keep away.</p> 241 + <h5>Hide replies for everyone</h5> 242 + <p>If you've chosen to let anyone reply to your posts, you might encounter a scenario in which someone says something in the comments that you don't want to be publicly attached to your content. In these situations, you can easily hide a reply for everyone. There are ways to find these posts if people really want to see them, but it can be an effective means of protecting your space from spam or offensive content.</p> 243 + <p>If you're experiencing a pile-on or targeted harassment, my recommendation is to disable replies from accounts you don't follow, disable quote-posts, and then hide any replies that you don't want featured next to your content.</p> 244 + <h3>Active Monitoring</h3> 245 + <p>To stay ahead of the curve, you might want to consider utilizing a few third-party tools that can give you insight into how people are talking about your brand or industry. </p> 246 + <h4>Custom Feeds</h4> 247 + <p>The simplest approach to begin active monitoring is by creating a special "brand mentions" feed using a feed builder tool like <a href="https://www.graze.social/">Graze</a>. This is a unified feed that can be configured granularly to show you a lot more than what the "mentions" part of your notifications can. For instance, you can configure a feed to show you all posts on the network if they match any of the following criteria:</p> 248 + <ul> 249 + <li>@ mentions of your brand accounts </li> 250 + <li>mentions of your brand name that aren't explicitly tagged with an @ </li> 251 + <li>posts containing links that contain your domain name</li> 252 + </ul> 253 + <p>You could even configure a "competitors" feed that displays similar information but for one of your key competitors if you want to stay in the loop with what's happening to them.</p> 254 + <h4>Firehose/Jetstream</h4> 255 + <p>For larger brands and organizations with development resources and heightened monitoring needs, you can directly tap into <a href="https://docs.bsky.app/blog/jetstream">the live stream of raw data flowing through the network</a> to get virtually instantaneous alerts. This mechanism is commonly referred to as accessing the Bluesky/ATProto "firehose", and it requires highly technical skills to implement effectively. For this reason, most people should just rely upon a feed builder like Graze.</p> 256 + <h4>BluNotify</h4> 257 + <p>The official Bluesky app currently only serves push notifications for certain things like new likes, reposts, and mentions. If you're in need of getting alerts when a specific account posts something new, you can download a separate app called <a href="https://bluenotify.app/">BluNotify</a> which seems to have reliable coverage in this area.</p> 258 + <h4>UnfollowTracker</h4> 259 + <p>While the name is focused upon one specific feature, <a href="https://bsky.app/profile/bluesky-tracker.bsky.social">this third-party app</a> actually offers a variety of monitoring features in addition to just showing you when an account unfollows you. You can also see blocks, when people deactivate, or when accounts are banned.</p> 260 + <h4>Clearsky</h4> 261 + <p><a href="https://clearsky.app/">This tool</a> was mentioned earlier in the guide, but it can be helpful for brands as a monitoring application because you're able to see all the lists your account has been put on as well as all the accounts that are blocking you. If you notice a huge spike out of the blue in the number of blockers you have, that might be an indicator that your account is being spread around in a negative context.</p> 262 + <h3>Behavioral approaches</h3> 263 + <h4>Defuse</h4> 264 + <p>In the event that you feel like you must actually engage with a negative comment, it's usually best to not "fight back". Attempt to de-escalate rather than poking the hornet's nest, and don't try to "silence" someone or move them to a private channel... doing so will often just get you in more trouble because it will be seen as an attempt to avoid public accountability.</p> 265 + <h4>Ignore</h4> 266 + <p>Try to ignore negative posts if they are coming from an audience that isn't your audience or that doesn't impact your audience. Spending energy on these efforts is likely a waste of time and a distraction from your mission.</p> 267 + <h4>Introspect</h4> 268 + <p>When you are going to make a post about a topic that is sensitive, think carefully about how you word what you say and if it's necessary. Actively try to see your own words in the worst possible light because this is how some people will be perceiving them.</p> 269 + <h3>Community guidelines</h3> 270 + <p>Lastly, be aware of Bluesky's rules and make sure you don't violate them.</p> 271 + <p><a href="https://bsky.social/about/support/community-guidelines">https://bsky.social/about/support/community-guidelines</a></p> 272 + <h2 id="section-6">Next Steps</h2> 273 + <p>While Bluesky has only been on the social media scene for a tiny fraction of the industry's history, it has already made significant strides towards creating a more consumer-friendly platform and foundation upon which a healthier system could emerge. Establishing yourself as a brand or creator early in the journey positions you for the potential benefits that could come if the AT Protocol succeeds in its mission.</p> 274 + <p>With that being said, you should likely still maintain some sort of a presence on other networks in the meantime. Bluesky is still relatively small in comparison to legacy platforms like LinkedIn, Facebook, and Instagram, so it probably can't meet all your communication needs just yet. It can be a viable alternative to Twitter/X though if you prefer to avoid that place completely.</p> 275 + <p>The information contained in this guide will hopefully get you started, and I will do my best to provide updates as new features come out and dynamics evolve. If you have any questions, feel free to leave a comment below or in the replies to my post on Bluesky.</p> 276 + <hr> 277 + <p><em>Thanks for reading!</em></p> 278 + <p>Dame<br><a href="https://bsky.app/profile/dame.is">Find me on Bluesky (@dame.is)</a></p> 279 + </div> 280 + 281 + <footer class="footer"> 282 + <p>Written by <a href="https://bsky.app/profile/dame.is">Dame</a></p> 283 + <p>ยฉ 2024 atpotato. All rights reserved.</p> 284 + </footer> 285 + </main> 286 + <!-- Table of contents will be inserted here by JavaScript --> 287 + <nav class="toc-container"><h2>Table of Contents</h2><ul class="toc-list"><li class="toc-item toc-h2"><a href="#section-0" class="toc-link toc-h2 active">Introduction</a></li><li class="toc-item toc-h2"><a href="#section-1" class="toc-link toc-h2">Quick Start Guide</a></li><li class="toc-item toc-h2"><a href="#section-2" class="toc-link toc-h2">Understanding Bluesky</a></li><li class="toc-item toc-h2"><a href="#section-3" class="toc-link toc-h2">Achieving Growth and Engagement</a></li><li class="toc-item toc-h2"><a href="#section-4" class="toc-link toc-h2">Analyzing and Measuring success</a></li><li class="toc-item toc-h2"><a href="#section-5" class="toc-link toc-h2">Risk Mitigation</a></li><li class="toc-item toc-h2"><a href="#section-6" class="toc-link toc-h2">Next Steps</a></li></ul></nav></div> 288 + 289 + <div id="floating-buttons"></div> 290 + 291 + <!-- Markdown processing --> 292 + <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> 293 + <script src="guide-components.js"></script> 294 + <script> 295 + // Configure marked options 296 + marked.setOptions({ 297 + headerIds: true, 298 + gfm: true 299 + }); 300 + 301 + // Fetch and render markdown 302 + fetch('how-to-use-bluesky-to-grow-your-brand.md') 303 + .then(response => response.text()) 304 + .then(markdown => { 305 + document.getElementById('markdown-content').innerHTML = marked.parse(markdown); 306 + }); 307 + 308 + // Render floating buttons with Liquid Glass 309 + const FloatingButtons = () => { 310 + const containerRef = React.useRef(null); 311 + 312 + return React.createElement('div', { 313 + ref: containerRef, 314 + style: { 315 + position: 'fixed', 316 + bottom: '2rem', 317 + right: '2rem', 318 + display: 'flex', 319 + gap: '1rem', 320 + zIndex: 1000 321 + } 322 + }, [ 323 + React.createElement(LiquidGlass, { 324 + key: 'contents', 325 + displacementScale: 64, 326 + blurAmount: 0.1, 327 + saturation: 130, 328 + aberrationIntensity: 2, 329 + elasticity: 0.35, 330 + cornerRadius: 100, 331 + padding: '8px 16px', 332 + mouseContainer: containerRef, 333 + onClick: () => { 334 + const toc = document.querySelector('.toc-container'); 335 + if (toc) toc.style.display = toc.style.display === 'none' ? 'block' : 'none'; 336 + } 337 + }, React.createElement('span', { 338 + style: { 339 + display: 'flex', 340 + alignItems: 'center', 341 + gap: '0.5rem', 342 + color: 'white', 343 + fontWeight: 500 344 + } 345 + }, '๐Ÿ“‘ Contents')), 346 + React.createElement(LiquidGlass, { 347 + key: 'theme', 348 + displacementScale: 64, 349 + blurAmount: 0.1, 350 + saturation: 130, 351 + aberrationIntensity: 2, 352 + elasticity: 0.35, 353 + cornerRadius: 100, 354 + padding: '8px 16px', 355 + mouseContainer: containerRef, 356 + onClick: () => { 357 + document.documentElement.classList.toggle('dark'); 358 + } 359 + }, React.createElement('span', { 360 + style: { 361 + display: 'flex', 362 + alignItems: 'center', 363 + gap: '0.5rem', 364 + color: 'white', 365 + fontWeight: 500 366 + } 367 + }, '๐ŸŒ™ Dark Mode')) 368 + ]); 369 + }; 370 + 371 + ReactDOM.render( 372 + React.createElement(FloatingButtons), 373 + document.getElementById('floating-buttons') 374 + ); 375 + </script> 376 + 377 + <button class="theme-toggle">๐ŸŒ™ Dark Mode</button><button class="toc-toggle">๐Ÿ“‘ Contents</button><div class="toc-overlay"><div class="toc-modal"><h2>Table of Contents</h2><ul class="toc-list"><li class="toc-item toc-h2"><a href="#section-0" class="toc-link toc-h2 active">Introduction</a></li><li class="toc-item toc-h2"><a href="#section-1" class="toc-link toc-h2">Quick Start Guide</a></li><li class="toc-item toc-h2"><a href="#section-2" class="toc-link toc-h2">Understanding Bluesky</a></li><li class="toc-item toc-h2"><a href="#section-3" class="toc-link toc-h2">Achieving Growth and Engagement</a></li><li class="toc-item toc-h2"><a href="#section-4" class="toc-link toc-h2">Analyzing and Measuring success</a></li><li class="toc-item toc-h2"><a href="#section-5" class="toc-link toc-h2">Risk Mitigation</a></li><li class="toc-item toc-h2"><a href="#section-6" class="toc-link toc-h2">Next Steps</a></li></ul><button class="toc-close">ร—</button></div></div></body>
+55
package-lock.json
··· 1 + { 2 + "name": "atpotato-workspace", 3 + "lockfileVersion": 3, 4 + "requires": true, 5 + "packages": { 6 + "": { 7 + "dependencies": { 8 + "liquid-glass-react": "^1.0.1" 9 + } 10 + }, 11 + "node_modules/liquid-glass-react": { 12 + "version": "1.0.1", 13 + "resolved": "https://registry.npmjs.org/liquid-glass-react/-/liquid-glass-react-1.0.1.tgz", 14 + "integrity": "sha512-eIoT+9d8KWrB/8vrITGPMZZ8JFPt4iECKpAJy6apdhnlHFY6FO0/JeftqcK6hYdxS31kQkVaCap29m80XmU4Kg==", 15 + "license": "MIT", 16 + "workspaces": [ 17 + "liquid-glass" 18 + ], 19 + "peerDependencies": { 20 + "react": ">=19", 21 + "react-dom": ">=19" 22 + } 23 + }, 24 + "node_modules/react": { 25 + "version": "19.1.0", 26 + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", 27 + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", 28 + "license": "MIT", 29 + "peer": true, 30 + "engines": { 31 + "node": ">=0.10.0" 32 + } 33 + }, 34 + "node_modules/react-dom": { 35 + "version": "19.1.0", 36 + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", 37 + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", 38 + "license": "MIT", 39 + "peer": true, 40 + "dependencies": { 41 + "scheduler": "^0.26.0" 42 + }, 43 + "peerDependencies": { 44 + "react": "^19.1.0" 45 + } 46 + }, 47 + "node_modules/scheduler": { 48 + "version": "0.26.0", 49 + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", 50 + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", 51 + "license": "MIT", 52 + "peer": true 53 + } 54 + } 55 + }
+5
package.json
··· 1 + { 2 + "dependencies": { 3 + "liquid-glass-react": "^1.0.1" 4 + } 5 + }
website/.DS_Store

This is a binary file and will not be displayed.

+35
website/.gitignore
··· 1 + # Dependencies 2 + node_modules/ 3 + .pnp 4 + .pnp.js 5 + 6 + # Testing 7 + coverage/ 8 + 9 + # Production 10 + build/ 11 + dist/ 12 + .next/ 13 + out/ 14 + 15 + # Misc 16 + .DS_Store 17 + .env.local 18 + .env.development.local 19 + .env.test.local 20 + .env.production.local 21 + .env 22 + 23 + # Debug 24 + npm-debug.log* 25 + yarn-debug.log* 26 + yarn-error.log* 27 + 28 + # IDE 29 + .idea/ 30 + .vscode/ 31 + *.swp 32 + *.swo 33 + 34 + # Vercel 35 + .vercel/
website/atpotato-dead.png

This is a binary file and will not be displayed.

website/atpotato-kawaii.png

This is a binary file and will not be displayed.

website/atpotato-normal.png

This is a binary file and will not be displayed.

website/atpotato-scawy.png

This is a binary file and will not be displayed.

website/atpotato-waow.png

This is a binary file and will not be displayed.

website/bsky-bg.JPG

This is a binary file and will not be displayed.

+81
website/guides/bluesky-for-brands.html
··· 1 + <!DOCTYPE html> 2 + <html lang="en"> 3 + <head> 4 + <meta charset="UTF-8"> 5 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 + <title>How to use Bluesky to grow your brand - atpotato</title> 7 + <link rel="stylesheet" href="https://use.typekit.net/jik6ttx.css"> 8 + <link rel="stylesheet" href="../styles.css"> 9 + <link rel="stylesheet" href="guide-styles.css"> 10 + 11 + <!-- Favicon --> 12 + <link rel="icon" href="/favicon.png" type="image/png"> 13 + 14 + <!-- Primary Meta Tags --> 15 + <meta name="title" content="How to use Bluesky to grow your brand - atpotato"> 16 + <meta name="description" content="A comprehensive guide for brands, organizations, and creators looking to establish and grow their presence on Bluesky."> 17 + 18 + <!-- Open Graph --> 19 + <meta property="og:type" content="article"> 20 + <meta property="og:url" content="https://atpota.to/guides/bluesky-for-brands"> 21 + <meta property="og:title" content="How to use Bluesky to grow your brand - atpotato"> 22 + <meta property="og:description" content="A comprehensive guide for brands, organizations, and creators looking to establish and grow their presence on Bluesky."> 23 + <meta property="og:image" content="/og-image.jpg"> 24 + 25 + <script> 26 + window.va = window.va || function () { (window.vaq = window.vaq || []).push(arguments); }; 27 + </script> 28 + <script defer src="/_vercel/insights/script.js"></script> 29 + <script defer src="../theme.js"></script> 30 + <script defer src="../logo-hover.js"></script> 31 + </head> 32 + <body> 33 + <div class="guide-container"> 34 + <main class="guide-content"> 35 + <header class="guide-header"> 36 + <h1>How to use Bluesky to grow your brand</h1> 37 + <h2>A comprehensive guide for companies, communities, and creators</h2> 38 + <div class="guide-meta"> 39 + <span class="reading-time">๐Ÿ“š calculating...</span> 40 + <span class="meta-separator">ยท</span> 41 + <span class="guide-author">written by <a href="https://bsky.app/profile/dame.is" target="_blank">@dame.is</a></span> 42 + </div> 43 + <p class="support-thanks"><em>Thanks to Protocol Labs for their support in helping make this guide possible.</em></p> 44 + </header> 45 + 46 + <div id="markdown-content"> 47 + <!-- Markdown content will be inserted here --> 48 + </div> 49 + 50 + <footer class="footer"> 51 + <p>Written by <a href="https://bsky.app/profile/dame.is">Dame</a></p> 52 + <p>ยฉ 2024 atpotato. All rights reserved.</p> 53 + </footer> 54 + </main> 55 + <!-- Table of contents will be inserted here by JavaScript --> 56 + </div> 57 + 58 + <div class="button-backdrop"></div> 59 + <a href="/" class="home-button"> 60 + <img src="/atpotato-normal.png" alt="atpotato logo" class="home-logo"> 61 + </a> 62 + 63 + <!-- Markdown processing --> 64 + <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> 65 + <script src="guide-components.js"></script> 66 + <script> 67 + // Configure marked options 68 + marked.setOptions({ 69 + headerIds: true, 70 + gfm: true 71 + }); 72 + 73 + // Fetch and render markdown 74 + fetch('how-to-use-bluesky-to-grow-your-brand.md') 75 + .then(response => response.text()) 76 + .then(markdown => { 77 + document.getElementById('markdown-content').innerHTML = marked.parse(markdown); 78 + }); 79 + </script> 80 + </body> 81 + </html>
+226
website/guides/guide-components.js
··· 1 + // Table of Contents 2 + class TableOfContents { 3 + constructor(contentSelector = '.guide-content') { 4 + this.content = document.querySelector(contentSelector); 5 + this.headings = this.content.querySelectorAll('h2'); 6 + this.tocContainer = null; 7 + this.tocOverlay = null; 8 + this.isAnimating = false; 9 + this.init(); 10 + } 11 + 12 + init() { 13 + this.createTocContainer(); 14 + this.createMobileElements(); 15 + this.buildToc(); 16 + this.initIntersectionObserver(); 17 + } 18 + 19 + createTocContainer() { 20 + this.tocContainer = document.createElement('nav'); 21 + this.tocContainer.className = 'toc-container'; 22 + this.tocContainer.innerHTML = '<h2>Table of Contents</h2><ul class="toc-list"></ul>'; 23 + document.querySelector('.guide-container').appendChild(this.tocContainer); 24 + } 25 + 26 + createMobileElements() { 27 + // Create toggle button 28 + const toggleButton = document.createElement('button'); 29 + toggleButton.className = 'toc-toggle'; 30 + toggleButton.innerHTML = '๐Ÿ“‘'; 31 + toggleButton.onclick = () => this.showMobileToc(); 32 + document.body.appendChild(toggleButton); 33 + 34 + // Create overlay 35 + this.tocOverlay = document.createElement('div'); 36 + this.tocOverlay.className = 'toc-overlay'; 37 + 38 + // Create modal 39 + const modal = document.createElement('div'); 40 + modal.className = 'toc-modal'; 41 + modal.innerHTML = '<h2>Table of Contents</h2><ul class="toc-list"></ul>'; 42 + 43 + // Create close button 44 + const closeButton = document.createElement('button'); 45 + closeButton.className = 'toc-close'; 46 + closeButton.innerHTML = 'ร—'; 47 + closeButton.onclick = () => this.hideMobileToc(); 48 + modal.appendChild(closeButton); 49 + 50 + this.tocOverlay.appendChild(modal); 51 + document.body.appendChild(this.tocOverlay); 52 + 53 + // Close on overlay click 54 + this.tocOverlay.addEventListener('click', (e) => { 55 + if (e.target === this.tocOverlay) { 56 + this.hideMobileToc(); 57 + } 58 + }); 59 + 60 + // Close on escape key 61 + document.addEventListener('keydown', (e) => { 62 + if (e.key === 'Escape') { 63 + this.hideMobileToc(); 64 + } 65 + }); 66 + } 67 + 68 + buildToc() { 69 + const tocLists = document.querySelectorAll('.toc-list'); 70 + 71 + tocLists.forEach(tocList => { 72 + tocList.innerHTML = ''; // Clear existing items 73 + 74 + this.headings.forEach((heading, index) => { 75 + // Skip if heading is inside guide-header 76 + if (heading.closest('.guide-header')) return; 77 + 78 + const link = document.createElement('a'); 79 + const listItem = document.createElement('li'); 80 + const id = `section-${index}`; 81 + 82 + heading.id = id; 83 + link.href = `#${id}`; 84 + link.textContent = heading.textContent; 85 + link.className = `toc-link toc-${heading.tagName.toLowerCase()}`; 86 + 87 + // Add click handler for mobile 88 + link.addEventListener('click', () => { 89 + this.hideMobileToc(); 90 + }); 91 + 92 + listItem.className = `toc-item toc-${heading.tagName.toLowerCase()}`; 93 + listItem.appendChild(link); 94 + tocList.appendChild(listItem.cloneNode(true)); 95 + }); 96 + }); 97 + } 98 + 99 + showMobileToc() { 100 + if (this.isAnimating) return; 101 + this.isAnimating = true; 102 + 103 + // Prevent body scroll 104 + document.body.style.overflow = 'hidden'; 105 + 106 + // Show overlay 107 + requestAnimationFrame(() => { 108 + this.tocOverlay.classList.add('active'); 109 + 110 + // Reset animation state after transition 111 + setTimeout(() => { 112 + this.isAnimating = false; 113 + }, 300); // Match the CSS transition duration 114 + }); 115 + } 116 + 117 + hideMobileToc() { 118 + if (this.isAnimating) return; 119 + this.isAnimating = true; 120 + 121 + // Hide overlay 122 + this.tocOverlay.classList.remove('active'); 123 + 124 + // Re-enable body scroll after animation 125 + setTimeout(() => { 126 + document.body.style.overflow = ''; 127 + this.isAnimating = false; 128 + }, 300); // Match the CSS transition duration 129 + } 130 + 131 + initIntersectionObserver() { 132 + const observer = new IntersectionObserver( 133 + (entries) => { 134 + entries.forEach(entry => { 135 + const id = entry.target.getAttribute('id'); 136 + const tocLinks = document.querySelectorAll(`[href="#${id}"]`); 137 + 138 + if (entry.isIntersecting) { 139 + document.querySelectorAll('.toc-link').forEach(link => link.classList.remove('active')); 140 + tocLinks.forEach(link => link.classList.add('active')); 141 + } 142 + }); 143 + }, 144 + { rootMargin: '-100px 0px -66% 0px' } 145 + ); 146 + 147 + this.headings.forEach(heading => observer.observe(heading)); 148 + } 149 + } 150 + 151 + // Reading Progress 152 + class ReadingProgress { 153 + constructor(contentSelector = '.guide-content') { 154 + this.content = document.querySelector(contentSelector); 155 + this.progressBar = null; 156 + this.init(); 157 + } 158 + 159 + init() { 160 + this.createProgressBar(); 161 + window.addEventListener('scroll', () => this.updateProgress()); 162 + } 163 + 164 + createProgressBar() { 165 + const container = document.createElement('div'); 166 + container.className = 'reading-progress'; 167 + 168 + this.progressBar = document.createElement('div'); 169 + this.progressBar.className = 'progress-bar'; 170 + 171 + container.appendChild(this.progressBar); 172 + document.body.insertBefore(container, document.body.firstChild); 173 + } 174 + 175 + updateProgress() { 176 + const windowHeight = window.innerHeight; 177 + const documentHeight = document.documentElement.scrollHeight - windowHeight; 178 + const scrolled = window.scrollY; 179 + const progress = (scrolled / documentHeight) * 100; 180 + 181 + this.progressBar.style.width = `${progress}%`; 182 + } 183 + } 184 + 185 + // Reading Time Estimator 186 + class ReadingTimeEstimator { 187 + constructor(contentSelector = '.guide-content') { 188 + this.content = document.querySelector(contentSelector); 189 + this.wordsPerMinute = 200; 190 + this.init(); 191 + } 192 + 193 + init() { 194 + const wordCount = this.countWords(); 195 + const readingTime = Math.ceil(wordCount / this.wordsPerMinute); 196 + this.displayReadingTime(readingTime); 197 + } 198 + 199 + countWords() { 200 + const text = this.content.textContent || this.content.innerText; 201 + return text.trim().split(/\s+/).length; 202 + } 203 + 204 + displayReadingTime(minutes) { 205 + const readingTimeElement = document.querySelector('.reading-time'); 206 + if (readingTimeElement) { 207 + readingTimeElement.innerHTML = `๐Ÿ“š ${minutes} min read`; 208 + } 209 + } 210 + } 211 + 212 + // Initialize all components when the DOM is loaded 213 + document.addEventListener('DOMContentLoaded', () => { 214 + // Wait for markdown content to be loaded 215 + const checkContent = setInterval(() => { 216 + const content = document.querySelector('.guide-content'); 217 + if (content && content.children.length > 0) { 218 + clearInterval(checkContent); 219 + 220 + // Initialize components 221 + new TableOfContents(); 222 + new ReadingProgress(); 223 + new ReadingTimeEstimator(); 224 + } 225 + }, 100); 226 + });
+783
website/guides/guide-styles.css
··· 1 + /* Guide Theme Variables */ 2 + :root { 3 + /* Layout */ 4 + --max-width: 500px; 5 + --side-padding: 2rem; 6 + --toc-width: 280px; 7 + } 8 + 9 + body { 10 + position: relative; 11 + margin: 0; 12 + min-height: 100vh; 13 + } 14 + 15 + body::before { 16 + content: ''; 17 + position: fixed; 18 + top: 0; 19 + left: 0; 20 + width: 100%; 21 + height: 100%; 22 + background-image: url('/bsky-bg.JPG'); 23 + background-size: cover; 24 + background-position: center; 25 + background-repeat: no-repeat; 26 + filter: blur(2px); 27 + opacity: 0.15; 28 + z-index: -2; 29 + } 30 + 31 + body::after { 32 + content: ''; 33 + position: fixed; 34 + top: 0; 35 + left: 0; 36 + width: 100%; 37 + height: 100%; 38 + background: var(--background-color); 39 + opacity: 0.2; 40 + z-index: -1; 41 + } 42 + 43 + /* Base Layout */ 44 + .guide-container { 45 + display: grid; 46 + grid-template-columns: 1fr var(--max-width) var(--toc-width); 47 + gap: 5rem; 48 + max-width: calc(var(--max-width) + var(--toc-width) + 4rem + 200px); 49 + margin: 0 auto; 50 + margin-top: 5rem; 51 + } 52 + 53 + /* Main Content */ 54 + .guide-content { 55 + grid-column: 2; 56 + font-family: "eigerdals", sans-serif; 57 + font-weight: 400; 58 + font-style: normal; 59 + font-size: 1.1rem; 60 + line-height: 1.6; 61 + color: var(--text-color); 62 + text-align: left; 63 + width: 100%; 64 + max-width: var(--max-width); 65 + background: transparent; 66 + margin-top: 5rem; 67 + } 68 + 69 + .header { 70 + text-align: center; 71 + margin-bottom: 4rem; 72 + padding-bottom: 2rem; 73 + border-bottom: 1px solid var(--border-color); 74 + } 75 + 76 + .header h1 { 77 + font-size: 2.5rem; 78 + font-weight: 900; 79 + font-style: normal; 80 + margin-bottom: 1rem; 81 + color: var(--text-color); 82 + } 83 + 84 + .header .meta { 85 + color: var(--secondary-color); 86 + font-size: 0.9rem; 87 + } 88 + 89 + /* Typography */ 90 + h1, h2, h3, h4, h5 { 91 + font-family: "eigerdals", sans-serif; 92 + font-weight: 700; 93 + font-style: normal; 94 + margin-top: 2em; 95 + margin-bottom: 1em; 96 + color: var(--text-color); 97 + text-align: left; 98 + } 99 + 100 + h1 { 101 + font-size: 2.5rem; 102 + line-height: 2.5rem; 103 + font-weight: 900; 104 + margin-top: 0; 105 + margin-bottom: 1rem; 106 + } 107 + 108 + h2 { 109 + font-size: 2rem; 110 + line-height: 2rem; 111 + border-bottom: 2px solid var(--section-border-color); 112 + padding-bottom: 1em; 113 + margin-bottom: 1.5em; 114 + color: var(--highlight-color); 115 + } 116 + 117 + h3 { 118 + font-size: 1.5rem; 119 + line-height: 1.5rem; 120 + } 121 + 122 + h4 { 123 + font-size: 1.25rem; 124 + line-height: 1.25rem; 125 + } 126 + 127 + h5 { 128 + font-size: 1.1rem; 129 + line-height: 1.1rem; 130 + font-weight: 800; 131 + } 132 + 133 + p { 134 + font-size: 1.1rem; 135 + line-height: 1.6; 136 + margin-bottom: 1.5em; 137 + text-align: left; 138 + } 139 + 140 + a { 141 + color: var(--highlight-color); 142 + text-decoration: none; 143 + font-weight: 700; 144 + font-style: normal; 145 + transition: all 0.2s ease; 146 + } 147 + 148 + a:hover { 149 + text-decoration: underline; 150 + } 151 + 152 + /* Table of Contents */ 153 + .toc-container { 154 + grid-column: 3; 155 + position: sticky; 156 + top: 50vh; 157 + transform: translateY(-50%); 158 + height: fit-content; 159 + max-height: min(600px, calc(100vh - 4rem)); 160 + overflow-y: auto; 161 + padding: 1.5rem; 162 + background: var(--surface-color); 163 + border: 1px solid var(--border-color); 164 + border-radius: 12px; 165 + font-size: 0.9rem; 166 + font-family: "eigerdals", sans-serif; 167 + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); 168 + backdrop-filter: blur(10px); 169 + } 170 + 171 + .toc-container h2 { 172 + font-size: 1.2rem; 173 + margin-top: 0; 174 + margin-bottom: 1rem; 175 + padding-bottom: 0.8rem; 176 + border-bottom: 1px solid var(--border-color); 177 + color: var(--highlight-color); 178 + } 179 + 180 + .toc-list { 181 + list-style: none; 182 + padding: 0; 183 + margin: 0; 184 + } 185 + 186 + .toc-item { 187 + margin-bottom: 0rem; 188 + } 189 + 190 + .toc-link { 191 + color: var(--text-color); 192 + text-decoration: none; 193 + transition: color 0.2s; 194 + display: block; 195 + padding: 0.3rem 0; 196 + font-weight: 500; 197 + } 198 + 199 + .toc-link:hover { 200 + color: var(--highlight-color); 201 + text-decoration: none; 202 + } 203 + 204 + .toc-link.active { 205 + color: var(--highlight-color); 206 + font-weight: 700; 207 + } 208 + 209 + .toc-h2 { padding-left: 0; } 210 + .toc-h3 { padding-left: 1rem; } 211 + .toc-h4 { padding-left: 2rem; } 212 + 213 + /* Floating Buttons */ 214 + .toc-toggle { 215 + position: fixed; 216 + bottom: 2rem; 217 + left: 6rem; 218 + width: 3.5rem; 219 + height: 3.5rem; 220 + background: var(--surface-color); 221 + border: 1px solid var(--border-color); 222 + border-radius: 50%; 223 + cursor: pointer; 224 + display: flex; 225 + align-items: center; 226 + justify-content: center; 227 + transition: all 0.2s ease; 228 + z-index: 1000; 229 + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); 230 + backdrop-filter: blur(10px); 231 + font-size: 1.5rem; 232 + } 233 + 234 + .toc-toggle:hover { 235 + transform: translateY(-2px); 236 + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); 237 + } 238 + 239 + @media (min-width: 1201px) { 240 + .toc-toggle { 241 + display: none; 242 + } 243 + } 244 + 245 + @media (max-width: 1200px) { 246 + .guide-container { 247 + grid-template-columns: 1fr; 248 + max-width: var(--max-width); 249 + padding: 2rem var(--side-padding); 250 + margin-top: 5rem; 251 + } 252 + 253 + .guide-content { 254 + grid-column: 1; 255 + margin: 0 auto; 256 + } 257 + 258 + .toc-container { 259 + display: none; 260 + } 261 + 262 + .toc-toggle { 263 + display: flex; 264 + } 265 + 266 + .toc-close { 267 + display: block; 268 + } 269 + 270 + .toc-overlay.active { 271 + display: flex; 272 + } 273 + } 274 + 275 + @media (max-width: 768px) { 276 + .guide-container { 277 + padding: 1.5rem var(--side-padding); 278 + } 279 + 280 + .button-backdrop { 281 + display: block; 282 + position: fixed; 283 + bottom: 0; 284 + left: 0; 285 + right: 0; 286 + height: 6rem; 287 + background: linear-gradient(to bottom, transparent, var(--background-color) 50%); 288 + pointer-events: none; 289 + z-index: 999; 290 + } 291 + 292 + .theme-toggle { 293 + width: 3rem; 294 + height: 3rem; 295 + font-size: 1.3rem; 296 + bottom: 1.5rem; 297 + left: 1.5rem; 298 + } 299 + 300 + .toc-toggle { 301 + width: 3rem; 302 + height: 3rem; 303 + font-size: 1.3rem; 304 + bottom: 1.5rem; 305 + left: 5.5rem; 306 + } 307 + 308 + .home-button { 309 + width: 3rem; 310 + height: 3rem; 311 + bottom: 1.5rem; 312 + right: 1.5rem; 313 + } 314 + 315 + .home-logo { 316 + width: 2rem; 317 + height: 2rem; 318 + } 319 + 320 + .toc-close { 321 + display: block; 322 + } 323 + 324 + .toc-overlay.active { 325 + display: flex; 326 + } 327 + } 328 + 329 + @media (max-width: 480px) { 330 + .guide-container { 331 + padding: 1rem var(--side-padding); 332 + } 333 + 334 + .theme-toggle { 335 + width: 3rem; 336 + height: 3rem; 337 + font-size: 1.2rem; 338 + bottom: 1.5rem; 339 + left: 1.5rem; 340 + } 341 + 342 + .toc-toggle { 343 + width: 3rem; 344 + height: 3rem; 345 + font-size: 1.2rem; 346 + bottom: 1.5rem; 347 + left: 5.5rem; 348 + } 349 + 350 + .home-button { 351 + width: 3rem; 352 + height: 3rem; 353 + bottom: 1.5rem; 354 + right: 1.5rem; 355 + } 356 + 357 + .home-logo { 358 + width: 2rem; 359 + height: 2rem; 360 + } 361 + 362 + .toc-close { 363 + display: block; 364 + } 365 + 366 + .toc-overlay.active { 367 + display: flex; 368 + } 369 + } 370 + 371 + .toc-overlay { 372 + position: fixed; 373 + top: 0; 374 + left: 0; 375 + width: 100%; 376 + height: 100%; 377 + background: var(--overlay-background); 378 + display: flex; 379 + justify-content: center; 380 + align-items: center; 381 + z-index: 1001; 382 + opacity: 0; 383 + visibility: hidden; 384 + transition: opacity 0.3s ease, visibility 0.3s ease; 385 + } 386 + 387 + .toc-overlay.active { 388 + opacity: 1; 389 + visibility: visible; 390 + } 391 + 392 + .toc-modal { 393 + position: relative; 394 + width: 90%; 395 + max-width: 400px; 396 + max-height: 80vh; 397 + background: var(--background-color); 398 + border-radius: 12px; 399 + padding: 1.5rem; 400 + overflow-y: auto; 401 + font-family: "eigerdals", sans-serif; 402 + margin: 2rem; 403 + transform: scale(0.95) translateY(10px); 404 + opacity: 0; 405 + transition: transform 0.3s ease, opacity 0.3s ease; 406 + } 407 + 408 + .toc-overlay.active .toc-modal { 409 + transform: scale(1) translateY(0); 410 + opacity: 1; 411 + } 412 + 413 + .toc-modal h2 { 414 + font-family: "eigerdals", sans-serif; 415 + font-size: 1.8rem; 416 + font-weight: 900; 417 + color: var(--highlight-color); 418 + margin-top: 0; 419 + margin-bottom: 1.5rem; 420 + padding-bottom: 0.5rem; 421 + border-bottom: 1px solid var(--border-color); 422 + } 423 + 424 + .toc-modal .toc-list { 425 + list-style: none; 426 + padding: 0; 427 + margin: 0; 428 + } 429 + 430 + .toc-modal .toc-link { 431 + color: var(--text-color); 432 + text-decoration: none; 433 + font-family: "eigerdals", sans-serif; 434 + font-weight: 400; 435 + font-size: 1.1rem; 436 + line-height: 1.4; 437 + display: block; 438 + padding: 0.5rem 0; 439 + transition: color 0.2s; 440 + } 441 + 442 + .toc-modal .toc-link:hover, 443 + .toc-modal .toc-link.active { 444 + color: var(--highlight-color); 445 + text-decoration: none; 446 + } 447 + 448 + .toc-close { 449 + position: absolute; 450 + top: 1rem; 451 + right: 1rem; 452 + background: none; 453 + border: none; 454 + color: var(--text-color); 455 + font-family: "eigerdals", sans-serif; 456 + font-size: 1.5rem; 457 + font-weight: 400; 458 + cursor: pointer; 459 + padding: 0.5rem; 460 + display: none; 461 + transition: color 0.2s; 462 + } 463 + 464 + .toc-close:hover { 465 + color: var(--highlight-color); 466 + } 467 + 468 + /* Reading Progress */ 469 + .reading-progress { 470 + position: fixed; 471 + top: 0; 472 + left: 0; 473 + width: 100%; 474 + height: 4px; 475 + background: var(--progress-background); 476 + z-index: 1000; 477 + } 478 + 479 + .progress-bar { 480 + height: 100%; 481 + background: var(--progress-fill); 482 + width: 0; 483 + transition: width 0.1s; 484 + } 485 + 486 + /* Reading Time */ 487 + .reading-time { 488 + color: var(--secondary-color); 489 + font-size: 0.9rem; 490 + font-family: "eigerdals", sans-serif; 491 + font-weight: 400; 492 + } 493 + 494 + /* Footer */ 495 + .footer { 496 + margin-top: 4rem; 497 + padding-top: 2rem; 498 + border-top: 1px solid var(--border-color); 499 + text-align: center; 500 + color: var(--secondary-color); 501 + font-size: 0.9rem; 502 + } 503 + 504 + /* Code Blocks */ 505 + code { 506 + background: var(--code-background); 507 + padding: 0.2em 0.4em; 508 + border-radius: 3px; 509 + font-size: 0.9em; 510 + font-family: ui-monospace, monospace; 511 + } 512 + 513 + /* Blockquotes */ 514 + blockquote { 515 + border-left: 4px solid var(--highlight-color); 516 + margin: 0; 517 + padding-left: 1em; 518 + color: var(--secondary-color); 519 + } 520 + 521 + /* Images */ 522 + img { 523 + max-width: 100%; 524 + height: auto; 525 + border-radius: 8px; 526 + } 527 + 528 + /* Lists */ 529 + ul, ol { 530 + padding-left: 1.5em; 531 + margin-bottom: 1.5em; 532 + } 533 + 534 + li { 535 + margin-bottom: 0.5em; 536 + } 537 + 538 + @media (max-width: 600px) { 539 + :root { 540 + --side-padding: 1rem; 541 + } 542 + 543 + .guide-content { 544 + font-size: 1rem; 545 + } 546 + 547 + .header h1 { 548 + font-size: 2rem; 549 + } 550 + } 551 + 552 + /* Home Button */ 553 + .home-button { 554 + position: fixed; 555 + bottom: 2rem; 556 + right: 2rem; 557 + width: 3.5rem; 558 + height: 3.5rem; 559 + background: var(--surface-color); 560 + border: 1px solid var(--border-color); 561 + border-radius: 50%; 562 + display: flex; 563 + align-items: center; 564 + justify-content: center; 565 + transition: all 0.2s ease; 566 + z-index: 1000; 567 + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); 568 + backdrop-filter: blur(10px); 569 + } 570 + 571 + .home-button:hover { 572 + transform: translateY(-2px); 573 + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); 574 + } 575 + 576 + .home-logo { 577 + width: 2.5rem; 578 + height: 2.5rem; 579 + object-fit: contain; 580 + transition: all 0.2s ease; 581 + } 582 + 583 + @media (max-width: 768px) { 584 + .home-button { 585 + bottom: 1.5rem; 586 + right: 1.5rem; 587 + width: 3rem; 588 + height: 3rem; 589 + } 590 + 591 + .home-logo { 592 + width: 2rem; 593 + height: 2rem; 594 + } 595 + } 596 + 597 + /* Remove old nav styles */ 598 + .nav-container, .nav-content, .nav-inner, .nav-logo, .nav-links, .nav-link { 599 + display: none; 600 + } 601 + 602 + /* Button Backdrop */ 603 + .button-backdrop { 604 + display: none; 605 + } 606 + 607 + @media (max-width: 768px) { 608 + .button-backdrop { 609 + display: block; 610 + position: fixed; 611 + bottom: 0; 612 + left: 0; 613 + right: 0; 614 + height: 6rem; 615 + background: linear-gradient(to bottom, transparent, var(--background-color) 50%); 616 + pointer-events: none; 617 + z-index: 999; 618 + } 619 + 620 + .theme-toggle { 621 + width: 3rem; 622 + height: 3rem; 623 + font-size: 1.3rem; 624 + bottom: 1.5rem; 625 + left: 1.5rem; 626 + } 627 + 628 + .toc-toggle { 629 + width: 3rem; 630 + height: 3rem; 631 + font-size: 1.3rem; 632 + bottom: 1.5rem; 633 + left: 5.5rem; 634 + } 635 + 636 + .home-button { 637 + width: 3rem; 638 + height: 3rem; 639 + bottom: 1.5rem; 640 + right: 1.5rem; 641 + } 642 + 643 + .home-logo { 644 + width: 2rem; 645 + height: 2rem; 646 + } 647 + 648 + .toc-close { 649 + display: block; 650 + } 651 + 652 + .toc-overlay.active { 653 + display: flex; 654 + } 655 + } 656 + 657 + /* Guide Meta */ 658 + .guide-meta { 659 + color: var(--secondary-color); 660 + font-size: 0.9rem; 661 + margin-bottom: 1.5rem; 662 + font-family: "eigerdals", sans-serif; 663 + font-weight: 400; 664 + display: flex; 665 + align-items: center; 666 + flex-wrap: wrap; 667 + gap: 0.5rem; 668 + } 669 + 670 + .reading-time { 671 + display: inline-flex; 672 + align-items: center; 673 + gap: 0.3rem; 674 + white-space: nowrap; 675 + } 676 + 677 + .meta-separator { 678 + color: var(--text-color); 679 + } 680 + 681 + .guide-author { 682 + display: inline-flex; 683 + align-items: center; 684 + white-space: nowrap; 685 + } 686 + 687 + .guide-author a { 688 + color: var(--highlight-color); 689 + text-decoration: none; 690 + font-weight: 700; 691 + } 692 + 693 + .guide-author a:hover { 694 + text-decoration: underline; 695 + } 696 + 697 + /* Guide Header */ 698 + .guide-header { 699 + margin-bottom: 4rem; 700 + text-align: left; 701 + } 702 + 703 + .guide-header h1 { 704 + font-size: 3.5rem; 705 + line-height: 1.2; 706 + margin-bottom: 1rem; 707 + color: var(--text-color); 708 + } 709 + 710 + .guide-header h2 { 711 + font-size: 1.5rem; 712 + line-height: 1.4; 713 + margin-bottom: 1.5rem; 714 + color: var(--secondary-color); 715 + font-weight: 400; 716 + border: none; 717 + } 718 + 719 + .guide-meta { 720 + color: var(--secondary-color); 721 + font-size: 0.9rem; 722 + margin-bottom: 1.5rem; 723 + font-family: "eigerdals", sans-serif; 724 + font-weight: 400; 725 + display: flex; 726 + align-items: center; 727 + flex-wrap: wrap; 728 + gap: 0.5rem; 729 + } 730 + 731 + .reading-time { 732 + display: inline-flex; 733 + align-items: center; 734 + gap: 0.3rem; 735 + white-space: nowrap; 736 + } 737 + 738 + .meta-separator { 739 + color: var(--text-color); 740 + } 741 + 742 + .guide-author { 743 + display: inline-flex; 744 + align-items: center; 745 + white-space: nowrap; 746 + } 747 + 748 + .guide-author a { 749 + color: var(--highlight-color); 750 + text-decoration: none; 751 + font-weight: 700; 752 + padding-left: 5px; 753 + } 754 + 755 + .guide-author a:hover { 756 + text-decoration: underline; 757 + } 758 + 759 + .support-thanks { 760 + color: var(--secondary-color); 761 + font-size: 0.9rem; 762 + margin: 0; 763 + } 764 + 765 + @media (max-width: 768px) { 766 + .guide-header h1 { 767 + font-size: 2.5rem; 768 + } 769 + 770 + .guide-header h2 { 771 + font-size: 1.3rem; 772 + } 773 + } 774 + 775 + @media (max-width: 480px) { 776 + .guide-header h1 { 777 + font-size: 2rem; 778 + } 779 + 780 + .guide-header h2 { 781 + font-size: 1.2rem; 782 + } 783 + }
+481
website/guides/how-to-use-bluesky-to-grow-your-brand.md
··· 1 + ## Introduction 2 + 3 + As of May 2025, Bluesky has over 36 million users. Journalists, politicians, activists, celebrities, and everyday individuals are now using it to engage with the content and communities that matter most to them. If you're wondering whether your brand should be there too, this guide will help you decideโ€”and show you exactly how to succeed if you do. 4 + 5 + Getting started on Bluesky is actually very easy, and it doesn't require a big time investment. You don't need to completely change your content strategy or brand voice to make it work. All it takes are a few small tweaks. You can eventually choose to dive deeper and use Bluesky's more advanced features, but they are not necessary. 6 + 7 + But first, why should you listen to me about this? 8 + 9 + Well, I've been on Bluesky since basically the beginningโ€ฆ I was account #1,216. I also set up one of the first brand accounts there, back when that wasn't something anyone thought was worth doing. My background is in social media and community growth, but nowadays I create resources that help people have a more enjoyable experience online. My hope is that I can use all the hours I've invested on Bluesky to make it more accessible to everyone else, including brands and communities that want to connect with their audiences there. 10 + 11 + We'll start with the basics if you're itching to jump right in, and then proceed to covering the complexities if you're ready to take things to the next level. 12 + 13 + Let's do this. 14 + 15 + ## Quick Start Guide 16 + 17 + **1. Create your account.** 18 + - Go to [bsky.app](https://bsky.app/) and click "Create account". 19 + - Choose an initial default username. 20 + 21 + **2. Setup your profile.** 22 + - Add an avatar, banner image, display name, and description. 23 + - Include a link to your website in your bio. 24 + 25 + **3. Connect your custom domain.** 26 + - Usernames on Bluesky are just domain names. You get a default one to start, but if you want to look credible and trustworthy, you should likely use your own domain. 27 + - Go to [bsky.app/settings/account](https://bsky.app/settings/account) and choose "Update Handle". 28 + - Press the "I have my own domain" button. 29 + - Update your domain's DNS settings with the records provided. If you work at a company, you may have to get your IT department to make this change for you. 30 + - Wait for the DNS record changes to take effect (usually within 15 minutes, could be 24 hours). 31 + 32 + ![Screenshot showing how to change your handle on Bluesky](/guides/screenshots/change-handle.png) 33 + 34 + **4. Make your first post.** 35 + - Introduce yourself, announce your latest news, or share something helpful within your niche. It's probably best to wait to do this until your handle updates to your custom domain though. 36 + 37 + **5. Share about it on other channels.** 38 + - Let your world know they can find you on Bluesky now. 39 + 40 + **6. Add your Bluesky link to your website.** 41 + - If you have icons or links to your social media profiles on your website, add Bluesky to that list. 42 + 43 + Now, while you wait for your custom domain to finish connecting or your IT department to make the necessary configurations, let's learn more about what makes Bluesky tick. 44 + 45 + ## Understanding Bluesky 46 + 47 + ### Backstory 48 + 49 + In 2019, the CEO of Twitter announced an internal research project called "Bluesky". The goal was to explore the feasibility of creating a decentralized internet protocol that could power Twitter and other social media platforms. After a couple of years and an initial funding pool of $13 million contributed by Twitter, Bluesky became an independent organization with zero ties to its originator. In late 2022, Bluesky privately launched its new social media app to a small group of test users. The app was very similar to Twitter, but it was powered by what became known as the AT Protocol. 50 + 51 + ![Jack Dorsey's tweet announcing the Bluesky project](/guides/screenshots/jack-tweet.png) 52 + 53 + ### The AT Protocol 54 + 55 + While you might be tempted to gloss over the decentralized technology that underlies Bluesky, learning the basics of it can be extremely helpful in both understanding part of the Bluesky culture and also tapping into the unique opportunities it can provide. 56 + 57 + On a traditional social media site like Twitter or Facebook, all of the user data is controlled by one centralized entity โ€” a for-profit corporation โ€” and stored within that entity's database in a proprietary data format that will only work with their app. If that social media app goes rogue, declines in quality, or gets bought by a billionaire, then the users don't have much of a choice in the matter... they either have to forfeit all of the followers and content they amassed over a long period of time or continue using the platform despite all of its toxicity and general crappiness. 58 + 59 + The AT Protocol was built to solve this problem, and within a short period of time it has come a long way towards reaching its goals. You can now own your social networking data, take it with you, and hopefully avoid being locked into one specific app in the future. 60 + 61 + ### Where your data is stored 62 + 63 + When you create an account on Bluesky, you're actually creating an AT Protocol account, even if you don't realize it. When you make a post on Bluesky, reply to another user, or like an image, all of that data gets stored in what's called your account's Personal Data Server (PDS). The PDS is basically just a file folder containing lots of different files that get stored on a server in the cloud. You can decide which company (or even yourself) "hosts" your data, similar to how a website is hosted. By default, new Bluesky accounts have their PDS data hosted on one of many different Bluesky servers, each of which is named after a different type of mushroom. 64 + 65 + `shiitake.us-east.host.bsky.network` 66 + `oyster.us-east.host.bsky.network` 67 + `maitake.us-west.host.bsky.network` 68 + 69 + If Bluesky were to ever shut down or become hostile, users could move their data to a new host and still maintain all of their likes, followers, and posts. 70 + 71 + Think of it like your email providerโ€“there are many different companies that provide email hosting services such as Gmail, Outlook, and iCloud. If you ever need or want to, you can download your email archive and contacts and import that data into another email provider. The reason this is possible is because all of these companies chose to build their product on top of the decentralized protocols that emailing technology uses. In that sense, the AT Protocol is sort of like email but for social media. 72 + 73 + ### How Bluesky is different from Twitter 74 + 75 + Many people think Bluesky is just a decentralized version of Twitter. And while that's true in some ways, Bluesky has many features that Twitter (now X) never had and likely never will have. Let's walk through each of these features now and then revisit some of them later to discuss how to use them effectively. 76 + 77 + #### 1. Custom feeds (algorithms) 78 + 79 + Perhaps its flagship feature, Bluesky has become famous for how it allows anyone to not only choose which algorithm their feed uses, but also build their own. Gone are the days of being forced to scroll through a non-chronological feed containing posts from random accounts you've never followed. For creators and brands, this means that your primary goal is no longer just "appease the algorithm"โ€ฆ because there isn't just one singular algorithm to please. 80 + 81 + Bluesky comes with two optional feeds out of the box: 1) Discover, and 2) Following. Discover is Bluesky's proprietary suggestion algorithm similar to TikTok's "For You" feed. The difference of course is that no one has to use the Discover feed or even have it appear in their app at allโ€ฆ you can disable it completely and rely upon the Following feed, another community-made feed, or a custom feed of your own design. 82 + 83 + ![A screenshot showing a custom feed called "Fungi Friends" that filters posts about mushrooms](/guides/screenshots/fungi-friends-feed.png) 84 + 85 + #### 2. Third-party moderation services 86 + 87 + After the AT Protocol launched, there was a lot of talk about the concept of "composable moderation", which basically just means a suite of tools and infrastructure that would let anyone provide moderation services on top of the network. One of the most well known examples of this is a service called Blacksky which provides a space for Black community building. Users can report posts/content to this third-party service and filter content out of their feeds based on it instead of Bluesky's official moderation. 88 + 89 + ![A screenshot showing Blacksky's moderation labeler interface](/guides/screenshots/blacksky-moderation-labeler.png) 90 + 91 + There's even [a moderation service that automatically detects and flags posts that contain screenshots](https://bsky.app/profile/xblock.aendra.dev) from other prominent social networks like X, allowing users to hide this content from their feeds completely. So, cross-posting by using screenshots might not be the wisest idea. 92 + 93 + Bluesky also has "moderation lists" that any user can create and subscribe to. On a basic level, these are just lists of accounts, but you can use them to automatically block or mute accounts in bulk with one tap. For instance, there are a series of [semi-popular moderation lists](https://bsky.app/profile/automated-lists.bsky.social) for blocking or muting all accounts that follow more than a certain number of accounts (10,000+, 50,000+, etc). Lists like these allow people to keep follower farmers away from them before they even have a chance. 94 + 95 + #### 3. Labelers + content filters 96 + 97 + Connected to third-party moderation services are a special tool called "labelers". A labelerโ€ฆ well, it applies labels to things. There are moderation labelers that allow you to hide, warn, or filter posts/accounts based on specific labels (i.e. like the screenshot labeler mentioned earlier), but then there are also vanity labelers that allow users to self-identify with communities, causes, and more by applying a voluntary public label to their own account. For instance, there's a [vanity labeler for Swifties](https://bsky.app/profile/eras.bsky.sh) that lets them add a little "flair" to their profile in the form of their favorite Taylor Swift Era. Custom labelers of all types and sizes can be created by anyone, though the process to do so is still quite technical at this time. 98 + 99 + ![A screenshot showing the Taylor Swift Eras vanity labeler interface](/guides/screenshots/taylor-swift-eras-labeler.png) 100 + 101 + #### 4. Domain names as usernames 102 + 103 + Ever been frustrated when joining a new app and learning that your username of choice has already been taken? On Bluesky, this problem is not nearly as significant thanks to the fact that all usernames on the network are actually domain names. When you create a new account, you can get a default username that looks like `name.bsky.social`, but you can (and should) set up your own because brands and creators without default names are sometimes seen as potential impersonators. 104 + 105 + Using your own domain name is a way to self-verify your account's legitimacy. For instance, NPR (National Public Radio) has the username of @npr.org, which is also its official website. Only the owner of the domain can possibly set it as a Bluesky username, so you know with confidence that the account is official. There are even many government accounts that can be quickly identified by their country's government domain structure (such as .gov in the United States). 106 + 107 + You can even set up subdomains as usernames, so if you are a company or part of an organization, you could set up team member accounts such as name.brand.team. Bluesky itself does this for many of its employeesโ€”check out the CEO's handle: @jay.bsky.team 108 + 109 + [How to use your domain as your Bluesky handle](https://bsky.social/about/blog/4-28-2023-domain-handle-tutorial) 110 + 111 + #### 5. Trusted Verifiers 112 + 113 + In addition to being able to self-verify your account via your domain name, there is also a unique verification system run by Bluesky that functions similarly to the original "blue check" from Twitter before it became X. While it may look the same on the surface though, there's actually a lot more going on. 114 + 115 + Bluesky itself can issue blue check verifications like you would expect, but it also can designate a third-party organization like the New York Times to be considered a "Trusted Verifier", meaning that there are now multiple independent sources for verification. At launch, there are only a handful of "Trusted Verifiers", but Bluesky says they will be increasing this over time. 116 + 117 + ![A screenshot showing Paul Frazee's verified account on Bluesky](/guides/screenshots/paul-verification.png) 118 + 119 + Furthermore, the underlying technology that makes this system possible is completely composable, meaning that any user on the network can issue a "verification" record for any other account and theoretically become a "Trusted Verifier" themself. While the odds of this are relatively low in the official Bluesky app, there are already third-party Bluesky client apps like [deer.social](https://deer.social) that allow you to fully customize and set your own list of "Trusted Verifiers". This means that you could have blue check marks appear next to accounts verified by your friends or colleagues rather than from Bluesky or the New York Times. 120 + 121 + #### 6. Free, open API 122 + 123 + Back in the day, Twitter used to have a free and relatively open stream of public data that was accessible via an API (Application Programming Interface). But when Elon Musk took over, he quickly shut this API down and started charging exorbitant prices to access the data. If you're unsure of what an API is or why it's significant, just compare the vibrant ecosystem of third-party integrations and applications that exists on Bluesky with what X has today. Bluesky is like a lush beautiful garden filled with lots of diverse plant life, and X is a vacant backlot with some old and dying corporate landscaping. An API is merely a highway that developers and creators can use to retrieve and send data. With Bluesky, that highway is open, free, and lets you explore the world. With X, only the wealthiest can drive on the highway and there are speed traps everywhere. 124 + 125 + An open and free API means an environment friendly to innovation, network effects, and the public good. 126 + 127 + #### 7. Everything is public 128 + 129 + Speaking of the public, almost everything you do on Bluesky at the moment is publicly accessible data (even blocks and likes). This won't matter that much to brands, organizations, and creators who explicitly want their content to be public, but it might matter to some of the people in your audience or community. Stay mindful about it, and just know that there are long-term plans to introduce some form of private accounts or content in the future. 130 + 131 + #### 8. In-app media players 132 + 133 + On most social platforms, sharing a YouTube or Spotify link won't play the media directly within the app. Instead, users have to leave the platform and open YouTube or Spotify to watch or listen. On Bluesky, this isn't the case. You can listen to music or watch embedded videos without having to leave the app. 134 + 135 + #### 9. No outbound link suppression 136 + 137 + Since we're on the subject of sharing links, now's a great time to mention that another defining and powerful feature of Bluesky is that its "Discover" feed algorithm doesn't suppress and down rank content that contains links to outside websites. This is huge for companies, brands, and creators because you no longer need to perform silly algorithmic gymnastics just to get followers onto your website. 138 + 139 + #### 10. Starter packs 140 + 141 + One of the difficult parts of joining a new social network is finding accounts that share your common interests and values. Bluesky tackled this issue by introducing an industry-first that was soon copied by Meta/Instagram: Starter Packs. 142 + 143 + At their core, Starter Packs are hand-curated lists of accounts that anyone can make. Each starter pack even has an invite-link attached to it. By sharing a Starter Pack with someone, they can create a new Bluesky account and follow a bunch of relevant accounts instantly. This feature works especially well for community building, whether that's for a city (Boston!), a fandom (kpop!), or even bicycle riders. If you're a brand, you could set up a Starter Pack that has your entire team in it or trusted industry voices. 144 + 145 + ![A screenshot showing the Starter Pack interface on Bluesky](/guides/screenshots/starter-pack.png) 146 + 147 + #### 11. Advanced preferences 148 + 149 + Hidden under the surface of Bluesky are a plethora of user customizations that apply to the "Following" feed (the reverse-chronological feed of just posts from accounts someone follows) and beyond. For instance, you can disable seeing reposts, quote posts, and replies. Within threads, you have the option of ordering the replies however you want: most liked, oldest, newest, prioritize following, hot, or even random (aka "Poster's Roulette"). 150 + 151 + ![A screenshot showing the feed preferences interface on Bluesky](/guides/screenshots/feed-preferences.png) 152 + 153 + #### 12. Advanced reply/quote settings 154 + 155 + Not only can you turn off replies to your Bluesky posts, but you can also customize reply permissions in extremely granular ways. For instance, you can specify that only users on a custom list can contribute to the conversation or make it so that someone has to be following you in order to reply. Additionally, you can prevent people from quote-posting your content completely. Both of these features are even available to apply retroactively. 156 + 157 + #### 13. Nuclear block 158 + 159 + Bluesky has intentionally tried to cultivate an environment where users have more control over who can interact with them. One of the ways they achieved this is through what is colloquially known as the Nuclear Block. If two people are having a conversation on Bluesky and one blocks the other, the entire conversation thread becomes hidden from view for everyone else. This feature is a bit controversial, having divided the community into two camps: people who love it and people who hate it. There's not much that can be done about it though, so it's good to keep in mind if you ever run into a situation where you see a "blocked" message in a thread and wonder what's going on. It doesn't mean that you've been blocked by anyone. 160 + 161 + #### 14. Paragraph breaks in bios 162 + 163 + Lastly, it's a small detail, but Bluesky profiles offer flexible bio formatting, making it easy to create a clean, readable description without needing tedious tricks or workarounds. 164 + 165 + ### Audience and Culture 166 + 167 + Over the course of Bluesky's two-year history, its culture has shifted as the network has become more populated. When there were only several thousands users, the space reflected the industry it sprang out of, so it was very centered on the United States' technology scene. Eventually there were growth periods that were dominated by other countries such as Japan or Brazil, with the latter case happening due to X temporarily abandoning Brazilian users entirely. 168 + 169 + The biggest growth event that the network has experienced so far though was in the days surrounding the 2024 United States presidential election, at which point millions of people created new Bluesky accounts in an effort to migrate off of big tech social networks like Facebook, Instagram, and X. 170 + 171 + In the months that followed, many people mistakenly started to assume that Bluesky had effectively become a politically left-leaning platform just for democrats. In reality, the culture of Bluesky is so multi-faceted at this point that it really doesn't make sense to think about it with such a hegemonic lens. For starters, there isn't a singular "Bluesky Culture" anymore. The user base is made up of millions of people from all over the world with an innumerable number of niche interests. 172 + 173 + Start thinking about Bluesky as a diverse system of hundreds (if not thousands) of large to small sub-cultures. Approaching the network with this perspective will better prepare you for all the different types of people you likely will encounter on any given day, and it will help you find the communities you are most interested in. 174 + 175 + ### Why people use Bluesky 176 + 177 + While it's true that Bluesky is hard to pin-down culturally, there are a variety of big ideas that tend to attract people to it and influence it on a monocultural level. Keeping these themes in mind might help you avoid some faux paus. 178 + 179 + #### 1. It's an open, decentralized, interoperable network. 180 + 181 + For the past two decades most people have only experienced an online social network that is both centralized and closed. Platforms like Facebook, TikTok, and X became known for trying to keep people on their apps for as long as possible to make money off of advertisements, even if it meant making the user experience worse by pushing invasive algorithms and hiding links to outside websites. 182 + 183 + As the years went on and the user experience continued to degrade, more and more people started to be fed up. A new future began to be conceived of, and experimental versions of alternative social apps started to gain some traction. The properties that defined these platforms were usually one or all of the following: 184 + 185 + ##### A. Open 186 + 187 + A significant portion of the network's data is publicly accessible via open APIs and storage. Additionally, anyone can create and host their own account. 188 + 189 + ##### B. Decentralized 190 + 191 + The network is designed and implemented in such a way that one centralized entity can't own or control everything within it. Decentralization exists on a spectrum, but in general a decentralized social network will be accessible from many different apps called "clients" and the data on the network is somewhat distributed across many different "nodes" or "hosts". 192 + 193 + ##### C. Interoperable 194 + 195 + An "interoperable" network means that the data and features that exist in one place can be easily used in many different places. A simple example of this would be multiple different apps that rely on the same user profile data. For instance, you can login to a new app and it could recognize your existing profile data that you made on Bluesky, and it would automatically use that for your profile. 196 + 197 + #### 2. There aren't any adsโ€ฆ yet. 198 + 199 + One of the primary reasons traditional corporations implement user-hostile things like link suppression and intrusive algorithms is to maintain their revenue model: advertising. Their primary incentive is to keep users in their apps for as long as possible at all costs. 200 + 201 + With Bluesky, an advertising model is not nearly as feasible or sustainable due to the underlying protocol's design and open nature. If Bluesky began having advertisements in their app, people could easily choose to consume the same content through the "window" of a third-party app that doesn't have ads. 202 + 203 + In this situation, the primary way to keep people using your app (and even get them to pay for it!) is to offer an unparalleled positive experience with quality features. This framework better aligns the company's incentives to be in step with users' needs. This isn't to say that advertising will never exist within Bluesky, but at the very least it won't manifest in the exact same way as it has in the past. 204 + 205 + #### 3. There's more control. 206 + 207 + One of the many benefits of an open and interoperable network is that users have greater control over their individual experiences. Bluesky empowers users with a significantly higher degree of agency in comparison to traditional social platforms. This reality also contributes to Bluesky's multicultural environment... for any given user that you meet, you can't assume that they are engaging with the network using the same "lens" that you are. 208 + 209 + #### 4. It's not "big tech". 210 + 211 + Given the fact that almost all of the major social media platforms in the United States have decided to align themselves with specific government administrations and policy proposals, even to extreme degrees, many people are uncomfortable spending their time with products made by those companies. Bluesky does not have the same affiliations or baggage, making it an attractive alternative. 212 + 213 + Now that you have a solid understanding of Bluesky, its underlying technology, and its multifaceted community, you're ready to start thinking about how you can practically fit into the equation. 214 + 215 + ## Achieving Growth and Engagement 216 + 217 + If you're reading this guide, then you're probably not on Bluesky just to have fun and blow off steam. You're likely an organization or a public figure that has a product, service, or cause that you need to promote to achieve your goals. In order to do that, you need to get peoples' attention and sustain their interest in what you have to say. Thus, you need followers and engagement. 218 + 219 + Attracting followers wasn't easy on traditional social media, so it's not easy on a decentralized network like Bluesky either. Plus, there aren't many ways to do advertising on Bluesky, so you can't reliably use "paid media" to force your way into peoples' attention spans. Instead, you have to provide valuable content that gets spread organically. This guide won't be teaching you how to do that, but it will be showing you how to apply that content creation to the unique context of Bluesky in a way that maximizes its potential. 220 + 221 + ### Formatting and packaging your content for Bluesky 222 + 223 + #### 1. Use links directly, without any weird hacks 224 + 225 + As we've discussed in previous sections, Bluesky does not suppress outbound links to other websites. This means that you don't need to perform the same algorithmic gymnastics just to sneak in a link to your website. Instead of making a main post and then following up with a reply containing the link or pointing to your bio, just put the link in the main post. If you keep using the workarounds from other platforms when you post to Bluesky, many people will see your activity as half-hearted, ignorant, or even cringe. On Bluesky, people like links and don't want to have to go searching for them in weird places. 226 + 227 + โŒ Don't: 'Great article! (link in bio)' 228 + 229 + โœ… Do: 'Great article on sustainable fashion: [direct link]' 230 + 231 + #### 2. If you post images, always include alt text 232 + 233 + More so than in other places, a significant part of Bluesky's culture cares a lot about web accessibility. By including alt text on your images, you're being inclusive to people with certain disabilities, but you're also making your content more discoverable because alt text is included in search results! For instance, if you post a screenshot of a document or promotional graphic, you should include all of that content as alt text too. If you didn't do this, then your post would not end up in relevant search results where it otherwise could and should. There are even Bluesky users who refuse to share content from other accounts if alt text isn't present. 234 + 235 + #### 3. Be conservative and mindful with your hashtag usage 236 + 237 + While hashtags are supported on Bluesky and used in many different contexts, including them in all of your posts can be seen as a desperate attempt for attention and a form of engagement hacking in many spheres. Depending on your industry and context though, the usage of hashtags might be more normal and prevalent. It's wise to take a look at what other accounts in your community/niche are doing to see what is and isn't common practice. 238 + 239 + #### 4. Consider the variables or conditions of relevant topical feeds 240 + 241 + Remember, Bluesky doesn't just have one algorithmic feed. It has a limitless number of customizable user-generated algorithms in addition to its default "Discover" algorithm. Many people disable the default algorithm completely and choose to rely upon more niche or topical feeds instead. For instance, there are community-controlled feeds dedicated to lovers of mushrooms/fungi, web development, and academic research. Each of these feeds has a custom algorithm with different conditions/rules that dictates which posts or content show up in the feed (for instance, this is often a good use-case for hashtags). Consider if there are any popular feeds in your niche that would welcome your contributions. If you do find such feeds, try to see if there is any public documentation available that shows how the feed works, and then use that information appropriately while crafting your related content. 242 + 243 + Be warned though, if your activity violates the norms or rules of a custom feed, your entire account could get banned from ever appearing in it again. Don't be a bad actor. 244 + 245 + #### 5. There is no edit button, so spell-check your content carefully 246 + 247 + Most social platforms these days allow for editing a post if you make a typo or need to add something after the fact. Bluesky does not currently support this feature. While it is technically possible to edit posts under the hood on the protocol level, doing so is a bit technical, requires third-party tools, and usually is not advisable (it also will reset the post's engagement stats). 248 + 249 + #### 6. You can embed videos and audio from some third-party services directly in a post 250 + 251 + While it doesn't make sense to post a YouTube link on Twitter/X anymore because of suppression, Bluesky embraces offsite media content and allows users to watch/listen without ever leaving the app. At the time of writing, the following services are supported: YouTube, Vimeo, Twitch, GIPHY, Spotify, Apple Music, Soundcloud, and Flickr. 252 + 253 + ### Leveraging Bluesky's unique features for growth and engagement 254 + 255 + #### 1. Labelers (Advanced/Expert) 256 + 257 + A Labeler is a customizable integration that can be used to display helpful information within the Bluesky app. They are used to apply textual and visual labels to either specific accounts or specific posts. These labels are only visible to users who "Subscribe" to the Labeler, meaning users must explicitly opt-in for the tags to appear in the user interface of their app. 258 + 259 + While Labelers were originally designed as a "serious" moderation tool, they have now evolved to include even more casual or even silly use cases. For instance, the official Bluesky company released a Superbowl Labeler for the 2025 Superbowl which allowed users to apply a label to their accounts to display which football team they were rooting for. Other Labelers use more advanced technology to automatically analyze and categorize accounts/posts based on publicly available data. 260 + 261 + ![Screenshot of the Bikesky Labeler showing a profile with a "Cyclist" label applied](screenshots/bikesky-labeler.png) 262 + 263 + Let's say you are a video game studio with a beloved game that has many different playable characters. You could create a Labeler that would allow your community members to tag their profile with their preferred character's name. Or, maybe you are a coffee shop chain and want to create a Labeler that would allow people to display their favorite drink order. 264 + 265 + Here is a list of some different Labelers to give you more inspiration and ideas: 266 + 267 + * [Bikesky Labeler](https://bsky.app/profile/labeler.bikesky.social) 268 + * [Super Bowl Labeler](https://bsky.app/profile/did:plc:h2btirb6mgshvzf2jqrsggbc) 269 + * [Pronouns Labeler](https://bsky.app/profile/pronouns.diy) 270 + * [The Cave of Trophonius](https://bsky.app/profile/oracle.posters.rip) 271 + * [Screenshots Labeler](https://bsky.app/profile/xblock.aendra.dev) 272 + 273 + Creating your own Labeler is not user-friendly at the moment, but it is possible if you have some coding/development skills. To get you started [check out this Github Repository](https://github.com/aliceisjustplaying/labeler-starter-kit-bsky) that a community member created to serve as a starter kit for creating a Labeler. 274 + 275 + #### 2. Feeds (Easy/Moderate) 276 + 277 + One of the most popular, powerful, and relatively easy things you can do is create a custom feed using the no-code tool [graze.social](https://graze.social). In fact, I would recommend creating at least two different feeds: 1) an internal feed you use for actively listening for mentions of your brand, product, or cause, and 2) a public community feed that ties in with your niche in some way. 278 + 279 + The internal feed is designed to include only mentions of your brand name, links to your website, or important keywords for monitoring trends. You, your social media manager, or customer support team can use this feed to stay on top of feedback or community questions. Keep in mind though that any feed you create is public and could be followed by any other user. 280 + 281 + The public feed is your brand's contribution to the community. It should be a highly valuable feed that displays curated or relevant posts from other accounts that are about or adjacent to your niche. This feed shouldn't be specifically for just the people that are aware of you, but rather it should be for appealing to newcomers first and foremost. By doing this, you're providing a valuable free service to people and getting your brand name out there at the same time. Be tactful in how you promote yourself though using this method, most people are not going to be interested in a feed that is created or managed in a way that comes across as self promo or advertising. 282 + 283 + ![Screenshot of the Graze.social feed editor interface showing various filtering options](screenshots/graze-editor.jpg) 284 + 285 + #### 3. Starter packs (Easy) 286 + 287 + As a reminder, Starter Packs are just curated lists of Bluesky accounts that you're recommending other folks check out or follow. They are helpful for sending to new community members or onboarding people to Bluesky itself. When you send a Starter Pack link to someone, they can easily follow all of the accounts contained within it and create their very own Bluesky account using the pack's special invite link. 288 + 289 + When thinking about creating your own Starter Packs, one simple option would be to create a Starter Pack with prominent public-facing members of your organization or team that you want people to also follow. Another idea would be to curate a list of adjacent brands and content creators within your niche. For instance, if you are a fashion designer, then you could put together a list of other prominent designers and clothing labels within your industry. There's a chance your Starter Pack might be well-received and get spread around a lot, thereby earning you some attention for it. 290 + 291 + You can even include up to three custom feeds in a Starter Pack, so people can use them to find the feeds you created earlier as well. 292 + 293 + ![Screenshot of the Boston Starter Pack showing various city department accounts that can be followed](screenshots/boston-starter-pack.png) 294 + 295 + #### 4. Custom domains / handles (Moderate) 296 + 297 + While the primary purpose of domain names as usernames on Bluesky is for self-verification purposes, people have begun using subdomains for community identification and recognition. A subdomain is when you see a normal domain like example.com but it has an extra part tacked on at the beginning such as extra.example.com, with the "extra" part being the subdomain. This can be powerful when you pair it with a tool like [handles.net](https://handles.net/) which allows you to give custom subdomains to your community members, volunteers, or team members. 298 + 299 + For instance, Taylor Swift fans can get their very own free username that uses the swifties.social domain, making it easy for other Swifties to identify them as part of the group. So your username could be @hannah.swifties.social. The Bluesky team itself uses this for some of their team members, such as @jay.bsky.team. If you only want to issue a handful of subdomain handles, then you can do this fairly easy within your domain manager's DNS settings, but if you intend to issue a large number of usernames then it's best to get your IT team to assist or use a service like [handles.net](https://handles.net/). 300 + 301 + ### Case Studies 302 + 303 + #### The City of Boston 304 + 305 + [https://bsky.app/profile/boston.gov](https://bsky.app/profile/boston.gov) 306 + 307 + The most populous city in the commonwealth of Massachusetts is also one of the most active municipalities on Bluesky currently. Boasting over two dozen interconnected government-owned accounts, Boston's embrace of Bluesky is a great blueprint for how a large entity could join the social network. 308 + 309 + Each of their major city departments has its own Bluesky account with consistent branding and official usernames that are self-verified using the subdomain system. Anyone can instantly recognize a Boston account as being official because they all end in the [boston.gov](https://boston.gov) domain name. Furthermore, they have a Starter Pack that contains all of the city accounts, plus the mayor, in one place so that Boston residents can quickly follow all of these local sources with one tap. 310 + 311 + Boston even has a unified custom feed that you can follow to see just city updates across all departments in one timeline, clutter free. 312 + 313 + #### Bikesky 314 + 315 + [https://bsky.app/profile/bikesky.social](https://bsky.app/profile/bikesky.social) 316 + 317 + A bicycle rider named Derek van Vliet took it upon himself to organize a community on Bluesky for bike enthusiasts. He called it "Bikesky", and it now has over 12,000 followers. It also supports almost every single one of Bluesky's unique features such as custom feeds, labelers, starter packs, and even a moderation service. 318 + 319 + The Bikesky labeler lets a community member add a tag to their profile to display what kind of bike enthusiast they are. These labels range from "Tandem Bike Partner", "Recumbent Rider", to even "Folding Bike Fan". This tool makes it easy for community members to recognize and connect with one another, especially those that share common interests or traits. 320 + 321 + Bikesky even has over a dozen custom feeds so that bike enjoyers can find niche content created within the biking community. Many of these feeds gather/curate posts automatically by matching the #Bikesky hashtag. 322 + 323 + #### Bandcamp 324 + 325 + [https://blog.bandcamp.com/2025/03/17/now-introducing-our-integration-with-bluesky/](https://blog.bandcamp.com/2025/03/17/now-introducing-our-integration-with-bluesky/) 326 + 327 + The popular online music platform for independent recording artists known as Bandcamp recently launched a first-of-its-kind integration with Bluesky that allows its Pro users to set their Bluesky username to be their Bandcamp subdomain such as `artist.bandcamp.com`. It's unclear how many of its users have adopted the feature yet, but either way it serves as a powerful example of how another platform can choose to connect to Bluesky and let its community self-identify in a way that makes sense to them. 328 + 329 + ## Analyzing and Measuring success 330 + 331 + Once you've started posting on Bluesky, there might come a time where you need to see if what you're doing is having any effect. How you go about investigating this will largely depend on your specific goals and needs, but in general here are some of the key areas you can pay attention to: 332 + 333 + - Follower count 334 + - Engagements received (likes, reposts, replies, mentions) 335 + - Engagement rate 336 + - Website traffic from Bluesky 337 + 338 + Unlike other major social media platforms, Bluesky does not yet offer in-app analytics or social media management features that you might be accustomed to. Thanks to its free and public API though, dozens of third-party social media management tools have popped up that can help fill the gap. 339 + 340 + While most of these tools are free for anyone to use, they vary widely in terms of the quality and set of features they provide. The right combination of tools for you will depend on what you're trying to accomplish or track. If you're a for-profit business with a marketing budget, consider donating or leaving a tip for the creator of the tool you end up using the most! 341 + 342 + ### Analytics 343 + 344 + * [Bluesky Meter](https://blueskymeter.com/) 345 + * [Skykit](https://skykit.blue/) 346 + * [Blueview](https://blueview.app/) 347 + * [Skyzoo](https://skyzoo.blue/) 348 + * [Alt Text Rating](https://cred.blue/alt-text) 349 + * [Fedica](https://fedica.com/) 350 + * [Best time to post](https://besttimetopost.blue/) 351 + 352 + Note: there is no way to count the number of views/impressions that a Bluesky post gets. The core metrics for a post are limited to likes, replies, quotes, and reposts. 353 + 354 + ### Cross-posting and Scheduling 355 + 356 + * [Buffer](https://buffer.com/) 357 + * [Fedica](https://fedica.com/) 358 + * [CoSchedule](https://coschedule.com/) 359 + 360 + ### Monitoring 361 + 362 + * [deck.blue](https://deck.blue/) 363 + * [graze.social](https://graze.social/) 364 + * [ClearSky](https://clearsky.app/) 365 + 366 + ### Utilities 367 + 368 + * [Network Analyzer](https://bsky-follow-finder.theo.io/) 369 + * [Skeetbeaver](https://skeetbeaver.pages.dev/) 370 + * [Boat](https://boat.kelinci.net/) 371 + 372 + ## Risk Mitigation 373 + 374 + The internet is a wonderful place, but it can sometimes be difficult to navigate safely thanks to the fact that humans are complicated and don't always get along well. They have differing (sometimes opposing) viewpoints, cultural norms, and expectations for everything under the sun. It's important to keep this in mind as you join the Bluesky community because there are some key things you should be aware of and watch out for. 375 + 376 + ### Sensitive Topics 377 + 378 + In additions to all of the usual touchy subjects in social settings such as politics and religion, Bluesky in particular has several vocal sub communities that are critical or outright opposed to the following issues: 379 + 380 + * Artificial Intelligence (especially Image Generation) 381 + * Cryptocurrency and Blockchain 382 + * Big Tech (Facebook, Google, etc) 383 + 384 + If your brand provides products or content that cover any of these topics, keep in mind that you could encounter some pushback or even aggression in the replies to your posts at times. You can reduce the degree to which this becomes an issue by communicating clearly and being prepared with some mitigation strategies. 385 + 386 + ### Mitigation Strategies 387 + 388 + #### Safety features 389 + 390 + One approach to risk mitigation is through effective use of Bluesky's safety features, as well as a few third-party tools. Let's briefly take a look at each technique so that you'll understand how they work. 391 + 392 + When using these features it is important to remember that the way in which you use them can sometimes make matters worse if you don't know what you're doing. Also, I don't reference the "mute" feature here because I don't typically recommend brands mute people because you will no longer have visibility into what they are potentially saying under your posts or about you in other places. If you are an individual, personality, or content creator this advice probably doesn't apply. 393 + 394 + ##### Post Interaction Settings (Thread gates) 395 + 396 + At any moment you can change who is able to reply to or quote your posts. If a potentially risky or sensitive post starts getting traction outside of your direct audience, you could always preemptively restrict who has permission to reply and quote in order to avoid unwanted engagement. 397 + 398 + ![Screenshot showing Bluesky's post interaction settings interface, which allows you to control who can reply to or quote your posts](screenshots/post-interaction-settings.png) 399 + 400 + While it's generally best to let anyone reply to a post, there are plenty of scenarios in which it would make sense to add restrictions. You can even limit replies to specific groups of users by making custom lists. 401 + 402 + And if you find yourself in an extreme scenario, there's even [a third-party tool](https://boat.kelinci.net/bsky-threadgate-applicator) that helps you limit the replies on all of your past posts in bulk. 403 + 404 + ##### Blocks and blocklists 405 + 406 + For organization or company accounts, it's typically best to avoid blocking people because it could make your brand be perceived negatively, so proceed with caution. 407 + 408 + If you're a content creator, influencer, or personality, then utilizing the block feature is much more acceptable and likely not to create any PR issues. Your mental health and safety matters a lot, so protect yourself as is needed. 409 + 410 + That being said, you should know that your blocks and blocklists are public data, so anyone can use a tool like [Clearsky](https://clearsky.app/) to see who you're trying to keep away. 411 + 412 + ##### Hide replies for everyone 413 + 414 + If you've chosen to let anyone reply to your posts, you might encounter a scenario in which someone says something in the comments that you don't want to be publicly attached to your content. In these situations, you can easily hide a reply for everyone. There are ways to find these posts if people really want to see them, but it can be an effective means of protecting your space from spam or offensive content. 415 + 416 + If you're experiencing a pile-on or targeted harassment, my recommendation is to disable replies from accounts you don't follow, disable quote-posts, and then hide any replies that you don't want featured next to your content. 417 + 418 + ### Active Monitoring 419 + 420 + To stay ahead of the curve, you might want to consider utilizing a few third-party tools that can give you insight into how people are talking about your brand or industry. 421 + 422 + #### Custom Feeds 423 + 424 + The simplest approach to begin active monitoring is by creating a special "brand mentions" feed using a feed builder tool like [Graze](https://www.graze.social/) which we talked about earlier. This is a unified feed that can be configured granularly to show you a lot more than what the "mentions" part of your notifications can. For instance, you can configure a feed to show you all posts on the network if they match any of the following criteria: 425 + 426 + * @ mentions of your brand accounts 427 + * mentions of your brand name that aren't explicitly tagged with an @ 428 + * posts containing links that contain your domain name 429 + 430 + You could even configure a "competitors" feed that displays similar information but for one of your key competitors if you want to stay in the loop with what's happening to them. 431 + 432 + #### Firehose/Jetstream 433 + 434 + For larger brands and organizations with development resources and heightened monitoring needs, you can directly tap into [the live stream of raw data flowing through the network](https://docs.bsky.app/blog/jetstream) to get virtually instantaneous alerts. This mechanism is commonly referred to as accessing the Bluesky/ATProto "firehose", and it requires highly technical skills to implement effectively. For this reason, most people should just rely upon a feed builder like Graze. 435 + 436 + #### BluNotify 437 + 438 + The official Bluesky app currently only serves push notifications for certain things like new likes, reposts, and mentions. If you're in need of getting alerts when a specific account posts something new, you can download a separate app called [BluNotify](https://bluenotify.app/) which seems to have reliable coverage in this area. 439 + 440 + #### UnfollowTracker 441 + 442 + While the name is focused upon one specific feature, [this third-party app](https://bsky.app/profile/bluesky-tracker.bsky.social) actually offers a variety of monitoring features in addition to just showing you when an account unfollows you. You can also see blocks, when people deactivate, or when accounts are banned. 443 + 444 + #### Clearsky 445 + 446 + [This tool](https://clearsky.app/) was mentioned earlier in the guide, but it can be helpful for brands as a monitoring application because you're able to see all the lists your account has been put on as well as all the accounts that are blocking you. If you notice a huge spike out of the blue in the number of blockers you have, that might be an indicator that your account is being spread around in a negative context. 447 + 448 + ### Behavioral approaches 449 + 450 + #### Defuse 451 + 452 + In the event that you feel like you must actually engage with a negative comment, it's usually best to not "fight back". Attempt to de-escalate rather than poking the hornet's nest, and don't try to "silence" someone or move them to a private channel... doing so will often just get you in more trouble because it will be seen as an attempt to avoid public accountability. 453 + 454 + #### Ignore 455 + 456 + Try to ignore negative posts if they are coming from an audience that isn't your audience or that doesn't impact your audience. Spending energy on these efforts is likely a waste of time and a distraction from your mission. 457 + 458 + #### Introspect 459 + 460 + When you are going to make a post about a topic that is sensitive, think carefully about how you word what you say and if it's necessary. Actively try to see your own words in the worst possible light because this is how some people will be perceiving them. 461 + 462 + ### Community guidelines 463 + 464 + Lastly, be aware of Bluesky's rules and make sure you don't violate them. 465 + 466 + [https://bsky.social/about/support/community-guidelines](https://bsky.social/about/support/community-guidelines) 467 + 468 + ## Next Steps 469 + 470 + While Bluesky has only been on the social media scene for a tiny fraction of the industry's history, it has already made significant strides towards creating a more consumer-friendly platform and foundation upon which a healthier system could emerge. Establishing yourself as a brand or creator early in the journey positions you for the potential benefits that could come if the AT Protocol succeeds in its mission. 471 + 472 + With that being said, you should likely still maintain some sort of a presence on other networks in the meantime. Bluesky is still relatively small in comparison to legacy platforms like LinkedIn, Facebook, and Instagram, so it probably can't meet all your communication needs just yet. It can be a viable alternative to Twitter/X though if you prefer to avoid that place completely. 473 + 474 + The information contained in this guide will hopefully get you started, and I will do my best to provide updates as new features come out and dynamics evolve. If you have any questions, feel free to leave a comment below or in the replies to my post on Bluesky. 475 + 476 + --- 477 + 478 + *Thanks for reading!* 479 + 480 + Dame 481 + [Find me on Bluesky (@dame.is)](https://bsky.app/profile/dame.is)
website/guides/screenshots/bikesky-labeler.png

This is a binary file and will not be displayed.

website/guides/screenshots/blacksky-moderation-labeler.png

This is a binary file and will not be displayed.

website/guides/screenshots/boston-starter-pack.png

This is a binary file and will not be displayed.

website/guides/screenshots/change-handle.png

This is a binary file and will not be displayed.

website/guides/screenshots/feed-preferences.png

This is a binary file and will not be displayed.

website/guides/screenshots/fungi-friends-feed.png

This is a binary file and will not be displayed.

website/guides/screenshots/graze-editor.jpg

This is a binary file and will not be displayed.

website/guides/screenshots/jack-tweet.png

This is a binary file and will not be displayed.

website/guides/screenshots/labeler-options.png

This is a binary file and will not be displayed.

website/guides/screenshots/paul-verification.png

This is a binary file and will not be displayed.

website/guides/screenshots/post-interaction-settings.png

This is a binary file and will not be displayed.

website/guides/screenshots/starter-pack.png

This is a binary file and will not be displayed.

website/guides/screenshots/taylor-swift-eras-labeler.png

This is a binary file and will not be displayed.

website/guides/screenshots/thread-preferences.png

This is a binary file and will not be displayed.

+13 -5
website/index.html
··· 22 22 <meta property="og:image" content="/og-image.jpg"> 23 23 <script> 24 24 window.va = window.va || function () { (window.vaq = window.vaq || []).push(arguments); }; 25 - </script> 26 - <script defer src="/_vercel/insights/script.js"></script> 25 + </script> 26 + <script defer src="/_vercel/insights/script.js"></script> 27 + <script defer src="theme.js"></script> 28 + <script defer src="logo-hover.js"></script> 27 29 </head> 28 30 <body> 29 31 <div class="container"> 30 32 <section class="profile"> 31 33 <div class="logo-container"> 32 - <img src="/atpotato-logo.png" alt="atpotato logo" class="logo"> 34 + <img src="/atpotato-normal.png" alt="atpotato logo" class="logo"> 33 35 </div> 34 36 <h1>atpotato</h1> 35 37 <p class="subtitle">https://atpota.to</p> ··· 43 45 </section> 44 46 <section class="projects"> 45 47 <div class="project-list"> 46 - <a href="https://cred.blue" class="project-link" target="_blank">cred.blue ๐Ÿฅ</a> 47 - <a href="https://flushes.app" class="project-link" target="_blank">flushes ๐Ÿงป</a> 48 + <a href="/guides/bluesky-for-brands" class="project-link">How to use Bluesky to grow your brand</a> 49 + <a href="https://cred.blue" class="project-link" target="_blank">cred.blue</a> 50 + <a href="https://flushes.app" class="project-link" target="_blank">flushes.app</a> 48 51 <p class="coming-soon">...more coming soon</p> 49 52 </div> 50 53 </section> 51 54 </div> 55 + 56 + <div class="button-backdrop"></div> 57 + <a href="/" class="home-button"> 58 + <img src="/atpotato-normal.png" alt="atpotato logo" class="home-logo"> 59 + </a> 52 60 </body> 53 61 </html>
+37
website/logo-hover.js
··· 1 + // Array of all potato expressions 2 + const potatoExpressions = [ 3 + '/atpotato-dead.png', 4 + '/atpotato-waow.png', 5 + '/atpotato-scawy.png', 6 + '/atpotato-kawaii.png' 7 + ]; 8 + 9 + // Get a random potato expression 10 + function getRandomExpression() { 11 + return potatoExpressions[Math.floor(Math.random() * potatoExpressions.length)]; 12 + } 13 + 14 + // Handle logo hover effect 15 + function initLogoHover() { 16 + // Target all logo variants 17 + const logos = document.querySelectorAll('.nav-logo, .logo, .home-logo'); 18 + 19 + logos.forEach(logo => { 20 + const defaultSrc = '/atpotato-normal.png'; 21 + 22 + // Set initial source 23 + logo.src = defaultSrc; 24 + 25 + // Add hover listeners 26 + logo.addEventListener('mouseenter', () => { 27 + logo.src = getRandomExpression(); 28 + }); 29 + 30 + logo.addEventListener('mouseleave', () => { 31 + logo.src = defaultSrc; 32 + }); 33 + }); 34 + } 35 + 36 + // Initialize when DOM is loaded 37 + document.addEventListener('DOMContentLoaded', initLogoHover);
+1073
website/package-lock.json
··· 1 + { 2 + "name": "atpotato-website", 3 + "version": "1.0.0", 4 + "lockfileVersion": 3, 5 + "requires": true, 6 + "packages": { 7 + "": { 8 + "name": "atpotato-website", 9 + "version": "1.0.0", 10 + "devDependencies": { 11 + "serve": "^14.2.1" 12 + } 13 + }, 14 + "node_modules/@zeit/schemas": { 15 + "version": "2.36.0", 16 + "resolved": "https://registry.npmjs.org/@zeit/schemas/-/schemas-2.36.0.tgz", 17 + "integrity": "sha512-7kjMwcChYEzMKjeex9ZFXkt1AyNov9R5HZtjBKVsmVpw7pa7ZtlCGvCBC2vnnXctaYN+aRI61HjIqeetZW5ROg==", 18 + "dev": true, 19 + "license": "MIT" 20 + }, 21 + "node_modules/accepts": { 22 + "version": "1.3.8", 23 + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 24 + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 25 + "dev": true, 26 + "license": "MIT", 27 + "dependencies": { 28 + "mime-types": "~2.1.34", 29 + "negotiator": "0.6.3" 30 + }, 31 + "engines": { 32 + "node": ">= 0.6" 33 + } 34 + }, 35 + "node_modules/ajv": { 36 + "version": "8.12.0", 37 + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", 38 + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", 39 + "dev": true, 40 + "license": "MIT", 41 + "dependencies": { 42 + "fast-deep-equal": "^3.1.1", 43 + "json-schema-traverse": "^1.0.0", 44 + "require-from-string": "^2.0.2", 45 + "uri-js": "^4.2.2" 46 + }, 47 + "funding": { 48 + "type": "github", 49 + "url": "https://github.com/sponsors/epoberezkin" 50 + } 51 + }, 52 + "node_modules/ansi-align": { 53 + "version": "3.0.1", 54 + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", 55 + "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", 56 + "dev": true, 57 + "license": "ISC", 58 + "dependencies": { 59 + "string-width": "^4.1.0" 60 + } 61 + }, 62 + "node_modules/ansi-align/node_modules/ansi-regex": { 63 + "version": "5.0.1", 64 + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 65 + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 66 + "dev": true, 67 + "license": "MIT", 68 + "engines": { 69 + "node": ">=8" 70 + } 71 + }, 72 + "node_modules/ansi-align/node_modules/emoji-regex": { 73 + "version": "8.0.0", 74 + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 75 + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 76 + "dev": true, 77 + "license": "MIT" 78 + }, 79 + "node_modules/ansi-align/node_modules/string-width": { 80 + "version": "4.2.3", 81 + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 82 + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 83 + "dev": true, 84 + "license": "MIT", 85 + "dependencies": { 86 + "emoji-regex": "^8.0.0", 87 + "is-fullwidth-code-point": "^3.0.0", 88 + "strip-ansi": "^6.0.1" 89 + }, 90 + "engines": { 91 + "node": ">=8" 92 + } 93 + }, 94 + "node_modules/ansi-align/node_modules/strip-ansi": { 95 + "version": "6.0.1", 96 + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 97 + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 98 + "dev": true, 99 + "license": "MIT", 100 + "dependencies": { 101 + "ansi-regex": "^5.0.1" 102 + }, 103 + "engines": { 104 + "node": ">=8" 105 + } 106 + }, 107 + "node_modules/ansi-regex": { 108 + "version": "6.1.0", 109 + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", 110 + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", 111 + "dev": true, 112 + "license": "MIT", 113 + "engines": { 114 + "node": ">=12" 115 + }, 116 + "funding": { 117 + "url": "https://github.com/chalk/ansi-regex?sponsor=1" 118 + } 119 + }, 120 + "node_modules/ansi-styles": { 121 + "version": "6.2.1", 122 + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", 123 + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", 124 + "dev": true, 125 + "license": "MIT", 126 + "engines": { 127 + "node": ">=12" 128 + }, 129 + "funding": { 130 + "url": "https://github.com/chalk/ansi-styles?sponsor=1" 131 + } 132 + }, 133 + "node_modules/arch": { 134 + "version": "2.2.0", 135 + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", 136 + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", 137 + "dev": true, 138 + "funding": [ 139 + { 140 + "type": "github", 141 + "url": "https://github.com/sponsors/feross" 142 + }, 143 + { 144 + "type": "patreon", 145 + "url": "https://www.patreon.com/feross" 146 + }, 147 + { 148 + "type": "consulting", 149 + "url": "https://feross.org/support" 150 + } 151 + ], 152 + "license": "MIT" 153 + }, 154 + "node_modules/arg": { 155 + "version": "5.0.2", 156 + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", 157 + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", 158 + "dev": true, 159 + "license": "MIT" 160 + }, 161 + "node_modules/balanced-match": { 162 + "version": "1.0.2", 163 + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 164 + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 165 + "dev": true, 166 + "license": "MIT" 167 + }, 168 + "node_modules/boxen": { 169 + "version": "7.0.0", 170 + "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz", 171 + "integrity": "sha512-j//dBVuyacJbvW+tvZ9HuH03fZ46QcaKvvhZickZqtB271DxJ7SNRSNxrV/dZX0085m7hISRZWbzWlJvx/rHSg==", 172 + "dev": true, 173 + "license": "MIT", 174 + "dependencies": { 175 + "ansi-align": "^3.0.1", 176 + "camelcase": "^7.0.0", 177 + "chalk": "^5.0.1", 178 + "cli-boxes": "^3.0.0", 179 + "string-width": "^5.1.2", 180 + "type-fest": "^2.13.0", 181 + "widest-line": "^4.0.1", 182 + "wrap-ansi": "^8.0.1" 183 + }, 184 + "engines": { 185 + "node": ">=14.16" 186 + }, 187 + "funding": { 188 + "url": "https://github.com/sponsors/sindresorhus" 189 + } 190 + }, 191 + "node_modules/brace-expansion": { 192 + "version": "1.1.12", 193 + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", 194 + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", 195 + "dev": true, 196 + "license": "MIT", 197 + "dependencies": { 198 + "balanced-match": "^1.0.0", 199 + "concat-map": "0.0.1" 200 + } 201 + }, 202 + "node_modules/bytes": { 203 + "version": "3.0.0", 204 + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", 205 + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", 206 + "dev": true, 207 + "license": "MIT", 208 + "engines": { 209 + "node": ">= 0.8" 210 + } 211 + }, 212 + "node_modules/camelcase": { 213 + "version": "7.0.1", 214 + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-7.0.1.tgz", 215 + "integrity": "sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==", 216 + "dev": true, 217 + "license": "MIT", 218 + "engines": { 219 + "node": ">=14.16" 220 + }, 221 + "funding": { 222 + "url": "https://github.com/sponsors/sindresorhus" 223 + } 224 + }, 225 + "node_modules/chalk": { 226 + "version": "5.0.1", 227 + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", 228 + "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", 229 + "dev": true, 230 + "license": "MIT", 231 + "engines": { 232 + "node": "^12.17.0 || ^14.13 || >=16.0.0" 233 + }, 234 + "funding": { 235 + "url": "https://github.com/chalk/chalk?sponsor=1" 236 + } 237 + }, 238 + "node_modules/chalk-template": { 239 + "version": "0.4.0", 240 + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", 241 + "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", 242 + "dev": true, 243 + "license": "MIT", 244 + "dependencies": { 245 + "chalk": "^4.1.2" 246 + }, 247 + "engines": { 248 + "node": ">=12" 249 + }, 250 + "funding": { 251 + "url": "https://github.com/chalk/chalk-template?sponsor=1" 252 + } 253 + }, 254 + "node_modules/chalk-template/node_modules/ansi-styles": { 255 + "version": "4.3.0", 256 + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 257 + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 258 + "dev": true, 259 + "license": "MIT", 260 + "dependencies": { 261 + "color-convert": "^2.0.1" 262 + }, 263 + "engines": { 264 + "node": ">=8" 265 + }, 266 + "funding": { 267 + "url": "https://github.com/chalk/ansi-styles?sponsor=1" 268 + } 269 + }, 270 + "node_modules/chalk-template/node_modules/chalk": { 271 + "version": "4.1.2", 272 + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 273 + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 274 + "dev": true, 275 + "license": "MIT", 276 + "dependencies": { 277 + "ansi-styles": "^4.1.0", 278 + "supports-color": "^7.1.0" 279 + }, 280 + "engines": { 281 + "node": ">=10" 282 + }, 283 + "funding": { 284 + "url": "https://github.com/chalk/chalk?sponsor=1" 285 + } 286 + }, 287 + "node_modules/cli-boxes": { 288 + "version": "3.0.0", 289 + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", 290 + "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", 291 + "dev": true, 292 + "license": "MIT", 293 + "engines": { 294 + "node": ">=10" 295 + }, 296 + "funding": { 297 + "url": "https://github.com/sponsors/sindresorhus" 298 + } 299 + }, 300 + "node_modules/clipboardy": { 301 + "version": "3.0.0", 302 + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz", 303 + "integrity": "sha512-Su+uU5sr1jkUy1sGRpLKjKrvEOVXgSgiSInwa/qeID6aJ07yh+5NWc3h2QfjHjBnfX4LhtFcuAWKUsJ3r+fjbg==", 304 + "dev": true, 305 + "license": "MIT", 306 + "dependencies": { 307 + "arch": "^2.2.0", 308 + "execa": "^5.1.1", 309 + "is-wsl": "^2.2.0" 310 + }, 311 + "engines": { 312 + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 313 + }, 314 + "funding": { 315 + "url": "https://github.com/sponsors/sindresorhus" 316 + } 317 + }, 318 + "node_modules/color-convert": { 319 + "version": "2.0.1", 320 + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 321 + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 322 + "dev": true, 323 + "license": "MIT", 324 + "dependencies": { 325 + "color-name": "~1.1.4" 326 + }, 327 + "engines": { 328 + "node": ">=7.0.0" 329 + } 330 + }, 331 + "node_modules/color-name": { 332 + "version": "1.1.4", 333 + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 334 + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 335 + "dev": true, 336 + "license": "MIT" 337 + }, 338 + "node_modules/compressible": { 339 + "version": "2.0.18", 340 + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", 341 + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", 342 + "dev": true, 343 + "license": "MIT", 344 + "dependencies": { 345 + "mime-db": ">= 1.43.0 < 2" 346 + }, 347 + "engines": { 348 + "node": ">= 0.6" 349 + } 350 + }, 351 + "node_modules/compression": { 352 + "version": "1.7.4", 353 + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", 354 + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", 355 + "dev": true, 356 + "license": "MIT", 357 + "dependencies": { 358 + "accepts": "~1.3.5", 359 + "bytes": "3.0.0", 360 + "compressible": "~2.0.16", 361 + "debug": "2.6.9", 362 + "on-headers": "~1.0.2", 363 + "safe-buffer": "5.1.2", 364 + "vary": "~1.1.2" 365 + }, 366 + "engines": { 367 + "node": ">= 0.8.0" 368 + } 369 + }, 370 + "node_modules/concat-map": { 371 + "version": "0.0.1", 372 + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 373 + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 374 + "dev": true, 375 + "license": "MIT" 376 + }, 377 + "node_modules/content-disposition": { 378 + "version": "0.5.2", 379 + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 380 + "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==", 381 + "dev": true, 382 + "license": "MIT", 383 + "engines": { 384 + "node": ">= 0.6" 385 + } 386 + }, 387 + "node_modules/cross-spawn": { 388 + "version": "7.0.6", 389 + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", 390 + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", 391 + "dev": true, 392 + "license": "MIT", 393 + "dependencies": { 394 + "path-key": "^3.1.0", 395 + "shebang-command": "^2.0.0", 396 + "which": "^2.0.1" 397 + }, 398 + "engines": { 399 + "node": ">= 8" 400 + } 401 + }, 402 + "node_modules/debug": { 403 + "version": "2.6.9", 404 + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 405 + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 406 + "dev": true, 407 + "license": "MIT", 408 + "dependencies": { 409 + "ms": "2.0.0" 410 + } 411 + }, 412 + "node_modules/deep-extend": { 413 + "version": "0.6.0", 414 + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 415 + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", 416 + "dev": true, 417 + "license": "MIT", 418 + "engines": { 419 + "node": ">=4.0.0" 420 + } 421 + }, 422 + "node_modules/eastasianwidth": { 423 + "version": "0.2.0", 424 + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", 425 + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", 426 + "dev": true, 427 + "license": "MIT" 428 + }, 429 + "node_modules/emoji-regex": { 430 + "version": "9.2.2", 431 + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", 432 + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", 433 + "dev": true, 434 + "license": "MIT" 435 + }, 436 + "node_modules/execa": { 437 + "version": "5.1.1", 438 + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", 439 + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", 440 + "dev": true, 441 + "license": "MIT", 442 + "dependencies": { 443 + "cross-spawn": "^7.0.3", 444 + "get-stream": "^6.0.0", 445 + "human-signals": "^2.1.0", 446 + "is-stream": "^2.0.0", 447 + "merge-stream": "^2.0.0", 448 + "npm-run-path": "^4.0.1", 449 + "onetime": "^5.1.2", 450 + "signal-exit": "^3.0.3", 451 + "strip-final-newline": "^2.0.0" 452 + }, 453 + "engines": { 454 + "node": ">=10" 455 + }, 456 + "funding": { 457 + "url": "https://github.com/sindresorhus/execa?sponsor=1" 458 + } 459 + }, 460 + "node_modules/fast-deep-equal": { 461 + "version": "3.1.3", 462 + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 463 + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 464 + "dev": true, 465 + "license": "MIT" 466 + }, 467 + "node_modules/get-stream": { 468 + "version": "6.0.1", 469 + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", 470 + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", 471 + "dev": true, 472 + "license": "MIT", 473 + "engines": { 474 + "node": ">=10" 475 + }, 476 + "funding": { 477 + "url": "https://github.com/sponsors/sindresorhus" 478 + } 479 + }, 480 + "node_modules/has-flag": { 481 + "version": "4.0.0", 482 + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 483 + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 484 + "dev": true, 485 + "license": "MIT", 486 + "engines": { 487 + "node": ">=8" 488 + } 489 + }, 490 + "node_modules/human-signals": { 491 + "version": "2.1.0", 492 + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", 493 + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", 494 + "dev": true, 495 + "license": "Apache-2.0", 496 + "engines": { 497 + "node": ">=10.17.0" 498 + } 499 + }, 500 + "node_modules/ini": { 501 + "version": "1.3.8", 502 + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", 503 + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", 504 + "dev": true, 505 + "license": "ISC" 506 + }, 507 + "node_modules/is-docker": { 508 + "version": "2.2.1", 509 + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", 510 + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", 511 + "dev": true, 512 + "license": "MIT", 513 + "bin": { 514 + "is-docker": "cli.js" 515 + }, 516 + "engines": { 517 + "node": ">=8" 518 + }, 519 + "funding": { 520 + "url": "https://github.com/sponsors/sindresorhus" 521 + } 522 + }, 523 + "node_modules/is-fullwidth-code-point": { 524 + "version": "3.0.0", 525 + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 526 + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 527 + "dev": true, 528 + "license": "MIT", 529 + "engines": { 530 + "node": ">=8" 531 + } 532 + }, 533 + "node_modules/is-port-reachable": { 534 + "version": "4.0.0", 535 + "resolved": "https://registry.npmjs.org/is-port-reachable/-/is-port-reachable-4.0.0.tgz", 536 + "integrity": "sha512-9UoipoxYmSk6Xy7QFgRv2HDyaysmgSG75TFQs6S+3pDM7ZhKTF/bskZV+0UlABHzKjNVhPjYCLfeZUEg1wXxig==", 537 + "dev": true, 538 + "license": "MIT", 539 + "engines": { 540 + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 541 + }, 542 + "funding": { 543 + "url": "https://github.com/sponsors/sindresorhus" 544 + } 545 + }, 546 + "node_modules/is-stream": { 547 + "version": "2.0.1", 548 + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", 549 + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", 550 + "dev": true, 551 + "license": "MIT", 552 + "engines": { 553 + "node": ">=8" 554 + }, 555 + "funding": { 556 + "url": "https://github.com/sponsors/sindresorhus" 557 + } 558 + }, 559 + "node_modules/is-wsl": { 560 + "version": "2.2.0", 561 + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", 562 + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", 563 + "dev": true, 564 + "license": "MIT", 565 + "dependencies": { 566 + "is-docker": "^2.0.0" 567 + }, 568 + "engines": { 569 + "node": ">=8" 570 + } 571 + }, 572 + "node_modules/isexe": { 573 + "version": "2.0.0", 574 + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 575 + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 576 + "dev": true, 577 + "license": "ISC" 578 + }, 579 + "node_modules/json-schema-traverse": { 580 + "version": "1.0.0", 581 + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", 582 + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", 583 + "dev": true, 584 + "license": "MIT" 585 + }, 586 + "node_modules/merge-stream": { 587 + "version": "2.0.0", 588 + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", 589 + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", 590 + "dev": true, 591 + "license": "MIT" 592 + }, 593 + "node_modules/mime-db": { 594 + "version": "1.54.0", 595 + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", 596 + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", 597 + "dev": true, 598 + "license": "MIT", 599 + "engines": { 600 + "node": ">= 0.6" 601 + } 602 + }, 603 + "node_modules/mime-types": { 604 + "version": "2.1.35", 605 + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 606 + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 607 + "dev": true, 608 + "license": "MIT", 609 + "dependencies": { 610 + "mime-db": "1.52.0" 611 + }, 612 + "engines": { 613 + "node": ">= 0.6" 614 + } 615 + }, 616 + "node_modules/mime-types/node_modules/mime-db": { 617 + "version": "1.52.0", 618 + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 619 + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 620 + "dev": true, 621 + "license": "MIT", 622 + "engines": { 623 + "node": ">= 0.6" 624 + } 625 + }, 626 + "node_modules/mimic-fn": { 627 + "version": "2.1.0", 628 + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", 629 + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", 630 + "dev": true, 631 + "license": "MIT", 632 + "engines": { 633 + "node": ">=6" 634 + } 635 + }, 636 + "node_modules/minimatch": { 637 + "version": "3.1.2", 638 + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 639 + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 640 + "dev": true, 641 + "license": "ISC", 642 + "dependencies": { 643 + "brace-expansion": "^1.1.7" 644 + }, 645 + "engines": { 646 + "node": "*" 647 + } 648 + }, 649 + "node_modules/minimist": { 650 + "version": "1.2.8", 651 + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", 652 + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", 653 + "dev": true, 654 + "license": "MIT", 655 + "funding": { 656 + "url": "https://github.com/sponsors/ljharb" 657 + } 658 + }, 659 + "node_modules/ms": { 660 + "version": "2.0.0", 661 + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 662 + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", 663 + "dev": true, 664 + "license": "MIT" 665 + }, 666 + "node_modules/negotiator": { 667 + "version": "0.6.3", 668 + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 669 + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", 670 + "dev": true, 671 + "license": "MIT", 672 + "engines": { 673 + "node": ">= 0.6" 674 + } 675 + }, 676 + "node_modules/npm-run-path": { 677 + "version": "4.0.1", 678 + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", 679 + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", 680 + "dev": true, 681 + "license": "MIT", 682 + "dependencies": { 683 + "path-key": "^3.0.0" 684 + }, 685 + "engines": { 686 + "node": ">=8" 687 + } 688 + }, 689 + "node_modules/on-headers": { 690 + "version": "1.0.2", 691 + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", 692 + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", 693 + "dev": true, 694 + "license": "MIT", 695 + "engines": { 696 + "node": ">= 0.8" 697 + } 698 + }, 699 + "node_modules/onetime": { 700 + "version": "5.1.2", 701 + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", 702 + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", 703 + "dev": true, 704 + "license": "MIT", 705 + "dependencies": { 706 + "mimic-fn": "^2.1.0" 707 + }, 708 + "engines": { 709 + "node": ">=6" 710 + }, 711 + "funding": { 712 + "url": "https://github.com/sponsors/sindresorhus" 713 + } 714 + }, 715 + "node_modules/path-is-inside": { 716 + "version": "1.0.2", 717 + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", 718 + "integrity": "sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==", 719 + "dev": true, 720 + "license": "(WTFPL OR MIT)" 721 + }, 722 + "node_modules/path-key": { 723 + "version": "3.1.1", 724 + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 725 + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 726 + "dev": true, 727 + "license": "MIT", 728 + "engines": { 729 + "node": ">=8" 730 + } 731 + }, 732 + "node_modules/path-to-regexp": { 733 + "version": "3.3.0", 734 + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", 735 + "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", 736 + "dev": true, 737 + "license": "MIT" 738 + }, 739 + "node_modules/punycode": { 740 + "version": "2.3.1", 741 + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 742 + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 743 + "dev": true, 744 + "license": "MIT", 745 + "engines": { 746 + "node": ">=6" 747 + } 748 + }, 749 + "node_modules/range-parser": { 750 + "version": "1.2.0", 751 + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 752 + "integrity": "sha512-kA5WQoNVo4t9lNx2kQNFCxKeBl5IbbSNBl1M/tLkw9WCn+hxNBAW5Qh8gdhs63CJnhjJ2zQWFoqPJP2sK1AV5A==", 753 + "dev": true, 754 + "license": "MIT", 755 + "engines": { 756 + "node": ">= 0.6" 757 + } 758 + }, 759 + "node_modules/rc": { 760 + "version": "1.2.8", 761 + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 762 + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 763 + "dev": true, 764 + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", 765 + "dependencies": { 766 + "deep-extend": "^0.6.0", 767 + "ini": "~1.3.0", 768 + "minimist": "^1.2.0", 769 + "strip-json-comments": "~2.0.1" 770 + }, 771 + "bin": { 772 + "rc": "cli.js" 773 + } 774 + }, 775 + "node_modules/registry-auth-token": { 776 + "version": "3.3.2", 777 + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.3.2.tgz", 778 + "integrity": "sha512-JL39c60XlzCVgNrO+qq68FoNb56w/m7JYvGR2jT5iR1xBrUA3Mfx5Twk5rqTThPmQKMWydGmq8oFtDlxfrmxnQ==", 779 + "dev": true, 780 + "license": "MIT", 781 + "dependencies": { 782 + "rc": "^1.1.6", 783 + "safe-buffer": "^5.0.1" 784 + } 785 + }, 786 + "node_modules/registry-url": { 787 + "version": "3.1.0", 788 + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", 789 + "integrity": "sha512-ZbgR5aZEdf4UKZVBPYIgaglBmSF2Hi94s2PcIHhRGFjKYu+chjJdYfHn4rt3hB6eCKLJ8giVIIfgMa1ehDfZKA==", 790 + "dev": true, 791 + "license": "MIT", 792 + "dependencies": { 793 + "rc": "^1.0.1" 794 + }, 795 + "engines": { 796 + "node": ">=0.10.0" 797 + } 798 + }, 799 + "node_modules/require-from-string": { 800 + "version": "2.0.2", 801 + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", 802 + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", 803 + "dev": true, 804 + "license": "MIT", 805 + "engines": { 806 + "node": ">=0.10.0" 807 + } 808 + }, 809 + "node_modules/safe-buffer": { 810 + "version": "5.1.2", 811 + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 812 + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 813 + "dev": true, 814 + "license": "MIT" 815 + }, 816 + "node_modules/serve": { 817 + "version": "14.2.4", 818 + "resolved": "https://registry.npmjs.org/serve/-/serve-14.2.4.tgz", 819 + "integrity": "sha512-qy1S34PJ/fcY8gjVGszDB3EXiPSk5FKhUa7tQe0UPRddxRidc2V6cNHPNewbE1D7MAkgLuWEt3Vw56vYy73tzQ==", 820 + "dev": true, 821 + "license": "MIT", 822 + "dependencies": { 823 + "@zeit/schemas": "2.36.0", 824 + "ajv": "8.12.0", 825 + "arg": "5.0.2", 826 + "boxen": "7.0.0", 827 + "chalk": "5.0.1", 828 + "chalk-template": "0.4.0", 829 + "clipboardy": "3.0.0", 830 + "compression": "1.7.4", 831 + "is-port-reachable": "4.0.0", 832 + "serve-handler": "6.1.6", 833 + "update-check": "1.5.4" 834 + }, 835 + "bin": { 836 + "serve": "build/main.js" 837 + }, 838 + "engines": { 839 + "node": ">= 14" 840 + } 841 + }, 842 + "node_modules/serve-handler": { 843 + "version": "6.1.6", 844 + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz", 845 + "integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==", 846 + "dev": true, 847 + "license": "MIT", 848 + "dependencies": { 849 + "bytes": "3.0.0", 850 + "content-disposition": "0.5.2", 851 + "mime-types": "2.1.18", 852 + "minimatch": "3.1.2", 853 + "path-is-inside": "1.0.2", 854 + "path-to-regexp": "3.3.0", 855 + "range-parser": "1.2.0" 856 + } 857 + }, 858 + "node_modules/serve-handler/node_modules/mime-db": { 859 + "version": "1.33.0", 860 + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", 861 + "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==", 862 + "dev": true, 863 + "license": "MIT", 864 + "engines": { 865 + "node": ">= 0.6" 866 + } 867 + }, 868 + "node_modules/serve-handler/node_modules/mime-types": { 869 + "version": "2.1.18", 870 + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", 871 + "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", 872 + "dev": true, 873 + "license": "MIT", 874 + "dependencies": { 875 + "mime-db": "~1.33.0" 876 + }, 877 + "engines": { 878 + "node": ">= 0.6" 879 + } 880 + }, 881 + "node_modules/shebang-command": { 882 + "version": "2.0.0", 883 + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 884 + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 885 + "dev": true, 886 + "license": "MIT", 887 + "dependencies": { 888 + "shebang-regex": "^3.0.0" 889 + }, 890 + "engines": { 891 + "node": ">=8" 892 + } 893 + }, 894 + "node_modules/shebang-regex": { 895 + "version": "3.0.0", 896 + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 897 + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 898 + "dev": true, 899 + "license": "MIT", 900 + "engines": { 901 + "node": ">=8" 902 + } 903 + }, 904 + "node_modules/signal-exit": { 905 + "version": "3.0.7", 906 + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", 907 + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", 908 + "dev": true, 909 + "license": "ISC" 910 + }, 911 + "node_modules/string-width": { 912 + "version": "5.1.2", 913 + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", 914 + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", 915 + "dev": true, 916 + "license": "MIT", 917 + "dependencies": { 918 + "eastasianwidth": "^0.2.0", 919 + "emoji-regex": "^9.2.2", 920 + "strip-ansi": "^7.0.1" 921 + }, 922 + "engines": { 923 + "node": ">=12" 924 + }, 925 + "funding": { 926 + "url": "https://github.com/sponsors/sindresorhus" 927 + } 928 + }, 929 + "node_modules/strip-ansi": { 930 + "version": "7.1.0", 931 + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", 932 + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", 933 + "dev": true, 934 + "license": "MIT", 935 + "dependencies": { 936 + "ansi-regex": "^6.0.1" 937 + }, 938 + "engines": { 939 + "node": ">=12" 940 + }, 941 + "funding": { 942 + "url": "https://github.com/chalk/strip-ansi?sponsor=1" 943 + } 944 + }, 945 + "node_modules/strip-final-newline": { 946 + "version": "2.0.0", 947 + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", 948 + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", 949 + "dev": true, 950 + "license": "MIT", 951 + "engines": { 952 + "node": ">=6" 953 + } 954 + }, 955 + "node_modules/strip-json-comments": { 956 + "version": "2.0.1", 957 + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 958 + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", 959 + "dev": true, 960 + "license": "MIT", 961 + "engines": { 962 + "node": ">=0.10.0" 963 + } 964 + }, 965 + "node_modules/supports-color": { 966 + "version": "7.2.0", 967 + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 968 + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 969 + "dev": true, 970 + "license": "MIT", 971 + "dependencies": { 972 + "has-flag": "^4.0.0" 973 + }, 974 + "engines": { 975 + "node": ">=8" 976 + } 977 + }, 978 + "node_modules/type-fest": { 979 + "version": "2.19.0", 980 + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", 981 + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", 982 + "dev": true, 983 + "license": "(MIT OR CC0-1.0)", 984 + "engines": { 985 + "node": ">=12.20" 986 + }, 987 + "funding": { 988 + "url": "https://github.com/sponsors/sindresorhus" 989 + } 990 + }, 991 + "node_modules/update-check": { 992 + "version": "1.5.4", 993 + "resolved": "https://registry.npmjs.org/update-check/-/update-check-1.5.4.tgz", 994 + "integrity": "sha512-5YHsflzHP4t1G+8WGPlvKbJEbAJGCgw+Em+dGR1KmBUbr1J36SJBqlHLjR7oob7sco5hWHGQVcr9B2poIVDDTQ==", 995 + "dev": true, 996 + "license": "MIT", 997 + "dependencies": { 998 + "registry-auth-token": "3.3.2", 999 + "registry-url": "3.1.0" 1000 + } 1001 + }, 1002 + "node_modules/uri-js": { 1003 + "version": "4.4.1", 1004 + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 1005 + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 1006 + "dev": true, 1007 + "license": "BSD-2-Clause", 1008 + "dependencies": { 1009 + "punycode": "^2.1.0" 1010 + } 1011 + }, 1012 + "node_modules/vary": { 1013 + "version": "1.1.2", 1014 + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1015 + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", 1016 + "dev": true, 1017 + "license": "MIT", 1018 + "engines": { 1019 + "node": ">= 0.8" 1020 + } 1021 + }, 1022 + "node_modules/which": { 1023 + "version": "2.0.2", 1024 + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1025 + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1026 + "dev": true, 1027 + "license": "ISC", 1028 + "dependencies": { 1029 + "isexe": "^2.0.0" 1030 + }, 1031 + "bin": { 1032 + "node-which": "bin/node-which" 1033 + }, 1034 + "engines": { 1035 + "node": ">= 8" 1036 + } 1037 + }, 1038 + "node_modules/widest-line": { 1039 + "version": "4.0.1", 1040 + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", 1041 + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", 1042 + "dev": true, 1043 + "license": "MIT", 1044 + "dependencies": { 1045 + "string-width": "^5.0.1" 1046 + }, 1047 + "engines": { 1048 + "node": ">=12" 1049 + }, 1050 + "funding": { 1051 + "url": "https://github.com/sponsors/sindresorhus" 1052 + } 1053 + }, 1054 + "node_modules/wrap-ansi": { 1055 + "version": "8.1.0", 1056 + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", 1057 + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", 1058 + "dev": true, 1059 + "license": "MIT", 1060 + "dependencies": { 1061 + "ansi-styles": "^6.1.0", 1062 + "string-width": "^5.0.1", 1063 + "strip-ansi": "^7.0.1" 1064 + }, 1065 + "engines": { 1066 + "node": ">=12" 1067 + }, 1068 + "funding": { 1069 + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 1070 + } 1071 + } 1072 + } 1073 + }
+13
website/package.json
··· 1 + { 2 + "name": "atpotato-website", 3 + "version": "1.0.0", 4 + "private": true, 5 + "scripts": { 6 + "dev": "serve .", 7 + "build": "cp -r . .next/", 8 + "start": "serve ." 9 + }, 10 + "devDependencies": { 11 + "serve": "^14.2.1" 12 + } 13 + }
+216 -18
website/styles.css
··· 1 1 /* Global Styles */ 2 2 :root { 3 - --text-color: #212121; 4 - --secondary-color: #555555; 5 - --highlight-color: #00BDFF; 3 + /* Light Theme */ 4 + --text-color: #2a2a2a; 5 + --highlight-color: #00A6ED; 6 + --secondary-color: #666666; 7 + --background-color: rgba(255, 255, 255, 0.95); 8 + --surface-color: rgba(255, 255, 255, 0.8); 9 + --border-color: rgba(230, 230, 230, 0.8); 10 + --section-border-color: rgba(200, 200, 200, 0.8); 11 + --toc-background: rgba(248, 249, 250, 0.8); 12 + --code-background: rgba(245, 245, 245, 0.8); 13 + --progress-background: rgba(224, 224, 224, 0.8); 14 + --progress-fill: var(--highlight-color); 15 + --overlay-background: rgba(0, 0, 0, 0.5); 16 + } 17 + 18 + /* Dark theme */ 19 + [data-theme="dark"] { 20 + --text-color: #ffffff; 21 + --highlight-color: #00A6ED; 22 + --secondary-color: #999999; 23 + --background-color: rgba(18, 18, 18, 0.95); 24 + --surface-color: rgba(30, 30, 30, 0.8); 25 + --border-color: rgba(50, 50, 50, 0.8); 26 + --section-border-color: rgba(80, 80, 80, 0.8); 27 + --toc-background: rgba(26, 26, 26, 0.8); 28 + --code-background: rgba(42, 42, 42, 0.8); 29 + --progress-background: rgba(42, 42, 42, 0.8); 30 + --progress-fill: var(--highlight-color); 31 + --overlay-background: rgba(0, 0, 0, 0.7); 6 32 } 7 33 8 34 body { 35 + position: relative; 9 36 font-family: "eigerdals", sans-serif; 10 37 font-weight: 400; 11 38 font-style: normal; 12 - background-color: #ffffff; 39 + background-color: var(--background-color); 13 40 color: var(--text-color); 14 41 margin: 0; 15 42 padding: 0; 43 + min-height: 100vh; 44 + } 45 + 46 + body::before { 47 + content: ''; 48 + position: fixed; 49 + top: 0; 50 + left: 0; 51 + width: 100%; 52 + height: 100%; 53 + background-image: url('/bsky-bg.JPG'); 54 + background-size: cover; 55 + background-position: center; 56 + background-repeat: no-repeat; 57 + filter: blur(2px); 58 + opacity: 0.15; 59 + z-index: -2; 60 + } 61 + 62 + body::after { 63 + content: ''; 64 + position: fixed; 65 + top: 0; 66 + left: 0; 67 + width: 100%; 68 + height: 100%; 69 + background: var(--background-color); 70 + opacity: 0.2; 71 + z-index: -1; 16 72 } 17 73 18 74 .container { ··· 23 79 flex-direction: column; 24 80 justify-content: center; 25 81 max-width: 400px; 26 - } 82 + position: relative; 83 + z-index: 1; 84 + } 27 85 28 86 /* Profile Section */ 29 87 .profile { 30 88 padding: 30px 0; 31 89 margin-bottom: 25px; 32 - border-bottom: 1px solid #eaeaea; 90 + border-bottom: 1px solid var(--border-color); 33 91 } 34 92 35 93 .logo-container { 36 94 width: 180px; 37 95 height: 150px; 38 96 margin: 0 auto 20px; 39 - overflow: hidden; 97 + overflow: visible; 40 98 border-radius: 10px; 99 + display: flex; 100 + align-items: center; 101 + justify-content: center; 41 102 } 42 103 43 104 .logo { 44 105 width: 100%; 45 - height: 100%; 46 - object-fit: cover; 106 + height: auto; 107 + object-fit: contain; 108 + transition: all 0.2s ease; 47 109 } 48 110 49 111 .tagline { ··· 55 117 margin: 10px 0; 56 118 font-weight: 900; 57 119 font-style: normal; 120 + color: var(--text-color); 58 121 } 59 122 60 123 h2 { ··· 80 143 81 144 .button { 82 145 display: inline-block; 83 - padding: 10px 20px; 84 - background-color: #f5f5f5; 146 + padding: 0.7rem 1rem; 147 + background: var(--surface-color); 85 148 color: var(--text-color); 86 149 text-decoration: none; 87 - border-radius: 5px; 88 - transition: background-color 0.2s ease, transform 0.2s ease; 150 + border-radius: 20px; 151 + border: 1px solid var(--border-color); 152 + transition: all 0.2s ease; 153 + font-family: "eigerdals", sans-serif; 154 + font-size: 0.9rem; 89 155 font-weight: 700; 90 - font-style: normal; 156 + display: flex; 157 + align-items: center; 158 + gap: 0.5rem; 159 + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); 160 + backdrop-filter: blur(10px); 91 161 } 92 162 93 163 .button:hover { 94 - background-color: #e0e0e0; 164 + background: var(--border-color); 95 165 transform: translateY(-2px); 166 + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); 96 167 } 97 168 98 169 /* Projects Section */ ··· 119 190 120 191 .project-link:hover { 121 192 text-decoration: underline; 122 - transform: translateX(5px); 193 + transform: scale(1.05); 123 194 } 124 195 125 196 .email { 126 197 color: var(--secondary-color); 127 198 font-size: 0.8em; 128 199 margin-top: 25px; 129 - } 200 + } 130 201 131 202 .coming-soon { 132 203 color: var(--secondary-color); ··· 134 205 margin-top: 10px; 135 206 } 136 207 208 + /* Theme Toggle */ 209 + .theme-toggle { 210 + position: fixed; 211 + bottom: 2rem; 212 + left: 2rem; 213 + width: 3.5rem; 214 + height: 3.5rem; 215 + background: var(--surface-color); 216 + border: 1px solid var(--border-color); 217 + border-radius: 50%; 218 + cursor: pointer; 219 + display: flex; 220 + align-items: center; 221 + justify-content: center; 222 + transition: all 0.2s ease; 223 + z-index: 1000; 224 + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); 225 + backdrop-filter: blur(10px); 226 + font-size: 1.5rem; 227 + } 228 + 229 + .theme-toggle:hover { 230 + transform: translateY(-2px); 231 + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); 232 + } 233 + 234 + /* Home Button */ 235 + .home-button { 236 + position: fixed; 237 + bottom: 2rem; 238 + right: 2rem; 239 + width: 3.5rem; 240 + height: 3.5rem; 241 + background: var(--surface-color); 242 + border: 1px solid var(--border-color); 243 + border-radius: 50%; 244 + display: flex; 245 + align-items: center; 246 + justify-content: center; 247 + transition: all 0.2s ease; 248 + z-index: 1000; 249 + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); 250 + backdrop-filter: blur(10px); 251 + } 252 + 253 + .home-button:hover { 254 + transform: translateY(-2px); 255 + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); 256 + } 257 + 258 + .home-logo { 259 + width: 2.5rem; 260 + height: 2.5rem; 261 + object-fit: contain; 262 + transition: all 0.2s ease; 263 + } 264 + 137 265 /* Responsive Adjustments */ 266 + @media (max-width: 768px) { 267 + .theme-toggle { 268 + width: 3rem; 269 + height: 3rem; 270 + font-size: 1.3rem; 271 + bottom: 1.5rem; 272 + left: 1.5rem; 273 + } 274 + 275 + .home-button { 276 + width: 3rem; 277 + height: 3rem; 278 + bottom: 1.5rem; 279 + right: 1.5rem; 280 + } 281 + 282 + .home-logo { 283 + width: 2rem; 284 + height: 2rem; 285 + } 286 + } 287 + 138 288 @media (max-width: 480px) { 139 289 .logo-container { 140 290 width: 180px; ··· 146 296 } 147 297 148 298 .button { 149 - padding: 8px 16px; 299 + padding: 0.6rem 1rem; 300 + font-size: 0.85rem; 301 + } 302 + 303 + .theme-toggle { 304 + width: 3rem; 305 + height: 3rem; 306 + font-size: 1.2rem; 307 + bottom: 1.5rem; 308 + left: 1.5rem; 309 + } 310 + 311 + .home-button { 312 + width: 3rem; 313 + height: 3rem; 314 + bottom: 1.5rem; 315 + right: 1.5rem; 316 + } 317 + 318 + .home-logo { 319 + width: 2rem; 320 + height: 2rem; 321 + } 322 + } 323 + 324 + /* Button Backdrop */ 325 + .button-backdrop { 326 + display: none; 327 + } 328 + 329 + @media (max-width: 768px) { 330 + .button-backdrop { 331 + display: block; 332 + position: fixed; 333 + bottom: 0; 334 + left: 0; 335 + right: 0; 336 + height: 6rem; 337 + background: linear-gradient(to bottom, transparent, var(--background-color) 50%); 338 + pointer-events: none; 339 + z-index: 999; 340 + } 341 + 342 + .theme-toggle { 343 + width: 3rem; 344 + height: 3rem; 345 + font-size: 1.3rem; 346 + bottom: 1.5rem; 347 + left: 1.5rem; 150 348 } 151 349 }
+32
website/theme.js
··· 1 + // Theme Management 2 + class ThemeManager { 3 + constructor() { 4 + this.theme = localStorage.getItem('theme') || 'light'; 5 + this.init(); 6 + } 7 + 8 + init() { 9 + document.documentElement.setAttribute('data-theme', this.theme); 10 + this.createToggleButton(); 11 + } 12 + 13 + createToggleButton() { 14 + const button = document.createElement('button'); 15 + button.className = 'theme-toggle'; 16 + button.innerHTML = this.theme === 'light' ? '๐ŸŒš' : '๐ŸŒž'; 17 + button.onclick = () => this.toggleTheme(); 18 + document.body.appendChild(button); 19 + } 20 + 21 + toggleTheme() { 22 + this.theme = this.theme === 'light' ? 'dark' : 'light'; 23 + localStorage.setItem('theme', this.theme); 24 + document.documentElement.setAttribute('data-theme', this.theme); 25 + document.querySelector('.theme-toggle').innerHTML = this.theme === 'light' ? '๐ŸŒš' : '๐ŸŒž'; 26 + } 27 + } 28 + 29 + // Initialize theme manager when the DOM is loaded 30 + document.addEventListener('DOMContentLoaded', () => { 31 + new ThemeManager(); 32 + });
+3 -1
website/vercel.json
··· 39 39 } 40 40 ] 41 41 } 42 - ] 42 + ], 43 + "cleanUrls": true, 44 + "trailingSlash": false 43 45 }