How the Project is Organized
Modular has an opinionated directory structure suggested to organize your application. When you initiate a new Modular Project, you will have the default Laravel directories and files, along with a few additional ones described below.
Primarily, you will have two main directories:
The
modules
directory, where you will structure your PHP/Laravel related code, organized into modules, with app Routes, Controllers, Models, Validations, etc.The
resources
directory, where you will structure your Vue.js related code, organized into Pages (instead of Blade views), Vue Components, CSS, etc.
All communication between your frontend (Vue 3) and backend (Laravel) will be handled using the Inertia.js adapter. Modular will provide some facilities to make this communication even smoother, exposing validation error messages, and other useful information to your Vue context.
The Modules Directory
You can regard the modules
directory as domains of your application, and you are free to organize them as you see fit. The example below illustrates a simple web application with a Customer
Management Module, and a Calendar
Module, where it's possible to schedule events for existing customers.
The Modules Directory in this case, will be:
./modules
├── Calendar
│ ├── CalendarServiceProvider.php
│ ├── Http
│ │ ├── Controllers
│ │ └── Requests
│ ├── Models
│ │ └── Calendar.php
│ └── routes
│ └── app.php
└── Customer
├── CustomerServiceProvider.php
├── Http
│ ├── Controllers
│ └── Requests
├── Models
│ └── Customer.php
└── routes
└── app.php
Please note that both modules don't have a views
directory. This is because the views are represented as Vue.js Page Components
, and are located in the resources/js/Pages
directory.
The Resources Directory
The resources
directory holds significant importance in a Modular Application and follows the structure outlined below:
./resources
├── css
│ └── app.css
├── images
│ └── logo.svg
├── js
│ ├── Components
│ ├── Composables
│ ├── Configs
│ ├── Layouts
│ ├── Pages
│ ├── Plugins
│ ├── Resolvers
│ ├── app.js
│ └── bootstrap.js
└── views
├── app.blade.php
├── components
└── welcome.blade.php
The resources/css directory
In this directory, you'll find the app.css
file, which serves as the main (entrypoint) CSS file for your application. The app.css
file imports:
- Tailwind CSS files, enabling you to utilize Tailwind CSS classes in your application.
Additionally, it defines several root CSS variables
that can be utilized in your custom Vue Components and custom css files.
TIP
The color palette in the root CSS variables
is derived from the Radix UI Scales. Each level (e.g., --color-primary-1
, --color-primary-2
, etc.) is intended for a specific context, such as:
- App background
- UI element background
- UI element border and focus rings
- Low-contrast text, etc.
The Radix Project is an excellent resource for learning about color palettes, composing a palette, understanding the scale, and how to apply them. This concept is embraced across all Custom Vue Components within the Modular project.
For instance, the root CSS variables
adopt the following syntax:
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
@layer base {
:root {
--color-primary-1: 253 253 254; /* #fdfdfe */
--color-primary-2: 248 250 255; /* #f8faff */
--color-primary-3: 240 244 255; /* #f0f4ff */
--color-primary-4: 230 237 254; /* #e6edfe */
--color-primary-5: 217 226 252; /* #d9e2fc */
--color-primary-6: 198 212 249; /* #c6d4f9 */
--color-primary-7: 174 192 245; /* #aec0f5 */
--color-primary-8: 141 164 239; /* #8da4ef */
--color-primary-9: 62 99 221; /* #3e63dd */
--color-primary-10: 58 92 204; /* #3a5ccc */
--color-primary-11: 52 81 178; /* #3451b2 */
--color-primary-12: 31 45 92; /* #1f2d5c */
/* 1 - App background, 2 - Subtle background */
--color-neutral-1: 252 252 253; /* #fcfcfd */
--color-neutral-2: 249 249 251; /* #f9f9fb */
/* 3 - UI element background, 4 - Hovered UI element background, 5 - Active / Selected UI element background */
--color-neutral-3: 242 242 245; /* #f2f2f5 */
--color-neutral-4: 235 235 239; /* #ebebef */
--color-neutral-5: 228 228 233; /* #e4e4e9 */
/* 6 - Subtle borders and separators, 7 - UI element border and focus rings, 8 - Hovered UI element border */
--color-neutral-6: 221 221 227; /* #dddde3 */
--color-neutral-7: 211 212 219; /* #d3d4db */
--color-neutral-8: 185 187 198; /* #b9bbc6 */
/* 9 - Solid backgrounds, 10 - Hovered solid backgrounds */
--color-neutral-9: 139 141 152; /* #8b8d98 */
--color-neutral-10: 126 128 138; /* #7e808a */
/* 11 - Low-contrast text, 12 - High-contrast text */
--color-neutral-11: 96 100 108; /* #60646c */
--color-neutral-12: 28 32 36; /* #1c2024 */
/* Semantic Colors */
--color-info: 11 104 203; /* #0b68cb 11*/
--color-info-light: 237 246 255; /* #edf6ff 3*/
--color-info-dark: 17 50 100; /* #113264 12*/
--color-success: 24 121 78; /* #18794e */
--color-success-light: 233 249 238; /* #e9f9ee */
--color-success-dark: 25 59 45; /* #193b2d */
--color-warning: 119 95 40; /* #775f28 */
--color-warning-light: 255 248 198; /* #fff8c6 */
--color-warning-dark: 71 59 31; /* #473b1f */
--color-error: 198 42 47; /* #c62a2f */
--color-error-light: 255 239 239; /* #ffefef */
--color-error-dark: 100 23 35; /* #641723 */
}
.dark-theme {
--color-primary-1: 253 253 254; /* #fdfdfe */
--color-primary-2: 248 250 255; /* #f8faff */
--color-primary-3: 240 244 255; /* #f0f4ff */
--color-primary-4: 230 237 254; /* #e6edfe */
--color-primary-5: 217 226 252; /* #d9e2fc */
--color-primary-6: 198 212 249; /* #c6d4f9 */
--color-primary-7: 174 192 245; /* #aec0f5 */
--color-primary-8: 141 164 239; /* #8da4ef */
--color-primary-9: 62 99 221; /* #3e63dd */
--color-primary-10: 58 92 204; /* #3a5ccc */
--color-primary-11: 52 81 178; /* #3451b2 */
--color-primary-12: 31 45 92; /* #1f2d5c */
/* 1 - App background, 2 - Subtle background */
--color-neutral-1: 17 17 19; /* #111113 */
--color-neutral-2: 24 25 27; /* #18191b */
/* 3 - UI element background, 4 - Hovered UI element background, 5 - Active / Selected UI element background */
--color-neutral-3: 33 34 37; /* #212225 */
--color-neutral-4: 39 42 45; /* #272a2d */
--color-neutral-5: 46 49 53; /* #2e3135 */
/* 6 - Subtle borders and separators, 7 - UI element border and focus rings, 8 - Hovered UI element border */
--color-neutral-6: 54 58 63; /* #363a3f */
--color-neutral-7: 67 72 78; /* #43484e */
--color-neutral-8: 90 97 105; /* #5a6169 */
/* 9 - Solid backgrounds, 10 - Hovered solid backgrounds */
--color-neutral-9: 105 110 119; /* #696e77 */
--color-neutral-10: 119 123 132; /* #777b84 */
/* 11 - Low-contrast text, 12 - High-contrast text */
--color-neutral-11: 176 180 186; /* #b0b4ba */
--color-neutral-12: 237 238 240; /* #edeef0 */
/* Semantic Colors */
--color-info: 11 104 203; /* #0b68cb 11*/
--color-info-light: 237 246 255; /* #edf6ff 3*/
--color-info-dark: 17 50 100; /* #113264 12*/
--color-success: 24 121 78; /* #18794e */
--color-success-light: 233 249 238; /* #e9f9ee */
--color-success-dark: 25 59 45; /* #193b2d */
--color-warning: 119 95 40; /* #775f28 */
--color-warning-light: 255 248 198; /* #fff8c6 */
--color-warning-dark: 71 59 31; /* #473b1f */
--color-error: 198 42 47; /* #c62a2f */
--color-error-light: 255 239 239; /* #ffefef */
--color-error-dark: 100 23 35; /* #641723 */
}
html {
@apply bg-skin-neutral-1 text-skin-neutral-12;
}
}
@layer components {
.input-error {
@apply !ring-skin-error;
}
}
And these variables can be used in your custom Vue Components, with the skin
prefix, like this:
<h1 class="text-skin-primary-12">
My Custom Title, with high-contrast text...
</h1>
<p class="text-skin-neutral-11">
My Custom Description, with low-contrast text...
</p>
The variables will be available in your custom css files if/when needed, throught the Tailwind's theme function, like this:
.button {
color: theme(colors.skin.primary.11);
background-color: theme(colors.skin.primary.3);
}
The resources/image directory
Here you will find the logo.svg
file, which is used as the logo on the Authentication
page and in the Sidebar
of the logged view of your application. You can replace it with your own logo.
The resources/js directory
Here you will find the app.js
file, which is the main (entrypoint) JavaScript file of your application. This app.js
file will:
- Import the
app.css
file, enabling it to be handled by Vite. - Import the Remix Icons CSS, allowing the library to be utilized in your project.
- Configure support for streamlined translations in your Vue Components.
- Import the Ziggy library, enabling the use of Laravel routes in your Vue Components.
- Configure the Inertia app to utilize the resources mentioned above.
The resources/js/Components directory
Here you will find some Modular Components that are automatically available globally for use in your application. You are free to create your own global components here, and prefixing the component's name with App
(like AppMyCustomComponent.vue
), will ensure these components are automatically loaded in your application when needed, through the resources/js/Resolvers/AppComponentsResolver.js
that configures the unplugin-vue-components to facilitate this autoload mechanism.
The resources/js/Composables directory
This directory houses some Vue Composables functions that assist in solving common tasks without code duplication, offering an easy-to-use API.
Some available Composables include:
- useAuthCan: A Composable aiding with the ACL (Access Control List) System, allowing the logged user to view only the interface elements they have permissions to use.
- useDataSearch: A Composable assisting in implementing a search feature in your data grids.
- useFormContext: A Composable aiding in detecting the state of a form, whether it's in
create
orupdate
mode. - useFormErrors: A Composable assisting in collecting, parsing, and displaying the validation errors reported by Laravel.
- useTitle: A Composable aiding in setting the form title of the page, according to the form context, for example:
Create User
orEdit User
.
The resources/js/Configs directory
This directory contains the menu.js
file, which is the configuration file for the Sidebar
menu of your application. You can add, remove, or edit the menu items here.
The resources/js/Layouts directory
This directory houses two layouts used by Modular:
- The
GuestLayout.vue
layout, used by theAuthentication
page and reset password forms, primarily intended for unauthenticated users. - The
AuthenticatedLayout.vue
layout, used by theDashboard
page and all other pages of your application, primarily intended for authenticated users.
The resources/js/Pages directory
In this directory, you'll find the Pages
returned by the Laravel Controllers as Inertia Pages. By default, each new module you create in the modules
directory will have a corresponding directory within Pages
. For instance, if you create a Customer
module, a Customer
directory will also be created inside the Pages
directory, resembling:
./modules
└── Customer
./resources/js/Pages/Customer
├── CustomerForm.vue
└── CustomerIndex.vue
The CustomerForm.vue
file is designated for creating
and editing
customers, while the CustomerIndex.vue
file is used for listing
customers, typically in a searchable, paginated Data Grid.
As your pages begin to expand, it's advisable to break them down into smaller components, craft custom composables, configuration files, and so forth. Hence, it's usual to see a structure like the following within your Pages/Customer
directory:
./resources/js/Pages/Customer
├── Components
├── Composables
├── CustomerForm.vue
└── CustomerIndex.vue
This arrangement facilitates a more organized and manageable codebase, especially as the complexity of your pages increases.
You have the flexibility to organize your Pages
directory as you see fit. A practical approach to begin with is to establish a 1:1 mapping between your module's controllers and your Pages
directories. For instance, if you have two controllers named CustomerController
and CustomerStatsController
, you could create two corresponding directories within the Pages
directory, like Pages/Customer
and Pages/CustomerStats
.
The resources/js/Plugins directory
This directory contains the Translations.js
Modular Plugin, which globally provides a translate function __('name')
to your Vue Components. It's integrated with the default Laravel translation files, so you can define the translations as usual in your Laravel applications, and utilize the same keys to translate content in your Vue Components.
The resources/js/Resolvers directory
This directory contains the AppComponentsResolver.js
file. This file is the configuration file that sets up the unplugin-vue-components to autoload the Modular Global Components found in the resources/js/Components
directory and prefixed with the App
namespace.