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.
| Value | Where |
|---|---|
site, main | The <body> and the main content column (every page) |
site-header, primary-nav, mobile-nav | The header and its menus |
hero, hero-body | The homepage hero |
platforms | The "Watch & listen on" band |
featured-episodes, episode-feed | Homepage episode sections |
from-blog, blog-feed | The homepage "From the blog" band and the /blog/ index |
hosts, guests | The homepage host and guest sections |
hosts-index, guests-index, frequent-guests | The Hosts and Guests pages and the "Frequent guests" strip |
about, testimonials, sponsors, membership | Homepage about / testimonials / sponsors / support sections |
subscribe-hero | The Subscribe page heading band |
contact-hero, contact-reasons | The Contact page |
archive-header | The heading band on the /episodes/ and /blog/ indexes and topic archives |
author-header, archive-feed, more-guests | A person's page: heading band, their episode list, and the "More guests" row |
post / page, page-body | An episode, a blog post, or a generic page |
post-header, post-media, post-body | The post header, the episode player area, and the body (post-media is episodes only) |
comments, newsletter | The comments and newsletter areas |
library, error / error-suggestions | The Library page and error pages |
site-footer, footer-nav | The 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.
| Value | What |
|---|---|
episode-row, episode-card | Episode list rows and cards |
article-row, article-card | Blog-post rows and cards (the written-content counterparts of the episode ones) |
blog-byline, author-bio, more-from-blog | A blog post's byline, end-of-article author card, and related-articles row |
guest-card, host-card, guest-cta | People cards and the "be a guest" prompt |
author-socials, social-links | Social icon rows |
platform-list | A listen-links mount |
audio-player | The waveform player on an #audio episode |
up-next | The "Up next" row at the end of an episode |
member-cta | The members-only prompt shown in place of gated content |
sponsors, tier-list, tier-card | Sponsor logos and membership tiers |
testimonial-card | One quote card in the "What listeners say" section |
contact-form, newsletter-cta | The contact and newsletter forms |
filter-chips, episode-tabs, pagination | Topic filters, episode tabs, pagination |
footer-brand, footer-legal, footer-listen | Footer blocks |
Variants — data-variant
A flavour of its host component or region.
| Host | Values |
|---|---|
hero | latest, podcast, host, listen, cinematic, cinematic-full — the homepage hero layout (set by the Home hero setting) |
post | blog on a #blog written post; an episode carries no variant |
post-header | audio on #audio episodes, embed on #embed episodes; a default video episode header carries no variant |
archive-header | blog on the /blog/ index, tag on a topic archive; the /episodes/ index header carries no variant |
member-cta | signup for a signed-out visitor, upgrade for a signed-in member on a lower tier |
error | 404 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.
| Value | What |
|---|---|
title, kicker, excerpt, meta, image | The shared core (any preview, header, or card) |
logo, wordmark | The brand logo image, and the link that wraps it |
author, role | A person's name and their location line |
description | A row's summary line (episode and article rows) |
people | The Hosts & Guests panel on an episode |
poster, transcript, chapters | The media poster and the transcript/chapters content |
note | A small print line (for example a tier's free-trial note) |
quote, rating | A 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 itsdata-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-scopeanddata-mp-poster(not the video stage'sdata-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-linkdata-platform,data-name,data-svg,data-placements, and thedata-social-platformmarker 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], anddata-diron 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/-nocontrols, 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-subitemand.is-navheaditem markers, and thedata-submenu-label/data-overflow-label/data-quick-links-labelattributes.[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): thejsclass,dark,sig-bundle-failed,[data-nav-collapsed],[data-color-scheme], plus[data-set-theme]on the toggle itself and the.is-revealedclass on revealed sections. The internal.sig-*class namespace is theme-owned too: style it if you must, but it can change between releases, unlike thedata-*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 hydrated | How it's held | Released by |
|---|---|---|
| Desktop header menu, the overflow "More" pass | [data-nav-pending] on the [data-region="primary-nav"] [data-nav="primary"] list, via visibility: hidden | navigation.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: none | library.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 motion | reveal.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: none | navigation.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: none | testimonials.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 card | html.js:not(.sig-bundle-failed) [data-component="episode-tabs"] .gh-content …, via display: none | the 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.