A plugin that cleans up the appearance of NPF images on Tumblr themes, allowing them to mimic the appearance of legacy photo posts. Only applies to Tumblr themes, not the Tumblr dashboard.
This fix attempts to be a one-size-fits-all solution, but likely needs HTML and Tumblr docs knowledge to make it work as intended, as the markup/structure of every theme will be different depending on how the theme maker set it up.
- 💭 What is NPF?
- ✨ Features
- 👁️ Demos + Previews
- 🚀 How to install
- 📝 Further notes
- 💖 Attribution
- 🙋 Questions?
NPF stands for "Neue Post Format". Prior to 2020, users could create posts of a primary post type (namingly text, photo, quote, link, chat, video, questions). In recent years, Tumblr introduced NPF as the only available post type (in other words, everything becomes a text post), despite their post editor showing the illusion of different post types. NPF images can also refer to images between paragraphs (inline images).
Although NPF has been in the works as early as 2018, its support on custom themes has been lackluster. While the documentation on the {NPF} variable is extensive, creating themes this way is vastly different from Tumblr's "blocks" system and even the default Tumblr theme does not use the {NPF} variable. Furthermore, it's impossible to swap out {block:Text} for {NPF} directly in hopes of fixing the visual inconsistencies.
- Turns images HD if available
- Prevents images from stretching / overflowing
- Custom photoset spacing
- Custom caption spacing
- Improved lightbox functionality
- Supports legacy blockquote captions, unnested captions, and modern captions
- Adds media types to each NPF instance (including multimedia posts)
- Automatic infinite scroll support
- [optional] Reassigns post type from
textto its intended type (e.g.photo,video, ormultimedia) - [optional] Moves main photoset to the top of the post if they were meant to look like legacy photo posts:
- Removes the blockquote border from the main photoset (if applicable)
- Adds original poster's username if the original post does not come with a caption (e.g.:
(Source: glen_px))
- [optional] Custom actions/functions you can perform after the fix executes (e.g. fading in posts)
| Captions type | Demo | Code |
|---|---|---|
| Legacy blockquote captions | view | example theme code |
| Unnested captions by neothm & magnusthemes | view | example theme code |
| Modern captions | view | example theme code |
Please note that the theme codes provided above only render text posts (i.e. excludes all other legacy post types) as the purpose is to aid you in finding your selectors and identifying your theme's markup.
Before we begin, please familiarize yourself with the Tumblr HTML editor's search tool, which can be accessed by clicking anywhere in your theme code and pressing Ctrl+F (CMD+F for Mac), or like so:
Paste the following above </head> in your theme code:
<!-- NPF images fix v4.0 by glenthemes [2026] --->
<!-- github.com/glenthemes/npf-images-v4 -->
<link href="//glenthemes.github.io/npf-images-v4/core.css" rel="stylesheet">
<script src="//glenthemes.github.io/npf-images-v4/main.js"></script>
<style npf-v4-settings>
:root {
--NPF-Text-Container:".caption";
--NPF-Reblogs-Selector:".tumblr_parent";
--NPF-Move-To-Top:"yes";
--NPF-Captionless-Add-Source:"yes";
--NPF-Change-Post-Type:"yes";
--NPF-Caption-Spacing:1em;
--NPF-Images-Spacing:4px;
}
</style>
<script>NPFv4()</script>☝️ You can also try putting it just above </body> if it doesn't seem to do anything!
Type {block:Posts (starts with a curly bracket but doesn't end with one) into the searchbar to see if it exists. You can use the accompanying arrow keys to check if there's more than one result:
If you see {block:PostSummary} or {block:Post1}, ignore them and keep searching.
Stop when you arrive at the line where you should see <div class="posts"> (or similar) on the next line:
{block:Posts}
<div class="posts">Replace that {block:Posts line with the following:
{block:Posts inlineMediaWidth="1280" inlineNestedMediaWidth="1280"}Under the {block:Posts ...} line that you just added should be the HTML that renders your posts. Add post-type="{PostType}" at the end (just before the closing pointy bracket) like so:
<div class="posts" post-type="{PostType}">If you're not sure which line to put it on, use the one with {PostID} if it has one.
Go back to the batch of code you paste from Step 1; specifically this section:
<style npf-v4-settings>
:root {
--NPF-Text-Container:".caption"; /* text caption selector */
--NPF-Reblogs-Selector:".tumblr_parent"; /* reblogs selector */
--NPF-Move-To-Top:"yes"; /* moves first photoset to top of post */
--NPF-Captionless-Add-Source:"yes"; /* adds "(Source: BLOG-NAME)" on posts without caption text */
--NPF-Change-Post-Type:"yes"; /* updates NPF posts to their intended post type */
--NPF-Caption-Spacing:1em; /* spacing between first photoset and its caption */
--NPF-Images-Spacing:4px; /* spacing between NPF media */
}
</style>| Variable name | Description | Accepted values |
|---|---|---|
--NPF-Text-Container |
The CSS selector name for your text posts' captions. Typically the first element that sits under {block:Text}. |
CSS selector names only, wrapped in quotes. Common text container selector names are ".text-block", ".caption", ".tcaption", ".capt", ".cpt", ".text", ".txt", ".tex". |
--NPF-Reblogs-Selector |
The CSS selector name for each of your text post's reblogs. Using this post as an example, @claude-money's post is one reblog, and @odetocody's post is another reblog.Typically the first element that sits under {block:Text}. |
CSS selector names only, wrapped in quotes. Common reblogs selector names are ".tumblr_parent", ".comment_container", ".comment", ".reblog-wrap" |
--NPF-Move-To-Top |
[Optional] Moves the first photoset to the top of the post. | "yes" or "no" |
--NPF-Captionless-Add-Source |
[Optional] Adds (Source: BLOG-NAME) on posts without caption text.💡 Only works if --NPF-Move-To-Top is set to "yes". |
"yes" or "no" |
--NPF-Change-Post-Type |
[Optional] Changes NPF posts from text type to their intended post type (e.g. photo, video, multimedia). Applies to your posts selector.Adds a .previously-npf class to it.💡 Only works if --NPF-Move-To-Top is set to "yes". |
"yes" or "no" |
--NPF-Caption-Spacing |
The spacing between the first photoset and the caption text. 💡 Only works if --NPF-Move-To-Top is set to "yes". |
Any valid size value in CSS (e.g. 14px, 1em) |
--NPF-Images-Spacing |
The spacing between NPF images (and other media). | Any valid size value in CSS (e.g. 4px, 10px) |
- Please do not remove any semi-colons, and do not leave any quotes unclosed.
- If you have trouble pinpointing the selectors for your text container and reblogs, you can leave them blank, like so:
<style npf-v4-settings>
:root {
--NPF-Text-Container:""; /* text caption selector */
--NPF-Reblogs-Selector:""; /* reblogs selector */
/* ...rest of the options */
}
</style>Expand for tips on finding your selectors!
Key points:
- Both
--NPF-Text-Containerand--NPF-Reblogs-Selectorcan be found under{block:Text}in your theme code, where NPF posts are rendered. - The line of HTML immediately below
{block:Text}is usually the--NPF-Text-Container. - If
{block:Reblogs}exists, the line below it is usually the--NPF-Reblogs-Selector. - If
{block:Reblogs}does not exist, leave--NPF-Reblogs-Selectorblank.
Legacy captions: structure example A: (typically found on older themes):
{block:Text}
{block:Title}<h2>{Title}</h2>{/block:Title}
<div class="caption">{Body}</div>
{/block:Text}☝️ In the above example:
- Since
{block:Reblogs}doesn't exist, leave--NPF-Reblogs-Selectorblank. - The
--NPF-Text-Containeris.caption.
Legacy captions: structure example B: (typically found on older themes):
{block:Text}
<div class="text">
{block:Title}<h2>{Title}</h2>{/block:Title}
<div class="caption">{Body}</div>
</div>
{/block:Text}☝️ In the above example:
- Since
{block:Reblogs}doesn't exist, leave--NPF-Reblogs-Selectorblank. - The
--NPF-Text-Containercan be either.textor.caption; either should work.
Legacy captions: structure example C: (typically found on older themes):
{block:Text}
{block:Title}<h2>{Title}</h2>{/block:Title}
<div class="text">
<div class="caption">{Body}</div>
</div>
{/block:Text}☝️ In the above example:
- Since
{block:Reblogs}doesn't exist, leave--NPF-Reblogs-Selectorblank. - The
--NPF-Text-Containercan be either.textor.caption; either should work.
Modern captions: structure example A: (typically found on newer themes):
{block:Text}
<div class="caption">
{block:Title}<h2>{Title}</h2>{/block:Title}
{block:NotReblog}
<div class="comment">{Body}</div>
{/block:NotReblog}
{block:RebloggedFrom}
{block:Reblogs}
<div class="comment">{Body}</div>
{/block:Reblogs}
{/block:RebloggedFrom}
</div>
{/block:Text}☝️ In the above example:
{block:Reblogs}exists; the--NPF-Reblogs-Selectoris.comment.- The
--NPF-Text-Containeris.caption.
Modern captions: structure example B: (typically found on newer themes):
{block:Text}
<div class="caption">
{block:Title}<h2>{Title}</h2>{/block:Title}
{block:NotReblog}
<div class="comment_container">
<div class="comment-header"> ... </div>
<div class="comment">{Body}</div>
</div>
{/block:NotReblog}
{block:RebloggedFrom}
{block:Reblogs}
<div class="comment_container">
<div class="comment-header"> ... </div>
<div class="comment">{Body}</div>
</div>
{/block:Reblogs}
{/block:RebloggedFrom}
</div>
{/block:Text}☝️ In the above example:
{block:Reblogs}exists; the--NPF-Reblogs-Selectoris.comment_container.- The
--NPF-Text-Containeris.caption.
Modern captions: structure example C: (typically found on newer themes):
{block:Text}
{block:Title}<h2>{Title}</h2>{/block:Title}
{block:NotReblog}
<div class="caption">
<div class="comment">{Body}</div>
</div>
{/block:NotReblog}
{block:RebloggedFrom}
{block:Reblogs}
<div class="caption">
<div class="comment">{Body}</div>
</div>
{/block:Reblogs}
{/block:RebloggedFrom}
{/block:Text}☝️ In the above example:
{block:Reblogs}exists; the--NPF-Reblogs-Selectorcan be either.captionor.comment; either should work.- The
--NPF-Text-Containerdoes not exist, leave it blank.
💡 If you're using unnested captions by neothm & magnusthemes, please make sure that the NPF v4 scripts after the unnest script, and do not change the name of .tumblr_parent!
💡 If you've set --NPF-Move-To-Top to "yes" but the photoset still isn't repositioning itself, make sure the {Body} segment in your text block is wrapped in a div or similar:
Example, not wrapped ❌:
<div class="reblogs">
<span class="username">username text</span>
{Body}
</div>Example, wrapped ✅:
<div class="reblogs">
<span class="username">username text</span>
<div>{Body}</div>
</div>💡 If you wish to perform further actions (i.e. JavaScript functions) after the fix has executed, go to this line:
<script>NPFv4()</script>...and modify it like so (and insert the functions/actions you wish to do):
<script>
NPFv4(posts => {
posts?.forEach(post => {
console.log(`This NPF post is now a ${ post.getAttribute("post-type") } post!`) // console logs e.g. "This NPF post is now a photo post!"
})
})
</script>For Tumblr theme users:
The credit is already present in the essential NPF scripts pasted; no further attribution is required.
For Tumblr theme makers:
You are welcome to use this fix in both free and premium themes; in your credits list or page, please include a link to either this repository, my Tumblr blog, or my Tumblr post.
If you run into any issues or need help with installing this fix, please reach out to me via my Discord.
Checklist of things to include when asking for help:
- A link to your blog, e.g.
https://glen-px.tumblr.com - Clarify which theme you are using, and by whom
- Send your full theme code (tutorial: glenthemes.notion.site/dpaste-tutorial)!
If you made it this far, thank you!
Please consider sharing the post or sending me a donation if you found this fix useful! It helps me out a lot 💖
🌟 HT ⋆ glenthemes
