@fragments-sdk/ui 0.18.0 → 0.19.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 (222) hide show
  1. package/dist/assets/ui.css +2700 -3256
  2. package/dist/chart.cjs +0 -1
  3. package/dist/chart.js +0 -1
  4. package/dist/codeblock.cjs +0 -1
  5. package/dist/codeblock.js +0 -1
  6. package/dist/colorpicker.cjs +0 -1
  7. package/dist/colorpicker.js +0 -1
  8. package/dist/components/Accordion/Accordion.module.scss.cjs +8 -8
  9. package/dist/components/Accordion/Accordion.module.scss.js +8 -8
  10. package/dist/components/Alert/Alert.module.scss.cjs +12 -12
  11. package/dist/components/Alert/Alert.module.scss.js +12 -12
  12. package/dist/components/AppShell/AppShell.module.scss.cjs +12 -12
  13. package/dist/components/AppShell/AppShell.module.scss.js +12 -12
  14. package/dist/components/Avatar/Avatar.module.scss.cjs +13 -13
  15. package/dist/components/Avatar/Avatar.module.scss.js +13 -13
  16. package/dist/components/Badge/Badge.module.scss.cjs +13 -13
  17. package/dist/components/Badge/Badge.module.scss.js +13 -13
  18. package/dist/components/BentoGrid/BentoGrid.module.scss.cjs +14 -14
  19. package/dist/components/BentoGrid/BentoGrid.module.scss.js +14 -14
  20. package/dist/components/Box/Box.module.scss.cjs +152 -152
  21. package/dist/components/Box/Box.module.scss.js +152 -152
  22. package/dist/components/Breadcrumbs/Breadcrumbs.module.scss.cjs +8 -8
  23. package/dist/components/Breadcrumbs/Breadcrumbs.module.scss.js +8 -8
  24. package/dist/components/Button/Button.module.scss.cjs +12 -12
  25. package/dist/components/Button/Button.module.scss.js +12 -12
  26. package/dist/components/Card/Card.module.scss.cjs +14 -14
  27. package/dist/components/Card/Card.module.scss.js +14 -14
  28. package/dist/components/Chart/Chart.module.scss.cjs +15 -15
  29. package/dist/components/Chart/Chart.module.scss.js +15 -15
  30. package/dist/components/Chart/index.d.ts +0 -1
  31. package/dist/components/Chart/index.d.ts.map +1 -1
  32. package/dist/components/Checkbox/Checkbox.module.scss.cjs +10 -10
  33. package/dist/components/Checkbox/Checkbox.module.scss.js +10 -10
  34. package/dist/components/Chip/Chip.module.scss.cjs +15 -15
  35. package/dist/components/Chip/Chip.module.scss.js +15 -15
  36. package/dist/components/CodeBlock/CodeBlock.module.scss.cjs +21 -21
  37. package/dist/components/CodeBlock/CodeBlock.module.scss.js +21 -21
  38. package/dist/components/CodeBlock/index.d.ts +0 -1
  39. package/dist/components/CodeBlock/index.d.ts.map +1 -1
  40. package/dist/components/Collapsible/Collapsible.module.scss.cjs +10 -10
  41. package/dist/components/Collapsible/Collapsible.module.scss.js +10 -10
  42. package/dist/components/ColorPicker/ColorPicker.module.scss.cjs +14 -14
  43. package/dist/components/ColorPicker/ColorPicker.module.scss.js +14 -14
  44. package/dist/components/ColorPicker/index.d.ts +0 -1
  45. package/dist/components/ColorPicker/index.d.ts.map +1 -1
  46. package/dist/components/Combobox/Combobox.module.scss.cjs +23 -23
  47. package/dist/components/Combobox/Combobox.module.scss.js +23 -23
  48. package/dist/components/Combobox/index.cjs +13 -10
  49. package/dist/components/Combobox/index.d.ts.map +1 -1
  50. package/dist/components/Combobox/index.js +13 -10
  51. package/dist/components/Command/Command.module.scss.cjs +11 -11
  52. package/dist/components/Command/Command.module.scss.js +11 -11
  53. package/dist/components/ConversationList/ConversationList.module.scss.cjs +10 -10
  54. package/dist/components/ConversationList/ConversationList.module.scss.js +10 -10
  55. package/dist/components/DataTable/DataTable.module.scss.cjs +26 -26
  56. package/dist/components/DataTable/DataTable.module.scss.js +26 -26
  57. package/dist/components/DataTable/index.cjs +0 -1
  58. package/dist/components/DataTable/index.d.ts +0 -1
  59. package/dist/components/DataTable/index.d.ts.map +1 -1
  60. package/dist/components/DataTable/index.js +0 -1
  61. package/dist/components/DatePicker/DatePicker.module.scss.cjs +31 -31
  62. package/dist/components/DatePicker/DatePicker.module.scss.js +31 -31
  63. package/dist/components/DatePicker/index.d.ts +0 -1
  64. package/dist/components/DatePicker/index.d.ts.map +1 -1
  65. package/dist/components/Dialog/Dialog.module.scss.cjs +14 -14
  66. package/dist/components/Dialog/Dialog.module.scss.js +14 -14
  67. package/dist/components/Drawer/Drawer.module.scss.cjs +33 -27
  68. package/dist/components/Drawer/Drawer.module.scss.js +34 -28
  69. package/dist/components/Drawer/index.cjs +36 -14
  70. package/dist/components/Drawer/index.d.ts +21 -3
  71. package/dist/components/Drawer/index.d.ts.map +1 -1
  72. package/dist/components/Drawer/index.js +36 -14
  73. package/dist/components/Editor/Editor.module.scss.cjs +17 -17
  74. package/dist/components/Editor/Editor.module.scss.js +17 -17
  75. package/dist/components/EmptyState/EmptyState.module.scss.cjs +8 -8
  76. package/dist/components/EmptyState/EmptyState.module.scss.js +8 -8
  77. package/dist/components/Field/Field.module.scss.cjs +4 -4
  78. package/dist/components/Field/Field.module.scss.js +4 -4
  79. package/dist/components/Fieldset/Fieldset.module.scss.cjs +3 -3
  80. package/dist/components/Fieldset/Fieldset.module.scss.js +3 -3
  81. package/dist/components/Header/Header.module.scss.cjs +28 -28
  82. package/dist/components/Header/Header.module.scss.js +28 -28
  83. package/dist/components/Icon/Icon.module.scss.cjs +8 -8
  84. package/dist/components/Icon/Icon.module.scss.js +8 -8
  85. package/dist/components/Image/Image.module.scss.cjs +27 -27
  86. package/dist/components/Image/Image.module.scss.js +27 -27
  87. package/dist/components/Input/Input.module.scss.cjs +19 -19
  88. package/dist/components/Input/Input.module.scss.js +19 -19
  89. package/dist/components/Link/Link.module.scss.cjs +10 -10
  90. package/dist/components/Link/Link.module.scss.js +10 -10
  91. package/dist/components/Listbox/Listbox.module.scss.cjs +8 -8
  92. package/dist/components/Listbox/Listbox.module.scss.js +8 -8
  93. package/dist/components/Loading/Loading.module.scss.cjs +30 -30
  94. package/dist/components/Loading/Loading.module.scss.js +30 -30
  95. package/dist/components/Markdown/Markdown.module.scss.cjs +1 -1
  96. package/dist/components/Markdown/Markdown.module.scss.js +1 -1
  97. package/dist/components/Markdown/index.d.ts +0 -1
  98. package/dist/components/Markdown/index.d.ts.map +1 -1
  99. package/dist/components/Menu/Menu.module.scss.cjs +16 -13
  100. package/dist/components/Menu/Menu.module.scss.js +17 -14
  101. package/dist/components/Menu/index.cjs +1 -1
  102. package/dist/components/Menu/index.d.ts.map +1 -1
  103. package/dist/components/Menu/index.js +1 -1
  104. package/dist/components/Message/Message.module.scss.cjs +18 -18
  105. package/dist/components/Message/Message.module.scss.js +18 -18
  106. package/dist/components/NavigationMenu/NavigationMenu.module.scss.cjs +28 -28
  107. package/dist/components/NavigationMenu/NavigationMenu.module.scss.js +28 -28
  108. package/dist/components/Pagination/Pagination.module.scss.cjs +7 -7
  109. package/dist/components/Pagination/Pagination.module.scss.js +7 -7
  110. package/dist/components/Popover/Popover.module.scss.cjs +10 -10
  111. package/dist/components/Popover/Popover.module.scss.js +10 -10
  112. package/dist/components/Progress/Progress.module.scss.cjs +25 -25
  113. package/dist/components/Progress/Progress.module.scss.js +25 -25
  114. package/dist/components/Prompt/Prompt.module.scss.cjs +26 -14
  115. package/dist/components/Prompt/Prompt.module.scss.js +26 -14
  116. package/dist/components/Prompt/index.cjs +16 -0
  117. package/dist/components/Prompt/index.d.ts +17 -1
  118. package/dist/components/Prompt/index.d.ts.map +1 -1
  119. package/dist/components/Prompt/index.js +16 -0
  120. package/dist/components/RadioGroup/RadioGroup.module.scss.cjs +16 -16
  121. package/dist/components/RadioGroup/RadioGroup.module.scss.js +16 -16
  122. package/dist/components/ScrollArea/ScrollArea.module.scss.cjs +10 -10
  123. package/dist/components/ScrollArea/ScrollArea.module.scss.js +10 -10
  124. package/dist/components/Select/Select.module.scss.cjs +17 -17
  125. package/dist/components/Select/Select.module.scss.js +17 -17
  126. package/dist/components/Select/index.cjs +20 -20
  127. package/dist/components/Select/index.d.ts.map +1 -1
  128. package/dist/components/Select/index.js +20 -20
  129. package/dist/components/Separator/Separator.module.scss.cjs +10 -10
  130. package/dist/components/Separator/Separator.module.scss.js +10 -10
  131. package/dist/components/Sidebar/Sidebar.module.scss.cjs +42 -42
  132. package/dist/components/Sidebar/Sidebar.module.scss.js +42 -42
  133. package/dist/components/Slider/Slider.module.scss.cjs +12 -12
  134. package/dist/components/Slider/Slider.module.scss.js +12 -12
  135. package/dist/components/Slider/index.cjs +23 -21
  136. package/dist/components/Slider/index.js +23 -21
  137. package/dist/components/Stack/Stack.module.scss.cjs +35 -35
  138. package/dist/components/Stack/Stack.module.scss.js +35 -35
  139. package/dist/components/Table/Table.module.scss.cjs +16 -16
  140. package/dist/components/Table/Table.module.scss.js +16 -16
  141. package/dist/components/Table/index.d.ts +0 -1
  142. package/dist/components/Table/index.d.ts.map +1 -1
  143. package/dist/components/TableOfContents/TableOfContents.module.scss.cjs +7 -7
  144. package/dist/components/TableOfContents/TableOfContents.module.scss.js +7 -7
  145. package/dist/components/Tabs/Tabs.module.scss.cjs +9 -9
  146. package/dist/components/Tabs/Tabs.module.scss.js +9 -9
  147. package/dist/components/Text/Text.module.scss.cjs +38 -38
  148. package/dist/components/Text/Text.module.scss.js +38 -38
  149. package/dist/components/Textarea/Textarea.module.scss.cjs +23 -23
  150. package/dist/components/Textarea/Textarea.module.scss.js +23 -23
  151. package/dist/components/Theme/ThemeToggle.module.scss.cjs +6 -6
  152. package/dist/components/Theme/ThemeToggle.module.scss.js +6 -6
  153. package/dist/components/ThinkingIndicator/ThinkingIndicator.module.scss.cjs +22 -22
  154. package/dist/components/ThinkingIndicator/ThinkingIndicator.module.scss.js +22 -22
  155. package/dist/components/Toast/Toast.module.scss.cjs +20 -20
  156. package/dist/components/Toast/Toast.module.scss.js +20 -20
  157. package/dist/components/Toggle/Toggle.module.scss.cjs +13 -13
  158. package/dist/components/Toggle/Toggle.module.scss.js +13 -13
  159. package/dist/components/ToggleGroup/ToggleGroup.module.scss.cjs +17 -17
  160. package/dist/components/ToggleGroup/ToggleGroup.module.scss.js +17 -17
  161. package/dist/components/Tooltip/Tooltip.module.scss.cjs +3 -3
  162. package/dist/components/Tooltip/Tooltip.module.scss.js +3 -3
  163. package/dist/components/Tooltip/index.cjs +4 -3
  164. package/dist/components/Tooltip/index.d.ts +4 -1
  165. package/dist/components/Tooltip/index.d.ts.map +1 -1
  166. package/dist/components/Tooltip/index.js +4 -3
  167. package/dist/datepicker.cjs +0 -1
  168. package/dist/datepicker.js +0 -1
  169. package/dist/index.cjs +0 -1
  170. package/dist/index.d.ts +2 -3
  171. package/dist/index.d.ts.map +1 -1
  172. package/dist/index.js +0 -1
  173. package/dist/markdown.cjs +0 -1
  174. package/dist/markdown.js +0 -1
  175. package/dist/table.cjs +0 -1
  176. package/dist/table.js +0 -1
  177. package/dist/utils/seed-derivation.cjs +29 -0
  178. package/dist/utils/seed-derivation.d.ts +1 -1
  179. package/dist/utils/seed-derivation.d.ts.map +1 -1
  180. package/dist/utils/seed-derivation.js +29 -0
  181. package/fragments.json +1 -1
  182. package/package.json +18 -14
  183. package/src/components/Button/Button.module.scss +24 -1
  184. package/src/components/Card/Card.module.scss +5 -1
  185. package/src/components/Chart/index.tsx +0 -1
  186. package/src/components/Chip/Chip.module.scss +4 -4
  187. package/src/components/CodeBlock/index.tsx +0 -1
  188. package/src/components/ColorPicker/index.tsx +0 -1
  189. package/src/components/Combobox/index.tsx +7 -8
  190. package/src/components/Command/Command.module.scss +22 -9
  191. package/src/components/DataTable/index.tsx +0 -2
  192. package/src/components/DatePicker/index.tsx +0 -2
  193. package/src/components/Drawer/Drawer.contract.json +1 -0
  194. package/src/components/Drawer/Drawer.module.scss +45 -5
  195. package/src/components/Drawer/index.tsx +66 -23
  196. package/src/components/Header/Header.contract.json +2 -0
  197. package/src/components/Input/Input.module.scss +16 -6
  198. package/src/components/Markdown/Markdown.module.scss +0 -3
  199. package/src/components/Markdown/index.tsx +0 -1
  200. package/src/components/Menu/Menu.module.scss +6 -0
  201. package/src/components/Menu/index.tsx +3 -1
  202. package/src/components/Message/Message.module.scss +2 -2
  203. package/src/components/NavigationMenu/NavigationMenu.module.scss +18 -23
  204. package/src/components/Pagination/Pagination.module.scss +1 -1
  205. package/src/components/Prompt/Prompt.module.scss +117 -3
  206. package/src/components/Prompt/index.tsx +40 -0
  207. package/src/components/Select/index.tsx +20 -25
  208. package/src/components/Sidebar/Sidebar.module.scss +1 -1
  209. package/src/components/Slider/index.tsx +10 -10
  210. package/src/components/Table/index.tsx +0 -2
  211. package/src/components/TableOfContents/TableOfContents.module.scss +19 -15
  212. package/src/components/Toggle/Toggle.module.scss +3 -3
  213. package/src/components/Tooltip/index.tsx +7 -3
  214. package/src/index.ts +8 -3
  215. package/src/styles/globals.scss +6 -1
  216. package/src/tokens/_computed.scss +3 -1
  217. package/src/tokens/_density.scss +4 -4
  218. package/src/tokens/_derive.scss +48 -55
  219. package/src/tokens/_palettes.scss +20 -1
  220. package/src/tokens/_seeds.scss +2 -2
  221. package/src/tokens/_variables.scss +45 -29
  222. package/src/utils/seed-derivation.ts +23 -1
@@ -36,6 +36,9 @@ export interface TooltipProps extends Omit<React.HTMLAttributes<HTMLDivElement>,
36
36
  defaultOpen?: boolean;
37
37
  /** Callback when open state changes */
38
38
  onOpenChange?: (open: boolean) => void;
39
+ /** Whether clicking the trigger closes the tooltip.
40
+ * @default false */
41
+ closeOnClick?: boolean;
39
42
  /** Explicit props for the tooltip popup element (preferred over top-level HTMLAttributes for clarity) */
40
43
  contentProps?: React.HTMLAttributes<HTMLDivElement>;
41
44
  }
@@ -83,6 +86,7 @@ function TooltipRoot({
83
86
  open,
84
87
  defaultOpen,
85
88
  onOpenChange,
89
+ closeOnClick = false,
86
90
  contentProps,
87
91
  className,
88
92
  style,
@@ -113,8 +117,8 @@ function TooltipRoot({
113
117
  [children],
114
118
  );
115
119
 
116
- if (disabled) {
117
- return children;
120
+ if (disabled || !children) {
121
+ return children ?? null;
118
122
  }
119
123
 
120
124
  const {
@@ -129,7 +133,7 @@ function TooltipRoot({
129
133
  defaultOpen={defaultOpen}
130
134
  onOpenChange={onOpenChange}
131
135
  >
132
- <BaseTooltip.Trigger render={renderTrigger} />
136
+ <BaseTooltip.Trigger closeOnClick={closeOnClick} render={renderTrigger} />
133
137
  <BaseTooltip.Portal>
134
138
  <BaseTooltip.Positioner
135
139
  side={side}
package/src/index.ts CHANGED
@@ -1,6 +1,8 @@
1
- // Import CSS variables and base styles
2
- // This ensures --fui-* variables are available when using any component
3
- import './styles/globals.scss';
1
+ // CSS variables and base styles are NOT auto-imported here.
2
+ // Consumers must import styles separately with seed configuration:
3
+ // @use '@fragments-sdk/ui/styles' with ($fui-brand: ..., $fui-neutral: ...);
4
+ // Bundling globals.scss here would compile with default seeds and
5
+ // override any consumer-configured palette.
4
6
 
5
7
  // Runtime CSS detection — warns if component styles aren't loaded
6
8
  import { checkCssLoaded } from './utils/css-warning';
@@ -404,6 +406,8 @@ export {
404
406
  type PromptVariant,
405
407
  type PromptTextareaProps,
406
408
  type PromptToolbarProps,
409
+ type PromptTabsProps,
410
+ type PromptTabProps,
407
411
  type PromptActionsProps,
408
412
  type PromptInfoProps,
409
413
  type PromptActionButtonProps,
@@ -591,6 +595,7 @@ export {
591
595
  type DrawerBodyProps,
592
596
  type DrawerFooterProps,
593
597
  type DrawerCloseProps,
598
+ type DrawerSwipeAreaProps,
594
599
  } from './components/Drawer';
595
600
 
596
601
  // Pagination
@@ -83,13 +83,18 @@ $fui-info: #3b82f6 !default;
83
83
  }
84
84
 
85
85
  // Base Element Resets
86
- :where(html) {
86
+ // Use real specificity (not :where()) for font-size so it overrides
87
+ // the browser default 16px. This makes 1rem = 14px throughout.
88
+ html {
89
+ font-size: #{$fui-base-font-size};
87
90
  scroll-behavior: smooth;
88
91
  }
89
92
 
90
93
  :where(body) {
91
94
  margin: 0;
95
+ font-size: inherit;
92
96
  font-family: var(--fui-font-sans, Inter, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif);
97
+ line-height: var(--fui-line-height-normal, $fui-line-height-normal);
93
98
  background-color: var(--fui-bg-primary, #ffffff);
94
99
  color: var(--fui-text-primary, #171717);
95
100
  -webkit-font-smoothing: antialiased;
@@ -86,6 +86,7 @@ $computed-sidebar-item-height: density.px-to-rem($_density, map.get($_density, s
86
86
 
87
87
  // Accent colors (from brand seed)
88
88
  $computed-color-accent: seeds.$fui-brand;
89
+ $computed-color-on-accent: derive.contrast-color(seeds.$fui-brand);
89
90
  $computed-color-accent-hover: derive.derive-accent-hover(seeds.$fui-brand, false);
90
91
  $computed-color-accent-active: derive.derive-accent-active(seeds.$fui-brand, false);
91
92
 
@@ -153,8 +154,9 @@ $computed-backdrop: derive.derive-backdrop(false);
153
154
  // Computed: Colors - Dark Mode
154
155
  // --------------------------------------------
155
156
 
156
- // Accent colors (derive light version for dark mode)
157
+ // Accent colors dark brands lightened for visibility; on-accent handles text contrast
157
158
  $computed-dark-color-accent: derive.derive-dark-accent(seeds.$fui-brand);
159
+ $computed-dark-color-on-accent: derive.contrast-color($computed-dark-color-accent);
158
160
  $computed-dark-color-accent-hover: derive.derive-accent-hover($computed-dark-color-accent, true);
159
161
  $computed-dark-color-accent-active: derive.derive-accent-active($computed-dark-color-accent, true);
160
162
 
@@ -59,8 +59,8 @@ $density-default: (
59
59
  // Input height
60
60
  input-height: 40px,
61
61
  // ~6 units
62
- input-height-sm: 28px,
63
- // 4 units
62
+ input-height-sm: 32px,
63
+ // ~4.5 units
64
64
  input-height-lg: 44px,
65
65
  // ~6 units
66
66
  // Touch targets (baseline)
@@ -70,8 +70,8 @@ $density-default: (
70
70
  touch-lg: 44px,
71
71
  // WCAG AAA recommended
72
72
  // Sidebar navigation
73
- sidebar-item-height: 35px,
74
- // 5 units
73
+ sidebar-item-height: 36px,
74
+ // ~5 units
75
75
  );
76
76
 
77
77
  $density-relaxed: (
@@ -20,14 +20,8 @@
20
20
  /// @param {Color} $color - Color to check
21
21
  /// @return {Boolean} True if dark
22
22
  @function is-dark-color($color) {
23
- $r: math.div(color.channel($color, 'red', $space: rgb), 255);
24
- $g: math.div(color.channel($color, 'green', $space: rgb), 255);
25
- $b: math.div(color.channel($color, 'blue', $space: rgb), 255);
26
-
27
- // Relative luminance formula (simplified)
28
- $luminance: 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;
29
-
30
- @return $luminance < 0.5;
23
+ // oklch lightness is perceptually uniform (0-1 range)
24
+ @return color.channel($color, 'lightness', $space: oklch) < 0.5;
31
25
  }
32
26
 
33
27
  /// Get appropriate contrast color (black or white)
@@ -52,22 +46,22 @@
52
46
  /// @param {Boolean} $is-dark - Whether in dark mode
53
47
  /// @return {Color} Hover state color
54
48
  @function derive-accent-hover($base, $is-dark: false) {
55
- $l: color.channel($base, 'lightness', $space: hsl);
49
+ $l: color.channel($base, 'lightness', $space: oklch);
56
50
 
57
51
  @if $is-dark {
58
- @if $l > 85% {
52
+ @if $l > 0.85 {
59
53
  // Very light accent on dark bg: darken for visible hover
60
- @return color.scale($base, $lightness: -10%);
54
+ @return color.adjust($base, $lightness: -0.05, $space: oklch);
61
55
  }
62
56
  // Standard dark mode: lighten for hover
63
- @return color.scale($base, $lightness: 8%);
57
+ @return color.adjust($base, $lightness: 0.04, $space: oklch);
64
58
  } @else {
65
- @if $l < 15% {
59
+ @if $l < 0.15 {
66
60
  // Very dark accent on light bg: lighten for visible hover
67
- @return color.scale($base, $lightness: 18%);
61
+ @return color.adjust($base, $lightness: 0.09, $space: oklch);
68
62
  }
69
63
  // Standard light mode: darken for hover
70
- @return color.scale($base, $lightness: -10%);
64
+ @return color.adjust($base, $lightness: -0.05, $space: oklch);
71
65
  }
72
66
  }
73
67
 
@@ -78,38 +72,36 @@
78
72
  /// @param {Boolean} $is-dark - Whether in dark mode
79
73
  /// @return {Color} Active state color
80
74
  @function derive-accent-active($base, $is-dark: false) {
81
- $l: color.channel($base, 'lightness', $space: hsl);
75
+ $l: color.channel($base, 'lightness', $space: oklch);
82
76
 
83
77
  @if $is-dark {
84
- @if $l > 85% {
78
+ @if $l > 0.85 {
85
79
  // Very light accent on dark bg: darken more for active
86
- @return color.scale($base, $lightness: -16%);
80
+ @return color.adjust($base, $lightness: -0.08, $space: oklch);
87
81
  }
88
82
  // Standard dark mode: lighten more for active
89
- @return color.scale($base, $lightness: 14%);
83
+ @return color.adjust($base, $lightness: 0.07, $space: oklch);
90
84
  } @else {
91
- @if $l < 15% {
85
+ @if $l < 0.15 {
92
86
  // Very dark accent on light bg: lighten more for active
93
- @return color.scale($base, $lightness: 28%);
87
+ @return color.adjust($base, $lightness: 0.14, $space: oklch);
94
88
  }
95
89
  // Standard light mode: darken more for active
96
- @return color.scale($base, $lightness: -18%);
90
+ @return color.adjust($base, $lightness: -0.09, $space: oklch);
97
91
  }
98
92
  }
99
93
 
100
94
  /// Derive a light variant of accent for dark mode
101
- /// Auto-lightens dark brand colors so they're visible on dark backgrounds
95
+ /// Lightens dark brand colors so the accent stands out on dark backgrounds.
96
+ /// Clamps lightness to 0.45–0.85 and chroma to 0.12 to keep it muted.
102
97
  /// @param {Color} $brand - Brand color
103
98
  /// @return {Color} Lightened accent for dark mode
104
99
  @function derive-dark-accent($brand) {
105
- @if is-dark-color($brand) {
106
- // If brand is dark, invert to light for dark mode
107
- // Use a light gray as the dark mode accent
108
- @return #f2f2f2;
109
- } @else {
110
- // If brand is already light, use it as-is or slightly adjust
111
- @return color.scale($brand, $lightness: 10%);
112
- }
100
+ $l: color.channel($brand, 'lightness', $space: oklch);
101
+ $c: color.channel($brand, 'chroma', $space: oklch);
102
+ $clamped-l: math.max(0.45, math.min($l, 0.85));
103
+ $clamped-c: math.min($c, 0.12);
104
+ @return oklch($clamped-l $clamped-c color.channel($brand, 'hue', $space: oklch));
113
105
  }
114
106
 
115
107
  // --------------------------------------------
@@ -129,9 +121,9 @@
129
121
  @for $i from 1 through 40 {
130
122
  @if wcag-contrast($adjusted, $bg) < $target {
131
123
  @if $is-dark {
132
- $adjusted: color.adjust($adjusted, $lightness: 2%);
124
+ $adjusted: color.adjust($adjusted, $lightness: 2%, $space: oklch);
133
125
  } @else {
134
- $adjusted: color.adjust($adjusted, $lightness: -2%);
126
+ $adjusted: color.adjust($adjusted, $lightness: -2%, $space: oklch);
135
127
  }
136
128
  }
137
129
  }
@@ -202,13 +194,14 @@
202
194
  /// @return {Map} Text token values
203
195
  @function derive-text($palette, $is-dark: false, $palette-name: 'stone') {
204
196
  @if $is-dark {
205
- // Dark mode: light text on dark backgrounds
197
+ // Dark mode: neutral white text on all palettes.
198
+ // Palette-tinted text (e.g. green-on-green-dark) looks muddy and reduces
199
+ // readability. Every major dark theme (Linear, Vercel, Stripe, GitHub)
200
+ // uses neutral white text regardless of background tint.
206
201
  @return (
207
- primary: get-shade($palette, 100), // Main text (light on dark)
208
- secondary: get-shade($palette, 400), // Secondary text
209
- // 90/10 mix of palette[400]/palette[500] for WCAG AAA 7:1
210
- // against palette[950] backgrounds (pure palette[500] gives ~4.1:1)
211
- tertiary: color.mix(get-shade($palette, 500), get-shade($palette, 400), 10%),
202
+ primary: #ffffff,
203
+ secondary: rgba(255, 255, 255, 0.6),
204
+ tertiary: rgba(255, 255, 255, 0.4),
212
205
  inverse: get-shade($palette, 900) // Text on light accents
213
206
  );
214
207
  }
@@ -239,16 +232,16 @@
239
232
  /// @return {Map} Border token values
240
233
  @function derive-borders($palette, $is-dark: false) {
241
234
  @if $is-dark {
242
- // Dark mode: semi-transparent borders that adapt to any surface
235
+ // Dark mode: subtle white-alpha borders
243
236
  @return (
244
237
  default: rgba(255, 255, 255, 0.08),
245
- strong: rgba(255, 255, 255, 0.14)
238
+ strong: rgba(255, 255, 255, 0.15)
246
239
  );
247
240
  } @else {
248
- // Light mode: semi-transparent borders that adapt to any surface
241
+ // Light mode: reduced opacity (ChatGPT --border-light ~5%)
249
242
  @return (
250
- default: rgba(0, 0, 0, 0.08),
251
- strong: rgba(0, 0, 0, 0.14)
243
+ default: rgba(0, 0, 0, 0.05),
244
+ strong: rgba(0, 0, 0, 0.10)
252
245
  );
253
246
  }
254
247
  }
@@ -262,18 +255,18 @@
262
255
  /// @return {Map} Shadow token values
263
256
  @function derive-shadows($is-dark: false) {
264
257
  @if $is-dark {
265
- // Dark mode: reduced shadows for AI-era aesthetic
258
+ // Dark mode: near-zero shadows (Linear uses borders, not shadows)
266
259
  @return (
267
- sm: (0 1px 2px 0 rgba(0, 0, 0, 0.2)),
268
- md: (0 2px 4px -1px rgba(0, 0, 0, 0.25), 0 1px 3px -2px rgba(0, 0, 0, 0.2)),
269
- lg: (0 8px 12px -3px rgba(0, 0, 0, 0.35), 0 3px 5px -4px rgba(0, 0, 0, 0.25))
260
+ sm: (0 1px 2px 0 rgba(0, 0, 0, 0.15)),
261
+ md: (0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 1px 3px -2px rgba(0, 0, 0, 0.15)),
262
+ lg: (0 8px 12px -3px rgba(0, 0, 0, 0.25), 0 3px 5px -4px rgba(0, 0, 0, 0.18))
270
263
  );
271
264
  } @else {
272
- // Light mode: minimal shadows ("felt but not noticed")
265
+ // Light mode: flatter aesthetic (Linear/ChatGPT near-zero shadows)
273
266
  @return (
274
- sm: (0 1px 2px 0 rgba(0, 0, 0, 0.03)),
275
- md: (0 2px 4px -1px rgba(0, 0, 0, 0.06), 0 1px 3px -2px rgba(0, 0, 0, 0.04)),
276
- lg: (0 8px 12px -3px rgba(0, 0, 0, 0.08), 0 3px 5px -4px rgba(0, 0, 0, 0.05))
267
+ sm: (0 1px 2px 0 rgba(0, 0, 0, 0.02)),
268
+ md: (0 2px 4px -1px rgba(0, 0, 0, 0.04), 0 1px 3px -2px rgba(0, 0, 0, 0.03)),
269
+ lg: (0 8px 12px -3px rgba(0, 0, 0, 0.06), 0 3px 5px -4px rgba(0, 0, 0, 0.04))
277
270
  );
278
271
  }
279
272
  }
@@ -324,7 +317,7 @@
324
317
  /// @param {Color} $color - The semantic color
325
318
  /// @return {Color} Hover state color
326
319
  @function derive-semantic-hover($color) {
327
- @return color.scale($color, $lightness: -10%);
320
+ @return color.adjust($color, $lightness: -0.05, $space: oklch);
328
321
  }
329
322
 
330
323
  // --------------------------------------------
@@ -424,9 +417,9 @@
424
417
  @for $i from 1 through 40 {
425
418
  @if wcag-contrast($adjusted, $bg) < $target {
426
419
  @if $is-dark {
427
- $adjusted: color.adjust($adjusted, $lightness: 2%);
420
+ $adjusted: color.adjust($adjusted, $lightness: 2%, $space: oklch);
428
421
  } @else {
429
- $adjusted: color.adjust($adjusted, $lightness: -2%);
422
+ $adjusted: color.adjust($adjusted, $lightness: -2%, $space: oklch);
430
423
  }
431
424
  }
432
425
  }
@@ -83,6 +83,24 @@ $palette-earth: (
83
83
  950: #0a0f08 // Very dark green-gray for dark mode bg
84
84
  );
85
85
 
86
+ // --------------------------------------------
87
+ // Fragments - Rich saturated green tones
88
+ // The Fragments brand palette — deep forest greens
89
+ // --------------------------------------------
90
+ $palette-fragments: (
91
+ 50: #f0f7f3,
92
+ 100: #dceee3,
93
+ 200: #b8ddc6,
94
+ 300: #88c5a0,
95
+ 400: #5aaa7a,
96
+ 500: #3d8f60,
97
+ 600: #2d7049,
98
+ 700: #235536,
99
+ 800: #1a3025, // Dark card surface
100
+ 900: #142318, // Dark surface
101
+ 950: #0d1f17 // Darkest bg
102
+ );
103
+
86
104
  // --------------------------------------------
87
105
  // Fire - Warm red/orange tones
88
106
  // Energy, passion feel
@@ -110,6 +128,7 @@ $palettes: (
110
128
  "sand": $palette-sand,
111
129
  "earth": $palette-earth,
112
130
  "fire": $palette-fire,
131
+ "fragments": $palette-fragments,
113
132
  "wind": $palette-stone // Legacy alias — "wind" was renamed to "stone"
114
133
  );
115
134
 
@@ -118,7 +137,7 @@ $palettes: (
118
137
  // --------------------------------------------
119
138
 
120
139
  /// Get a palette map by name
121
- /// @param {String} $name - Palette name (stone, ice, sand, earth, fire)
140
+ /// @param {String} $name - Palette name (stone, ice, sand, earth, fire, fragments)
122
141
  /// @return {Map} The palette map with shades 50-950
123
142
  @function get-palette($name) {
124
143
  @if not map.has-key($palettes, $name) {
@@ -26,7 +26,7 @@
26
26
  $fui-brand: #18181b !default;
27
27
 
28
28
  /// Neutral palette name for surfaces, text, and borders
29
- /// Options: "stone" | "ice" | "earth" | "sand" | "fire"
29
+ /// Options: "stone" | "ice" | "earth" | "sand" | "fire" | "fragments"
30
30
  /// @type String
31
31
  $fui-neutral: "stone" !default;
32
32
 
@@ -45,7 +45,7 @@ $fui-radius-style: "default" !default;
45
45
  // --------------------------------------------
46
46
  // Fail fast at compile time if invalid seed values are provided.
47
47
 
48
- $_valid-neutrals: "stone", "ice", "earth", "sand", "fire";
48
+ $_valid-neutrals: "stone", "ice", "earth", "sand", "fire", "fragments";
49
49
  @if not list.index($_valid-neutrals, $fui-neutral) {
50
50
  @error "Invalid $fui-neutral: '#{$fui-neutral}'. Must be one of: #{$_valid-neutrals}";
51
51
  }
@@ -57,13 +57,15 @@ $fui-font-mono:
57
57
  "Geist Mono", "SF Mono", SFMono-Regular, ui-monospace, "Cascadia Code", Menlo, monospace !default;
58
58
 
59
59
  // Font sizes (rem based on 14px root)
60
+ // Compressed scale: base/md = 14px (1rem). No 16px step.
61
+ // Hierarchy driven by weight, not size jumps.
60
62
  $fui-font-size-2xs: 0.714rem !default; // 10px
61
63
  $fui-font-size-xs: 0.857rem !default; // 12px
62
64
  $fui-font-size-sm: 1rem !default; // 14px (base)
63
- $fui-font-size-base: 1.143rem !default; // 16px
65
+ $fui-font-size-base: 1rem !default; // 14px (= sm, body base)
64
66
  $fui-font-size-lg: 1.286rem !default; // 18px
65
- $fui-font-size-xl: 1.714rem !default; // 24px
66
- $fui-font-size-2xl: 2.143rem !default; // 30px
67
+ $fui-font-size-xl: 1.429rem !default; // 20px
68
+ $fui-font-size-2xl: 1.714rem !default; // 24px
67
69
 
68
70
  $fui-font-weight-normal: 400 !default;
69
71
  $fui-font-weight-medium: 500 !default;
@@ -74,6 +76,10 @@ $fui-line-height-tight: 1.25 !default;
74
76
  $fui-line-height-normal: 1.5 !default;
75
77
  $fui-line-height-relaxed: 1.625 !default;
76
78
 
79
+ // Letter spacing (Linear-inspired tight tracking for headings)
80
+ $fui-letter-spacing-tight: -0.02em !default; // for xl+ headings
81
+ $fui-letter-spacing-normal: normal !default;
82
+
77
83
  // --------------------------------------------
78
84
  // Spacing (derived from density seed)
79
85
  // --------------------------------------------
@@ -105,14 +111,15 @@ $fui-radius-full: computed.$computed-radius-full !default;
105
111
  // --------------------------------------------
106
112
  // Transitions
107
113
  // --------------------------------------------
108
- $fui-transition-fast: 150ms ease !default;
109
- $fui-transition-normal: 200ms ease !default;
114
+ $fui-transition-fast: 160ms cubic-bezier(0.25, 0.46, 0.45, 0.94) !default; // Linear's easing
115
+ $fui-transition-normal: 200ms cubic-bezier(0.4, 0, 0.2, 1) !default; // ChatGPT's easing
110
116
 
111
117
  // --------------------------------------------
112
118
  // Colors - Light Mode (derived from brand/neutral seeds)
113
119
  // --------------------------------------------
114
120
  // Accent colors (derived from $fui-brand seed)
115
121
  $fui-color-accent: computed.$computed-color-accent !default;
122
+ $fui-color-on-accent: computed.$computed-color-on-accent !default;
116
123
  $fui-color-accent-hover: computed.$computed-color-accent-hover !default;
117
124
  $fui-color-accent-active: computed.$computed-color-accent-active !default;
118
125
 
@@ -174,8 +181,9 @@ $fui-scrollbar-thumb-hover: computed.$computed-scrollbar-thumb-hover !default;
174
181
  // --------------------------------------------
175
182
  // Colors - Dark Mode (derived from seeds)
176
183
  // --------------------------------------------
177
- // Accent colors (light on dark background)
184
+ // Accent colors (brand used directly in dark mode; on-accent handles text contrast)
178
185
  $fui-dark-color-accent: computed.$computed-dark-color-accent !default;
186
+ $fui-dark-color-on-accent: computed.$computed-dark-color-on-accent !default;
179
187
  $fui-dark-color-accent-hover: computed.$computed-dark-color-accent-hover !default;
180
188
  $fui-dark-color-accent-active: computed.$computed-dark-color-accent-active !default;
181
189
 
@@ -265,15 +273,15 @@ $fui-touch-lg: computed.$computed-touch-lg !default;
265
273
  $fui-target-size-min: 1.714rem !default; // 24px
266
274
  $fui-target-size-comfortable: $fui-touch-md !default;
267
275
 
268
- // Toggle/Switch (iOS-proportioned: ~1.65:1 width:height ratio)
269
- $fui-toggle-width-sm: 2.857rem !default; // 40px
270
- $fui-toggle-width-md: 3.643rem !default; // 51px (iOS standard)
271
- $fui-toggle-width-lg: 4.286rem !default; // 60px
272
- $fui-toggle-height-sm: $fui-target-size-min !default; // 24px
273
- $fui-toggle-height-md: 2.214rem !default; // 31px (iOS standard)
274
- $fui-toggle-height-lg: 2.571rem !default; // 36px
275
- $fui-toggle-thumb-sm: 1.286rem !default; // 18px
276
- $fui-toggle-thumb-md: 1.929rem !default; // 27px (iOS standard)
276
+ // Toggle/Switch (web-proportioned: ~1.8:1 width:height ratio)
277
+ $fui-toggle-width-sm: 1.857rem !default; // 26px
278
+ $fui-toggle-width-md: 2.571rem !default; // 36px
279
+ $fui-toggle-width-lg: 3.286rem !default; // 46px
280
+ $fui-toggle-height-sm: 1.143rem !default; // 16px
281
+ $fui-toggle-height-md: 1.429rem !default; // 20px
282
+ $fui-toggle-height-lg: 1.857rem !default; // 26px
283
+ $fui-toggle-thumb-sm: 0.857rem !default; // 12px
284
+ $fui-toggle-thumb-md: 1.143rem !default; // 16px
277
285
  $fui-toggle-thumb-offset: 2px !default; // Thumb inset from track edge
278
286
 
279
287
  // Badge
@@ -430,6 +438,8 @@ $fui-dark-tooltip-shadow:
430
438
  --fui-line-height-tight: #{$fui-line-height-tight};
431
439
  --fui-line-height-normal: #{$fui-line-height-normal};
432
440
  --fui-line-height-relaxed: #{$fui-line-height-relaxed};
441
+ --fui-letter-spacing-tight: #{$fui-letter-spacing-tight};
442
+ --fui-letter-spacing-normal: #{$fui-letter-spacing-normal};
433
443
 
434
444
  // Spacing (micro)
435
445
  --fui-space-px: #{$fui-space-px};
@@ -460,6 +470,7 @@ $fui-dark-tooltip-shadow:
460
470
 
461
471
  // Colors — Accent (light-dark for automatic dark mode)
462
472
  --fui-color-accent: light-dark(#{$fui-color-accent}, #{$fui-dark-color-accent});
473
+ --fui-color-on-accent: light-dark(#{$fui-color-on-accent}, #{$fui-dark-color-on-accent});
463
474
  --fui-color-accent-hover: light-dark(#{$fui-color-accent-hover}, #{$fui-dark-color-accent-hover});
464
475
  --fui-color-accent-active: light-dark(#{$fui-color-accent-active}, #{$fui-dark-color-accent-active});
465
476
 
@@ -506,7 +517,7 @@ $fui-dark-tooltip-shadow:
506
517
  --fui-border-strong: light-dark(#{$fui-border-strong}, #{$fui-dark-border-strong});
507
518
 
508
519
  // Form chrome (derived from theme surfaces + seed-driven accent)
509
- --fui-field-bg: color-mix(in srgb, var(--fui-bg-elevated) 92%, var(--fui-bg-primary));
520
+ --fui-field-bg: var(--fui-bg-tertiary);
510
521
  --fui-field-bg-disabled: color-mix(in srgb, var(--fui-bg-tertiary) 88%, var(--fui-bg-primary));
511
522
  --fui-field-border: color-mix(in srgb, var(--fui-border-strong) 78%, var(--fui-bg-primary));
512
523
  --fui-field-border-hover: color-mix(in srgb, var(--fui-text-tertiary) 58%, var(--fui-border-strong));
@@ -590,18 +601,22 @@ $fui-dark-tooltip-shadow:
590
601
  @supports (color: oklch(from red l c h)) {
591
602
  :root {
592
603
  // Accent derived from seed-brand
593
- // Light mode: brand as-is; dark mode: inverted lightness, reduced chroma
604
+ // Light mode: brand as-is. Dark mode: lightened for visibility on dark surfaces.
594
605
  --fui-color-accent: light-dark(
595
606
  var(--fui-seed-brand),
596
- oklch(from var(--fui-seed-brand) clamp(0.88, calc(1 - l + 0.1), 0.96) min(c, 0.05) h)
607
+ oklch(from var(--fui-seed-brand) clamp(0.45, l, 0.85) min(c, 0.12) h)
597
608
  );
609
+
610
+ // Auto-contrast text for elements on accent backgrounds (buttons, chips, etc.)
611
+ // Lightness > 0.6 → dark text; otherwise → white. Users can override this token.
612
+ --fui-color-on-accent: oklch(from var(--fui-color-accent) calc(0 + (1 - clamp(0, calc((l - 0.6) * 999), 1))) 0 0);
598
613
  --fui-color-accent-hover: light-dark(
599
614
  oklch(from var(--fui-seed-brand) max(0.05, calc(l - 0.06)) c h),
600
- oklch(from var(--fui-seed-brand) clamp(0.80, calc(1 - l + 0.02), 0.90) min(c, 0.05) h)
615
+ oklch(from var(--fui-seed-brand) clamp(0.52, calc(l + 0.07), 0.90) min(c, 0.12) h)
601
616
  );
602
617
  --fui-color-accent-active: light-dark(
603
618
  oklch(from var(--fui-seed-brand) max(0.05, calc(l - 0.12)) c h),
604
- oklch(from var(--fui-seed-brand) clamp(0.73, calc(1 - l - 0.05), 0.85) min(c, 0.05) h)
619
+ oklch(from var(--fui-seed-brand) clamp(0.40, calc(l - 0.05), 0.80) min(c, 0.12) h)
605
620
  );
606
621
 
607
622
  // Semantic hover derived from seed
@@ -626,23 +641,23 @@ $fui-dark-tooltip-shadow:
626
641
  );
627
642
 
628
643
  // Focus ring derived from seed-brand
629
- --fui-focus-ring-color: light-dark(
630
- var(--fui-seed-brand),
631
- oklch(from var(--fui-seed-brand) clamp(0.88, calc(1 - l + 0.1), 0.96) min(c, 0.05) h)
632
- );
644
+ --fui-focus-ring-color: var(--fui-seed-brand);
633
645
  }
634
646
 
635
647
  // Runtime-derived dark mode overrides (replaces SCSS-computed when supported)
648
+ // Dark brands are lightened so the button stands out against dark backgrounds.
649
+ // Hover goes slightly lighter, active slightly darker. on-accent handles text.
636
650
  :root[data-theme="dark"],
637
651
  :root.dark {
638
- --fui-color-accent: oklch(from var(--fui-seed-brand) clamp(0.88, calc(1 - l + 0.1), 0.96) min(c, 0.05) h);
639
- --fui-color-accent-hover: oklch(from var(--fui-seed-brand) clamp(0.80, calc(1 - l + 0.02), 0.90) min(c, 0.05) h);
640
- --fui-color-accent-active: oklch(from var(--fui-seed-brand) clamp(0.73, calc(1 - l - 0.05), 0.85) min(c, 0.05) h);
652
+ --fui-color-accent: oklch(from var(--fui-seed-brand) clamp(0.45, l, 0.85) min(c, 0.12) h);
653
+ --fui-color-on-accent: oklch(from var(--fui-color-accent) calc(0 + (1 - clamp(0, calc((l - 0.6) * 999), 1))) 0 0);
654
+ --fui-color-accent-hover: oklch(from var(--fui-seed-brand) clamp(0.52, calc(l + 0.07), 0.90) min(c, 0.12) h);
655
+ --fui-color-accent-active: oklch(from var(--fui-seed-brand) clamp(0.40, calc(l - 0.05), 0.80) min(c, 0.12) h);
641
656
  --fui-color-danger-bg: color-mix(in oklch, var(--fui-seed-danger) 15%, transparent);
642
657
  --fui-color-success-bg: color-mix(in oklch, var(--fui-seed-success) 15%, transparent);
643
658
  --fui-color-warning-bg: color-mix(in oklch, var(--fui-seed-warning) 15%, transparent);
644
659
  --fui-color-info-bg: color-mix(in oklch, var(--fui-seed-info) 15%, transparent);
645
- --fui-focus-ring-color: oklch(from var(--fui-seed-brand) clamp(0.88, calc(1 - l + 0.1), 0.96) min(c, 0.05) h);
660
+ --fui-focus-ring-color: var(--fui-seed-brand);
646
661
  }
647
662
  }
648
663
 
@@ -717,8 +732,9 @@ $fui-dark-tooltip-shadow:
717
732
 
718
733
  // Private mixin for dark mode token values
719
734
  @mixin _fui-dark-tokens {
720
- // Accent colors invert for dark mode (light on dark)
735
+ // Accent colors brand used directly; on-accent handles text contrast
721
736
  --fui-color-accent: #{$fui-dark-color-accent};
737
+ --fui-color-on-accent: #{$fui-dark-color-on-accent};
722
738
  --fui-color-accent-hover: #{$fui-dark-color-accent-hover};
723
739
  --fui-color-accent-active: #{$fui-dark-color-accent-active};
724
740
 
@@ -13,7 +13,7 @@
13
13
  // Types
14
14
  // ============================================
15
15
 
16
- export type NeutralPalette = 'stone' | 'ice' | 'sand' | 'earth' | 'fire';
16
+ export type NeutralPalette = 'stone' | 'ice' | 'sand' | 'earth' | 'fire' | 'fragments';
17
17
  export type DensityPreset = 'compact' | 'default' | 'relaxed';
18
18
  export type RadiusStyle = 'sharp' | 'subtle' | 'default' | 'rounded' | 'pill';
19
19
 
@@ -128,6 +128,21 @@ export const PALETTES: Record<NeutralPalette, PaletteShades> = {
128
128
  900: '#1c100a', // Darkened and desaturated for dark mode
129
129
  950: '#120a06', // Very dark warm gray for dark mode bg
130
130
  },
131
+ // Fragments - Rich saturated green tones (the Fragments brand palette)
132
+ // Dark shades anchor to the Fragments marketing aesthetic
133
+ fragments: {
134
+ 50: '#f0f7f3',
135
+ 100: '#dceee3',
136
+ 200: '#b8ddc6',
137
+ 300: '#88c5a0',
138
+ 400: '#5aaa7a',
139
+ 500: '#3d8f60',
140
+ 600: '#2d7049',
141
+ 700: '#235536',
142
+ 800: '#1a3025', // Dark card surface
143
+ 900: '#142318', // Dark surface
144
+ 950: '#0d1f17', // Darkest bg
145
+ },
131
146
  };
132
147
 
133
148
  // ============================================
@@ -181,6 +196,13 @@ export const PALETTE_SEMANTIC_COLORS: Record<NeutralPalette, SemanticColors> = {
181
196
  danger: '#dc2626', // Red 600 (complements orange)
182
197
  info: '#2563eb', // Blue 600
183
198
  },
199
+ // Fragments - Brand-complementary semantic colors
200
+ fragments: {
201
+ success: '#39d98a', // Vibrant green (brand signature)
202
+ warning: '#f59e0b', // Amber 500
203
+ danger: '#ef4444', // Red 500
204
+ info: '#3b82f6', // Blue 500
205
+ },
184
206
  };
185
207
 
186
208
  /**