Como centralizar grid no GeneratePress: 1 Solução Definitiva

Como centralizar grid no GeneratePress parece simples, mas quem já tentou sabe que não é. O problema surge quando usamos GenerateBlocks para criar layouts dinâmicos com múltiplas colunas.

Na teoria, o Grid resolve tudo. Na prática, a última linha quase sempre fica desalinhada à esquerda.

Isso quebra a estética, reduz a percepção de qualidade e pode impactar a experiência do usuário.

Este estudo de caso mostra como identificamos o problema, analisamos as limitações da ferramenta e criamos uma solução robusta, automática e escalável.

Sem gambiarras. Sem plugins extras. Sem comprometer performance.

Se você usa GeneratePress profissionalmente, este conteúdo pode economizar horas do seu tempo.



O problema real do Grid no GeneratePress

O GeneratePress é conhecido pela leveza e flexibilidade. Quando combinado com GenerateBlocks, ele se torna uma ferramenta extremamente poderosa para layouts modernos.

Mas existe uma limitação estrutural.

Quando você cria um grid com, por exemplo, três colunas, tudo funciona bem enquanto a quantidade de itens preenche as linhas completas.

O problema aparece na última linha.

Imagine este cenário:

  • Grid com 3 colunas
  • 7 itens

As duas primeiras linhas ficam perfeitas.
A última linha terá apenas 1 item.

E ele ficará colado à esquerda.

Mesmo configurando:

  • align items
  • justify content
  • justify self
  • align self

Nada resolve.

O motivo é técnico.

O CSS Grid distribui as colunas, não os itens. A última linha não é tratada como um grupo centralizável.

Isso não é um bug do GeneratePress.

É uma característica do Grid.


LEIA TAMBÉM:


Por que isso é um problema sério e Como centralizar grid no GeneratePress

Pode parecer apenas um detalhe visual, mas não é. Vamos os detalhes de como centralizar grid no GeneratePress.

Layouts desalinhados passam sensação de amadorismo.

Usuários não sabem explicar o motivo, mas percebem quando algo está “estranho”.

Além disso, grids são frequentemente usados para:

  • vitrines de produtos
  • materiais educativos
  • portfólios
  • bibliotecas de conteúdo

Ou seja, áreas críticas do site.

Um desalinhamento reduz a taxa de interação.

E experiência ruim impacta SEO indiretamente.


Tentativas iniciais que não funcionaram

Antes da solução definitiva, várias abordagens foram testadas.

Ajustes no próprio Grid

Primeiro tentamos resolver apenas com configurações nativas.

Nada funcionou.

O Grid simplesmente não centraliza a última linha.

JavaScript puro

Depois testamos uma solução 100% automática via JS.

A ideia era detectar grids e aplicar estilos dinâmicos.

Funcionou parcialmente.

Mas gerou efeitos colaterais, incluindo problemas no carregamento de imagens.

Em projetos profissionais, qualquer risco à renderização é inaceitável.

Essa abordagem foi descartada.

Alterar o conteúdo salvo do Gutenberg

Outra tentativa foi modificar o HTML salvo no editor.

Tecnicamente possível.

Mas perigoso.

O Gutenberg valida blocos. Alterar a estrutura pode gerar blocos inválidos no futuro.

Para um site escalável, essa não era uma opção segura.

Precisávamos de algo mais elegante.


A virada técnica do projeto

A solução surgiu quando mudamos a pergunta principal.

Em vez de tentar fazer o Grid se comportar como queríamos, decidimos usar a ferramenta certa para o comportamento desejado.

E aqui está o ponto-chave:

Flexbox é superior ao Grid para distribuição dinâmica de itens.

Principalmente quando precisamos centralizar linhas incompletas.

O que fizemos foi simples na teoria e poderoso na prática.

Criamos um sistema que:

  • Detecta grids do GenerateBlocks
  • Extrai o número de colunas automaticamente
  • Converte o layout para Flex apenas na renderização
  • Mantém gaps e responsividade
  • Não altera o editor
  • Não polui o banco de dados

Ou seja, o melhor dos dois mundos.


Fase 1 – Identificação inteligente dos grids

O primeiro passo foi criar uma lógica capaz de identificar quando um bloco realmente era um grid.

Para isso usamos o filtro:

render_block

Essa abordagem garante que nada seja alterado no conteúdo salvo.

A modificação ocorre apenas na saída do HTML.

Mais seguro. Mais profissional.

O que a função analisa:

  • Se o bloco é do GenerateBlocks
  • Se possui display grid
  • Se utiliza repeat() nas colunas
  • Se tem um uniqueId válido

Somente quando tudo isso é verdadeiro a transformação acontece.

/* Fase 1 - Identificação inteligente dos grids (detecção + gatilhos)
   - Detecta bloco generateblocks/element
   - Confere display:grid
   - Valida uniqueId
   - Extrai colunas repeat(n, ...)
*/

function iedu_gb_extract_repeat_cols($template) {
  if (!is_string($template) || $template === '') {
    return 0;
  }

  if (preg_match('/repeat\(\s*(\d+)\s*,/i', $template, $m)) {
    return (int) $m[1];
  }

  return 0;
}

add_filter('render_block', function ($block_content, $block) {
  if (empty($block['blockName']) || $block['blockName'] !== 'generateblocks/element') {
    return $block_content;
  }

  $attrs  = $block['attrs'] ?? [];
  $styles = $attrs['styles'] ?? [];

  if (($styles['display'] ?? '') !== 'grid') {
    return $block_content;
  }

  $unique_id = $attrs['uniqueId'] ?? '';
  if ($unique_id === '') {
    return $block_content;
  }

  // Colunas desktop (repeat N)
  $cols = iedu_gb_extract_repeat_cols($styles['gridTemplateColumns'] ?? '');
  if ($cols <= 0) {
    return $block_content;
  }

  // Continua na Fase 2 e 3...
  return $block_content;
}, 10, 2);

Fase 2 – Extraindo o número real de colunas

Não podíamos usar valores fixos.

Cada grid pode ter:

  • 2 colunas
  • 3 colunas
  • 4 colunas
  • até 6 ou mais

Então criamos uma função que lê o:

repeat(3, minmax(0, 1fr))

E retorna o número correto.

Isso permite calcular larguras com precisão.

Sem achismos.

/* Fase 2 - Extraindo o numero real de colunas e gaps (dados do layout)
   - Extrai colunas do desktop
   - Extrai colunas do mobile (quando existir)
   - Captura columnGap e rowGap
   - Normaliza valores
*/

function iedu_gb_norm_gap($gap) {
  if (!is_string($gap)) {
    return '';
  }
  $gap = trim($gap);
  return $gap;
}

// Dentro do mesmo render_block, após $cols estar validado:

  $cols_mobile = 0;
  if (!empty($styles['@media (max-width:767px)']['gridTemplateColumns'])) {
    $cols_mobile = iedu_gb_extract_repeat_cols($styles['@media (max-width:767px)']['gridTemplateColumns']);
  }
  if ($cols_mobile <= 0) {
    $cols_mobile = 2; // fallback razoavel
  }

  $col_gap = iedu_gb_norm_gap($styles['columnGap'] ?? '');
  $row_gap = iedu_gb_norm_gap($styles['rowGap'] ?? '');

Fase 3 – Injeção automática de classe e variáveis

Depois da detecção, injetamos:

.gb-grid-center-media

Junto com variáveis CSS como:

  • –gb-cols
  • –gb-cols-mobile
  • –gb-col-gap
  • –gb-row-gap

Essas variáveis tornam o layout totalmente dinâmico.

Nada fica hardcoded.

/* Fase 3 - Injecao automatica de classe e variaveis CSS (aplicacao)
   - Monta as CSS variables
   - Injeta classe gb-grid-center-media
   - Injeta style com as variaveis no wrapper gb-element-<uniqueId>
   - Retorna o HTML modificado no frontend
*/

function iedu_gb_inject_class_and_vars($html, $unique_id, $vars) {
  if (!is_string($html) || $html === '' || !is_string($unique_id) || $unique_id === '') {
    return $html;
  }

  $class_token = 'gb-element-' . $unique_id;

  $style_vars = '';
  foreach ($vars as $k => $v) {
    if ($v === '' || $v === null) continue;
    $style_vars .= $k . ':' . $v . ';';
  }

  $pattern = '/<div\s+([^>]*class="[^"]*' . preg_quote($class_token, '/') . '[^"]*"[^>]*)>/i';

  return preg_replace_callback($pattern, function($m) use ($style_vars) {
    $tag_attrs = $m[1];

    if (stripos($tag_attrs, 'gb-grid-center-media') === false) {
      $tag_attrs = preg_replace('/class="([^"]*)"/i', 'class="$1 gb-grid-center-media"', $tag_attrs, 1);
    }

    if ($style_vars !== '') {
      if (preg_match('/\sstyle="([^"]*)"/i', $tag_attrs)) {
        $tag_attrs = preg_replace('/\sstyle="([^"]*)"/i', ' style="$1 ' . $style_vars . '"', $tag_attrs, 1);
      } else {
        $tag_attrs .= ' style="' . $style_vars . '"';
      }
    }

    return '<div ' . $tag_attrs . '>';
  }, $html, 1);
}

// Dentro do mesmo render_block, após calcular cols/gaps:

  $vars = [
    '--gb-cols' => (string) $cols,
    '--gb-cols-mobile' => (string) $cols_mobile,
  ];

  if ($col_gap !== '') $vars['--gb-col-gap'] = $col_gap;
  if ($row_gap !== '') $vars['--gb-row-gap'] = $row_gap;

  return iedu_gb_inject_class_and_vars($block_content, $unique_id, $vars);

Fase 4 – Convertendo Grid em Flex com inteligência

Agora entra a parte que realmente resolve o problema.

Substituímos o display grid por flex.

Mas apenas na renderização.

O editor continua intacto.

Isso evita conflitos com o Gutenberg.

O Flex permite:

  • quebrar linhas automaticamente
  • centralizar cada linha
  • manter proporções
  • respeitar gaps

E o mais importante:

centralizar perfeitamente a última linha.

/* =========================================================
   GenerateBlocks Grid -> Flex Gallery (dinamico por variaveis)
   Requer: .gb-grid-center-media + variaveis --gb-cols etc.
   ========================================================= */

.gb-grid-center-media {
  display: flex !important;
  flex-wrap: wrap;
  justify-content: center;

  /* Mantem gap do grid original (row/column) via variaveis */
  gap: var(--gb-row-gap, 1em) var(--gb-col-gap, 1em);
}

/* Itens do flex: largura dinamica por colunas */
.gb-grid-center-media > * {
  box-sizing: border-box;
  min-width: 0;

  /* Calcula largura: (100% - (cols-1)*gap) / cols */
  flex: 0 0 calc(
    (100% - ((var(--gb-cols, 3) - 1) * var(--gb-col-gap, 1em)))
    / var(--gb-cols, 3)
  );

  max-width: calc(
    (100% - ((var(--gb-cols, 3) - 1) * var(--gb-col-gap, 1em)))
    / var(--gb-cols, 3)
  );
}

/* Mobile: usa colunas mobile se existir */
@media (max-width: 767px) {
  .gb-grid-center-media > * {
    flex: 0 0 calc(
      (100% - ((var(--gb-cols-mobile, 2) - 1) * var(--gb-col-gap, 1em)))
      / var(--gb-cols-mobile, 2)
    );

    max-width: calc(
      (100% - ((var(--gb-cols-mobile, 2) - 1) * var(--gb-col-gap, 1em)))
      / var(--gb-cols-mobile, 2)
    );
  }
}

/* Centralizacao interna (imagens/figures) */
.gb-grid-center-media figure {
  margin-left: auto;
  margin-right: auto;
}

.gb-grid-center-media img {
  display: block;
  max-width: 100%;
  height: auto;
}

.gb-grid-center-media figcaption {
  text-align: center;
}

/* Opcional: se algum texto entrar como filho direto, evita bagunca */
.gb-grid-center-media > :is(
  p, h1, h2, h3, h4, h5, h6,
  ul, ol, li, blockquote, pre
) {
  flex: 0 0 100%;
  max-width: 100%;
}

O resultado prático

Com essa arquitetura, todos os cenários funcionam:

  • Grid com 2 colunas → itens a 50%
  • Grid com 3 colunas → itens a 33%
  • Grid com 4 colunas → itens a 25%
  • Grid com 5 ou 6 → tudo calculado automaticamente

Se a última linha tiver:

  • 1 item → fica centralizado
  • 2 itens → centralizados
  • 3 itens → centralizados

Sem qualquer configuração manual.

Zero intervenção do editor.


Por que essa solução é extremamente segura

Existem três motivos principais.

Não altera o banco de dados

Nada é salvo com modificações.

Isso reduz risco técnico.


Não depende de JavaScript no frontend

Menos scripts.

Mais performance.

Melhor Core Web Vitals.


É escalável

Criou um grid novo?

Ele já funciona automaticamente.

Sem classes extras.

Sem lembrar configurações.


Impacto na experiência do usuário

Depois da implementação, o layout ficou visualmente mais equilibrado.

O site passou a transmitir maior sensação de organização.

Isso aumenta:

  • tempo de permanência
  • percepção de autoridade
  • confiança

Fatores que influenciam comportamento.

E comportamento influencia SEO.


Uma lição importante sobre arquitetura

Este projeto reforça um princípio essencial do frontend moderno:

não force uma tecnologia a fazer o que ela não foi feita para fazer.

Grid é excelente para estrutura.

Flex é excelente para distribuição.

Quando combinados com inteligência, o resultado é superior.


Quando usar essa solução

Ela é ideal para sites que utilizam GeneratePress com:

  • bibliotecas de materiais
  • grids educacionais
  • vitrines
  • páginas de recursos
  • portfólios
  • blogs ricos em mídia

Principalmente quando o número de itens varia muito.


Quando NÃO usar

Evite aplicar em grids estruturais.

Exemplo:

Layouts complexos com áreas nomeadas.

Ali o Grid deve permanecer.

Nossa solução foi pensada para grids de mídia e containers repetitivos.


Centralizar grid no GeneratePress parecia apenas um ajuste visual. Mas este estudo mostrou que o verdadeiro ganho está em entender a tecnologia e aplicar a solução correta.

Em vez de forçar o CSS Grid além do seu propósito, utilizamos uma abordagem mais estratégica. Mantivemos a estrutura do Grid e aplicamos o Flexbox na renderização para garantir que a última linha ficasse sempre centralizada.

O resultado foi uma solução automática, leve, escalável e segura. Sem plugins, sem scripts pesados e sem alterar o editor. Uma melhoria técnica que eleva imediatamente a percepção de qualidade do layout.

E aqui fica um ponto importante: construir soluções assim só é possível quando o tema oferece uma base realmente bem estruturada. O GeneratePress se destaca justamente por permitir esse nível de controle sem comprometer performance.

Se você leva velocidade, organização e escalabilidade a sério, talvez seja hora de repensar o tema que está usando hoje.

👉 Conheça o GeneratePress e veja por que tantos desenvolvedores profissionais não abrem mão dele.

Depois que você experimenta essa flexibilidade, fica difícil voltar para qualquer outro tema.

Deixe um comentário