AD JB Toolbox 01

CSS 강좌 / 폼 상태와 키보드 초점 꾸미기

폼의 모양은 CSS로 바꾸고, 필수 여부와 기본 형식 검사는 HTML의 required·type 같은 속성으로 정합니다. 키보드 초점, 잘못된 입력, 비활성 상태는 서로 다른 의미이므로 색과 함께 이름·설명·오류 문장을 제공해야 합니다. CSS로 빨간 테두리를 만들었다고 입력 검증이나 오류 설명이 완성되는 것은 아닙니다.

항목 이름과 설명부터 연결하기

label의 for와 입력창의 id를 같게 지정하면 항목 이름과 입력창이 연결됩니다. placeholder는 입력을 시작하면 사라지므로 이름이나 중요한 조건을 대신하지 않습니다. 추가 설명은 계속 보이는 문장으로 두고 aria-describedby에 해당 문장의 id를 지정합니다.

<label for="email">이메일 주소 (필수)</label>
<input id="email" name="email" type="email" required
  placeholder="name@example.com" aria-describedby="email-help">
<p id="email-help">name@example.com 같은 형식으로 입력하세요.</p>

위 부분 코드는 아래 전체 예제의 이메일 구역을 간략하게 나타냅니다. id는 문서에서 대상을 구별하고, name은 제출 데이터의 항목 이름이 됩니다. name이 없으면 화면에서 입력할 수 있어도 일반적인 제출 항목에 포함되지 않습니다. 레이블과 설명의 구성은 W3C 폼 레이블 안내와 폼 입력 안내를 참고하세요.

기본 입력창이 작은 화면과 큰 글자에 맞도록 만들기

입력창의 고유 너비에만 맡기면 글자 크기를 늘렸을 때 좁은 화면을 넘어갈 수 있습니다. 양식의 최대 너비를 정하고 입력창에는 width: 100%와 box-sizing: border-box를 함께 사용합니다. 그러면 테두리와 안쪽 여백도 지정한 너비 안에 포함됩니다.

*, *::before, *::after { box-sizing: border-box; }
form { max-width: 28rem; }
input { width: 100%; min-width: 0; padding: .65rem; font: inherit; }

이 규칙은 전체 예제의 style 영역에 들어 있습니다. font: inherit는 입력창과 버튼이 본문의 글꼴·크기·줄 높이를 따르게 하며, min-width는 Flexbox·Grid 안에 입력창을 넣을 때도 축소를 방해하는 최소 크기를 줄이는 데 도움이 됩니다. 설명을 늘리거나 글자를 키워도 아래 항목이 자연스럽게 내려가도록 고정 높이는 사용하지 않습니다.

초점과 입력 오류를 구분하기

:focus는 현재 초점을 가진 요소에 일치하고, :focus-visible은 브라우저가 초점 표시가 필요하다고 판단한 경우에 일치합니다. 키보드 탐색 때 주로 표시되지만 텍스트 입력창에서는 마우스로 눌러도 표시될 수 있으므로 “키보드에서만 적용된다”고 이해하면 안 됩니다. MDN focus-visible 설명에 판단 방식이 나와 있습니다. 초점 표시를 링크에도 일관되게 적용하려면 링크의 키보드 포커스 표시와 비교하세요.

초점은 지금 조작하는 항목을, 오류는 입력 조건을 충족하지 못한 항목을 나타냅니다. 두 상태가 동시에 있을 수 있으므로 전체 예제는 초점을 파란 윤곽선, 오류를 진한 빨간 테두리와 오류 문장으로 표시합니다. 외곽 윤곽선이 잘리지 않도록 주변에 여유를 두고, outline을 없앤 뒤 다른 초점 표시를 제공하지 않는 방식은 피합니다.

:invalid는 현재 값이 유효하지 않으면 일치하므로 빈 required 입력창도 처음부터 해당합니다. :user-invalid는 사용자 상호작용 뒤 브라우저가 오류 상태를 표시할 시점에 일치합니다. 입력을 끝내거나 제출을 시도하는 등 표시 시점은 브라우저의 동작에 따라 달라질 수 있습니다. 실습은 제출 버튼을 눌러 오류를 확인합니다. MDN user-invalid 설명을 참고하세요.

오래된 지원 대상까지 고려해야 한다면 선택자의 지원 여부를 확인하고 별도 검증 흐름을 설계해야 합니다. 선택자가 지원되지 않아 오류 꾸미기가 나타나지 않더라도 HTML의 필수·형식 검사까지 사라지는 것은 아닙니다. 초점 색은 실제 배경과 충분히 구분되게 고르고, W3C 비텍스트 대비 안내의 적용 조건도 확인하세요.

필수·유효성·비활성·읽기 전용의 차이

상태 의미 초점과 일반적인 제출
required 사용자가 값을 입력해야 함 활성 항목이면 초점 가능; 빈 값은 제출 시 검증 대상
:valid·:invalid HTML 검증 조건 충족 여부 테두리 모양과 별개로 검증 결과가 정해짐
disabled 현재 사용 불가 일반적으로 Tab 탐색과 제출 항목에서 제외
readonly 지원하는 텍스트 입력 등의 값을 수정할 수 없음 초점과 선택 가능; name이 있으면 제출 항목에 포함

읽기 전용은 모든 폼 요소에서 쓸 수 있는 공통 상태가 아닙니다. 이 예제처럼 지원하는 텍스트 입력에 적용해야 하며, select를 readonly로 만드는 용도로 사용하지 않습니다. 또한 readonly 항목은 일반적인 제약 검증 대상에서 제외됩니다. 실제 제출 규칙은 HTML 표준의 데이터 목록 구성과 MDN readonly 안내를 참고하세요.

이메일 예시에는 name@example.com을 사용합니다. 이 형식이 검사에 통과한다고 계정이 실제로 존재하거나 메일이 도착한다는 뜻은 아닙니다. type="email"의 문법 검사는 국제화된 모든 주소 형식을 포괄하는 검사가 아니며, 한글을 @ 앞에 넣은 예시를 이 입력창의 통과 예시로 제공하지 않습니다. HTML 표준의 이메일 입력 조건이 적용 범위를 정합니다.

전체 예제: 상태 표시와 제출 항목 확인

다음 코드를 UTF-8의 form-states.html로 저장하고 브라우저에서 여세요. HTML과 CSS가 폼 상태를 결정합니다. 끝의 짧은 JavaScript는 유효한 제출을 가로채 데이터 목록을 화면에 보여 주기 위한 보조 코드이며, 서버로 전송하거나 별도 입력 검사를 수행하지 않습니다. JavaScript가 꺼져 있으면 결과 출력 기능은 동작하지 않습니다.

<!doctype html>
<html lang="ko">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>폼 상태와 제출 항목 비교</title>
  <style>
*, *::before, *::after { box-sizing: border-box; }
body { margin: 0; padding: 1rem; color: #183044; font-family: sans-serif; line-height: 1.6; }
main { max-width: 46rem; margin-inline: auto; }
h1 { font-size: 1.5rem; } h2 { font-size: 1.2rem; }
button, input { font: inherit; }
button { cursor: pointer; }
:focus-visible { outline: 3px solid #164f73; outline-offset: 3px; }
form { max-width: 28rem; }
.field { margin-block: 1.25rem; }
label { display: block; margin-bottom: .35rem; font-weight: 700; }
input { display: block; width: 100%; min-width: 0; padding: .65rem;
  border: 2px solid #6d7e8b; border-radius: .3rem; background: white; color: #183044; }
input:required { border-inline-start-width: 4px; }
input:user-invalid { border-color: #9d2632; }
.help, .error { margin-block: .4rem 0; }
.error { display: none; color: #9d2632; }
input:user-invalid + .error { display: block; }
input[readonly] { background: #eef6fa; }
input:disabled { background: #eceff2; color: #465766; cursor: not-allowed; }
button { padding: .65rem 1rem; border: 1px solid #164f73; border-radius: .3rem;
  background: #166b92; color: white; }
output { display: block; margin-block: 1rem; white-space: pre-wrap; overflow-wrap: anywhere; }
  </style>
</head>
<body>
<main>
<h1>입력 상태와 제출 데이터 확인</h1>
<form id="demo-form">
  <div class="field">
    <label for="email">이메일 주소 (필수)</label>
    <input id="email" name="email" type="email" autocomplete="email" required
      placeholder="name@example.com" aria-describedby="email-help email-error">
    <p id="email-error" class="error">이메일을 비워 두지 말고 name@example.com 같은 형식으로 입력하세요.</p>
    <p id="email-help" class="help">형식을 확인하는 실습용 입력창입니다. 예시 주소의 실제 수신 여부는 확인하지 않습니다.</p>
  </div>
  <div class="field">
    <label for="plan">요금제 (읽기 전용)</label>
    <input id="plan" name="plan" type="text" value="기본" readonly>
  </div>
  <div class="field">
    <label for="coupon">쿠폰 (현재 비활성)</label>
    <input id="coupon" name="coupon" type="text" value="WELCOME" disabled>
  </div>
  <button type="submit">제출 항목 확인</button>
</form>
<output id="result" for="email plan coupon" aria-live="polite"></output>
<script>
  const form = document.getElementById("demo-form");
  form.addEventListener("submit", (event) => {
    event.preventDefault();
    const entries = Array.from(new FormData(form).entries());
    document.getElementById("result").textContent = JSON.stringify(entries, null, 2);
  });
</script>
</main>
</body>
</html>
  1. 이메일을 비운 채 제출 항목 확인을 누릅니다. 브라우저의 필수 입력 안내와 오류 표시를 확인합니다.
  2. 이메일에 abc를 입력하고 다시 제출합니다. 형식 오류로 결과 목록이 새로 출력되지 않아야 합니다.
  3. name@example.com을 입력하고 제출합니다. 결과에는 email과 plan이 있고 disabled인 coupon은 없습니다.
  4. Tab 키로 이메일→요금제→제출 버튼을 탐색합니다. 비활성 쿠폰은 일반적인 Tab 순서에서 건너뛰고, 읽기 전용 요금제는 초점을 받을 수 있습니다.
  5. 전체 예제에서 쿠폰의 disabled 속성만 제거합니다. 쿠폰에 초점을 둘 수 있고 제출 목록에 값이 추가됩니다. 확인 후 disabled를 복원합니다.
  6. 요금제의 readonly 속성만 제거하면 값을 바꿀 수 있습니다. 실습 후 값을 “기본”으로 돌리고 readonly를 복원합니다.
  7. 화면 폭을 줄이고 브라우저 확대나 글자 크기 설정을 조절합니다. 입력창·초점 윤곽선·오류 문장이 잘리거나 겹치지 않는지 확인합니다.

CSS와 브라우저 검증의 역할을 넘겨짚지 않기

CSS는 항목의 상태를 보여 주는 역할입니다. 브라우저의 검증은 사용자의 입력 실수를 줄일 수 있지만 개발자 도구나 별도 요청으로 우회할 수 있으므로, 실제 서비스는 서버에서 허용 값·길이·권한 등을 다시 검사해야 합니다. 오류 문장은 문제가 있는 항목과 해결 방법을 구체적으로 설명하고, 서버 오류가 생기면 입력 내용을 보존하는 흐름도 별도로 설계합니다.

실습이 예상과 다르면 label의 for와 id, 제출용 name, required와 input type, disabled·readonly 여부를 먼저 확인하세요. 초점 표시가 없으면 다른 outline 규칙이 덮어쓰는지 살펴봅니다. 오류가 처음부터 나타나면 :invalid를 사용했는지, 제출 뒤에도 문장이 안 나타나면 :user-invalid 지원과 형제 선택자의 HTML 순서를 확인하면 원인을 좁힐 수 있습니다.

같은 카테고리의 다른 글
CSS 강좌 / position의 값이 fixed일 때 가운데 정렬하는 방법

CSS 강좌 / position의 값이 fixed일 때 가운데 정렬하는 방법

position의 fixed를 이용하여 요소의 위치를 고정시킬 수 있습니다. 예를 들어 상단에 고정되는 메뉴바를 만들 때 사용합니다. 그런데, fixed를 하면 요소가 한쪽으로 치우칩니다. 만약 가운데에 위치시키고 싶다면 어떻게 할까요? transform 속성으로 해결할 수 있습니다.

CSS / Tutorial / placeholder의 색, 글자 모양 등 꾸미는 방법

CSS / Tutorial / placeholder의 색, 글자 모양 등 꾸미는 방법

placeholder는 HTML5에서 새로 나온 속성으로 input 요소나 textarea 요소에 안내문을 넣을 수 있습니다. 기본 모양은 회색의 글자로, 배경색이 하얀색 또는 밝은 색이면 보기에 괜찮습니다. 하지만 배경색이 어두운 색이거나 화려한 색이면 회색 글자가 어울리지 않을 수 있습니다. placeholder는 ::placeholder 선택자로 선택하여 꾸밀 수 있습니다.

CSS 강좌 / 링크 꾸미는 방법

CSS 강좌 / 링크 꾸미는 방법

링크의 기본 밑줄과 방문·호버·활성 상태를 설정하고 키보드 포커스를 표시합니다. 두 파일로 방문 이력과 Tab·Enter 동작을 확인하며 상태 규칙이 겹칠 때의 결과를 설명합니다.

CSS 강좌 / 미디어 쿼리와 콘텐츠 기준 분기점 만들기

CSS 강좌 / 미디어 쿼리와 콘텐츠 기준 분기점 만들기

카드의 최소 폭과 여백을 기준으로 미디어 쿼리 분기점을 정합니다. 1·2·3열 전체 예제와 경계값 비교로 조건이 적용되는 과정을 확인합니다.

CSS 강좌 / 변수 사용하는 방법: var()·상속·대체값

CSS 강좌 / 변수 사용하는 방법: var()·상속·대체값

CSS 변수의 범위와 상속, var()의 대체값과 잘못된 값 처리 방법을 예제로 설명합니다. calc()와 단위 조합, 미디어 쿼리 조건에 변수를 사용할 수 없는 적용 한계도 확인할 수 있습니다.

CSS 강좌 / ul, ol 가운데 정렬하는 방법

CSS 강좌 / ul, ol 가운데 정렬하는 방법

목록 자체가 아니라 목록 안의 내용만 가운데 정렬할 때는 text-align 속성을 사용합니다. 목록 요소 자체를 가운데 정렬할 때는 margin 속성을 사용합니다. 만약 요소의 내용에 맞게 자동으로 크기가 정해지게 하고 싶으면 display 속성을 사용합니다.

CSS / Tutorial / 문자열 자르는 방법

CSS / Tutorial / 문자열 자르는 방법

웹페이지에 출력하는 문자열의 길이는 서버단에서 조절하는 게 보통입니다. 하지만, 어떤 경우에는 그게 불가능할 수 있습니다. 이미 전송 받은 문자열을 잘라야 하는 상황이 생길 수 있습니다. CSS를 이용하여 문자열의 일부만 보여주는 방법을 소개해드립니다.

CSS 강좌 / CSS를 HTML에 적용시키는 방법

CSS 강좌 / CSS를 HTML에 적용시키는 방법

인라인·내부·외부 CSS의 작성 위치를 비교하고, 두 HTML 페이지가 하나의 스타일시트를 공유하도록 구성합니다. 폴더 위치에 따른 경로와 CSS가 적용되지 않을 때의 확인 순서도 설명합니다.

CSS 강좌 / 기본 선택자 - 요소, 클래스, ID, 속성

CSS 강좌 / 기본 선택자 - 요소, 클래스, ID, 속성

요소·클래스·ID·속성 선택자를 같은 HTML에 적용해 대상과 결과를 비교합니다. 복합 클래스, 공백으로 구분한 속성값, 필수 입력칸을 포함한 전체 예제와 선택자 수정 실습을 제공합니다.

CSS / Tutorial / 마우스 호버 효과 / 텍스트 나오게 하는 방법

CSS / Tutorial / 마우스 호버 효과 / 텍스트 나오게 하는 방법

CSS로 특정 요소에 마우스를 올렸을 때 보이지 않던 특정 요소가 보이게 할 수 있습니다. 아래는 문단에 마우스를 올렸을 때 div 요소가 나오게 하는 예제입니다. div 요소 안에 텍스트가 있지만, 텍스트가 아닌 이미지 등 다른 것도 가능합니다.