@ng-solid/mcp 22.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/GUIDE.md +292 -0
  2. package/README.md +159 -0
  3. package/data/design-system.json +24204 -0
  4. package/data/icons.json +1 -0
  5. package/data/skills/ns-solid-components/SKILL.md +140 -0
  6. package/data/skills/ns-solid-forms/SKILL.md +183 -0
  7. package/data/skills/ns-solid-icons/SKILL.md +95 -0
  8. package/data/skills/ns-solid-install/SKILL.md +120 -0
  9. package/data/skills/ns-solid-style-contract/SKILL.md +84 -0
  10. package/data/skills/ns-solid-theming/SKILL.md +123 -0
  11. package/index.d.ts +3 -0
  12. package/index.js +38 -0
  13. package/lib/data.d.ts +26 -0
  14. package/lib/data.js +51 -0
  15. package/lib/install-skills.d.ts +1 -0
  16. package/lib/install-skills.js +159 -0
  17. package/lib/preset-validator.d.ts +44 -0
  18. package/lib/preset-validator.js +172 -0
  19. package/lib/prompts.d.ts +2 -0
  20. package/lib/prompts.js +173 -0
  21. package/lib/resources.d.ts +2 -0
  22. package/lib/resources.js +328 -0
  23. package/lib/server.d.ts +3 -0
  24. package/lib/server.js +31 -0
  25. package/lib/style-contract.d.ts +37 -0
  26. package/lib/style-contract.js +167 -0
  27. package/lib/template-parser.d.ts +29 -0
  28. package/lib/template-parser.js +230 -0
  29. package/lib/tools.components.d.ts +44 -0
  30. package/lib/tools.components.js +836 -0
  31. package/lib/tools.d.ts +9 -0
  32. package/lib/tools.guides.d.ts +15 -0
  33. package/lib/tools.guides.js +103 -0
  34. package/lib/tools.icons.d.ts +7 -0
  35. package/lib/tools.icons.js +106 -0
  36. package/lib/tools.js +18 -0
  37. package/lib/tools.tokens.d.ts +7 -0
  38. package/lib/tools.tokens.js +278 -0
  39. package/lib/types.d.ts +349 -0
  40. package/lib/types.js +11 -0
  41. package/package.json +52 -0
  42. package/skills/ns-solid-components/SKILL.md +77 -0
  43. package/skills/ns-solid-forms/SKILL.md +166 -0
  44. package/skills/ns-solid-icons/SKILL.md +95 -0
  45. package/skills/ns-solid-install/SKILL.md +119 -0
  46. package/skills/ns-solid-style-contract/SKILL.md +84 -0
  47. package/skills/ns-solid-theming/SKILL.md +123 -0
@@ -0,0 +1,140 @@
1
+ ---
2
+ name: ns-solid-components
3
+ description: Reference for @ng-solid/design-system's public component catalog — parent/child composition rules, service-driven components, and internal subcomponents that are not part of the public API.
4
+ ---
5
+
6
+ # Catálogo de componentes de ng-solid
7
+
8
+ ## Tabla generada
9
+
10
+ | Selector | Clase | Familia | Categoría |
11
+ | --- | --- | --- | --- |
12
+ | `ns-accordion` | `NsAccordionComponent` | accordion | Contenido |
13
+ | `ns-accordion-item` | `NsAccordionItemComponent` | accordion | — |
14
+ | `ns-alert` | `NsAlertComponent` | alert | Feedback |
15
+ | `ns-avatar` | `NsAvatarComponent` | avatar | Contenido |
16
+ | `ns-badge` | `NsBadgeComponent` | badge | Feedback |
17
+ | `ns-breadcrumb-item` | `NsBreadcrumbItemComponent` | breadcrumbs | — |
18
+ | `ns-breadcrumbs` | `NsBreadcrumbsComponent` | breadcrumbs | Navegación |
19
+ | `ns-button` | `NsButtonComponent` | button | Acciones |
20
+ | `ns-button-group` | `NsButtonGroupComponent` | button-group | Acciones |
21
+ | `ns-card` | `NsCardComponent` | card | Superficies |
22
+ | `ns-card-body` | `NsCardBodyComponent` | card | — |
23
+ | `ns-card-footer` | `NsCardFooterComponent` | card | — |
24
+ | `ns-card-header` | `NsCardHeaderComponent` | card | — |
25
+ | `ns-checkbox` | `NsCheckboxComponent` | form/checkbox | Formularios |
26
+ | `ns-chip` | `NsChipComponent` | chip | Feedback |
27
+ | `ns-comment` | `NsCommentComponent` | comment | Formularios |
28
+ | `ns-content-editable` | `NsContentEditableComponent` | form/directives | Formularios |
29
+ | `ns-date-picker` | `NsDatePickerComponent` | form/date-picker | Formularios |
30
+ | `ns-date-range-picker` | `NsDateRangePickerComponent` | form/date-picker | Formularios |
31
+ | `ns-description` | `NsDescriptionComponent` | description | Accesibilidad |
32
+ | `ns-divider` | `NsDividerComponent` | divider | Contenido |
33
+ | `ns-drawer` | `NsDrawerComponent` | drawer | Overlays |
34
+ | `ns-dropdown` | `NsDropdownComponent` | form/dropdown | Formularios |
35
+ | `ns-dropdown-item` | `NsDropdownItemDirective` | form/dropdown | — |
36
+ | `ns-editor` | `NsEditorComponent` | editor | Formularios |
37
+ | `ns-editor-color-picker` | `NsEditorColorPickerComponent` | editor | — |
38
+ | `ns-editor-link` | `NsEditorLinkComponent` | editor | — |
39
+ | `ns-editor-list` | `NsEditorListComponent` | editor | — |
40
+ | `ns-editor-paragraph` | `NsEditorParagraphComponent` | editor | — |
41
+ | `ns-editor-toggle` | `NsEditorToggleCommandComponent` | editor | — |
42
+ | `[nsField]` | `NsFieldDirective` | a11y | — |
43
+ | `ns-image-upload` | `NsImageUploadComponent` | image-upload | Formularios |
44
+ | `ns-input` | `NsInputComponent` | form/input | Formularios |
45
+ | `ns-input-code` | `NsInputCodeComponent` | form/input-code | Formularios |
46
+ | `ns-input-group` | `NsInputGroupComponent` | form/input-group | Formularios |
47
+ | `ns-input-group-addon` | `NsInputGroupAddonComponent` | form/input-group | — |
48
+ | `ns-input-number` | `NsInputNumberComponent` | form/input-number | Formularios |
49
+ | `ns-input-search` | `NsInputSearchComponent` | form/input-search | Formularios |
50
+ | `ns-link` | `NsLinkComponent` | link | Navegación |
51
+ | `ns-meter` | `NsMeterComponent` | meter | Datos |
52
+ | `ns-modal` | `NsModalComponent` | modal | Overlays |
53
+ | `ns-pagination` | `NsPaginationComponent` | pagination | Navegación |
54
+ | `ns-password` | `NsPasswordComponent` | form/password | Formularios |
55
+ | `ns-popover` | `NsPopoverComponent` | popover | Overlays |
56
+ | `ns-progress-bar` | `NsProgressBarComponent` | progress-bar | Feedback |
57
+ | `ns-radio` | `NsRadioComponent` | form/radio | Formularios |
58
+ | `ns-radio-group` | `NsRadioGroupComponent` | form/radio | Formularios |
59
+ | `ns-schedule` | `NsScheduleComponent` | schedule | Datos |
60
+ | `ns-schema` | `NsSchemaMarkupComponent` | schema | — |
61
+ | `ns-skeleton` | `NsSkeletonComponent` | skeleton | Feedback |
62
+ | `ns-slider` | `NsSliderComponent` | form/slider | Formularios |
63
+ | `ns-surface` | `NsSurfaceComponent` | surface | Superficies |
64
+ | `ns-switch` | `NsSwitchComponent` | form/switch | Formularios |
65
+ | `ns-tab` | `NsTabComponent` | tabs | — |
66
+ | `ns-tabs` | `NsTabsComponent` | tabs | Navegación |
67
+ | `ns-tag` | `NsTagComponent` | tag-group | — |
68
+ | `ns-tag-group` | `NsTagGroupComponent` | tag-group | Formularios |
69
+ | `[nsTemplate]` | `TemplateDirective` | template | — |
70
+ | `ns-textarea` | `NsTextAreaComponent` | form/text-area | Formularios |
71
+ | `ns-toast` | `NsToastComponent` | toast | Feedback |
72
+ | `ns-tooltip` | `NsTooltipComponent` | tooltip | — |
73
+ | `[nsTooltip]` | `NsTooltipDirective` | tooltip | Overlays |
74
+
75
+ Todos se importan desde `@ng-solid/design-system` (entry point único —
76
+ ver la skill `ns-solid-install`) y son standalone: deben declararse en
77
+ `imports: [...]` de quien los usa.
78
+
79
+ ## Reglas de composición padre → hijo
80
+
81
+ Estos pares no son componentes independientes: el hijo sólo tiene sentido
82
+ proyectado dentro del padre, y ambos deben estar en `imports`:
83
+
84
+ | Padre | Hijo(s) | Notas |
85
+ | ---------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
86
+ | `ns-tabs` | `ns-tab` | Cada `ns-tab` es una pestaña; `ns-tabs` las descubre vía `contentChildren(NsTabComponent)`. |
87
+ | `ns-card` | `ns-card-header`, `ns-card-body`, `ns-card-footer` | Tres selectores adicionales del mismo fichero `card.component.ts`; son slots con nombre, no requieren los tres a la vez. |
88
+ | `ns-accordion` | `ns-accordion-item` | `ns-accordion` gestiona `expandedKeys` como `model()`; los items son sólo presentación + trigger. |
89
+ | `ns-dropdown` | `ns-dropdown-item` | `ns-dropdown-item` es una **directiva** (`NsDropdownItemDirective`), no un componente — se aplica sobre el elemento que hace de opción. |
90
+ | `ns-tag-group` | `ns-tag` | Agrupa chips seleccionables/removibles. |
91
+ | `ns-breadcrumbs` | `ns-breadcrumb-item` | Cada nivel de la ruta es un `ns-breadcrumb-item`. |
92
+ | `ns-radio-group` | `ns-radio` | `ns-radio-group` es el `ControlValueAccessor` (hereda de `NsValueAccessor`, ver `ns-solid-forms`); `ns-radio` por sí solo **no** implementa CVA. |
93
+
94
+ `ns-input-group` es sólo un wrapper de layout — no tiene `label` ni `wrap`;
95
+ los inputs de formulario y sus labels van en el `ns-input`/`ns-input-number`
96
+ proyectado dentro (`ns-input-group-addon` para prefijos/sufijos).
97
+
98
+ ## Componentes que se usan vía servicio
99
+
100
+ Estos no se instancian con una etiqueta en el template del consumidor — se
101
+ abren/gestionan imperativamente inyectando el servicio correspondiente:
102
+
103
+ - **`NsToastService`** → produce instancias de `ns-toast` (y el
104
+ subcomponente interno `ns-toast-region` que las hostea).
105
+ - **`NsModalService`** → abre `ns-modal`, con contenido proyectado o un
106
+ `contentComponent` tipado.
107
+ - **`NsDrawerService`** → abre `ns-drawer`.
108
+ - **`NsScheduleService`** → coordina el estado del calendario detrás de
109
+ `ns-schedule` y sus celdas/eventos internos.
110
+
111
+ `NsThemeService` (ver `ns-solid-theming`) y `NsOverlayService` (posicionamiento,
112
+ focus trap, scroll lock, `inert` — usado internamente por
113
+ popover/tooltip/drawer/modal) también son servicios públicos, pero no
114
+ "envuelven" un componente de la misma forma.
115
+
116
+ ## Subcomponentes internos — no exportados
117
+
118
+ Existen en el código pero **no** están en el barrel público
119
+ (`visibility: 'internal'` en el snapshot). Un agente no debe importarlos ni
120
+ sugerirlos; si un usuario pregunta por ellos, la respuesta correcta es
121
+ señalar el componente público que los engloba:
122
+
123
+ - `ns-schedule-cell`, `ns-schedule-day`, `ns-schedule-event`,
124
+ `ns-schedule-week`, `ns-schedule-workweek` → usar `ns-schedule`.
125
+ - `ns-date-picker-calendar` → usar `ns-date-picker` / `ns-date-range-picker`.
126
+ - `ns-toast-region` → se instancia sólo a través de `NsToastService`, nunca
127
+ directamente.
128
+
129
+ ## Variantes visuales — lenguaje HeroUI
130
+
131
+ Los nombres de variante son consistentes entre componentes:
132
+
133
+ - `solid` — relleno de acción sólido.
134
+ - `soft` — versión plana (`flat` en HeroUI).
135
+ - `outline` — bordeada (`bordered` en HeroUI).
136
+ - `ghost` — ligera (`light` en HeroUI).
137
+
138
+ Cada familia posee sus propios tokens `--ns-component-<familia>-*` — no
139
+ tomes prestados los tokens de otro componente aunque el valor visual
140
+ coincida.
@@ -0,0 +1,183 @@
1
+ ---
2
+ name: ns-solid-forms
3
+ description: Build forms with @ng-solid/design-system's ControlValueAccessor controls — formControlName/[(value)] bindings, the 14 inputs inherited from NsValueAccessor, and [nsField] wiring.
4
+ ---
5
+
6
+ # Formularios con ng-solid
7
+
8
+ ## Controles CVA y sus inputs heredados
9
+
10
+ | Selector | Clase | Inputs propios (además de los 14 heredados) |
11
+ | --- | --- | --- |
12
+ | `ns-checkbox` | `NsCheckboxComponent` | — |
13
+ | `ns-content-editable` | `NsContentEditableComponent` | `maxLength` |
14
+ | `ns-date-picker` | `NsDatePickerComponent` | `firstDayOfWeek`, `maxDate`, `minDate`, `unavailableDates` |
15
+ | `ns-date-range-picker` | `NsDateRangePickerComponent` | `firstDayOfWeek`, `maxDate`, `minDate`, `unavailableDates` |
16
+ | `ns-dropdown` | `NsDropdownComponent` | `collection`, `emptyText`, `loading`, `placement`, `searchable`, `searchPlaceholder`, `selectionMode` |
17
+ | `ns-input` | `NsInputComponent` | — |
18
+ | `ns-input-code` | `NsInputCodeComponent` | `length` |
19
+ | `ns-input-number` | `NsInputNumberComponent` | `currency`, `format`, `max`, `min`, `step` |
20
+ | `ns-input-search` | `NsInputSearchComponent` | — |
21
+ | `ns-password` | `NsPasswordComponent` | `forgotLink`, `showAndHide` |
22
+ | `ns-radio-group` | `NsRadioGroupComponent` | — |
23
+ | `ns-slider` | `NsSliderComponent` | `max`, `min`, `range`, `step` |
24
+ | `ns-switch` | `NsSwitchComponent` | — |
25
+ | `ns-textarea` | `NsTextAreaComponent` | `rows` |
26
+
27
+ Todos heredan de `NsValueAccessor`: 14 `input()` (`type`, `placeholder`, `label`, `description`, `errorMessage`, `required`, `invalid`, `readonly`, `name`, `ariaLabel`, `ariaDescribedBy`, `size`, `color`, `variant`) más `disabled` y `value` como `model()`.
28
+
29
+ Todos implementan `ControlValueAccessor` extendiendo la clase base abstracta
30
+ `NsValueAccessor<T>`
31
+ (`packages/design-system/src/lib/form/models/value-accessor.ts`). Muchos
32
+ componentes (`NsInputComponent`, `NsSwitchComponent`, `NsCheckboxComponent`…)
33
+ tienen cuerpo de clase vacío — **toda** su API pública viene por herencia. Si
34
+ consultas la API de un control y ves 0 inputs propios, no significa que el
35
+ componente no acepte nada: revisa `inheritedFrom: 'NsValueAccessor'`.
36
+
37
+ `NsValueAccessor` expone, en todos los controles por igual:
38
+
39
+ - `type`, `placeholder`, `label`, `description`, `errorMessage`, `required`,
40
+ `invalid`, `readonly`, `name`, `ariaLabel`, `ariaDescribedBy` — `input()`.
41
+ - `size: 'sm' | 'md' | 'lg'` (default `'md'`), `color: 'neutral' | 'primary'
42
+ | 'success' | 'warning' | 'danger'` (default `'neutral'`), `variant:
43
+ 'solid' | 'soft' | 'outline' | 'ghost'` (default `'outline'`) — `input()`.
44
+ - `disabled` — **`model()`**, no `input()`. Por eso `formControl.disable()` /
45
+ `.enable()` funciona de forma transparente: `setDisabledState` (parte del
46
+ contrato `ControlValueAccessor`) llama a `this.disabled.set(isDisabled)`
47
+ por debajo.
48
+ - `value` — también `model()`, escrito por Angular Forms vía `writeValue` y
49
+ leído/emitido por el propio control con `setValue()`.
50
+
51
+ ## Bindings — los tres equivalentes
52
+
53
+ ```html
54
+ <!-- Reactive Forms -->
55
+ <ns-input formControlName="email" label="Correo" />
56
+
57
+ <!-- Template-driven -->
58
+ <ns-input [(ngModel)]="email" label="Correo" />
59
+
60
+ <!-- Binding directo al model, sin FormsModule -->
61
+ <ns-input [(value)]="email" label="Correo" />
62
+ ```
63
+
64
+ Los tres funcionan porque el control implementa `ControlValueAccessor` (los
65
+ dos primeros) y expone `value` como `model()` (el tercero). Para
66
+ `disabled` como two-way binding equivalente: `[(disabled)]="isDisabled"`.
67
+
68
+ ## `[nsField]` — vincular label, descripción y error
69
+
70
+ `NsFieldDirective` (selector `[nsField]`,
71
+ `packages/design-system/src/lib/a11y/field-context.ts`) provee un
72
+ `NsFieldContext` al subárbol. El control lo consume vía `inject(NsFieldContext,
73
+ { optional: true, skipSelf: true })` para resolver su `id` real
74
+ (`controlId()`), y componentes como `NsDescriptionComponent` (hint) se
75
+ registran contra el mismo contexto para el `aria-describedby`:
76
+
77
+ ```html
78
+ <div nsField="email">
79
+ <ns-input formControlName="email" label="Correo" />
80
+ <ns-description id="email-hint"
81
+ >Usaremos este correo para notificarte.</ns-description
82
+ >
83
+ </div>
84
+ ```
85
+
86
+ Sin `[nsField]`, cada control genera su propio id vía `NsAriaIdService`; con
87
+ él, el id explícito (`nsField="email"`) gana y el `aria-describedby` se
88
+ compone automáticamente con las descripciones registradas en el contexto.
89
+
90
+ ## `errorMessage` + `invalid`
91
+
92
+ El mensaje de error sólo se renderiza cuando **ambos** están presentes:
93
+ `invalid()` es `true` y `errorMessage()` no es vacío/undefined (ver
94
+ `input.component.html`: `@if (invalid() && errorMessage())`). Calcula
95
+ `invalid` desde el estado del `FormControl`, no lo dejes fijo:
96
+
97
+ ```html
98
+ <ns-input
99
+ [invalid]="control.invalid && control.touched"
100
+ [errorMessage]="
101
+ control.hasError('required') ? 'Este campo es obligatorio' : undefined
102
+ "
103
+ />
104
+ ```
105
+
106
+ ## Ejemplo completo con `FormBuilder`
107
+
108
+ ```ts
109
+ import { Component, inject } from '@angular/core';
110
+ import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
111
+ import {
112
+ NsButtonComponent,
113
+ NsFieldDirective,
114
+ NsInputComponent,
115
+ NsPasswordComponent,
116
+ NsSwitchComponent
117
+ } from '@ng-solid/design-system';
118
+
119
+ @Component({
120
+ selector: 'app-signup-form',
121
+ standalone: true,
122
+ imports: [
123
+ ReactiveFormsModule,
124
+ NsFieldDirective,
125
+ NsInputComponent,
126
+ NsPasswordComponent,
127
+ NsSwitchComponent,
128
+ NsButtonComponent
129
+ ],
130
+ template: `
131
+ <form [formGroup]="form" (ngSubmit)="submit()">
132
+ <div nsField="signup-email">
133
+ <ns-input
134
+ formControlName="email"
135
+ label="Correo"
136
+ type="email"
137
+ [invalid]="form.controls.email.invalid && form.controls.email.touched"
138
+ [errorMessage]="'Ingresa un correo válido'"
139
+ />
140
+ </div>
141
+
142
+ <div nsField="signup-password">
143
+ <ns-password
144
+ formControlName="password"
145
+ label="Contraseña"
146
+ [invalid]="
147
+ form.controls.password.invalid && form.controls.password.touched
148
+ "
149
+ [errorMessage]="'Mínimo 8 caracteres'"
150
+ />
151
+ </div>
152
+
153
+ <ns-switch formControlName="acceptTerms" label="Acepto los términos" />
154
+
155
+ <ns-button variant="primary" type="submit" [disabled]="form.invalid">
156
+ Crear cuenta
157
+ </ns-button>
158
+ </form>
159
+ `
160
+ })
161
+ export class SignupFormComponent {
162
+ private readonly fb = inject(FormBuilder);
163
+
164
+ readonly form = this.fb.group({
165
+ email: this.fb.control('', [Validators.required, Validators.email]),
166
+ password: this.fb.control('', [
167
+ Validators.required,
168
+ Validators.minLength(8)
169
+ ]),
170
+ acceptTerms: this.fb.control(false, [Validators.requiredTrue])
171
+ });
172
+
173
+ submit(): void {
174
+ if (this.form.valid) {
175
+ // this.form.getRawValue()
176
+ }
177
+ }
178
+ }
179
+ ```
180
+
181
+ Nota: `NsPasswordComponent` añade `forgotLink` y `showAndHide` propios sobre
182
+ la base heredada; `NsSwitchComponent` no añade inputs propios (todo viene de
183
+ `NsValueAccessor`).
@@ -0,0 +1,95 @@
1
+ ---
2
+ name: ns-solid-icons
3
+ description: Render Lucide icons with @ng-solid/icons' <ns-icon> component — registering symbols with provideIcons/provideIconsPatch and resolving camelCase symbol names.
4
+ ---
5
+
6
+ # Iconos con @ng-solid/icons
7
+
8
+ ## El componente `<ns-icon>`
9
+
10
+ ```html
11
+ <ns-icon name="alarm-clock" [size]="20" />
12
+ ```
13
+
14
+ `NsIconsComponent` (selector `ns-icon`) acepta:
15
+
16
+ - `name: string` — el nombre **kebab-case** del icono Lucide (`alarm-clock`,
17
+ no `alarmClock`).
18
+ - `size: number` (default `24`) — acepta el atributo plano (`numberAttribute`),
19
+ así que `<ns-icon name="x" size="16">` también funciona.
20
+ - `color: string` (default `'currentColor'`).
21
+ - `strokeWidth: number` (default `2`).
22
+ - `viewBox: string` (default `'0 0 24 24'`).
23
+
24
+ Internamente resuelve el SVG contra un `Map` poblado por
25
+ `provideIcons`/`provideIconsPatch`; si el nombre no fue registrado, emite un
26
+ `console.warn` y no renderiza el path.
27
+
28
+ ## Registrar iconos — dos providers, dos momentos distintos
29
+
30
+ **`provideIcons(icons)`** — en el bootstrap de la app (`appConfig.providers`
31
+ o `bootstrapApplication`). Usar para los iconos que se necesitan siempre:
32
+
33
+ ```ts
34
+ import { provideIcons, alarmClock, x } from '@ng-solid/icons';
35
+
36
+ export const appConfig: ApplicationConfig = {
37
+ providers: [
38
+ // ...
39
+ provideIcons([alarmClock, x])
40
+ ]
41
+ };
42
+ ```
43
+
44
+ **`provideIconsPatch(icons)`** — a nivel de `Route.providers`, para iconos
45
+ que sólo hace falta cargar cuando se navega a esa ruta (lazy):
46
+
47
+ ```ts
48
+ const routes: Routes = [
49
+ {
50
+ path: 'products',
51
+ providers: [provideIconsPatch([alarmClock])],
52
+ loadComponent: () =>
53
+ import('./products.component').then((m) => m.ProductsComponent)
54
+ }
55
+ ];
56
+ ```
57
+
58
+ `provideIconsPatch` registra además `NsIconsPatchService`, que
59
+ `NsIconsComponent` detecta e invoca (`doPatch()`) antes de intentar resolver
60
+ el icono, para que el registro parcial se aplique aunque el componente ya
61
+ esté instanciado.
62
+
63
+ ## Los símbolos se importan en camelCase
64
+
65
+ El catálogo generado (`packages/icons/src/lib/generated/index.ts`) exporta
66
+ un símbolo por icono con el nombre kebab-case convertido a camelCase:
67
+
68
+ | Icono (`name` en `<ns-icon>`) | Símbolo importado de `@ng-solid/icons` |
69
+ | --------------------------------- | -------------------------------------- |
70
+ | `alarm-clock` | `alarmClock` |
71
+ | `a-arrow-down` | `aArrowDown` |
72
+ | `align-horizontal-justify-center` | `alignHorizontalJustifyCenter` |
73
+ | `circle-check` | `circleCheck` |
74
+
75
+ Cada símbolo es un objeto `{ name, icon }` (ver `NsIconDefinition`) — nunca
76
+ pases un string al provider, pasa el símbolo importado.
77
+
78
+ ## Catálogo
79
+
80
+ **1790 iconos** Lucide registrados en el catálogo (`NS_ICON_COUNT`), licencia ISC.
81
+
82
+ - Fuente: Lucide (`LUCIDE_SOURCE_URL` en `packages/icons/src/lib/generated/metadata.ts`).
83
+ - Licencia: **ISC** (`LUCIDE_SOURCE_LICENSE`).
84
+ - Usa la tool `search_icons` del MCP para encontrar el `name`/símbolo correcto
85
+ por palabra clave en vez de adivinar el nombre exacto de Lucide.
86
+
87
+ ## Errores comunes
88
+
89
+ - Registrar el símbolo pero usar un `name` distinto en el template (deben
90
+ coincidir exactamente, kebab-case).
91
+ - Olvidar `provideIcons`/`provideIconsPatch` por completo — el icono no
92
+ truena, simplemente no renderiza y deja un warning en consola.
93
+ - Confundir `provideIcons` (bootstrap, global) con `provideIconsPatch`
94
+ (`Route.providers`, por ruta) — usar el segundo en el bootstrap no falla,
95
+ pero pierde el propósito de carga diferida.
@@ -0,0 +1,120 @@
1
+ ---
2
+ name: ns-solid-install
3
+ description: Install and bootstrap @ng-solid/design-system (Arvisfy) plus @ng-solid/icons in an Angular 21 standalone application, including the Tailwind bridge and theme provider.
4
+ ---
5
+
6
+ # Instalación de @ng-solid/design-system
7
+
8
+ ## Paquetes y peer dependencies
9
+
10
+ ```bash
11
+ pnpm add @ng-solid/design-system @ng-solid/icons
12
+ ```
13
+
14
+ `@ng-solid/design-system` declara estos peer dependencies (ver
15
+ `packages/design-system/package.json`):
16
+
17
+ - `@angular/core`, `@angular/common`, `@angular/forms`,
18
+ `@angular/platform-browser`, `@angular/router` en `^21.0.0`.
19
+ - `@ng-solid/icons` en `^22.0.5`.
20
+ - `tailwindcss` en `^4.0.0`, marcado **opcional** (`peerDependenciesMeta`) —
21
+ sólo hace falta si la app consume el puente de utilidades semánticas.
22
+
23
+ Versiones publicadas actualmente:
24
+
25
+ - `@ng-solid/design-system@22.2.1` (peer `@angular/*@^21.0.0`)
26
+ - `@ng-solid/icons@22.1.0`
27
+
28
+ Si tu app usa Tailwind, añade también el plugin de PostCSS:
29
+
30
+ ```bash
31
+ pnpm add -D tailwindcss @tailwindcss/postcss
32
+ ```
33
+
34
+ ## Entrypoint único — nada de subpaths
35
+
36
+ `@ng-solid/design-system` tiene **un solo entry point público**
37
+ (`ng-package.json` → `entryFile: src/index.ts`, sin `secondaryEntrypoints`).
38
+ Todo se importa desde la raíz del paquete:
39
+
40
+ ```ts
41
+ import { NsButtonComponent, NsInputComponent } from '@ng-solid/design-system';
42
+ ```
43
+
44
+ Nunca inventes rutas como `@ng-solid/design-system/button` — no existen. La
45
+ única excepción es el asset CSS publicado explícitamente como
46
+ `@ng-solid/design-system/themes/tailwind.css` (ver más abajo), que no es un
47
+ entry point de JS/TS.
48
+
49
+ ## Puente de Tailwind
50
+
51
+ Si la app importa el puente semántico, hazlo una sola vez en el stylesheet
52
+ global, después de las capas de Tailwind:
53
+
54
+ ```css
55
+ @import '@ng-solid/design-system/themes/tailwind.css';
56
+ ```
57
+
58
+ Ese fichero define el bridge `@theme inline` que mapea los tokens de
59
+ Tailwind a `--ns-semantic-*`, además de registrar localmente la fuente
60
+ variable Inter (sin peticiones de red en runtime).
61
+
62
+ ## Registrar el tema en `appConfig`
63
+
64
+ ```ts
65
+ import { Arvisfy, provideNsTheme } from '@ng-solid/design-system';
66
+ import { ApplicationConfig } from '@angular/core';
67
+
68
+ export const appConfig: ApplicationConfig = {
69
+ providers: [provideNsTheme({ preset: Arvisfy, colorScheme: 'system' })]
70
+ };
71
+ ```
72
+
73
+ `colorScheme: 'system'` sigue `prefers-color-scheme`. Ver la skill
74
+ `ns-solid-theming` para `defineNsPreset`, `NsThemeService.setScheme` y las
75
+ reglas de validación del preset.
76
+
77
+ ## Standalone: `imports` es obligatorio
78
+
79
+ Todos los componentes son **standalone**. Angular no los resuelve
80
+ automáticamente: hay que declararlos explícitamente en el `imports` del
81
+ componente (o del `NgModule`) que los usa. Omitir esto es el error más común
82
+ al generar código con un agente:
83
+
84
+ ```ts
85
+ import { Component } from '@angular/core';
86
+ import { NsButtonComponent } from '@ng-solid/design-system';
87
+
88
+ @Component({
89
+ selector: 'app-toolbar',
90
+ standalone: true,
91
+ imports: [NsButtonComponent], // obligatorio — <ns-button> no funciona sin esto
92
+ template: `<ns-button variant="primary">Guardar</ns-button>`
93
+ })
94
+ export class ToolbarComponent {}
95
+ ```
96
+
97
+ Esto vale también para directivas de composición (`NsFieldDirective`,
98
+ `NsTooltipDirective`, `NsDropdownItemDirective`, etc.) y para componentes
99
+ usados vía `contentChildren`/proyección (`ns-card-header`, `ns-tab`,
100
+ `ns-accordion-item`…): todos deben aparecer en `imports`.
101
+
102
+ ## Iconos
103
+
104
+ `@ng-solid/icons` se instala aparte y se registra con `provideIcons(...)` en
105
+ el bootstrap. Ver la skill `ns-solid-icons` para el detalle completo
106
+ (símbolos camelCase, `provideIconsPatch` para rutas con lazy loading).
107
+
108
+ ## Extender el preset
109
+
110
+ No copies `Arvisfy`; extiéndelo con `defineNsPreset` (ver `ns-solid-theming`
111
+ para las reglas de validación completas):
112
+
113
+ ```ts
114
+ import { Arvisfy, defineNsPreset } from '@ng-solid/design-system';
115
+
116
+ export const ProductTheme = defineNsPreset(Arvisfy, {
117
+ name: 'product',
118
+ primitive: { color: { brand: { 500: '#0052ff' } } }
119
+ });
120
+ ```
@@ -0,0 +1,84 @@
1
+ ---
2
+ name: ns-solid-style-contract
3
+ description: Follow ng-solid's enforced style architecture — ns-* BEM class naming, zero Tailwind utilities in markup, and the @reference-only SCSS contract checked by pnpm quality:styles.
4
+ ---
5
+
6
+ # Contrato de estilos de ng-solid
7
+
8
+ Este contrato lo enforcea en CI
9
+ `tools/scripts/check-design-system-styles.mjs` (`pnpm quality:styles`) sobre
10
+ `packages/design-system/src/lib` y `apps/demo/src`. Un PR que lo viole falla
11
+ el build, tanto en la librería como en aplicaciones que sigan la misma
12
+ convención.
13
+
14
+ ## 1. Clases de template: sólo `ns-*` BEM
15
+
16
+ Toda clase en `class="…"`, `[class.x]` o claves de objeto `[ngClass]` debe
17
+ matchear:
18
+
19
+ ```
20
+ ns-block__element--modifier
21
+ ```
22
+
23
+ Regex real usada por el gate:
24
+
25
+ ```
26
+ /^ns-[a-z0-9]+(?:-[a-z0-9]+)*(?:(?:__[a-z0-9]+(?:-[a-z0-9]+)*)?(?:--[a-z0-9]+(?:-[a-z0-9]+)*)?)?$/
27
+ ```
28
+
29
+ Válido: `ns-button`, `ns-button__icon`, `ns-button--danger`,
30
+ `ns-card__header--sticky`. Inválido: `btn-primary`, `NsButton`,
31
+ `ns_button`, `flex`.
32
+
33
+ ## 2. Cero utilidades Tailwind en markup
34
+
35
+ Ninguna clase de template puede empezar con un prefijo de utilidad Tailwind
36
+ (`flex`, `grid`, `gap-`, `p-`/`m-` + variantes, `text-`, `bg-`, `border`,
37
+ `rounded`, `shadow`, `absolute`/`relative`/`fixed`/`sticky`, `inset`/`top-`/
38
+ `bottom-`/`start-`/`end-`, `z-`, `w-`/`h-`, `min-`/`max-`, `overflow`,
39
+ `opacity`, `font-`, `items-`/`justify-`/`content-`). Tailwind se compone
40
+ **sólo** desde SCSS con `@apply`, nunca directamente en el HTML.
41
+
42
+ ## 3. Nada de `styles:` inline
43
+
44
+ Un componente no puede declarar estilos inline en el decorador
45
+ (`styles: [...]`). Usa `styleUrl` (o `styleUrls`) apuntando a un `.scss`
46
+ hermano.
47
+
48
+ ## 4. SCSS de componente: `@reference`, sin `@use`/`@import`, sin valores crudos
49
+
50
+ Cada `.scss` de componente (fuera de `lib/theme/`) debe:
51
+
52
+ - Incluir literalmente `@reference` (al bridge de Tailwind:
53
+ `@reference '../theme/tailwind.css';` o la ruta relativa que corresponda).
54
+ - **No** usar `@use` ni `@import` — extender Tailwind sólo vía `@reference`.
55
+ - **No** contener valores crudos: hex (`#fff`), `rgb()`/`rgba()`, literales
56
+ `px`/`rem`/`em`/`ms`/`s`, o `var(--x, fallback)` con valor por defecto.
57
+ Toda decisión visual se lee de un token `--ns-component-*` ya resuelto por
58
+ el preset (ver la skill `ns-solid-theming`) — sin fallback embebido, porque
59
+ el preset garantiza que la variable siempre está definida.
60
+
61
+ Ejemplo correcto:
62
+
63
+ ```scss
64
+ @reference '../theme/tailwind.css';
65
+
66
+ .ns-button {
67
+ @apply inline-flex items-center;
68
+ background: var(--ns-component-button-background);
69
+ color: var(--ns-component-button-foreground);
70
+ }
71
+ ```
72
+
73
+ ## 5. Prohibido reintroducir Bootstrap / `@popperjs`
74
+
75
+ El gate falla si `package.json` o `pnpm-lock.yaml` vuelven a traer
76
+ `bootstrap` o `@popperjs/*` — el design-system fue migrado fuera de esas
77
+ dependencias deliberadamente.
78
+
79
+ ## Validar un snippet antes de proponerlo
80
+
81
+ Usa la tool del MCP `validate_style_contract` (o `validate_component_usage`
82
+ para un uso completo de componente) contra cualquier markup/SCSS que generes
83
+ antes de entregarlo — es la misma lógica que corre `pnpm quality:styles`,
84
+ expuesta como función pura.