synthesisui 0.16.90 → 0.16.91

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.
@@ -121,10 +121,114 @@ const RADIUS = {
121
121
  /** Tailwind's own colour table, for the values a project uses without declaring.
122
122
  * Only the ones that actually turn up: white, black, and the neutral ramp a
123
123
  * real project reached for 106 times. */
124
+ /**
125
+ * THE SIXTY DROPS WERE MINE, NOT THEIRS. test14's not-expressed listed ~60 classes that
126
+ * "start like a design decision and read as nothing" - and enumerating them found that
127
+ * every family is real, valid Tailwind these tables never held: `w-full`, `text-center`,
128
+ * `leading-tight`, `tracking-wide`, `shadow-sm` (with the token DECLARED), `gap-x-6`,
129
+ * `rounded-t-lg`, `border-none` (dono, 01/08). The unreadable report did its job; this is
130
+ * the coverage it bought.
131
+ */
132
+ /** Tailwind's spacing formula: N x 0.25rem, halves included - `size-4.5` is 1.125rem,
133
+ * and a table of steps is always one step short (test14). */
134
+ function numericSpacing(step) {
135
+ if (!/^\d+(\.\d+)?$/.test(step))
136
+ return null;
137
+ const n = Number(step);
138
+ return n === 0 ? "0" : `${n * 0.25}rem`;
139
+ }
140
+ const SIZE_KEYWORD = {
141
+ full: "100%",
142
+ auto: "auto",
143
+ min: "min-content",
144
+ max: "max-content",
145
+ fit: "fit-content",
146
+ px: "1px",
147
+ "0": "0",
148
+ };
149
+ /** Axis-dependent: `w-screen` is 100vw and `h-screen` is 100vh. */
150
+ const SCREEN = {
151
+ w: "100vw",
152
+ h: "100vh",
153
+ "min-w": "100vw",
154
+ "min-h": "100vh",
155
+ "max-w": "100vw",
156
+ "max-h": "100vh",
157
+ };
158
+ const TEXT_ALIGN = new Set([
159
+ "left",
160
+ "center",
161
+ "right",
162
+ "justify",
163
+ "start",
164
+ "end",
165
+ ]);
166
+ const LEADING = {
167
+ none: "1",
168
+ tight: "1.25",
169
+ snug: "1.375",
170
+ normal: "1.5",
171
+ relaxed: "1.625",
172
+ loose: "2",
173
+ };
174
+ const TRACKING = {
175
+ tighter: "-0.05em",
176
+ tight: "-0.025em",
177
+ normal: "0",
178
+ wide: "0.025em",
179
+ wider: "0.05em",
180
+ widest: "0.1em",
181
+ };
182
+ /** Tailwind's own shadows, for when a project uses them undeclared. A declared
183
+ * `--shadow-x` always wins and travels as the ref. */
184
+ const TAILWIND_SHADOW = {
185
+ sm: "0 1px 2px 0 rgb(0 0 0 / 0.05)",
186
+ md: "0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)",
187
+ lg: "0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)",
188
+ xl: "0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)",
189
+ "2xl": "0 25px 50px -12px rgb(0 0 0 / 0.25)",
190
+ inner: "inset 0 2px 4px 0 rgb(0 0 0 / 0.05)",
191
+ none: "none",
192
+ };
193
+ const BORDER_STYLE = new Set([
194
+ "solid",
195
+ "dashed",
196
+ "dotted",
197
+ "double",
198
+ "hidden",
199
+ "none",
200
+ ]);
201
+ /** `rounded-t-lg` → the two top corners: the side names half the corners each. */
202
+ const RADIUS_SIDE = {
203
+ t: ["borderTopLeftRadius", "borderTopRightRadius"],
204
+ b: ["borderBottomLeftRadius", "borderBottomRightRadius"],
205
+ l: ["borderTopLeftRadius", "borderBottomLeftRadius"],
206
+ r: ["borderTopRightRadius", "borderBottomRightRadius"],
207
+ tl: ["borderTopLeftRadius"],
208
+ tr: ["borderTopRightRadius"],
209
+ bl: ["borderBottomLeftRadius"],
210
+ br: ["borderBottomRightRadius"],
211
+ };
124
212
  const TAILWIND_COLOR = {
125
213
  white: "#ffffff",
126
214
  black: "#000000",
127
215
  transparent: "transparent",
216
+ // zinc and blue, because a real Select is styled entirely in them and every
217
+ // reference read as nothing (test14, 01/08). Tailwind's own values, verbatim.
218
+ "zinc-50": "#fafafa",
219
+ "zinc-100": "#f4f4f5",
220
+ "zinc-200": "#e4e4e7",
221
+ "zinc-300": "#d4d4d8",
222
+ "zinc-400": "#a1a1aa",
223
+ "zinc-500": "#71717a",
224
+ "zinc-600": "#52525b",
225
+ "zinc-700": "#3f3f46",
226
+ "zinc-800": "#27272a",
227
+ "zinc-900": "#18181b",
228
+ "zinc-950": "#09090b",
229
+ "blue-400": "#60a5fa",
230
+ "blue-500": "#3b82f6",
231
+ "blue-600": "#2563eb",
128
232
  "neutral-50": "#fafafa",
129
233
  "neutral-100": "#f5f5f5",
130
234
  "neutral-200": "#e5e5e5",
@@ -340,6 +444,18 @@ export function readUtility(utility, declared) {
340
444
  return null;
341
445
  const prefix = core.slice(0, dash);
342
446
  const rest = core.slice(dash + 1);
447
+ /**
448
+ * `border-none` is a style and `border-collapse` a table's own model - neither a
449
+ * width nor a colour. BEFORE the colour branch, because `border` is a colour prefix
450
+ * and that branch returns null for a name it does not know (test14).
451
+ */
452
+ if (prefix === "border") {
453
+ if (BORDER_STYLE.has(rest))
454
+ return { property: "borderStyle", value: rest };
455
+ if (rest === "collapse" || rest === "separate") {
456
+ return { property: "borderCollapse", value: rest };
457
+ }
458
+ }
343
459
  const colorProp = COLOR_PROPERTY[prefix];
344
460
  if (colorProp) {
345
461
  /**
@@ -381,6 +497,10 @@ export function readUtility(utility, declared) {
381
497
  return null;
382
498
  }
383
499
  if (prefix === "text") {
500
+ // `text-center` is an alignment, not a colour and not a size - and it read as
501
+ // nothing on a real table's cells (test14).
502
+ if (TEXT_ALIGN.has(rest))
503
+ return { property: "textAlign", value: rest };
384
504
  /**
385
505
  * A SIZE TRAVELS AS A LITERAL, and that is not a shortcut.
386
506
  *
@@ -406,6 +526,10 @@ export function readUtility(utility, declared) {
406
526
  if (prefix === "font") {
407
527
  // A family (`font-sans`) and a weight (`font-bold`) share this prefix, and
408
528
  // the weight table is what tells them apart.
529
+ // `font-regular` is this library's own spelling of 400 - non-standard Tailwind,
530
+ // real in their files, and dropped whole (test14).
531
+ if (rest === "regular")
532
+ return { property: "fontWeight", value: "400" };
409
533
  if (FONT_WEIGHT[rest]) {
410
534
  /**
411
535
  * ALSO A LITERAL, for a reason worth writing down: a weight ref is a valid
@@ -439,11 +563,86 @@ export function readUtility(utility, declared) {
439
563
  if (prefix === "opacity" && /^\d{1,3}$/.test(rest)) {
440
564
  return { property: "opacity", value: String(Number(rest) / 100) };
441
565
  }
442
- const spaceProp = SPACING_PROPERTY[prefix];
443
- if (spaceProp && SPACING[rest]) {
444
- return { property: spaceProp, value: SPACING[rest] };
566
+ /**
567
+ * `shadow-sm` with `--shadow-sm` DECLARED read as nothing - there was no shadow branch
568
+ * at all, and elevations on parts vanished across a whole real library (test14).
569
+ */
570
+ if (prefix === "shadow") {
571
+ const own = `--shadow-${rest}`;
572
+ if (declared.has(own)) {
573
+ return { property: "boxShadow", value: `{shadow.${rest}}`, token: own };
574
+ }
575
+ if (TAILWIND_SHADOW[rest]) {
576
+ return { property: "boxShadow", value: TAILWIND_SHADOW[rest] };
577
+ }
578
+ }
579
+ // `leading-tight`, `tracking-wide` - the typography refinements, fixed scales.
580
+ if (prefix === "leading") {
581
+ if (LEADING[rest])
582
+ return { property: "lineHeight", value: LEADING[rest] };
583
+ if (/^\d+(\.\d+)?$/.test(rest)) {
584
+ return { property: "lineHeight", value: `${Number(rest) * 0.25}rem` };
585
+ }
586
+ }
587
+ if (prefix === "tracking" && TRACKING[rest]) {
588
+ return { property: "letterSpacing", value: TRACKING[rest] };
589
+ }
590
+ /**
591
+ * `min-w-0` splits at the FIRST dash into prefix `min`, which no table holds - the
592
+ * `min-w`/`max-h` keys were dead on arrival. Recombine the axis before looking up.
593
+ */
594
+ let sizePrefix = prefix;
595
+ let sizeRest = rest;
596
+ if (prefix === "min" || prefix === "max") {
597
+ const axis = /^(w|h)-(.+)$/.exec(rest);
598
+ if (axis) {
599
+ sizePrefix = `${prefix}-${axis[1]}`;
600
+ sizeRest = axis[2];
601
+ }
602
+ }
603
+ const spaceProp = SPACING_PROPERTY[sizePrefix];
604
+ if (spaceProp) {
605
+ /** `gap-x-6` / `gap-y-2` - the two halves of gap, which one prefix cannot say. */
606
+ if (sizePrefix === "gap" && /^[xy]-/.test(sizeRest)) {
607
+ const axisProp = sizeRest.startsWith("x-") ? "columnGap" : "rowGap";
608
+ const step = sizeRest.slice(2);
609
+ const size = SPACING[step] ?? numericSpacing(step);
610
+ if (size)
611
+ return { property: axisProp, value: size };
612
+ }
613
+ const size = SPACING[sizeRest] ?? numericSpacing(sizeRest);
614
+ if (size)
615
+ return { property: spaceProp, value: size };
616
+ /** `w-full`, `h-screen`, `min-w-0` - the keyword sizes, axis-aware. */
617
+ if (sizeRest === "screen" && SCREEN[sizePrefix]) {
618
+ return { property: spaceProp, value: SCREEN[sizePrefix] };
619
+ }
620
+ if (SIZE_KEYWORD[sizeRest] &&
621
+ /^(w|h|min-w|min-h|max-w|max-h|size)$/.test(sizePrefix)) {
622
+ return { property: spaceProp, value: SIZE_KEYWORD[sizeRest] };
623
+ }
445
624
  }
446
625
  if (prefix === "rounded") {
626
+ /**
627
+ * `rounded-t-lg` - one side, two corners. The side names which half; the scale is
628
+ * the same one the whole-box read already resolves, their token first.
629
+ */
630
+ const side = /^(tl|tr|bl|br|t|b|l|r)-(.+)$/.exec(rest);
631
+ if (side && RADIUS_SIDE[side[1]]) {
632
+ const own = `--radius-${side[2]}`;
633
+ const value = declared.has(own)
634
+ ? `{radius.${side[2]}}`
635
+ : (RADIUS[side[2]] ?? null);
636
+ if (value) {
637
+ // One utility, two corners: returned as the first and the caller's write
638
+ // handles pairs the same way `size-N` is handled - see the transcribe loop.
639
+ return {
640
+ property: RADIUS_SIDE[side[1]].join("+"),
641
+ value,
642
+ ...(declared.has(own) ? { token: own } : {}),
643
+ };
644
+ }
645
+ }
447
646
  const own = `--radius-${rest}`;
448
647
  if (declared.has(own)) {
449
648
  return {
@@ -828,6 +1027,19 @@ export function transcribe(classes, declared) {
828
1027
  out.fromLiteral += 1;
829
1028
  continue;
830
1029
  }
1030
+ // `rounded-t-lg` names two corners: the reader returns them joined with `+`, and
1031
+ // the write splits - the same shape the `size` expansion above uses.
1032
+ if (decl.property.includes("+")) {
1033
+ for (const property of decl.property.split("+")) {
1034
+ if (target[property] == null)
1035
+ target[property] = decl.value;
1036
+ }
1037
+ if (decl.token)
1038
+ out.fromToken += 1;
1039
+ else
1040
+ out.fromLiteral += 1;
1041
+ continue;
1042
+ }
831
1043
  if (target[decl.property] != null)
832
1044
  continue;
833
1045
  target[decl.property] = decl.value;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.90",
3
+ "version": "0.16.91",
4
4
  "description": "Bring SynthesisUI design systems into any project - tokens, typed components, whole pages and an agent-ready CLAUDE.md manifest.",
5
5
  "type": "module",
6
6
  "bin": {