Skip to content

Instantly share code, notes, and snippets.

@teles
Last active January 15, 2026 12:10
Show Gist options
  • Select an option

  • Save teles/8ab6ae78ffdff3c81c448815b4945094 to your computer and use it in GitHub Desktop.

Select an option

Save teles/8ab6ae78ffdff3c81c448815b4945094 to your computer and use it in GitHub Desktop.

🎨 Kanban Force - Destaque de Cards (Reactive Theme)

Este script cria um bookmarklet para o Kanban Force que destaca visualmente cards recentes na coluna.

A grande vantagem desta versão é que ela é Reativa ao Tema: as cores do destaque se ajustam automaticamente se você alternar entre o Modo Claro e o Modo Escuro do site, garantindo sempre a legibilidade do texto.

✨ Funcionalidades

  1. Destaque Configurável: Você define o limite de dias (ex: destacar tudo com menos de 2 dias na coluna).
  2. Inteligente: Ignora cards que ainda não têm tempo contabilizado (marcados com -).
  3. Cores Adaptativas:
    • ☀️ Modo Claro: Fundo Amarelo Pastel (#fff9c4).
    • 🌙 Modo Escuro: Fundo Dourado/Marrom Escuro (#423900), permitindo ler o texto branco sem ofuscar.
    • 🟥 Borda: Vermelha em ambos os casos.

📂 Arquivos neste Gist

  • bookmarklet.source.js: O código fonte completo, comentado e legível. Ideal para ler e entender a lógica.
  • bookmarklet.dist.js: O código compactado e pronto para uso (já formatado como javascript:...). Use este arquivo para a instalação.

🚀 Como instalar (Modo Fácil)

Não é necessário usar sites externos. O código em bookmarklet.dist.js já está pronto.

  1. Abra o arquivo bookmarklet.dist.js deste Gist e copie todo o seu conteúdo.
  2. No seu navegador, clique com o botão direito na Barra de Favoritos e selecione "Adicionar página..." (ou "Adicionar favorito").
  3. No campo Nome, digite algo como KF Highlight.
  4. No campo URL (ou Endereço), cole o código que você copiou.
  5. Clique em Salvar.

🕹️ Como usar

  1. Abra seu board no Kanban Force.
  2. Clique no favorito que você acabou de criar.
  3. Digite o número de dias desejado na janela que abrir e dê Enter.
  4. Pronto! Se você trocar o tema do site nas preferências, o destaque muda de cor automaticamente para se adaptar.

Script roda localmente no navegador.

javascript:(function()%7B%2F**%0A%20*%20%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%0A%20*%20KANBAN%20FORCE%20-%20HIGHLIGHT%20(V11%20-%20CONTRASTE%20PERFEITO)%0A%20*%20%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%0A%20*%20*%20Corre%C3%A7%C3%A3o%20V11%3A%20Ajuste%20de%20cores%20para%20leitura%20no%20Modo%20Escuro.%0A%20*%20*%20-%20Modo%20Claro%3A%20Fundo%20Amarelo%20Pastel%20(%23fff9c4)%20%2B%20Texto%20Preto%20(%23000).%0A%20*%20*%20-%20Modo%20Escuro%3A%20Fundo%20Ouro%20Profundo%20(%232b2500)%20%2B%20Texto%20Branco%20(%23fff).%0A%20*%20*%20Isso%20garante%20contraste%20m%C3%A1ximo%20e%20evita%20que%20o%20texto%20fique%20%22apagado%22.%0A%20*%20%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%0A%20*%2F%0A%0A(function%20highlightCardsV11()%20%7B%0A%20%20%20%20%2F%2F%20---%201.%20Configura%C3%A7%C3%B5es%20---%0A%20%20%20%20const%20HIGHLIGHT_CLASS%20%3D%20'kf-highlight-final'%3B%0A%20%20%20%20const%20STYLE_ID%20%3D%20'kf-highlight-style-v11'%3B%0A%0A%20%20%20%20let%20input%20%3D%20prompt(%22Destacar%20cards%20com%20MENOS%20de%20quantos%20dias%3F%22%2C%20%222%22)%3B%0A%20%20%20%20let%20limitDays%20%3D%20parseInt(input%2C%2010)%3B%0A%20%20%20%20if%20(isNaN(limitDays)%20%7C%7C%20limitDays%20%3C%200)%20limitDays%20%3D%201%3B%0A%0A%20%20%20%20%2F%2F%20---%202.%20CSS%20com%20Vari%C3%A1veis%20de%20Texto%20e%20Fundo%20---%0A%20%20%20%20const%20oldStyle%20%3D%20document.getElementById(STYLE_ID)%3B%0A%20%20%20%20if%20(oldStyle)%20oldStyle.remove()%3B%0A%0A%20%20%20%20const%20css%20%3D%20%60%0A%20%20%20%20%20%20%20%20%2F*%20%3D%3D%3D%20VARI%C3%81VEIS%20(PADR%C3%83O%20%2F%20CLARO)%20%3D%3D%3D%20*%2F%0A%20%20%20%20%20%20%20%20%3Aroot%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20--kf-hl-bg%3A%20%23fff9c4%3B%20%20%20%20%20%20%2F*%20Amarelo%20Pastel%20*%2F%0A%20%20%20%20%20%20%20%20%20%20%20%20--kf-hl-text%3A%20%23000000%3B%20%20%20%20%2F*%20Texto%20Preto%20(Contraste%20Alto)%20*%2F%0A%20%20%20%20%20%20%20%20%20%20%20%20--kf-hl-border%3A%20%23ff0000%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%2F*%20%3D%3D%3D%20REGRAS%20PARA%20MODO%20ESCURO%20(DATA-THEME)%20%3D%3D%3D%20*%2F%0A%20%20%20%20%20%20%20%20body%5Bdata-theme%3D'dark'%5D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20--kf-hl-bg%3A%20%23362f00%3B%20%20%20%20%20%20%2F*%20Ouro%2FOliva%20Muito%20Escuro%20(Fundo)%20*%2F%0A%20%20%20%20%20%20%20%20%20%20%20%20--kf-hl-text%3A%20%23ffffff%3B%20%20%20%20%2F*%20Texto%20Branco%20Puro%20(Leitura%20f%C3%A1cil)%20*%2F%0A%20%20%20%20%20%20%20%20%20%20%20%20--kf-hl-border%3A%20%23ff3333%3B%20%20%2F*%20Vermelho%20um%20pouco%20mais%20claro%20para%20brilhar%20no%20escuro%20*%2F%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%2F*%20%3D%3D%3D%20APLICA%C3%87%C3%83O%20NOS%20CARDS%20%3D%3D%3D%20*%2F%0A%20%20%20%20%20%20%20%20app-kanbanforce-card.%24%7BHIGHLIGHT_CLASS%7D%20ion-card%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20background-color%3A%20var(--kf-hl-bg)%20!important%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20--background%3A%20var(--kf-hl-bg)%20!important%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3A%20var(--kf-hl-text)%20!important%3B%20%2F*%20For%C3%A7a%20a%20cor%20do%20texto%20para%20garantir%20leitura%20*%2F%0A%20%20%20%20%20%20%20%20%20%20%20%20transition%3A%20background-color%200.3s%20ease%2C%20color%200.3s%20ease%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%2F*%20Garante%20que%20titulos%20e%20textos%20dentro%20do%20card%20herdem%20a%20cor%20for%C3%A7ada%20*%2F%0A%20%20%20%20%20%20%20%20app-kanbanforce-card.%24%7BHIGHLIGHT_CLASS%7D%20ion-card%20*%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3A%20var(--kf-hl-text)%20!important%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%2F*%20Borda%20do%20container%20*%2F%0A%20%20%20%20%20%20%20%20app-kanbanforce-card.%24%7BHIGHLIGHT_CLASS%7D%20.card-container%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20border%3A%203px%20solid%20var(--kf-hl-border)%20!important%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20border-radius%3A%208px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20box-sizing%3A%20border-box%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%60%3B%0A%0A%20%20%20%20const%20styleEl%20%3D%20document.createElement('style')%3B%0A%20%20%20%20styleEl.id%20%3D%20STYLE_ID%3B%0A%20%20%20%20styleEl.innerHTML%20%3D%20css%3B%0A%20%20%20%20document.head.appendChild(styleEl)%3B%0A%0A%20%20%20%20%2F%2F%20---%203.%20Execu%C3%A7%C3%A3o%20(L%C3%B3gica%20de%20Tempo)%20---%0A%20%20%20%20console.log(%60%F0%9F%94%8E%20Marcando%20cards%20com%20%3C%20%24%7BlimitDays%7D%20dias...%60)%3B%0A%20%20%20%20const%20cards%20%3D%20document.querySelectorAll('app-kanbanforce-card')%3B%0A%20%20%20%20let%20count%20%3D%200%3B%0A%0A%20%20%20%20cards.forEach(card%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20try%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Reset%0A%20%20%20%20%20%20%20%20%20%20%20%20card.classList.remove(HIGHLIGHT_CLASS)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20container%20%3D%20card.querySelector('.card-container')%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20inner%20%3D%20card.querySelector('ion-card')%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if(container)%20container.style.border%20%3D%20''%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if(inner)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20inner.style.backgroundColor%20%3D%20''%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20inner.style.removeProperty('--background')%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20inner.style.removeProperty('color')%3B%20%2F%2F%20Reseta%20cor%20do%20texto%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Busca%20Tempo%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20rows%20%3D%20Array.from(card.querySelectorAll('.card-row'))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20timeRow%20%3D%20rows.find(row%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20const%20title%20%3D%20row.querySelector('.cycle-time-title')%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20title%20%26%26%20title.innerText.toLowerCase().includes('tempo%20na%20coluna')%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(!timeRow)%20return%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20timeValComponent%20%3D%20timeRow.querySelector('app-card-cycle-time%20ion-col')%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(!timeValComponent)%20return%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20timeText%20%3D%20timeValComponent.innerText.trim()%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Valida%20Regra%0A%20%20%20%20%20%20%20%20%20%20%20%20let%20shouldHighlight%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(timeText%20%3D%3D%3D%20'-'%20%7C%7C%20timeText%20%3D%3D%3D%20'')%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20shouldHighlight%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20if%20(timeText.includes('d'))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20const%20daysMatch%20%3D%20timeText.match(%2F(%5Cd%2B)%5Cs*d%2Fi)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(daysMatch)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20const%20days%20%3D%20parseInt(daysMatch%5B1%5D%2C%2010)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(days%20%3C%20limitDays)%20shouldHighlight%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20shouldHighlight%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%2F%2F%20Aplica%20Classe%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(shouldHighlight)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20card.classList.add(HIGHLIGHT_CLASS)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20count%2B%2B%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%7D%20catch%20(err)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20console.error(err)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D)%3B%0A%0A%20%20%20%20console.log(%60%E2%9C%85%20Conclu%C3%ADdo!%20%24%7Bcount%7D%20cards%20marcados.%60)%3B%0A%7D)()%3B%7D)()%3B
/**
* ============================================================================
* KANBAN FORCE - HIGHLIGHT (V11 - CONTRASTE PERFEITO)
* ============================================================================
* * Correção V11: Ajuste de cores para leitura no Modo Escuro.
* * - Modo Claro: Fundo Amarelo Pastel (#fff9c4) + Texto Preto (#000).
* * - Modo Escuro: Fundo Ouro Profundo (#2b2500) + Texto Branco (#fff).
* * Isso garante contraste máximo e evita que o texto fique "apagado".
* ============================================================================
*/
(function highlightCardsV11() {
// --- 1. Configurações ---
const HIGHLIGHT_CLASS = 'kf-highlight-final';
const STYLE_ID = 'kf-highlight-style-v11';
let input = prompt("Destacar cards com MENOS de quantos dias?", "2");
let limitDays = parseInt(input, 10);
if (isNaN(limitDays) || limitDays < 0) limitDays = 1;
// --- 2. CSS com Variáveis de Texto e Fundo ---
const oldStyle = document.getElementById(STYLE_ID);
if (oldStyle) oldStyle.remove();
const css = `
/* === VARIÁVEIS (PADRÃO / CLARO) === */
:root {
--kf-hl-bg: #fff9c4; /* Amarelo Pastel */
--kf-hl-text: #000000; /* Texto Preto (Contraste Alto) */
--kf-hl-border: #ff0000;
}
/* === REGRAS PARA MODO ESCURO (DATA-THEME) === */
body[data-theme='dark'] {
--kf-hl-bg: #362f00; /* Ouro/Oliva Muito Escuro (Fundo) */
--kf-hl-text: #ffffff; /* Texto Branco Puro (Leitura fácil) */
--kf-hl-border: #ff3333; /* Vermelho um pouco mais claro para brilhar no escuro */
}
/* === APLICAÇÃO NOS CARDS === */
app-kanbanforce-card.${HIGHLIGHT_CLASS} ion-card {
background-color: var(--kf-hl-bg) !important;
--background: var(--kf-hl-bg) !important;
color: var(--kf-hl-text) !important; /* Força a cor do texto para garantir leitura */
transition: background-color 0.3s ease, color 0.3s ease;
}
/* Garante que titulos e textos dentro do card herdem a cor forçada */
app-kanbanforce-card.${HIGHLIGHT_CLASS} ion-card * {
color: var(--kf-hl-text) !important;
}
/* Borda do container */
app-kanbanforce-card.${HIGHLIGHT_CLASS} .card-container {
border: 3px solid var(--kf-hl-border) !important;
border-radius: 8px;
box-sizing: border-box;
}
`;
const styleEl = document.createElement('style');
styleEl.id = STYLE_ID;
styleEl.innerHTML = css;
document.head.appendChild(styleEl);
// --- 3. Execução (Lógica de Tempo) ---
console.log(`🔎 Marcando cards com < ${limitDays} dias...`);
const cards = document.querySelectorAll('app-kanbanforce-card');
let count = 0;
cards.forEach(card => {
try {
// Reset
card.classList.remove(HIGHLIGHT_CLASS);
const container = card.querySelector('.card-container');
const inner = card.querySelector('ion-card');
if(container) container.style.border = '';
if(inner) {
inner.style.backgroundColor = '';
inner.style.removeProperty('--background');
inner.style.removeProperty('color'); // Reseta cor do texto
}
// Busca Tempo
const rows = Array.from(card.querySelectorAll('.card-row'));
const timeRow = rows.find(row => {
const title = row.querySelector('.cycle-time-title');
return title && title.innerText.toLowerCase().includes('tempo na coluna');
});
if (!timeRow) return;
const timeValComponent = timeRow.querySelector('app-card-cycle-time ion-col');
if (!timeValComponent) return;
const timeText = timeValComponent.innerText.trim();
// Valida Regra
let shouldHighlight = false;
if (timeText === '-' || timeText === '') {
shouldHighlight = false;
} else if (timeText.includes('d')) {
const daysMatch = timeText.match(/(\d+)\s*d/i);
if (daysMatch) {
const days = parseInt(daysMatch[1], 10);
if (days < limitDays) shouldHighlight = true;
}
} else {
shouldHighlight = true;
}
// Aplica Classe
if (shouldHighlight) {
card.classList.add(HIGHLIGHT_CLASS);
count++;
}
} catch (err) {
console.error(err);
}
});
console.log(`✅ Concluído! ${count} cards marcados.`);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment