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

Creating themes

This document is an introduction to theming in Mastodon. It's primarily aimed at theme creators, but also contains useful information for anyone interested in working on Mastodon's styles.

    • The groundwork
    • Custom styles
    • Respecting color-scheme and contrast preferences

The groundwork

The entrypoint of the default Mastodon theme is located at app/javascript/styles/application.scss. To create your own theme, duplicate this file and add a new entry for it to the file config/themes.yml, for example:

default: styles/application.scss
my_theme: styles/my_theme.scss

After this, rebuild assets (RAILS_ENV=production bundle exec rails assets:precompile) and restart the Mastodon services or Docker container.

Once this is done, your custom theme will be selectable

  • for users from Preferences > Appearance > Theme
  • as the server’s default theme from Administration > Server settings > Design.

Custom styles

With the steps above completed, you’ve created a new theme entrypoint, but its styles are identical to the default Mastodon theme.

You could now go on to duplicate the entire app/javascript/styles/mastodon CSS folder and start tweaking things, but if you only want to change the colours of the Mastodon UI, it’s enough to create a copy of the mastodon/theme folder.

Once that’s done, you need to change your entrypoint file (the one referenced in config/themes.yml) to import your copy of the theme folder instead of the original.

Example: If you’ve duplicated the mastodon/theme folder to app/javascript/styles/my_theme, open my_theme.scss and replace @use 'mastodon/theme' with @use 'my_theme'.

Once that’s done, you can change the theme tokens defined in the files my_theme/_base.scss, my_theme/_dark.scss, and my_theme/_light.scss to create a new color theme, and the changes should be reflected in the UI.

Respecting color-scheme and contrast preferences

Prior to Mastodon 4.6, theme variations like light mode, dark mode, and high-contrast had to be implemented as separate themes. This has changed in 4.6 where these preferences were made independent settings.

The newly available user options are:

  • Color scheme: Auto, Light, or Dark
  • Contrast: Auto or High

Current values are reflected in the HTML attributes data-color-scheme and data-contrast so that they can be used to scope your styles.

.component {
  color: black;
  background: black;

  [data-color-scheme='dark'] & {
    color: white;
    background: black;
  }
}
The “Auto” setting will always be resolved to the actually displayed option. For color-scheme, that’s light or dark, for contrast it’s default or high.

You can also use the light-dark() CSS function to achieve the same result:

.component {
  color: light-dark(black, white);
  background: light-dark(white, black);
}

Generally we recommend handling color-scheme and contrast-dependent styles at the level of theme tokens. See our Design Tokens reference for a list of tokens available by default.

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