Handleidingen

Frontendstack

De standaardopzet met Tailwind CSS, DaisyUI en Alpine.js, en React, Vue of elke andere toolset.

Nimbly maakt HTML op de server en laat de browserkant aan jou over. Een project krijgt standaard Tailwind CSS, DaisyUI en Alpine.js, en de admin is daarmee gebouwd. Je eigen routes kunnen ze gebruiken, aanvullen of een heel andere toolset gebruiken.

De standaardstack

Tool Gebruikt voor
Tailwind CSS 4 Utility-classes in templates
DaisyUI 5 Componentclasses en thema's
Alpine.js Interactie, direct in de markup

./nimbly build zoekt in de templates van core/ en ext/ naar classes en bouwt ext/static/app.css en ext/static/app.js. ./nimbly watch houdt ze bij terwijl je bewerkt.

  • Thema. Kleuren en DaisyUI-thema's staan in ext/tailwind.theme.js. De admin en de Nimbly-balk gebruiken dezelfde tokens, dus de bewerktools volgen de huisstijl.
  • Styles. ext/theme.css bevat de eigen CSS van het project, ingeladen na de styles van het framework zodat die van jou winnen.
  • Classes in variabelen. Tailwind vindt classes door de broncode te lezen. Zet een classlijst die in een variabele staat tussen aanhalingstekens, [#set menu-text="text-stone-500"#], zodat de classes als tekst in de template staan.

Markup van de server met Alpine

Alpine voegt gedrag toe aan markup die de server al heeft gemaakt. Houd content in HTML als de browser die alleen toont, verbergt, sorteert of filtert, en geef Alpine de paar waarden die het nodig heeft:

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

Bouwt de browser de interface op uit data, geef de records dan als JSON mee in een scriptblok; dat is veilig voor elke tekst in de records:

<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>

Laat de pagina bij grote of afgeschermde datasets de API om één pagina tegelijk vragen, in plaats van alles in te bedden.

Een library op één pagina

Laad een library op de route die hem nodig heeft. Het projectenvoorbeeld zet zijn kaart met Leaflet op één pagina:

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

en laadt het script met de eigen index.js van de route, of een <script> in de main.tpl. Andere pagina's laden het nooit.

React, Vue of een ander framework

Een route kan een applicatie bevatten die met elk framework is geschreven. Het patroon is voor elk hetzelfde:

  1. Bouw de applicatie met zijn eigen toolchain, bijvoorbeeld Vite, naar een map onder ext/static/. Bestanden in ext/static/ worden vanaf de root van de site geleverd, dus ext/static/planner/main.js is /planner/main.js.
  2. Mount hem vanuit een route: maak het element waarop hij mount, en laad de build.
  3. Geef data mee in de pagina, of laat de applicatie de API aanroepen.

Een route die de standaardshell houdt, ext/uri/planner/index.tpl:

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

en de main.tpl ervan:

[#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>

De applicatie leest zijn startdata uit het scriptblok:

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} />);

In de browser kan de applicatie via de REST API lezen en schrijven met de sessie van de ingelogde gebruiker, met dezelfde rechten als in de 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 wordt door de shell gezet. Schrijf op een route met een eigen document het basisadres in de pagina met [#base-url#].

Houd de broncode en toolchain van de applicatie in een eigen map binnen ext/, zoals ext/planner/ met een eigen package.json, en commit of deploy de build-uitvoer op dezelfde manier als app.css.

Een andere stack voor de hele site

Wil je op elke publieke pagina een ander CSS-framework of helemaal geen framework, geef elke route dan een eigen document, of vervang delen van de pagina-shell in ext/tpl/html/. De admin gebruikt dezelfde shell en heeft app.css en app.js nodig, dus zet je eigen assets ernaast in plaats van ze weg te halen. Zie Je eigen HTML.

Verder lezen

Bekijk deze pagina als Markdown