@donotdev/ui 0.0.6 → 0.0.7

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.
@@ -0,0 +1,64 @@
1
+ import type { ComponentType } from 'react';
2
+ interface FontPreload {
3
+ /** Path to font file (e.g., '/fonts/Inter-latin.woff2') */
4
+ href: string;
5
+ /** Font MIME type @default 'font/woff2' */
6
+ type?: string;
7
+ /** Cross-origin setting @default 'anonymous' */
8
+ crossOrigin?: 'anonymous' | 'use-credentials';
9
+ }
10
+ interface PerformanceHintsProps {
11
+ /**
12
+ * Additional origins to preconnect to
13
+ * These are merged with defaults
14
+ */
15
+ preconnects?: string[];
16
+ /**
17
+ * Additional origins to dns-prefetch
18
+ * These are merged with defaults
19
+ */
20
+ dnsPrefetch?: string[];
21
+ /**
22
+ * Fonts to preload for faster LCP
23
+ * Pass string[] for simple paths, or FontPreload[] for full control
24
+ * @default ['/fonts/Inter-latin.woff2']
25
+ */
26
+ fontPreloads?: (string | FontPreload)[];
27
+ /**
28
+ * Disable default preconnects (use only custom ones)
29
+ * @default false
30
+ */
31
+ disableDefaults?: boolean;
32
+ /**
33
+ * Disable default font preloads
34
+ * @default false
35
+ */
36
+ disableFontPreloads?: boolean;
37
+ }
38
+ /**
39
+ * PerformanceHints component
40
+ *
41
+ * Adds preconnect, dns-prefetch, and font preload hints to improve page load.
42
+ * Automatically includes common third-party origins and critical fonts.
43
+ *
44
+ * @example
45
+ * ```tsx
46
+ * // Use defaults (preloads Inter-latin.woff2)
47
+ * <PerformanceHints />
48
+ *
49
+ * // Custom font preloads
50
+ * <PerformanceHints fontPreloads={['/fonts/Roboto-400-latin.woff2']} />
51
+ *
52
+ * // Full control over font preload
53
+ * <PerformanceHints
54
+ * fontPreloads={[{ href: '/fonts/Custom.woff2', type: 'font/woff2' }]}
55
+ * />
56
+ *
57
+ * // Disable font preloads (e.g., when using next/font)
58
+ * <PerformanceHints disableFontPreloads />
59
+ * ```
60
+ */
61
+ declare const PerformanceHints: ComponentType<PerformanceHintsProps>;
62
+ export default PerformanceHints;
63
+ export type { FontPreload, PerformanceHintsProps };
64
+ //# sourceMappingURL=PerformanceHints.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PerformanceHints.d.ts","sourceRoot":"","sources":["../../../../src/internal/layout/components/PerformanceHints.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAkC3C,UAAU,WAAW;IACnB,2DAA2D;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,WAAW,CAAC,EAAE,WAAW,GAAG,iBAAiB,CAAC;CAC/C;AAED,UAAU,qBAAqB;IAC7B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB;;;;OAIG;IACH,YAAY,CAAC,EAAE,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC;IACxC;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,QAAA,MAAM,gBAAgB,EAAE,aAAa,CAAC,qBAAqB,CA6D1D,CAAC;AAEF,eAAe,gBAAgB,CAAC;AAChC,YAAY,EAAE,WAAW,EAAE,qBAAqB,EAAE,CAAC"}
@@ -0,0 +1,87 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ // packages/ui/src/internal/layout/components/PerformanceHints.tsx
4
+ /**
5
+ * @fileoverview Performance hints for faster page loads
6
+ * @description Adds preconnect, dns-prefetch, and font preload hints
7
+ *
8
+ * - Preconnect establishes early connections (DNS + TCP + TLS) to origins
9
+ * the page will need, saving 100-300ms per origin on slow networks.
10
+ * - Font preload initiates font downloads early, reducing LCP by ~500-1000ms
11
+ *
12
+ * @version 0.0.2
13
+ * @since 0.0.1
14
+ * @author AMBROISE PARK Consulting
15
+ */
16
+ import { Helmet } from 'react-helmet-async';
17
+ /**
18
+ * Common third-party origins that benefit from preconnect
19
+ * Only includes origins that are commonly used and safe to preconnect
20
+ */
21
+ const DEFAULT_PRECONNECTS = [
22
+ // YouTube embeds - thumbnails and iframe
23
+ 'https://img.youtube.com',
24
+ 'https://www.youtube.com',
25
+ // Google Fonts (if used)
26
+ 'https://fonts.googleapis.com',
27
+ 'https://fonts.gstatic.com',
28
+ // Google APIs (Firebase, Analytics, etc.)
29
+ 'https://www.googleapis.com',
30
+ // Stripe (if used)
31
+ 'https://js.stripe.com',
32
+ ];
33
+ /**
34
+ * Origins to dns-prefetch only (less aggressive than preconnect)
35
+ * Use for origins that might be needed but aren't certain
36
+ */
37
+ const DEFAULT_DNS_PREFETCH = [
38
+ 'https://www.google-analytics.com',
39
+ 'https://www.googletagmanager.com',
40
+ ];
41
+ /**
42
+ * Default critical fonts to preload
43
+ * Inter-latin is the primary UI font used by the framework
44
+ */
45
+ const DEFAULT_FONT_PRELOADS = ['/fonts/Inter-latin.woff2'];
46
+ /**
47
+ * PerformanceHints component
48
+ *
49
+ * Adds preconnect, dns-prefetch, and font preload hints to improve page load.
50
+ * Automatically includes common third-party origins and critical fonts.
51
+ *
52
+ * @example
53
+ * ```tsx
54
+ * // Use defaults (preloads Inter-latin.woff2)
55
+ * <PerformanceHints />
56
+ *
57
+ * // Custom font preloads
58
+ * <PerformanceHints fontPreloads={['/fonts/Roboto-400-latin.woff2']} />
59
+ *
60
+ * // Full control over font preload
61
+ * <PerformanceHints
62
+ * fontPreloads={[{ href: '/fonts/Custom.woff2', type: 'font/woff2' }]}
63
+ * />
64
+ *
65
+ * // Disable font preloads (e.g., when using next/font)
66
+ * <PerformanceHints disableFontPreloads />
67
+ * ```
68
+ */
69
+ const PerformanceHints = ({ preconnects = [], dnsPrefetch = [], fontPreloads = [], disableDefaults = false, disableFontPreloads = false, }) => {
70
+ // Merge defaults with custom origins
71
+ const allPreconnects = disableDefaults
72
+ ? preconnects
73
+ : [...new Set([...DEFAULT_PRECONNECTS, ...preconnects])];
74
+ const allDnsPrefetch = disableDefaults
75
+ ? dnsPrefetch
76
+ : [...new Set([...DEFAULT_DNS_PREFETCH, ...dnsPrefetch])];
77
+ // Normalize font preloads to FontPreload objects
78
+ const normalizeFontPreload = (font) => typeof font === 'string' ? { href: font } : font;
79
+ const customFontPaths = fontPreloads.map((f) => typeof f === 'string' ? f : f.href);
80
+ const allFontPreloads = disableFontPreloads
81
+ ? []
82
+ : fontPreloads.length > 0
83
+ ? fontPreloads.map(normalizeFontPreload)
84
+ : DEFAULT_FONT_PRELOADS.filter((f) => !customFontPaths.includes(f)).map(normalizeFontPreload);
85
+ return (_jsxs(Helmet, { children: [allFontPreloads.map((font) => (_jsx("link", { rel: "preload", as: "font", href: font.href, type: font.type || 'font/woff2', crossOrigin: font.crossOrigin || 'anonymous' }, `font-${font.href}`))), allPreconnects.map((origin) => (_jsx("link", { rel: "preconnect", href: origin, crossOrigin: "anonymous" }, `preconnect-${origin}`))), allDnsPrefetch.map((origin) => (_jsx("link", { rel: "dns-prefetch", href: origin }, `dns-${origin}`)))] }));
86
+ };
87
+ export default PerformanceHints;
@@ -20,7 +20,7 @@ import { useAppConfig } from '@donotdev/core';
20
20
  * @since 0.0.1
21
21
  * @author AMBROISE PARK Consulting
22
22
  */
23
- export const FooterBranding = ({ className, size = 'xs', }) => {
23
+ export const FooterBranding = ({ className, size = 'sm', }) => {
24
24
  const appUrl = useAppConfig('url');
25
25
  const isFrameworkSite = appUrl === 'https://donotdev.com';
26
26
  const sizeStyles = {
@@ -1 +1 @@
1
- {"version":3,"file":"NextJsAppProviders.d.ts","sourceRoot":"","sources":["../../src/providers/NextJsAppProviders.tsx"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAiDxD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,WAAW,uBAAwB,SAAQ,iBAAiB;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,2CAyChE"}
1
+ {"version":3,"file":"NextJsAppProviders.d.ts","sourceRoot":"","sources":["../../src/providers/NextJsAppProviders.tsx"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAkDxD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,WAAW,uBAAwB,SAAQ,iBAAiB;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,2CA4ChE"}
@@ -15,6 +15,7 @@ import { QueryProviders, AppConfigProvider } from '@donotdev/core';
15
15
  import { useConsent } from '@donotdev/core';
16
16
  import { NextJsStoresInitializer } from '../internal/initializers/NextJsStoresInitializer';
17
17
  import FaviconHead from '../internal/layout/components/FaviconHead';
18
+ import PerformanceHints from '../internal/layout/components/PerformanceHints';
18
19
  // import NextJsAutoMetaTags from '../internal/layout/components/NextJsAutoMetaTags'; // Deprecated - metadata now server-side
19
20
  import { DnDevLayout } from '../internal/layout/DnDevLayout';
20
21
  import { SentryInitializer } from '../internal/providers/SentryInitializer';
@@ -45,5 +46,5 @@ function ConsentBanner() {
45
46
  }
46
47
  export function NextJsAppProviders(props) {
47
48
  const { config = {}, layout, children, serverCookies, customStores } = props;
48
- return (_jsxs(AppConfigProvider, { config: config, platform: "nextjs", children: [_jsx(SentryInitializer, {}), _jsx(HelmetProvider, { children: _jsxs(NextJsStoresInitializer, { serverCookies: serverCookies, customStores: customStores, children: [_jsx(FaviconHead, {}), _jsx(QueryProviders, { children: _jsxs(UIProviders, { children: [_jsx(DnDevLayout, { layout: layout, children: children }), _jsx(ConsentBanner, {}), _jsx(Suspense, { fallback: null, children: _jsx(PWAUpdateNotification, {}) }), _jsx(Suspense, { fallback: null, children: _jsx(LicenseWatermark, {}) })] }) })] }) })] }));
49
+ return (_jsxs(AppConfigProvider, { config: config, platform: "nextjs", children: [_jsx(SentryInitializer, {}), _jsx(HelmetProvider, { children: _jsxs(NextJsStoresInitializer, { serverCookies: serverCookies, customStores: customStores, children: [_jsx(FaviconHead, {}), _jsx(PerformanceHints, {}), _jsx(QueryProviders, { children: _jsxs(UIProviders, { children: [_jsx(DnDevLayout, { layout: layout, children: children }), _jsx(ConsentBanner, {}), _jsx(Suspense, { fallback: null, children: _jsx(PWAUpdateNotification, {}) }), _jsx(Suspense, { fallback: null, children: _jsx(LicenseWatermark, {}) })] }) })] }) })] }));
49
50
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ViteAppProviders.d.ts","sourceRoot":"","sources":["../../src/providers/ViteAppProviders.tsx"],"names":[],"mappings":"AAcA,OAAO,aAAa,CAAC;AACrB,OAAO,gBAAgB,CAAC;AACxB,OAAO,sBAAsB,CAAC;AAC9B,OAAO,gBAAgB,CAAC;AACxB,OAAO,gBAAgB,CAAC;AAGxB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAsBxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,2CA+BxD"}
1
+ {"version":3,"file":"ViteAppProviders.d.ts","sourceRoot":"","sources":["../../src/providers/ViteAppProviders.tsx"],"names":[],"mappings":"AAcA,OAAO,aAAa,CAAC;AACrB,OAAO,gBAAgB,CAAC;AACxB,OAAO,sBAAsB,CAAC;AAC9B,OAAO,gBAAgB,CAAC;AACxB,OAAO,gBAAgB,CAAC;AAGxB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AA2BxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,2CAkCxD"}
@@ -30,6 +30,9 @@ const HelmetProvider = lazy(() => import('react-helmet-async').then((m) => ({ de
30
30
  const FaviconHead = lazy(() => import('../internal/layout/components/FaviconHead').then((m) => ({
31
31
  default: m.default,
32
32
  })));
33
+ const PerformanceHints = lazy(() => import('../internal/layout/components/PerformanceHints').then((m) => ({
34
+ default: m.default,
35
+ })));
33
36
  /**
34
37
  * ViteAppProviders - Vite platform-specific app providers
35
38
  *
@@ -77,5 +80,5 @@ export function ViteAppProviders(props) {
77
80
  routeGroups,
78
81
  layout,
79
82
  });
80
- return (_jsxs(AppConfigProvider, { config: config, platform: "vite", children: [_jsx(SentryInitializer, {}), _jsx(ViteStoresInitializer, { customStores: customStores, children: _jsx(Suspense, { fallback: null, children: _jsxs(HelmetProvider, { children: [_jsx(FaviconHead, {}), _jsx(NavigationProvider, { router: router })] }) }) })] }));
83
+ return (_jsxs(AppConfigProvider, { config: config, platform: "vite", children: [_jsx(SentryInitializer, {}), _jsx(ViteStoresInitializer, { customStores: customStores, children: _jsx(Suspense, { fallback: null, children: _jsxs(HelmetProvider, { children: [_jsx(FaviconHead, {}), _jsx(PerformanceHints, {}), _jsx(NavigationProvider, { router: router })] }) }) })] }));
81
84
  }
@@ -1,12 +1,11 @@
1
- import { type NavigationItem } from './useNavigation';
2
1
  /**
3
2
  * Hook for navigation with favorites and recents
4
3
  * Command component handles search/filtering
5
4
  */
6
5
  export declare const useGoTo: () => {
7
- navigationItems: NavigationItem[];
8
- favoriteItems: NavigationItem[];
9
- recentItems: NavigationItem[];
6
+ navigationItems: import("./useNavigation").NavigationItem[];
7
+ favoriteItems: import("./useNavigation").NavigationItem[];
8
+ recentItems: import("./useNavigation").NavigationItem[];
10
9
  toggleFavorite: (path: string) => void;
11
10
  isFavorite: (path: string) => boolean;
12
11
  navigateToItem: (path: string) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"useGoTo.d.ts","sourceRoot":"","sources":["../../src/routing/useGoTo.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAO1E;;;GAGG;AACH,eAAO,MAAM,OAAO;;;;2BAmCT,MAAM;uBAUN,MAAM;2BAgBN,MAAM;CAehB,CAAC"}
1
+ {"version":3,"file":"useGoTo.d.ts","sourceRoot":"","sources":["../../src/routing/useGoTo.ts"],"names":[],"mappings":"AAmBA;;;GAGG;AACH,eAAO,MAAM,OAAO;;;;2BA4BT,MAAM;uBAQN,MAAM;2BAcN,MAAM;CAehB,CAAC"}
@@ -3,19 +3,15 @@
3
3
  * @fileoverview useGoTo Hook - Navigation search logic
4
4
  * @description Clean hook for Cmd+K navigation with favorites, recents, and filtering
5
5
  *
6
- * @version 0.0.1
6
+ * @version 0.0.2
7
7
  * @since 0.0.1
8
8
  * @author AMBROISE PARK Consulting
9
9
  */
10
10
  import { useCallback, useMemo } from 'react';
11
- import { useLocalStorage } from '@donotdev/core';
11
+ import { useNavigationStore } from '@donotdev/core';
12
12
  // Platform-specific hooks via conditional exports
13
13
  import { useNavigate } from '@donotdev/ui/routing/hooks';
14
14
  import { useNavigationItems } from './useNavigation';
15
- const FAVORITES_KEY = 'nav_favorites';
16
- const RECENT_KEY = 'nav_recent';
17
- const MAX_RECENT = 8;
18
- const EMPTY_ARRAY = [];
19
15
  /**
20
16
  * Hook for navigation with favorites and recents
21
17
  * Command component handles search/filtering
@@ -23,15 +19,12 @@ const EMPTY_ARRAY = [];
23
19
  export const useGoTo = () => {
24
20
  const navigate = useNavigate();
25
21
  const navigationItems = useNavigationItems() ?? [];
26
- // LocalStorage state
27
- const { value: favorites, setValue: setFavorites } = useLocalStorage(FAVORITES_KEY, {
28
- defaultValue: EMPTY_ARRAY,
29
- syncAcrossTabs: true,
30
- });
31
- const { value: recent, setValue: setRecent } = useLocalStorage(RECENT_KEY, {
32
- defaultValue: EMPTY_ARRAY,
33
- syncAcrossTabs: false,
34
- });
22
+ // Navigation store (favorites and recent persisted per computer)
23
+ const favorites = useNavigationStore((state) => state.favorites);
24
+ const recent = useNavigationStore((state) => state.recent);
25
+ const toggleFavoriteStore = useNavigationStore((state) => state.toggleFavorite);
26
+ const isFavoriteStore = useNavigationStore((state) => state.isFavorite);
27
+ const addRecentStore = useNavigationStore((state) => state.addRecent);
35
28
  // Get favorite items
36
29
  const favoriteItems = useMemo(() => {
37
30
  if (!navigationItems.length)
@@ -45,16 +38,16 @@ export const useGoTo = () => {
45
38
  const items = navigationItems.filter((item) => recent.includes(item.path));
46
39
  return items.slice(0, 5);
47
40
  }, [recent, navigationItems]);
48
- // Toggle favorite
41
+ // Toggle favorite (delegates to navigation store)
49
42
  const toggleFavorite = useCallback((path) => {
50
- setFavorites((prev) => prev.includes(path) ? prev.filter((p) => p !== path) : [...prev, path]);
51
- }, [setFavorites]);
52
- // Check if item is favorite
53
- const isFavorite = useCallback((path) => favorites.includes(path), [favorites]);
54
- // Add to recent
43
+ toggleFavoriteStore(path);
44
+ }, [toggleFavoriteStore]);
45
+ // Check if item is favorite (delegates to navigation store)
46
+ const isFavorite = useCallback((path) => isFavoriteStore(path), [isFavoriteStore]);
47
+ // Add to recent (delegates to navigation store)
55
48
  const addRecent = useCallback((path) => {
56
- setRecent((prev) => [path, ...prev.filter((p) => p !== path)].slice(0, MAX_RECENT));
57
- }, [setRecent]);
49
+ addRecentStore(path);
50
+ }, [addRecentStore]);
58
51
  // Navigate to item
59
52
  const navigateToItem = useCallback((path) => {
60
53
  addRecent(path);
@@ -18,9 +18,9 @@
18
18
  Used by: CSS @media queries, JavaScript hooks, responsive components
19
19
  Note: CSS variables don't work in @media, so we use hardcoded px values
20
20
  =========================== */
21
- --breakpoint-mobile: 768px; /* < 768 = mobile */
22
- --breakpoint-tablet: 1024px; /* 768-1023 = tablet */
23
- --breakpoint-laptop: 1440px; /* 1024-1439 = laptop (key layout split) */
21
+ --breakpoint-mobile: 768px; /* < 768 = mobile */
22
+ --breakpoint-tablet: 1024px; /* 768-1023 = tablet */
23
+ --breakpoint-laptop: 1440px; /* 1024-1439 = laptop (key layout split) */
24
24
  --breakpoint-desktop: 1920px; /* 1440+ = desktop */
25
25
 
26
26
  /* ===========================
@@ -287,23 +287,39 @@
287
287
 
288
288
  [data-density='compact'] {
289
289
  /* Base sizes: Fluid responsiveness for all text, zoom-safe */
290
- --font-size-base: clamp(0.875rem, 0.8125rem + 0.25vw, 0.9375rem); /* 14-15px fluid */
291
- --font-size-lg: clamp(1.05rem, 0.9375rem + 0.5vw, 1.125rem); /* 17-18px fluid */
292
- --font-size-xl: clamp(1.26rem, 1.125rem + 0.75vw, 1.375rem); /* 20-22px fluid */
290
+ --font-size-base: clamp(
291
+ 0.875rem,
292
+ 0.8125rem + 0.25vw,
293
+ 0.9375rem
294
+ ); /* 14-15px fluid */
295
+ --font-size-lg: clamp(
296
+ 1.05rem,
297
+ 0.9375rem + 0.5vw,
298
+ 1.125rem
299
+ ); /* 17-18px fluid */
300
+ --font-size-xl: clamp(
301
+ 1.26rem,
302
+ 1.125rem + 0.75vw,
303
+ 1.375rem
304
+ ); /* 20-22px fluid */
293
305
  /* Display sizes: Musical scale + fluid responsiveness */
294
306
  --font-size-2xl: clamp(
295
307
  1.512rem,
296
308
  1.375rem + 0.75vw,
297
309
  1.625rem
298
310
  ); /* 24-26px fluid */
299
- --font-size-3xl: clamp(
300
- 1.814rem,
301
- 1.625rem + 1vw,
302
- 2rem
303
- ); /* 29-32px fluid */
311
+ --font-size-3xl: clamp(1.814rem, 1.625rem + 1vw, 2rem); /* 29-32px fluid */
304
312
  --gap-sm: clamp(0.375rem, 0.3125rem + 0.25vw, 0.5rem); /* 6-8px fluid */
305
- --gap-md: clamp(0.75rem, 0.625rem + 0.5vw, 0.875rem); /* 12-14px fluid, zoom-safe */
306
- --gap-lg: clamp(1.5rem, 1.25rem + 1vw, 1.75rem); /* 24-28px fluid, zoom-safe */
313
+ --gap-md: clamp(
314
+ 0.75rem,
315
+ 0.625rem + 0.5vw,
316
+ 0.875rem
317
+ ); /* 12-14px fluid, zoom-safe */
318
+ --gap-lg: clamp(
319
+ 1.5rem,
320
+ 1.25rem + 1vw,
321
+ 1.75rem
322
+ ); /* 24-28px fluid, zoom-safe */
307
323
  --line-height: 1.2; /* Minor Third - All text */
308
324
  }
309
325
 
@@ -311,22 +327,34 @@
311
327
 
312
328
  [data-density='standard'] {
313
329
  /* Base sizes: Fluid responsiveness for all text, zoom-safe */
314
- --font-size-base: clamp(1rem, 0.9375rem + 0.25vw, 1.0625rem); /* 16-17px fluid */
315
- --font-size-lg: clamp(1.25rem, 1.125rem + 0.5vw, 1.375rem); /* 20-22px fluid */
316
- --font-size-xl: clamp(1.563rem, 1.375rem + 0.75vw, 1.75rem); /* 25-28px fluid */
330
+ --font-size-base: clamp(
331
+ 1rem,
332
+ 0.9375rem + 0.25vw,
333
+ 1.0625rem
334
+ ); /* 16-17px fluid */
335
+ --font-size-lg: clamp(
336
+ 1.25rem,
337
+ 1.125rem + 0.5vw,
338
+ 1.375rem
339
+ ); /* 20-22px fluid */
340
+ --font-size-xl: clamp(
341
+ 1.563rem,
342
+ 1.375rem + 0.75vw,
343
+ 1.75rem
344
+ ); /* 25-28px fluid */
317
345
  /* Display sizes: Musical scale + fluid responsiveness */
318
- --font-size-2xl: clamp(
319
- 1.953rem,
320
- 1.75rem + 1vw,
321
- 2.25rem
322
- ); /* 31-36px fluid */
346
+ --font-size-2xl: clamp(1.953rem, 1.75rem + 1vw, 2.25rem); /* 31-36px fluid */
323
347
  --font-size-3xl: clamp(
324
348
  2.441rem,
325
349
  2rem + 1.5vw,
326
350
  3rem
327
351
  ); /* 39-48px fluid - hero text */
328
352
  --gap-sm: clamp(0.5rem, 0.4375rem + 0.25vw, 0.625rem); /* 8-10px fluid */
329
- --gap-md: clamp(1rem, 0.875rem + 0.5vw, 1.25rem); /* 16-20px fluid, zoom-safe */
353
+ --gap-md: clamp(
354
+ 1rem,
355
+ 0.875rem + 0.5vw,
356
+ 1.25rem
357
+ ); /* 16-20px fluid, zoom-safe */
330
358
  --gap-lg: clamp(2rem, 1.75rem + 1vw, 2.5rem); /* 32-40px fluid, zoom-safe */
331
359
  --line-height: 1.25; /* Major Third - All text */
332
360
  }
@@ -581,6 +609,7 @@ h6 {
581
609
  line-height: var(--line-height);
582
610
  color: var(--foreground);
583
611
  background: transparent;
612
+ text-wrap: balance; /* Equalize line lengths, prevent orphans */
584
613
  }
585
614
 
586
615
  h1 {
@@ -618,6 +647,7 @@ p {
618
647
  line-height: var(--line-height);
619
648
  color: var(--foreground);
620
649
  background: transparent;
650
+ text-wrap: pretty; /* Optimize last line, avoid orphans */
621
651
  }
622
652
 
623
653
  ul,
@@ -627,6 +657,7 @@ ol {
627
657
  color: var(--foreground);
628
658
  background: transparent;
629
659
  list-style-position: outside;
660
+ text-wrap: pretty;
630
661
  }
631
662
 
632
663
  ul {
@@ -1070,8 +1101,10 @@ em {
1070
1101
  -webkit-backdrop-filter: blur(20px) saturate(180%);
1071
1102
  border: var(--border-hairline) solid
1072
1103
  color-mix(in oklab, var(--card-foreground) 25%, transparent);
1073
- box-shadow: var(--shadow-lg),
1074
- inset 0 1px 0 0 color-mix(in oklab, var(--card-foreground) 10%, transparent);
1104
+ box-shadow:
1105
+ var(--shadow-lg),
1106
+ inset 0 1px 0 0
1107
+ color-mix(in oklab, var(--card-foreground) 10%, transparent);
1075
1108
  }
1076
1109
 
1077
1110
  /* Separator style */
@@ -8312,14 +8345,20 @@ main[role='main'][data-routing-animation='none'] {
8312
8345
  'header header'
8313
8346
  'sidebar main'
8314
8347
  'footer footer';
8315
- grid-template-rows: var(--header-height) 1fr minmax(var(--footer-height), auto);
8348
+ grid-template-rows: var(--header-height) 1fr minmax(
8349
+ var(--footer-height),
8350
+ auto
8351
+ );
8316
8352
  grid-template-columns: var(--sidebar-width) 1fr;
8317
8353
 
8318
8354
  /* Game layout: Grid rows adjust based on breakpoint (footer hidden on tablet/mobile) */
8319
8355
  }
8320
8356
 
8321
8357
  [data-layout='game']:root .dndev-layout {
8322
- grid-template-rows: var(--header-height) 1fr minmax(var(--footer-height), auto);
8358
+ grid-template-rows: var(--header-height) 1fr minmax(
8359
+ var(--footer-height),
8360
+ auto
8361
+ );
8323
8362
  }
8324
8363
 
8325
8364
  /* ===========================
@@ -8575,10 +8614,14 @@ aside[role='navigation'].sidebar[data-collapsed='true'] .dndev-interactive kbd {
8575
8614
 
8576
8615
  /* Force sidebar children to shrink - no blocking resize */
8577
8616
 
8578
- aside.sidebar[role='navigation'] .sidebar-content,aside.sidebar[role='navigation'] .sidebar-top,aside.sidebar[role='navigation'] .sidebar-bottom {
8617
+ aside.sidebar[role='navigation'] .sidebar-top,aside.sidebar[role='navigation'] .sidebar-bottom {
8579
8618
  overflow: hidden;
8580
8619
  }
8581
8620
 
8621
+ aside.sidebar[role='navigation'] .sidebar-content {
8622
+ overflow-x: hidden; /* Prevent horizontal scroll, allow vertical */
8623
+ }
8624
+
8582
8625
  aside.sidebar[role='navigation'] .dndev-interactive {
8583
8626
  overflow: hidden;
8584
8627
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@donotdev/ui",
3
- "version": "0.0.6",
3
+ "version": "0.0.7",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -48,19 +48,18 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "@hookform/resolvers": "^5.2.2",
51
- "marked": "^17.0.1",
52
51
  "react-helmet-async": "^2.0.5",
53
- "react-hook-form": "^7.68.0"
52
+ "react-hook-form": "^7.71.0"
54
53
  },
55
54
  "peerDependencies": {
56
- "@donotdev/adv-comps": "0.0.6",
57
- "@donotdev/auth": "0.0.6",
58
- "@donotdev/billing": "0.0.6",
59
- "@donotdev/components": "0.0.6",
60
- "@donotdev/core": "0.0.6",
61
- "@donotdev/crud": "0.0.6",
62
- "@donotdev/firebase": "0.0.6",
63
- "@donotdev/oauth": "0.0.6",
55
+ "@donotdev/adv-comps": "0.0.7",
56
+ "@donotdev/auth": "0.0.7",
57
+ "@donotdev/billing": "0.0.7",
58
+ "@donotdev/components": "0.0.7",
59
+ "@donotdev/core": "0.0.7",
60
+ "@donotdev/crud": "0.0.7",
61
+ "@donotdev/firebase": "0.0.7",
62
+ "@donotdev/oauth": "0.0.7",
64
63
  "firebase": "^12.5.0",
65
64
  "lucide-react": "^0.562.0",
66
65
  "react": "^19.2.3",