Style Guide and Standards
This document defines the strict coding conventions and quality requirements for the Elo Orgânico project. All code must adhere to these standards to ensure consistency and maintainability across the monorepo.
1. Code Formatting (Prettier)
Code must follow the rules defined in .prettierrc:
- Indentation: 2 spaces.
- Semicolons: Always use (true).
- Quotes: Use single quotes (true), except in JSX.
- Trailing Comma: Always use where possible (all).
- Line Width: Maximum of 100 characters.
2. TypeScript Rules (Strict Mode)
We prioritize maximum type safety through a rigorous TypeScript configuration.
- Object Definitions: Use
interfacefor object definitions to ensure consistency. Type aliases are permitted for unions or utility types (e.g.,z.infer<>). - Arrays: Use the
T[]syntax instead ofArray<T>. - Type Imports: Always use
import typefor types. Import types separately from values (style: separate-type-imports). - Unused Variables: Prefix with an underscore (e.g.,
_id) to signal intent. - Strict Typing: The use of
anyis strictly forbidden. - Comparisons: Always use strict equality (
===). - Strict Booleans: Boolean expressions must be explicit. Use
if (value !== undefined)instead ofif (value). - Safe Conversions: Do not use
String(value)or${value}on genericunknownorobjecttypes. Use explicit type guards for primitives to prevent[object Object]bugs. - Type Assertions: Avoid unnecessary type assertions (e.g.,
value as Twhenvalueis alreadyT). The linter will flag these; remove them to keep code clean.
3. Asynchronous Code Management (Critical)
- No Floating Promises: Never leave promises "floating" without handling or awaiting.
- The
voidOperator: When an async function is called for its side effects and is NOT awaited, prefix it withvoid(e.g.,void startServer()). This signals intentional non-blocking execution. - Fastify Handlers: Route handlers should use the
FastifyZodHandlertype and returnPromise<void>. Usevoid reply.send()when not returning the reply directly. - Fastify Plugins: If a
FastifyPluginAsyncdoes not useawait, remove theasynckeyword and returnPromise.resolve()to comply withrequire-awaitwhile maintaining type integrity. - Error Handling: Every
awaitoperation must be within atry/catchblock or part of a.catch()chain.
4. Package-Specific Standards
4.1. Core Packages (@elo-instance/core / @elo-portal/core)
- Zero Warnings: These packages must have zero lint warnings or errors.
- Public APIs: All exported members must have explicitly defined return types.
4.2. API (Fastify)
- Naming: Controllers and Services use
PascalCasefor classes andcamelCasefor methods. - Mapping: Controllers are responsible for mapping database models to Core DTOs via private methods.
4.3. Web (React)
- React 19 Patterns: Use the
use()hook for consuming promises and context when applicable, reducinguseEffectboilerplate. Prefer Server Actions (if applicable) or optimizedactionprops in forms for a smoother user experience. - Global State: Trigger async side-effects in Stores or Effects using the
voidoperator for unawaited calls. - Refs: Accessing
ref.currentduring render is strictly forbidden. When passing multiple refs to a child component, pass them individually rather than in a grouped object to avoid linter confusion and ensure clarity. - Strict Booleans in UI: In JSX, always use explicit comparisons:
{isValid === true && <Component />}to avoid rendering0orNaNin the UI. - State Syncing: Avoid calling
setStatesynchronously withinuseEffectif the update is derived from props or other state. Instead, sync state during render (the "prevProps" pattern) or initialize state with a function. - List Keys: Always use stable, unique IDs (e.g.,
_id) as keys in lists. Avoid using array indices unless the list is static and has no unique identifiers. If indices must be used, document the reason. - Styling & Responsive Measures: Use CSS Modules (
.module.css). Utility classes from Tailwind are allowed inside modules via@apply. Adhere strictly to the Responsive Standards defined in Section 5. - Console Logs:
console.logis forbidden and will fail the production build. Useconsole.info/warn/errorsparingly.
5. CSS & Responsive Standards (Strict)
To ensure a fluid, accessible, and modern user interface, the following rules are mandatory for all applications:
- No Pixels (
px): The use of fixedpxvalues is strictly forbidden for sizing, spacing, and typography.- Exception:
1pxis allowed for borders when a hairline effect is desired.
- Exception:
- Relative Typography: Always use
remfor font sizes. Never usepxoremfor typography. - Fluid Spacing: Use
remfor consistent spacing orclamp()for fluid spacing that scales with the viewport. - Structural Layouts: Use modern layout primitives:
flexbox,grid,%,vw, andvh. - Logical Functions: Leverage
clamp(),min(), andmax()to create boundaries for fluid elements without using media queries for every minor adjustment.
Responsive Examples
.container {
/* Fluid width: min 320px, preferred 90%, max 75rem (1200px) */
width: clamp(20rem, 90%, 75rem);
padding: 1.5rem;
}
.heroTitle {
/* Fluid typography: min 1.5rem, scales with 4vw, max 3rem */
font-size: clamp(1.5rem, 4vw, 3rem);
color: var(--color-title-dark);
}
.heroSection {
/* Full viewport height minus header height (assumed 4rem) */
min-height: calc(100vh - 4rem);
display: flex;
align-items: center;
justify-content: center;
background-color: var(--color-background-tint);
}
import styles from './ResponsiveHero.module.css';
export const ResponsiveHero = () => {
return (
<section className={styles.heroSection}>
<div className={styles.container}>
<h1 className={styles.heroTitle}>Design Fluido e Responsivo</h1>
</div>
</section>
);
};
6. Naming Conventions
- Schemas: Must end with
Schema(e.g.,ProductSchema). - Files: Follow the
name.type.tspattern (e.g.,auth.controller.ts,apiPlugin.ts). - Directories: Use
camelCasefor directory names within source folders.
7. Language Standards (Strict English-First)
To maintain global accessibility, scalability, and code consistency across all contexts of the monorepo:
- Source Code & Comments: All source code (variable names, functions, classes, interfaces, properties, schemas, files) and comments within code files MUST be written exclusively in English (en-US).
- Documentation: All technical and product documentation, READMEs, security guidelines, and architectural briefs MUST be written in English.
- Git History: Commit messages and Pull Request titles/descriptions MUST follow the Conventional Commits specification and be written in English.
- Localization & i18n Exceptions: The ONLY exceptions are localization files (e.g.,
i18nconfigurations, translation tables, dictionary JSON files) and explicit mock data representing end-user text in Portuguese. All internal application logic and definitions must remain strictly in English.
8. Code Block Standards (live blocks)
The Docusaurus live code block (interactive editor) is EXCLUSIVELY reserved for this Style Guide. It must NOT be used in any other documentation page.
Its purpose is strictly to demonstrate:
- React Components: Following rigorous patterns (explicit booleans, strict types, etc.).
- API Domain Structures: Visualizing how domain models should be structured.
7.1. Rigorous React & CSS Module Pattern Example
The example below demonstrates our preferred pattern for interactive components using CSS Modules. It showcases the integration of state management, memoization, and asynchronous handling while strictly adhering to our TypeScript, boolean logic, and styling rules.
.container {
padding: 1.5rem;
border: 0.0625rem solid var(--color-border-light);
border-radius: 0.75rem;
background-color: var(--color-background-white);
box-shadow: 0 0.25rem 0.375rem -0.0625rem rgba(0, 0, 0, 0.1);
}
.header {
display: flex;
justify-content: space-between;
margin-bottom: 1.5rem;
align-items: center;
}
.title {
margin: 0;
color: var(--color-title-dark);
}
.statusBadge {
font-size: 0.7rem;
font-weight: 800;
padding: 0.3rem 0.8rem;
border-radius: 1.25rem;
letter-spacing: 0.05em;
}
.statusInvalid {
background-color: #fee2e2;
color: #991b1b;
}
.statusReady {
background-color: #f0fdf4;
color: #166534;
}
.productList {
display: flex;
flex-direction: column;
gap: 0.8rem;
margin-bottom: 1.5rem;
}
.productItem {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.8rem;
border-radius: 0.5rem;
border: 0.0625rem solid var(--color-border-light);
background-color: var(--color-background-tint);
}
.productInvalid {
border-color: #fecaca;
background-color: #fff1f2;
}
.productName {
font-weight: 700;
font-size: 0.9rem;
color: var(--color-title-dark);
}
.productId {
font-size: 0.7rem;
color: var(--color-subtitle-dark);
}
.inputGroup {
display: flex;
align-items: center;
gap: 0.5rem;
}
.currency {
font-size: 0.8rem;
font-weight: 600;
color: var(--color-subtitle-dark);
}
.priceInput {
width: 5rem;
padding: 0.4rem;
border-radius: 0.375rem;
border: 0.0625rem solid var(--color-border-light);
text-align: right;
font-size: 0.9rem;
}
.submitButton {
width: 100%;
padding: 1rem;
border-radius: 0.5rem;
border: none;
background-color: var(--color-identity-primary);
color: white;
font-weight: 700;
font-size: 0.95rem;
transition:
opacity 0.2s,
background-color 0.2s;
}
.submitButton:disabled {
cursor: not-allowed;
opacity: 0.6;
}
.submitButton:hover:not(:disabled) {
filter: brightness(1.1);
cursor: pointer;
}
/** * Advanced Cycle Product Manager * Demonstrates: CSS Modules (styles object), useCallback, void operator, explicit booleans, and stable keys. */ // import styles from './AdvancedCycleManager.module.css'; function AdvancedCycleManager() { // In real development, 'styles' comes from the CSS Module import above. // In this live demo, we map keys to the simulated class names defined below. const styles = { container: 'ACM_container', header: 'ACM_header', title: 'ACM_title', statusBadge: 'ACM_statusBadge', statusInvalid: 'ACM_statusInvalid', statusReady: 'ACM_statusReady', productList: 'ACM_productList', productItem: 'ACM_productItem', productInvalid: 'ACM_productInvalid', productName: 'ACM_productName', productId: 'ACM_productId', inputGroup: 'ACM_inputGroup', currency: 'ACM_currency', priceInput: 'ACM_priceInput', submitButton: 'ACM_submitButton', }; const [products, setProducts] = React.useState([ { id: 'uuid-1', name: 'Alface Crespa', price: 4.5, isValid: true }, { id: 'uuid-2', name: 'Tomate Cereja', price: 0, isValid: false }, ]); const [isSubmitting, setIsSubmitting] = React.useState(false); const hasErrors = React.useMemo(() => { return products.some((p) => p.isValid === false); }, [products]); const updateProductPrice = React.useCallback((id, value) => { const numericValue = parseFloat(value) || 0; setProducts((prev) => prev.map((p) => (p.id === id ? { ...p, price: numericValue, isValid: numericValue > 0 } : p)), ); }, []); const handleSubmit = async () => { setIsSubmitting(true); try { await new Promise((resolve) => setTimeout(resolve, 1500)); console.info('Products submitted:', products); alert('Cycle products updated successfully!'); } catch (err) { console.error('[Update Error]:', err); } finally { setIsSubmitting(false); } }; const handleAction = () => { void handleSubmit(); }; return ( <div className={styles.container}> <div className={styles.header}> <h3 className={styles.title}>Gerenciador de Ciclo</h3> <span className={`${styles.statusBadge} ${ hasErrors === true ? styles.statusInvalid : styles.statusReady }`} > {hasErrors === true ? '⚠ ITENS INVÁLIDOS' : '✓ PRONTO PARA SALVAR'} </span> </div> <div className={styles.productList}> {products.map((product) => ( <div key={product.id} className={`${styles.productItem} ${ product.isValid === false ? styles.productInvalid : '' }`} > <div> <div className={styles.productName}>{product.name}</div> <div className={styles.productId}>ID: {product.id}</div> </div> <div className={styles.inputGroup}> <span className={styles.currency}>R$</span> <input type="number" value={product.price} onChange={(e) => updateProductPrice(product.id, e.target.value)} className={styles.priceInput} /> </div> </div> ))} </div> <button disabled={isSubmitting === true || hasErrors === true} onClick={handleAction} className={styles.submitButton} > {isSubmitting === true ? 'Processando Atualização...' : 'Salvar Alterações do Ciclo'} </button> {/* Internal CSS for the demo purpose only - NOT for production */} <style>{` .ACM_container { padding: 1.5rem; border: 1px solid #e2e8f0; border-radius: 12px; background: white; box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1); } .ACM_header { display: flex; justify-content: space-between; margin-bottom: 1.5rem; align-items: center; } .ACM_title { margin: 0; color: #1e293b; } .ACM_statusBadge { font-size: 0.7rem; font-weight: 800; padding: 0.3rem 0.8rem; border-radius: 20px; letter-spacing: 0.05em; } .ACM_statusInvalid { background-color: #fee2e2; color: #991b1b; } .ACM_statusReady { background-color: #f0fdf4; color: #166534; } .ACM_productList { display: flex; flex-direction: column; gap: 0.8rem; margin-bottom: 1.5rem; } .ACM_productItem { display: flex; align-items: center; justify-content: space-between; padding: 0.8rem; border-radius: 8px; border: 1px solid #f1f5f9; background: #fafafa; } .ACM_productInvalid { border-color: #fecaca; background-color: #fff1f2; } .ACM_productName { font-weight: 700; font-size: 0.9rem; color: #334155; } .ACM_productId { font-size: 0.7rem; color: #94a3b8; } .ACM_inputGroup { display: flex; align-items: center; gap: 0.5rem; } .ACM_currency { font-size: 0.8rem; font-weight: 600; color: #64748b; } .ACM_priceInput { width: 80px; padding: 0.4rem; border-radius: 6px; border: 1px solid #cbd5e1; text-align: right; } .ACM_submitButton { width: 100%; padding: 1rem; border-radius: 8px; border: none; background: #16a34a; color: white; font-weight: 700; cursor: pointer; } .ACM_submitButton:disabled { opacity: 0.6; cursor: not-allowed; } `}</style> </div> ); }
Last Updated: June 2026