Guias

Stack de frontend

A configuração padrão com Tailwind CSS, DaisyUI e Alpine.js, e React, Vue ou qualquer outro conjunto de ferramentas.

O Nimbly gera HTML no servidor e deixa o lado do navegador com você. Um projeto já vem com Tailwind CSS, DaisyUI e Alpine.js configurados, e o admin é construído com eles. Suas próprias rotas podem usá-los, acrescentar a eles ou usar um conjunto de ferramentas totalmente diferente.

A stack padrão

Ferramenta Usada para
Tailwind CSS 4 Classes utilitárias nos templates
DaisyUI 5 Classes de componentes e temas
Alpine.js Interação, direto no markup

O ./nimbly build procura classes nos templates de core/ e ext/ e gera ext/static/app.css e ext/static/app.js. O ./nimbly watch os mantém atualizados enquanto você edita.

  • Tema. Cores e temas do DaisyUI ficam em ext/tailwind.theme.js. O admin e a barra do Nimbly usam os mesmos tokens, então as ferramentas de edição seguem a marca.
  • Estilos. ext/theme.css contém o CSS próprio do projeto, importado depois dos estilos do framework para prevalecer.
  • Classes em variáveis. O Tailwind encontra classes lendo o código-fonte. Coloque entre aspas uma lista de classes guardada numa variável, [#set menu-text="text-stone-500"#], para que as classes apareçam como texto no template.

Markup do servidor com Alpine

O Alpine acrescenta comportamento a um markup que o servidor já gerou. Mantenha o conteúdo em HTML quando o navegador só o mostra, esconde, ordena ou filtra, e passe ao Alpine os poucos valores de que ele precisa:

<article data-sector="[#item.sector#]" x-show="sector === 'all' || sector === $el.dataset.sector">
  [#get-html item.description#]
</article>

Quando o navegador monta a interface a partir de dados, passe os registros como JSON num bloco de script, o que é seguro para qualquer texto dos registros:

<script type="application/json" id="projects-data">[#fmt var=data.projects empty={} type=json#]</script>
<div x-data="{ projects: Object.values(JSON.parse(document.getElementById('projects-data').textContent)) }">
  <template x-for="project in projects" :key="project.uuid">
    <h3 x-text="project.title"></h3>
  </template>
</div>

Para conjuntos de dados grandes ou restritos, deixe a página pedir à API uma página de cada vez, em vez de embutir tudo.

Uma biblioteca numa página

Carregue uma biblioteca na rota que precisa dela. O exemplo de projetos coloca seu mapa numa página com Leaflet:

[#set head="<link rel='stylesheet' href='https://unpkg.com/leaflet@1.9.4/dist/leaflet.css'>"#]
[#html#]

e carrega o script com o index.js próprio da rota, ou com um <script> no main.tpl dela. As outras páginas nunca o carregam.

React, Vue ou outro framework

Uma rota pode hospedar uma aplicação escrita com qualquer framework. O padrão é o mesmo para todos:

  1. Faça o build da aplicação com a toolchain dela, por exemplo Vite, numa pasta dentro de ext/static/. Os arquivos em ext/static/ são servidos a partir da raiz do site, então ext/static/planner/main.js é /planner/main.js.
  2. Monte-a a partir de uma rota: gere o elemento em que ela é montada e carregue o build.
  3. Passe os dados na página, ou deixe a aplicação chamar a API.

Uma rota que mantém a shell padrão, ext/uri/planner/index.tpl:

[#set page-title="Planner"#]
[#set head="<link rel='stylesheet' href='[#base-url#]/planner/main.css'>"#]
[#html#]

e o main.tpl dela:

[#data projects filter=published:1#]
<div id="planner"></div>
<script type="application/json" id="planner-data">[#fmt var=data.projects empty={} type=json#]</script>
<script type="module" src="[#base-url#]/planner/main.js"></script>

A aplicação lê seus dados iniciais do bloco de script:

import { createRoot } from 'react-dom/client';
import Planner from './Planner';

const projects = Object.values(JSON.parse(document.getElementById('planner-data').textContent));
createRoot(document.getElementById('planner')).render(<Planner projects={projects} />);

No navegador, a aplicação pode ler e gravar pela API REST com a sessão do usuário autenticado, com as mesmas permissões do admin:

const response = await fetch(`${window.nb.base_url}/api/v1/projects?limit=20`, { credentials: 'same-origin' });
const { projects, total } = await response.json();

window.nb.base_url é definido pela shell. Numa rota com documento próprio, escreva o endereço base na página com [#base-url#].

Mantenha o código-fonte e a toolchain da aplicação numa pasta própria dentro de ext/, como ext/planner/ com seu próprio package.json, e faça commit ou deploy do resultado do build do mesmo jeito que o app.css.

Uma stack diferente para o site inteiro

Para usar outro framework de CSS, ou nenhum, em todas as páginas públicas, dê a cada rota um documento próprio, ou substitua partes da shell da página em ext/tpl/html/. O admin usa a mesma shell e precisa de app.css e app.js, então acrescente seus assets ao lado deles em vez de removê-los. Veja Seu próprio HTML.

Leia a seguir

Ver esta página em Markdown