Our solution for blurring NSFW content

At the Blender Artists forum we have a faily liberal content policy where we allow nudity and violence (up to a point). While most members are fine with this kind of content, there are of course audiences and situations where this is not suitable (for us mainly schools and children). And since we make heavy use of tiled galleries with the Topic List Preview plugin, we needed a way to make this kind of content opt-in and get it out of your face by default.

The solution was easier to implement than expected and I decided to share it here in case anyone else can use it. Fair warning: I’ll link to some NSFW content here. Let’s go!

We already required an #nsfw tag for any relevant posts and have been enforcing this strictly for the last few months. Our AdSense plugin is configured to not display ads on these pages as that will (and has!) get us into trouble with Google. (Big thanks to @neil for adding this feature!)

Using some CSS we added a blur and overlay text for all media inside such topics. The blur will be removed on hover:

/* display nsfw blur and overlay text on any media in #nswf topics */
.tag-nsfw { 
	.topic-body .cooked img, 
	.topic-body .cooked iframe, 
	.topic-body .cooked .lazyYT-container, 
	.topic-thumbnail img {
        filter: blur(10px);	
        -webkit-transition: .3s ease-in-out;
        transition: .2s ease-in-out;
	}

	.topic-body:hover .cooked img, 
	.topic-body:hover .cooked iframe,
	.topic-body:hover .cooked .lazyYT-container, 		
	.topic-thumbnail:hover img {
        filter: blur(0);	
        -webkit-transition: .3s ease-in-out;
        transition: .2s ease-in-out;
	}

	.topic-body .cooked a.lightbox:before, 
	.topic-body .cooked iframe:before,
	.topic-thumbnail a:before {
	    z-index:2;
        padding: 5px;
        font-size:1em;
        position:absolute;

        color:#fff;
        content: '⚠️ Mature content - Hover to show';
        background: #e86800;

	}
	
	.topic-body .cooked a.lightbox:before, 
	.topic-body .cooked iframe:before {
        top: 15px;
        left: 10px;
	}

	.topic-thumbnail a:before {
        top: 65px;
        left: 20px;
	}
	
	.topic-body .cooked a.lightbox:hover:before, 
	.topic-body .cooked iframe:hover:before,
	.topic-thumbnail a:hover:before {		
	    display:none;
	}
}

Images and videos in a topic now look like this:

And in any TLP tiled galleries, we have:

Next, we added a preference that allows users to disable the blurring for their account. This turned out to be easier to implement than I thought using custom fields.

We started by creating a checkbox custom field:

Then, we repurposed some code to add ‘nsfw-always-show’ tag to the body class for these users:

<!-- add current user's nsfw preferences to body tag -->
<script type="text/discourse-plugin" version="0.8">

// https://meta.discourse.org/t/css-classes-for-group-membership-for-simplified-ui-mode/60838/2
if (window.jQuery) {
    window.jQuery(function ($) {
        var u = Discourse.User.current();

        // always show NSFW
        if (u.custom_fields.user_field_2) {
            console.log('show nsfw for user');
            $('body').addClass('nsfw-always-show' );
        }

    });
};

</script>

A last bit of CSS removes the blurring for these users:

/* hide custom fields from signup form */

.login-form .user-fields {display:none;}

/* disable nsfw blurring for users who set this in their preferences */

.nsfw-always-show .tag-nsfw {
	.topic-body .cooked img, 
	.topic-body .cooked iframe, 
	.topic-body .cooked .lazyYT-container, 
	.topic-thumbnail img {
        filter: blur(0px);	
	}
	
	.topic-body .cooked a.lightbox:before, 
	.topic-body .cooked iframe:before,
	.topic-thumbnail a:before {
	    display:none;
	    content: none;
	}
}

A known issue with this approach is that it does not work great on mobile yet as :hover is not supported.

If you want to see this in action you can visit our #nsfw tag page, but, well, you might see some nsfw content there :slight_smile:

I hope this was useful for someone!

52 Likes

Also, on desktop, if an image take some space on the screen, you can easily hover it by mistake.
Instead of hovering to unblur, what about dynamically add a button “show the nsfw image” on top or under each nsfw image ?

3 Likes

At krita-artists.org We have slightly modified this to be on click and not on hover. However the setting used in the profile doesn’t work. Even if the user has set to show nsfw content he gets the blurred content. Is there a fix for it?

I can’t edit my OP anymore, but here’s the updated code. Mind sharing your ‘on click’ solution too?

<!-- add current user's nsfw preferences to body tag -->
<script type="text/discourse-plugin" version="0.8.7">

// https://meta.discourse.org/t/css-classes-for-group-membership-for-simplified-ui-mode/60838/2
if (window.jQuery) {
    window.jQuery(function ($) {

        let currentUser = api.getCurrentUser();
        
        if (currentUser) {
            api.container.lookup('store:main').find('user', currentUser.username).then((user) => {

                if (user.user_fields[2]) {
                    $('body').addClass('nsfw-always-show' );
                }
            });
        }
    });
};
</script>
2 Likes

Our onlick solution is hacky and I think may not be ideal, we just removed blur from hover and added blur by default. The message was also changed to say user has click to go the post. Now the user has to click to go to post and click again to reveal the nsfw image in lightbox. Which is cumbersome but it prevents inadvertent hover and reveal. It might be good to use js to remove blur on click.

/* display nsfw blur and overlay text on any media in #nswf topics */
.tag-nsfw { 
.topic-thumbnail {
    overflow:hidden;
}

	.topic-body .cooked .lightbox img, 
	.topic-body .cooked iframe, 
	.topic-body .cooked .lazyYT-container, 
	.topic-thumbnail img {
    filter: blur(30px);	
    -webkit-transition: .3s ease-in-out;
    transition: .2s ease-in-out;
	}

	.topic-body .cooked a.lightbox:before, 
	.topic-body .cooked iframe:before,
	.topic-thumbnail a:before {
	    z-index:2;
    padding: 5px;
    font-size:1em;
    position:absolute;

    color:#fff;
    content: '⚠️ Mature content - Click to see the picture';
    background: #000;

	}
	
	.topic-body .cooked a.lightbox:before, 
	.topic-body .cooked iframe:before {
    top: 50%;
    left: 10px;
    right: 10px;
    text-align:center;
	}

	.topic-thumbnail a:before {
    top: 65px;
    left: 20px;
	}
	
}

/* hide custom fields from signup form */
.login-form .user-fields {display:none;}

/* disable nsfw blurring for users who set this in their preferences */
.nsfw-always-show .tag-nsfw {
	.topic-body .cooked img, 
	.topic-body .cooked iframe, 
	.topic-body .cooked .lazyYT-container, 
	.topic-thumbnail img {
    filter: blur(0px);	
	}
	
	.topic-body .cooked a.lightbox:before, 
	.topic-body .cooked iframe:before,
	.topic-thumbnail a:before {
	    display:none;
	    content: none;
	}
}

@bartv @Terrapop

If someone wants to take this on, its possible to use Discourse Image Filter - plugin - Discourse Meta to build a auto nsfw blurring plugin.

4 Likes

Hi there, what would I have to do to remove the blur and instead make the text be hyperlinked as we want to have it link to a donation off-site.

1 Like

You can’t do that with CSS only I’m afraid; you’ll have to add your own code to do that.

2 Likes

Just remove the filter: blur(10px); lines from the CSS.

5 Likes

@bartv I’ve made the first post wiki, please feel free to update it as needed! :pray:

9 Likes