domma-js 0.40.0 → 0.42.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "domma-js",
3
- "version": "0.40.0",
3
+ "version": "0.42.0",
4
4
  "description": "Dynamic Object Manipulation & Modeling API - A complete front-end toolkit.",
5
5
  "main": "public/dist/domma.min.js",
6
6
  "module": "public/dist/domma.esm.js",
@@ -46,11 +46,12 @@
46
46
  "build:css": "node scripts/build-css.js",
47
47
  "validate:classes": "node scripts/validate-classes.js",
48
48
  "validate:theme": "node scripts/validate-theme-contrast.js",
49
+ "validate:contrast": "node scripts/validate-contrast-pairs.js",
49
50
  "validate:conventions": "node scripts/validate-conventions.js",
50
51
  "validate:showcase": "vitest run src/showcase-pages.test.js",
51
52
  "validate:showcase:strict": "SHOWCASE_STRICT=1 vitest run src/showcase-pages.test.js",
52
53
  "validate:showcase:baseline": "SHOWCASE_BASELINE=1 vitest run src/showcase-pages.test.js",
53
- "validate": "npm run validate:classes && npm run validate:theme && npm run validate:conventions",
54
+ "validate": "npm run validate:classes && npm run validate:theme && npm run validate:contrast && npm run validate:conventions",
54
55
  "build:css-bundles": "node scripts/build-css-bundles.js",
55
56
  "build:archives": "node scripts/build-archives.js",
56
57
  "build:kickstart-files": "node scripts/build-kickstart-files.js",
@@ -1,8 +1,8 @@
1
1
  /*!
2
- * Domma Complete CSS Bundle v0.40.0
2
+ * Domma Complete CSS Bundle v0.42.0
3
3
  * Dynamic Object Manipulation & Modeling API
4
4
  * (c) 2026 Darryl Waterhouse & DCBW-IT
5
- * Built: 2026-08-21T16:29:27.417Z
5
+ * Built: 2026-08-22T16:17:44.263Z
6
6
  */
7
7
 
8
8
  /* ============================================
@@ -11,11 +11,11 @@
11
11
  ============================================ */
12
12
 
13
13
  /*!
14
- * Domma Core CSS v0.40.0
14
+ * Domma Core CSS v0.42.0
15
15
  * Dynamic Object Manipulation & Modeling API
16
16
  * (c) 2026 Darryl Waterhouse & DCBW-IT
17
- * Built: 2026-08-21T16:29:27.255Z
18
- * Commit: c788672
17
+ * Built: 2026-08-22T16:17:44.102Z
18
+ * Commit: 0f0475f
19
19
  */
20
20
 
21
21
  /**
@@ -4838,11 +4838,11 @@ body.dm-cloaked.dm-ready {
4838
4838
  ============================================ */
4839
4839
 
4840
4840
  /*!
4841
- * Domma Grid CSS v0.40.0
4841
+ * Domma Grid CSS v0.42.0
4842
4842
  * Dynamic Object Manipulation & Modeling API
4843
4843
  * (c) 2026 Darryl Waterhouse & DCBW-IT
4844
- * Built: 2026-08-21T16:29:27.259Z
4845
- * Commit: c788672
4844
+ * Built: 2026-08-22T16:17:44.108Z
4845
+ * Commit: 0f0475f
4846
4846
  */
4847
4847
 
4848
4848
  /**
@@ -5463,11 +5463,11 @@ body.dm-cloaked.dm-ready {
5463
5463
  ============================================ */
5464
5464
 
5465
5465
  /*!
5466
- * Domma Elements CSS v0.40.0
5466
+ * Domma Elements CSS v0.42.0
5467
5467
  * Dynamic Object Manipulation & Modeling API
5468
5468
  * (c) 2026 Darryl Waterhouse & DCBW-IT
5469
- * Built: 2026-08-21T16:29:27.264Z
5470
- * Commit: c788672
5469
+ * Built: 2026-08-22T16:17:44.114Z
5470
+ * Commit: 0f0475f
5471
5471
  */
5472
5472
 
5473
5473
  /**
@@ -5729,8 +5729,13 @@ body.dm-cloaked.dm-ready {
5729
5729
  border-color: var(--dm-primary);
5730
5730
  }
5731
5731
 
5732
+ /* No --dm-primary-text here. That token is the foreground for text drawn ON a
5733
+ primary fill, which is what the header and footer above do. The body has no
5734
+ fill - it sits on the card's own surface - so borrowing the fill foreground
5735
+ put near-black text (#1a1a1a in the dark themes) on a dark surface, at 1.19
5736
+ contrast. Inheriting --dm-text from the card is the correct behaviour. */
5732
5737
  .card-primary .card-body {
5733
- color: var(--dm-primary-text, var(--dm-white));
5738
+ color: var(--dm-text);
5734
5739
  }
5735
5740
 
5736
5741
  .card-primary .card-footer {
@@ -7645,10 +7650,25 @@ code {
7645
7650
  border-radius: 0;
7646
7651
  }
7647
7652
 
7653
+ /* The active link used to take --dm-primary as its text colour. --dm-primary is
7654
+ a mid-tone in several theme families, so on the navbar surface it measured as
7655
+ low as 1.72 (lemon-light). Primary stays as the accent - the tinted pill and
7656
+ the underline - but the text itself uses the one token guaranteed to read on
7657
+ this surface. */
7658
+ /* The active link used to take --dm-primary as its text colour. --dm-primary is
7659
+ a mid-tone in several theme families, so on the navbar surface it measured as
7660
+ low as 1.72 (lemon-light).
7661
+
7662
+ The affordance is now the primary underline plus the weight, and the text
7663
+ uses the one token guaranteed to read on this surface. Deliberately no pill
7664
+ background: a tint of --dm-primary shifts the bar toward the text colour in
7665
+ the themes where primary is light, which cost ~2 points of contrast on the
7666
+ already-tightest combination (dark theme + .navbar-light). Underline only
7667
+ means the active state can never read worse than the brand beside it. */
7648
7668
  .navbar-link.active {
7649
- color: var(--dm-primary);
7669
+ color: var(--dm-navbar-text);
7650
7670
  font-weight: 500;
7651
- background-color: color-mix(in srgb, var(--dm-primary) 10%, transparent);
7671
+ box-shadow: inset 0 -2px 0 0 var(--dm-primary);
7652
7672
  }
7653
7673
 
7654
7674
  .nav-version {
@@ -7656,78 +7676,77 @@ code {
7656
7676
  color: var(--dm-text-secondary, #eee);
7657
7677
  }
7658
7678
 
7659
- /* Light navbar variant */
7660
-
7661
- .navbar-light {
7662
- background-color: var(--dm-surface);
7663
- border-bottom-color: var(--dm-border);
7664
- }
7665
-
7666
- .navbar-light .navbar-brand,
7667
- .navbar-light .navbar-brand-text {
7668
- color: var(--dm-text);
7669
- }
7679
+ /* ---------------------------------------------------------------------------
7680
+ Navbar variants
7670
7681
 
7671
- .navbar-light .navbar-link {
7672
- color: var(--dm-text-secondary);
7673
- }
7682
+ These predate the theme system and used to be written as absolute colours:
7683
+ .navbar-dark painted the bar with --dm-background and its text with
7684
+ --dm-text-inverse. Those two tokens resolve to the SAME value in every dark
7685
+ theme (both are the theme's darkest slate), so the brand rendered at 1.00
7686
+ contrast - invisible, on every dark theme, in every consumer. .navbar-light
7687
+ failed the mirror way in light themes: a white bar with white text, and an
7688
+ active link at 2.56.
7674
7689
 
7675
- .navbar-light .navbar-link:hover {
7676
- color: #ffffff !important;
7677
- background-color: #1e293b !important;
7678
- border-radius: var(--dm-radius-md);
7679
- }
7690
+ Every theme already ships the pair meant for this job - --dm-navbar-bg (the
7691
+ theme's navbar surface) and --dm-navbar-text (text guaranteed to read on it).
7692
+ So the variants are now tint modifiers on that surface rather than absolute
7693
+ colours: "dark" sits a shade below the theme's navbar, "light" a shade above.
7694
+ The two stay visually distinct, both follow the theme, and neither can
7695
+ collide with its own foreground.
7680
7696
 
7681
- /* Force dark hover in light mode using data-mode attribute */
7682
- [data-mode="light"] .navbar-link:hover {
7683
- color: #ffffff !important;
7684
- background-color: #1e293b !important;
7685
- border-radius: var(--dm-radius-md);
7686
- }
7697
+ Measured worst case across 21 themes x both variants: 5.78 (was 1.00).
7698
+ --------------------------------------------------------------------------- */
7687
7699
 
7688
- .navbar-light .navbar-toggle {
7689
- color: var(--dm-text);
7690
- }
7700
+ /* Light navbar variant */
7691
7701
 
7692
- .navbar-light .navbar-toggle:hover {
7693
- background: var(--dm-background-alt);
7694
- color: var(--dm-text);
7702
+ .navbar-light {
7703
+ background-color: color-mix(in srgb, #fff 18%, var(--dm-navbar-bg));
7704
+ border-bottom-color: var(--dm-border);
7695
7705
  }
7696
7706
 
7697
7707
  /* Dark navbar variant */
7698
7708
 
7699
7709
  .navbar-dark {
7700
- background-color: var(--dm-background);
7710
+ background-color: color-mix(in srgb, #000 18%, var(--dm-navbar-bg));
7701
7711
  border-bottom-color: var(--dm-border-dark);
7702
7712
  }
7703
7713
 
7714
+ .navbar-light .navbar-brand,
7715
+ .navbar-light .navbar-brand-text,
7716
+ .navbar-light .navbar-toggle,
7704
7717
  .navbar-dark .navbar-brand,
7705
- .navbar-dark .navbar-brand-text {
7706
- color: var(--dm-text-inverse, var(--dm-white));
7707
- }
7708
-
7718
+ .navbar-dark .navbar-brand-text,
7709
7719
  .navbar-dark .navbar-toggle {
7710
- color: var(--dm-text-disabled);
7711
- }
7712
-
7713
- .navbar-dark .navbar-toggle:hover {
7714
- background: var(--dm-surface);
7715
- color: var(--dm-text-inverse, var(--dm-white));
7720
+ color: var(--dm-navbar-text);
7716
7721
  }
7717
7722
 
7723
+ /* Slightly muted against the brand, but never below AA. 85% is the floor that
7724
+ clears 4.5 in every theme - 72% dropped grayve/wedding/christmas to 3.8-4.0. */
7725
+ .navbar-light .navbar-link,
7718
7726
  .navbar-dark .navbar-link {
7719
- color: var(--dm-gray-400, #adb5bd);
7727
+ color: color-mix(in srgb, var(--dm-navbar-text) 85%, var(--dm-navbar-bg));
7720
7728
  }
7721
7729
 
7730
+ .navbar-light .navbar-link:hover,
7722
7731
  .navbar-dark .navbar-link:hover {
7723
- background-color: rgba(255, 255, 255, 0.3);
7724
- color: var(--dm-text-inverse, var(--dm-white));
7725
- border-radius: var(--dm-radius-sm);
7732
+ color: var(--dm-navbar-text);
7733
+ background-color: color-mix(in srgb, var(--dm-navbar-text) 12%, transparent);
7734
+ border-radius: var(--dm-radius-md);
7726
7735
  }
7727
7736
 
7728
- .navbar-dark .navbar-link.active {
7729
- background-color: var(--dm-surface);
7730
- color: var(--dm-text-inverse, var(--dm-white));
7737
+ .navbar-light .navbar-toggle:hover,
7738
+ .navbar-dark .navbar-toggle:hover {
7739
+ background: color-mix(in srgb, var(--dm-navbar-text) 12%, transparent);
7740
+ color: var(--dm-navbar-text);
7741
+ }
7742
+
7743
+ /* Legacy forced hover for light mode. Kept as-is for the plain/transparent
7744
+ navbar, but excluded from the two variants above - it carries !important and
7745
+ would otherwise override their theme-derived hover. */
7746
+ [data-mode="light"] .navbar:not(.navbar-light):not(.navbar-dark) .navbar-link:hover {
7747
+ color: #ffffff !important;
7748
+ background-color: #1e293b !important;
7749
+ border-radius: var(--dm-radius-md);
7731
7750
  }
7732
7751
 
7733
7752
  /* Dark navbar collapse border color (mobile) */
@@ -8006,23 +8025,19 @@ code {
8006
8025
  list-style: none;
8007
8026
  }
8008
8027
 
8009
- /* Dark navbar dropdown styles */
8010
- .navbar-dark .navbar-dropdown-menu {
8011
- background: var(--dm-surface);
8012
- border-color: var(--dm-border-dark, var(--dm-border));
8013
- }
8028
+ /* Dark navbar dropdown styles
8014
8029
 
8015
- .navbar-dark .navbar-dropdown-item {
8016
- color: var(--dm-gray-400, #adb5bd);
8017
- }
8018
-
8019
- .navbar-dark .navbar-dropdown-item:hover {
8020
- background: rgba(255, 255, 255, 0.3);
8021
- color: var(--dm-text-inverse, var(--dm-white));
8022
- }
8030
+ The dropdown panel is painted from --dm-surface whatever the bar variant, so
8031
+ the base rules above already render it correctly in both modes. The overrides
8032
+ that used to live here only re-stated that same background while replacing
8033
+ the text with a fixed grey and the hover with --dm-text-inverse over 30%
8034
+ white - which measured 2.56 and 1.00 respectively in every light theme, and
8035
+ 3.25 on hover in the dark ones. The divider was being painted --dm-surface,
8036
+ i.e. the panel's own colour, so it was invisible.
8023
8037
 
8024
- .navbar-dark .navbar-dropdown-divider {
8025
- background: var(--dm-surface);
8038
+ Only the border tint is worth keeping. */
8039
+ .navbar-dark .navbar-dropdown-menu {
8040
+ border-color: var(--dm-border-dark, var(--dm-border));
8026
8041
  }
8027
8042
 
8028
8043
  /* ============================================
@@ -8062,14 +8077,9 @@ code {
8062
8077
  border-radius: var(--dm-radius-md);
8063
8078
  }
8064
8079
 
8065
- .navbar-dark .navbar-submenu-toggle {
8066
- color: var(--dm-gray-400, #adb5bd);
8067
- }
8068
-
8069
- .navbar-dark .navbar-submenu-toggle:hover {
8070
- background: rgba(255, 255, 255, 0.3);
8071
- color: var(--dm-text-inverse, var(--dm-white));
8072
- }
8080
+ /* Same as the dropdown items above - the submenu toggle sits on the panel, not
8081
+ on the bar, so the base --dm-text / --dm-hover-bg pairing is the correct one
8082
+ and the fixed grey plus --dm-text-inverse overrides have been removed. */
8073
8083
 
8074
8084
  /* Submenu caret points to the side to signal a fly-out */
8075
8085
  .navbar-submenu-toggle .navbar-caret {
@@ -10790,7 +10800,9 @@ code {
10790
10800
  z-index: 1000;
10791
10801
  min-width: 10rem;
10792
10802
  padding: 0.5rem 0;
10793
- margin: 0.125rem 0 0;
10803
+ /* No margin: the menu is placed by JS from `offset`, and a margin here would
10804
+ silently add to it, so the box would not sit where it was measured to. */
10805
+ margin: 0;
10794
10806
  background-color: var(--dm-surface);
10795
10807
  border: 1px solid var(--dm-border);
10796
10808
  border-radius: var(--dm-radius-md);
@@ -10808,30 +10820,13 @@ code {
10808
10820
  }
10809
10821
 
10810
10822
  /*
10811
- * Hover bridge: the menu is rendered as a document.body sibling positioned with a
10812
- * small offset/margin gap from its trigger. On a hover-triggered dropdown that gap
10813
- * is a dead-zone - the pointer leaves the trigger before reaching the menu, firing
10814
- * the close. These transparent pseudo-elements extend the menu's hoverable area
10815
- * above and below it, so the trigger→menu path is one continuous hover surface.
10816
- * Only present on hover dropdowns, so click dropdowns are unaffected.
10823
+ * The trigger→menu gap is bridged in JS, not CSS. A transparent pseudo-element
10824
+ * strip used to sit above and below the menu to keep the hover path continuous,
10825
+ * but it only ever covered a fixed 12px directly in line with the menu box: any
10826
+ * `offset` larger than that, or an approach taken at an angle, fell outside it,
10827
+ * and while open it also blocked the page underneath. Dropdown._pointerInSafeZone()
10828
+ * does the same job geometrically - see src/elements.js.
10817
10829
  */
10818
- .domma-dropdown-menu-hover::before,
10819
- .domma-dropdown-menu-hover::after {
10820
- content: '';
10821
- position: absolute;
10822
- left: 0;
10823
- right: 0;
10824
- height: 0.75rem;
10825
- /* transparent - a hover surface only, never painted */
10826
- }
10827
-
10828
- .domma-dropdown-menu-hover::before {
10829
- bottom: 100%; /* bridges the gap when the menu opens below the trigger */
10830
- }
10831
-
10832
- .domma-dropdown-menu-hover::after {
10833
- top: 100%; /* bridges the gap when the menu opens above the trigger */
10834
- }
10835
10830
 
10836
10831
  .domma-dropdown-item {
10837
10832
  display: block;
@@ -13570,11 +13565,11 @@ code {
13570
13565
  ============================================ */
13571
13566
 
13572
13567
  /*!
13573
- * Domma Themes v0.40.0
13568
+ * Domma Themes v0.42.0
13574
13569
  * Dynamic Object Manipulation & Modeling API
13575
13570
  * (c) 2026 Darryl Waterhouse & DCBW-IT
13576
- * Built: 2026-08-21T16:29:27.241Z
13577
- * Commit: c788672
13571
+ * Built: 2026-08-22T16:17:44.088Z
13572
+ * Commit: 0f0475f
13578
13573
  */
13579
13574
 
13580
13575
  /**