Search by

webx-ui / site

Ahabzig

A Laravel application with the WebX UI admin panel on it, ready for the modules the site needs.

Package info

github.com/webx-ui/site

Homepage

Issues

Type:project

pkg:composer/webx-ui/site

Statistics

Installs: 85

Dependents: 0

Suggesters: 0

Stars: 0


README

A Laravel website with the WebX UI admin panel on it: the public pages are this site's theme over a shared one, the content — pages, articles, products, menus, forms — is edited in the panel at /cms, and the modules that do the work are Composer packages webx-ui/* in vendor/.

Two ways to run it

With Docker only — nothing else installed, no experience needed. Follow Docker from zero step by step; it ends with the site on http://localhost:8080 and the panel on http://localhost:8080/cms.

On a machine with PHP 8.4, Composer, Node and MySQL or MariaDB:

composer create-project webx-ui/site example.local

The questions that follow — name, domain, database, languages, modules, the first administrator — are php artisan webx:setup, and it is the same command on a site that is already running: run it again after installing a module and it wires in what is new and leaves the rest alone.

At the end it prints the panel's address, the login and a generated password — and the commands setup leaves to you. Setup writes no block types and seeds no demo: the same command makes staging and production sites, and a block type reaches those only when somebody imports it. On a dev copy, run what it printed, in that order:

php artisan webx:blocks:import --theme --publish   # the theme's block types
php artisan webx:blocks:offered --install          # the blocks the modules offer
php artisan webx:demo                              # demo content, on top of both

Changing it, with an agent or without

  • Where the styles live — which files hold the design, what is safe to change and what is not, how to see a change.
  • AGENTS.md — what an AI agent (Claude Code, Codex, Cursor) reads first. webx:setup writes it and php artisan webx:panel --sync keeps its block current; your own notes about this site go under ## This project, and both it and CLAUDE.md belong in git. Beside them, .mcp.json connects Claude Code opened in this folder to this site's own panel.
  • A new site — everything below, in full.

The first rule, for people and agents alike: never edit vendor/ and never copy a module into the site. Configure it, publish its views, patch its screens — the seams are below.

What to delete first

Everything here is yours, and two things are here only so that the first page is not blank (the demo content only when you seeded it):

what when
resources/views/demo.blade.php and its route as soon as the site has a front page of its own
the demo content php artisan webx:demo --remove

The demo content is rows in the database and files on disk, and the command takes out exactly what it put in, backwards, by a journal in storage/app/webx-demo.json.

The look

webx:setup gives the site a theme of its own, theme/, standing on webx-ui/theme-default: the layout, the header, the footer, the typography and a value for every site token come from there, so the site opens finished. theme/ holds only what makes this site different, and it starts empty:

file what goes in it
theme/tokens.json colours, fonts, radii — "defaults": { "color-accent": "#…" }
theme/src/css/ rules on the classes the shell and the modules print, var(--site-…) only
theme/views/ a copy of a view, only when the markup itself has to change
theme/assets/ pictures of the theme, asked for as theme_asset('img/wave.svg')
theme/tests/ the theme's test, run with the site's own by php artisan test

npm run dev rebuilds theme/src as you type; php artisan webx:theme:sync publishes the packaged theme's files after composer update, and theme/assets/ and theme/icons/ after you add a file there. What it publishes lands in public/themes/ under a hash of its content — kept out of git like public/build, and cached forever by the server: /themes/ and /build/ are immutable in docker/nginx.conf and public/.htaccess; a server of your own needs the same. A view in resources/views/components/ is above every theme layer and wins over it, so leave layout, header and footer out of there.

A site set up with --no-theme has none of this: webx:setup writes the layout, the header and the footer into resources/views/components/ and its styles go in resources/css/app.css.

What not to delete

routes/web.php is empty, and that is the point. Every public address of this site comes from the address registry — a page, an article, a tag — and Route::fallback() answers them. A route declared in web.php wins over the fallback for good, so it takes an address the panel can then never hand out. / costs the most: the home page of the tree is an empty slug formatted to /, and a Route::get('/') here is a home page the panel cannot even save.

The seams

Three places where the site makes the library its own:

The layout. components/layout.blade.php is <x-layout> — the theme's, or the site's own with --no-theme — and every public view a module ships stands inside it. The agreement is two lines long — a head slot and the default slot for the content — and config('webx-pages.layout') and its siblings are what point at it. Keep @stack('head'): a slot is one place, and what a block type pushes cannot reach it. The header and the footer are regions — <x-webx-blocks::region name="header" fallback="components.header" />, declared in config/webx-blocks.php: until somebody publishes blocks there in the panel, the site prints components/header.blade.php from the theme. Without webx-ui/module-blocks the tag prints its fallback and nothing else (AppServiceProvider).

The views. php artisan vendor:publish --tag=webx-blog-views copies a module's public views into resources/views/vendor/, where they win over the package's. Per file, not per directory: keep the one you rewrote and delete the rest, and those keep arriving fresh with each release.

The services. A module's behaviour is replaced through the container, in register() and not in boot(). Where the site adds something beside what a package does rather than instead of it — a route type, a field type, an SEO source — there is a registry to put it in, and that is the usual answer.

Containers

Optional, and yours to delete like everything else here: Dockerfile, the two compose files and docker/. With nothing but Docker on the machine, start from Docker from zero.

docker compose up -d --build                                        # the production shape
docker compose -f docker-compose.yml -f docker-compose.dev.yml up   # somewhere to work

One image — Composer dependencies, the front end built by this site's own Vite, then php-fpm, nginx, a queue worker and the scheduler under supervisor — beside a MariaDB. Build it after webx:setup: both lock files are what the image installs. Nothing here terminates TLS; the container listens on ${APP_BIND}:${APP_PORT} and a proxy in front of it holds the certificate. The site believes that proxy's X-Forwarded-* only from private networks and localhost — config/trustedproxy.php; a proxy elsewhere (a CDN) is named in TRUSTED_PROXIES. The development stack mounts this checkout into the same image, adds the Vite dev server and a Mailpit, and caches nothing.

The variables they read are at the bottom of .env.example, in the same .env as everything else. What a container does between starting and serving is php artisan webx:boot — run it with --pretend to see the list.

Day to day

php artisan webx:panel --sync     # after installing a module: wires it into the panel's build
npm run dev                       # while working on the panel or the design
php artisan webx:doctor           # before a deploy: says what has come loose