Setup & basics

Flat Cake is a lightweight static-site framework built on CakePHP 5. It gives you automatic routing, templating, and an asset pipeline without the overhead of a database or CMS. Create a PHP file, visit the URL — done.

PHP 8.2+, Composer, and Node.js 18+ for the asset pipeline. Run composer install, npm install, npm run build, and start the dev server. That's it.

No. Flat Cake is designed for static sites that don't need a database. If a page needs dynamic data, you can call an API or query a database from a controller method — but it's entirely optional.

Two commands, complementary roles:

  • bin/setup-client.sh — one-shot bash wizard. Verifies the upstream remote, collects client metadata interactively, generates CLAUDE.local.md from the template, copies config/{app_local,panel}.example.php into place, and offers to remove the demo content chain. Run it once when you fork.
  • bin/cake fork_init — idempotent Cake CLI. Strips every piece of upstream demo content (demo Pages, demo SCSS/JS, demo gallery folders with their thumb/ caches and .webp renditions) and writes back minimal starter templates so the build keeps compiling. Safe to re-run any time — useful after an upstream sync brings new demo pages, or when you want a clean baseline mid-development. Prints the deletion plan first; pass --force to skip the confirmation prompt.

The two-tool split is on purpose: setup-client.sh handles things you do once (per-client metadata, env files, brand colour). fork_init handles things you might need repeatedly (cleanup against upstream drift).

Working with pages

Create a .php file in templates/Pages/. The URL is derived from the filename automatically — pricing.php becomes /pricing. The page also appears in the navigation without any configuration.

Create a subdirectory in templates/Pages/. For example, demos/gallery.php maps to /demos/gallery. If a parent file exists (e.g. demos.php), the subdirectory items become dropdown children in the navigation.

Add a public method to PagesController named after the camelCase URL slug. For /pricing it's pricing(); for /demos/api-posts it's demosApiPosts(). The method runs automatically before rendering.

Yes. Template resolution is case-insensitive. Uppercase letters in filenames are preserved in navigation labels — API_posts.php shows as "API Posts" in the menu while the URL stays lowercase (/demos/api-posts).

Yes. Prefix the filename with a number and underscore — 20_about.php lands at position 20. The prefix is purely a filesystem convention; it's stripped from the label ("About") and the URL (/about), and the URL resolver accepts either about.php or NN_about.php. Numbered items come first (ascending), unnumbered fall back to alphabetical. home.php is always first and contact.php always last. See the docs for the full rule.

FlatPanel

Yes — the FlatPanel plugin mounts a Monaco-powered editor at /panel. Edit any file under templates/Pages/, templates/layout/, or templates/element/ straight from the browser, with php -l syntax validation before every save. No SSH needed. Full reference at /docs/flat-panel.

Copy config/panel.example.php to config/panel.php, then generate a bcrypt password hash with the built-in command — the password is asked for interactively so it never lands in shell history:

cp config/panel.example.php config/panel.php
bin/cake hash_password yourusername
# paste the printed line into FlatPanel.users, visit /panel

Yes. FlatPanel.users accepts any number of entries — run bin/cake hash_password once per person and add each returned line under users in config/panel.php. Everyone shares the same permissions; the sign-in rate limit is per IP (5 failures → 15-minute lock).

Yes — while editing a file under templates/Pages/, the eye button in the top header opens a side-by-side live preview. The Monaco editor slides to 50% width and the rendered page slides in from the right. The preview reloads automatically after every save, so you immediately see the result of your changes. The neighbouring Preview ↗ button opens the same page in a new tab.

Every save, rename and delete drops a timestamped copy into tmp/panel_backups/ before touching the live file. The Backups button on the dashboard opens a read-only view of every stored version with a one-click restore. Restoring over an existing file backs up the current version first, so you can roll forward again if needed.

Theming & deployment

The entire palette derives from a single CSS custom property --cb-brand. Change it at runtime and all derived colors (dark, light, wash, surface, border) update instantly via color-mix(). The value is stored in localStorage and a cookie for persistence.

Yes. Click the moon/sun icon in the navigation bar. The preference persists in localStorage and syncs across tabs. An inline script in <head> restores it before first paint — no flash.

Run npm run build and composer install --no-dev --optimize-autoloader. Point the document root to webroot/. Set App.fullBaseUrl in config/app_local.php. No Node.js runtime is needed in production.

Yes. Any hosting with PHP 8.2+ works. Build assets locally, upload the project, and configure the document root. Flat Cake has no runtime dependencies beyond PHP and Composer's autoloader.

Only functional ones — flat-cake-theme for your accent colour and CAKEPHP for CSRF on the contact form. Both are exempt from GDPR consent (art. 5(3) ePrivacy). A subtle bar on the first visit informs visitors and stores the analytics decision in flat-cake-consent; the full list with persistent Accept / Reject controls lives at /cookies.

Drop the original files into webroot/img/gallery/ (one folder per gallery if you like). In templates, reference them via the Gallery helper:

<img src="<?= $this->Gallery->thumb('lazienka/photo.jpg', 800) ?>"
     data-lightbox-src="<?= $this->Gallery->image('lazienka/photo.jpg') ?>"
     data-lightbox="lazienka" alt="Bathroom">

Thumbnails (whitelisted sizes: 400, 800, 1920 px) are generated on the first request and cached on disk next to the source — every later request bypasses PHP entirely. Originals smaller than the requested size are never upscaled. A WebP rendition is generated alongside each JPEG/PNG when the image engine supports it, and $this->Gallery->thumb() already returns the WebP URL by default for raster sources — the snippet above is PageSpeed-friendly without any extra arguments. Pair with the Gallery Cell (auto-emits <picture> with a WebP <source>) or thumbSrcset() for responsive images. See the image pipeline docs for configuration, the offline batch, and the 'raw' opt-out for <picture> fallbacks.

No — the controller generates each thumb on its first request, so a fresh deploy just works. If you want to pre-warm them (or you've uploaded a large batch and want the first visitors to be fast), run bin/cake thumbs. It warms every size and adds a .webp sibling when the engine supports WebP (pass --no-webp to skip), shows per-folder progress as it goes, and is idempotent — every up-to-date size is skipped. To invalidate the cache after changing Thumbs.sizes or Thumbs.quality, run bin/cake thumbs --wipe: it deletes every thumb/ subdirectory without touching the sources, so the next warm-up rebuilds from scratch. Needs the PHP imagick or gd extension — Imagick is preferred, GD is the fallback. Without either, the controller serves a placeholder.

Set FlatCake.gaId in config/app.php (or app_local.php) to a G-XXXXXXXXXX measurement ID. The loader in templates/element/head.php validates the format, anonymises IPs, and only injects the GA snippet after the visitor accepts the consent bar. Without a configured ID, no GA code is emitted. Other scripts can hook into the same gate by listening for cookie-consent:granted on window.

Still have questions?

Check the full documentation or reach out directly.

Documentation Contact us