@frontfriend/tailwind 4.0.0 → 4.0.1

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.
@@ -4,7 +4,11 @@ const path = require('path');
4
4
 
5
5
  const packageRoot = path.join(__dirname, '..');
6
6
  const repoRoot = path.join(packageRoot, '..', '..');
7
- const sourcePath = path.join(repoRoot, 'apps', 'saas', 'components', 'code-editor', 'setup', 'components-config.json');
7
+ // Vendored snapshot of the Master Design System componentsConfig. Lives inside
8
+ // the package so the bundled fallback can be regenerated without depending on
9
+ // apps/saas (which no longer ships the code-editor setup config). Refresh it
10
+ // from the canonical source with `--from-master-db`.
11
+ const sourcePath = path.join(packageRoot, 'scripts', 'master-components-config.json');
8
12
  const outputPath = path.join(packageRoot, 'lib', 'core', 'default-config-data.js');
9
13
  const SYSTEM_WORKSPACE_SLUG = 'system-templates';
10
14
  const MASTER_DS_NAME = 'Master Design System';
@@ -256,6 +260,78 @@ const MINIMAL_V4_FALLBACKS = {
256
260
  },
257
261
  };
258
262
 
263
+ // Class values re-authored from the shadcn/ui new-york-v4 sources
264
+ // (clone: apps/v4/registry/new-york-v4/ui/{button,badge,input}.tsx). These are
265
+ // deep-merged on top of the neutralized Master DS so the out-of-the-box fallback
266
+ // renders like a stock shadcn install: token-driven radius (rounded-md), the
267
+ // shadcn size scale (default h-9 / sm h-8 / lg h-10 / icon size-9), and shadcn
268
+ // variant treatments. shadcn's cva variant/size buckets are mapped into the
269
+ // FrontFriend component envelope keys (e.g. shadcn `default` -> FF `main`,
270
+ // shadcn `outline` -> FF `tertiary`). FrontFriend-only buckets (ghostmain,
271
+ // linkdestructive, badge success/warning/subtle, ...) are left to the
272
+ // neutralized Master DS values and survive the merge untouched.
273
+ const SHADCN_DEFAULTS = {
274
+ button: {
275
+ root:
276
+ 'font-primary group inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20',
277
+ variant: {
278
+ main: 'bg-primary text-primary-foreground hover:bg-primary/90',
279
+ destructive:
280
+ 'bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20',
281
+ tertiary:
282
+ 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground',
283
+ secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
284
+ ghost: 'hover:bg-accent hover:text-accent-foreground',
285
+ ghostmain: 'text-primary hover:bg-accent hover:text-primary/90',
286
+ link: 'text-primary underline-offset-4 hover:underline',
287
+ linkdestructive: 'text-destructive underline-offset-4 hover:underline',
288
+ },
289
+ size: {
290
+ xs: 'h-6 gap-1 px-2 text-xs',
291
+ sm: 'h-8 gap-1.5 px-3',
292
+ default: 'h-9 px-4 py-2',
293
+ lg: 'h-10 px-6',
294
+ icon: 'size-9',
295
+ },
296
+ icon: {
297
+ size: { sm: '4', default: '4', lg: '4' },
298
+ color: {
299
+ main: 'text-primary-foreground',
300
+ secondary: 'text-secondary-foreground',
301
+ tertiary: 'text-foreground group-hover:text-accent-foreground',
302
+ destructive: 'text-white',
303
+ ghost: 'text-foreground group-hover:text-accent-foreground',
304
+ ghostmain: 'text-primary',
305
+ link: 'text-primary',
306
+ linkdestructive: 'text-destructive',
307
+ },
308
+ },
309
+ iconPosition: {
310
+ default: {
311
+ small: 'w-8 px-0',
312
+ default: 'w-9 px-0',
313
+ large: 'w-10 px-0',
314
+ },
315
+ },
316
+ },
317
+ badge: {
318
+ root:
319
+ 'font-primary inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive [&>svg]:pointer-events-none [&>svg]:size-3',
320
+ variant: {
321
+ main: 'bg-primary border-transparent text-primary-foreground [&>svg]:text-primary-foreground',
322
+ secondary:
323
+ 'bg-secondary border-transparent text-secondary-foreground [&>svg]:text-secondary-foreground',
324
+ destructive:
325
+ 'bg-destructive border-transparent text-white [&>svg]:text-white',
326
+ tertiary: 'bg-transparent border-border text-foreground [&>svg]:text-foreground',
327
+ },
328
+ },
329
+ input: {
330
+ root:
331
+ 'font-primary flex h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base md:text-sm shadow-xs transition-[color,box-shadow] outline-none text-foreground placeholder:text-muted-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive disabled:cursor-not-allowed disabled:opacity-50',
332
+ },
333
+ };
334
+
259
335
  const DIRECT_CLASS_MAP = {
260
336
  'border-transperent': 'border-transparent',
261
337
  'hover:interactive-brand-mid-hover': 'hover:text-primary/90',
@@ -315,6 +391,8 @@ const FORBIDDEN_DEFAULT_CLASS_PATTERNS = [
315
391
  /(?:^|:)fill-disabled$/,
316
392
  /(?:^|:)border-disabled$/,
317
393
  /transperent/,
394
+ // Fixed radius that ignores --radius; must use the rounded-md/lg/xl scale.
395
+ /(?:^|:)rounded(?:-(?:t|b|l|r|tl|tr|bl|br|ss|se|es|ee|s|e))?$/,
318
396
  ];
319
397
 
320
398
  function clone(value) {
@@ -415,6 +493,16 @@ function mapTextClass(base, prefix) {
415
493
  return important + withVariantTone(prefix, mapped);
416
494
  }
417
495
 
496
+ // Tailwind v4 / shadcn theme ties rounded-{sm,md,lg,xl} to the --radius token.
497
+ // Bare `rounded` (and bare directional variants) are fixed 0.25rem and ignore
498
+ // --radius, so map them onto the token-driven `rounded-md` scale.
499
+ function mapRadiusClass(base) {
500
+ if (base === 'rounded') return 'rounded-md';
501
+ const match = base.match(/^rounded-(t|b|l|r|tl|tr|bl|br|ss|se|es|ee|s|e)$/);
502
+ if (match) return `rounded-${match[1]}-md`;
503
+ return null;
504
+ }
505
+
418
506
  function mapBorderClass(base, prefix) {
419
507
  const match = base.match(/^!?border-([a-z]+)(?:-[a-z]+)*(?:-(hover|active|selected|disabled))?$/);
420
508
  if (!match) return null;
@@ -438,6 +526,7 @@ function neutralizeClassToken(token) {
438
526
  || mapBgClass(base, prefix)
439
527
  || mapTextClass(base, prefix)
440
528
  || mapBorderClass(base, prefix)
529
+ || mapRadiusClass(base)
441
530
  || base;
442
531
 
443
532
  return `${prefix}${mappedBase}`;
@@ -506,6 +595,12 @@ function buildDefaultConfigData(rawMasterConfig) {
506
595
  generated[key] = key in generated ? deepMerge(generated[key], config) : clone(config);
507
596
  }
508
597
 
598
+ // Re-author shadcn-covered components from the shadcn sources. Deep-merge so
599
+ // shadcn leaves win (root/variant/size) while FrontFriend-only buckets persist.
600
+ for (const [key, override] of Object.entries(SHADCN_DEFAULTS)) {
601
+ generated[key] = key in generated ? deepMerge(generated[key], override) : clone(override);
602
+ }
603
+
509
604
  const forbidden = findForbiddenDefaultClasses(generated);
510
605
  if (forbidden.length > 0) {
511
606
  throw new Error(`Default config still contains FrontFriend-only classes: ${forbidden.join(', ')}`);
@@ -520,9 +615,13 @@ function generateDefaultConfigData() {
520
615
  }
521
616
 
522
617
  function formatModule(config) {
523
- return `// Generated from apps/saas/components/code-editor/setup/components-config.json as the neutral bundled\n` +
524
- `// Master Design System fallback for Tailwind v4 registry components.\n` +
525
- `// Run \`node packages/frontfriend-tailwind/scripts/update-default-config-data.js\` after Master DS changes.\n` +
618
+ return `// Neutral bundled Master Design System fallback for Tailwind v4 registry components.\n` +
619
+ `// Structure + FrontFriend-only components are derived from\n` +
620
+ `// packages/frontfriend-tailwind/scripts/master-components-config.json (colors neutralized).\n` +
621
+ `// shadcn-covered components (button, badge, input) have their root/variant/size classes\n` +
622
+ `// re-authored from the shadcn new-york-v4 sources so the out-of-the-box fallback renders\n` +
623
+ `// like a stock shadcn install (token-driven --radius, shadcn size scale + treatments).\n` +
624
+ `// Run \`node packages/frontfriend-tailwind/scripts/update-default-config-data.js\` to regenerate.\n` +
526
625
  `// Do not edit this file by hand.\n\n` +
527
626
  `module.exports = ${JSON.stringify(config, null, 2)};\n`;
528
627
  }
@@ -557,6 +656,8 @@ async function readMasterComponentsConfigFromDb() {
557
656
 
558
657
  async function writeDefaultConfigDataFromMasterDb() {
559
658
  const masterConfig = await readMasterComponentsConfigFromDb();
659
+ // Keep the vendored snapshot in sync so the file-based generator stays reproducible.
660
+ fs.writeFileSync(sourcePath, `${JSON.stringify(masterConfig, null, 2)}\n`);
560
661
  const config = buildDefaultConfigData(masterConfig);
561
662
  fs.writeFileSync(outputPath, formatModule(config));
562
663
  return config;
package/src/theme.css CHANGED
@@ -1,13 +1,9 @@
1
1
  /*
2
- * Frontfriend Tailwind v4 Theme Placeholder
2
+ * Frontfriend Tailwind v4 bundled source.
3
3
  *
4
- * `frontfriend init` generates the real CSS-first theme at:
5
- * node_modules/.cache/frontfriend/theme.css
6
- *
7
- * The Frontfriend Next.js and Vite integrations alias this package entrypoint
8
- * to the generated cache file so apps can import a stable path:
9
- * @import "@frontfriend/tailwind/theme.css";
10
- *
11
- * If this placeholder appears in your compiled CSS, run `npx frontfriend init`
12
- * and make sure the Frontfriend Next.js/Vite integration is enabled.
4
+ * `frontfriend init` aliases this package entrypoint to the generated cache
5
+ * theme at node_modules/.cache/frontfriend/theme.css when available. Until
6
+ * then, this file still points Tailwind v4 at the bundled component config so
7
+ * config-driven component utilities are generated out-of-the-box.
13
8
  */
9
+ @source "../dist/lib/core/default-config-data.js";