gyeol 0.1.6 → 0.1.7
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 +141 -128
- package/gyeol.css +158 -151
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,128 +1,141 @@
|
|
|
1
|
-
# 결 gyeol
|
|
2
|
-
|
|
3
|
-
한지를 겹겹이 올리듯 화면을 쌓는 디자인 시스템이에요. 다른 프로젝트나 도구에서 쓸 수 있게 세 가지 형식으로 나눠 두었어요.
|
|
4
|
-
|
|
5
|
-
```bash
|
|
6
|
-
npm install gyeol
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
소개 사이트: https://minjaek111.github.io/gyeol/
|
|
10
|
-
|
|
11
|
-
| 파일 | 쓰는 곳 |
|
|
12
|
-
| --- | --- |
|
|
13
|
-
| `gyeol.css` | 아무 웹 프로젝트(HTML, React, Vue, Svelte 등). 토큰, 팔레트, 테마, 겹 소재, 컴포넌트 클래스가 모두 들어 있어요. |
|
|
14
|
-
| `tailwind.preset.cjs` | Tailwind CSS 프로젝트. `gyeol.css`의 CSS 변수를 Tailwind 클래스로 연결해요. |
|
|
15
|
-
| `tokens.json` | 디자인 토큰 표준(DTCG) 형식. Figma Tokens Studio, Style Dictionary, iOS·Android 변환에 써요. |
|
|
16
|
-
|
|
17
|
-
## 1. 웹 (CSS)
|
|
18
|
-
|
|
19
|
-
```html
|
|
20
|
-
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Hahmlet:wght@300;
|
|
21
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gyeol@0.1.
|
|
22
|
-
<!-- 또는 unpkg: <link rel="stylesheet" href="https://unpkg.com/gyeol@0.1.
|
|
23
|
-
|
|
24
|
-
<html data-palette="meok" data-theme="dark">
|
|
25
|
-
<body class="gy">
|
|
26
|
-
<div class="gy-gyeop gy-du">
|
|
27
|
-
<button class="gy-btn">저장하기</button>
|
|
28
|
-
<button class="gy-btn gy-btn--paper">취소</button>
|
|
29
|
-
</div>
|
|
30
|
-
</body>
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
CDN은 jsDelivr와 unpkg 중 하나를 쓰면 돼요. 버전을 빼고 `gyeol/gyeol.css`로 쓰면 항상 최신 버전을 불러오지만, 실제 서비스에서는 버전을 고정하는 게 안전해요.
|
|
34
|
-
|
|
35
|
-
| CDN | 주소 |
|
|
36
|
-
| --- | --- |
|
|
37
|
-
| jsDelivr | `https://cdn.jsdelivr.net/npm/gyeol@0.1.
|
|
38
|
-
| unpkg | `https://unpkg.com/gyeol@0.1.
|
|
39
|
-
|
|
40
|
-
번들러(Vite, Next.js 등)를 쓰면 CSS를 이렇게 불러와요.
|
|
41
|
-
|
|
42
|
-
```js
|
|
43
|
-
import 'gyeol/gyeol.css';
|
|
44
|
-
import { setPalette, setTheme, color, tokens } from 'gyeol';
|
|
45
|
-
|
|
46
|
-
setPalette('cheongja');
|
|
47
|
-
setTheme('light');
|
|
48
|
-
color('meok', 'dark', 'accent'); // '#85aeff'
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
- `data-palette`: `meok`(기본) · `jjok` · `cheongja` · `songhwa` · `yeonji` · `jaju` · `chija` · `hwangto` · `oksaek` · `haneul` · `odi`
|
|
52
|
-
|
|
53
|
-
| 팔레트 | 키 | 팔레트 | 키 |
|
|
54
|
-
| --- | --- | --- | --- |
|
|
55
|
-
| 먹 | `meok` | 치자 | `chija` |
|
|
56
|
-
| 쪽빛 | `jjok` | 황토 | `hwangto` |
|
|
57
|
-
| 청자 | `cheongja` | 옥색 | `oksaek` |
|
|
58
|
-
| 송화 | `songhwa` | 하늘 | `haneul` |
|
|
59
|
-
| 연지 | `yeonji` | 오디 | `odi` |
|
|
60
|
-
| 자주 | `jaju` | | |
|
|
61
|
-
- `data-theme`: `dark`(기본) · `light` · `system`
|
|
62
|
-
- 컴포넌트 클래스는 모두 `gy-` 접두사를 써서 기존 CSS와 겹치지 않아요.
|
|
63
|
-
- 화면 일부만 다른 팔레트나 테마로 바꿀 수 있어요(0.1.2부터). 그 범위 안의 색과 글자색이 모두 다시 계산돼요.
|
|
64
|
-
|
|
65
|
-
```html
|
|
66
|
-
<html data-palette="meok" data-theme="dark">
|
|
67
|
-
<section data-palette="yeonji" data-theme="light">이 영역만 연지 · 라이트</section>
|
|
68
|
-
<section data-palette="cheongja">테마를 빼면 바깥 테마(다크)를 따라가요</section>
|
|
69
|
-
</html>
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
```js
|
|
73
|
-
setPalette('jaju', document.querySelector('#promo')); // 특정 요소에만 적용
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
| 컴포넌트 | 클래스 |
|
|
77
|
-
| --- | --- |
|
|
78
|
-
| 겹 소재 | `gy-gyeop` + `gy-hol` / `gy-du` / `gy-se` |
|
|
79
|
-
| 버튼 | `gy-btn` + `--ink` / `--paper` / `--ghost` / `--danger` / `--sm` |
|
|
80
|
-
| 스위치 | `<button class="gy-switch" role="switch" aria-checked="true">` |
|
|
81
|
-
| 세그먼트 | `gy-seg` 안에 `aria-pressed` 버튼 |
|
|
82
|
-
| 칩 | `gy-chip` + `aria-pressed` |
|
|
83
|
-
| 입력 필드 | `gy-field` (오류는 `gy-field--error`) |
|
|
84
|
-
| 체크박스 | `<label class="gy-check"><input type="checkbox"> 텍스트</label>` |
|
|
85
|
-
| 배지 | `gy-badge` + `--ok` / `--warn` / `--err` |
|
|
86
|
-
| 진행 막대 | `<div class="gy-progress" style="--p:60%"><i></i></div>` |
|
|
87
|
-
| 토스트·툴팁 | `gy-toast`(겹과 함께), `gy-tooltip` |
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
gyeol
|
|
108
|
-
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
1
|
+
# 결 gyeol
|
|
2
|
+
|
|
3
|
+
한지를 겹겹이 올리듯 화면을 쌓는 디자인 시스템이에요. 다른 프로젝트나 도구에서 쓸 수 있게 세 가지 형식으로 나눠 두었어요.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install gyeol
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
소개 사이트: https://minjaek111.github.io/gyeol/
|
|
10
|
+
|
|
11
|
+
| 파일 | 쓰는 곳 |
|
|
12
|
+
| --- | --- |
|
|
13
|
+
| `gyeol.css` | 아무 웹 프로젝트(HTML, React, Vue, Svelte 등). 토큰, 팔레트, 테마, 겹 소재, 컴포넌트 클래스가 모두 들어 있어요. |
|
|
14
|
+
| `tailwind.preset.cjs` | Tailwind CSS 프로젝트. `gyeol.css`의 CSS 변수를 Tailwind 클래스로 연결해요. |
|
|
15
|
+
| `tokens.json` | 디자인 토큰 표준(DTCG) 형식. Figma Tokens Studio, Style Dictionary, iOS·Android 변환에 써요. |
|
|
16
|
+
|
|
17
|
+
## 1. 웹 (CSS)
|
|
18
|
+
|
|
19
|
+
```html
|
|
20
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Hahmlet:wght@300;700;800&family=IBM+Plex+Sans+KR:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap">
|
|
21
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gyeol@0.1.6/gyeol.css">
|
|
22
|
+
<!-- 또는 unpkg: <link rel="stylesheet" href="https://unpkg.com/gyeol@0.1.6/gyeol.css"> -->
|
|
23
|
+
|
|
24
|
+
<html data-palette="meok" data-theme="dark">
|
|
25
|
+
<body class="gy">
|
|
26
|
+
<div class="gy-gyeop gy-du">
|
|
27
|
+
<button class="gy-btn">저장하기</button>
|
|
28
|
+
<button class="gy-btn gy-btn--paper">취소</button>
|
|
29
|
+
</div>
|
|
30
|
+
</body>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
CDN은 jsDelivr와 unpkg 중 하나를 쓰면 돼요. 버전을 빼고 `gyeol/gyeol.css`로 쓰면 항상 최신 버전을 불러오지만, 실제 서비스에서는 버전을 고정하는 게 안전해요.
|
|
34
|
+
|
|
35
|
+
| CDN | 주소 |
|
|
36
|
+
| --- | --- |
|
|
37
|
+
| jsDelivr | `https://cdn.jsdelivr.net/npm/gyeol@0.1.6/gyeol.css` |
|
|
38
|
+
| unpkg | `https://unpkg.com/gyeol@0.1.6/gyeol.css` |
|
|
39
|
+
|
|
40
|
+
번들러(Vite, Next.js 등)를 쓰면 CSS를 이렇게 불러와요.
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
import 'gyeol/gyeol.css';
|
|
44
|
+
import { setPalette, setTheme, color, tokens } from 'gyeol';
|
|
45
|
+
|
|
46
|
+
setPalette('cheongja');
|
|
47
|
+
setTheme('light');
|
|
48
|
+
color('meok', 'dark', 'accent'); // '#85aeff'
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
- `data-palette`: `meok`(기본) · `jjok` · `cheongja` · `songhwa` · `yeonji` · `jaju` · `chija` · `hwangto` · `oksaek` · `haneul` · `odi`
|
|
52
|
+
|
|
53
|
+
| 팔레트 | 키 | 팔레트 | 키 |
|
|
54
|
+
| --- | --- | --- | --- |
|
|
55
|
+
| 먹 | `meok` | 치자 | `chija` |
|
|
56
|
+
| 쪽빛 | `jjok` | 황토 | `hwangto` |
|
|
57
|
+
| 청자 | `cheongja` | 옥색 | `oksaek` |
|
|
58
|
+
| 송화 | `songhwa` | 하늘 | `haneul` |
|
|
59
|
+
| 연지 | `yeonji` | 오디 | `odi` |
|
|
60
|
+
| 자주 | `jaju` | | |
|
|
61
|
+
- `data-theme`: `dark`(기본) · `light` · `system`
|
|
62
|
+
- 컴포넌트 클래스는 모두 `gy-` 접두사를 써서 기존 CSS와 겹치지 않아요.
|
|
63
|
+
- 화면 일부만 다른 팔레트나 테마로 바꿀 수 있어요(0.1.2부터). 그 범위 안의 색과 글자색이 모두 다시 계산돼요.
|
|
64
|
+
|
|
65
|
+
```html
|
|
66
|
+
<html data-palette="meok" data-theme="dark">
|
|
67
|
+
<section data-palette="yeonji" data-theme="light">이 영역만 연지 · 라이트</section>
|
|
68
|
+
<section data-palette="cheongja">테마를 빼면 바깥 테마(다크)를 따라가요</section>
|
|
69
|
+
</html>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
setPalette('jaju', document.querySelector('#promo')); // 특정 요소에만 적용
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
| 컴포넌트 | 클래스 |
|
|
77
|
+
| --- | --- |
|
|
78
|
+
| 겹 소재 | `gy-gyeop` + `gy-hol` / `gy-du` / `gy-se` |
|
|
79
|
+
| 버튼 | `gy-btn` + `--ink` / `--paper` / `--ghost` / `--danger` / `--sm` |
|
|
80
|
+
| 스위치 | `<button class="gy-switch" role="switch" aria-checked="true">` |
|
|
81
|
+
| 세그먼트 | `gy-seg` 안에 `aria-pressed` 버튼 |
|
|
82
|
+
| 칩 | `gy-chip` + `aria-pressed` |
|
|
83
|
+
| 입력 필드 | `gy-field` (오류는 `gy-field--error`) |
|
|
84
|
+
| 체크박스 | `<label class="gy-check"><input type="checkbox"> 텍스트</label>` |
|
|
85
|
+
| 배지 | `gy-badge` + `--ok` / `--warn` / `--err` |
|
|
86
|
+
| 진행 막대 | `<div class="gy-progress" style="--p:60%"><i></i></div>` |
|
|
87
|
+
| 토스트·툴팁 | `gy-toast`(겹과 함께), `gy-tooltip` |
|
|
88
|
+
|
|
89
|
+
**접근성 메모**
|
|
90
|
+
|
|
91
|
+
- 터치 화면에서는 작은 버튼, 칩, 세그먼트, 체크박스가 40px 이상으로 커지고 스위치는 누르는 범위가 넓어져요(0.1.7부터).
|
|
92
|
+
- 토스트는 화면 낭독기가 읽을 수 있게 늘 존재하는 알림 영역 안에 넣어 주세요.
|
|
93
|
+
|
|
94
|
+
```html
|
|
95
|
+
<div role="status" aria-live="polite">
|
|
96
|
+
<div class="gy-toast gy-gyeop gy-se" hidden>저장했어요</div>
|
|
97
|
+
</div>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- 다이얼로그를 띄울 때는 뒤쪽 내용에 `inert`를 걸고, 닫으면 다이얼로그를 연 버튼으로 포커스를 돌려주세요.
|
|
101
|
+
|
|
102
|
+
## 2. Tailwind
|
|
103
|
+
|
|
104
|
+
```js
|
|
105
|
+
// tailwind.config.js
|
|
106
|
+
module.exports = {
|
|
107
|
+
presets: [require('gyeol/tailwind')],
|
|
108
|
+
};
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`gyeol.css`를 함께 불러와야 색이 정의돼요. 그다음 `bg-paper`, `text-ink-2`, `bg-accent`, `p-pun-4`, `rounded-r-3`, `font-display`, `gyeop gyeop-du`처럼 써요.
|
|
112
|
+
|
|
113
|
+
## 3. 디자인 토큰 JSON (Figma, iOS, Android)
|
|
114
|
+
|
|
115
|
+
`tokens.json`은 [DTCG](https://tr.designtokens.org/format/) 형식이고, 색은 팔레트·테마별로 계산된 HEX 값이에요.
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
gyeol.color.<팔레트>.<light|dark>.<역할> 예) gyeol.color.meok.dark.accent = #85aeff
|
|
119
|
+
gyeol.space.pun-4 = 16px
|
|
120
|
+
gyeol.radius.r-3 = 20px
|
|
121
|
+
gyeol.material.du.thickness = 0.72
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
- **JS에서 바로**: `import tokens from 'gyeol'` 또는 `import tokens from 'gyeol/tokens.json'`
|
|
125
|
+
- **Figma**: Tokens Studio 플러그인에서 `tokens.json`을 불러오세요.
|
|
126
|
+
- **iOS / Android**: Style Dictionary로 `tokens.json`을 Swift, Kotlin, XML 리소스로 변환하세요.
|
|
127
|
+
|
|
128
|
+
HEX 값은 화면용 sRGB로 변환한 근사치예요. 웹에서는 `gyeol.css`의 oklch 원본을 쓰는 게 더 정확해요.
|
|
129
|
+
|
|
130
|
+
## 패키지 경로
|
|
131
|
+
|
|
132
|
+
| 경로 | 내용 |
|
|
133
|
+
| --- | --- |
|
|
134
|
+
| `gyeol` | 토큰 객체와 `setPalette`, `setTheme`, `color` 함수 (ESM·CommonJS·TypeScript 타입 포함) |
|
|
135
|
+
| `gyeol/gyeol.css` (또는 `gyeol/css`) | 전체 CSS |
|
|
136
|
+
| `gyeol/tailwind` | Tailwind 프리셋 |
|
|
137
|
+
| `gyeol/tokens.json` | DTCG 토큰 원본 |
|
|
138
|
+
|
|
139
|
+
## 라이선스
|
|
140
|
+
|
|
141
|
+
MIT
|
package/gyeol.css
CHANGED
|
@@ -1,151 +1,158 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* 결 gyeol — layered-paper design system
|
|
3
|
-
* 사용법: <html data-palette="meok" data-theme="dark">
|
|
4
|
-
* data-palette: meok | jjok | cheongja | songhwa | yeonji | jaju |
|
|
5
|
-
* chija | hwangto | oksaek | haneul | odi (기본 meok)
|
|
6
|
-
* data-theme: dark | light | system (기본 dark)
|
|
7
|
-
* 화면 일부만 다른 팔레트·테마로: <section data-palette="yeonji" data-theme="light"> (0.1.2+)
|
|
8
|
-
* 모든 컴포넌트 클래스는 gy- 접두사를 써서 다른 CSS와 충돌하지 않아요.
|
|
9
|
-
* 글꼴: https://fonts.googleapis.com/css2?family=Hahmlet:wght@300;
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
/* ---------- tokens ---------- */
|
|
13
|
-
:root{
|
|
14
|
-
--h:255; --c:.012; --ah:265; --ac:.14; --ad:0%; /* 기본 팔레트: 먹 + 쪽빛 강조 */
|
|
15
|
-
--f-display: "Hahmlet", "Nanum Myeongjo", "Apple SD Gothic Neo", serif;
|
|
16
|
-
--f-body: "IBM Plex Sans KR", "Apple SD Gothic Neo", "Malgun Gothic", system-ui, sans-serif;
|
|
17
|
-
--f-mono: "IBM Plex Mono", ui-monospace, "Consolas", monospace;
|
|
18
|
-
|
|
19
|
-
--pun-1:4px; --pun-2:8px; --pun-3:12px; --pun-4:16px; --pun-6:24px; --pun-8:32px; --pun-12:48px; --pun-16:64px;
|
|
20
|
-
--r-1:6px; --r-2:12px; --r-3:20px; --r-4:28px; --r-pill:999px;
|
|
21
|
-
--ease-bleed: cubic-bezier(.2,.7,.2,1);
|
|
22
|
-
--t-1:120ms; --t-2:220ms; --t-3:420ms;
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
/* 색 토큰: 루트와, 팔레트·테마를 따로 지정한 모든 요소에서 다시 계산돼요 */
|
|
26
|
-
:root, [data-palette], [data-theme]{
|
|
27
|
-
--bg: oklch(96.5% calc(var(--c)*.12) var(--h));
|
|
28
|
-
--bg-2: oklch(92.5% calc(var(--c)*.22) var(--h));
|
|
29
|
-
--ink: oklch(23% calc(var(--c)*.35) var(--h));
|
|
30
|
-
--ink-2: oklch(43% calc(var(--c)*.25) var(--h));
|
|
31
|
-
--ink-3: oklch(50% calc(var(--c)*.18) var(--h));
|
|
32
|
-
--line: oklch(84% calc(var(--c)*.22) var(--h));
|
|
33
|
-
--paper: oklch(99.2% calc(var(--c)*.05) var(--h));
|
|
34
|
-
--accent: oklch(calc(49% + var(--ad)) var(--ac) var(--ah));
|
|
35
|
-
--accent-soft: oklch(90% calc(var(--ac)*.45) var(--ah));
|
|
36
|
-
--on-accent: oklch(99% 0 0);
|
|
37
|
-
--stain: oklch(80% calc(var(--c)*.9 + .03) calc(var(--h) + 70));
|
|
38
|
-
--ok: oklch(50% .13 150); --warn: oklch(54% .13 65); --err: oklch(52% .19 25);
|
|
39
|
-
--shadow: 0 1px 0 oklch(100% 0 0 / .7) inset, 0 18px 40px -22px oklch(25% .06 var(--h) / .35), 0 2px 6px -2px oklch(25% .04 var(--h) / .12);
|
|
40
|
-
--grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='a'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .32 0'/></filter><filter id='b'><feTurbulence type='fractalNoise' baseFrequency='.012 .32' numOctaves='2' seed='4' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .7 -.42'/></filter><rect width='100%' height='100%' filter='url(%23a)'/><rect width='100%' height='100%' filter='url(%23b)'/></svg>");
|
|
41
|
-
--grain-o: .4;
|
|
42
|
-
|
|
43
|
-
color-scheme: light;
|
|
44
|
-
}
|
|
45
|
-
@media (prefers-color-scheme: dark){ :root[data-theme="system"], :root[data-theme="system"] [data-palette]:not([data-theme]):not([data-theme="light"] *){
|
|
46
|
-
--bg: oklch(17% calc(var(--c)*.22) var(--h)); --bg-2: oklch(21% calc(var(--c)*.3) var(--h));
|
|
47
|
-
--ink: oklch(95% calc(var(--c)*.08) var(--h)); --ink-2: oklch(79% calc(var(--c)*.15) var(--h)); --ink-3: oklch(70% calc(var(--c)*.15) var(--h));
|
|
48
|
-
--line: oklch(33% calc(var(--c)*.25) var(--h)); --paper: oklch(29% calc(var(--c)*.25) var(--h));
|
|
49
|
-
--accent: oklch(calc(76% + var(--ad)) var(--ac) var(--ah)); --accent-soft: oklch(33% calc(var(--ac)*.55) var(--ah)); --on-accent: oklch(18% .03 var(--ah));
|
|
50
|
-
--stain: oklch(48% calc(var(--c)*.9 + .03) calc(var(--h) + 70));
|
|
51
|
-
--ok: oklch(74% .14 150); --warn: oklch(80% .13 75); --err: oklch(72% .16 25);
|
|
52
|
-
--shadow: 0 1px 0 oklch(100% 0 0 / .07) inset, 0 20px 44px -20px oklch(5% .02 var(--h) / .7), 0 2px 6px -2px oklch(5% 0 0 / .4);
|
|
53
|
-
--grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='a'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .22 0'/></filter><filter id='b'><feTurbulence type='fractalNoise' baseFrequency='.012 .32' numOctaves='2' seed='4' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .6 -.4'/></filter><rect width='100%' height='100%' filter='url(%23a)'/><rect width='100%' height='100%' filter='url(%23b)'/></svg>");
|
|
54
|
-
--grain-o: .35; color-scheme: dark;
|
|
55
|
-
}}
|
|
56
|
-
:root:not([data-theme="light"]):not([data-theme="system"]), [data-theme="dark"],
|
|
57
|
-
:root:not([data-theme="light"]):not([data-theme="system"]) [data-palette]:not([data-theme]):not([data-theme="light"] *){
|
|
58
|
-
--bg: oklch(17% calc(var(--c)*.22) var(--h)); --bg-2: oklch(21% calc(var(--c)*.3) var(--h));
|
|
59
|
-
--ink: oklch(95% calc(var(--c)*.08) var(--h)); --ink-2: oklch(79% calc(var(--c)*.15) var(--h)); --ink-3: oklch(70% calc(var(--c)*.15) var(--h));
|
|
60
|
-
--line: oklch(33% calc(var(--c)*.25) var(--h)); --paper: oklch(29% calc(var(--c)*.25) var(--h));
|
|
61
|
-
--accent: oklch(calc(76% + var(--ad)) var(--ac) var(--ah)); --accent-soft: oklch(33% calc(var(--ac)*.55) var(--ah)); --on-accent: oklch(18% .03 var(--ah));
|
|
62
|
-
--stain: oklch(48% calc(var(--c)*.9 + .03) calc(var(--h) + 70));
|
|
63
|
-
--ok: oklch(74% .14 150); --warn: oklch(80% .13 75); --err: oklch(72% .16 25);
|
|
64
|
-
--shadow: 0 1px 0 oklch(100% 0 0 / .07) inset, 0 20px 44px -20px oklch(5% .02 var(--h) / .7), 0 2px 6px -2px oklch(5% 0 0 / .4);
|
|
65
|
-
--grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='a'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .22 0'/></filter><filter id='b'><feTurbulence type='fractalNoise' baseFrequency='.012 .32' numOctaves='2' seed='4' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .6 -.4'/></filter><rect width='100%' height='100%' filter='url(%23a)'/><rect width='100%' height='100%' filter='url(%23b)'/></svg>");
|
|
66
|
-
--grain-o: .35; color-scheme: dark;
|
|
67
|
-
}
|
|
68
|
-
[data-palette="meok"] {--h:255;--c:.012;--ah:265;--ac:.14;--ad:0%}
|
|
69
|
-
[data-palette="jjok"] {--h:265;--c:.14; --ah:265;--ac:.14;--ad:0%}
|
|
70
|
-
[data-palette="cheongja"]{--h:178;--c:.08;--ah:178;--ac:.08;--ad:4%}
|
|
71
|
-
[data-palette="songhwa"] {--h:98; --c:.12; --ah:98; --ac:.12;--ad:0%}
|
|
72
|
-
[data-palette="yeonji"] {--h:16; --c:.16; --ah:16; --ac:.16;--ad:0%}
|
|
73
|
-
[data-palette="jaju"] {--h:330;--c:.13; --ah:330;--ac:.13;--ad:0%}
|
|
74
|
-
[data-palette="chija"] {--h:70;--c:0.14;--ah:70;--ac:0.14;--ad:0%}
|
|
75
|
-
[data-palette="hwangto"] {--h:48;--c:.065;--ah:48;--ac:.065;--ad:0%}
|
|
76
|
-
[data-palette="oksaek"] {--h:195;--c:.10;--ah:195;--ac:.10;--ad:-5%}
|
|
77
|
-
[data-palette="haneul"] {--h:235;--c:.14;--ah:235;--ac:.14;--ad:-5%}
|
|
78
|
-
[data-palette="odi"] {--h:300;--c:.15;--ah:300;--ac:.15;--ad:-5%}
|
|
79
|
-
/* 화면 일부에 팔레트·테마를 따로 지정하면 글자색도 그 범위의 먹색을 따라가요 */
|
|
80
|
-
:where([data-palette], [data-theme]):not(:root){color:var(--ink)}
|
|
81
|
-
|
|
82
|
-
/* ---------- base (opt-in: add class="gy" to <body>) ---------- */
|
|
83
|
-
.gy{margin:0;color:var(--ink);background:var(--bg);font:400 16px/1.65 var(--f-body);-webkit-font-smoothing:antialiased}
|
|
84
|
-
.gy :focus-visible{outline:2px solid var(--accent);outline-offset:3px;box-shadow:0 0 0 7px color-mix(in oklch, var(--accent) 22%, transparent)}
|
|
85
|
-
|
|
86
|
-
/* ---------- material: 겹 ---------- */
|
|
87
|
-
.gy-gyeop{position:relative;isolation:isolate;background:color-mix(in oklch, var(--paper) var(--thick,74%), transparent);-webkit-backdrop-filter:blur(var(--blur,14px)) saturate(1.2);backdrop-filter:blur(var(--blur,14px)) saturate(1.2);border:1px solid color-mix(in oklch, var(--paper) 50%, var(--line));border-radius:var(--r-3);box-shadow:var(--shadow)}
|
|
88
|
-
.gy-gyeop::before{content:"";position:absolute;inset:0;border-radius:inherit;background-image:var(--grain);opacity:var(--grain-o);pointer-events:none;z-index:-1}
|
|
89
|
-
.gy-hol{--thick:46%;--blur:6px}
|
|
90
|
-
.gy-du {--thick:72%;--blur:14px}
|
|
91
|
-
.gy-se {--thick:90%;--blur:24px}
|
|
92
|
-
|
|
93
|
-
/* ---------- button ---------- */
|
|
94
|
-
.gy-btn{--bg-b:var(--accent);--fg-b:var(--on-accent);appearance:none;border:0;cursor:pointer;font:600 15px/1 var(--f-body);color:var(--fg-b);background:var(--bg-b);padding:13px 20px;border-radius:var(--r-2);display:inline-flex;align-items:center;gap:8px;text-decoration:none;transition:transform var(--t-1) var(--ease-bleed), box-shadow var(--t-3) var(--ease-bleed)}
|
|
95
|
-
.gy-btn:hover{box-shadow:0 0 0 1px var(--bg-b), 0 6px 22px -4px color-mix(in oklch, var(--bg-b) 60%, transparent)}
|
|
96
|
-
.gy-btn:active{transform:translateY(1px) scale(.985)}
|
|
97
|
-
.gy-btn--ink{--bg-b:var(--ink);--fg-b:var(--bg)}
|
|
98
|
-
.gy-btn--paper{--fg-b:var(--ink);background:color-mix(in oklch, var(--paper) 74%, transparent);border:1px solid var(--line);backdrop-filter:blur(12px)}
|
|
99
|
-
.gy-btn--paper:hover{box-shadow:0 6px 22px -8px color-mix(in oklch, var(--ink) 40%, transparent)}
|
|
100
|
-
.gy-btn--ghost{--fg-b:var(--accent);background:transparent}
|
|
101
|
-
.gy-btn--ghost:hover{background:color-mix(in oklch, var(--accent) 10%, transparent);box-shadow:none}
|
|
102
|
-
.gy-btn--danger{--bg-b:var(--err);--fg-b:oklch(99% 0 0)}
|
|
103
|
-
.gy-btn--sm{padding:9px 14px;font-size:14px;border-radius:10px}
|
|
104
|
-
.gy-btn:disabled{opacity:.45;cursor:not-allowed;box-shadow:none}
|
|
105
|
-
|
|
106
|
-
/* ---------- switch: <button class="gy-switch" role="switch" aria-checked="true"> ---------- */
|
|
107
|
-
.gy-switch{appearance:none;border:0;cursor:pointer;width:50px;height:30px;border-radius:var(--r-pill);background:var(--line);position:relative;transition:background var(--t-2) var(--ease-bleed);flex:none}
|
|
108
|
-
.gy-switch::after{content:"";position:absolute;top:3px;left:3px;width:24px;height:24px;border-radius:50%;background:var(--paper);box-shadow:0 2px 6px oklch(0% 0 0 / .25);transition:transform var(--t-2) var(--ease-bleed)}
|
|
109
|
-
.gy-switch[aria-checked="true"]{background:var(--accent)}
|
|
110
|
-
.gy-switch[aria-checked="true"]::after{transform:translateX(20px);background:oklch(99% 0 0)}
|
|
111
|
-
|
|
112
|
-
/* ---------- segmented: buttons with aria-pressed ---------- */
|
|
113
|
-
.gy-seg{display:inline-flex;padding:4px;border-radius:var(--r-pill);background:color-mix(in oklch, var(--ink) 7%, transparent);gap:2px}
|
|
114
|
-
.gy-seg button{appearance:none;border:0;background:transparent;color:var(--ink-2);font:500 14px/1 var(--f-body);padding:8px 14px;border-radius:var(--r-pill);cursor:pointer}
|
|
115
|
-
.gy-seg button[aria-pressed="true"]{background:var(--paper);color:var(--ink);box-shadow:0 1px 3px oklch(0% 0 0 / .14)}
|
|
116
|
-
|
|
117
|
-
/* ---------- chip ---------- */
|
|
118
|
-
.gy-chip{appearance:none;cursor:pointer;border:1px solid var(--line);background:transparent;color:var(--ink-2);font:500 14px/1 var(--f-body);padding:8px 13px;border-radius:var(--r-pill)}
|
|
119
|
-
.gy-chip[aria-pressed="true"]{background:var(--accent-soft);border-color:transparent;color:var(--ink)}
|
|
120
|
-
|
|
121
|
-
/* ---------- field ---------- */
|
|
122
|
-
.gy-field{display:flex;flex-direction:column;gap:6px}
|
|
123
|
-
.gy-field label{font-size:13px;font-weight:500;color:var(--ink-2)}
|
|
124
|
-
.gy-field input,.gy-field textarea,.gy-field select{font:400 15px var(--f-body);color:var(--ink);background:color-mix(in oklch, var(--paper) 80%, transparent);border:1px solid var(--line);border-radius:var(--r-2);padding:11px 13px;transition:border-color var(--t-2), box-shadow var(--t-3) var(--ease-bleed)}
|
|
125
|
-
.gy-field :is(input,textarea,select):focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 5px color-mix(in oklch, var(--accent) 18%, transparent), 0 0 18px 2px color-mix(in oklch, var(--accent) 22%, transparent)}
|
|
126
|
-
.gy-field small{font-size:13px;color:var(--ink-3)}
|
|
127
|
-
.gy-field--error :is(input,textarea,select){border-color:var(--err)}
|
|
128
|
-
.gy-field--error small{color:var(--err)}
|
|
129
|
-
|
|
130
|
-
/* ---------- checkbox: <label class="gy-check"><input type="checkbox"> 텍스트</label> ---------- */
|
|
131
|
-
.gy-check{display:flex;gap:10px;align-items:center;font-size:15px;cursor:pointer}
|
|
132
|
-
.gy-check input{appearance:none;width:20px;height:20px;border-radius:6px;border:1.5px solid var(--ink-3);display:grid;place-items:center;margin:0;cursor:pointer}
|
|
133
|
-
.gy-check input:checked{background:var(--accent);border-color:var(--accent)}
|
|
134
|
-
.gy-check input:checked::after{content:"";width:9px;height:5px;border:2px solid var(--on-accent);border-top:0;border-right:0;transform:rotate(-45deg) translate(1px,-1px)}
|
|
135
|
-
|
|
136
|
-
/* ---------- badge ---------- */
|
|
137
|
-
.gy-badge{font:600 12px/1 var(--f-body);padding:7px 11px;border-radius:var(--r-pill);display:inline-flex;align-items:center;gap:5px;color:var(--accent);background:color-mix(in oklch, var(--accent) 13%, transparent)}
|
|
138
|
-
.gy-badge::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor}
|
|
139
|
-
.gy-badge--ok{color:var(--ok);background:color-mix(in oklch, var(--ok) 14%, transparent)}
|
|
140
|
-
.gy-badge--warn{color:var(--warn);background:color-mix(in oklch, var(--warn) 16%, transparent)}
|
|
141
|
-
.gy-badge--err{color:var(--err);background:color-mix(in oklch, var(--err) 14%, transparent)}
|
|
142
|
-
|
|
143
|
-
/* ---------- progress: <div class="gy-progress" style="--p:60%"><i></i></div> ---------- */
|
|
144
|
-
.gy-progress{height:8px;border-radius:var(--r-pill);background:color-mix(in oklch, var(--ink) 9%, transparent);overflow:hidden}
|
|
145
|
-
.gy-progress > i{display:block;height:100%;width:var(--p,0%);border-radius:inherit;background:linear-gradient(90deg, color-mix(in oklch, var(--accent) 50%, transparent), var(--accent))}
|
|
146
|
-
|
|
147
|
-
/* ---------- toast / tooltip ---------- */
|
|
148
|
-
.gy-toast{display:flex;align-items:center;gap:12px;padding:12px 16px;font-size:14.5px}
|
|
149
|
-
.gy-tooltip{font-size:13px;padding:7px 11px;border-radius:10px;background:var(--ink);color:var(--bg)}
|
|
150
|
-
|
|
151
|
-
|
|
1
|
+
/*!
|
|
2
|
+
* 결 gyeol — layered-paper design system
|
|
3
|
+
* 사용법: <html data-palette="meok" data-theme="dark">
|
|
4
|
+
* data-palette: meok | jjok | cheongja | songhwa | yeonji | jaju |
|
|
5
|
+
* chija | hwangto | oksaek | haneul | odi (기본 meok)
|
|
6
|
+
* data-theme: dark | light | system (기본 dark)
|
|
7
|
+
* 화면 일부만 다른 팔레트·테마로: <section data-palette="yeonji" data-theme="light"> (0.1.2+)
|
|
8
|
+
* 모든 컴포넌트 클래스는 gy- 접두사를 써서 다른 CSS와 충돌하지 않아요.
|
|
9
|
+
* 글꼴: https://fonts.googleapis.com/css2?family=Hahmlet:wght@300;700;800&family=IBM+Plex+Sans+KR:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/* ---------- tokens ---------- */
|
|
13
|
+
:root{
|
|
14
|
+
--h:255; --c:.012; --ah:265; --ac:.14; --ad:0%; /* 기본 팔레트: 먹 + 쪽빛 강조 */
|
|
15
|
+
--f-display: "Hahmlet", "Nanum Myeongjo", "Apple SD Gothic Neo", serif;
|
|
16
|
+
--f-body: "IBM Plex Sans KR", "Apple SD Gothic Neo", "Malgun Gothic", system-ui, sans-serif;
|
|
17
|
+
--f-mono: "IBM Plex Mono", ui-monospace, "Consolas", monospace;
|
|
18
|
+
|
|
19
|
+
--pun-1:4px; --pun-2:8px; --pun-3:12px; --pun-4:16px; --pun-6:24px; --pun-8:32px; --pun-12:48px; --pun-16:64px;
|
|
20
|
+
--r-1:6px; --r-2:12px; --r-3:20px; --r-4:28px; --r-pill:999px;
|
|
21
|
+
--ease-bleed: cubic-bezier(.2,.7,.2,1);
|
|
22
|
+
--t-1:120ms; --t-2:220ms; --t-3:420ms;
|
|
23
|
+
|
|
24
|
+
}
|
|
25
|
+
/* 색 토큰: 루트와, 팔레트·테마를 따로 지정한 모든 요소에서 다시 계산돼요 */
|
|
26
|
+
:root, [data-palette], [data-theme]{
|
|
27
|
+
--bg: oklch(96.5% calc(var(--c)*.12) var(--h));
|
|
28
|
+
--bg-2: oklch(92.5% calc(var(--c)*.22) var(--h));
|
|
29
|
+
--ink: oklch(23% calc(var(--c)*.35) var(--h));
|
|
30
|
+
--ink-2: oklch(43% calc(var(--c)*.25) var(--h));
|
|
31
|
+
--ink-3: oklch(50% calc(var(--c)*.18) var(--h));
|
|
32
|
+
--line: oklch(84% calc(var(--c)*.22) var(--h));
|
|
33
|
+
--paper: oklch(99.2% calc(var(--c)*.05) var(--h));
|
|
34
|
+
--accent: oklch(calc(49% + var(--ad)) var(--ac) var(--ah));
|
|
35
|
+
--accent-soft: oklch(90% calc(var(--ac)*.45) var(--ah));
|
|
36
|
+
--on-accent: oklch(99% 0 0);
|
|
37
|
+
--stain: oklch(80% calc(var(--c)*.9 + .03) calc(var(--h) + 70));
|
|
38
|
+
--ok: oklch(50% .13 150); --warn: oklch(54% .13 65); --err: oklch(52% .19 25);
|
|
39
|
+
--shadow: 0 1px 0 oklch(100% 0 0 / .7) inset, 0 18px 40px -22px oklch(25% .06 var(--h) / .35), 0 2px 6px -2px oklch(25% .04 var(--h) / .12);
|
|
40
|
+
--grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='a'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .32 0'/></filter><filter id='b'><feTurbulence type='fractalNoise' baseFrequency='.012 .32' numOctaves='2' seed='4' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .7 -.42'/></filter><rect width='100%' height='100%' filter='url(%23a)'/><rect width='100%' height='100%' filter='url(%23b)'/></svg>");
|
|
41
|
+
--grain-o: .4;
|
|
42
|
+
|
|
43
|
+
color-scheme: light;
|
|
44
|
+
}
|
|
45
|
+
@media (prefers-color-scheme: dark){ :root[data-theme="system"], :root[data-theme="system"] [data-palette]:not([data-theme]):not([data-theme="light"] *){
|
|
46
|
+
--bg: oklch(17% calc(var(--c)*.22) var(--h)); --bg-2: oklch(21% calc(var(--c)*.3) var(--h));
|
|
47
|
+
--ink: oklch(95% calc(var(--c)*.08) var(--h)); --ink-2: oklch(79% calc(var(--c)*.15) var(--h)); --ink-3: oklch(70% calc(var(--c)*.15) var(--h));
|
|
48
|
+
--line: oklch(33% calc(var(--c)*.25) var(--h)); --paper: oklch(29% calc(var(--c)*.25) var(--h));
|
|
49
|
+
--accent: oklch(calc(76% + var(--ad)) var(--ac) var(--ah)); --accent-soft: oklch(33% calc(var(--ac)*.55) var(--ah)); --on-accent: oklch(18% .03 var(--ah));
|
|
50
|
+
--stain: oklch(48% calc(var(--c)*.9 + .03) calc(var(--h) + 70));
|
|
51
|
+
--ok: oklch(74% .14 150); --warn: oklch(80% .13 75); --err: oklch(72% .16 25);
|
|
52
|
+
--shadow: 0 1px 0 oklch(100% 0 0 / .07) inset, 0 20px 44px -20px oklch(5% .02 var(--h) / .7), 0 2px 6px -2px oklch(5% 0 0 / .4);
|
|
53
|
+
--grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='a'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .22 0'/></filter><filter id='b'><feTurbulence type='fractalNoise' baseFrequency='.012 .32' numOctaves='2' seed='4' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .6 -.4'/></filter><rect width='100%' height='100%' filter='url(%23a)'/><rect width='100%' height='100%' filter='url(%23b)'/></svg>");
|
|
54
|
+
--grain-o: .35; color-scheme: dark;
|
|
55
|
+
}}
|
|
56
|
+
:root:not([data-theme="light"]):not([data-theme="system"]), [data-theme="dark"],
|
|
57
|
+
:root:not([data-theme="light"]):not([data-theme="system"]) [data-palette]:not([data-theme]):not([data-theme="light"] *){
|
|
58
|
+
--bg: oklch(17% calc(var(--c)*.22) var(--h)); --bg-2: oklch(21% calc(var(--c)*.3) var(--h));
|
|
59
|
+
--ink: oklch(95% calc(var(--c)*.08) var(--h)); --ink-2: oklch(79% calc(var(--c)*.15) var(--h)); --ink-3: oklch(70% calc(var(--c)*.15) var(--h));
|
|
60
|
+
--line: oklch(33% calc(var(--c)*.25) var(--h)); --paper: oklch(29% calc(var(--c)*.25) var(--h));
|
|
61
|
+
--accent: oklch(calc(76% + var(--ad)) var(--ac) var(--ah)); --accent-soft: oklch(33% calc(var(--ac)*.55) var(--ah)); --on-accent: oklch(18% .03 var(--ah));
|
|
62
|
+
--stain: oklch(48% calc(var(--c)*.9 + .03) calc(var(--h) + 70));
|
|
63
|
+
--ok: oklch(74% .14 150); --warn: oklch(80% .13 75); --err: oklch(72% .16 25);
|
|
64
|
+
--shadow: 0 1px 0 oklch(100% 0 0 / .07) inset, 0 20px 44px -20px oklch(5% .02 var(--h) / .7), 0 2px 6px -2px oklch(5% 0 0 / .4);
|
|
65
|
+
--grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='220' height='220'><filter id='a'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .22 0'/></filter><filter id='b'><feTurbulence type='fractalNoise' baseFrequency='.012 .32' numOctaves='2' seed='4' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 .6 -.4'/></filter><rect width='100%' height='100%' filter='url(%23a)'/><rect width='100%' height='100%' filter='url(%23b)'/></svg>");
|
|
66
|
+
--grain-o: .35; color-scheme: dark;
|
|
67
|
+
}
|
|
68
|
+
[data-palette="meok"] {--h:255;--c:.012;--ah:265;--ac:.14;--ad:0%}
|
|
69
|
+
[data-palette="jjok"] {--h:265;--c:.14; --ah:265;--ac:.14;--ad:0%}
|
|
70
|
+
[data-palette="cheongja"]{--h:178;--c:.08;--ah:178;--ac:.08;--ad:4%}
|
|
71
|
+
[data-palette="songhwa"] {--h:98; --c:.12; --ah:98; --ac:.12;--ad:0%}
|
|
72
|
+
[data-palette="yeonji"] {--h:16; --c:.16; --ah:16; --ac:.16;--ad:0%}
|
|
73
|
+
[data-palette="jaju"] {--h:330;--c:.13; --ah:330;--ac:.13;--ad:0%}
|
|
74
|
+
[data-palette="chija"] {--h:70;--c:0.14;--ah:70;--ac:0.14;--ad:0%}
|
|
75
|
+
[data-palette="hwangto"] {--h:48;--c:.065;--ah:48;--ac:.065;--ad:0%}
|
|
76
|
+
[data-palette="oksaek"] {--h:195;--c:.10;--ah:195;--ac:.10;--ad:-5%}
|
|
77
|
+
[data-palette="haneul"] {--h:235;--c:.14;--ah:235;--ac:.14;--ad:-5%}
|
|
78
|
+
[data-palette="odi"] {--h:300;--c:.15;--ah:300;--ac:.15;--ad:-5%}
|
|
79
|
+
/* 화면 일부에 팔레트·테마를 따로 지정하면 글자색도 그 범위의 먹색을 따라가요 */
|
|
80
|
+
:where([data-palette], [data-theme]):not(:root){color:var(--ink)}
|
|
81
|
+
|
|
82
|
+
/* ---------- base (opt-in: add class="gy" to <body>) ---------- */
|
|
83
|
+
.gy{margin:0;color:var(--ink);background:var(--bg);font:400 16px/1.65 var(--f-body);-webkit-font-smoothing:antialiased}
|
|
84
|
+
.gy :focus-visible{outline:2px solid var(--accent);outline-offset:3px;box-shadow:0 0 0 7px color-mix(in oklch, var(--accent) 22%, transparent)}
|
|
85
|
+
|
|
86
|
+
/* ---------- material: 겹 ---------- */
|
|
87
|
+
.gy-gyeop{position:relative;isolation:isolate;background:color-mix(in oklch, var(--paper) var(--thick,74%), transparent);-webkit-backdrop-filter:blur(var(--blur,14px)) saturate(1.2);backdrop-filter:blur(var(--blur,14px)) saturate(1.2);border:1px solid color-mix(in oklch, var(--paper) 50%, var(--line));border-radius:var(--r-3);box-shadow:var(--shadow)}
|
|
88
|
+
.gy-gyeop::before{content:"";position:absolute;inset:0;border-radius:inherit;background-image:var(--grain);opacity:var(--grain-o);pointer-events:none;z-index:-1}
|
|
89
|
+
.gy-hol{--thick:46%;--blur:6px}
|
|
90
|
+
.gy-du {--thick:72%;--blur:14px}
|
|
91
|
+
.gy-se {--thick:90%;--blur:24px}
|
|
92
|
+
|
|
93
|
+
/* ---------- button ---------- */
|
|
94
|
+
.gy-btn{--bg-b:var(--accent);--fg-b:var(--on-accent);appearance:none;border:0;cursor:pointer;font:600 15px/1 var(--f-body);color:var(--fg-b);background:var(--bg-b);padding:13px 20px;border-radius:var(--r-2);display:inline-flex;align-items:center;gap:8px;text-decoration:none;transition:transform var(--t-1) var(--ease-bleed), box-shadow var(--t-3) var(--ease-bleed)}
|
|
95
|
+
.gy-btn:hover{box-shadow:0 0 0 1px var(--bg-b), 0 6px 22px -4px color-mix(in oklch, var(--bg-b) 60%, transparent)}
|
|
96
|
+
.gy-btn:active{transform:translateY(1px) scale(.985)}
|
|
97
|
+
.gy-btn--ink{--bg-b:var(--ink);--fg-b:var(--bg)}
|
|
98
|
+
.gy-btn--paper{--fg-b:var(--ink);background:color-mix(in oklch, var(--paper) 74%, transparent);border:1px solid var(--line);backdrop-filter:blur(12px)}
|
|
99
|
+
.gy-btn--paper:hover{box-shadow:0 6px 22px -8px color-mix(in oklch, var(--ink) 40%, transparent)}
|
|
100
|
+
.gy-btn--ghost{--fg-b:var(--accent);background:transparent}
|
|
101
|
+
.gy-btn--ghost:hover{background:color-mix(in oklch, var(--accent) 10%, transparent);box-shadow:none}
|
|
102
|
+
.gy-btn--danger{--bg-b:var(--err);--fg-b:oklch(99% 0 0)}
|
|
103
|
+
.gy-btn--sm{padding:9px 14px;font-size:14px;border-radius:10px}
|
|
104
|
+
.gy-btn:disabled{opacity:.45;cursor:not-allowed;box-shadow:none}
|
|
105
|
+
|
|
106
|
+
/* ---------- switch: <button class="gy-switch" role="switch" aria-checked="true"> ---------- */
|
|
107
|
+
.gy-switch{appearance:none;border:0;cursor:pointer;width:50px;height:30px;border-radius:var(--r-pill);background:var(--line);position:relative;transition:background var(--t-2) var(--ease-bleed);flex:none}
|
|
108
|
+
.gy-switch::after{content:"";position:absolute;top:3px;left:3px;width:24px;height:24px;border-radius:50%;background:var(--paper);box-shadow:0 2px 6px oklch(0% 0 0 / .25);transition:transform var(--t-2) var(--ease-bleed)}
|
|
109
|
+
.gy-switch[aria-checked="true"]{background:var(--accent)}
|
|
110
|
+
.gy-switch[aria-checked="true"]::after{transform:translateX(20px);background:oklch(99% 0 0)}
|
|
111
|
+
|
|
112
|
+
/* ---------- segmented: buttons with aria-pressed ---------- */
|
|
113
|
+
.gy-seg{display:inline-flex;padding:4px;border-radius:var(--r-pill);background:color-mix(in oklch, var(--ink) 7%, transparent);gap:2px}
|
|
114
|
+
.gy-seg button{appearance:none;border:0;background:transparent;color:var(--ink-2);font:500 14px/1 var(--f-body);padding:8px 14px;border-radius:var(--r-pill);cursor:pointer}
|
|
115
|
+
.gy-seg button[aria-pressed="true"]{background:var(--paper);color:var(--ink);box-shadow:0 1px 3px oklch(0% 0 0 / .14)}
|
|
116
|
+
|
|
117
|
+
/* ---------- chip ---------- */
|
|
118
|
+
.gy-chip{appearance:none;cursor:pointer;border:1px solid var(--line);background:transparent;color:var(--ink-2);font:500 14px/1 var(--f-body);padding:8px 13px;border-radius:var(--r-pill)}
|
|
119
|
+
.gy-chip[aria-pressed="true"]{background:var(--accent-soft);border-color:transparent;color:var(--ink)}
|
|
120
|
+
|
|
121
|
+
/* ---------- field ---------- */
|
|
122
|
+
.gy-field{display:flex;flex-direction:column;gap:6px}
|
|
123
|
+
.gy-field label{font-size:13px;font-weight:500;color:var(--ink-2)}
|
|
124
|
+
.gy-field input,.gy-field textarea,.gy-field select{font:400 15px var(--f-body);color:var(--ink);background:color-mix(in oklch, var(--paper) 80%, transparent);border:1px solid var(--line);border-radius:var(--r-2);padding:11px 13px;transition:border-color var(--t-2), box-shadow var(--t-3) var(--ease-bleed)}
|
|
125
|
+
.gy-field :is(input,textarea,select):focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 5px color-mix(in oklch, var(--accent) 18%, transparent), 0 0 18px 2px color-mix(in oklch, var(--accent) 22%, transparent)}
|
|
126
|
+
.gy-field small{font-size:13px;color:var(--ink-3)}
|
|
127
|
+
.gy-field--error :is(input,textarea,select){border-color:var(--err)}
|
|
128
|
+
.gy-field--error small{color:var(--err)}
|
|
129
|
+
|
|
130
|
+
/* ---------- checkbox: <label class="gy-check"><input type="checkbox"> 텍스트</label> ---------- */
|
|
131
|
+
.gy-check{display:flex;gap:10px;align-items:center;font-size:15px;cursor:pointer}
|
|
132
|
+
.gy-check input{appearance:none;width:20px;height:20px;border-radius:6px;border:1.5px solid var(--ink-3);display:grid;place-items:center;margin:0;cursor:pointer}
|
|
133
|
+
.gy-check input:checked{background:var(--accent);border-color:var(--accent)}
|
|
134
|
+
.gy-check input:checked::after{content:"";width:9px;height:5px;border:2px solid var(--on-accent);border-top:0;border-right:0;transform:rotate(-45deg) translate(1px,-1px)}
|
|
135
|
+
|
|
136
|
+
/* ---------- badge ---------- */
|
|
137
|
+
.gy-badge{font:600 12px/1 var(--f-body);padding:7px 11px;border-radius:var(--r-pill);display:inline-flex;align-items:center;gap:5px;color:var(--accent);background:color-mix(in oklch, var(--accent) 13%, transparent)}
|
|
138
|
+
.gy-badge::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor}
|
|
139
|
+
.gy-badge--ok{color:var(--ok);background:color-mix(in oklch, var(--ok) 14%, transparent)}
|
|
140
|
+
.gy-badge--warn{color:var(--warn);background:color-mix(in oklch, var(--warn) 16%, transparent)}
|
|
141
|
+
.gy-badge--err{color:var(--err);background:color-mix(in oklch, var(--err) 14%, transparent)}
|
|
142
|
+
|
|
143
|
+
/* ---------- progress: <div class="gy-progress" style="--p:60%"><i></i></div> ---------- */
|
|
144
|
+
.gy-progress{height:8px;border-radius:var(--r-pill);background:color-mix(in oklch, var(--ink) 9%, transparent);overflow:hidden}
|
|
145
|
+
.gy-progress > i{display:block;height:100%;width:var(--p,0%);border-radius:inherit;background:linear-gradient(90deg, color-mix(in oklch, var(--accent) 50%, transparent), var(--accent))}
|
|
146
|
+
|
|
147
|
+
/* ---------- toast / tooltip ---------- */
|
|
148
|
+
.gy-toast{display:flex;align-items:center;gap:12px;padding:12px 16px;font-size:14.5px}
|
|
149
|
+
.gy-tooltip{font-size:13px;padding:7px 11px;border-radius:10px;background:var(--ink);color:var(--bg)}
|
|
150
|
+
|
|
151
|
+
/* ---------- 터치 화면: 누르는 곳은 40px 이상 (0.1.7+) ---------- */
|
|
152
|
+
@media (pointer:coarse){
|
|
153
|
+
.gy-btn--sm,.gy-chip,.gy-seg button{min-height:40px}
|
|
154
|
+
.gy-check{min-height:40px}
|
|
155
|
+
.gy-switch::before{content:"";position:absolute;inset:-6px -4px}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
@media (prefers-reduced-motion: reduce){.gy *,.gy *::before,.gy *::after{transition:none!important;animation:none!important}}
|