Approved 2026-10-11 — the build spec until it ships
Library — controls and fields
Buttons, choices, door pills, sliders, the sign-in provider buttons and the one field grammar: label on top, a 3:1 border, a support line that holds only an error and the counter, an error that shows in three ways.
Button
The action. One ball primary per screen region; a tonal Button sits beside it in a pair, as a band’s second way and as more of a list (4개 더 보기); ghost is the quiet command inside content. A door is the Door pill, never a Button.
참석하기
Ideal lg primary, full width on the rail
States
참석하기
Pressed scale .97, dim .92
참석하기
Hover web only: dim .85
참석하기
Focus 2px ink ring, offset 2, also on ball
다음
Soft-disabled gray, pressable; a press shows what is missing
대진 짜기
Disabled inert at .5; no reason line beside it
저장 중…저장
Loading spinner beside the progressive label, width held
Variants
참석참석참석
닫기닫기닫기
건너뛰기건너뛰기건너뛰기
삭제삭제삭제
Tone × size four tones, three sizes, the same states
일정 만들기되돌리기
Icon leading a 20 glyph and 8 before the label
4개 더 보기접기답글 3개 더 보기
Trailing glyph trail: a 16 glyph 8 after the label; 더 보기 takes a down chevron, 접기 an up one
이메일로 로그인다른 방법으로 인증
Tonal, a second way a band’s second way is a tonal md Button, never a text link
초기화결과 128개 보기
닫기일정 취소
Pair tonal one third, the commit two thirds, gap 12
저장 중…저장저장 중…저장삭제 중…삭제
Loading by tone ink spinner, white on danger
Dark
참석하기닫기삭제다음
Dark the ball stays; danger takes ink text
200% text
일정 만들기일정 만들기일정 만들기
200% labels wrap and never clip; heights grow to 76, 66, 58
English
JoinSaving…Save
Keep goingLeave
English at most two words, sentence case
Sizessm 36 · r10 · 15/22·600 · pad 0 16 | md 44 · r12 · pad 0 20 | lg 52 · r12 · 17/24·600 · pad 0 20. Ghost pad 0 12 at every size so the text edge lands on the rail. Glyph 20 + 8 before the label; `trail`, a 16 glyph 8 after it.Tonesprimary ball + ink · tonal gray100 + ink · ghost no fill + ink · danger $error + white (ink in dark).Tonal beyond the paira band’s second way under the primary (이메일로 로그인, 다른 방법으로 인증, 비밀번호 재설정): a tonal md Button, full width. More of a list (4개 더 보기, 답글 3개 더 보기): a tonal Button at the list’s end, md full width (sm under a comment’s replies), the label stating its count, a 16 chevron 8 after it (`trail`; down opens the rest in place, up folds it as 접기), `expanded` for aria-expanded. An action cue is a control, never bare text (owner review 48).Text linka text link (FvkTextAction kind link) is now only the legal footer link (이용약관 · 개인정보 처리방침, `cue="legal"`) and, as kind command, a Toast’s action. A door is the Door pill; more of a list is the tonal Button; every other command is a Button, ghost when it should be quiet. The verifier’s text-cue check fails any other text link.Pairin a Dialog, a Sheet footer or the FilterSheet: the tonal Button takes one third of the line and the commit two thirds, set explicitly with a 12 gap (112.7 : 225.3 on the 350 line, 99.3 : 198.7 in the Dialog’s 310). Apple’s guidelines prefer equal sizes; the larger commit on the thumb’s side is a ratified departure. The one-third Button keeps 12 of side padding so a four-syllable label stays on one line. The scoring sheets use it too (거절 · 동의, 요청 취소 · 수정).Soft-disabledprimary only: gray100 fill, $textSecondary 600 label (6.59:1), focusable, aria-disabled; the press gives a warning haptic, focuses the missing control and shows its error line; the fill becomes ball when the requirement is met.Validate on presstwo or more inputs pending, or the missing part off screen: the button stays enabled and a press marks every invalid control, scrolls to the first and focuses it.Loading20 spinner (ink; white on danger) + 8 + the progressive label; width held at the wider label; aria-busy; presses ignored.200% textlg 76 · md 66 · sm 58; a Dialog pair stacks with the commit on top.Accessibilitybutton; the one spoken label is the visible label, so while loading it follows "저장 중…"; targets 52 / 44 / 36 with slop to 44; a more-of-a-list Button carries aria-expanded.Motionpress settle and dim on the UI thread (80 in, 150 out); soft-disabled to primary is a 100 ms fill change; reduced motion keeps the dim.Replaces in codebutton.tsx, button-visual.ts, action-button.tsx, action-pill.tsx, hand-drawn pressable pills, variantFor in confirm-sheet.tsx.
IconButton
An action whose glyph is its whole label: header, row and sheet controls.
3
Ideal a 24 glyph in a 48 target, pitch 48
States
Pressed right: scale .92, dim .78; left: rest
Hover web only: right is dim .85
Focus the ring follows the circle
Selected a filled glyph, never colour alone
Disabled right: .5 and inert
Variants
Tones ink, secondary, danger; inverse on a Block
Square a 40 gray100 square in a 48 target: the wizard’s back
참석을 취소했어요
되돌리기
On the inverse onInverse: the Toast’s ×, white on ink, ink on light
On a photo a 40 circle at ink α0.5, white glyph
399+
Badge a dot (something pending) or a count, never both
Glyph 20 inside rows, sheets, composers and comments
Dark
3
Dark ink follows the theme
Anatomy48 × 48 target, 24 glyph centred (20 inside rows, sheets, composers, comments), ghost. Two side by side touch edge to edge (96). The glyph's own edge, not the target's, sits on the rail: bleed start or end lets the target reach into the margin (12 for a 24 glyph, 14 for a 20, 4 for a 40 circle), so no row computes it by hand; bleed both hangs a button between two neighbours.Tonesink · secondary · danger = remove (a saved object asks a Dialog naming it; a draft item removes at once and offers 되돌리기) · inverse on a Block. Toggle: outline to filled.Variantssquare: a 40 gray100 r12 square in the 48 target, pressing like a Button (the wizard’s back, and × on its first step) · onInverse: the glyph colour on the inverse surface (white on ink; ink on the light surface in dark), for the Toast’s × · inverse stays white on an ink Block.On a photoa 40 circle in the on-photo fill, ink α0.6 (α0.8 under Reduce Transparency), with a white 24 glyph inside the 48 target; the MediaBand owns placement.Accessibilitybutton; aria-label required, at most three syllables ("공유", "알림 3개"); the web tooltip repeats it; toggles use aria-pressed.Motionsettle and dim; reduced motion keeps the dim.Replaces in codeheader-icon-button.tsx, action-icon-button.tsx, remove-icon-button.tsx (its confirm becomes the Dialog rule), sessions/save-button.tsx.
Choice
One of a small set, or a control that opens a Sheet of values. Never a status and never navigation.
복식단식혼합 복식
Ideal a set wraps left-aligned, gap 8, never scrolls
States
복식단식
Pressed scale .96, dim .85
복식단식
Hover web only: dim .85
복식단식
Focus 2px ink ring, offset 2
복식단식
Disabled .5 and inert
초보자도 환영하는 일요일 아침 복식 정기 모임 참가자 모집
Long text the label wraps and the pill grows
Variants
전체내 클럽저장됨
단식복식
Fill two to four share the line: the segmented look
10월필터 2
Choice ▾ opens a Sheet; the active value is ink-filled
NTRP 3.0–4.0
Range the label shows the range; it opens a Sheet
서초구복식
Removable the × is its own 44 target and removes only itself
복식단식혼합 복식
In a form 44 tall with pad 0 20
Dark
복식단식필터 2
Dark the selected fill is the light ink
200% text
복식단식
혼합 복식팀 경기
200% the label cap is 2.0 and the set wraps
English
DoublesSingles
Mixed doublesFilter 2
English one or two words, at most 14 characters
Anatomy36 tall (44 inside forms) · r full · 15/22·500 · pad 0 16 (0 20 at 44) · resting 1px #D4D4D4 on white · selected = ink fill, white 700 label, a leading 16 check. Hit slop to 44. A set wraps left-aligned with gap 8 and never scrolls sideways, and a wrap never leaves one Choice alone on its last row (see Choice set); at most five options (a longer list is a Picker Cell).Choice ▾a 16 chevron after the label; shows the current value or the active count and opens a bottom Sheet of radio Cells, never a Menu. With an active value it takes the selected look without the check, the chevron kept. Range: the label shows the range.Removablea × 16 after the label in its own 44 target; removes only itself.Accessibilitya set is a radiogroup of radio (several: toggle buttons with aria-pressed) with a group label; Choice ▾ is a button with aria-expanded; the name starts with the visible text; a removable’s × is "{label} 삭제".Motionfill changes over 100 ms, the check appears with the fill; reduced motion: instant.Replaces in codeselection-chip.tsx (56 files), dropdown-chip.tsx, range-chip.tsx, removable-chip.tsx, chip-picker-panel.tsx (the Sheet shell), the PillNav pill variant and the segmented control inside forms.
Door pill
A door drawn as a control: it names the place it opens, and is never bare text. A section head’s, a Block’s, a Cell’s and a viewer’s.
전체 보기점수 안내
Ideal md on the canvas: the Choice’s pill with a right chevron
친구24명
전체 보기
강남 라켓클럽
티어 안내기간
In a head centred on the title’s line; a Choice follows it, 12 after
States
전체 보기
Pressed scale .96, dim .85
전체 보기
Hover web only: dim .85
전체 보기
Focus 2px ink ring, offset 2
전체 보기
Disabled .5 and inert
Surfaces and variants
다음 일정내일, 9월 19일 (토)
일정 보기
09:00
정
6/8
올림픽공원 테니스장
☀️ 18°
sm on a Block a Block’s zone A end: 24 tall, white α.8 label, white α.4 border
9월 회비
월 선택
370,000원
/ 570,000원
19명 중 12명 납부미납 7명
sm, a choice of where a down chevron: it opens a Sheet of months, not a page
앨범 전체 보기
Photo on a photo or a map: the photo-circle fill, a white label, no border
전체 보기월 선택수정
Chevron right goes to a page, down opens a choice, none when the label says it
[필수] 이용약관 동의
보기
[필수] 개인정보 처리방침 동의
보기
In a Cell a consent’s 보기: the Cell’s trailing door and its own stop
Dark
친구24명
전체 보기
Dark the border is white α.14; a Block’s pill is the same in both
다음 일정내일, 9월 19일 (토)
일정 보기
09:00
정
6/8
올림픽공원 테니스장
☀️ 18°
Dark, on a Block
200% text
이번 주말 올림픽공원 모임3개
전체 보기
200% the title wraps and the pill keeps its line; the label never wraps
English
Friends24
See all
Ranking
Tier guidePeriod
English one or two words, sentence case, at most 14 characters
Anatomythe Choice’s geometry. md: 36 tall · 1px edge border (#D4D4D4; dark white α0.14) · r full · 16 of side padding · the label 15/22·500 ink · a 16 chevron 4 after it. sm (a Block’s zone A, whose row is 24): 24 tall · 10 of side padding · the label 13/18·600. The label stays on one line. Hit slop to 44, at least 44 wide.Surfacescanvas (the default) · block, the ink Block: the label white α0.8, the border white α0.4 · photo, a photo or a map: the photo-circle fill (ink α0.6), a white label, no border. The Block’s and the photo’s pills are the same in both themes.Chevronright when it goes to a page (the default); down when it opens a choice of where (a Block’s period, 월 선택); none when the label already says it. The law of the chevron holds: a right chevron always means going somewhere.Wherea section head’s door (전체 보기 ›, 점수 안내 ›, 티어 안내 ›), a Block’s door (일정 보기 ›, 점수 입력 ›), a Cell’s trailing door (a consent’s 보기 ›) and a viewer’s (앨범 전체 보기 ›). In a head it is the one pill beside the Choice: ▾ chooses, › goes; with a Choice the door comes first and the Choice last, 12 apart.Not textan action cue is a control, never bare text (the owner, 2026-10-28). A text link is now only the legal footer link (이용약관 · 개인정보 처리방침) and a Toast’s action; a door is this pill, more of a list is the tonal Button (see Button), and a band’s second way is a tonal md Button. The verifier’s text-cue check fails any other text link.Statesrest · pressed (scale .96, dim .85, the pill physics) · hover (web: dim .85) · focus (2px ink ring, offset 2) · disabled (.5, inert).200% textthe label grows with the text and the pill with it, on one line; in a head the title wraps and the pill keeps its line (see Section head).Accessibilitya link whose name is the visible label ("전체 보기"; in a head "전체 보기, 내일 일정"); a down chevron makes it a button with aria-expanded. Inside a Block it is not a second stop (the Block is the one button); a Cell’s 보기 is its own stop ("이용약관 보기").Motionthe pill press; none of its own. Reduced motion keeps the dim.Replaces in codethe text links of SectionHeader’s action and the Block’s door (packages/ui/src/section-header.tsx); the app’s ActionPill when it opens a page.
Switch
An immediate on or off. The toggle is the feedback; use it for a setting that applies at once, never in a form that ends in Save.
경기 알림
Ideal on: ink track, 52 × 32, the row is the target
States
경기 알림
Off the track is the 3:1 control boundary
경기 알림
Focus the ring goes around the track
경기 알림
Hover web only: dim .85
경기 알림
Disabled .5 and inert
Dark
경기 알림
클럽 소식
Dark the thumb darkens on the light track
Not in a Save-gated form
참가비*
무료유료
1인 참가비*
10,000
원
Choice set the answer waits for 저장, so a Choice set carries it and the amount nests under 유료
Anatomytrack 52 × 32 · r full · off $borderControl (3.26:1) · on ink. Thumb 24, white, 1px #D4D4D4 edge, 4 in from the track (travel 20). Inside a toggle Cell the whole 48 row is the target.Contentthe Cell label names the setting ("경기 알림"); it never names the state and never changes with it.Not in a Save-gated forma Switch applies at once, so it never sits in a form that ends in Save (an Edit Sheet, a wizard step, a Save-gated page). There the answer is a Choice set, or check rows when the options are sentences, and what depends on the answer nests under its option. Example: 참가비 is a 무료 | 유료 Choice set with the amount under 유료, not a 별도 참가비 Switch beside a 저장.Accessibilityrole="switch" with aria-checked; Space toggles on web; the Cell is the one stop.Motionthumb 150 ms, track colour 100 ms; reduced motion: the thumb snaps and the track still fades over 100 ms. Haptics only through the mutation outcome.Replaces in codetoggle-pill.tsx (TogglePill, display-only 44 × 24) and every hand-rolled switch.
Slider
A value or a band dialled by drag with the live value always on screen. A shortcut, never the only way.
시간대
19:00~21:00
Ideal a band: the tag follows the thumb while dragging
States
70%
Focus the ring goes around the thumb; arrows step
70%
Disabled .5 and inert
Variants
70%
Single one bounded value
Hue the same core on a hue bar for club colours
19:00~21:00
2
Stepper beside the alternative to dragging: the readout and a Stepper above the track, one snap per tap
5
Stepper, hue a hue has no readout: the Stepper alone, its right edge on the rail
Dark
19:00~21:00
Dark the ink thumb and the tag follow the theme
Anatomytrack 4 #E5E5E5 · fill ink · thumb 24 ink (19:1 on the canvas) · a live value tag (ink fill, white text, 24 tall) rides above the thumb. A band has three grab surfaces (left thumb, right thumb, band body), each a 44 target centred on the visual. The track starts 20 from the edge. The domain is fitted, snapped and labelled live so a drag never shares an axis with a scroll.Alternatives (2.5.7)a tap on the track sets the nearest thumb to the tapped snap; a 44 Stepper beside the readout steps one snap; a band's body shift is two taps. The Stepper is the kit's stepper prop: a row above the track with the readout (in place of the tag that rides above the thumb, so the value shows once) on the rail and the compact Stepper, its right edge on the rail. A screen with a horizontal-pan control sets fullScreenGestureEnabled: false; if the Android edge-back wins the device check, the track insets to 28.Accessibilityadjustable (slider), increment and decrement by one snap, value in words through aria-valuetext ("오후 7시").Motionthe thumb follows the finger; snap settle 150 ms; reduced motion: no settle.Replaces in codevalue-slider.tsx, time-band-slider.tsx, range-band-slider.tsx (through band-slider-core.tsx and single-band-slider-core.tsx), hue-slider.tsx.
Stepper
A bounded integer dialled up or down: counts and limits. Not for scores and not for open ranges.
참가 인원
8
Ideal a 136 × 36 pill; its right edge is the rail
States
참가 인원
8
Pressed the end settles; the pill never moves
참가 인원
8
Focus the ring goes on the focused end
참가 인원
2
At a bound the end that cannot go further dims to .5
참가 인원
8
Disabled .5 and inert
Variants
참가 인원*
8
Fill a lone Stepper in a Field: a 48 bordered row
Dark
참가 인원
8
Dark the pill is gray100 on the dark canvas
200% text
참가 인원
8
200% the label scales; the pill holds its size
Anatomya 136 × 36 pill (gray100, r full): − end 44, value cell 48 (15/22·600 tabular, fixed so digits do not jitter), + end 44; glyphs 20; each end is a 44 target (slop 4 above and below). Fill variant: a 48 bordered row (r12, 1px $borderControl), ends 48 wide, value centred 16/24·600.Contentthe value is a number; the unit lives in the Cell label ("참가 인원").Accessibilitygroup with a spin value (min, max, now, text); each end is a button named for its effect and the field ("참가 인원 줄이기"); Up and Down arrows step on web.Motionglyph settle and dim only.Replaces in codestepper.tsx (32 px circle buttons, fullWidth bordered row).
Search field
A find field. On a list screen it lives behind a header search glyph and takes over the title; in a takeover or a sheet it fills the bar.
코트, 클럽, 사람 검색
Ideal 40 tall, r12, gray100, inside a 44 target
States
코트, 클럽, 사람 검색
취소
Focus the field shrinks; 취소 appears at the right
올림픽
취소
Filled the clear × shows once there is text
코트, 클럽, 사람 검색
Disabled .5 and inert
Dark
올림픽
취소
Dark gray100 on the dark canvas
English
Search courts, clubs, people
Cancel
English the placeholder names what is searched
Anatomythe field is 40 tall, r12, gray100, inside a 44 target. Search glyph 20 at 12 from the edge + 8 · text 16/24 ink · placeholder 16/24 $textSecondary · clear × 20 in a 48 target once there is text · focused: the field shrinks and 취소 appears as an sm tonal Button. No border: the glyph and placeholder identify it.Variantsheader takeover (the glyph swaps the title for the field in place, the keyboard opens with it, back closes it) · in-page field · the bar of a full-screen search takeover.Contentthe placeholder names what is searched ("코트, 클럽, 사람 검색"); the keyboard return key reads 검색.Accessibilitythe web search landmark; label "검색"; clear is "검색어 지우기"; focus lands in the field on open and returns to the header glyph on close.Motionthe title-to-field swap and the shrink are instant.Replaces in codesearch-bar.tsx (SearchBar: $card fill, $borderSubtle border, 44-tall pill).
Checkbox and radio rows
Choosing from a list inside a Sheet or a settings page: a trailing check for one that picks and closes, a leading ring for one that waits for a button, a 24 square for several.
전체
테니스장
연습장
Ideal a tap picks and closes: the chosen row is 600 with a check
States
테니스장
Pressed the row settles to .98 and dims to .80
테니스장
Focus the ring is drawn inside the row
테니스장
Disabled .5 and inert
Variants
하드
클레이
잔디
Several a trailing 24 square: 2px edge, ink fill, minus
김민준
박서준
이서연
Leading Select mode and consent: the square leads at the rail
클럽 멤버에게만
멤버만 프로필을 볼 수 있어요
모두에게
링크를 아는 사람도 볼 수 있어요
Two lines the option's meaning under the label, min 68
A button follows
공개 범위
모두에게
링크를 아는 사람도 볼 수 있어요
클럽 멤버에게만
멤버만 볼 수 있어요
나만
나만 볼 수 있어요
저장
Radio, leading A footer CTA follows: every row shows its ring
경기 1시간 전
직접 정하기
몇 분 전에 알릴까요?
30
분 전
알리지 않기
Expanded The chosen row opens its field; the hairline follows it
Dark
하드
클레이
잔디
전체
테니스장
Dark the ring and the 2px edges stay 7:1 on the canvas
Anatomya Cell 48 with the cell hairline #E5E5E5 inset to the rail between rows and none after the last. One that picks and closes (a value picker): the chosen row's label turns 600 and a 20 ink check sits at the end (no fill, no tint). One that waits for a footer CTA (Dispute, Decide, an Edit Sheet): every row leads with a 24 ring, a 2px $textSecondary edge with a 12 ink dot when chosen, the content shifted 36 and the ring riding line 1 through the line slot; the chosen label is 600 and there is no trailing check, so an unchosen row still says it is a choice. Several: a 24 square r6; unchecked = a 2px $textSecondary edge (7.4:1); checked = ink fill with a white 16 check; mixed = ink fill with a white 16 minus; the square trails in a picker and leads at the rail in Select mode and the Consent group. Expand: content under a row that owns its padding (8 above, 16 below, on the rail); the hairline follows it, never precedes it.Accessibilityradio or checkbox with aria-checked (mixed for the master); the whole row is the target; arrow keys move a radio group on web.Motionfill 100 ms; the glyph appears with it; reduced motion: instant.Contentthe ring or the trailing check is chosen by what the person does next, never by taste: a tap that closes the Sheet takes the check; a button that commits the choice takes the ring.Replaces in codeselection-card.tsx lists, selection-check.tsx.
Date and time picker
One date or one time chosen on the platform wheel inside the standard bottom Sheet. A range uses the Time range wheel.
시작 시간
17
18
19
20
21
40
50
00
10
20
완료
Ideal time: the title names the field; 24-hour HH:MM
날짜
7월
8월
9월
10월
11월
16일
17일
18일
19일
20일
수
목
금
토
일
완료
Date the same Sheet with a date wheel
시작 시간
17
18
19
20
21
40
50
00
10
20
완료
Dark the Sheet is the card surface
Anatomya Sheet; the title row names the field (17/24·600) with the × at the right; the native wheel; the footer is one primary lg 완료. On iOS the platform's wheel (UIDatePicker) sits in the Sheet; on Android the Sheet hands over to the system's Material date or time dialog. For a date more than a week out the Calendar sits in the Sheet on both platforms, since a wheel is the slowest way to reach a distant date. The value it commits is shown in 24-hour HH:MM.Statesopen · committed (완료) · dismissed (scrim, × or back: nothing is applied). Minute interval per field.Accessibilitythe wheel is accessible by the OS; the Sheet's title is the first stop; focus returns to the Picker Cell.Replaces in coderaw react-native-modal-datetime-picker (DateTimePickerModal, six files).
Time range wheel
Pick a start and an end time in one Sheet with one wheel (quiet hours, opening hours).
운영 시간
시작19:00
종료21:00
17
18
19
20
21
40
50
00
10
20
완료
Ideal the start tab is selected; both values show
운영 시간
시작19:00
종료21:00
19
20
21
22
23
40
50
00
10
20
완료
End tab the underline moves; the same wheel edits the end
운영 시간
시작19:00
종료18:00
16
17
18
19
20
40
50
00
10
20
종료가 시작보다 빨라요
완료
Invalid end before start: a danger helper, 완료 soft-disabled
방해 금지 시간
시작22:00
종료다음 날 07:00
05
06
07
08
09
40
50
00
10
20
완료
Wrapping a range that may cross midnight reads 다음 날
Anatomya Sheet titled with the field name. Tabs 시작 and 종료 (52 tall, the Tabs grammar) show their current values in 24-hour HH:MM; one native time wheel edits the selected tab; the footer is one primary 완료 that commits both. A range that cannot cross midnight and has an end before the start shows the danger helper "종료가 시작보다 빨라요" above 완료, and 완료 is soft-disabled. A range that may wrap accepts it, and the end reads "다음 날 07:00".Accessibilitythe two tabs are a tablist; each reads "시작, 19:00"; the helper is announced when it appears.Motionthe underline slides to the other tab (navSlide; reduced motion: it jumps); the Sheet's rise and exit.Replaces in codethe single-value DateTimePickerModal rows that set a start and an end separately: settings-signal-preferences-screen.tsx (quiet hours) and venue-hours-composer.tsx.
Field
The wrapper of every form control: one grammar for text, number, text area, picker Cell and Choice set. Every field is label, control, support line.
클럽 이름*
예: 강남 라켓클럽
참가비*
10,000
원
소개(선택)
예: 강남에서 매주 일요일 아침에 복식을 쳐요
0/200
지역*
선택
종목*
단식복식혼합 복식
Ideal label on top, a 3:1 border, the support line empty until an error
클럽 이름*
강남 라켓클럽 일요 아침 복식 모임입니다
클럽 이름은 20자 이하로 입력해 주세요
−3
Support line the error at the left, the counter at the right, 4 under
Dark
클럽 이름*
예: 강남 라켓클럽
소개(선택)
예: 강남에서 매주 일요일 아침에 복식을 쳐요
0/200
Dark the border is white α0.40, 3.83:1 on the canvas
200% text
클럽 이름*
예: 강남 라켓클럽
200% the control grows to the scaled line plus 24
English
Club name*
e.g. Gangnam Racket Club
About (optional)
e.g. We play doubles every Sunday morning
0/200
English the label is a noun that fits one line
Anatomytop to bottom: label 13/18·600 ink (marker per Required and optional marker) → 4 → control → 4 → support line 18, drawn only when it holds an error or a counter (no hint, 1.16). Fields sit 32 apart in a wizard. Control (text, number, text area): min 48 · r12 · 1px $borderControl · pad 0 16 · text 16/24 ink; focus a 2px ink border (padding drops by 1 so the text does not move); error a 2px $error border, at rest and in focus. Support line: the error at the left, the counter at the right, 13/18·500 $textSecondary; a message that wraps takes 13/20 and grows. With the keyboard up the scroll-into-view target is control bottom + 4 + 18 + 16.Field box and caretone box serves the Input, the TextArea, the set cell (48 × 48, r12) and the tiebreak box (48 × 36, r10): 1px $borderControl at rest, 2px ink in focus, 2px $error on an error, and the padding drops by 1 at 2px so nothing moves. The caret is one shared piece: 2 wide, ink, the line of its text role less 2 tall (a numeral cell’s, less 4), placed before a placeholder and after a value.HeightsText and Number: 70 bare, 92 with a line · Text area: 142 / 164 · Picker Cell: 48 / 70 · Choice set: 118 / 140 (two rows). The control grows to the scaled line plus 24 at 200%.Accessibilitythe label is the control's name ("클럽 이름, 필수"); the error, when there is one, is its description; an error adds the name suffix "입력 오류" (React Native has no aria-invalid) and is announced when it appears. No shake, no bounce.Motionborder width changes over 100 ms; the error appears in the 18 line at once, so nothing moves.Replaces in codelabeled-field.tsx (LabeledField and StyledInput: caption and error render as one slot, error ?? caption, never both), labeled-field-logic.ts, picker-row.tsx, the labelRight counter.
Input
One line of text.
클럽 이름*
예: 강남 라켓클럽
Ideal an example in tertiary, never an instruction
States
클럽 이름*
강남 라켓
Focus 2px ink border, ink caret, the text does not move
클럽 이름*
예: 강남 라켓클럽
Focus, empty the caret leads the placeholder
클럽 이름*
강남 라켓클럽
Filled unchanged from rest; no success check in the box
클럽 이름*
강남 라켓클럽
Filled, focused a trailing clear × in a 48 target
클럽 이름*
강
클럽 이름은 2자 이상 입력해 주세요
Error 2px danger border; the caret stays ink
클럽 이름*
강남 라켓클럽
Disabled .5 and not-allowed
Loading a Field skeleton in place of the control
Variants
비밀번호*
••••••••••
Password dots and a show glyph; paste and managers allowed
6
5
4
Sizes 48 × 48 set cell, 48 × 36 tiebreak box
테니스장*
예: 올림픽공원 테니스장
Leading glyph a glyph only when it names the input type
클럽 이름*
강남 라켓클럽
이미 있는 클럽 이름이에요. 다른 클럽 이름을 입력해 주세요
Async check a failed check is an error: text with a glyph in the support line, not in the box
Anatomythe Field control. Leading or trailing 20 glyphs only when they name the input type (search, place); a trailing clear × (48 target) when filled and focused; show and hide on a password. After an asynchronous check that fails (name taken, invite code invalid) the result is an ordinary error: text with a glyph in the support line, never a mark in the box; a pass draws nothing.Placeholderan example value in tertiary (5.46:1 on white), 16/24: "예: 강남 라켓클럽". Never an instruction, never a substitute for the label, never "(선택)", never on a picker or a numeric field.Contentthe keyboard type fits the content (email, phone, number); autofill and paste are allowed. A fixed-format value (phone, birth year, invite code) formats itself as typed and may use a hard maxLength.Sizescell: 48 × 48, r12, the numeral 24/32·700 centred (a set score; the caret is the numeral line less 4) · compact: 48 × 36, r10, 17/24·700, in a 44 target (the tiebreak points). Both draw the same field box as the default Input.Accessibilityname = label (+ "필수"); the placeholder is never the name; the error is the description.Motionborder 100 ms.Replaces in codeStyledInput single-line in labeled-field.tsx; the bare inputs of the composers.
Number field
A number the person types: a fee, a count above what a Stepper suits.
참가비*
10,000
원
Ideal amounts format with separators as typed
참가비*
10,000
원
Focus the unit stays a non-editable suffix
참가비*
250,000
원
참가비는 0원에서 100,000원 사이로 입력해 주세요
Error out of range: the line states the range
최대 인원*
8
명
Integer one syllable where possible: 원, 명, 회
Keypad
참가비*
10,000
원
9:41
123
456
789
0⌫
Numeric inputMode numeric, never type=number
Anatomythe Input with the numeric keypad (inputMode numeric; never type="number"). The unit is a non-editable 15/22 $textSecondary suffix at the right inside the box, part of the accessible name ("참가비, 원"), never a support line.Statesas Input. Out-of-range input shows the field error with the range.Replaces in codewon-amount-field.tsx, suggested-count-field.tsx, numeric StyledInput uses.
TextArea
Free text: the Input's border and states with room to grow.
소개(선택)
예: 강남에서 매주 일요일 아침에 복식을 쳐요
0/200
Ideal min 120; the counter is always on the support line
States
소개(선택)
예: 강남에서 매주 일요일 아침에 복식을 쳐요
0/200
Focus, empty the caret sits at the first line
소개(선택)
강남에서 매주 일요일 아침 7시에 복식을 치는 모임이에요. 초보자도 환영하고, 라켓과 공은 각자 준비해요. 첫 참석은 무료예요.
63/200
Typing grows with its lines; the counter counts
소개(선택)
강남에서 매주 일요일 아침 7시에 복식을 치는 모임이에요. 초보자도 환영하고, 라켓과 공은 각자 준비해요. 첫 참석은 무료예요.
회비는 월 3만 원이고 코트 대관비와 공 값에 써요. 총무가 매달 1일에 정산 내역을 공유해요. 비 오는 날은 전날 저녁에 공지해요. 궁금한 점은 총무에게 편하게 물어봐 주세요.
186/200
Grown to 200 then it scrolls inside; a thin indicator shows
소개(선택)
강남에서 매주 일요일 아침 7시에 복식을 치는 모임이에요. 초보자도 환영하고, 라켓과 공은 각자 준비해요. 첫 참석은 무료예요.
회비는 월 3만 원이고 코트 대관비와 공 값에 써요. 총무가 매달 1일에 정산 내역을 공유해요. 비 오는 날은 전날 저녁에 공지해요. 궁금한 점은 총무에게 편하게 물어봐 주세요.
−3
Past the limit allowed and kept; the counter reads −n in red
소개(선택)
강남에서 매주 일요일 아침 7시에 복식을 치는 모임이에요. 초보자도 환영하고, 라켓과 공은 각자 준비해요. 첫 참석은 무료예요.
회비는 월 3만 원이고 코트 대관비와 공 값에 써요. 총무가 매달 1일에 정산 내역을 공유해요. 비 오는 날은 전날 저녁에 공지해요. 궁금한 점은 총무에게 편하게 물어봐 주세요.
소개는 200자 이하로 입력해 주세요
−3
Left over the limit the field error appears; the counter stays
소개(선택)
강남에서 매주 일요일 아침 7시에 복식을 치는 모임이에요. 초보자도 환영하고, 라켓과 공은 각자 준비해요. 첫 참석은 무료예요.
63/200
Disabled .5 and not-allowed
Dark
소개(선택)
강남에서 매주 일요일 아침 7시에 복식을 치는 모임이에요. 초보자도 환영하고, 라켓과 공은 각자 준비해요. 첫 참석은 무료예요.
63/200
Dark the border is white α0.40
English
About (optional)
e.g. We play doubles every Sunday morning
0/200
English the counter counts characters, not bytes
Anatomymin 120 (12 + 4 × 24 + 12), text 16/24, grows with its lines to 200 and then scrolls inside (a thin indicator shows at the right). The counter sits on the support line, never inside the box, and is always shown.Contenta club bio is often pasted from KakaoTalk, so a long paste is kept and flagged, never cut.Accessibilityname = label; the counter is spoken at 80% and at the limit.Replaces in codeStyledInput multiline, the in-box counter.
Picker Cell
A field whose value is chosen elsewhere: a Cell with a value and a chevron that opens a Sheet or the platform wheel.
지역*
선택
시작 시간
19:00
종료 시간
21:00
Ideal an unset required value reads 선택
States
지역*
서울 강남구
Pressed the row settles to .98 and dims to .80
지역*
서울 강남구
Focus the ring is drawn inside the row
지역*
선택
지역을 골라 주세요
Error no border to tint: the support line only
지역*
서울 강남구
Disabled .5 and not-allowed
Dark
지역*
선택
종목
복식
Dark the chevron is tertiary
200% text
지역*
서울 강남구
200% from 1.5× the label sits over the value
English
Region*
Select
Format
Doubles
English Select is the unset word
AnatomyCell 48: label 15/22·400 $textSecondary at the left (the required marker rides on it at 15/22), value 15/22·500 ink at the right followed by a 16 chevron. An empty required value reads 선택 in secondary (the bare word; "(선택)" is only the optional suffix on a label). The support line sits under the Cell at the rail (22 = 4 + 18). Error: no border, the line only. Cells in a group are separated by the cell hairline inset to the rail.Variantsvalue picker (a Sheet of radio Cells) · wheel (date, time) · search takeover (venue): the Cell looks the same, only what it opens differs.Contentthe value is the chosen option, never a sentence.Accessibilitybutton named "{label}, {value}" ("지역, 서울 강남구") that opens a Sheet.Replaces in codepicker-row.tsx (PickerRow, bordered field row), edit-field-row.tsx.
Choice set
Pick one or several from a small list inside a form.
종목*
단식복식혼합 복식
Ideal a required set with no default starts empty
States and variants
종목*
단식복식혼합 복식
Selected single: a radio group, one ink fill
코트 바닥
하드클레이잔디
Several toggle buttons; none, one or many
종목*
단식복식혼합 복식
종목을 골라 주세요
Error no border to tint: the line only
분류
자유후기소개
공지운영진방
Wrap never one alone on the last row: 3 + 2, not 4 + 1
공개 범위*
모두에게클럽 멤버만
Fill two to four share the line
우리 팀이 이겼어요
6-06-16-26-36-47-57-6
Small 36 tall in equal columns: a Sheet’s quick picks
요일*
월화수목금
토일
A fixed series seven for 요일; two rows measure 118
Dark
종목*
단식복식혼합 복식
Dark the selected fill is the light ink
English
Format*
SinglesDoublesMixed
English one or two words per option
Anatomythe legend above (13/18·600, the label style, the marker on it) → 4 → Choices at 44 tall, r full, wrapped left-aligned with gap 8 both ways. At most five options, or seven for a fixed series (요일); a longer list is a Picker Cell. Two rows is 118. A set that starts defaulted (an axis with a real default) shows its default selected; a required set with no default starts with none selected.Wrapa set of several never leaves one Choice alone on its last row: the rows are laid out from the Choices’ measured widths, and when the last row would hold one, the row before gives it its last Choice (4 + 1 becomes 3 + 2, 3 + 1 becomes 2 + 2, 3 + 3 + 1 becomes 3 + 2 + 2); a set whose rows cannot spare one (two to a row, an odd one out) holds sentences, not labels, so it is drawn as check rows (Cells with a check lead), as the manner-tag sheet is; a single Choice by itself is fine. The same rule holds for every wrapping set: a FilterSheet group, an Edit Sheet’s Choices and the filter row under a header.Smallinside a Sheet a set takes size sm: Choices 36 tall in n equal columns, 8 apart, a pick that applies at once drops the 16 check (its fill alone says it is chosen); two sets then share their columns (ScoreEntry’s picks by who won).Accessibilityradiogroup or a group of toggle buttons with a group label; arrow keys on web.Replaces in codeSelectionChip groups in wizards, paired SelectionCard forks.
Field hint
Retired (the owner, 2026-10-20; 1.16): a field has no hint, a rule the person cannot guess is the text of its error when it is broken, an exact phrase to retype and a scale the label cannot hold join the label, and a consequence that changes the decision is the confirmation Dialog's body.
전화번호*
010-1234
전화번호는 숫자만 입력해 주세요
Only the error the support line is the error, once it occurs; the rule the person broke is its text
Input counter
How much of a limited field is used.
클럽 이름*
강남 라켓
Hidden below 80% of the limit a single line shows none
클럽 이름*
강남 라켓클럽 일요 모임
16/20
From 80% 12/18·500 secondary, right-aligned
클럽 이름*
강남 라켓클럽 일요 아침 복식 모임입니다
−3
Over the limit allowed, never blocked: −n in red, text kept
소개(선택)
예: 강남에서 매주 일요일 아침에 복식을 쳐요
0/200
TextArea always shown, never inside the box
Anatomy"12/20" in 12/18·500 $textSecondary, right-aligned on the support line. Shown from 80% of the limit on single-line fields and always on a TextArea. Typing past the limit is allowed and never blocked: the counter turns $errorText and reads the overflow as −n, the text is kept, the field error appears on leaving or on the press, and the CTA becomes soft-disabled. A hard maxLength is only for fixed-format fields (phone, birth year, codes).Contentcharacters, not bytes. Why: GOV.UK tested a soft limit with low-digital-skill users: they type or paste the full answer, then edit it down.Accessibilityspoken at 80% and at the limit ("20자 중 16자") and on overflow ("3자 초과"); never a live region on every keystroke.Replaces in codethe labelRight "123 / 500" counter, the in-box TextArea counter, hard maxLength on free-text fields.
Field error and validation
Say what is wrong where it is wrong, and when to say it. Reward early, punish late.
클럽 이름*
강
클럽 이름은 2자 이상 입력해 주세요
Ideal a 2px border, a 16 glyph, the message in $errorText
Timing
클럽 이름*
예: 강남 라켓클럽
1 Untouched an empty required field shows no error
클럽 이름*
강
2 Typing first time: no error, only the live counter
클럽 이름*
강
클럽 이름은 2자 이상 입력해 주세요
3 Left wrong on leaving a non-empty value it is checked
클럽 이름*
강남
4 Fixed live re-checked on every change; cleared once valid, and nothing replaces it
On submit
클럽 이름*
예: 강남 라켓클럽
클럽 이름을 입력해 주세요
Empty required flagged when the CTA is pressed, then focused
확인할 항목이 3개 있어요
클럽 이름: 2자 이상 입력해 주세요지역: 지역을 골라 주세요종목: 종목을 골라 주세요
Summary three or more: a danger Notice with one line per item
클럽 이름*
예: 강남 라켓클럽
클럽 이름을 입력해 주세요
클럽 이름*
강
클럽 이름은 2자 이상 입력해 주세요
전화번호*
1234
전화번호를 010-0000-0000 형식으로 입력해 주세요
클럽 이름*
강남 라켓클럽
이미 있는 클럽 이름이에요. 다른 클럽 이름을 입력해 주세요
Copy the fix plus the rule: empty, length, format, taken
Dark
클럽 이름*
강
클럽 이름은 2자 이상 입력해 주세요
Dark the border and text share the light danger
English
Club name*
G
Enter at least 2 characters for the club name
English the instruction form, the same words everywhere
Anatomythree cues that do not depend on colour: the 2px $error border; a 16 alert glyph, 8, then the message in 13/18·600 $errorText (7.36:1), the glyph centred on the first line of the message through the line slot; the weight. The message takes the 18 support line and the counter stays at the right. The label is not recoloured. A Choice set or a Picker Cell has no border to tint: the line alone.Timingfirst time typing: no error (live feedback only for the counter). On leaving the field: validate a non-empty value for format, length and range; asynchronous checks run here. An empty required field: no error on leaving, flagged when the CTA is pressed or on a later visit after a press. After an error has shown: re-validate on every change and clear the moment it is valid. CTA pressed with something missing: validate everything, mark every invalid field, scroll to the first and focus it, announce "N개 항목을 확인해 주세요" once; keep the keyboard and never clear input. The server rejects: the same lines, the input kept. Three or more fields, or the first error off screen: an error summary.Copythe fix plus the rule in the instruction form: empty "[항목]을 입력해 주세요" or "[항목]을 골라 주세요" · length "[항목]은 2자 이상 입력해 주세요" · format "[항목]을 [형식]으로 입력해 주세요" · taken "이미 있는 [항목]이에요. 다른 [항목]을 입력해 주세요". The same words in the field line, the announcement and the summary.Accessibilitythe control's name carries the suffix "입력 오류"; the line is a polite live region plus one explicit announcement; never a Toast; no shake.Replaces in codetoast and banner validation, hand-drawn red hints, LabeledField showing caption and error together.
Required and optional marker
The one sign that a field must be answered.
클럽 이름*
예: 강남 라켓클럽
소개(선택)
예: 강남에서 매주 일요일 아침에 복식을 쳐요
0/200
Ideal one asterisk after the label; (선택) on the optional
지역*
선택
활동 요일(선택)
토요일, 일요일
Cell label the marker rides on the Cell's own 15/22 label
종목*
단식복식혼합 복식
Choice set legend the legend carries it in the label style
확인한 근거
둘 중 하나는 필요해요
무엇을 확인했나요?
예: 입구 간판과 코트 4면을 직접 확인했어요
0/500
공개 참고 링크
https://
Either/or one of two is needed: the head says so and neither field is marked (선택)
Anatomythe one asterisk (owner LAW): after the label, inside the label's own text, the label's size (13/18·600 on a Field, 15/22 on a Cell), colour $errorText (7.36:1); hidden from assistive technology, with "필수" in the accessible name ("클럽 이름, 필수"). No legend explains the asterisk: "* 필수 항목" is not drawn (1.16). Optional fields that sit beside required ones end their label with a quiet "(선택)"; it never appears in a placeholder.Statesa required control starts genuinely unset (no literal default; an empty Choice set; a Stepper below its range); a press shows what is missing. A control that cannot represent unset (a hue slider) is exempt. A step whose every field is optional marks none: its 건너뛰기 already says so.Either/or groupfields of which one must be answered, whichever, are each optional alone and required as a pair: the group's head says "둘 중 하나는 필요해요" (the Section head's note) and its fields drop (선택), so no field claims to be optional while one is not. Example: 장소 정보 제안's evidence, the head 확인한 근거 over 무엇을 확인했나요? and 공개 참고 링크.Replaces in codethe LabeledField required asterisk (labeled-field.tsx, read aloud as "별표"); "(선택)" inside placeholders (clubs.ts, sessions.ts, venues.ts, dm.ts).
Field note
Retired (the owner, 2026-10-20; 1.16): a field carries no note, a cost the press commits is the CTA band's total, a consequence that changes the decision is the confirmation Dialog's body, and who sees an answer is the 공개 범위 page's.
참가비*
250,000
원
참가비는 0원에서 100,000원 사이로 입력해 주세요
Only the error the support line is the error, once it occurs; the range the amount left is its text
Field skeleton
The placeholder of a form field while its value loads.
Ideal a label bar and a 48 box: 70 per field, 32 apart
Dark gray100 on the dark canvas
Anatomya label bar 12 tall at 30% width inside the 18 label line → 4 → a 48 gray100 r12 box: 70 per field, 32 between fields; a Picker Cell is a 48 bar. One shimmer per screen: every bar pulses together, opacity .6 to .9 over an 800 ms half-cycle, stopping after 5 s (static .75 under reduced motion).Statesloading; swaps to the field with a 150 ms cross-fade; appears after the Skeleton delay (300 ms).Accessibilityone label on the container ("불러오는 중"), aria-busy; the bars are hidden.Replaces in codefield-shaped Skeleton blocks in the edit and settings screens.
Map face in a form
Show the picked venue on a map under its Cell.
테니스장*
올림픽공원 테니스장
Ideal 350 × 140, a 24 ink pin, 8 under the 장소 Cell
테니스장*
올림픽공원 테니스장
Loading gray100 until the tile arrives
테니스장*
올림픽공원 테니스장
Error the court tile when the map fails
Anatomyafter a venue is picked: a 350 × 140 r12 static map with a 24 ink pin, a 1px inset #E5E5E5, 8 under the 장소 Cell. A tap reopens the search takeover.Accessibilitybutton named "{venue}, 장소 변경". Image load: a 150 ms fade; reduced motion: instant.Replaces in codepicked-venue-card.tsx.
Composer photo tiles
The photos attached to a post being written: see them, remove one, add more, put them in order.
2/4
Ideal 2/4: the add tile counts against the real limit
At the limit the add tile is hidden at 4/4
3/4
Dragging a long press lifts one tile to reorder
Anatomy4-up wrapping rows, gap 8; tile = (350 − 3 × 8) / 4 = 81.5 (computed, a listed exemption from the grid), r8. Remove: a 24 ink α0.6 circle with a white 16 × at the top-right, inset 4, inside a 48 target. The add tile is gray100 with a 24 + and the count against the content's real limit ("2/4": a club post holds four). Long-press drag reorders.Accessibilityeach tile is a button named "사진 2/4"; a tap opens a MenuSheet with 앞으로 이동 · 뒤로 이동 and, in the destructive group, 삭제 (the alternative to dragging, WCAG 2.5.7); removing a draft photo is immediate with a 되돌리기 Toast.Motionthe drag follows the finger; tiles reflow instantly; reduced motion: unchanged.Replaces in codethe attachment strip in post-create-screen.tsx.
Consent group
Agree to the terms one by one or all at once, and read each before agreeing.
전체 동의
[필수] 서비스 이용약관에 동의합니다
보기
[필수] 개인정보 처리방침에 동의합니다
보기
[필수] 개인정보 국외 이전에 동의합니다
보기
[선택] 마케팅 푸시 알림 수신에 동의합니다
보기
Ideal nothing is pre-ticked
States
전체 동의
[필수] 서비스 이용약관에 동의합니다
보기
[필수] 개인정보 처리방침에 동의합니다
보기
[필수] 개인정보 국외 이전에 동의합니다
보기
[선택] 마케팅 푸시 알림 수신에 동의합니다
보기
Mixed the master shows a minus while partly ticked
전체 동의
[필수] 서비스 이용약관에 동의합니다
보기
[필수] 개인정보 처리방침에 동의합니다
보기
[필수] 개인정보 국외 이전에 동의합니다
보기
[선택] 마케팅 푸시 알림 수신에 동의합니다
보기
All required optional items stay a choice
전체 동의
[필수] 서비스 이용약관에 동의합니다
보기
[필수] 개인정보 처리방침에 동의합니다
보기
[필수] 개인정보 국외 이전에 동의합니다
보기
[선택] 마케팅 푸시 알림 수신에 동의합니다
보기
All ticked the master ticks every item
전체 동의
[필수] 서비스 이용약관에 동의합니다
보기
[필수] 개인정보 처리방침에 동의합니다
보기
[필수] 개인정보 국외 이전에 동의합니다
보기
[선택] 마케팅 푸시 알림 수신에 동의합니다
보기
Focus the ring goes on the focused item
In the step
7/8
약관에 동의해 주세요
전체 동의
[필수] 서비스 이용약관에 동의합니다
보기
[필수] 개인정보 처리방침에 동의합니다
보기
[필수] 개인정보 국외 이전에 동의합니다
보기
[선택] 마케팅 푸시 알림 수신에 동의합니다
보기
9:41
다음
Soft-disabled grey and pressable; a press shows what is missing
7/8
약관에 동의해 주세요
전체 동의
[필수] 서비스 이용약관에 동의합니다
보기
[필수] 개인정보 처리방침에 동의합니다
보기
[필수] 개인정보 국외 이전에 동의합니다
보기
개인정보 국외 이전에 동의해 주세요
[선택] 마케팅 푸시 알림 수신에 동의합니다
보기
9:41
다음
After a press the missing item takes focus and shows its error
7/8
약관에 동의해 주세요
전체 동의
[필수] 서비스 이용약관에 동의합니다
보기
[필수] 개인정보 처리방침에 동의합니다
보기
[필수] 개인정보 국외 이전에 동의합니다
보기
[선택] 마케팅 푸시 알림 수신에 동의합니다
보기
9:41
다음
Ready the fill becomes ball once every required item is ticked
Revoking a consent
위치 정보 이용 동의를 철회할까요?철회해도 동의 관리에서 언제든 다시 동의할 수 있어요
닫기철회
Equal choices a consent dialog nudges nowhere: two choices, one size, one shape, one colour
위치 정보 이용 동의를 철회할까요?철회해도 동의 관리에서 언제든 다시 동의할 수 있어요
닫기철회
200% the pair stacks in reading order, the same two buttons
Anatomya master row 전체 동의 (label weight 600; ticks or clears every item; mixed when partly ticked), then 16 and a cell hairline, then the items. Each item is a Cell 48: a leading 24 checkbox at the rail, the label with its [필수] or [선택] prefix 15/22 (content shifted 36), and a trailing 보기 › Door pill (md 36, a 16 chevron 4 after the label; its own focus stop; 44 by slop) that pushes the Document page. Marketing sits last as its own group with the (광고) wording. Nothing is pre-ticked.Statesunticked · ticked · mixed (master) · the CTA follows the Button rule (soft-disabled while one required item remains, validate on press while several do) and a press shows the error of each missing item ("개인정보 국외 이전에 동의해 주세요"), the first taking focus; the band has no helper. A consent is never folded into a button label.Revokinga consent Dialog gives its two choices equal size, shape and colour (the kit's FvkDialog equal): two tonal lg Buttons of half the line each, neither primary and neither danger, in reading order (닫기, 철회); a one-sentence body that names the purpose and the way back. From 1.5× text the pair stacks, the first on top. The legal text stays verbatim until counsel reviews it.Accessibilityeach item is a checkbox named with its prefix ("[필수] 이용약관"); 보기 is its own stop ("이용약관 보기"); the master reads "전체 동의" with aria-checked true, false or mixed.Replaces in codeonboarding-step-consent.tsx (one SelectionCheck row per purpose).
ProviderButton
The sign-in buttons of the providers we ship, each drawn as its service requires inside our geometry: the same size and shape, only the brand fills differ.
Android and web Kakao, Naver, Google: the same size and shape
Providers
카카오 로그인
Kakao #FEE500, black symbol, black label at α0.85
네이버 로그인
Naver Brand-mandated: #03A94D with white, 3.1 : 1
Apple로 로그인
Apple Black with white; the system button on iOS
Google 로그인
Google White, a 1px #747775 stroke, the full-colour G
States
네이버 로그인
Pressed dims .85 with no settle
카카오 로그인
Loading a spinner in the symbol’s colour beside the label
카카오 로그인네이버 로그인Apple로 로그인
In a set only the tapped provider spins; the rest wait inert
Google 로그인
Disabled .5 while offline; the strip says why
로그인이 취소됐어요. 다시 시도해 주세요
카카오 로그인
Cancelled a danger line above the first button, not a Notice
Dark
카카오 로그인네이버 로그인Apple로 로그인Google 로그인
Dark Apple turns white, Google #131314; the others stay
200% text
카카오 로그인
200% the label stops at 1.5× and the container grows
English
Login with KakaoLogin with NaverSign in with AppleSign in with Google
English Kakao’s mandated label; the others follow its verb
Anatomythe lg Button geometry (52, full width on the rail, radius 12 as Kakao requires and as the lg Button has) with the provider’s own fill, symbol and label. The symbol sits left of the label, 8 between, both centred in the button; the label is 17/24·500 in the system font, at most one third of the container’s height: at large text it stops at the 1.5 cap and the container grows with it. Brand fills are exempt from the one-ball rule; a sign-in screen draws no ball action.ProvidersKakao: container #FEE500, speech-bubble symbol #000000, label #000000 at α0.85, "카카오 로그인" (English "Login with Kakao", Kakao’s mandated label, which overrides "Sign in") · Naver: container #03A94D, white N (16, the guide’s floor), white label, "네이버 로그인"; white on #03A94D is 3.1 : 1, which the brand guide fixes, so it is a brand-mandated exception like Kakao’s colours · Apple: black with white symbol and label, white with an outline in dark, "Apple로 로그인" ("Sign in with Apple"); on iOS it is the system button (the approved look and the translation) and custom artwork is black or white only · Google: #FFFFFF with a 1px #747775 stroke, the full-colour G, label #1F1F1F ("Google 로그인"); dark #131314 with a #8E918F stroke and #E3E3E3 text.The setiOS shows Kakao, Naver and Apple; Android and the web show Kakao, Naver and Google. Order is fixed so a second visit finds the same thing. All 52 tall, radius 12, full width, 12 apart, none smaller than the others (the Apple and Google guides require equal prominence). App Review 4.8 makes Sign in with Apple mandatory on iOS once Kakao or Naver is how an account is made; the app’s own email sign-in does not satisfy it. A ghost md Button 이메일로 로그인 follows the set, then the terms links.Contenta cancelled provider flow shows a danger helper line above the first button (the core helper line: the alert glyph, 8, the message 15/22·600 $errorText), never a Notice. The label is the provider’s: 카카오 로그인, 네이버 로그인, Apple로 로그인, Google 로그인.Accessibilitybutton; name = the visible label; while loading the label stays and aria-busy is set, and only the tapped provider shows the spinner (the other buttons are disabled until it settles); a cancelled flow is announced as an alert; the symbol is hidden from assistive technology.Motiondim only.Replaces in codethe Kakao button in login-screen.tsx.
DOM check (scripts/verify-flat-v2-boards.mjs): 205 frames, rail 0, type 0, targets 0, shadow 0, dot 0, ball 0.
Spec (grounding, anatomy)
Controls and fields, composed from the shared flat v2 kit.
route · flat-v2-kit-controls.jsxgrounded in 30 source file(s)