@smartsoft001-mobilems/claude-plugins 2.88.0 → 2.90.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 (44) hide show
  1. package/README.md +27 -2
  2. package/package.json +1 -1
  3. package/plugins/mobilems/.claude-plugin/README.md +21 -2
  4. package/plugins/mobilems/.claude-plugin/plugin.json +1 -1
  5. package/plugins/mobilems/skills/mobilems-articles/SKILL.md +52 -20
  6. package/plugins/mobilems/skills/mobilems-articles/references/module-and-routes.md +24 -0
  7. package/plugins/mobilems/skills/mobilems-articles/scripts/check-translations.js +90 -0
  8. package/plugins/mobilems/skills/mobilems-articles/scripts/verify-module.sh +53 -0
  9. package/plugins/mobilems/skills/mobilems-devops-config/SKILL.md +141 -0
  10. package/plugins/mobilems/skills/mobilems-devops-config/references/docker.md +58 -0
  11. package/plugins/mobilems/skills/mobilems-devops-config/references/env-and-lint.md +59 -0
  12. package/plugins/mobilems/skills/mobilems-devops-config/references/github-hygiene.md +54 -0
  13. package/plugins/mobilems/skills/mobilems-devops-config/references/tooling.md +105 -0
  14. package/plugins/mobilems/skills/mobilems-devops-config/references/workflows.md +410 -0
  15. package/plugins/mobilems/skills/mobilems-devops-config/scripts/detect-foundation.sh +47 -0
  16. package/plugins/mobilems/skills/mobilems-devops-config/scripts/verify-devops.sh +96 -0
  17. package/plugins/mobilems/skills/mobilems-events/SKILL.md +27 -1
  18. package/plugins/mobilems/skills/mobilems-events/references/module-and-routes.md +24 -0
  19. package/plugins/mobilems/skills/mobilems-events/scripts/verify-module.sh +43 -0
  20. package/plugins/mobilems/skills/mobilems-object-collections/SKILL.md +46 -14
  21. package/plugins/mobilems/skills/mobilems-object-collections/references/lib-contents.md +11 -0
  22. package/plugins/mobilems/skills/mobilems-object-collections/references/module-and-routes.md +26 -0
  23. package/plugins/mobilems/skills/mobilems-object-collections/scripts/check-translations.js +90 -0
  24. package/plugins/mobilems/skills/mobilems-object-collections/scripts/verify-module.sh +54 -0
  25. package/plugins/mobilems/skills/mobilems-objects/SKILL.md +42 -10
  26. package/plugins/mobilems/skills/mobilems-objects/references/module-and-routes.md +24 -0
  27. package/plugins/mobilems/skills/mobilems-objects/scripts/check-translations.js +90 -0
  28. package/plugins/mobilems/skills/mobilems-objects/scripts/verify-module.sh +53 -0
  29. package/plugins/mobilems/skills/mobilems-partners/SKILL.md +30 -2
  30. package/plugins/mobilems/skills/mobilems-partners/references/module-and-routes.md +24 -0
  31. package/plugins/mobilems/skills/mobilems-partners/scripts/check-translations.js +90 -0
  32. package/plugins/mobilems/skills/mobilems-partners/scripts/verify-module.sh +53 -0
  33. package/plugins/mobilems/skills/mobilems-project-init/SKILL.md +68 -8
  34. package/plugins/mobilems/skills/mobilems-project-init/references/branding-reset.md +112 -0
  35. package/plugins/mobilems/skills/mobilems-project-init/references/claude-config.md +7 -0
  36. package/plugins/mobilems/skills/mobilems-project-init/references/config-providers.md +1 -1
  37. package/plugins/mobilems/skills/mobilems-project-init/references/qa-hardening.md +30 -30
  38. package/plugins/mobilems/skills/mobilems-project-init/references/shared-lib.md +42 -5
  39. package/plugins/mobilems/skills/mobilems-project-init/references/tailwind-styles.md +33 -23
  40. package/plugins/mobilems/skills/mobilems-project-init/scripts/verify-init.sh +62 -9
  41. package/plugins/mobilems/skills/mobilems-thematic-publications/SKILL.md +25 -1
  42. package/plugins/mobilems/skills/mobilems-thematic-publications/references/lib-contents.md +11 -0
  43. package/plugins/mobilems/skills/mobilems-thematic-publications/references/module-and-routes.md +26 -0
  44. package/plugins/mobilems/skills/mobilems-thematic-publications/scripts/verify-module.sh +43 -0
@@ -0,0 +1,112 @@
1
+ # Branding + SEO reset — strip the reference museum's identity
2
+
3
+ Reference mode copies a **different museum's** app. Its brand and SEO travel with the code and are
4
+ invisible until QA (or the client) finds them: fonts named after the old project, its palette, its
5
+ logo, its domain in `og:url`, its name in `<title>` and in the PL/EN translation strings.
6
+
7
+ Run this **after** the shared lib + app shell are copied (steps 7–8) and **before** QA hardening.
8
+ It is a **token/content** change only.
9
+
10
+ ## The invariant — re-skin, never re-build
11
+
12
+ Allowed to change: colour/font/spacing **token values**, asset **files**, text **content**, site
13
+ identity (name, domain, social image).
14
+
15
+ Never change as part of this step:
16
+
17
+ - layout or utility classes in templates (`smart-*`), DOM structure, component inputs/outputs
18
+ - API field mapping, `baseQuery`/`query` keys, dictionary keys (lowercase — a rename breaks filters
19
+ with HTTP 409), route paths and slugs
20
+ - translation **keys** (only their values), NgRx state shape, service contracts
21
+
22
+ If a colour/font swap seems to require a template change, it is a design change — raise it with the
23
+ user instead of doing it silently.
24
+
25
+ ## 1. Decide the mode (ask once, `AskUserQuestion`)
26
+
27
+ - **Brand spec mode** — the user supplies fonts, palette, logo, social image, site name/domain.
28
+ Apply it, and still run every check below.
29
+ - **Neutral mode (default)** — apply the generic token set in §2. The result is a deliberately plain
30
+ but complete, WCAG-clean skin the client's designer can replace later.
31
+
32
+ ## 2. Neutral token set (the default)
33
+
34
+ | Token | Value |
35
+ | -------------------- | --------------------------------------------------------------------------- |
36
+ | Heading font | **Playfair Display** (full latin-ext — covers `ą ć ę ł ń ś ź ż`) |
37
+ | Body / UI font | **Inter** (fallback `system-ui, sans-serif`) |
38
+ | Text on white | `#1f2937` (≈ 15:1), secondary `#4b5563` (≈ 7.6:1) — never `#9ca3af` (2.5:1) |
39
+ | Surfaces | `#ffffff` page, `#f4f4f5` muted, `#e4e4e7` border |
40
+ | Accent / primary | `#3f5b76` with white text (≈ 5.6:1); active `#334a61` |
41
+ | High-contrast (WCAG) | bg `#1d1d1d`, text `#ffed00`, links `#86d7be` (keep the framework's values) |
42
+ | Logo / `no-image` | Neutral placeholder SVG carrying the app name, no reference museum mark |
43
+ | Social image | Locally generated `gfx/social-logo.jpg` 1200×550 with the app name |
44
+
45
+ Every generated text/background pair must be checked ≥4.5:1 before moving on.
46
+
47
+ ## 3. Carriers to reset — checklist
48
+
49
+ **Styles**
50
+
51
+ - `tailwind.config.js` — `theme.extend.colors.*` and `fontFamily.*`. Step 4 already wrote the neutral
52
+ values under role-based keys (`heading`, `body`, `ui`); in brand-spec mode swap the **values**, never
53
+ the keys. Delete any font token named after a typeface or another project that a copied file
54
+ re-added. If copied templates still use such class names, remap those classes onto the role tokens
55
+ or keep one clearly labelled remap block — never leave an unexplained legacy name. No `fontFamily`
56
+ entry may be `[]`.
57
+ - `apps/web/src/styles.scss` — the typography header block, `@font-face` families and the metric
58
+ fallbacks (`* Fallback` / `* Fallback Android`) must name the new fonts; drop rules that target the
59
+ reference museum's own selectors.
60
+ - Any hardcoded hex in copied shared components that duplicates a palette token → replace with the
61
+ token (do not restyle the component).
62
+
63
+ **Assets** (`apps/web/public/gfx`, `apps/web/public`)
64
+
65
+ - logo(s) (`main-logo`, footer/white variants), `no-image` placeholder, `social-logo.jpg`,
66
+ `favicon.ico` + 32×32 + 192×192 + `apple-touch-icon`, `manifest.webmanifest` name/icons.
67
+ - Delete reference-only assets (partner/funding logos of the other museum, its banners). Serve each
68
+ image near its rendered size (oversized copies trigger `NG0913`).
69
+
70
+ **SEO / identity**
71
+
72
+ - `apps/web/src/index.html` — `<title>`, `description`, `og:title`/`og:description`/`og:url`/
73
+ `og:image`(+`:secure_url`,`:width`,`:height`,`:alt`), `twitter:*`. Absolute URLs come from the new
74
+ `SITE_URL`.
75
+ - `museum-seo.service.ts` — default OG image, base title, locale map; no reference host anywhere.
76
+ - `sitemap.xml` / `robots.txt` — dynamic `SITE_URL`-driven endpoints only (see `qa-hardening.md` §2).
77
+ - `environment.ts` / `.prod.ts` / `.env.dev` — `apiUrl`, `multimediaUrl`, `siteUrl` of THIS museum.
78
+
79
+ **Content**
80
+
81
+ - `apps/web/public/translations.json` — museum name, base title (`MODEL.mainTitle`), skip-link
82
+ labels, footer/contact copy, static-page fallbacks — **both** PL and EN. Keys unchanged.
83
+ - Footer/contact templates: no hardcoded reference address, phone, e-mail or socials (live data comes
84
+ from the CMS API).
85
+ - Comments that name the origin project — rewrite or drop.
86
+
87
+ ## 4. Prove it — no leftovers
88
+
89
+ Grep the workspace for the reference project's identity and fix every hit (case-insensitive; use the
90
+ reference repo's own name, museum name and domain, plus its import prefix):
91
+
92
+ ```bash
93
+ grep -rniE "<source-project-name>|<source-domain>|@<source-prefix>/" \
94
+ apps libs *.js *.json --include="*.ts" --include="*.html" --include="*.scss" \
95
+ --include="*.json" --include="*.js" | grep -v node_modules
96
+ ```
97
+
98
+ Expected result: **no hits**.
99
+
100
+ `verify-init.sh` independently asserts a narrower set: font tokens are role-named (`heading` / `body`
101
+ / `ui`, not named after a typeface or a project), no `{{...}}` placeholder survives, no workspace
102
+ alias other than `@<PREFIX>/` is mapped, `fontFamily` has no empty entry, and a favicon exists.
103
+
104
+ It hardcodes **no** project identity. In reference mode, pass the source project's slug or domain as
105
+ its third argument and it will grep for that too:
106
+
107
+ ```bash
108
+ scripts/verify-init.sh <workspace-root> <PREFIX> <source-project-slug>
109
+ ```
110
+
111
+ That narrower set is not a substitute for the sweep above — it does not look at logos, `og:`/
112
+ `twitter:` tags, the manifest, or contrast ratios. Run the full sweep yourself and report it.
@@ -29,6 +29,13 @@ permission / MCP allow-lists. **Extend, do not replace** the base file — keep
29
29
  > hosts (its own portal domain, project n8n/maia webhooks). Seed the generic-useful entries and let
30
30
  > the project grow its own.
31
31
 
32
+ ## Agent config lives in `.claude/`, never `.github/`
33
+
34
+ The Nx scaffolding may have written `.github/skills/`, `.github/agents/`, `.github/prompts/` (the
35
+ Copilot layout). Those duplicate the enabled `nx@nx-claude-plugins` plugin. `mobilems-devops-config`
36
+ owns that cleanup (`references/github-hygiene.md`) — if you see them here, either run that skill or
37
+ apply the same rule: delete plugin duplicates, `git mv` bespoke ones into `.claude/`.
38
+
32
39
  ## tools/specs + tools/prompts
33
40
 
34
41
  Seed pointers so the museum conventions are discoverable (the framework repo keeps the canonical
@@ -1,7 +1,7 @@
1
1
  # app.config.ts — the MobilEMS provider set
2
2
 
3
3
  Overwrite `apps/web/src/app/app.config.ts` with the providers below (the base shipped a minimal
4
- version). Thread `<PREFIX>` (reference uses `@msr`). `app.config.server.ts` and
4
+ version). Thread `<PREFIX>`. `app.config.server.ts` and
5
5
  `app.routes.server.ts` stay as the base emitted them.
6
6
 
7
7
  The imports from `@<PREFIX>/angular` (`LanguageInterceptor`, `PreviewAuthInterceptor`,
@@ -1,26 +1,27 @@
1
- # QA hardening — lessons from live museum deployments
1
+ # QA hardening
2
2
 
3
- Every item below is a **real defect reported in QA on production museum projects** (Linear: Muzeum
4
- Ostrowiec MOB-938…MOB-995, Muzeum Tomaszów Lubelski MOB-948…MOB-996). The foundation is copied from
5
- a reference repo, so the default failure mode is **reference-project leftovers** plus a handful of
3
+ Every item below is a defect class that recurs on museum projects. The foundation is copied from a
4
+ reference repo, so the default failure mode is **source-project leftovers** plus a handful of
6
5
  recurring SEO/i18n/WCAG gaps. Work through this checklist when applying the delta — each item is
7
- cheap to do during init and expensive to chase later in QA.
6
+ cheap to do during init and expensive to chase later.
8
7
 
9
8
  ## 1. No reference-project leftovers (branding)
10
9
 
11
10
  The reference repo belongs to a _different museum_. Everything carrying that museum's identity must
12
- be replaced or generated for the target project:
13
-
14
- - **`sitemap.xml` + `robots.txt`** — never copy static files (QA found `zbiory.muzeum-orkana.pl`
15
- URLs shipped to another museum). Implement them as **dynamic SSR endpoints** driven by the
16
- `SITE_URL` env var — see section 2.
17
- - **`og:image` / `og:image:secure_url` / `twitter:image` / `og:url`** — must point at the target
18
- museum's domain, not the reference deploy (QA found `mobilems-muzeum-swiatowid-qa.smartsoft.biz.pl/gfx/social-logo.jpg`).
19
- Use `SITE_URL`/`siteUrl` to build absolute URLs; ship a local `gfx/social-logo.jpg` for this app.
11
+ be replaced or generated for the target project. **`references/branding-reset.md` (step 9) is the
12
+ full checklist — styles, fonts, palette, assets, translations — and runs before this one.** What
13
+ follows are the SEO-side items:
14
+
15
+ - **`sitemap.xml` + `robots.txt`** — never ship static files; a static copy hardcodes whatever
16
+ domain it was written with. Implement them as **dynamic SSR endpoints** driven by the `SITE_URL`
17
+ env var — see section 2.
18
+ - **`og:image` / `og:image:secure_url` / `twitter:image` / `og:url`** — must point at this app's own
19
+ domain. Build absolute URLs from `SITE_URL`/`siteUrl`; ship a local `gfx/social-logo.jpg` for this
20
+ app rather than pointing at another deploy.
20
21
  - **Favicon** — the base often has none. Ship the full set: `favicon.ico`, 32×32 + 192×192 PNG,
21
- `apple-touch-icon` (two projects opened QA bugs "Brak ikony").
22
+ `apple-touch-icon`.
22
23
  - **Skip links** — keep them (they are required), but rewrite their labels/targets for THIS app's
23
- sections; copied skip links still said "Światowid". Localize them (PL/EN translation keys).
24
+ sections; a copied skip link still names the source project. Localize them (PL/EN translation keys).
24
25
  - **Footer / contact data** — target museum's phone, e-mail, socials; links come from the CMS API,
25
26
  not hardcode.
26
27
  - Local static assets (funding logos etc.) must be served near their rendered size — oversized
@@ -28,7 +29,7 @@ be replaced or generated for the target project:
28
29
 
29
30
  ## 2. Dynamic sitemap + robots endpoints (server.ts)
30
31
 
31
- Pattern proven in MOB-948 — put the logic in **pure, testable functions** in
32
+ Put the logic in **pure, testable functions** in
32
33
  `apps/web/src/seo/seo-endpoints.ts` (`resolveSiteUrl`, `buildSitemapXml`, `buildRobotsTxt`), and
33
34
  wire thin routes in `server.ts`:
34
35
 
@@ -47,7 +48,7 @@ wire thin routes in `server.ts`:
47
48
 
48
49
  - `<meta name="description">` + complete OpenGraph (`og:title`, `og:description`, `og:image`,
49
50
  `og:url`) + Twitter Card on the home page, in **both** languages, re-set on language switch.
50
- - `og:locale` must follow the active language — a hardcoded `pl_PL` was a QA bug. In
51
+ - `og:locale` must follow the active language; never hardcode `pl_PL`. In
51
52
  `museum-seo.service.ts` use a locale map (`pl → pl_PL`, `en → en_GB`) read from
52
53
  `translateService.currentLang` (fallback via `defaultLang` — `currentLang` can be `undefined`
53
54
  during SSR), and emit one `og:locale:alternate`.
@@ -67,22 +68,21 @@ wire thin routes in `server.ts`:
67
68
 
68
69
  ## 5. WCAG invariants
69
70
 
70
- - **Contrast:** the reference grey `#9ca3af` on white is 2.53:1 — fails AA and was flagged on menus
71
- and breadcrumbs in two projects. Use ≥4.5:1 for text: `#6f6f6f` ≈ 5.0:1, `#4b5563` ≈ 7.6:1.
71
+ - **Contrast:** light greys fail AA on white — `#9ca3af` is only 2.53:1. Use ≥4.5:1 for text:
72
+ `#6f6f6f` ≈ 5.0:1, `#4b5563` ≈ 7.6:1.
72
73
  Figma mockups themselves sometimes specify failing colours — match the design _intent_ with the
73
74
  nearest compliant shade instead of copying the hex blindly, and note the deviation.
74
- - **Headings:** exactly one `h1` per page; no skipped levels (an `h1 → h3` jump was a recurring
75
- axe-core finding). Detail pages get an `h1` (the item title), image captions are NOT headings.
76
- - **alt text:** when the alt comes from a CMS title, strip HTML first (`smartRemoveHtml`) —
77
- `alt="<p>Tytuł</p>"` shipped to QA.
75
+ - **Headings:** exactly one `h1` per page; no skipped levels (an `h1 → h3` jump is an axe-core
76
+ finding). Detail pages get an `h1` (the item title), image captions are NOT headings.
77
+ - **alt text:** when the alt comes from a CMS title, strip HTML first (`smartRemoveHtml`),
78
+ otherwise the alt reads `<p>Tytuł</p>`.
78
79
  - **Keyboard:** any card implemented as `role="link"`/`tabindex=0` must activate on Enter/Space
79
80
  (or better, be a real `<a href>`). Focus must be visible; labels should react to focus/hover the
80
81
  same way the rest of the card content does.
81
- - **Fonts must cover Polish diacritics.** Bacasime Antique, Prata and Italiana lack 16/18 of
82
- `ą ć ę ł ń ś ź ż` (+ uppercase) — titles rendered in a fallback font and QA rejected it. Verified
83
- full-latin-ext replacements: **Gilda Display** (closest to Bacasime) or **Playfair Display**.
84
- Never leave a `tailwind.config.js` `fontFamily` entry as an empty array — it silently falls back
85
- to system sans.
82
+ - **Fonts must cover Polish diacritics.** Decorative serifs frequently lack `ą ć ę ł ń ś ź ż`
83
+ (+ uppercase), so titles render in a silent fallback. Check glyph coverage before adopting a face;
84
+ **Playfair Display** and **Gilda Display** are full-latin-ext. Never leave a `tailwind.config.js`
85
+ `fontFamily` entry as an empty array — it silently falls back to system sans.
86
86
 
87
87
  ## 6. Console hygiene (build + runtime must be warning-clean)
88
88
 
@@ -107,11 +107,11 @@ These conventions were enforced by QA across modules — bake them in from the s
107
107
 
108
108
  - **One card component per entity, reused everywhere.** The home page, module lists and search
109
109
  results must render the same shared `*-grid-item` / `*-list-item` components — a second ad-hoc
110
- card implementation on the home page was a High-priority bug (MOB-994).
110
+ card implementation on the home page is a defect, not a variant.
111
111
  - **Full-text search inputs debounce 250 ms** (`@debounce(250)` from `lodash-decorators`) — in every
112
112
  module's filters (objects, collections, articles/news).
113
113
  - **Every list page shows the total result count** (`facade.totalCount()`), independent of
114
- pagination (MOB-995/996).
114
+ pagination.
115
115
  - **"Next page" button** at the bottom of each list follows one shared pattern
116
116
  (`NextPageComponent`: `selectedPage`/`lastPageIndex` from `totalCount()` + limit, scroll-to-top on
117
117
  advance, hidden on the last page).
@@ -1,10 +1,14 @@
1
1
  # Build the foundation shared library
2
2
 
3
3
  The shared library `libs/shared/angular` (alias `@<PREFIX>/angular`) holds the museum's shared
4
- Angular building blocks: the `App` shell, header, footer, the generic page renderer, filters,
5
- gallery, map, slider, carousels, content widgets, WCAG + SEO services, interceptors, pipes,
6
- directives, tools, models, and `configs.module.ts`. Build it by copying from the reference repo's
7
- `libs/shared/angular/src/lib` and threading `<PREFIX>` (the reference uses `@msr`).
4
+ Angular building blocks: the `App` shell, header, footer, the generic page renderer, gallery, map,
5
+ slider, carousels, content widgets, WCAG + SEO services, interceptors, pipes, directives, tools,
6
+ models, and `configs.module.ts`. Build it by copying from the reference repo's
7
+ `libs/shared/angular/src/lib` and threading `<PREFIX>` in place of that repo's own alias.
8
+
9
+ Filter components are **not** on that list. Each module builds its own by extending the framework's
10
+ `FiltersBaseComponent<T>` (see `SKILL.md` → "Framework first"); the shared lib holds no filter
11
+ implementation to copy.
8
12
 
9
13
  The library starts as the base shell that exports only `environment` + `EnvironmentService` with
10
14
  empty `lib/` placeholder dirs — fill those dirs with the **foundation shared components**.
@@ -86,6 +90,29 @@ see `server-env.md`), and `configs.module.ts`. These are foundation code. Confir
86
90
  build-safety grep below; if any of these files imports a feature package on your reference, it is
87
91
  feature code and belongs to a module skill — leave it out.
88
92
 
93
+ **Build each of these ONCE here — module skills reuse them and must never re-create a local copy.**
94
+ Check the framework catalog (`node_modules/@smartsoft001-mobilems/angular/README.md`, in-repo
95
+ [`packages/shared/angular/README.md`](https://github.com/smartsoft001/mobilems-framework/blob/main/packages/shared/angular/README.md))
96
+ first: if the framework already exports it — `MasonryGridComponent` and `FiltersBaseComponent` /
97
+ `FiltersContext` above all, plus `FileUrlService`, `SeoService`, `MetaService`, `DictionaryService`,
98
+ `SearchService`, `WcagService`, `ContrastService`, `StyleService`, `ModalService`,
99
+ `ClickOutsideDirective`, `HoverDirective`, `ScrollableDirective`, … — import it instead of copying
100
+ the reference's version. The reference repo predates parts of the framework, so several of its shared
101
+ components duplicate exports that now exist; copying those is the single most common defect in this
102
+ step. The app-level set the framework does **not** provide, and that the module skills expect to find
103
+ here:
104
+
105
+ | Kind | Expected members |
106
+ | ------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
107
+ | pipes | `altText`, `capitalize`, `fileUrl`, `truncateHtml` |
108
+ | tools | `isInternalLink`, `normalizeUrl`, `stripHtml` (+ the shared `tools.ts` helpers) |
109
+ | directives | `externalLink` |
110
+ | interceptors | `language`, `preview-auth`, `ssr-timeout` |
111
+ | services | `museum-seo`, `filter-persistence`, `local-storage`, `orientation`, `confirmation-modal`, `contact`, `data`, `environment`, `wcag` |
112
+
113
+ Adjust to what the reference actually ships, but keep the rule: one implementation, in this lib,
114
+ exported from the barrel, documented in the app's own README so the module skills can find it.
115
+
89
116
  ## Barrels
90
117
 
91
118
  - `src/index.ts` — the full barrel: re-export `./lib/components`, `./lib/services`,
@@ -95,6 +122,16 @@ feature code and belongs to a module skill — leave it out.
95
122
  which holds `AppComponent`/`FooterComponent`/`HeaderComponent`). Module skills add their feature
96
123
  shared components' exports when they build them.
97
124
 
125
+ ## `libs/shared/angular/README.md` — required
126
+
127
+ Write a README for the lib listing what it exports, in the same shape as the framework catalog:
128
+ components, services, pipes, tools, directives, interceptors, models — one line each, saying what it
129
+ is for. This is not decoration: the module skills read it to decide what to reuse, and it is the only
130
+ place the app-level (non-framework) helpers are discoverable. Head it with a pointer to the framework
131
+ catalog (`node_modules/@smartsoft001-mobilems/angular/README.md`) and the rule "framework first, then
132
+ this lib, then — only if neither has it — new code". Update it whenever a module skill adds a shared
133
+ component.
134
+
98
135
  ## Build-safety invariant
99
136
 
100
137
  The foundation library must build with no feature package installed. The check is positive and
@@ -122,4 +159,4 @@ copy those from the reference and normalize their `dest`/`coverageDirectory`/pat
122
159
  After copying, the only reference-prefix string left should be gone. Beyond the import statements,
123
160
  remember to thread `<PREFIX>` in `libs/shared/angular/package.json` `"name"` (it reads
124
161
  `@<reference-prefix>/angular` in the reference → set it to `@<PREFIX>/angular`). A workspace-wide grep
125
- for the reference prefix (`@msr/`) should return nothing.
162
+ for the source repo's own prefix should return nothing.
@@ -6,6 +6,10 @@ postcss, so scss and Tailwind coexist — no migration.
6
6
 
7
7
  ## 1. `tailwind.config.js` (workspace root — create verbatim, no placeholders)
8
8
 
9
+ The token **values** below are the neutral default set (`references/branding-reset.md` §2) — a plain,
10
+ WCAG-clean skin. The token **keys** are the structural contract the templates use; keep them exactly
11
+ as written. Step 9 swaps the values for the client's brand spec if there is one.
12
+
9
13
  ```js
10
14
  /** @type {import('tailwindcss').Config} */
11
15
  module.exports = {
@@ -16,13 +20,14 @@ module.exports = {
16
20
  extend: {
17
21
  colors: {
18
22
  primary: {
19
- DEFAULT: '#414141',
20
- active: 'rgb(223 223 223)',
21
- red: 'rgb(193 18 31)',
23
+ DEFAULT: '#1f2937',
24
+ active: '#e4e4e7',
25
+ accent: '#3f5b76',
26
+ red: '#b3261e',
22
27
  },
23
28
  dark: {
24
- DEFAULT: '#100f0d',
25
- yellow: '#ffff00',
29
+ DEFAULT: '#1d1d1d',
30
+ yellow: '#ffed00',
26
31
  },
27
32
  },
28
33
  gridTemplateColumns: {
@@ -33,22 +38,24 @@ module.exports = {
33
38
  '0px 5px 5px rgba(0, 0, 0, 0.2), 0 10px 10px rgba(0, 0, 0, 0.1), 0 20px 20px rgba(0, 0, 0, 0.15)',
34
39
  },
35
40
  fontFamily: {
36
- satoshi: [
37
- 'Satoshi',
38
- 'Satoshi Fallback',
39
- 'Satoshi Fallback Android',
40
- 'sans-serif',
41
+ heading: [
42
+ 'Playfair Display',
43
+ 'Playfair Display Fallback',
44
+ 'Playfair Display Fallback Android',
45
+ 'serif',
41
46
  ],
42
- oswald: [
43
- 'Oswald',
44
- 'Oswald Fallback',
45
- 'Oswald Fallback Android',
47
+ body: [
48
+ 'Inter',
49
+ 'Inter Fallback',
50
+ 'Inter Fallback Android',
51
+ 'system-ui',
46
52
  'sans-serif',
47
53
  ],
48
- 'fira-sans': [
49
- 'Fira Sans',
50
- 'Fira Sans Fallback',
51
- 'Fira Sans Fallback Android',
54
+ ui: [
55
+ 'Inter',
56
+ 'Inter Fallback',
57
+ 'Inter Fallback Android',
58
+ 'system-ui',
52
59
  'sans-serif',
53
60
  ],
54
61
  },
@@ -62,11 +69,14 @@ module.exports = {
62
69
  > The `content` globs already cover `libs/**`, so feature libs added later are picked up — no edit
63
70
  > needed when modules arrive. `darkMode` keys off `contrast-high` for the WCAG high-contrast toggle.
64
71
 
65
- > **QA gotcha (fonts):** when the project's design swaps in its own heading font, verify it covers
66
- > Polish diacritics (`ą ć ę ł ń ś ź ż` + uppercase). Bacasime Antique, Prata and Italiana lack them
67
- > — titles silently render in a fallback font and QA rejects it (verified replacements: Gilda
68
- > Display, Playfair Display). Never leave a `fontFamily` entry as an empty array — it falls back to
69
- > system sans. See `references/qa-hardening.md` §5.
72
+ > **Font tokens are named by role** (`heading`, `body`, `ui`), never after a typeface or a project.
73
+ > In reference mode the copied templates may use the source project's own token names — remap those
74
+ > class names onto these three in step 9 rather than re-adding the old tokens.
75
+
76
+ > **Fonts must cover Polish diacritics** (`ą ć ę ł ń ś ź ż` + uppercase). A heading face that lacks
77
+ > them renders titles in a silent fallback. Check the glyph coverage of any face before adopting it;
78
+ > the defaults above are chosen for full latin-ext support. Never leave a `fontFamily` entry as an
79
+ > empty array — it falls back to system sans. See `references/qa-hardening.md` §5.
70
80
 
71
81
  ## 2. postcss
72
82
 
@@ -8,8 +8,11 @@
8
8
  # Portable to macOS system bash (3.2). Exit 0 = all pass; exit 1 = one or more checks failed.
9
9
  set -eu
10
10
 
11
- ROOT="${1:?usage: verify-init.sh <workspace-root> <PREFIX>}"
12
- PREFIX="${2:?usage: verify-init.sh <workspace-root> <PREFIX>}"
11
+ ROOT="${1:?usage: verify-init.sh <workspace-root> <PREFIX> [SOURCE_ID]}"
12
+ PREFIX="${2:?usage: verify-init.sh <workspace-root> <PREFIX> [SOURCE_ID]}"
13
+ # Optional: in reference mode, the source project's slug or domain, so branding leakage from it can
14
+ # be checked. No project identity is hardcoded in this script.
15
+ SOURCE_ID="${3:-}"
13
16
  cd "$ROOT" 2>/dev/null || { echo "ERROR: no such dir: $ROOT" >&2; exit 2; }
14
17
 
15
18
  fail=0
@@ -26,15 +29,56 @@ grep -q "multimediaUrl" apps/web/src/server.ts && pass "multimediaUrl in server.
26
29
  grep -q "/api/translations" apps/web/src/server.ts && pass "/api/translations endpoint" || bad "/api/translations in server.ts"
27
30
 
28
31
  echo "== QA hardening (references/qa-hardening.md) =="
29
- # dynamic sitemap/robots endpoints (static copies shipped another museum's domain in past projects)
32
+ # dynamic sitemap/robots endpoints — a static copy hardcodes whatever domain it was written with
30
33
  grep -q "sitemap.xml" apps/web/src/server.ts && pass "dynamic /sitemap.xml endpoint" || bad "/sitemap.xml endpoint in server.ts (SITE_URL-driven, never a static copy)"
31
34
  grep -q "robots.txt" apps/web/src/server.ts && pass "dynamic /robots.txt endpoint" || bad "/robots.txt endpoint in server.ts (SITE_URL-driven, never a static copy)"
32
- if ls apps/web/public/favicon.ico apps/web/src/favicon.ico >/dev/null 2>&1; then pass "favicon present"; else bad "favicon.ico missing (QA bug 'Brak ikony' in two projects)"; fi
33
- # an empty fontFamily array silently falls back to system sans (QA-rejected typography)
34
- if grep -qE ":\s*\[\s*\]" tailwind.config.js 2>/dev/null; then bad "empty fontFamily array in tailwind.config.js (heading font falls back to system sans)"; else pass "no empty fontFamily entries"; fi
35
+ if ls apps/web/public/favicon.ico apps/web/src/favicon.ico >/dev/null 2>&1; then pass "favicon present"; else bad "favicon.ico missing"; fi
36
+ # an empty fontFamily array silently falls back to system sans
37
+ if grep -qE ":[[:space:]]*\[[[:space:]]*\]" tailwind.config.js 2>/dev/null; then bad "empty fontFamily array in tailwind.config.js (heading font falls back to system sans)"; else pass "no empty fontFamily entries"; fi
35
38
  grep -q "./lib/components" libs/shared/angular/src/index.ts && pass "shared-lib components barrel populated" || bad "components barrel in shared-lib index.ts"
39
+ [ -f libs/shared/angular/README.md ] && pass "shared-lib README (module skills read it)" || bad "libs/shared/angular/README.md missing — document the lib's components/services/pipes/tools/directives"
36
40
  [ -f .claude/settings.json ] && grep -q "flow@mobilems" .claude/settings.json && pass "flow@mobilems enabled" || bad "flow@mobilems in .claude/settings.json"
37
41
 
42
+ echo "== Branding + SEO reset (references/branding-reset.md) =="
43
+ # Font tokens are named by role, never after a typeface or a project. A token named after a
44
+ # typeface means a copied file re-added the source project's naming.
45
+ if grep -qE "fontFamily:" tailwind.config.js 2>/dev/null; then
46
+ if grep -qE "^[[:space:]]*(heading|body|ui):" tailwind.config.js 2>/dev/null; then
47
+ pass "role-named font tokens"
48
+ else
49
+ bad "font tokens are not role-named (expected heading/body/ui) — see branding-reset.md §3"
50
+ fi
51
+ fi
52
+ # unreplaced placeholders ship literal '{{...}}' to users
53
+ PH="$(grep -rl "{{" tailwind.config.js apps/web/src/index.html apps/web/public/translations.json 2>/dev/null || true)"
54
+ if [ -n "$PH" ]; then
55
+ bad "unreplaced {{...}} placeholders:"
56
+ printf ' %s\n' $PH >&2
57
+ else
58
+ pass "no unreplaced placeholders"
59
+ fi
60
+ # In reference mode the app is copied from a source project, so its identity can travel with the
61
+ # code. Pass that project's slug/domain as $3 to have it checked; omit it in foundation-only mode.
62
+ if [ -n "${SOURCE_ID:-}" ]; then
63
+ LEAKID="$(grep -rilE "$SOURCE_ID" tailwind.config.js apps/web/src apps/web/public .env.dev 2>/dev/null || true)"
64
+ if [ -n "$LEAKID" ]; then
65
+ bad "source project identity ('$SOURCE_ID') still present — run step 9 (branding reset):"
66
+ printf ' %s\n' $LEAKID >&2
67
+ else
68
+ pass "no source project identity"
69
+ fi
70
+ fi
71
+
72
+ echo "== Framework first (packages/shared/angular/README.md) =="
73
+ # masonry belongs to the framework's MasonryGridComponent — no app-level instances
74
+ MAS="$(grep -rlnE "new Masonry\(|MasonryInstance" libs apps/web/src 2>/dev/null || true)"
75
+ if [ -n "$MAS" ]; then
76
+ bad "hand-rolled masonry — use <smart-mobilems-masonry-grid> (MasonryGridComponent):"
77
+ printf ' %s\n' $MAS >&2
78
+ else
79
+ pass "no hand-rolled masonry instances"
80
+ fi
81
+
38
82
  echo "== Foundation scope (no feature modules yet) =="
39
83
  if ls -d libs/museum-* >/dev/null 2>&1; then bad "libs/museum-* present (feature modules are built by the module skills)"; else pass "no libs/museum-*"; fi
40
84
  # feature routes reference a museum-* lib; loadChildren + the import often span lines, so match museum- anywhere
@@ -70,10 +114,19 @@ else
70
114
  fi
71
115
 
72
116
  echo "== Prefix threaded =="
73
- if grep -rq "@msr/" libs/shared/angular/src apps/web/src 2>/dev/null && [ "$PREFIX" != "msr" ]; then
74
- bad "stray @msr/ imports (prefix not fully threaded to @$PREFIX/)"
117
+ # Derive the workspace's own aliases from tsconfig.base.json rather than naming any project's
118
+ # prefix here. Anything mapped that is neither @$PREFIX/ nor a framework scope is a leftover.
119
+ FOREIGN="$(grep -oE '"@[A-Za-z0-9_-]+/[A-Za-z0-9_*/-]*"' tsconfig.base.json 2>/dev/null |
120
+ tr -d '"' | grep -vE "^@smartsoft001" | grep -vE "^@$PREFIX/" | cut -d/ -f1 | sort -u || true)"
121
+ if [ -n "$FOREIGN" ]; then
122
+ bad "tsconfig.base.json maps a foreign workspace alias (expected @$PREFIX/):"
123
+ printf ' %s\n' $FOREIGN >&2
124
+ for a in $FOREIGN; do
125
+ SRC="$(grep -rl "$a/" libs/shared/angular/src apps/web/src 2>/dev/null || true)"
126
+ [ -n "$SRC" ] && printf ' imported in: %s\n' $SRC >&2
127
+ done
75
128
  else
76
- pass "no stray reference-prefix imports"
129
+ pass "workspace aliases use @$PREFIX/"
77
130
  fi
78
131
 
79
132
  echo
@@ -28,7 +28,31 @@ OWN domain entity, its OWN reducer (`getReducer`), and **no `baseQuery`**.
28
28
  Run `scripts/detect-init.sh <workspace-root>`. It confirms the MobilEMS foundation is present
29
29
  (`SharedModule.forRoot` in `app.config.ts`, a populated shared-lib barrel). If not, tell the user to
30
30
  run `/mobilems:project-init` first. Hold `<PREFIX>` (from `tsconfig.base.json`; reference uses
31
- `@msr`) and the reference repo path (same one init used).
31
+ own) and the reference repo path (same one init used).
32
+
33
+ ## Framework first — MANDATORY
34
+
35
+ Read these before writing ANY component, service, pipe, directive or helper, and reuse what they
36
+ list — re-implementing framework code is a defect, not a shortcut:
37
+
38
+ 0. [**The Framework API index**](https://github.com/smartsoft001/mobilems-framework/blob/main/README.md#-framework-api) — the map of every package to
39
+ its catalog. Start here when you do not already know which package owns what.
40
+ 1. The framework API catalog — `packages/shared/angular/README.md` in this repo, installed as
41
+ `node_modules/@smartsoft001-mobilems/angular/README.md` in a museum app ([browse](https://github.com/smartsoft001/mobilems-framework/blob/main/packages/shared/angular/README.md)).
42
+ This is the path `scripts/verify-module.sh` names when it fails.
43
+ 2. [`packages/articles/README.md`](https://github.com/smartsoft001/mobilems-framework/blob/main/packages/articles/README.md) — the package this module reuses
44
+ 3. `libs/shared/angular/README.md` — the app's own shared lib (built by `mobilems-project-init`)
45
+
46
+ | Do not hand-roll | Use |
47
+ | ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
48
+ | a masonry grid (`new Masonry(...)`, per-page masonry JS/CSS) | `<smart-mobilems-masonry-grid>` (`MasonryGridComponent`) with a `#masonryContentTpl`; the `Masonry` provider only feeds it |
49
+ | filter state / hand-built `query` arrays | `FiltersBaseComponent<MuseumThematicPath>` + its `FiltersContext` accessors |
50
+ | list/detail plumbing (facade wiring, id-from-route) | the `ArticlesListComponent` / `ArticlesItemComponent` base classes |
51
+ | a paged publication reader's content blocks | `WidgetComponent` + the `ArticleWidgetType` map |
52
+ | media URLs, meta/OG tags, modals, WCAG | `FileUrlService`, `SeoService`/`MetaService`, `ModalService`, `WcagService` |
53
+
54
+ Anything the framework genuinely lacks goes into `libs/shared/angular` **once** (and into its
55
+ README) — never a per-module copy. `scripts/verify-module.sh` fails the run on hand-rolled masonry.
32
56
 
33
57
  ## Steps
34
58
 
@@ -32,6 +32,17 @@ libs/museum-thematic-paths/
32
32
  > There is **no `src/__mocks__/` dir** in this lib (objects had one; thematic-paths does not). Copy
33
33
  > only what the reference has.
34
34
 
35
+ `components/filters` **must** extend the framework base class rather than build `query` arrays by
36
+ hand — that base is what wires the debounced writes back through `CrudFacade`:
37
+
38
+ ```ts
39
+ export class FiltersComponent extends FiltersBaseComponent<MuseumThematicPath> {}
40
+ ```
41
+
42
+ Read filter state through the inherited `context` accessors (`searchText`, `keywords`, `page`,
43
+ `sort`), never by assembling a `query` yourself. `scripts/verify-module.sh` fails the run if this
44
+ component does not extend `FiltersBaseComponent`.
45
+
35
46
  ## Domain entity (verbatim — thread nothing; pure types)
36
47
 
37
48
  Unlike objects' `MuseumObject extends MsObject`, this entity `implements` the local interfaces — it
@@ -69,6 +69,32 @@ import { routes } from './thematic-paths.routes';
69
69
  export class MuseumThematicPathsModule {}
70
70
  ```
71
71
 
72
+ > **The `Masonry` provider exists only to feed the framework's `MasonryGridComponent`.** It is DI
73
+ > plumbing, not a grid — never call `new Masonry(...)`, keep a `MasonryInstance`, or re-implement the
74
+ > layout/`reloadItems` dance in a page component. A hand-rolled CSS-grid or column layout is the same
75
+ > defect. `scripts/verify-module.sh` fails the run on both.
76
+
77
+ The list page renders the grid like this — copy this shape, it is the required one:
78
+
79
+ ```html
80
+ <smart-mobilems-masonry-grid [list]="list()" [options]="{ gutter: 24 }">
81
+ <ng-template #masonryContentTpl>
82
+ <div class="masonry-grid-sizer"></div>
83
+ @for (item of list(); track item.id) {
84
+ <div class="masonry-item">
85
+ <app-thematic-paths-list-item [item]="item" />
86
+ </div>
87
+ }
88
+ </ng-template>
89
+ </smart-mobilems-masonry-grid>
90
+ ```
91
+
92
+ `.masonry-grid-sizer` and `.masonry-item` are required — they are the component's default
93
+ `columnWidth` / `itemSelector`. The card (lib-contents.md) is the only module-specific part. Inputs,
94
+ public methods and the SSR provider are documented in the framework catalog,
95
+ [`packages/shared/angular/README.md`](https://github.com/smartsoft001/mobilems-framework/blob/main/packages/shared/angular/README.md#masonrygridcomponent)
96
+ (installed as `node_modules/@smartsoft001-mobilems/angular/README.md`).
97
+
72
98
  > Contrast with `mobilems-objects` (`ObjectsSharedModule.forFeature` over `MsObject`, no
73
99
  > `reducerFactory`): here the module reuses `ArticlesSharedModule`, supplies `reducerFactory: getReducer`,
74
100
  > and there is deliberately **no `baseQuery`** in the config.