@newtonedev/components 0.1.20 → 0.1.22

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.
@@ -1,30 +1,20 @@
1
1
  import React from 'react';
2
2
  import type { PageProps } from './Page.types';
3
3
  /**
4
- * Page — Full-viewport layout root with optional navbar and sidebar slots.
4
+ * Page — Full-viewport layout root with optional layout slots.
5
5
  *
6
6
  * Page fills the full screen height and arranges layout regions:
7
- * - Navbar at the top (outside scroll)
8
- * - Sidebar to the left of the main content
7
+ * - NavHeader pinned at the top of the navigation column (brand/logo)
8
+ * - NavContent to the left of the main content (scrollable nav)
9
+ * - NavFooter pinned at the bottom of the navigation column (user menu, sign out)
10
+ * - Header sticky at the top of the content area
9
11
  * - Children fill the remaining space (typically a Viewport)
12
+ * - Footer pinned at the bottom of the content area
10
13
  *
11
- * When a `scheme` prop is provided, Page activates that scheme from the
12
- * parent NewtoneProvider so all descendants receive the scheme's config.
13
- *
14
- * Page is invisible — no background, border, or shadow.
15
- *
16
- * @example
17
- * ```tsx
18
- * <Page navbar={<Nav />}>
19
- * <Viewport>
20
- * <Section size="sm" fill>
21
- * <Frame elevation={1} padding={32}>
22
- * <Text>Centered content</Text>
23
- * </Frame>
24
- * </Section>
25
- * </Viewport>
26
- * </Page>
27
- * ```
14
+ * Panels are separated by 1px gaps that reveal the root background
15
+ * (divider color), rather than borders. The navigation column uses a
16
+ * sunken (elevation -1) background; the content area uses grounded
17
+ * (elevation 0).
28
18
  */
29
- export declare function Page({ children, scheme, navbar, sidebar, testID, nativeID, ref, style, }: PageProps): React.JSX.Element;
19
+ export declare function Page({ children, scheme, header, navHeader, navFooter, footer, navContent, testID, nativeID, ref, style, }: PageProps): React.JSX.Element;
30
20
  //# sourceMappingURL=Page.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Page.d.ts","sourceRoot":"","sources":["../../../src/layout/Page/Page.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAkB9C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,IAAI,CAAC,EACnB,QAAQ,EACR,MAAM,EACN,MAAM,EACN,OAAO,EACP,MAAM,EACN,QAAQ,EACR,GAAG,EACH,KAAK,GACN,EAAE,SAAS,qBA+CX"}
1
+ {"version":3,"file":"Page.d.ts","sourceRoot":"","sources":["../../../src/layout/Page/Page.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAyC9C;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,IAAI,CAAC,EACnB,QAAQ,EACR,MAAM,EACN,MAAM,EACN,SAAS,EACT,SAAS,EACT,MAAM,EACN,UAAU,EACV,MAAM,EACN,QAAQ,EACR,GAAG,EACH,KAAK,GACN,EAAE,SAAS,qBAyGX"}
@@ -3,14 +3,13 @@ import type { View, ViewStyle } from 'react-native';
3
3
  * Props for Page — the outermost layout container for a screen.
4
4
  *
5
5
  * Page fills the full viewport height and arranges optional layout slots:
6
- * navbar (top), sidebar (left), and children (main content area).
7
- * It can also activate a scheme from the parent NewtoneProvider.
8
- *
9
- * Page is invisible — no background, border, or shadow.
6
+ * header (top), navigation column (left with navHeader + navContent),
7
+ * and children (main content area). It can also activate a scheme from
8
+ * the parent NewtoneProvider.
10
9
  *
11
10
  * @example
12
11
  * ```tsx
13
- * <Page navbar={<Nav />}>
12
+ * <Page header={<Nav />}>
14
13
  * <Viewport>
15
14
  * <Section size="md">
16
15
  * <Text>Page content</Text>
@@ -21,7 +20,7 @@ import type { View, ViewStyle } from 'react-native';
21
20
  *
22
21
  * @example
23
22
  * ```tsx
24
- * <Page scheme="vibrant" sidebar={<Sidebar />}>
23
+ * <Page scheme="vibrant" navContent={<Sidebar />}>
25
24
  * <Viewport>
26
25
  * <Section size="lg">
27
26
  * <Text>Dashboard content</Text>
@@ -39,15 +38,30 @@ export interface PageProps {
39
38
  */
40
39
  readonly scheme?: string;
41
40
  /**
42
- * Navbar element rendered at the top, outside the scrollable area.
43
- * The navbar's height is determined by its own content.
41
+ * Header element rendered at the top of the content area (sticky).
42
+ * The header's height is determined by its own content.
43
+ */
44
+ readonly header?: React.ReactNode;
45
+ /**
46
+ * Nav header element pinned at the top of the navigation column.
47
+ * Sits above the navContent and does not scroll. Use for brand/logo.
48
+ */
49
+ readonly navHeader?: React.ReactNode;
50
+ /**
51
+ * Footer element rendered at the bottom of the content area.
52
+ * Pinned below the Viewport — does not scroll with content.
53
+ */
54
+ readonly footer?: React.ReactNode;
55
+ /**
56
+ * Navigation content element rendered in the navigation column.
57
+ * Typically a Sidebar component with scrollable nav items.
44
58
  */
45
- readonly navbar?: React.ReactNode;
59
+ readonly navContent?: React.ReactNode;
46
60
  /**
47
- * Sidebar element rendered to the left of the main content area.
48
- * The sidebar manages its own width and scroll behavior.
61
+ * Nav footer element pinned at the bottom of the navigation column.
62
+ * Does not scroll. Use for user menu, settings, sign out, etc.
49
63
  */
50
- readonly sidebar?: React.ReactNode;
64
+ readonly navFooter?: React.ReactNode;
51
65
  /** Test identifier — maps to `data-testid` on web. */
52
66
  readonly testID?: string;
53
67
  /** DOM id — maps to `id` attribute on web. */
@@ -1 +1 @@
1
- {"version":3,"file":"Page.types.d.ts","sourceRoot":"","sources":["../../../src/layout/Page/Page.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,WAAW,SAAS;IACxB,gDAAgD;IAChD,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAEpC;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAElC;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAInC,sDAAsD;IACtD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEzB,8CAA8C;IAC9C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B,0CAA0C;IAC1C,QAAQ,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAE/B,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;CAC1C"}
1
+ {"version":3,"file":"Page.types.d.ts","sourceRoot":"","sources":["../../../src/layout/Page/Page.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,WAAW,SAAS;IACxB,gDAAgD;IAChD,QAAQ,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAEpC;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAElC;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAErC;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAElC;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAEtC;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAIrC,sDAAsD;IACtD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEzB,8CAA8C;IAC9C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B,0CAA0C;IAC1C,QAAQ,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAE/B,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,GAAG,SAAS,EAAE,CAAC;CAC1C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newtonedev/components",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "description": "React + React Native Web component library for Newtone",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -37,7 +37,7 @@
37
37
  "react-native": ">=0.70.0"
38
38
  },
39
39
  "dependencies": {
40
- "newtone-api": "^0.0.5",
40
+ "newtone-api": "^0.0.6",
41
41
  "newtone": "^0.1.0",
42
42
  "@newtonedev/fonts": "^0.1.0",
43
43
  "react-native-web": "^0.19.10"
@@ -55,9 +55,6 @@ export function ContentCard({
55
55
  <Frame
56
56
  elevation={variant === 'elevated' ? 2 : undefined}
57
57
  appearance={variant === 'elevated' ? 'tinted' : undefined}
58
- href={href}
59
- onPress={onPress}
60
- disabled={disabled}
61
58
  padding={padding}
62
59
  gap={gap}
63
60
  style={[
@@ -1,39 +1,21 @@
1
1
  import { StyleSheet } from 'react-native';
2
- import type { ResolvedTokens, AppearanceTokens, ThemeName, AppearanceName } from 'newtone-api';
3
2
 
4
3
  interface SidebarStyleInput {
5
- readonly tokens: ResolvedTokens;
6
4
  readonly width: number;
7
- readonly bordered: boolean;
8
- readonly theme?: ThemeName;
9
- readonly appearance?: AppearanceName;
10
5
  }
11
6
 
12
- export function getSidebarStyles({ tokens, width, bordered, theme = 'primary', appearance = 'main' }: SidebarStyleInput) {
13
- const at: AppearanceTokens = tokens.colors[theme][appearance];
14
- const borderColor = at.fontSecondary;
15
-
7
+ export function getSidebarStyles({ width }: SidebarStyleInput) {
16
8
  return StyleSheet.create({
17
9
  container: {
18
10
  width,
19
11
  flexShrink: 0,
20
12
  flexDirection: 'column',
21
- backgroundColor: at.background,
22
- borderRightWidth: bordered ? 1 : 0,
23
- borderRightColor: borderColor,
24
- },
25
- header: {
26
- flexShrink: 0,
27
- borderBottomWidth: 1,
28
- borderBottomColor: borderColor,
29
13
  },
30
14
  body: {
31
15
  flex: 1,
32
16
  },
33
17
  footer: {
34
18
  flexShrink: 0,
35
- borderTopWidth: 1,
36
- borderTopColor: borderColor,
37
19
  },
38
20
  });
39
21
  }
@@ -1,27 +1,31 @@
1
1
  import React from 'react';
2
- import { View, ScrollView } from 'react-native';
2
+ import { View, ScrollView, StyleSheet } from 'react-native';
3
3
  import type { SidebarProps } from './Sidebar.types';
4
- import { useTokens, useFrameContext } from 'newtone-api';
5
- import { getSidebarStyles } from './Sidebar.styles';
4
+
5
+ const styles = StyleSheet.create({
6
+ container: {
7
+ flex: 1,
8
+ flexDirection: 'column',
9
+ },
10
+ body: {
11
+ flex: 1,
12
+ padding: 8,
13
+ },
14
+ bodyContent: {
15
+ gap: 8,
16
+ },
17
+ footer: {
18
+ flexShrink: 0,
19
+ },
20
+ });
6
21
 
7
22
  export function Sidebar({
8
23
  children,
9
- header,
10
24
  footer,
11
- width = 260,
12
- bordered = true,
13
25
  }: SidebarProps) {
14
- const tokens = useTokens();
15
- const frameCtx = useFrameContext();
16
- const styles = React.useMemo(
17
- () => getSidebarStyles({ tokens, width, bordered, theme: frameCtx?.theme, appearance: frameCtx?.appearance }),
18
- [tokens, width, bordered, frameCtx?.theme, frameCtx?.appearance]
19
- );
20
-
21
26
  return (
22
27
  <View style={styles.container}>
23
- {header && <View style={styles.header}>{header}</View>}
24
- <ScrollView style={styles.body}>{children}</ScrollView>
28
+ <ScrollView style={styles.body} contentContainerStyle={styles.bodyContent}>{children}</ScrollView>
25
29
  {footer && <View style={styles.footer}>{footer}</View>}
26
30
  </View>
27
31
  );
@@ -3,12 +3,6 @@ import type { ReactNode } from 'react';
3
3
  export interface SidebarProps {
4
4
  /** Scrollable body content (navigation items, etc.) */
5
5
  readonly children?: ReactNode;
6
- /** Sticky header slot (logo, brand) */
7
- readonly header?: ReactNode;
8
6
  /** Sticky footer slot (user menu, settings) */
9
7
  readonly footer?: ReactNode;
10
- /** Fixed width in pixels. @default 260 */
11
- readonly width?: number;
12
- /** Show right border. @default true */
13
- readonly bordered?: boolean;
14
8
  }
@@ -1,54 +1,70 @@
1
1
  import React, { useMemo } from 'react';
2
2
  import { Platform, View, StyleSheet } from 'react-native';
3
3
  import type { PageProps } from './Page.types';
4
- import { useNewtoneTheme, _ThemeContext } from 'newtone-api';
4
+ import { useNewtoneTheme, _ThemeContext, computeTokens } from 'newtone-api';
5
5
 
6
- const styles = StyleSheet.create({
6
+ const baseStyles = StyleSheet.create({
7
7
  root: {
8
8
  flex: 1,
9
+ flexDirection: 'row',
10
+ ...(Platform.OS === 'web'
11
+ ? { height: '100vh' as unknown as number, overflow: 'hidden' as any }
12
+ : {}),
13
+ },
14
+ navigation: {
15
+ width: 256,
16
+ flexShrink: 0,
9
17
  flexDirection: 'column',
10
- ...(Platform.OS === 'web' ? { minHeight: '100vh' as unknown as number } : {}),
18
+ overflow: 'hidden' as any,
11
19
  },
12
- body: {
13
- flex: 1,
14
- flexDirection: 'row',
20
+ navHeader: {
21
+ flexShrink: 0,
22
+ height: 64,
15
23
  },
16
24
  content: {
17
25
  flex: 1,
26
+ flexDirection: 'column',
27
+ overflow: 'hidden' as any,
28
+ },
29
+ header: {
30
+ flexShrink: 0,
31
+ height: 64,
32
+ ...(Platform.OS === 'web'
33
+ ? { position: 'sticky' as any, top: 0, zIndex: 10 }
34
+ : {}),
35
+ },
36
+ navFooter: {
37
+ flexShrink: 0,
38
+ },
39
+ footer: {
40
+ flexShrink: 0,
18
41
  },
19
42
  });
20
43
 
21
44
  /**
22
- * Page — Full-viewport layout root with optional navbar and sidebar slots.
45
+ * Page — Full-viewport layout root with optional layout slots.
23
46
  *
24
47
  * Page fills the full screen height and arranges layout regions:
25
- * - Navbar at the top (outside scroll)
26
- * - Sidebar to the left of the main content
48
+ * - NavHeader pinned at the top of the navigation column (brand/logo)
49
+ * - NavContent to the left of the main content (scrollable nav)
50
+ * - NavFooter pinned at the bottom of the navigation column (user menu, sign out)
51
+ * - Header sticky at the top of the content area
27
52
  * - Children fill the remaining space (typically a Viewport)
53
+ * - Footer pinned at the bottom of the content area
28
54
  *
29
- * When a `scheme` prop is provided, Page activates that scheme from the
30
- * parent NewtoneProvider so all descendants receive the scheme's config.
31
- *
32
- * Page is invisible — no background, border, or shadow.
33
- *
34
- * @example
35
- * ```tsx
36
- * <Page navbar={<Nav />}>
37
- * <Viewport>
38
- * <Section size="sm" fill>
39
- * <Frame elevation={1} padding={32}>
40
- * <Text>Centered content</Text>
41
- * </Frame>
42
- * </Section>
43
- * </Viewport>
44
- * </Page>
45
- * ```
55
+ * Panels are separated by 1px gaps that reveal the root background
56
+ * (divider color), rather than borders. The navigation column uses a
57
+ * sunken (elevation -1) background; the content area uses grounded
58
+ * (elevation 0).
46
59
  */
47
60
  export function Page({
48
61
  children,
49
62
  scheme,
50
- navbar,
51
- sidebar,
63
+ header,
64
+ navHeader,
65
+ navFooter,
66
+ footer,
67
+ navContent,
52
68
  testID,
53
69
  nativeID,
54
70
  ref,
@@ -69,6 +85,41 @@ export function Page({
69
85
  };
70
86
  }, [scheme, themeCtx]);
71
87
 
88
+ const resolvedConfig = schemeThemeCtx?.config ?? themeCtx.config;
89
+ const { mode, gamut } = themeCtx;
90
+
91
+ // Compute tokens at sunken (elevation -1) and grounded (elevation 0)
92
+ // to derive panel backgrounds and the divider gap color.
93
+ const { dividerBg, sunkenBg, groundedBg } = useMemo(() => {
94
+ const groundedTokens = computeTokens(
95
+ resolvedConfig.colorSystem,
96
+ mode,
97
+ gamut,
98
+ 'grounded',
99
+ resolvedConfig.spacing,
100
+ resolvedConfig.radius,
101
+ resolvedConfig.typography,
102
+ resolvedConfig.icons,
103
+ resolvedConfig.themeMappings,
104
+ );
105
+ const sunkenTokens = computeTokens(
106
+ resolvedConfig.colorSystem,
107
+ mode,
108
+ gamut,
109
+ 'sunken',
110
+ resolvedConfig.spacing,
111
+ resolvedConfig.radius,
112
+ resolvedConfig.typography,
113
+ resolvedConfig.icons,
114
+ resolvedConfig.themeMappings,
115
+ );
116
+ return {
117
+ dividerBg: groundedTokens.colors.primary.main.divider,
118
+ sunkenBg: sunkenTokens.colors.primary.main.background,
119
+ groundedBg: groundedTokens.colors.primary.main.background,
120
+ };
121
+ }, [resolvedConfig, mode, gamut]);
122
+
72
123
  const userStyles = Array.isArray(style) ? style : style ? [style] : [];
73
124
 
74
125
  const content = (
@@ -77,17 +128,40 @@ export function Page({
77
128
  testID={testID}
78
129
  nativeID={nativeID}
79
130
  accessibilityRole="none"
80
- style={[styles.root, ...userStyles]}
131
+ style={[baseStyles.root, { backgroundColor: dividerBg, gap: 1 } as any, ...userStyles]}
81
132
  >
82
- {navbar}
83
- {sidebar ? (
84
- <View style={styles.body}>
85
- {sidebar}
86
- <View style={styles.content}>{children}</View>
133
+ {(navHeader || navContent || navFooter) ? (
134
+ <View style={[baseStyles.navigation, { backgroundColor: dividerBg, gap: 1 } as any]}>
135
+ {navHeader ? (
136
+ <View style={[baseStyles.navHeader, { backgroundColor: sunkenBg }]}>
137
+ {navHeader}
138
+ </View>
139
+ ) : null}
140
+ <View style={{ flex: 1, backgroundColor: sunkenBg, overflow: 'hidden' as any }}>
141
+ {navContent}
142
+ </View>
143
+ {navFooter ? (
144
+ <View style={[baseStyles.navFooter, { backgroundColor: sunkenBg }]}>
145
+ {navFooter}
146
+ </View>
147
+ ) : null}
148
+ </View>
149
+ ) : null}
150
+ <View style={[baseStyles.content, { backgroundColor: dividerBg, gap: 1 } as any]}>
151
+ {header ? (
152
+ <View style={[baseStyles.header, { backgroundColor: groundedBg }]}>
153
+ {header}
154
+ </View>
155
+ ) : null}
156
+ <View style={{ flex: 1, backgroundColor: groundedBg, overflow: 'hidden' as any }}>
157
+ {children}
87
158
  </View>
88
- ) : (
89
- children
90
- )}
159
+ {footer ? (
160
+ <View style={[baseStyles.footer, { backgroundColor: groundedBg }]}>
161
+ {footer}
162
+ </View>
163
+ ) : null}
164
+ </View>
91
165
  </View>
92
166
  );
93
167
 
@@ -4,14 +4,13 @@ import type { View, ViewStyle } from 'react-native';
4
4
  * Props for Page — the outermost layout container for a screen.
5
5
  *
6
6
  * Page fills the full viewport height and arranges optional layout slots:
7
- * navbar (top), sidebar (left), and children (main content area).
8
- * It can also activate a scheme from the parent NewtoneProvider.
9
- *
10
- * Page is invisible — no background, border, or shadow.
7
+ * header (top), navigation column (left with navHeader + navContent),
8
+ * and children (main content area). It can also activate a scheme from
9
+ * the parent NewtoneProvider.
11
10
  *
12
11
  * @example
13
12
  * ```tsx
14
- * <Page navbar={<Nav />}>
13
+ * <Page header={<Nav />}>
15
14
  * <Viewport>
16
15
  * <Section size="md">
17
16
  * <Text>Page content</Text>
@@ -22,7 +21,7 @@ import type { View, ViewStyle } from 'react-native';
22
21
  *
23
22
  * @example
24
23
  * ```tsx
25
- * <Page scheme="vibrant" sidebar={<Sidebar />}>
24
+ * <Page scheme="vibrant" navContent={<Sidebar />}>
26
25
  * <Viewport>
27
26
  * <Section size="lg">
28
27
  * <Text>Dashboard content</Text>
@@ -42,16 +41,34 @@ export interface PageProps {
42
41
  readonly scheme?: string;
43
42
 
44
43
  /**
45
- * Navbar element rendered at the top, outside the scrollable area.
46
- * The navbar's height is determined by its own content.
44
+ * Header element rendered at the top of the content area (sticky).
45
+ * The header's height is determined by its own content.
46
+ */
47
+ readonly header?: React.ReactNode;
48
+
49
+ /**
50
+ * Nav header element pinned at the top of the navigation column.
51
+ * Sits above the navContent and does not scroll. Use for brand/logo.
52
+ */
53
+ readonly navHeader?: React.ReactNode;
54
+
55
+ /**
56
+ * Footer element rendered at the bottom of the content area.
57
+ * Pinned below the Viewport — does not scroll with content.
58
+ */
59
+ readonly footer?: React.ReactNode;
60
+
61
+ /**
62
+ * Navigation content element rendered in the navigation column.
63
+ * Typically a Sidebar component with scrollable nav items.
47
64
  */
48
- readonly navbar?: React.ReactNode;
65
+ readonly navContent?: React.ReactNode;
49
66
 
50
67
  /**
51
- * Sidebar element rendered to the left of the main content area.
52
- * The sidebar manages its own width and scroll behavior.
68
+ * Nav footer element pinned at the bottom of the navigation column.
69
+ * Does not scroll. Use for user menu, settings, sign out, etc.
53
70
  */
54
- readonly sidebar?: React.ReactNode;
71
+ readonly navFooter?: React.ReactNode;
55
72
 
56
73
  // ── Testing & Platform ──
57
74