$npx -y skills add gdarko/laravel-vue-starter --skill vue-component-developmentActivate when creating or modifying Vue 3 components, pages, layouts, stores, or services in the frontend. Covers component patterns, DaisyUI usage, icon system, toast notifications, drawer patterns, form handling, and the SPA routing/auth flow. Use when working in resources/app/
| 1 | # Vue Component Development |
| 2 | |
| 3 | Patterns and conventions for building Vue 3 frontend components in this project. |
| 4 | |
| 5 | ## Component Architecture |
| 6 | |
| 7 | ### Directory Structure |
| 8 | |
| 9 | - `views/layouts/` — Layout wrappers (Page, Menu, Auth) |
| 10 | - `views/components/` — Reusable UI components (Button, TextInput, Table, Modal, etc.) |
| 11 | - `views/components/icons/` — Icon system (Icon, Avatar, Spinner) |
| 12 | - `views/components/input/` — Form input components |
| 13 | - `views/components/filters/` — Filter components for list pages |
| 14 | - `views/pages/` — Page-level components organized by auth/private/shared |
| 15 | - `stores/` — Pinia stores (auth, toast, alert, global) |
| 16 | - `services/` — TypeScript API service classes extending BaseService |
| 17 | - `helpers/` — Pure utility functions |
| 18 | |
| 19 | ### Component Conventions |
| 20 | |
| 21 | - Use Options API with `defineComponent()` and `setup()` function (project convention) |
| 22 | - Use `reactive()` for form state, `ref()` for simple values, `computed()` for derived state |
| 23 | - Import components explicitly — no global registration |
| 24 | - Props use camelCase in JS, kebab-case in templates |
| 25 | |
| 26 | ## UI Framework: DaisyUI 5 |
| 27 | |
| 28 | This project uses DaisyUI 5 on top of Tailwind CSS 4. Prefer DaisyUI component classes over hand-rolled Tailwind. |
| 29 | |
| 30 | ### Key classes |
| 31 | |
| 32 | - Buttons: `btn btn-primary`, `btn-success`, `btn-error`, `btn-outline`, `btn-ghost`, `btn-sm` |
| 33 | - Inputs: `input input-bordered`, `textarea textarea-bordered`, `select` |
| 34 | - Cards: `card bg-base-100 shadow`, `card-body`, `card-title` |
| 35 | - Tables: `table` with `hover` on `<tr>` |
| 36 | - Badges: `badge badge-success`, `badge-error`, `badge-warning`, `badge-info` |
| 37 | - Alerts: `alert alert-success`, `alert-error` |
| 38 | - Modal: `modal`, `modal-open`, `modal-box`, `modal-backdrop` |
| 39 | - Navigation: `navbar`, `menu`, `breadcrumbs`, `dropdown` |
| 40 | - Loading: `loading loading-spinner`, `skeleton` |
| 41 | - Layout: `drawer`, `drawer-side`, `drawer-content` |
| 42 | |
| 43 | ### Theme Colors |
| 44 | |
| 45 | Use semantic DaisyUI colors, not raw Tailwind colors: |
| 46 | - `primary` (teal), `secondary`, `accent`, `neutral` |
| 47 | - `success`, `warning`, `error`, `info` |
| 48 | - `base-100` (surface), `base-200` (background), `base-300` (border), `base-content` (text) |
| 49 | - Opacity variants: `text-base-content/50`, `border-base-300/50` |
| 50 | |
| 51 | ### Dark Mode |
| 52 | |
| 53 | The project supports light/dark themes via `data-theme` attribute on `<html>`. Both themes use the same teal primary. Custom sidebar colors use `--color-sidebar` / `--color-sidebar-content` CSS variables that adapt per theme. Never hardcode white/black — use semantic DaisyUI colors. |
| 54 | |
| 55 | ## Icon System |
| 56 | |
| 57 | Icons use a thin wrapper around [Heroicons](https://heroicons.com): |
| 58 | |
| 59 | ```vue |
| 60 | <Icon name="users" class="h-5 w-5" /> |
| 61 | ``` |
| 62 | |
| 63 | To add a new icon: import it in `views/components/icons/Icon.vue` from `@heroicons/vue/24/outline` and add a name→component entry to the `iconMap`. |
| 64 | |
| 65 | ## Toast Notifications |
| 66 | |
| 67 | Use the toast store for user feedback instead of inline alerts: |
| 68 | |
| 69 | ```js |
| 70 | import {useToastStore} from "@/stores/toast"; |
| 71 | const toastStore = useToastStore(); |
| 72 | toastStore.success('Done!'); |
| 73 | toastStore.error('Failed.'); |
| 74 | toastStore.info('FYI...'); |
| 75 | toastStore.warning('Careful!'); |
| 76 | ``` |
| 77 | |
| 78 | Toasts auto-dismiss after 5 seconds. They accept strings, arrays, or validation error objects. |
| 79 | |
| 80 | ## Drawer Pattern for CRUD |
| 81 | |
| 82 | List pages use a slide-in drawer instead of separate create/edit pages: |
| 83 | |
| 84 | - Drawer state: `reactive({ open, mode, loading, form })` |
| 85 | - Open with `drawer.open = true` and set `drawer.mode` to `'create'` or `'edit'` |
| 86 | - Close with `drawer.open = false`, refresh list on success |
| 87 | - Use `<Transition name="slide-right">` for animation and `<Teleport>` if needed |
| 88 | |
| 89 | ## API Services |
| 90 | |
| 91 | - Services extend `BaseService` or `ModelService` (for CRUD) |
| 92 | - `ModelService` provides: `index()`, `store()`, `update()`, `delete()`, `edit()`, `find()` |
| 93 | - `handleCreate()` and `handleUpdate()` manage loading states and toast notifications |
| 94 | - PUT/PATCH use POST with `_method` field to support FormData file uploads |
| 95 | |
| 96 | ## Form Handling |
| 97 | |
| 98 | - Use `reactive()` for form state |
| 99 | - Use `reduceProperties(form, ['roles'], 'id')` to flatten nested objects before submission |
| 100 | - Use `fillObject(form, data)` to populate form from API response |
| 101 | - Use `clearObject(form)` to reset form |