@nurix/ui-component-library 1.1.4-stage.117 → 1.1.4-stage.119

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1408,6 +1408,7 @@ declare const DEFAULT_THEME: {
1408
1408
  readonly borderBrand: "#1d4885";
1409
1409
  readonly borderDestructive: "#b91c1c";
1410
1410
  readonly borderInput: "#e5e5e5";
1411
+ readonly borderInputHover: "#dadada";
1411
1412
  readonly hovOpacityPrimary: "#0a0a0a0d";
1412
1413
  readonly hovOpacitySecondary: "#0a0a0a0d";
1413
1414
  readonly hovSolidPrimary: "#fafafa";
@@ -1494,6 +1495,7 @@ declare const DEFAULT_THEME: {
1494
1495
  readonly borderBrand: "#bfdbfe";
1495
1496
  readonly borderDestructive: "#f87171";
1496
1497
  readonly borderInput: "#262626";
1498
+ readonly borderInputHover: "#404040";
1497
1499
  readonly hovOpacityPrimary: "#ffffff0d";
1498
1500
  readonly hovOpacitySecondary: "#ffffff1a";
1499
1501
  readonly hovSolidPrimary: "#262626";
@@ -1588,6 +1590,10 @@ interface DialogProps {
1588
1590
  showCancel?: boolean;
1589
1591
  onCancel?: () => void;
1590
1592
  onConfirm?: () => void;
1593
+ /** @default "primary" */
1594
+ confirmVariant?: ButtonVariant;
1595
+ /** Disable the confirm button. */
1596
+ confirmDisabled?: boolean;
1591
1597
  className?: string;
1592
1598
  }
1593
1599
  interface DialogIconProps {
@@ -1615,6 +1621,10 @@ interface DialogFooterProps {
1615
1621
  showCancel?: boolean;
1616
1622
  onCancel?: () => void;
1617
1623
  onConfirm?: () => void;
1624
+ /** @default "primary" */
1625
+ confirmVariant?: ButtonVariant;
1626
+ /** Disable the confirm button. */
1627
+ confirmDisabled?: boolean;
1618
1628
  showDivider?: boolean;
1619
1629
  className?: string;
1620
1630
  }
package/dist/index.d.ts CHANGED
@@ -1408,6 +1408,7 @@ declare const DEFAULT_THEME: {
1408
1408
  readonly borderBrand: "#1d4885";
1409
1409
  readonly borderDestructive: "#b91c1c";
1410
1410
  readonly borderInput: "#e5e5e5";
1411
+ readonly borderInputHover: "#dadada";
1411
1412
  readonly hovOpacityPrimary: "#0a0a0a0d";
1412
1413
  readonly hovOpacitySecondary: "#0a0a0a0d";
1413
1414
  readonly hovSolidPrimary: "#fafafa";
@@ -1494,6 +1495,7 @@ declare const DEFAULT_THEME: {
1494
1495
  readonly borderBrand: "#bfdbfe";
1495
1496
  readonly borderDestructive: "#f87171";
1496
1497
  readonly borderInput: "#262626";
1498
+ readonly borderInputHover: "#404040";
1497
1499
  readonly hovOpacityPrimary: "#ffffff0d";
1498
1500
  readonly hovOpacitySecondary: "#ffffff1a";
1499
1501
  readonly hovSolidPrimary: "#262626";
@@ -1588,6 +1590,10 @@ interface DialogProps {
1588
1590
  showCancel?: boolean;
1589
1591
  onCancel?: () => void;
1590
1592
  onConfirm?: () => void;
1593
+ /** @default "primary" */
1594
+ confirmVariant?: ButtonVariant;
1595
+ /** Disable the confirm button. */
1596
+ confirmDisabled?: boolean;
1591
1597
  className?: string;
1592
1598
  }
1593
1599
  interface DialogIconProps {
@@ -1615,6 +1621,10 @@ interface DialogFooterProps {
1615
1621
  showCancel?: boolean;
1616
1622
  onCancel?: () => void;
1617
1623
  onConfirm?: () => void;
1624
+ /** @default "primary" */
1625
+ confirmVariant?: ButtonVariant;
1626
+ /** Disable the confirm button. */
1627
+ confirmDisabled?: boolean;
1618
1628
  showDivider?: boolean;
1619
1629
  className?: string;
1620
1630
  }
package/dist/index.js CHANGED
@@ -6974,6 +6974,7 @@ var DEFAULT_THEME = {
6974
6974
  borderBrand: "#1d4885",
6975
6975
  borderDestructive: "#b91c1c",
6976
6976
  borderInput: "#e5e5e5",
6977
+ borderInputHover: "#dadada",
6977
6978
  // Interaction tokens (Figma: color > interaction)
6978
6979
  hovOpacityPrimary: "#0a0a0a0d",
6979
6980
  hovOpacitySecondary: "#0a0a0a0d",
@@ -7065,6 +7066,7 @@ var DEFAULT_THEME = {
7065
7066
  borderBrand: "#bfdbfe",
7066
7067
  borderDestructive: "#f87171",
7067
7068
  borderInput: "#262626",
7069
+ borderInputHover: "#404040",
7068
7070
  // Interaction tokens (Figma: color > interaction)
7069
7071
  hovOpacityPrimary: "#ffffff0d",
7070
7072
  hovOpacitySecondary: "#ffffff1a",
@@ -7305,10 +7307,17 @@ var INPUT_TOKENS = {
7305
7307
  // Borderless: used in table-like contexts (key-value editor). No visible
7306
7308
  // border at rest. Focus reveals the brand border. No hover/pressed overlay.
7307
7309
  fieldBgBorderless: "bg-transparent",
7308
- // Base border + focus-within treatment.
7309
- // Inputs do NOT paint a fill on hover (matches Figma — the field reads as
7310
- // a static surface; only focus changes the border colour).
7310
+ // Base border + focus-within treatment. Hover darkens the border (Figma
7311
+ // hover variant: #dadada). focus-within wins over hover via pseudo-class
7312
+ // ordering, so focus colour still applies when the field is hovered.
7311
7313
  fieldBase: "border border-token-input focus-within:border-token-brand disabled:pointer-events-none transition-colors",
7314
+ // Hover-only border darkening for the active (non-invalid, non-disabled)
7315
+ // state. Applied conditionally by the component so it does not override
7316
+ // the destructive border on invalid fields. The `:not(:focus-within)`
7317
+ // guard is required because Tailwind emits `:hover` rules AFTER
7318
+ // `:focus-within` — without it, hovering a focused field would replace
7319
+ // the brand border with the hover border.
7320
+ fieldHover: "[&:not(:focus-within)]:hover:border-token-input-hover",
7312
7321
  // Borderless base — transparent resting border, brand border only on focus.
7313
7322
  // No hover/pressed overlay (these cells sit inside a larger interactive row).
7314
7323
  // Layout-shift safe: border width stays 1px across states (color-only swap).
@@ -7359,6 +7368,7 @@ var Input = React2.forwardRef(
7359
7368
  }, ref) => {
7360
7369
  const isDisabled = forceState === "disabled" || disabled;
7361
7370
  const isFocussed = forceState === "focussed";
7371
+ const isInvalid = invalid || supportingTextType === "error";
7362
7372
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
7363
7373
  "div",
7364
7374
  {
@@ -7377,9 +7387,10 @@ var Input = React2.forwardRef(
7377
7387
  inputSize === "sm" ? INPUT_TOKENS.fieldSizeSm : INPUT_TOKENS.fieldSizeMd,
7378
7388
  inputVariant === "borderless" ? INPUT_TOKENS.fieldBgBorderless : inputVariant === "white" ? INPUT_TOKENS.fieldBgWhite : INPUT_TOKENS.fieldBgGrey,
7379
7389
  inputVariant === "borderless" ? INPUT_TOKENS.fieldBaseBorderless : INPUT_TOKENS.fieldBase,
7390
+ inputVariant !== "borderless" && !isDisabled && !isInvalid && INPUT_TOKENS.fieldHover,
7380
7391
  isDisabled && INPUT_TOKENS.fieldDisabled,
7381
7392
  isFocussed && "border-token-brand",
7382
- (invalid || supportingTextType === "error") && INPUT_TOKENS.fieldInvalid
7393
+ isInvalid && INPUT_TOKENS.fieldInvalid
7383
7394
  ),
7384
7395
  style: {
7385
7396
  borderRadius: INPUT_TOKENS.radius[input_border_radius],
@@ -7523,7 +7534,12 @@ var INPUT_GROUP_TOKENS = {
7523
7534
  field: "flex h-[var(--input-height,40px)] w-full items-center gap-2 overflow-hidden px-[var(--input-padding-horizontal,12px)] py-[10px] text-sm shadow-none",
7524
7535
  fieldBgGrey: "bg-token-white",
7525
7536
  fieldBgWhite: "bg-token-white",
7526
- fieldBase: "border border-token-light focus-within:border-token-brand disabled:pointer-events-none",
7537
+ fieldBase: "border border-token-light focus-within:border-token-brand disabled:pointer-events-none transition-colors",
7538
+ // Hover-only border darkening (Figma hover variant: #dadada). Applied
7539
+ // conditionally by the component so it does not override invalid/disabled.
7540
+ // The `:not(:focus-within)` guard prevents the hover border from beating
7541
+ // the brand focus border (Tailwind emits `:hover` after `:focus-within`).
7542
+ fieldHover: "[&:not(:focus-within)]:hover:border-token-input-hover",
7527
7543
  fieldInvalid: "border-token-destructive focus-within:border-token-destructive",
7528
7544
  fieldDisabled: "border-token-xlight",
7529
7545
  // Inner input area
@@ -7532,7 +7548,8 @@ var INPUT_GROUP_TOKENS = {
7532
7548
  segment: "shrink-0 flex items-center text-xs font-normal text-fg-placeholder px-3",
7533
7549
  segmentDivider: "border-token-light",
7534
7550
  // Textarea
7535
- textareaField: "w-full bg-token-white py-[10px] px-[var(--input-padding-horizontal,12px)] text-sm border border-token-light focus-visible:outline-none focus-visible:border-token-brand disabled:pointer-events-none shadow-none",
7551
+ textareaField: "w-full bg-token-white py-[10px] px-[var(--input-padding-horizontal,12px)] text-sm border border-token-light focus-visible:outline-none focus-visible:border-token-brand disabled:pointer-events-none shadow-none transition-colors",
7552
+ textareaHover: "[&:not(:focus-within)]:hover:border-token-input-hover",
7536
7553
  textareaInvalid: "border-token-destructive focus-visible:border-token-destructive",
7537
7554
  textareaDisabled: "border-token-xlight",
7538
7555
  radius: {
@@ -7582,6 +7599,7 @@ var InputGroup = React4.forwardRef(
7582
7599
  const isDisabled = forceState === "disabled" || disabled;
7583
7600
  const isFocussed = forceState === "focussed";
7584
7601
  const isActive = forceState === "active";
7602
+ const isInvalid = !!invalid;
7585
7603
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
7586
7604
  "div",
7587
7605
  {
@@ -7596,9 +7614,10 @@ var InputGroup = React4.forwardRef(
7596
7614
  INPUT_GROUP_TOKENS.field,
7597
7615
  inputVariant === "white" ? INPUT_GROUP_TOKENS.fieldBgWhite : INPUT_GROUP_TOKENS.fieldBgGrey,
7598
7616
  INPUT_GROUP_TOKENS.fieldBase,
7617
+ !isDisabled && !isInvalid && INPUT_GROUP_TOKENS.fieldHover,
7599
7618
  isDisabled && INPUT_GROUP_TOKENS.fieldDisabled,
7600
7619
  isFocussed && "border-token-brand",
7601
- invalid && INPUT_GROUP_TOKENS.fieldInvalid
7620
+ isInvalid && INPUT_GROUP_TOKENS.fieldInvalid
7602
7621
  ),
7603
7622
  style: {
7604
7623
  borderRadius: INPUT_GROUP_TOKENS.radius[input_border_radius],
@@ -7691,16 +7710,20 @@ var TEXTAREA_TOKENS = {
7691
7710
  labelRow: "flex items-center gap-[2px] text-xs font-medium leading-4 tracking-normal text-fg-black",
7692
7711
  mandatory: "text-fg-destructive",
7693
7712
  // Base shared field layout/typography.
7694
- // `relative` + `overflow-hidden` so the `before:` overlay respects the
7695
- // field's rounded corners via [border-radius:inherit].
7696
7713
  field: "relative flex min-h-[80px] w-full flex-col overflow-hidden px-[var(--input-padding-horizontal,12px)] py-[10px] text-sm shadow-none",
7697
7714
  fieldBgGrey: "bg-token-white",
7698
7715
  fieldBgWhite: "bg-token-white",
7699
- // Base border + focus treatment.
7700
- // Hover/pressed are painted via a pseudo-element overlay (see fieldBase) to avoid layout shift.
7716
+ // Base border + focus treatment. Hover darkens the border (Figma hover
7717
+ // variant: #dadada). focus-within wins over hover via pseudo-class
7718
+ // ordering, so focus colour still applies when the field is hovered.
7701
7719
  // Focus ring uses outline (not ring) so it doesn't contribute to box size.
7702
- fieldBase: "border border-token-input focus-within:border-token-brand disabled:pointer-events-none before:pointer-events-none before:absolute before:inset-0 before:content-[''] before:[border-radius:inherit] before:transition-colors before:bg-transparent hover:before:bg-interaction-hov-opacity-primary active:before:bg-interaction-pressed-opacity-primary [&>*]:relative [&>*]:z-10",
7703
- fieldDisabled: "border-token-xlight hover:before:bg-transparent active:before:bg-transparent",
7720
+ fieldBase: "border border-token-input focus-within:border-token-brand disabled:pointer-events-none transition-colors",
7721
+ // Hover-only border darkening. Applied conditionally by the component so
7722
+ // it does not override invalid/disabled borders. The `:not(:focus-within)`
7723
+ // guard keeps the brand focus border visible when a focused field is
7724
+ // hovered (Tailwind emits `:hover` after `:focus-within`).
7725
+ fieldHover: "[&:not(:focus-within)]:hover:border-token-input-hover",
7726
+ fieldDisabled: "border-token-xlight",
7704
7727
  fieldInvalid: "border-token-destructive focus-within:border-token-destructive",
7705
7728
  // Textarea itself.
7706
7729
  // `selection:` applies styles to the user's text selection (::selection):
@@ -7745,6 +7768,7 @@ var Textarea = React5.forwardRef(
7745
7768
  }, ref) => {
7746
7769
  const isDisabled = forceState === "disabled" || disabled;
7747
7770
  const isFocussed = forceState === "focussed";
7771
+ const isInvalid = invalid || supportingTextType === "error";
7748
7772
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
7749
7773
  "div",
7750
7774
  {
@@ -7762,9 +7786,10 @@ var Textarea = React5.forwardRef(
7762
7786
  TEXTAREA_TOKENS.field,
7763
7787
  inputVariant === "white" ? TEXTAREA_TOKENS.fieldBgWhite : TEXTAREA_TOKENS.fieldBgGrey,
7764
7788
  TEXTAREA_TOKENS.fieldBase,
7789
+ !isDisabled && !isInvalid && TEXTAREA_TOKENS.fieldHover,
7765
7790
  isDisabled && TEXTAREA_TOKENS.fieldDisabled,
7766
7791
  isFocussed && "border-token-brand outline outline-2 outline-token-brand outline-offset-2",
7767
- (invalid || supportingTextType === "error") && TEXTAREA_TOKENS.fieldInvalid
7792
+ isInvalid && TEXTAREA_TOKENS.fieldInvalid
7768
7793
  ),
7769
7794
  style: {
7770
7795
  borderRadius: TEXTAREA_TOKENS.radius[input_border_radius],
@@ -8879,6 +8904,7 @@ var UI_COLOR_KEY_TO_CSS_VAR = {
8879
8904
  borderBrand: "--border-brand",
8880
8905
  borderDestructive: "--border-destructive",
8881
8906
  borderInput: "--border-input",
8907
+ borderInputHover: "--border-input-hover",
8882
8908
  // Interaction tokens (Figma: color > interaction)
8883
8909
  hovOpacityPrimary: "--hov-opacity-primary",
8884
8910
  hovOpacitySecondary: "--hov-opacity-secondary",
@@ -9249,8 +9275,12 @@ var import_lucide_react6 = require("lucide-react");
9249
9275
  var SELECT_TOKENS = {
9250
9276
  // Main select container
9251
9277
  container: {
9252
- // Figma: trigger uses rounded-xl (12px), border visible, padding px-12 py-10
9253
- default: "rounded-[var(--rounded-xl,12px)] border border-token-input flex items-center justify-between gap-2 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none data-[state=open]:border-token-brand",
9278
+ // Figma: trigger uses rounded-xl (12px), border visible, padding px-12 py-10.
9279
+ // Hover darkens the border to match Figma (#dadada). The hover is scoped
9280
+ // via `[&:not([data-state=open])]` so the brand border wins when the
9281
+ // dropdown is open (Tailwind emits `data-[*]` selectors before `:hover`,
9282
+ // so without the guard hover would override the open-state border).
9283
+ default: "rounded-[var(--rounded-xl,12px)] border border-token-input flex items-center justify-between gap-2 transition-colors [&:not([data-state=open])]:hover:border-token-input-hover disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:border-token-input focus-visible:outline-none data-[state=open]:border-token-brand",
9254
9284
  // bg variants: default = input (grey), white = card
9255
9285
  bgDefault: "bg-token-white",
9256
9286
  bgWhite: "bg-token-white",
@@ -10519,7 +10549,7 @@ var DIALOG_TOKENS = {
10519
10549
  },
10520
10550
  header: {
10521
10551
  wrapper: "flex flex-col shrink-0",
10522
- row: "flex items-center gap-[10px] px-6 py-6",
10552
+ row: "flex items-center gap-[10px] px-6 pt-6 pb-2",
10523
10553
  title: "flex-1 text-lg font-semibold leading-7 text-fg-black",
10524
10554
  closeBtn: "size-9 flex items-center justify-center rounded-md text-fg-black hover:bg-token-grey transition-colors cursor-pointer",
10525
10555
  divider: "h-px bg-border-token-light"
@@ -10554,6 +10584,8 @@ var Dialog = React29.forwardRef(
10554
10584
  showCancel = true,
10555
10585
  onCancel,
10556
10586
  onConfirm,
10587
+ confirmVariant = "primary",
10588
+ confirmDisabled = false,
10557
10589
  className
10558
10590
  }, ref) => {
10559
10591
  const close = React29.useCallback(() => onOpenChange(false), [onOpenChange]);
@@ -10626,9 +10658,10 @@ var Dialog = React29.forwardRef(
10626
10658
  /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
10627
10659
  Button,
10628
10660
  {
10629
- variant: "primary",
10661
+ variant: confirmVariant,
10630
10662
  button_border_radius: "rounded",
10631
10663
  onClick: onConfirm,
10664
+ disabled: confirmDisabled,
10632
10665
  children: confirmLabel
10633
10666
  }
10634
10667
  )
@@ -10688,6 +10721,8 @@ var DialogFooter = React29.forwardRef(
10688
10721
  showCancel = true,
10689
10722
  onCancel,
10690
10723
  onConfirm,
10724
+ confirmVariant = "primary",
10725
+ confirmDisabled = false,
10691
10726
  showDivider = false,
10692
10727
  className
10693
10728
  }, ref) => {
@@ -10707,9 +10742,10 @@ var DialogFooter = React29.forwardRef(
10707
10742
  /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
10708
10743
  Button,
10709
10744
  {
10710
- variant: "primary",
10745
+ variant: confirmVariant,
10711
10746
  button_border_radius: "rounded",
10712
10747
  onClick: onConfirm,
10748
+ disabled: confirmDisabled,
10713
10749
  children: confirmLabel
10714
10750
  }
10715
10751
  )
package/dist/index.mjs CHANGED
@@ -6870,6 +6870,7 @@ var DEFAULT_THEME = {
6870
6870
  borderBrand: "#1d4885",
6871
6871
  borderDestructive: "#b91c1c",
6872
6872
  borderInput: "#e5e5e5",
6873
+ borderInputHover: "#dadada",
6873
6874
  // Interaction tokens (Figma: color > interaction)
6874
6875
  hovOpacityPrimary: "#0a0a0a0d",
6875
6876
  hovOpacitySecondary: "#0a0a0a0d",
@@ -6961,6 +6962,7 @@ var DEFAULT_THEME = {
6961
6962
  borderBrand: "#bfdbfe",
6962
6963
  borderDestructive: "#f87171",
6963
6964
  borderInput: "#262626",
6965
+ borderInputHover: "#404040",
6964
6966
  // Interaction tokens (Figma: color > interaction)
6965
6967
  hovOpacityPrimary: "#ffffff0d",
6966
6968
  hovOpacitySecondary: "#ffffff1a",
@@ -7201,10 +7203,17 @@ var INPUT_TOKENS = {
7201
7203
  // Borderless: used in table-like contexts (key-value editor). No visible
7202
7204
  // border at rest. Focus reveals the brand border. No hover/pressed overlay.
7203
7205
  fieldBgBorderless: "bg-transparent",
7204
- // Base border + focus-within treatment.
7205
- // Inputs do NOT paint a fill on hover (matches Figma — the field reads as
7206
- // a static surface; only focus changes the border colour).
7206
+ // Base border + focus-within treatment. Hover darkens the border (Figma
7207
+ // hover variant: #dadada). focus-within wins over hover via pseudo-class
7208
+ // ordering, so focus colour still applies when the field is hovered.
7207
7209
  fieldBase: "border border-token-input focus-within:border-token-brand disabled:pointer-events-none transition-colors",
7210
+ // Hover-only border darkening for the active (non-invalid, non-disabled)
7211
+ // state. Applied conditionally by the component so it does not override
7212
+ // the destructive border on invalid fields. The `:not(:focus-within)`
7213
+ // guard is required because Tailwind emits `:hover` rules AFTER
7214
+ // `:focus-within` — without it, hovering a focused field would replace
7215
+ // the brand border with the hover border.
7216
+ fieldHover: "[&:not(:focus-within)]:hover:border-token-input-hover",
7208
7217
  // Borderless base — transparent resting border, brand border only on focus.
7209
7218
  // No hover/pressed overlay (these cells sit inside a larger interactive row).
7210
7219
  // Layout-shift safe: border width stays 1px across states (color-only swap).
@@ -7255,6 +7264,7 @@ var Input = React2.forwardRef(
7255
7264
  }, ref) => {
7256
7265
  const isDisabled = forceState === "disabled" || disabled;
7257
7266
  const isFocussed = forceState === "focussed";
7267
+ const isInvalid = invalid || supportingTextType === "error";
7258
7268
  return /* @__PURE__ */ jsxs2(
7259
7269
  "div",
7260
7270
  {
@@ -7273,9 +7283,10 @@ var Input = React2.forwardRef(
7273
7283
  inputSize === "sm" ? INPUT_TOKENS.fieldSizeSm : INPUT_TOKENS.fieldSizeMd,
7274
7284
  inputVariant === "borderless" ? INPUT_TOKENS.fieldBgBorderless : inputVariant === "white" ? INPUT_TOKENS.fieldBgWhite : INPUT_TOKENS.fieldBgGrey,
7275
7285
  inputVariant === "borderless" ? INPUT_TOKENS.fieldBaseBorderless : INPUT_TOKENS.fieldBase,
7286
+ inputVariant !== "borderless" && !isDisabled && !isInvalid && INPUT_TOKENS.fieldHover,
7276
7287
  isDisabled && INPUT_TOKENS.fieldDisabled,
7277
7288
  isFocussed && "border-token-brand",
7278
- (invalid || supportingTextType === "error") && INPUT_TOKENS.fieldInvalid
7289
+ isInvalid && INPUT_TOKENS.fieldInvalid
7279
7290
  ),
7280
7291
  style: {
7281
7292
  borderRadius: INPUT_TOKENS.radius[input_border_radius],
@@ -7419,7 +7430,12 @@ var INPUT_GROUP_TOKENS = {
7419
7430
  field: "flex h-[var(--input-height,40px)] w-full items-center gap-2 overflow-hidden px-[var(--input-padding-horizontal,12px)] py-[10px] text-sm shadow-none",
7420
7431
  fieldBgGrey: "bg-token-white",
7421
7432
  fieldBgWhite: "bg-token-white",
7422
- fieldBase: "border border-token-light focus-within:border-token-brand disabled:pointer-events-none",
7433
+ fieldBase: "border border-token-light focus-within:border-token-brand disabled:pointer-events-none transition-colors",
7434
+ // Hover-only border darkening (Figma hover variant: #dadada). Applied
7435
+ // conditionally by the component so it does not override invalid/disabled.
7436
+ // The `:not(:focus-within)` guard prevents the hover border from beating
7437
+ // the brand focus border (Tailwind emits `:hover` after `:focus-within`).
7438
+ fieldHover: "[&:not(:focus-within)]:hover:border-token-input-hover",
7423
7439
  fieldInvalid: "border-token-destructive focus-within:border-token-destructive",
7424
7440
  fieldDisabled: "border-token-xlight",
7425
7441
  // Inner input area
@@ -7428,7 +7444,8 @@ var INPUT_GROUP_TOKENS = {
7428
7444
  segment: "shrink-0 flex items-center text-xs font-normal text-fg-placeholder px-3",
7429
7445
  segmentDivider: "border-token-light",
7430
7446
  // Textarea
7431
- textareaField: "w-full bg-token-white py-[10px] px-[var(--input-padding-horizontal,12px)] text-sm border border-token-light focus-visible:outline-none focus-visible:border-token-brand disabled:pointer-events-none shadow-none",
7447
+ textareaField: "w-full bg-token-white py-[10px] px-[var(--input-padding-horizontal,12px)] text-sm border border-token-light focus-visible:outline-none focus-visible:border-token-brand disabled:pointer-events-none shadow-none transition-colors",
7448
+ textareaHover: "[&:not(:focus-within)]:hover:border-token-input-hover",
7432
7449
  textareaInvalid: "border-token-destructive focus-visible:border-token-destructive",
7433
7450
  textareaDisabled: "border-token-xlight",
7434
7451
  radius: {
@@ -7478,6 +7495,7 @@ var InputGroup = React4.forwardRef(
7478
7495
  const isDisabled = forceState === "disabled" || disabled;
7479
7496
  const isFocussed = forceState === "focussed";
7480
7497
  const isActive = forceState === "active";
7498
+ const isInvalid = !!invalid;
7481
7499
  return /* @__PURE__ */ jsxs4(
7482
7500
  "div",
7483
7501
  {
@@ -7492,9 +7510,10 @@ var InputGroup = React4.forwardRef(
7492
7510
  INPUT_GROUP_TOKENS.field,
7493
7511
  inputVariant === "white" ? INPUT_GROUP_TOKENS.fieldBgWhite : INPUT_GROUP_TOKENS.fieldBgGrey,
7494
7512
  INPUT_GROUP_TOKENS.fieldBase,
7513
+ !isDisabled && !isInvalid && INPUT_GROUP_TOKENS.fieldHover,
7495
7514
  isDisabled && INPUT_GROUP_TOKENS.fieldDisabled,
7496
7515
  isFocussed && "border-token-brand",
7497
- invalid && INPUT_GROUP_TOKENS.fieldInvalid
7516
+ isInvalid && INPUT_GROUP_TOKENS.fieldInvalid
7498
7517
  ),
7499
7518
  style: {
7500
7519
  borderRadius: INPUT_GROUP_TOKENS.radius[input_border_radius],
@@ -7587,16 +7606,20 @@ var TEXTAREA_TOKENS = {
7587
7606
  labelRow: "flex items-center gap-[2px] text-xs font-medium leading-4 tracking-normal text-fg-black",
7588
7607
  mandatory: "text-fg-destructive",
7589
7608
  // Base shared field layout/typography.
7590
- // `relative` + `overflow-hidden` so the `before:` overlay respects the
7591
- // field's rounded corners via [border-radius:inherit].
7592
7609
  field: "relative flex min-h-[80px] w-full flex-col overflow-hidden px-[var(--input-padding-horizontal,12px)] py-[10px] text-sm shadow-none",
7593
7610
  fieldBgGrey: "bg-token-white",
7594
7611
  fieldBgWhite: "bg-token-white",
7595
- // Base border + focus treatment.
7596
- // Hover/pressed are painted via a pseudo-element overlay (see fieldBase) to avoid layout shift.
7612
+ // Base border + focus treatment. Hover darkens the border (Figma hover
7613
+ // variant: #dadada). focus-within wins over hover via pseudo-class
7614
+ // ordering, so focus colour still applies when the field is hovered.
7597
7615
  // Focus ring uses outline (not ring) so it doesn't contribute to box size.
7598
- fieldBase: "border border-token-input focus-within:border-token-brand disabled:pointer-events-none before:pointer-events-none before:absolute before:inset-0 before:content-[''] before:[border-radius:inherit] before:transition-colors before:bg-transparent hover:before:bg-interaction-hov-opacity-primary active:before:bg-interaction-pressed-opacity-primary [&>*]:relative [&>*]:z-10",
7599
- fieldDisabled: "border-token-xlight hover:before:bg-transparent active:before:bg-transparent",
7616
+ fieldBase: "border border-token-input focus-within:border-token-brand disabled:pointer-events-none transition-colors",
7617
+ // Hover-only border darkening. Applied conditionally by the component so
7618
+ // it does not override invalid/disabled borders. The `:not(:focus-within)`
7619
+ // guard keeps the brand focus border visible when a focused field is
7620
+ // hovered (Tailwind emits `:hover` after `:focus-within`).
7621
+ fieldHover: "[&:not(:focus-within)]:hover:border-token-input-hover",
7622
+ fieldDisabled: "border-token-xlight",
7600
7623
  fieldInvalid: "border-token-destructive focus-within:border-token-destructive",
7601
7624
  // Textarea itself.
7602
7625
  // `selection:` applies styles to the user's text selection (::selection):
@@ -7641,6 +7664,7 @@ var Textarea = React5.forwardRef(
7641
7664
  }, ref) => {
7642
7665
  const isDisabled = forceState === "disabled" || disabled;
7643
7666
  const isFocussed = forceState === "focussed";
7667
+ const isInvalid = invalid || supportingTextType === "error";
7644
7668
  return /* @__PURE__ */ jsxs5(
7645
7669
  "div",
7646
7670
  {
@@ -7658,9 +7682,10 @@ var Textarea = React5.forwardRef(
7658
7682
  TEXTAREA_TOKENS.field,
7659
7683
  inputVariant === "white" ? TEXTAREA_TOKENS.fieldBgWhite : TEXTAREA_TOKENS.fieldBgGrey,
7660
7684
  TEXTAREA_TOKENS.fieldBase,
7685
+ !isDisabled && !isInvalid && TEXTAREA_TOKENS.fieldHover,
7661
7686
  isDisabled && TEXTAREA_TOKENS.fieldDisabled,
7662
7687
  isFocussed && "border-token-brand outline outline-2 outline-token-brand outline-offset-2",
7663
- (invalid || supportingTextType === "error") && TEXTAREA_TOKENS.fieldInvalid
7688
+ isInvalid && TEXTAREA_TOKENS.fieldInvalid
7664
7689
  ),
7665
7690
  style: {
7666
7691
  borderRadius: TEXTAREA_TOKENS.radius[input_border_radius],
@@ -8775,6 +8800,7 @@ var UI_COLOR_KEY_TO_CSS_VAR = {
8775
8800
  borderBrand: "--border-brand",
8776
8801
  borderDestructive: "--border-destructive",
8777
8802
  borderInput: "--border-input",
8803
+ borderInputHover: "--border-input-hover",
8778
8804
  // Interaction tokens (Figma: color > interaction)
8779
8805
  hovOpacityPrimary: "--hov-opacity-primary",
8780
8806
  hovOpacitySecondary: "--hov-opacity-secondary",
@@ -9145,8 +9171,12 @@ import { Check as Check3, ChevronDown as ChevronDown3, Search } from "lucide-rea
9145
9171
  var SELECT_TOKENS = {
9146
9172
  // Main select container
9147
9173
  container: {
9148
- // Figma: trigger uses rounded-xl (12px), border visible, padding px-12 py-10
9149
- default: "rounded-[var(--rounded-xl,12px)] border border-token-input flex items-center justify-between gap-2 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none data-[state=open]:border-token-brand",
9174
+ // Figma: trigger uses rounded-xl (12px), border visible, padding px-12 py-10.
9175
+ // Hover darkens the border to match Figma (#dadada). The hover is scoped
9176
+ // via `[&:not([data-state=open])]` so the brand border wins when the
9177
+ // dropdown is open (Tailwind emits `data-[*]` selectors before `:hover`,
9178
+ // so without the guard hover would override the open-state border).
9179
+ default: "rounded-[var(--rounded-xl,12px)] border border-token-input flex items-center justify-between gap-2 transition-colors [&:not([data-state=open])]:hover:border-token-input-hover disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:border-token-input focus-visible:outline-none data-[state=open]:border-token-brand",
9150
9180
  // bg variants: default = input (grey), white = card
9151
9181
  bgDefault: "bg-token-white",
9152
9182
  bgWhite: "bg-token-white",
@@ -10415,7 +10445,7 @@ var DIALOG_TOKENS = {
10415
10445
  },
10416
10446
  header: {
10417
10447
  wrapper: "flex flex-col shrink-0",
10418
- row: "flex items-center gap-[10px] px-6 py-6",
10448
+ row: "flex items-center gap-[10px] px-6 pt-6 pb-2",
10419
10449
  title: "flex-1 text-lg font-semibold leading-7 text-fg-black",
10420
10450
  closeBtn: "size-9 flex items-center justify-center rounded-md text-fg-black hover:bg-token-grey transition-colors cursor-pointer",
10421
10451
  divider: "h-px bg-border-token-light"
@@ -10450,6 +10480,8 @@ var Dialog = React29.forwardRef(
10450
10480
  showCancel = true,
10451
10481
  onCancel,
10452
10482
  onConfirm,
10483
+ confirmVariant = "primary",
10484
+ confirmDisabled = false,
10453
10485
  className
10454
10486
  }, ref) => {
10455
10487
  const close = React29.useCallback(() => onOpenChange(false), [onOpenChange]);
@@ -10522,9 +10554,10 @@ var Dialog = React29.forwardRef(
10522
10554
  /* @__PURE__ */ jsx25(
10523
10555
  Button,
10524
10556
  {
10525
- variant: "primary",
10557
+ variant: confirmVariant,
10526
10558
  button_border_radius: "rounded",
10527
10559
  onClick: onConfirm,
10560
+ disabled: confirmDisabled,
10528
10561
  children: confirmLabel
10529
10562
  }
10530
10563
  )
@@ -10584,6 +10617,8 @@ var DialogFooter = React29.forwardRef(
10584
10617
  showCancel = true,
10585
10618
  onCancel,
10586
10619
  onConfirm,
10620
+ confirmVariant = "primary",
10621
+ confirmDisabled = false,
10587
10622
  showDivider = false,
10588
10623
  className
10589
10624
  }, ref) => {
@@ -10603,9 +10638,10 @@ var DialogFooter = React29.forwardRef(
10603
10638
  /* @__PURE__ */ jsx25(
10604
10639
  Button,
10605
10640
  {
10606
- variant: "primary",
10641
+ variant: confirmVariant,
10607
10642
  button_border_radius: "rounded",
10608
10643
  onClick: onConfirm,
10644
+ disabled: confirmDisabled,
10609
10645
  children: confirmLabel
10610
10646
  }
10611
10647
  )
package/dist/styles.css CHANGED
@@ -1511,15 +1511,9 @@
1511
1511
  .lego-land .py-4 {
1512
1512
  padding-block: calc(var(--spacing) * 4);
1513
1513
  }
1514
- .lego-land .py-6 {
1515
- padding-block: calc(var(--spacing) * 6);
1516
- }
1517
1514
  .lego-land .py-8 {
1518
1515
  padding-block: calc(var(--spacing) * 8);
1519
1516
  }
1520
- .lego-land .py-10 {
1521
- padding-block: calc(var(--spacing) * 10);
1522
- }
1523
1517
  .lego-land .py-\[3px\] {
1524
1518
  padding-block: 3px;
1525
1519
  }
@@ -1541,6 +1535,9 @@
1541
1535
  .lego-land .pt-4 {
1542
1536
  padding-top: calc(var(--spacing) * 4);
1543
1537
  }
1538
+ .lego-land .pt-6 {
1539
+ padding-top: calc(var(--spacing) * 6);
1540
+ }
1544
1541
  .lego-land .pr-2 {
1545
1542
  padding-right: calc(var(--spacing) * 2);
1546
1543
  }
@@ -2160,24 +2157,12 @@
2160
2157
  inset: calc(var(--spacing) * 0);
2161
2158
  }
2162
2159
  }
2163
- .lego-land .before\:\[border-radius\:inherit\] {
2164
- .lego-land &::before {
2165
- content: var(--tw-content);
2166
- border-radius: inherit;
2167
- }
2168
- }
2169
2160
  .lego-land .before\:rounded-full {
2170
2161
  .lego-land &::before {
2171
2162
  content: var(--tw-content);
2172
2163
  border-radius: calc(infinity * 1px);
2173
2164
  }
2174
2165
  }
2175
- .lego-land .before\:bg-transparent {
2176
- .lego-land &::before {
2177
- content: var(--tw-content);
2178
- background-color: transparent;
2179
- }
2180
- }
2181
2166
  .lego-land .before\:transition-colors {
2182
2167
  .lego-land &::before {
2183
2168
  content: var(--tw-content);
@@ -2388,16 +2373,6 @@
2388
2373
  }
2389
2374
  }
2390
2375
  }
2391
- .lego-land .hover\:before\:bg-transparent {
2392
- .lego-land &:hover {
2393
- @media (hover: hover) {
2394
- &::before {
2395
- content: var(--tw-content);
2396
- background-color: transparent;
2397
- }
2398
- }
2399
- }
2400
- }
2401
2376
  .lego-land .hover\:after\:opacity-100 {
2402
2377
  .lego-land &:hover {
2403
2378
  @media (hover: hover) {
@@ -2531,14 +2506,6 @@
2531
2506
  }
2532
2507
  }
2533
2508
  }
2534
- .lego-land .active\:before\:bg-transparent {
2535
- .lego-land &:active {
2536
- .lego-land &::before {
2537
- content: var(--tw-content);
2538
- background-color: transparent;
2539
- }
2540
- }
2541
- }
2542
2509
  .lego-land .active\:after\:bg-black\/25 {
2543
2510
  .lego-land &:active {
2544
2511
  .lego-land &::after {
@@ -2608,6 +2575,15 @@
2608
2575
  opacity: 100%;
2609
2576
  }
2610
2577
  }
2578
+ .lego-land .disabled\:hover\:border-token-input {
2579
+ .lego-land &:disabled {
2580
+ .lego-land &:hover {
2581
+ @media (hover: hover) {
2582
+ border-color: hsl(var(--border-input));
2583
+ }
2584
+ }
2585
+ }
2586
+ }
2611
2587
  .lego-land .disabled\:hover\:before\:bg-transparent {
2612
2588
  .lego-land &:disabled {
2613
2589
  .lego-land &:hover {
@@ -2842,6 +2818,24 @@
2842
2818
  appearance: none;
2843
2819
  }
2844
2820
  }
2821
+ .lego-land .\[\&\:not\(\:focus-within\)\]\:hover\:border-token-input-hover {
2822
+ .lego-land &:not(:focus-within) {
2823
+ .lego-land &:hover {
2824
+ @media (hover: hover) {
2825
+ border-color: hsl(var(--border-input-hover));
2826
+ }
2827
+ }
2828
+ }
2829
+ }
2830
+ .lego-land .\[\&\:not\(\[data-state\=open\]\)\]\:hover\:border-token-input-hover {
2831
+ .lego-land &:not([data-state=open]) {
2832
+ .lego-land &:hover {
2833
+ @media (hover: hover) {
2834
+ border-color: hsl(var(--border-input-hover));
2835
+ }
2836
+ }
2837
+ }
2838
+ }
2845
2839
  .lego-land .\[\&\>\*\]\:relative {
2846
2840
  .lego-land &>* {
2847
2841
  position: relative;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nurix/ui-component-library",
3
- "version": "1.1.4-stage.117",
3
+ "version": "1.1.4-stage.119",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",