@zvoove/unity-ui 2.23.1 → 2.25.0

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/dist/llms.txt CHANGED
@@ -3,6 +3,8 @@
3
3
  > This file describes all available components in the Unity UI design system.
4
4
  > When building UIs, use ONLY these components. Do NOT create custom components for functionality already provided here.
5
5
 
6
+ **Styling exports from this library:** Do **not** pass `className` or `style` to Unity UI components (for example `Stack`, `Grid`, `Button`, `Card`, `Typography`). They are already styled via their public props and design tokens. Use token-based Tailwind classes only on **native HTML elements** (for example a wrapping `div` or `section`) when you need extra positioning or chrome that has no matching component prop.
7
+
6
8
  ## Setup
7
9
 
8
10
  Install:
@@ -53,18 +55,23 @@ Values cascade upward — setting `mobile` applies to all larger breakpoints unt
53
55
 
54
56
  ### Stack
55
57
  **Primary layout component.** Use Stack for ALL layout needs — arranging children in rows or columns with consistent spacing. Do NOT use Card or raw divs for layout.
58
+
59
+ **Default `align` is `baseline`** — always set it explicitly when you need centering, stretching, or any other alignment. Never assume items are centered without `align="center"`.
60
+
61
+ **Dividers need `width="100%"` on the Stack** — without it the divider may not reach the edges.
62
+
56
63
  ```tsx
57
64
  import { Stack } from '@zvoove/unity-ui';
58
65
 
59
66
  <Stack
60
67
  direction="column" // ResponsiveType<'row' | 'row-reverse' | 'column' | 'column-reverse'>
61
68
  gap="md" // ResponsiveType<SpacingKeys> - none|xs2|xs|sm|md|lg|xl|xl2|xl3|xl4|xl5|xl6|xl7
62
- align="center" // ResponsiveType<AlignItems> - default: 'baseline'
69
+ align="center" // ResponsiveType<AlignItems> - default: 'baseline' — set explicitly every time
63
70
  justify="space-between" // ResponsiveType<JustifyContent>
64
71
  wrap="wrap" // ResponsiveType<FlexWrap>
65
72
  padding="md" // ResponsiveType<Padding>
66
73
  margin="none" // ResponsiveType<Margin>
67
- width="100%" // ResponsiveType<number | string>
74
+ width="100%" // ResponsiveType<number | string> — required when Stack contains a Divider
68
75
  height="auto" // ResponsiveType<number | string>
69
76
  minWidth, maxWidth, minHeight, maxHeight // ResponsiveType<number | string>
70
77
  >
@@ -74,18 +81,25 @@ import { Stack } from '@zvoove/unity-ui';
74
81
 
75
82
  Common patterns:
76
83
  ```tsx
84
+ // Row with icon + label — must set align="center" or they baseline-align
85
+ <Stack direction="row" gap="sm" align="center">
86
+ <Icon name="info" />
87
+ <Typography variant="body" size="medium">Label</Typography>
88
+ </Stack>
89
+
90
+ // Divider spanning full width — Stack must have width="100%"
91
+ <Stack direction="column" gap="md" width="100%">
92
+ <Typography variant="title" size="large">Title</Typography>
93
+ <Divider />
94
+ <Typography variant="body" size="medium">Content</Typography>
95
+ </Stack>
96
+
77
97
  // Page layout — stacks vertically on mobile, horizontally on tablet+
78
98
  <Stack direction={{ mobile: 'column', tablet: 'row' }} gap="lg">
79
99
  <Sidebar />
80
100
  <MainContent />
81
101
  </Stack>
82
102
 
83
- // Form fields in a row
84
- <Stack direction="row" gap="md" align="flex-end">
85
- <TextField label="First name" name="first" />
86
- <TextField label="Last name" name="last" />
87
- </Stack>
88
-
89
103
  // Action bar — right-aligned buttons
90
104
  <Stack direction="row" gap="sm" justify="flex-end">
91
105
  <Button variant="outlined">Cancel</Button>
@@ -790,9 +804,9 @@ import { Breadcrumbs } from '@zvoove/unity-ui';
790
804
  ```
791
805
 
792
806
  ### SideNavigation
793
- Collapsible sidebar navigation with user area and mid-sections.
807
+ Collapsible sidebar navigation with user area, optional **root** mid-sections, and optional **sub-menu panels** (`parentId` + arbitrary `content`).
794
808
  ```tsx
795
- import { SideNavigation, type MidSection } from '@zvoove/unity-ui';
809
+ import { MidSectionMenus, SideNavigation, type MidSection, type SubMenuLayer } from '@zvoove/unity-ui';
796
810
 
797
811
  <SideNavigation
798
812
  menuItems={[ // ListMenuItem[] (required)
@@ -802,16 +816,14 @@ import { SideNavigation, type MidSection } from '@zvoove/unity-ui';
802
816
  utilityItems={[ // ListMenuItem[]
803
817
  { id: 'settings', label: 'Einstellungen', icon: 'settings', href: '/settings' },
804
818
  ]}
805
- midSections={[ // MidSection[] — sections between menu and utility items
806
- {
807
- title: 'ANGEPINNT',
808
- replaceWithIconOnClose: 'pin', // collapses to icon when closed, hover shows PopUpMenu
809
- items: [{ id: 'p1', label: 'Pinned item', href: '#' }],
810
- },
819
+ midSections={[ // MidSection[] — root column only; sub-panel lists go in `subMenus[].content`
820
+ { title: 'ANGEPINNT', replaceWithIconOnClose: 'pin', items: [{ id: 'p1', label: 'Pinned item', href: '#' }] },
821
+ ]}
822
+ subMenus={[ // SubMenuLayer[] matching menu rows get chevron-right endContent; shell renders back row (Tag = parent label) + spacer; body is your ReactNode (e.g. ListMenu from an MFE)
811
823
  {
812
- title: 'HEUTE',
813
- hideOnMenuClosed: true, // fully hidden when menu closed
814
- items: [{ id: 't1', label: 'Today item', href: '#' }],
824
+ parentId: 'users', // must match an item id in menuItems, utilityItems, or midSections
825
+ backLabel: 'Hauptmenü', // optional (default: 'Hauptmenü')
826
+ content: <MidSectionMenus sections={...} open={open} variant={variant} />,
815
827
  },
816
828
  ]}
817
829
  userArea={{ // UserAreaProps — user info at the bottom
@@ -826,22 +838,42 @@ import { SideNavigation, type MidSection } from '@zvoove/unity-ui';
826
838
  name: <MyWordmark />, // shown when expanded (fades in/out)
827
839
  }}
828
840
  hideUserAreaInMobile={true} // boolean (default: true) — hides user area on mobile
829
- activeItem="home" // string
841
+ activeItem="home" // string — root id, or `parentId` / `parentId.*` to open a sub panel (segment after dot is app-defined)
830
842
  variant="default" // 'default' | 'compact'
831
843
  open={true} // boolean
832
844
  onToggleOpen={handleToggle} // (open: boolean) => void
833
- onItemClick={handleClick} // (item: ListMenuItem) => void
845
+ onActiveItemChange={setId} // (activeItem: string) => void — root/utility/parentId when opening a layer; not for `__back__` or clicks inside `content`
846
+ onItemClick={handleClick} // (item: ListMenuItem) => void — root, utility, sub parent, back row (`__back__`); not clicks inside `content`
834
847
  linkComponent={Link} // React.ElementType
835
848
  />
836
849
  ```
837
850
 
851
+ ### MidSectionMenus
852
+ Renders the same **mid-section** blocks as `SideNavigation`’s `midSections` (titles, dividers, `ListMenu`, `replaceWithIconOnClose` hover menus when collapsed on desktop). **Inner lists are always `ListMenu` `variant="compact"`** (fixed; not configurable). Prop **`variant`** only matches the parent sidebar **rail width** (`default` vs `compact` open/closed widths), not row density. Uses **`useBreakpoint()`** internally (`isSmallerThan('tablet')` for mobile). Use in `subMenus[].content` with the same **`open`** / **`variant`** as the parent shell.
853
+ ```tsx
854
+ import { MidSectionMenus, type MidSection } from '@zvoove/unity-ui';
855
+
856
+ <MidSectionMenus
857
+ sections={[ // MidSection[]
858
+ { title: 'ANGEPINNT', replaceWithIconOnClose: 'pin', items: [/* ListMenuItem[] */] },
859
+ ]}
860
+ open={true} // boolean — sidebar expanded
861
+ variant="default" // 'default' | 'compact' — shell width only; lists stay compact
862
+ activeItem="x" // string | undefined
863
+ onItemClick={handleClick} // (item: ListMenuItem) => void
864
+ linkComponent={Link} // optional, default 'a'
865
+ />
866
+ ```
867
+
838
868
  ### ListMenu
839
869
  Standalone vertical menu with icon + label items. Used internally by SideNavigation, available standalone.
870
+
871
+ **`items`:** either a flat `ListMenuItem[]` **or** sectioned `ListMenuSection[]` (`{ title?, items: ListMenuItem[] }[]`). Mutually exclusive — do not mix row objects and section objects in one array. Empty `[]` is treated as flat. Optional `title` per section is **uppercase** (`label` / `small` / `on-surface-variant`); omit it or use whitespace-only to hide the header (e.g. first section untitled). Titles collapse when `open` is `false`. Use `isListMenuSections(items)` for the same runtime check the component uses.
840
872
  ```tsx
841
- import { ListMenu, type ListMenuItem } from '@zvoove/unity-ui';
873
+ import { ListMenu, type ListMenuItem, type ListMenuSection, isListMenuSections } from '@zvoove/unity-ui';
842
874
 
843
875
  <ListMenu
844
- items={[ // ListMenuItem[] (required) — icon is optional
876
+ items={[ // ListMenuItem[] | ListMenuSection[] (required)
845
877
  { id: 'home', label: 'Startseite', icon: 'home', href: '/' },
846
878
  { id: 'chat', label: 'Chat title', href: '#',
847
879
  endContent: <Icon name="pin" size="xs" />, // extra content on the right
@@ -849,6 +881,11 @@ import { ListMenu, type ListMenuItem } from '@zvoove/unity-ui';
849
881
  },
850
882
  { id: 'spacer', label: '', isSpacer: true }, // renders a 16px vertical spacer
851
883
  ]}
884
+ // Sectioned example (separate usage — not mixed with flat rows):
885
+ // items={[
886
+ // { title: 'Gruppe', items: [{ id: 'a', label: 'A', href: '#' }] },
887
+ // { items: [{ id: 'b', label: 'B', href: '#' }] },
888
+ // ]}
852
889
  activeItem="home" // string
853
890
  open={true} // boolean — shows/hides labels with animation
854
891
  variant="default" // 'default' | 'compact'
@@ -1537,14 +1574,17 @@ function ContactForm() {
1537
1574
  ## RULES FOR AI AGENTS
1538
1575
 
1539
1576
  1. **Always import from `@zvoove/unity-ui`** - never recreate components that exist in this library
1540
- 2. **Use Stack and Grid for layout** - do not use Card, div, or CSS for layout. Stack is for rows/columns, Grid is for multi-column layouts
1541
- 3. **Card is a visual container, NOT a layout tool** - Card provides background, elevation, and border. Wrap layout content in Stack/Grid INSIDE a Card. Never use Card to arrange items
1542
- 4. **Use Typography for all text** - never use raw `<p>`, `<h1>`, `<span>` etc.
1543
- 5. **Use the spacing scale** for gap/padding/margin - never use arbitrary pixel values
1544
- 6. **Use Button variants** appropriately - primary actions: `filled`, secondary: `outlined`, destructive: `negative`, success: `positive`
1545
- 7. **Use TextField/Select/Checkbox/Radio for forms** - never create custom form inputs
1546
- 8. **Use Dialog for modals, Sheet for panels** - never create custom overlays
1547
- 9. **Use Snackbar for notifications** - never create custom toast systems
1548
- 10. **Use responsive props** when the UI should adapt to screen sizes. Values cascade up from smallest breakpoint. Common pattern: `{{ mobile: 'column', tablet: 'row' }}`
1549
- 11. **Card, Dialog, Sheet padding can be "none"** - use `padding="none"` for edge-to-edge content like images, tables, or dividers inside these components
1550
- 12. **Icons use semantic names** as strings (e.g., `"search"`, `"delete"`, `"edit"`) - never import Phosphor icon components directly
1577
+ 2. **NEVER pass `className` or `style` to ANY Unity UI component** components are not customizable via className/style; use their props API exclusively. For layout or chrome not covered by a prop, wrap in a native HTML element (`div`, `section`, etc.) and apply design-token Tailwind utilities there
1578
+ 3. **Use Stack and Grid for layout** - do not use Card, div, or CSS for layout. Stack is for rows/columns, Grid is for multi-column layouts
1579
+ 4. **Card is a visual container, NOT a layout tool** - Card provides background, elevation, and border. Wrap layout content in Stack/Grid INSIDE a Card. Never use Card to arrange items
1580
+ 5. **Use Typography for all text** - never use raw `<p>`, `<h1>`, `<span>` etc.
1581
+ 6. **Use the spacing scale** for gap/padding/margin - never use arbitrary pixel values
1582
+ 7. **Use Button variants** appropriately - primary actions: `filled`, secondary: `outlined`, destructive: `negative`, success: `positive`
1583
+ 8. **Use TextField/Select/Checkbox/Radio for forms** - never create custom form inputs
1584
+ 9. **Use Dialog for modals, Sheet for panels** - never create custom overlays
1585
+ 10. **Use Snackbar for notifications** - never create custom toast systems
1586
+ 11. **Use responsive props** when the UI should adapt to screen sizes. Values cascade up from smallest breakpoint. Common pattern: `{{ mobile: 'column', tablet: 'row' }}`
1587
+ 12. **Card, Dialog, Sheet padding can be "none"** - use `padding="none"` for edge-to-edge content like images, tables, or dividers inside these components
1588
+ 13. **Icons use semantic names** as strings (e.g., `"search"`, `"delete"`, `"edit"`) - never import Phosphor icon components directly
1589
+ 14. **Stack `align` defaults to `baseline`** — always set `align` explicitly when you need centering (`align="center"`), stretching, or any other alignment. Never assume items are centered without it
1590
+ 15. **Stack containing a Divider needs `width="100%"`** — without it the divider will not span the full width of the container
package/dist/theme.css CHANGED
@@ -139,6 +139,7 @@
139
139
  --color-primary-70: #9aa4ff;
140
140
  --color-primary-80: #bdc2ff;
141
141
  --color-primary-90: #dfe0ff;
142
+ --color-primary-92: #eeeff7;
142
143
  --color-primary-95: #eff0ff;
143
144
  --color-primary-99: #fbfbff;
144
145
  --color-primary-100: #ffffff;
@@ -285,6 +286,9 @@
285
286
  --color-on-primary-container: var(--color-primary-10);
286
287
  --color-secondary-container: var(--color-secondary-90);
287
288
  --color-on-secondary-container: var(--color-secondary-10);
289
+ --color-container-neutral: var(--color-neutral-variant-95);
290
+ --color-container-neutral-hover: var(--color-neutral-variant-92);
291
+ --color-on-container-neutral: var(--color-neutral-variant-10);
288
292
  --color-tertiary-container: var(--color-tertiary-90);
289
293
  --color-tertiary-container-hover: var(--color-tertiary-80);
290
294
  --color-tertiary-container-active: var(--color-tertiary-70);
@@ -508,6 +512,9 @@
508
512
  --color-on-primary-container: var(--color-primary-90);
509
513
  --color-secondary-container: var(--color-secondary-30);
510
514
  --color-on-secondary-container: var(--color-secondary-90);
515
+ --color-container-neutral: var(--color-neutral-variant-10);
516
+ --color-container-neutral-hover: var(--color-neutral-variant-15);
517
+ --color-on-container-neutral: var(--color-neutral-variant-95);
511
518
  --color-tertiary-container: var(--color-tertiary-30);
512
519
  --color-tertiary-container-hover: var(--color-tertiary-40);
513
520
  --color-tertiary-container-active: var(--color-tertiary-50);
@@ -612,6 +619,53 @@
612
619
  display: none; /* Chrome, Safari */
613
620
  }
614
621
  }
622
+
623
+ .scrollbar-overlay {
624
+ /* Firefox — thin bar, transparent until hover/scroll */
625
+ scrollbar-width: thin;
626
+ scrollbar-color: transparent transparent;
627
+
628
+ /* Chrome / Edge / Safari */
629
+ &::-webkit-scrollbar {
630
+ width: 6px;
631
+ height: 6px;
632
+ background: transparent;
633
+ }
634
+
635
+ &::-webkit-scrollbar-thumb {
636
+ background: transparent;
637
+ border-radius: 9999px;
638
+ transition: background 0.2s ease;
639
+ }
640
+
641
+ /* Visible on hover */
642
+ &:hover {
643
+ scrollbar-color: color-mix(
644
+ in srgb,
645
+ var(--color-on-surface) 30%,
646
+ transparent
647
+ )
648
+ transparent;
649
+ }
650
+
651
+ &:hover::-webkit-scrollbar-thumb {
652
+ background: color-mix(in srgb, var(--color-on-surface) 30%, transparent);
653
+ }
654
+
655
+ /* Visible while actively scrolling (toggled by JS scroll listener) */
656
+ &[data-scrolling='true'] {
657
+ scrollbar-color: color-mix(
658
+ in srgb,
659
+ var(--color-on-surface) 30%,
660
+ transparent
661
+ )
662
+ transparent;
663
+ }
664
+
665
+ &[data-scrolling='true']::-webkit-scrollbar-thumb {
666
+ background: color-mix(in srgb, var(--color-on-surface) 30%, transparent);
667
+ }
668
+ }
615
669
  .prevent-select {
616
670
  -webkit-user-select: none; /* Safari */
617
671
  -ms-user-select: none; /* IE 10 and IE 11 */