Mastodon
  • What is Mastodon?
  • Using Mastodon
    • Signing up for an account
    • Setting up your profile
    • Posting to your profile
    • Using the network features
    • Quoting other posts
    • Dealing with unwanted content
    • Promoting yourself and others
    • Set your preferences
    • More settings
    • Using Mastodon externally
    • Moving or leaving accounts
    • Official iOS and Android apps
    • Running your own server
  • Running Mastodon
    • Preparing your machine
    • Installing from source
    • Configuring your environment
    • Configuring full-text search
    • Installing optional features
      • Object storage
      • Onion services
      • Captcha
      • Single Sign On
    • Setting up your new instance
    • Using the admin CLI
    • Upgrading to a new release
    • Backing up your server
    • Migrating to a new machine
    • Scaling up your server
    • Moderation actions
    • Troubleshooting errors
      • Database index corruption
    • Webhooks
    • Roles
  • Developing Mastodon apps
    • Getting started with the API
    • Playing with public data
    • Obtaining client app access
    • Logging in with an account
    • Libraries and implementations
    • Implementing quote posts
  • Contributing to Mastodon
    • Technical overview
    • Setting up a dev environment
    • Code structure
    • Routes
    • Security issues and responsible disclosure
    • Frontend guide
      • Components
      • State management
      • CSS and styling
      • Creating themes
      • Design tokens reference
  • Spec compliance
    • ActivityPub
    • WebFinger
    • Security
    • Microformats
    • OAuth
    • Bearcaps
  • REST API
    • Datetime formats
    • Guidelines and best practices
    • OAuth Tokens
    • OAuth Scopes
    • Rate limits
  • API Methods
    • accounts
      • blocks
      • bookmarks
      • domain_blocks
      • endorsements
      • favourites
      • featured_tags
      • filters
      • follow_requests
      • followed_tags
      • mutes
      • preferences
      • reports
      • suggestions
      • tags
    • admin
      • accounts
      • canonical_email_blocks
      • dimensions
      • domain_allows
      • domain_blocks
      • email_domain_blocks
      • ip_blocks
      • measures
      • reports
      • retention
      • trends
    • apps
      • emails
      • oauth
    • async_refreshes
    • collections
    • grouped notifications
    • health
    • instance
      • announcements
      • custom_emojis
      • directory
      • trends
    • notifications
      • push
    • oembed
    • profile
    • proofs
    • search
    • statuses
      • media
      • polls
      • scheduled_statuses
    • timelines
      • conversations
      • lists
      • markers
      • streaming
  • API Entities
    • Account
    • AccountWarning
    • Admin::Account
    • Admin::CanonicalEmailBlock
    • Admin::Cohort
    • Admin::Dimension
    • Admin::DomainAllow
    • Admin::DomainBlock
    • Admin::EmailDomainBlock
    • Admin::Ip
    • Admin::IpBlock
    • Admin::Measure
    • Admin::Report
    • Announcement
    • Appeal
    • Application
    • AsyncRefresh
    • Collection
    • CollectionItem
    • CollectionWithAccounts
    • Context
    • Conversation
    • CustomEmoji
    • DomainBlock
    • Error
    • ExtendedDescription
    • FamiliarFollowers
    • FeatureApproval
    • FeaturedTag
    • Filter
    • FilterKeyword
    • FilterResult
    • FilterStatus
    • IdentityProof
    • Instance
    • List
    • Marker
    • MediaAttachment
    • Notification
    • NotificationFallback
    • NotificationPolicy
    • NotificationRequest
    • Poll
    • Preferences
    • PreviewCard
    • PreviewCardAuthor
    • PrivacyPolicy
    • Profile
    • Quote
    • QuoteApproval
    • Reaction
    • Relationship
    • RelationshipSeveranceEvent
    • Report
    • Role
    • Rule
    • ScheduledStatus
    • Search
    • ShallowQuote
    • ShallowTag
    • Status
    • StatusEdit
    • StatusSource
    • Suggestion
    • Tag
    • TermsOfService
    • Token
    • Translation
    • V1::Filter
    • V1::Instance
    • V1::NotificationPolicy
    • WebPushSubscription

CSS and styling

How to style components in the Mastodon frontend

    • Background
    • How to use CSS Modules
    • Styling variants
    • Styling sub-components
    • Theming

Background

Before Mastodon 4.6, Mastodon’s UI was styled using global SCSS files like components.scss. These files often grew large and became hard to navigate and change without unintended side-effects.

To improve on this, we decided to build the new functionality for the 4.6 release using CSS Modules. This allows styles to be defined right next to the related components and reduces the likelihood of style conflicts.

How to use CSS Modules

  • When creating a new React component, create a new folder for it (use snake_case naming) and add a styles.module.scss file (note the .module suffix – leaving it out will prevent the styles from being imported).
    • For multiple CSS files in one folder, use component_name.module.scss, matching the name of the related subcomponent.
  • Import and use the styles in the component: import classes from './styles.module.scss'
  • The naming of classes in CSS Modules should be kept simple, e.g. .card, or .actions. There’s no need for BEM-like naming patterns. Use camelCase for multiple words to make it easier to access the class in a component.
  • CSS Modules will generate names for all classes in a module based on the location and module name, providing a stable-ish hook for theme authors.

Styling variants

To style component variants, you can take one of two approaches:

  • Compose classes using the classNames utility, e.g.:
    <section
      className={classNames(
        classes.wrapper,
        !hasBorder && classes.wrapperWithoutBorder,
      )}
    >
    
  • Add a custom data attribute to the component markup and add styles based on that, e.g.:
    <section
      className={classes.wrapper}
      data-has-border={hasBorder}
    >
    
    .wrapper {
      /* ... */
      &[data-has-border='true'] {
        border: 1px solid var(--color-border-primary);
      }
    }
    

Styling sub-components

If you need to style sub-components that use legacy (global) CSS classes (e.g. .avatar), you can wrap the class like this: :global(.avatar) to prevent CSS Modules from turning it into a generated class name. Avoid this if possible and prefer to create a new local class and pass it to the nested component.

Theming

Use our design tokens for colours and learn more about user theme preferences in our Theming guide.

Last updated May 29, 2026 · Improve this page

Sponsored by

Dotcom-Monitor LoadView Stephen Tures Swayable SponsorMotion

Join Mastodon · Blog ·

View source · CC BY-SA 4.0 · Imprint