Skip to main content

start9-frontend

Start9 house style for every Angular + Taiga UI web frontend (StartOS ui/setup-wizard, StartWRT, StartTunnel, brochure-marketplace, start9-store, ops-server, support-server). Use BEFORE writing or reviewing any frontend code in these apps — components, templates, styles/CSS, forms, dialogs/toasts, routing, state, DI, i18n — and when bootstrapping a new Start9 web UI or upgrading Angular/Taiga. Includes doctrine, recipes, an antipattern catalog distilled from the fleet's refactor history, and a verified Taiga 5 API reference.

Source facts

Repository
Start9Labs/start-technologies
Last source activity
October 12, 2026 at 23:40
Detected SKILL.md language
English
Stars
1,991
Forks
198

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
15 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
start9-frontend
description
Start9 house style for every Angular + Taiga UI web frontend (StartOS ui/setup-wizard, StartWRT, StartTunnel, brochure-marketplace, start9-store, ops-server, support-server). Use BEFORE writing or reviewing any frontend code in these apps — components, templates, styles/CSS, forms, dialogs/toasts, routing, state, DI, i18n — and when bootstrapping a new Start9 web UI or upgrading Angular/Taiga. Includes doctrine, recipes, an antipattern catalog distilled from the fleet's refactor history, and a verified Taiga 5 API reference.
# Start9 Frontend — Angular + Taiga UI house style Canonical for **every Start9 web UI**, distilled from the fleet's codebases and from the refactor and review history that shaped them. Follow it and new code matches the fleet; deviate and expect it to be rewritten in review. **Authority order: Taiga UI docs > this skill > neighboring code.** The codebases are mid-migration — _"matches existing repo patterns" is not a quality bar_; old code is often exactly what this skill exists to eliminate. Never guess a Taiga API: verify against the `taiga-ui-mcp` MCP server or `https://taiga-ui.dev/llms-full.txt`. ## Reference files (read on demand — don't preload) | File | Read when | | ---------------------------- | --------------------------------------------------------------------------------------------------------- | | `references/bootstrap.md` | creating/touching app.config, main.ts, angular.json, icons, theming, or a new app/repo | | `references/components.md` | writing any component or template (selectors, hostDirectives, signal APIs, control flow) | | `references/styling.md` | about to write ANY CSS (the escalation ladder, breakpoints, tokens) | | `references/di.md` | providers, tokens, inject patterns | | `references/state.md` | services, data flow, loading/error UX, API/RPC integration | | `references/forms.md` | any form, validation, or submit flow | | `references/overlays.md` | dialogs, confirms, toasts, dropdowns, hints, drawers | | `references/routing.md` | route files, guards, navigation, URL state | | `references/conventions.md` | i18n strings, file naming, folder structure, import order | | `references/shared-libs.md` | before writing ANY utility/component in monorepo apps — it may exist in @start9labs/shared or marketplace | | `references/antipatterns.md` | reviewing code, refactoring, or upgrading Angular/Taiga (before→after catalog + verbatim review quotes) | | `references/taiga.md` | unsure of a Taiga 5 API; v4→v5 renames; need→primitive lookup table | | `references/recipes.md` | step-by-step: new page, dialog, form, table, control component, endpoint | | `references/repos.md` | repo-specific rules and build/verify commands | ## The fleet | App | Location | Angular | Taiga | Zone | Theme | i18n | Backend | | ---------------------- | ----------------------------------------------- | ------- | ----- | -------------------------- | ------------------------------------ | ------------------ | --------------------------------------------------- | | StartOS `ui` | `start-technologies` `projects/start-os/web/ui` | 22 | 5.11 | zone.js (zoneless pending) | dark, `provideTaiga({mode})` | yes (shared dicts) | JSON-RPC + PatchDB push | | `setup-wizard` | `projects/start-os/web/setup-wizard` | 22 | 5.11 | zone.js | dark | yes (shared) | JSON-RPC | | `start-tunnel` | `projects/start-tunnel/web` | 22 | 5.11 | **zoneless** | dark | yes (local dicts) | JSON-RPC + PatchDB | | `start-wrt` | `projects/start-wrt/web` | 22 | 5.11 | **zoneless** | dual (`TUI_DARK_MODE`) | yes (local dicts) | JSON-RPC, own HTTP stack, 5s polling | | `brochure-marketplace` | `projects/brochure-marketplace` | 22 | 5.11 | zone.js (legacy) | dark | yes (shared) | registry RPC direct | | `start9-store` | `ops/start9-store/web` | 22 | 5.22 | **zoneless** | light | no | REST + Zod via `/api` BFF, **SSR** | | `ops-server` | `ops/ops-server/web` | 22 | 5.14 | **zoneless** | dark, `#07a4ff`, Montserrat | no | REST `/_api`, same-origin Express | | `support-server` | `ops/support-server/portal/web` | 22 | 5.27 | **zoneless** | dual (theme setting), StartOS tokens | yes (local dicts) | RPC `/api` + socket.io, same-origin; `mock/` in dev | TypeScript ~6.0, rxjs ~7.8 everywhere. Taiga is **pinned exact** — bump only with the maintainer's blessing. Monorepo apps share **one Angular workspace rooted at the repo root**; ops repos are standalone. Zoneless is the target state — write all new code zoneless-safe. This table is the **only** place in the fleet where stack versions are written down — update it with every Angular/Taiga bump (other repos' docs deliberately carry no version specifics). ## Doctrine 1. **Taiga does it all.** Components, layout, forms, dialogs, icons, theming, animation. If you're hand-rolling HTML/CSS/JS for something that feels generic, Taiga ships it — look it up first. "If you think Taiga can't do something, you're probably wrong." 2. **Never guess a Taiga API.** Taiga 5 is fast-moving and easy to hallucinate. Verify every component/directive/token against the docs before use. 3. **Configure the design system, don't fight it.** The escalation ladder for any visual need: ① a Taiga primitive/appearance → ② an option provider (root or component `providers`) → ③ a `--tui-*` design-token override in the theme sheet → ④ a shared `g-*` utility class → ⑤ a few lines of `:host` layout CSS. Reaching for ⑤ first means you missed ①–④. 4. **Delete code.** The canonical refactors are net-negative. The best version has fewer lines, fewer files, fewer wrappers, fewer names. Don't restate framework defaults, don't name single-use values, don't duplicate markup per breakpoint. 5. **Signals at the component boundary; RxJS composes once, in services.** Components read signals; services own streams and convert with `toSignal` at the edge. Data is never manually subscribed in a component. 6. **One source of truth per fact.** Nav links live in one object; validation messages in one provider map; sizes in option providers; colors in the theme sheet. 7. **Everything host-related goes in the decorator.** `host: {}` for classes/attrs/listeners/ style bindings, `hostDirectives` for composition. `@HostBinding`/`@HostListener` are dead. 8. **English strings are i18n keys** (monorepo apps): every user-facing string goes through `| i18n` and exists in all five dictionaries; `tsc` enforces via the `i18nKey` type. 9. **Verification is `npm run check` + a manual pass; a test only where logic earns it.** `check` (strict + `strictTemplates`), the i18n check, a prod build and clicking through the app are the bar. A test is for state machinery a manual pass can't drive deterministically — request sequencing, a store's transitions, merge order — written with `node:test` against the plain class and wired into `check`; never for a template, a style or what a component renders. No test framework; don't claim "tests pass" in a repo that has none. 10. **The docs ship with the change — this skill first.** This skill is the fleet-wide frontend source of truth: the ops repos reach it through committed symlinks, and stack versions live only in its fleet table. When frontend conventions, versions, or idioms change, update `SKILL.md` and the affected `references/*.md` in the same change; other repos' docs carry only project-specific facts. ## If you learned Angular anywhere else — the surprise index - **No `NgModule` anywhere** — standalone components only. - **No `.html`/`.scss` component files.** 100% inline `template:` and `styles:` — one file per component. - **No constructor parameter injection.** `inject()` in field initializers, even chained: `protected route = toSignal(inject(Router).events)`. - **No `ngOnInit`.** Field initializers; a bare `constructor() { this.load() }` kicks off fetches; `afterNextRender` for browser-only side effects. - **No `*ngIf`/`*ngFor`/`ngClass`/`ngStyle`.** `@if`/`@for` (with `track`), `@let`, `@switch`; `[class.x]` and unit-typed `[style.prop.unit]` bindings. - **No `@Input`/`@Output`/`@ViewChild` decorators.** `input()`, `input.required()`, `output()`, `model()`, `viewChild()`, `contentChild()`. - **No `provideAnimations()`.** Taiga 5 animates with CSS (`TuiAnimated`). - **No explicit event-plugins provider.** `provideTaiga()` bundles it — that's what makes `(submit.prevent)`, `(click.self)`, `.stop`, `.capture` modifiers work. - **No `TuiAlertService`.** Toasts are `TuiNotificationService`; blocking loaders are `TuiNotificationMiddleService`. - **No `FormBuilder`.** `inject(NonNullableFormBuilder).group({...})` with array shorthand; `[(ngModel)]="signal"` for single ad-hoc fields. - **No icons inside switches.** `tuiSwitchOptionsProvider({ showIcons: false })` in the app config; a toggle is a track and a thumb. - **No autofocus on phones.** A focused field opens the keyboard over the page. Bind `[tuiAutoFocus]="!isMobile"` with `isMobile = inject(WA_IS_MOBILE)`, the device rather than the breakpoint. - **No cleaner on a select.** `[tuiTextfieldCleaner]="false"` on every `tui-textfield` holding an `input[tuiSelect]`; the default is on, and it also lets Backspace clear the choice. - **No route-level `providers`,** no resolvers, few guards (inline `canMatch` arrows). Providers go on components — lazy-route providers spin up confusing semi-root injectors. - **No `@media` queries** for the app-standard mobile swap: `tui-root._mobile &` CSS, `TUI_BREAKPOINT`/`WA_IS_MOBILE` signals, template `@if` swaps — one DOM, never two. - **No BEM, no barrels, no `index.ts` re-exports** in apps. Taiga-style `_state` classes and shared `g-*` utilities; deep relative or `src/`-absolute imports. - **No semicolons.** Prettier: `singleQuote`, `semi: false`, `arrowParens: "avoid"`, `trailingComma: "all"`, `htmlWhitespaceSensitivity: "ignore"`, `tabWidth: 2`. - **No ESLint, no test framework.** Logic tests are `node:test` (doctrine 9). Prettier runs via husky/lint-staged — never `git commit --no-verify`; fix the formatting. - **Suffixless files and classes** in new code: `routes/devices/index.ts` exporting `export default class Devices`, plus `dialog.ts`, `table.ts`, `service.ts` — not `devices-page.component.ts` / `DevicesPageComponent`. - **Services can _be_ Observables** (`class ConnectionService extends Observable<boolean>`); tokens can _be_ signals (`new InjectionToken('…', { factory: () => signal(false) })`). - **`DOCUMENT` and `@Service()` import from `@angular/core`** now — `@Service()` is the emerging norm for new services over `@Injectable({providedIn: 'root'})`. - **The mock backend is a DI swap**: `useClass: useMocks ? MockApiService : LiveApiService`. ## Review checklist (greppable) Any hit is a finding unless it matches a documented exception in the reference files: ``` NgModule constructor(private *ngIf / *ngFor ngClass / ngStyle @Input( / @Output( / @ViewChild( @HostBinding / @HostListener templateUrl / styleUrl FormBuilder (not NonNullable) new FormGroup( : any provideAnimations TuiAlertService tuiFieldError ngOnInit setTimeout .subscribe( [outside allowed shapes] ::ng-deep !important letter-spacing / text-transform #[0-9a-f]{3,6} [outside the theme sheet] @media [outside documented divergences] window. / document. / localStorage [outside infrastructure] providers: [ on a route track $index [on entity lists] input<T | null>(null) display: grid on :host of a wrapper around one child display: contents [on :host around one Taiga primitive] outline: none [(tuiDropdownOpen)] [for a plain menu] .trim() [in a submit handler] ``` Softer review questions: does a `computed` just reshape for the template (→ pipe)? Is a value named but used once (→ inline)? Does a field alias a service's signal or a method wrap its call (→ expose the service to the template)? Do form controls rename the API's fields (→ same names, no mapping)? Is the same appearance/size attribute repeated (→ option provider)? Is there a second DOM for mobile (→ one DOM + `_mobile` CSS)? Did copy ship in Title Case (→ sentence case)? Is a Taiga API used that you didn't verify against the docs?
View on GitHub