@chayns-ui/core 0.4.0 → 0.4.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.
package/dist/button.css CHANGED
@@ -14,6 +14,13 @@
14
14
  text-align: center;
15
15
  color: var(--text);
16
16
  cursor: pointer;
17
+ transition:
18
+ background 0.2s ease,
19
+ color 0.2s ease,
20
+ border-color 0.2s ease,
21
+ box-shadow 0.2s ease,
22
+ transform 0.15s ease,
23
+ filter 0.2s ease;
17
24
  }
18
25
 
19
26
  .chayns-button {
@@ -142,12 +149,14 @@
142
149
  :where(.chayns-button--primary, .chayns-icon-button--primary):hover:not(:disabled) {
143
150
  background: var(--accent-hover);
144
151
  box-shadow: var(--shadow-btn-hover);
152
+ transform: translateY(-1px);
145
153
  }
146
154
 
147
155
  :where(.chayns-button--outline, .chayns-icon-button--outline):hover:not(:disabled) {
148
156
  background: var(--accent);
149
157
  color: var(--on-accent);
150
158
  box-shadow: var(--shadow-btn-hover);
159
+ transform: translateY(-1px);
151
160
  }
152
161
 
153
162
  :where(.chayns-button--ghost, .chayns-icon-button--ghost):hover:not(:disabled) {
@@ -157,6 +166,7 @@
157
166
 
158
167
  :where(.chayns-button--danger, .chayns-icon-button--danger):hover:not(:disabled) {
159
168
  background: var(--danger-bg-hover);
169
+ transform: translateY(-1px);
160
170
  }
161
171
 
162
172
  :where(.chayns-button, .chayns-icon-button):hover:not(:disabled)
@@ -188,4 +198,10 @@
188
198
  outline-offset: 1px;
189
199
  }
190
200
  }
201
+
202
+ @media (prefers-reduced-motion: reduce) {
203
+ :where(.chayns-button, .chayns-icon-button) {
204
+ transition: none;
205
+ }
206
+ }
191
207
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SegmentedControl.d.ts","sourceRoot":"","sources":["../../../src/components/segmented-control/SegmentedControl.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAyB,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAwJvF,QAAA,MAAM,gBAAgB;;;;;;;;;;;;CAAmD,CAAC;AAE1E,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"SegmentedControl.d.ts","sourceRoot":"","sources":["../../../src/components/segmented-control/SegmentedControl.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAyB,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAqMvF,QAAA,MAAM,gBAAgB;;;;;;;;;;;;CAAmD,CAAC;AAE1E,eAAe,gBAAgB,CAAC"}
@@ -56,6 +56,11 @@ var SegmentedControlRoot = forwardRef(function SegmentedControlRoot({ children,
56
56
  const labelId = useId();
57
57
  const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);
58
58
  const segments = useRef(/* @__PURE__ */ new Map());
59
+ const segmentsElement = useRef(null);
60
+ const [indicator, setIndicator] = useState({
61
+ offset: 0,
62
+ width: 0
63
+ });
59
64
  const selectedValue = value ?? uncontrolledValue ?? "";
60
65
  const resolvedClassName = ["chayns-segmented-control", className].filter(Boolean).join(" ");
61
66
  function selectValue(nextValue) {
@@ -83,6 +88,25 @@ var SegmentedControlRoot = forwardRef(function SegmentedControlRoot({ children,
83
88
  selectValue(nextValue);
84
89
  nextElement.focus();
85
90
  }
91
+ useLayoutEffect(() => {
92
+ const container = segmentsElement.current;
93
+ const selectedSegment = segments.current.get(selectedValue);
94
+ if (container === null || selectedSegment === void 0) return;
95
+ const observedContainer = container;
96
+ const observedSegment = selectedSegment;
97
+ function updateIndicator() {
98
+ const paddingInlineStart = Number.parseFloat(window.getComputedStyle(observedContainer).paddingInlineStart);
99
+ setIndicator({
100
+ offset: observedSegment.offsetLeft - (Number.isNaN(paddingInlineStart) ? 0 : paddingInlineStart),
101
+ width: observedSegment.offsetWidth
102
+ });
103
+ }
104
+ updateIndicator();
105
+ if (typeof ResizeObserver === "undefined") return;
106
+ const observer = new ResizeObserver(updateIndicator);
107
+ observer.observe(observedContainer);
108
+ return () => observer.disconnect();
109
+ }, [selectedValue]);
86
110
  return /* @__PURE__ */ jsx(SegmentedControlContext.Provider, {
87
111
  value: {
88
112
  moveFocus,
@@ -100,9 +124,17 @@ var SegmentedControlRoot = forwardRef(function SegmentedControlRoot({ children,
100
124
  className: "chayns-segmented-control__label",
101
125
  id: labelId,
102
126
  children: label
103
- }), /* @__PURE__ */ jsx("div", {
127
+ }), /* @__PURE__ */ jsxs("div", {
104
128
  className: "chayns-segmented-control__segments",
105
- children
129
+ ref: segmentsElement,
130
+ children: [/* @__PURE__ */ jsx("span", {
131
+ "aria-hidden": "true",
132
+ className: "chayns-segmented-control__indicator",
133
+ style: {
134
+ transform: `translateX(${indicator.offset}px)`,
135
+ width: indicator.width
136
+ }
137
+ }), children]
106
138
  })]
107
139
  })
108
140
  });
@@ -1 +1 @@
1
- {"version":3,"file":"SegmentedControl.js","names":[],"sources":["../../../src/components/segmented-control/SegmentedControl.tsx"],"sourcesContent":["import { forwardRef, useId, useLayoutEffect, useRef, useState } from 'react';\nimport type { KeyboardEvent } from 'react';\n\nimport { SegmentedControlContext, useSegmentedControlContext } from './SegmentedControlContext.js';\nimport type { SegmentedControlProps, SegmentProps } from './SegmentedControl.types.js';\n\nfunction getOrderedSegments(segments: Map<string, HTMLButtonElement>) {\n return [...segments.entries()]\n .filter(([, element]) => !element.disabled)\n .sort(([, first], [, second]) => {\n const position = first.compareDocumentPosition(second);\n return position & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;\n });\n}\n\nconst Segment = forwardRef<HTMLButtonElement, SegmentProps>(function Segment(\n { children, className, disabled, value, ...buttonProps },\n ref,\n) {\n const control = useSegmentedControlContext();\n const localRef = useRef<HTMLButtonElement>(null);\n const isSelected = control.value === value;\n const resolvedClassName = [\n 'chayns-segmented-control__segment',\n isSelected ? 'chayns-segmented-control__segment--selected' : '',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n useLayoutEffect(() => {\n if (localRef.current === null) {\n return undefined;\n }\n\n return control.registerSegment(value, localRef.current);\n }, [control, value]);\n\n function setRef(element: HTMLButtonElement | null) {\n localRef.current = element;\n\n if (typeof ref === 'function') {\n ref(element);\n } else if (ref !== null) {\n ref.current = element;\n }\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLButtonElement>) {\n const keys = ['ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'End', 'Home'];\n\n if (keys.includes(event.key)) {\n event.preventDefault();\n control.moveFocus(value, event.key);\n }\n }\n\n return (\n <button\n {...buttonProps}\n aria-checked={isSelected}\n className={resolvedClassName}\n disabled={disabled}\n onClick={() => control.selectValue(value)}\n onKeyDown={handleKeyDown}\n ref={setRef}\n role=\"radio\"\n tabIndex={isSelected ? 0 : -1}\n type=\"button\"\n >\n {children}\n </button>\n );\n});\n\nconst SegmentedControlRoot = forwardRef<HTMLDivElement, SegmentedControlProps>(\n function SegmentedControlRoot(\n { children, className, defaultValue, label, onValueChange, value, ...rootProps },\n ref,\n ) {\n const labelId = useId();\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n const segments = useRef(new Map<string, HTMLButtonElement>());\n const selectedValue = value ?? uncontrolledValue ?? '';\n const resolvedClassName = ['chayns-segmented-control', className].filter(Boolean).join(' ');\n\n function selectValue(nextValue: string) {\n if (value === undefined) {\n setUncontrolledValue(nextValue);\n }\n\n onValueChange?.(nextValue);\n }\n\n function registerSegment(segmentValue: string, element: HTMLButtonElement) {\n segments.current.set(segmentValue, element);\n\n return () => {\n segments.current.delete(segmentValue);\n };\n }\n\n function moveFocus(currentValue: string, key: string) {\n const enabledSegments = getOrderedSegments(segments.current);\n const currentIndex = enabledSegments.findIndex(\n ([segmentValue]) => segmentValue === currentValue,\n );\n\n if (currentIndex === -1 || enabledSegments.length === 0) {\n return;\n }\n\n let targetIndex = currentIndex;\n\n if (key === 'Home') {\n targetIndex = 0;\n } else if (key === 'End') {\n targetIndex = enabledSegments.length - 1;\n } else if (key === 'ArrowLeft' || key === 'ArrowUp') {\n targetIndex = (currentIndex - 1 + enabledSegments.length) % enabledSegments.length;\n } else if (key === 'ArrowRight' || key === 'ArrowDown') {\n targetIndex = (currentIndex + 1) % enabledSegments.length;\n }\n\n const nextSegment = enabledSegments[targetIndex];\n\n if (nextSegment === undefined) {\n return;\n }\n\n const [nextValue, nextElement] = nextSegment;\n selectValue(nextValue);\n nextElement.focus();\n }\n\n return (\n <SegmentedControlContext.Provider\n value={{ moveFocus, registerSegment, selectValue, value: selectedValue }}\n >\n <div\n {...rootProps}\n aria-labelledby={labelId}\n className={resolvedClassName}\n ref={ref}\n role=\"radiogroup\"\n >\n <span className=\"chayns-segmented-control__label\" id={labelId}>\n {label}\n </span>\n <div className=\"chayns-segmented-control__segments\">{children}</div>\n </div>\n </SegmentedControlContext.Provider>\n );\n },\n);\n\nconst SegmentedControl = Object.assign(SegmentedControlRoot, { Segment });\n\nexport default SegmentedControl;\n"],"mappings":"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"}
1
+ {"version":3,"file":"SegmentedControl.js","names":[],"sources":["../../../src/components/segmented-control/SegmentedControl.tsx"],"sourcesContent":["import { forwardRef, useId, useLayoutEffect, useRef, useState } from 'react';\nimport type { KeyboardEvent } from 'react';\n\nimport { SegmentedControlContext, useSegmentedControlContext } from './SegmentedControlContext.js';\nimport type { SegmentedControlProps, SegmentProps } from './SegmentedControl.types.js';\n\nfunction getOrderedSegments(segments: Map<string, HTMLButtonElement>) {\n return [...segments.entries()]\n .filter(([, element]) => !element.disabled)\n .sort(([, first], [, second]) => {\n const position = first.compareDocumentPosition(second);\n return position & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;\n });\n}\n\nconst Segment = forwardRef<HTMLButtonElement, SegmentProps>(function Segment(\n { children, className, disabled, value, ...buttonProps },\n ref,\n) {\n const control = useSegmentedControlContext();\n const localRef = useRef<HTMLButtonElement>(null);\n const isSelected = control.value === value;\n const resolvedClassName = [\n 'chayns-segmented-control__segment',\n isSelected ? 'chayns-segmented-control__segment--selected' : '',\n className,\n ]\n .filter(Boolean)\n .join(' ');\n\n useLayoutEffect(() => {\n if (localRef.current === null) {\n return undefined;\n }\n\n return control.registerSegment(value, localRef.current);\n }, [control, value]);\n\n function setRef(element: HTMLButtonElement | null) {\n localRef.current = element;\n\n if (typeof ref === 'function') {\n ref(element);\n } else if (ref !== null) {\n ref.current = element;\n }\n }\n\n function handleKeyDown(event: KeyboardEvent<HTMLButtonElement>) {\n const keys = ['ArrowDown', 'ArrowLeft', 'ArrowRight', 'ArrowUp', 'End', 'Home'];\n\n if (keys.includes(event.key)) {\n event.preventDefault();\n control.moveFocus(value, event.key);\n }\n }\n\n return (\n <button\n {...buttonProps}\n aria-checked={isSelected}\n className={resolvedClassName}\n disabled={disabled}\n onClick={() => control.selectValue(value)}\n onKeyDown={handleKeyDown}\n ref={setRef}\n role=\"radio\"\n tabIndex={isSelected ? 0 : -1}\n type=\"button\"\n >\n {children}\n </button>\n );\n});\n\nconst SegmentedControlRoot = forwardRef<HTMLDivElement, SegmentedControlProps>(\n function SegmentedControlRoot(\n { children, className, defaultValue, label, onValueChange, value, ...rootProps },\n ref,\n ) {\n const labelId = useId();\n const [uncontrolledValue, setUncontrolledValue] = useState(defaultValue);\n const segments = useRef(new Map<string, HTMLButtonElement>());\n const segmentsElement = useRef<HTMLDivElement>(null);\n const [indicator, setIndicator] = useState({ offset: 0, width: 0 });\n const selectedValue = value ?? uncontrolledValue ?? '';\n const resolvedClassName = ['chayns-segmented-control', className].filter(Boolean).join(' ');\n\n function selectValue(nextValue: string) {\n if (value === undefined) {\n setUncontrolledValue(nextValue);\n }\n\n onValueChange?.(nextValue);\n }\n\n function registerSegment(segmentValue: string, element: HTMLButtonElement) {\n segments.current.set(segmentValue, element);\n\n return () => {\n segments.current.delete(segmentValue);\n };\n }\n\n function moveFocus(currentValue: string, key: string) {\n const enabledSegments = getOrderedSegments(segments.current);\n const currentIndex = enabledSegments.findIndex(\n ([segmentValue]) => segmentValue === currentValue,\n );\n\n if (currentIndex === -1 || enabledSegments.length === 0) {\n return;\n }\n\n let targetIndex = currentIndex;\n\n if (key === 'Home') {\n targetIndex = 0;\n } else if (key === 'End') {\n targetIndex = enabledSegments.length - 1;\n } else if (key === 'ArrowLeft' || key === 'ArrowUp') {\n targetIndex = (currentIndex - 1 + enabledSegments.length) % enabledSegments.length;\n } else if (key === 'ArrowRight' || key === 'ArrowDown') {\n targetIndex = (currentIndex + 1) % enabledSegments.length;\n }\n\n const nextSegment = enabledSegments[targetIndex];\n\n if (nextSegment === undefined) {\n return;\n }\n\n const [nextValue, nextElement] = nextSegment;\n selectValue(nextValue);\n nextElement.focus();\n }\n\n useLayoutEffect(() => {\n const container = segmentsElement.current;\n const selectedSegment = segments.current.get(selectedValue);\n\n if (container === null || selectedSegment === undefined) {\n return undefined;\n }\n\n const observedContainer = container;\n const observedSegment = selectedSegment;\n\n function updateIndicator() {\n const paddingInlineStart = Number.parseFloat(\n window.getComputedStyle(observedContainer).paddingInlineStart,\n );\n\n setIndicator({\n offset:\n observedSegment.offsetLeft -\n (Number.isNaN(paddingInlineStart) ? 0 : paddingInlineStart),\n width: observedSegment.offsetWidth,\n });\n }\n\n updateIndicator();\n\n if (typeof ResizeObserver === 'undefined') {\n return undefined;\n }\n\n const observer = new ResizeObserver(updateIndicator);\n observer.observe(observedContainer);\n\n return () => observer.disconnect();\n }, [selectedValue]);\n\n return (\n <SegmentedControlContext.Provider\n value={{ moveFocus, registerSegment, selectValue, value: selectedValue }}\n >\n <div\n {...rootProps}\n aria-labelledby={labelId}\n className={resolvedClassName}\n ref={ref}\n role=\"radiogroup\"\n >\n <span className=\"chayns-segmented-control__label\" id={labelId}>\n {label}\n </span>\n <div className=\"chayns-segmented-control__segments\" ref={segmentsElement}>\n <span\n aria-hidden=\"true\"\n className=\"chayns-segmented-control__indicator\"\n style={{ transform: `translateX(${indicator.offset}px)`, width: indicator.width }}\n />\n {children}\n </div>\n </div>\n </SegmentedControlContext.Provider>\n );\n },\n);\n\nconst SegmentedControl = Object.assign(SegmentedControlRoot, { Segment });\n\nexport default SegmentedControl;\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
1
  import type { TextAreaProps } from './TextArea.types.js';
2
- declare function TextArea({ className, counter, error, helpText, id, label, placeholder, ...textareaProps }: TextAreaProps): import("react").JSX.Element;
2
+ declare function TextArea({ className, counter, error, helpText, id, placeholder, ...textareaProps }: TextAreaProps): import("react").JSX.Element;
3
3
  export default TextArea;
4
4
  //# sourceMappingURL=TextArea.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TextArea.d.ts","sourceRoot":"","sources":["../../../src/components/text-area/TextArea.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,iBAAS,QAAQ,CAAC,EAChB,SAAS,EACT,OAAO,EACP,KAAK,EACL,QAAQ,EACR,EAAE,EACF,KAAK,EACL,WAAW,EACX,GAAG,aAAa,EACjB,EAAE,aAAa,+BAqCf;AAED,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"TextArea.d.ts","sourceRoot":"","sources":["../../../src/components/text-area/TextArea.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,iBAAS,QAAQ,CAAC,EAChB,SAAS,EACT,OAAO,EACP,KAAK,EACL,QAAQ,EACR,EAAE,EACF,WAAW,EACX,GAAG,aAAa,EACjB,EAAE,aAAa,+BAuCf;AAED,eAAe,QAAQ,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useId } from "react";
3
3
  //#region src/components/text-area/TextArea.tsx
4
- function TextArea({ className, counter, error, helpText, id, label, placeholder, ...textareaProps }) {
4
+ function TextArea({ className, counter, error, helpText, id, placeholder, ...textareaProps }) {
5
5
  const generatedId = useId();
6
6
  const textareaId = id ?? generatedId;
7
7
  const hasError = error !== void 0;
@@ -18,13 +18,13 @@ function TextArea({ className, counter, error, helpText, id, label, placeholder,
18
18
  "aria-invalid": hasError || void 0,
19
19
  className: textareaClassName,
20
20
  id: textareaId,
21
- placeholder: placeholder ?? " "
21
+ placeholder: placeholder === void 0 ? void 0 : " "
22
22
  }),
23
- /* @__PURE__ */ jsx("label", {
23
+ placeholder !== void 0 ? /* @__PURE__ */ jsx("label", {
24
24
  className: "chayns-text-area__label",
25
25
  htmlFor: textareaId,
26
- children: label
27
- }),
26
+ children: placeholder
27
+ }) : null,
28
28
  helpText !== void 0 || hasError || counter !== void 0 ? /* @__PURE__ */ jsxs("div", {
29
29
  className: "chayns-text-area__help",
30
30
  children: [/* @__PURE__ */ jsxs("div", { children: [helpText !== void 0 ? /* @__PURE__ */ jsx("div", {
@@ -1 +1 @@
1
- {"version":3,"file":"TextArea.js","names":[],"sources":["../../../src/components/text-area/TextArea.tsx"],"sourcesContent":["import { useId } from 'react';\n\nimport type { TextAreaProps } from './TextArea.types.js';\n\nfunction TextArea({\n className,\n counter,\n error,\n helpText,\n id,\n label,\n placeholder,\n ...textareaProps\n}: TextAreaProps) {\n const generatedId = useId();\n const textareaId = id ?? generatedId;\n const hasError = error !== undefined;\n const helpId = helpText !== undefined ? `${textareaId}-help` : undefined;\n const errorId = hasError ? `${textareaId}-error` : undefined;\n const describedBy = [helpId, errorId].filter(Boolean).join(' ') || undefined;\n const textareaClassName = ['chayns-text-area__input', className].filter(Boolean).join(' ');\n\n return (\n <div className={`chayns-text-area${hasError ? ' chayns-text-area--error' : ''}`}>\n <textarea\n {...textareaProps}\n aria-describedby={describedBy}\n aria-invalid={hasError || undefined}\n className={textareaClassName}\n id={textareaId}\n placeholder={placeholder ?? ' '}\n />\n <label className=\"chayns-text-area__label\" htmlFor={textareaId}>\n {label}\n </label>\n {helpText !== undefined || hasError || counter !== undefined ? (\n <div className=\"chayns-text-area__help\">\n <div>\n {helpText !== undefined ? <div id={helpId}>{helpText}</div> : null}\n {hasError ? (\n <div className=\"chayns-text-area__error\" id={errorId}>\n {error}\n </div>\n ) : null}\n </div>\n {counter !== undefined ? <div>{counter}</div> : null}\n </div>\n ) : null}\n </div>\n );\n}\n\nexport default TextArea;\n"],"mappings":";;;AAIA,SAAS,SAAS,EAChB,WACA,SACA,OACA,UACA,IACA,OACA,aACA,GAAG,iBACa;CAChB,MAAM,cAAc,MAAM;CAC1B,MAAM,aAAa,MAAM;CACzB,MAAM,WAAW,UAAU,KAAA;CAC3B,MAAM,SAAS,aAAa,KAAA,IAAY,GAAG,WAAW,SAAS,KAAA;CAC/D,MAAM,UAAU,WAAW,GAAG,WAAW,UAAU,KAAA;CACnD,MAAM,cAAc,CAAC,QAAQ,OAAO,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,KAAK,KAAA;CACnE,MAAM,oBAAoB,CAAC,2BAA2B,SAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CAEzF,OACE,qBAAC,OAAD;EAAK,WAAW,mBAAmB,WAAW,6BAA6B;EAA3E,UAAA;GACE,oBAAC,YAAD;IACE,GAAI;IACJ,oBAAkB;IAClB,gBAAc,YAAY,KAAA;IAC1B,WAAW;IACX,IAAI;IACJ,aAAa,eAAe;GAC7B,CAAA;GACD,oBAAC,SAAD;IAAO,WAAU;IAA0B,SAAS;IACjD,UAAA;GACI,CAAA;GACN,aAAa,KAAA,KAAa,YAAY,YAAY,KAAA,IACjD,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,qBAAC,OAAD,EAAA,UAAA,CACG,aAAa,KAAA,IAAY,oBAAC,OAAD;KAAK,IAAI;KAAS,UAAA;IAAc,CAAA,IAAI,MAC7D,WACC,oBAAC,OAAD;KAAK,WAAU;KAA0B,IAAI;KAC1C,UAAA;IACE,CAAA,IACH,IACD,EAAA,CAAA,GACJ,YAAY,KAAA,IAAY,oBAAC,OAAD,EAAA,UAAM,QAAa,CAAA,IAAI,IAC7C;GACH,CAAA,IAAA;EACD;;AAET"}
1
+ {"version":3,"file":"TextArea.js","names":[],"sources":["../../../src/components/text-area/TextArea.tsx"],"sourcesContent":["import { useId } from 'react';\n\nimport type { TextAreaProps } from './TextArea.types.js';\n\nfunction TextArea({\n className,\n counter,\n error,\n helpText,\n id,\n placeholder,\n ...textareaProps\n}: TextAreaProps) {\n const generatedId = useId();\n const textareaId = id ?? generatedId;\n const hasError = error !== undefined;\n const helpId = helpText !== undefined ? `${textareaId}-help` : undefined;\n const errorId = hasError ? `${textareaId}-error` : undefined;\n const describedBy = [helpId, errorId].filter(Boolean).join(' ') || undefined;\n const textareaClassName = ['chayns-text-area__input', className].filter(Boolean).join(' ');\n\n return (\n <div className={`chayns-text-area${hasError ? ' chayns-text-area--error' : ''}`}>\n <textarea\n {...textareaProps}\n aria-describedby={describedBy}\n aria-invalid={hasError || undefined}\n className={textareaClassName}\n id={textareaId}\n placeholder={placeholder === undefined ? undefined : ' '}\n />\n {placeholder !== undefined ? (\n <label className=\"chayns-text-area__label\" htmlFor={textareaId}>\n {placeholder}\n </label>\n ) : null}\n {helpText !== undefined || hasError || counter !== undefined ? (\n <div className=\"chayns-text-area__help\">\n <div>\n {helpText !== undefined ? <div id={helpId}>{helpText}</div> : null}\n {hasError ? (\n <div className=\"chayns-text-area__error\" id={errorId}>\n {error}\n </div>\n ) : null}\n </div>\n {counter !== undefined ? <div>{counter}</div> : null}\n </div>\n ) : null}\n </div>\n );\n}\n\nexport default TextArea;\n"],"mappings":";;;AAIA,SAAS,SAAS,EAChB,WACA,SACA,OACA,UACA,IACA,aACA,GAAG,iBACa;CAChB,MAAM,cAAc,MAAM;CAC1B,MAAM,aAAa,MAAM;CACzB,MAAM,WAAW,UAAU,KAAA;CAC3B,MAAM,SAAS,aAAa,KAAA,IAAY,GAAG,WAAW,SAAS,KAAA;CAC/D,MAAM,UAAU,WAAW,GAAG,WAAW,UAAU,KAAA;CACnD,MAAM,cAAc,CAAC,QAAQ,OAAO,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,KAAK,KAAA;CACnE,MAAM,oBAAoB,CAAC,2BAA2B,SAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CAEzF,OACE,qBAAC,OAAD;EAAK,WAAW,mBAAmB,WAAW,6BAA6B;EAA3E,UAAA;GACE,oBAAC,YAAD;IACE,GAAI;IACJ,oBAAkB;IAClB,gBAAc,YAAY,KAAA;IAC1B,WAAW;IACX,IAAI;IACJ,aAAa,gBAAgB,KAAA,IAAY,KAAA,IAAY;GACtD,CAAA;GACA,gBAAgB,KAAA,IACf,oBAAC,SAAD;IAAO,WAAU;IAA0B,SAAS;IACjD,UAAA;GACI,CAAA,IACL;GACH,aAAa,KAAA,KAAa,YAAY,YAAY,KAAA,IACjD,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,qBAAC,OAAD,EAAA,UAAA,CACG,aAAa,KAAA,IAAY,oBAAC,OAAD;KAAK,IAAI;KAAS,UAAA;IAAc,CAAA,IAAI,MAC7D,WACC,oBAAC,OAAD;KAAK,WAAU;KAA0B,IAAI;KAC1C,UAAA;IACE,CAAA,IACH,IACD,EAAA,CAAA,GACJ,YAAY,KAAA,IAAY,oBAAC,OAAD,EAAA,UAAM,QAAa,CAAA,IAAI,IAC7C;GACH,CAAA,IAAA;EACD;;AAET"}
@@ -1,8 +1,6 @@
1
1
  import type { ComponentPropsWithRef, ReactNode } from 'react';
2
- /** Props for a labelled native multi-line textarea. */
2
+ /** Props for a native multi-line textarea with optional help and error content. */
3
3
  export interface TextAreaProps extends Omit<ComponentPropsWithRef<'textarea'>, 'aria-describedby' | 'aria-invalid' | 'children'> {
4
- /** Visible label associated with the native textarea. */
5
- label: ReactNode;
6
4
  /** Supporting content linked to the textarea. */
7
5
  helpText?: ReactNode;
8
6
  /** Validation content linked to the textarea and reflected through aria-invalid. */
@@ -1 +1 @@
1
- {"version":3,"file":"TextArea.types.d.ts","sourceRoot":"","sources":["../../../src/components/text-area/TextArea.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE9D,uDAAuD;AACvD,MAAM,WAAW,aAAc,SAAQ,IAAI,CACzC,qBAAqB,CAAC,UAAU,CAAC,EACjC,kBAAkB,GAAG,cAAc,GAAG,UAAU,CACjD;IACC,yDAAyD;IACzD,KAAK,EAAE,SAAS,CAAC;IACjB,iDAAiD;IACjD,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,oFAAoF;IACpF,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,kDAAkD;IAClD,OAAO,CAAC,EAAE,SAAS,CAAC;IAEpB,0EAA0E;IAC1E,kBAAkB,CAAC,EAAE,KAAK,CAAC;IAE3B,2EAA2E;IAC3E,cAAc,CAAC,EAAE,KAAK,CAAC;CACxB"}
1
+ {"version":3,"file":"TextArea.types.d.ts","sourceRoot":"","sources":["../../../src/components/text-area/TextArea.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE9D,mFAAmF;AACnF,MAAM,WAAW,aAAc,SAAQ,IAAI,CACzC,qBAAqB,CAAC,UAAU,CAAC,EACjC,kBAAkB,GAAG,cAAc,GAAG,UAAU,CACjD;IACC,iDAAiD;IACjD,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,oFAAoF;IACpF,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,kDAAkD;IAClD,OAAO,CAAC,EAAE,SAAS,CAAC;IAEpB,0EAA0E;IAC1E,kBAAkB,CAAC,EAAE,KAAK,CAAC;IAE3B,2EAA2E;IAC3E,cAAc,CAAC,EAAE,KAAK,CAAC;CACxB"}
@@ -1,4 +1,4 @@
1
1
  import type { TextFieldProps } from './TextField.types.js';
2
- declare function TextField({ className, counter, error, helpText, id, label, placeholder, ...inputProps }: TextFieldProps): import("react").JSX.Element;
2
+ declare function TextField({ className, counter, error, helpText, id, placeholder, ...inputProps }: TextFieldProps): import("react").JSX.Element;
3
3
  export default TextField;
4
4
  //# sourceMappingURL=TextField.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TextField.d.ts","sourceRoot":"","sources":["../../../src/components/text-field/TextField.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,iBAAS,SAAS,CAAC,EACjB,SAAS,EACT,OAAO,EACP,KAAK,EACL,QAAQ,EACR,EAAE,EACF,KAAK,EACL,WAAW,EACX,GAAG,UAAU,EACd,EAAE,cAAc,+BAqChB;AAED,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"TextField.d.ts","sourceRoot":"","sources":["../../../src/components/text-field/TextField.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,iBAAS,SAAS,CAAC,EACjB,SAAS,EACT,OAAO,EACP,KAAK,EACL,QAAQ,EACR,EAAE,EACF,WAAW,EACX,GAAG,UAAU,EACd,EAAE,cAAc,+BAuChB;AAED,eAAe,SAAS,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { useId } from "react";
3
3
  //#region src/components/text-field/TextField.tsx
4
- function TextField({ className, counter, error, helpText, id, label, placeholder, ...inputProps }) {
4
+ function TextField({ className, counter, error, helpText, id, placeholder, ...inputProps }) {
5
5
  const generatedId = useId();
6
6
  const inputId = id ?? generatedId;
7
7
  const hasError = error !== void 0;
@@ -18,13 +18,13 @@ function TextField({ className, counter, error, helpText, id, label, placeholder
18
18
  "aria-invalid": hasError || void 0,
19
19
  className: inputClassName,
20
20
  id: inputId,
21
- placeholder: placeholder ?? " "
21
+ placeholder: placeholder === void 0 ? void 0 : " "
22
22
  }),
23
- /* @__PURE__ */ jsx("label", {
23
+ placeholder !== void 0 ? /* @__PURE__ */ jsx("label", {
24
24
  className: "chayns-text-field__label",
25
25
  htmlFor: inputId,
26
- children: label
27
- }),
26
+ children: placeholder
27
+ }) : null,
28
28
  helpText !== void 0 || hasError || counter !== void 0 ? /* @__PURE__ */ jsxs("div", {
29
29
  className: "chayns-text-field__help",
30
30
  children: [/* @__PURE__ */ jsxs("div", { children: [helpText !== void 0 ? /* @__PURE__ */ jsx("div", {
@@ -1 +1 @@
1
- {"version":3,"file":"TextField.js","names":[],"sources":["../../../src/components/text-field/TextField.tsx"],"sourcesContent":["import { useId } from 'react';\n\nimport type { TextFieldProps } from './TextField.types.js';\n\nfunction TextField({\n className,\n counter,\n error,\n helpText,\n id,\n label,\n placeholder,\n ...inputProps\n}: TextFieldProps) {\n const generatedId = useId();\n const inputId = id ?? generatedId;\n const hasError = error !== undefined;\n const helpId = helpText !== undefined ? `${inputId}-help` : undefined;\n const errorId = hasError ? `${inputId}-error` : undefined;\n const describedBy = [helpId, errorId].filter(Boolean).join(' ') || undefined;\n const inputClassName = ['chayns-text-field__input', className].filter(Boolean).join(' ');\n\n return (\n <div className={`chayns-text-field${hasError ? ' chayns-text-field--error' : ''}`}>\n <input\n {...inputProps}\n aria-describedby={describedBy}\n aria-invalid={hasError || undefined}\n className={inputClassName}\n id={inputId}\n placeholder={placeholder ?? ' '}\n />\n <label className=\"chayns-text-field__label\" htmlFor={inputId}>\n {label}\n </label>\n {helpText !== undefined || hasError || counter !== undefined ? (\n <div className=\"chayns-text-field__help\">\n <div>\n {helpText !== undefined ? <div id={helpId}>{helpText}</div> : null}\n {hasError ? (\n <div className=\"chayns-text-field__error\" id={errorId}>\n {error}\n </div>\n ) : null}\n </div>\n {counter !== undefined ? <div>{counter}</div> : null}\n </div>\n ) : null}\n </div>\n );\n}\n\nexport default TextField;\n"],"mappings":";;;AAIA,SAAS,UAAU,EACjB,WACA,SACA,OACA,UACA,IACA,OACA,aACA,GAAG,cACc;CACjB,MAAM,cAAc,MAAM;CAC1B,MAAM,UAAU,MAAM;CACtB,MAAM,WAAW,UAAU,KAAA;CAC3B,MAAM,SAAS,aAAa,KAAA,IAAY,GAAG,QAAQ,SAAS,KAAA;CAC5D,MAAM,UAAU,WAAW,GAAG,QAAQ,UAAU,KAAA;CAChD,MAAM,cAAc,CAAC,QAAQ,OAAO,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,KAAK,KAAA;CACnE,MAAM,iBAAiB,CAAC,4BAA4B,SAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CAEvF,OACE,qBAAC,OAAD;EAAK,WAAW,oBAAoB,WAAW,8BAA8B;EAA7E,UAAA;GACE,oBAAC,SAAD;IACE,GAAI;IACJ,oBAAkB;IAClB,gBAAc,YAAY,KAAA;IAC1B,WAAW;IACX,IAAI;IACJ,aAAa,eAAe;GAC7B,CAAA;GACD,oBAAC,SAAD;IAAO,WAAU;IAA2B,SAAS;IAClD,UAAA;GACI,CAAA;GACN,aAAa,KAAA,KAAa,YAAY,YAAY,KAAA,IACjD,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,qBAAC,OAAD,EAAA,UAAA,CACG,aAAa,KAAA,IAAY,oBAAC,OAAD;KAAK,IAAI;KAAS,UAAA;IAAc,CAAA,IAAI,MAC7D,WACC,oBAAC,OAAD;KAAK,WAAU;KAA2B,IAAI;KAC3C,UAAA;IACE,CAAA,IACH,IACD,EAAA,CAAA,GACJ,YAAY,KAAA,IAAY,oBAAC,OAAD,EAAA,UAAM,QAAa,CAAA,IAAI,IAC7C;GACH,CAAA,IAAA;EACD;;AAET"}
1
+ {"version":3,"file":"TextField.js","names":[],"sources":["../../../src/components/text-field/TextField.tsx"],"sourcesContent":["import { useId } from 'react';\n\nimport type { TextFieldProps } from './TextField.types.js';\n\nfunction TextField({\n className,\n counter,\n error,\n helpText,\n id,\n placeholder,\n ...inputProps\n}: TextFieldProps) {\n const generatedId = useId();\n const inputId = id ?? generatedId;\n const hasError = error !== undefined;\n const helpId = helpText !== undefined ? `${inputId}-help` : undefined;\n const errorId = hasError ? `${inputId}-error` : undefined;\n const describedBy = [helpId, errorId].filter(Boolean).join(' ') || undefined;\n const inputClassName = ['chayns-text-field__input', className].filter(Boolean).join(' ');\n\n return (\n <div className={`chayns-text-field${hasError ? ' chayns-text-field--error' : ''}`}>\n <input\n {...inputProps}\n aria-describedby={describedBy}\n aria-invalid={hasError || undefined}\n className={inputClassName}\n id={inputId}\n placeholder={placeholder === undefined ? undefined : ' '}\n />\n {placeholder !== undefined ? (\n <label className=\"chayns-text-field__label\" htmlFor={inputId}>\n {placeholder}\n </label>\n ) : null}\n {helpText !== undefined || hasError || counter !== undefined ? (\n <div className=\"chayns-text-field__help\">\n <div>\n {helpText !== undefined ? <div id={helpId}>{helpText}</div> : null}\n {hasError ? (\n <div className=\"chayns-text-field__error\" id={errorId}>\n {error}\n </div>\n ) : null}\n </div>\n {counter !== undefined ? <div>{counter}</div> : null}\n </div>\n ) : null}\n </div>\n );\n}\n\nexport default TextField;\n"],"mappings":";;;AAIA,SAAS,UAAU,EACjB,WACA,SACA,OACA,UACA,IACA,aACA,GAAG,cACc;CACjB,MAAM,cAAc,MAAM;CAC1B,MAAM,UAAU,MAAM;CACtB,MAAM,WAAW,UAAU,KAAA;CAC3B,MAAM,SAAS,aAAa,KAAA,IAAY,GAAG,QAAQ,SAAS,KAAA;CAC5D,MAAM,UAAU,WAAW,GAAG,QAAQ,UAAU,KAAA;CAChD,MAAM,cAAc,CAAC,QAAQ,OAAO,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,KAAK,KAAA;CACnE,MAAM,iBAAiB,CAAC,4BAA4B,SAAS,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CAEvF,OACE,qBAAC,OAAD;EAAK,WAAW,oBAAoB,WAAW,8BAA8B;EAA7E,UAAA;GACE,oBAAC,SAAD;IACE,GAAI;IACJ,oBAAkB;IAClB,gBAAc,YAAY,KAAA;IAC1B,WAAW;IACX,IAAI;IACJ,aAAa,gBAAgB,KAAA,IAAY,KAAA,IAAY;GACtD,CAAA;GACA,gBAAgB,KAAA,IACf,oBAAC,SAAD;IAAO,WAAU;IAA2B,SAAS;IAClD,UAAA;GACI,CAAA,IACL;GACH,aAAa,KAAA,KAAa,YAAY,YAAY,KAAA,IACjD,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA,CACE,qBAAC,OAAD,EAAA,UAAA,CACG,aAAa,KAAA,IAAY,oBAAC,OAAD;KAAK,IAAI;KAAS,UAAA;IAAc,CAAA,IAAI,MAC7D,WACC,oBAAC,OAAD;KAAK,WAAU;KAA2B,IAAI;KAC3C,UAAA;IACE,CAAA,IACH,IACD,EAAA,CAAA,GACJ,YAAY,KAAA,IAAY,oBAAC,OAAD,EAAA,UAAM,QAAa,CAAA,IAAI,IAC7C;GACH,CAAA,IAAA;EACD;;AAET"}
@@ -1,8 +1,6 @@
1
1
  import type { ComponentPropsWithRef, ReactNode } from 'react';
2
- /** Props for a labelled native single-line input. */
2
+ /** Props for a native single-line input with optional help and error content. */
3
3
  export interface TextFieldProps extends Omit<ComponentPropsWithRef<'input'>, 'aria-describedby' | 'aria-invalid' | 'children'> {
4
- /** Visible label associated with the native input. */
5
- label: ReactNode;
6
4
  /** Supporting content linked to the input. */
7
5
  helpText?: ReactNode;
8
6
  /** Validation content linked to the input and reflected through aria-invalid. */
@@ -1 +1 @@
1
- {"version":3,"file":"TextField.types.d.ts","sourceRoot":"","sources":["../../../src/components/text-field/TextField.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE9D,qDAAqD;AACrD,MAAM,WAAW,cAAe,SAAQ,IAAI,CAC1C,qBAAqB,CAAC,OAAO,CAAC,EAC9B,kBAAkB,GAAG,cAAc,GAAG,UAAU,CACjD;IACC,sDAAsD;IACtD,KAAK,EAAE,SAAS,CAAC;IACjB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,iFAAiF;IACjF,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,kDAAkD;IAClD,OAAO,CAAC,EAAE,SAAS,CAAC;IAEpB,0EAA0E;IAC1E,kBAAkB,CAAC,EAAE,KAAK,CAAC;IAE3B,2EAA2E;IAC3E,cAAc,CAAC,EAAE,KAAK,CAAC;CACxB"}
1
+ {"version":3,"file":"TextField.types.d.ts","sourceRoot":"","sources":["../../../src/components/text-field/TextField.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAE9D,iFAAiF;AACjF,MAAM,WAAW,cAAe,SAAQ,IAAI,CAC1C,qBAAqB,CAAC,OAAO,CAAC,EAC9B,kBAAkB,GAAG,cAAc,GAAG,UAAU,CACjD;IACC,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,iFAAiF;IACjF,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,kDAAkD;IAClD,OAAO,CAAC,EAAE,SAAS,CAAC;IAEpB,0EAA0E;IAC1E,kBAAkB,CAAC,EAAE,KAAK,CAAC;IAE3B,2EAA2E;IAC3E,cAAc,CAAC,EAAE,KAAK,CAAC;CACxB"}
@@ -13,17 +13,34 @@
13
13
  }
14
14
 
15
15
  .chayns-segmented-control__segments {
16
- display: flex;
17
- flex-wrap: wrap;
16
+ position: relative;
17
+ display: inline-grid;
18
+ grid-auto-columns: minmax(0, 1fr);
19
+ grid-auto-flow: column;
18
20
  gap: var(--k4);
19
21
  padding: var(--k4);
20
22
  border-radius: 12px;
21
23
  background: var(--toggle-bg);
22
24
  }
23
25
 
26
+ .chayns-segmented-control__indicator {
27
+ position: absolute;
28
+ z-index: 0;
29
+ inset-block: var(--k4);
30
+ inset-inline-start: var(--k4);
31
+ border: 1px solid var(--border);
32
+ border-radius: 8px;
33
+ background: var(--surface);
34
+ box-shadow: var(--shadow-card);
35
+ pointer-events: none;
36
+ transition: transform 0.2s ease;
37
+ }
38
+
24
39
  .chayns-segmented-control__segment {
25
40
  min-block-size: var(--ctrl-h);
26
- flex: 1 1 0;
41
+ position: relative;
42
+ z-index: 1;
43
+ min-inline-size: 0;
27
44
  padding: calc(9 * var(--u)) var(--k12);
28
45
  border: 0;
29
46
  border-radius: 8px;
@@ -36,11 +53,6 @@
36
53
  text-align: center;
37
54
  }
38
55
 
39
- .chayns-segmented-control__segment--selected {
40
- background: var(--surface);
41
- box-shadow: var(--shadow-card);
42
- }
43
-
44
56
  .chayns-segmented-control__segment:focus-visible {
45
57
  outline: var(--focus-ring-size) solid rgb(var(--focus-ring-rgb), var(--focus-ring-alpha-strong));
46
58
  outline-offset: 2px;
@@ -52,10 +64,16 @@
52
64
  }
53
65
 
54
66
  .chayns-segmented-control__segment:hover:not(:disabled) {
55
- background: var(--hover-bg);
67
+ background: var(--hover);
56
68
  }
57
69
 
58
70
  .chayns-segmented-control__segment--selected:hover:not(:disabled) {
59
- background: var(--surface);
71
+ background: transparent;
72
+ }
73
+
74
+ @media (prefers-reduced-motion: reduce) {
75
+ .chayns-segmented-control__indicator {
76
+ transition: none;
77
+ }
60
78
  }
61
79
  }
@@ -19,6 +19,9 @@
19
19
  color: var(--text);
20
20
  background: var(--surface);
21
21
  outline: none;
22
+ transition:
23
+ border-color 0.18s ease,
24
+ box-shadow 0.18s ease;
22
25
  }
23
26
 
24
27
  .chayns-text-area__label {
@@ -26,15 +29,18 @@
26
29
  inset-block-start: 0;
27
30
  inset-inline-start: 10px;
28
31
  padding-inline: var(--k5);
32
+ color: var(--text-3);
33
+ pointer-events: none;
29
34
  font-size: var(--fs-body);
30
35
  font-weight: 400;
31
36
  line-height: 1.2;
32
- color: var(--text-3);
33
- pointer-events: none;
37
+ white-space: nowrap;
34
38
  background: var(--surface);
35
39
  transform: translateY(calc(var(--input-py) + 1.5px + 0.6em - 50%));
36
40
  transform-origin: left center;
37
- transition: transform 0.2s cubic-bezier(0.22, 0.61, 0.36, 1);
41
+ transition:
42
+ transform 0.2s cubic-bezier(0.22, 0.61, 0.36, 1),
43
+ color 0.15s ease;
38
44
  }
39
45
 
40
46
  :where(.chayns-text-area__input:hover:not(:disabled)) {
@@ -47,33 +53,58 @@
47
53
  rgb(var(--focus-ring-rgb), var(--focus-ring-alpha-soft));
48
54
  }
49
55
 
50
- :where(
51
- .chayns-text-area__input:focus + .chayns-text-area__label,
52
- .chayns-text-area__input:not(:placeholder-shown) + .chayns-text-area__label
53
- ) {
54
- color: var(--accent);
55
- transform: translateY(-50%) scale(0.8);
56
+ .chayns-text-area__input:disabled {
57
+ color: var(--disabled-fg);
58
+ cursor: not-allowed;
59
+ background: var(--disabled-bg);
60
+ border-color: var(--disabled-border);
61
+ }
62
+
63
+ .chayns-text-area__input:read-only {
64
+ color: var(--text-2);
65
+ cursor: text;
66
+ background: var(--surface-2);
67
+ }
68
+
69
+ .chayns-text-area--error .chayns-text-area__label {
70
+ color: var(--danger);
71
+ }
72
+
73
+ .chayns-text-area__input:disabled + .chayns-text-area__label {
74
+ color: var(--disabled-fg);
75
+ background: var(--disabled-bg);
76
+ }
77
+
78
+ .chayns-text-area__input:read-only + .chayns-text-area__label {
79
+ background: var(--surface-2);
56
80
  }
57
81
 
58
82
  .chayns-text-area--error .chayns-text-area__input {
59
83
  border-color: var(--danger);
60
84
  }
61
85
 
62
- .chayns-text-area--error .chayns-text-area__label {
86
+ .chayns-text-area__input:focus + .chayns-text-area__label,
87
+ .chayns-text-area__input:not(:placeholder-shown) + .chayns-text-area__label {
88
+ transform: translateY(-50%) scale(0.8);
89
+ }
90
+
91
+ .chayns-text-area__input:focus + .chayns-text-area__label {
92
+ color: var(--accent);
93
+ }
94
+
95
+ .chayns-text-area--error .chayns-text-area__input:focus + .chayns-text-area__label {
63
96
  color: var(--danger);
64
97
  }
65
98
 
66
- .chayns-text-area__input:disabled {
99
+ .chayns-text-area--error .chayns-text-area__input:disabled {
67
100
  color: var(--disabled-fg);
68
- cursor: not-allowed;
69
101
  background: var(--disabled-bg);
70
102
  border-color: var(--disabled-border);
71
103
  }
72
104
 
73
- .chayns-text-area__input:read-only {
74
- color: var(--text-2);
75
- cursor: text;
76
- background: var(--surface-2);
105
+ .chayns-text-area--error .chayns-text-area__input:disabled + .chayns-text-area__label {
106
+ color: var(--disabled-fg);
107
+ background: var(--disabled-bg);
77
108
  }
78
109
 
79
110
  .chayns-text-area__help {
@@ -90,16 +121,16 @@
90
121
  color: var(--danger);
91
122
  }
92
123
 
93
- @media (prefers-reduced-motion: reduce) {
94
- .chayns-text-area__label {
95
- transition: none;
96
- }
97
- }
98
-
99
124
  @media (forced-colors: active) {
100
125
  .chayns-text-area__input:focus-visible {
101
126
  outline: var(--focus-ring-size) solid ButtonText;
102
127
  outline-offset: 1px;
103
128
  }
104
129
  }
130
+
131
+ @media (prefers-reduced-motion: reduce) {
132
+ .chayns-text-area__label {
133
+ transition: none;
134
+ }
135
+ }
105
136
  }
@@ -16,6 +16,9 @@
16
16
  color: var(--text);
17
17
  background: var(--surface);
18
18
  outline: none;
19
+ transition:
20
+ border-color 0.18s ease,
21
+ box-shadow 0.18s ease;
19
22
  }
20
23
 
21
24
  .chayns-text-field__label {
@@ -23,15 +26,18 @@
23
26
  inset-block-start: 0;
24
27
  inset-inline-start: 10px;
25
28
  padding-inline: var(--k5);
29
+ color: var(--text-3);
30
+ pointer-events: none;
26
31
  font-size: var(--fs-body);
27
32
  font-weight: 400;
28
33
  line-height: 1.2;
29
- color: var(--text-3);
30
- pointer-events: none;
34
+ white-space: nowrap;
31
35
  background: var(--surface);
32
36
  transform: translateY(calc(var(--input-py) + 1.5px + 0.6em - 50%));
33
37
  transform-origin: left center;
34
- transition: transform 0.2s cubic-bezier(0.22, 0.61, 0.36, 1);
38
+ transition:
39
+ transform 0.2s cubic-bezier(0.22, 0.61, 0.36, 1),
40
+ color 0.15s ease;
35
41
  }
36
42
 
37
43
  :where(.chayns-text-field__input:hover:not(:disabled)) {
@@ -44,18 +50,6 @@
44
50
  rgb(var(--focus-ring-rgb), var(--focus-ring-alpha-soft));
45
51
  }
46
52
 
47
- :where(
48
- .chayns-text-field__input:focus + .chayns-text-field__label,
49
- .chayns-text-field__input:not(:placeholder-shown) + .chayns-text-field__label
50
- ) {
51
- color: var(--accent);
52
- transform: translateY(-50%) scale(0.8);
53
- }
54
-
55
- .chayns-text-field--error .chayns-text-field__input {
56
- border-color: var(--danger);
57
- }
58
-
59
53
  .chayns-text-field__input:disabled {
60
54
  color: var(--disabled-fg);
61
55
  cursor: not-allowed;
@@ -69,15 +63,52 @@
69
63
  background: var(--surface-2);
70
64
  }
71
65
 
72
- .chayns-text-field--error .chayns-text-field__input:focus {
73
- box-shadow: 0 0 0 var(--focus-ring-size)
74
- rgb(var(--focus-ring-rgb), var(--focus-ring-alpha-soft));
66
+ .chayns-text-field--error .chayns-text-field__label {
67
+ color: var(--danger);
75
68
  }
76
69
 
77
- .chayns-text-field--error .chayns-text-field__label {
70
+ .chayns-text-field__input:disabled + .chayns-text-field__label {
71
+ color: var(--disabled-fg);
72
+ background: var(--disabled-bg);
73
+ }
74
+
75
+ .chayns-text-field__input:read-only + .chayns-text-field__label {
76
+ background: var(--surface-2);
77
+ }
78
+
79
+ .chayns-text-field--error .chayns-text-field__input {
80
+ border-color: var(--danger);
81
+ }
82
+
83
+ .chayns-text-field__input:focus + .chayns-text-field__label,
84
+ .chayns-text-field__input:not(:placeholder-shown) + .chayns-text-field__label {
85
+ transform: translateY(-50%) scale(0.8);
86
+ }
87
+
88
+ .chayns-text-field__input:focus + .chayns-text-field__label {
89
+ color: var(--accent);
90
+ }
91
+
92
+ .chayns-text-field--error .chayns-text-field__input:focus + .chayns-text-field__label {
78
93
  color: var(--danger);
79
94
  }
80
95
 
96
+ .chayns-text-field--error .chayns-text-field__input:disabled {
97
+ color: var(--disabled-fg);
98
+ background: var(--disabled-bg);
99
+ border-color: var(--disabled-border);
100
+ }
101
+
102
+ .chayns-text-field--error .chayns-text-field__input:disabled + .chayns-text-field__label {
103
+ color: var(--disabled-fg);
104
+ background: var(--disabled-bg);
105
+ }
106
+
107
+ .chayns-text-field--error .chayns-text-field__input:focus {
108
+ box-shadow: 0 0 0 var(--focus-ring-size)
109
+ rgb(var(--focus-ring-rgb), var(--focus-ring-alpha-soft));
110
+ }
111
+
81
112
  .chayns-text-field__help {
82
113
  display: flex;
83
114
  align-items: center;
@@ -92,16 +123,16 @@
92
123
  color: var(--danger);
93
124
  }
94
125
 
95
- @media (prefers-reduced-motion: reduce) {
96
- .chayns-text-field__label {
97
- transition: none;
98
- }
99
- }
100
-
101
126
  @media (forced-colors: active) {
102
127
  .chayns-text-field__input:focus-visible {
103
128
  outline: var(--focus-ring-size) solid ButtonText;
104
129
  outline-offset: 1px;
105
130
  }
106
131
  }
132
+
133
+ @media (prefers-reduced-motion: reduce) {
134
+ .chayns-text-field__label {
135
+ transition: none;
136
+ }
137
+ }
107
138
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chayns-ui/core",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "Accessible React core components for chayns UI",
5
5
  "license": "MIT",
6
6
  "type": "module",