Um dashboard corporativo que parece um app de tutorial passa uma mensagem ruim antes mesmo de mostrar um número: "isto é um protótipo". Identidade visual — cores da marca, tipografia, espaçamento, o logotipo no lugar certo — é o que faz um app parecer um produto. E a boa notícia é que, no Shiny moderno, você consegue quase tudo isso sem escrever CSS na unha, graças ao bslib e ao Sass. Este módulo transforma a interface funcional de arquitetura responsiva numa interface com marca.
Aplicar identidade visual profissional a apps Shiny: temas do Bootstrap com bslib::bs_theme (cores, fontes, modo claro/escuro), variáveis Sass para customização fina, tipografia com Google Fonts, harmonização automática de gráficos com thematic, e centralização da marca com brand.yml.
O caminho fácil: bs_theme
O bslib deixa você definir um tema com poucos argumentos — cores principais, fontes e a versão do Bootstrap. Isso já reestiliza o app inteiro de forma coerente.
library(bslib)
meu_tema <- bs_theme(
version = 5,
bg = "#ffffff", # cor de fundo
fg = "#1a1a2e", # cor do texto (foreground)
primary = "#e94560", # cor primária (botões, links, destaques)
secondary = "#0f3460",
success = "#16a34a",
base_font = font_google("Inter"), # fonte do corpo
heading_font = font_google("Poppins"), # fonte dos títulos
code_font = font_google("JetBrains Mono")
)
ui <- page_sidebar(
theme = meu_tema, # aplica o tema à página inteira
title = "App com Marca",
sidebar = sidebar(sliderInput("n", "N", 1, 100, 50)),
card(plotOutput("g"))
)
Definir primary já propaga a cor da marca para botões, links, value_box, abas ativas e realces — porque o Bootstrap deriva dezenas de estilos dessa única variável. É o maior retorno visual por linha de código em todo o Shiny.
Fontes: font_google, font_local, font_face
Tipografia é metade da identidade. bslib traz três formas de carregar fontes:
bs_theme(
base_font = font_google("Inter"), # baixa do Google Fonts
heading_font = font_local("Marca", "www/marca.woff2"), # fonte da empresa, local
# ou definição manual completa:
base_font = font_face(
family = "MinhaFonte",
src = "url('fonts/minha.woff2') format('woff2')"
)
)
Use font_google para prototipar, font_local/font_face em produção (não depender de um serviço externo, e cumprir políticas de privacidade que proíbem chamar o Google). As fontes locais ficam em www/, como visto em arquitetura responsiva e HTML.
Modo claro e escuro
Usuários esperam poder alternar tema. bslib faz isso com um seletor pronto e temas por modo.
ui <- page_navbar(
title = "Dashboard",
theme = bs_theme(version = 5, primary = "#e94560"),
# Botão de alternância claro/escuro no navbar
nav_spacer(),
nav_item(input_dark_mode(id = "modo")) # toggle claro/escuro pronto
)
Você pode inspecionar o modo escolhido (input$modo = "light"/"dark") e ajustar gráficos ou lógica conforme o tema. O bslib cuida de reescrever as cores do Bootstrap automaticamente ao alternar.
Customização fina com variáveis Sass
Quando os argumentos de bs_theme não bastam, você mexe diretamente nas variáveis Sass do Bootstrap — centenas delas controlam raios de borda, sombras, espaçamentos, alturas. Passe-as como argumentos nomeados.
bs_theme(
version = 5,
primary = "#e94560",
"border-radius" = "0.75rem", # cantos mais arredondados
"card-border-width" = "0", # cards sem borda
"card-box-shadow" = "0 4px 20px rgba(0,0,0,.08)", # sombra suave
"navbar-padding-y" = "1rem",
"font-size-base" = "0.95rem"
)
Para CSS/Sass mais extenso, escreva um arquivo .scss e injete-o. O bslib compila Sass em tempo real, então você pode usar variáveis, aninhamento e funções do Sass — não apenas CSS plano.
# www/custom.scss usa variáveis do próprio tema
bs_theme(version = 5, primary = "#e94560") |>
bs_add_rules(sass::sass_file("www/custom.scss"))
/* www/custom.scss */
.value-box {
transition: transform .2s ease;
&:hover { transform: translateY(-4px); } /* aninhamento Sass */
}
.card-header { font-weight: 600; letter-spacing: .02em; }
thematic: gráficos que seguem o tema automaticamente
Um problema comum: o app fica lindo com a cor da marca, mas os ggplot2/base R continuam cinza-padrão, destoando. O pacote thematic resolve isso fazendo os gráficos herdarem as cores e fontes do tema do app — automaticamente.
library(thematic)
# Uma linha ativa a harmonização de TODOS os gráficos com o tema bslib
thematic_shiny(font = "auto")
server <- function(input, output, session) {
output$g <- renderPlot({
# Este ggplot herda bg, cor de texto, fonte e paleta do tema do app
ggplot(mtcars, aes(wt, mpg)) + geom_point() + theme_minimal()
})
}
Com thematic_shiny(), mudar a cor primária do tema ou alternar para modo escuro reflete nos gráficos sem você tocar em cada ggplot. É o elo que faz a identidade visual ser coerente do HTML aos gráficos.
brand.yml: a marca num só arquivo
A novidade que profissionaliza tudo: brand.yml — um único arquivo YAML que descreve a marca (cores, fontes, logo) e é lido por bslib, thematic, Quarto e outras ferramentas Posit. Defina a marca uma vez, use em todos os artefatos.
# _brand.yml
meta:
name: Minha Empresa
color:
palette:
vermelho: "#e94560"
azul-escuro: "#0f3460"
cinza: "#1a1a2e"
primary: vermelho
secondary: azul-escuro
foreground: cinza
background: "#ffffff"
typography:
base:
family: Inter
headings:
family: Poppins
weight: 600
logo:
image: logo.png
# O app inteiro lê a marca do arquivo — cores, fontes e logo de uma vez
ui <- page_sidebar(
theme = bs_theme(brand = "_brand.yml"),
title = "Dashboard",
# ...
)
A grande vantagem: uma fonte de verdade para a identidade. Trocar a cor primária da empresa é editar uma linha do brand.yml; o app, os relatórios Quarto e os gráficos se atualizam juntos. É o padrão recomendado para organizações com múltiplos apps/relatórios.
Prototipando temas ao vivo: bs_theme_preview
Não adivinhe cores — teste ao vivo. bs_theme_preview abre um app interativo onde você mexe nas variáveis e vê o resultado na hora.
bs_theme_preview(bs_theme(version = 5, primary = "#e94560"))
# Abre uma UI com todos os componentes e controles para ajustar o tema
Ajuste até gostar, copie os valores para o seu bs_theme/brand.yml, e pronto. Economiza horas de tentativa e erro.
O bs_theme() reestiliza o app inteiro a partir de poucos argumentos — primary, bg, fg, base_font, heading_font —, e basta definir o primary para a cor da marca se propagar por dezenas de componentes. Nas fontes, font_google serve para prototipar e font_local ou font_face para produção, onde não convém depender de serviço externo; os arquivos ficam em www/. Modo claro e escuro vem pronto com input_dark_mode(), e o valor aparece em input$modo. A customização fina passa por variáveis Sass entregues ao bs_theme, como "border-radius" e "card-box-shadow", e por .scss compilado com bs_add_rules e sass_file. O thematic_shiny() fecha a coerência entre HTML e gráficos, fazendo ggplot2 e os gráficos da base herdarem cores e fontes do tema. Acima de tudo isso, o brand.yml centraliza a marca — cores, fontes e logo — num arquivo lido por bslib, thematic e Quarto, virando uma fonte de verdade para a organização inteira. E o bs_theme_preview() permite ajustar o tema ao vivo antes de fixá-lo.
Exercícios
-
Por que definir apenas
primary = "#e94560"embs_themejá muda a aparência de botões, links e abas ativas de uma vez?✓ Resposta:Porque o Bootstrap é construído sobre variáveis Sass e a cor
primaryé uma variável-raiz da qual dezenas de estilos são derivados: cor de botões primários, links, realce de abas ativas, foco de inputs,value_boxetc. Ao mudarprimary, obslibrecompila o Sass e todos esses estilos derivados mudam juntos, de forma coerente. É por isso que uma única linha tem tanto impacto visual. -
Quando preferir
font_local/font_faceafont_google, e por quê?✓ Resposta:Em produção, especialmente em ambientes corporativos.
font_googlebaixa a fonte dos servidores do Google em tempo de execução, o que (a) cria dependência de um serviço externo — se ele estiver indisponível/bloqueado, a tipografia quebra — e (b) pode violar políticas de privacidade/LGPD-GDPR que proíbem chamadas externas.font_local/font_faceservem a fonte a partir dewww/, mantendo tudo self-contained e sob controle. Usefont_googlesó para prototipar. -
O app está com a cor da marca, mas os gráficos
ggplot2continuam cinza e destoam. Qual a solução de uma linha?✓ Resposta:Ativar o
thematiccomthematic::thematic_shiny(font = "auto")no início do app. Isso faz todos os gráficosggplot2/base R herdarem automaticamente o fundo, a cor do texto, a fonte e a paleta do temabslib— inclusive ao alternar para modo escuro. Semthematic, seria preciso reestilizar cadaggplotmanualmente comtheme()e escalas de cor. -
O que é o
brand.ymle qual problema organizacional ele resolve?✓ Resposta:É um arquivo YAML único que descreve a identidade da marca — paleta de cores, cores semânticas (primary/secondary/foreground/background), tipografia e logo. Ele resolve o problema de inconsistência e duplicação: em vez de reespecificar as cores da empresa em cada app, relatório e gráfico (e correr o risco de divergirem), define-se a marca uma vez e todas as ferramentas Posit (
bslib,thematic, Quarto) a leem. Trocar a cor primária vira uma edição de uma linha que se propaga a todos os artefatos. -
Você quer cantos mais arredondados e uma sombra suave nos cards, algo que
bs_theme(primary=)não cobre. Como fazer sem escrever CSS "na unha" espalhado?✓ Resposta:Passando variáveis Sass do Bootstrap como argumentos nomeados ao próprio
bs_theme, por exemplo:bs_theme(version = 5, primary = "#e94560", "border-radius" = "0.75rem", "card-box-shadow" = "0 4px 20px rgba(0,0,0,.08)", "card-border-width" = "0")Isso ajusta o design system de forma centralizada e coerente (todos os cards seguem a regra), em vez de espalhar seletores CSS soltos. Para regras mais extensas, um
.scsscompilado combs_add_rules(sass_file(...)), aproveitando variáveis e aninhamento do Sass.