@medquantis/design-system 1.1.0 → 1.3.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.
package/README.md CHANGED
@@ -11,13 +11,14 @@ The single source of the MedQuantis visual identity: design tokens, shared CSS,
11
11
  | `dist/tokens.css` | Every consumer: `--mq-*` custom properties ([ADR-0006](docs/architecture/decisions/adr-0006-mq-token-prefix.md)) |
12
12
  | `dist/tokens.json` | Scripts and pipelines that need resolved values |
13
13
  | `dist/tailwind.css` | Tailwind 4 and Nuxt UI apps (`@theme` with 50 to 950 scales); imports `tokens.css` itself |
14
+ | `dist/themes/dark.css`, `dist/themes/dark.json` | The dark theme: the semantic roles re-valued under `.dark`, for apps, documents and slides (see Dark theme) |
14
15
  | `dist/kit-contract.css` | homemade-claude-kit renderers only, deployed as `docs/ux/tokens.css`; never loaded in an app. Carries the latin fonts as embedded data, since the renderers inline this one file |
15
16
  | `css/` | Shared `mq-` components, slide canvas and A4 print styles for build-less media (see Shared CSS) |
16
17
  | `fonts/fonts.css` | Self-hosted Outfit, Newsreader and Geist Mono (variable woff2, latin and latin-ext, relative paths); deploy the folder whole |
17
18
  | `icons/lucide.min.js` | A pinned Lucide build (version and hash in `icons/manifest.json`) |
18
19
  | `brand/` | Monogram, favicon set and wordmark: `wordmark-on-light` and `wordmark-on-navy`, live text for screen and `-print` with outlined text |
19
- | `guidelines/` | Brand, voice, colour, typography, chart, data and accessibility rules |
20
- | `kit-manifest.json`, `dist/kit/` | The homemade-claude-kit delivery-comms renderers (contract 2.0.0): the manifest declares nine modules, each a file in `dist/kit/` (see Using it from the delivery-comms kit) |
20
+ | `guidelines/` | Brand, voice, colour, typography, chart, data and accessibility rules, and the app patterns of dense internal tools |
21
+ | `kit-manifest.json`, `dist/kit/` | The homemade-claude-kit delivery-comms renderers (contract 2.1.0): the manifest declares ten modules, each a file in `dist/kit/` (see Using it from the delivery-comms kit) |
21
22
  | `dist/DESIGN.md` | Tools and agents outside the kit: the tokens as [Google DESIGN.md](https://github.com/google-labs-code/design.md) front matter, then the guidelines as prose |
22
23
  | `dist/llms.txt`, `dist/llms-full.txt` | The guidelines and the token list for language models ([llmstxt.org](https://llmstxt.org/) format), generated from `guidelines/` and the tokens |
23
24
  | `plugins/medquantis-design-system/` | The Claude skill, as a Claude Code plugin and as a zip for claude.ai (see Claude skill) |
@@ -59,6 +60,7 @@ Every `mq-` component is `trial` until a consumer uses it in production; it beco
59
60
  | Distribution bar | `mq-distribution`, `mq-segment` | `css/components.css` | trial |
60
61
  | Legend | `mq-legend` (`is-column`, `mq-legend-value`), `mq-swatch` | `css/components.css` | trial |
61
62
  | Takeaway | `mq-takeaway` | `css/components.css` | trial |
63
+ | Monogram | `mq-monogram` | `css/components.css` | trial |
62
64
  | Footer | `mq-foot` | `css/components.css` | trial |
63
65
  | Slide canvas | `mq-slide` (`mq-slide-label`, `-title`, `-body`, `-foot`, `-number`) | `css/slide.css` | trial |
64
66
  | A4 page and desk | `mq-page`, `mq-desk`, `mq-no-print` | `css/print.css` | trial |
@@ -66,9 +68,27 @@ Every `mq-` component is `trial` until a consumer uses it in production; it beco
66
68
 
67
69
  Consumers lint their own CSS with the same rules: `export { default } from "@medquantis/design-system/stylelint";` in their `stylelint.config.mjs`. It rejects literal colours, fonts and shadows, `!important`, unknown custom properties, any radius other than the three `--mq-radius-*` tokens (so no pill shape can be drawn), and a local `--app-*`, `--site-*`, `--viz-*` or `--deck-*` property set to a literal instead of a `var(--mq-*)` reference (a `calc()` that scales tokens by a plain number is allowed). The config loads two stylelint plugins that the package does not install for you, so a consumer that lints adds them as its own dev dependencies, at the versions this release is tested with: `npm install --save-dev --save-exact stylelint@17.16.0 stylelint-declaration-strict-value@1.12.1 stylelint-value-no-unknown-custom-properties@6.1.1`.
68
70
 
71
+ ## Dark theme
72
+
73
+ MedQuantis has one dark theme ([ADR-0007](docs/architecture/decisions/adr-0007-medquantis-has-a-dark-theme.md)). It re-values the semantic `--mq-*` roles under the `.dark` class and ships in its own files: `dist/themes/dark.css` (exported as `@medquantis/design-system/themes/dark.css`) and its resolved values in `dist/themes/dark.json`. `tokens.css` keeps the light values only, and a role the theme does not name keeps its light value, so the `mq-` components and the Tailwind utilities, which all read `var(--mq-*)`, turn dark with no second class name. The values and every contrast pair are in [guidelines/colour.md](guidelines/colour.md), Dark theme.
74
+
75
+ - **Opt in:** load `themes/dark.css` after `tokens.css` (or after `tailwind.css`, which imports it), then set `.dark` on `<html>` for a whole page, or on one slide. The design system never switches on `prefers-color-scheme` by itself.
76
+ - **Apps:** the app decides how the viewer chooses, and may follow the viewer's setting by toggling `.dark`. A Tailwind 4 app declares the class as its dark variant, since Tailwind's `dark:` follows `prefers-color-scheme` by default; Nuxt UI 4 already declares it, so a Nuxt UI app adds only the theme file.
77
+
78
+ ```css
79
+ @import "tailwindcss";
80
+ @import "@medquantis/design-system/tailwind.css";
81
+ @import "@medquantis/design-system/themes/dark.css";
82
+ @custom-variant dark (&:where(.dark, .dark *));
83
+ ```
84
+
85
+ - **Documents and decks** (A4 reports, papers, PDFs, slides): light or dark is the author's choice, set at build time by putting `.dark` in the page, never taken from the reader's screen. A dark document stays dark in its PDF; light stays the default for anything likely to be printed on paper. Through the kit, see Using it from the delivery-comms kit.
86
+
87
+ `.dark` is a generic class name: a page that loads `themes/dark.css` and already uses `.dark` for something else gets the theme there too.
88
+
69
89
  ## Specimen
70
90
 
71
- The specimen is the visual reference of the design system: every colour token with its contrast pairings and their allowed use, the type scale, the radii and shadows, every `mq-` component, a 1280 x 720 slide, an A4 page, the brand files and the app status layer. `make specimen` generates it into `dist/specimen/index.html` from `dist/tokens.json`, `css/` and the fictitious data in `examples/`; open that file in a browser. The folder holds its own copies of the CSS, fonts and brand files, so it works offline and requests nothing outside itself. It is never edited by hand and never committed. `make build` does not build it, so the package outputs stay free of it.
91
+ The specimen is the visual reference of the design system: every colour token with its contrast pairings and their allowed use, the type scale, the radii and shadows, every `mq-` component, a 1280 x 720 slide, an A4 page, the brand files and the app status layer. `make specimen` generates it into `dist/specimen/index.html` from `dist/tokens.json`, `css/` and the fictitious data in `examples/`, and the same page in the dark theme into `dist/specimen/dark.html` from `dist/themes/dark.json`; open either file in a browser. The folder holds its own copies of the CSS, fonts and brand files, so it works offline and requests nothing outside itself. It is never edited by hand and never committed. `make build` does not build it, so the package outputs stay free of it.
72
92
 
73
93
  `make test` runs the unit tests, then Playwright and axe on the specimen: WCAG 2.2 AA with no serious or critical violation, no request outside the folder, a tagged PDF export, and screenshot baselines of each part of the page (header, every section, footer) and of the focus ring in `tests/e2e/specimen.spec.mjs-snapshots/`. The browser is Playwright's own Chromium headless shell, whose build is pinned by the exact `@playwright/test` version; `make browsers` installs it locally and CI installs the same build, so the baselines compare like with like. After a deliberate visual change, regenerate the baselines with `node_modules/.bin/playwright test --update-snapshots` and review the new images in the PR. When the CI job fails, it uploads Playwright's `test-results/` (actual, expected and diff images) as the `playwright-test-results` artifact, kept for 7 days.
74
94
 
@@ -110,15 +130,17 @@ Every other shipped file (`css/*.css`, `fonts/fonts.css`, `dist/kit-contract.css
110
130
 
111
131
  ## Using it from the delivery-comms kit
112
132
 
113
- The homemade-claude-kit renders decks (`com-slide-deck`), artefact views (`com-artefact-viz`) and release notes (`com-release-note`) with the design system a project pins. A project pins this package once, with the kit's `ux-design-system` skill, at an exact version (1.1.0 is the first release with the kit modules):
133
+ The homemade-claude-kit renders decks (`com-slide-deck`), artefact views (`com-artefact-viz`) and release notes (`com-release-note`) with the design system a project pins. A project pins this package once, with the kit's `ux-design-system` skill, at an exact version (1.1.0 is the first release with the kit modules; 1.2.0 adds the `dark` module and the classification cell):
114
134
 
115
135
  ```sh
116
- python3 <skill-dir>/scripts/design_system.py point @medquantis/design-system@1.1.0
136
+ python3 <skill-dir>/scripts/design_system.py point @medquantis/design-system@1.2.0
117
137
  ```
118
138
 
119
- In the kit's words this is `ux-design-system point @medquantis/design-system@1.1.0`. It writes the pin to `docs/ux/package.json`, checks the package's `kit-manifest.json` through the kit resolver and prints the Dependabot entry (`directory: "/docs/ux"`) that turns later releases into pull requests. Change the pin only with `point`, never by hand, and never add a `docs/ux/tokens.css`. Each render writes `<output>.provenance.json`, which names the module each value came from.
139
+ In the kit's words this is `ux-design-system point @medquantis/design-system@1.2.0`. It writes the pin to `docs/ux/package.json`, checks the package's `kit-manifest.json` through the kit resolver and prints the Dependabot entry (`directory: "/docs/ux"`) that turns later releases into pull requests. Change the pin only with `point`, never by hand, and never add a `docs/ux/tokens.css`. Each render writes `<output>.provenance.json`, which names the module each value came from.
120
140
 
121
- `kit-manifest.json`, at the top of the package, declares all nine modules of the contract, so the kit's base theme fills none of them:
141
+ The kit modules of 1.2.0 need delivery-comms 2.1.0 or later: the manifest declares contract 2.1.0 and the `dark` module, and a 2.0 kit refuses the unknown `dark` module. A project on a 2.0 kit updates the kit before it points at 1.2.0, or stays on 1.1.0.
142
+
143
+ `kit-manifest.json`, at the top of the package, declares all ten modules of the contract, so the kit's base theme fills none of them:
122
144
 
123
145
  | Module | File | Carries |
124
146
  | :-- | :-- | :-- |
@@ -127,12 +149,15 @@ In the kit's words this is `ux-design-system point @medquantis/design-system@1.1
127
149
  | `type` | `dist/kit/type.css` | The type scale, the body and tight line heights, weights 400 and 500 |
128
150
  | `space-shape` | `dist/kit/space-shape.css` | The smallest and largest spacing steps, the small radius, the 6 px maximum radius (no pill shapes) and the hairline stroke |
129
151
  | `document` | `dist/kit/document.css` | The A4 document: page colour, text size, rules, and the MedQuantis rules for the kit's document classes |
130
- | `slide-frame` | `dist/kit/slide-frame.css` | The 1280 by 720 slide: canvas, padding, section label, and the footer row with the legal line and slide number |
152
+ | `slide-frame` | `dist/kit/slide-frame.css` | The 1280 by 720 slide: canvas, padding, section label, and the footer row with the legal line, the classification cell and the slide number |
153
+ | `dark` | `dist/kit/dark.css` | The dark twin of every colour role (`--dark-<role>`), with the MedQuantis dark values, and the key element on dark: the navy panel gradient with its 1 px light-navy outline, never a glow |
131
154
  | `print` | `dist/kit/print.css` | The release-note report: page size, margins, and breaking changes in navy, never a status hue |
132
- | `brand` | `dist/kit/brand.css` | The wordmarks on light and on navy, the monogram and the legal line |
155
+ | `brand` | `dist/kit/brand.css` | The outlined (`-print`) wordmarks on light and on navy, since kit pages and PDFs cannot load the web font into an image; the monogram and the legal line |
133
156
  | `guidance` | `dist/kit/guidance.md` | The rules from `guidelines/` that the kit's skills read before writing a page |
134
157
 
135
- The kit lines a consumer repository adds to its `AGENTS.md` (or `CLAUDE.md`) are the kit's own: copy the [AGENTS.md block of `ux-design-system`](https://github.com/VictorHueni/homemade-claude-kit/blob/release/2.0/plugins/delivery-comms/skills/ux-design-system/references/agents-md-block.md) as it stands at the kit version you use, rather than a copy kept here. Below it, add the MedQuantis lines:
158
+ A kit deck turns dark per slide, with `dark` on the slide's root (`<div class="slide dark">`), or whole, with `theme: dark` in its `config.yaml`, which also serves a `mode: document`; the kit draws the key element (`dark-key`) and the highlighted text (`dark-accent`) from the `dark` roles. `classification:` in `config.yaml` prints the deck's classification at the right of the footer, before the slide number, in the small tertiary mono of the legal line: the label only, in the deck's language (`lang:`), never a code ([guidelines/brand.md](guidelines/brand.md)).
159
+
160
+ The kit lines a consumer repository adds to its `AGENTS.md` (or `CLAUDE.md`) are the kit's own: copy the [AGENTS.md block of `ux-design-system`](https://github.com/VictorHueni/homemade-claude-kit/blob/main/plugins/delivery-comms/skills/ux-design-system/references/agents-md-block.md) as it stands at the kit version you use, rather than a copy kept here. Below it, add the MedQuantis lines:
136
161
 
137
162
  ```markdown
138
163
  ## MedQuantis brand
@@ -181,7 +206,7 @@ make skill-zip # zip the skill for claude.ai into dist/
181
206
  make kit-check # run the homemade-claude-kit resolver and renderers on the packed package (needs KIT_DIR)
182
207
  ```
183
208
 
184
- `make kit-check` packs the package as npm would publish it, unpacks it into a fresh temporary folder and runs the kit resolver (`plugins/delivery-comms/shared/design_source.py provenance`) on it, through the kit's own mise environment, expecting all nine modules from this package. It then renders the fictitious fixtures of `tests/fixtures/kit-render/` with the kit's own renderers (a deck, an A4 document and a release-note report) and requires `PASS: no findings` from the kit validator `check_render.py` on each; `KIT_CHECK_OUT=<folder>` keeps a copy of the three pages. `KIT_DIR` names a local homemade-claude-kit checkout, for example `KIT_DIR=../homemade-claude-kit make kit-check`; without it the target prints a skip message and exits 0. It is a separate gate, not part of `make check`, because CI has no kit checkout. `kit-manifest.json` is a build output at the repository root (gitignored), because the kit reads the manifest only at the top of the package.
209
+ `make kit-check` packs the package as npm would publish it, unpacks it into a fresh temporary folder and runs the kit resolver (`plugins/delivery-comms/shared/design_source.py provenance`) on it, through the kit's own mise environment, expecting all ten modules from this package. It then renders the fictitious fixtures of `tests/fixtures/kit-render/` with the kit's own renderers (a deck, an A4 document and a release-note report) and requires `PASS: no findings` from the kit validator `check_render.py` on each; `KIT_CHECK_OUT=<folder>` keeps a copy of the three pages. `KIT_DIR` names a local homemade-claude-kit checkout, for example `KIT_DIR=../homemade-claude-kit make kit-check`; without it the target prints a skip message and exits 0. It is a separate gate, not part of `make check`, because CI has no kit checkout. `kit-manifest.json` is a build output at the repository root (gitignored), because the kit reads the manifest only at the top of the package.
185
210
 
186
211
  `fonts/` and `icons/` are committed copies of exact-pinned packages because they ship in the npm package; a unit test fails when they drift, so a Dependabot bump of a font or of Lucide needs `make assets` in the same PR. The screen wordmarks use live text, which renders in Outfit only where the page loads `fonts.css` (inline SVG); in an `<img>` use the `-print` variant.
187
212
 
package/css/app.css CHANGED
@@ -53,6 +53,6 @@
53
53
  :where(.mq-status.is-warning.is-filled) {
54
54
  border-color: transparent;
55
55
  background-color: var(--mq-warning-fill);
56
- color: var(--mq-ink);
56
+ color: var(--mq-on-warning-fill);
57
57
  }
58
58
  }
@@ -1,7 +1,7 @@
1
1
  /*! @medquantis/design-system | © 2026 MedQuantis SA | MIT License, see LICENSE */
2
2
  /*
3
3
  * MedQuantis document components: cards, navy panel, KPI tiles, table, badges, callout,
4
- * progress and distribution bars, legend, takeaway and footer.
4
+ * progress and distribution bars, legend, takeaway, monogram and footer.
5
5
  * Documents are navy-only (ADR-0004): states read by mark style (solid, ring, pale, outline,
6
6
  * quiet, dashed), never by a status hue. Status badges live in app.css.
7
7
  * Text on a navy or tinted background names it in a mq-bg comment, so the contrast test can
@@ -150,6 +150,7 @@
150
150
  :where(.mq-kpi.is-navy) {
151
151
  border-color: transparent;
152
152
  background: linear-gradient(160deg, var(--mq-panel-start), var(--mq-panel-end));
153
+ box-shadow: var(--mq-shadow-edge);
153
154
  }
154
155
 
155
156
  :where(.mq-kpi.is-navy .mq-kpi-label, .mq-kpi.is-navy .mq-kpi-caption) {
@@ -174,7 +175,7 @@
174
175
 
175
176
  :where(.mq-kpi.is-pale .mq-kpi-value) {
176
177
  /* mq-bg: --mq-accent-pale */
177
- color: var(--mq-accent);
178
+ color: var(--mq-accent-highlight);
178
179
  }
179
180
 
180
181
  /* Table */
@@ -223,6 +224,11 @@
223
224
  background-color: var(--mq-accent-pale);
224
225
  }
225
226
 
227
+ :where(.mq-table tr.is-highlight > .is-key) {
228
+ /* mq-bg: --mq-accent-pale */
229
+ color: var(--mq-accent-highlight);
230
+ }
231
+
226
232
  :where(.mq-table tr.is-total > td) {
227
233
  border-top: var(--mq-stroke-strong) solid var(--mq-accent);
228
234
  border-bottom: 0;
@@ -258,7 +264,7 @@
258
264
 
259
265
  :where(.mq-badge.is-pale) {
260
266
  background-color: var(--mq-accent-pale);
261
- color: var(--mq-accent);
267
+ color: var(--mq-accent-highlight);
262
268
  }
263
269
 
264
270
  :where(.mq-badge.is-outline) {
@@ -292,7 +298,7 @@
292
298
  :where(.mq-callout-title) {
293
299
  /* mq-bg: --mq-accent-pale-2 */
294
300
  margin: 0 0 var(--mq-space-1);
295
- color: var(--mq-accent);
301
+ color: var(--mq-accent-highlight);
296
302
  font-family: var(--mq-font-family-mono);
297
303
  font-size: var(--mq-font-size-eyebrow);
298
304
  letter-spacing: 0.14em;
@@ -456,6 +462,16 @@
456
462
  line-height: 1.4;
457
463
  }
458
464
 
465
+ /* Monogram: the brand file at 48 px, where its corners of 8/64 of the side are the 6 px
466
+ radius, so the panel's edge it takes on dark follows them (owner ruling 14). */
467
+ :where(.mq-monogram) {
468
+ display: block;
469
+ width: var(--mq-space-7);
470
+ height: var(--mq-space-7);
471
+ border-radius: var(--mq-radius-medium);
472
+ box-shadow: var(--mq-shadow-edge);
473
+ }
474
+
459
475
  /* Footer: sources, legal line, illustrative-data note */
460
476
  :where(.mq-foot) {
461
477
  margin: var(--mq-space-6) 0 0;
@@ -0,0 +1,6 @@
1
+ /*! @medquantis/design-system | © 2026 MedQuantis SA | MIT License, see LICENSE */
2
+ /*
3
+ * No class rule (Plan-0003 increment 04): the kit's own dark layer remaps the roles to their
4
+ * --dark- twins on a dark slide or page and draws .dark-key and .dark-accent from the dark roles,
5
+ * so a rule here would compete with it. The dark roles come from tokens/kit/, on :root only.
6
+ */
@@ -8,8 +8,9 @@
8
8
  * right (owner ruling 11, guidelines/brand.md).
9
9
  *
10
10
  * Cascade: the kit inlines its base slide-frame.css before this module, so these rules restyle
11
- * it; com-slide-deck adds its baseline (controls hint, presentation mode) after the design layer,
12
- * so the rules for those classes carry one more element in their selector.
11
+ * it; com-slide-deck adds its baseline (controls hint, presentation mode) and the classification
12
+ * cell after the design layer, so the rules for those classes carry one more element in their
13
+ * selector.
13
14
  * The footer row and the source line use --tertiary, the tertiary grey of owner ruling 1.
14
15
  */
15
16
 
@@ -56,6 +57,18 @@
56
57
  margin: 0;
57
58
  }
58
59
 
60
+ /* The deck's classification (owner ruling 17): at the right of the footer row, before the number,
61
+ in the legal line's small tertiary mono, the label as written in the deck's language. The kit's
62
+ cell rule (margin-left: auto, capitals) comes after the design layer, so this one carries the
63
+ footer in its selector. */
64
+ .slide-footer .slide-classification {
65
+ margin-left: auto;
66
+ color: var(--tertiary);
67
+ font-family: var(--font-mono);
68
+ font-size: var(--font-size-sm);
69
+ text-transform: none;
70
+ }
71
+
59
72
  /* mq-slide-number: the eyebrow size, tabular figures. */
60
73
  .slide-number {
61
74
  font-size: var(--font-size-xs);