Loading...

This is taking longer than expected.

Make it your own

Where your product goes, so an update from the base never fights it.

You will keep merging new versions of Weblabor Base into your project for as long as it lives. Every file that both of you edit is a conflict you resolve again on every merge, so the base keeps what is shared and gives you folders of your own for what is yours. This guide says which folders those are, what goes in each, and how to bring the next version of the base in without losing a line of your product.

The rule behind all of it: anything that belongs to your product lives in a folder named after your product. A file the base ships is a file the base will change.

Name your project once

config/app.php has a project key:

'project' => 'weblabor-base',

Change it to a short slug for your product, your-project in the rest of this guide. It is a committed literal, not an environment variable, on purpose: it is set once, when your project starts, and config/ is yours, so it survives every merge.

The application reads three places from that name:

What Path
Landing page and feature pages resources/views/landing/your-project/
Help guides and changelog docs/your-project/help/
Design components and layouts resources/views/components/your-project/

Nothing in the base's own folders is inherited. landing/weblabor-base/, docs/weblabor-base/ and resources/views/components/weblabor-base/ stay in the repository untouched and unused, so a merge that changes them changes nothing you ship.

What a folder of yours does not cover comes from a generic folder named project, written for any product. The rule is the same for each of them: your folder if it exists, and project if it does not. The help centre reads docs/project/help/ until docs/your-project/help/ exists, which gives a new product guides written for its users, such as how to edit a profile or change a plan. Once you create your own help folder, only yours are read: copy the generic guides you want to keep into it, as /help/write-guides-and-changelog explains. The design components work the same way, one component at a time, as "Your look" below explains.

Your landing page

The home page at / renders resources/views/landing/your-project/home.blade.php inside the public layout. Until resources/views/landing/your-project/ exists, the landing follows the same rule as the rest: the home page is the generic one in resources/views/landing/project/, with your application's name, a neutral line and the buttons to sign in and register, and /features shows its empty state. Once you create your folder, it needs its own home.blade.php: without it the home page answers with an error naming the file to create. The base's own home is in resources/views/landing/weblabor-base/home.blade.php; copy it as a starting point, then rewrite it.

resources/views/landing/your-project/identity.php is optional and says who is behind the product, for the header and the footer:

return [
    'logo' => 'images/landing/your-project/logo.png',
    'contact' => 'https://wa.me/5215550000000',
    'about' => '#about',
];

logo is your logo with its name, a path under public/, for the footer; without it the footer prints your application's name. The header does not read this file: it shows the logo of your brand, the logo of resources/brand/your-project.php or of config/app.php (its icon when there is no logo), whole and at the same size on every screen. On a phone it takes the first row and the menu button, the language switcher and the sign-in and register buttons go to a second, so nothing has to shrink; only a product with neither image shows its name there. contact is where the footer's Contact link leads; without it the footer shows no Contact, since there is nowhere to send it. about is where the footer's About link leads, under the same rule: without it there is no About. A section of your home page, such as #about, is reached from every public page, not only from the home page; a full address is followed as it is. Read either in your own views with App\Classes\Landing::identity('contact').

resources/views/landing/your-project/demo.blade.php is optional too: when it exists it is served at /demo, public, as the page that invites a visitor to register and try the product; without it /demo is not found. Link it from your nav-links.

The public navigation shows Help, preceded by Features once features.php lists at least one page (until then it would lead to an empty page), then whatever resources/views/landing/your-project/nav-links.blade.php prints. The file is optional. Put there the anchors and pages your landing actually has, so the menu never points at a section that does not exist. The header prints the file twice, in its bar and in the menu its button opens on a phone, so write only the links and no layout around them; whatever conditions they carry hold in both:

<a class="text-default-500 hover:text-default-900 transition-colors" href="#about">
    {{ __('About') }}
</a>

Images for the landing go under public/images/landing/your-project/. The header and the footer are the design components public-header and public-footer: to change them, add your own version to your design folder, as "Your look" below explains, instead of copying the layouts/web.blade.php frame.

Your feature pages

/features lists what your product sells, one page per feature. The list is resources/views/landing/your-project/features.php, an array of entries:

return [
    [
        'slug' => 'your-account',
        'title' => 'Your account',
        'summary' => 'Keep your details and your preferences the way you want them.',
        'icon' => 'user-circle',
        'image' => 'images/landing/your-project/features/your-account/profile.png',
        'order' => 20,
    ],
];

Each entry is a declaration: its page is the Blade view named after its slug, resources/views/landing/your-project/features/your-account.blade.php, and an entry whose view was never written is left out of the listing. Pages are built from three components, <x-design::landing-hero>, <x-design::landing-block> and <x-design::landing-cta>, and their screenshots live in public/images/landing/your-project/features/{slug}/.

Write them as sales pages, not documentation: lead with what the reader gets, one or two sentences each, second person, present tense. The manifest keys, the components and their attributes are in Write guides and the changelog.

Your guides and your changelog

The help centre at /help reads Markdown from your own folder:

docs/your-project/help/
├── guides/
│   ├── get-it-running.md
│   └── es/
│       └── get-it-running.md
└── changelog/
    ├── 2026-09.md
    └── es/
        └── 2026-09.md

A guide is one file per slug, in English, with a front matter of title, summary, category and order. Its translation is the same file name inside a folder named after the locale, one folder per language in config/app.php other than English. A guide with no translation does not exist in that language: it is left out of its lists and its address answers 404, so translate it before you publish it. The changelog is one file per month.

The categories a guide can declare are in config/help.php, with their English labels. A category no guide uses is hidden, so replace the list with the categories your reader needs and nothing shows empty.

Translations carry a fingerprint of the English they were written from, so a guide whose English moved on can be named. Two commands maintain it:

php artisan help:stamp --check   # lists missing, stale and orphaned translations
php artisan help:stamp           # rewrites the fingerprint on every translation

composer install points Git at scripts/git-hooks, whose pre-push hook runs the check on the help documents the push changes, as they stand in its commits, and stops a push that would ship a stale one. A document already broken on the branch does not stop it, and what is not in a commit does not count. Skip it once with git push --no-verify. The file formats, the changelog entries and how the pages are rendered are in Write guides and the changelog.

Your brief

Your brief says what your product is, what it promises, who it speaks to, the tone, and what it never claims. It is not rendered anywhere: it is what keeps the landing copy, the feature pages and the guides consistent when several people write them. Write it before the copy, not after.

It is a working document, so it does not live in the repository. It lives in your project in the Weblabor toolkit, as identity/product.md, next to your identity manual, and you write and read it with the toolkit's document tools. That is where /design-catalog looks for it and uses it. The base's own brief is identity/product.md in the weblabor-base project of the toolkit and is a good template.

Your look

Every screen draws its frame and its repeated pieces from design components, called with a neutral name: <x-design::page-header>, <x-design::surface>, <x-design::auth-page> and the rest. The name never says which product it belongs to. It is looked up in layers: first in resources/views/components/your-project/, then in resources/views/components/project/, which holds the generic look every product starts with. The first folder that has the component wins.

So you change the look by adding files, not by editing the generic ones. Copy a component from resources/views/components/project/ into your folder under the same file name, keep its attributes and slots, and change its markup. Every screen that calls it, the sign-in screens, the signed-in area, the layouts and the billing pages included, follows. A component you never copy keeps the generic look.

The record screens of the admin, the list, the detail and the create and edit forms that Laravel Front draws for every resource, follow your layer too. The base replaces the package's views with copies in resources/views/vendor/front/ that call design components such as page-header, panel, action-button and dialog, so a component you redefine reaches those screens without touching the copies. Leave the copies alone and change the component instead.

The rest of the screens follow it as well: the file manager, the notification and announcement bells and the account menu, the profile, the subscription, plans and add-ons, the file upload field, the support chats, the data space, the page links of every list and the cards of the admin. Only the mail templates, the sitemap, the offline page and the admin design screen stay drawn by hand.

The layouts follow the same rule. The five frames, layouts/base, layouts/app, layouts/web, layouts/auth and layouts/help, the sidebars in layouts/sidebars/ and the partials in layouts/partials/ are looked up in a layouts/ folder inside each design folder: resources/views/components/your-project/layouts/ first, then resources/views/components/project/layouts/, which holds the generic ones. Copy into your folder, under the same path, only the layouts you change; the rest keep coming from the generic folder. Every screen keeps calling them by the same name, such as layouts.app or layouts.sidebars.admin, so nothing that uses them changes.

The signed-in frame picks its sidebar from the first segment of the address: layouts/sidebars/admin.blade.php under /admin, layouts/sidebars/account.blade.php under /account. To change the menu of one section, copy only that sidebar. To give a section of your own its menu, add a sidebar named after it, such as layouts/sidebars/reports.blade.php for /reports; a section no folder has a sidebar for shows none. A frame or partial that no folder has fails with an error naming it.

Weblabor Base has its own folder too, resources/views/components/weblabor-base/, where the base redefines a component for itself alone: its public header and footer, its landing pieces and the links of its header. Your project does not read it, so the base can change its own look without changing yours. Every component the base's own pages call also exists in resources/views/components/project/, so php artisan view:cache compiles those pages in your project too, even though it never shows them.

A product built on another product, such as one built on Weblabor Teams, lists the whole chain in config/app.php:

'design_layers' => ['your-project', 'weblabor-teams'],

Left empty, the chain is your project and then project. A product that wants the look of Weblabor Base lists weblabor-base in the chain. project is always searched last. A component that no layer has fails with an error naming it, instead of drawing nothing.

Your palette and your font follow the same rule: resources/css/themes/your-project.css when it exists, looked up along the same chain, and the generic resources/css/themes/project.css when it does not. The other tokens, the colours of success, error, warning and information, stay in resources/css/app.css, shared by every product, as Brand and interface explains. A super administrator sees every design component in use, in each of its states, and which layer each one comes from, at /admin/design.

What else is yours

config/ belongs to the derived project. That is where you set the super administrators, the logo and the icon, the languages, the feature flags, the help categories and the PWA manifest, and a merge from the base never needs to overwrite them. When the base adds a new key to a config file you will see it as a conflict in that file: keep your values and take the new key.

The files behind the brand, public/images/logo.svg, public/images/icon.svg and the generated public/images/icons/, are yours to replace. .env is never committed. Your migrations, models, Livewire components and views are new files that merge cleanly as long as you add rather than edit.

Two things are shared and worth knowing about. lang/en.json and lang/es.json hold every on-screen string of the base and of your product together, so both sides add lines on every merge; when they conflict, keep both sides. And the generic layouts under resources/views/components/project/layouts/ are shared: change a layout by copying it into resources/views/components/your-project/layouts/, as "Your look" explains, never by editing the generic one.

If your product edited the layouts when they still lived in resources/views/layouts/, move your versions once into resources/views/components/your-project/layouts/, under the same path. The merge that brings the move conflicts on those files that one time; after it, the base's changes to its layouts merge without touching yours.

Keep merging the base

Your repository starts as a copy of Weblabor Base. Add the base as a second remote and merge its main branch whenever a version you want is out:

git remote add upstream https://gitlab.weblabor.mx/weblabormx/proyectos-internos/sistemas-base/weblabor-base.git
git fetch upstream
git merge upstream/master

After the merge, update what a release changes:

composer install
npm ci && npm run build
php artisan migrate
php artisan db:seed

The seeders are safe to re-run: they create what is missing and re-sync the admin role with every permission, and never overwrite a password.

Conflicts you should expect, and how to close them:

File Why it conflicts Resolution
config/*.php The base added or renamed a key Keep your values, take the new key
lang/*.json Both sides added strings Keep both sides
composer.lock, package-lock.json Both sides changed dependencies Take the base's file, run the install, then composer require your own packages again
resources/views/layouts/* You edited a layout before the layouts moved to the design folders Once: move your version to resources/views/components/your-project/layouts/ under the same path, and take the base's deletion

Everything under resources/views/landing/your-project/, resources/views/components/your-project/, docs/your-project/ and config/ never conflicts, because the base does not touch it. The more of your product lives there, the shorter every merge is.

Back