Skip to content

Owner review 1, lane B: notices, messages, fields, wizard chrome and CTA helpers (2026-10-11) ​

Status: Draft · Date: 2026-10-11 · Purpose: answer the owner's review-1 points 7, 20, 9, 11, 12, 13, 10 and 16 on the flat v2 boards (/wireframes/flat-v2-controls, /wireframes/flat-v2-patterns) with cited evidence, then audit library §7 to §10 for every value or behaviour that departs from current guidance or measured peer practice. Audience for every verdict: Korean amateur-club members aged 40 to 60 with low digital literacy, on phones, with the keyboard up for most field steps. Every source below was opened in this session; dates are the page's own where it shows one. Contrast ratios are computed with the WCAG 2.x relative-luminance formula from the S2 ledger values. Companions: behaviour addendum (toast timing, announcements, B6 text scaling), colour research (its "text ≤ 14px needs ≥ 6.5:1" floor is used below), wizard canon.

0. How to read this note ​

0.1 Evidence tags ​

TagMeaning
[S]Standard or law: WCAG 2.2, ARIA APG, KS X OT0003 (KWCAG 2.2) by way of KRDS, 전자상거래법
[G]Platform or government guideline: Apple HIG, Android, KRDS, GOV.UK
[D]Design-system documentation: Material (MDC, Material Web), Seed (당근), TDS (토스), Carbon, Atlassian, Primer, Polaris
[R]Research firm: NN/g, Baymard
[P]Peer-reviewed study (sample size given)
[Q]Published study that is not peer-reviewed (sample size given)
[W]Practitioner opinion with reasoning (Silver, Roselli, Axess Lab, Smashing)
[O]Observed in a real app (App Store frame or a published teardown); see §0.4

0.2 Verdicts at a glance ​

PointOwner's questionVerdict
7A better way than the generic icon?Yes. A state-word title carries the meaning; the icon is off by default (a specific glyph, or the ! on warning and danger only); five tiers; the action sits under the body.
20Compact messages that get users to actOne surface per kind of message (point 20 table). Toast = result or undo only. A failure the user caused is a persistent Notice beside the action, not a toast. State first, then one verb.
9Best practice for the input fieldVisible top label; example-only placeholder in tertiary; one 18 px support line under the control (hint or error, counter on the right); border at 3:1; no status icons in the box.
11Are helper texts needed, and whenRarely. Four cases only. Below the control, one sentence, replaced by the error, which restates the rule.
12The notice under a field, and its iconIt answers "what will this answer do", not "how do I type it". One line goes in the support line; more becomes a neutral Notice. No default icon; never hide it behind an ⓘ.
13Validation: labels, controls, helpersReward early, punish late. Error = 2 px danger border + 16 glyph + 600 text, replacing the hint. Focus the first invalid field. The asterisk LAW holds, with three caveats (point 13).
10Top back plus bottom primary: unify?Keep the split: it is the convention for step funnels. The defect is spacing: move the progress to its own 4 pt row, 16 px clear of the controls, with a count.
16Best practice for the CTA helperAbove the button; decision facts only (cost, terms, what is missing); one or two lines; the disabled primary becomes soft-disabled (grey, focusable, press validates).

0.3 What could not be read, and what that does to the evidence ​

  • m3.material.io is client-rendered and returned an empty body in every fetch. Material 3 statements rest on the Android MDC and Material Web docs on GitHub, plus the archived Material 1 site (m1.material.io), which Google labels as no longer maintained. Treat Material 1 as history, not current spec.
  • KWCAG 2.2 (KS X OT0003:2022, 33 items): the clause text was not obtainable. KRDS pages print the KWCAG criterion names they satisfy (레이블 제공, 오류 정정, 반복 입력 정보, 초점 이동과 표시); those names are cited as KRDS reports them, without clause numbers.
  • Baymard guideline pages are premium; only public blog and list pages were used.
  • App Store frames are marketing frames. They show header chrome and a few notices; none shows a form error, a wizard header, or a CTA helper line. The 당근 글쓰기, 카카오T 호출 and 배민 주문 flows could not be observed from any public frame or text, so no claim is made about them. The only text observations of CTA position are yozm's reviews of Toss and 요기요. The Toss 송금 and 쿠팡 결제 CTAs themselves are not visible in any frame; what is known of them comes from the TDS docs, yozm and the KFTC case in point 16.
  • No public document describes Toss's or Kakao Bank's validation timing; their practice is inferred only from TDS docs and store frames, and is labelled that way.

0.4 Frames viewed ​

Each lookup URL returns the listing's screenshotUrls (version and release date as of the lookup, 2026-10-11). Frame numbers count left to right across the listing.

App (listing version)FrameWhat the frame shows (and nothing more)
카카오뱅크 2.65.0 (lookup)5A scam-account warning as a bottom sheet over a dimmed transfer screen: red siren illustration, bold title "사기 신고 계좌입니다. 이체에 주의하세요!", red account line, grey source line, then a button pair: "계속하기" (grey tonal, about one third) and "이체 취소" (yellow primary, about two thirds). The safe action is the primary.
카카오뱅크 2.65.07"내 비즈니스" card: a yellow bell glyph, a blue title "비즈니스 알림", a chevron, and a second line "7.1 ~ 7.25 : 부가가치세 신고기간".
카카오 T 7.14.0 (lookup)8A notice row with a bold text prefix "공지", one line "안전을 위해 자동결제를 이용하세요", a chevron, a hairline above. No fill, no icon.
카카오 T 7.14.02, 4Promo and suggestion rows: pale fill with a small illustration at the right ("해외에서도 그대로 부르세요!"); a blue "추천" mini-tag, a question, a chevron.
카카오페이 26.8.11 (lookup)6A one-line info row: a specific glyph, "내 건강 괜찮은지 궁금하다면", and a small tonal button "확인하기" at the right.
카카오페이 26.8.117A poll: four option rows and a full-width grey disabled button "투표하고 결과보기" under them (no reason text is visible).
카카오페이 26.8.115A stock screen with two equal-width buttons at the bottom: "판매" (blue) and "구매" (red).
쏘카 19.0.0 (lookup)8A map screen whose bottom card lists three facts, each with its own specific glyph (a parking mark, a car, a won mark), and a bare chevron-left back at the top left.
쏘카 19.0.06A bottom sheet with a centred title, × at the top right, and tone cards in pale blue.
토스 5.279.0 (lookup)5A loan-finder screen: bare chevron-left at the top left, a blue status line "내 대출 승인률 99% · 매우 높음", and a full-width blue CTA "내 대출 한도 확인" under the content.
쿠팡 9.4.1 (lookup)7"구매완료" screen: centred title, × at the right, "원터치결제 주문이 완료되었습니다!" with a text action "변경하기 >", delivery guarantee as green text.
당근 26.41.0 (lookup)5-8Sub-service screens (당근알바, 부동산, 중고차, pay): bare × at the top left, centred title, one or two icons at the right.
배민 16.26.0 (lookup)3, 4Pushed screens with a bare ← at the top left and home and cart icons at the right.
플랩풋볼 2.0.1 (lookup)4Match page: bare chevron-left and a centred two-line title (date, venue).
Duolingo 7.143.1 (lookup)2-4Lesson screens: × at the left, a thick progress bar filling the rest of the same row, a counter at the right.
Meetup 2026.10.8 (lookup)4Event page: the back chevron sits in a pale filled rounded button, as do share and bookmark.
Venmo 26.22.0 (lookup)1Amount entry: two equal-weight buttons "Request" and "Pay" directly above the numeric keypad.

0.5 Contrast computations used in this note ​

WCAG 2.x relative luminance on the S2 ledger values; the ratio is foreground against background. "Floor" is the colour research's 6.5:1 for text of 14px or less.

PairRatioUsed for
#D4D4D4 on white1.48:1today's Input, TextArea, Switch-off and thumb edge
#8E8E93 on white3.26:1proposed $borderControl, Switch off track
white at α0.40 over dark canvas #0F1211 (#6F7170)3.83:1proposed dark control border
#B8332E on white / on gray1005.91 / 5.28:1today's small danger text (below the 6.5:1 floor)
#A3261F on white / on gray1007.36 / 6.57:1proposed $errorText
#4F5851 secondary on white7.38:1helper text, label secondary
#626C64 tertiary on white / on gray1005.46 / 4.87:1proposed placeholder
ink #0D100E against tertiary / against secondary3.51 / 2.59:1how far a placeholder sits from typed text
success #014B40 on #CBEBE5; info #4B0082 on #E8DCFB7.95; 9.91:1Notice titles
ink on warning #F5B301; white on danger #B8332E; ink on gray10010.32; 5.91; 17.09:1Notice titles
ball primary at .5 over white: container #EAF8A4 on white1.14:1today's disabled primary container
the same label #7C8459 on #EAF8A43.48:1today's disabled primary label
secondary on gray100 #F2F2F2; gray100 on white6.59; 1.12:1proposed soft-disabled label; Segmented and Search fill
white thumb on #E5E5E5 track; #D4D4D4 edge on the track1.26; 1.18:1today's Slider thumb
ink on #E5E5E5 track15.19:1progress done against remaining

7. Notices: a better way than the icon ​

What we have ​

  • Notice (library §4, board flat-v2-components Notice sheet; it replaces Banner, Callout, Well, NetworkBanner): inline, r12, pad 12 16, min 48 (68 with a body). Tone fills: success #CBEBE5 / text #014B40, warning #F5B301 / ink, danger #B8332E / white, info #E8DCFB / #4B0082, neutral gray100. A 20 glyph + 12 gap in every tone (the generic set: check-circle, alert, alert-circle, info, info). Title 15/22·600, body 15/22·400, optional text action 15/22·600 at the right (it drops under the text from 150% text size, addendum B6), optional close 24 in a 44 target.
  • The same box is the offline strip, CountdownNotice and the neutral Notice under a field (spec 10.5 on the controls board).
  • Two of the five fills are not weak. The warning fill is the Tag's solid amber and the danger fill is solid red with white text, so a 350 × 68 Notice in either tone is the loudest object on a screen.
  • The Toast (library §8) is a separate ink surface whose status glyph (K5) comes from the same generic set in dark-theme tones.

Evidence ​

7.1 How systems and apps present notices

SourceTreatmentTag
GOV.UK notification bannerA boxed banner with a heading ("Important", "Success"): "Use headings like 'Success' - so that you're not relying on colour alone to convey meaning"; "Use notification banners sparingly"; placed "immediately before the page h1"; "Avoid showing more than one"; not for validation errors ("just show the error summary").[G]
GOV.UK warning text, inset textWarning text is a ! glyph plus bold text, with a visually hidden "Warning" prefix; its markup has no container. Inset text: "Use inset text very sparingly" and "avoid using inset text as a way of highlighting very important information" (use warning text).[G]
KRDS 긴급 공지Icon + type label + one line of text (two on a narrow screen) + at most one link. First element of the content, full width. Three urgency levels (상: national disaster; 중: advance notice of service interruption; 하: matters to most users). "여러 개의 긴급 공지를 사용하지 않는다"; no hide button; not for form feedback.[G]
KRDS 스낵바, 토스트Snackbar: one sentence, at most one text button at the right of the message, "아이콘은 반드시 필요한 경우에만 사용한다 … 시각적으로 부각되지 않게". Toast: one line. Neither is for field messages: those go "인라인 텍스트 방식으로".[G]
Seed (당근) Callout, Page BannerSix tones; icon optional (Page Banner: Fill type recommended); title optional and inline, a short status word (not its own line); Callout is local and content-width, Page Banner is global, top of page, full width, one per screen, dismissible only for once-only information and never for warnings or errors. "콜아웃은 꼭 필요한 경우에만 절제하여 사용해요."[D]
TDS Mobile index (토스)No Banner, Notice, Callout or Alert component. Notices are BottomInfo (a text list at the foot of a screen for legal notes; no icon), Toast, Dialogs, and full-page Result (figure, title, description, button).[D]
Toss UI/UX guide"상황을 정확히 전달하는 그래픽을 사용해 주세요" (no warning icon when nothing is wrong); "한 화면에 그래픽을 많이 사용하지 마세요".[D]
Carbon notificationToast, inline, actionable, callout. "Each status is associated with a specific color and icon." Low contrast by default ("When in doubt, use low-contrast notifications"); high contrast "best for critical messaging". "Only one action is allowed." Toasts: two lines. "They are disruptive and should be used sparingly."[D]
Atlassian section message, Primer banner, Polaris bannerTinted box with an icon. Atlassian: titles optional, "Don't rely on color alone", "avoid dead ends". Primer: titles required, "Icons match the Banner variant and can't be disabled", "Whenever possible it is recommended to only add a single action", one banner per page, critical banners not dismissible. Polaris: info, warning and critical banners "should include a call to action with specific next steps"; "Present one piece of information or required action per banner".[D]
Material 1 snackbar (archived)"Snackbars contain a single line of text"; "a single action, neither of which may be 'Dismiss' or 'Cancel'"; "They cannot contain icons."; important enough to block the screen: a dialog.[D]
NN/g, indicators, validations, notifications (2024-01-17)Notifications are either action-required (modal) or passive. In its examples a top-of-page banner was often missed because it sat far from the add-to-cart button, and a small toast that faded after 5 seconds was missed by a user. Its conclusion: a toast is unsuitable for error messages, because users can miss it.[R]
Store frames (§0.4)카카오T "공지" row: a bold text prefix instead of an icon or a box. 카카오뱅크 scam warning: the top tier is an interrupting sheet with an illustration, a bold title, and the safe action as the primary. 카카오페이 and 쏘카: when an icon appears it is specific (a parking mark, a car, a won mark), not a generic ⓘ.[O]

7.2 The owner's five questions against that evidence

QuestionWhat the evidence shows
Fill or no fillGlobal systems use a toned box (Carbon's low-contrast default, Atlassian, Primer, Polaris, Seed Weak). Low tiers drop the box: GOV.UK warning text and inset text, BottomInfo, the 카카오T "공지" row, the 쏘카 fact lines. Carbon ties contrast to importance: quiet by default, loud only for critical.
Icon, illustration or emojiSplit. Always an icon: Carbon, Primer, Atlassian. Optional or none: GOV.UK banner, KRDS snackbar (icons only when truly necessary), Material 1 snackbar ("They cannot contain icons."), Seed, BottomInfo. Korean apps that do use glyphs make them specific (쏘카, 카카오페이). Illustration appears only at the interrupting tier (카카오뱅크). No design system puts an emoji in a notice. Toss's own rule is that a graphic must state the situation, not decorate it.
Title weight and colourThe title is the non-colour cue: GOV.UK headings, KRDS's type label, Primer's required title, Seed's inline status word. None of these sources specifies a weight; our 600 follows the library's title role. WCAG 1.4.1 needs a non-colour cue only for information carried by colour, and a sentence stating the state already is one (1.4.1).
Where the action goesOne action in Carbon and KRDS (one link or one button); Primer recommends one and allows two; Polaris allows up to two. On one-line messages the action trails the text (KRDS snackbar: "메시지 우측에 구분하여"). No source studied specifies the position for a notice with a body; our own addendum already moves it under the text from 150% (B6).
How importance is tieredBy urgency or blocking: KRDS (urgency 상/중/하), Carbon (low vs high contrast), Seed (local Callout vs global sticky Page Banner; Weak vs Solid), GOV.UK (inset text, warning text, banner, error summary), NN/g (passive vs action-required). Every source that states a cap says one prominent notice per screen.

Recommendation: Notice grammar v3 ​

Anatomy: title first, no default icon. The first line is a 600 state sentence in the tone's text colour; the body is optional; the action is the last thing read.

┌──────────────────────────────────────────────┐   r12 · pad 12 16 · 350 wide
│ 마감이 가까워요                                │   title 15/22·600, tone text colour
│ 내일 18:00에 모집이 끝나요.                     │   body 15/22·400 ink, ≤ 2 lines
│ 지금 신청하기                                   │   action 15/22·600, 8 under the body, at the text edge
└──────────────────────────────────────────────┘   heights: 48 one line · 68 title + body · 98 with the action
  • One-line Notice: the single line is the title (600); the action trails it at the right (KRDS snackbar), min 48. Notice with a body: the action goes under the body, left-aligned at the text edge, at every text size. That removes the special case in B6 (action at the right until 150%) and keeps the action out of a 318 px Korean text column.
  • Title copy: a state in 12 syllables or fewer, 해요체 ("입금 대기 중", "마감이 가까워요", "오프라인이에요"). Never a bare category word ("안내", "주의") as the whole title. Body: the consequence, then the way out; two lines at most (about 38 syllables at 15 px in the 318 px column: our estimate from a full-width Hangul advance of 1 em). More than two lines is a Cell or a sheet, not a Notice.
  • Contrast (computed, all pass 4.5:1): success #014B40 on #CBEBE5 7.95; info #4B0082 on #E8DCFB 9.91; warning ink on #F5B301 10.32; danger white on #B8332E 5.91; neutral ink on gray100 17.09.

Icon rule. Two kinds of glyph are allowed and nothing else.

  1. The severity glyph (alert, 20, in the tone's text colour) on warning and danger Notices only. This is the tier where the fill is loud and a severity cue that survives greyscale is worth its width. Evidence: GOV.UK warning text and Atlassian ("warning messages are yellow with an exclamation point icon") pair the exclamation glyph with warnings; Carbon pairs an icon with every status; NN/g's error guideline 5 adds that an icon beside an error helps colour-blind users. We keep it only where severity is the point, which is the GOV.UK reading.
  2. A specific glyph that adds meaning the words do not: wifi-off on the offline strip, lock on a privacy note, clock on a hold, card on a payment note, pin on a place note. This is the 카카오페이 and 쏘카 pattern, and Toss's own "상황을 정확히 전달하는 그래픽" rule.

Never the generic ⓘ or check-circle on info, success or neutral (the "generic and ugly" glyphs); never emoji (canon reserves emoji for facts and summaries, and they render differently per OS); illustration only at tier 4. For info and success the title already says it, which is what GOV.UK's "Success" heading and KRDS's type label do.

Tiers. Escalation is by how much the message demands, not by how scary it is.

TierComponentFillTitleGlyphActionCloseCap per screenUse when
T0Caption row with a label (library §18.2)noneoptional bold prefix 13/18·600 ink ("안내", "공지")nonenonenon/acontext that needs no action (the 카카오T "공지" row)
T1Notice, neutralgray100optionalnone (specific glyph allowed)at most 1only if it can recur2 in totala tip, a consequence, a field note
T2Notice, success or infoweak tonerequired (state word)none (specific glyph allowed)at most 1info only, once-only2 in totalan outcome or state worth a box
T3Notice, warning or dangeramber / solid redrequiredalert 201, or the exact instructionnever; persists until resolved1it blocks or time-boxes the primary action, or a failure the user caused
T4Dialog or Sheetwhite17/24·600optional 48 tone disc with a specific glyph2 buttons; for a system warning the safe one is primaryper sheet rules1irreversible, financial, safety
  • T3 is the only tier that uses the amber and solid-red fills. A notice that does not block or time-box anything is T1 or T2, so the loud slabs become rare, which is the real fix for "ugly".
  • Dismissal follows Seed and Primer: a close exists only for information that is shown once; warnings and errors are never dismissible (Seed Page Banner: "경고·오류 메시지에는 사용하지 않습니다"; Primer: "Critical banners communicate an error which the user must resolve in order for the banner to disappear").
  • Every T3 either carries its action or says the exact instruction (Polaris, Atlassian: no dead ends). If neither is possible it is not a Notice but a status line (T0).
  • T4: when the system is warning about a risk the user has not weighed (a flagged account, a payment that will fail), the safe action takes the primary role, as 카카오뱅크 does in frame 5 and as HIG asks ("Don't assign the primary role to a button that performs a destructive action, even if that action is the most likely choice", buttons). A destructive confirm the user started themselves (삭제, 탈퇴) keeps the danger style on its confirm button (library §8).

Accessibility.

  • Colour is never the only signal: the title states the state (WCAG 1.4.1); T3 adds the glyph.
  • A Notice inserted after load is a status message (WCAG 4.1.3: role=status, ARIA22). role=alert only for T3 failures the user caused (ARIA19); the same page lists role=alert on non-urgent content as a failure.
  • The action is its own focus stop with a 44 target; the close is aria-label "닫기"; neither moves focus on insertion.

What changes ​

  • Library §4 Notice row and library §9 Offline strip: replace the generic per-tone glyph set with the icon rule; make the title required on T2 and T3; move the action under the body; add the tier table; restrict amber and solid red to T3; cap T3 at one per screen.
  • Library §14 CountdownNotice keeps the amber fill (it time-boxes the primary action) and gains a clock glyph; library §10 AttachedPanel to Notice follows point 12 below.
  • Boards: flat-v2-components Notice sheet (variants: remove the generic glyphs, add the action-under-body and tier specimens, show T0 and T1 side by side); flat-v2-patterns CountdownNotice; flat-v2-controls spec 10.5.
  • Code: one Notice in @twomore/ui replacing banner.tsx, callout.tsx, Well and NetworkBanner; a glyph prop typed to an allow-list (severity, wifi-off, lock, clock, card, pin) so a generic ⓘ cannot be passed; a lint rule for the allow-list.

20. Warnings and messages: compact, and they get users to act ​

What we have ​

  • Toast (library §8, K1, K5, addendum B7.4): ink surface, r12, min 48, pad 12 16, 15/22 white text, optional action 15/22·600 in ball on ink, 16 above the tab bar or FAB, a 20 status glyph in dark-theme tones for every kind, × on error and action toasts, swipe or tap to dismiss, one at a time.
  • Durations disagree inside our own docs. Library §15 says "4000 / 6000 / 8000 ms by kind (plain / with action / error)"; addendum B7.4 says success and info 4000, warning and error 6000, any toast with an action 8000.
  • Where failures go today: every mutation failure passes one choke point, createMutationHook, which calls showToast(msg, 'error') after a haptic (packages/app/src/presentation/hooks/create-mutation-hook.ts:246-251). The voice guide's failure strings (rsvpFailed, joinClubFailed, profileSaveFailed, scoreSubmitFailed, voice-and-tone-guide lines 379 to 394) are one template, "앗, [X]하지 못했어요. 다시 시도해 주세요.", so each one reaches the user as an error toast. The same guide (lines 417 to 427) already warns that the template "becomes meaningless" when seen repeatedly.
  • Inline: field errors never use a toast (library §10). Library §22.2 adds a danger line in the CTA band for failures that belong to the action, while the patterns board still says "an error is a Notice above the band, never red text on it".

Evidence ​

SourceWhat it saysTag
KRDS 토스트"사용자 행동에 대한 결과를 일시적으로 표시했다 사라지는 알림 메시지"; "정보형 메시지는 2초 ~ 3초가 적정하며, 주의·경고형 메시지는 3초 ~ 4초가 적정하다"; "한 줄의 메시지"; bottom centre. Not for: "중요하거나 긴급한 메시지", "사용자의 추가 행동", "한 문장 이상의 긴 설명", "개별 입력 양식에 대한 오류, 성공, 경고 메시지".[G]
KRDS 스낵바For "되돌리기, 재시도, 목록 이동 등 … 간단한 조치"; "메시지는 한 문장 이내로"; "버튼은 최대 1개만"; "한 페이지에 하나의 스낵바만"; must not overlap the tab bar or floating button.[G]
TDS ToastBottom or top; "기본은 3000이고, 버튼을 사용했다면 5000이에요."; the button only on bottom toasts; higherThanCTA shows a toast above a fixed CTA "주요 액션 버튼을 가리지 않으면서도"; aria-live default polite.[D]
MDC Web snackbarTimeout "must be between 4000 and 10000", "Defaults to 5000"; "Snackbars are intended to dismiss on their own after a few seconds", with an optional dismiss icon "for accessibility"; announced politely.[D]
Carbon, Material 1Carbon: toasts limited to "two lines of text", one action, can time out after five seconds. Material 1: one line, one action that is not Dismiss or Cancel, no icons.[D]
APG alert, WCAG 2.2.1APG: "It is also important to avoid designing alerts that disappear automatically." WCAG 2.2.1's own 5-second message example passes only because the user can find the same information another way: "If the user has no other means of discovering the same information … each message would need to meet this success criterion."[S]
NN/gA toast is unsuitable for error messages, because users can miss it; a toast that faded after 5 seconds was missed in its testing.[R]
HIG alerts"Avoid using an alert merely to provide information."; "Include informative text only if it adds value."; avoid a title like "Error".[G]
GOV.UK error message, NN/g error-message guidelines, Google tech writing"In the error message explain what went wrong and how to fix it." NN/g: "Merely stating the problem is also not enough; offer some potential remedies." Google: name the offending value and the constraint it broke ("The specified bid ($5) is below the minimum bid ($8).").[G]
Seed Field, Toss UI/UX guide, AtlassianSeed: error messages are short and written as an instruction (its example: "휴대폰 번호 10-11자리로 입력해 주세요"). Toss: 해요체, active, positive. Atlassian: "avoid dead ends and provide people with information on how to proceed".[D]
전자상거래법 다크패턴 (Shin & Kim)Since 2025-02-14 six patterns are banned, among them 순차공개가격책정 ("총비용이 아닌 일부 금액만 표시·광고"), 특정옵션사전선택 and 잘못된 계층구조 ("선택항목의 크기·모양·색깔 등에 현저한 차이를 두어 사업자에게 유리한 특정 항목으로 유인"). A message about money must show the total.[S]

Recommendation ​

One surface per kind of message.

The message is aboutSurfaceSize and timingActionWhy
One field's valueField message (support line, point 13)18 line, until the value is validnone: the fix is in the fieldKRDS: field messages are inline; WCAG 3.3.1
What the user just did, succeeded or undoableToastmin 48; at most 2 lines at 100% (about 21 syllables on one line, 16 beside an action); 4 s, 8 s with an actionat most one real verb (되돌리기, 보기), never 닫기KRDS, TDS, Material 1: results and undo
A failure of something the user submitted (save, join, pay, RSVP)The CTA helper in danger when a CTA press caused it; a T3 Notice at the top of the section otherwise; a Dialog when it blockspersistent until the user retries or leaves; role=alertthe retry, as the helper's or Notice's actionNN/g and APG: an error must not disappear; KRDS: a toast is not for important messages
A background failure with nothing lost (refresh, sync)Toast with 다시 시도no timer; × to dismiss다시 시도KRDS snackbar lists 재시도 as a simple action
The state of a screen or section (offline, deadline, hold)Notice T1 to T3 (point 7)persists while truethe way out, if there is oneKRDS 긴급 공지, Seed
Irreversible, financial, safetyDialog or Sheet; for a system warning the safe action is primarybackdrop does not dismisstwo buttons카카오뱅크 frame 5; HIG alerts; WCAG 3.3.4
The cost or terms of the action about to be takenCTA helper (point 16)1 or 2 linesnoneBaymard; 전자상거래법 순차공개가격책정

A toast never holds the only copy of something the user will need later (WCAG 2.2.1).

Toast changes. (1) The status glyph (K5) becomes warning-only, by the point 7 icon rule: success and info toasts carry no glyph, because the text says "저장했어요" (KRDS: icons only when necessary; Material 1: none). (2) Fix library §15 to the B7.4 order, the one the addendum reasons through: success and info 4000, warning and error 6000, any action 8000, plus Android's recommended timeout. Our timings are longer than KRDS (2 to 4 s) and TDS (3 s, 5 s with a button) and inside Material's 4 to 10 s; keep them for older readers, but record it as a deliberate divergence. (3) Error toasts remain only for background failures (row 4 above), with no timer.

Copy rules: lead with the state or the consequence, then one verb.

  1. Order. The state or consequence first, then the single way out: "참석을 취소했어요 · 되돌리기", "입금 시간이 지났어요 · 다시 신청". Never an apology first.
  2. Errors carry the rule. What went wrong plus the constraint, in the instruction form: "이름은 2자 이상 입력해 주세요", not "잘못 입력했어요" (Google, GOV.UK, Seed). The generic "앗, [X]하지 못했어요. 다시 시도해 주세요." is acceptable only when the cause is unknown; otherwise add the cause or the next step, as the voice guide already says.
  3. 해요체, active, positive (Toss guide): "저장하지 못했어요" over "저장이 실패되었습니다"; no blame.
  4. Action labels are verbs of 2 to 5 syllables and name the result (입금하기, 다시 시도, 되돌리기). 닫기 is a dismissal, not an action.
  5. One idea per message. A second idea is a Cell, a Notice body or a sheet.
  6. Money is stated as a total (참가비 10,000원, not "10,000원부터"), and a consent is never folded into a button label (point 16).
  7. Same words everywhere the error appears (field line, spoken announcement, summary), as GOV.UK requires.

Syllable budgets (our estimate from a full-width Hangul advance of 1 em, so a 318 px column at 15 px holds about 21): toast 21, or 16 with an action; Notice title 12, body 38; field message 24 at 13 px in 350; CTA helper 24 per line.

What changes ​

  • Library §8 Toast, §15 durations, §10 "never a toast": the changes above; add "a failure the user caused is not a toast".
  • Voice guide: add the order rule (state or consequence, then verb) and map the failure strings to their surface (CTA helper, T3 Notice, Dialog) instead of one toast template.
  • Boards: flat-v2-components Toast sheet (success without glyph, the 8 s action specimen, a no-timer background failure); flat-v2-patterns CTA helper danger specimen (point 16).
  • Code: packages/app/src/presentation/components/toast-host.tsx (glyph by kind, no-timer variant). The single choke point is createMutationHook's onError (create-mutation-hook.ts:246-251): give it an errorSurface option ('toast' for background failures, 'helper' for a CTA-caused failure, 'notice', 'dialog') that extends the existing config, rather than a second error path.

9. Input: best practice for the field ​

What we have ​

  • Input (library §7, controls board 7.6): 48 tall, r12, 1px #D4D4D4, pad 0 16, text 15/22 ink, placeholder in secondary #4F5851, label 13/18·600 above (4), helper 13/18 secondary below (4), leading and trailing 20 glyphs, focus = 2px ink border, error = border and helper in #B8332E, disabled .5.
  • TextArea (§7): min 120, grows to 200, counter 13/18 at the bottom right inside the box. Input counter (library §21.2): "12/20" 12/16·500 on the helper line, "further typing is refused" at the limit, spoken at 80% and at the limit. The two specs disagree on where the counter lives.
  • Field (library §10): label + control + helper; no helper = 70 tall; Choice sets, PickerRow and Stepper sit in the same wrapper.
  • Shipped LabeledField / StyledInput (packages/ui/src/labeled-field.tsx): caption, error, required, labelRight; the 'sm' label is 13px in secondary colour.

Evidence ​

TopicWhat the sources sayTag
Label above, always visibleGOV.UK: "All text inputs must have labels, and in most cases the label should be visible." "You should align labels above the text input they refer to." NN/g (2016): labels "immediately above the field for mobile and shorter desktop forms". CHI 2014, 65 participants, 20 guidelines: labels above the field gave fewer fixations, less fixation time and fewer saccades. Seed puts the label in a header above the control. TDS offers appear (label shown only once a value exists) and sustain ("항상 label이 보여요"); its docs call sustain useful when there are several fields.[G] [R] [P] [D] (GOV.UK, NN/g, Google blog, Seed, TDS)
PlaceholdersNN/g (2014, reviewed 2018): "placeholders in form fields often hurt usability more than help it"; "Disappearing placeholder text strains users' short-term memory." HIG: "Because placeholder text disappears when people start typing, it can also be useful to include a separate label". GOV.UK: "Do not use placeholder text in place of a label, or for hints or examples". KRDS: a placeholder is optional and "레이블이나 도움말의 대체 수단으로 사용되어서는 안 된다". Silver: users mistake placeholders for values, and grey on white lacks contrast.[R] [G] [W] (NN/g, HIG, KRDS, Silver)
Support text: above or belowBelow: Material (supporting text, and "Supporting text can be replaced with error text when validating"), Seed (footer: helper and error left, counter right), TDS (help, "텍스트 필드의 아래쪽 도움말"), KRDS (help or error in the help slot). Above: GOV.UK (hint between label and input; markup order label, hint, error, input), Silver (hint above the control, so it stays visible while the user types). Roselli: "Avoid putting important actionable or informational text exclusively below form fields", because "Auto-complete features in browsers obscure them" and "On-screen keyboards can obscure them". Peer-reviewed placement data: 303 participants, six positions (Seckler 2012): positions near the field beat the top or bottom of the form, the right side was rated best; no above-versus-below difference reported. 32 participants (Inal 2016): the right side was spotted fastest, with no significant difference in completion or correction time.[D] [G] [W] [P] (Material Web, Roselli, Seckler 2012, Inal 2016)
CounterGOV.UK: "Only use the character count component when there is a good reason for limiting the number of characters users can enter"; the message sits below the textarea; an optional threshold shows it only at 75% of the limit; "This component does not restrict the user from entering information", which "lets them type or copy and paste their full answer, then edit it down"; tested with 17 users "including those with low digital skills". KRDS: show it when a limit exists, after the label or after the input, updated dynamically ("0/100"). Seed: counter at the right of the footer, neutral colour. MDC: overflow turns the error colour.[G] [D] (GOV.UK, KRDS, MDC)
Icons in or under the fieldHIG: "Display a Clear button in the trailing end of a text field". Wroblewski (22 participants, not peer-reviewed): on simple, familiar fields success checkmarks caused confusion, and validation shown inside the input gave no substantial benefit. Baymard (2024-01-09): positive validation such as green checks "added a sense of accomplishment and progression". TDS: the right slot may show a validation icon. MDC ships an error trailing icon (mtrl_ic_error, tinted with the error colour). Silver: offer show/hide for passwords.[G] [Q] [R] [D] (HIG, Wroblewski, Baymard)
Border contrastWCAG 1.4.11: "Where a text-input has a visual indicator to show it is an input, that indicator must meet 3:1 contrast ratio". KRDS (text input and textarea pages): "입력 필드와 인접 배경 간 명도 대비를 3:1 이상으로 표현한다". Our 1px #D4D4D4 on white is 1.48:1; the library already applied the same logic to the checkbox (K2).[S] [G] (WCAG)
Size, numbers, unitsHIG: "match the size of a text field to the quantity of anticipated text"; "show the appropriate keyboard type". GOV.UK: fixed-width inputs "for content that has a specific, known length"; inputmode numeric and "Do not use <input type="number">"; prefixes and suffixes "outside of their input" and "Do not rely on prefixes or suffixes alone, because screen readers will not read them out." KRDS: show the unit ("'개', '부수' 등") apart from the label. Seed: do not split fixed-format values such as phone numbers.[G] [D] (KRDS, Seed)

Recommendation ​

  1. Label. Top, always visible, 13/18·600 ink, 4 above the control. Never floating, never TDS appear, never a placeholder. The required and optional markers follow point 13.
  2. Placeholder. Allowed only as an example value ("예: 강남 라켓클럽") on free-text fields where an example prevents a wrong guess. Never an instruction, never "(선택)" (five shipped placeholders do this: clubs.ts:104 "제목 (선택)", clubs.ts:428, sessions.ts:213, venues.ts:248, dm.ts:62), never on pickers or numeric fields. Colour: tertiary #626C64 (5.46:1 on white, which is allowed for a "non-essential string" under the colour research) so it sits 3.5:1 away from typed ink instead of today's 2.6:1.
  3. Border. $borderControl #8E8E93, 1px (3.26:1 on white), for Input, TextArea, the picker field and the AddRow dashed outline. Focus stays 2px ink; error is 2px danger (point 13). Row hairlines keep #D4D4D4 and #E5E5E5: they are decoration, not control boundaries. Dark theme: white at α0.40 over the canvas (3.83:1), to be gated at S1 with the other derived values.
  4. One support line under every control (points 11 and 13): 18 tall, 4 below the control; hint or error at the left, counter at the right. TextArea's inside-the-box counter is retired.
  5. Counter. TextArea: always. Single-line: from 80% of the limit (the same threshold as the spoken announcement; GOV.UK's optional threshold is 75%). Format n/N in 12/16·500 secondary; past the limit it turns $errorText.
  6. Soft limit. Do not refuse typing at the limit (library §21.2 does). Let the text exceed it, show "−n" in danger, raise the error on blur or press, and soft-disable the CTA. GOV.UK tested exactly this with low-digital-skill users, and a club bio is often pasted from KakaoTalk. A hard maxLength stays only for fixed-format fields (phone, birth year, codes).
  7. Icons. In the box: a leading glyph only when it names the input type (search, place), a trailing clear × (44 target) when the field is filled and focused, show/hide on a password. No success check on ordinary fields. After an asynchronous check (nickname available, invite code valid) the result is text and a glyph in the support line ("사용할 수 있는 이름이에요"), not a mark in the box.
  8. Numbers and units. Numeric keypad (inputMode numeric; no type=number); the unit is a non-editable 15/22 secondary suffix at the right inside the box and is also in the accessible name ("참가비, 원"); fixed-format values format themselves as typed.
  9. Height stays 48 (HIG and KRDS ask for 44).

What changes ​

  • Library §7 Input and TextArea rows, library §21.2 Input counter: the border token, the placeholder colour, one support line, the counter rule, the soft limit. Tokens: $borderControl, $errorText (point 13).
  • Boards: controls 7.6 (Input) and the TextArea specimen; add a keyboard-up specimen showing the support line clear of the CTA band.
  • Code: StyledInput / LabeledField; maxLength only for fixed-format fields; move "(선택)" out of the five placeholders.

11. Helper text: needed, and when ​

What we have ​

  • Field helper: 13/18·500 secondary, 4 below the control, on nearly every Input specimen of the controls board ("2–20자", "클럽이 주로 모이는 곳", "가입 후에는 바꿀 수 없어요").
  • Canon (wizards, "Below-control caption"): one line, info-only (a unit hint, a derived value, a live suggestion), "swaps with error rather than stacking".
  • The shipped LabeledField does not swap: it renders caption and error as two independent lines, so both show at once (labeled-field.tsx lines 221 and 226: the {!!caption && …} and {!!error && …} blocks).

Evidence ​

SourceWhat it saysTag
CHI 2014 (Seckler et al., N = 65), Google summaryImproved forms were faster, needed fewer submission attempts and fewer eye movements. The guideline with the largest positive effect was stating format requirements in advance (for example a minimum password length before submission).[P]
GOV.UK text input, question pagesHint text is "for help that's relevant to the majority of users, like how their information will be used, or where to find it"; "a single short sentence, without any full stops"; "Do not use hint text to explain anything that's longer than a short, simple sentence"; "Do not use links in hint text."[G]
WCAG 3.3.2, NN/g forms top 10"Instructions or labels may also specify data formats" (technique G89: expected format and example). NN/g: "If a field requires a specific format or type of input, state the exact instructions."[S] [R]
KRDS 도움 pattern, 맥락적 도움말"반드시 확인해야 하는 중요한 정보는 도움이 필요한 요소에 인접한 위치에 인라인 텍스트로 제공하는 것이 적합하다." An ⓘ help button is for non-essential help only: "필수 정보가 맥락적 도움말 콘텐츠에서만 제공되지 않도록 한다."[G]
Seed Field, MDC TextField, Material 1 errorsSeed: when helper and error both exist only the error shows ("Error Message와 Helper Text가 동시에 존재할 경우 Error Message만 표시합니다"). MDC: "Non-null error text will replace any existing helper text". Material 1: "helper text may transform into error text". GOV.UK differs: the hint stays and the error is added above the input.[D]
Position evidenceSee point 9: below is the native-mobile default (Material, Seed, TDS, KRDS); above is argued by GOV.UK, Silver and Roselli on keyboard and autofill occlusion. No experiment compares them.[W]

Are helper texts needed? Mostly not. The evidence supports a hint where a format or rule would otherwise cause an error (CHI 2014) and a one-line consequence beside the field that carries it (KRDS). It does not support a line under every field. Two board specimens fail that test: "2–20자" under a club name (a counter does that job, and a minimum of 2 is self-evident) and "클럽이 주로 모이는 곳" under 지역 (it restates the label).

Recommendation ​

  • Default: no support line. A field is label + control (70 tall). A line exists only when one of four cases holds:
    1. A format or rule the user cannot guess, which causes an error if missed (password rules, a code format, "숫자만 입력해요", a minimum that is not obvious).
    2. The consequence of the answer, in one line ("가입 후에는 바꿀 수 없어요", "가입한 멤버에게만 보여요"). Longer than one line becomes a Notice (point 12).
    3. A scale the label cannot hold and that is not a unit suffix ("월 기준").
    4. A live derived value (the canon's "총 N명" caption).
  • Never: restate the label; carry an example (that is the placeholder's job); marketing; a link; two sentences.
  • Content: one sentence, 24 syllables or fewer at 13 px in 350 (our estimate), 해요체 or a noun phrase, no closing full stop (GOV.UK).
  • Position. Verdict: below the control for the default one-line hint; above it only for a multi-rule hint. The line sits in the 18 support line, 4 under the control, in secondary 13/18·500 (7.38:1). GOV.UK's above-the-input hint has the stronger mechanism (it stays visible while the user types); the below-the-field convention of Material, Seed and TDS has the stronger fit (one slot for hint, error and counter, no control pushed down). Reasons for choosing below: it is the convention of Material, Seed and TDS (Toss's own design system) and of the KRDS example, the one slot also holds the error and the counter so nothing jumps, and the only documented failure (occlusion by the keyboard or autofill) is fixed by scroll behaviour we own. Guard: the scroll-into-view target is the control bottom + 4 + 18 + 16, so the support line and a 16 gap always clear the CTA band while the keyboard is up. Flip condition: a multi-rule hint (password rules) goes above the control as a short list (GOV.UK, Silver), and if the on-device keyboard-up check on the boards shows the line hidden, the default flips for all fields. This is the one point where the sources disagree and no study decides, so the board should show the keyboard-up state.
  • Replacement: the error takes the hint's line and restates the rule (Seed's example "휴대폰 번호 10-11자리로 입력해 주세요", Google's "offending value and constraint"), so nothing is lost by replacing.

What changes ​

  • Library §10 Field and §7 Input: "helper" becomes the support line with the four-case test; remove "2–20자" style hints from the specimens and show the counter instead.
  • Canon wizards.md "Below-control caption": add the four-case test and make swap-not-stack a tested behaviour.
  • Code: LabeledField renders error ?? caption, not both; a unit test for the swap.

12. A notice under a field, and its icon ​

What we have ​

  • Controls spec 10.5: a neutral Notice (gray100, r12, pad 12 16, glyph 20 + 12) 8 under its Field, one line 48 or title and body 68, static, read after the control, never an action in a wizard; it replaces AttachedPanel. The board draws it with an info glyph.
  • Wizard canon: a consequence of a field sits on that field's row as its note on the review step, "never one generic callout under the list".

Evidence ​

SourceWhat it saysTag
KRDS 도움 pattern, 맥락적 도움말Essential information is inline text next to the element; a contextual-help ⓘ is on request only and must not hold essential information; at most one guide area per screen ("한 화면에 여러 개의 안내 영역을 사용하지 않는다").[G]
GOV.UK warning text, inset textWarning text is for "something important", including "legal consequences of an action, or lack of action"; inset text only "very sparingly".[G]
Seed Page Banner vs CalloutCallout is local to its content, content-width and non-sticky, for tips, feature explanations and risk notices.[D]
TDS BottomInfo, 쏘카 frame 8 (§0.4)Important notes are plain text lists with no icon box; where an icon appears (쏘카: parking mark, car, won mark) it is specific to the fact.[D] [O]
NN/g placeholdersGuidance that matters must be "persistent and placed outside of the field".[R]

Recommendation ​

  • What it is for: what the answer does: who sees it, whether it can be changed, what depends on it, what it costs. Not how to type it (that is the hint, point 11) and not an error (that is the field's own line, point 13).
  • Size ladder:
    • One line, 24 syllables or fewer, and the support line is free: it is the support line (T0): secondary 13/18·500, optionally with a 16 specific glyph. No box. This removes a 56-tall box (8 + 48) from every one-line case the board currently draws as a Notice.
    • Anything else (a title, two lines, a link, or the support line already holds a hint): a neutral Notice (T1, point 7) 8 under the support line. At most one per field, body two lines at most.
  • Icon: none by default. A specific glyph is allowed from the point 7 allow-list (lock for "can't change later", eye for "who sees this"). Never the generic ⓘ, and never an ⓘ button that hides the content: essential information must be visible (KRDS).
  • Action: none inside a wizard (the CTA is the action); one text action elsewhere.
  • Tone: neutral only. A warning or error about the value is the field's error line, as the board already ruled.
  • Not a notice: a control that depends on its parent (the old AttachedPanel's real job, for example the pay-day Stepper under a monthly fee) is a nested Field.

What changes ​

  • Library §10 (AttachedPanel to Notice row) and controls spec 10.5: drop the default info glyph, add the one-line-is-a-support-line rule, state the allow-list.
  • Code: the AttachedPanel consumers that hold only a sentence move to the support line.

13. Validation error: labels, controls, helpers and behaviour ​

What we have ​

  • Controls spec 10.8: an inline danger helper under the field: 13/18·500 #B8332E, 4 under the control, border #B8332E on Input and TextArea, never a toast; it swaps into the helper's own 18 line; it "names the fix"; "Colour is never the only signal: the text is"; timing "follows the wizard's live-draft model, unchanged"; "The first field in error takes focus when the CTA is pressed with errors; the error clears the moment the value is valid"; a Choice set or PickerRow shows the line with no border change.
  • Required: the owner LAW: one $error asterisk, the label's size, inside the label's own text (aria-hidden on the board), and the field starts unset. Optional: v2 says no marker and no "(선택)" suffix; the shipped code uses "(선택)" in 14 strings.
  • Announcement (addendum B8.5, B8.10): the error is announced when it appears; RN 0.86 has no aria-invalid, so the name suffix "입력 오류" stays.

Evidence ​

13.1 Timing

SourceWhat it recommendsTag
GOV.UK validation"Do not validate when the user moves away from a field." "Generally speaking, avoid validating the information in a field before the user has finished entering it." Show an error summary and move focus to it.[G]
HIG text fields, entering data"Validate fields when it makes sense": an email "when people switch to another field", a user name or password "before people switch to another field". "Dynamically validate field values." Also: after a set of text fields a Next or Continue button should be "available only after people enter the data you require".[G]
KRDS 입력폼Keyup validation "적절하다" for fields such as usernames and passwords; focusout for the rest ("다음 항목을 탐색하기 시작했을 때 메시지를 제공해야 한다"); on submit "오류가 발생한 가장 첫 번째 항목으로 Focus 이벤트가 발생해야 한다".[G]
Material 1 errors"Show error text only after user interaction with a field." "Help users fix input errors as soon as they are detected." "The Submit button should be enabled by default."[D]
Baymard, 2024-01-0931% of sites lack inline validation and 4% get it wrong. "the validity of each field input should be checked when the user leaves the field"; "should not be checked before the user has had a chance to fully type a correct input"; the error "must live update on a keystroke level, disappearing the moment users enter a valid input".[R]
Wroblewski, 2009 (22 participants, six form variants; not peer-reviewed)Best inline variant against submit-only: +22% success, −22% errors, +31% satisfaction, −42% completion time, −47% fixations. Validating after the field (on blur) was 7 to 10 seconds faster on the first fields than validating while typing; "before and while" had the most errors and worst ratings. It helped most on non-obvious fields (usernames, passwords).[Q]
NN/g, 10 guidelines, Smashing, 2022NN/g guideline 1: "Aim for Inline Validation Whenever Possible". Smashing: "Late validation almost always performs better"; reward early and punish late; "A calmer option is to validate empty fields only on submit"; a password strength meter is the exception.[R] [W]

13.2 Placement, colour, icon, and the label and border states

SourceWhat it saysTag
GOV.UK error message"put the message in red after the question text and hint text", with "a red border to visually connect the message and the question it belongs to"; a visually hidden "Error:" prefix is spoken; "explain what went wrong and how to fix it". No icon.[G]
NN/g, 10 guidelines (2019, reviewed 2024-12-12), NN/g error messages (2023)Guideline 3: keep messages next to the field. 4: colour to set errors apart. 5: iconography or subtle animation "for easy scanning", which helps colour-blind users. 8: a summary must not be the only indication. "never use exclusively color or animation to indicate errors"; Amazon combines border highlighting, iconography and red text.[R]
WCAG 3.3.1, 1.4.1"The error must be indicated in text." A colour or image cue is allowed in addition; failure F81 is identifying required or error fields "using color differences only".[S]
MDC TextField, Material WebError text replaces supporting text; an error trailing icon (mtrl_ic_error) tinted colorError; the error live region defaults to polite.[D]
Seed TextInputSix states, with Error and Error Focused as separate states.[D]
Seckler 2012 (303 participants), Inal 2016 (32)Positions near the field beat the form's top or bottom on performance (efficiency and effectiveness), and the right side was rated most satisfying; no above-versus-below result; in 2016 no significant difference in completion time or correction time.[P]
Silver, RoselliError "just above the field", because below it "we'd increase the chance of it being obscured" by autocomplete or the keyboard; or duplicate the message (a summary plus the field line).[W]
Colour research, ledger row 9 (our own floor)Text of 14px or less needs 6.5:1. #B8332E on white is 5.91:1 (5.28:1 on gray100): it passes WCAG AA but not this floor at 13px.(own)

13.3 Focus and summary

SourceWhat it saysTag
GOV.UK error summary"Always show an error summary when there is a validation error, even if there's only one." Top of the main container; focus moves to it; each entry links to the first field in error; "make sure the error messages in the error summary are worded the same as those which appear next to the inputs".[G]
KRDS 오류"오류가 발생한 요소로 초점이 자동으로 이동되도록 제공한다."; "사용자의 입력값이 유지되도록 한다." No summary guidance on the page; it cites KWCAG 2.2 오류 정정.[G]
NN/g guideline 8, WCAG 3.3.3A summary alone "forces the user to search for the field"; G139, a mechanism to jump to errors, is an advisory technique.[R] [S]
CHI 2014Placing the error beside the erroneous field reduced completion time and raised satisfaction; not clearing completed fields after an error is one of the 20 guidelines.[P]

13.4 Required and optional marking

SourceWhat it saysTag
NN/g, Marking Required Fields in Forms (2019-06-16)"If most fields in the form are required, should we still mark them?": "The short answer is yes." The asterisk "has become very common on the web and users are familiar with its meaning"; marking optional fields "does lighten the user's cognitive load"; red "has become the expected required-marker color on the web". It reports no formal study.[R]
CHI 2014 summary"Distinguish optional and mandatory fields" is the guideline participants mentioned most often.[P]
GOV.UK question pages"Never mark mandatory fields with asterisks."; "in most contexts, add '(optional)' to the labels of optional fields."[G]
KRDS 입력폼"필수 입력 항목을 별표를 사용하여 표시" or the text "필수"/"선택"; "대부분의 항목이 필수인 경우 선택 입력 항목만 표시하고, 대부분의 필드가 선택 사항인 경우 필수 필드 레이블만 표시한다."; "필수 입력 항목 정보는 보조 기술이 확인할 수 있는 형태로 제공한다."[G]
Seed FieldRequired = a requirement mark, optional = the text "선택". By the page's rule, when two thirds or more of a form's fields are required only the optional ones are marked, otherwise only the required ones; the two methods are never mixed in one form.[D]
TDS Agreement, Material WebTDS marks consent necessity with words: 필수 "파란색으로 표시돼요", 선택 "진한 회색으로 표시돼요". Material Web: a required field's label "will render an asterisk" unless no-asterisk is set.[D]
WCAG 3.3.2No symbol is mandated; techniques H90 (label or legend) and ARIA2 (aria-required).[S]
Shipped code, packages/ui/src/labeled-field.tsx (lines 180 to 184), packages/app/src/config/i18n/ko/*.tsThe asterisk is a separate Text that is not hidden from assistive technology (a screen reader reads it as "별표"); "(선택)" is in 14 Korean strings (clubs 7, sessions 5, venues 1, dm 1), five of them placeholders.[O]

Recommendation ​

(a) Timing: reward early, punish late. Seven sources agree on "not while the user is still typing the first time"; GOV.UK goes furthest (not on blur at all) and the platform guides allow live checks for a few fields. The policy below honours the first and keeps the exceptions narrow.

MomentRule
First time typingNo error. Live feedback only for the counter, a password rule checklist, and an availability check that shows "확인 중" in the support line.
On blur (the wizard's commit)Validate a non-empty value for format, length and range. An asynchronous check (name taken, invite code) runs here.
An empty required fieldNo error on blur. It is flagged only when the user presses the CTA, or on a later visit to the step after a press (Smashing, GOV.UK, Material 1).
After a field has shown an errorRe-validate on every keystroke and clear the moment it is valid (Baymard).
CTA pressed with something missingValidate everything, mark every invalid field, scroll to and focus the first, announce the count once (point 16).
The server rejectsThe same lines, the input kept (KRDS).

(b) Error anatomy: one look, three non-colour cues.

  • Control: border 2px $error #B8332E in both rest and focus (the error colour persists while the field is focused, which is why Seed lists Error Focused as its own state; the caret stays ink). Choice sets, PickerRow and Cells have no border to tint: the line alone.
  • Label: unchanged (ink, with its marker). It is not recoloured: the label is how the field is found, and colour-blind users still need it (our judgement; none of the sources studied specifies the label's colour in error).
  • Support line: a 16 alert-circle glyph + 4 + the message in 13/18·600 $errorText #A3261F (7.36:1 on white, 6.57:1 on gray100, meeting the 6.5:1 floor that #B8332E misses). It replaces the hint in the same line; the counter stays at the right. The cues that do not depend on colour: the glyph, the weight, the 2px border.
  • Copy: the fix plus the rule, in the instruction form: empty "[항목]을 입력해 주세요" or "[항목]을 골라 주세요"; length "[항목]은 2자 이상 입력해 주세요"; format "[항목]을 [형식]으로 입력해 주세요"; taken "이미 있는 [항목]이에요. 다른 [항목]을 입력해 주세요". These follow GOV.UK's templates ("Enter [whatever it is]", "[whatever it is] must be [number] characters or less") and Seed's example, through the house-style gate.
  • Placement: below the control, replacing the hint (point 11 for the reasoning and the flip condition). This is where our recommendation sits on a real disagreement: GOV.UK and Silver put the error above the input, Material, Seed, TDS and KRDS below, and the only controlled data (Seckler, Inal) say "beside the field" without ranking above and below.
  • Spoken: the control's name carries the suffix "입력 오류" (B8.10) and the line is a polite live region plus one explicit announcement (B8.5); no shake or bounce (B7.8).
  • Counter at the limit: $errorText, no further treatment; it is not an error until the user presses or leaves.

(c) Focus on submit. When the CTA is pressed with errors: scroll the first invalid control into view with its support line and 16 clear of the CTA band; move accessibility focus to it; announce "N개 항목을 확인해 주세요" once; keep the keyboard as it was; never clear input.

(d) Error summary. Only where a screen can hold three or more fields that may be invalid, or where the first invalid field may be off-screen (the edit Sheets, the 일정 만들기 review page): a T3 Notice at the top of the scroll area titled "확인할 항목이 N개 있어요", with one tappable line per item ("클럽 이름: 2자 이상 입력해 주세요") that scrolls to and focuses the field, worded identically to the field line. A wizard step has one to three fields, and the pinned CTA helper that names what is missing (point 16) is its summary. This threshold is our judgement: GOV.UK says always, KRDS is silent.

(e) Required and optional: the asterisk LAW stands, with three caveats.

  • Why it stands. NN/g says to mark every required field even when most are required; Material Web marks required fields with an asterisk by default; KRDS allows the asterisk; and our steps mix required and optional fields (the tagline rides along with the name), which is the branch where Seed also uses * (on a one-field step Seed would mark nothing). Against it: GOV.UK, KRDS's majority rule and Seed's rule point to words, or to marking only the minority, in a mostly-required form, and Toss marks consent necessity with the words 필수 and 선택. If we chose on the weight of the sources alone for a mostly-required form we would mark the optional fields and drop the asterisk; we keep the asterisk because the LAW is ratified, NN/g and Material support it, no source tests the symbol against the word with low-digital-literacy users, and switching is one prop. So this is a trade-off, not a verdict.
  • Caveat 1, assistive technology. The field's accessible name must include "필수" (KRDS: "필수 입력 항목 정보는 보조 기술이 확인할 수 있는 형태로 제공한다"). Today the shipped asterisk is read as "별표". Hide the glyph from assistive technology and put "필수" in the name.
  • Caveat 2, explain the symbol. A one-time legend "* 필수 항목" (13/18 secondary) at the top of any screen with six or more fields, or where marked and unmarked fields are mixed. The asterisk colour is $errorText.
  • Caveat 3, restore "(선택)". The v2 spec drops the optional suffix; the shipped code has it, and NN/g, the CHI 2014 participants, KRDS, Seed and GOV.UK all support marking optional fields somewhere. Keep a quiet "(선택)" (13/18·500 secondary) in the label of optional fields that sit beside required ones, and keep it out of placeholders. Mixing both marks is what Seed forbids; we accept it because optional fields here are few and sit beside required ones.
  • If a 5-person on-device check shows people asking what * means, switch the wizard to the KRDS and Seed majority rule (mark only the optional fields) with one Field prop.

The one Field grammar ​

Every field is label · control · [support line]. Heights: label 18, 4, control, 4, support line 18.

PartTextNumberChoice setPicker cellText area
Label13/18·600 ink above; markersamethe set's legend, same stylethe Cell's own label (15/22·400 secondary) carries the markersame as text
Control48, r12, border 1px $borderControlsame; numeric keypad; unit suffix 15/22 secondary inside, rightChoices 44 high, wrap, gap 8; at most 5 options, else a pickerCell 48: value or "선택" + chevron, opens a Sheetmin 120, grows to 200, then scrolls
Hint (rare)support line, four cases onlythe unit is the suffix, never the hintsupport linesupport line under the Cell, at the railsupport line
Counterfrom 80% of its limit, right of the linenonenonenonealways, right of the line
Error2px danger border; glyph + 600 messagesameno border; line onlyno border; line only2px border; line; counter stays
Required / optionalasterisk after the label, spoken "필수" / "(선택)" suffixsameon the legendon the Cell labelsame
Start stateempty, no default (required)emptynone selected"선택"empty
Height70 without a line, 92 with70 / 92118 for two rows of 4448, plus 22 with a line164 with a line

What changes ​

  • Library §10 Validation row, §10 Field, §7 Input; controls specs 10.2 (Field, required) and 10.8 (Validation): the timing table, the error anatomy (2px border, glyph, 600, $errorText), the focus rule, the summary threshold, the three required-marking caveats, "(선택)" restored.
  • Tokens: $borderControl #8E8E93, $errorText #A3261F (and dark equivalents behind the S1 gate).
  • Code: LabeledField (swap, glyph, aria-hidden asterisk with "필수" in the name, "(선택)" suffix prop); StyledInput (invalid already exists); a shared first-invalid helper (extend the wizard's firstFailingGatedStep pattern to field level, not a second implementation); a keyboard-up scroll offset that includes the support line.

10. WizardShell: split controls, spacing, progress ​

What we have ​

  • The owner LAW (canon, ratified 2026-08-15 after three footer redesigns in a week): the footer owns one CTA; the header owns movement (back) and departure (건너뛰기). Back is a real 40 button, skip is a secondary button, hardware back mirrors the header back.
  • v2 drawing (library §10, §21.2, K6; FmWizardHeader in flat-v2-forms.jsx): header 56; back = 40 × 40 r12 gray100 with a 20 chevron in a 44 target (× on step 1 with the exit Dialog); 건너뛰기 = sm tonal; the progress is a 2pt line on the header's own bottom edge, ink for done and current, #E5E5E5 for the rest.
  • Measured from that source: the 40 square spans y 8 to 48, the 36-tall skip button y 10 to 46, the line y 54 to 56. So the line is 6 px under the back square and 8 px under skip: it reads as touching the controls. The owner's spacing complaint is accurate.
  • Shipped code (packages/ui/src/wizard-shell.tsx) differs from the drawing: 4px r2 progress segments sit inline in the header row between back and skip, with a step count, and completed segments are tap targets.

Evidence ​

10.1 Where back and the primary action sit

SourceBack or closeForward or primaryTag
HIG toolbarsLeading edge: "Use the standard Back and Close buttons", with the standard symbols, not a text label."Use the .prominent style for key actions such as Done or Submit … Only specify one primary action, and put it on the trailing side of the toolbar."[G]
HIG sheetsMulti-step sheet: first step Cancel at the leading edge; later steps "the Back button replaces the Cancel button". Back "isn't intended to dismiss a sheet".Done at the trailing edge, inactive until the final step. "Avoid showing all three buttons — Cancel, Done, and Back — together." The whole flow lives in the top bar.[G]
HIG modality"Always give people an obvious way to dismiss a modal view."; keep modal tasks "simple, short, and streamlined"; "provide a single path through the hierarchy".none stated[G]
Material 1 dialogs (archived)Full-screen dialog: "the 'X' at the top left"; touching it discards."Place confirmation and dismissive actions for full-screen dialogs at the top of the screen": the confirming action top right with a descriptive verb.[D]
GOV.UK back link, question pages"Always place back links at the top of a page, before the <main> element."; "Always include the Back link component on GOV.UK Question pages".The button is in the flow, below the question, "labelled 'Continue', not 'Next'" and "aligned to the left so users do not miss it". "Start by asking one question per page."[G]
KRDS 뒤로가기, 단계 표시기, 신청"뒤로가기 버튼은 항상 상단바 좌측에 표시한다."; "시스템 뒤로가기와 앱 내 뒤로가기는 최대한 동일하게 동작하여야 한다."; "작성 중인 내용이 사라지는 경우에는 항상 경고하여야 한다."KRDS states no rule for the previous and next buttons' position on its step-indicator or application pages.[G]
Apps in Toss checklist, nav bar, TDS BottomCTABack is the host navigation bar's (optional, at the left), with 더보기 and X at the right; "닫기 버튼과 뒤로가기 버튼이 노출되고, 동작이 명확해요."; "최초 화면에서 뒤로가기를 누르면 미니앱이 종료돼요."The CTA is FixedBottomCTA, fixed at the screen's bottom edge, single by default, and fixedAboveKeyboard exists on the Single form (Single).[D]
Seed Top Navigation, Action ButtonBack and close are different controls; back "네비게이션 스택을 기반으로" returns; close ends a layer. Right slot: icon buttons up to 2, one text button.The CTA is an Action Button ("Large는 주로 CTA 역할로 사용됩니다"); a solid and a weak button pair at a 3:7 ratio.[D]
yozm reviews: Toss, 2021-01-11, 요기요, 2021-09-17none describedToss sign-up: "한 페이지당 질문이 하나인 셈이다"; transfer screen: amount first, "하단에 받기/보내기 버튼". 요기요: a bottom 주문하기 button.[O]
Store frames (§0.4)Bare chevron or arrow at the top left on 토스, 쏘카, 플랩풋볼, 배민; bare × at the top left on 당근 sub-services; × at the right on 쿠팡 구매완료; a filled pale button on Meetup; × plus inline progress on Duolingo.Toss: a full-width CTA under the content; Venmo: a pair above the keypad; Duolingo's primary is covered by marketing overlays in the frames.[O]

10.2 Reach, gesture and signifiers

SourceWhat it saysTag
Hoober 2013, Hoober 20171,333 observations: 49% one hand, 36% cradled, 15% two hands. In 2017 he calls the thumb-sweep chart "well-known, but incorrect": people "prefer to view and touch the center of the screen" and "use the Back button all the time" even in a top corner; targets can be 7 mm at the centre, about 12 mm in a corner; secondary actions go "along the top and bottom edges". The reach argument for bottom buttons is weaker than its reputation.[Q]
Android gesture navigationThe back gesture is an inward swipe from either side edge and "might interfere with app navigation elements in those areas"; the bottom home and quick-switch gestures cannot be opted out of, so a bottom CTA must clear the system gesture inset (our band adds the home inset).[G]
NN/g, wizards (2017)"a clear indicator of the current step can help keep users oriented"; "Generic labels such as Next and Previous have weak information scent"; wizards "are not gracefully interruptible", so allow exit and resume. The article gives no position rule for Back and Next.[R]
NN/g, flat UI (2017, 71 participants)Weak clickability signifiers cost 22% more task time and 25% more fixations. This supports the LAW's "top controls must look like buttons"; it is also a departure from peers, who use bare glyphs for back.[R]

10.3 Progress style and spacing

SourceWhat it saysTag
KRDS 단계 표시기"단계의 수는 최소 3개에서 최대 7개"; on small displays keep only the current and total ("4단계 중 1단계"); "색상 이외의 수단으로 구분할 수 있는 시각적 단서를 제공해야 한다"; optional steps come last; skip labels "건너뛰기" or "다음에 입력하기".[G]
Material 1 steppers, MUI mobile stepper"Use dots when the number of steps isn't large"; "Use a progress bar when there are many steps"; a text variant shows "The current step and total number of steps"; "Optional steps within a linear flow should be marked as optional."[D]
TDS ProgressStepper, MDC ProgressIndicatorTDS: "프로그레스바와 스테퍼가 결합된 형태", compact variant, paddingTop default 16, wide 24. MDC: linear track thickness 4dp, corner radius 50%.[D]
Villar, Callegaro, Yang 2013 (32 randomized experiments)"using a constant progress indicator does not significantly help reduce drop-offs"; fast-to-slow indicators reduced drop-off and slow-to-fast increased it.[P]
Our grammar (spacing ladder)16 = "gap under tabs"; 24 = "block → section header". Header controls to the progress element is the same kind of seam as tabs to content.(own)

Recommendation ​

Verdict: keep the split. It is the convention for step funnels, and what is wrong is the spacing and the progress element, not the placement.

OptionDiscoverabilityReach and keyboardSystem backEvidenceVerdict
Split (current): back and skip top, one CTA bottomEach control sits where its kind lives; the CTA is the one big thingCTA rides above the keyboard (TDS fixedAboveKeyboard); reach evidence is weak either wayHeader back mirrors system back (KRDS, Toss checklist)GOV.UK (back top, Continue in flow), KRDS (back top-left), Toss and Seed (back top, CTA bottom), HIG (back at the leading edge)Keep
Unified top: 다음 as a header actionLow for a step that repeats: a 15/22 word in a corner, far from the field and the keyboardWorst with the keyboard upFineHIG puts Done or Submit (the completion) at the top trailing edge and Material 1 does the same for a full-screen dialog's confirmation, neither for a repeated NextReject for 다음. Right for an Edit Sheet's single 저장 (see the audit)
Unified bottom: 뒤로 + 다음 in the footerMedium; back duplicates the system gestureEasy to reach, but a far-left back sits in Android's edge-back zoneConflicts at the edge on AndroidA tonal and a solid button at 3:7 is a Seed pattern for dialogs, not for back; our own three redesigns read "out of proportion"Reject

The honest counter-evidence: HIG's multi-step sheet is the strongest written precedent for putting everything in the top bar. It is written for short modal tasks whose forward action is the completion; the Korean practice we can verify (TDS FixedBottomCTA, Seed) and GOV.UK split the two for funnels, and the keyboard is up on most of our steps. The thumb-reach argument is not what decides it (Hoober 2017).

The header and progress geometry. Keep the header at 56 with back (40 in a 44 target) and skip (sm 36), both centred. Move the progress out of the header into its own row:

y 0    ┌────────────────────────────────────────────┐
       │ [‹ 40]                       [건너뛰기 sm 36] │  header 56: 8 above and below the 40 square
y 56   ├────────────────────────────────────────────┤
       │  8                                          │
y 64   │ ▬▬▬▬ ▬▬▬▬ ▬▬▬▬ ▬▬▬▬ ▬▬▬▬ ▬▬▬▬   3/6         │  progress row: 4 tall, gap 4; count 13/18·600, 12 gap
y 68   │  24                                         │
y 92   │ 클럽 이름을 정해요                            │  step title 24/32·700
  • Square to bar: 16 clear (it is 6 today). That equals TDS's default paddingTop for its ProgressStepper and our own "tabs to content" seam. The title is 24 under the bar, so the chrome above the title grows from 80 to 92.
  • Thickness 4, r2 (MDC's linear default, and what the shipped segments already use), not 2pt. Ink for done and current, #E5E5E5 for the rest (K6 stays; ink against the track is 15.2:1).
  • At most 7 segments (KRDS 3 to 7; Material 1: bars when there are many steps). The 9-step club wizard uses one continuous 4-tall bar with the same count.
  • The count "3/6" at the right, 13/18·600 secondary, tabular. It is the text alternative KRDS asks for, tells users how long the flow is, and is spoken "6단계 중 3단계" with the step title on each step change (B8.5).
  • Tap-to-jump on completed segments stays (canon), with 44 hit zones as shipped.
  • No motivational animation. A 150 ms fill is enough. A constant progress indicator does not reduce drop-off (Villar); the fast-to-slow indicators that did (quick early progress, slower later) misreport progress, which we would not fake. Treat the bar as orientation only.
  • The header's edge hairline, when content scrolls under, is drawn under the progress row, not between the header and the row, so the pair moves as one.

What changes ​

  • Library §10 WizardShell paragraph, §21.2 "WizardShell header (owner LAW)" row, K6: the progress is its own row (4, count, 8 and 16 spacing, 7-segment cap); the LAW itself is unchanged.
  • Board: FmWizardHeader in flat-v2-forms.jsx (draw the row, measure 16 clear); the forms-board wizard frames and the wizard-header guide marks.
  • Canon wizards.md "Progress segments" bullet: add the geometry and the 7-segment cap.
  • Code: wizard-shell.tsx moves the segments out of the header row (today they sit between back and skip), which is a bigger change than the 2pt-line drawing suggests.

16. The CTA helper ​

What we have ​

  • Library §14 CTA band helper line: inside the band above the button, 13/18·500 secondary, at most two lines, left at the rail, MetaSegments, 8 above the button; the band is 118, 144 with one helper line, 162 with two; static, never a second action; "the reason a CTA is disabled is also secondary". Examples: "참가비 10,000원 · 시작 2시간 전까지 무료 취소", "NTRP 3.5 이상만 참석할 수 있어요".
  • Contradiction: the patterns board says "an error is a Notice above the band, never red text on it", while library §22.2 adds a danger helper line for a failure that belongs to the action.
  • Disabled: Button dims to opacity .5 and Tamagui drops press events (button-visual.ts); the wizard pins primaryHint to a disabled primary. The disabled ball primary computes to a container 1.14:1 against white and a label 3.48:1 against it: nearly invisible.

Evidence ​

16.1 What belongs near the button

SourceWhat it saysTag
Baymard, Payment UX, 2026-06-01, abandonment listThe primary button "should say exactly what happens next"; "Reassurance near the primary button helps", especially when clarifying whether the order is placed now or later; "Dense legal copy at the moment of payment undermines confidence"; the total must show "before users are asked to enter payment details". Reasons to abandon: extra costs too high 40%, could not see the total up front 12%.[R]
Baymard guideline 586 (the free text)"After inputting their payment information, users worry that their order may be placed immediately once they click the primary button to continue checkout."[R]
WCAG 3.3.4, GOV.UK check answersFor financial submissions: "A mechanism is available for reviewing, confirming, and correcting information before finalizing the submission." GOV.UK: "make sure the 'submit' button clearly shows the action it performs"; "make it clear the transaction will not be complete until a user confirms their information is correct"; a Change link per answer returns to the review page.[S] [G]
전자상거래법 다크패턴 (Shin & Kim), KFTC and Coupang (SBS, 2025-10-15)In force since 2025-02-14, banned: showing only part of the total cost, pre-selecting an option, and tilting the choice hierarchy by size, shape or colour. Coupang changed "결제하기" to "월회비 변경에 동의하고 구매하기" and styled the deferral small and white; fined 2.5 million won.[S] [O]
Toss UI/UX guide, TDS BottomCTA SingleA dark pattern: a CTA whose label repeats the value already explained, or "CTA 위에 과장되거나 중복된 보조 설명을 붙여서 버튼의 역할이 흐려지는 경우". TDS gives the CTA a topAccessory ("CTA 위에 렌더링되는 악세서리") and a bottomAccessory, and no content rules.[D]
KRDS 확인, 요기요 reviewKRDS: label the button with the specific action and offer both confirm and cancel. 요기요 (2021): under a strict minimum, tapping the bottom 주문하기 opens a dialog; the reviewer proposes greying the button and showing the amount needed in a toast above it.[G] [O]
NN/g, error messages"Proximity helps users associate the error message content with the interface elements needing attention."[R]

16.2 Disabled primary, or enabled and validate on press

SourceWhat it saysTag
HIG entering data"if you include a Next or Continue button after a set of text fields, make the button available only after people enter the data you require." (Apple withholds the button.)[G]
GOV.UK button"Disabled buttons have poor contrast and can confuse some users, so avoid them if possible." "Only use disabled buttons if research shows it makes the user interface easier to understand."[G]
Axess Lab, 2017, Roselli, 2024Keep the button enabled and show the message on click; if a disabled look is wanted use grey with sufficient contrast, switch to colour when valid, keep it enabled in code and move focus to the error. Roselli: "The place where buttons should not be disabled is within the context of submitting a form." The "99%" figure in Axess Lab is by its own admission a gut feeling.[W]
Material 1 errors"The Submit button should be enabled by default" (and, with inline validation and a clearly marked field, it "may be disabled until the error is corrected": the page contradicts itself).[D]
NN/g, button states, 2025-04-25A disabled submit "may not be active" until required fields are filled; "When a user clicks the disabled button, nothing happens"; lower contrast but keep the label readable; add aria-disabled.[R]
Store frame, 카카오페이 frame 7 (§0.4)A grey full-width disabled CTA under an unanswered four-option poll, with no reason text visible: the pattern exists in the Korean market for a single pending decision.[O]
WCAG inactive-component exemption (1.4.11)Inactive components are exempt from contrast requirements (Axess Lab notes the same exemption for text), which is why Axess Lab and NN/g both say readability must be kept on purpose.[S]

The sources split: Apple, and the Korean observation, withhold the button; GOV.UK, Axess Lab, Roselli and Material 1 keep it enabled; NN/g describes both and asks for aria-disabled and readability. No source compares them with our audience. The position evidence for above versus below the button is thin: Baymard says "near", TDS offers both slots, and nothing tests the two.

Recommendation ​

  1. When a helper is warranted (any one): the press commits money or a penalty (fee, total, cancellation window); terms that change the decision; the button is unavailable and the user can fix it on this screen (what is missing); the action failed (danger). Otherwise no helper and the 118 band.
  2. Content: decision facts only.
    • Numbers and dates, one sentence or one MetaSegments line; the total, never a part ("참가비 10,000원", not "10,000원부터"; the law cited in 16.1).
    • No promotion and no echo of the button label (Toss); never a consent folded into the label or pre-ticked, and a consent dialog's two choices equal in size, shape and colour (the law's "잘못된 계층구조").
    • No link to long legal text: a Cell "취소 규정" opens it (the board's rule); no second action; the same words as the field line (point 20 copy rules).
    • Length: one line by default (about 24 syllables at 13 px), two at most; more is a Cell.
    • Missing requirement, positive form: "클럽 이름을 입력하면 다음으로 갈 수 있어요"; gate: "NTRP 3.5 이상만 참석할 수 있어요".
  3. Position: above the button, 8 above it (the library's geometry). Our reasoning, since no source tests it: the fee and terms must be read before the commitment, and the thumb sits on the button and the zone beneath it. Legal links go below the button only on the sign-in band, where the board already puts them.
  4. Disabled primary: soft-disabled, not dimmed.
    • Enabled and validate on press when two or more inputs are pending or the missing part is off-screen (long forms, the review page).
    • Soft-disabled for a step with exactly one pending decision whose missing part is visible (a single field, one poll option, one required choice, a consent): fill gray100, label secondary 600 (6.59:1), aria-disabled=true (focusable, announced with the reason), and pressing it does something: a haptic, the missing control takes focus and shows its danger line. When the requirement is met the fill becomes ball. This keeps Apple's and the Korean "button waits" cue and drops the ball-at-.5 slab that is 1.14:1 against white. It answers the objections that GOV.UK, Axess Lab and NN/g raise to a disabled button: it is readable, it is focusable, and a press is answered.
    • Hard disabled (opacity .5, inert) only while the action is in flight or where nothing the user can do on this screen helps (a role wall); then a reason line is mandatory.
  5. Danger helper for a failure that belongs to the action (provider failed, RSVP failed, payment declined): a 16 alert-circle glyph + 13/18·600 $errorText, one line, directly above the button, which stays enabled for the retry. NN/g's proximity point and 요기요's "reason near the button" support it; this resolves the contradiction in favour of library §22.2 and deletes "never red text on it" from the patterns board.
  6. Sizes unchanged: band 118; 144 with one line; 162 with two; 16 above and below the button plus the home inset; the danger variant keeps the same heights (the glyph sits inside the line).

What changes ​

  • Library §14 CTA band helper row: the warranted-cases test, the content rules, soft-disabled, the danger variant. Library §7 Button: a soft-disabled state (aria-disabled, press runs validation) distinct from the inert disabled. Library §22.2: confirmed.
  • Canon wizards.md primaryHint bullet: the pinned hint stays; the disabled primary becomes soft-disabled.
  • Boards: flat-v2-patterns CTA band helper (add the soft-disabled specimen with its press result, and the danger specimen; remove "never red text on it"); flat-v2-controls Button states.
  • Code: button.tsx (a softDisabled path that keeps press and sets aria-disabled), bottom-cta-band.tsx, wizard-shell.tsx (primary), packages/features/sessions/src/session-detail/actions/primary-action.tsx (fee and gate helpers).

Audit of unreviewed sections (library §7 to §10) ​

Priority in the first column: high = fails a standard or contradicts our own rule; med = departs from peer practice with evidence; low = polish. Items already settled in points 7 to 16 are listed once more here so the build has one list.

#SectionIssueEvidenceFix
A1 (high)§7 Input, TextArea, picker field, AddRowThe resting border is 1px #D4D4D4, 1.48:1 on white; the text input's boundary is its only indicator.WCAG 1.4.11: "Where a text-input has a visual indicator to show it is an input, that indicator must meet 3:1" (page); KRDS "명도 대비를 3:1 이상" on both input pages; library K2 already applied this to the checkbox.$borderControl #8E8E93 (3.26:1), 1px; hairlines keep #D4D4D4/#E5E5E5; dark: white α0.40 (3.83:1), gated at S1.
A2 (med)§7 Input, §10 ValidationSmall danger text: #B8332E at 13px is 5.91:1 on white and 5.28:1 on gray100, below the project's own 6.5:1 floor for text ≤ 14px.Colour research, ledger row 9; computed.$errorText #A3261F (7.36:1 / 6.57:1) for the error line, asterisk, counter at the limit and the danger CTA helper; #B8332E stays for fills and borders.
A3 (med)§7 Input placeholderThe placeholder is secondary #4F5851, only 2.6:1 away from typed ink, so it can read as a value; five shipped placeholders also carry "(선택)" (clubs.ts:104 and :428, sessions.ts:213, venues.ts:248, dm.ts:62).Silver: users mistake placeholders for values (page); NN/g placeholders (page); colour research allows tertiary for non-essential strings.Placeholder = tertiary #626C64 (5.46:1, 3.5:1 from ink), example values only; "(선택)" moves to the label.
A4 (med)§10 Field, optional markerv2 says "no marker and no 선택 suffix"; the shipped app shows "(선택)" in 14 strings and the evidence supports marking at least the minority type.NN/g required fields (page); CHI 2014 summary (page); KRDS 입력폼; Seed Field; GOV.UK question pages.Keep a quiet "(선택)" suffix (13/18·500 secondary) on optional fields that sit beside required ones (point 13).
A5 (med)§7 TextArea, §21.2 counterTwo specs disagree: counter inside the box (§7) versus on the helper line (§21.2).GOV.UK (below the textarea), KRDS (after the label or the input), Seed (right of the footer), Material (supporting row).One place: the support line, right-aligned; remove the inside-the-box counter.
A6 (med)§21.2 counter"Further typing is refused" at the limit.GOV.UK: "This component does not restrict the user from entering information … then edit it down", tested with 17 users including low digital skills (page).Soft limit with a danger counter and an error on blur or press; hard maxLength only for fixed-format fields.
A7 (med)§7 Segmented (form)Selected = a white pill on a gray100 track (about 1.1:1) distinguished mainly by weight 600 against 500.WCAG 1.4.11: a state's identifying information needs 3:1 (page).Selected = ink fill with a white 600 label (the Choice grammar), or a 2px ink outline.
A8 (low)§7 SwitchThe off track #D4D4D4 is 1.48:1 on white; the thumb is white with a 1px edge.WCAG 1.4.11 (the control's state and boundary).Off track #8E8E93 (3.26:1); the thumb is unchanged.
A9 (med)§7 SliderThe thumb is white with a 1px #D4D4D4 edge over a #E5E5E5 track: 1.26:1 and 1.18:1, so it nearly disappears at low values.WCAG 1.4.11; computed.Ink thumb (24), or white with a 2px ink ring; track and value tag unchanged.
A10 (high)§7 Button disabledOpacity .5 on the ball primary gives a 1.14:1 container and a 3.48:1 label; a press does nothing.NN/g button states: keep the label readable, add aria-disabled, "When a user clicks the disabled button, nothing happens"; GOV.UK; Axess Lab; Roselli (point 16).Soft-disabled state: gray100 fill, secondary 600 label, aria-disabled, press validates (point 16).
A11 (low)§7 Button loadingA spinner replaces the label.HIG buttons: show an activity indicator and "you can also configure the button to display a different label alongside" it ("Checkout" to "Checking out…") (page).Spinner beside a "…중" label for actions over 1 s, so the reason for the wait stays readable and spoken.
A12 (high, owner decision)§18.2 Edit Sheet footerAn edit Sheet has no ×; its footer is the pair 취소 + 저장. This contradicts the logic of the owner's own wizard LAW (one CTA in the footer) and the Korean systems.KRDS bottom sheet: "닫기 버튼을 포함하여 구현하여야 한다" and, when a sheet confirms data, the close acts as cancel (page); Seed: a top close for forms and payments (page); HIG sheets: Cancel at the leading edge (page).Header × (close = cancel, with the unsaved-changes Dialog) and one 저장 CTA in the footer.
A13 (low)§8 Dialog copyThe pair's copy is unspecified; 취소 can be misread as cancelling the user's task, and HIG requires "Cancel" only for a button that cancels the alert's action.Toss guide ("취소는 사용자가 하고 있는 작업이 취소된다고 오해할 수 있어 쓰지 않아요"); HIG alerts ("Always use 'Cancel' to title a button that cancels the alert's action"); the law's 잘못된 계층구조.닫기 for a pure dismissal; verb pairs for guards ("계속 작성" / "나가기"); where one option favours the business, equal size, shape and colour.
A14 (high)§8 Toast, §10Failures the user caused (save, join, pay, RSVP) are error toasts that vanish after 6 to 8 s.NN/g: a toast is unsuitable for error messages (page); APG: "avoid designing alerts that disappear automatically" (page); KRDS: a toast is not for important messages or field errors.CTA helper (danger), a T3 Notice, or a Dialog, chosen per mutation through an errorSurface option on createMutationHook (create-mutation-hook.ts:246-251 is the one place that toasts every failure today); background failures keep a no-timer toast with 다시 시도 (point 20).
A15 (low)§15 Toast durationsLibrary §15 lists 4/6/8 s as plain / with action / error; addendum B7.4 lists success and info 4, warning and error 6, any action 8.Both are our documents.Make §15 match B7.4.
A16 (low)§8 Toast glyph (K5)A 20 status glyph on every kind.KRDS snackbar: icons only when truly necessary; Material 1: "They cannot contain icons."Glyph on warning and error only (point 7 icon rule).
A17 (med)§9 SkeletonNo show-delay and no time thresholds.NN/g: "If a page takes less than 1 second to load, skeleton screens or spinners aren't necessary"; skeletons under 10 s, progress bars above (page); Seed loading pattern: under 1 s nothing, 10 s or more a determinate indicator with an estimate (page).Show a skeleton only after 300 ms, keep it at least 500 ms once shown, and move to a progress bar with an estimate past 10 s (our timings; the B7 slow-load Notice at 10 s fits).
A18 (low)§9 EmptyState error variantThe copy rule is unspecified; the voice guide's generic failure template is the default.HIG alerts: avoid a title like "Error"; Toss Result: figure, title, description, button (page); NN/g empty states: direct pathways (page).Title = what failed in 해요체, body = the next step, one 다시 시도 button.
A19 (med)§10 Required marker (shipped)The asterisk Text is not hidden from assistive technology (read as "별표") and "필수" is not in the name.KRDS: "필수 입력 항목 정보는 보조 기술이 확인할 수 있는 형태로 제공한다"; WCAG 3.3.2 techniques H90 and ARIA2; labeled-field.tsx lines 180 to 184.Hide the glyph, put "필수" in the name; legend "* 필수 항목" on screens with six or more fields.
A20 (high)§14 vs §22.2 CTA band"Never red text on the band" (patterns board) contradicts the danger helper line of §22.2.NN/g proximity; yozm 요기요 observation (point 16).Keep §22.2; delete the sentence from the board.
A21 (med)§10 / canon captionThe canon says the caption swaps with the error; LabeledField renders both.Seed, MDC, Material Web, Material 1 all replace; the canon's own text says swap.error ?? caption, with a unit test.
A22 (med)§10 AttachedPanel to NoticeA neutral Notice with a default info glyph and a 48-tall box for a one-line note.KRDS: essential information inline; Toss: specific graphics; 쏘카 and TDS BottomInfo (point 12).One line goes in the support line; no default glyph (point 12).
A23 (med)§10 Validation timing"Unchanged" leaves the timing unspecified.Seven sources (point 13).The timing table (point 13).
A24 (med)§10 / §21.2 WizardShell headerThe progress line is 6 px under the back square and 8 under skip; the shipped shell draws inline segments instead.TDS ProgressStepper default top padding 16; our "tabs to content" 16 (point 10).Progress row, 16 clear of the controls, 4 thick, count, 7-segment cap (point 10).
A25 (low)§7 Search, composer fieldBorderless inputs: a gray100 fill (1.12:1 on white) is the only boundary.WCAG 1.4.11 asks 3:1 of the indicator that shows a text input, but a boundary is needed only when no other visual cue identifies the control (page); the magnifier glyph and the placeholder are such cues.Keep; add a 1px $borderControl if low-vision testing shows the field is missed.

Checked, no departure found: the Choice resting border (a text-labelled control needs no boundary, WCAG 1.4.11); 44 targets with hit slop (HIG buttons 44 pt; KRDS 버튼 44 px); the Dialog's backdrop not dismissing and at most two actions (Seed Alert Dialog); the Sheet's close button in the title row (KRDS requires one); "validation is never a toast" (KRDS 토스트); EmptyState's one button (NN/g: direct pathways). Stepper, Date and time pickers and the picker Cells offer choices instead of typing, which HIG entering data recommends ("When possible, offer choices instead of requiring text entry"); the Menu's 48 rows meet the 44 target of HIG and KRDS.

Where the sources disagree, and what we recommend ​

#QuestionOne sideOther sideOur call
1Mark required fields with an asteriskNN/g, Material Web, KRDS (allowed)GOV.UK ("Never"), Seed and KRDS majority rule (mark the minority), TDS (words)Keep the asterisk LAW, three caveats, restore "(선택)", re-test on device (point 13)
2Validate on blurBaymard, Wroblewski, HIG, KRDSGOV.UK: not on blurBlur for non-empty values, never for empty ones, live only for availability and password rules
3Disable the primary until validApple HIG, the Korean frame (카카오페이)GOV.UK, Axess Lab, Roselli, Material 1 (enabled by default)Soft-disabled for one pending decision; enabled-and-validate for two or more
4Hint and error above or below the controlMaterial, Seed, TDS, KRDS (below)GOV.UK, Silver, Roselli (above)Below, with a keyboard-up scroll guard; a multi-rule hint goes above; verify on device
5An icon on every noticeCarbon, Primer, AtlassianGOV.UK banner, KRDS snackbar, Material 1, Seed (optional), Toss (specific only)Severity glyph on warning and danger, specific glyphs elsewhere, never the generic ⓘ
6Hard or soft character limitThe platform default (maxLength)GOV.UK (tested with 17 users, low digital skills)Soft, except fixed-format fields
7Forward action in the top barHIG sheets and Material 1 full-screen dialogs (the completion)GOV.UK, Toss and Seed (a bottom CTA for funnels)Split for funnels; top-bar 저장 for an Edit Sheet
8Toast durationKRDS 2 to 4 s, TDS 3 s and 5 s with a buttonMaterial 4 to 10 s (default 5), our 4/6/8Keep ours for older readers; no timer for background failures
9취소 or 닫기 in a dialogHIG: "Cancel" for a cancel actionToss: 닫기, because 취소 reads as cancelling the task닫기 for a dismissal, verb pairs for guards
10An error summaryGOV.UK: always, even for one errorKRDS: silent; NN/g: not the only indicationOnly for three or more fields or an off-screen first error; the CTA helper is the wizard's summary
  1. Notice (point 7): title-first, no default icon (a severity ! on warning and danger, a specific glyph elsewhere, never the generic ⓘ), five tiers T0 to T4, the action under the body, amber and solid red only for blocking states and one per screen.
  2. Messages (point 20): one surface per kind of message; a toast is for results and undo only (one line, one real verb); failures the user caused become a persistent CTA-helper or T3 Notice, not an error toast; copy is state or consequence first, then one verb; fix library §15 to the B7.4 toast timings.
  3. Field (points 9 and 13): one grammar for text, number, choice set, picker and text area: a visible top label, one 18 px support line (hint or error left, counter right), border $borderControl #8E8E93 (3:1), error text $errorText #A3261F, placeholder as an example value in tertiary.
  4. Helper text (point 11): default none; allowed only for an unguessable rule, a one-line consequence, a scale, or a live value; below the control, one sentence, replaced by an error that restates the rule; scroll-into-view includes the line; LabeledField stops stacking caption and error.
  5. Notice under a field (point 12): it answers "what will this answer do"; one line goes in the support line, more becomes a neutral Notice with no default icon; essential information is never hidden behind an ⓘ.
  6. Validation (point 13): reward early, punish late (blur for non-empty values, empty only on press, live clear once an error shows); error = 2px danger border + 16 glyph + 600 text; focus and announce the first invalid field; a summary only for three or more fields; the asterisk LAW stands with "필수" in the accessible name, a legend, and "(선택)" restored.
  7. Wizard header (point 10): keep back and skip at the top and one CTA at the bottom; move the progress into its own 4-thick row 16 px clear of the controls, with a "3/6" count, segments up to 7 and a continuous bar beyond.
  8. CTA helper (point 16): above the button, decision facts only (total cost, terms, what is missing), one or two lines, no promotion and no folded consent; a danger helper for action failures; the primary becomes soft-disabled (grey, focusable, press validates) for a single pending decision.
  9. Audit fixes: field, switch off-track and slider-thumb contrast (A1, A8, A9), segmented selected state (A7), counter location and soft limit (A5, A6), Edit Sheet to a header × and a single 저장 (A12), skeleton show-delay (A17), dialog copy (A13).
  10. Build and gates: add the tokens $borderControl and $errorText (with dark values behind the S1 gate), a Notice glyph allow-list lint, an error ?? caption test, a keyboard-up support-line check on the boards, and a 5-person on-device check of * against "필수" before the marker is final.

Markdown remains the source of truth. Run yarn docs:check before handoff.