Point color, radius, stroke, focus, and type tokens at the Figma scale so later sections use the same values.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
63d5d21ae0
commit
e808fe70bf
162 files changed
+1385
-1377
No files matched your search
@@ -51,9 +51,9 @@ function AddCustomFieldViewComponent({
|
||||
<button
|
||||
type="button"
|
||||
onClick={onPressAdd}
|
||||
className={`flex h-[88px] w-full shrink-0 cursor-pointer items-center justify-center rounded-[var(--measures-radius-medium,8px)] bg-[var(--color-surface-default-secondary)] px-6 text-medium-label text-[var(--color-content-default-primary)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-border-invert-primary)] ${className ?? ""}`.trim()}
|
||||
className={`flex h-[88px] w-full shrink-0 cursor-pointer items-center justify-center rounded-[var(--radius-400)] bg-[var(--color-surface-default-secondary)] px-6 text-medium-label text-[var(--color-content-default-primary)] focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-border-invert-primary)] ${className ?? ""}`.trim()}
|
||||
>
|
||||
<span className="flex items-center gap-[var(--spacing-scale-006)] rounded-full px-4 py-3">
|
||||
<span className="flex items-center gap-[var(--space-150)] rounded-full px-4 py-3">
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
|
||||
@@ -56,7 +56,7 @@ const CheckboxContainer = memo<CheckboxProps>(
|
||||
// Default state: tertiary border, with hover and focus states via CSS
|
||||
// Hover changes border to brand primary color
|
||||
// Focus removes border and shows shadow (double ring: 2px white inner, 4px dark outer)
|
||||
return `${baseBox} bg-[var(--color-surface-default-primary)] border border-solid border-[var(--color-border-default-tertiary,#464646)] hover:border-[var(--color-border-default-brand-primary,#fdfaa8)] focus:border-transparent focus:shadow-[0px_0px_0px_2px_var(--color-border-invert-primary,white),0px_0px_0px_4px_var(--color-border-default-primary,#141414)] focus:outline-none`;
|
||||
return `${baseBox} bg-[var(--color-surface-default-primary)] border border-solid border-[var(--color-border-default-control)] hover:border-[var(--color-border-default-brand-primary,#fdfaa8)] focus:border-transparent focus:shadow-[var(--shadow-focus-invert)] focus:outline-none`;
|
||||
}
|
||||
|
||||
// Inverse mode styles per Figma
|
||||
@@ -64,7 +64,7 @@ const CheckboxContainer = memo<CheckboxProps>(
|
||||
// Inverse: transparent background, white border
|
||||
// Hover changes border to brand primary color
|
||||
// Focus shows shadow (2px dark inner, 4px white outer) - note: reversed from standard
|
||||
return `${baseBox} bg-transparent border border-solid border-[var(--color-border-invert-primary,white)] hover:border-[var(--color-border-default-brand-primary,#fdfaa8)] focus:shadow-[0px_0px_0px_2px_var(--color-border-default-primary,#141414),0px_0px_0px_4px_var(--color-border-invert-primary,white)] focus:outline-none`;
|
||||
return `${baseBox} bg-transparent border border-solid border-[var(--color-border-invert-primary,white)] hover:border-[var(--color-border-default-brand-primary,#fdfaa8)] focus:shadow-[var(--shadow-focus-default)] focus:outline-none`;
|
||||
}
|
||||
|
||||
return baseBox;
|
||||
@@ -76,12 +76,12 @@ const CheckboxContainer = memo<CheckboxProps>(
|
||||
const checkGlyphColor = checked
|
||||
? isStandard
|
||||
? "var(--color-content-default-brand-primary, #fefcc9)" // Light yellow/cream for standard mode
|
||||
: "var(--color-content-inverse-primary, #000000)" // Black for inverse mode
|
||||
: "var(--color-content-invert-primary, #000000)" // Black for inverse mode
|
||||
: "transparent";
|
||||
|
||||
// Label color
|
||||
const labelColor = isInverse
|
||||
? "var(--color-content-inverse-primary)"
|
||||
? "var(--color-content-invert-primary)"
|
||||
: "var(--color-content-default-primary)";
|
||||
|
||||
const handleToggle = (e: React.MouseEvent | React.KeyboardEvent) => {
|
||||
|
||||
@@ -39,7 +39,7 @@ export function CheckboxGroupView({
|
||||
<span
|
||||
className={`text-small-paragraph ${
|
||||
mode === "inverse"
|
||||
? "text-[var(--color-content-inverse-primary)]"
|
||||
? "text-[var(--color-content-invert-primary)]"
|
||||
: "text-[var(--color-content-default-primary)]"
|
||||
}`}
|
||||
>
|
||||
@@ -48,7 +48,7 @@ export function CheckboxGroupView({
|
||||
<span
|
||||
className={`text-small-paragraph ${
|
||||
mode === "inverse"
|
||||
? "text-[var(--color-content-inverse-secondary,#1f1f1f)]"
|
||||
? "text-[var(--color-content-invert-secondary,#1f1f1f)]"
|
||||
: "text-[var(--color-content-default-tertiary,#b4b4b4)]"
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -32,20 +32,20 @@ function chipSurfaceStyle(
|
||||
if (palette === "inverse") {
|
||||
if (state === "disabled") {
|
||||
return paint(
|
||||
"var(--color-surface-inverse-tertiary)",
|
||||
"var(--color-content-inverse-primary)",
|
||||
"var(--color-surface-invert-tertiary)",
|
||||
"var(--color-content-invert-primary)",
|
||||
);
|
||||
}
|
||||
if (state === "selected") {
|
||||
return paint(
|
||||
"var(--color-surface-default-semi-opaque)",
|
||||
"var(--color-content-inverse-primary)",
|
||||
"var(--color-content-invert-primary)",
|
||||
"var(--color-border-default-primary)",
|
||||
);
|
||||
}
|
||||
return paint(
|
||||
"transparent",
|
||||
"var(--color-content-inverse-primary)",
|
||||
"var(--color-content-invert-primary)",
|
||||
"var(--color-border-default-primary)",
|
||||
);
|
||||
}
|
||||
@@ -72,7 +72,7 @@ function chipSurfaceStyle(
|
||||
return paint(
|
||||
"transparent",
|
||||
"var(--color-content-default-brand-primary, #fefcc9)",
|
||||
"var(--color-border-default-tertiary, #464646)",
|
||||
"var(--color-border-default-control)",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -110,11 +110,11 @@ function ChipView({
|
||||
// Custom state has different padding
|
||||
const sizeClasses = isCustom
|
||||
? isSmall
|
||||
? "px-[var(--measures-spacing-100,4px)] py-[3px] text-x-small-paragraph"
|
||||
: "px-[var(--measures-spacing-150,6px)] py-[10px] text-medium-paragraph"
|
||||
? "px-[var(--space-100)] py-[3px] text-x-small-paragraph"
|
||||
: "px-[var(--space-150)] py-[10px] text-medium-paragraph"
|
||||
: isSmall
|
||||
? "h-[30px] px-[var(--measures-spacing-200,8px)] gap-[var(--measures-spacing-050,2px)] text-x-small-label"
|
||||
: "px-[var(--measures-spacing-300,12px)] py-[var(--measures-spacing-300,12px)] gap-[var(--measures-spacing-150,6px)] text-medium-label";
|
||||
? "h-[30px] px-[var(--space-200)] gap-[var(--space-050)] text-x-small-label"
|
||||
: "px-[var(--space-300)] py-[var(--space-300)] gap-[var(--space-150)] text-medium-label";
|
||||
|
||||
const baseClasses = `
|
||||
appearance-none
|
||||
@@ -122,7 +122,7 @@ function ChipView({
|
||||
max-w-full
|
||||
items-center
|
||||
justify-center
|
||||
rounded-[var(--measures-radius-full,9999px)]
|
||||
rounded-[var(--radius-full)]
|
||||
overflow-clip
|
||||
box-border
|
||||
focus:outline-none
|
||||
@@ -185,7 +185,7 @@ function ChipView({
|
||||
{onCheck && (
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center justify-center p-[var(--measures-spacing-150,6px)] rounded-full hover:bg-[var(--color-surface-default-semi-opaque,rgba(0,0,0,0.1))] transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="flex items-center justify-center p-[var(--space-150)] rounded-full hover:bg-[var(--color-surface-default-semi-opaque,rgba(0,0,0,0.1))] transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
aria-label={confirmAriaLabel}
|
||||
disabled={!inputValue || !inputValue.trim()}
|
||||
onClick={(event) => {
|
||||
@@ -227,8 +227,8 @@ function ChipView({
|
||||
className="bg-transparent border-none outline-none flex-1 min-w-0 font-normal text-[color:var(--color-content-default-tertiary,#b4b4b4)] placeholder:text-[color:var(--color-content-default-tertiary,#b4b4b4)]"
|
||||
style={{
|
||||
fontSize: isSmall
|
||||
? "var(--sizing-300,12px)"
|
||||
: "var(--sizing-400,16px)",
|
||||
? "var(--sizing-300)"
|
||||
: "var(--sizing-400)",
|
||||
lineHeight: isSmall ? "16px" : "24px",
|
||||
}}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
@@ -240,7 +240,7 @@ function ChipView({
|
||||
{onClose && (
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center justify-center p-[var(--measures-spacing-150,6px)] rounded-full hover:bg-[var(--color-surface-default-semi-opaque,rgba(0,0,0,0.1))] transition-colors"
|
||||
className="flex items-center justify-center p-[var(--space-150)] rounded-full hover:bg-[var(--color-surface-default-semi-opaque,rgba(0,0,0,0.1))] transition-colors"
|
||||
aria-label={closeAriaLabel}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
@@ -309,7 +309,7 @@ function ChipView({
|
||||
{onRemove && !isDisabled && (
|
||||
<button
|
||||
type="button"
|
||||
className="ml-[var(--measures-spacing-050,2px)] p-[var(--measures-spacing-050,2px)] rounded-full hover:bg-[var(--color-surface-default-semi-opaque,rgba(0,0,0,0.05))]"
|
||||
className="ml-[var(--space-050)] p-[var(--space-050)] rounded-full hover:bg-[var(--color-surface-default-semi-opaque,rgba(0,0,0,0.05))]"
|
||||
aria-label={`Remove ${label}`}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
|
||||
@@ -4,7 +4,7 @@ import { memo } from "react";
|
||||
import type { IncrementerViewProps } from "./Incrementer.types";
|
||||
|
||||
const STEP_BUTTON_CLASSES =
|
||||
"bg-[var(--color-surface-default-secondary,#141414)] text-[var(--color-content-default-primary,#fff)] inline-flex shrink-0 items-center justify-center overflow-clip rounded-[var(--measures-radius-full,9999px)] px-[var(--space-200,8px)] py-[var(--measures-spacing-150,6px)] transition-[background,color,opacity] duration-200 ease-in-out hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-border-invert-primary,#fff)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--color-surface-default-primary,#000)] disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:opacity-50";
|
||||
"bg-[var(--color-surface-default-secondary,#141414)] text-[var(--color-content-default-primary,#fff)] inline-flex shrink-0 items-center justify-center overflow-clip rounded-[var(--radius-full)] px-[var(--space-200)] py-[var(--space-150)] transition-[background,color,opacity] duration-200 ease-in-out hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-border-invert-primary,#fff)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--color-surface-default-primary,#000)] disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:opacity-50";
|
||||
|
||||
function MinusIcon() {
|
||||
return (
|
||||
@@ -75,7 +75,7 @@ function IncrementerView({
|
||||
</button>
|
||||
<span
|
||||
aria-live="polite"
|
||||
className={`shrink-0 whitespace-nowrap text-[length:var(--sizing-350,14px)] font-medium leading-[18px] ${valueColor}`}
|
||||
className={`shrink-0 whitespace-nowrap text-[length:var(--sizing-350)] font-medium leading-[18px] ${valueColor}`}
|
||||
>
|
||||
{displayValue}
|
||||
</span>
|
||||
|
||||
@@ -17,8 +17,11 @@ const IncrementerBlockContainer = ({
|
||||
labelSize = "s",
|
||||
palette = "default",
|
||||
blockClassName = "",
|
||||
state = "default",
|
||||
disabled,
|
||||
...incrementerProps
|
||||
}: IncrementerBlockProps) => {
|
||||
const isDisabled = state === "disabled" || Boolean(disabled);
|
||||
return (
|
||||
<IncrementerBlockView
|
||||
label={label}
|
||||
@@ -28,6 +31,8 @@ const IncrementerBlockContainer = ({
|
||||
labelSize={labelSize}
|
||||
palette={palette}
|
||||
blockClassName={blockClassName}
|
||||
state={state}
|
||||
disabled={isDisabled}
|
||||
{...incrementerProps}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { IncrementerBlockStateValue } from "../../../../lib/propNormalization";
|
||||
import type { IncrementerProps } from "../Incrementer/Incrementer.types";
|
||||
import type {
|
||||
InputLabelPaletteValue,
|
||||
@@ -5,6 +6,11 @@ import type {
|
||||
} from "../../type/InputLabel/InputLabel.types";
|
||||
|
||||
export interface IncrementerBlockProps extends IncrementerProps {
|
||||
/**
|
||||
* One state axis. Disabled dims the block and turns off both buttons.
|
||||
* @default "default"
|
||||
*/
|
||||
state?: IncrementerBlockStateValue;
|
||||
/** Label text displayed above the incrementer. */
|
||||
label: string;
|
||||
/** Show the help "?" icon next to the label. Defaults to `true`. */
|
||||
@@ -31,6 +37,7 @@ export interface IncrementerBlockProps extends IncrementerProps {
|
||||
}
|
||||
|
||||
export interface IncrementerBlockViewProps extends IncrementerProps {
|
||||
state: IncrementerBlockStateValue;
|
||||
label: string;
|
||||
helpIcon: boolean;
|
||||
helperText: boolean | string | undefined;
|
||||
|
||||
@@ -13,12 +13,14 @@ function IncrementerBlockView({
|
||||
labelSize,
|
||||
palette,
|
||||
blockClassName,
|
||||
state: _state,
|
||||
disabled,
|
||||
className,
|
||||
...incrementerProps
|
||||
}: IncrementerBlockViewProps) {
|
||||
return (
|
||||
<div
|
||||
className={`flex flex-col items-start gap-[var(--measures-spacing-300,12px)] py-[8px] ${blockClassName}`.trim()}
|
||||
className={`flex flex-col items-start gap-[var(--space-300)] py-[8px] ${disabled ? "opacity-60" : ""} ${blockClassName}`.trim()}
|
||||
data-figma-node="19883:13283"
|
||||
>
|
||||
<InputLabel
|
||||
@@ -29,7 +31,11 @@ function IncrementerBlockView({
|
||||
size={labelSize}
|
||||
palette={palette}
|
||||
/>
|
||||
<Incrementer {...incrementerProps} className={className} />
|
||||
<Incrementer
|
||||
{...incrementerProps}
|
||||
disabled={disabled}
|
||||
className={className}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ function InfoMessageBoxView({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex w-full min-w-0 items-start gap-[var(--measures-spacing-200,8px)] p-[var(--spacing-measures-spacing-500,20px)] rounded-[var(--measures-radius-300,12px)] border-l-2 border-solid border-[var(--color-border-default-secondary,#1f1f1f)] bg-[var(--color-content-inverse-secondary,#1f1f1f)] ${className}`}
|
||||
className={`flex w-full min-w-0 items-start gap-[var(--space-200)] p-[var(--space-500)] rounded-[var(--radius-300)] border-l-2 border-solid border-[var(--color-border-default-secondary,#1f1f1f)] bg-[var(--color-content-invert-secondary,#1f1f1f)] ${className}`}
|
||||
role="region"
|
||||
aria-label={title}
|
||||
>
|
||||
|
||||
@@ -15,10 +15,10 @@ export function InputWithCounterView({
|
||||
}: InputWithCounterProps) {
|
||||
const inputId = useId();
|
||||
return (
|
||||
<div className={`space-y-[var(--spacing-scale-008)] ${className}`}>
|
||||
<div className={`space-y-[var(--space-200)] ${className}`}>
|
||||
{/* Label with help icon */}
|
||||
{label && (
|
||||
<div className="flex items-center gap-[var(--spacing-scale-002)]">
|
||||
<div className="flex items-center gap-[var(--space-050)]">
|
||||
<label
|
||||
htmlFor={inputId}
|
||||
className="text-[14px] leading-[20px] font-medium text-[var(--color-content-default-primary)]"
|
||||
@@ -43,12 +43,12 @@ export function InputWithCounterView({
|
||||
width="12"
|
||||
height="12"
|
||||
>
|
||||
<rect width="12" height="12" fill="#D9D9D9" />
|
||||
<rect width="12" height="12" fill="var(--color-gray-200)" />
|
||||
</mask>
|
||||
<g mask="url(#mask0_21296_8257)">
|
||||
<path
|
||||
d="M5.99449 8.80766C6.13725 8.80766 6.25784 8.75838 6.35624 8.6598C6.45463 8.56123 6.50383 8.44055 6.50383 8.29779C6.50383 8.15502 6.45454 8.03444 6.35596 7.93605C6.25739 7.83765 6.13672 7.78845 5.99395 7.78845C5.85118 7.78845 5.7306 7.83774 5.63221 7.93631C5.53381 8.03489 5.48461 8.15556 5.48461 8.29833C5.48461 8.44109 5.5339 8.56168 5.63248 8.66008C5.73105 8.75847 5.85172 8.80766 5.99449 8.80766ZM5.64038 7.01729H6.34421C6.35062 6.77114 6.38668 6.5745 6.45239 6.42739C6.51809 6.28028 6.67754 6.08525 6.93075 5.8423C7.15062 5.62243 7.31905 5.41938 7.43604 5.23316C7.55302 5.04695 7.61151 4.82703 7.61151 4.57343C7.61151 4.14307 7.45687 3.80689 7.14759 3.5649C6.8383 3.32292 6.47243 3.20193 6.04999 3.20193C5.63269 3.20193 5.28734 3.3133 5.01394 3.53606C4.74055 3.75881 4.54552 4.02115 4.42885 4.32306L5.07114 4.58075C5.13204 4.41473 5.2362 4.25303 5.38364 4.09566C5.53108 3.93829 5.74999 3.8596 6.04038 3.8596C6.33589 3.8596 6.55432 3.94053 6.69566 4.1024C6.83701 4.26426 6.90769 4.4423 6.90769 4.63654C6.90769 4.80641 6.85929 4.96185 6.76249 5.10288C6.6657 5.2439 6.5423 5.38012 6.3923 5.51154C6.0641 5.80769 5.85673 6.0439 5.77019 6.22019C5.68365 6.39647 5.64038 6.66217 5.64038 7.01729ZM6.00082 10.75C5.34386 10.75 4.72634 10.6253 4.14828 10.376C3.5702 10.1266 3.06736 9.78827 2.63975 9.36085C2.21213 8.93343 1.8736 8.43081 1.62416 7.85299C1.37472 7.27518 1.25 6.65779 1.25 6.00083C1.25 5.34386 1.37467 4.72634 1.624 4.14828C1.87333 3.5702 2.21171 3.06736 2.63913 2.63975C3.06655 2.21213 3.56917 1.8736 4.14699 1.62416C4.7248 1.37472 5.34218 1.25 5.99915 1.25C6.65612 1.25 7.27363 1.37467 7.8517 1.624C8.42978 1.87333 8.93262 2.21171 9.36023 2.63913C9.78784 3.06655 10.1264 3.56917 10.3758 4.14699C10.6253 4.7248 10.75 5.34218 10.75 5.99915C10.75 6.65612 10.6253 7.27363 10.376 7.8517C10.1266 8.42978 9.78827 8.93262 9.36085 9.36023C8.93343 9.78784 8.43081 10.1264 7.85299 10.3758C7.27518 10.6253 6.65779 10.75 6.00082 10.75ZM5.99999 9.99999C7.11665 9.99999 8.06249 9.61249 8.83749 8.83749C9.61249 8.06249 9.99999 7.11665 9.99999 5.99999C9.99999 4.88332 9.61249 3.93749 8.83749 3.16249C8.06249 2.38749 7.11665 1.99999 5.99999 1.99999C4.88332 1.99999 3.93749 2.38749 3.16249 3.16249C2.38749 3.93749 1.99999 4.88332 1.99999 5.99999C1.99999 7.11665 2.38749 8.06249 3.16249 8.83749C3.93749 9.61249 4.88332 9.99999 5.99999 9.99999Z"
|
||||
fill="var(--color-content-brand-darker-accent-2)"
|
||||
fill="var(--color-content-default-brand-secondary)"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
@@ -69,7 +69,7 @@ export function InputWithCounterView({
|
||||
}
|
||||
}}
|
||||
maxLength={maxLength}
|
||||
className={`w-full h-[36px] px-[12px] py-[8px] bg-[var(--color-surface-default-primary)] border-2 border-[var(--color-border-default-tertiary)] rounded-[var(--measures-radius-medium,8px)] text-medium-paragraph text-[var(--color-content-default-primary)] placeholder:text-[var(--color-content-default-tertiary)] focus:outline-none ${inputClassName}`}
|
||||
className={`w-full h-[36px] px-[12px] py-[8px] bg-[var(--color-surface-default-primary)] border-2 border-[var(--color-border-default-tertiary)] rounded-[var(--radius-400)] text-medium-paragraph text-[var(--color-content-default-primary)] placeholder:text-[var(--color-content-default-tertiary)] focus:outline-none ${inputClassName}`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@ function MultiSelectView({
|
||||
|
||||
// Size-based spacing
|
||||
const gapClass = isSmall
|
||||
? "gap-[var(--measures-spacing-200,8px)]"
|
||||
: "gap-[var(--measures-spacing-300,12px)]";
|
||||
? "gap-[var(--space-200)]"
|
||||
: "gap-[var(--space-300)]";
|
||||
|
||||
const chipSize = size;
|
||||
const selectedCount = options.filter((o) => o.state === "selected").length;
|
||||
@@ -43,7 +43,7 @@ function MultiSelectView({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex flex-col ${isSmall ? "gap-[var(--measures-spacing-200,8px)]" : "gap-[var(--measures-spacing-300,12px)]"} items-start relative w-full ${className}`}
|
||||
className={`flex flex-col ${isSmall ? "gap-[var(--space-200)]" : "gap-[var(--space-300)]"} items-start relative w-full ${className}`}
|
||||
>
|
||||
{/* Label using InputLabel component */}
|
||||
{formHeader && label && (
|
||||
@@ -133,12 +133,12 @@ function MultiSelectView({
|
||||
className={
|
||||
!addButtonText
|
||||
? // Circular button with border (Rule style)
|
||||
`${atLimit ? "cursor-not-allowed opacity-60" : "cursor-pointer hover:opacity-80"} bg-[var(--color-surface-default-transparent,rgba(0,0,0,0))] border-[1.25px] ${isInverse ? "border-[var(--color-border-default-primary,#141414)]" : "border-[var(--color-border-default-tertiary,#464646)]"} border-solid flex items-center justify-center ${isSmall ? "size-[30px]" : "size-[40px]"} rounded-[var(--measures-radius-full,9999px)] shrink-0 transition-opacity`
|
||||
`${atLimit ? "cursor-not-allowed opacity-60" : "cursor-pointer hover:opacity-80"} bg-[var(--color-surface-default-transparent,rgba(0,0,0,0))] border-[1.25px] ${isInverse ? "border-[var(--color-border-default-primary,#141414)]" : "border-[var(--color-border-default-control)]"} border-solid flex items-center justify-center ${isSmall ? "size-[30px]" : "size-[40px]"} rounded-[var(--radius-full)] shrink-0 transition-opacity`
|
||||
: // Text add control (default palette: white label + brand “+”; inverse: inverse primary for both)
|
||||
`${atLimit ? "cursor-not-allowed opacity-60" : "cursor-pointer hover:opacity-80"} flex items-center justify-center overflow-hidden rounded-[var(--measures-radius-full,9999px)] shrink-0 transition-opacity ${
|
||||
`${atLimit ? "cursor-not-allowed opacity-60" : "cursor-pointer hover:opacity-80"} flex items-center justify-center overflow-hidden rounded-[var(--radius-full)] shrink-0 transition-opacity ${
|
||||
isSmall
|
||||
? "gap-[var(--measures-spacing-100,4px)] px-[var(--measures-spacing-300,12px)] py-[var(--measures-spacing-200,8px)]"
|
||||
: "gap-[var(--measures-spacing-150,6px)] px-[var(--space-400,16px)] py-[var(--measures-spacing-300,12px)]"
|
||||
? "gap-[var(--space-100)] px-[var(--space-300)] py-[var(--space-200)]"
|
||||
: "gap-[var(--space-150)] px-[var(--space-400)] py-[var(--space-300)]"
|
||||
}`
|
||||
}
|
||||
>
|
||||
@@ -151,7 +151,7 @@ function MultiSelectView({
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={`shrink-0 ${
|
||||
!addButtonText && isInverse
|
||||
? "text-[var(--color-content-inverse-primary,black)]"
|
||||
? "text-[var(--color-content-invert-primary,black)]"
|
||||
: "text-[var(--color-content-default-brand-primary,#fefcc9)]"
|
||||
}`}
|
||||
>
|
||||
@@ -165,9 +165,9 @@ function MultiSelectView({
|
||||
</svg>
|
||||
{addButtonText && (
|
||||
<span
|
||||
className={`font-medium ${isSmall ? "text-[length:var(--sizing-300,12px)] leading-[14px]" : "text-medium-label"} ${
|
||||
className={`font-medium ${isSmall ? "text-[length:var(--sizing-300)] leading-[14px]" : "text-medium-label"} ${
|
||||
isInverse
|
||||
? "text-[color:var(--color-content-inverse-primary,black)]"
|
||||
? "text-[color:var(--color-content-invert-primary,black)]"
|
||||
: "text-[color:var(--color-content-default-primary,white)]"
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -54,14 +54,14 @@ const RadioButtonContainer = ({
|
||||
// When checked, border is brand primary (but changes to invert tertiary on focus)
|
||||
const defaultBorder = checked
|
||||
? "border-[var(--color-border-default-brand-primary,#fdfaa8)]"
|
||||
: "border-[var(--color-border-default-tertiary,#464646)]";
|
||||
: "border-[var(--color-border-default-control)]";
|
||||
|
||||
// When focused and checked, border should be invert tertiary (#2d2d2d) per Figma
|
||||
const focusBorder = checked
|
||||
? "focus:border-[var(--color-content-invert-tertiary,#2d2d2d)]"
|
||||
: "focus:border-[var(--color-border-default-tertiary,#464646)]";
|
||||
: "focus:border-[var(--color-border-default-control)]";
|
||||
|
||||
return `${baseBox} bg-[var(--color-surface-default-primary)] border border-solid ${defaultBorder} hover:border-[var(--color-border-default-brand-primary,#fdfaa8)] ${focusBorder} focus:shadow-[0px_0px_0px_2px_var(--color-border-invert-primary,white),0px_0px_0px_4px_var(--color-border-default-primary,#141414)] focus:outline-none`;
|
||||
return `${baseBox} bg-[var(--color-surface-default-primary)] border border-solid ${defaultBorder} hover:border-[var(--color-border-default-brand-primary,#fdfaa8)] ${focusBorder} focus:shadow-[var(--shadow-focus-invert)] focus:outline-none`;
|
||||
}
|
||||
|
||||
// Inverse mode styles
|
||||
@@ -83,7 +83,7 @@ const RadioButtonContainer = ({
|
||||
? "focus:border-[var(--color-border-invert-primary,white)]"
|
||||
: "focus:border-[var(--color-border-invert-primary,white)]";
|
||||
|
||||
return `${baseBox} bg-transparent border border-solid ${defaultBorder} ${hoverBorder} ${focusBorder} focus:shadow-[0px_0px_0px_2px_var(--color-border-default-primary,#141414),0px_0px_0px_4px_var(--color-border-invert-primary,white)] focus:outline-none`;
|
||||
return `${baseBox} bg-transparent border border-solid ${defaultBorder} ${hoverBorder} ${focusBorder} focus:shadow-[var(--shadow-focus-default)] focus:outline-none`;
|
||||
}
|
||||
|
||||
return baseBox;
|
||||
@@ -93,7 +93,7 @@ const RadioButtonContainer = ({
|
||||
|
||||
// Label color
|
||||
const labelColor = isInverse
|
||||
? "var(--color-content-inverse-primary)"
|
||||
? "var(--color-content-invert-primary)"
|
||||
: "var(--color-content-default-primary)";
|
||||
|
||||
// Generate unique ID for accessibility if not provided
|
||||
|
||||
@@ -43,7 +43,7 @@ export function RadioGroupView({
|
||||
<span
|
||||
className={`text-small-paragraph ${
|
||||
mode === "inverse"
|
||||
? "text-[var(--color-content-inverse-primary)]"
|
||||
? "text-[var(--color-content-invert-primary)]"
|
||||
: "text-[var(--color-content-default-primary)]"
|
||||
}`}
|
||||
>
|
||||
@@ -52,7 +52,7 @@ export function RadioGroupView({
|
||||
<span
|
||||
className={`text-small-paragraph ${
|
||||
mode === "inverse"
|
||||
? "text-[var(--color-content-inverse-secondary,#1f1f1f)]"
|
||||
? "text-[var(--color-content-invert-secondary,#1f1f1f)]"
|
||||
: "text-[var(--color-content-default-secondary,#b4b4b4)]"
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -11,9 +11,9 @@ interface SelectDropdownProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
const SelectDropdown = forwardRef<HTMLDivElement, SelectDropdownProps>(
|
||||
({ className = "", children, ariaLabel, ...props }, ref) => {
|
||||
const menuClasses = `
|
||||
bg-black
|
||||
bg-[var(--color-surface-default-primary)]
|
||||
border border-[var(--color-border-default-tertiary)]
|
||||
rounded-[var(--measures-radius-medium)]
|
||||
rounded-[var(--radius-400)]
|
||||
shadow-lg
|
||||
p-[4px]
|
||||
min-w-[200px]
|
||||
@@ -29,7 +29,6 @@ const SelectDropdown = forwardRef<HTMLDivElement, SelectDropdownProps>(
|
||||
className={menuClasses}
|
||||
role="listbox"
|
||||
aria-label={ariaLabel}
|
||||
style={{ backgroundColor: "#000000" }}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -109,7 +109,7 @@ export function SelectInputView({
|
||||
.replace(/\s+/g, " ");
|
||||
|
||||
if (disabled) {
|
||||
return `${baseClasses} bg-[var(--color-surface-default-secondary)] text-[var(--color-content-inverse-tertiary,#2d2d2d)] border-[var(--color-border-default-primary)] cursor-not-allowed opacity-40`;
|
||||
return `${baseClasses} bg-[var(--color-surface-default-secondary)] text-[var(--color-content-invert-tertiary,#2d2d2d)] border-[var(--color-border-default-primary)] cursor-not-allowed opacity-40`;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
@@ -118,16 +118,16 @@ export function SelectInputView({
|
||||
|
||||
if (state === "focus") {
|
||||
// Focus state: secondary background, tertiary border, with focus ring
|
||||
return `${baseClasses} bg-[var(--color-surface-default-secondary)] text-[var(--color-content-default-primary)] border border-solid border-[var(--color-border-default-tertiary)]`;
|
||||
return `${baseClasses} bg-[var(--color-surface-default-secondary)] text-[var(--color-content-default-primary)] border border-solid border-[var(--color-border-default-control)]`;
|
||||
}
|
||||
|
||||
if (state === "active" || isOpen) {
|
||||
// Active state per Figma: secondary background, tertiary border
|
||||
return `${baseClasses} bg-[var(--color-surface-default-secondary)] text-[var(--color-content-default-primary)] border-[var(--color-border-default-tertiary)]`;
|
||||
return `${baseClasses} bg-[var(--color-surface-default-secondary)] text-[var(--color-content-default-primary)] border-[var(--color-border-default-control)]`;
|
||||
}
|
||||
|
||||
// Default state per Figma: secondary background, primary border (subtle)
|
||||
return `${baseClasses} bg-[var(--color-surface-default-secondary)] text-[var(--color-content-default-primary)] border-[var(--color-border-default-primary)]`;
|
||||
return `${baseClasses} bg-[var(--color-surface-default-secondary)] text-[var(--color-content-default-primary)] border-[var(--color-border-default-control)]`;
|
||||
};
|
||||
|
||||
const buttonClasses = getButtonClasses();
|
||||
@@ -145,8 +145,8 @@ export function SelectInputView({
|
||||
return (
|
||||
<div className={containerClasses}>
|
||||
{label && (
|
||||
<div className="flex flex-wrap gap-[var(--measures-spacing-200,4px_8px)] items-baseline pr-[var(--measures-spacing-100,4px)] relative shrink-0 w-full">
|
||||
<div className="flex gap-[var(--measures-spacing-050,2px)] items-center relative shrink-0">
|
||||
<div className="flex flex-wrap gap-[var(--space-100)_var(--space-200)] items-baseline pr-[var(--space-100)] relative shrink-0 w-full">
|
||||
<div className="flex gap-[var(--space-050)] items-center relative shrink-0">
|
||||
<label id={labelId} className={labelClasses}>
|
||||
{label}
|
||||
</label>
|
||||
@@ -214,7 +214,7 @@ export function SelectInputView({
|
||||
</button>
|
||||
{state === "focus" && (
|
||||
<div
|
||||
className="absolute border-2 border-solid border-[var(--color-border-inverse-primary)] inset-0 rounded-[8px] shadow-[0px_0px_0px_2px_var(--color-border-default-primary)] pointer-events-none z-10"
|
||||
className="absolute border-2 border-solid border-[var(--color-border-invert-primary)] inset-0 rounded-[8px] shadow-[0px_0px_0px_2px_var(--color-border-default-primary)] pointer-events-none z-10"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
)}
|
||||
@@ -269,7 +269,13 @@ export function SelectInputView({
|
||||
</div>
|
||||
{textHint && (
|
||||
<div className="flex items-start relative shrink-0 w-full">
|
||||
<p className="flex-[1_0_0] text-x-small-paragraph min-h-px min-w-px relative text-[color:var(--color-content-default-tertiary,#b4b4b4)]">
|
||||
<p
|
||||
className={`flex-[1_0_0] text-x-small-paragraph min-h-px min-w-px relative ${
|
||||
ariaInvalid
|
||||
? "text-[color:var(--color-content-default-negative-primary)]"
|
||||
: "text-[color:var(--color-content-default-tertiary)]"
|
||||
}`}
|
||||
>
|
||||
{hintDefault}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -44,13 +44,13 @@ const SelectOptionContainer = forwardRef<HTMLDivElement, SelectOptionProps>(
|
||||
transition-colors duration-150
|
||||
${
|
||||
selected
|
||||
? "bg-[var(--color-surface-default-secondary)] rounded-[var(--measures-radius-small)]"
|
||||
? "bg-[var(--color-surface-default-secondary)] rounded-[var(--radius-300)]"
|
||||
: ""
|
||||
}
|
||||
${
|
||||
disabled
|
||||
? "opacity-50 cursor-not-allowed"
|
||||
: "hover:!bg-[var(--color-surface-default-secondary)] hover:!rounded-[var(--measures-radius-small)]"
|
||||
: "hover:!bg-[var(--color-surface-default-secondary)] hover:!rounded-[var(--radius-300)]"
|
||||
}
|
||||
${className}
|
||||
`
|
||||
|
||||
@@ -69,7 +69,7 @@ const SwitchContainer = memo(
|
||||
// Switch track styles based on propSwitch state
|
||||
const getTrackStyles = useCallback(() => {
|
||||
return propSwitch
|
||||
? "bg-[var(--color-surface-inverse-tertiary)]"
|
||||
? "bg-[var(--color-surface-invert-tertiary)]"
|
||||
: "bg-[var(--color-surface-default-tertiary)]";
|
||||
}, [propSwitch]);
|
||||
|
||||
@@ -81,7 +81,7 @@ const SwitchContainer = memo(
|
||||
// Focus styles
|
||||
const getFocusStyles = useCallback(() => {
|
||||
if (state === "focus") {
|
||||
return "shadow-[0_0_5px_3px_#3281F8] rounded-full";
|
||||
return "shadow-[var(--shadow-focus-default)] rounded-full";
|
||||
}
|
||||
return "";
|
||||
}, [state]);
|
||||
@@ -98,7 +98,7 @@ const SwitchContainer = memo(
|
||||
transition-all
|
||||
duration-200
|
||||
focus:outline-none
|
||||
focus-visible:shadow-[0_0_5px_3px_#3281F8]
|
||||
focus-visible:shadow-[var(--shadow-focus-default)]
|
||||
focus-visible:rounded-full
|
||||
${focusStyles}
|
||||
${className}
|
||||
@@ -123,9 +123,9 @@ const SwitchContainer = memo(
|
||||
|
||||
const thumbClasses = `
|
||||
${thumbStyles}
|
||||
w-[var(--measures-sizing-020)]
|
||||
h-[var(--measures-sizing-020)]
|
||||
rounded-[var(--measures-radius-xlarge)]
|
||||
w-[var(--space-500)]
|
||||
h-[var(--space-500)]
|
||||
rounded-[var(--radius-500)]
|
||||
transition-all
|
||||
duration-200
|
||||
shadow-sm
|
||||
@@ -134,7 +134,7 @@ const SwitchContainer = memo(
|
||||
.replace(/\s+/g, " ");
|
||||
|
||||
const labelClasses = `
|
||||
ml-[var(--measures-spacing-008)]
|
||||
ml-[var(--space-200)]
|
||||
text-small-paragraph
|
||||
text-[var(--color-content-default-primary)]
|
||||
`
|
||||
|
||||
@@ -61,7 +61,7 @@ const TextAreaContainer = forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
||||
: "h-[60px] px-[12px] py-[8px] text-xx-small-paragraph",
|
||||
label: "text-x-small-label",
|
||||
container: "gap-[4px]",
|
||||
radius: "var(--measures-radius-xsmall)",
|
||||
radius: "var(--radius-200)",
|
||||
},
|
||||
medium: {
|
||||
textarea:
|
||||
@@ -70,13 +70,13 @@ const TextAreaContainer = forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
||||
: "h-[100px] px-[12px] py-[8px] text-small-paragraph",
|
||||
label: "text-small-label",
|
||||
container: "gap-[8px]",
|
||||
radius: "var(--measures-radius-xsmall)",
|
||||
radius: "var(--radius-200)",
|
||||
},
|
||||
large: {
|
||||
textarea: "h-[150px] px-[12px] py-[8px] text-medium-paragraph",
|
||||
label: "text-medium-label",
|
||||
container: "gap-[12px]",
|
||||
radius: "var(--measures-radius-small)",
|
||||
radius: "var(--radius-300)",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -90,13 +90,13 @@ const TextAreaContainer = forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
||||
if (disabled) {
|
||||
return {
|
||||
textarea:
|
||||
"border-0 bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-tertiary,#b4b4b4)] cursor-not-allowed opacity-60 min-h-[356px] h-[356px] px-[var(--measures-spacing-300,12px)] py-[var(--space-250,10px)]",
|
||||
"border-0 bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-tertiary,#b4b4b4)] cursor-not-allowed opacity-60 min-h-[356px] h-[356px] px-[var(--space-300)] py-[var(--space-250)]",
|
||||
label: "text-[var(--color-content-default-secondary)]",
|
||||
};
|
||||
}
|
||||
return {
|
||||
textarea:
|
||||
"border-0 bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] placeholder:text-[var(--color-content-default-tertiary,#b4b4b4)] min-h-[356px] h-[356px] px-[var(--measures-spacing-300,12px)] py-[var(--space-250,10px)] focus:outline-none focus:ring-0",
|
||||
"border-0 bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] placeholder:text-[var(--color-content-default-tertiary,#b4b4b4)] min-h-[356px] h-[356px] px-[var(--space-300)] py-[var(--space-250)] focus:outline-none focus:ring-0",
|
||||
label: "text-[var(--color-content-default-secondary)]",
|
||||
};
|
||||
}
|
||||
@@ -111,7 +111,7 @@ const TextAreaContainer = forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
||||
}
|
||||
return {
|
||||
textarea:
|
||||
"border-0 bg-[var(--color-surface-default-secondary)] text-[var(--color-content-default-tertiary,#b4b4b4)] placeholder:text-[var(--color-content-default-tertiary,#b4b4b4)] focus:text-[var(--color-content-default-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-default-tertiary)] focus:ring-inset",
|
||||
"border-0 bg-[var(--color-surface-default-secondary)] text-[var(--color-content-default-tertiary,#b4b4b4)] placeholder:text-[var(--color-content-default-tertiary,#b4b4b4)] focus:text-[var(--color-content-default-primary)] focus:outline-none focus:ring-2 focus:ring-[var(--color-border-default-control)] focus:ring-inset",
|
||||
label: "text-[var(--color-content-default-secondary)]",
|
||||
};
|
||||
}
|
||||
@@ -119,7 +119,7 @@ const TextAreaContainer = forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
||||
if (disabled) {
|
||||
return {
|
||||
textarea:
|
||||
"bg-[var(--color-content-default-secondary)] text-[var(--color-content-default-primary)] border border-[var(--color-border-default-tertiary)] cursor-not-allowed",
|
||||
"bg-[var(--color-content-default-secondary)] text-[var(--color-content-default-primary)] border border-[var(--color-border-default-control)] cursor-not-allowed",
|
||||
label: "text-[var(--color-content-default-secondary)]",
|
||||
};
|
||||
}
|
||||
@@ -136,25 +136,25 @@ const TextAreaContainer = forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
||||
case "active":
|
||||
return {
|
||||
textarea:
|
||||
"bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] border border-[var(--color-border-default-tertiary)]",
|
||||
"bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] border border-[var(--color-border-default-control)]",
|
||||
label: "text-[var(--color-content-default-secondary)]",
|
||||
};
|
||||
case "hover":
|
||||
return {
|
||||
textarea:
|
||||
"bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] border border-[var(--color-border-default-tertiary)] shadow-[0_0_0_2px_var(--color-border-default-tertiary)]",
|
||||
"bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] border border-[var(--color-border-default-control)] shadow-[0_0_0_2px_var(--color-border-default-control)]",
|
||||
label: "text-[var(--color-content-default-secondary)]",
|
||||
};
|
||||
case "focus":
|
||||
return {
|
||||
textarea:
|
||||
"bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] border border-[var(--color-border-default-utility-info)] shadow-[0_0_5px_3px_#3281F8]",
|
||||
"bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] border border-[var(--color-border-default-control)] shadow-[var(--shadow-focus-default)]",
|
||||
label: "text-[var(--color-content-default-secondary)]",
|
||||
};
|
||||
default:
|
||||
return {
|
||||
textarea:
|
||||
"bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] border border-[var(--color-border-default-tertiary)] hover:shadow-[0_0_0_2px_var(--color-border-default-tertiary)]",
|
||||
"bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] border border-[var(--color-border-default-control)] hover:shadow-[0_0_0_2px_var(--color-border-default-control)]",
|
||||
label: "text-[var(--color-content-default-secondary)]",
|
||||
};
|
||||
}
|
||||
@@ -176,7 +176,7 @@ const TextAreaContainer = forwardRef<HTMLTextAreaElement, TextAreaProps>(
|
||||
|
||||
const textareaClasses = `
|
||||
scrollbar-design w-full transition-all duration-200 ease-in-out resize-none
|
||||
${appearance === "embedded" || appearance === "compose" ? "rounded-[var(--radius-300,12px)]" : "border"}
|
||||
${appearance === "embedded" || appearance === "compose" ? "rounded-[var(--radius-300)]" : "border"}
|
||||
${appearance === "compose" ? "text-medium-paragraph" : currentSize.textarea}
|
||||
${stateStyles.textarea}
|
||||
${className}
|
||||
|
||||
@@ -31,15 +31,15 @@ export const TextAreaView = forwardRef<HTMLTextAreaElement, TextAreaViewProps>(
|
||||
size: _size,
|
||||
labelVariant: _labelVariant,
|
||||
state: _state,
|
||||
error: _error,
|
||||
error = false,
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const labelEl = label ? (
|
||||
formHeader ? (
|
||||
<div className="flex flex-wrap gap-[var(--measures-spacing-200,4px_8px)] items-baseline pr-[var(--measures-spacing-100,4px)] relative shrink-0 w-full">
|
||||
<div className="flex gap-[var(--measures-spacing-050,2px)] items-center relative shrink-0">
|
||||
<div className="flex flex-wrap gap-[var(--space-100)_var(--space-200)] items-baseline pr-[var(--space-100)] relative shrink-0 w-full">
|
||||
<div className="flex gap-[var(--space-050)] items-center relative shrink-0">
|
||||
<label
|
||||
id={labelId}
|
||||
htmlFor={textareaId}
|
||||
@@ -90,7 +90,13 @@ export const TextAreaView = forwardRef<HTMLTextAreaElement, TextAreaViewProps>(
|
||||
</div>
|
||||
{textHint ? (
|
||||
<div className="flex items-start relative shrink-0 w-full">
|
||||
<p className="flex-[1_0_0] text-x-small-paragraph min-h-px min-w-px relative text-[color:var(--color-content-default-tertiary,#b4b4b4)]">
|
||||
<p
|
||||
className={`flex-[1_0_0] text-x-small-paragraph min-h-px min-w-px relative ${
|
||||
error
|
||||
? "text-[color:var(--color-content-default-negative-primary)]"
|
||||
: "text-[color:var(--color-content-default-tertiary)]"
|
||||
}`}
|
||||
>
|
||||
{typeof textHint === "string" ? textHint : hintDefault}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -83,13 +83,13 @@ const TextInputContainer = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
"h-[32px] px-[10px] py-[6px] text-[16px] md:text-[14px]",
|
||||
label: "text-[12px] leading-[16px] font-medium",
|
||||
container: "gap-[6px]",
|
||||
radius: "var(--measures-radius-200,8px)",
|
||||
radius: "var(--radius-200)",
|
||||
}
|
||||
: {
|
||||
input: "h-[40px] px-[12px] py-[8px] text-[16px]",
|
||||
label: "text-[14px] leading-[20px] font-medium",
|
||||
container: "gap-[8px]",
|
||||
radius: "var(--measures-radius-200,8px)",
|
||||
radius: "var(--radius-200)",
|
||||
};
|
||||
|
||||
// State styles based on Figma designs
|
||||
@@ -102,7 +102,7 @@ const TextInputContainer = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
if (disabled) {
|
||||
return {
|
||||
input:
|
||||
"bg-[var(--color-surface-default-secondary)] text-[var(--color-content-inverse-tertiary,#2d2d2d)] border border-solid border-[var(--color-border-default-primary)] cursor-not-allowed",
|
||||
"bg-[var(--color-surface-default-secondary)] text-[var(--color-content-invert-tertiary,#2d2d2d)] border border-solid border-[var(--color-border-default-primary)] cursor-not-allowed",
|
||||
label: "text-[var(--color-content-default-secondary)]",
|
||||
inputWrapper: "relative",
|
||||
focusRing: "",
|
||||
@@ -127,7 +127,7 @@ const TextInputContainer = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
? "font-medium leading-[20px]"
|
||||
: "font-normal leading-[24px]";
|
||||
return {
|
||||
input: `bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] border-2 border-solid border-[var(--color-border-default-tertiary)] ${filledStyles}`,
|
||||
input: `bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] border-2 border-solid border-[var(--color-border-default-control)] ${filledStyles}`,
|
||||
label: "text-[var(--color-content-default-secondary)]",
|
||||
inputWrapper: "relative",
|
||||
focusRing: "",
|
||||
@@ -138,11 +138,11 @@ const TextInputContainer = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
? "font-medium leading-[20px]"
|
||||
: "font-normal leading-[24px]";
|
||||
return {
|
||||
input: `bg-[var(--color-surface-default-secondary)] text-[var(--color-content-default-primary)] border border-solid border-[var(--color-border-default-tertiary)] ${filledStyles}`,
|
||||
input: `bg-[var(--color-surface-default-secondary)] text-[var(--color-content-default-primary)] border border-solid border-[var(--color-border-default-control)] ${filledStyles}`,
|
||||
label: "text-[var(--color-content-default-secondary)]",
|
||||
inputWrapper: "relative",
|
||||
focusRing:
|
||||
"absolute border-2 border-solid border-[var(--color-border-inverse-primary)] inset-0 rounded-[var(--measures-radius-200,8px)] shadow-[0px_0px_0px_2px_var(--color-border-default-primary)] pointer-events-none",
|
||||
"absolute inset-0 rounded-[var(--radius-200)] shadow-[var(--shadow-focus-default)] pointer-events-none",
|
||||
};
|
||||
}
|
||||
default: {
|
||||
@@ -151,7 +151,7 @@ const TextInputContainer = forwardRef<HTMLInputElement, TextInputProps>(
|
||||
: "font-normal leading-[24px]";
|
||||
// Default state uses primary border (matches Figma - border color same as background, so border is subtle)
|
||||
return {
|
||||
input: `bg-[var(--color-surface-default-secondary)] text-[var(--color-content-default-primary)] border border-solid border-[var(--color-border-default-primary)] ${filledStyles}`,
|
||||
input: `bg-[var(--color-surface-default-secondary)] text-[var(--color-content-default-primary)] border border-solid border-[var(--color-border-default-control)] ${filledStyles}`,
|
||||
label: "text-[var(--color-content-default-secondary)]",
|
||||
inputWrapper: "relative",
|
||||
focusRing: "",
|
||||
|
||||
@@ -43,8 +43,8 @@ export const TextInputView = forwardRef<HTMLInputElement, TextInputViewProps>(
|
||||
) => {
|
||||
const labelEl = label ? (
|
||||
formHeader ? (
|
||||
<div className="flex flex-wrap gap-[var(--measures-spacing-200,4px_8px)] items-baseline pr-[var(--measures-spacing-100,4px)] relative shrink-0 w-full">
|
||||
<div className="flex gap-[var(--measures-spacing-050,2px)] items-center relative shrink-0">
|
||||
<div className="flex flex-wrap gap-[var(--space-100)_var(--space-200)] items-baseline pr-[var(--space-100)] relative shrink-0 w-full">
|
||||
<div className="flex gap-[var(--space-050)] items-center relative shrink-0">
|
||||
<label
|
||||
id={labelId}
|
||||
htmlFor={inputId}
|
||||
@@ -106,7 +106,13 @@ export const TextInputView = forwardRef<HTMLInputElement, TextInputViewProps>(
|
||||
</div>
|
||||
{textHint && (
|
||||
<div className="flex items-start relative shrink-0 w-full">
|
||||
<p className="flex-[1_0_0] text-x-small-paragraph min-h-px min-w-px relative text-[color:var(--color-content-default-tertiary,#b4b4b4)]">
|
||||
<p
|
||||
className={`flex-[1_0_0] text-x-small-paragraph min-h-px min-w-px relative ${
|
||||
error
|
||||
? "text-[color:var(--color-content-default-negative-primary)]"
|
||||
: "text-[color:var(--color-content-default-tertiary)]"
|
||||
}`}
|
||||
>
|
||||
{typeof textHint === "string" ? textHint : hintDefault}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -33,7 +33,7 @@ const ToggleContainer = forwardRef<HTMLButtonElement, ToggleProps>(
|
||||
|
||||
// Size styles - single size with specific dimensions
|
||||
const sizeStyles = {
|
||||
toggle: "h-[var(--measures-sizing-032)] px-[16px] py-[8px] gap-[4px]",
|
||||
toggle: "h-[var(--space-800)] px-[16px] py-[8px] gap-[4px]",
|
||||
label: "text-[12px] leading-[16px]",
|
||||
};
|
||||
|
||||
@@ -61,7 +61,7 @@ const ToggleContainer = forwardRef<HTMLButtonElement, ToggleProps>(
|
||||
case "focus":
|
||||
return {
|
||||
toggle:
|
||||
"bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] shadow-[0_0_5px_1px_#3281F8]",
|
||||
"bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] shadow-[var(--shadow-focus-default)]",
|
||||
label: "text-[var(--color-content-default-secondary)]",
|
||||
};
|
||||
default:
|
||||
@@ -82,7 +82,7 @@ const ToggleContainer = forwardRef<HTMLButtonElement, ToggleProps>(
|
||||
case "focus":
|
||||
return {
|
||||
toggle:
|
||||
"bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] shadow-[0_0_5px_1px_#3281F8]",
|
||||
"bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] shadow-[var(--shadow-focus-default)]",
|
||||
label: "text-[var(--color-content-default-secondary)]",
|
||||
};
|
||||
default:
|
||||
@@ -112,7 +112,7 @@ const ToggleContainer = forwardRef<HTMLButtonElement, ToggleProps>(
|
||||
transition-all
|
||||
duration-200
|
||||
focus:outline-none
|
||||
focus-visible:shadow-[0_0_5px_1px_#3281F8]
|
||||
focus-visible:shadow-[var(--shadow-focus-default)]
|
||||
${!checked ? "hover:!bg-[var(--color-surface-default-secondary)]" : ""}
|
||||
flex
|
||||
items-center
|
||||
|
||||
@@ -34,13 +34,13 @@ const ToggleGroupContainer = memo(
|
||||
const getPositionStyles = useCallback((pos: string): string => {
|
||||
switch (pos) {
|
||||
case "left":
|
||||
return "rounded-l-[var(--measures-radius-medium)] rounded-r-none";
|
||||
return "rounded-l-[var(--radius-400)] rounded-r-none";
|
||||
case "middle":
|
||||
return "rounded-none";
|
||||
case "right":
|
||||
return "rounded-r-[var(--measures-radius-medium)] rounded-l-none";
|
||||
return "rounded-r-[var(--radius-400)] rounded-l-none";
|
||||
default:
|
||||
return "rounded-[var(--measures-radius-medium)]";
|
||||
return "rounded-[var(--radius-400)]";
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -50,7 +50,7 @@ const ToggleGroupContainer = memo(
|
||||
case "hover":
|
||||
return "bg-[var(--color-magenta-magenta100)] text-[var(--color-content-default-primary)]";
|
||||
case "focus":
|
||||
return "bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] shadow-[0_0_5px_1px_#3281F8]";
|
||||
return "bg-[var(--color-surface-default-primary)] text-[var(--color-content-default-primary)] shadow-[var(--shadow-focus-default)]";
|
||||
case "selected":
|
||||
return "bg-[var(--color-magenta-magenta100)] text-[var(--color-content-default-primary)] shadow-[inset_0_0_0_1px_var(--color-border-default-secondary)]";
|
||||
case "default":
|
||||
@@ -104,9 +104,9 @@ const ToggleGroupContainer = memo(
|
||||
const toggleClasses = `
|
||||
${positionStyles}
|
||||
${stateStyles}
|
||||
py-[var(--measures-spacing-008)]
|
||||
px-[var(--measures-spacing-008)]
|
||||
gap-[var(--measures-spacing-008)]
|
||||
py-[var(--space-200)]
|
||||
px-[var(--space-200)]
|
||||
gap-[var(--space-200)]
|
||||
font-medium
|
||||
text-[12px]
|
||||
leading-[12px]
|
||||
@@ -114,7 +114,7 @@ const ToggleGroupContainer = memo(
|
||||
transition-all
|
||||
duration-200
|
||||
focus:outline-none
|
||||
focus-visible:shadow-[0_0_5px_1px_#3281F8]
|
||||
focus-visible:shadow-[var(--shadow-focus-default)]
|
||||
hover:bg-[var(--color-magenta-magenta100)]
|
||||
flex
|
||||
items-center
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { memo } from "react";
|
||||
import { useTranslation } from "../../../contexts/MessagesContext";
|
||||
import UploadView from "./Upload.view";
|
||||
import type { UploadStateValue } from "../../../../lib/propNormalization";
|
||||
import type { UploadProps } from "./Upload.types";
|
||||
|
||||
/**
|
||||
@@ -12,6 +13,8 @@ import type { UploadProps } from "./Upload.types";
|
||||
const UploadContainer = memo<UploadProps>(
|
||||
({
|
||||
active = true,
|
||||
state,
|
||||
hasLabel,
|
||||
label,
|
||||
showHelpIcon = true,
|
||||
hintText,
|
||||
@@ -19,10 +22,13 @@ const UploadContainer = memo<UploadProps>(
|
||||
className = "",
|
||||
}) => {
|
||||
const t = useTranslation("controlsChrome");
|
||||
const resolvedState: UploadStateValue =
|
||||
state ?? (active ? "default" : "disabled");
|
||||
|
||||
return (
|
||||
<UploadView
|
||||
active={active}
|
||||
state={resolvedState}
|
||||
hasLabel={hasLabel ?? Boolean(label)}
|
||||
label={label}
|
||||
showHelpIcon={showHelpIcon}
|
||||
hintText={hintText ?? t("uploadHintDefault")}
|
||||
|
||||
@@ -1,11 +1,23 @@
|
||||
import type { UploadStateValue } from "../../../../lib/propNormalization";
|
||||
|
||||
export interface UploadProps {
|
||||
/**
|
||||
* Whether the upload component is in active state.
|
||||
* When active, button has white background with black text.
|
||||
* When inactive, button has dark background with gray text.
|
||||
* Prefer `state`. `active={false}` maps to `disabled`.
|
||||
* @default true
|
||||
*/
|
||||
active?: boolean;
|
||||
/**
|
||||
* Figma state axis. Disabled dims the control and turns the button off.
|
||||
*/
|
||||
state?: UploadStateValue;
|
||||
/**
|
||||
* Show the label. The upload step uses the control with this off.
|
||||
* Defaults to true when `label` is set.
|
||||
*/
|
||||
hasLabel?: boolean;
|
||||
/**
|
||||
* Label text displayed above the upload component
|
||||
*/
|
||||
@@ -31,7 +43,8 @@ export interface UploadProps {
|
||||
}
|
||||
|
||||
export interface UploadViewProps {
|
||||
active: boolean;
|
||||
state: UploadStateValue;
|
||||
hasLabel: boolean;
|
||||
label?: string;
|
||||
showHelpIcon: boolean;
|
||||
hintText: string;
|
||||
|
||||
@@ -5,7 +5,8 @@ import InputLabel from "../../type/InputLabel";
|
||||
import type { UploadViewProps } from "./Upload.types";
|
||||
|
||||
function UploadView({
|
||||
active = true,
|
||||
state,
|
||||
hasLabel,
|
||||
label,
|
||||
showHelpIcon = true,
|
||||
hintText,
|
||||
@@ -14,7 +15,8 @@ function UploadView({
|
||||
onClick,
|
||||
className = "",
|
||||
}: UploadViewProps) {
|
||||
const isActive = active;
|
||||
const isActive = state !== "disabled";
|
||||
const isDisabled = state === "disabled";
|
||||
|
||||
// Button styles based on active state
|
||||
const buttonBgClass = isActive
|
||||
@@ -37,10 +39,10 @@ function UploadView({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`flex flex-col gap-[var(--measures-spacing-300,12px)] items-start relative w-full ${className}`}
|
||||
className={`flex flex-col gap-[var(--space-300)] items-start relative w-full ${className}`}
|
||||
>
|
||||
{/* Label using InputLabel component */}
|
||||
{label && (
|
||||
{hasLabel && label ? (
|
||||
<InputLabel
|
||||
label={label}
|
||||
helpIcon={showHelpIcon}
|
||||
@@ -49,15 +51,24 @@ function UploadView({
|
||||
size="s"
|
||||
palette="default"
|
||||
/>
|
||||
)}
|
||||
) : null}
|
||||
|
||||
{/* Upload container — max width per create-flow spec */}
|
||||
<div className="bg-[var(--color-surface-default-secondary,#141414)] mx-auto flex w-full max-w-[474px] shrink-0 items-center justify-center gap-[24px] rounded-[var(--measures-radius-200,8px)] px-[var(--measures-spacing-600,24px)] py-[var(--measures-spacing-1200,48px)]">
|
||||
<div
|
||||
className={`bg-[var(--color-surface-default-secondary,#141414)] mx-auto flex w-full max-w-[474px] shrink-0 items-center justify-center gap-[24px] rounded-[var(--radius-200)] px-[var(--space-600)] py-[var(--space-1200)] ${
|
||||
state === "error"
|
||||
? "border border-[var(--color-border-default-utility-negative)]"
|
||||
: ""
|
||||
} ${state === "focus" ? "shadow-[var(--shadow-focus-default)]" : ""}`}
|
||||
>
|
||||
{/* Upload button */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
className={`${buttonBgClass} flex gap-[var(--measures-spacing-150,6px)] items-center justify-center overflow-clip px-[var(--space-400,16px)] py-[var(--measures-spacing-300,12px)] rounded-[var(--measures-radius-full,9999px)] shrink-0 hover:opacity-80 transition-opacity`}
|
||||
disabled={isDisabled}
|
||||
className={`${buttonBgClass} flex gap-[var(--space-150)] items-center justify-center overflow-clip px-[var(--space-400)] py-[var(--space-300)] rounded-[var(--radius-full)] shrink-0 hover:opacity-80 transition-opacity ${
|
||||
state === "hover" ? "opacity-80" : ""
|
||||
} ${state === "active" ? "scale-[0.98]" : ""}`}
|
||||
aria-label={uploadAriaLabel}
|
||||
>
|
||||
{/* Upload icon */}
|
||||
@@ -98,7 +109,7 @@ function UploadView({
|
||||
</div>
|
||||
{/* Button text */}
|
||||
<div
|
||||
className={`flex flex-col font-medium justify-center leading-[0] relative shrink-0 text-[length:var(--sizing-400,16px)] whitespace-nowrap ${buttonTextColor}`}
|
||||
className={`flex flex-col font-medium justify-center leading-[0] relative shrink-0 text-[length:var(--sizing-400)] whitespace-nowrap ${buttonTextColor}`}
|
||||
>
|
||||
<p className="leading-[20px]">{uploadButtonLabel}</p>
|
||||
</div>
|
||||
@@ -106,7 +117,7 @@ function UploadView({
|
||||
|
||||
{/* Description text */}
|
||||
<div
|
||||
className={`flex flex-[1_0_0] flex-col font-normal h-[32px] justify-center leading-[0] min-h-px min-w-px relative text-[length:var(--sizing-350,14px)] ${descriptionTextColor}`}
|
||||
className={`flex flex-[1_0_0] flex-col font-normal h-[32px] justify-center leading-[0] min-h-px min-w-px relative text-[length:var(--sizing-350)] ${descriptionTextColor}`}
|
||||
>
|
||||
<p className="leading-[20px] whitespace-pre-wrap">{hintText}</p>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user