에이전트와 함께 테마 만들기
MarsDawn 미리보기 테마를 만드는 AI 에이전트를 위한 참고 자료입니다. 에이전트는 theme.json 하나를 쓰고 명령줄 도구 marsdawn으로 검사하고 렌더링한 뒤 링크를 건넵니다. 사람은 테마 만들기 페이지에서 눈으로 다듬어 제출합니다. 아래 필드 표는 kit의 소스 코드에서 직접 읽은 것이며, 검사가 둘이 같음을 보장합니다.
theme.json을 쓰고, 종료 코드가 0이 될 때까지 marsdawn theme validate theme.json --require-complete --json을 실행하고, marsdawn theme preview로 라이트와 다크를 렌더링한 뒤 사람에게 링크를 건네세요. 테마는 매개변수일 뿐이며 CSS를 담지 않습니다.
테마란 무엇인가
- 라이트와 다크 각각의 색: 기본 팔레트와 선택 사항인 구문 색, 다이어그램 색. 모두
#RRGGBB형식입니다. - 글꼴 디자인:
sans,serif,rounded중 하나. - 닫힌 스타일 옵션 집합: 고정된 범위의 숫자와 고정된 목록에서 고르는 값. 스타일 옵션의 색은 항상
accent같은 팔레트 역할이며 색 값을 직접 쓰지 않습니다. - 표시 문자열: 이름과 요약, 그리고 선택 사항인 작성자와 라이선스.
아래 표에 없는 키는 오류이며, 같은 키를 두 번 쓰는 것도 오류입니다. 파일은 최대 16,384바이트입니다.
검사하고 렌더링하기
theme.json이 있는 폴더에서 실행하세요. marsdawn 도구가 필요합니다. 에이전트를 위한 marsdawn을 보세요.
marsdawn theme validate theme.json --require-complete --json
marsdawn theme css theme.json --json
marsdawn theme preview theme.json --appearance light -o light.png
marsdawn theme preview theme.json --appearance dark -o dark.png
- 종료 코드: 0은 유효, 1은 규칙 ID와 함께 무효, 2는 파일 없음, 64는 사용법 오류입니다.
preview에는 PNG가 이미 있을 때의 4(--force를 주거나 다른 이름을 고르세요)와 렌더링 실패의 5가 더해집니다. --json을 주면 무효한 테마는issues아래에 문제를 나열하며, 각각ruleID,path,message가 있습니다. 고친 뒤 다시 실행하세요.--require-complete는 게시할 테마를 위한 검사입니다. 두 팔레트 모두에syntax와diagram색도 요구합니다.css는 미리보기가 쓰는 CSS를 출력하고,preview는 예시 문서를 PNG로 렌더링합니다. 둘 다 먼저 검증하므로 무효한 테마는 렌더링되지 않습니다.- 테마를 건네기 전에 라이트와 다크 PNG를 둘 다 확인하세요.
기본 제공 테마에서 시작하기
기본 제공 테마 네 가지 중 하나를 복사해 고치세요. 모두 완전하고 유효한 파일입니다.
/assets/theme-sim/kit/themes/dawn.json: 기본값이며 아래 예시/assets/theme-sim/kit/themes/classic.json: 더 차분한 문서 같은 팔레트/assets/theme-sim/kit/themes/modern.json: 더 차갑고 현대적인 팔레트/assets/theme-sim/kit/themes/vivid.json: 더 밝고 대비가 큰 팔레트
예시: Dawn
dawn.json을 한 바이트도 바꾸지 않고 옮긴 것입니다. 스타일 옵션은 hr의 얇은 강조색 선 하나뿐입니다.
{
"schemaVersion": 1,
"id": "dawn",
"version": "1.0.0",
"name": { "en": "Dawn" },
"summary": { "en": "Warm Martian sunrise" },
"fontDesign": "sans",
"scenarios": ["agent-review"],
"light": {
"background": "#FFFDFB", "surface": "#F6F0EC", "text": "#26211F", "muted": "#6F6660",
"border": "#EADFD8", "heading": "#26211F", "accent": "#C8471B", "link": "#B03C0C", "quote": "#D97A4A",
"syntax": { "keyword": "#B03C0C", "string": "#2F6F5E", "comment": "#746B66", "number": "#9A5B00", "function": "#5B3E8C", "type": "#1F6FB2" },
"diagram": { "node": "#FBE9E0", "nodeBorder": "#CA6C3C", "text": "#26211F", "line": "#8A6A5C", "secondary": "#E6F1EE", "tertiary": "#FFF6F1", "note": "#FFF1D6" }
},
"dark": {
"background": "#1C1A1F", "surface": "#262229", "text": "#EBE4DF", "muted": "#A39992",
"border": "#3A343A", "heading": "#F4EEEA", "accent": "#FF8A50", "link": "#FF9E6B", "quote": "#E08A5C",
"syntax": { "keyword": "#FF9E6B", "string": "#7FD1B9", "comment": "#938A84", "number": "#F2C572", "function": "#C7A6F5", "type": "#6CB6FF" },
"diagram": { "node": "#3A2A26", "nodeBorder": "#E08A5C", "text": "#EBE4DF", "line": "#B8A69C", "secondary": "#23332F", "tertiary": "#2B2427", "note": "#3D3322" }
},
"style": {
"hr": { "style": { "type": "line", "color": "accent", "thickness": 1 } }
}
}
필드
아래의 모든 객체는 같은 방식으로 읽힙니다. required는 키가 반드시 있어야 한다는 뜻이며, 나열되지 않은 키는 오류입니다.
타입: string, integer, number, boolean, map(언어 태그에서 텍스트로), enum(나열된 값 중 하나), role(팔레트 역할), object(연결된 표). 타입 뒤의 []는 목록을 뜻합니다.
theme.json
| 필드 | 타입 | 필수 여부 | 값과 참고 |
|---|---|---|---|
schemaVersion | integer | required | 항상 1. |
id | string | required | 1자에서 32자: 소문자와 숫자가 하이픈 하나로 이어진 덩어리, 예: calm-green. |
version | string | required | MAJOR.MINOR.PATCH, 예: 1.0.0. |
name | map | required | 언어 태그에서 텍스트로. en은 필수이며, 다른 키는 zh-Hant 같은 언어 태그입니다. 각 텍스트는 최대 48자이며 링크와 줄바꿈을 넣을 수 없습니다. |
summary | map | required | name과 같은 형식이며 텍스트당 최대 120자. |
fontDesign | enum | required | sans, serif, rounded |
scenarios | enum[] | required | agent-review, technical-docs, formal-output, notes-sharing — 하나 또는 둘, 중복 불가. 테마가 달성해야 하는 대비 수준을 정합니다. |
author | object | optional | author |
license | string | optional | SPDX 식별자, 예: Apache-2.0. 제출된 테마는 Apache-2.0으로 게시됩니다. |
light | object | required | light, dark |
dark | object | required | light, dark |
style | object | optional | style |
author
| 필드 | 타입 | 필수 여부 | 값과 참고 |
|---|---|---|---|
name | string | required | 최대 64자, 링크 불가. |
github | string | optional | GitHub 로그인. 제출할 때는 제출하는 사람의 것을 넣으세요. |
light, dark
| 필드 | 타입 | 필수 여부 | 값과 참고 |
|---|---|---|---|
background | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
surface | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
text | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
muted | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
border | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
heading | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
accent | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
link | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
quote | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
syntax | object | optional (게시하려면 필수, --require-complete로 검사) | syntax |
diagram | object | optional (게시하려면 필수, --require-complete로 검사) | diagram |
syntax
| 필드 | 타입 | 필수 여부 | 값과 참고 |
|---|---|---|---|
keyword | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
string | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
comment | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
number | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
function | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
type | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
diagram
| 필드 | 타입 | 필수 여부 | 값과 참고 |
|---|---|---|---|
node | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
nodeBorder | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
text | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
line | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
secondary | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
tertiary | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
note | string | required | #과 16진수 여섯 자리, 예: #1C1C1C. |
style
| 필드 | 타입 | 필수 여부 | 값과 참고 |
|---|---|---|---|
bodySize | number | optional | 범위 |
lineHeight | number | optional | 범위 |
headingWeight | number | optional | 범위 |
radius | number | optional | 범위 |
maxWidth | number | optional | 범위 |
h1 | object | optional | style.h1 |
h2 | object | optional | style.h2 |
blockquote | object | optional | style.blockquote |
hr | object | optional | style.hr |
table | object | optional | style.table |
listMarker | role | optional | 팔레트 역할 |
inlineCode | role | optional | 팔레트 역할 — text 또는 구문 역할만 가능: keyword, string, comment, number, function, type. |
link | object | optional | style.link |
syntax | object | optional | style.syntax |
style.h1
| 필드 | 타입 | 필수 여부 | 값과 참고 |
|---|---|---|---|
size | number | optional | 범위 |
letterSpacing | number | optional | 범위 |
align | enum | optional | left, center |
decoration | object | optional | type |
style.h2
| 필드 | 타입 | 필수 여부 | 값과 참고 |
|---|---|---|---|
letterSpacing | number | optional | 범위 |
decoration | object | optional | type |
italic | boolean | optional | true, false |
style.blockquote
| 필드 | 타입 | 필수 여부 | 값과 참고 |
|---|---|---|---|
style | object | optional | type |
italic | boolean | optional | true, false |
style.hr
| 필드 | 타입 | 필수 여부 | 값과 참고 |
|---|---|---|---|
style | object | optional | type |
style.table
| 필드 | 타입 | 필수 여부 | 값과 참고 |
|---|---|---|---|
header | object | optional | type |
verticalRules | boolean | optional | true, false |
rounded | boolean | optional | true, false |
style.link
| 필드 | 타입 | 필수 여부 | 값과 참고 |
|---|---|---|---|
underline | boolean | optional | true, false |
style.syntax
| 필드 | 타입 | 필수 여부 | 값과 참고 |
|---|---|---|---|
boldKeywords | boolean | optional | true, false |
type이 있는 옵션
이 스타일 옵션은 type이 있는 객체입니다. 각 type은 그것에 나열된 필드만 받고 다른 필드는 받지 않습니다.
| 옵션 | type | 필드 | 필수 여부 | 타입 |
|---|---|---|---|---|
style.h1.decoration | rule | — | — | — |
style.h1.decoration | none | — | — | — |
style.h1.decoration | shortRule | color | required | role |
style.h1.decoration | gradientBar | from | required | role |
style.h1.decoration | gradientBar | to | required | role |
style.h2.decoration | rule | — | — | — |
style.h2.decoration | none | — | — | — |
style.h2.decoration | dot | color | required | role |
style.blockquote.style | bar | width | optional | number 범위 |
style.blockquote.style | panel | — | — | — |
style.hr.style | line | color | optional | role |
style.hr.style | line | thickness | optional | number 범위 |
style.hr.style | shortCentered | color | required | role |
style.hr.style | gradient | colors | required | role[] 역할 둘 또는 셋. |
style.table.header | surface | — | — | — |
style.table.header | accentRule | color | required | role |
style.table.header | filled | background | required | role |
style.table.header | filled | text | required | role |
style.table.header | filled | border | optional | role |
팔레트 역할
팔레트 역할은 다음 이름 중 하나입니다.
background, surface, text, muted, border, heading, accent, link, quote, keyword, string, comment, number, function, type
숫자
숫자는 가장 가까운 간격의 배수로 맞춰진 뒤 최소와 최대 사이에 있어야 합니다. 그렇지 않으면 규칙은 number.range입니다.
| 필드 | 최소 | 최대 | 간격 |
|---|---|---|---|
style.bodySize | 14 | 18 | 1 |
style.lineHeight | 1.4 | 1.9 | 0.05 |
style.headingWeight | 400 | 900 | 50 |
style.radius | 0 | 16 | 1 |
style.maxWidth | 600 | 1000 | 1 |
style.h1.size | 1.8 | 2.4 | 0.1 |
style.h1.letterSpacing | -0.03 | 0.03 | 0.005 |
style.h2.letterSpacing | -0.03 | 0.03 | 0.005 |
style.blockquote.style.width | 2 | 4 | 1 |
style.hr.style.thickness | 1 | 4 | 1 |
그 밖에 검사하는 것
- 대비.
background위의text처럼 미리보기가 그리는 모든 색 쌍은 텍스트는 4.5:1, 선과 그 밖의 비텍스트는 3:1을 만족해야 합니다. 지정한 시나리오는 일부 색을 4.5:1로 올립니다. 메시지에는 해당 쌍과 그 비율, 필요한 비율이 나옵니다. - 문자열. 이름, 요약, 작성자 이름이 비어 있거나 너무 길거나, 링크, 제어 문자, 보이지 않는 문자 또는 양방향 서식 문자를 포함하면 거부됩니다.
- 규칙 ID. 모든 문제에는
contrast.pair,color.hex,number.range,id.pattern,schema.unknownKey,palette.incomplete같은 ID가 붙습니다. ID와path가 가리키는 대로 파일을 고치세요.
링크로 건네기
테마 만들기 페이지는 링크로 테마를 열어 사람이 눈으로 다듬을 수 있게 합니다. 링크는 그 페이지의 주소, #theme=, 그리고 패딩 없는 base64url로 인코딩한 파일 바이트로 이루어집니다. 파일은 최대 16,384바이트입니다. 이 한 줄이 macOS와 Linux의 zsh와 bash에서 링크를 만들어 줍니다.
printf 'https://marsdawn.southern-light.dev/ko/themes/new/#theme=%s\n' "$(base64 < theme.json | tr -d '\n=' | tr '+/' '-_')"
테마는 브라우저가 사이트로 보내지 않는 # 뒤에 담기며, 사이트는 이를 어디에도 보내지 않습니다. 다만 링크는 셸과 브라우저의 기록에 남고, 링크를 받은 사람은 누구나 테마를 읽을 수 있습니다.
제출하기
에이전트는 파일과 링크를 준비하고, 제출은 사람이 합니다. 두 방법 모두 Developer Certificate of Origin과 Apache-2.0 게시에 대한 확인이 필요하며, 이 확인은 사람이 직접 합니다. 에이전트가 상자에 체크하거나 대신 서명하지 않습니다.
- 이슈. 사람이 링크를 열어 “테마 만들기” 페이지에서 Submit을 누르거나, 이슈 양식을 열어 JSON을 Theme에 붙여 넣습니다. 워크플로가 테마를 검사하고 답합니다.
- 풀 리퀘스트.
themes/<id>/theme.json만 바꾸고,author.github를 풀 리퀘스트 작성자의 로그인으로 설정하고, 모든 커밋을git commit -s로 서명하세요. 규칙은 CONTRIBUTING.md에 있습니다.
제출된 테마의 작성자 이름, 이름, 요약, GitHub 로그인은 영구적으로 공개됩니다. author.name에는 그 사람이 공개해도 좋다고 한 이름을 넣으세요.
게시된 테마
게시된 테마가 어떻게 생겼는지는 테마 갤러리에서 볼 수 있습니다.