Concepts Overview
Loopar is a meta-framework β a framework that can build itself. At its core lies a powerful concept: Entity, the master builder that creates other builders.
The Philosophy
Traditional frameworks require you to write code for every model, view, and controller. Loopar inverts this: you define what you want, and the framework generates everything else.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β THE LOOPAR WAY β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Traditional: Code β Application β
β β
β Loopar: Definition β Code β Application β
β β β
β (drag & drop) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Core Concepts
| Concept | Description |
|---|---|
| Entity | The master builder β defines data models and generates everything |
| Builder | Specialized entity types (Page, View, Form, Controller) |
| App | A self-contained application package |
| Module | Groups related entities within an app |
| Document | A data-centric entity (stored in database) |
| Page | A presentation-centric entity (web pages) |
| Component | UI elements used in drag-and-drop design |
The Auto-Recursive Magic
What makes Loopar unique is that Entity is built using Entity. The system bootstraps itself:
- Entity defines the structure of all models
- Entity creates Builders (specialized entity types)
- Builders create specific model types (Pages, Forms, Views)
- You use Builders to create your application
This means you can extend Loopar using Loopar itself.
Entity
Entity is the foundational concept in Loopar. It's the master builder that defines data models and automatically generates all the code needed to work with them.
What Entity Creates
When you define an Entity, Loopar automatically generates:
| Generated | Description |
|---|---|
| Database Table | With proper schema and migrations |
| Sequelize Model | ORM model with validations |
| REST API | Full CRUD endpoints |
| React Form | With all field types rendered |
| List View | With search, filters, and pagination |
| Controller | With lifecycle hooks |
Entity Structure
Every Entity consists of three optional files:
/modules/[module-name]/[entity-name]/
βββ entity-name.json # Field definitions & metadata
βββ entity-name.js # Server-side controller (optional)
βββ entity-name.jsx # Client-side component (optional)
The JSON File (Required)
Defines the entity's fields, metadata, and structure:
{
"name": "Customer",
"module": "CRM",
"is_single": false,
"fields": [
{ "name": "name", "type": "Data", "required": true },
{ "name": "email", "type": "Email", "unique": true },
{ "name": "status", "type": "Select",
"options": ["Lead", "Active", "Inactive"] }
]
}
The JS File (Optional)
Server-side controller for custom business logic:
import { BaseDocument } from "loopar";
export default class Customer extends BaseDocument {
constructor(props) {
super(props);
}
async beforeInsert() {
// Runs before saving a new record
this.created_at = new Date();
}
async afterInsert() {
// Runs after saving a new record
await this.sendWelcomeEmail();
}
async validate() {
if (!this.email.includes('@')) {
throw new Error('Invalid email format');
}
}
}
The JSX File (Optional)
Client-side React component for custom UI:
import { BaseForm } from "@loopar/components";
export default function CustomerForm(props) {
return (
<BaseForm {...props}>
{/* Custom UI elements */}
</BaseForm>
);
}
Entity Types
| Type | is_single | Description |
|---|---|---|
| Document | false | Multiple records (customers, products) |
| Single | true | One record only (settings, config) |
Document vs Single
Document entities store multiple records:
- Customer, Product, Order, Invoice
- Have list views with pagination
- CRUD operations on individual records
Single entities store only one record:
- System Settings, Company Info, App Config
- No list view, direct access to the form
- Perfect for configuration data
Builders
Builders are specialized entities designed to create specific types of models. While Entity is the general-purpose builder, each Builder is optimized for a particular use case.
Why Builders?
Entity is powerful but generic. Builders provide:
- Focused metadata β Only relevant fields for the model type
- Optimized UI β Tailored design experience
- Specialized behavior β Logic specific to the model type
Builder Types
Page Builder
Creates complete web pages: homepages, landing pages, blogs, documentation.
| Feature | Description |
|---|---|
| Purpose | Design full web pages visually |
| Output | Standalone page with route |
| Components | Sections, rows, columns, text, images, etc. |
| Use Cases | Homepage, About, Contact, Blog posts |
/pages/homepage/
βββ homepage.json # Page structure & components
βββ homepage.js # Page controller (optional)
βββ homepage.jsx # Custom React component (optional)
View Builder
Creates data display components that connect to a model.
| Feature | Description |
|---|---|
| Purpose | Visualize data from an entity |
| Output | Connected view component |
| Components | Tables, cards, charts, grids |
| Use Cases | Dashboards, reports, data displays |
Views connect to a source entity and render its data with custom layouts.
Form Builder
Creates forms for data collection without requiring a full entity.
| Feature | Description |
|---|---|
| Purpose | Collect and submit data |
| Output | Form that submits to a controller |
| Components | Inputs, selects, checkboxes, buttons |
| Use Cases | Login, registration, contact forms |
/forms/login-form/
βββ login-form.json # Form fields & layout
βββ login-form.js # Form submission handler
Controller Builder
Creates server-side controllers for custom actions without a model.
| Feature | Description |
|---|---|
| Purpose | Handle specific backend actions |
| Output | API routes and actions |
| Components | N/A (server-side only) |
| Use Cases | Auth endpoints, webhooks, integrations |
// login-controller.js
export default class LoginController extends BaseController {
async actionLogin() {
const { email, password } = this.request.body;
// Authentication logic
}
async actionLogout() {
// Logout logic
}
}
Choosing the Right Builder
| Need | Builder |
|---|---|
| Database model with CRUD | Entity |
| Web page design | Page Builder |
| Display existing data | View Builder |
| Standalone form | Form Builder |
| Custom API endpoints | Controller Builder |
App
App is Loopar's packaging system. An App is a self-contained application that bundles modules, entities, pages, and assets into an installable unit.
What is an App?
Think of an App as an independent program within Loopar:
- Contains all its modules and entities
- Can be installed/uninstalled on any tenant
- Can be shared, distributed, or sold
- Has its own version and dependencies
App Structure
/apps/
βββ my-crm/ # App directory
βββ app.json # App metadata
βββ modules/ # App modules
β βββ contacts/ # Module: Contacts
β β βββ customer/ # Entity: Customer
β β β βββ customer.json
β β β βββ customer.js
β β β βββ customer.jsx
β β βββ lead/ # Entity: Lead
β β βββ lead.json
β βββ sales/ # Module: Sales
β βββ quote/
β βββ invoice/
βββ public/ # Static assets
βββ images/
βββ styles/
App Metadata (app.json)
{
"name": "My CRM",
"version": "1.0.0",
"description": "Customer relationship management app",
"author": "Your Name",
"dependencies": ["loopar"],
"modules": ["contacts", "sales"]
}
App Lifecycle
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β Create β β β Develop β β β Install β
β (in dev) β β (add models)β β (on tenant) β
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
β Publish β β β Export β β β Test β
β (marketplace)β β (package) β β (staging) β
ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ
App Manager
Access the App Manager at desk/App Manager/view to:
| Action | Description |
|---|---|
| Create | Start a new app |
| Install | Install app on current tenant |
| Uninstall | Remove app from tenant |
| Update | Update to newer version |
| Export | Package app for distribution |
| Import | Install from repository or file |
Default App Creation
When you create an App, Loopar automatically:
- Creates the app directory structure
- Generates a module with the same name
- Sets up the app.json metadata
- Registers the app in the system
All entities you create are linked to the app through their module.
Module
Module is Loopar's organizational unit. Modules group related entities within an app, providing logical separation and structure.
What is a Module?
A Module is like a folder that contains related functionality:
- Groups entities by domain (sales, inventory, HR)
- Provides namespace isolation
- Enables selective installation
- Organizes the Desk sidebar
Module Structure
/apps/my-app/modules/
βββ core/ # Core functionality
β βββ user/
β βββ role/
β βββ settings/
βββ sales/ # Sales module
β βββ customer/
β βββ quote/
β βββ invoice/
βββ inventory/ # Inventory module
βββ product/
βββ warehouse/
βββ stock-entry/
Module in the Desk
Modules appear in the Desk sidebar, grouping their entities:
βββββββββββββββββββββββββββββββ
β DESK β
βββββββββββββββββββββββββββββββ€
β βΌ Core β
β Users β
β Roles β
β Settings β
β βΌ Sales β
β Customers β
β Quotes β
β Invoices β
β βΌ Inventory β
β Products β
β Warehouses β
β Stock Entries β
βββββββββββββββββββββββββββββββ
Creating a Module
- Go to Desk β Module β New
- Enter module details:
- Name: Module identifier (e.g.,
sales) - Label: Display name (e.g.,
Sales) - App: Parent app
- Icon: Module icon for the sidebar
- Name: Module identifier (e.g.,
- Save the module
Module vs App
| Aspect | App | Module |
|---|---|---|
| Scope | Complete application | Functional group |
| Contains | Modules, assets, config | Entities only |
| Installable | Yes | No (part of app) |
| Example | CRM App | Sales Module |
Fields & Components
Loopar provides a rich set of field types and UI components for building your entities and pages.
Data Fields
Fields that store data in the database:
| Field Type | Description | Database Type |
|---|---|---|
| Data | Single line text | VARCHAR(255) |
| Text | Multi-line text | TEXT |
| Int | Integer number | INT |
| Float | Decimal number | FLOAT |
| Currency | Money values | DECIMAL(18,6) |
| Date | Date only | DATE |
| DateTime | Date and time | DATETIME |
| Time | Time only | TIME |
| Check | Boolean (yes/no) | TINYINT(1) |
| Select | Dropdown options | VARCHAR(255) |
| Link | Reference to another entity | VARCHAR(255) |
| Table | Child table (one-to-many) | β |
| Email with validation | VARCHAR(255) | |
| Phone | Phone number | VARCHAR(50) |
| Password | Encrypted password | VARCHAR(255) |
| Color | Color picker | VARCHAR(20) |
| Image | Image upload | TEXT (path) |
| File | File upload | TEXT (path) |
| Markdown | Rich markdown text | LONGTEXT |
| Code | Code editor | LONGTEXT |
| JSON | JSON data | LONGTEXT |
Layout Components
Components for structuring pages (no database storage):
| Component | Description |
|---|---|
| Section | Full-width container |
| Row | Horizontal container with columns |
| Col | Column within a row |
| Panel | Card-like container with styling |
| Div | Generic container |
| Tab | Tabbed content container |
| Collapse | Collapsible content |
Display Components
| Component | Description |
|---|---|
| Title | Heading text (h1-h6) |
| Subtitle | Secondary heading |
| Paragraph | Body text |
| Image | Display image |
| Icon | Lucide icon |
| Button | Clickable button |
| Link | Navigation link |
| Markdown | Rendered markdown |
| Code Block | Syntax-highlighted code |
Interactive Components
| Component | Description |
|---|---|
| Carousel | Image/content slider |
| Particles | Animated particle background |
| Banner Image | Hero banner with overlay |
| Feature Card | Feature highlight card |
| Pricing Card | Pricing table card |
| Testimonial | Customer testimonial |
Field Properties
Common properties available on most fields:
| Property | Description |
|---|---|
name | Field identifier (snake_case) |
label | Display label |
type | Field type |
required | Must have a value |
unique | Must be unique in database |
default | Default value |
hidden | Hide from UI |
readonly | Cannot be edited |
options | For Select fields |
description | Help text |
class | CSS classes |
Lifecycle Hooks
Loopar provides hooks at key points in an entity's lifecycle. Use these to add custom business logic.
Server-Side Hooks
Defined in the entity's .js file:
import { BaseDocument } from "loopar";
export default class Invoice extends BaseDocument {
// βββββββββββββββββββββββββββββββββββββββ
// VALIDATION
// βββββββββββββββββββββββββββββββββββββββ
async validate() {
// Runs before any save operation
// Throw error to prevent save
if (this.total < 0) {
throw new Error("Total cannot be negative");
}
}
// βββββββββββββββββββββββββββββββββββββββ
// INSERT HOOKS (new records)
// βββββββββββββββββββββββββββββββββββββββ
async beforeInsert() {
// Before saving new record
this.created_by = this.session.user;
this.status = "Draft";
}
async afterInsert() {
// After saving new record
await this.notifyAccountant();
}
// βββββββββββββββββββββββββββββββββββββββ
// UPDATE HOOKS (existing records)
// βββββββββββββββββββββββββββββββββββββββ
async beforeUpdate() {
// Before updating existing record
this.modified_by = this.session.user;
}
async afterUpdate() {
// After updating existing record
if (this.status === "Paid") {
await this.updateCustomerBalance();
}
}
// βββββββββββββββββββββββββββββββββββββββ
// SAVE HOOKS (both insert and update)
// βββββββββββββββββββββββββββββββββββββββ
async beforeSave() {
// Before any save (insert or update)
this.calculateTotals();
}
async afterSave() {
// After any save (insert or update)
await this.updateStockLevels();
}
// βββββββββββββββββββββββββββββββββββββββ
// DELETE HOOKS
// βββββββββββββββββββββββββββββββββββββββ
async beforeDelete() {
// Before deleting record
if (this.status === "Paid") {
throw new Error("Cannot delete paid invoice");
}
}
async afterDelete() {
// After deleting record
await this.reverseStockEntries();
}
}
Hook Execution Order
On Insert (new record)
1. validate()
2. beforeSave()
3. beforeInsert()
4. β DATABASE INSERT β
5. afterInsert()
6. afterSave()
On Update (existing record)
1. validate()
2. beforeSave()
3. beforeUpdate()
4. β DATABASE UPDATE β
5. afterUpdate()
6. afterSave()
On Delete
1. beforeDelete()
2. β DATABASE DELETE β
3. afterDelete()
Accessing Data in Hooks
async beforeSave() {
// Access field values
const customerName = this.customer_name;
// Access session/user info
const currentUser = this.session.user;
// Access request data
const requestBody = this.request?.body;
// Query other documents
const customer = await loopar.getDocument("Customer", this.customer);
// Set field values
this.total = this.calculateTotal();
}
Client vs Server
Loopar separates client-side and server-side code clearly. Understanding this separation is key to extending the framework.
File Responsibilities
/entity-name/
βββ entity-name.json β Definition (shared)
βββ entity-name.js β Server-side (Node.js)
βββ entity-name.jsx β Client-side (React)
| File | Runs On | Purpose |
|---|---|---|
.json | Both | Field definitions, metadata |
.js | Server | Business logic, database, API |
.jsx | Client | UI components, interactions |
Server-Side (.js)
Runs in Node.js. Has access to:
- Database operations
- File system
- Environment variables
- External APIs (server-to-server)
- Session and authentication
- Business logic hooks
// entity-name.js
import { BaseDocument } from "loopar";
export default class MyEntity extends BaseDocument {
async beforeSave() {
// Database access
const count = await loopar.db.count("Customer");
// File system
const config = await loopar.readFile("config.json");
// Environment
const apiKey = process.env.API_KEY;
}
// Custom API action
async actionCustomEndpoint() {
return { success: true, data: this.getData() };
}
}
Client-Side (.jsx)
Runs in the browser (React). Has access to:
- UI rendering
- User interactions
- Browser APIs
- HTTP requests to server
- Component state
// entity-name.jsx
import { BaseForm, useDocument } from "@loopar/components";
import { useState } from "react";
export default function MyEntity(props) {
const { document, setValue } = useDocument();
const [loading, setLoading] = useState(false);
const handleCustomAction = async () => {
setLoading(true);
const response = await fetch(`/api/my-entity/custom-endpoint`);
setLoading(false);
};
return (
<BaseForm {...props}>
<button onClick={handleCustomAction}>
{loading ? "Loading..." : "Custom Action"}
</button>
</BaseForm>
);
}
Communication Between Client & Server
βββββββββββββββββββ βββββββββββββββββββ
β CLIENT β β SERVER β
β (Browser) β β (Node.js) β
βββββββββββββββββββ€ βββββββββββββββββββ€
β β HTTP β β
β React Componentβ βββββββ β Controller β
β (.jsx) β Request β (.js) β
β β β β
β β βββββββ β β
β β Responseβ β
βββββββββββββββββββ βββββββββββββββββββ
When to Use Each
| Use Server (.js) For | Use Client (.jsx) For |
|---|---|
| Database queries | UI customization |
| Data validation | User interactions |
| Business rules | Form behavior |
| API integrations | Animations |
| File operations | Conditional rendering |
| Authentication | Real-time updates |
Multi-Tenant Architecture
Loopar's multi-tenant system allows you to run multiple isolated sites from a single codebase.
What is Multi-Tenancy?
Each tenant is a completely independent site with:
- Own database β Complete data isolation
- Own domain β Custom URL for each client
- Own config β Independent settings
- Own users β Separate user base
- Shared code β Same apps, different data
Architecture
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SINGLE CODEBASE β
β /apps, /packages β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββββ βββββββββββββββββ βββββββββββββββββ β
β β TENANT A β β TENANT B β β TENANT C β β
β β (dev) β β (client-1) β β (client-2) β β
β βββββββββββββββββ€ βββββββββββββββββ€ βββββββββββββββββ€ β
β β Database: dev β β Database: c1 β β Database: c2 β β
β β Port: 3000 β β Port: 3001 β β Port: 3002 β β
β β Domain: β β β app1.com β β app2.com β β
β βββββββββββββββββ βββββββββββββββββ βββββββββββββββββ β
β β β β β
β ββββββββββββββ¬βββββ΄βββββββββββββββββ β
β β β
β PM2 β
β (Process Manager) β
β β β
β CADDY β
β (Reverse Proxy + SSL) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Site Directory Structure
/sites/
βββ dev/ # Development tenant
β βββ .env # Environment config
β βββ installed-apps.json # Installed apps list
β βββ public/
β β βββ uploads/ # Tenant-specific uploads
β βββ sessions/ # User sessions
β
βββ client-1/ # Production tenant
β βββ .env
β βββ installed-apps.json
β βββ ...
β
βββ client-2/ # Another tenant
βββ ...
Tenant Configuration (.env)
Each tenant has its own environment file:
# sites/client-1/.env
DB_TYPE=mysql
DB_HOST=localhost
DB_NAME=client1_db
DB_USER=client1
DB_PASSWORD=secret
PORT=3001
NODE_ENV=production
DOMAIN=app1.com
Use Cases
| Scenario | Setup |
|---|---|
| SaaS Product | One tenant per customer |
| Agency | One tenant per client project |
| Enterprise | Separate tenants for departments |
| Staging | Dev, Staging, Production tenants |
Key Benefits
| Benefit | Description |
|---|---|
| Data Isolation | Each client's data is completely separate |
| Custom Domains | Professional URLs for each client |
| Independent Scaling | Scale individual tenants as needed |
| Easy Deployment | Deploy updates to all tenants at once |
| Cost Efficient | Single server, multiple clients |
API & Routes
Loopar automatically generates RESTful API endpoints for every entity. You can also create custom actions.
Auto-Generated Endpoints
For an entity named Customer, Loopar creates:
| Method | Endpoint | Action |
|---|---|---|
GET | /api/Customer | List all records |
GET | /api/Customer/:name | Get single record |
POST | /api/Customer | Create new record |
PUT | /api/Customer/:name | Update record |
DELETE | /api/Customer/:name | Delete record |
Query Parameters
The list endpoint supports:
# Pagination
GET /api/Customer?page=1&limit=20
# Search
GET /api/Customer?search=john
# Filters
GET /api/Customer?status=Active&country=USA
# Sorting
GET /api/Customer?order_by=created_at&order=desc
# Field selection
GET /api/Customer?fields=name,email,status
Custom Actions
Add custom API endpoints in your entity's .js file:
export default class Customer extends BaseDocument {
// Creates: POST /api/Customer/send-welcome-email
async actionSendWelcomeEmail() {
await this.sendEmail({
to: this.email,
subject: "Welcome!",
template: "welcome"
});
return {
success: true,
message: "Email sent successfully"
};
}
// Creates: GET /api/Customer/stats
async actionStats() {
const total = await loopar.db.count("Customer");
const active = await loopar.db.count("Customer", { status: "Active" });
return {
total,
active,
inactive: total - active
};
}
}
Calling Actions from Client
// From React component
const response = await fetch("/api/Customer/send-welcome-email", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "CUST-001" })
});
const data = await response.json();
Page Routes
Pages get their own routes automatically:
| Page Name | Route |
|---|---|
homepage | /homepage |
about-us | /about-us |
contact | /contact |
Desk Routes
Internal desk routes follow this pattern:
| Route | View |
|---|---|
/desk/Customer/list | List view |
/desk/Customer/new | New record form |
/desk/Customer/edit/:name | Edit record form |
/desk/Customer/view/:name | Read-only view |
Database
Loopar uses Sequelize ORM to support multiple database backends. The schema is generated automatically from your entity definitions.
Supported Databases
| Database | Best For |
|---|---|
| MySQL | Production, high performance |
| PostgreSQL | Advanced features, JSON support |
| MariaDB | MySQL alternative |
| SQLite | Development, small deployments |
Automatic Schema Management
When you create or modify an entity:
- Loopar reads the field definitions from
.json - Compares with current database schema
- Generates migration SQL
- Applies changes automatically
Entity Definition β Schema Diff β Migration β Database
Field to Column Mapping
| Field Type | MySQL | PostgreSQL |
|---|---|---|
| Data | VARCHAR(255) | VARCHAR(255) |
| Text | TEXT | TEXT |
| Int | INT | INTEGER |
| Float | FLOAT | REAL |
| Currency | DECIMAL(18,6) | NUMERIC(18,6) |
| Date | DATE | DATE |
| DateTime | DATETIME | TIMESTAMP |
| Check | TINYINT(1) | BOOLEAN |
| JSON | LONGTEXT | JSONB |
Database API
Access the database in server-side code:
import loopar from "loopar";
// Get a document
const customer = await loopar.getDocument("Customer", "CUST-001");
// Create a document
const newCustomer = await loopar.newDocument("Customer");
newCustomer.name = "John Doe";
newCustomer.email = "john@example.com";
await newCustomer.save();
// Query documents
const customers = await loopar.db.getAll("Customer", {
filters: { status: "Active" },
fields: ["name", "email"],
orderBy: "created_at DESC",
limit: 10
});
// Count documents
const count = await loopar.db.count("Customer", { status: "Active" });
// Raw SQL (when needed)
const results = await loopar.db.execute(
"SELECT * FROM Customer WHERE created_at > ?",
["2024-01-01"]
);
Relationships
Link Fields (Many-to-One)
{
"name": "customer",
"type": "Link",
"options": "Customer"
}
Creates a foreign key reference to the Customer entity.
Table Fields (One-to-Many)
{
"name": "items",
"type": "Table",
"options": "Invoice Item"
}
Creates a child table with parent reference.
Database Per Tenant
Each tenant has its own database:
βββββββββββββββ βββββββββββββββ βββββββββββββββ
β Tenant A β β Tenant B β β Tenant C β
β β β β β β
β βββββββββ β β βββββββββ β β βββββββββ β
β β MySQL β β β β MySQL β β β βSQLite β β
β β db_a β β β β db_b β β β β db_c β β
β βββββββββ β β βββββββββ β β βββββββββ β
βββββββββββββββ βββββββββββββββ βββββββββββββββ
Each tenant can even use a different database type.
Theming
Loopar uses Tailwind CSS with CSS variables for a flexible theming system. Themes can be changed dynamically without rebuilds.
Theme System
Loopar supports:
- Light/Dark modes β Automatic switching
- Custom color schemes β Define your brand colors
- Per-tenant themes β Each tenant can have its own theme
- Dynamic generation β Create themes programmatically
CSS Variables
Themes are defined using CSS custom properties:
:root {
--background: 0 0% 100%;
--foreground: 222 84% 5%;
--primary: 221 83% 53%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96%;
--muted: 210 40% 96%;
--accent: 210 40% 96%;
--destructive: 0 84% 60%;
--border: 214 32% 91%;
--radius: 0.5rem;
}
.dark {
--background: 222 84% 5%;
--foreground: 210 40% 98%;
/* ... dark mode colors */
}
Using Theme Colors
In your components, use Tailwind classes:
// Background colors
<div className="bg-background" />
<div className="bg-primary" />
<div className="bg-secondary" />
// Text colors
<p className="text-foreground" />
<p className="text-muted-foreground" />
<p className="text-primary" />
// Border colors
<div className="border border-border" />
Built-in Themes
Loopar includes several pre-built themes:
| Theme | Description |
|---|---|
| Default | Clean, professional blue |
| Slate | Neutral gray tones |
| Rose | Warm pink accent |
| Orange | Energetic orange |
| Green | Fresh green accent |
| Violet | Purple tones |
Creating Custom Themes
Define a new theme in your app:
// Custom theme definition
const myTheme = {
name: "corporate",
colors: {
primary: { h: 210, s: 100, l: 40 }, // Corporate blue
secondary: { h: 210, s: 20, l: 95 },
accent: { h: 45, s: 100, l: 50 }, // Gold accent
background: { h: 0, s: 0, l: 100 },
foreground: { h: 210, s: 50, l: 10 },
}
};
Dark Mode
Dark mode is automatic based on:
- User preference in settings
- System preference (
prefers-color-scheme) - Manual toggle
// Toggle dark mode
import { useTheme } from "@loopar/components";
function ThemeToggle() {
const { theme, setTheme } = useTheme();
return (
<button onClick={() => setTheme(theme === "dark" ? "light" : "dark")}>
Toggle Theme
</button>
);
}
Glossary
Quick reference for Loopar terminology.
| Term | Definition |
|---|---|
| Entity | The core model definition that generates database tables, APIs, and UI |
| Builder | Specialized entity type for creating specific model types |
| App | Self-contained application package with modules and entities |
| Module | Organizational unit that groups related entities |
| Document | Instance of an entity (a record in the database) |
| Single | Entity type that stores only one record (for settings/config) |
| Tenant | Independent site with own database and configuration |
| Desk | The admin interface for managing entities and data |
| Component | UI element used in drag-and-drop design |
| Field | Data attribute defined in an entity |
| Hook | Lifecycle callback (beforeSave, afterInsert, etc.) |
| Controller | Server-side class handling business logic |
| Action | Custom API endpoint defined in a controller |
| Link | Field type referencing another entity (foreign key) |
| Table | Field type for child records (one-to-many) |
| PM2 | Process manager running all tenant sites |
| Caddy | Reverse proxy for SSL and domain routing |
Common Patterns
| Pattern | Example |
|---|---|
| Entity name | Customer, Sales Invoice, Stock Entry |
| Field name | customer_name, total_amount, is_active |
| Module name | core, sales, inventory |
| App name | my-crm, loopar, hr-management |
| Route | /desk/Customer/list, /api/Customer |
File Extensions
| Extension | Purpose |
|---|---|
.json | Entity/component definitions |
.js | Server-side controller |
.jsx | Client-side React component |
.env | Environment configuration |
.mjs | ES Module JavaScript |