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.cssconté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:
- Faça o build da aplicação com a toolchain dela, por exemplo Vite, numa pasta dentro de
ext/static/. Os arquivos emext/static/são servidos a partir da raiz do site, entãoext/static/planner/main.jsé/planner/main.js. - Monte-a a partir de uma rota: gere o elemento em que ela é montada e carregue o build.
- 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.