@infomind-ux/infoux-mcp 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -2
- package/bin/server.js +236 -0
- package/data/art-direction.json +460 -0
- package/data/contract.md +1 -0
- package/data/manifest.json +41 -5
- package/data/presets/coast-teal.json +366 -0
- package/data/presets/market-terracotta.json +366 -0
- package/data/presets/trust-blue.json +368 -0
- package/data/profiles.json +157 -0
- package/data/references/art-direction.md +206 -0
- package/data/references/krds-components.md +1 -1
- package/data/references/krds-tokens.md +3 -2
- package/data/references/project-profiles.md +84 -19
- package/data/references/release-checklist.md +80 -0
- package/data/references/trade-off-rules.md +66 -0
- package/data/rules.json +134 -0
- package/data/snippets/hero-bleed.md +45 -0
- package/data/snippets/section-media.md +61 -0
- package/data/snippets/signature.md +107 -0
- package/data/tokens.css +157 -3
- package/data/workflows/design-page.md +6 -5
- package/data/workflows/init-project.md +18 -0
- package/package.json +1 -1
package/data/rules.json
CHANGED
|
@@ -775,6 +775,140 @@
|
|
|
775
775
|
"note": "모션 최소화 선호 시 정지"
|
|
776
776
|
}
|
|
777
777
|
]
|
|
778
|
+
},
|
|
779
|
+
{
|
|
780
|
+
"id": "R-23",
|
|
781
|
+
"category": "html",
|
|
782
|
+
"summary": "가짜 콘텐츠 금지 — lorem ipsum·자리 채움 문구·placeholder 핫링크",
|
|
783
|
+
"severity": "error",
|
|
784
|
+
"enforcement": [
|
|
785
|
+
"check-violations.js"
|
|
786
|
+
],
|
|
787
|
+
"lang": "html",
|
|
788
|
+
"rationale": "시안·납품물에 채움 콘텐츠가 남으면 검수 신뢰가 깨지고, placeholder 이미지 핫링크는 외부 서비스 의존이라 납품 self-contained 정책과도 이중으로 충돌한다. 콘텐츠가 미확정이면 task contract의 contentSources로 실제 용어·문서 출처를 먼저 확보하고, 이미지는 실제 자산 경로 또는 실제 비율의 data-uri 표기를 쓴다.",
|
|
789
|
+
"bad": [
|
|
790
|
+
{
|
|
791
|
+
"code": "<p>Lorem ipsum dolor sit amet</p>",
|
|
792
|
+
"note": "영문 채움 텍스트"
|
|
793
|
+
},
|
|
794
|
+
{
|
|
795
|
+
"code": "<p>여기에 설명 텍스트가 들어갑니다</p>",
|
|
796
|
+
"note": "한글 자리 채움 문구"
|
|
797
|
+
},
|
|
798
|
+
{
|
|
799
|
+
"code": "<img src=\"https://placehold.co/300x200\" alt=\"\">",
|
|
800
|
+
"note": "placeholder 핫링크 — self-contained 정책과 이중 위반"
|
|
801
|
+
}
|
|
802
|
+
],
|
|
803
|
+
"good": [
|
|
804
|
+
{
|
|
805
|
+
"code": "<p>독서문화 프로그램은 매월 첫째 주 월요일부터 신청을 받습니다.</p>",
|
|
806
|
+
"note": "실제 서비스 문안 (contentSources 기반)"
|
|
807
|
+
},
|
|
808
|
+
{
|
|
809
|
+
"code": "<img src=\"/images/program-reading.jpg\" alt=\"독서문화 프로그램 진행 모습\">",
|
|
810
|
+
"note": "실제 자산 경로 + 의미 있는 alt"
|
|
811
|
+
}
|
|
812
|
+
],
|
|
813
|
+
"refs": [
|
|
814
|
+
"references/art-direction.md",
|
|
815
|
+
"contracts/task-contract.schema.json"
|
|
816
|
+
]
|
|
817
|
+
},
|
|
818
|
+
{
|
|
819
|
+
"id": "R-24",
|
|
820
|
+
"category": "css",
|
|
821
|
+
"summary": "한글 조판 하한 — keep-all 존재 필수·leading-none 금지·line-height 1.5 미만 경고",
|
|
822
|
+
"severity": "error",
|
|
823
|
+
"enforcement": [
|
|
824
|
+
"check-violations.js"
|
|
825
|
+
],
|
|
826
|
+
"rationale": "한글은 단어 중간 줄바꿈(keep-all 부재)과 좁은 행간에서 가독성이 급락한다. reset 계층(3-generic/reset.css)의 word-break: keep-all 부재와 HTML의 leading-none 클래스는 error, CSS line-height 1.5 미만은 warn으로 검출한다. 임계값 1.5의 정본은 contracts/art-direction.json hangul.bodyLineHeight.lintFloor이며 단위 테스트가 동일성을 강제한다. badge·tag·btn처럼 한 줄로 고정되는 단행 컴포넌트 CSS 파일은 예외다.",
|
|
827
|
+
"bad": [
|
|
828
|
+
{
|
|
829
|
+
"code": "p { line-height: 1.3; }",
|
|
830
|
+
"note": "본문 하한(1.5) 미만 — warn"
|
|
831
|
+
},
|
|
832
|
+
{
|
|
833
|
+
"code": "/* 3-generic/reset.css에 word-break: keep-all 부재 */",
|
|
834
|
+
"note": "한글 조판 기본값 누락 — error"
|
|
835
|
+
}
|
|
836
|
+
],
|
|
837
|
+
"good": [
|
|
838
|
+
{
|
|
839
|
+
"code": "body {\n word-break: keep-all;\n overflow-wrap: break-word;\n line-height: 1.7;\n}",
|
|
840
|
+
"note": "한글 조판 기본값 (3-generic/reset.css 소유)"
|
|
841
|
+
},
|
|
842
|
+
{
|
|
843
|
+
"code": ".badge { line-height: 1; }",
|
|
844
|
+
"note": "단행 컴포넌트 예외 — badge·tag·btn"
|
|
845
|
+
}
|
|
846
|
+
],
|
|
847
|
+
"refs": [
|
|
848
|
+
"contracts/art-direction.json",
|
|
849
|
+
"references/art-direction.md",
|
|
850
|
+
"src/styles/3-generic/reset.css"
|
|
851
|
+
]
|
|
852
|
+
},
|
|
853
|
+
{
|
|
854
|
+
"id": "R-25",
|
|
855
|
+
"category": "html",
|
|
856
|
+
"summary": "섹션 리듬 — 동일 archetype 3연속·카드 중첩 금지, 카드 그리드 남발 경고",
|
|
857
|
+
"severity": "error",
|
|
858
|
+
"enforcement": [
|
|
859
|
+
"check-html-structure.js"
|
|
860
|
+
],
|
|
861
|
+
"lang": "html",
|
|
862
|
+
"rationale": "\"중앙 제목 + 카드 그리드\" 반복은 규정 준수형 무개성의 최빈 패턴이다. 형제 section 시퀀스에서 동일 archetype(section--X) 3연속과 카드 안 카드 중첩은 error, 카드 그리드 섹션(같은 부모 아래 직계 .card 3개 이상)의 페이지당 3개 이상 또는 연속 배치는 warn으로 검출한다. 대체 패턴은 5-objects의 section-media(이미지-텍스트 교차)·hero-bleed(풀블리드 인트로)와 src/snippets/signature.md의 시그니처 요소가 공급한다.",
|
|
863
|
+
"bad": [
|
|
864
|
+
{
|
|
865
|
+
"code": "<main id=\"main\">\n <section class=\"section section--list\">…</section>\n <section class=\"section section--list\">…</section>\n <section class=\"section section--list\">…</section>\n</main>",
|
|
866
|
+
"note": "동일 archetype 3연속 — 연속 상한 2"
|
|
867
|
+
},
|
|
868
|
+
{
|
|
869
|
+
"code": "<article class=\"card\">\n <article class=\"card\">…</article>\n</article>",
|
|
870
|
+
"note": "카드 안 카드 중첩"
|
|
871
|
+
}
|
|
872
|
+
],
|
|
873
|
+
"good": [
|
|
874
|
+
{
|
|
875
|
+
"code": "<main id=\"main\">\n <section class=\"section section--intro hero-bleed\">…</section>\n <section class=\"section section--list\">…</section>\n <section class=\"section section--content\">\n <div class=\"container\">\n <div class=\"section-media\">…</div>\n </div>\n </section>\n</main>",
|
|
876
|
+
"note": "archetype 교차 + 변주 패턴 삽입"
|
|
877
|
+
}
|
|
878
|
+
],
|
|
879
|
+
"refs": [
|
|
880
|
+
"references/art-direction.md",
|
|
881
|
+
"src/snippets/section-media.md",
|
|
882
|
+
"src/snippets/hero-bleed.md",
|
|
883
|
+
"src/snippets/signature.md"
|
|
884
|
+
]
|
|
885
|
+
},
|
|
886
|
+
{
|
|
887
|
+
"id": "R-26",
|
|
888
|
+
"category": "css",
|
|
889
|
+
"summary": "폰트 스택 한글 fallback 필수 — --font-sans·--font-heading에 한글 가용 폰트 포함",
|
|
890
|
+
"severity": "error",
|
|
891
|
+
"enforcement": [
|
|
892
|
+
"build-tokens.js"
|
|
893
|
+
],
|
|
894
|
+
"rationale": "영문 전용 폰트 스택은 한글이 시스템 기본 폰트로 떨어져 fallback 미설계 조판이 된다. scripts/lib/token-source.js가 build:tokens·check:presets 경로에서 --font-sans·--font-heading 값에 한글 가용 폰트(Pretendard·Noto Sans/Serif KR·Wanted Sans 등) 포함을 강제하며, 미포함이면 빌드가 실패한다. 미정의 슬롯은 하위호환으로 통과한다.",
|
|
895
|
+
"bad": [
|
|
896
|
+
{
|
|
897
|
+
"code": "--font-sans: 'Inter', sans-serif;",
|
|
898
|
+
"note": "한글 가용 폰트 없음 — build:tokens 실패"
|
|
899
|
+
}
|
|
900
|
+
],
|
|
901
|
+
"good": [
|
|
902
|
+
{
|
|
903
|
+
"code": "--font-sans: 'Pretendard GOV', Pretendard, 'Apple SD Gothic Neo', sans-serif;",
|
|
904
|
+
"note": "한글 가용 폰트 + 시스템 폴백"
|
|
905
|
+
}
|
|
906
|
+
],
|
|
907
|
+
"refs": [
|
|
908
|
+
"scripts/lib/token-source.js",
|
|
909
|
+
"tokens/brand.json",
|
|
910
|
+
"contracts/art-direction.json"
|
|
911
|
+
]
|
|
778
912
|
}
|
|
779
913
|
]
|
|
780
914
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# 히어로 블리드 (Hero Bleed) — 풀블리드 인트로 패턴
|
|
2
|
+
|
|
3
|
+
화면 가로 전체를 쓰는 도입 섹션이다. section 자체에 클래스를 얹어 쓰고, 내부 `.container`가 본문 폭을 잡는다. 배경 이미지 위에 텍스트를 얹을 때는 `--scrim`을 함께 쓴다.
|
|
4
|
+
|
|
5
|
+
## 기본 마크업
|
|
6
|
+
|
|
7
|
+
```html
|
|
8
|
+
<section class="section section--intro hero-bleed hero-bleed--scrim" aria-labelledby="intro-title">
|
|
9
|
+
<div class="hero-bleed__media">
|
|
10
|
+
<img class="hero-bleed__img" src="/images/hero-harbor.jpg" alt="">
|
|
11
|
+
</div>
|
|
12
|
+
<div class="container hero-bleed__content">
|
|
13
|
+
<h1 class="hero-bleed__title" id="intro-title">항구도시 문화재단</h1>
|
|
14
|
+
<p class="hero-bleed__desc">공연·전시·교육 프로그램을 한곳에서 찾고 신청할 수 있습니다.</p>
|
|
15
|
+
<div class="hero-bleed__actions">
|
|
16
|
+
<a class="btn btn--primary btn--large" href="/program">프로그램 신청</a>
|
|
17
|
+
<a class="btn btn--secondary btn--large" href="/space">공간 대관 안내</a>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
</section>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Variant
|
|
24
|
+
|
|
25
|
+
- `.hero-bleed` — 기본 (배경색 `--color-bg-inverse` 위 흰 텍스트)
|
|
26
|
+
- `.hero-bleed--scrim` — 배경 이미지 위 단색 스크림(alpha-black-50). 이미지 위 텍스트는 반드시 이 변형과 함께 쓴다
|
|
27
|
+
|
|
28
|
+
## 사용 조건
|
|
29
|
+
|
|
30
|
+
- 페이지당 1회, 문서 최상단 도입 구간 전용 — 본문 중간에 반복하지 않는다
|
|
31
|
+
- 공공서비스 프로필은 장식 hero를 화면 1/2 이하로 유지한다 (`contracts/profiles.json` rhythm)
|
|
32
|
+
- CMS·관리자 프로필은 hero 없음 — 이 패턴을 쓰지 않는다
|
|
33
|
+
- 배경 영상 hero는 표현 등급 expressive + task contract 근거가 전제다 (references/art-direction.md §1)
|
|
34
|
+
|
|
35
|
+
## 접근성
|
|
36
|
+
|
|
37
|
+
- 배경 이미지는 장식이다 — `alt=""` 고정. 의미 있는 이미지는 본문 콘텐츠로 옮긴다
|
|
38
|
+
- 텍스트 대비 4.5:1은 스크림 위에서 실측한다 — 스크림 없이 이미지 위 텍스트 금지
|
|
39
|
+
- 제목이 섹션의 접근 가능한 이름이 되도록 `aria-labelledby`로 연결한다
|
|
40
|
+
|
|
41
|
+
## 출처
|
|
42
|
+
|
|
43
|
+
- 높이·간격은 프로젝트 밀도에 맞는 CSS/Tailwind 직접값 사용
|
|
44
|
+
- CSS: `src/styles/5-objects/hero-bleed.css`
|
|
45
|
+
- 이미지 트리트먼트·스크림 기준: `references/art-direction.md` §7
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# 섹션 미디어 (Section Media) — 이미지-텍스트 교차 패턴
|
|
2
|
+
|
|
3
|
+
동일 archetype 연속(R-25)을 끊는 변주 패턴이다. 태블릿 이상에서 이미지와 본문이 2컬럼으로 나란히 서고, `--reverse`로 좌우를 교차해 zigzag 리듬을 만든다.
|
|
4
|
+
|
|
5
|
+
## 기본 마크업
|
|
6
|
+
|
|
7
|
+
```html
|
|
8
|
+
<section class="section section--content" aria-labelledby="walk-title">
|
|
9
|
+
<div class="container">
|
|
10
|
+
<div class="section-media">
|
|
11
|
+
<div class="section-media__media">
|
|
12
|
+
<img class="section-media__img" src="/images/course-haean.jpg" alt="해안 산책로를 걷는 방문객">
|
|
13
|
+
</div>
|
|
14
|
+
<div class="section-media__body">
|
|
15
|
+
<p class="section-media__eyebrow">추천 코스</p>
|
|
16
|
+
<h2 class="section-media__title" id="walk-title">해안 산책로 3.2km</h2>
|
|
17
|
+
<p class="section-media__desc">방파제에서 등대까지 이어지는 완만한 코스입니다. 휠체어·유아차 통행이 가능하며, 중간 쉼터 2곳에 그늘막이 있습니다.</p>
|
|
18
|
+
<div class="section-media__actions">
|
|
19
|
+
<a class="btn btn--primary" href="/course/haean">코스 안내</a>
|
|
20
|
+
<a class="btn btn--tertiary" href="/course/haean/map">지도 내려받기</a>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</section>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Variant
|
|
29
|
+
|
|
30
|
+
- `.section-media` — 기본 (모바일 세로 스택 → 태블릿 이상 미디어|본문)
|
|
31
|
+
- `.section-media--reverse` — 미디어를 오른쪽에 배치. DOM 순서는 그대로라 읽기 순서가 보존된다
|
|
32
|
+
|
|
33
|
+
```html
|
|
34
|
+
<div class="section-media section-media--reverse">
|
|
35
|
+
<div class="section-media__media">
|
|
36
|
+
<img class="section-media__img" src="/images/course-oreum.jpg" alt="오름 정상에서 내려다본 풍경">
|
|
37
|
+
</div>
|
|
38
|
+
<div class="section-media__body">
|
|
39
|
+
<h2 class="section-media__title">오름 전망 코스</h2>
|
|
40
|
+
<p class="section-media__desc">정상까지 40분. 일몰 1시간 전 출발을 권장합니다.</p>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 사용 조건
|
|
46
|
+
|
|
47
|
+
- 연속 배치는 1왕복(기본 → `--reverse`)까지만 — 전 섹션 교차는 그 자체로 단조롭다 (references/art-direction.md §5)
|
|
48
|
+
- 이미지 비율은 4:3 기본. 같은 페이지 안에서 비율을 섞지 않는다
|
|
49
|
+
- 첫 콘텐츠 섹션은 1순위 과업이 차지한다 — 이 패턴은 소개·안내 구간에 쓴다
|
|
50
|
+
|
|
51
|
+
## 접근성
|
|
52
|
+
|
|
53
|
+
- 이미지 `alt`는 장면을 설명한다. 장식이면 `alt=""`
|
|
54
|
+
- 제목(`__title`)이 섹션의 접근 가능한 이름이 되도록 `aria-labelledby`로 연결한다
|
|
55
|
+
- `--reverse`는 시각 순서만 바꾼다 — 키보드·스크린리더 순서는 DOM 순서(미디어 → 본문) 그대로다
|
|
56
|
+
|
|
57
|
+
## 출처
|
|
58
|
+
|
|
59
|
+
- 간격·타이포는 프로젝트 밀도에 맞는 CSS/Tailwind 직접값 사용
|
|
60
|
+
- CSS: `src/styles/5-objects/section-media.css`
|
|
61
|
+
- 리듬 원칙: `references/art-direction.md` §5
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# 시그니처 요소 (Signature) — 실물 3패턴
|
|
2
|
+
|
|
3
|
+
"이 사이트만의 것"으로 기억되는 반복 장치 3패턴이다. 등급별 상한(utility 0 · restrained 1 · expressive 3)은 `contracts/art-direction.json` expressionLevels가 정본이고, 선정·배치 원칙은 `references/art-direction.md` §6을 따른다. 아래 CSS는 프로젝트별 스타일 확장 슬롯에 추가한다 — 색은 전부 토큰 경유(R-01), 데코 텍스트·패턴은 스크린리더에서 숨긴다.
|
|
4
|
+
|
|
5
|
+
## 1. 타이포 모티프
|
|
6
|
+
|
|
7
|
+
브랜드 키워드를 크게 깔아 섹션 도입의 정체성을 만든다. 장식이므로 `aria-hidden="true"` 필수.
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<section class="section section--intro" aria-labelledby="festival-title">
|
|
11
|
+
<div class="container">
|
|
12
|
+
<p class="motif" aria-hidden="true">바다</p>
|
|
13
|
+
<h1 id="festival-title">항구도시 등대 축제</h1>
|
|
14
|
+
<p>10월 셋째 주, 방파제 일대에서 열립니다.</p>
|
|
15
|
+
</div>
|
|
16
|
+
</section>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
```css
|
|
20
|
+
/* 타이포 모티프 — 헤딩 폰트 토큰(--font-heading) 사용, 옅은 브랜드색으로 뒤에 깐다 */
|
|
21
|
+
.motif {
|
|
22
|
+
@apply absolute [z-index:-1] select-none pointer-events-none m-0;
|
|
23
|
+
font-family: var(--font-heading);
|
|
24
|
+
font-size: 16rem;
|
|
25
|
+
font-weight: 700;
|
|
26
|
+
line-height: 1; /* 단행 장식 — 본문 하한(R-24)과 무관한 display 전용 값 */
|
|
27
|
+
letter-spacing: -0.03em;
|
|
28
|
+
color: var(--color-primary-5);
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- 페이지당 1곳, hero·인트로 구간 한정. 본문 문단 사이에 끼우지 않는다
|
|
33
|
+
- 고대비 모드에서 본문을 가리지 않는지 확인한다 — 겹침이 생기면 opacity가 아니라 위치를 조정한다
|
|
34
|
+
|
|
35
|
+
## 2. 커스텀 구분선
|
|
36
|
+
|
|
37
|
+
섹션 경계의 기본 `border` 대신 브랜드 형태를 가진 구분선을 쓴다. mask + 배경색 토큰 조합이라 색 변경이 토큰으로 따라온다.
|
|
38
|
+
|
|
39
|
+
```html
|
|
40
|
+
<hr class="divider divider--wave" aria-hidden="true">
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```css
|
|
44
|
+
/* 커스텀 구분선 — SVG는 mask로만 쓰고 색은 토큰이 소유한다 */
|
|
45
|
+
.divider {
|
|
46
|
+
@apply border-0 mx-auto my-[4.8rem];
|
|
47
|
+
width: 12rem;
|
|
48
|
+
height: 1.2rem;
|
|
49
|
+
background-color: var(--color-primary-40);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.divider--wave {
|
|
53
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 12' preserveAspectRatio='none'%3E%3Cpath d='M0 6 Q15 0 30 6 T60 6 T90 6 T120 6' fill='none' stroke='black' stroke-width='2'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
54
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 12' preserveAspectRatio='none'%3E%3Cpath d='M0 6 Q15 0 30 6 T60 6 T90 6 T120 6' fill='none' stroke='black' stroke-width='2'/%3E%3C/svg%3E") center / contain no-repeat;
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
- 형태는 사이트당 1종으로 고정한다 — 페이지마다 다른 구분선은 시그니처가 아니라 소음이다
|
|
59
|
+
- `<hr>`는 의미 구분이 필요한 자리에만. 순수 장식 위치면 `<div class="divider" aria-hidden="true">`
|
|
60
|
+
|
|
61
|
+
## 3. 배경 패턴 (SVG data-uri)
|
|
62
|
+
|
|
63
|
+
섹션 배경에 반복 패턴을 옅게 깐다. 외부 이미지 없이 data-uri 하나로 끝나 납품 self-contained 정책과 맞고, mask 방식이라 패턴색도 토큰이 소유한다.
|
|
64
|
+
|
|
65
|
+
```html
|
|
66
|
+
<section class="section section--content section-pattern" aria-labelledby="notice-title">
|
|
67
|
+
<div class="container">
|
|
68
|
+
<h2 id="notice-title">이용 안내</h2>
|
|
69
|
+
<p>대관 신청은 이용일 14일 전까지 접수합니다.</p>
|
|
70
|
+
</div>
|
|
71
|
+
</section>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```css
|
|
75
|
+
/* 배경 패턴 — 도트 그리드. 패턴층은 ::before로 분리해 본문 대비에 영향을 주지 않는다 */
|
|
76
|
+
.section-pattern {
|
|
77
|
+
@apply relative;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.section-pattern::before {
|
|
81
|
+
@apply content-[''] absolute inset-0 [z-index:-1] pointer-events-none;
|
|
82
|
+
background-color: var(--color-primary);
|
|
83
|
+
opacity: 0.06;
|
|
84
|
+
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Ccircle cx='2' cy='2' r='2' fill='black'/%3E%3C/svg%3E") repeat;
|
|
85
|
+
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Ccircle cx='2' cy='2' r='2' fill='black'/%3E%3C/svg%3E") repeat;
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
패턴 형태만 바꾸는 mask용 SVG 변형 2종 — 색·투명도 규칙은 동일하다.
|
|
90
|
+
|
|
91
|
+
| 형태 | mask용 data-uri |
|
|
92
|
+
|------|-----------------|
|
|
93
|
+
| 사선 해칭 | `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12'%3E%3Cpath d='M-2 14 14 -2M-2 2 2 -2M10 14 14 10' stroke='black' stroke-width='1'/%3E%3C/svg%3E` |
|
|
94
|
+
| 물결 반복 | `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='16'%3E%3Cpath d='M0 8 Q10 0 20 8 T40 8' fill='none' stroke='black' stroke-width='1.5'/%3E%3C/svg%3E` |
|
|
95
|
+
|
|
96
|
+
- 패턴 투명도는 0.04~0.08 감각 — 본문 텍스트 대비 4.5:1을 침해하면 안 된다
|
|
97
|
+
- 사이트당 패턴 1종. 시그니처 총량은 표현 등급의 maxCount를 넘지 않는다
|
|
98
|
+
|
|
99
|
+
## 접근성
|
|
100
|
+
|
|
101
|
+
- 세 패턴 모두 순수 장식이다 — `aria-hidden="true"` 또는 pseudo-element로만 구현해 보조기기 트리에서 제외한다
|
|
102
|
+
- 고대비 모드(hc)에서 패턴·모티프가 본문 가독을 해치지 않는지 확인한다
|
|
103
|
+
|
|
104
|
+
## 출처
|
|
105
|
+
|
|
106
|
+
- 등급별 상한: `contracts/art-direction.json` expressionLevels / 선정·배치 원칙: `references/art-direction.md` §6
|
|
107
|
+
- 색 토큰: `tokens/build/tokens.css` — raw 색상 금지(R-01), SVG는 mask 전용
|
package/data/tokens.css
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AUTO-GENERATED — tokens/build/tokens.css
|
|
3
|
-
* 출처: tokens/foundation.json
|
|
4
|
-
* 직접 수정 금지. 변경은 tokens/
|
|
3
|
+
* 출처: tokens/foundation.json + tokens/brand.json
|
|
4
|
+
* 직접 수정 금지. 브랜드 변경은 tokens/brand.json에서 한다.
|
|
5
5
|
*
|
|
6
6
|
* 공개 토큰은 색상, 기본 폰트, 브레이크포인트만 발행한다.
|
|
7
7
|
* 간격·크기·타이포 스케일·반경·모션·그림자·z-index는 CSS/Tailwind 직접값으로 작성한다.
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
:root {
|
|
11
11
|
/* Font families */
|
|
12
12
|
--font-sans: 'Pretendard GOV', 'Apple SD Gothic Neo', 'Noto Sans KR', system-ui, sans-serif;
|
|
13
|
+
--font-heading: 'Pretendard GOV', 'Apple SD Gothic Neo', 'Noto Sans KR', system-ui, sans-serif;
|
|
13
14
|
--font-mono: 'JetBrains Mono', 'D2Coding', SFMono-Regular, Consolas, monospace;
|
|
14
15
|
|
|
15
16
|
/* Breakpoints */
|
|
@@ -27,6 +28,7 @@
|
|
|
27
28
|
--color-text-subtle: #464c53;
|
|
28
29
|
--color-text-disabled: #8a949e;
|
|
29
30
|
--color-text-inverse: #ffffff;
|
|
31
|
+
--color-text-primary: #0b50d0;
|
|
30
32
|
--color-bg: #ffffff;
|
|
31
33
|
--color-bg-subtler: #f4f5f6;
|
|
32
34
|
--color-bg-subtle: #e6e8ea;
|
|
@@ -43,6 +45,7 @@
|
|
|
43
45
|
--color-border: #b1b8be;
|
|
44
46
|
--color-border-light: #cdd1d5;
|
|
45
47
|
--color-border-dark: #58616a;
|
|
48
|
+
--color-border-control: #6d7882;
|
|
46
49
|
--color-border-primary: #256ef4;
|
|
47
50
|
--color-border-primary-light: #d8e5fd;
|
|
48
51
|
--color-border-information-light: #d3ebfd;
|
|
@@ -187,14 +190,17 @@
|
|
|
187
190
|
}
|
|
188
191
|
|
|
189
192
|
[data-color-mode="high-contrast"] {
|
|
193
|
+
/* Semantic colors */
|
|
190
194
|
--color-primary: #256ef4;
|
|
191
195
|
--color-primary-hover: #0b50d0;
|
|
192
196
|
--color-primary-pressed: #083891;
|
|
197
|
+
--color-secondary: #268097;
|
|
193
198
|
--color-text: #f4f5f6;
|
|
194
199
|
--color-text-bolder: #e6e8ea;
|
|
195
200
|
--color-text-subtle: #cdd1d5;
|
|
196
201
|
--color-text-disabled: #58616a;
|
|
197
202
|
--color-text-inverse: #1e2124;
|
|
203
|
+
--color-text-primary: #b1cefb;
|
|
198
204
|
--color-bg: #000000;
|
|
199
205
|
--color-bg-subtler: #131416;
|
|
200
206
|
--color-bg-subtle: #1e2124;
|
|
@@ -203,11 +209,156 @@
|
|
|
203
209
|
--color-surface: #000000;
|
|
204
210
|
--color-surface-subtler: #131416;
|
|
205
211
|
--color-surface-disabled: #464c53;
|
|
206
|
-
--color-
|
|
212
|
+
--color-surface-primary-subtler: #020f27;
|
|
213
|
+
--color-surface-information-subtler: #021a2c;
|
|
214
|
+
--color-surface-success-subtler: #0e2012;
|
|
215
|
+
--color-surface-warning-subtler: #241800;
|
|
216
|
+
--color-surface-danger-subtler: #260903;
|
|
217
|
+
--color-border: #6d7882;
|
|
207
218
|
--color-border-light: #33363d;
|
|
208
219
|
--color-border-dark: #8a949e;
|
|
220
|
+
--color-border-control: #8a949e;
|
|
209
221
|
--color-border-primary: #256ef4;
|
|
222
|
+
--color-border-primary-light: #03163a;
|
|
223
|
+
--color-border-information-light: #03253f;
|
|
224
|
+
--color-border-success-light: #122b18;
|
|
225
|
+
--color-border-warning-light: #2e1f00;
|
|
226
|
+
--color-border-danger-light: #390d05;
|
|
210
227
|
--color-border-disabled: #464c53;
|
|
228
|
+
--color-link: #86aff9;
|
|
229
|
+
--color-link-hover: #b1cefb;
|
|
230
|
+
--color-link-pressed: #d8e5fd;
|
|
231
|
+
--color-link-visited: #c49ff9;
|
|
232
|
+
--color-button-primary-fill: #86aff9;
|
|
233
|
+
--color-button-primary-fill-hover: #b1cefb;
|
|
234
|
+
--color-button-primary-fill-pressed: #d8e5fd;
|
|
235
|
+
--color-button-secondary-fill: #020f27;
|
|
236
|
+
--color-button-secondary-fill-hover: #03163a;
|
|
237
|
+
--color-button-secondary-fill-pressed: #052561;
|
|
238
|
+
--color-button-secondary-border: #256ef4;
|
|
239
|
+
--color-button-tertiary-fill: #00000000;
|
|
240
|
+
--color-button-tertiary-fill-hover: #131416;
|
|
241
|
+
--color-button-tertiary-fill-pressed: #33363d;
|
|
242
|
+
--color-button-tertiary-border: #8a949e;
|
|
243
|
+
--color-button-text-fill: #00000000;
|
|
244
|
+
--color-button-text-fill-hover: #091f25;
|
|
245
|
+
--color-button-text-fill-pressed: #0e3139;
|
|
246
|
+
--color-button-text-border: #ffffff80;
|
|
247
|
+
--color-button-disabled-fill: #33363d;
|
|
248
|
+
--color-button-disabled-border: #464c53;
|
|
249
|
+
--color-input-surface: #1e2124;
|
|
250
|
+
--color-input-surface-disabled: #131416;
|
|
251
|
+
--color-input-border: #8a949e;
|
|
252
|
+
--color-input-border-active: #256ef4;
|
|
253
|
+
--color-input-border-disabled: #464c53;
|
|
254
|
+
--color-input-border-error: #de3412;
|
|
255
|
+
--color-danger: #de3412;
|
|
256
|
+
--color-danger-text: #f7afa1;
|
|
257
|
+
--color-danger-surface: #260903;
|
|
258
|
+
--color-warning: #9e6a00;
|
|
259
|
+
--color-warning-text: #ffc95c;
|
|
260
|
+
--color-warning-surface: #241800;
|
|
261
|
+
--color-success: #228738;
|
|
262
|
+
--color-success-text: #a9dab4;
|
|
263
|
+
--color-success-surface: #0e2012;
|
|
264
|
+
--color-info: #0b78cb;
|
|
265
|
+
--color-info-text: #9ed2fa;
|
|
266
|
+
--color-info-surface: #021a2c;
|
|
267
|
+
--color-point: #d63d4a;
|
|
268
|
+
|
|
269
|
+
/* 단계 색상: 필요한 경우에만 제한적으로 사용 */
|
|
270
|
+
--color-primary-5: #ecf2fe;
|
|
271
|
+
--color-primary-10: #d8e5fd;
|
|
272
|
+
--color-primary-20: #b1cefb;
|
|
273
|
+
--color-primary-30: #86aff9;
|
|
274
|
+
--color-primary-40: #4c87f6;
|
|
275
|
+
--color-primary-50: #256ef4;
|
|
276
|
+
--color-primary-60: #0b50d0;
|
|
277
|
+
--color-primary-70: #083891;
|
|
278
|
+
--color-primary-80: #052561;
|
|
279
|
+
--color-primary-90: #03163a;
|
|
280
|
+
--color-primary-95: #020f27;
|
|
281
|
+
--color-secondary-5: #edf6f8;
|
|
282
|
+
--color-secondary-10: #d5ebf1;
|
|
283
|
+
--color-secondary-20: #abd8e3;
|
|
284
|
+
--color-secondary-30: #75c0d1;
|
|
285
|
+
--color-secondary-40: #3d9fb8;
|
|
286
|
+
--color-secondary-50: #268097;
|
|
287
|
+
--color-secondary-60: #1f687a;
|
|
288
|
+
--color-secondary-70: #17505e;
|
|
289
|
+
--color-secondary-80: #113b45;
|
|
290
|
+
--color-secondary-90: #0e3139;
|
|
291
|
+
--color-secondary-95: #091f25;
|
|
292
|
+
--color-gray-0: #ffffff;
|
|
293
|
+
--color-gray-5: #f4f5f6;
|
|
294
|
+
--color-gray-10: #e6e8ea;
|
|
295
|
+
--color-gray-20: #cdd1d5;
|
|
296
|
+
--color-gray-30: #b1b8be;
|
|
297
|
+
--color-gray-40: #8a949e;
|
|
298
|
+
--color-gray-50: #6d7882;
|
|
299
|
+
--color-gray-60: #58616a;
|
|
300
|
+
--color-gray-70: #464c53;
|
|
301
|
+
--color-gray-80: #33363d;
|
|
302
|
+
--color-gray-90: #1e2124;
|
|
303
|
+
--color-gray-95: #131416;
|
|
304
|
+
--color-gray-100: #000000;
|
|
305
|
+
--color-danger-5: #fdefec;
|
|
306
|
+
--color-danger-10: #fcdfd9;
|
|
307
|
+
--color-danger-20: #f7afa1;
|
|
308
|
+
--color-danger-30: #f48771;
|
|
309
|
+
--color-danger-40: #f05f42;
|
|
310
|
+
--color-danger-50: #de3412;
|
|
311
|
+
--color-danger-60: #bd2c0f;
|
|
312
|
+
--color-danger-70: #8a240f;
|
|
313
|
+
--color-danger-80: #5c180a;
|
|
314
|
+
--color-danger-90: #390d05;
|
|
315
|
+
--color-danger-95: #260903;
|
|
316
|
+
--color-warning-5: #fff3db;
|
|
317
|
+
--color-warning-10: #ffe0a3;
|
|
318
|
+
--color-warning-20: #ffc95c;
|
|
319
|
+
--color-warning-30: #ffb114;
|
|
320
|
+
--color-warning-40: #c78500;
|
|
321
|
+
--color-warning-50: #9e6a00;
|
|
322
|
+
--color-warning-60: #8a5c00;
|
|
323
|
+
--color-warning-70: #614100;
|
|
324
|
+
--color-warning-80: #422c00;
|
|
325
|
+
--color-warning-90: #2e1f00;
|
|
326
|
+
--color-warning-95: #241800;
|
|
327
|
+
--color-success-5: #eaf6ec;
|
|
328
|
+
--color-success-10: #d8eedd;
|
|
329
|
+
--color-success-20: #a9dab4;
|
|
330
|
+
--color-success-30: #7ec88e;
|
|
331
|
+
--color-success-40: #3fa654;
|
|
332
|
+
--color-success-50: #228738;
|
|
333
|
+
--color-success-60: #267337;
|
|
334
|
+
--color-success-70: #285d33;
|
|
335
|
+
--color-success-80: #1f4727;
|
|
336
|
+
--color-success-90: #122b18;
|
|
337
|
+
--color-success-95: #0e2012;
|
|
338
|
+
--color-information-5: #e7f4fe;
|
|
339
|
+
--color-information-10: #d3ebfd;
|
|
340
|
+
--color-information-20: #9ed2fa;
|
|
341
|
+
--color-information-30: #5fb5f7;
|
|
342
|
+
--color-information-40: #2098f3;
|
|
343
|
+
--color-information-50: #0b78cb;
|
|
344
|
+
--color-information-60: #096ab3;
|
|
345
|
+
--color-information-70: #085691;
|
|
346
|
+
--color-information-80: #053961;
|
|
347
|
+
--color-information-90: #03253f;
|
|
348
|
+
--color-information-95: #021a2c;
|
|
349
|
+
--color-point-5: #fbeff0;
|
|
350
|
+
--color-point-10: #f5d6d9;
|
|
351
|
+
--color-point-20: #ebadb2;
|
|
352
|
+
--color-point-30: #e0858c;
|
|
353
|
+
--color-point-40: #d65c66;
|
|
354
|
+
--color-point-50: #d63d4a;
|
|
355
|
+
--color-point-60: #ab2b36;
|
|
356
|
+
--color-point-70: #7a1f26;
|
|
357
|
+
--color-point-80: #521419;
|
|
358
|
+
--color-point-90: #310c0f;
|
|
359
|
+
--color-point-95: #21080a;
|
|
360
|
+
--color-alpha-black-50: #00000080;
|
|
361
|
+
--color-alpha-black-75: #000000bf;
|
|
211
362
|
}
|
|
212
363
|
|
|
213
364
|
@theme {
|
|
@@ -215,6 +366,7 @@
|
|
|
215
366
|
--color-*: initial;
|
|
216
367
|
|
|
217
368
|
--font-sans: 'Pretendard GOV', 'Apple SD Gothic Neo', 'Noto Sans KR', system-ui, sans-serif;
|
|
369
|
+
--font-heading: 'Pretendard GOV', 'Apple SD Gothic Neo', 'Noto Sans KR', system-ui, sans-serif;
|
|
218
370
|
--font-mono: 'JetBrains Mono', 'D2Coding', SFMono-Regular, Consolas, monospace;
|
|
219
371
|
|
|
220
372
|
/* Tailwind responsive variants: mobile:, tablet:, pc: */
|
|
@@ -232,6 +384,7 @@
|
|
|
232
384
|
--color-text-subtle: var(--color-text-subtle);
|
|
233
385
|
--color-text-disabled: var(--color-text-disabled);
|
|
234
386
|
--color-text-inverse: var(--color-text-inverse);
|
|
387
|
+
--color-text-primary: var(--color-text-primary);
|
|
235
388
|
--color-bg: var(--color-bg);
|
|
236
389
|
--color-bg-subtler: var(--color-bg-subtler);
|
|
237
390
|
--color-bg-subtle: var(--color-bg-subtle);
|
|
@@ -248,6 +401,7 @@
|
|
|
248
401
|
--color-border: var(--color-border);
|
|
249
402
|
--color-border-light: var(--color-border-light);
|
|
250
403
|
--color-border-dark: var(--color-border-dark);
|
|
404
|
+
--color-border-control: var(--color-border-control);
|
|
251
405
|
--color-border-primary: var(--color-border-primary);
|
|
252
406
|
--color-border-primary-light: var(--color-border-primary-light);
|
|
253
407
|
--color-border-information-light: var(--color-border-information-light);
|
|
@@ -7,11 +7,12 @@
|
|
|
7
7
|
## 절차
|
|
8
8
|
|
|
9
9
|
1. `references/project-profiles.md`에서 사이트 유형과 기본 section 흐름을 판정한다.
|
|
10
|
-
2.
|
|
11
|
-
3.
|
|
12
|
-
4. `
|
|
13
|
-
5.
|
|
14
|
-
6.
|
|
10
|
+
2. `get_art_direction(profile)`로 표현 등급, 섹션 리듬 변주, 카피 톤을 확정하고, 실제 기관 용어·콘텐츠의 출처(`contentSources`)를 확보한다. 등급 상향은 Task Contract의 `expression` 필드에 근거와 함께 기록한다.
|
|
11
|
+
3. 사용자의 핵심 과업을 한 문장으로 쓴다.
|
|
12
|
+
4. `references/krds-components.md`에서 재사용할 컴포넌트를 고른다.
|
|
13
|
+
5. `contracts/html-page-contract.json`으로 page shell과 section 구조를 확정한다.
|
|
14
|
+
6. 인터랙티브 위젯이 있으면 `design-widget` 절차를 함께 적용한다.
|
|
15
|
+
7. Task Contract의 필수 항목이 채워진 뒤에만 구현한다.
|
|
15
16
|
|
|
16
17
|
## 생성 원칙
|
|
17
18
|
|
|
@@ -34,6 +34,24 @@ npm run check
|
|
|
34
34
|
npm run build
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
## 브랜드 프리셋 적용
|
|
38
|
+
|
|
39
|
+
브랜드 색이 확정되지 않았거나 검증된 팔레트에서 출발하려면 `tokens/presets/`의 프리셋을 그대로 적용한다. 후보와 무드는 `get_art_direction` 또는 `references/art-direction.md` §4에서 고른다. 프리셋은 대비 검사 전량 통과 상태로만 입고되므로 적용 직후 검사가 깨지지 않는다.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
cp tokens/presets/<id>.json tokens/brand.json
|
|
43
|
+
npm run build:tokens
|
|
44
|
+
npm run check:contrast
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## 제목 폰트(--font-heading)
|
|
48
|
+
|
|
49
|
+
제목 서체를 본문과 다르게 가져가는 프로젝트만 설정한다. 생략하면 `--font-heading`은 `--font-sans` 값으로 폴백된다.
|
|
50
|
+
|
|
51
|
+
1. `references/art-direction.md` §3 타이포 카탈로그에서 페어링을 고른다 — 재배포 가능 라이선스 + 한글 폴백 스택이 전제다.
|
|
52
|
+
2. `tokens/brand.json`의 `font.family.heading`에 페어링 스택을 기록한다. 한글 가용 폰트가 스택에 없으면 R-26이 빌드를 실패시킨다.
|
|
53
|
+
3. `npm run build:tokens`로 `--font-heading`을 재발행한다.
|
|
54
|
+
|
|
37
55
|
## 프로젝트 CSS 예시
|
|
38
56
|
|
|
39
57
|
```css
|