@cl-light-ds/ui 0.4.0 → 0.6.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.
Files changed (127) hide show
  1. package/package.json +19 -17
  2. package/src/components/accordion/AccordionTrigger.astro +5 -1
  3. package/src/components/accordion/CHANGELOG.md +9 -0
  4. package/src/components/alert/Alert.astro +42 -3
  5. package/src/components/alert/CHANGELOG.md +19 -0
  6. package/src/components/button/Button.astro +78 -19
  7. package/src/components/button/CHANGELOG.md +22 -0
  8. package/src/components/button-group/ButtonGroup.astro +14 -3
  9. package/src/components/button-group/CHANGELOG.md +17 -0
  10. package/src/components/card/CHANGELOG.md +9 -0
  11. package/src/components/card/Card.astro +22 -0
  12. package/src/components/collapsible/CHANGELOG.md +9 -0
  13. package/src/components/collapsible/CollapsibleTrigger.astro +5 -1
  14. package/src/components/combobox/CHANGELOG.md +27 -0
  15. package/src/components/combobox/Combobox.astro +18 -1
  16. package/src/components/combobox/ComboboxTrigger.astro +32 -2
  17. package/src/components/command-search/CHANGELOG.md +57 -0
  18. package/src/components/command-search/CommandSearch.astro +594 -0
  19. package/src/components/command-search/CommandSearchGroup.astro +43 -0
  20. package/src/components/command-search/CommandSearchItem.astro +64 -0
  21. package/src/components/command-search/classes.ts +18 -0
  22. package/src/components/command-search/index.ts +12 -0
  23. package/src/components/command-search/nothing-matches.svg +42 -0
  24. package/src/components/dialog/CHANGELOG.md +50 -0
  25. package/src/components/dialog/Dialog.astro +14 -3
  26. package/src/components/dialog/DialogClose.astro +28 -12
  27. package/src/components/dialog/DialogContent.astro +19 -1
  28. package/src/components/dialog/DialogTrigger.astro +5 -1
  29. package/src/components/dropdown-menu/CHANGELOG.md +32 -0
  30. package/src/components/dropdown-menu/DropdownMenu.astro +13 -3
  31. package/src/components/dropdown-menu/DropdownMenuRadioGroup.astro +17 -2
  32. package/src/components/dropdown-menu/DropdownMenuTrigger.astro +7 -1
  33. package/src/components/dropdown-menu/dropdown-menu-item-variants.ts +1 -1
  34. package/src/components/empty-state/CHANGELOG.md +18 -0
  35. package/src/components/empty-state/EmptyState.astro +157 -0
  36. package/src/components/empty-state/illustrations/AllCaughtUp.astro +52 -0
  37. package/src/components/empty-state/illustrations/EmptyWorkspaces.astro +56 -0
  38. package/src/components/empty-state/illustrations/LoadError.astro +61 -0
  39. package/src/components/empty-state/illustrations/NoResults.astro +60 -0
  40. package/src/components/empty-state/illustrations/NothingMatches.astro +66 -0
  41. package/src/components/empty-state/illustrations/index.ts +27 -0
  42. package/src/components/empty-state/index.ts +7 -0
  43. package/src/components/favorite-toggle/CHANGELOG.md +13 -0
  44. package/src/components/favorite-toggle/FavoriteToggle.astro +94 -0
  45. package/src/components/favorite-toggle/index.ts +1 -0
  46. package/src/components/field/CHANGELOG.md +18 -0
  47. package/src/components/field/Field.astro +18 -0
  48. package/src/components/field/FieldError.astro +13 -1
  49. package/src/components/field/FieldGroup.astro +12 -3
  50. package/src/components/hover-card/CHANGELOG.md +9 -0
  51. package/src/components/hover-card/HoverCardTrigger.astro +5 -1
  52. package/src/components/input-group/CHANGELOG.md +7 -0
  53. package/src/components/input-group/InputGroup.astro +2 -0
  54. package/src/components/loading-overlay/CHANGELOG.md +25 -0
  55. package/src/components/loading-overlay/LoadingOverlay.astro +179 -0
  56. package/src/components/loading-overlay/index.ts +8 -0
  57. package/src/components/notifications-menu/CHANGELOG.md +29 -0
  58. package/src/components/notifications-menu/NotificationsMenu.astro +375 -0
  59. package/src/components/notifications-menu/NotificationsMenuItem.astro +130 -0
  60. package/src/components/notifications-menu/all-caught-up.svg +28 -0
  61. package/src/components/notifications-menu/index.ts +3 -0
  62. package/src/components/notifications-menu/types.ts +32 -0
  63. package/src/components/option-card/CHANGELOG.md +26 -0
  64. package/src/components/option-card/OptionCard.astro +143 -0
  65. package/src/components/option-card/index.ts +1 -0
  66. package/src/components/organisms/top-bar/CHANGELOG.md +27 -0
  67. package/src/components/organisms/top-bar/TopBar.astro +257 -0
  68. package/src/components/organisms/top-bar/index.ts +2 -0
  69. package/src/components/organisms/top-bar/types.ts +30 -0
  70. package/src/components/organisms/workspace/CHANGELOG.md +80 -0
  71. package/src/components/organisms/workspace/OrganisationOverview.astro +62 -0
  72. package/src/components/organisms/workspace/RecentWorkspaceCard.astro +235 -0
  73. package/src/components/organisms/workspace/RecentWorkspaces.astro +93 -0
  74. package/src/components/organisms/workspace/WorkspaceAvatar.astro +144 -0
  75. package/src/components/organisms/workspace/WorkspaceCard.astro +172 -0
  76. package/src/components/organisms/workspace/WorkspaceGlyph.astro +62 -0
  77. package/src/components/organisms/workspace/WorkspaceIcon.astro +42 -0
  78. package/src/components/organisms/workspace/WorkspaceIconPicker.astro +288 -0
  79. package/src/components/organisms/workspace/WorkspaceIconPickerGlyph.astro +44 -0
  80. package/src/components/organisms/workspace/icons.ts +98 -0
  81. package/src/components/organisms/workspace/index.ts +35 -0
  82. package/src/components/organisms/workspace/labels.ts +54 -0
  83. package/src/components/organisms/workspace/recent-card-rings-strong.png +0 -0
  84. package/src/components/organisms/workspace/types.ts +30 -0
  85. package/src/components/popover/CHANGELOG.md +14 -0
  86. package/src/components/popover/PopoverClose.astro +2 -1
  87. package/src/components/popover/PopoverTrigger.astro +7 -1
  88. package/src/components/profile-menu/CHANGELOG.md +22 -0
  89. package/src/components/profile-menu/ProfileMenu.astro +236 -0
  90. package/src/components/profile-menu/index.ts +2 -0
  91. package/src/components/profile-menu/types.ts +27 -0
  92. package/src/components/search-field/CHANGELOG.md +14 -0
  93. package/src/components/search-field/SearchField.astro +163 -0
  94. package/src/components/search-field/index.ts +1 -0
  95. package/src/components/select/CHANGELOG.md +8 -0
  96. package/src/components/select/SelectTrigger.astro +1 -1
  97. package/src/components/table/CHANGELOG.md +15 -0
  98. package/src/components/table/TableCell.astro +6 -0
  99. package/src/components/table/TableEditableCell.astro +2 -2
  100. package/src/components/table/TableHead.astro +5 -1
  101. package/src/components/table/TablePagination.astro +4 -4
  102. package/src/components/table/TableRow.astro +3 -1
  103. package/src/components/table/table-cell-variants.ts +2 -0
  104. package/src/components/tabs/CHANGELOG.md +16 -0
  105. package/src/components/tabs/Tabs.astro +26 -0
  106. package/src/components/tabs/TabsList.astro +30 -3
  107. package/src/components/toast/CHANGELOG.md +32 -0
  108. package/src/components/toast/Toast.astro +145 -0
  109. package/src/components/toast/Toaster.astro +322 -0
  110. package/src/components/toast/index.ts +9 -0
  111. package/src/components/toggle/CHANGELOG.md +7 -0
  112. package/src/components/toggle-group/CHANGELOG.md +11 -0
  113. package/src/components/toggle-group/ToggleGroup.astro +19 -2
  114. package/src/components/toggle-group/ToggleGroupItem.astro +10 -0
  115. package/src/components/tooltip/CHANGELOG.md +26 -0
  116. package/src/components/tooltip/Tooltip.astro +31 -3
  117. package/src/components/tooltip/TooltipTrigger.astro +5 -1
  118. package/src/lib/command-search.ts +78 -0
  119. package/src/lib/dialog-focus.ts +73 -0
  120. package/src/lib/dropdown-menu-radio-groups.ts +79 -0
  121. package/src/lib/loading-overlay.ts +174 -0
  122. package/src/lib/scroll-lock.ts +97 -0
  123. package/src/lib/toast.ts +127 -0
  124. package/src/lib/toggle-shared.ts +9 -1
  125. package/src/lib/workspace-palette.ts +119 -0
  126. package/src/styles/CHANGELOG.md +35 -0
  127. package/src/styles/globals.css +109 -0
@@ -0,0 +1,52 @@
1
+ ---
2
+ /**
3
+ * Illustration "all-caught-up" (144×104, with its own padding inside the viewBox).
4
+ * The designer's artwork; colours mapped to design tokens so it follows the theme.
5
+ * Decorative. Generated from the workspace selection handover assets (N15).
6
+ */
7
+ import type { HTMLAttributes } from "astro/types";
8
+
9
+ type Props = Omit<HTMLAttributes<"svg">, "width" | "height">;
10
+
11
+ const props = Astro.props as Props;
12
+ // Unique ids per instance, so several illustrations on one page don't share filters.
13
+ const uid = `es-all-caught-up-${Math.random().toString(36).slice(2, 8)}`;
14
+ ---
15
+
16
+ <svg
17
+ xmlns="http://www.w3.org/2000/svg"
18
+ width="144"
19
+ height="104"
20
+ viewBox="-12 -12 144 104"
21
+ fill="none"
22
+ aria-hidden="true"
23
+ focusable="false"
24
+ {...props}
25
+ >
26
+ <defs>
27
+ <filter id={`${uid}-sBack`} x="-30%" y="-40%" width="160%" height="190%" color-interpolation-filters="sRGB">
28
+ <feDropShadow dx="0" dy="1.5" stdDeviation="2" flood-color="#051729" flood-opacity="0.05"></feDropShadow>
29
+ </filter>
30
+ <filter id={`${uid}-sFront`} x="-40%" y="-40%" width="180%" height="190%" color-interpolation-filters="sRGB">
31
+ <feDropShadow dx="0" dy="5" stdDeviation="6" flood-color="#051729" flood-opacity="0.10"></feDropShadow>
32
+ </filter>
33
+ <filter id={`${uid}-sCheck`} x="-60%" y="-60%" width="220%" height="220%" color-interpolation-filters="sRGB">
34
+ <feDropShadow dx="0" dy="3" stdDeviation="3" flood-opacity="0.32" style="flood-color:var(--primary)"></feDropShadow>
35
+ </filter>
36
+ </defs>
37
+ <circle cx="60" cy="40" r="35" stroke-width="10" style="stroke:color-mix(in oklab, var(--primary-50) 50%, var(--background))"></circle>
38
+ <circle cx="60" cy="40" r="19" stroke-width="8" style="stroke:var(--primary-50)"></circle>
39
+ <g filter={`url(#${uid}-sBack)`}><rect x="22" y="12" width="76" height="18" rx="6" style="fill:var(--background)"></rect></g>
40
+ <rect x="22.5" y="12.5" width="75" height="17" rx="5.5" stroke-opacity="0.06" style="stroke:var(--foreground)"></rect>
41
+ <circle cx="31" cy="21" r="4" style="fill:var(--muted)"></circle>
42
+ <rect x="39" y="19" width="30" height="4" rx="2" style="fill:var(--muted)"></rect>
43
+ <g filter={`url(#${uid}-sFront)`}><rect x="16" y="34" width="88" height="24" rx="7" style="fill:var(--background)"></rect></g>
44
+ <rect x="16.5" y="34.5" width="87" height="23" rx="6.5" stroke-opacity="0.06" style="stroke:var(--foreground)"></rect>
45
+ <circle cx="28" cy="46" r="6" style="fill:var(--primary-50)"></circle>
46
+ <path d="M25.5 45.5 a2.5 2.5 0 0 1 5 0 c0 2.2 1 3 1 3 h-7 s1 -0.8 1 -3 Z M27.2 50.2 a1 1 0 0 0 1.6 0" stroke-width="1.1" stroke-linecap="round" stroke-linejoin="round" style="stroke:var(--primary)"></path>
47
+ <rect x="39" y="41" width="40" height="4" rx="2" style="fill:var(--border)"></rect>
48
+ <rect x="39" y="48.5" width="26" height="3.5" rx="1.75" style="fill:var(--muted)"></rect>
49
+ <g filter={`url(#${uid}-sCheck)`}><circle cx="100" cy="58" r="11" style="fill:var(--primary)"></circle></g>
50
+ <circle cx="100" cy="58" r="11" stroke-width="2.5" style="stroke:var(--background)"></circle>
51
+ <path d="M95.5 58.2 l3 3 l6 -6" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" style="stroke:var(--background)"></path>
52
+ </svg>
@@ -0,0 +1,56 @@
1
+ ---
2
+ /**
3
+ * Illustration "empty-workspaces" (280×200, with its own padding inside the viewBox).
4
+ * The designer's artwork; colours mapped to design tokens so it follows the theme.
5
+ * Decorative. Generated from the workspace selection handover assets (N15).
6
+ */
7
+ import type { HTMLAttributes } from "astro/types";
8
+
9
+ type Props = Omit<HTMLAttributes<"svg">, "width" | "height">;
10
+
11
+ const props = Astro.props as Props;
12
+ // Unique ids per instance, so several illustrations on one page don't share filters.
13
+ const uid = `es-empty-workspaces-${Math.random().toString(36).slice(2, 8)}`;
14
+ ---
15
+
16
+ <svg
17
+ xmlns="http://www.w3.org/2000/svg"
18
+ width="280"
19
+ height="200"
20
+ viewBox="-20 -20 280 200"
21
+ fill="none"
22
+ aria-hidden="true"
23
+ focusable="false"
24
+ {...props}
25
+ >
26
+ <defs>
27
+ <filter id={`${uid}-sBack`} x="-20%" y="-30%" width="140%" height="160%" color-interpolation-filters="sRGB">
28
+ <feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="#051729" flood-opacity="0.05"></feDropShadow>
29
+ </filter>
30
+ <filter id={`${uid}-sFront`} x="-30%" y="-40%" width="160%" height="190%" color-interpolation-filters="sRGB">
31
+ <feDropShadow dx="0" dy="8" stdDeviation="10" flood-color="#051729" flood-opacity="0.10"></feDropShadow>
32
+ </filter>
33
+ <filter id={`${uid}-sBlue`} x="-60%" y="-60%" width="220%" height="240%" color-interpolation-filters="sRGB">
34
+ <feDropShadow dx="0" dy="4" stdDeviation="5" flood-opacity="0.35" style="flood-color:var(--primary)"></feDropShadow>
35
+ </filter>
36
+ </defs>
37
+ <circle cx="120" cy="80" r="69" stroke-width="20" style="stroke:color-mix(in oklab, var(--primary-50) 50%, var(--background))"></circle>
38
+ <circle cx="120" cy="80" r="40" stroke-width="16" style="stroke:var(--primary-50)"></circle>
39
+ <g transform="rotate(-9 94 70)" filter={`url(#${uid}-sBack)`}>
40
+ <rect x="34" y="34" width="120" height="72" rx="10" style="fill:var(--background)"></rect>
41
+ <rect x="34.5" y="34.5" width="119" height="71" rx="9.5" stroke-opacity="0.06" style="stroke:var(--foreground)"></rect>
42
+ </g>
43
+ <g transform="rotate(8 152 66)" filter={`url(#${uid}-sBack)`}>
44
+ <rect x="92" y="30" width="120" height="72" rx="10" style="fill:var(--background)"></rect>
45
+ <rect x="92.5" y="30.5" width="119" height="71" rx="9.5" stroke-opacity="0.06" style="stroke:var(--foreground)"></rect>
46
+ </g>
47
+ <g filter={`url(#${uid}-sFront)`}>
48
+ <rect x="50" y="52" width="140" height="84" rx="12" style="fill:var(--background)"></rect>
49
+ </g>
50
+ <rect x="50.5" y="52.5" width="139" height="83" rx="11.5" stroke-opacity="0.06" style="stroke:var(--foreground)"></rect>
51
+ <rect x="64" y="66" width="28" height="28" rx="8" filter={`url(#${uid}-sBlue)`} style="fill:var(--primary)"></rect>
52
+ <rect x="77" y="74" width="2" height="12" rx="1" style="fill:var(--background)"></rect>
53
+ <rect x="72" y="79" width="12" height="2" rx="1" style="fill:var(--background)"></rect>
54
+ <rect x="64" y="106" width="76" height="7" rx="3.5" style="fill:var(--primary-100)"></rect>
55
+ <rect x="64" y="119" width="48" height="7" rx="3.5" style="fill:var(--muted)"></rect>
56
+ </svg>
@@ -0,0 +1,61 @@
1
+ ---
2
+ /**
3
+ * Illustration "load-error" (280×200, with its own padding inside the viewBox).
4
+ * The designer's artwork; colours mapped to design tokens so it follows the theme.
5
+ * Decorative. Generated from the workspace selection handover assets (N15).
6
+ */
7
+ import type { HTMLAttributes } from "astro/types";
8
+
9
+ type Props = Omit<HTMLAttributes<"svg">, "width" | "height">;
10
+
11
+ const props = Astro.props as Props;
12
+ // Unique ids per instance, so several illustrations on one page don't share filters.
13
+ const uid = `es-load-error-${Math.random().toString(36).slice(2, 8)}`;
14
+ ---
15
+
16
+ <svg
17
+ xmlns="http://www.w3.org/2000/svg"
18
+ width="280"
19
+ height="200"
20
+ viewBox="-20 -20 280 200"
21
+ fill="none"
22
+ aria-hidden="true"
23
+ focusable="false"
24
+ {...props}
25
+ >
26
+ <defs>
27
+ <filter id={`${uid}-sBack`} x="-20%" y="-30%" width="140%" height="160%" color-interpolation-filters="sRGB">
28
+ <feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="#051729" flood-opacity="0.05"></feDropShadow>
29
+ </filter>
30
+ <filter id={`${uid}-sFront`} x="-30%" y="-40%" width="160%" height="190%" color-interpolation-filters="sRGB">
31
+ <feDropShadow dx="0" dy="8" stdDeviation="10" flood-color="#051729" flood-opacity="0.10"></feDropShadow>
32
+ </filter>
33
+ <filter id={`${uid}-sBadge`} x="-60%" y="-60%" width="220%" height="220%" color-interpolation-filters="sRGB">
34
+ <feDropShadow dx="0" dy="4" stdDeviation="5" flood-opacity="0.28" style="flood-color:var(--destructive)"></feDropShadow>
35
+ </filter>
36
+ </defs>
37
+ <circle cx="120" cy="80" r="69" stroke-width="20" style="stroke:color-mix(in oklab, var(--primary-50) 50%, var(--background))"></circle>
38
+ <circle cx="120" cy="80" r="40" stroke-width="16" style="stroke:var(--primary-50)"></circle>
39
+ <g transform="rotate(-9 94 70)" filter={`url(#${uid}-sBack)`}>
40
+ <rect x="34" y="34" width="120" height="72" rx="10" style="fill:var(--background)"></rect>
41
+ <rect x="34.5" y="34.5" width="119" height="71" rx="9.5" stroke-opacity="0.06" style="stroke:var(--foreground)"></rect>
42
+ </g>
43
+ <g transform="rotate(8 152 66)" filter={`url(#${uid}-sBack)`}>
44
+ <rect x="92" y="30" width="120" height="72" rx="10" style="fill:var(--background)"></rect>
45
+ <rect x="92.5" y="30.5" width="119" height="71" rx="9.5" stroke-opacity="0.06" style="stroke:var(--foreground)"></rect>
46
+ </g>
47
+ <g filter={`url(#${uid}-sFront)`}>
48
+ <rect x="50" y="52" width="140" height="84" rx="12" style="fill:var(--background)"></rect>
49
+ </g>
50
+ <rect x="50.5" y="52.5" width="139" height="83" rx="11.5" stroke-opacity="0.06" style="stroke:var(--foreground)"></rect>
51
+ <rect x="64" y="66" width="28" height="28" rx="8" style="fill:var(--muted)"></rect>
52
+ <path d="M71 80 h3 l2.5 -5 l4 10 l2.5 -5 h3" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="stroke:var(--color-zinc-400, #a1a1aa)"></path>
53
+ <rect x="64" y="106" width="76" height="7" rx="3.5" style="fill:var(--border)"></rect>
54
+ <rect x="64" y="119" width="48" height="7" rx="3.5" style="fill:var(--muted)"></rect>
55
+ <g filter={`url(#${uid}-sBadge)`}>
56
+ <circle cx="180" cy="62" r="20" style="fill:var(--destructive)"></circle>
57
+ </g>
58
+ <circle cx="180" cy="62" r="20" stroke-width="4" style="stroke:var(--background)"></circle>
59
+ <rect x="178" y="51" width="4" height="14" rx="2" style="fill:var(--background)"></rect>
60
+ <circle cx="180" cy="71.5" r="2.4" style="fill:var(--background)"></circle>
61
+ </svg>
@@ -0,0 +1,60 @@
1
+ ---
2
+ /**
3
+ * Illustration "no-results" (280×200, with its own padding inside the viewBox).
4
+ * The designer's artwork; colours mapped to design tokens so it follows the theme.
5
+ * Decorative. Generated from the workspace selection handover assets (N15).
6
+ */
7
+ import type { HTMLAttributes } from "astro/types";
8
+
9
+ type Props = Omit<HTMLAttributes<"svg">, "width" | "height">;
10
+
11
+ const props = Astro.props as Props;
12
+ // Unique ids per instance, so several illustrations on one page don't share filters.
13
+ const uid = `es-no-results-${Math.random().toString(36).slice(2, 8)}`;
14
+ ---
15
+
16
+ <svg
17
+ xmlns="http://www.w3.org/2000/svg"
18
+ width="280"
19
+ height="200"
20
+ viewBox="-20 -20 280 200"
21
+ fill="none"
22
+ aria-hidden="true"
23
+ focusable="false"
24
+ {...props}
25
+ >
26
+ <defs>
27
+ <filter id={`${uid}-sBack`} x="-20%" y="-30%" width="140%" height="160%" color-interpolation-filters="sRGB">
28
+ <feDropShadow dx="0" dy="2" stdDeviation="3" flood-color="#051729" flood-opacity="0.05"></feDropShadow>
29
+ </filter>
30
+ <filter id={`${uid}-sFront`} x="-30%" y="-40%" width="160%" height="190%" color-interpolation-filters="sRGB">
31
+ <feDropShadow dx="0" dy="8" stdDeviation="10" flood-color="#051729" flood-opacity="0.10"></feDropShadow>
32
+ </filter>
33
+ <filter id={`${uid}-sLens`} x="-60%" y="-60%" width="220%" height="220%" color-interpolation-filters="sRGB">
34
+ <feDropShadow dx="0" dy="6" stdDeviation="6" flood-opacity="0.28" style="flood-color:var(--primary)"></feDropShadow>
35
+ </filter>
36
+ </defs>
37
+ <circle cx="120" cy="80" r="69" stroke-width="20" style="stroke:color-mix(in oklab, var(--primary-50) 50%, var(--background))"></circle>
38
+ <circle cx="120" cy="80" r="40" stroke-width="16" style="stroke:var(--primary-50)"></circle>
39
+ <g transform="rotate(-9 94 70)" filter={`url(#${uid}-sBack)`}>
40
+ <rect x="34" y="34" width="120" height="72" rx="10" style="fill:var(--background)"></rect>
41
+ <rect x="34.5" y="34.5" width="119" height="71" rx="9.5" stroke-opacity="0.06" style="stroke:var(--foreground)"></rect>
42
+ </g>
43
+ <g transform="rotate(8 152 66)" filter={`url(#${uid}-sBack)`}>
44
+ <rect x="92" y="30" width="120" height="72" rx="10" style="fill:var(--background)"></rect>
45
+ <rect x="92.5" y="30.5" width="119" height="71" rx="9.5" stroke-opacity="0.06" style="stroke:var(--foreground)"></rect>
46
+ </g>
47
+ <g filter={`url(#${uid}-sFront)`}>
48
+ <rect x="50" y="52" width="140" height="84" rx="12" style="fill:var(--background)"></rect>
49
+ </g>
50
+ <rect x="50.5" y="52.5" width="139" height="83" rx="11.5" stroke-opacity="0.06" style="stroke:var(--foreground)"></rect>
51
+ <rect x="64.75" y="66.75" width="26.5" height="26.5" rx="7.25" stroke-width="1.5" stroke-dasharray="4 3" style="stroke:var(--color-zinc-300, #d4d4d8)"></rect>
52
+ <rect x="64" y="106" width="76" height="7" rx="3.5" style="fill:var(--border)"></rect>
53
+ <rect x="64" y="119" width="48" height="7" rx="3.5" style="fill:var(--muted)"></rect>
54
+ <g filter={`url(#${uid}-sLens)`}>
55
+ <line x1="183" y1="127" x2="197" y2="141" stroke-width="9" stroke-linecap="round" style="stroke:var(--primary-700)"></line>
56
+ <circle cx="168" cy="112" r="20" stroke-width="6" style="fill:var(--background);stroke:var(--primary)"></circle>
57
+ </g>
58
+ <line x1="162" y1="106" x2="174" y2="118" stroke-width="3" stroke-linecap="round" style="stroke:var(--primary-300)"></line>
59
+ <line x1="174" y1="106" x2="162" y2="118" stroke-width="3" stroke-linecap="round" style="stroke:var(--primary-300)"></line>
60
+ </svg>
@@ -0,0 +1,66 @@
1
+ ---
2
+ /**
3
+ * Illustration "nothing-matches" (184×124, with its own padding inside the viewBox).
4
+ * The designer's artwork; colours mapped to design tokens so it follows the theme.
5
+ * Decorative. Generated from the workspace selection handover assets (N15).
6
+ */
7
+ import type { HTMLAttributes } from "astro/types";
8
+
9
+ type Props = Omit<HTMLAttributes<"svg">, "width" | "height">;
10
+
11
+ const props = Astro.props as Props;
12
+ // Unique ids per instance, so several illustrations on one page don't share filters.
13
+ const uid = `es-nothing-matches-${Math.random().toString(36).slice(2, 8)}`;
14
+ ---
15
+
16
+ <svg
17
+ xmlns="http://www.w3.org/2000/svg"
18
+ width="184"
19
+ height="124"
20
+ viewBox="-12 -12 184 124"
21
+ fill="none"
22
+ aria-hidden="true"
23
+ focusable="false"
24
+ {...props}
25
+ >
26
+ <defs>
27
+ <filter id={`${uid}-sBack`} x="-30%" y="-30%" width="160%" height="160%" color-interpolation-filters="sRGB">
28
+ <feDropShadow dx="0" dy="1.5" stdDeviation="2" flood-color="#051729" flood-opacity="0.05"></feDropShadow>
29
+ </filter>
30
+ <filter id={`${uid}-sFront`} x="-40%" y="-30%" width="180%" height="170%" color-interpolation-filters="sRGB">
31
+ <feDropShadow dx="0" dy="5" stdDeviation="6" flood-color="#051729" flood-opacity="0.10"></feDropShadow>
32
+ </filter>
33
+ <filter id={`${uid}-sLens`} x="-60%" y="-60%" width="220%" height="220%" color-interpolation-filters="sRGB">
34
+ <feDropShadow dx="0" dy="4" stdDeviation="4" flood-opacity="0.28" style="flood-color:var(--primary)"></feDropShadow>
35
+ </filter>
36
+ </defs>
37
+ <circle cx="80" cy="50" r="44" stroke-width="12" style="stroke:color-mix(in oklab, var(--primary-50) 50%, var(--background))"></circle>
38
+ <circle cx="80" cy="50" r="24" stroke-width="10" style="stroke:var(--primary-50)"></circle>
39
+ <g transform="rotate(-12 44 52)">
40
+ <g filter={`url(#${uid}-sBack)`}><rect x="22" y="26" width="44" height="52" rx="8" style="fill:var(--background)"></rect></g>
41
+ <rect x="22.5" y="26.5" width="43" height="51" rx="7.5" stroke-opacity="0.06" style="stroke:var(--foreground)"></rect>
42
+ <rect x="28" y="32" width="14" height="14" rx="4" style="fill:var(--primary-100)"></rect>
43
+ <rect x="28" y="54" width="24" height="4" rx="2" style="fill:var(--border)"></rect>
44
+ <rect x="28" y="62" width="16" height="4" rx="2" style="fill:var(--muted)"></rect>
45
+ </g>
46
+ <g transform="rotate(12 116 52)">
47
+ <g filter={`url(#${uid}-sBack)`}><rect x="94" y="26" width="44" height="52" rx="8" style="fill:var(--background)"></rect></g>
48
+ <rect x="94.5" y="26.5" width="43" height="51" rx="7.5" stroke-opacity="0.06" style="stroke:var(--foreground)"></rect>
49
+ <rect x="100" y="34" width="28" height="4" rx="2" style="fill:var(--border)"></rect>
50
+ <rect x="100" y="42" width="32" height="4" rx="2" style="fill:var(--muted)"></rect>
51
+ <rect x="100" y="50" width="22" height="4" rx="2" style="fill:var(--muted)"></rect>
52
+ <rect x="100" y="58" width="28" height="4" rx="2" style="fill:var(--muted)"></rect>
53
+ </g>
54
+ <g filter={`url(#${uid}-sFront)`}><rect x="56" y="18" width="48" height="58" rx="9" style="fill:var(--background)"></rect></g>
55
+ <rect x="56.5" y="18.5" width="47" height="57" rx="8.5" stroke-opacity="0.06" style="stroke:var(--foreground)"></rect>
56
+ <rect x="62" y="24" width="36" height="22" rx="5" style="fill:var(--primary-50)"></rect>
57
+ <path d="M77 30.5 L84.5 35 L77 39.5 Z" stroke-width="1.5" stroke-linejoin="round" style="fill:var(--primary-300);stroke:var(--primary-300)"></path>
58
+ <rect x="62" y="53" width="28" height="4" rx="2" style="fill:var(--border)"></rect>
59
+ <rect x="62" y="61" width="18" height="4" rx="2" style="fill:var(--muted)"></rect>
60
+ <g filter={`url(#${uid}-sLens)`}>
61
+ <line x1="113.5" y1="79.5" x2="122" y2="88" stroke-width="6" stroke-linecap="round" style="stroke:var(--primary-700)"></line>
62
+ <circle cx="104" cy="70" r="13" stroke-width="4" style="fill:var(--background);stroke:var(--primary)"></circle>
63
+ </g>
64
+ <line x1="100" y1="66" x2="108" y2="74" stroke-width="2.2" stroke-linecap="round" style="stroke:var(--primary-300)"></line>
65
+ <line x1="108" y1="66" x2="100" y2="74" stroke-width="2.2" stroke-linecap="round" style="stroke:var(--primary-300)"></line>
66
+ </svg>
@@ -0,0 +1,27 @@
1
+ import AllCaughtUp from "./AllCaughtUp.astro";
2
+ import EmptyWorkspaces from "./EmptyWorkspaces.astro";
3
+ import LoadError from "./LoadError.astro";
4
+ import NoResults from "./NoResults.astro";
5
+ import NothingMatches from "./NothingMatches.astro";
6
+
7
+ /**
8
+ * Built-in empty state illustrations. Each has its own padding inside the
9
+ * viewBox; `pull` is the negative vertical margin that takes it back out, so
10
+ * the drawing sits at the message's normal spacing.
11
+ */
12
+ export const emptyStateIllustrations = {
13
+ /** Nothing created yet (280×200, page). */
14
+ empty: { component: EmptyWorkspaces, width: 280, height: 200, pull: "-my-6" },
15
+ /** A search found nothing (280×200, page). */
16
+ "no-results": { component: NoResults, width: 280, height: 200, pull: "-my-6" },
17
+ /** Something failed to load (280×200, page). */
18
+ error: { component: LoadError, width: 280, height: 200, pull: "-my-6" },
19
+ /** Nothing new, e.g. notifications (144×104, menu). */
20
+ "all-caught-up": { component: AllCaughtUp, width: 144, height: 104, pull: "-my-2" },
21
+ /** A search in a menu or command palette found nothing (184×124, menu). */
22
+ "nothing-matches": { component: NothingMatches, width: 184, height: 124, pull: "-my-3" },
23
+ } as const;
24
+
25
+ export type EmptyStateIllustrationName = keyof typeof emptyStateIllustrations;
26
+
27
+ export const EMPTY_STATE_ILLUSTRATIONS = Object.keys(emptyStateIllustrations) as EmptyStateIllustrationName[];
@@ -0,0 +1,7 @@
1
+ export { default as EmptyState } from "./EmptyState.astro";
2
+ export { emptyStateVariants } from "./EmptyState.astro";
3
+ export {
4
+ EMPTY_STATE_ILLUSTRATIONS,
5
+ emptyStateIllustrations,
6
+ type EmptyStateIllustrationName,
7
+ } from "./illustrations";
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ All notable changes to the `FavoriteToggle` component will be documented in this file.
4
+
5
+ ## Date 15th Oct 2026
6
+ ## Workspace selection handover (N8)
7
+
8
+ ### Added
9
+
10
+ - `FavoriteToggle`: a `Toggle` with `variant="ghost"` and a Lucide `Star`. Pressed fills the star with the `favorite` token (was `badge-warning` in the screen, gap G3); no pressed background, the normal hover background on hover (C12).
11
+ - Props: `label` (the accessible name, required), `defaultPressed`, `disabled`, `size` (Toggle sizes, default `sm`, 32px), `class`, and any button attribute. State is `aria-pressed`; changes fire the Toggle runtime's `toggle:change`. `data-favorite-toggle` marks it for scripts.
12
+ - "Only on a favourite, or on hover or focus" is left to the consumer: documented as a `group/*` + `opacity-0 group-hover/*:opacity-100 group-focus-within/*:opacity-100 aria-pressed:opacity-100` recipe, so the star stays in the Tab order.
13
+ - Storybook: "Favorite Toggle" under Actions.
@@ -0,0 +1,94 @@
1
+ ---
2
+ /**
3
+ * @component FavoriteToggle
4
+ * @title Favorite Toggle
5
+ * @description A star that adds something to favourites or takes it out. A ghost `Toggle`: pressed shows as a star filled with the `favorite` colour, with no pressed background.
6
+ *
7
+ * @preview
8
+ *
9
+ * <div class="flex items-center gap-2">
10
+ * <FavoriteToggle label="Favourite Acme Logistics" />
11
+ * <FavoriteToggle label="Favourite Northwind" defaultPressed />
12
+ * </div>
13
+ *
14
+ * @usage
15
+ *
16
+ * ```astro nocollapse
17
+ * ---
18
+ * import { FavoriteToggle } from '@cl-light-ds/ui/components/favorite-toggle';
19
+ * ---
20
+ *
21
+ * <FavoriteToggle label="Favourite Acme Logistics" defaultPressed={item.starred} />
22
+ * ```
23
+ *
24
+ * `label` names what is favourited ("Favourite Acme Logistics"); the state is
25
+ * announced through `aria-pressed`, so the label stays the same either way.
26
+ *
27
+ * ## Only when favourited, or on hover / focus
28
+ *
29
+ * Not built in. Put a `group/<name>` on the container and reveal the star with
30
+ * classes, so it is always there for keyboard and screen reader users:
31
+ *
32
+ * ```astro nocollapse
33
+ * <div class="group/card relative ...">
34
+ * <FavoriteToggle
35
+ * label="Favourite Acme Logistics"
36
+ * class="opacity-0 group-hover/card:opacity-100 group-focus-within/card:opacity-100 aria-pressed:opacity-100"
37
+ * />
38
+ * </div>
39
+ * ```
40
+ *
41
+ * @api
42
+ *
43
+ * ### Events
44
+ *
45
+ * | Event | Detail | Description |
46
+ * |-------|--------|-------------|
47
+ * | `toggle:change` | `{ pressed: boolean }` | Fired by the Toggle runtime when the state changes |
48
+ *
49
+ * Set it from outside with `toggle:set` (`{ detail: { value: true } }`), as for `Toggle`.
50
+ */
51
+
52
+ import type { HTMLAttributes } from "astro/types";
53
+ import { Star } from "@lucide/astro";
54
+ import { cn } from "@cl-light-ds/ui/lib/utils";
55
+ import type { ToggleSize } from "@cl-light-ds/ui/lib/toggle-shared";
56
+ import { Toggle } from "../toggle";
57
+
58
+ interface Props extends Omit<HTMLAttributes<"button">, "class"> {
59
+ /** Accessible name, e.g. "Favourite Acme Logistics". */
60
+ label: string;
61
+ /** Initially a favourite. */
62
+ defaultPressed?: boolean;
63
+ disabled?: boolean;
64
+ /** Toggle size; the star is 16px at every size. */
65
+ size?: ToggleSize;
66
+ class?: string;
67
+ }
68
+
69
+ const {
70
+ label,
71
+ defaultPressed = false,
72
+ disabled = false,
73
+ size = "sm",
74
+ class: className = "",
75
+ ...rest
76
+ } = Astro.props as Props;
77
+ ---
78
+
79
+ <Toggle
80
+ {...rest}
81
+ variant="ghost"
82
+ size={size}
83
+ defaultPressed={defaultPressed}
84
+ disabled={disabled}
85
+ aria-label={label}
86
+ data-favorite-toggle
87
+ class={cn("transition-[color,box-shadow,opacity]", className)}
88
+ >
89
+ <Star
90
+ data-slot="favorite-toggle-icon"
91
+ class="size-4 group-aria-pressed/toggle:fill-favorite group-aria-pressed/toggle:text-favorite"
92
+ aria-hidden="true"
93
+ />
94
+ </Toggle>
@@ -0,0 +1 @@
1
+ export { default as FavoriteToggle } from "./FavoriteToggle.astro";
@@ -2,6 +2,24 @@
2
2
 
3
3
  All notable changes to the `Field` component will be documented in this file.
4
4
 
5
+ ## Date 15th Oct 2026
6
+ ## Workspace selection handover (C21)
7
+
8
+ ### Added
9
+
10
+ - `leading` prop on `FieldError` (`"default" | "compact"`, default `"default"`), sets `data-leading`. `"compact"` sets a 16px line height (`leading-4`) instead of `text-sm`'s 20px, so a one-line message under a control leaves as much space below it as the gap between fields (D147: 26px against 27px, instead of 28px against 25px). Replaces the workspace-selection screen's `class="leading-4"`.
11
+ - Opt-in, not a default fix: the 20px line is `text-sm`'s own and matches Figma's `text/md` style (14/20) used across the form text, so it isn't a bug; the tighter line is for evening out the space in a compact form.
12
+ - Storybook: a "Compact error line height" preview.
13
+
14
+ ## Date 15th Oct 2026
15
+ ## Workspace selection handover (C20)
16
+
17
+ ### Added
18
+
19
+ - `spacing` prop on `FieldGroup` (`"default" | "compact"`, default `"default"`), sets `data-spacing`. `"compact"` puts 20px between the fields (`gap-5`) instead of 28px (`gap-7`), for a form that sits tighter, e.g. the New workspace dialog (D140). Nested field groups (`gap-4`) and checkbox groups (`gap-3`) keep their own gaps. Replaces the workspace-selection screen's `class="gap-5"`.
20
+ - Opt-in: field groups without `spacing` keep 28px.
21
+ - Storybook: a "Compact field group" preview.
22
+
5
23
  ## 2026-09-09 — Fixed control-to-label vertical alignment for native checkbox/radio
6
24
 
7
25
  ### Fixed
@@ -185,13 +185,22 @@
185
185
  *
186
186
  * | Prop | Type | Default |
187
187
  * |------|------|---------|
188
+ * | `spacing` | `"default" \| "compact"` | `"default"` |
188
189
  * | `class` | `string` | - |
189
190
  *
191
+ * `spacing="compact"` puts 20px between the fields instead of 28px, for a
192
+ * form that sits tighter (e.g. in a dialog).
193
+ *
190
194
  * ```astro nocollapse
191
195
  * <FieldGroup class="@container/field-group flex flex-col gap-6">
192
196
  * <Field>...</Field>
193
197
  * <Field>...</Field>
194
198
  * </FieldGroup>
199
+ *
200
+ * <FieldGroup spacing="compact">
201
+ * <Field>...</Field>
202
+ * <Field>...</Field>
203
+ * </FieldGroup>
195
204
  * ```
196
205
  *
197
206
  * ### Field
@@ -292,12 +301,21 @@
292
301
  * |------|------|---------|
293
302
  * | `errors` | `Array<{ message?: string } \| undefined>` | `[]` |
294
303
  * | `forceMount` | `boolean` | `false` |
304
+ * | `leading` | `"default" \| "compact"` | `"default"` |
295
305
  * | `class` | `string` | - |
296
306
  *
297
307
  * ```astro nocollapse
298
308
  * <FieldError errors={[{ message: "Username is required" }]} />
299
309
  * ```
300
310
  *
311
+ * `leading="compact"` sets a 16px line height (20px by default), so a one-line
312
+ * message under a control leaves as much space below it as the gap between
313
+ * fields:
314
+ *
315
+ * ```astro nocollapse
316
+ * <FieldError leading="compact">That address is taken.</FieldError>
317
+ * ```
318
+ *
301
319
  * When `errors` contains multiple unique messages, the component renders a list
302
320
  * automatically.
303
321
  *
@@ -7,12 +7,19 @@ type ErrorItem = { message?: string } | undefined;
7
7
  interface Props extends HTMLAttributes<"div"> {
8
8
  errors?: ErrorItem[];
9
9
  forceMount?: boolean;
10
+ /**
11
+ * Line height: `"default"` 20px (text-sm's own), `"compact"` 16px
12
+ * (`leading-4`), so a one-line message under a control leaves as much space
13
+ * below it as the gap between fields (C21, D147).
14
+ */
15
+ leading?: "default" | "compact";
10
16
  }
11
17
 
12
18
  const {
13
19
  class: className = "",
14
20
  errors = [],
15
21
  forceMount = false,
22
+ leading = "default",
16
23
  ...rest
17
24
  } = Astro.props as Props;
18
25
 
@@ -34,7 +41,12 @@ const shouldRender = forceMount || hasContent || uniqueMessages.length > 0;
34
41
  <div
35
42
  role="alert"
36
43
  data-slot="field-error"
37
- class={cn("text-destructive text-sm font-normal", className)}
44
+ data-leading={leading}
45
+ class={cn(
46
+ "text-destructive text-sm font-normal",
47
+ leading === "compact" && "leading-4",
48
+ className,
49
+ )}
38
50
  {...rest}
39
51
  >
40
52
  {hasContent && <slot />}
@@ -2,15 +2,24 @@
2
2
  import type { HTMLAttributes } from "astro/types";
3
3
  import { cn } from "@cl-light-ds/ui/lib/utils";
4
4
 
5
- interface Props extends HTMLAttributes<"div"> {}
5
+ interface Props extends HTMLAttributes<"div"> {
6
+ /**
7
+ * Space between the fields: `"default"` 28px (`gap-7`), `"compact"` 20px
8
+ * (`gap-5`), for a form that sits tighter, e.g. in a dialog (C20, D140).
9
+ * Nested field groups and checkbox groups keep their own gaps.
10
+ */
11
+ spacing?: "default" | "compact";
12
+ }
6
13
 
7
- const { class: className = "", ...rest } = Astro.props as Props;
14
+ const { spacing = "default", class: className = "", ...rest } = Astro.props as Props;
8
15
  ---
9
16
 
10
17
  <div
11
18
  data-slot="field-group"
19
+ data-spacing={spacing}
12
20
  class={cn(
13
- "gap-7 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4",
21
+ spacing === "compact" ? "gap-5" : "gap-7",
22
+ "data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4",
14
23
  "group/field-group @container/field-group flex w-full flex-col",
15
24
  className,
16
25
  )}
@@ -2,6 +2,15 @@
2
2
 
3
3
  All notable changes to the `HoverCard` component family (`HoverCard`, `HoverCardTrigger`, `HoverCardContent`) are documented in this file.
4
4
 
5
+ ## Date 15th Oct 2026
6
+ ## Handover gaps: triggers don't submit forms (C29, gap G18)
7
+
8
+ ### Docs
9
+
10
+ - `HoverCardTrigger` already renders `type="button"` unless you pass a `type`; no change.
11
+ - **`asChild`:** the wrapper is a `<div>`, not a button, so it can't set a type. Inside a `<form>`, give your child `type="button"` (now in the prop's JSDoc).
12
+
13
+
5
14
  ## 2026-09-09 — Figma alignment (ClickLearn Lightning Design Library, node `5455-14770`)
6
15
 
7
16
  Figma shows three content **Variants** (Default, Simple, Avatar) and two **States** (Default, Focus) for the card surface.
@@ -4,7 +4,11 @@ import type { HTMLAttributes } from "astro/types";
4
4
 
5
5
  interface Props extends HTMLAttributes<"button"> {
6
6
  class?: string;
7
- /** Radix-like: render wrapper so the child can be the interactive element */
7
+ /**
8
+ * Radix-like: render wrapper so the child can be the interactive element.
9
+ * The wrapper is not a button, so the child sets its own type: give a
10
+ * `<button>`/`<Button>` child `type="button"` inside a `<form>` or it submits it.
11
+ */
8
12
  asChild?: boolean;
9
13
  }
10
14
 
@@ -2,6 +2,13 @@
2
2
 
3
3
  All notable changes to the `InputGroup` component family (`InputGroup`, `InputGroupAddon`, `InputGroupButton`, `InputGroupInput`, `InputGroupText`, `InputGroupTextarea`) are documented in this file.
4
4
 
5
+ ## Date 15th Oct 2026
6
+ ## Workspace selection handover (C5)
7
+
8
+ - An input directly after an `InputGroupAddon align="inline-start"` holding `InputGroupText` has no left padding, so a prefix such as `lightning.com/` runs straight into what's typed. Icon prefixes are unchanged. Needs the addon before the input in the markup.
9
+ - Storybook: "Text prefix" preview.
10
+
11
+
5
12
  ## 2026-09-10 — Figma alignment (ClickLearn Lightning Design Library, node `2282-6923`)
6
13
 
7
14
  Figma documents a single "Default" composition with three example rows: a lead-icon search input with trailing supportive text, a plain input with a trailing icon, and a multi-line textarea with a toolbar (icon button + ghost text button + supportive text + separator + circular primary send button). No Sizes/States/Variants pages exist for this component in Figma (unlike `Input`) — scope is narrower accordingly.