gyeol 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +98 -2
- package/gyeol.css +136 -0
- package/index.cjs +22 -0
- package/index.d.ts +29 -0
- package/index.mjs +942 -0
- package/package.json +59 -4
- package/tailwind.preset.cjs +66 -0
- package/tokens.json +920 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 gyeol contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,99 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 결 gyeol
|
|
2
2
|
|
|
3
|
-
|
|
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;500;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.1/gyeol.css">
|
|
22
|
+
|
|
23
|
+
<html data-palette="meok" data-theme="dark">
|
|
24
|
+
<body class="gy">
|
|
25
|
+
<div class="gy-gyeop gy-du">
|
|
26
|
+
<button class="gy-btn">저장하기</button>
|
|
27
|
+
<button class="gy-btn gy-btn--paper">취소</button>
|
|
28
|
+
</div>
|
|
29
|
+
</body>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
번들러(Vite, Next.js 등)를 쓰면 CSS를 이렇게 불러와요.
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
import 'gyeol/gyeol.css';
|
|
36
|
+
import { setPalette, setTheme, color, tokens } from 'gyeol';
|
|
37
|
+
|
|
38
|
+
setPalette('cheongja');
|
|
39
|
+
setTheme('light');
|
|
40
|
+
color('meok', 'dark', 'accent'); // '#85aeff'
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
- `data-palette`: `meok`(기본) · `jjok` · `cheongja` · `songhwa` · `yeonji` · `jaju`
|
|
44
|
+
- `data-theme`: `dark`(기본) · `light` · `system`
|
|
45
|
+
- 컴포넌트 클래스는 모두 `gy-` 접두사를 써서 기존 CSS와 겹치지 않아요.
|
|
46
|
+
|
|
47
|
+
| 컴포넌트 | 클래스 |
|
|
48
|
+
| --- | --- |
|
|
49
|
+
| 겹 소재 | `gy-gyeop` + `gy-hol` / `gy-du` / `gy-se` |
|
|
50
|
+
| 버튼 | `gy-btn` + `--ink` / `--paper` / `--ghost` / `--danger` / `--sm` |
|
|
51
|
+
| 스위치 | `<button class="gy-switch" role="switch" aria-checked="true">` |
|
|
52
|
+
| 세그먼트 | `gy-seg` 안에 `aria-pressed` 버튼 |
|
|
53
|
+
| 칩 | `gy-chip` + `aria-pressed` |
|
|
54
|
+
| 입력 필드 | `gy-field` (오류는 `gy-field--error`) |
|
|
55
|
+
| 체크박스 | `<label class="gy-check"><input type="checkbox"> 텍스트</label>` |
|
|
56
|
+
| 배지 | `gy-badge` + `--ok` / `--warn` / `--err` |
|
|
57
|
+
| 진행 막대 | `<div class="gy-progress" style="--p:60%"><i></i></div>` |
|
|
58
|
+
| 토스트·툴팁 | `gy-toast`(겹과 함께), `gy-tooltip` |
|
|
59
|
+
|
|
60
|
+
## 2. Tailwind
|
|
61
|
+
|
|
62
|
+
```js
|
|
63
|
+
// tailwind.config.js
|
|
64
|
+
module.exports = {
|
|
65
|
+
presets: [require('gyeol/tailwind')],
|
|
66
|
+
};
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`gyeol.css`를 함께 불러와야 색이 정의돼요. 그다음 `bg-paper`, `text-ink-2`, `bg-accent`, `p-pun-4`, `rounded-r-3`, `font-display`, `gyeop gyeop-du`처럼 써요.
|
|
70
|
+
|
|
71
|
+
## 3. 디자인 토큰 JSON (Figma, iOS, Android)
|
|
72
|
+
|
|
73
|
+
`tokens.json`은 [DTCG](https://tr.designtokens.org/format/) 형식이고, 색은 팔레트·테마별로 계산된 HEX 값이에요.
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
gyeol.color.<팔레트>.<light|dark>.<역할> 예) gyeol.color.meok.dark.accent = #85aeff
|
|
77
|
+
gyeol.space.pun-4 = 16px
|
|
78
|
+
gyeol.radius.r-3 = 20px
|
|
79
|
+
gyeol.material.du.thickness = 0.72
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
- **JS에서 바로**: `import tokens from 'gyeol'` 또는 `import tokens from 'gyeol/tokens.json'`
|
|
83
|
+
- **Figma**: Tokens Studio 플러그인에서 `tokens.json`을 불러오세요.
|
|
84
|
+
- **iOS / Android**: Style Dictionary로 `tokens.json`을 Swift, Kotlin, XML 리소스로 변환하세요.
|
|
85
|
+
|
|
86
|
+
HEX 값은 화면용 sRGB로 변환한 근사치예요. 웹에서는 `gyeol.css`의 oklch 원본을 쓰는 게 더 정확해요.
|
|
87
|
+
|
|
88
|
+
## 패키지 경로
|
|
89
|
+
|
|
90
|
+
| 경로 | 내용 |
|
|
91
|
+
| --- | --- |
|
|
92
|
+
| `gyeol` | 토큰 객체와 `setPalette`, `setTheme`, `color` 함수 (ESM·CommonJS·TypeScript 타입 포함) |
|
|
93
|
+
| `gyeol/gyeol.css` (또는 `gyeol/css`) | 전체 CSS |
|
|
94
|
+
| `gyeol/tailwind` | Tailwind 프리셋 |
|
|
95
|
+
| `gyeol/tokens.json` | DTCG 토큰 원본 |
|
|
96
|
+
|
|
97
|
+
## 라이선스
|
|
98
|
+
|
|
99
|
+
MIT
|
package/gyeol.css
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* 결 gyeol — layered-paper design system
|
|
3
|
+
* 사용법: <html data-palette="meok" data-theme="dark">
|
|
4
|
+
* data-palette: meok | jjok | cheongja | songhwa | yeonji | jaju (기본 meok)
|
|
5
|
+
* data-theme: dark | light | system (기본 dark)
|
|
6
|
+
* 모든 컴포넌트 클래스는 gy- 접두사를 써서 다른 CSS와 충돌하지 않아요.
|
|
7
|
+
* 글꼴: https://fonts.googleapis.com/css2?family=Hahmlet:wght@300;500;700;800&family=IBM+Plex+Sans+KR:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/* ---------- tokens ---------- */
|
|
11
|
+
:root{
|
|
12
|
+
--h:255; --c:.012; --ah:var(--h); --ac:var(--c);
|
|
13
|
+
--bg: oklch(96.5% calc(var(--c)*.12) var(--h));
|
|
14
|
+
--bg-2: oklch(92.5% calc(var(--c)*.22) var(--h));
|
|
15
|
+
--ink: oklch(23% calc(var(--c)*.35) var(--h));
|
|
16
|
+
--ink-2: oklch(43% calc(var(--c)*.25) var(--h));
|
|
17
|
+
--ink-3: oklch(50% calc(var(--c)*.18) var(--h));
|
|
18
|
+
--line: oklch(84% calc(var(--c)*.22) var(--h));
|
|
19
|
+
--paper: oklch(99.2% calc(var(--c)*.05) var(--h));
|
|
20
|
+
--accent: oklch(49% var(--ac) var(--ah));
|
|
21
|
+
--accent-soft: oklch(90% calc(var(--ac)*.45) var(--ah));
|
|
22
|
+
--on-accent: oklch(99% 0 0);
|
|
23
|
+
--stain: oklch(80% calc(var(--c)*.9 + .03) calc(var(--h) + 70));
|
|
24
|
+
--ok: oklch(50% .13 150); --warn: oklch(54% .13 65); --err: oklch(52% .19 25);
|
|
25
|
+
--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);
|
|
26
|
+
--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>");
|
|
27
|
+
--grain-o: .4;
|
|
28
|
+
|
|
29
|
+
--f-display: "Hahmlet", "Nanum Myeongjo", "Apple SD Gothic Neo", serif;
|
|
30
|
+
--f-body: "IBM Plex Sans KR", "Apple SD Gothic Neo", "Malgun Gothic", system-ui, sans-serif;
|
|
31
|
+
--f-mono: "IBM Plex Mono", ui-monospace, "Consolas", monospace;
|
|
32
|
+
|
|
33
|
+
--pun-1:4px; --pun-2:8px; --pun-3:12px; --pun-4:16px; --pun-6:24px; --pun-8:32px; --pun-12:48px; --pun-16:64px;
|
|
34
|
+
--r-1:6px; --r-2:12px; --r-3:20px; --r-4:28px; --r-pill:999px;
|
|
35
|
+
--ease-bleed: cubic-bezier(.2,.7,.2,1);
|
|
36
|
+
--t-1:120ms; --t-2:220ms; --t-3:420ms;
|
|
37
|
+
}
|
|
38
|
+
@media (prefers-color-scheme: dark){ :root[data-theme="system"]{
|
|
39
|
+
--bg: oklch(17% calc(var(--c)*.22) var(--h)); --bg-2: oklch(21% calc(var(--c)*.3) var(--h));
|
|
40
|
+
--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));
|
|
41
|
+
--line: oklch(33% calc(var(--c)*.25) var(--h)); --paper: oklch(29% calc(var(--c)*.25) var(--h));
|
|
42
|
+
--accent: oklch(76% var(--ac) var(--ah)); --accent-soft: oklch(33% calc(var(--ac)*.55) var(--ah)); --on-accent: oklch(18% .03 var(--ah));
|
|
43
|
+
--stain: oklch(48% calc(var(--c)*.9 + .03) calc(var(--h) + 70));
|
|
44
|
+
--ok: oklch(74% .14 150); --warn: oklch(80% .13 75); --err: oklch(72% .16 25);
|
|
45
|
+
--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);
|
|
46
|
+
--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>");
|
|
47
|
+
--grain-o: .35; color-scheme: dark;
|
|
48
|
+
}}
|
|
49
|
+
:root:not([data-theme="light"]):not([data-theme="system"]){
|
|
50
|
+
--bg: oklch(17% calc(var(--c)*.22) var(--h)); --bg-2: oklch(21% calc(var(--c)*.3) var(--h));
|
|
51
|
+
--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));
|
|
52
|
+
--line: oklch(33% calc(var(--c)*.25) var(--h)); --paper: oklch(29% calc(var(--c)*.25) var(--h));
|
|
53
|
+
--accent: oklch(76% var(--ac) var(--ah)); --accent-soft: oklch(33% calc(var(--ac)*.55) var(--ah)); --on-accent: oklch(18% .03 var(--ah));
|
|
54
|
+
--stain: oklch(48% calc(var(--c)*.9 + .03) calc(var(--h) + 70));
|
|
55
|
+
--ok: oklch(74% .14 150); --warn: oklch(80% .13 75); --err: oklch(72% .16 25);
|
|
56
|
+
--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);
|
|
57
|
+
--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>");
|
|
58
|
+
--grain-o: .35; color-scheme: dark;
|
|
59
|
+
}
|
|
60
|
+
:root[data-palette="meok"] {--h:255;--c:.012;--ah:265;--ac:.14}
|
|
61
|
+
:root[data-palette="jjok"] {--h:265;--c:.14}
|
|
62
|
+
:root[data-palette="cheongja"]{--h:178;--c:.075}
|
|
63
|
+
:root[data-palette="songhwa"] {--h:98; --c:.12}
|
|
64
|
+
:root[data-palette="yeonji"] {--h:16; --c:.16}
|
|
65
|
+
:root[data-palette="jaju"] {--h:330;--c:.13}
|
|
66
|
+
|
|
67
|
+
/* ---------- base (opt-in: add class="gy" to <body>) ---------- */
|
|
68
|
+
.gy{margin:0;color:var(--ink);background:var(--bg);font:400 16px/1.65 var(--f-body);-webkit-font-smoothing:antialiased}
|
|
69
|
+
.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)}
|
|
70
|
+
|
|
71
|
+
/* ---------- material: 겹 ---------- */
|
|
72
|
+
.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)}
|
|
73
|
+
.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}
|
|
74
|
+
.gy-hol{--thick:46%;--blur:6px}
|
|
75
|
+
.gy-du {--thick:72%;--blur:14px}
|
|
76
|
+
.gy-se {--thick:90%;--blur:24px}
|
|
77
|
+
|
|
78
|
+
/* ---------- button ---------- */
|
|
79
|
+
.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)}
|
|
80
|
+
.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)}
|
|
81
|
+
.gy-btn:active{transform:translateY(1px) scale(.985)}
|
|
82
|
+
.gy-btn--ink{--bg-b:var(--ink);--fg-b:var(--bg)}
|
|
83
|
+
.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)}
|
|
84
|
+
.gy-btn--paper:hover{box-shadow:0 6px 22px -8px color-mix(in oklch, var(--ink) 40%, transparent)}
|
|
85
|
+
.gy-btn--ghost{--fg-b:var(--accent);background:transparent}
|
|
86
|
+
.gy-btn--ghost:hover{background:color-mix(in oklch, var(--accent) 10%, transparent);box-shadow:none}
|
|
87
|
+
.gy-btn--danger{--bg-b:var(--err);--fg-b:oklch(99% 0 0)}
|
|
88
|
+
.gy-btn--sm{padding:9px 14px;font-size:14px;border-radius:10px}
|
|
89
|
+
.gy-btn:disabled{opacity:.45;cursor:not-allowed;box-shadow:none}
|
|
90
|
+
|
|
91
|
+
/* ---------- switch: <button class="gy-switch" role="switch" aria-checked="true"> ---------- */
|
|
92
|
+
.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}
|
|
93
|
+
.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)}
|
|
94
|
+
.gy-switch[aria-checked="true"]{background:var(--accent)}
|
|
95
|
+
.gy-switch[aria-checked="true"]::after{transform:translateX(20px);background:oklch(99% 0 0)}
|
|
96
|
+
|
|
97
|
+
/* ---------- segmented: buttons with aria-pressed ---------- */
|
|
98
|
+
.gy-seg{display:inline-flex;padding:4px;border-radius:var(--r-pill);background:color-mix(in oklch, var(--ink) 7%, transparent);gap:2px}
|
|
99
|
+
.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}
|
|
100
|
+
.gy-seg button[aria-pressed="true"]{background:var(--paper);color:var(--ink);box-shadow:0 1px 3px oklch(0% 0 0 / .14)}
|
|
101
|
+
|
|
102
|
+
/* ---------- chip ---------- */
|
|
103
|
+
.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)}
|
|
104
|
+
.gy-chip[aria-pressed="true"]{background:var(--accent-soft);border-color:transparent;color:var(--ink)}
|
|
105
|
+
|
|
106
|
+
/* ---------- field ---------- */
|
|
107
|
+
.gy-field{display:flex;flex-direction:column;gap:6px}
|
|
108
|
+
.gy-field label{font-size:13px;font-weight:500;color:var(--ink-2)}
|
|
109
|
+
.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)}
|
|
110
|
+
.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)}
|
|
111
|
+
.gy-field small{font-size:13px;color:var(--ink-3)}
|
|
112
|
+
.gy-field--error :is(input,textarea,select){border-color:var(--err)}
|
|
113
|
+
.gy-field--error small{color:var(--err)}
|
|
114
|
+
|
|
115
|
+
/* ---------- checkbox: <label class="gy-check"><input type="checkbox"> 텍스트</label> ---------- */
|
|
116
|
+
.gy-check{display:flex;gap:10px;align-items:center;font-size:15px;cursor:pointer}
|
|
117
|
+
.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}
|
|
118
|
+
.gy-check input:checked{background:var(--accent);border-color:var(--accent)}
|
|
119
|
+
.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)}
|
|
120
|
+
|
|
121
|
+
/* ---------- badge ---------- */
|
|
122
|
+
.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)}
|
|
123
|
+
.gy-badge::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor}
|
|
124
|
+
.gy-badge--ok{color:var(--ok);background:color-mix(in oklch, var(--ok) 14%, transparent)}
|
|
125
|
+
.gy-badge--warn{color:var(--warn);background:color-mix(in oklch, var(--warn) 16%, transparent)}
|
|
126
|
+
.gy-badge--err{color:var(--err);background:color-mix(in oklch, var(--err) 14%, transparent)}
|
|
127
|
+
|
|
128
|
+
/* ---------- progress: <div class="gy-progress" style="--p:60%"><i></i></div> ---------- */
|
|
129
|
+
.gy-progress{height:8px;border-radius:var(--r-pill);background:color-mix(in oklch, var(--ink) 9%, transparent);overflow:hidden}
|
|
130
|
+
.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))}
|
|
131
|
+
|
|
132
|
+
/* ---------- toast / tooltip ---------- */
|
|
133
|
+
.gy-toast{display:flex;align-items:center;gap:12px;padding:12px 16px;font-size:14.5px}
|
|
134
|
+
.gy-tooltip{font-size:13px;padding:7px 11px;border-radius:10px;background:var(--ink);color:var(--bg)}
|
|
135
|
+
|
|
136
|
+
@media (prefers-reduced-motion: reduce){.gy *,.gy *::before,.gy *::after{transition:none!important;animation:none!important}}
|
package/index.cjs
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// 결 gyeol — CommonJS entry
|
|
2
|
+
const tokens = require("./tokens.json");
|
|
3
|
+
|
|
4
|
+
const palettes = ["meok", "jjok", "cheongja", "songhwa", "yeonji", "jaju"];
|
|
5
|
+
const themes = ["dark", "light", "system"];
|
|
6
|
+
|
|
7
|
+
/** <html data-palette> 값을 바꿔요. */
|
|
8
|
+
function setPalette(palette, el = document.documentElement) {
|
|
9
|
+
el.setAttribute("data-palette", palette);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** <html data-theme> 값을 바꿔요. */
|
|
13
|
+
function setTheme(theme, el = document.documentElement) {
|
|
14
|
+
el.setAttribute("data-theme", theme);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** 팔레트·테마·역할로 HEX 색을 돌려줘요. 예) color("meok", "dark", "accent") */
|
|
18
|
+
function color(palette, theme, role) {
|
|
19
|
+
return tokens.gyeol.color[palette][theme][role].$value;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = { tokens, palettes, themes, setPalette, setTheme, color, default: tokens };
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type Palette = "meok" | "jjok" | "cheongja" | "songhwa" | "yeonji" | "jaju";
|
|
2
|
+
export type Theme = "dark" | "light" | "system";
|
|
3
|
+
export type ColorMode = "light" | "dark";
|
|
4
|
+
export type ColorRole =
|
|
5
|
+
| "bg" | "bg-2" | "ink" | "ink-2" | "ink-3" | "line" | "paper"
|
|
6
|
+
| "accent" | "accent-soft" | "on-accent" | "stain" | "ok" | "warn" | "err";
|
|
7
|
+
|
|
8
|
+
interface Token<T> { $type: string; $value: T; $description?: string }
|
|
9
|
+
|
|
10
|
+
export interface GyeolTokens {
|
|
11
|
+
gyeol: {
|
|
12
|
+
color: Record<Palette, { $description: string } & Record<ColorMode, Record<ColorRole, Token<string>>>>;
|
|
13
|
+
font: Record<"display" | "body" | "mono", Token<string[]>>;
|
|
14
|
+
type: Record<"display" | "h1" | "h2" | "h3" | "body" | "label", Token<{ fontFamily: string; fontSize: string; fontWeight: number; lineHeight: number }>>;
|
|
15
|
+
space: Record<"pun-1" | "pun-2" | "pun-3" | "pun-4" | "pun-6" | "pun-8" | "pun-12" | "pun-16", Token<string>>;
|
|
16
|
+
radius: Record<"r-1" | "r-2" | "r-3" | "r-4" | "pill", Token<string>>;
|
|
17
|
+
duration: Record<"t-1" | "t-2" | "t-3", Token<string>>;
|
|
18
|
+
easing: { bleed: Token<[number, number, number, number]> };
|
|
19
|
+
material: Record<"hol" | "du" | "se", { thickness: Token<number>; blur: Token<string> }>;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export declare const tokens: GyeolTokens;
|
|
24
|
+
export declare const palettes: Palette[];
|
|
25
|
+
export declare const themes: Theme[];
|
|
26
|
+
export declare function setPalette(palette: Palette, el?: HTMLElement): void;
|
|
27
|
+
export declare function setTheme(theme: Theme, el?: HTMLElement): void;
|
|
28
|
+
export declare function color(palette: Palette, theme: ColorMode, role: ColorRole): string;
|
|
29
|
+
export default tokens;
|