@mlola-ui/engine 1.0.7 → 1.0.8
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/generated/agents.md +4 -0
- package/generated/assets.json +1047 -45
- package/generated/contract.json +23 -0
- package/generated/recipes.css +123 -24
- package/package.json +1 -1
- package/src/color.mjs +1 -1
- package/src/palette.mjs +2 -2
- package/src/theme.mjs +1 -1
package/generated/contract.json
CHANGED
|
@@ -194,6 +194,11 @@
|
|
|
194
194
|
"data-danger": [],
|
|
195
195
|
"data-highlighted": []
|
|
196
196
|
},
|
|
197
|
+
"ml-copy-button": {
|
|
198
|
+
"data-status": [
|
|
199
|
+
"error"
|
|
200
|
+
]
|
|
201
|
+
},
|
|
197
202
|
"ml-date-picker-panel": {
|
|
198
203
|
"data-presets": []
|
|
199
204
|
},
|
|
@@ -316,6 +321,18 @@
|
|
|
316
321
|
"active"
|
|
317
322
|
]
|
|
318
323
|
},
|
|
324
|
+
"ml-password-input-strength": {
|
|
325
|
+
"data-strength": [
|
|
326
|
+
"medium",
|
|
327
|
+
"strong",
|
|
328
|
+
"weak"
|
|
329
|
+
]
|
|
330
|
+
},
|
|
331
|
+
"ml-password-input-toggle": {
|
|
332
|
+
"aria-pressed": [
|
|
333
|
+
"true"
|
|
334
|
+
]
|
|
335
|
+
},
|
|
319
336
|
"ml-popover": {
|
|
320
337
|
"data-side": [
|
|
321
338
|
"bottom",
|
|
@@ -438,6 +455,12 @@
|
|
|
438
455
|
"sm"
|
|
439
456
|
]
|
|
440
457
|
},
|
|
458
|
+
"ml-spinner": {
|
|
459
|
+
"data-size": [
|
|
460
|
+
"lg",
|
|
461
|
+
"sm"
|
|
462
|
+
]
|
|
463
|
+
},
|
|
441
464
|
"ml-status-icon": {
|
|
442
465
|
"data-status": [
|
|
443
466
|
"backlog",
|
package/generated/recipes.css
CHANGED
|
@@ -1575,15 +1575,6 @@
|
|
|
1575
1575
|
gap: var(--ml-space-1-5);
|
|
1576
1576
|
}
|
|
1577
1577
|
|
|
1578
|
-
.ml-button-spinner {
|
|
1579
|
-
width: .875rem;
|
|
1580
|
-
height: .875rem;
|
|
1581
|
-
border: 1.5px solid currentColor;
|
|
1582
|
-
border-inline-end-color: transparent;
|
|
1583
|
-
border-radius: 50%;
|
|
1584
|
-
animation: ml-spin .7s linear infinite;
|
|
1585
|
-
}
|
|
1586
|
-
|
|
1587
1578
|
.ml-button[data-state="success"] {
|
|
1588
1579
|
background: var(--ml-success);
|
|
1589
1580
|
border-color: var(--ml-success);
|
|
@@ -2771,6 +2762,13 @@
|
|
|
2771
2762
|
background: var(--ml-border-subtle);
|
|
2772
2763
|
}
|
|
2773
2764
|
|
|
2765
|
+
/* components/copy-button */
|
|
2766
|
+
/* A copy the browser refused reads as a problem, not as a success. On a
|
|
2767
|
+
filled button the label keeps its own ink; the spoken message carries it. */
|
|
2768
|
+
.ml-copy-button[data-status="error"]:not([data-variant="primary"], [data-variant="danger"]) {
|
|
2769
|
+
color: var(--ml-danger-text);
|
|
2770
|
+
}
|
|
2771
|
+
|
|
2774
2772
|
/* components/date-picker */
|
|
2775
2773
|
/* Wide enough for a range in the reader's format. */
|
|
2776
2774
|
.ml-date-picker {
|
|
@@ -3769,6 +3767,82 @@
|
|
|
3769
3767
|
color: var(--ml-text-muted);
|
|
3770
3768
|
}
|
|
3771
3769
|
|
|
3770
|
+
/* components/password-input */
|
|
3771
|
+
.ml-password-input {
|
|
3772
|
+
display: grid;
|
|
3773
|
+
gap: var(--ml-space-2);
|
|
3774
|
+
}
|
|
3775
|
+
|
|
3776
|
+
/* While the password shows, the toggle takes full ink: its state is seen,
|
|
3777
|
+
not only announced. */
|
|
3778
|
+
.ml-password-input-toggle[aria-pressed="true"] {
|
|
3779
|
+
color: var(--ml-text);
|
|
3780
|
+
}
|
|
3781
|
+
|
|
3782
|
+
.ml-password-input-strength {
|
|
3783
|
+
display: grid;
|
|
3784
|
+
gap: var(--ml-space-2);
|
|
3785
|
+
color: var(--ml-text-muted);
|
|
3786
|
+
font-size: var(--ml-type-xs);
|
|
3787
|
+
}
|
|
3788
|
+
|
|
3789
|
+
.ml-password-input-strength p {
|
|
3790
|
+
margin: 0;
|
|
3791
|
+
}
|
|
3792
|
+
|
|
3793
|
+
.ml-password-input-strength strong {
|
|
3794
|
+
color: var(--ml-text);
|
|
3795
|
+
font-weight: 600;
|
|
3796
|
+
}
|
|
3797
|
+
|
|
3798
|
+
/* Three segments light up as the password grows. They are marks on the page,
|
|
3799
|
+
so they take the -text roles, which clear 3:1 against it. */
|
|
3800
|
+
.ml-password-input-meter {
|
|
3801
|
+
display: grid;
|
|
3802
|
+
grid-template-columns: repeat(3, 1fr);
|
|
3803
|
+
gap: var(--ml-space-1);
|
|
3804
|
+
}
|
|
3805
|
+
|
|
3806
|
+
.ml-password-input-meter > span {
|
|
3807
|
+
height: 0.25rem;
|
|
3808
|
+
border-radius: var(--ml-radius-pill);
|
|
3809
|
+
background: var(--ml-track);
|
|
3810
|
+
transition: background-color var(--ml-duration-normal) var(--ml-ease-standard);
|
|
3811
|
+
}
|
|
3812
|
+
|
|
3813
|
+
.ml-password-input-strength[data-strength="weak"] .ml-password-input-meter > span:nth-child(1) {
|
|
3814
|
+
background: var(--ml-danger-text);
|
|
3815
|
+
}
|
|
3816
|
+
|
|
3817
|
+
.ml-password-input-strength[data-strength="medium"] .ml-password-input-meter > span:nth-child(-n + 2) {
|
|
3818
|
+
background: var(--ml-warning-text);
|
|
3819
|
+
}
|
|
3820
|
+
|
|
3821
|
+
.ml-password-input-strength[data-strength="strong"] .ml-password-input-meter > span {
|
|
3822
|
+
background: var(--ml-success-text);
|
|
3823
|
+
}
|
|
3824
|
+
|
|
3825
|
+
.ml-password-input-strength[data-strength="weak"] strong {
|
|
3826
|
+
color: var(--ml-danger-text);
|
|
3827
|
+
}
|
|
3828
|
+
|
|
3829
|
+
.ml-password-input-strength[data-strength="strong"] strong {
|
|
3830
|
+
color: var(--ml-success-text);
|
|
3831
|
+
}
|
|
3832
|
+
|
|
3833
|
+
@media (forced-colors: active) {
|
|
3834
|
+
.ml-password-input-meter > span {
|
|
3835
|
+
border: 1px solid CanvasText;
|
|
3836
|
+
background: Canvas;
|
|
3837
|
+
}
|
|
3838
|
+
|
|
3839
|
+
.ml-password-input-strength:not([data-strength="empty"]) .ml-password-input-meter > span:nth-child(1),
|
|
3840
|
+
.ml-password-input-strength:is([data-strength="medium"], [data-strength="strong"]) .ml-password-input-meter > span:nth-child(2),
|
|
3841
|
+
.ml-password-input-strength[data-strength="strong"] .ml-password-input-meter > span:nth-child(3) {
|
|
3842
|
+
background: CanvasText;
|
|
3843
|
+
}
|
|
3844
|
+
}
|
|
3845
|
+
|
|
3772
3846
|
/* components/popover */
|
|
3773
3847
|
.ml-popover {
|
|
3774
3848
|
position: fixed;
|
|
@@ -4984,6 +5058,43 @@
|
|
|
4984
5058
|
outline-offset: 2px;
|
|
4985
5059
|
}
|
|
4986
5060
|
|
|
5061
|
+
/* components/spinner */
|
|
5062
|
+
/* A ring of the current text color with one bright arc. It is drawn from
|
|
5063
|
+
currentColor alone, so it matches the label beside it in a button, a
|
|
5064
|
+
toast or a line of text, in every theme and mode. */
|
|
5065
|
+
.ml-spinner {
|
|
5066
|
+
display: inline-block;
|
|
5067
|
+
flex: none;
|
|
5068
|
+
width: 1rem;
|
|
5069
|
+
height: 1rem;
|
|
5070
|
+
border: 2px solid color-mix(in oklab, currentColor 22%, transparent);
|
|
5071
|
+
border-top-color: currentColor;
|
|
5072
|
+
border-radius: var(--ml-radius-pill);
|
|
5073
|
+
vertical-align: -0.125em;
|
|
5074
|
+
animation: ml-spin 0.7s linear infinite;
|
|
5075
|
+
}
|
|
5076
|
+
|
|
5077
|
+
.ml-spinner[data-size="sm"] {
|
|
5078
|
+
width: 0.875rem;
|
|
5079
|
+
height: 0.875rem;
|
|
5080
|
+
border-width: 1.5px;
|
|
5081
|
+
}
|
|
5082
|
+
|
|
5083
|
+
.ml-spinner[data-size="lg"] {
|
|
5084
|
+
width: 1.5rem;
|
|
5085
|
+
height: 1.5rem;
|
|
5086
|
+
border-width: 2.5px;
|
|
5087
|
+
}
|
|
5088
|
+
|
|
5089
|
+
/* Under forced colors the ring is the system's text color, and the arc is
|
|
5090
|
+
a real border that stays visible. */
|
|
5091
|
+
@media (forced-colors: active) {
|
|
5092
|
+
.ml-spinner {
|
|
5093
|
+
border-color: CanvasText;
|
|
5094
|
+
border-top-color: Highlight;
|
|
5095
|
+
}
|
|
5096
|
+
}
|
|
5097
|
+
|
|
4987
5098
|
/* components/status-icon */
|
|
4988
5099
|
/* Color carries the stage; the glyph carries it without color too. */
|
|
4989
5100
|
.ml-status-icon {
|
|
@@ -5255,9 +5366,12 @@ button.ml-stepper-target:focus-visible {
|
|
|
5255
5366
|
background: transparent;
|
|
5256
5367
|
}
|
|
5257
5368
|
|
|
5369
|
+
/* A header cell lines up with its column. Browsers center th by default, which
|
|
5370
|
+
a table's own text-align does not reach. */
|
|
5258
5371
|
.ml-table th,
|
|
5259
5372
|
.ml-table-header th,
|
|
5260
5373
|
.ml-table-head {
|
|
5374
|
+
text-align: start;
|
|
5261
5375
|
height: 2.5rem;
|
|
5262
5376
|
padding: var(--ml-space-2) var(--ml-space-4);
|
|
5263
5377
|
border-bottom: var(--ml-border-width) solid var(--ml-border-subtle);
|
|
@@ -6096,21 +6210,6 @@ button.ml-stepper-target:focus-visible {
|
|
|
6096
6210
|
color: var(--ml-info-text);
|
|
6097
6211
|
}
|
|
6098
6212
|
|
|
6099
|
-
.ml-toast-spinner {
|
|
6100
|
-
width: 1rem;
|
|
6101
|
-
height: 1rem;
|
|
6102
|
-
border: 2px solid var(--ml-track);
|
|
6103
|
-
border-top-color: var(--ml-text);
|
|
6104
|
-
border-radius: 50%;
|
|
6105
|
-
animation: ml-toast-spin 0.7s linear infinite;
|
|
6106
|
-
}
|
|
6107
|
-
|
|
6108
|
-
@keyframes ml-toast-spin {
|
|
6109
|
-
to {
|
|
6110
|
-
transform: rotate(1turn);
|
|
6111
|
-
}
|
|
6112
|
-
}
|
|
6113
|
-
|
|
6114
6213
|
.ml-toast-content {
|
|
6115
6214
|
display: grid;
|
|
6116
6215
|
flex: 1;
|
package/package.json
CHANGED
package/src/color.mjs
CHANGED
|
@@ -117,7 +117,7 @@ export function contrast(foreground, background) {
|
|
|
117
117
|
return (Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05);
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
/**
|
|
120
|
+
/** Serialize an OKLCH color, gamut-mapped, at stable precision. */
|
|
121
121
|
export function formatColor(color) {
|
|
122
122
|
const mapped = toGamut(color);
|
|
123
123
|
const chroma = round(mapped.C, 3);
|
package/src/palette.mjs
CHANGED
|
@@ -183,7 +183,7 @@ function statusFor(name, mode, neutrals) {
|
|
|
183
183
|
* Six categorical colors for charts, apart from the status roles so a series
|
|
184
184
|
* never reads as "good" or "bad" by accident. The first follows the brand's
|
|
185
185
|
* hue (blue for a monochrome brand); the rest are the hues furthest from those
|
|
186
|
-
* already taken, so
|
|
186
|
+
* already taken, so neighbors never share a family. Each is solved to 3:1
|
|
187
187
|
* against the surface (WCAG 1.4.11 for graphics).
|
|
188
188
|
*/
|
|
189
189
|
const CHART_HUES = [255, 185, 300, 50, 10, 145];
|
|
@@ -222,7 +222,7 @@ function hoverFor(fill, foreground, neutrals) {
|
|
|
222
222
|
return solveLightness({ L: Math.min(1, Math.max(0, fill.L + away)), C: fill.C, H: fill.H }, foreground, TARGETS.body, away < 0 ? "darker" : "lighter");
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
/** Every palette token for one mode, as
|
|
225
|
+
/** Every palette token for one mode, as serialized OKLCH strings. */
|
|
226
226
|
export function derivePalette(spec, mode = "light") {
|
|
227
227
|
const neutrals = mode === "dark" ? neutralsDark(spec) : neutralsLight(spec);
|
|
228
228
|
const primary = primaryFor(spec, mode, neutrals);
|
package/src/theme.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import { renderSpecCss } from "./theme-css.mjs";
|
|
|
11
11
|
* - `inherit` names the canonical theme whose decisions fill anything the
|
|
12
12
|
* file leaves out.
|
|
13
13
|
* - `scale` overrides spacing or type steps, and `extend` adds raw custom
|
|
14
|
-
* properties, for anything the engine has not
|
|
14
|
+
* properties, for anything the engine has not modeled yet. Both are
|
|
15
15
|
* emitted after the derived tokens, so they win.
|
|
16
16
|
*
|
|
17
17
|
* The spec itself renders through the same function as every canonical theme.
|