@omnitend/dashboard-for-laravel 0.26.0 → 0.28.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.
@@ -1,8 +1,8 @@
1
1
  {
2
- "generated": "2026-07-13T10:49:19.086Z",
2
+ "generated": "2026-07-18T13:55:43.374Z",
3
3
  "package": {
4
4
  "name": "@omnitend/dashboard-for-laravel",
5
- "version": "0.26.0"
5
+ "version": "0.28.0"
6
6
  },
7
7
  "components": {
8
8
  "base": [
@@ -1265,7 +1265,7 @@
1265
1265
  "name": "DXBarChart",
1266
1266
  "category": "extended",
1267
1267
  "filePath": "resources/js/components/charts/DXBarChart.vue",
1268
- "description": "A themed bar chart. Thin wrapper around vue-chartjs (chart.js) with the Bootstrap palette applied and dashboard-friendly defaults (no x gridlines, formatted value ticks, responsive, legend hidden for a single series). Pass `labels` + `datasets`; `options` deep-merges over the defaults. Requires the optional peer deps `chart.js` and `vue-chartjs`.",
1268
+ "description": "A themed bar chart. Thin wrapper around vue-chartjs (chart.js) with the theme's dedicated chart palette (--dx-chart-1..8) applied and dashboard-friendly defaults (no x gridlines, formatted value ticks, responsive, legend hidden for a single series). Pass `labels` + `datasets`; `options` deep-merges over the defaults. Requires the optional peer deps `chart.js` and `vue-chartjs`.",
1269
1269
  "props": [
1270
1270
  {
1271
1271
  "name": "labels",
@@ -1751,7 +1751,7 @@
1751
1751
  "name": "DXDoughnutChart",
1752
1752
  "category": "extended",
1753
1753
  "filePath": "resources/js/components/charts/DXDoughnutChart.vue",
1754
- "description": "A themed doughnut chart. Thin wrapper around vue-chartjs (chart.js) with one Bootstrap palette colour per slice and dashboard-friendly defaults. Pass `labels` + either `data` (a number array) or full `datasets`; `options` deep-merges over the defaults. Requires the optional peer deps `chart.js` and `vue-chartjs`.",
1754
+ "description": "A themed doughnut chart. Thin wrapper around vue-chartjs (chart.js) with one chart-palette colour (--dx-chart-1..8) per slice and dashboard-friendly defaults. Pass `labels` + either `data` (a number array) or full `datasets`; `options` deep-merges over the defaults. Requires the optional peer deps `chart.js` and `vue-chartjs`.",
1755
1755
  "props": [
1756
1756
  {
1757
1757
  "name": "labels",
@@ -2324,7 +2324,7 @@
2324
2324
  "name": "DXLineChart",
2325
2325
  "category": "extended",
2326
2326
  "filePath": "resources/js/components/charts/DXLineChart.vue",
2327
- "description": "A themed line chart. Thin wrapper around vue-chartjs (chart.js) with the Bootstrap palette applied (themed stroke + translucent fill, smoothed line) and dashboard-friendly defaults. Pass `labels` + `datasets`; `options` deep-merges over the defaults. Requires the optional peer deps `chart.js` and `vue-chartjs`.",
2327
+ "description": "A themed line chart. Thin wrapper around vue-chartjs (chart.js) with the theme's dedicated chart palette applied (--dx-chart-1..8 stroke + translucent fill, smoothed line) and dashboard-friendly defaults. Pass `labels` + `datasets`; `options` deep-merges over the defaults. Requires the optional peer deps `chart.js` and `vue-chartjs`.",
2328
2328
  "props": [
2329
2329
  {
2330
2330
  "name": "labels",
@@ -3045,7 +3045,123 @@
3045
3045
  "title": "binding"
3046
3046
  }
3047
3047
  ]
3048
+ }
3049
+ ],
3050
+ "methods": []
3051
+ },
3052
+ {
3053
+ "name": "DXTableEditorModal",
3054
+ "category": "extended",
3055
+ "filePath": "resources/js/components/extended/DXTableEditorModal.vue",
3056
+ "description": "Internal edit/create modal for `DXTable` (#129). Renders the `DModal` + `DXForm` and maps DXTable's `edit-value`/`edit-span`/`tab-*` slots onto DXForm's `value`/`span`/`tab-*` slots, providing the row context and a `close` handle. Extracted so `DXTable` is a thin façade over the table render, the pagination, the editor logic (`useResourceEditor`) and this modal — rather than owning all four. The edit-modal slots reach here forwarded from DXTable (consumer → DXTable → here → DXForm). This component computes which keyed slots the consumer actually provided from its OWN `$slots`, so the slot-plumbing lives entirely with the modal, not the composable.",
3057
+ "props": [
3058
+ {
3059
+ "name": "show",
3060
+ "type": "boolean",
3061
+ "required": true,
3062
+ "description": "Whether the modal is open (v-model:show)."
3063
+ },
3064
+ {
3065
+ "name": "title",
3066
+ "type": "string",
3067
+ "required": true,
3068
+ "description": "Modal title."
3069
+ },
3070
+ {
3071
+ "name": "size",
3072
+ "type": "union",
3073
+ "required": false,
3074
+ "description": "Modal size."
3075
+ },
3076
+ {
3077
+ "name": "loading",
3078
+ "type": "boolean",
3079
+ "required": true,
3080
+ "description": "True while the full record is loading via showUrl."
3081
+ },
3082
+ {
3083
+ "name": "form",
3084
+ "type": "any",
3085
+ "required": true,
3086
+ "description": "The edit/create form (a useForm instance), or null before first open."
3087
+ },
3088
+ {
3089
+ "name": "formInstanceKey",
3090
+ "type": "number",
3091
+ "required": true,
3092
+ "description": "Bumped per modal open to remount the DXForm subtree (fresh DXField state)."
3093
+ },
3094
+ {
3095
+ "name": "activeTab",
3096
+ "type": "number",
3097
+ "required": true,
3098
+ "description": "Active tab index (v-model:active-tab)."
3099
+ },
3100
+ {
3101
+ "name": "fields",
3102
+ "type": "Array",
3103
+ "required": true,
3104
+ "description": "Field definitions passed to DXForm."
3105
+ },
3106
+ {
3107
+ "name": "tabs",
3108
+ "type": "Array",
3109
+ "required": false,
3110
+ "description": "Tab definitions passed to DXForm."
3111
+ },
3112
+ {
3113
+ "name": "item",
3114
+ "type": "any",
3115
+ "required": true,
3116
+ "description": "The row being edited (null in create mode) — the slot/context row."
3117
+ },
3118
+ {
3119
+ "name": "isCreateMode",
3120
+ "type": "boolean",
3121
+ "required": true,
3122
+ "description": "Whether the modal is in create mode (vs edit)."
3123
+ },
3124
+ {
3125
+ "name": "pendingAction",
3126
+ "type": "union",
3127
+ "required": true,
3128
+ "description": "Which action is in flight, so Save/Delete show their own loading label."
3129
+ },
3130
+ {
3131
+ "name": "deleteUrl",
3132
+ "type": "string",
3133
+ "required": false,
3134
+ "description": "Delete endpoint — gates the Delete button."
3135
+ }
3136
+ ],
3137
+ "events": [
3138
+ {
3139
+ "name": "update:show",
3140
+ "description": "v-model:show — the modal open state changed.",
3141
+ "properties": []
3142
+ },
3143
+ {
3144
+ "name": "update:activeTab",
3145
+ "description": "v-model:active-tab — the active tab changed.",
3146
+ "properties": []
3048
3147
  },
3148
+ {
3149
+ "name": "save",
3150
+ "description": "Save was requested (footer button or DXForm submit).",
3151
+ "properties": []
3152
+ },
3153
+ {
3154
+ "name": "cancel",
3155
+ "description": "Cancel/close was requested.",
3156
+ "properties": []
3157
+ },
3158
+ {
3159
+ "name": "delete",
3160
+ "description": "Delete was requested.",
3161
+ "properties": []
3162
+ }
3163
+ ],
3164
+ "slots": [
3049
3165
  {
3050
3166
  "name": "`edit-value(${key})`",
3051
3167
  "description": "Custom input for field `<key>` in the edit/create modal, forwarded to DXForm. Name it `edit-value(<fieldKey>)`.",
@@ -3211,6 +3327,76 @@
3211
3327
  ],
3212
3328
  "methods": []
3213
3329
  },
3330
+ {
3331
+ "name": "DXTablePagination",
3332
+ "category": "extended",
3333
+ "filePath": "resources/js/components/extended/DXTablePagination.vue",
3334
+ "description": "Internal pagination + per-page footer for `DXTable`. Renders the pager, the per-page selector, and the \"X to Y out of Z items\" info line (with the \"Filtered from N\" note when a column filter is active). Extracted because `DXTable` used to carry three byte-identical copies of this footer — one per data mode (client-side / inertia / provider) — that differed only in which `PaginationData` object and page-change handler they used, so every footer fix had to be written three times (#123). `DXTable` now derives the active pagination + handler and renders this once. It owns its own `:deep(.pagination …)` styling (rather than leaving it in `DXTable`), because a scoped `:deep()` in `DXTable` would not reach the pager once it lives in a child component — the scope-id doesn't cross the boundary. Anchoring the rules on this component's own `.dx-table-pagination` root (a plain element it owns) gives the scope-id a deterministic host.",
3335
+ "props": [
3336
+ {
3337
+ "name": "pagination",
3338
+ "type": "PaginationData",
3339
+ "required": true,
3340
+ "description": "The active pagination metadata (client-side / inertia / provider)."
3341
+ },
3342
+ {
3343
+ "name": "perPage",
3344
+ "type": "number",
3345
+ "required": true,
3346
+ "description": "The effective page size, shown in the selector."
3347
+ },
3348
+ {
3349
+ "name": "perPageOptions",
3350
+ "type": "Array",
3351
+ "required": true,
3352
+ "description": "Page sizes offered by the selector."
3353
+ },
3354
+ {
3355
+ "name": "showPagination",
3356
+ "type": "boolean",
3357
+ "required": true,
3358
+ "description": "Whether to render the pager (still hidden when there is only one page)."
3359
+ },
3360
+ {
3361
+ "name": "showPerPageSelector",
3362
+ "type": "boolean",
3363
+ "required": true,
3364
+ "description": "Whether to render the per-page selector."
3365
+ },
3366
+ {
3367
+ "name": "singularItemName",
3368
+ "type": "string",
3369
+ "required": true,
3370
+ "description": "Singular item noun for the info line (e.g. \"product\")."
3371
+ },
3372
+ {
3373
+ "name": "pluralItemName",
3374
+ "type": "string",
3375
+ "required": true,
3376
+ "description": "Plural item noun for the info line (e.g. \"products\")."
3377
+ },
3378
+ {
3379
+ "name": "hasActiveFilters",
3380
+ "type": "boolean",
3381
+ "required": true,
3382
+ "description": "Whether a column filter is active — gates the \"Filtered from N\" note."
3383
+ }
3384
+ ],
3385
+ "events": [
3386
+ {
3387
+ "name": "page-change",
3388
+ "description": "The user picked a page. Payload is the new 1-based page number.",
3389
+ "properties": []
3390
+ },
3391
+ {
3392
+ "name": "per-page-change",
3393
+ "description": "The per-page selector changed. Payload is the raw DFormSelect model value\n(the parent's `handlePerPageChange` narrows it — DFormSelect mirrors BVN's\nmodel, which can be a scalar, null, or array).",
3394
+ "properties": []
3395
+ }
3396
+ ],
3397
+ "slots": [],
3398
+ "methods": []
3399
+ },
3214
3400
  {
3215
3401
  "name": "DXTableShell",
3216
3402
  "category": "extended",
@@ -3295,8 +3481,8 @@
3295
3481
  ]
3296
3482
  },
3297
3483
  "stats": {
3298
- "totalComponents": 73,
3484
+ "totalComponents": 75,
3299
3485
  "baseComponents": 56,
3300
- "extendedComponents": 17
3486
+ "extendedComponents": 19
3301
3487
  }
3302
3488
  }
@@ -1,7 +1,7 @@
1
1
  # Documentation Map
2
2
 
3
3
  > Auto-generated hierarchical overview of all documentation
4
- > Last updated: 2026-07-13T10:49:19.148Z
4
+ > Last updated: 2026-07-18T13:55:43.432Z
5
5
 
6
6
  This file provides a complete map of all available documentation for AI agents and developers.
7
7
 
@@ -95,7 +95,7 @@ This file provides a complete map of all available documentation for AI agents a
95
95
  - [Forms](/guide/forms): The library includes a type-safe form system with validation, auto-generated forms, and composables.
96
96
  - [Getting Started](/guide/getting-started): > **Alpha Notice:** v0.x — expect changes between minor versions.
97
97
  - [Installation](/guide/installation): ```bash
98
- - [Theming](/guide/theming): The library includes a custom Bootstrap 5 theme that can be easily customised using CSS variables an
98
+ - [Theming](/guide/theming): The library ships a custom Bootstrap 5 theme built around a **soft-first
99
99
  - [TypeScript](/guide/typescript): The library is written in TypeScript and includes full type definitions for all components, composab
100
100
 
101
101
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnitend/dashboard-for-laravel",
3
- "version": "0.26.0",
3
+ "version": "0.28.0",
4
4
  "description": "Vue 3 dashboard components for Laravel with Bootstrap Vue Next",
5
5
  "type": "module",
6
6
  "main": "./dist/dashboard-for-laravel.umd.cjs",
@@ -38,7 +38,7 @@
38
38
  "pretest:headless": "vite build",
39
39
  "test:headless": "vitest --run --browser.headless",
40
40
  "docs:dev": "astro dev",
41
- "docs:build": "npm run docs:generate:ai && astro build",
41
+ "docs:build": "npm run docs:generate:ai && astro build && node scripts/check-docs-base-links.mjs",
42
42
  "docs:preview": "astro preview",
43
43
  "docs:generate": "node scripts/generate-component-docs.mjs",
44
44
  "docs:generate:ai": "node scripts/generate-llms-txt.mjs && node scripts/generate-api-manifest.mjs && node scripts/generate-docs-map.mjs",
@@ -7,19 +7,32 @@
7
7
  // The Sass module for `color.mix()` etc. `@use` must precede all other
8
8
  // statements (variable overrides, `@import`), so it stays at the very top.
9
9
  @use "sass:color";
10
+ @use "sass:list";
11
+ @use "sass:map";
10
12
 
11
13
  // ----------------------------------------------------------------------------
12
14
  // Colors
13
15
  // ----------------------------------------------------------------------------
14
16
 
15
- $primary: #4f46e5; // Indigo-600 (8.6:1 with white)
16
- $secondary: #475569; // Slate-600 (7.0:1 with white) - darkened for better contrast
17
- $success: #10b981; // Emerald-500 (6.7:1 with white)
18
- $danger: #dc2626; // Red-600 (7.2:1 with white) - darkened for better contrast
19
- $warning: #f59e0b; // Amber-500 (7.7:1 with white)
20
- $info: #2563eb; // Blue-600 (7.3:1 with white)
17
+ // Base $theme-colors carry each hue's READABLE-ON-LIGHT identity (its "emphasis"
18
+ // shade) — this is what `.text-*`, `.link-*`, `.border-*` and outline buttons
19
+ // use, so every one must clear AA on white. Bold solid FILLS (primary/danger
20
+ // buttons) and SOFT tints (soft buttons, badges, alerts) are applied as explicit
21
+ // overrides after the Bootstrap import, from the $dx-variants map. See
22
+ // plans/2026-07-18-semantic-colour-system.md.
23
+ $primary: #151e2d; // Omni Tend brand navy (16.73:1 on white)
24
+ $secondary: #475569; // Slate-600 (7.58:1 on white)
25
+ $success: #4d7c0f; // Lime-700 emphasis (4.99:1 on white); soft/vivid tints in $dx-variants
26
+ $danger: #dc2626; // Red-600 (4.83:1 on white)
27
+ $warning: #b45309; // Amber-700 emphasis (5.02:1 on white)
28
+ $info: #2563eb; // Blue-600 (5.17:1 on white)
21
29
  $light: #f8fafc; // Slate-50 (uses dark text)
22
- $dark: #0f172a; // Slate-900 (uses white text)
30
+ $dark: #151e2d; // Brand navy — sidebar/high-contrast surfaces (was Slate-900 #0f172a)
31
+
32
+ // Links are the info-blue, independent of the near-black brand primary (a navy
33
+ // link would read as bold text, not a link).
34
+ $link-color: #2563eb;
35
+ $link-shade-percentage: 15%;
23
36
 
24
37
  // ----------------------------------------------------------------------------
25
38
  // Contrast Settings
@@ -154,6 +167,21 @@ $badge-padding-y: 0.25rem;
154
167
  $badge-padding-x: 0.625rem;
155
168
  $badge-font-weight: 500;
156
169
 
170
+ // The ONE source of truth for the semantic colour system. Each variant carries
171
+ // its SOLID fill + same-hue text (primary/danger buttons, solid fills), its SOFT
172
+ // tint pair (soft buttons, badges, alerts, status), its EMPHASIS shade (outline
173
+ // buttons, coloured links, .text-*), and whether its button is solid or soft.
174
+ // The overrides after the Bootstrap import loop over this map. All pairs are
175
+ // WCAG AA verified. See plans/2026-07-18-semantic-colour-system.md.
176
+ $dx-variants: (
177
+ "primary": (solid-bg: #151e2d, solid-text: #e9f0f8, soft-bg: #e9f0f8, soft-text: #151e2d, emphasis: #151e2d, button: "solid"),
178
+ "secondary": (solid-bg: #475569, solid-text: #e6ebf2, soft-bg: #e6ebf2, soft-text: #29374a, emphasis: #475569, button: "soft"),
179
+ "success": (solid-bg: #84cc16, solid-text: #203b0e, soft-bg: #cdf9b2, soft-text: #203b0e, emphasis: #4d7c0f, button: "soft"),
180
+ "danger": (solid-bg: #dc2626, solid-text: #ffffff, soft-bg: #f5dff1, soft-text: #59194a, emphasis: #dc2626, button: "solid"),
181
+ "warning": (solid-bg: #f59e0b, solid-text: #512d05, soft-bg: #fce5c4, soft-text: #512d05, emphasis: #b45309, button: "soft"),
182
+ "info": (solid-bg: #2563eb, solid-text: #eef4ff, soft-bg: #deebff, soft-text: #12376c, emphasis: #2563eb, button: "soft"),
183
+ );
184
+
157
185
  // ----------------------------------------------------------------------------
158
186
  // Dashboard
159
187
  // ----------------------------------------------------------------------------
@@ -420,6 +448,139 @@ $bootstrap-icons-font-src: url("bootstrap-icons/font/fonts/bootstrap-icons.woff2
420
448
  }
421
449
  }
422
450
 
451
+ // ----------------------------------------------------------------------------
452
+ // Semantic colour system (soft-first)
453
+ // ----------------------------------------------------------------------------
454
+ // One loop over $dx-variants applies the whole system on top of Bootstrap:
455
+ //
456
+ // - Buttons: primary/danger are SOLID (fill from the base colour; we only
457
+ // override the label to the same-hue tint, letting Bootstrap shade hover/
458
+ // active). secondary/success/warning/info are SOFT (light tint bg + dark
459
+ // text, fully overridden incl. hover/active).
460
+ // - Outline buttons: the emphasis shade for border/label, the soft tint on hover.
461
+ // - Badges: `.badge.text-bg-*` re-tinted soft — needs `!important` because
462
+ // Bootstrap's `.text-bg-*` helper sets its colours `!important` by design.
463
+ // - Alerts: the soft tint.
464
+ // - Links/text: `.link-*` and `.text-*` take the emphasis shade so they read
465
+ // on white (the base colours already carry emphasis for most variants; this
466
+ // pins the two — success/warning — whose base differs).
467
+ @each $name, $v in $dx-variants {
468
+ $solid-bg: map.get($v, "solid-bg");
469
+ $solid-text: map.get($v, "solid-text");
470
+ $soft-bg: map.get($v, "soft-bg");
471
+ $soft-text: map.get($v, "soft-text");
472
+ $emphasis: map.get($v, "emphasis");
473
+ $is-solid: map.get($v, "button") == "solid";
474
+
475
+ @if $is-solid {
476
+ // Solid button: keep Bootstrap's fill + hover/active shading (from the base
477
+ // colour), override only the LABEL to the same-hue tint.
478
+ .btn-#{$name} {
479
+ --bs-btn-color: #{$solid-text};
480
+ --bs-btn-hover-color: #{$solid-text};
481
+ --bs-btn-active-color: #{$solid-text};
482
+ --bs-btn-disabled-color: #{$solid-text};
483
+ }
484
+ } @else {
485
+ // Soft button: light tint bg + dark same-hue label; darken the tint slightly
486
+ // on hover/active for feedback.
487
+ .btn-#{$name} {
488
+ --bs-btn-color: #{$soft-text};
489
+ --bs-btn-bg: #{$soft-bg};
490
+ --bs-btn-border-color: #{$soft-bg};
491
+ --bs-btn-hover-color: #{$soft-text};
492
+ --bs-btn-hover-bg: #{color.scale($soft-bg, $lightness: -6%)};
493
+ --bs-btn-hover-border-color: #{color.scale($soft-bg, $lightness: -6%)};
494
+ --bs-btn-active-color: #{$soft-text};
495
+ --bs-btn-active-bg: #{color.scale($soft-bg, $lightness: -10%)};
496
+ --bs-btn-active-border-color: #{color.scale($soft-bg, $lightness: -10%)};
497
+ --bs-btn-disabled-color: #{$soft-text};
498
+ --bs-btn-disabled-bg: #{$soft-bg};
499
+ --bs-btn-disabled-border-color: #{$soft-bg};
500
+ }
501
+ }
502
+
503
+ // Outline button: emphasis border/label, fills to the soft tint on hover so it
504
+ // stays calm (not a sudden saturated block).
505
+ .btn-outline-#{$name} {
506
+ --bs-btn-color: #{$emphasis};
507
+ --bs-btn-border-color: #{$emphasis};
508
+ --bs-btn-hover-color: #{$soft-text};
509
+ --bs-btn-hover-bg: #{$soft-bg};
510
+ --bs-btn-hover-border-color: #{$emphasis};
511
+ --bs-btn-active-color: #{$soft-text};
512
+ --bs-btn-active-bg: #{$soft-bg};
513
+ --bs-btn-active-border-color: #{$emphasis};
514
+ }
515
+
516
+ // Badge — soft tint. `!important` beats Bootstrap's `.text-bg-*` helper.
517
+ .badge.text-bg-#{$name} {
518
+ background-color: #{$soft-bg} !important;
519
+ color: #{$soft-text} !important;
520
+ }
521
+
522
+ // Alert — soft tint, with the emphasis shade as the accent border.
523
+ .alert-#{$name} {
524
+ --bs-alert-bg: #{$soft-bg};
525
+ --bs-alert-color: #{$soft-text};
526
+ --bs-alert-border-color: #{color.scale($soft-bg, $lightness: -8%)};
527
+ --bs-alert-link-color: #{$emphasis};
528
+ }
529
+
530
+ // `.text-#{$name}` and `.link-#{$name}` need no override: each variant's base
531
+ // colour IS its emphasis shade, so Bootstrap already renders them legible.
532
+ }
533
+
534
+ // primary is near-black navy, so Bootstrap's darken-on-hover is imperceptible —
535
+ // the main action button would have no visible hover feedback. Lighten instead.
536
+ // (danger's red darkens visibly, so the generic solid path is fine for it.)
537
+ .btn-primary {
538
+ --bs-btn-hover-bg: #{color.scale($primary, $lightness: 20%)};
539
+ --bs-btn-hover-border-color: #{color.scale($primary, $lightness: 20%)};
540
+ --bs-btn-active-bg: #{color.scale($primary, $lightness: 12%)};
541
+ --bs-btn-active-border-color: #{color.scale($primary, $lightness: 12%)};
542
+ }
543
+
544
+ // Tertiary "ghost" button: a `link`-variant button restyled to read as a quiet
545
+ // action, not a hyperlink — body colour, no underline, faint hover surface.
546
+ .btn-link {
547
+ --bs-btn-color: var(--bs-body-color);
548
+ --bs-btn-hover-color: var(--bs-body-color);
549
+ --bs-btn-active-color: var(--bs-body-color);
550
+ text-decoration: none;
551
+
552
+ &:hover {
553
+ background-color: var(--bs-light);
554
+ }
555
+ }
556
+
557
+ // ----------------------------------------------------------------------------
558
+ // Chart palette (data-viz)
559
+ // ----------------------------------------------------------------------------
560
+ // Dedicated categorical palette for the DX*Chart components — deliberately NOT
561
+ // the semantic UI colours (#141). Two reasons: the base $theme-colors carry
562
+ // dark "emphasis" shades (AA text/link colours, too muted for data series), and
563
+ // status colours (success/danger) shouldn't impersonate "series 2" — a green or
564
+ // red series should mean something.
565
+ //
566
+ // Eight vivid mid-lightness hues in a FIXED order. The order is load-bearing:
567
+ // it was derived by exhaustively permuting the hues (slot 1 pinned to the brand
568
+ // info-blue) to maximise the minimum adjacent-pair colour-vision-deficiency
569
+ // separation — validated at min adjacent ΔE 13.8 (OKLab ×100, protan/deutan
570
+ // simulated), min normal-vision ΔE 20.3, every hue ≥ 3:1 contrast on white.
571
+ // Reordering or swapping a hue needs that validation re-run, not an eyeball.
572
+ //
573
+ // Published as --dx-chart-1..8 so consumers can retheme charts by overriding
574
+ // the variables; chartTheme.ts reads them at runtime (with these hexes as
575
+ // fallbacks for SSR / missing-CSS cases — keep the two lists in sync).
576
+ $dx-chart-palette: (#2563eb, #65a30d, #7c3aed, #0d9488, #ea580c, #0891b2, #d97706, #db2777);
577
+
578
+ :root {
579
+ @for $i from 1 through list.length($dx-chart-palette) {
580
+ --dx-chart-#{$i}: #{list.nth($dx-chart-palette, $i)};
581
+ }
582
+ }
583
+
423
584
  // ----------------------------------------------------------------------------
424
585
  // Autocomplete
425
586
  // ----------------------------------------------------------------------------
@@ -1,7 +1,7 @@
1
1
  <!--
2
2
  @component
3
3
  A themed bar chart. Thin wrapper around vue-chartjs (chart.js) with the
4
- Bootstrap palette applied and dashboard-friendly defaults (no x gridlines,
4
+ theme's dedicated chart palette (--dx-chart-1..8) applied and dashboard-friendly defaults (no x gridlines,
5
5
  formatted value ticks, responsive, legend hidden for a single series). Pass
6
6
  `labels` + `datasets`; `options` deep-merges over the defaults.
7
7
 
@@ -1,7 +1,7 @@
1
1
  <!--
2
2
  @component
3
3
  A themed doughnut chart. Thin wrapper around vue-chartjs (chart.js) with one
4
- Bootstrap palette colour per slice and dashboard-friendly defaults. Pass
4
+ chart-palette colour (--dx-chart-1..8) per slice and dashboard-friendly defaults. Pass
5
5
  `labels` + either `data` (a number array) or full `datasets`; `options`
6
6
  deep-merges over the defaults.
7
7
 
@@ -1,7 +1,7 @@
1
1
  <!--
2
2
  @component
3
3
  A themed line chart. Thin wrapper around vue-chartjs (chart.js) with the
4
- Bootstrap palette applied (themed stroke + translucent fill, smoothed line)
4
+ theme's dedicated chart palette applied (--dx-chart-1..8 stroke + translucent fill, smoothed line)
5
5
  and dashboard-friendly defaults. Pass `labels` + `datasets`; `options`
6
6
  deep-merges over the defaults.
7
7
 
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Shared theming for the DX*Chart wrappers: a categorical palette pulled from
3
- * the live Bootstrap theme, dashboard-friendly default options (no gridlines /
2
+ * Shared theming for the DX*Chart wrappers: the dedicated categorical chart
3
+ * palette (--dx-chart-* from the live theme), dashboard-friendly default options (no gridlines /
4
4
  * legend clutter, responsive, formatted ticks), and a one-time Chart.js
5
5
  * registration. chart.js / vue-chartjs are OPTIONAL peer deps.
6
6
  */
@@ -40,20 +40,23 @@ export function registerCharts(): void {
40
40
  registered = true;
41
41
  }
42
42
 
43
- // Bootstrap CSS variables to read, in a sensible categorical order. Fallbacks
44
- // mirror Bootstrap 5 defaults so charts still render without the theme (SSR /
45
- // tests / a consumer who didn't import the CSS).
46
- const PALETTE_VARS: Array<[string, string]> = [
47
- ["--bs-primary", "#0d6efd"],
48
- ["--bs-success", "#198754"],
49
- ["--bs-info", "#0dcaf0"],
50
- ["--bs-warning", "#ffc107"],
51
- ["--bs-danger", "#dc3545"],
52
- ["--bs-purple", "#6f42c1"],
53
- ["--bs-teal", "#20c997"],
54
- ["--bs-orange", "#fd7e14"],
55
- ["--bs-pink", "#d63384"],
56
- ["--bs-secondary", "#6c757d"],
43
+ // The dedicated data-viz palette (`$dx-chart-palette` in theme.scss) — eight
44
+ // vivid hues in a fixed, CVD-validated order, published as --dx-chart-1..8.
45
+ // Deliberately NOT the semantic --bs-* colours (#141): the base theme colours
46
+ // are dark AA "emphasis" shades (too muted for series), and status colours
47
+ // shouldn't impersonate "series 2". Fallbacks mirror the shipped theme so
48
+ // charts still render without the CSS (SSR / tests / a consumer who didn't
49
+ // import it). Sync with theme.scss's $dx-chart-palette is enforced by a test
50
+ // that parses the Sass source (charts.test.ts), which is why this is exported.
51
+ export const PALETTE_VARS: Array<[string, string]> = [
52
+ ["--dx-chart-1", "#2563eb"], // blue
53
+ ["--dx-chart-2", "#65a30d"], // lime
54
+ ["--dx-chart-3", "#7c3aed"], // violet
55
+ ["--dx-chart-4", "#0d9488"], // teal
56
+ ["--dx-chart-5", "#ea580c"], // orange
57
+ ["--dx-chart-6", "#0891b2"], // cyan
58
+ ["--dx-chart-7", "#d97706"], // amber
59
+ ["--dx-chart-8", "#db2777"], // pink
57
60
  ];
58
61
 
59
62
  /** Resolve the themed categorical palette from the document root. */