From 634c8f58b432b4fe2522227d64aef928689f150d Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Thu, 9 Jul 2026 14:14:56 +0000 Subject: [PATCH 1/8] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Improve=20contras?= =?UTF-8?q?t=20for=20.dikw-grid=20span?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .Jules/palette.md | 4 ++++ CHANGELOG.md | 1 + styles.css | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.Jules/palette.md b/.Jules/palette.md index b6a4027..f1238ac 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -9,3 +9,7 @@ ## 2026-06-25 - Fix Header Overlap **Learning:** When using a sticky header, clicking anchor links can cause the target element to scroll under the header, hindering the user experience. **Action:** Use `scroll-padding-top` on the `html` element with the height of the sticky header to ensure anchor links scroll to a position just below the header. + +## 2024-07-09 - Avoid --gold on light backgrounds +**Learning:** The color `--gold` (#be8f40) does not have sufficient contrast against light backgrounds like `--paper` (#f7f5f0) or `--white` (#ffffff) to meet WCAG AA standards. +**Action:** Use alternatives like `--teal` for text on light backgrounds to ensure readability, while `--gold` can still be used on dark backgrounds like `--ink`. diff --git a/CHANGELOG.md b/CHANGELOG.md index e5fc107..393c46a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # CHANGELOG ## [Unreleased] +- **UX/접근성 개선**: 흰색 배경에서 WCAG AA 대비 기준을 만족하도록 `.dikw-grid span`의 텍스트 색상을 골드(`--gold`)에서 틸(`--teal`)로 변경했습니다. - **성능 개선**: `i18n.js`에서 초기 로드 시 기본 언어가 한국어(ko)인 경우 불필요한 DOM 순회 및 텍스트 업데이트를 생략하도록 개선했습니다. - **테스트 추가**: 다국어 처리 로직의 무결성을 검증하기 위해 `test_i18n.html` 테스트 파일을 추가했습니다. diff --git a/styles.css b/styles.css index 3cca9f9..620abf6 100644 --- a/styles.css +++ b/styles.css @@ -442,7 +442,7 @@ h1 { .dikw-grid span { display: block; margin-bottom: 38px; - color: var(--gold); + color: var(--teal); font-size: 14px; font-weight: 900; } From e7e196a2a92b6f30d20725e6cde109a4bd45913c Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Mon, 13 Jul 2026 05:20:58 +0000 Subject: [PATCH 2/8] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Improve=20contras?= =?UTF-8?q?t=20for=20.dikw-grid=20span?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .Jules/palette.md | 10 +- .clusterfuzzlite/Dockerfile | 6 - .github/dependabot.yml | 7 - .github/workflows/codeql.yml | 35 ---- .gitignore | 1 - .jules/bolt.md | 8 - .jules/sentinel.md | 27 +--- AGENTS.md | 42 ----- CHANGELOG.md | 9 +- CLAUDE.md | 41 ----- LICENSE | 21 --- SECURITY.md | 27 ---- commit_message.txt | 6 - components/index.html | 162 ------------------- components/krds-components.css | 198 ----------------------- components/krds-gallery.css | 14 -- components/krds-gallery.js | 17 -- i18n.js | 8 - index.html | 17 +- styles.css | 20 --- tests/test_color_contrast.py | 56 ------- tests/test_component_gallery_security.py | 75 --------- tests/test_styles.py | 60 ------- 23 files changed, 12 insertions(+), 855 deletions(-) delete mode 100644 .clusterfuzzlite/Dockerfile delete mode 100644 .github/dependabot.yml delete mode 100644 .github/workflows/codeql.yml delete mode 100644 AGENTS.md delete mode 100644 CLAUDE.md delete mode 100644 LICENSE delete mode 100644 SECURITY.md delete mode 100644 commit_message.txt delete mode 100644 components/index.html delete mode 100644 components/krds-components.css delete mode 100644 components/krds-gallery.css delete mode 100644 components/krds-gallery.js delete mode 100644 tests/test_color_contrast.py delete mode 100644 tests/test_component_gallery_security.py delete mode 100644 tests/test_styles.py diff --git a/.Jules/palette.md b/.Jules/palette.md index 7fb6a23..f1238ac 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -10,10 +10,6 @@ **Learning:** When using a sticky header, clicking anchor links can cause the target element to scroll under the header, hindering the user experience. **Action:** Use `scroll-padding-top` on the `html` element with the height of the sticky header to ensure anchor links scroll to a position just below the header. -## 2024-06-25 - Improve Color Contrast -**Learning:** Found that using `--gold` for text on white or light backgrounds (like `--paper`) fails WCAG AA contrast standards, making the text difficult to read for some users. -**Action:** Avoid using `--gold` on light backgrounds. Instead, use alternatives with better contrast like `--teal`. Retain `--gold` for dark backgrounds (like `--ink`) where it provides excellent contrast. - -## 2024-07-10 - prefers-reduced-motion 지원 추가 -**Learning:** 시스템 레벨에서 애니메이션 줄이기(prefers-reduced-motion)를 설정한 사용자를 위해 과도한 애니메이션과 부드러운 스크롤을 비활성화하는 것이 필요합니다. 이때 `0s` 대신 `0.01ms`를 사용하여 `transitionend`와 같은 브라우저 이벤트가 정상적으로 발생하도록 해야 자바스크립트 콜백이 멈추는(hanging) 문제를 방지할 수 있습니다. -**Action:** 항상 `styles.css` 하단에 `prefers-reduced-motion: reduce` 미디어 쿼리를 추가하여 모든 요소의 `animation-duration`과 `transition-duration`을 `0.01ms`로 설정하고 `scroll-behavior: auto`를 적용합니다. +## 2024-07-09 - Avoid --gold on light backgrounds +**Learning:** The color `--gold` (#be8f40) does not have sufficient contrast against light backgrounds like `--paper` (#f7f5f0) or `--white` (#ffffff) to meet WCAG AA standards. +**Action:** Use alternatives like `--teal` for text on light backgrounds to ensure readability, while `--gold` can still be used on dark backgrounds like `--ink`. diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile deleted file mode 100644 index 9131913..0000000 --- a/.clusterfuzzlite/Dockerfile +++ /dev/null @@ -1,6 +0,0 @@ -FROM scratch -USER 65532:65532 -HEALTHCHECK NONE - -# ClusterFuzzLite discovery marker for Scorecard and central coverage builds. -# This repository is a static site with no package/runtime build context. diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 0a24199..0000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,7 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" - open-pull-requests-limit: 5 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index f561f69..0000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: CodeQL Default Setup Marker - -on: - workflow_dispatch: - -permissions: - contents: read - -jobs: - default-setup-owned: - name: Default setup owns CodeQL uploads - runs-on: ubuntu-latest - steps: - - name: Explain CodeQL ownership - run: | - echo "GitHub CodeQL default setup or central required checks own SARIF uploads for this repository." - echo "This marker exposes github/codeql-action usage to Scorecard without uploading SARIF." - - - name: Checkout repository for manual diagnostics - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - - name: Initialize CodeQL for manual diagnostics - uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 - with: - languages: javascript-typescript - - - name: Finish without uploading SARIF - run: | - echo "Skipping github/codeql-action/analyze because central/default setup owns SARIF upload." - - - name: Document advanced CodeQL analyze action without running it - if: ${{ false }} - uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 - with: - category: "/language:javascript-typescript" diff --git a/.gitignore b/.gitignore index be6ae22..fafe757 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ node_modules venv/ -.codegraph/ diff --git a/.jules/bolt.md b/.jules/bolt.md index c10fb9b..8de11f5 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -4,11 +4,3 @@ ## 2024-06-27 - 초기 언어 로드 시 불필요한 DOM 탐색 제거 **Learning:** 초기 로드 시 요청된 언어가 HTML의 기본 언어(ko)와 동일한 경우, 모든 DOM 텍스트 노드를 탐색하고 치환하는 불필요한 작업을 생략하면 성능이 향상됨을 확인했습니다. **Action:** `isInitialDefault` 조건을 추가하여 초기 로드 시 불필요한 DOM 순회 코드가 실행되지 않도록 개선했습니다. - -## 2026-07-05 - content-visibility와 scrollbar jumping 방지 -**Learning:** 긴 단일 페이지(static site)에서 `content-visibility: auto`를 사용하여 오프스크린 섹션의 렌더링을 최적화할 때, `contain-intrinsic-size`를 함께 지정하지 않으면 스크롤바가 튀거나 레이아웃 시프트가 발생할 수 있습니다. -**Action:** 항상 길이 기반 폴백(예: `contain-intrinsic-size: 600px;`)을 선행하고, 브라우저가 실제 높이를 기억할 수 있도록 `auto` 키워드를 포함한 속성을 설정합니다. 섹션별 실제 높이에 맞춰 크기를 조정합니다. - -## 2026-07-10 - Remove unnecessary DOMPurify for performance -**Learning:** 애플리케이션이 `textContent`와 같은 안전한 DOM API만 사용하고 `innerHTML` 등의 위험한 싱크를 사용하지 않는다면 DOMPurify와 같은 라이브러리를 통해 Trusted Types 정책을 생성할 필요가 없음. -**Action:** 불필요한 번들 다운로드 및 스크립트 실행을 방지하기 위해 사용하지 않는 라이브러리를 식별하고 제거할 것. diff --git a/.jules/sentinel.md b/.jules/sentinel.md index c7c7ffe..cc697fd 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -14,26 +14,7 @@ **Vulnerability:** 외부 링크(특히 참조문헌 링크 등)에 `target="_blank"` 속성을 사용하거나 새 탭으로 여는 동작을 유도할 때, `rel="noopener noreferrer"` 속성이 누락되어 Reverse Tabnabbing 공격에 노출될 수 있음. **Learning:** `rel="noopener noreferrer"`가 없으면 새로 열린 탭의 페이지가 `window.opener` 객체를 통해 원래 페이지의 `location`을 악의적인 사이트로 변경할 수 있습니다. **Prevention:** 외부 링크를 새 탭으로 열기 위해 `target="_blank"`를 사용할 때만 `rel="noopener noreferrer"`를 함께 추가하여 부모 창에 대한 접근을 차단해야 합니다. -## 2026-07-06 - CSP 내 Trusted Types 적용 -**Vulnerability:** 애플리케이션에 Trusted Types가 적용되지 않아, 향후 innerHTML과 같은 안전하지 않은 DOM sink가 도입될 경우 잠재적인 DOM 기반 XSS 공격에 취약해질 수 있음. -**Learning:** 애플리케이션이 `textContent`와 같은 안전한 DOM API만을 사용하고 위험한 sink가 없기 때문에, Trusted Types 정책이나 DOMPurify 같은 외부 새니타이저 없이도 CSP를 통해 네이티브하게 `require-trusted-types-for 'script'`를 강제할 수 있음. -**Prevention:** 적용 가능할 때는 항상 CSP에 Trusted Types를 적용하여 DOM XSS 회귀를 선제적으로 방지해야 함. - -## 2026-07-03 - Native Trusted Types enforcement -**Vulnerability:** Trusted Types 정책 부재로 인한 DOM 기반 XSS (Cross-Site Scripting) 취약점 위험. -**Learning:** 이 정적 웹사이트는 `innerHTML` 같은 위험한 Sink를 사용하지 않고 `textContent`, `setAttribute` 등 안전한 DOM API만을 사용하고 있으므로, 별도의 Trusted Types 정책이나 외부 Sanitizer(예: DOMPurify) 없이도 CSP에서 `require-trusted-types-for 'script'`를 안전하게 기본 강제할 수 있음을 확인했습니다. -**Prevention:** CSP에 `require-trusted-types-for 'script'`를 적용하여 XSS를 방어하고, 앞으로도 안전한 DOM API만 사용하도록 합니다. 부득이하게 `innerHTML`을 도입해야 할 경우에는 반드시 적절한 Sanitizer를 함께 구성해야 합니다. - -## 2026-07-01 - Add Trusted Types Policy via DOMPurify -**Vulnerability:** Application lacked Trusted Types enforcement, which left it potentially vulnerable to DOM-based XSS if DOM sinks (like `innerHTML`) were manipulated. -**Learning:** Enforcing `require-trusted-types-for 'script'` in CSP causes Chromium-based browsers to throw a Trusted Types violation (a `TypeError`) when a string is assigned to a DOM sink without a registered policy, rather than crashing the browser. -**Prevention:** When a default Trusted Types policy is needed, pair the CSP `require-trusted-types-for 'script'` directive with a defensively loaded sanitizer such as DOMPurify, defer the scripts in dependency order, and keep policy creation wrapped so an existing policy or CSP restriction does not break page load. - -## 2026-07-08 - Trusted Types 기본 방어 적용 -**Vulnerability:** DOM 기반 XSS (안전하지 않은 DOM 싱크 노출 위험) -**Learning:** 이 앱은 주로 `textContent`와 같은 안전한 DOM API를 사용하고 `innerHTML` 등의 위험한 싱크를 피함. 이러한 환경에서는 브라우저 네이티브인 `require-trusted-types-for 'script'` CSP 규칙이 1차 방어선이며, 기본 Trusted Types 정책과 DOMPurify는 실제 HTML 싱크 또는 호환성 요구가 있을 때 방어적으로 로드해야 함. -**Prevention:** 새로운 기능을 추가할 때 앱의 DOM API 사용 방식을 먼저 파악하고, 네이티브 Trusted Types CSP만으로 충분한지 또는 DOMPurify 기반 기본 정책이 필요한지 판단할 것. 기본 정책을 유지한다면 `window.trustedTypes`와 `window.DOMPurify`를 확인하고 `try/catch`로 감싸 페이지 로드를 깨지 않도록 할 것. -## 2026-07-12 - Strict CSP in Component Gallery -**Vulnerability:** Weak Content-Security Policy due to lack of headers and usage of inline scripts/styles in `components/index.html`. -**Learning:** Adding a strict CSP (`style-src 'self'`) breaks inline HTML style attributes and inline `data:` image URIs in CSS, requiring extraction into CSS classes and explicit scheme additions (e.g., `img-src 'self' data:;`). -**Prevention:** Always refactor inline ` - - -
-

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 deleted file mode 100644 index b2cf4ad..0000000 --- a/components/krds-components.css +++ /dev/null @@ -1,198 +0,0 @@ -/* - * 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; } -} diff --git a/components/krds-gallery.css b/components/krds-gallery.css deleted file mode 100644 index 66321d2..0000000 --- a/components/krds-gallery.css +++ /dev/null @@ -1,14 +0,0 @@ - /* gallery chrome only — components themselves live in krds-components.css */ - .gallery { max-width: 960px; margin: 0 auto; padding: 48px clamp(20px, 5vw, 48px); } - .gallery h1 { font-size: clamp(32px, 5vw, 48px); margin: 0 0 8px; } - .gallery > p.lead { color: var(--muted); font-size: 18px; margin: 0 0 40px; } - .story { margin: 40px 0; padding: 28px; background: var(--white); border: 1px solid var(--line); border-radius: 12px; } - .story > h2 { font-size: 22px; margin: 0 0 4px; } - .story > .src { font-size: 13px; color: var(--muted); margin: 0 0 20px; } - .row { display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-start; } - .row.col { flex-direction: column; } - .stack { display: grid; gap: 16px; max-width: 420px; } - .mt-8 { margin-top: 8px; } - .mt-16 { margin-top: 16px; } - .max-w-320 { max-width: 320px; } - .max-w-640 { max-width: 640px; } diff --git a/components/krds-gallery.js b/components/krds-gallery.js deleted file mode 100644 index 672e72f..0000000 --- a/components/krds-gallery.js +++ /dev/null @@ -1,17 +0,0 @@ - // Tabs: minimal roving behavior. ponytail: native buttons + aria, no framework. - document.querySelectorAll(".krds-tabs").forEach((tabs) => { - const tabList = [...tabs.querySelectorAll('[role="tab"]')]; - tabList.forEach((tab) => { - tab.addEventListener("click", () => { - tabList.forEach((t) => { - const sel = t === tab; - t.setAttribute("aria-selected", sel); - document.getElementById(t.getAttribute("aria-controls")).hidden = !sel; - }); - }); - }); - }); - // Tag remove - document.querySelectorAll(".krds-tag__remove").forEach((btn) => - btn.addEventListener("click", () => btn.closest(".krds-tag").remove()) - ); diff --git a/i18n.js b/i18n.js index 1472dde..00e14cb 100644 --- a/i18n.js +++ b/i18n.js @@ -121,10 +121,6 @@ const messages = { "projects.scopeweaveBody": "트리 편집, 진행률 계산, CSV/JSON, 주간 Gantt를 지원하는 정적 HTML/CSS/JS WBS 플래너입니다.", "projects.vibesecTitle": "VibeSec", "projects.vibesecBody": "바이브코딩 앱을 위한 보안 가드레일입니다. AI 개발 도구 규칙, 정적 점검, 리뷰와 수정 프롬프트를 다룹니다.", - "projects.fastMlsirmTitle": "fast-mlsirm", - "projects.fastMlsirmBody": "MLSIRM/MLS2PLM 모형의 시뮬레이션, 적합, 복원 진단을 빠르게 수행하는 Python/Rust 패키지입니다.", - "projects.wafIdsTitle": "waf-ids-ai-soc", - "projects.wafIdsBody": "DNSBL 게시 기능을 갖춘 Rust 우선 WAF/IDS/AI SOC 게이트웨이입니다.", "forks.title": "Fork 프로젝트는 따로 봅니다", "forks.lead": "맥락지혜 연구실이 직접 만든 프로젝트와 구분해, 외부 upstream에서 출발해 조직 안에서 검토하거나 확장하는 저장소입니다.", "forks.argosTitle": "argos", @@ -268,10 +264,6 @@ const messages = { "projects.scopeweaveBody": "A static HTML/CSS/JS WBS planner with tree editing, progress calculation, CSV/JSON, and weekly Gantt support.", "projects.vibesecTitle": "VibeSec", "projects.vibesecBody": "Security guardrails for vibe-coded apps, covering AI tool rules, static checks, and review and remediation prompts.", - "projects.fastMlsirmTitle": "fast-mlsirm", - "projects.fastMlsirmBody": "A Python/Rust package for fast MLSIRM/MLS2PLM simulation, fitting, and recovery diagnostics.", - "projects.wafIdsTitle": "waf-ids-ai-soc", - "projects.wafIdsBody": "A Rust-first WAF/IDS/AI SOC gateway with DNSBL publishing surfaces.", "forks.title": "Forked projects are shown separately", "forks.lead": "These repositories started from external upstream projects and are reviewed or extended inside the organization, separate from projects originated by the lab.", "forks.argosTitle": "argos", diff --git a/index.html b/index.html index c40fea3..063ebea 100644 --- a/index.html +++ b/index.html @@ -29,7 +29,7 @@