⚠️ Important disclaimer
The script probably conflicts with LinkedIn’s terms:
“Overlay or otherwise modify the Services or their appearance (such as by inserting elements into the Services or removing, covering, or obscuring an advertisement included on the Services)”.
If you want to strictly comply with LinkedIn’s terms, do NOT use this script. If you decide to use it, understand it’s probably against their terms even though it’s for personal use only.
Note for my future self: Review the legal stuff before writing a single line of code.
Even if I’m not going to use it, I’ll share the process and the code anyway as it might inspire somebody.
Why I created this
I got this idea from Tim Woodruff’s post:
But instead of just blocking (or tagging) things I don’t want to see, I though about highlighting things that I don’t want to miss either.

How it works
The script (that you can find at the bottom of this post) runs in your browser using Tampermonkey.
- Scans your LinkedIn timeline for keywords you define.
- Highlights and labels posts containing terms you want to prioritise with coloured borders.
- Labels posts with keywords you’d prefer to ignore.
- Automatically processes new content as you scroll.
- Refreshes periodically to catch updates.
I couldn’t find a solution using only CSS as I did for ServiceNow VTBs, so I went with JavaScript and DOM manipulation for this implementation.
How it looks
Some examples from my company’s timeline.
Single keyword match
When a post contains just one keyword:
- The tag is displayed with its corresponding color.
- The post gets a colored border for easy visual identification.

Multiple keyword matches
When a post match multiple keywords:
- All matching tags are displayed.
- The color is based on the first matching keyword.

Excluding keywords
For posts containing terms you’ve flagged for exclusion:
- No border is applied (doesn’t draw extra attention).
- The keyword is displayed in a neutral gray label.
- These take priority over highlight keywords.

Setting up the LinkedIn post highlighter
Installation
- Install Tampermonkey extension in your browser.
- Create a new script and paste the code below.
[Tested in Chrome Version 134.0.6998.44]
// ==UserScript==
// @name LinkedIn post highlighter
// @match https://www.linkedin.com/*
// ==/UserScript==
(function() {
'use strict';
// Configuration
const config = {
highlightOpacity: '33', // 20% opacity in hex
refreshInterval: 2000,
initialDelay: 1000,
scrollThrottle: 300
};
// Define CSS classes for LinkedIn elements
// These classes are based on LinkedIn's current structure and may change over time
// so they should be verified periodically
const postClass = '.feed-shared-update-v2';
const postContentClass = '.update-components-text';
const postTag = 'span';
// Define colors for highlight keywords
// The order of the keywords in this object matters, as the first one found will be used
// to determine the color of the post
const keywordColors = {
'Documate': '#f15a2a',
'Raycast': '#0d6efd',
'Documentation': '#e83e8c',
'ServiceNow': '#81b5a1'
};
// Define keywords that will cause posts to be excluded
const excludeKeywords = [
'Delve',
'Delving',
'Dive into',
'Exciting news',
'Seamlessly',
'Tapping into'
];
// Exclude label color (gray)
const excludeLabelColor = '#6c757d';
// Add common styles as constants
const COMMON_LABEL_STYLES = `
color: white !important;
padding: 8px 4px !important;
position: absolute !important;
top: 0 !important;
left: -32px !important;
display: flex !important;
align-items: center !important;
writing-mode: vertical-rl !important;
text-orientation: mixed !important;
transform: rotate(180deg) !important;
font-weight: bold !important;
border-radius: 0 8px 8px 0 !important;`;
/**
* Checks if a text contains a specific word as a whole word or hashtag
*
* @param {string} text - The text content to search within
* @param {string} word - The word to search for
* @returns {boolean} - True if the word is found as a whole word or hashtag, false otherwise
*/
function containsWholeWord(text, word) {
//Regex that handles various word boundaries and hashtags
const regex = new RegExp(`(^|[^\\w])${word}($|[^\\w])|#${word}`, 'i');
return regex.test(text);
}
/**
* Creates and appends a label to a post with the given text and color
*
* @param {HTMLElement} post - The post element to attach the label to
* @param {string} text - The text to display in the label
* @param {string} color - The background color of the label (hex format)
*/
function createAndAppendLabel(post, text, color) {
const label = document.createElement('div');
label.className = 'keyword-filter-label';
label.textContent = text;
label.setAttribute('style', `
background: ${color} !important;
${COMMON_LABEL_STYLES}`);
post.appendChild(label);
}
/**
* Filters and highlights LinkedIn posts based on keywords
*
* This function scans all posts in the feed that haven't been processed yet,
* checks for highlight keywords and exclude keywords, and applies appropriate
* styling and labels based on the content.
*/
function filterLinkedInPosts() {
// Select all posts that are not already processed
// and contain the specified classes
const posts = document.querySelectorAll(`${postClass}:has(${postContentClass}):has(${postTag}):not(.processed-by-highlighter)`);
const highlightWords = Object.keys(keywordColors);
posts.forEach(post => {
// Mark as processed
post.classList.add('processed-by-highlighter');
// Get ONLY the post's main content text (excluding comments and profiles)
let postContent = post.querySelector(postContentClass);
if (!postContent) return;
// Extract only the text from the main post content
const postText = postContent.textContent;
// Check if post contains any highlighting keywords (whole word match)
const containsHighlightKeyword = highlightWords.filter(keyword =>
containsWholeWord(postText, keyword)
);
// Check if post contains any excluding keywords (whole word match)
const containsExcludeKeyword = excludeKeywords.filter(keyword =>
containsWholeWord(postText, keyword)
);
// Remove existing label if it exists
const existingLabel = post.querySelector('.keyword-filter-label');
if (existingLabel) {
post.removeChild(existingLabel);
}
// First check for exclude keywords - they take priority
if (containsExcludeKeyword.length > 0) {
post.setAttribute('style', 'position: relative !important; border-radius: 8px !important;');
createAndAppendLabel(post, containsExcludeKeyword.join(' / '), excludeLabelColor);
}
// Then check for highlight keywords
else if (containsHighlightKeyword.length > 0) {
// Get the first found keyword to determine the color
const primaryKeyword = containsHighlightKeyword[0];
const color = keywordColors[primaryKeyword];
// Create a background with the same color but at 20% opacity
post.setAttribute('style', `border: 2px solid ${color} !important; position: relative !important; opacity: 1 !important; border-radius: 8px !important;`);
createAndAppendLabel(post, containsHighlightKeyword.join(' / '), color);
}
});
}
// Run once when page loads
setTimeout(() => filterLinkedInPosts(), config.initialDelay);
// Re-check when scrolling (LinkedIn loads content dynamically)
window.addEventListener('scroll', () => {
setTimeout(() => filterLinkedInPosts(), config.scrollThrottle);
});
// Also check periodically for new content
setInterval(() => filterLinkedInPosts(), config.refreshInterval);
})();
Code language: JavaScript (javascript)
Customisation Tips
To personalise the script for your needs:
- Change the keywords in the
keywordColors
object to terms relevant to your interests. - Add or remove terms from the
excludeKeywords
array to flag content you want to ignore. - Modify the colours to match your preferences.
- Adjust the timing parameters in the
config
object if needed.
Ideas for improvement
- Send the text post to an AI analyser and exclude posts based on a AI probability treshold instead of using “excluding keywords”.
- Hide the posts instead of adding a tag (this definitely goes against LinkedIn’s terms).