Hook reference

Every stable data-* selector Signal exposes for Code injection — regions, components, variants, and parts — plus the protected names you must not rename.

The complete list of Signal's stable styling handles. These names don't change between theme updates, so you can build Code-injection CSS against them with confidence. See Code injection for how the system works.

Regions — data-region

A structural area, usually one per page.

ValueWhere
site, mainThe <body> and the main content column (every page)
site-header, primary-nav, mobile-navThe header and its menus
hero, hero-bodyThe homepage hero
platformsThe "Watch & listen on" band
featured-episodes, episode-feedHomepage episode sections
from-blog, blog-feedThe homepage "From the blog" band and the /blog/ index
hosts, guestsThe homepage host and guest sections
hosts-index, guests-index, frequent-guestsThe Hosts and Guests pages and the "Frequent guests" strip
about, testimonials, sponsors, membershipHomepage about / testimonials / sponsors / support sections
subscribe-heroThe Subscribe page heading band
contact-hero, contact-reasonsThe Contact page
archive-headerThe heading band on the /episodes/ and /blog/ indexes and topic archives
author-header, archive-feed, more-guestsA person's page: heading band, their episode list, and the "More guests" row
post / page, page-bodyAn episode, a blog post, or a generic page
post-header, post-media, post-bodyThe post header, the episode player area, and the body (post-media is episodes only)
comments, newsletterThe comments and newsletter areas
library, error / error-suggestionsThe Library page and error pages
site-footer, footer-navThe footer

The header has no separate inner region

Everything in the header sits directly inside [data-region="site-header"], so that's the handle to scope header rules to, including logo sizing.

Components — data-component

A reusable unit that can appear many times.

ValueWhat
episode-row, episode-cardEpisode list rows and cards
article-row, article-cardBlog-post rows and cards (the written-content counterparts of the episode ones)
blog-byline, author-bio, more-from-blogA blog post's byline, end-of-article author card, and related-articles row
guest-card, host-card, guest-ctaPeople cards and the "be a guest" prompt
author-socials, social-linksSocial icon rows
platform-listA listen-links mount
audio-playerThe waveform player on an #audio episode
up-nextThe "Up next" row at the end of an episode
member-ctaThe members-only prompt shown in place of gated content
sponsors, tier-list, tier-cardSponsor logos and membership tiers
testimonial-cardOne quote card in the "What listeners say" section
contact-form, newsletter-ctaThe contact and newsletter forms
filter-chips, episode-tabs, paginationTopic filters, episode tabs, pagination
footer-brand, footer-legal, footer-listenFooter blocks

Variants — data-variant

A flavour of its host component or region.

HostValues
herolatest, podcast, host, listen, cinematic, cinematic-full — the homepage hero layout (set by the Home hero setting)
postblog on a #blog written post; an episode carries no variant
post-headeraudio on #audio episodes, embed on #embed episodes; a default video episode header carries no variant
archive-headerblog on the /blog/ index, tag on a topic archive; the /episodes/ index header carries no variant
member-ctasignup for a signed-out visitor, upgrade for a signed-in member on a lower tier
error404 on the not-found page, generic on any other error page

Both cinematic heroes

The two cinematic layouts are separate values, so [data-variant="cinematic"] will not match the full-width one. To style both, use [data-variant^="cinematic"].

Parts — data-part

A piece inside a component or region. The core names are the same everywhere.

ValueWhat
title, kicker, excerpt, meta, imageThe shared core (any preview, header, or card)
logo, wordmarkThe brand logo image, and the link that wraps it
author, roleA person's name and their location line
descriptionA row's summary line (episode and article rows)
peopleThe Hosts & Guests panel on an episode
poster, transcript, chaptersThe media poster and the transcript/chapters content
noteA small print line (for example a tier's free-trial note)
quote, ratingA testimonial's quote block and its row of stars. The name, source line, photo, and top heading on the same card reuse author, meta, image, and kicker

Dates, durations, and reading times

These are not separate handles: they render inside [data-part="meta"] together with the author and date. Style the whole meta line, or the specific <time> element inside it.

Scoping a part

Parts repeat, so scope them to a region or component: [data-component="article-row"] [data-part="title"] targets only blog-row headlines, not every title on the page.

Protected hooks

These are theme-owned functional names that the player, transcript, Library, comments, and navigation depend on. You may read and style them, but renaming, moving, or removing them — or wrapping the player host — breaks playback, cross-page continuity, transcript seeking, the Library, or comments.

Don't build CSS that relies on changing these; treat them as read-only structure:

  • Player: #mp-player-host (and its data-mode / data-mp-position / data-media / data-mp-* attributes), #mp-live, #mp-progress, #main-content, .mp-focus-target. Never wrap or reparent the player host. The three #mp-* elements exist only while the Persistent mini-player setting is on.
  • Video stage: [data-mp-stage], .js-mp-stage, data-mp-source-scope, data-mp-key, data-mp-title, data-poster, data-mp-active.
  • Audio player: [data-audio-player] and, note, its own attribute names: data-source-scope and data-mp-poster (not the video stage's data-mp-source-scope / data-poster). Also [data-audio], [data-waveform], [data-audio-download], [data-speed].
  • Platform-embed hero: [data-platform-embed], data-source-scope, data-embed-title.
  • Transcript & chapters: [data-transcript-empty] / -ready / -scroll / -search / -noresults / -live, [data-autoscroll-toggle], [data-highlight-mode] and its [data-hl] buttons, [data-chapters-empty] / -ready / -count / [data-chapters], [data-seek-seconds].
  • Comments: [data-lazy-comments-mount], [data-lazy-comments-template], #panel-comments.
  • Episode tabs: [data-tabs], [data-tab-activate], and the #tab-* / #panel-* id pairs that tie each tab to its panel.
  • Topic filter chips: [data-filter-chips], [data-filter-chips-track], [data-chip-arrow].
  • Guest search: [data-guests-grid], [data-guests-input], [data-guests-empty], [data-guests-live], [data-guest].
  • Contact form: [data-contact-form] / -fields / -status / -done, [data-reason-chip], [data-reason-input].
  • Share menu: [data-share-trigger], [data-share-panel], [data-share-copy], [data-share-copy-text], [data-share-os].
  • Members forms: Ghost's own [data-members-form], [data-members-email], [data-members-error], [data-members-label], and [data-portal].
  • Listen links: [data-signal-platforms], [data-signal-listen-links], [data-signal-listen-section], [data-listen-built], plus the attributes you write on each <a> inside the block: data-platform, data-name, data-svg, data-color.
  • Custom social links: [data-signal-social-links] and [data-signal-social], plus the per-link data-platform, data-name, data-svg, data-placements, and the data-social-platform marker the theme uses to avoid duplicating a platform Ghost already renders. You write these yourself, so keep the names exactly as documented on Social links.
  • Testimonials: [data-testimonials], [data-testimonials-source], [data-testimonials-mount], [data-testimonials-i18n], [data-testimonials-marquee], [data-marquee-toggle], and data-dir on each row. The whole "What listeners say" section is built from the page body at runtime, so these hold it together. Two read-only state hooks come with it: [data-paused] on the marquee while the quotes are stopped, and [data-static] when there is no movement at all (three quotes or fewer, or a visitor who prefers reduced motion).
  • Library: [data-library-toggle], [data-library-widget] / -trigger / -panel / -tab / -tabcount / -list / -count / -url, [data-library-page] (and its post-hydration [data-hydrated] flag) / -section / -sectioncount / -rows / -empty / -clear, the [data-clear-trigger] / -confirm / -yes / -no controls, and the [data-lib-i18n] label block.
  • Navigation: data-nav="primary" / "secondary", [data-nav-panel], [data-footer-nav] / -source / -target, [data-mobile-open] / [data-mobile-menu] / [data-mobile-close] / [data-mobile-backdrop], [data-header-actions], the .is-subitem and .is-navhead item markers, and the data-submenu-label / data-overflow-label / data-quick-links-label attributes. [data-part="wordmark"] and [data-header-actions] are measured to lay out the centered menu, so they are functional here as well as stylable.
  • State on <html> (select on these, never set them): the js class, dark, sig-bundle-failed, [data-nav-collapsed], [data-color-scheme], plus [data-set-theme] on the toggle itself and the .is-revealed class on revealed sections. The internal .sig-* class namespace is theme-owned too: style it if you must, but it can change between releases, unlike the data-* handles above.

Pre-hydration guards (avoiding flash)

Signal is built to render cleanly on the very first paint, before its JavaScript runs. A few elements are therefore held by CSS until the scripts hydrate them, so they never flash in a half-finished state. Each guard is gated on the js class, which is added to <html> before paint, so a visitor with JavaScript switched off is never held at all. A 2.5-second failsafe in the page head releases every guard if the bundle fails to load, so nothing can get stuck hidden.

Held until hydratedHow it's heldReleased by
Desktop header menu, the overflow "More" pass[data-nav-pending] on the [data-region="primary-nav"] [data-nav="primary"] list, via visibility: hiddennavigation.js, once it has measured the bar and built "More"
Library empty states on /library/[data-library-page]:not([data-hydrated]) [data-library-empty], via display: nonelibrary.js, once it has read your saved items
Below-the-fold sections[data-reveal], via opacity: 0 and a small downward offset. The element still takes up space and stays clickable while held, and the guard is skipped entirely for visitors who prefer reduced motionreveal.js, when the section scrolls into view. It also reveals everything immediately for reduced-motion visitors, and on browsers without IntersectionObserver
The flat footer link list, before it becomes headed columns.js:not(.sig-bundle-failed) [data-footer-nav-source], via display: nonenavigation.js, once it has built the columns into [data-footer-nav-target]
The raw testimonials page body, before it becomes quote cards.js:not(.sig-bundle-failed) [data-testimonials-source], via display: nonetestimonials.js, once it has built the cards into [data-testimonials-mount]. With scripts off, this list of Product cards is what a visitor reads, so it is never hidden from them
Episode show-notes source blocks: raw transcript/chapter code and the duplicated media cardhtml.js:not(.sig-bundle-failed) [data-component="episode-tabs"] .gh-content …, via display: nonethe player scripts, which move them into the hero and tabs. These are relocated rather than revealed, which is why the failsafe releases the whole rule instead

Don't force any of these visible from Code injection (for example visibility: visible on the pending nav, or display: block on [data-library-empty]). Overriding a guard re-introduces the exact flash it prevents. Style the settled result instead.

`sig-bundle-failed` is the failsafe's signal

The failsafe adds sig-bundle-failed to <html>. Treat it as read-only state, like dark or data-color-scheme: you can style off it, but setting or removing it yourself will show content the scripts are about to rearrange.

Adding a hook

If you need a hook that isn't here, it can be added to the theme (and documented on this page). Renaming or removing an existing one is a breaking change. See For developers.