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

  1. Por que definir apenas primary = "#e94560" em bs_theme já 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_box etc. Ao mudar primary, o bslib recompila o Sass e todos esses estilos derivados mudam juntos, de forma coerente. É por isso que uma única linha tem tanto impacto visual.

  2. Quando preferir font_local/font_face a font_google, e por quê?

    ✓ Resposta:

    Em produção, especialmente em ambientes corporativos. font_google baixa 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_face servem a fonte a partir de www/, mantendo tudo self-contained e sob controle. Use font_google só para prototipar.

  3. O app está com a cor da marca, mas os gráficos ggplot2 continuam cinza e destoam. Qual a solução de uma linha?

    ✓ Resposta:

    Ativar o thematic com thematic::thematic_shiny(font = "auto") no início do app. Isso faz todos os gráficos ggplot2/base R herdarem automaticamente o fundo, a cor do texto, a fonte e a paleta do tema bslib — inclusive ao alternar para modo escuro. Sem thematic, seria preciso reestilizar cada ggplot manualmente com theme() e escalas de cor.

  4. O que é o brand.yml e 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.

  5. 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 .scss compilado com bs_add_rules(sass_file(...)), aproveitando variáveis e aninhamento do Sass.

Fontes e leituras recomendadas