| name | design-system |
| description | Expert knowledge of the Nuclia design system — @nuclia/sistema and @guillotinaweb/pastanaga-angular. Use this skill whenever a task involves ANY of the following: creating or modifying Angular UI components in dashboard, rao, manager-v2, or nucliadb-admin; using or overriding design tokens (colours, spacing, typography); adding icons, modals, toasts, dropdowns, tables, tabs, form fields, or any pa-/nsi- prefixed component; styling with SCSS tokens or utility classes; implementing settings pages, configuration rows, cards, badges, labels, progress indicators, or navigation elements. Don't wait to be asked explicitly about design system — if the task is about frontend UI in this monorepo, this skill applies.
|
Nuclia Design System — Quick Reference
Architecture
@guillotinaweb/pastanaga-angular ← base (pa- prefix, NgModule-based, general UI)
↓ theme overrides via SCSS @forward…with()
@nuclia/sistema ← Nuclia layer (nsi- prefix, mostly standalone)
↓ import in apps
dashboard / rao / manager-v2 / nucliadb-admin
Key rule: Always prefer SisModalService / SisToastService (from @nuclia/sistema) over the
raw Pastanaga equivalents. They apply opinionated defaults (cancelAspect: 'basic', toast titles).
Mandatory: Demo Page for Every Sistema Component
Whenever a component is added to or updated in @nuclia/sistema, a corresponding demo page
in apps/sistema-demo must also be added or updated.
Demo pages live in apps/sistema-demo/src/app/sistema-pages/. Follow this checklist:
1 — Create the demo folder
Create sistema-pages/<component-name>/ with these files:
<component-name>.component.ts
@Component({
selector: 'nsd-sistema-<name>',
templateUrl: './<component-name>.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
})
export class Sistema<Name>DemoComponent { ... }
<component-name>.component.html — uses the pa-demo-* DSL:
<pa-demo-page>
<pa-demo-title>Component Name</pa-demo-title>
<pa-demo-description>
<p>Brief description of what the component does.</p>
</pa-demo-description>
<pa-demo-examples>
</pa-demo-examples>
<pa-demo-code>
<pre><code>{{ codeSnippet }}</code></pre>
</pa-demo-code>
</pa-demo-page>
Add <component-name>.component.scss only when additional demo-page styles are needed.
2 — Register in sistema-pages.module.ts
In apps/sistema-demo/src/app/sistema-pages/sistema-pages.module.ts:
- Add the demo component class to
declarations
- Add any new
NsiXxxComponent (or module) from @nuclia/sistema to imports
3 — Export from index.ts
Add a barrel export to apps/sistema-demo/src/app/sistema-pages/index.ts:
export * from './sistema-<name>/sistema-<name>.component';
4 — Register in the app menu
In apps/sistema-demo/src/app/app.component.ts, add an entry to the menu array under the
'Nuclia Sistema' section, keeping alphabetical order by title:
{ path: 'sistema-<name>', title: '<Display Name>' }
Reference example
apps/sistema-demo/src/app/sistema-pages/sistema-spinner/ is the canonical minimal example
(no SCSS needed, single component, three size variants shown in pa-demo-examples +
pa-demo-code).
SCSS Setup (every app's global styles)
@use '@nuclia/sistema/styles/apps-common';
@use 'path/to/pastanaga-angular/src/styles/core';
@forward '@nuclia/sistema/styles/variables';
In component .scss files bind to tokens via the includePaths alias:
@use 'variables' as v;
TypeScript Imports Cheat-Sheet
import {
BadgeComponent,
StatusComponent,
BackButtonComponent,
ButtonMiniComponent,
DropdownButtonComponent,
SegmentedButtonsComponent,
SearchInputComponent,
ExpandableTextareaComponent,
ActionCardComponent,
InfoCardComponent,
FolderListComponent,
FolderTreeComponent,
HomeContainerComponent,
StickyFooterComponent,
TwoColumnsConfigurationItemComponent,
ProgressBarComponent,
JsonViewerComponent,
LabelsExpanderComponent,
StandaloneMimeIconPipe,
} from '@nuclia/sistema';
import { SisLabelModule, SisPasswordInputModule, SisProgressModule } from '@nuclia/sistema';
import { SisModalService, SisToastService } from '@nuclia/sistema';
import {
PaButtonModule,
PaIconModule,
PaTableModule,
PaTabsModule,
PaTextFieldModule,
PaTogglesModule,
PaDropdownModule,
PaModalModule,
PaExpanderModule,
PaChipsModule,
PaScrollModule,
PaCardModule,
PaAvatarModule,
PaPopupModule,
PaTranslateModule,
PaTooltipModule,
} from '@guillotinaweb/pastanaga-angular';
import { AccordionComponent, AccordionItemComponent } from '@guillotinaweb/pastanaga-angular';
import { ModalService, ToastService, BreakpointObserver } from '@guillotinaweb/pastanaga-angular';
import {
Kind,
Size,
Aspect,
ViewportMode,
ModalRef,
ModalConfig,
ConfirmationData,
ToastConfig,
AvatarModel,
} from '@guillotinaweb/pastanaga-angular';
Services
SisModalService — openModal / openConfirm
private modalService = inject(SisModalService);
const ref: ModalRef = this.modalService.openModal(MyDialogComponent, new ModalConfig({ data: { id: '123' } }));
ref.onClose.subscribe(result => { });
this.modalService.openConfirm({
title: 'Delete this resource?',
description: 'This cannot be undone.',
isDestructive: true,
confirmLabel: 'Delete',
cancelLabel: 'Cancel',
}).onClose.subscribe(confirmed => { if (confirmed) this.doDelete(); });
Custom modal component pattern:
@Component({
template: `
<pa-modal-dialog>
<pa-modal-title>My Title</pa-modal-title>
<pa-modal-content>{{ modal.config.data.id }}</pa-modal-content>
<pa-modal-footer>
<pa-button (click)="modal.close(true)">OK</pa-button>
<pa-button
aspect="basic"
(click)="modal.dismiss()">
Cancel
</pa-button>
</pa-modal-footer>
</pa-modal-dialog>
`,
})
export class MyDialogComponent extends BaseModalComponent {
constructor(
public override ref: ModalRef,
protected override cdr: ChangeDetectorRef,
) {
super(ref, cdr);
}
}
Import BaseModalComponent, ModalRef, ModalConfig, PaModalModule from @guillotinaweb/pastanaga-angular.
SisToastService — 4 methods
private toast = inject(SisToastService);
this.toast.success('Resource saved successfully.');
this.toast.error('Something went wrong.');
this.toast.warning('Quota is running low.');
this.toast.info('Processing started in background.');
Each method auto-sets title, icon, and closeButtonConf. Never use ToastService directly — use SisToastService.
Icons
Always use <pa-icon name="GLYPH_NAME">. There are ~110 glyphs in libs/sistema/glyphs/.
Key icon names:
arrow-left arrow-right arrow-up arrow-down
check circle-check circle-dash cross
warning info question
add remove edit delete copy download upload
eye eye-closed lock unlock settings
search filter sort
file file-pdf file-empty spreadsheet image play audio chat link
folder home account agent clock calendar
star heart tag label book
chevron-right chevron-left chevron-up chevron-down
more-options close expand collapse
Register the sprite once per app: AngularSvgIconModule.forRoot() in the root module,
and include assets/glyphs-sprite.svg in angular.json assets.
For file-type icons, use the mimeIcon pipe: {{ mimeType | mimeIcon }} or StandaloneMimeIconPipe.
Utility CSS Classes (from _apps-common.scss / styles.scss)
| Class | Usage |
|---|
.page-spacing | Full-page container with topbar-aware min-height and layout padding |
.page-title | Inside .page-spacing — heading row using display-small typography |
.page-description | Inside .page-spacing — muted body text below title |
.form-container | flex column gap-16 — wraps a form's fields |
.form-container.small-gap | Same but gap-12 |
.inline-form | flex row gap-16 — side-by-side fields |
.inline-form.full-width | Stretches to 100% width |
.container-with-border | Rounded bordered box with padding: 16px |
.separator-line | 1px $color-neutral-lighter horizontal divider |
.separator-word | Lowercase grey text separator (e.g. "or") |
a.accent-link | Blue text link using $color-text-link-accent |
.nsi-list | Styled list — hover states + hidden action buttons on hover |
.close-button | Absolute top-right close button |
.pa-modal .pa-modal-footer.pa-dual-buttons | Centred dual-button modal footer |
pa-tab.unauthorized | Faded, non-interactive locked tab |
pa-radio.unauthorized / pa-checkbox.unauthorized / pa-toggle.unauthorized | Locked controls |
Typography Tokens
In Angular app component SCSS (dashboard, rao, manager-v2, nucliadb-admin)
After @use 'apps/dashboard/src/variables' as *;, use SCSS functions/variables — NOT CSS custom properties.
_cssvariables.scss (which declares --font-size-* CSS vars) is loaded only by standalone-css.scss for the search-widget, not by _core.scss used by Angular apps.
font-size: font-size(xxs);
font-size: font-size(xs);
font-size: font-size(s);
font-size: font-size(m);
font-size: font-size(l);
font-size: font-size(xl);
font-size: font-size(xxl);
font-weight: $font-weight-semi-bold;
font-family: $font-family-monospace;
font-family: $font-family-sans-serif;
Colour Palette (CSS custom properties)
Brand tokens — override any via --custom-color-* hooks in app global styles:
--color-primary-stronger --color-primary-strong --color-primary-regular
--color-primary-light --color-primary-lighter --color-primary-lightest
--color-secondary-stronger ... --color-secondary-lightest
--color-tertiary-stronger ... --color-tertiary-lightest
--color-dark-stronger --color-light-stronger
--color-neutral-regular --color-neutral-light --color-neutral-lighter --color-neutral-lightest
--shadow-small --shadow-default --shadow-medium --shadow-large
Critical rule: Never hardcode colour hex values (#666, #ccc, #f0f0f0, #999, etc.) in
component SCSS. Always use theme variables:
$color-neutral-regular for muted text / axis labels
$color-neutral-light for borders, tick lines, secondary fills
$color-neutral-lighter for dividers, light borders
$color-neutral-lightest for backgrounds, grid lines
$color-primary-regular for interactive / accent elements
$color-light-stronger for text on dark backgrounds
White-label override hooks:
--custom-color-primary-regular
--custom-font-family
--custom-topbar-logo-width
Spacing (SCSS)
rhythm(1) = 8px rhythm(2) = 16px rhythm(3) = 24px rhythm(4) = 32px
rhythm(5) = 40px rhythm(6) = 48px rhythm(8) = 64px rhythm(9) = 72px
$height-top-bar: rhythm(9) // 72px
$width-sidenav: rhythm(26) // 208px
$padding-left-page: rhythm(6) // 48px
$padding-right-page: rhythm(4) // 32px
$padding-top-page: rhythm(4) // 32px
$border-radius-default: 0.25rem
$border-radius-card: rhythm(1) // 8px
Critical rule: Never use hardcoded px values for spacing (gap, margin, padding, width, height).
Always use rhythm(N). For example:
gap: rhythm(3) not gap: 24px
margin-bottom: rhythm(2) not margin-bottom: 16px
padding: rhythm(1) rhythm(2) not padding: 8px 16px
To use rhythm() in a component SCSS, import the variables:
@use 'apps/dashboard/src/variables' as *;
Z-Index Stack (SCSS)
$z-index-toast: 10000
$z-index-modal-backdrop: 9999
$z-index-tooltip: 1200
$z-index-alert: 1000
$z-index-modal: 600
$z-index-popup: 400
$z-index-default: 1
$z-index-background: -50
$z-index-deepdive: -100
SCSS Mixins (from _mixins.scss)
@include dark-mode() // dark bg + light text
@include reset-button() // strip browser button styles
@include text-truncate() // ellipsis overflow
@include scrollbar-style($size, $bg, $bg-hover, $thumb, $thumb-active, $radius, $border) @include
scrollbar-black($bg: #fff);
Reference Files (load when needed)
These files contain the complete API for every component. Load the relevant one before
implementing any feature that involves those components.
| File | Contents |
|---|
references/sistema-components.md | All 22 nsi-* components — selector, inputs, outputs, module, snippet |
references/pastanaga-components.md | All pa-* components grouped by category |
references/tokens.md | Complete token reference — palette, buttons, table, tabs, textfield, toasts, toggle, shadows, scrollbar, expander, modal, menu, chips, card |
references/patterns.md | Complete working code recipes for common patterns (modal, toast, settings page layout, table, form, dropdown, tabs) |
When to load which file:
- Adding a new
nsi-* component → sistema-components.md
- Adding a
pa-button, pa-input, pa-table, pa-modal, etc. → pastanaga-components.md
- Overriding colours, spacing, or component styles via SCSS →
tokens.md
- Implementing a common UI pattern (settings row, confirmation flow, sorted table) →
patterns.md