@mindlogic-ai/logician-ui 5.0.0-alpha.0 → 5.0.0-alpha.2

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 (58) hide show
  1. package/MIGRATION-5.0.md +18 -18
  2. package/codemods/5.0/button-variants.js +1 -0
  3. package/codemods/5.0/codemods.test.ts +1 -0
  4. package/dist/components/Button/Button.legacy.d.ts.map +1 -1
  5. package/dist/components/Button/Button.legacy.js +1 -0
  6. package/dist/components/Button/Button.legacy.js.map +1 -1
  7. package/dist/components/Button/Button.legacy.mjs +1 -0
  8. package/dist/components/Button/Button.legacy.mjs.map +1 -1
  9. package/dist/components/Button/Button.styles.d.ts.map +1 -1
  10. package/dist/components/Button/Button.styles.js +18 -0
  11. package/dist/components/Button/Button.styles.js.map +1 -1
  12. package/dist/components/Button/Button.styles.mjs +18 -0
  13. package/dist/components/Button/Button.styles.mjs.map +1 -1
  14. package/dist/components/Button/Button.types.d.ts +5 -3
  15. package/dist/components/Button/Button.types.d.ts.map +1 -1
  16. package/dist/components/LineGraph/seriesColors.d.ts +2 -0
  17. package/dist/components/LineGraph/seriesColors.d.ts.map +1 -1
  18. package/dist/components/LineGraph/seriesColors.js.map +1 -1
  19. package/dist/components/LineGraph/seriesColors.mjs.map +1 -1
  20. package/dist/components/Select/Select.d.ts +5 -1
  21. package/dist/components/Select/Select.d.ts.map +1 -1
  22. package/dist/components/Select/Select.js +2 -2
  23. package/dist/components/Select/Select.js.map +1 -1
  24. package/dist/components/Select/Select.mjs +3 -3
  25. package/dist/components/Select/Select.mjs.map +1 -1
  26. package/dist/components/Select/Select.styles.d.ts +38 -0
  27. package/dist/components/Select/Select.styles.d.ts.map +1 -1
  28. package/dist/components/Select/Select.styles.js +35 -0
  29. package/dist/components/Select/Select.styles.js.map +1 -1
  30. package/dist/components/Select/Select.styles.mjs +35 -2
  31. package/dist/components/Select/Select.styles.mjs.map +1 -1
  32. package/dist/components/Select/Select.types.d.ts +10 -0
  33. package/dist/components/Select/Select.types.d.ts.map +1 -1
  34. package/dist/components/Select/SelectField.d.ts +1 -1
  35. package/dist/components/Select/SelectField.d.ts.map +1 -1
  36. package/dist/components/Select/SelectField.js +3 -2
  37. package/dist/components/Select/SelectField.js.map +1 -1
  38. package/dist/components/Select/SelectField.mjs +3 -2
  39. package/dist/components/Select/SelectField.mjs.map +1 -1
  40. package/dist/components/Select/index.d.ts +1 -0
  41. package/dist/components/Select/index.d.ts.map +1 -1
  42. package/package.json +1 -1
  43. package/src/components/Button/Button.legacy.ts +1 -0
  44. package/src/components/Button/Button.stories.tsx +24 -0
  45. package/src/components/Button/Button.styles.ts +18 -0
  46. package/src/components/Button/Button.types.ts +5 -2
  47. package/src/components/Button/Button.variants.test.tsx +9 -0
  48. package/src/components/LineGraph/seriesColors.ts +2 -0
  49. package/src/components/Select/Comparison.stories.tsx +19 -0
  50. package/src/components/Select/Select.stories.tsx +55 -0
  51. package/src/components/Select/Select.styles.ts +36 -1
  52. package/src/components/Select/Select.tsx +13 -3
  53. package/src/components/Select/Select.types.ts +11 -0
  54. package/src/components/Select/SelectField.tsx +7 -3
  55. package/src/components/Select/index.ts +1 -0
  56. package/src/theme/SemanticTokens.mdx +27 -1
  57. package/src/theme/claude.md +3 -0
  58. package/src/theme/contrast.test.ts +4 -3
@@ -1,5 +1,10 @@
1
1
  import type { ReactNode } from 'react';
2
2
  export type SelectSize = 'xs' | 'sm' | 'md' | 'lg';
3
+ /**
4
+ * `outline` (default) — the bordered trigger shared with Input and Textarea.
5
+ * `ghost` — no border at rest, shown on hover/focus; for dense settings rows.
6
+ */
7
+ export type SelectVariant = 'outline' | 'ghost';
3
8
  export interface SelectOption<T = string> {
4
9
  label: string;
5
10
  value: T;
@@ -42,6 +47,11 @@ interface FieldBaseProps<T> {
42
47
  width?: string | number;
43
48
  }
44
49
  export interface SelectFieldProps<T = string> extends FieldBaseProps<T> {
50
+ /**
51
+ * Trigger look. `ghost` drops the resting border and sizes the field to its
52
+ * content (`width` defaults to `auto` instead of `full`).
53
+ */
54
+ variant?: SelectVariant;
45
55
  /**
46
56
  * Deal the options in one after another when the list opens.
47
57
  *
@@ -1 +1 @@
1
- {"version":3,"file":"Select.types.d.ts","sourceRoot":"","sources":["../../../src/components/Select/Select.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEnD,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,MAAM;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,UAAU,cAAc,CAAC,CAAC;IACxB,wCAAwC;IACxC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3B,iCAAiC;IACjC,KAAK,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;IACjB,4CAA4C;IAC5C,YAAY,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;IACxB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC,GAAG,MAAM,CAAE,SAAQ,cAAc,CAAC,CAAC,CAAC;IACrE;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB,CAAC,CAAC,GAAG,MAAM,CAAE,SAAQ,cAAc,CAAC,CAAC,CAAC;IACvE,4DAA4D;IAC5D,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB"}
1
+ {"version":3,"file":"Select.types.d.ts","sourceRoot":"","sources":["../../../src/components/Select/Select.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEnD;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,OAAO,CAAC;AAEhD,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,MAAM;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,UAAU,cAAc,CAAC,CAAC;IACxB,wCAAwC;IACxC,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3B,iCAAiC;IACjC,KAAK,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;IACjB,4CAA4C;IAC5C,YAAY,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC;IACxB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC,GAAG,MAAM,CAAE,SAAQ,cAAc,CAAC,CAAC,CAAC;IACrE;;;OAGG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB,CAAC,CAAC,GAAG,MAAM,CAAE,SAAQ,cAAc,CAAC,CAAC,CAAC;IACvE,4DAA4D;IAC5D,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB"}
@@ -7,5 +7,5 @@ import { SelectFieldProps } from './Select.types';
7
7
  * For multi-select, grouped options or custom layouts, compose the `Select`
8
8
  * primitives directly.
9
9
  */
10
- export declare function SelectField<T = string>({ options, value, defaultValue, onChange, placeholder, label, ariaLabel, size, invalid, disabled, readOnly, required, name, width, stagger, }: SelectFieldProps<T>): import("react/jsx-runtime").JSX.Element;
10
+ export declare function SelectField<T = string>({ options, value, defaultValue, onChange, placeholder, label, ariaLabel, size, invalid, disabled, readOnly, required, name, variant, width, stagger, }: SelectFieldProps<T>): import("react/jsx-runtime").JSX.Element;
11
11
  //# sourceMappingURL=SelectField.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SelectField.d.ts","sourceRoot":"","sources":["../../../src/components/Select/SelectField.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,CAAC,GAAG,MAAM,EAAE,EACtC,OAAO,EACP,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,KAAK,EACL,SAAS,EACT,IAAW,EACX,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,IAAI,EACJ,KAAc,EACd,OAAe,GAChB,EAAE,gBAAgB,CAAC,CAAC,CAAC,2CA0ErB"}
1
+ {"version":3,"file":"SelectField.d.ts","sourceRoot":"","sources":["../../../src/components/Select/SelectField.tsx"],"names":[],"mappings":"AAOA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,CAAC,GAAG,MAAM,EAAE,EACtC,OAAO,EACP,KAAK,EACL,YAAY,EACZ,QAAQ,EACR,WAAW,EACX,KAAK,EACL,SAAS,EACT,IAAW,EACX,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,IAAI,EACJ,OAAmB,EACnB,KAA6C,EAC7C,OAAe,GAChB,EAAE,gBAAgB,CAAC,CAAC,CAAC,2CA4ErB"}
@@ -6,6 +6,7 @@ var React = require('react');
6
6
  var react = require('@chakra-ui/react');
7
7
  var staggerProps = require('../../utils/staggerProps.js');
8
8
  var Select = require('./Select.js');
9
+ var Select_styles = require('./Select.styles.js');
9
10
 
10
11
  /**
11
12
  * Single-select dropdown with the Logician design-system styling applied.
@@ -15,7 +16,7 @@ var Select = require('./Select.js');
15
16
  * For multi-select, grouped options or custom layouts, compose the `Select`
16
17
  * primitives directly.
17
18
  */
18
- function SelectField({ options, value, defaultValue, onChange, placeholder, label, ariaLabel, size = 'md', invalid, disabled, readOnly, required, name, width = 'full', stagger = false, }) {
19
+ function SelectField({ options, value, defaultValue, onChange, placeholder, label, ariaLabel, size = 'md', invalid, disabled, readOnly, required, name, variant = 'outline', width = variant === 'ghost' ? 'auto' : 'full', stagger = false, }) {
19
20
  const collection = React.useMemo(() => react.createListCollection({
20
21
  items: options,
21
22
  itemToValue: (item) => String(item.value),
@@ -26,7 +27,7 @@ function SelectField({ options, value, defaultValue, onChange, placeholder, labe
26
27
  return (jsxRuntime.jsxs(Select.Select.Root, { collection: collection, size: size, width: width, invalid: invalid, disabled: disabled, readOnly: readOnly, required: required, name: name, value: value === undefined ? undefined : toKeys(value), defaultValue: defaultValue === undefined ? undefined : toKeys(defaultValue), onValueChange: (details) => {
27
28
  const selected = details.items[0];
28
29
  onChange?.(selected ? selected.value : null);
29
- }, children: [jsxRuntime.jsx(Select.Select.HiddenSelect, {}), label != null ? (jsxRuntime.jsx(Select.Select.Label, { children: label })) : ((ariaLabel ?? placeholder) && (jsxRuntime.jsx(Select.Select.Label, { srOnly: true, children: ariaLabel ?? placeholder }))), jsxRuntime.jsxs(Select.Select.Control, { children: [jsxRuntime.jsx(Select.Select.Trigger, { children: jsxRuntime.jsx(Select.Select.ValueText, { placeholder: placeholder }) }), jsxRuntime.jsx(Select.Select.IndicatorGroup, { children: jsxRuntime.jsx(Select.Select.Indicator, {}) })] }), jsxRuntime.jsx(react.Portal, { children: jsxRuntime.jsx(Select.Select.Positioner, { children: jsxRuntime.jsx(Select.Select.Content, { children: options.map((option, index) => (jsxRuntime.jsxs(Select.Select.Item, { item: option, ...(stagger ? staggerProps.staggerProps(index) : {}), children: [jsxRuntime.jsx(Select.Select.ItemText, { children: option.label }), jsxRuntime.jsx(Select.Select.ItemIndicator, {})] }, String(option.value)))) }) }) })] }));
30
+ }, children: [jsxRuntime.jsx(Select.Select.HiddenSelect, {}), label != null ? (jsxRuntime.jsx(Select.Select.Label, { children: label })) : ((ariaLabel ?? placeholder) && (jsxRuntime.jsx(Select.Select.Label, { srOnly: true, children: ariaLabel ?? placeholder }))), jsxRuntime.jsxs(Select.Select.Control, { children: [jsxRuntime.jsx(Select.Select.Trigger, { variant: variant, children: jsxRuntime.jsx(Select.Select.ValueText, { placeholder: placeholder }) }), jsxRuntime.jsx(Select.Select.IndicatorGroup, { children: jsxRuntime.jsx(Select.Select.Indicator, {}) })] }), jsxRuntime.jsx(react.Portal, { children: jsxRuntime.jsx(Select.Select.Positioner, { children: jsxRuntime.jsx(Select.Select.Content, { css: variant === 'ghost' ? Select_styles.ghostContentStyles : undefined, children: options.map((option, index) => (jsxRuntime.jsxs(Select.Select.Item, { item: option, ...(stagger ? staggerProps.staggerProps(index) : {}), children: [jsxRuntime.jsx(Select.Select.ItemText, { children: option.label }), jsxRuntime.jsx(Select.Select.ItemIndicator, {})] }, String(option.value)))) }) }) })] }));
30
31
  }
31
32
 
32
33
  exports.SelectField = SelectField;
@@ -1 +1 @@
1
- {"version":3,"file":"SelectField.js","sources":["../../../src/components/Select/SelectField.tsx"],"sourcesContent":[null],"names":["useMemo","createListCollection","_jsxs","Select","_jsx","Portal","staggerProps"],"mappings":"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"}
1
+ {"version":3,"file":"SelectField.js","sources":["../../../src/components/Select/SelectField.tsx"],"sourcesContent":[null],"names":["useMemo","createListCollection","_jsxs","Select","_jsx","Portal","ghostContentStyles","staggerProps"],"mappings":"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"}
@@ -4,6 +4,7 @@ import { useMemo } from 'react';
4
4
  import { createListCollection, Portal } from '@chakra-ui/react';
5
5
  import { staggerProps } from '../../utils/staggerProps.mjs';
6
6
  import { Select } from './Select.mjs';
7
+ import { ghostContentStyles } from './Select.styles.mjs';
7
8
 
8
9
  /**
9
10
  * Single-select dropdown with the Logician design-system styling applied.
@@ -13,7 +14,7 @@ import { Select } from './Select.mjs';
13
14
  * For multi-select, grouped options or custom layouts, compose the `Select`
14
15
  * primitives directly.
15
16
  */
16
- function SelectField({ options, value, defaultValue, onChange, placeholder, label, ariaLabel, size = 'md', invalid, disabled, readOnly, required, name, width = 'full', stagger = false, }) {
17
+ function SelectField({ options, value, defaultValue, onChange, placeholder, label, ariaLabel, size = 'md', invalid, disabled, readOnly, required, name, variant = 'outline', width = variant === 'ghost' ? 'auto' : 'full', stagger = false, }) {
17
18
  const collection = useMemo(() => createListCollection({
18
19
  items: options,
19
20
  itemToValue: (item) => String(item.value),
@@ -24,7 +25,7 @@ function SelectField({ options, value, defaultValue, onChange, placeholder, labe
24
25
  return (jsxs(Select.Root, { collection: collection, size: size, width: width, invalid: invalid, disabled: disabled, readOnly: readOnly, required: required, name: name, value: value === undefined ? undefined : toKeys(value), defaultValue: defaultValue === undefined ? undefined : toKeys(defaultValue), onValueChange: (details) => {
25
26
  const selected = details.items[0];
26
27
  onChange?.(selected ? selected.value : null);
27
- }, children: [jsx(Select.HiddenSelect, {}), label != null ? (jsx(Select.Label, { children: label })) : ((ariaLabel ?? placeholder) && (jsx(Select.Label, { srOnly: true, children: ariaLabel ?? placeholder }))), jsxs(Select.Control, { children: [jsx(Select.Trigger, { children: jsx(Select.ValueText, { placeholder: placeholder }) }), jsx(Select.IndicatorGroup, { children: jsx(Select.Indicator, {}) })] }), jsx(Portal, { children: jsx(Select.Positioner, { children: jsx(Select.Content, { children: options.map((option, index) => (jsxs(Select.Item, { item: option, ...(stagger ? staggerProps(index) : {}), children: [jsx(Select.ItemText, { children: option.label }), jsx(Select.ItemIndicator, {})] }, String(option.value)))) }) }) })] }));
28
+ }, children: [jsx(Select.HiddenSelect, {}), label != null ? (jsx(Select.Label, { children: label })) : ((ariaLabel ?? placeholder) && (jsx(Select.Label, { srOnly: true, children: ariaLabel ?? placeholder }))), jsxs(Select.Control, { children: [jsx(Select.Trigger, { variant: variant, children: jsx(Select.ValueText, { placeholder: placeholder }) }), jsx(Select.IndicatorGroup, { children: jsx(Select.Indicator, {}) })] }), jsx(Portal, { children: jsx(Select.Positioner, { children: jsx(Select.Content, { css: variant === 'ghost' ? ghostContentStyles : undefined, children: options.map((option, index) => (jsxs(Select.Item, { item: option, ...(stagger ? staggerProps(index) : {}), children: [jsx(Select.ItemText, { children: option.label }), jsx(Select.ItemIndicator, {})] }, String(option.value)))) }) }) })] }));
28
29
  }
29
30
 
30
31
  export { SelectField };
@@ -1 +1 @@
1
- {"version":3,"file":"SelectField.mjs","sources":["../../../src/components/Select/SelectField.tsx"],"sourcesContent":[null],"names":["_jsxs","_jsx"],"mappings":"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"}
1
+ {"version":3,"file":"SelectField.mjs","sources":["../../../src/components/Select/SelectField.tsx"],"sourcesContent":[null],"names":["_jsxs","_jsx"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  export { Combobox } from './Combobox';
2
2
  export { ComboboxField } from './ComboboxField';
3
+ export type { SelectTriggerProps } from './Select';
3
4
  export { Select } from './Select';
4
5
  export type * from './Select.types';
5
6
  export { SelectField } from './SelectField';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Select/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,mBAAmB,gBAAgB,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EACL,oBAAoB,EACpB,SAAS,EACT,iBAAiB,GAClB,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/Select/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,mBAAmB,gBAAgB,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EACL,oBAAoB,EACpB,SAAS,EACT,iBAAiB,GAClB,MAAM,kBAAkB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindlogic-ai/logician-ui",
3
- "version": "5.0.0-alpha.0",
3
+ "version": "5.0.0-alpha.2",
4
4
  "description": "A comprehensive React design system built on Chakra UI",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -33,6 +33,7 @@ export type LegacyResolution = {
33
33
  const BUTTON_VARIANT_KEYS: Record<ButtonVariant, true> = {
34
34
  primary: true,
35
35
  action: true,
36
+ accent: true,
36
37
  secondary: true,
37
38
  subtle: true,
38
39
  ghost: true,
@@ -35,6 +35,7 @@ export const Basic: Story = {
35
35
  const VARIANT_NOTES: Record<ButtonVariant, string> = {
36
36
  primary: '화면의 주 행동 — BAZE 블랙',
37
37
  action: '실행 버튼 — 블랙 면 + 코랄 아이콘',
38
+ accent: '색 있는 CTA — 코랄 면 + 검정 글씨, 화면당 하나',
38
39
  secondary: '보조 행동 — 테두리 + 표면',
39
40
  subtle: '낮은 강조 — 웜 워시 (기본값)',
40
41
  ghost: '툴바·인라인 — 면/테두리 없음',
@@ -131,6 +132,29 @@ export const Action: Story = {
131
132
  ),
132
133
  };
133
134
 
135
+ /**
136
+ * `accent`: the coloured call to action — coral fill (`send.bg`), dark label
137
+ * (`send.fg`, 6.5:1). One per view: a hero or a marketing section's main CTA.
138
+ * Keep `primary` (black) for product screens, where coral is reserved for
139
+ * run/send signals.
140
+ */
141
+ export const Accent: Story = {
142
+ render: () => (
143
+ <HStack gap={4}>
144
+ <Button variant="accent" size="lg">
145
+ 입장하기
146
+ </Button>
147
+ <Button variant="accent">시작하기</Button>
148
+ <Button variant="accent" size="sm">
149
+ 시작하기
150
+ </Button>
151
+ <Button variant="accent" disabled>
152
+ 시작하기
153
+ </Button>
154
+ </HStack>
155
+ ),
156
+ };
157
+
134
158
  /**
135
159
  * All available button sizes.
136
160
  *
@@ -116,6 +116,23 @@ export const buttonVariantStyles: Record<ButtonVariant, StyleProps> = {
116
116
  },
117
117
  },
118
118
  },
119
+ /**
120
+ * Coral fill, dark label — the coloured CTA. Reuses the `send.*` tokens of
121
+ * the IconButton send button so the brand has one coral surface, and the
122
+ * contrast audit checks `send.fg` on all three fills as *text* (4.5:1).
123
+ */
124
+ accent: {
125
+ borderColor: 'send.bg',
126
+ bgColor: 'send.bg',
127
+ color: 'send.fg',
128
+ _hover: { borderColor: 'send.bgHover', bgColor: 'send.bgHover' },
129
+ _active: {
130
+ borderColor: 'send.bgActive',
131
+ bgColor: 'send.bgActive',
132
+ scale: PRESSED,
133
+ },
134
+ _disabled: { ...disabledControl },
135
+ },
119
136
  secondary: {
120
137
  borderColor: 'border.default',
121
138
  bgColor: 'bg.surface',
@@ -189,6 +206,7 @@ export const getButtonStyles = (
189
206
  export const buttonVariants: ButtonVariant[] = [
190
207
  'primary',
191
208
  'action',
209
+ 'accent',
192
210
  'secondary',
193
211
  'subtle',
194
212
  'ghost',
@@ -16,6 +16,8 @@ import type { PolymorphicProps } from '../../types/polymorphic';
16
16
  *
17
17
  * - `primary`: BAZE black fill — the one main action of a screen
18
18
  * - `action`: black "run" button whose icon is coral (`action.iconOnSolid`)
19
+ * - `accent`: coral fill with dark text (`send.*`) — the one coloured call to
20
+ * action of a marketing surface or a hero; at most one per view
19
21
  * - `secondary`: outlined surface button — cancel, alternative actions
20
22
  * - `subtle`: quiet warm-wash fill — low-emphasis actions (the default)
21
23
  * - `ghost`: no fill, no border — toolbars, inline actions
@@ -27,6 +29,7 @@ import type { PolymorphicProps } from '../../types/polymorphic';
27
29
  export type ButtonVariant =
28
30
  | 'primary'
29
31
  | 'action'
32
+ | 'accent'
30
33
  | 'secondary'
31
34
  | 'subtle'
32
35
  | 'ghost'
@@ -83,8 +86,8 @@ export type ButtonOwnProps = Omit<
83
86
  /**
84
87
  * Danger-coloured text (`danger.fg`) for a destructive action that does not
85
88
  * deserve a red fill. Applies to `secondary`, `ghost` and `subtle`; the
86
- * border and fill stay the variant's own. Ignored on `primary`, `action` and
87
- * `danger` (use `variant="danger"` for a filled destructive button).
89
+ * border and fill stay the variant's own. Ignored on `primary`, `action`,
90
+ * `accent` and `danger` (use `variant="danger"` for a filled destructive button).
88
91
  * @default false
89
92
  */
90
93
  isDestructive?: boolean;
@@ -47,6 +47,15 @@ describe('Button variants (5.0)', () => {
47
47
  expect(getButtonStyles('danger', true).color).toBe('danger.onSolid');
48
48
  });
49
49
 
50
+ it('accent is the coral fill with a dark label (send.* tokens)', () => {
51
+ const accent = getButtonStyles('accent');
52
+ expect(accent.bgColor).toBe('send.bg');
53
+ expect(accent.color).toBe('send.fg');
54
+ expect(accent._hover).toMatchObject({ bgColor: 'send.bgHover' });
55
+ expect(accent._active).toMatchObject({ bgColor: 'send.bgActive' });
56
+ expect(getButtonStyles('accent', true).color).toBe('send.fg');
57
+ });
58
+
50
59
  it('disabled is a fixed colour pair, not opacity', () => {
51
60
  expect(buttonVariantStyles.primary._disabled).toMatchObject({
52
61
  bg: 'disabled.bg',
@@ -8,6 +8,8 @@ import { SYSTEM_HUES } from '@/theme/palette.generated';
8
8
  * that carry no meaning in the system. Red, yellow and green are the danger,
9
9
  * warning and success colours, and blue is info and links, so none of them
10
10
  * may colour a series by default — a red "UV" line reads as an error.
11
+ * (Blue is reserved for data as a whole: a single-measure chart is a blue
12
+ * monochrome ramp — SemanticTokens.mdx, "Blue means data and links".)
11
13
  *
12
14
  * Every stroke clears 3:1 against the page in both modes. Cyan, mint and
13
15
  * orange are too light at their base value in light mode (≈1.9:1), so they
@@ -54,6 +54,25 @@ export const SideBySide = () => (
54
54
  <SelectField options={categoryOptions} placeholder="Select" invalid />
55
55
  <Textarea placeholder="Textarea" invalid />
56
56
  </Row>
57
+ <Row label='Select variant="ghost" — default / invalid / disabled'>
58
+ <SelectField
59
+ variant="ghost"
60
+ options={categoryOptions}
61
+ placeholder="Select"
62
+ />
63
+ <SelectField
64
+ variant="ghost"
65
+ options={categoryOptions}
66
+ placeholder="Select"
67
+ invalid
68
+ />
69
+ <SelectField
70
+ variant="ghost"
71
+ options={categoryOptions}
72
+ placeholder="Select"
73
+ disabled
74
+ />
75
+ </Row>
57
76
  <Row label="Disabled">
58
77
  <Input placeholder="Input" disabled />
59
78
  <SelectField options={categoryOptions} placeholder="Select" disabled />
@@ -2,10 +2,12 @@ import { useState } from 'react';
2
2
  import {
3
3
  Box,
4
4
  createListCollection,
5
+ Flex,
5
6
  HStack,
6
7
  Portal,
7
8
  Span,
8
9
  Stack,
10
+ Text,
9
11
  } from '@chakra-ui/react';
10
12
  import { Meta } from '@storybook/react';
11
13
 
@@ -78,6 +80,59 @@ export const Disabled = () => (
78
80
  </Box>
79
81
  );
80
82
 
83
+ /**
84
+ * `variant="ghost"` — no resting border, sized to its content. For dense
85
+ * settings rows where a column of bordered triggers outweighs the card.
86
+ * Hover shows the control border.
87
+ */
88
+ export const Ghost = () => {
89
+ const rows = [
90
+ ['크레딧 한도 변경', '관리자가 내 크레딧 한도를 바꾸면 알려드립니다.'],
91
+ ['크레딧 잔량 부족', '내 크레딧이 얼마 남지 않으면 알려드립니다.'],
92
+ ['결제 실패', '결제에 실패하거나 자동으로 환불되면 알려드립니다.'],
93
+ ];
94
+ const channelOptions = [
95
+ { label: '앱 알림', value: 'app' },
96
+ { label: '앱 알림 + 이메일', value: 'app_email' },
97
+ { label: '끄기', value: 'off' },
98
+ ];
99
+ return (
100
+ <Box
101
+ maxW="640px"
102
+ borderWidth="1px"
103
+ borderColor="border.subtle"
104
+ borderRadius="12px"
105
+ >
106
+ {rows.map(([title, description], index) => (
107
+ <Flex
108
+ key={title}
109
+ align="center"
110
+ justify="space-between"
111
+ gap={4}
112
+ px={4}
113
+ py={3}
114
+ borderTopWidth={index === 0 ? 0 : '1px'}
115
+ borderColor="border.subtle"
116
+ >
117
+ <Box>
118
+ <Text fontWeight="semibold">{title}</Text>
119
+ <Text color="fg.muted" fontSize="sm">
120
+ {description}
121
+ </Text>
122
+ </Box>
123
+ <SelectField
124
+ variant="ghost"
125
+ size="sm"
126
+ ariaLabel={title}
127
+ options={channelOptions}
128
+ defaultValue="app"
129
+ />
130
+ </Flex>
131
+ ))}
132
+ </Box>
133
+ );
134
+ };
135
+
81
136
  export const ManyOptions = () => {
82
137
  const manyOptions = Array.from({ length: 100 }, (_, i) => ({
83
138
  label: `Option ${i + 1}`,
@@ -8,7 +8,7 @@
8
8
  * update these values to match.
9
9
  */
10
10
 
11
- import { disabledControl } from '@/utils/disabledControl';
11
+ import { disabledControl, disabledText } from '@/utils/disabledControl';
12
12
 
13
13
  const focusVars = {
14
14
  '--focus-color': 'var(--chakra-colors-primary-main)',
@@ -47,6 +47,31 @@ export const triggerStyles = {
47
47
  },
48
48
  };
49
49
 
50
+ /**
51
+ * `variant="ghost"` delta on top of `triggerStyles`, for dense rows (settings
52
+ * lists) where a column of bordered triggers outweighs the card around them.
53
+ * No border at rest; hover brings back `border.control`, focus/open keep the
54
+ * normal ring, and an invalid field keeps its red border at rest so an error
55
+ * is never hidden. The chevron identifies the control (`fg.muted`, well over
56
+ * the 3:1 non-text floor), which is what lets the resting border go.
57
+ */
58
+ export const ghostTriggerStyles = {
59
+ bg: 'transparent',
60
+ borderColor: 'transparent',
61
+ // Content-sized: Chakra caps the value at `maxW: 80%` of the trigger and
62
+ // overlays the chevron, which is circular once the trigger sizes to the
63
+ // value — it truncates to "앱 …". Lift the cap and reserve the chevron's
64
+ // room (indicator group = icon + padding on both sides) instead.
65
+ pe: 'calc(var(--select-trigger-padding-x) * 2 + 1rem)',
66
+ '& [data-part=value-text]': { maxW: 'none', whiteSpace: 'nowrap' },
67
+ _hover: { borderColor: 'border.control' },
68
+ _disabled: {
69
+ ...disabledText,
70
+ cursor: 'not-allowed',
71
+ fontWeight: 'semibold',
72
+ },
73
+ };
74
+
50
75
  /** Combobox text input — the typeable control. */
51
76
  export const inputStyles = {
52
77
  ...fieldBase,
@@ -62,6 +87,16 @@ export const contentStyles = {
62
87
  padding: '4px',
63
88
  };
64
89
 
90
+ /**
91
+ * Dropdown panel for a ghost trigger — at least as wide as the trigger, but
92
+ * grown to the longest option, since a content-sized trigger is often
93
+ * narrower than the options it opens.
94
+ */
95
+ export const ghostContentStyles = {
96
+ width: 'max-content',
97
+ minW: 'var(--reference-width)',
98
+ };
99
+
65
100
  /** Individual option for both Select and Combobox. */
66
101
  export const itemStyles = {
67
102
  borderRadius: '4px',
@@ -5,18 +5,28 @@ import { mergeCss } from '@/utils/mergeCss';
5
5
 
6
6
  import {
7
7
  contentStyles,
8
+ ghostTriggerStyles,
8
9
  indicatorStyles,
9
10
  itemStyles,
10
11
  triggerStyles,
11
12
  } from './Select.styles';
13
+ import type { SelectVariant } from './Select.types';
12
14
 
13
- const Trigger = forwardRef<HTMLButtonElement, ChakraSelect.TriggerProps>(
14
- function SelectTrigger({ css, ...props }, ref) {
15
+ export interface SelectTriggerProps extends ChakraSelect.TriggerProps {
16
+ variant?: SelectVariant;
17
+ }
18
+
19
+ const Trigger = forwardRef<HTMLButtonElement, SelectTriggerProps>(
20
+ function SelectTrigger({ css, variant = 'outline', ...props }, ref) {
15
21
  return (
16
22
  <ChakraSelect.Trigger
17
23
  ref={ref}
18
24
  {...props}
19
- css={mergeCss(triggerStyles, css)}
25
+ css={mergeCss(
26
+ triggerStyles,
27
+ variant === 'ghost' && ghostTriggerStyles,
28
+ css
29
+ )}
20
30
  />
21
31
  );
22
32
  }
@@ -2,6 +2,12 @@ import type { ReactNode } from 'react';
2
2
 
3
3
  export type SelectSize = 'xs' | 'sm' | 'md' | 'lg';
4
4
 
5
+ /**
6
+ * `outline` (default) — the bordered trigger shared with Input and Textarea.
7
+ * `ghost` — no border at rest, shown on hover/focus; for dense settings rows.
8
+ */
9
+ export type SelectVariant = 'outline' | 'ghost';
10
+
5
11
  export interface SelectOption<T = string> {
6
12
  label: string;
7
13
  value: T;
@@ -46,6 +52,11 @@ interface FieldBaseProps<T> {
46
52
  }
47
53
 
48
54
  export interface SelectFieldProps<T = string> extends FieldBaseProps<T> {
55
+ /**
56
+ * Trigger look. `ghost` drops the resting border and sizes the field to its
57
+ * content (`width` defaults to `auto` instead of `full`).
58
+ */
59
+ variant?: SelectVariant;
49
60
  /**
50
61
  * Deal the options in one after another when the list opens.
51
62
  *
@@ -4,6 +4,7 @@ import { createListCollection, Portal } from '@chakra-ui/react';
4
4
  import { staggerProps } from '@/utils/staggerProps';
5
5
 
6
6
  import { Select } from './Select';
7
+ import { ghostContentStyles } from './Select.styles';
7
8
  import { SelectFieldProps } from './Select.types';
8
9
 
9
10
  /**
@@ -28,7 +29,8 @@ export function SelectField<T = string>({
28
29
  readOnly,
29
30
  required,
30
31
  name,
31
- width = 'full',
32
+ variant = 'outline',
33
+ width = variant === 'ghost' ? 'auto' : 'full',
32
34
  stagger = false,
33
35
  }: SelectFieldProps<T>) {
34
36
  const collection = useMemo(
@@ -79,7 +81,7 @@ export function SelectField<T = string>({
79
81
  )
80
82
  )}
81
83
  <Select.Control>
82
- <Select.Trigger>
84
+ <Select.Trigger variant={variant}>
83
85
  <Select.ValueText placeholder={placeholder} />
84
86
  </Select.Trigger>
85
87
  <Select.IndicatorGroup>
@@ -88,7 +90,9 @@ export function SelectField<T = string>({
88
90
  </Select.Control>
89
91
  <Portal>
90
92
  <Select.Positioner>
91
- <Select.Content>
93
+ <Select.Content
94
+ css={variant === 'ghost' ? ghostContentStyles : undefined}
95
+ >
92
96
  {options.map((option, index) => (
93
97
  <Select.Item
94
98
  item={option}
@@ -1,5 +1,6 @@
1
1
  export { Combobox } from './Combobox';
2
2
  export { ComboboxField } from './ComboboxField';
3
+ export type { SelectTriggerProps } from './Select';
3
4
  export { Select } from './Select';
4
5
  export type * from './Select.types';
5
6
  export { SelectField } from './SelectField';
@@ -40,7 +40,7 @@ createLogicianTheme({ neutral: 'slate' }) // cool gray — app code unchanged
40
40
  | Beige accent: text, icons, borders on those (`secondary` identity) | `secondary.fg` / `.fgStrong` / `.border` |
41
41
  | Beige tint (tint/accent only — **not a component background**: reads peach as a fill) | `secondary.surface` / `.surfaceStrong` |
42
42
  | Run button (black + coral icon) · small coral marks | `action.bg` / `.fg` / `.iconOnSolid` · `action.mark` |
43
- | Send button | `send.bg` / `.bgHover` / `.bgActive` / `.fg` |
43
+ | Send button, Button `accent` | `send.bg` / `.bgHover` / `.bgActive` / `.fg` |
44
44
  | Unread count | `count.bg` / `count.fg` |
45
45
  | Disabled (no opacity) | `disabled.bg` / `disabled.fg` |
46
46
  | Status | `danger` · `success` · `warning` · `info` → `.bg` `.border` `.fg` `.solid` `.onSolid` |
@@ -50,6 +50,32 @@ Emphasis is weight, not colour: headings use `fg.default`, never `primary.*`.
50
50
  Status colours mean status — don't borrow `success`/`warning` for decoration;
51
51
  that is what `category.*` is for.
52
52
 
53
+ ## Blue means data and links
54
+
55
+ Blue is the one hue BAZE gives a meaning outside status: **data and links**.
56
+ Use it for both, and for nothing decorative.
57
+
58
+ - **Links** — `fg.link` / `fg.linkHover` (blue ink, #48607A / #AFC7E1), in chat
59
+ messages, markdown and app copy alike. Never ink-black or coral for a link.
60
+ - **Data visualisation** — usage/share bars, single-measure bar and line charts,
61
+ maps, heatmaps, progress-by-share: a **blue monochrome ramp**, never a
62
+ rainbow and never a grey ramp. Steps differ in lightness only (the saturated
63
+ `blue.base` is left out); the largest value gets the strongest step.
64
+
65
+ | Step | Light | Dark |
66
+ | --- | --- | --- |
67
+ | 1 (largest) | `#48607A` (`blue.ink`) · white label | `#AFC7E1` (`blue.inkDark`) · ink label |
68
+ | 2 | `#7492B3` · ink label | `#7492B3` · ink label |
69
+ | 3 | `#AFC7E1` · ink label | `#48607A` · white label |
70
+ | 4 | `#DFEBF9` (`blue.surface`) · ink label | `#232F3D` (`blue.surfaceDark`) · white label |
71
+
72
+ Pick each label by measured contrast (≥ 4.5:1), not by step number.
73
+ A chart that must tell several *categories* apart (multi-series lines) keeps
74
+ `CHART_SERIES_HUES` in `LineGraph/seriesColors.ts`, which leaves blue out so
75
+ no series reads as a link or an info state.
76
+ `info` is also blue; status always comes with an icon and words, so a blue
77
+ chart is never mistaken for an info notice.
78
+
53
79
  ## Modes
54
80
 
55
81
  Every role flips with the colour mode (`_light` / `_dark`) and has a
@@ -249,6 +249,9 @@ follow the scheme and high contrast. Base font size is 14px.
249
249
  4. Beige (`secondary.surface*`) is for small surfaces (bubbles, soft
250
250
  badges/tags, the modal header band), never rows or panels.
251
251
  5. Disabled = `disabled.*`, not opacity. Focus = `focusRing`.
252
+ 6. Blue means data and links: links are `fg.link`; data visualisation
253
+ (usage bars, single-measure charts, maps) is a blue monochrome ramp, never a
254
+ rainbow or grey ramp. Values: SemanticTokens.mdx → "Blue means data and links".
252
255
 
253
256
  ## Extending the Theme
254
257
 
@@ -77,9 +77,10 @@ const PAIRS: [string, string, Kind][] = [
77
77
  ['action-iconOnSolid', 'action-bgHover', 'nontext'],
78
78
  ['action-fg', 'action-bgActive', 'text'],
79
79
  ['action-iconOnSolid', 'action-bgActive', 'nontext'],
80
- ['send-fg', 'send-bg', 'nontext'],
81
- ['send-fg', 'send-bgHover', 'nontext'],
82
- ['send-fg', 'send-bgActive', 'nontext'],
80
+ // `text`, not `nontext`: Button `accent` puts a label on these fills.
81
+ ['send-fg', 'send-bg', 'text'],
82
+ ['send-fg', 'send-bgHover', 'text'],
83
+ ['send-fg', 'send-bgActive', 'text'],
83
84
  ['danger-fg', 'danger-bg', 'text'],
84
85
  ['danger-onSolid', 'danger-solid', 'text'],
85
86
  ['success-fg', 'success-bg', 'text'],