Lynx
Checkbox
사용자가 하나 이상의 옵션을 선택할 수 있게 해주는 컴포넌트입니다. 목록에서 여러 항목을 선택하거나 약관 동의와 같은 선택적 작업에 사용됩니다.
사용 가능 버전@seed-design/[email protected], @seed-design/[email protected]
Lynx 예제를 불러오는 중입니다.
import "./styles";
import { VStack } from "@seed-design/lynx-react";
import { Checkbox, CheckboxGroup } from "@/components/ui/checkbox";
export default function Example() {
return (
<VStack className="checkbox-preview">
<CheckboxGroup
label="관심 분야"
description="관심 있는 분야를 모두 선택해 주세요."
indicator="선택"
>
<Checkbox label="디자인" tone="neutral" size="large" />
<Checkbox label="개발" tone="neutral" size="large" defaultChecked />
<Checkbox label="마케팅" tone="neutral" size="large" />
</CheckboxGroup>
</VStack>
);
}Installation
npx @seed-design/cli@latest add ui:checkboxpnpm dlx @seed-design/cli@latest add ui:checkboxyarn dlx @seed-design/cli@latest add ui:checkboxbun x @seed-design/cli@latest add ui:checkbox의존성 설치
npm install @karrotmarket/lynx-monochrome-icon @seed-design/lynx-reactyarn add @karrotmarket/lynx-monochrome-icon @seed-design/lynx-reactpnpm add @karrotmarket/lynx-monochrome-icon @seed-design/lynx-reactbun add @karrotmarket/lynx-monochrome-icon @seed-design/lynx-react아래 코드를 복사 후 붙여넣고 사용하세요
/**
* @file ui:checkbox
* @requires @seed-design/lynx-react@^1.0.0
* @requires @seed-design/lynx-css@^1.0.0
**/
import * as React from "@lynx-js/react";
import IconCheckmarkFatFill from "@karrotmarket/lynx-monochrome-icon/IconCheckmarkFatFill";
import IconMinusFatFill from "@karrotmarket/lynx-monochrome-icon/IconMinusFatFill";
import { Checkbox as SeedCheckbox, Field as SeedField } from "@seed-design/lynx-react";
type FieldRootRef = React.ComponentRef<typeof SeedField.Root>;
export interface CheckboxProps extends SeedCheckbox.RootProps {
label?: React.ReactNode;
}
/**
* @see https://seed-design.io/lynx/components/checkbox
*/
export const Checkbox = React.forwardRef<unknown, CheckboxProps>(
({ label, children, "accessibility-label": accessibilityLabel, ...otherProps }, ref) => {
return (
<SeedCheckbox.Root
ref={ref}
accessibility-label={accessibilityLabel ?? (typeof label === "string" ? label : undefined)}
{...otherProps}
>
<SeedCheckbox.Control>
<SeedCheckbox.Indicator
unchecked={otherProps.variant === "ghost" ? <IconCheckmarkFatFill /> : undefined}
checked={<IconCheckmarkFatFill />}
indeterminate={<IconMinusFatFill />}
/>
</SeedCheckbox.Control>
{label != null ? <SeedCheckbox.Label>{label}</SeedCheckbox.Label> : null}
{children}
</SeedCheckbox.Root>
);
},
);
Checkbox.displayName = "Checkbox";
export interface CheckmarkProps extends Omit<SeedCheckbox.ControlProps, "children"> {}
/**
* 상위 `Checkbox.Root`나 `ListCheckItem`의 상태를 표시하는 체크 마크입니다.
*
* @see https://seed-design.io/lynx/components/checkbox
*/
export const Checkmark = React.forwardRef<unknown, CheckmarkProps>((props, ref) => {
return (
<SeedCheckbox.Control ref={ref} {...props}>
<SeedCheckbox.Indicator
unchecked={props.variant === "ghost" ? <IconCheckmarkFatFill /> : undefined}
checked={<IconCheckmarkFatFill />}
indeterminate={<IconMinusFatFill />}
/>
</SeedCheckbox.Control>
);
});
Checkmark.displayName = "Checkmark";
export interface CheckboxGroupProps
extends SeedCheckbox.GroupProps,
Pick<SeedField.RootProps, "required" | "invalid" | "readOnly"> {
label?: React.ReactNode;
/**
* @default "medium"
*/
labelWeight?: SeedField.LabelProps["weight"];
indicator?: React.ReactNode;
showRequiredIndicator?: boolean;
description?: React.ReactNode;
errorMessage?: React.ReactNode;
}
/**
* @see https://seed-design.io/lynx/components/checkbox
*/
export const CheckboxGroup = React.forwardRef<FieldRootRef, CheckboxGroupProps>(
(
{
label,
labelWeight,
indicator,
showRequiredIndicator,
description,
errorMessage,
children,
...fieldProps
},
ref,
) => {
const renderHeader = label != null || indicator != null;
const renderErrorMessage = errorMessage != null;
const renderDescription = description != null && !renderErrorMessage;
const renderFooter = renderDescription || renderErrorMessage;
return (
<SeedField.Root ref={ref} {...fieldProps}>
{renderHeader ? (
<SeedField.Header>
<SeedField.Label weight={labelWeight}>
{label}
{showRequiredIndicator ? <SeedField.RequiredIndicator /> : null}
{indicator != null ? (
<SeedField.IndicatorText>{indicator}</SeedField.IndicatorText>
) : null}
</SeedField.Label>
</SeedField.Header>
) : null}
<SeedCheckbox.Group>{children}</SeedCheckbox.Group>
{renderFooter ? (
<SeedField.Footer>
{renderDescription ? (
<SeedField.Description>{description}</SeedField.Description>
) : null}
{renderErrorMessage ? (
<SeedField.ErrorMessage>{errorMessage}</SeedField.ErrorMessage>
) : null}
</SeedField.Footer>
) : null}
</SeedField.Root>
);
},
);
CheckboxGroup.displayName = "CheckboxGroup";
/**
* This file is a snippet from SEED Design, helping you get started quickly with @seed-design/* packages.
* You can extend this snippet however you want.
*/
Props
CheckboxGroup
Prop
Type
label?React.ReactNodeindicator?React.ReactNodeshowRequiredIndicator?boolean | undefineddescription?React.ReactNodeerrorMessage?React.ReactNodeCheckbox
Prop
Type
label?React.ReactNodeonCheckedChange?((checked: boolean) => void) | undefinedCheckmark
Prop
Type
접근성 이름
Checkbox에서 직접 작성한 <text>를 쓰거나 Checkmark로 직접 구성할 때는 상태를 가진 Root에 accessibility-label을 지정하세요. 컴포넌트 children의 문자열과 숫자는 접근성 이름으로 사용되지만, raw <text>의 정적 텍스트는 이름을 만들 수 없습니다. indeterminate 상태는 mixed로 전달됩니다.
Examples
Sizes
Lynx 예제를 불러오는 중입니다.
import "./styles";
import { HStack } from "@seed-design/lynx-react";
import { Checkbox, CheckboxGroup } from "@/components/ui/checkbox";
export default function Example() {
return (
<HStack className="checkbox-preview" gap="x8">
<CheckboxGroup>
<Checkbox label="Medium (default)" size="medium" defaultChecked tone="neutral" />
<Checkbox label="Large" size="large" defaultChecked tone="neutral" />
</CheckboxGroup>
<CheckboxGroup>
<Checkbox
label="Medium (default)"
size="medium"
variant="ghost"
defaultChecked
tone="neutral"
/>
<Checkbox label="Large" size="large" variant="ghost" defaultChecked tone="neutral" />
</CheckboxGroup>
</HStack>
);
}Tones and Variants
Brand
Lynx 예제를 불러오는 중입니다.
import "./styles";
import { VStack } from "@seed-design/lynx-react";
import { Checkbox, CheckboxGroup } from "@/components/ui/checkbox";
export default function Example() {
return (
<VStack className="checkbox-preview">
<CheckboxGroup>
<Checkbox
label="Square (default)"
variant="square"
tone="brand"
size="large"
defaultChecked
/>
<Checkbox label="Ghost" variant="ghost" tone="brand" size="large" defaultChecked />
</CheckboxGroup>
</VStack>
);
}Neutral
Lynx 예제를 불러오는 중입니다.
import "./styles";
import { VStack } from "@seed-design/lynx-react";
import { Checkbox, CheckboxGroup } from "@/components/ui/checkbox";
export default function Example() {
return (
<VStack className="checkbox-preview">
<CheckboxGroup>
<Checkbox
label="Square (default)"
variant="square"
tone="neutral"
size="large"
defaultChecked
/>
<Checkbox label="Ghost" variant="ghost" tone="neutral" size="large" defaultChecked />
</CheckboxGroup>
</VStack>
);
}Indeterminate
Lynx 예제를 불러오는 중입니다.
import "./styles";
import { Checkbox } from "@/components/ui/checkbox";
export default function Example() {
return (
<Checkbox defaultChecked label="indeterminate" indeterminate tone="neutral" size="large" />
);
}Weights
Lynx 예제를 불러오는 중입니다.
import "./styles";
import { VStack } from "@seed-design/lynx-react";
import { Checkbox, CheckboxGroup } from "@/components/ui/checkbox";
export default function Example() {
return (
<VStack className="checkbox-preview">
<CheckboxGroup>
<Checkbox label="Regular Label Text" weight="regular" tone="neutral" size="large" />
<Checkbox label="Bold Label Text" weight="bold" tone="neutral" size="large" />
</CheckboxGroup>
</VStack>
);
}Long Label
Lynx 예제를 불러오는 중입니다.
import "./styles";
import { VStack } from "@seed-design/lynx-react";
import { Checkbox, CheckboxGroup } from "@/components/ui/checkbox";
export default function Example() {
return (
<VStack className="checkbox-preview">
<CheckboxGroup>
<Checkbox
size="medium"
tone="neutral"
label="Consequat ut veniam aliqua deserunt occaecat enim occaecat veniam et et cillum nulla officia incididunt incididunt. Sint laboris labore occaecat fugiat culpa voluptate ullamco in elit dolore exercitation nulla."
/>
<Checkbox
size="large"
tone="neutral"
label="Consequat ut veniam aliqua deserunt occaecat enim occaecat veniam et et cillum nulla officia incididunt incididunt. Sint laboris labore occaecat fugiat culpa voluptate ullamco in elit dolore exercitation nulla."
/>
</CheckboxGroup>
</VStack>
);
}Disabled
Lynx 예제를 불러오는 중입니다.
import "./styles";
import { VStack } from "@seed-design/lynx-react";
import { Checkbox, CheckboxGroup } from "@/components/ui/checkbox";
export default function Example() {
return (
<VStack className="checkbox-preview">
<CheckboxGroup>
<Checkbox
defaultChecked
label="Disabled Checked, Square"
disabled
tone="neutral"
size="large"
/>
<Checkbox
checked={false}
label="Disabled without Checked, Square"
disabled
tone="neutral"
size="large"
/>
<Checkbox
variant="ghost"
defaultChecked
label="Disabled Checked, Ghost"
disabled
tone="neutral"
size="large"
/>
<Checkbox
variant="ghost"
checked={false}
label="Disabled without Checked, Ghost"
disabled
tone="neutral"
size="large"
/>
</CheckboxGroup>
</VStack>
);
}Listening to Value Changes
onCheckedChange로 체크박스의 선택 상태 변경을 감지할 수 있습니다.
Lynx 예제를 불러오는 중입니다.
import "./styles";
import { useState } from "@lynx-js/react";
import { VStack } from "@seed-design/lynx-react";
import { Checkbox } from "@/components/ui/checkbox";
export default function Example() {
const [count, setCount] = useState(0);
const [lastValue, setLastValue] = useState<boolean | null>(null);
function handleCheckedChange(checked: boolean) {
"background only";
setCount((previous) => previous + 1);
setLastValue(checked);
}
return (
<VStack className="checkbox-preview" gap="x4">
<Checkbox
label="Click me"
tone="neutral"
size="large"
onCheckedChange={handleCheckedChange}
/>
<text className="checkbox-preview__status">
onCheckedChange called: {count} times, last value:{" "}
{lastValue === null ? "-" : JSON.stringify(lastValue)}
</text>
</VStack>
);
}Use Cases
Using Checkmark
Checkmark는 상위 Checkbox.Root의 상태를 표시하는 체크 마크입니다. 커스텀 레이아웃이 필요하면 @seed-design/lynx-react의 Checkbox.Root 안에 Checkmark와 문구를 배치하고, Root에 accessibility-label을 지정하세요.
Lynx 예제를 불러오는 중입니다.
import "./styles";
import type { ReactNode } from "@lynx-js/react";
import { Checkbox, HStack, Text } from "@seed-design/lynx-react";
import { Checkmark } from "@/components/ui/checkbox";
interface CustomCheckboxProps extends Checkbox.RootProps {
label: string;
children: ReactNode;
}
function CustomCheckbox({ label, children, ...props }: CustomCheckboxProps) {
return (
<Checkbox.Root
accessibility-label={label}
style={{ display: "flex", flexDirection: "column", rowGap: "8px", alignItems: "center" }}
{...props}
>
<Checkmark tone="neutral" />
{children}
</Checkbox.Root>
);
}
export default function Example() {
return (
<HStack className="checkbox-preview" gap="x6">
<CustomCheckbox label="regular">
<Text textStyle="t7Regular">regular</Text>
</CustomCheckbox>
<CustomCheckbox label="medium" defaultChecked>
<Text textStyle="t7Medium">medium</Text>
</CustomCheckbox>
<CustomCheckbox label="bold">
<Text textStyle="t7Bold">bold</Text>
</CustomCheckbox>
</HStack>
);
}Fieldset Integration
CheckboxGroup을 사용하여 여러 체크박스를 그룹화하고 label, description, errorMessage 등의 Fieldset 관련 prop을 사용할 수 있습니다.
Lynx 예제를 불러오는 중입니다.
import "./styles";
import { useState } from "@lynx-js/react";
import { ActionButton, HStack, VStack } from "@seed-design/lynx-react";
import { Checkbox, CheckboxGroup } from "@/components/ui/checkbox";
export default function Example() {
const [apple, setApple] = useState(true);
const [banana, setBanana] = useState(false);
const [orange, setOrange] = useState(false);
const [firstErrorMessage, setFirstErrorMessage] = useState<string | undefined>();
const [terms, setTerms] = useState(false);
const [privacy, setPrivacy] = useState(true);
const [marketing, setMarketing] = useState(false);
const [secondErrorMessage, setSecondErrorMessage] = useState<string | undefined>();
const handleFirstSubmit = () => {
setFirstErrorMessage(apple ? "Apple은 선택할 수 없습니다." : undefined);
};
const handleSecondSubmit = () => {
setSecondErrorMessage(!terms || !privacy ? "필수 항목에 동의해 주세요." : undefined);
};
return (
<VStack className="checkbox-preview" maxWidth="640px">
<HStack width="full" gap="x8" align="flex-start" wrap="wrap" justify="center">
<VStack
grow
shrink
minWidth="240px"
style={{ flexBasis: 0 }}
gap="spacingY.componentDefault"
>
<CheckboxGroup
label="좋아하는 과일"
indicator="선택"
description="Apple을 선택하고 제출해보세요."
errorMessage={firstErrorMessage}
>
<Checkbox
label="Apple"
tone="neutral"
size="large"
checked={apple}
onCheckedChange={setApple}
/>
<Checkbox
label="Banana"
tone="neutral"
size="large"
checked={banana}
onCheckedChange={setBanana}
/>
<Checkbox
label="Orange"
tone="neutral"
size="large"
checked={orange}
onCheckedChange={setOrange}
/>
</CheckboxGroup>
<ActionButton variant="neutralSolid" bindtap={handleFirstSubmit}>
제출
</ActionButton>
</VStack>
<VStack
grow
shrink
minWidth="240px"
style={{ flexBasis: 0 }}
gap="spacingY.componentDefault"
>
<CheckboxGroup
label="약관 동의"
labelWeight="bold"
showRequiredIndicator
description="이용약관을 선택하지 않고 제출해보세요."
errorMessage={secondErrorMessage}
>
<Checkbox
label="이용약관 동의 (필수)"
tone="neutral"
size="large"
checked={terms}
onCheckedChange={setTerms}
/>
<Checkbox
label="개인정보 처리방침 동의 (필수)"
tone="neutral"
size="large"
checked={privacy}
onCheckedChange={setPrivacy}
/>
<Checkbox
label="마케팅 수신 동의 (선택)"
tone="neutral"
size="large"
checked={marketing}
onCheckedChange={setMarketing}
/>
</CheckboxGroup>
<ActionButton variant="neutralSolid" bindtap={handleSecondSubmit}>
제출
</ActionButton>
</VStack>
</HStack>
</VStack>
);
}웹 버전과의 차이
- 값 변경은
onChange가 아닌onCheckedChange로 받습니다. invalid상태에서는description을 제외하고errorMessage만 화면과 접근성 트리에 표시합니다.- Registry가 Lynx용 check와 minus 아이콘을 넣습니다. raw
<svg>아이콘은 지원하지 않습니다. - 직접 만든 Scale Feedback에서 축소를 끄는 React의
--seed-checkmark-feedback-scaleCSS 변수는 제공하지 않습니다.
Lynx 미지원 기능
현재 Lynx 플랫폼 제약으로 다음 기능이 지원되지 않습니다.
런타임 모델 차이로 제외
| 기능 | 웹 대응 | 설명 |
|---|---|---|
CheckboxHiddenInput | <input type="checkbox"> | Lynx에 HTML form 제출 모델 없음 |
inputProps | hidden input props | Lynx snippet은 hidden input을 렌더링하지 않음 |
개별 Checkbox의 name / value / required / invalid | form field props | Lynx에 native form 제출 모델 없음 |
| React Hook Form 예제 | HTML form과 hidden input 연동 | 앱 상태와 checked / onCheckedChange를 직접 연결해야 함 |
focus / focusVisible | 키보드 포커스 | Lynx에 키보드 포커스 개념 없음 |
onChange (raw DOM event) | React.ChangeEvent | 의미 없음. onCheckedChange로 대체 |
weight="default" / "stronger" | deprecated 호환 매핑 | Lynx는 "regular" / "bold"만 노출 |
Last updated on