diff --git a/.gitignore b/.gitignore index fafe757..be6ae22 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules venv/ +.codegraph/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..591bbf1 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 ContextualWisdomLab + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/components/index.html b/components/index.html new file mode 100644 index 0000000..5f88146 --- /dev/null +++ b/components/index.html @@ -0,0 +1,190 @@ + + + + + + KRDS Component Library · 맥락지혜 연구실 + + + + + + +
+

KRDS Component Library

+

맥락지혜 연구실 · 브랜드 토큰(:root) 기반 바닐라 구현 · Figma 6Kx2gzcduyIyLGN3Ck2rtm 대응

+ +
+

Button

+

.krds-btn · Figma Action/Button 36:67

+
+ + + + + + +
+
+ + + +
+
+ +
+

Text Input

+

.krds-field · Figma Input/Text Input 62:22

+
+ +
+ + + 올바른 이메일 형식이 아닙니다. +
+ +
+
+ +
+

Checkbox / Radio

+

.krds-choice · Figma Selection 38:16 / 38:27

+
+ + + + + +
+
+ +
+

Select

+

.krds-select · Figma Selection/Select 56:29

+ +
+ +
+

Badge & Tag

+

.krds-badge / .krds-tag · Figma 58:48 / 58:54

+
+ Default + Info + Success + Warning + Danger +
+
+ 필터 A + 필터 B +
+
+ +
+

Tabs

+

.krds-tabs · Figma Layout/Tabs 59:11

+
+
+ + + +
+
개요 패널 내용입니다.
+ + +
+
+ +
+

Accordion

+

.krds-accordion (native details) · Figma Layout/Accordion 59:21

+
+ DIKW란 무엇인가 +
자료·정보·지식·지혜를 점검하는 질문 체계입니다.
+
+
+ 왜 위계가 아닌가 +
맥락에 따라 순환·재구성되기 때문입니다.
+
+
+ +
+

Pagination

+

.krds-pagination · Figma Navigation/Pagination 58:25

+ +
+ +
+

Alert

+

.krds-alert · Figma Layout/Critical Alert

+
+
안내 정보 메시지입니다.
+
완료 성공적으로 처리되었습니다.
+
주의 확인이 필요합니다.
+
오류 처리에 실패했습니다.
+
+
+ +
+

Toast

+

.krds-toast · Figma Feedback/Toast 56:46

+
저장되었습니다.
+
+
+ + + + diff --git a/components/krds-components.css b/components/krds-components.css new file mode 100644 index 0000000..b2cf4ad --- /dev/null +++ b/components/krds-components.css @@ -0,0 +1,198 @@ +/* + * KRDS Component Library — vanilla, token-bound implementation + * Mirrors the Figma design system (file 6Kx2gzcduyIyLGN3Ck2rtm). + * Consumes the site's brand tokens from styles.css (:root) and adds the + * KRDS status/semantic tokens the components need. No build step, no framework. + */ + +:root { + /* status tokens — KRDS-standard, not present in brand palette */ + --krds-info: #256ef4; + --krds-success: #2e7d32; + --krds-warning: #b45309; + --krds-danger: #c0392b; + --krds-info-bg: #eef4ff; + --krds-success-bg: #eaf5eb; + --krds-warning-bg: #fdf3e7; + --krds-danger-bg: #fdecea; + /* component tokens derived from brand palette */ + --krds-radius: 4px; + --krds-radius-lg: 12px; + --krds-gap: 8px; + --krds-disabled: color-mix(in srgb, var(--muted) 40%, var(--white)); +} + +.krds-scope { + color: var(--ink); + font-family: "Pretendard CWL", "Apple SD Gothic Neo", "Noto Sans KR", system-ui, sans-serif; + line-break: keep-all; + word-break: keep-all; +} + +/* Shared focus ring — matches styles.css convention (teal, 2px, offset 2px) */ +.krds-scope :is(button, a, input, select, textarea, summary, [tabindex]):focus-visible { + outline: 2px solid var(--teal); + outline-offset: 2px; +} + +/* ---------- Button ---------- */ +.krds-btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: var(--krds-gap); + min-height: 48px; + padding: 12px 20px; + border: 1px solid var(--ink); + border-radius: var(--krds-radius); + background: var(--white); + color: var(--ink); + font: inherit; + font-size: 15px; + font-weight: 800; + cursor: pointer; + transition: opacity 0.2s, background 0.2s; +} +.krds-btn:hover { opacity: 0.85; } +.krds-btn:active { opacity: 0.7; } +.krds-btn--primary { background: var(--ink); color: var(--white); } +.krds-btn--secondary { background: var(--white); color: var(--ink); } +.krds-btn--tertiary { border-color: transparent; background: transparent; text-decoration: underline; } +.krds-btn--danger { background: var(--krds-danger); border-color: var(--krds-danger); color: var(--white); } +.krds-btn--sm { min-height: 36px; padding: 8px 14px; font-size: 14px; } +.krds-btn--lg { min-height: 56px; padding: 16px 28px; font-size: 16px; } +.krds-btn[disabled], .krds-btn[aria-disabled="true"] { + opacity: 1; + cursor: not-allowed; + background: var(--krds-disabled); + border-color: var(--krds-disabled); + color: var(--muted); +} +.krds-btn--loading { position: relative; color: transparent; pointer-events: none; } +.krds-btn--loading::after { + content: ""; position: absolute; width: 16px; height: 16px; + border: 2px solid currentColor; border-top-color: transparent; border-radius: 50%; + color: var(--white); animation: krds-spin 0.7s linear infinite; +} + +/* ---------- Text field (input / textarea) ---------- */ +.krds-field { display: grid; gap: 6px; } +.krds-field__label { font-size: 14px; font-weight: 700; } +.krds-field__label .krds-req { color: var(--krds-danger); margin-left: 2px; } +.krds-field__control { + min-height: 48px; + padding: 12px 14px; + border: 1px solid var(--line); + border-radius: var(--krds-radius); + background: var(--white); + font: inherit; + font-size: 15px; + color: var(--ink); +} +.krds-field__control::placeholder { color: var(--muted); } +.krds-field__control:hover { border-color: var(--muted); } +.krds-field__help { font-size: 13px; color: var(--muted); } +.krds-field__error { font-size: 13px; color: var(--krds-danger); font-weight: 700; } +.krds-field--error .krds-field__control { border-color: var(--krds-danger); } +.krds-field__control[disabled] { background: var(--paper); color: var(--muted); cursor: not-allowed; } + +/* ---------- Checkbox / Radio (native inputs, accent-color) ---------- */ +.krds-choice { display: inline-flex; align-items: center; gap: 10px; font-size: 15px; cursor: pointer; } +.krds-choice input { width: 20px; height: 20px; accent-color: var(--teal); cursor: pointer; } +.krds-choice input[disabled] { cursor: not-allowed; } +.krds-choice:has(input[disabled]) { color: var(--muted); cursor: not-allowed; } + +/* ---------- Select ---------- */ +.krds-select { + min-height: 48px; + padding: 12px 40px 12px 14px; + border: 1px solid var(--line); + border-radius: var(--krds-radius); + background: var(--white) + url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E%3Cpath d='M4 6l4 4 4-4' fill='none' stroke='%234f5968' stroke-width='2'/%3E%3C/svg%3E") + no-repeat right 14px center; + font: inherit; font-size: 15px; color: var(--ink); appearance: none; cursor: pointer; +} + +/* ---------- Badge ---------- */ +.krds-badge { + display: inline-flex; align-items: center; gap: 4px; + padding: 2px 8px; border-radius: 999px; + font-size: 12px; font-weight: 800; line-height: 1.6; + background: var(--line); color: var(--ink); +} +.krds-badge--info { background: var(--krds-info-bg); color: var(--krds-info); } +.krds-badge--success { background: var(--krds-success-bg); color: var(--krds-success); } +.krds-badge--warning { background: var(--krds-warning-bg); color: var(--krds-warning); } +.krds-badge--danger { background: var(--krds-danger-bg); color: var(--krds-danger); } + +/* ---------- Tag (removable) ---------- */ +.krds-tag { + display: inline-flex; align-items: center; gap: 6px; + padding: 4px 10px; border: 1px solid var(--line); border-radius: 999px; + font-size: 13px; background: var(--white); +} +.krds-tag__remove { + border: 0; background: none; padding: 0; cursor: pointer; + font-size: 14px; line-height: 1; color: var(--muted); +} +.krds-tag__remove:hover { color: var(--ink); } + +/* ---------- Tabs ---------- */ +.krds-tabs__list { display: flex; gap: 4px; border-bottom: 2px solid var(--line); } +.krds-tab { + border: 0; background: none; padding: 12px 16px; cursor: pointer; + font: inherit; font-size: 15px; font-weight: 700; color: var(--muted); + border-bottom: 2px solid transparent; margin-bottom: -2px; +} +.krds-tab[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--teal); } +.krds-tabpanel { padding: 20px 4px; } +.krds-tabpanel[hidden] { display: none; } + +/* ---------- Accordion (native details/summary) ---------- */ +.krds-accordion { border: 1px solid var(--line); border-radius: var(--krds-radius); } +.krds-accordion > summary { + list-style: none; cursor: pointer; padding: 16px 18px; + font-weight: 700; display: flex; justify-content: space-between; align-items: center; +} +.krds-accordion > summary::-webkit-details-marker { display: none; } +.krds-accordion > summary::after { content: "+"; font-size: 20px; color: var(--muted); } +.krds-accordion[open] > summary::after { content: "\2212"; } +.krds-accordion__body { padding: 0 18px 18px; color: var(--muted); } + +/* ---------- Pagination ---------- */ +.krds-pagination { display: flex; gap: 4px; align-items: center; list-style: none; padding: 0; } +.krds-pagination a, .krds-pagination span { + display: inline-flex; align-items: center; justify-content: center; + min-width: 40px; min-height: 40px; padding: 0 8px; + border-radius: var(--krds-radius); text-decoration: none; color: var(--ink); font-weight: 700; +} +.krds-pagination a:hover { background: var(--paper); } +.krds-pagination [aria-current="page"] { background: var(--ink); color: var(--white); } +.krds-pagination [aria-disabled="true"] { color: var(--krds-disabled); pointer-events: none; } + +/* ---------- Alert ---------- */ +.krds-alert { + display: flex; gap: 12px; padding: 14px 16px; + border-radius: var(--krds-radius); border-left: 4px solid var(--muted); + background: var(--paper); font-size: 15px; +} +.krds-alert--info { border-left-color: var(--krds-info); background: var(--krds-info-bg); } +.krds-alert--success { border-left-color: var(--krds-success); background: var(--krds-success-bg); } +.krds-alert--warning { border-left-color: var(--krds-warning); background: var(--krds-warning-bg); } +.krds-alert--danger { border-left-color: var(--krds-danger); background: var(--krds-danger-bg); } +.krds-alert__title { font-weight: 800; } + +/* ---------- Toast ---------- */ +.krds-toast { + display: inline-flex; align-items: center; gap: 12px; + padding: 14px 18px; border-radius: var(--krds-radius-lg); + background: var(--ink); color: var(--white); box-shadow: var(--shadow); font-size: 15px; +} +.krds-toast__action { border: 0; background: none; color: var(--gold); font-weight: 800; cursor: pointer; } + +@keyframes krds-spin { to { transform: rotate(360deg); } } + +@media (prefers-reduced-motion: reduce) { + .krds-btn--loading::after { animation: none; } +}