LLM 기반 대화형 AI
자연어 질문에 규정·법령 근거를 즉시 안내합니다.
실제 포털 페이지에서 사용되는 컴포넌트·토큰·레이아웃 패턴을 한 곳에 정리한 문서입니다.
페이지별로 등장하는 클래스와 마크업을 그대로 반영해, 신규 화면을 만들 때 바로 복사·재사용할 수 있도록 구성했습니다.
모든 토큰은 common/css/portal.css의 :root에서 정의됩니다.
액센트는 브랜드 Blue (#1a6bff)로 통일하고, 심사자 면만 Navy를 씁니다.
폰트는 SUIT(common/fonts/suit.css) · 아이콘은 Material Symbols Outlined(common/fonts/material-symbols.css) 로컬입니다.
/* :root (portal.css 발췌) */ :root{ --font: "SUIT", "Apple SD Gothic Neo", "맑은 고딕", system-ui, sans-serif; --fs-display: clamp(38px, 5.5vw, 64px); --fs-h1: 40px; --fs-h2: 26px; --fs-h3: 20px; --fs-h4: 17px; --fs-body: 15px; --fs-sm: 13.5px; --fs-xs: 12px; --fs-2xs: 11px; --tracking-tight: -.8px; --tracking-eyebrow: 2.5px; }
간격은 --s1…--s12 (4·8·12·16·20·24·32·40·48px),
라운드는 --r-xs/sm/md/lg/xl/pill,
그림자는 --sh-1/2/3·--sh-glow·--sh-glow-brand를 씁니다.
GNB는 <portal-gnb></portal-gnb> 커스텀 엘리먼트(portal.js가 마크업 주입), 푸터는 <portal-footer></portal-footer> 또는 본 페이지처럼 <footer class="footer">를 직접 씁니다.
랜딩2/3은 body.landing2-body 안에 .landing2-ambient 배경 레이어를 추가합니다.
<!doctype html> <html lang="ko" data-page="portal_xxx"> <head>…<link rel="stylesheet" href="common/fonts/suit.css" /> <link rel="stylesheet" href="common/fonts/material-symbols.css" /> <link rel="stylesheet" href="common/css/portal.css" /></head> <body> <a class="skip" href="#main">본문 바로가기</a> <portal-gnb></portal-gnb> <main id="main">…</main> <portal-footer></portal-footer> <script defer src="common/js/portal.js"></script> </body></html>
.breadcrumb)<nav class="breadcrumb" aria-label="위치"> <a href="portal_landing.html">홈</a> <span aria-hidden="true"><span class="icon icon--line">chevron_right</span></span> <span aria-current="page">현재 페이지</span> </nav>
.ds-demo-note)시연시 모든 데이터는 가상으로 구현된 데이터로 진행됩니다. 실제 데이터와는 다릅니다.
<div class="ds-demo-note" role="note"> <span class="icon">info</span> <div><p class="ds-demo-note__p">…</p></div> </div>
랜딩(portal_landing.html)은 다크 .hero + 캔버스 dots,
서브페이지는 .data-head > .container.chat-head의 브레드크럼·아이콘·제목 구성을 씁니다.
portal_mypage.html은 같은 영역에 .page-head·.page-kicker를 추가합니다.
.data-head + .chat-head)제출서류의 AI 자가검증 결과와 보완 가이드를 확인합니다.
<section class="data-head" aria-labelledby="page-title"> <div class="container chat-head"> <nav class="breadcrumb">…</nav> <div class="chat-head__row"> <span class="chat-head__icon"><span class="icon">fact_check</span></span> <div> <h1 class="chat-head__title" id="page-title">제목</h1> <p class="chat-head__sub">설명</p> </div> </div> </div> </section>
<div class="page-head"> <div> <p class="page-kicker">마이페이지 · 자가검증</p> <h1 id="page-title">제출자료 자가검증 현황</h1> <p>설명…</p> </div> </div>
.hero)
portal_landing.html 전용 다크 히어로. 배경 레이어 4종(.hero__bg · .hero__aurora · .hero__glow · canvas#hero-dots)이 겹쳐집니다.
제목 줄바꿈 <br class="hero__title-br">는 좁은 폭에서만 노출됩니다.
<section class="hero"> <div class="hero__bg" aria-hidden="true"></div> <div class="hero__aurora"><span></span><span></span><span></span></div> <div class="hero__glow"></div> <canvas class="hero__dots" id="hero-dots"></canvas> <div class="hero__content" data-reveal> <h1 class="hero__title">의약품 심사·행정을<br class="hero__title-br"><em>AI</em>가 함께합니다</h1> <p class="hero__desc">…</p> <div class="hero__actions">…</div> </div> </section>
.sh)의약품 산업지원 포털의 AI 서비스를 선택하여 이용해 보세요.
<header class="sh" data-reveal> <h2 class="sh__title">제목</h2> <p class="sh__desc">설명</p> </header>
body.login-body에 data-login-tab="portal"|"review"로 면을 구분합니다.
상단 행은 .login-top-row(돌아가기 + 모드 탭), 카드는 .login-card--portal / .login-card--review로 좌측 브랜드 열과 우측 폼 패널(.login-panel)이 한 카드에 들어갑니다.
.login-mode)<div class="login-mode-bar"> <div class="login-mode" role="tablist" aria-label="로그인 유형"> <button class="login-mode__btn" role="tab" aria-selected="true" aria-controls="panel-portal-m">포털 로그인</button> <button class="login-mode__btn" role="tab" aria-selected="false" aria-controls="panel-review-m">심사자 전용</button> </div> </div>
<div id="panel-portal-m" class="login-card login-card--portal" role="tabpanel"> <div class="login-brand login-brand--portal"> <div class="login-brand__inner"> <h1 class="login-brand__headline">의약품 산업지원 포털</h1> <div class="login-shield"> <span class="icon">verified_user</span> <p><strong>대국민·산업 지원 서비스입니다</strong> …</p> </div> <div class="login-status"> <div class="login-status__bar"><span>System status</span><em>ACTIVE</em></div> <div class="login-status__track"><div class="login-status__fill"></div></div> </div> </div> </div> <div class="login-panel"> <h2 class="login-panel__title">로그인</h2> <p class="login-panel__sub">…</p> <form class="js-login-form" novalidate> <div class="login-field"> <label for="uid-portal-m">아이디</label> <div class="login-input-wrap"> <span class="icon icon--line">person</span> <input id="uid-portal-m" name="uid" type="text" autocomplete="username" /> </div> </div> <div class="login-field"> <label for="pw-portal-m">비밀번호</label> <div class="login-input-wrap login-input-wrap--has-toggle"> <span class="icon icon--line">lock</span> <input type="password" id="pw-portal-m" name="password" /> <button type="button" class="login-pw-toggle" aria-pressed="false"> <span class="icon icon--line">visibility</span> </button> </div> </div> <div class="login-row"><label><input type="checkbox" name="keep" /> 로그인 상태 유지</label></div> <button type="submit" class="login-submit">로그인</button> </form> <div class="login-links"><a>아이디 찾기</a>…<strong><a>회원가입</a></strong></div> </div> </div>
.login-card--review / .login-brand--reviewer)
필드 라벨이 영문(User ID / Password)이고, 폼 하단에 .login-notice(info 아이콘 + 보안 안내) 한 줄이 들어갑니다.
"ID 저장" 체크박스와 "보안 토큰 재발급" 링크는 한 줄(.login-row)에 양 끝 정렬됩니다.
.btn--xs · .btn--sm · .btn--md · .btn--lg · .btn--xl · .btn--xxl)
크기 modifier 조합 시 항상 .btn .btn--{variant} .btn--{size} 순서로 작성. md가 기본값이라 별도 클래스 없이 .btn만 사용해도 같음. 표는 11px / 13px / 14px / 15px / 16px / 18px 글자 크기에 매핑됩니다.
<!-- 가장 작은 보조 액션 (테이블 행 안 등) --> <button class="btn btn--primary btn--xs">편집</button> <!-- 툴바 · 카드 액션 (가장 자주 쓰임) --> <button class="btn btn--primary btn--sm">저장하기</button> <!-- 본문 기본 액션 --> <button class="btn btn--primary">제출하기</button> <!-- = btn--md --> <!-- 폼 제출 / 다이얼로그 주 액션 --> <button class="btn btn--primary btn--lg">로그인</button> <!-- 랜딩 히어로 CTA --> <a class="btn btn--primary btn--xl" href="…">지금 시작하기</a> <!-- 마케팅 페이지 / 빅 CTA --> <a class="btn btn--primary btn--xxl"><span class="icon">auto_awesome</span>AI 자가검증 시작하기</a>
사용 가능한 색상 변형: .btn--primary / .btn--secondary / .btn--outline / .btn--line / .btn--ghost. 테이블의 각 행이 동일 사이즈(SM)에서의 변형 비교입니다.
<!-- ── 1행: SM 사이즈 × 5변형 ── --> <button class="btn btn--primary btn--sm"><span class="icon">arrow_forward</span>Primary</button> <button class="btn btn--secondary btn--sm">Secondary</button> <button class="btn btn--outline btn--sm">Outline</button> <button class="btn btn--line btn--sm">Line</button> <button class="btn btn--ghost btn--sm">Ghost</button> <!-- ── 2행: MD(기본) 사이즈 × 5변형 — 사이즈 클래스 생략 가능 ── --> <button class="btn btn--primary"><span class="icon">arrow_forward</span>Primary</button> <button class="btn btn--secondary">Secondary</button> <button class="btn btn--outline">Outline</button> <button class="btn btn--line">Line</button> <button class="btn btn--ghost">Ghost</button> <!-- ── 3행: LG 사이즈 × 5변형 ── --> <button class="btn btn--primary btn--lg"><span class="icon">arrow_forward</span>Primary</button> <button class="btn btn--secondary btn--lg">Secondary</button> <button class="btn btn--outline btn--lg">Outline</button> <button class="btn btn--line btn--lg">Line</button> <button class="btn btn--ghost btn--lg">Ghost</button> <!-- 변형 의미 --> <!-- Primary: 기본 액션. 페이지·다이얼로그·카드당 1개 권장 --> <!-- Secondary: 보조 액션. 흰 배경 + 테두리 --> <!-- Outline: 브랜드 컬러 강조 보조 액션 --> <!-- Line: 단순 테두리 보조 (toolbar refresh 등) --> <!-- Ghost: 텍스트만 (메뉴·내비) -->
.btn--icon) · 블록(.btn--block) · 비활성<!-- 아이콘 전용: aria-label 필수 --> <button class="btn btn--secondary btn--icon btn--sm" aria-label="편집"> <span class="icon">edit</span> </button> <!-- 폼·로그인 등 풀폭 액션 --> <button class="btn btn--primary btn--block btn--lg">로그인</button> <!-- 비활성: 둘 중 어느 쪽이든 OK (disabled 우선 권장) --> <button class="btn btn--primary" disabled>처리 중</button> <a class="btn btn--primary is-disabled" aria-disabled="true">대기</a>
.svl-btn-* / .landing2-btn--*)
자가검증 랜딩 SVL은 .svl-btn-primary · .svl-btn-ghost를, 타일형 메인은 .landing2-btn.landing2-btn--primary · .landing2-btn--ghost를 씁니다.
<a class="svl-btn-primary" href="portal_01_selfverify.html"><span class="icon">description</span>검증 시작하기</a> <a class="svl-btn-ghost" href="portal_mypage.html"><span class="icon">monitoring</span>나의 자가검증 현황</a> <a class="landing2-btn landing2-btn--primary">서비스 시작하기</a>
.chip--xs · .chip--sm · .chip--md · .chip--lg)
MD가 기본값. min-height는 18 / 22 / 24 / 30px, font-size는 10 / 11 / 12 / 13px로 4단계 명확히 구분.
실 사이트 매핑 — XS: 표 행 안 inline 카운터(향후) · SM: 알림·서브 메타 · MD: 자가검증 결과 행 · LG: 모달 헤더 강조.
<span class="chip chip--ok chip--xs">적정</span> <span class="chip chip--ok chip--sm">적정</span> <span class="chip chip--ok">적정</span> <!-- = chip--md --> <span class="chip chip--ok chip--lg">적정</span>
.chip--ok(녹색·적정/완료) · .chip--bad(빨강·미흡/부적정/반려) ·
.chip--warn(주황·확인요) · .chip--info(파랑·진행중) ·
.chip--neutral(회색·미신청/—) · .chip--brand(브랜드·신청완료).
자가검증 결과 행 (적정 / 미흡 / 확인요)
민원 신청 상태 (마이페이지 테이블)
검증 진행 (자가검증 / 검토)
<!-- ── 자가검증 결과 행 (chip--md, 기본 사이즈) ── --> <!-- portal.js renderVerifyResultStackHtml 가 그리는 200건 항목별 결과 --> <span class="chip chip--ok chip--md">적정</span> <span class="chip chip--bad chip--md">미흡</span> <span class="chip chip--bad chip--md">부적정</span> <span class="chip chip--warn chip--md">확인요</span> <!-- ── 마이페이지 민원신청 컬럼 (chip--sm, 표 행 안에 들어가므로 작게) ── --> <!-- portal_mypage.html — table.u-table-list 의 마지막 컬럼 --> <span class="chip chip--neutral chip--sm">미신청</span> <span class="chip chip--brand chip--sm">신청완료</span> <!-- ── 자가검증 진행 상태 (chip--sm, 표 행 안) ── --> <span class="chip chip--info chip--sm">진행중</span> <span class="chip chip--ok chip--sm">완료</span> <span class="chip chip--bad chip--sm">보완 필요</span> <span class="chip chip--neutral chip--sm">—</span>
.badge--xs · .badge--sm · .badge--md · .badge--lg)
min-height 16 / 18 / 22 / 28px, font-size 9 / 10 / 11 / 13px. 칩보다 한 단계 작아서 메타 라벨·태그 용도.
<span class="badge badge--brand badge--xs">NEW</span> <span class="badge badge--brand badge--sm">법령·가이드</span> <span class="badge badge--brand">인증된 서비스</span> <span class="badge badge--brand badge--lg"><span class="icon">verified</span>인증완료</span>
브랜드(--brand) · 청록(--teal) · 자주(--purple) · 초록(--green) ·
호박(--amber) · 장미(--rose) · 회색(--gray) · 자동저장(--save).
<!-- ── 위 미리보기는 모두 badge--md (기본 사이즈, 클래스 생략 가능) ── --> <span class="badge badge--brand badge--md"><span class="icon">verified</span>인증된 서비스</span> <span class="badge badge--teal badge--md">법령·가이드</span> <span class="badge badge--purple badge--md">로그인 필요</span> <span class="badge badge--green badge--md">데이터셋</span> <span class="badge badge--amber badge--md">검토 대기</span> <span class="badge badge--rose badge--md">처리 지연</span> <span class="badge badge--gray badge--md">미정</span> <span class="badge badge--save badge--md"><span class="icon">cloud_done</span>자동 저장됨</span> <!-- 출처 매핑 --> <!-- badge--teal/purple/green : portal_landing*.html 서비스 카드 라벨 --> <!-- badge--save : portal_01b_review_editor.html 자동저장 인디케이터 -->
.tag) — 카테고리·키워드 단순 라벨<span class="tag">Text · ko</span> <span class="tag">Benchmark</span>
.choice-chip) / 추천 chip (.rev-db-chip).field).radio-card)
portal_01_selfverify.html Step 1에서 4종 서류 유형 선택용. input[type=radio]를 시각적으로 숨기고, 카드 자체가 라벨이 됩니다.
<label class="radio-card"> <input type="radio" name="doc-type" value="bio-plan" /> <span class="radio-card__indicator"></span> <span class="radio-card__icon"><span class="icon">biotech</span></span> <span class="radio-card__title">생물학적 동등성 시험계획서</span> <span class="radio-card__desc">…</span> </label>
.dropzone)<div class="dropzone" id="dropzone" tabindex="0" role="button"> <div class="dropzone__icon"><span class="icon">upload_file</span></div> <div class="dropzone__title">파일을 여기에 끌어다 놓거나 선택하세요</div> <p class="dropzone__meta">PDF · XLSX · DOCX (최대 100MB · 12개)</p> <input type="file" id="file-input" multiple accept=".pdf,.docx,.xlsx" style="display:none" /> <button type="button" class="btn btn--secondary" id="btn-browse"><span class="icon">upload_file</span> 파일 찾기 및 선택</button> <div id="file-pill-wrap"></div> </div>
.mypg-filter) — 실제 portal_mypage.html에서 추출
한 줄에 검색2 + 일자범위 + 셀렉트3 + 초기화/조회 버튼이 배치되는 가로형 필터바.
.mypg-filter__row가 flex 컨테이너이며 자식 컨트롤들이 wrap되며 채워집니다.
<div class="mypg-filter"> <div class="mypg-filter__row"> <label class="mypg-filter__search"> <input type="search" id="q-id" placeholder="검증일련번호 검색" /> </label> <label class="mypg-filter__search"> <input type="search" id="q-file" placeholder="파일명 검색" /> </label> <div class="mypg-filter__date"> <span class="mypg-filter__date-label">검증일자</span> <input type="date" id="f-date-from" /> <span class="mypg-filter__date-sep">~</span> <input type="date" id="f-date-to" /> </div> <select id="f-status"><option value="">진행상태 전체</option>…</select> <select id="f-outcome"><option value="">결과구분 전체</option>…</select> <select id="f-civil"><option value="">신청여부 전체</option>…</select> <button class="btn btn--outline btn--sm" id="btn-filter-reset">초기화</button> <button class="btn btn--primary btn--sm" id="btn-filter-search"><span class="icon">search</span>조회</button> </div> </div>
.toolbar) — 자가검증 진입 페이지 등 보조 화면용
모든 목록 테이블은 .panel > .table-wrap > table.u-table-list 구조입니다.
빈 상태는 p.empty, 페이저는 nav.pager를 씁니다.
실 화면 6열: 검증일련번호 · 검증일자 · 파일명 · 검증진행상태 · 검증결과구분 · 민원신청여부.
헤더 배경은 navy(--brand-900 계열) · 흰 글씨. 행 클릭 시 상세결과 모달(#modal-verify-detail) 오픈.
상태/결과/신청여부 컬럼은 .chip--sm(작은 사이즈) 사용.
| 검증일련번호 | 검증일자 | 파일명 | 검증진행상태 | 검증결과구분 | 민원신청여부 |
|---|---|---|---|---|---|
| S-2026-TDVZUE | 2026-03-30 | 의료기기_SW_Verification_Report.pdf | 완료 | 미흡 (3/200) | 미신청 |
| S-2026-WLTNVG | 2026-03-28 | GMP_적합판정서_2026_v1.2.pdf | 완료 | 적정 (180/200) | 미신청 |
| S-2026-AQYPWP | 2026-03-26 | 포장재_적합성_시험성적서.pdf | 완료 | 적정 (180/200) | 신청완료 |
| S-2026-FSOWSW | 2026-03-24 | Dissolution_Profile_Table.xlsx | 진행중 | — | 미신청 |
<!-- portal_mypage.html ─ 실제 마크업 그대로 --> <div class="panel"> <div class="table-wrap"> <table class="u-table-list"> <thead> <tr> <th>검증일련번호</th> <th>검증일자</th> <th>파일명</th> <th>검증진행상태</th> <th>검증결과구분</th> <th>민원신청여부</th> </tr> </thead> <tbody id="tbody"> <!-- 행은 portal.js에서 동적 렌더 (DB.submissions 기반) --> <tr> <td>S-2026-TDVZUE</td> <td>2026-03-30</td> <td>의료기기_SW_Verification_Report.pdf</td> <td><span class="chip chip--ok chip--sm">완료</span></td> <td><span class="chip chip--bad chip--sm">미흡 (3/200)</span></td> <td><span class="chip chip--neutral chip--sm">미신청</span></td> </tr> </tbody> </table> </div> <p class="empty" id="empty" style="display:none">조건에 맞는 검증 건이 없습니다.</p> <nav class="pager" id="pager" hidden>…</nav> </div>
.pager)<nav class="pager" id="pager" aria-label="검증 목록 페이지"> <p class="pager__meta">총 N건…</p> <div class="pager__controls"> <button class="pager__btn pager__btn--icon" id="pager-first"><span class="icon">first_page</span></button> <button id="pager-prev">…</button> <ul class="pager__list" id="pager-list" role="list"></ul> <button id="pager-next">…</button> <button id="pager-last">…</button> </div> </nav>
.svc) · 피처 (.feat)
portal_landing.html에서 사용. 4대 서비스 카드는 .svc-carousel > .svc-carousel__track.u-grid-4 안에 .svc를 배치하고, 변형(--teal · --purple · --green)으로 카드 톤만 다르게 합니다.
피처는 .feat-carousel > .u-grid-3입니다.
서류 제출 전 AI가 규정 적합성을 미리 점검합니다.
auto_stories의약품 법령·지침을 대화로 쉽고 빠르게.
support_agent품목허가 신고부터 마약류 반입 신고까지.
dataset학습데이터·평가데이터·API를 제공합니다.
<a class="svc svc--teal" href="portal_02_info_chat.html"> <span class="svc__bg-icon icon" aria-hidden="true">auto_stories</span> <span class="svc__icon"><span class="icon">auto_stories</span></span> <h3 class="svc__title">정보제공 AI</h3> <p class="svc__desc">…</p> <div class="svc__footer"> <span class="badge badge--teal">법령·가이드</span> <span class="icon icon--line svc__arrow">arrow_forward</span> </div> </a>
.feat)자연어 질문에 규정·법령 근거를 즉시 안내합니다.
업로드 서류의 적합성을 자동 점검합니다.
처리 후 문서·개인정보를 즉시 삭제합니다.
body.svl-page 전용 다크 히어로 + 라이트 본문 구성. 히어로 SVG 애니메이션, 통계 카운터, 4종 서류 카드, 4단계 프로세스, 핵심 기능 그리드, CTA로 이어집니다.
.svl-section-head)Supported Documents
허가 신청 시 보완 요청이 잦은 핵심 서류별 맞춤 검증을 제공합니다.
<div class="svl-section-head" data-reveal> <p class="svl-section-eyebrow">Supported Documents</p> <h2 class="svl-section-title">검증 가능한 서류 4종</h2> <p class="svl-section-desc">…</p> </div>
.svl-doc-card).svl-process)<div class="svl-process" role="list"> <div class="svl-step" role="listitem" data-reveal> <div class="svl-step__icon"> <span class="svl-step__num-badge">01</span> <svg viewBox="0 0 48 48">…</svg> </div> <p class="svl-step__title">유형 선택</p> <p class="svl-step__desc">검증할 서류 유형을 선택</p> </div> <!-- 02 / 03 / 04 동일 구조 --> </div>
.svl-feature-grid)<section class="svl-section svl-section--dark"> <div class="svl-inner"> <div class="svl-feature-grid"> <div class="svl-feature-item"> <div class="svl-feature-item__icon"><span class="icon">format_list_bulleted</span></div> <div> <p class="svl-feature-item__title">필수 항목 누락 감지</p> <p class="svl-feature-item__desc">…</p> </div> </div> </div> </div> </section>
.svl-cta + .svl-notice)<div class="svl-cta"> <p class="svl-cta__eyebrow">GET STARTED</p> <h2 class="svl-cta__title">제출 전, 지금 점검해 보세요</h2> <p class="svl-cta__sub">…</p> <div class="svl-cta__actions"> <a class="svl-btn-primary">검증 시작하기</a> <a class="svl-btn-ghost svl-btn-ghost--light">나의 자가검증 현황</a> </div> <div class="svl-notice"> <span class="icon svl-notice__icon">info</span> <div> <p class="svl-notice__title">서비스 유의사항</p> <p class="svl-notice__body">…</p> </div> </div> </div>
body.landing2-body + .landing2-ambient(배경 블롭·그리드).
4개 타일이 .landing2-grid에 들어가고, 각 타일은 호버/포커스 시 하위 링크(.landing2-subs)가 펼쳐집니다.
portal_landing3.html은 html.landing3 data-theme="light"로 같은 컴포넌트를 라이트 톤으로 덮어씁니다.
<div class="landing2-ambient" aria-hidden="true"> <span class="landing2-ambient__blob landing2-ambient__blob--1"></span> <span class="landing2-ambient__blob landing2-ambient__blob--2"></span> <span class="landing2-ambient__blob landing2-ambient__blob--3"></span> <span class="landing2-ambient__grid"></span> </div> <header class="landing2-hero"> <h1 class="landing2-hero__title">의약품 산업지원 포털</h1> <div class="landing2-hero__cta"> <a class="landing2-btn landing2-btn--primary">서비스 시작하기</a> <a class="landing2-btn landing2-btn--ghost">로그인</a> </div> </header>
.landing2-tile) + 하위 링크<div class="landing2-grid" role="list"> <article class="landing2-tile landing2-tile--sv" tabindex="0" role="listitem"> <div class="landing2-tile__bg"></div> <span class="landing2-tile__float"><span class="icon">verified_user</span></span> <div class="landing2-tile__deco"> <span class="landing2-chip"><span class="icon">biotech</span>생동성 시험</span> <span class="landing2-chip landing2-chip--muted">…</span> </div> <div class="landing2-tile__inner"> <h2 class="landing2-tile__title">제출서류 자가검증 AI</h2> <p class="landing2-tile__lead">…</p> <div class="landing2-subs"> <a class="landing2-link" href="portal_01_selfverify.html?doc=bio-plan"> <span class="icon">biotech</span> <span class="landing2-link__txt">생물학적 동등성 시험계획서 검증</span> <span class="icon landing2-link__go">arrow_forward</span> </a> </div> </div> </article> <!-- --info / --civil / --data 동일 패턴 --> </div> <p class="landing2-hint">배너에 마우스를 올리거나…</p>
2열 셸(.sv-layout) — 좌측 절차 안내 아코디언(.sv-proc.sv-proc-acc) + 우측 본문(.sv-main).
스텝퍼(.wizard-stepper)·스텝 패널(.step-panel)·진행 모달(.modal-card--sv-steps)·접수 완료 모달(.modal-card--async-done)·결과 모달(.modal-card--sv-result)이 한 페이지 안에 함께 있습니다.
.wizard-stepper)<div class="wizard-stepper" id="wizard-stepper"> <div class="wizard-stepper__item is-active" data-ws="1"> <div class="wizard-stepper__circle">1</div> <div class="wizard-stepper__label">자가검증 AI 유형 선택</div> </div> <div class="wizard-stepper__item" data-ws="2">…</div> </div>
<section class="container section sv-section"> <div class="sv-layout"> <aside class="sv-proc"> <details class="sv-proc-acc"> <summary class="sv-proc-acc__summary"> <span class="sv-proc-acc__summary-text">자가검증 절차</span> <span class="icon sv-proc-acc__chev">expand_more</span> </summary> <div class="sv-proc-acc__panel"> <h2 class="sv-proc__title">제출서류 자가검증 절차 안내</h2> <ol class="sv-proc__list"> <li><span class="sv-proc__num">1</span> 검증할 제출서류에 맞는 …</li> </ol> <p class="sv-proc__note">※ 검증 완료시 …</p> </div> </details> <div class="sv-proc__foot"> <a class="btn btn--secondary btn--sm" href="portal_mypage.html">나의 자가검증 현황 열기</a> </div> </aside> <div class="sv-main"> <div class="wizard-stepper">…</div> <div class="step-panel is-active" id="step-1"> <header class="step-head"><div> <span class="eyebrow">Step 1</span> <h2 class="step-head__title">유형을 선택하세요</h2> <p class="step-head__desc">…</p> </div></header> <form id="verify-form"> <div class="u-grid-2" role="radiogroup">…</div> </form> </div> <div class="step-panel" id="step-2"> <header class="step-head"><div> <p class="step-head__picked-type">선택 유형: <strong>…</strong></p> <span class="eyebrow">Step 2</span>… </div></header> <div class="dropzone">…</div> <div class="action-row"> <button class="btn btn--secondary btn--outline" id="btn-back-2">이전으로</button> <div class="action-row__end"> <button class="btn btn--outline btn--sm" id="btn-reset-files">초기화</button> <button class="btn btn--primary btn--xl" id="btn-start-verify">AI 자가검증 시작</button> </div> </div> </div> </div> </div> </section>
.modal-card--sv-steps)
한 카드 안에 진행 패널(#prog-running-panel)과 완료 패널(#prog-complete-panel hidden)이 함께 들어갑니다.
상단 .prog-flow-type으로 선택 유형을 보여주고, 진행 시 .sv-step__bar·.sv-step__notes·.sv-step__eta를, 완료 시 .sv-step__result(결과 dl)·.sv-step__complete-desc를 노출합니다.
<div class="modal-backdrop" id="modal-progress"> <div class="modal-card modal-card--sv-steps"> <p class="prog-flow-type">선택 유형: <strong>생물학적 동등성 시험계획서</strong></p> <div id="prog-running-panel"> <h3 class="sv-step__title">제출서류 파일 업로드 중</h3> <p class="sv-step__sub">…</p> <div class="sv-step__bar"><div class="sv-step__bar-fill"></div></div> <ul class="sv-step__notes"><li>…</li></ul> <p class="sv-step__eta">예상완료시간: 약 30분 <span class="sv-step__eta-note">※ …</span></p> <div class="sv-step__foot"><button class="btn btn--outline">닫기</button></div> </div> <div id="prog-complete-panel" hidden> <h3 class="sv-step__title sv-step__title--done">자가검증 완료</h3> <dl class="sv-step__result"> <div class="sv-step__result-row"><dt>검증일련번호</dt><dd>—</dd></div> … </dl> <p class="sv-step__complete-desc">…</p> <div class="sv-step__foot"><a class="btn btn--primary" href="portal_mypage.html">나의 자가검증 현황 열기</a></div> </div> </div> </div>
.modal-card--async-done)<div class="modal-backdrop" id="modal-async-done"> <div class="modal-card modal-card--async-done"> <button class="modal-card__close" id="btn-async-x"><span class="icon">close</span></button> <h3 class="modal-card__title">접수가 완료되었습니다</h3> <div class="modal-card__body"> <p class="modal-async__lead"><strong>첨부 묶음</strong> 검증 요청이 접수되었습니다.</p> <p>약 <strong>12</strong>분 후 결과 확인이 가능…</p> <p class="modal-async__fine">이 창을 닫으셔도 됩니다…</p> </div> <div class="modal-actions"> <button class="btn btn--outline">확인 · 닫기</button> <a class="btn btn--primary">마이페이지에서 확인</a> </div> </div> </div>
마이페이지(portal_mypage)는 .mypg-filter + .u-table-list(6열) + #modal-verify-detail.modal-card--mypg-detail의 조합입니다.
자가검증 현황(portal_01a_verify_status)은 같은 패턴에 우측 aside.panel.detail.verify-detail-source 숨김 패널을 두고, #modal-verify-detail.modal-card--detail-mobile.modal-card--verify-detail로 상세를 띄웁니다.
.modal-card--mypg-detail)<div class="modal-backdrop" id="modal-verify-detail" role="dialog" aria-modal="true" aria-labelledby="mypg-detail-title" aria-hidden="true"> <div class="modal-card modal-card--wide modal-card--mypg-detail"> <h2 class="mypg-detail-modal__title" id="mypg-detail-title">자가검증 상세결과</h2> <div id="detail-modal-slot" class="detail-modal-slot"></div> <div class="mypg-detail-modal__foot"> <button class="btn btn--outline" id="btn-detail-close">닫기</button> </div> </div> </div>
.detail.verify-detail-source)<aside class="panel detail verify-detail-source" id="detail" aria-hidden="true"> <div class="empty" id="detail-placeholder">목록에서 항목을 선택하세요.</div> <div id="detail-body" style="display:none"> <div class="detail__head"> <div> <h2 id="d-title">자가검증 결과</h2> <p class="detail__file-line" id="d-file-line"></p> </div> <button class="btn btn--line" id="btn-close-detail"><span class="icon">close</span></button> </div> <div id="d-verify-root" class="detail-verify-root"></div> <div class="detail__foot-actions"> <button class="btn btn--primary" id="d-dl-verify"><span class="icon">download</span>검증결과 다운로드</button> <a class="btn btn--line" id="d-reverify-link"><span class="icon">upload</span>보완 자료 재검증</a> <a class="btn btn--outline" id="d-editor-link"><span class="icon">edit_document</span>AI 검토서 편집기</a> </div> </div> </aside>
두 챗은 동일한 셸을 공유합니다 — div.portal-flow-chat.portal-flow-chat--embed 안에 section.container.chat-area > .chat-wrap이 들어가고,
상단 고정 분야 패널(.civil-panel-fixed) → 스크롤 영역(.chat-scroll) → 입력창(.compose) 순으로 배치됩니다.
.civil-panel-fixed + .choice-list)
민원 챗에서는 「다빈도 민원신청 추천」 + <hr class="civil-group-divider"> + 「전자증명서 발급」 두 그룹이 한 패널 안에 들어갑니다.
그룹 헤더는 .civil-group-head(아이콘 + 제목 + 보조 라벨), 항목은 button.choice입니다.
<div class="chat-scroll" id="info-chat-scroll" aria-live="polite"> <div class="turn"> <span class="avatar avatar--ai" aria-hidden="true"><span class="icon">smart_toy</span></span> <div class="turn__body"> <div class="turn__meta"><strong>정보제공 AI</strong></div> <div class="bubble bubble--ai"><p>안녕하세요…</p></div> </div> </div> <!-- 내 메시지(우측): 아바타·이름 없이 정렬만 --> <div class="turn turn--user"><div class="turn__body"><div class="bubble bubble--user">…</div></div></div> </div> <div class="compose"> <div class="compose__row"> <div class="compose__input"> <span class="icon">edit_note</span> <input id="info-chat-input" type="text" placeholder="질문 내용 입력" /> </div> <button class="compose__send" id="info-chat-send" aria-label="보내기"><span class="icon">send</span></button> </div> <p class="compose__hint"><span class="icon">info</span>AI가 제공하는 정보는 참고용입니다.</p> </div>
rev-*)
body.rev-body · 3열 워크스페이스 (.rev-workspace) — 좌(제출서류·최근문서) / 중(탭별 작업 영역) / 우(챗봇).
가운데 영역은 6개 탭(편집기·허가DB·요약·번역·비교·비전AI)을 .rev-rail + .rev-pane으로 전환합니다.
<body class="rev-body"> <main id="main" class="rev-main"> <div class="rev-workspace" id="rev-workspace"> <aside class="rev-left" id="rev-left">…</aside> <div class="rev-gutter rev-gutter--l" id="rev-gutter-l" role="separator" tabindex="0">…</div> <section class="rev-center">…</section> <div class="rev-gutter rev-gutter--r" id="rev-gutter-r">…</div> <aside class="rev-right" id="rev-right">…</aside> </div> </main> </body>
<div class="rev-side-tabs rev-side-tabs--pill" role="tablist"> <button class="rev-side-tab is-active" data-rev-doc-tab="sub">제출서류</button> <button class="rev-side-tab" data-rev-doc-tab="recent">최근문서</button> </div> <div class="rev-doc-pane is-active" id="rev-pane-sub"> <div class="rev-proj-upload"> <input type="file" id="rev-proj-upload-input" multiple /> <button class="rev-proj-upload__hit"> <span class="icon rev-proj-upload__ic">upload</span> <span class="rev-proj-upload__ttl">파일 업로드</span> </button> <p class="rev-proj-upload__help">PDF · XLSX · DOCX (최대 100MB) · 데모</p> </div> <div class="rev-proj"> <h2 class="rev-proj__title">프로젝트 폴더</h2> <div class="rev-proj-tree"> <div class="rev-proj-node"> <button class="rev-proj-folder is-open" aria-expanded="true"> <span class="icon rev-proj-folder__ic">folder</span> <span class="rev-proj-folder__name">KD-2304 신약 허가</span> </button> <div class="rev-proj-nest"> <button class="rev-proj-file rev-proj-file--active"> <span class="icon rev-proj-file__ic">table_chart</span> <span class="rev-proj-file__name">독성시험 데이터_최종.xlsx</span> </button> </div> </div> </div> </div> <h2 class="rev-h2">관련 검토서 현황</h2> <div class="rev-status-cards"> <button class="rev-status-card"> <span class="rev-dot">·</span><span class="rev-name">아스피린정 100mg</span> <span class="rev-chip rev-chip--ok">허가심사 완료</span> </button> </div> <ol class="rev-timeline"> <li><time datetime="2026-01-13">2026. 1. 13.</time><span>안전성 데이터 보완</span></li> </ol> </div>
.rev-rail) + 패널 전환<nav class="rev-rail" role="tablist"> <button class="rev-rail__btn rev-rail__btn--active" data-rev-tab="ed"> <span class="icon icon--line">description</span> <span class="rev-rail__label">AI 검토서 편집기</span> </button> <button data-rev-tab="db">…허가DB검색…</button> <button data-rev-tab="sum">…내용요약…</button> <button data-rev-tab="tr">…다국어 번역…</button> <button data-rev-tab="cmp">…비교…</button> <button data-rev-tab="vis">…비전 AI…</button> </nav> <div class="rev-pane is-active" id="rev-pane-ed"><iframe src="editor/word_editor.html" class="rev-word-embed__frame"></iframe></div> <div class="rev-pane rev-pane--db rev-tool-page" id="rev-pane-db" hidden>…</div> <div class="rev-pane rev-pane--sum rev-tool-page" id="rev-pane-sum" hidden>…</div> <div class="rev-pane rev-pane--tr rev-tool-page" id="rev-pane-tr" hidden>…</div> <div class="rev-pane rev-pane--cmp rev-tool-page" id="rev-pane-cmp" hidden>…</div> <div class="rev-pane rev-pane--vis rev-tool-page" id="rev-pane-vis" hidden>…</div>
.rev-tool-page__head)<header class="rev-tool-page__head"> <div class="rev-tool-page__head-text"> <h2 class="rev-tool-page__title">의약품 허가 DB 정밀 검색</h2> <p class="rev-tool-page__desc">품목허가대장·검토서 메타를 조회합니다.</p> </div> <button class="rev-db-ai-toggle is-on" aria-pressed="true"><span class="icon">bolt</span><span class="rev-db-ai-toggle__txt">AI 보조 검색 활성</span></button> </header>
.rev-tr-board) / 비교 (.rev-cmp-shell)<!-- 번역: 좌 원문 / 우 번역문 --> <div class="rev-tr-board"> <section class="rev-tr-panel"> <header class="rev-tr-panel__head"> <h3 class="rev-tr-panel__title">원문</h3> <span class="rev-tr-badge rev-tr-badge--medical">의약품</span> </header> <textarea class="rev-tr-editor" id="rev-tr-src-editor" lang="en"></textarea> </section> <section class="rev-tr-panel"> <header><span class="rev-tr-badge rev-tr-badge--ai"><span class="icon">auto_awesome</span>AI 번역</span></header> <textarea class="rev-tr-editor" id="rev-tr-tgt-editor" lang="ko"></textarea> </section> </div> <!-- 비교: 변경 요약 + 좌우 비교 본문 --> <div class="rev-cmp-summary"> <span class="rev-cmp-summary__total"><span class="rev-cmp-dot rev-cmp-dot--neutral"></span><strong>전체 변경</strong> 8</span> <span class="rev-cmp-chip rev-cmp-chip--mod">수정 3</span> <span class="rev-cmp-chip rev-cmp-chip--del">삭제 2</span> <span class="rev-cmp-chip rev-cmp-chip--add">추가 3</span> </div> <div class="rev-cmp-panes"> <div class="rev-cmp-col"><div class="rev-cmp-doc">…<li class="rev-cmp-li--del">…</li>…<span class="rev-mark-mod">65</span>…</div></div> <div class="rev-cmp-col rev-cmp-col--new"><div class="rev-cmp-doc">…<li class="rev-cmp-li--add">…</li>…</div></div> </div>
.rev-right)<aside class="rev-right" id="rev-right"> <div class="rev-chat-arch"> <div class="rev-chat-arch__logo"><span class="icon">smart_toy</span></div> <div class="rev-chat-arch__meta"> <div class="rev-chat-arch__title">AI 인텔리전스 어시스턴트</div> <div class="rev-chat-arch__status"><span class="rev-chat-arch__dot"></span>SYSTEM ONLINE</div> </div> </div> <div class="rev-chat-scroll" id="rev-chat-scroll" role="log"> <div class="rev-chat-turn rev-chat-turn--user"><div class="rev-chat-msg rev-chat-msg--user">아스피린정이 주성분인 품목</div></div> <div class="rev-chat-turn rev-chat-turn--ai"> <div class="rev-chat-avatar rev-chat-avatar--ai"><span class="icon">smart_toy</span></div> <div class="rev-chat-msg rev-chat-msg--ai"><h3>아스피린</h3><p>…</p></div> </div> </div> <form class="rev-chat-form rev-chat-form--arch" id="rev-chat-form"> <div class="rev-chat-bar"> <textarea id="rev-chat-input" class="rev-chat-input rev-chat-input--bar"></textarea> <button class="rev-chat-icobtn rev-chat-icobtn--bar"><span class="icon">attach_file</span></button> <button type="submit" class="rev-chat-send-btn"><span class="icon">send</span></button> </div> </form> </aside>
#rev-modal-sv-progress / #rev-modal-sv-result)
자가검증 페이지 모달과 동일 계열(.modal-card--progress + .modal-card--progress-live / .modal-card--sv-result)을 쓰되 ID는 충돌을 피해 rev- 접두사를 둡니다.
진행 모달은 .prog-live 안에 .prog-live__main(아이콘 + 진행바 + 기어 애니메이션)과 우측 .prog-rail(5단계 리스트)이 들어갑니다.
ds-*)
상단 서브 탭(.subtabs) → 검색 바(.ds-search-bar) → 카드 그리드(.ds-card-grid) → 페이지네이션(.ds-pager) 순서.
카드와 페이지 항목은 portal.js가 채웁니다.
.subtabs)<div class="subtabs" role="tablist" aria-label="데이터 유형"> <button role="tab" aria-pressed="true" aria-controls="ds-list" data-type="train">학습데이터</button> <button role="tab" aria-pressed="false" data-type="eval">평가데이터</button> <button role="tab" aria-pressed="false" data-type="api">API</button> </div>
.ds-search-bar)<div class="ds-search-bar" role="search"> <label class="ds-search-bar__field ds-search-bar__field--keyword"> <span class="icon ds-search-bar__search-ic">search</span> <input type="search" id="ds-q" class="ds-search-bar__input" placeholder="데이터명 또는 API명 검색" /> </label> <div class="ds-search-bar__date-wrap"> <span class="ds-search-bar__date-label">구축일자</span> <input type="date" id="ds-date-from" class="ds-search-bar__date" /> <span class="ds-search-bar__date-sep">~</span> <input type="date" id="ds-date-to" class="ds-search-bar__date" /> </div> <div class="ds-search-bar__btns"> <button class="btn btn--line btn--sm" id="ds-reset">초기화</button> <button class="btn btn--primary btn--sm" id="ds-search">조회</button> </div> </div>
<div class="ds-card-grid" id="ds-list" role="list" aria-live="polite"></div> <nav class="ds-pager" id="ds-pager" aria-label="페이지 이동"></nav>
wl-*)
.page-hero + .wl-jump 점프 내비 + 섹션별 .wl-block + 카드 그리드 .wl-grid > .wl-card 구성.
미디어형 카드는 .wl-card--media로 좌측에 파비콘을 둡니다.
<section class="page-hero"> <div class="container"> <h1 class="page-hero__title">시연용 <em>화면·링크</em> 목록</h1> <p class="page-hero__desc">…</p> <nav class="wl-jump" aria-label="섹션 바로가기"> <a href="#wl-common">공통·입구</a>… </nav> </div> </section> <section id="wl-common" class="wl-block"> <div class="wl-sec section-head"> <span class="section-head__eyebrow">공통</span> <h2 class="section-head__title">공통·입구</h2> <p class="section-head__desc">…</p> </div> <div class="wl-grid"> <a class="wl-card" href="portal_landing.html"> <span class="wl-card__label">일반 · 입구</span> <span class="wl-card__title">포털 랜딩</span> <span class="wl-card__meta">설명</span> <span class="wl-card__url">portal_landing.html</span> </a> <a class="wl-card wl-card--media" href="nedrug_local/index.html"> <span class="wl-card__top"> <img class="wl-card__portico" src="common/img/favicon.png" alt="" /> <span class="wl-card__stack">…</span> </span> </a> </div> </section>
모든 모달은 .modal-backdrop > .modal-card 구조이며, 카드 우측 상단에 button.modal-card__close(아이콘 close)를 둡니다.
하단 액션은 .modal-actions(2~4개 버튼)·자가검증 결과는 .modal-actions--sv-result입니다. 토스트는 MFDS.toast(msg, opts)로 우상단 .mfds-toast 스택에 표시됩니다.
.modal-card--sv-result)<div class="modal-backdrop" id="modal-result"> <div class="modal-card modal-card--wide modal-card--sv-result"> <button class="modal-card__close" id="btn-close-result"><span class="icon">close</span></button> <h3 class="modal-card__title modal-card__title--sv-result">자가검증 결과</h3> <div class="modal-card__body modal-card__body--sv-result" id="res-detail-root"></div> <div class="modal-actions modal-actions--sv-result"> <a class="btn btn--outline" href="portal_01_selfverify.html"><span class="icon icon--line">upload</span>보완 자료 재검증</a> <a class="btn btn--outline" href="portal_01b_review_editor.html"><span class="icon icon--line">edit_note</span>AI 검토서 편집기</a> <button class="btn btn--secondary"><span class="icon">download</span>검증결과 다운로드</button> <a class="btn btn--primary" href="portal_mypage.html"><span class="icon">monitoring</span>나의 민원 · 자가검증 현황</a> </div> </div> </div>
MFDS.toast)MFDS.toast('평균 2영업일 이내에 이메일로 회신됩니다.', { title: '신청이 접수되었습니다', icon: 'check_circle' }); MFDS.toast('법적 효력이 있는 해석은 공식 경로로 확인해 주세요.', { title: '안내', icon: 'info', type: 'info' }); MFDS.toast('필수 항목을 다시 검토한 뒤 제출해 주세요.', { title: '확인 필요', icon: 'warning', type: 'warn' });
모두 로컬 woff2 (common/fonts/material-symbols.css) 사용. <span class="icon">name</span>로 호출하며, 라인 가중치는 .icon--line을 추가합니다.
페이지별로 자주 쓰는 글리프를 함께 정리해 두었습니다.
data-page 맵
portal.js는 document.documentElement.getAttribute("data-page") 값으로 페이지별 스크립트를 분기합니다.
새 페이지를 만들 때는 반드시 <html data-page="…">을 지정하고, 본 표에 키를 추가하세요.
| 파일 | data-page |
주요 컴포넌트 |
|---|---|---|
index.html |
portal_worklist |
.page-hero · .wl-jump ·
.wl-block · .wl-grid ·
.wl-card / .wl-card--media
|
portal_landing.html |
portal_landing |
.hero + canvas#hero-dots ·
.svc-carousel ·
.svc (--teal/--purple/--green) ·
.feat-carousel · .feat
|
portal_landing2.html |
portal_landing2 |
.landing2-ambient · .landing2-hero ·
.landing2-grid ·
.landing2-tile (--sv/--info/--civil/--data) ·
.landing2-subs · .landing2-link
|
portal_landing3.html |
portal_landing3 (html.landing3 data-theme="light") |
landing2 컴포넌트 + 라이트 테마 오버라이드 · 하단 CTA(.landing2-hero__cta--below-grid) |
portal_login.html |
portal_login |
body.login-body · data-login-tab ·
.login-mode-bar ·
.login-card--portal/--review ·
.login-brand--portal/--reviewer ·
.login-shield · .login-status ·
.login-panel · .login-field ·
.login-input-wrap · .login-pw-toggle ·
.login-submit · .login-links ·
.login-notice
|
portal_01_selfverify_landing.html |
portal_01_selfverify_landing (body.svl-page) |
.svl-hero + .svl-orb ·
.svl-hero__stats + .svl-count ·
.svl-doc-grid ·
.svl-doc-card (--blue/--violet/--green/--soft) ·
.svl-process · .svl-step ·
.svl-feature-grid · .svl-cta ·
.svl-notice ·
.svl-btn-primary/-ghost
|
portal_01_selfverify.html |
portal_01_selfverify |
.sv-layout · .sv-proc.sv-proc-acc ·
.sv-main · .wizard-stepper ·
.step-panel · .radio-card ·
.dropzone · .action-row ·
모달 3종 (.modal-card--sv-steps ·
.modal-card--async-done ·
.modal-card--sv-result)
|
portal_mypage.html |
portal_01a_verify_status |
.data-head + .page-head ·
.page-kicker · .toolbar ·
.layout · .panel ·
table.u-table-list (7열) · .pager ·
aside.detail.verify-detail-source ·
.modal-card--detail-mobile.modal-card--verify-detail
|
portal_01b_review_editor.html |
portal_01b_review_editor (body.rev-body) |
.rev-workspace 3열
(.rev-left / .rev-center / .rev-right) ·
.rev-gutter--l/--r ·
.rev-rail 6탭 (ed/db/sum/tr/cmp/vis) ·
.rev-pane · .rev-proj-tree ·
.rev-status-cards · .rev-timeline ·
.rev-tr-board · .rev-cmp-shell ·
.rev-vis-table ·
.rev-chat-arch + .rev-chat-bar ·
#rev-modal-sv-progress ·
#rev-modal-sv-result ·
#modal-rev-base
|
portal_mypage.html |
portal_mypage |
.chat-head · .mypg-filter +
.mypg-filter__row · .panel ·
table.u-table-list (6열) · .pager ·
.modal-card--mypg-detail +
.mypg-detail-modal__title/__foot ·
#detail-modal-slot
|
portal_02_info_chat.html |
portal_02_info_chat |
.portal-flow-chat.portal-flow-chat--embed ·
.chat-area · .chat-wrap ·
.civil-panel-fixed + .civil-group-head ·
.choice-list + .choice ·
.chat-scroll ·
.turn + .avatar--ai + .bubble--ai ·
.compose +
.compose__row/__input/__send/__hint
|
portal_03_civil_initial.html |
portal_03_civil_initial |
info_chat과 동일 셸 + .civil-group-divider ·
다빈도 민원 + 전자증명서 발급 그룹
|
portal_04_data.html |
portal_06_data |
.subtabs (train/eval/api) ·
.ds-search-bar +
__field/__date-wrap/__btns ·
.ds-card-grid · .ds-pager
|
portal_styleguide.html |
portal_styleguide |
본 문서 · .sg-toc · .sg-section ·
.sg-block · .sg-demo ·
.sg-code · .sw-grid ·
.iconset
|
nedrug_local/index.html |
— |
의약품안전나라 임베드 데모 ·
#nedrug-chat-fab · #nedrug-ai-panel
(별도 chatbot/embed-chatbot.css)
|
editor/word_editor.html |
— |
portal_01b_review_editor의
iframe#rev-word-frame으로 임베드
|
신규 페이지 추가 시 ① <html lang="ko" data-page="고유키"> 지정 →
② 공통 에셋 3종(suit.css · material-symbols.css · portal.css) 링크 →
③ <portal-gnb> · <portal-footer> 사용 →
④ 본 문서의 해당 컴포넌트 마크업 복사 →
⑤ portal.js에 페이지별 분기 추가.
① 액센트는 브랜드 Blue로 통일하고, 레거시 --accent-teal/-purple/-green은 같은 값으로 매핑됩니다.
② 로그인 면만 --auth-portal-*(공식 Blue) / --auth-gun-*(Navy)으로 면을 구분합니다.
③ 상태 색은 .chip--ok/--bad/--run/--neutral과 verify-rv-table의 행 클래스(.is-ok/.is-warn/.is-bad)에서만 사용합니다.
④ 강조 그림자(--sh-glow-brand / --sh-glow)는 Primary 버튼·챗 전송 등 한정된 요소에만 씁니다.
/* 1) 액센트는 브랜드 Blue로 통일 */ :root{ --brand: #1a6bff; --brand-600: #2563eb; --accent: var(--brand); --accent-600: var(--brand-600); --accent-bg: var(--brand-50); --accent-border: var(--brand-200); } /* 2) 로그인 면 구분 — 포털 = 공식 Blue, 심사자 = Navy */ .login-body[data-login-tab="portal"] .login-brand--portal{ /* --auth-portal-900 / 700 / 500 그라데이션 */ } .login-body[data-login-tab="review"] .login-brand--reviewer{ /* --auth-gun-950 … 300 Navy 스케일 */ } /* 3) 상태 색은 chip / verify-rv-table 행에서만 사용 */ .chip--ok { color: #15803D; } .chip--bad { color: #dc2626; } .chip--run { color: var(--brand-600); } .chip--neutral { color: var(--muted); } /* 4) 강조 그림자 — Primary 버튼 / 챗 전송 등 한정 사용 */ .btn--primary { box-shadow: var(--sh-glow-brand); } .compose__send { box-shadow: var(--sh-glow); }
가장 자주 쓰이는 “브레드크럼 + chat-head + container 본문 + 푸터” 구조입니다. 헤더 아이콘과 제목·설명만 바꿔서 시작하세요.
<!doctype html> <html lang="ko" data-page="portal_xx_new"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" /> <meta name="theme-color" content="#ffffff" /> <title>새 페이지 — 의약품 산업지원 포털</title> <link rel="icon" href="common/img/favicon.png" type="image/png" /> <link rel="stylesheet" href="common/fonts/suit.css" /> <link rel="stylesheet" href="common/fonts/material-symbols.css" /> <link rel="stylesheet" href="common/css/portal.css" /> </head> <body> <a class="skip" href="#main">본문 바로가기</a> <portal-gnb></portal-gnb> <main id="main"> <section class="data-head" aria-labelledby="page-title"> <div class="container chat-head"> <nav class="breadcrumb" aria-label="위치"> <a href="portal_landing.html">홈</a> <span aria-hidden="true"><span class="icon icon--line">chevron_right</span></span> <span aria-current="page">새 페이지</span> </nav> <div class="chat-head__row" data-reveal> <span class="chat-head__icon" aria-hidden="true"><span class="icon">fact_check</span></span> <div> <h1 class="chat-head__title" id="page-title">새 페이지</h1> <p class="chat-head__sub">한 줄 설명</p> </div> </div> </div> </section> <div class="container" style="padding-top:var(--s6);padding-bottom:var(--s10)"> <!-- 본문: 본 스타일가이드의 컴포넌트 섹션을 복사해 사용 --> </div> </main> <portal-footer></portal-footer> <script defer src="common/js/portal.js"></script> </body></html>
심사관 전용 대시보드(portal_admin_dashboard.html)에서 사용되는 컴포넌트 모음. 클래스 prefix는
.ad-*이며, 현재는 페이지 상단 <style> 블록에 스코프되어 있습니다. 다른 어드민 화면에서 재사용할 경우
해당 CSS 블록을 복사하거나 portal.css로 이전해 주세요.
.ad-top) — 햄버거 · 타이틀 · 사용자 · 알림sticky 56px 헤더. 드롭다운(사용자/알림) 토글은 페이지 하단 스크립트 참고.
<header class="ad-top"> <button class="ad-top__menu" id="ad-side-toggle" aria-label="메뉴 토글"><span class="icon icon--line">menu</span></button> <div class="ad-top__brand"> <span class="ad-top__title">AI 심사포털</span> <span class="ad-top__chip">식품의약품안전처</span> </div> <div class="ad-top__spacer"></div> <div class="ad-top__user"><b>홍대식</b> | 의약품심사부 | 심사관</div> <!-- 사용자 / 알림 드롭다운 버튼은 .ad-top__icon-wrap 으로 감싸기 --> </header>
.ad-side) — 그룹 · 라벨 · 즐겨찾기 · 뱃지<aside class="ad-side"> <a class="ad-side__item is-active"><span class="icon icon--line">space_dashboard</span>대시보드</a> <div class="ad-side__group"> <p class="ad-side__label">AI 기본도구</p> <a class="ad-side__item"> <span class="icon icon--line">search</span>AI 검색 <span class="star is-on">★</span> <!-- 즐겨찾기 ON --> </a> <a class="ad-side__item"> <span class="icon icon--line">folder_managed</span>검토서 관리 <span class="badge">3</span> <!-- 카운트 뱃지 --> </a> </div> </aside>
.ad-stat-card) — 5컬럼 그리드, 클릭 시 필터
.is-active로 선택 강조. data-filter(all/in/prog/reject/done) · data-count로 JS와 연결.
아이콘 컬러 modifier: --all(파랑) · --in(파랑) · --prog(주황) · --reject(빨강) · --done(초록).
<div class="ad-status-grid"> <div class="ad-stat-card is-active" data-filter="all" data-count="63"> <div class="ad-stat-card__label">전체 <span class="ad-stat-card__icon ad-stat-card__icon--all"><span class="icon">description</span></span> </div> <div class="ad-stat-card__num">63</div> <div class="ad-stat-card__sub">건 담당 · 클릭해 필터</div> </div> <!-- in / prog / reject / done 카드 동일 구조 --> </div>
.ad-rev-table) — pill / aitag / dday
헤더는 가운데 정렬, 품목명 컬럼만 좌측 정렬. 행 단위 modifier:
tr[data-status](필터 매칭) / .is-hidden(필터 결과 숨김).
상태 pill: .ad-pill--prog/in/done/reject. D-day: .ad-dday--urgent/done/rej.
<table class="ad-rev-table"> <thead><tr> <th class="col-no">No</th> <th class="col-name">품목명 / 신청 유형</th> <th class="col-stat">진행상태</th> <th class="col-ai">AI 처리</th> <th class="col-d">마감</th> </tr></thead> <tbody id="ad-rev-list"> <tr data-status="prog"> <td class="col-no">01</td> <td class="col-name">트라스투주맙주 150mg <small>셀트리온 · <code>S-2026-KZQTPL</code> · 수입품목 허가</small> </td> <td><span class="ad-pill ad-pill--prog">작성중</span></td> <td><span class="ad-aitag">자가검증 92점</span></td> <td class="col-d"><span class="ad-dday ad-dday--urgent">D-1</span></td> </tr> </tbody> </table>
.ad-tools-list--six) / 2×2 (.ad-tools-list--stack)<!-- 1×6 (의약품 AI 심사 기본도구) --> <div class="ad-tools-list ad-tools-list--six"> <a class="ad-tool-card" href="portal_01b_review_editor.html?tab=ed&blank=1"> <span class="icon">description</span> <span class="ad-tool-card__name">AI 검토서 편집기</span> <span class="ad-tool-card__desc">검토서 본문 작성·편집</span> </a> </div> <!-- 2×2 row 카드 + 강조 --> <a class="ad-tool-card ad-tool-card--row ad-tool-card--featured"> <span class="ad-tool-card__icon-wrap"><span class="icon">auto_awesome</span></span> <span> <span class="ad-tool-card__name">자동 검토서 생성</span> <span class="ad-tool-card__desc">AI 초안 자동 작성</span> </span> </a>
.ad-right · .ad-mini-block)
섹션 간 자동 구분선: .ad-mini-block + .ad-mini-block { border-top:1px solid var(--border); padding-top:22px; }.
컨테이너: 막대(.ad-bar) / 추이 SVG(.ad-trend) / 마감 임박(.ad-urgent-list) / 활동(.ad-activity-list).
<aside class="ad-right"> <div class="ad-right__head"><h3>업무 현황</h3></div> <div class="ad-mini-block"> <div class="ad-bar"> <div class="ad-bar__row"> <div class="ad-bar__top"><span>접수완료</span><b>14건</b></div> <div class="ad-bar__track"><div class="ad-bar__fill ad-bar__fill--in" style="width:22.2%"></div></div> </div> </div> </div> <div class="ad-mini-block"><h4>마감 임박</h4> <div class="ad-urgent-list"> <div class="ad-urgent-item"> <span class="ad-urgent-item__d">D-1</span> <span class="ad-urgent-item__name">트라스투주맙주 · 셀트리온</span> </div> </div> </div> <div class="ad-mini-block"><h4>최근 활동</h4> <div class="ad-activity-list"> <div class="ad-activity-item"> <span class="ad-activity-item__icon ad-activity-item__icon--ok"><span class="icon">check</span></span> <div class="ad-activity-item__body"> <strong>아토르바스타틴정 20mg</strong> · LG화학 — 검토 완료 <em>10분 전 · S-2026-ULYWTI</em> </div> </div> </div> </div> </aside>
.ad-pop)
.ad-top__icon-wrap > button + .ad-pop 패턴. .is-open으로 표시.
알림 새 항목은 .ad-noti-item.is-new(파란 배경) + 제목 옆 .new-dot.
<!-- 사용자 메뉴 --> <div class="ad-top__icon-wrap"> <button class="ad-top__icon-btn" id="ad-user-btn"><span class="icon icon--line">person</span></button> <div class="ad-pop ad-pop--user"> <div class="ad-pop__user-head"> <div class="ad-pop__user-name">홍대식 심사관</div> <div class="ad-pop__user-meta">의약품심사부 · ID: hong.ds@mfds.go.kr</div> </div> <div class="ad-pop__list"> <a class="ad-pop__item"><span class="icon icon--line">badge</span>마이페이지</a> <div class="ad-pop__sep"></div> <a class="ad-pop__item ad-pop__item--danger"><span class="icon icon--line">logout</span>로그아웃</a> </div> </div> </div> <!-- 알림 새 항목 --> <div class="ad-noti-item is-new"> <div class="ad-noti-item__icon ad-noti-item__icon--urgent"><span class="icon">priority_high</span></div> <div class="ad-noti-item__body"> <div class="ad-noti-item__title">처리 마감 임박 (D-1)<span class="new-dot"></span></div> <div class="ad-noti-item__desc">트라스투주맙주 · 셀트리온</div> </div> </div>
.adm-*)
portal_admin_login.html 단독 페이지 전용. portal.css의 .login-*과 분리된 .adm-* 네임스페이스.
로고는 카드 외부 상단, 카드 내부에는 단일 폼, 카드 아래 4개 시스템 바로가기 pill (AI심사포털만 활성).
<body class="adm-body"> <div class="adm-wrap"> <!-- 카드 외부 상단 로고 --> <p class="adm-logo"><img src="common/img/mfds_logo.png" alt="식품의약품안전처" /></p> <div class="adm-card"> <h1 class="adm-card__title">내부 행정포털시스템</h1> <p class="adm-card__sub">식품의약품안전처 · 내부 공무원 전용</p> <form> <div class="adm-field"> <label for="uid-admin">아이디</label> <div class="adm-input-wrap"> <span class="icon icon--line">badge</span> <input id="uid-admin" type="text" placeholder="공무원 ID" /> </div> </div> <button class="adm-submit">로그인</button> </form> </div> <!-- 카드 외부 하단 시스템 바로가기 (AI심사포털만 활성) --> <div class="adm-divider"><span>연계 시스템 바로가기</span></div> <div class="adm-systems"> <span class="adm-systems__btn" aria-disabled="true">의약품 통합정보시스템</span> <a class="adm-systems__btn adm-systems__btn--primary" href="portal_admin_dashboard.html">AI심사포털</a> </div> </div> </body>